Skip to content

Commit

Permalink
feat: parser options for preserving require and import (#7939)
Browse files Browse the repository at this point in the history
  • Loading branch information
fi3ework authored Oct 9, 2024
1 parent e7792c7 commit 5416306
Show file tree
Hide file tree
Showing 31 changed files with 712 additions and 4 deletions.
20 changes: 20 additions & 0 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,26 @@ export interface RawJavascriptParserOptions {
worker?: Array<string>
overrideStrict?: string
importMeta?: boolean
/**
* This option is experimental in Rspack only and subject to change or be removed anytime.
* @experimental
*/
requireAsExpression?: boolean
/**
* This option is experimental in Rspack only and subject to change or be removed anytime.
* @experimental
*/
requireDynamic?: boolean
/**
* This option is experimental in Rspack only and subject to change or be removed anytime.
* @experimental
*/
requireResolve?: boolean
/**
* This option is experimental in Rspack only and subject to change or be removed anytime.
* @experimental
*/
importDynamic?: boolean
}

export interface RawLazyCompilationOption {
Expand Down
16 changes: 16 additions & 0 deletions crates/rspack_binding_options/src/options/raw_module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,18 @@ pub struct RawJavascriptParserOptions {
pub worker: Option<Vec<String>>,
pub override_strict: Option<String>,
pub import_meta: Option<bool>,
/// This option is experimental in Rspack only and subject to change or be removed anytime.
/// @experimental
pub require_as_expression: Option<bool>,
/// This option is experimental in Rspack only and subject to change or be removed anytime.
/// @experimental
pub require_dynamic: Option<bool>,
/// This option is experimental in Rspack only and subject to change or be removed anytime.
/// @experimental
pub require_resolve: Option<bool>,
/// This option is experimental in Rspack only and subject to change or be removed anytime.
/// @experimental
pub import_dynamic: Option<bool>,
}

impl From<RawJavascriptParserOptions> for JavascriptParserOptions {
Expand Down Expand Up @@ -307,6 +319,10 @@ impl From<RawJavascriptParserOptions> for JavascriptParserOptions {
.override_strict
.map(|e| OverrideStrict::from(e.as_str())),
import_meta: value.import_meta,
require_as_expression: value.require_as_expression,
require_dynamic: value.require_dynamic,
require_resolve: value.require_resolve,
import_dynamic: value.import_dynamic,
}
}
}
Expand Down
1 change: 0 additions & 1 deletion crates/rspack_core/src/external_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ fn resolve_external_type<'a>(
external_type: &'a str,
dependency_meta: &'a DependencyMeta,
) -> &'a str {
// let external_type = self.external_type.as_str();
match external_type {
"module-import" => {
if let Some(external_type) = dependency_meta.external_type.as_ref() {
Expand Down
4 changes: 4 additions & 0 deletions crates/rspack_core/src/options/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ pub struct JavascriptParserOptions {
pub worker: Option<Vec<String>>,
pub override_strict: Option<OverrideStrict>,
pub import_meta: Option<bool>,
pub require_as_expression: Option<bool>,
pub require_dynamic: Option<bool>,
pub require_resolve: Option<bool>,
pub import_dynamic: Option<bool>,
}

#[derive(Debug, Clone, MergeFrom)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ pub struct CommonJsImportsParserPlugin;

impl CommonJsImportsParserPlugin {
fn process_resolve(&self, parser: &mut JavascriptParser, call_expr: &CallExpr, weak: bool) {
if matches!(parser.javascript_options.require_resolve, Some(false)) {
return;
}

if call_expr.args.len() != 1 {
return;
}
Expand Down Expand Up @@ -268,6 +272,10 @@ impl CommonJsImportsParserPlugin {
}
}

if matches!(parser.javascript_options.require_dynamic, Some(false)) && !param.is_string() {
return None;
}

// FIXME: should support `LocalModuleDependency`
if self
.process_require_item(parser, call_expr.span, &param)
Expand All @@ -290,6 +298,10 @@ impl CommonJsImportsParserPlugin {
parser: &mut JavascriptParser,
ident: &Ident,
) -> Option<bool> {
if parser.javascript_options.require_as_expression == Some(false) {
return None;
}

let start = ident.span().real_lo();
let end = ident.span().real_hi();
let mut dep = CommonJsRequireContextDependency::new(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ impl JavascriptParserPlugin for ImportParserPlugin {
parser.blocks.push(Box::new(block));
Some(true)
} else {
if matches!(parser.javascript_options.import_dynamic, Some(false)) {
return None;
}

let ContextModuleScanResult {
context,
reg,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,11 @@ Object {
"dynamicImportPrefetch": false,
"dynamicImportPreload": false,
"exprContextCritical": true,
"importDynamic": true,
"importMeta": true,
"requireAsExpression": true,
"requireDynamic": true,
"requireResolve": true,
"strictExportPresence": false,
"url": true,
"worker": Array [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const dir = process.env.name

import('./other.js')

import('./' + dir + '/other.js')
import(dir)


Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 'other';
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// TODO: move to webpack-test after merged into webpack

/** @type {import("../../../../types").Configuration} */
module.exports = {
entry: {
bundle0: "./index.js",
test: "./test.js"
},
module: {
parser: {
javascript: {
importDynamic: false
}
}
},
output: {
filename: "[name].js"
},
node: {
__dirname: false
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
findBundle: function (i, options) {
return ["test.js"];
}
};

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const fs = require("fs");
const path = require("path");

it("import() a dynamical expression should preserve as-is when `importDynamic` is disabled", () => {
const code = fs.readFileSync(path.join(__dirname, "./bundle0.js"), 'utf-8');
expect(code).not.toContain("import('./other.js')");
expect(code).toContain("import('./' + dir + '/other.js')");
expect(code).toContain("import(dir)");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require('./other.js')

const resolve1 = require.resolve
resolve1('./other.js')

const lazyFn = (module, requireFn) => {}
lazyFn('./other.js', require)


Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 'other';
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/** @type {import("../../../../types").Configuration} */
module.exports = {
entry: {
bundle0: "./index.js",
test: "./test.js"
},
module: {
parser: {
javascript: {
requireAsExpression: false
}
}
},
output: {
filename: "[name].js"
},
node: {
__dirname: false
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// TODO: move to webpack-test after merged into webpack

module.exports = {
findBundle: function (i, options) {
return ["test.js"];
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const fs = require("fs");
const path = require("path");

it("use require as a expression should preserve as-is when `requireAsExpression` is disabled", () => {
const code = fs.readFileSync(path.join(__dirname, "./bundle0.js"), 'utf-8');
expect(code).not.toContain("require('./other.js')");
expect(code).toContain("resolve1('./other.js')");
expect(code).toContain(`lazyFn('./other.js', require)`);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const dir = process.env.name

const require1 = require(dir)

const require2 = require('./other.js')

const require3 = require('./foo/' + dir + '.js')

const require4 = require(a + './foo/' + dir + '.js')

const require5 = require(dir ? './foo/' + dir + '.js' : './foo/nested' + dir + 'js')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 'other';
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// TODO: move to webpack-test after merged into webpack

/** @type {import("../../../../types").Configuration} */
module.exports = {
entry: {
bundle0: "./index.js",
test: "./test.js"
},
module: {
parser: {
javascript: {
requireDynamic: false,
// To preserve `require(...)`, we need to use `requireAsExpression: false` alongside.
requireAsExpression: false
}
}
},
output: {
filename: "[name].js"
},
node: {
__dirname: false
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
findBundle: function (i, options) {
return ["test.js"];
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const fs = require("fs");
const path = require("path");

it("require a dynamical expression should preserve as-is when `requireDynamic` is disabled", () => {
const code = fs.readFileSync(path.join(__dirname, "./bundle0.js"), 'utf-8');
expect(code).toContain("require(dir)");
expect(code).not.toContain("require('./other.js')");
expect(code).toContain("require('./foo/' + dir + '.js')");
expect(code).toContain("require(a + './foo/' + dir + '.js')");
expect(code).toContain("require(dir ? './foo/' + dir + '.js' : './foo/nested' + dir + 'js')");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const dir = process.env.DIR_READ_FROM_RUNTIME

const resolve1 = require.resolve(dir)

const resolve2 = require.resolve('./other.js')

const resolve3 = require.resolve('./foo/' + dir + '.js')

const resolve4 = require.resolve(process.env.RANDOM ? './foo/' + dir + '.js' : './bar/' + dir + 'js')


// Can't handle, `require` will turn into expression
// const resolve5 = require.resolve
// resolve5('./other.js')

// Can't handle, `require` will turn into `undefined`
// const __require = require
// const resolve6 = __require.resolve('./other.js')

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// TODO: move to webpack-test after merged into webpack

/** @type {import("../../../../types").Configuration} */
module.exports = {
entry: {
bundle0: "./index.js",
test: "./test.js"
},
module: {
parser: {
javascript: {
requireResolve: false,
// To preserve `require.resolve`, we need to use `requireResolve: false` to preserve
// the `resolve` method and `requireAsExpression: false` to preserve `require`.
requireAsExpression: false
}
}
},
output: {
filename: "[name].js"
},
node: {
__dirname: false
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
findBundle: function (i, options) {
return ["test.js"];
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const fs = require("fs");
const path = require("path");

it("`require.resolve` should preserve as-is when `requireResolve` is disabled", () => {
const code = fs.readFileSync(path.join(__dirname, "./bundle0.js"), 'utf-8');
expect(code).toContain("require.resolve(dir)");
expect(code).toContain("require.resolve('./other.js')");
expect(code).toContain("require.resolve('./foo/' + dir + '.js')");
expect(code).toContain("require.resolve(process.env.RANDOM ? './foo/' + dir + '.js' : './bar/' + dir + 'js')");
});
Loading

2 comments on commit 5416306

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Benchmark detail: Open

Name Base (2024-10-09 edf26c2) Current Change
10000_development-mode + exec 2.22 s ± 38 ms 2.2 s ± 19 ms -0.70 %
10000_development-mode_hmr + exec 683 ms ± 9.1 ms 684 ms ± 13 ms +0.17 %
10000_production-mode + exec 2.76 s ± 52 ms 2.78 s ± 35 ms +0.59 %
arco-pro_development-mode + exec 1.84 s ± 71 ms 1.84 s ± 74 ms -0.07 %
arco-pro_development-mode_hmr + exec 436 ms ± 1.2 ms 435 ms ± 1.8 ms -0.36 %
arco-pro_production-mode + exec 3.2 s ± 80 ms 3.14 s ± 87 ms -1.89 %
arco-pro_production-mode_generate-package-json-webpack-plugin + exec 3.22 s ± 82 ms 3.19 s ± 61 ms -1.09 %
threejs_development-mode_10x + exec 1.66 s ± 11 ms 1.67 s ± 12 ms +0.23 %
threejs_development-mode_10x_hmr + exec 782 ms ± 17 ms 780 ms ± 17 ms -0.34 %
threejs_production-mode_10x + exec 5.04 s ± 25 ms 5.11 s ± 46 ms +1.26 %

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Ran ecosystem CI: Open

suite result
modernjs ✅ success
_selftest ✅ success
rspress ✅ success
rslib ✅ success
rsbuild ✅ success
examples ✅ success
devserver ✅ success

Please sign in to comment.