Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support analysing AMD module format #8389

Merged
merged 24 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d39db15
feat(amd): add amd runtime globals, dep category, and dep type
nilptr Oct 20, 2024
7eea7a8
feat(amd): add amd deps
nilptr Oct 20, 2024
797b1f7
feat(amd): add amd_define_dependency_parser_plugin and require_js_stu…
nilptr Oct 28, 2024
046f3cc
feat(amd): set AmdRequireItem dep factory
nilptr Oct 29, 2024
260a409
fix(amd): json_stringify named_module
nilptr Oct 29, 2024
5f3c1f5
chore: save tests
nilptr Oct 29, 2024
0fee5a3
fix(amd): flag local module used when it's actually used
nilptr Oct 31, 2024
823bf30
feat(amd): add amd_require_dependencies_block_parser_plugin
nilptr Nov 5, 2024
1105bf1
chore: save amd require tests
nilptr Nov 10, 2024
f93209d
chore: save amd require tests
nilptr Nov 10, 2024
e1c9916
feat(amd): add unsupported dependency
nilptr Nov 10, 2024
aee8f9c
feat(amd): add amd option
nilptr Nov 21, 2024
7de7fde
feat(amd): add amd runtime modules
nilptr Nov 21, 2024
c8c18b9
feat(amd): use amd plugins
nilptr Nov 21, 2024
49ed652
feat(amd): move amd test cases under config
nilptr Nov 23, 2024
b42223e
feat(amd): add AMDPlugin requirejs-related logic to RequireJsStuffPlugin
nilptr Nov 24, 2024
35db167
chore(amd): update test cases
nilptr Nov 24, 2024
6a3f350
feat(amd): add real amd libs as amd test cases
nilptr Nov 24, 2024
1505d7a
refactor(amd_define_dependency): use format macro named parameters to…
nilptr Nov 24, 2024
a434965
fix(amd ci): update jest snapshot
nilptr Nov 24, 2024
cc60625
chore: format code
nilptr Nov 24, 2024
109ce4c
chore: fix rust check issues
nilptr Nov 24, 2024
48f3f4c
chore: revert unexpected change
nilptr Nov 25, 2024
e769a2c
chore: fix spellchecker issues
nilptr Nov 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1722,6 +1722,7 @@ export interface RawOptions {
experiments: RawExperiments
node?: RawNodeOption
profile: boolean
amd?: string
bail: boolean
__references: Record<string, any>
}
Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_binding_options/src/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub struct RawOptions {
pub experiments: RawExperiments,
pub node: Option<RawNodeOption>,
pub profile: bool,
pub amd: Option<String>,
pub bail: bool,
#[napi(js_name = "__references", ts_type = "Record<string, any>")]
pub __references: References,
Expand Down Expand Up @@ -93,6 +94,7 @@ impl TryFrom<RawOptions> for CompilerOptions {
optimization,
node,
profile: value.profile,
amd: value.amd,
bail: value.bail,
__references: value.__references,
})
Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_core/src/dependency/dependency_category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub enum DependencyCategory {
Unknown,
Esm,
CommonJS,
Amd,
Url,
CssImport,
CssCompose,
Expand Down Expand Up @@ -41,6 +42,7 @@ impl DependencyCategory {
DependencyCategory::Unknown => "unknown",
DependencyCategory::Esm => "esm",
DependencyCategory::CommonJS => "commonjs",
DependencyCategory::Amd => "amd",
DependencyCategory::Url => "url",
DependencyCategory::CssImport => "css-import",
DependencyCategory::CssCompose => "css-compose",
Expand Down
11 changes: 11 additions & 0 deletions crates/rspack_core/src/dependency/dependency_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ pub enum DependencyType {
CjsExportRequire,
// cjs self reference
CjsSelfReference,
// AMD
AmdDefine,
AmdRequireArray,
AmdRequireContext,
AmdRequire,
AmdRequireItem,
// new URL("./foo", import.meta.url)
NewUrl,
// new Worker()
Expand Down Expand Up @@ -126,6 +132,11 @@ impl DependencyType {
DependencyType::CjsExports => "cjs exports",
DependencyType::CjsExportRequire => "cjs export require",
DependencyType::CjsSelfReference => "cjs self exports reference",
DependencyType::AmdDefine => "amd define",
DependencyType::AmdRequireArray => "amd require array",
DependencyType::AmdRequireContext => "amd require context",
DependencyType::AmdRequire => "amd",
DependencyType::AmdRequireItem => "amd require",
DependencyType::NewUrl => "new URL()",
DependencyType::NewWorker => "new Worker()",
DependencyType::CreateScriptUrl => "create script url",
Expand Down
1 change: 1 addition & 0 deletions crates/rspack_core/src/options/compiler_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct CompilerOptions {
pub node: Option<NodeOption>,
pub optimization: Optimization,
pub profile: bool,
pub amd: Option<String>,
pub bail: bool,
pub __references: References,
}
Expand Down
6 changes: 6 additions & 0 deletions crates/rspack_core/src/runtime_globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ bitflags! {
const RSPACK_UNIQUE_ID = 1 << 65;

const HAS_FETCH_PRIORITY = 1 << 66;

// amd module support
const AMD_DEFINE = 1 << 67;
const AMD_OPTIONS = 1 << 68;
}
}

Expand Down Expand Up @@ -295,6 +299,8 @@ impl RuntimeGlobals {
R::GET_CHUNK_UPDATE_CSS_FILENAME => "__webpack_require__.hk",
R::HMR_MODULE_DATA => "__webpack_require__.hmrD",
R::HMR_RUNTIME_STATE_PREFIX => "__webpack_require__.hmrS",
R::AMD_DEFINE => "__webpack_require__.amdD",
R::AMD_OPTIONS => "__webpack_require__.amdO",
R::EXTERNAL_INSTALL_CHUNK => "__webpack_require__.C",
R::GET_FULL_HASH => "__webpack_require__.h",
R::GLOBAL => "__webpack_require__.g",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
use bitflags::bitflags;
use rspack_core::{
AffectType, AsContextDependency, AsModuleDependency, Compilation, Dependency, DependencyCategory,
DependencyId, DependencyTemplate, DependencyType, RuntimeGlobals, RuntimeSpec, TemplateContext,
TemplateReplaceSource,
};
use rspack_util::{atom::Atom, json_stringify};

use super::local_module::LocalModule;

bitflags! {
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
struct Branch: u8 {
const L = 1 << 0;
const A = 1 << 1;
const O = 1 << 2;
const F = 1 << 3;
}
}

impl Branch {
pub fn get_requests(&self) -> RuntimeGlobals {
match *self {
f if f == Branch::F => {
RuntimeGlobals::REQUIRE | RuntimeGlobals::EXPORTS | RuntimeGlobals::MODULE
}
o if o == Branch::O => RuntimeGlobals::MODULE,
o_f if o_f == (Branch::O | Branch::F) => {
RuntimeGlobals::REQUIRE | RuntimeGlobals::EXPORTS | RuntimeGlobals::MODULE
}
a_f if a_f == (Branch::A | Branch::F) => RuntimeGlobals::EXPORTS | RuntimeGlobals::MODULE,
a_o if a_o == (Branch::A | Branch::O) => RuntimeGlobals::MODULE,
a_o_f if a_o_f == (Branch::A | Branch::O | Branch::F) => {
RuntimeGlobals::EXPORTS | RuntimeGlobals::MODULE
}
l_f if l_f == (Branch::L | Branch::F) => RuntimeGlobals::REQUIRE | RuntimeGlobals::MODULE,
l_o if l_o == (Branch::L | Branch::O) => RuntimeGlobals::empty(),
l_o_f if l_o_f == (Branch::L | Branch::O | Branch::F) => {
RuntimeGlobals::REQUIRE | RuntimeGlobals::MODULE
}
l_a_f if l_a_f == (Branch::L | Branch::A | Branch::F) => RuntimeGlobals::empty(),
l_a_o if l_a_o == (Branch::L | Branch::A | Branch::O) => RuntimeGlobals::empty(),
l_a_o_f if l_a_o_f == (Branch::L | Branch::A | Branch::O | Branch::F) => {
RuntimeGlobals::empty()
}
_ => RuntimeGlobals::empty(),
}
}

pub fn get_definition(&self, local_module_var: &Option<String>) -> String {
let name = match local_module_var {
Some(name) => name,
None => "XXX",
};
match *self {
f if f == Branch::F => "var __WEBPACK_AMD_DEFINE_RESULT__;".to_string(),
o if o == Branch::O => "".to_string(),
o_f if o_f == (Branch::O | Branch::F) => {
"var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;".to_string()
}
a_f if a_f == (Branch::A | Branch::F) => {
"var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;".to_string()
}
a_o if a_o == (Branch::A | Branch::O) => "".to_string(),
a_o_f if a_o_f == (Branch::A | Branch::O | Branch::F) => "var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;".to_string(),
l_f if l_f == (Branch::L | Branch::F) => {
format!("var {}, {}module;", name, name)
},
l_o if l_o == (Branch::L | Branch::O) => {
format!("var {};", name)
},
l_o_f if l_o_f == (Branch::L | Branch::O | Branch::F) => {
format!("var {}, {}factory, {}module;", name, name, name)
},
l_a_f if l_a_f == (Branch::L | Branch::A | Branch::F) => {
format!("var __WEBPACK_AMD_DEFINE_ARRAY__, {}, {}exports;", name, name)
},
l_a_o if l_a_o == (Branch::L | Branch::A | Branch::O)=> {
format!("var {};", name)
},
l_a_o_f if l_a_o_f == (Branch::L | Branch::A | Branch::O | Branch::F) => {
format!("var {}array, {}factory, {}exports, {};", name, name, name, name)
},
_ => "".to_string(),
}
}

pub fn get_content(
&self,
local_module_var: &Option<String>,
named_module: &Option<Atom>,
) -> String {
let local_module_var = match local_module_var {
Some(name) => name,
None => "XXX",
};
let named_module = match named_module {
Some(name) => name,
None => "YYY",
};
match *self {
f if f == Branch::F => {
format!(
"!(__WEBPACK_AMD_DEFINE_RESULT__ = (#).call(exports, {require}, exports, module),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))",
require = RuntimeGlobals::REQUIRE.name()
)
}
o if o == Branch::O => "!(module.exports = #)".to_string(),
o_f if o_f == (Branch::O | Branch::F) => {
format!(
"!(__WEBPACK_AMD_DEFINE_FACTORY__ = (#),
__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
(__WEBPACK_AMD_DEFINE_FACTORY__.call(exports, {require}, exports, module)) :
__WEBPACK_AMD_DEFINE_FACTORY__),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))",
require = RuntimeGlobals::REQUIRE.name()
)
}
a_f if a_f == (Branch::A | Branch::F) => "!(__WEBPACK_AMD_DEFINE_ARRAY__ = #, __WEBPACK_AMD_DEFINE_RESULT__ = (#).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))".to_string(),
a_o if a_o == (Branch::A | Branch::O) => "!(#, module.exports = #)".to_string(),
a_o_f if a_o_f == (Branch::A | Branch::O | Branch::F) => {
"!(__WEBPACK_AMD_DEFINE_ARRAY__ = #, __WEBPACK_AMD_DEFINE_FACTORY__ = (#),
__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))".to_string()
}
l_f if l_f == (Branch::L | Branch::F) => {
format!(
"!({var_name}module = {{ id: {module_id}, exports: {{}}, loaded: false }}, {var_name} = (#).call({var_name}module.exports, {require}, {var_name}module.exports, {var_name}module), {var_name}module.loaded = true, {var_name} === undefined && ({var_name} = {var_name}module.exports))",
var_name = local_module_var,
module_id = json_stringify(named_module),
require = RuntimeGlobals::REQUIRE.name(),
)
}
l_o if l_o == (Branch::L | Branch::O) => format!("!({} = #)", local_module_var),
l_o_f if l_o_f == (Branch::L | Branch::O | Branch::F) => {
format!(
"!({var_name}factory = (#), (typeof {var_name}factory === 'function' ? (({var_name}module = {{ id: {module_id}, exports: {{}}, loaded: false }}), ({var_name} = {var_name}factory.call({var_name}module.exports, {require}, {var_name}module.exports, {var_name}module)), ({var_name}module.loaded = true), {var_name} === undefined && ({var_name} = {var_name}module.exports)) : {var_name} = {var_name}factory))",
var_name = local_module_var,
module_id = json_stringify(named_module),
require = RuntimeGlobals::REQUIRE.name(),
)
}
l_a_f if l_a_f == (Branch::L | Branch::A | Branch::F) => format!("!(__WEBPACK_AMD_DEFINE_ARRAY__ = #, {} = (#).apply({}exports = {{}}, __WEBPACK_AMD_DEFINE_ARRAY__), {} === undefined && ({} = {}exports))", local_module_var, local_module_var, local_module_var, local_module_var, local_module_var),
l_a_o if l_a_o == (Branch::L | Branch::A | Branch::O) => format!("!(#, {} = #)", local_module_var),
l_a_o_f if l_a_o_f == (Branch::L | Branch::A | Branch::O | Branch::F) => format!(
"!({var_name}array = #, {var_name}factory = (#),
(typeof {var_name}factory === 'function' ?
(({var_name} = {var_name}factory.apply({var_name}exports = {{}}, {var_name}array)), {var_name} === undefined && ({var_name} = {var_name}exports)) :
({var_name} = {var_name}factory)
))",
var_name = local_module_var,
),
_ => "".to_string(),
}
}
}

#[derive(Debug, Clone)]
pub struct AmdDefineDependency {
id: DependencyId,
range: (u32, u32),
array_range: Option<(u32, u32)>,
function_range: Option<(u32, u32)>,
object_range: Option<(u32, u32)>,
named_module: Option<Atom>,
local_module: Option<LocalModule>,
}

impl AmdDefineDependency {
pub fn new(
range: (u32, u32),
array_range: Option<(u32, u32)>,
function_range: Option<(u32, u32)>,
object_range: Option<(u32, u32)>,
named_module: Option<Atom>,
local_module: Option<LocalModule>,
) -> Self {
Self {
id: DependencyId::new(),
range,
array_range,
function_range,
object_range,
named_module,
local_module,
}
}

pub fn get_local_module_mut(&mut self) -> Option<&mut LocalModule> {
self.local_module.as_mut()
}
}

impl Dependency for AmdDefineDependency {
fn id(&self) -> &DependencyId {
&self.id
}

fn category(&self) -> &DependencyCategory {
&DependencyCategory::Amd
}

fn dependency_type(&self) -> &DependencyType {
&DependencyType::AmdDefine
}

fn could_affect_referencing_module(&self) -> AffectType {
AffectType::False
}
}

impl AmdDefineDependency {
fn local_module_var(&self) -> Option<String> {
self.local_module.as_ref().and_then(|m| {
if m.is_used() {
Some(m.variable_name())
} else {
None
}
})
}

fn branch(&self) -> Branch {
let mut ret = Branch::empty();
if self.local_module.as_ref().is_some_and(|m| m.is_used()) {
ret |= Branch::L;
}
if self.array_range.is_some() {
ret |= Branch::A;
}
if self.object_range.is_some() {
ret |= Branch::O;
}
if self.function_range.is_some() {
ret |= Branch::F;
}
ret
}
}

impl DependencyTemplate for AmdDefineDependency {
fn apply(
&self,
source: &mut TemplateReplaceSource,
code_generatable_context: &mut TemplateContext,
) {
let branch = self.branch();
code_generatable_context
.runtime_requirements
.insert(branch.get_requests());

let local_module_var = self.local_module_var();

let text = branch.get_content(&local_module_var, &self.named_module);
let definition = branch.get_definition(&local_module_var);

let mut texts = text.split('#');

if !definition.is_empty() {
source.insert(0, &definition, None);
}

let mut current = self.range.0;
if let Some(array_range) = self.array_range {
source.replace(current, array_range.0, texts.next().unwrap_or(""), None);
current = array_range.1;
}

if let Some(object_range) = self.object_range {
source.replace(current, object_range.0, texts.next().unwrap_or(""), None);
current = object_range.1;
} else if let Some(function_range) = self.function_range {
source.replace(current, function_range.0, texts.next().unwrap_or(""), None);
current = function_range.1;
}

source.replace(current, self.range.1, texts.next().unwrap_or(""), None);

if texts.next().is_some() {
panic!("Implementation error");
}
}

fn dependency_id(&self) -> Option<DependencyId> {
Some(self.id)
}

fn update_hash(
&self,
_hasher: &mut dyn std::hash::Hasher,
_compilation: &Compilation,
_runtime: Option<&RuntimeSpec>,
) {
}
}

impl AsModuleDependency for AmdDefineDependency {}

impl AsContextDependency for AmdDefineDependency {}
Loading
Loading