From f673c45eaafcfe3afafed411e25d3299b6ea7eab Mon Sep 17 00:00:00 2001 From: gaoyuan <9aoyuao@gmail.com> Date: Tue, 5 Sep 2023 15:35:33 +0800 Subject: [PATCH 01/30] chore: expose statsModule.nameForCondition (#4119) --- crates/node_binding/binding.d.ts | 16 +- crates/node_binding/src/js_values/stats.rs | 2 + crates/rspack_core/src/stats.rs | 2 + packages/rspack/tests/Stats.test.ts | 470 ++++++++++++++++++ .../__snapshots__/StatsTestCases.test.ts.snap | 30 ++ packages/rspack/tests/fixtures/abc-query.js | 3 + 6 files changed, 508 insertions(+), 15 deletions(-) create mode 100644 packages/rspack/tests/fixtures/abc-query.js diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 8a4a6e1ee1b7..b9010c2e8288 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -400,6 +400,7 @@ export interface JsStatsModule { issuerName?: string issuerId?: string issuerPath: Array + nameForCondition?: string reasons?: Array assets?: Array source?: string | Buffer @@ -431,13 +432,6 @@ export interface JsStatsWarning { formatted: string } -export interface NodeFS { - writeFile: (...args: any[]) => any - removeFile: (...args: any[]) => any - mkdir: (...args: any[]) => any - mkdirp: (...args: any[]) => any -} - export interface PathData { filename?: string hash?: string @@ -1037,11 +1031,3 @@ export function registerGlobalTrace(filter: string, layer: "chrome" | "logger", /** Builtin loader runner */ export function runBuiltinLoader(builtin: string, options: string | undefined | null, loaderContext: JsLoaderContext): Promise -export interface ThreadsafeNodeFS { - writeFile: (...args: any[]) => any - removeFile: (...args: any[]) => any - mkdir: (...args: any[]) => any - mkdirp: (...args: any[]) => any - removeDirAll: (...args: any[]) => any -} - diff --git a/crates/node_binding/src/js_values/stats.rs b/crates/node_binding/src/js_values/stats.rs index 550e9b24682c..17ac542dce86 100644 --- a/crates/node_binding/src/js_values/stats.rs +++ b/crates/node_binding/src/js_values/stats.rs @@ -201,6 +201,7 @@ pub struct JsStatsModule { pub issuer_name: Option, pub issuer_id: Option, pub issuer_path: Vec, + pub name_for_condition: Option, pub reasons: Option>, pub assets: Option>, pub source: Option>, @@ -236,6 +237,7 @@ impl TryFrom> for JsStatsModule { issuer: stats.issuer, issuer_name: stats.issuer_name, issuer_id: stats.issuer_id, + name_for_condition: stats.name_for_condition, issuer_path: stats.issuer_path.into_iter().map(Into::into).collect(), reasons: stats .reasons diff --git a/crates/rspack_core/src/stats.rs b/crates/rspack_core/src/stats.rs index 9fdbb450ebdf..3e56fc98e0e0 100644 --- a/crates/rspack_core/src/stats.rs +++ b/crates/rspack_core/src/stats.rs @@ -443,6 +443,7 @@ impl Stats<'_> { r#type: "module", module_type: *module.module_type(), identifier, + name_for_condition: module.name_for_condition().map(|n| n.to_string()), name: module .readable_identifier(&self.compilation.options.context) .into(), @@ -562,6 +563,7 @@ pub struct StatsModule<'a> { pub module_type: ModuleType, pub identifier: ModuleIdentifier, pub name: String, + pub name_for_condition: Option, pub id: Option, pub chunks: Vec, pub size: f64, diff --git a/packages/rspack/tests/Stats.test.ts b/packages/rspack/tests/Stats.test.ts index 167f1f0794f1..f8a019e72bf1 100644 --- a/packages/rspack/tests/Stats.test.ts +++ b/packages/rspack/tests/Stats.test.ts @@ -69,6 +69,7 @@ describe("Stats", () => { "issuerPath": [], "moduleType": "javascript/auto", "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", "reasons": [ { "type": "entry", @@ -122,6 +123,7 @@ describe("Stats", () => { "issuerPath": [], "moduleType": "javascript/auto", "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", "reasons": [ { "type": "entry", @@ -214,6 +216,474 @@ describe("Stats", () => { `); }); + it("should output stats with query", async () => { + const stats = await compile({ + context: __dirname, + entry: "./fixtures/abc-query" + }); + + const statsOptions = { + all: true, + timings: false, + builtAt: false, + version: false + }; + expect(stats?.toJson(statsOptions)).toMatchInlineSnapshot(` + { + "assets": [ + { + "chunkNames": [ + "main", + ], + "chunks": [ + "main", + ], + "emitted": true, + "info": { + "development": false, + "hotModuleReplacement": false, + }, + "name": "main.js", + "size": 478, + "type": "asset", + }, + ], + "assetsByChunkName": { + "main": [ + "main.js", + ], + }, + "chunks": [ + { + "auxiliaryFiles": [], + "children": [], + "entry": true, + "files": [ + "main.js", + ], + "id": "main", + "initial": true, + "modules": [ + { + "assets": [], + "chunks": [ + "main", + ], + "id": "876", + "identifier": "/tests/fixtures/a.js", + "issuer": "/tests/fixtures/c.js?c=3", + "issuerId": "970", + "issuerName": "./fixtures/c.js?c=3", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + { + "id": "970", + "identifier": "/tests/fixtures/c.js?c=3", + "name": "./fixtures/c.js?c=3", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": "970", + "moduleIdentifier": "/tests/fixtures/c.js?c=3", + "moduleName": "./fixtures/c.js?c=3", + "type": "cjs require", + "userRequest": "./a", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; + };", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "304", + "identifier": "/tests/fixtures/a.js?a=1", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js?a=1", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./a?a=1", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; + };", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "53", + "identifier": "/tests/fixtures/b.js?b=2", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/b.js?b=2", + "nameForCondition": "/tests/fixtures/b.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./b?b=2", + }, + ], + "size": 94, + "source": "module.exports = function b() { + return "This is b"; + }; + + // Test CJS top-level return + return; + ", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "970", + "identifier": "/tests/fixtures/c.js?c=3", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/c.js?c=3", + "nameForCondition": "/tests/fixtures/c.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./c?c=3", + }, + ], + "size": 72, + "source": "module.exports = function b() { + require("./a"); + return "This is c"; + };", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "issuerPath": [], + "moduleType": "javascript/auto", + "name": "./fixtures/abc-query.js", + "nameForCondition": "/tests/fixtures/abc-query.js", + "reasons": [ + { + "type": "entry", + "userRequest": "./fixtures/abc-query", + }, + ], + "size": 96, + "source": "exports.a = require("./a?a=1"); + exports.b = require("./b?b=2"); + exports.c = require("./c?c=3"); + ", + "type": "module", + }, + ], + "names": [ + "main", + ], + "parents": [], + "siblings": [], + "size": 372, + "type": "chunk", + }, + ], + "entrypoints": { + "main": { + "assets": [ + { + "name": "main.js", + "size": 478, + }, + ], + "assetsSize": 478, + "chunks": [ + "main", + ], + "name": "main", + }, + }, + "errors": [ + { + "formatted": "error[javascript]: JavaScript parsing error + ┌─ tests/fixtures/b.js:6:1 + │ + 2 │ return "This is b"; + 3 │ }; + 4 │ + 5 │ // Test CJS top-level return + 6 │ return; + │ ^^^^^^^ Return statement is not allowed here + 7 │ + + ", + "message": "Return statement is not allowed here", + "title": "JavaScript parsing error", + }, + ], + "errorsCount": 1, + "filteredModules": undefined, + "hash": "bee17f084d89e18f1263", + "logging": {}, + "modules": [ + { + "assets": [], + "chunks": [ + "main", + ], + "id": "876", + "identifier": "/tests/fixtures/a.js", + "issuer": "/tests/fixtures/c.js?c=3", + "issuerId": "970", + "issuerName": "./fixtures/c.js?c=3", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + { + "id": "970", + "identifier": "/tests/fixtures/c.js?c=3", + "name": "./fixtures/c.js?c=3", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": "970", + "moduleIdentifier": "/tests/fixtures/c.js?c=3", + "moduleName": "./fixtures/c.js?c=3", + "type": "cjs require", + "userRequest": "./a", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; + };", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "304", + "identifier": "/tests/fixtures/a.js?a=1", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js?a=1", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./a?a=1", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; + };", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "53", + "identifier": "/tests/fixtures/b.js?b=2", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/b.js?b=2", + "nameForCondition": "/tests/fixtures/b.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./b?b=2", + }, + ], + "size": 94, + "source": "module.exports = function b() { + return "This is b"; + }; + + // Test CJS top-level return + return; + ", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "970", + "identifier": "/tests/fixtures/c.js?c=3", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/c.js?c=3", + "nameForCondition": "/tests/fixtures/c.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./c?c=3", + }, + ], + "size": 72, + "source": "module.exports = function b() { + require("./a"); + return "This is c"; + };", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "issuerPath": [], + "moduleType": "javascript/auto", + "name": "./fixtures/abc-query.js", + "nameForCondition": "/tests/fixtures/abc-query.js", + "reasons": [ + { + "type": "entry", + "userRequest": "./fixtures/abc-query", + }, + ], + "size": 96, + "source": "exports.a = require("./a?a=1"); + exports.b = require("./b?b=2"); + exports.c = require("./c?c=3"); + ", + "type": "module", + }, + ], + "namedChunkGroups": { + "main": { + "assets": [ + { + "name": "main.js", + "size": 478, + }, + ], + "assetsSize": 478, + "chunks": [ + "main", + ], + "name": "main", + }, + }, + "outputPath": "/dist", + "publicPath": "auto", + "warnings": [], + "warningsCount": 0, + } + `); + }); + it("should output the specified number of modules when set stats.modulesSpace", async () => { const stats = await compile({ context: __dirname, diff --git a/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap b/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap index a7b4eb932b5e..81f3fb7b6abf 100644 --- a/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap +++ b/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap @@ -74,6 +74,7 @@ exports[`StatsTestCases should print correct stats for auxiliary-files-test 1`] ], "moduleType": "asset/resource", "name": "./raw.png", + "nameForCondition": "/tests/statsCases/auxiliary-files-test/raw.png", "reasons": [ { "moduleId": "10", @@ -108,6 +109,7 @@ exports[`StatsTestCases should print correct stats for auxiliary-files-test 1`] "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/auxiliary-files-test/index.js", "reasons": [ { "type": "entry", @@ -138,6 +140,7 @@ import rawModule from './raw.png'", ], "moduleType": "javascript/auto", "name": "./stringModule.js", + "nameForCondition": "/tests/statsCases/auxiliary-files-test/stringModule.js", "reasons": [ { "moduleId": "10", @@ -201,6 +204,7 @@ import rawModule from './raw.png'", ], "moduleType": "asset/resource", "name": "./raw.png", + "nameForCondition": "/tests/statsCases/auxiliary-files-test/raw.png", "reasons": [ { "moduleId": "10", @@ -235,6 +239,7 @@ import rawModule from './raw.png'", "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/auxiliary-files-test/index.js", "reasons": [ { "type": "entry", @@ -265,6 +270,7 @@ import rawModule from './raw.png'", ], "moduleType": "javascript/auto", "name": "./stringModule.js", + "nameForCondition": "/tests/statsCases/auxiliary-files-test/stringModule.js", "reasons": [ { "moduleId": "10", @@ -391,6 +397,7 @@ exports[`StatsTestCases should print correct stats for filename 1`] = ` ], "moduleType": "javascript/auto", "name": "./dynamic.js", + "nameForCondition": "/tests/statsCases/filename/dynamic.js", "reasons": [ { "moduleId": "10", @@ -436,6 +443,7 @@ exports[`StatsTestCases should print correct stats for filename 1`] = ` "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/filename/index.js", "reasons": [ { "type": "entry", @@ -487,6 +495,7 @@ exports[`StatsTestCases should print correct stats for filename 1`] = ` "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/filename/index.js", "reasons": [ { "type": "entry", @@ -516,6 +525,7 @@ exports[`StatsTestCases should print correct stats for filename 1`] = ` ], "moduleType": "javascript/auto", "name": "./dynamic.js", + "nameForCondition": "/tests/statsCases/filename/dynamic.js", "reasons": [ { "moduleId": "10", @@ -617,6 +627,7 @@ exports[`StatsTestCases should print correct stats for hot+production 1`] = ` "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/hot+production/index.js", "reasons": [ { "type": "entry", @@ -669,6 +680,7 @@ exports[`StatsTestCases should print correct stats for hot+production 1`] = ` "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/hot+production/index.js", "reasons": [ { "type": "entry", @@ -785,6 +797,7 @@ exports[`StatsTestCases should print correct stats for ignore-plugin 1`] = ` "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/ignore-plugin/index.js", "size": 103, "type": "module", }, @@ -811,6 +824,7 @@ exports[`StatsTestCases should print correct stats for ignore-plugin 1`] = ` ], "moduleType": "javascript/auto", "name": "./locals/en.js", + "nameForCondition": "/tests/statsCases/ignore-plugin/locals/en.js", "size": 30, "type": "module", }, @@ -1774,6 +1788,7 @@ exports[`StatsTestCases should print correct stats for reasons 1`] = ` ], "moduleType": "javascript/auto", "name": "./a.js", + "nameForCondition": "/tests/statsCases/reasons/a.js", "reasons": [ { "moduleId": "10", @@ -1795,6 +1810,7 @@ exports[`StatsTestCases should print correct stats for reasons 1`] = ` "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/reasons/index.js", "reasons": [ { "type": "entry", @@ -1861,6 +1877,7 @@ exports[`StatsTestCases should print correct stats for resolve-overflow 1`] = ` "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/resolve-overflow/index.js", "reasons": [ { "type": "entry", @@ -1966,6 +1983,7 @@ console.log(a); "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/resolve-overflow/index.js", "reasons": [ { "type": "entry", @@ -2114,6 +2132,7 @@ exports[`StatsTestCases should print correct stats for resolve-unexpected-export "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/resolve-unexpected-exports-in-pkg/index.js", "reasons": [ { "type": "entry", @@ -2213,6 +2232,7 @@ console.log(a); "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/resolve-unexpected-exports-in-pkg/index.js", "reasons": [ { "type": "entry", @@ -2355,6 +2375,7 @@ exports[`StatsTestCases should print correct stats for simple 1`] = ` "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/simple/index.js", "reasons": [ { "type": "entry", @@ -2407,6 +2428,7 @@ exports[`StatsTestCases should print correct stats for simple 1`] = ` "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/simple/index.js", "reasons": [ { "type": "entry", @@ -2508,6 +2530,7 @@ exports[`StatsTestCases should print correct stats for simple-module-source 1`] ], "moduleType": "javascript/auto", "name": "./raw.png", + "nameForCondition": "/tests/statsCases/simple-module-source/raw.png", "reasons": [ { "moduleId": "10", @@ -2531,6 +2554,7 @@ exports[`StatsTestCases should print correct stats for simple-module-source 1`] "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/simple-module-source/index.js", "reasons": [ { "type": "entry", @@ -2561,6 +2585,7 @@ import rawModule from './raw.png'", ], "moduleType": "javascript/auto", "name": "./stringModule.js", + "nameForCondition": "/tests/statsCases/simple-module-source/stringModule.js", "reasons": [ { "moduleId": "10", @@ -2624,6 +2649,7 @@ import rawModule from './raw.png'", ], "moduleType": "javascript/auto", "name": "./raw.png", + "nameForCondition": "/tests/statsCases/simple-module-source/raw.png", "reasons": [ { "moduleId": "10", @@ -2647,6 +2673,7 @@ import rawModule from './raw.png'", "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/simple-module-source/index.js", "reasons": [ { "type": "entry", @@ -2677,6 +2704,7 @@ import rawModule from './raw.png'", ], "moduleType": "javascript/auto", "name": "./stringModule.js", + "nameForCondition": "/tests/statsCases/simple-module-source/stringModule.js", "reasons": [ { "moduleId": "10", @@ -2770,6 +2798,7 @@ exports[`StatsTestCases should print correct stats for stats-hooks 1`] = ` "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/stats-hooks/index.js", "reasons": [ { "type": "entry", @@ -2820,6 +2849,7 @@ exports[`StatsTestCases should print correct stats for stats-hooks 1`] = ` "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/stats-hooks/index.js", "reasons": [ { "type": "entry", diff --git a/packages/rspack/tests/fixtures/abc-query.js b/packages/rspack/tests/fixtures/abc-query.js new file mode 100644 index 000000000000..f1ff8891c394 --- /dev/null +++ b/packages/rspack/tests/fixtures/abc-query.js @@ -0,0 +1,3 @@ +exports.a = require("./a?a=1"); +exports.b = require("./b?b=2"); +exports.c = require("./c?c=3"); From 03b90356103f02af5f47c5e24042b1e88f53a770 Mon Sep 17 00:00:00 2001 From: Hana Date: Tue, 5 Sep 2023 16:33:24 +0800 Subject: [PATCH 02/30] fix: `builtin:swc-loader` does not report error correctly (#4111) * fix: init * fix: diagnostics for sass-loader * test: add test case * chore: disable color --- .../src/options/raw_module/js_loader.rs | 5 +- crates/rspack_loader_runner/src/runner.rs | 22 +++++-- crates/rspack_loader_sass/src/lib.rs | 6 +- crates/rspack_loader_swc/src/lib.rs | 65 ++++++------------- package.json | 2 +- .../__snapshots__/StatsTestCases.test.ts.snap | 44 +++++++++++++ .../parse-error-builtin-swc-loader/index.ts | 1 + .../webpack.config.js | 18 +++++ 8 files changed, 107 insertions(+), 56 deletions(-) create mode 100644 packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/index.ts create mode 100644 packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/webpack.config.js diff --git a/crates/rspack_binding_options/src/options/raw_module/js_loader.rs b/crates/rspack_binding_options/src/options/raw_module/js_loader.rs index a204d64201a1..5ec0b7faa165 100644 --- a/crates/rspack_binding_options/src/options/raw_module/js_loader.rs +++ b/crates/rspack_binding_options/src/options/raw_module/js_loader.rs @@ -298,7 +298,7 @@ impl TryFrom<&rspack_core::LoaderContext<'_, rspack_core::LoaderRunnerContext>> current_loader: cx.current_loader().to_string(), is_pitching: true, context: External::new(cx.context.clone()), - diagnostics: External::new(cx.diagnostics.clone()), + diagnostics: External::new(cx.__diagnostics.clone()), }) } } @@ -358,8 +358,7 @@ pub async fn run_builtin_loader( ), asset_filenames: HashSet::from_iter(loader_context.asset_filenames.into_iter()), // Initialize with no diagnostic - diagnostics: vec![], - + __diagnostics: vec![], __resource_data: &ResourceData::new(Default::default(), Default::default()), __loader_items: LoaderItemList(list), __loader_index: 0, diff --git a/crates/rspack_loader_runner/src/runner.rs b/crates/rspack_loader_runner/src/runner.rs index 77aeb847508d..bc378a60066c 100644 --- a/crates/rspack_loader_runner/src/runner.rs +++ b/crates/rspack_loader_runner/src/runner.rs @@ -167,13 +167,22 @@ pub struct LoaderContext<'c, C> { pub asset_filenames: HashSet, + // Only used for cross-crate accessing. + // This field should not be accessed in builtin loaders. pub __loader_index: usize, + // Only used for cross-crate accessing. + // This field should not be accessed in builtin loaders. pub __loader_items: LoaderItemList<'c, C>, + // Only used for cross-crate accessing. + // This field should not be accessed in builtin loaders. #[derivative(Debug = "ignore")] pub __plugins: &'c [Box], + // Only used for cross-crate accessing. + // This field should not be accessed in builtin loaders. pub __resource_data: &'c ResourceData, - - pub diagnostics: Vec, + // Only used for cross-crate accessing. + // This field should not be accessed in builtin loaders. + pub __diagnostics: Vec, } impl<'c, C> LoaderContext<'c, C> { @@ -203,6 +212,11 @@ impl<'c, C> LoaderContext<'c, C> { pub fn loader_index(&self) -> usize { self.__loader_index } + + /// Emit a diagnostic, it can be a `warning` or `error`. + pub fn emit_diagnostic(&mut self, diagnostic: Diagnostic) { + self.__diagnostics.push(diagnostic) + } } async fn process_resource(loader_context: &mut LoaderContext<'_, C>) -> Result<()> { @@ -276,7 +290,7 @@ async fn create_loader_context<'c, C: 'c>( __loader_items: LoaderItemList(__loader_items), __plugins: plugins, __resource_data: resource_data, - diagnostics: vec![], + __diagnostics: vec![], }; Ok(loader_context) @@ -377,7 +391,7 @@ impl TryFrom> for TWithDiagnosticArray { source_map: loader_context.source_map, additional_data: loader_context.additional_data, } - .with_diagnostic(loader_context.diagnostics), + .with_diagnostic(loader_context.__diagnostics), ) } } diff --git a/crates/rspack_loader_sass/src/lib.rs b/crates/rspack_loader_sass/src/lib.rs index d95a7f364de2..7928d3b06685 100644 --- a/crates/rspack_loader_sass/src/lib.rs +++ b/crates/rspack_loader_sass/src/lib.rs @@ -506,9 +506,9 @@ impl Loader for SassLoader { loader_context.content = Some(result.css.into()); loader_context.source_map = source_map; - loader_context - .diagnostics - .append(&mut rx.into_iter().flatten().collect_vec()); + rx.into_iter().flatten().for_each(|d| { + loader_context.emit_diagnostic(d); + }); Ok(()) } } diff --git a/crates/rspack_loader_swc/src/lib.rs b/crates/rspack_loader_swc/src/lib.rs index 947a491429a8..18727e388e3f 100644 --- a/crates/rspack_loader_swc/src/lib.rs +++ b/crates/rspack_loader_swc/src/lib.rs @@ -4,7 +4,7 @@ use std::default::Default; use std::sync::Arc; use rspack_core::{rspack_sources::SourceMap, LoaderRunnerContext, Mode}; -use rspack_error::{errors_to_diagnostics, internal_error, Diagnostic, Error, Result}; +use rspack_error::{internal_error, Diagnostic, Result}; use rspack_loader_runner::{Identifiable, Identifier, Loader, LoaderContext}; use serde::Deserialize; use swc_config::config_types::{BoolConfig, MergingOption}; @@ -150,12 +150,13 @@ pub const SWC_LOADER_IDENTIFIER: &str = "builtin:swc-loader"; impl Loader for SwcLoader { async fn run(&self, loader_context: &mut LoaderContext<'_, LoaderRunnerContext>) -> Result<()> { let resource_path = loader_context.resource_path.to_path_buf(); - let content = std::mem::take(&mut loader_context.content).expect("content should available"); + let Some(content) = std::mem::take(&mut loader_context.content) else { + return Err(internal_error!("Content should be available")) + }; let c: Compiler = Compiler::new(Arc::from(swc_core::common::SourceMap::new( FilePathMapping::empty(), ))); - let mut errors: Vec = Default::default(); let default_development = matches!(loader_context.context.options.mode, Mode::Development); let mut options = self.options.clone(); if options.config.jsc.transform.as_ref().is_some() { @@ -174,7 +175,7 @@ impl Loader for SwcLoader { } if options.config.jsc.experimental.plugins.is_some() { - loader_context.diagnostics.push(Diagnostic::warn( + loader_context.emit_diagnostic(Diagnostic::warn( SWC_LOADER_IDENTIFIER.to_string(), "Experimental plugins are not currently supported.".to_string(), 0, @@ -183,56 +184,30 @@ impl Loader for SwcLoader { } GLOBALS.set(&Default::default(), || { - match try_with_handler(c.cm.clone(), Default::default(), |handler| { + try_with_handler(c.cm.clone(), Default::default(), |handler| { c.run(|| { let fm = c .cm .new_source_file(FileName::Real(resource_path), content.try_into_string()?); let comments = SingleThreadedComments::default(); - let out = match anyhow::Context::context( - c.process_js_with_custom_pass( - fm, - None, - handler, - &options, - comments, - |_a| noop(), - |_a| noop(), - ), - "failed to process js file", - ) { - Ok(out) => Some(out), - Err(e) => { - errors.push(Error::Anyhow { source: e }); - None - } - }; - if let Some(out) = out { - loader_context.content = Some(out.code.into()); - loader_context.source_map = if let Some(map) = out.map { - match SourceMap::from_json(&map).map_err(|e| internal_error!(e.to_string())) { - Ok(map) => Some(map), - Err(e) => { - errors.push(e); - None - } - } - } else { - None - }; - } + let out = c.process_js_with_custom_pass( + fm, + None, + handler, + &options, + comments, + |_a| noop(), + |_a| noop(), + )?; + loader_context.content = Some(out.code.into()); + loader_context.source_map = out.map.map(|m| SourceMap::from_json(&m)).transpose()?; Ok(()) }) - }) { - Ok(_) => {} - Err(e) => errors.push(Error::Anyhow { source: e }), - } - }); - loader_context - .diagnostics - .append(&mut errors_to_diagnostics(errors)); + }) + })?; + Ok(()) } } diff --git a/package.json b/package.json index 717e9aeaeb92..cb9bb50ef4b2 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "prepare": "is-ci || husky install", "pnpm:devPreinstall": "is-ci || node ./scripts/doctor.js", "test:example": "pnpm --filter \"example-*\" build", - "test:unit": "pnpm --filter \"@rspack/*\" test", + "test:unit": "NO_COLOR=1 pnpm --filter \"@rspack/*\" test", "test:e2e": "pnpm --filter \"@rspack-e2e/*\" test", "test:ci": "cross-env NODE_OPTIONS=--max_old_space_size=8192 pnpm run build:js && pnpm run test:example && pnpm run test:unit && pnpm test:webpack", "test:webpack": "pnpm --filter \"webpack-test\" test:metric" diff --git a/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap b/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap index 81f3fb7b6abf..8eb0590f68b7 100644 --- a/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap +++ b/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap @@ -1763,6 +1763,50 @@ exports[`StatsTestCases should print correct stats for parse-error 2`] = ` +rspack compiled with 1 error" +`; + +exports[`StatsTestCases should print correct stats for parse-error-builtin-swc-loader 1`] = ` +{ + "errors": [ + { + "formatted": "error[internal]: + x Expected '{', got 'error' + ,-[/tests/statsCases/parse-error-builtin-swc-loader/index.ts:1:1] + 1 | export error; + : ^^^^^ + \`---- + + +", + "message": " + x Expected '{', got 'error' + ,-[/tests/statsCases/parse-error-builtin-swc-loader/index.ts:1:1] + 1 | export error; + : ^^^^^ + \`---- +", + "title": "", + }, + ], + "errorsCount": 1, + "logging": {}, + "warnings": [], + "warningsCount": 0, +} +`; + +exports[`StatsTestCases should print correct stats for parse-error-builtin-swc-loader 2`] = ` +"error[internal]: + x Expected '{', got 'error' + ,-[/tests/statsCases/parse-error-builtin-swc-loader/index.ts:1:1] + 1 | export error; + : ^^^^^ + \`---- + + + + rspack compiled with 1 error" `; diff --git a/packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/index.ts b/packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/index.ts new file mode 100644 index 000000000000..4ebce3478420 --- /dev/null +++ b/packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/index.ts @@ -0,0 +1 @@ +export error; diff --git a/packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/webpack.config.js b/packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/webpack.config.js new file mode 100644 index 000000000000..7a90b69b7045 --- /dev/null +++ b/packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/webpack.config.js @@ -0,0 +1,18 @@ +module.exports = { + stats: "errors-warnings", + module: { + rules: [ + { + test: /\.ts$/, + loader: "builtin:swc-loader", + options: { + jsc: { + parser: { + syntax: "typescript" + } + } + } + } + ] + } +} From f5237df9ab6580cd744f15d3526e72966e42682e Mon Sep 17 00:00:00 2001 From: Boshen Date: Tue, 5 Sep 2023 17:51:42 +0800 Subject: [PATCH 03/30] feat(rspack_core): add oxc_resolver (turned off by default) (#4108) * feat(rspack_core): add oxc_resolver (turned off by default) Enable oxc_resolver with OXC_RESOLVER=1 environment variable. * feat(rspack): add experiments.rspackFuture.newResolver future flag * add tests * remove empty_structs_with_brackets --- Cargo.lock | 107 +++--- crates/node_binding/binding.d.ts | 2 +- .../src/options/raw_experiments.rs | 11 +- crates/rspack_core/Cargo.toml | 1 + crates/rspack_core/src/compiler/mod.rs | 8 +- crates/rspack_core/src/options/experiments.rs | 5 +- crates/rspack_core/src/resolver/factory.rs | 9 +- crates/rspack_core/src/resolver/mod.rs | 52 ++- .../rspack_core/src/resolver/resolver_impl.rs | 353 +++++++++++++----- packages/rspack/src/config/adapter.ts | 4 +- packages/rspack/src/config/types.ts | 4 +- packages/rspack/src/config/zod/experiments.ts | 6 +- .../configCases/resolve-new/alias/.gitignore | 1 + .../tests/configCases/resolve-new/alias/a.js | 1 + .../configCases/resolve-new/alias/index.js | 11 + .../alias/node_modules/alias/index.js | 1 + .../resolve-new/alias/webpack.config.js | 28 ++ .../resolve-new/by-dependency/baz.js | 1 + .../resolve-new/by-dependency/foo.bar | 1 + .../resolve-new/by-dependency/index.js | 10 + .../by-dependency/webpack.config.js | 15 + .../resolve-new/condition-exports/.gitignore | 1 + .../resolve-new/condition-exports/index.js | 13 + .../exports-conditional-2/lib.cjs | 1 + .../node_modules/exports-conditional-2/lib.js | 1 + .../exports-conditional-2/package.json | 10 + .../node_modules/exports-conditional/lib.cjs | 1 + .../node_modules/exports-conditional/lib.mjs | 1 + .../exports-conditional/package.json | 9 + .../condition-exports/webpack.config.js | 8 + .../resolve-new/conditions/.gitignore | 1 + .../resolve-new/conditions/index.js | 9 + .../node_modules/exports-field/lib/index.js | 0 .../exports-field/lib/lib2/main.js | 1 + .../node_modules/exports-field/lib/main.js | 1 + .../node_modules/exports-field/package.json | 19 + .../node_modules/exports-field/x.js | 1 + .../resolve-new/conditions/webpack.config.js | 11 + .../resolve-new/exports-fields/.gitignore | 1 + .../resolve-new/exports-fields/index.js | 4 + .../node_modules/exports-field/b.js | 1 + .../node_modules/exports-field/package.json | 5 + .../exports-fields/webpack.config.js | 11 + .../resolve-new/extension-alias/index.js | 5 + .../resolve-new/extension-alias/src/index.mts | 1 + .../extension-alias/webpack.config.js | 13 + .../configCases/resolve-new/fallback/#/a.js | 1 + .../configCases/resolve-new/fallback/a/1.js | 1 + .../configCases/resolve-new/fallback/a/2.js | 1 + .../configCases/resolve-new/fallback/b/2.js | 1 + .../configCases/resolve-new/fallback/index.js | 14 + .../resolve-new/fallback/webpack.config.js | 17 + .../resolve-new/modules/a/foo/a.js | 1 + .../resolve-new/modules/a/foo/package.json | 5 + .../resolve-new/modules/b/foo/b.js | 1 + .../resolve-new/modules/b/foo/package.json | 5 + .../configCases/resolve-new/modules/index.js | 7 + .../resolve-new/modules/webpack.config.js | 12 + .../resolve-new/multi-alias/a/1.js | 1 + .../resolve-new/multi-alias/b/2.js | 1 + .../resolve-new/multi-alias/index.js | 6 + .../resolve-new/multi-alias/webpack.config.js | 14 + .../resolve-new/nested-by-dependency/bar.mjs | 1 + .../resolve-new/nested-by-dependency/baz.js | 1 + .../resolve-new/nested-by-dependency/foo.bar | 3 + .../resolve-new/nested-by-dependency/index.js | 10 + .../nested-by-dependency/webpack.config.js | 31 ++ .../resolve-new/tsconfig-paths-map/index.js | 19 + .../resolve-new/tsconfig-paths-map/real.js | 1 + .../tsconfig-paths-map/src/file.js | 1 + .../tsconfig-paths-map/tsconfig.json | 9 + .../tsconfig-paths-map/webpack.config.js | 15 + 72 files changed, 774 insertions(+), 165 deletions(-) create mode 100644 packages/rspack/tests/configCases/resolve-new/alias/.gitignore create mode 100644 packages/rspack/tests/configCases/resolve-new/alias/a.js create mode 100644 packages/rspack/tests/configCases/resolve-new/alias/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/alias/node_modules/alias/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/alias/webpack.config.js create mode 100644 packages/rspack/tests/configCases/resolve-new/by-dependency/baz.js create mode 100644 packages/rspack/tests/configCases/resolve-new/by-dependency/foo.bar create mode 100644 packages/rspack/tests/configCases/resolve-new/by-dependency/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/by-dependency/webpack.config.js create mode 100644 packages/rspack/tests/configCases/resolve-new/condition-exports/.gitignore create mode 100644 packages/rspack/tests/configCases/resolve-new/condition-exports/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.cjs create mode 100644 packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.js create mode 100644 packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/package.json create mode 100644 packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.cjs create mode 100644 packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.mjs create mode 100644 packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/package.json create mode 100644 packages/rspack/tests/configCases/resolve-new/condition-exports/webpack.config.js create mode 100644 packages/rspack/tests/configCases/resolve-new/conditions/.gitignore create mode 100644 packages/rspack/tests/configCases/resolve-new/conditions/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/lib2/main.js create mode 100644 packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/main.js create mode 100644 packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/package.json create mode 100644 packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/x.js create mode 100644 packages/rspack/tests/configCases/resolve-new/conditions/webpack.config.js create mode 100644 packages/rspack/tests/configCases/resolve-new/exports-fields/.gitignore create mode 100644 packages/rspack/tests/configCases/resolve-new/exports-fields/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/b.js create mode 100644 packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/package.json create mode 100644 packages/rspack/tests/configCases/resolve-new/exports-fields/webpack.config.js create mode 100644 packages/rspack/tests/configCases/resolve-new/extension-alias/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/extension-alias/src/index.mts create mode 100644 packages/rspack/tests/configCases/resolve-new/extension-alias/webpack.config.js create mode 100644 packages/rspack/tests/configCases/resolve-new/fallback/#/a.js create mode 100644 packages/rspack/tests/configCases/resolve-new/fallback/a/1.js create mode 100644 packages/rspack/tests/configCases/resolve-new/fallback/a/2.js create mode 100644 packages/rspack/tests/configCases/resolve-new/fallback/b/2.js create mode 100644 packages/rspack/tests/configCases/resolve-new/fallback/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/fallback/webpack.config.js create mode 100644 packages/rspack/tests/configCases/resolve-new/modules/a/foo/a.js create mode 100644 packages/rspack/tests/configCases/resolve-new/modules/a/foo/package.json create mode 100644 packages/rspack/tests/configCases/resolve-new/modules/b/foo/b.js create mode 100644 packages/rspack/tests/configCases/resolve-new/modules/b/foo/package.json create mode 100644 packages/rspack/tests/configCases/resolve-new/modules/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/modules/webpack.config.js create mode 100644 packages/rspack/tests/configCases/resolve-new/multi-alias/a/1.js create mode 100644 packages/rspack/tests/configCases/resolve-new/multi-alias/b/2.js create mode 100644 packages/rspack/tests/configCases/resolve-new/multi-alias/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/multi-alias/webpack.config.js create mode 100644 packages/rspack/tests/configCases/resolve-new/nested-by-dependency/bar.mjs create mode 100644 packages/rspack/tests/configCases/resolve-new/nested-by-dependency/baz.js create mode 100644 packages/rspack/tests/configCases/resolve-new/nested-by-dependency/foo.bar create mode 100644 packages/rspack/tests/configCases/resolve-new/nested-by-dependency/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/nested-by-dependency/webpack.config.js create mode 100644 packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/index.js create mode 100644 packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/real.js create mode 100644 packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/src/file.js create mode 100644 packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/tsconfig.json create mode 100644 packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/webpack.config.js diff --git a/Cargo.lock b/Cargo.lock index ab59a1214cc1..40b31cbc4608 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,7 +140,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -151,7 +151,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -174,7 +174,7 @@ checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -599,7 +599,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eed5fff0d93c7559121e9c72bf9c242295869396255071ff2cb1617147b608c5" dependencies = [ "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -651,9 +651,9 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.5.0" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", "hashbrown 0.14.0", @@ -771,7 +771,7 @@ checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -831,7 +831,7 @@ dependencies = [ "pmutil", "proc-macro2", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -896,7 +896,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -1149,6 +1149,7 @@ checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ "equivalent", "hashbrown 0.14.0", + "serde", ] [[package]] @@ -1197,7 +1198,7 @@ dependencies = [ "pmutil", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -1713,6 +1714,23 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "oxc_resolver" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0cba9c2dd86cc336814aa1b2fb2e3c1bdee5cbf411debb736ab582856f249db" +dependencies = [ + "dashmap", + "dunce", + "indexmap 2.0.0", + "once_cell", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tracing", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -1833,7 +1851,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -1920,7 +1938,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -1971,7 +1989,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -2431,6 +2449,7 @@ dependencies = [ "mime_guess", "nodejs-resolver", "once_cell", + "oxc_resolver", "paste", "petgraph", "rayon", @@ -3265,9 +3284,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.171" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] @@ -3295,13 +3314,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -3317,9 +3336,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.100" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "indexmap 2.0.0", "itoa", @@ -3484,7 +3503,7 @@ dependencies = [ "pmutil", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -3535,7 +3554,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -3718,7 +3737,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -3796,7 +3815,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -3950,7 +3969,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4103,7 +4122,7 @@ dependencies = [ "swc_ecma_ast", "swc_ecma_parser", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4199,7 +4218,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4391,7 +4410,7 @@ dependencies = [ "pmutil", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4469,7 +4488,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4548,7 +4567,7 @@ dependencies = [ "pmutil", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4589,7 +4608,7 @@ checksum = "ff9719b6085dd2824fd61938a881937be14b08f95e2d27c64c825a9f65e052ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4613,7 +4632,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4629,9 +4648,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.25" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -4677,7 +4696,7 @@ dependencies = [ "quote", "regex", "relative-path", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4702,22 +4721,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4835,7 +4854,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4858,7 +4877,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -5116,7 +5135,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", "wasm-bindgen-shared", ] @@ -5138,7 +5157,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index b9010c2e8288..03f97663ff0a 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -959,7 +959,7 @@ export interface RawResolveOptions { } export interface RawRspackFuture { - + newResolver: boolean } export interface RawRuleSetCondition { diff --git a/crates/rspack_binding_options/src/options/raw_experiments.rs b/crates/rspack_binding_options/src/options/raw_experiments.rs index 94da9f858d86..aab912c87618 100644 --- a/crates/rspack_binding_options/src/options/raw_experiments.rs +++ b/crates/rspack_binding_options/src/options/raw_experiments.rs @@ -13,8 +13,9 @@ pub struct RawIncrementalRebuild { #[derive(Deserialize, Debug, Default)] #[serde(rename_all = "camelCase")] #[napi(object)] -#[allow(clippy::empty_structs_with_brackets)] -pub struct RawRspackFuture {} +pub struct RawRspackFuture { + pub new_resolver: bool, +} #[derive(Deserialize, Debug, Default)] #[serde(rename_all = "camelCase")] @@ -29,7 +30,9 @@ pub struct RawExperiments { } impl From for RspackFuture { - fn from(_value: RawRspackFuture) -> Self { - Self {} + fn from(value: RawRspackFuture) -> Self { + Self { + new_resolver: value.new_resolver, + } } } diff --git a/crates/rspack_core/Cargo.toml b/crates/rspack_core/Cargo.toml index 66d45ab08447..078294cc2f52 100644 --- a/crates/rspack_core/Cargo.toml +++ b/crates/rspack_core/Cargo.toml @@ -23,6 +23,7 @@ itertools = { workspace = true } mime_guess = { workspace = true } nodejs-resolver = { version = "0.1.0" } once_cell = { workspace = true } +oxc_resolver = { version = "0.1.0" } paste = { workspace = true } petgraph = { version = "0.6.3", features = ["serde-1"] } rayon = { workspace = true } diff --git a/crates/rspack_core/src/compiler/mod.rs b/crates/rspack_core/src/compiler/mod.rs index 58014bceccce..60a8b2b8c022 100644 --- a/crates/rspack_core/src/compiler/mod.rs +++ b/crates/rspack_core/src/compiler/mod.rs @@ -53,8 +53,12 @@ where plugins: Vec>, output_filesystem: T, ) -> Self { - let resolver_factory = Arc::new(ResolverFactory::new(options.resolve.clone())); - let loader_resolver_factory = Arc::new(ResolverFactory::new(options.resolve_loader.clone())); + let new_resolver = options.experiments.rspack_future.new_resolver; + let resolver_factory = Arc::new(ResolverFactory::new(new_resolver, options.resolve.clone())); + let loader_resolver_factory = Arc::new(ResolverFactory::new( + new_resolver, + options.resolve_loader.clone(), + )); let (plugin_driver, options) = PluginDriver::new(options, plugins, resolver_factory.clone()); let cache = Arc::new(Cache::new(options.clone())); diff --git a/crates/rspack_core/src/options/experiments.rs b/crates/rspack_core/src/options/experiments.rs index 5561be010f3b..c5afa9cce275 100644 --- a/crates/rspack_core/src/options/experiments.rs +++ b/crates/rspack_core/src/options/experiments.rs @@ -22,8 +22,9 @@ impl IncrementalRebuildMakeState { } #[derive(Debug, Default)] -#[allow(clippy::empty_structs_with_brackets)] -pub struct RspackFuture {} +pub struct RspackFuture { + pub new_resolver: bool, +} #[derive(Debug, Default)] pub struct Experiments { diff --git a/crates/rspack_core/src/resolver/factory.rs b/crates/rspack_core/src/resolver/factory.rs index cd259c8049d5..b36f28733d50 100644 --- a/crates/rspack_core/src/resolver/factory.rs +++ b/crates/rspack_core/src/resolver/factory.rs @@ -26,7 +26,7 @@ pub struct ResolverFactory { impl Default for ResolverFactory { fn default() -> Self { - Self::new(Resolve::default()) + Self::new(false, Resolve::default()) } } @@ -35,11 +35,10 @@ impl ResolverFactory { self.resolver.clear_cache(); } - pub fn new(options: Resolve) -> Self { - let resolver = Resolver::new(options.clone()); + pub fn new(new_resolver: bool, options: Resolve) -> Self { Self { - resolver, - base_options: options, + base_options: options.clone(), + resolver: Resolver::new(new_resolver, options), resolvers: Default::default(), } } diff --git a/crates/rspack_core/src/resolver/mod.rs b/crates/rspack_core/src/resolver/mod.rs index 6cf2269fdcdb..0461ee441829 100644 --- a/crates/rspack_core/src/resolver/mod.rs +++ b/crates/rspack_core/src/resolver/mod.rs @@ -1,7 +1,7 @@ mod factory; mod resolver_impl; -use std::path::PathBuf; +use std::{fmt, path::PathBuf}; use rspack_error::Error; use rspack_loader_runner::DescriptionData; @@ -11,7 +11,7 @@ pub use self::resolver_impl::{ResolveInnerOptions, Resolver}; use crate::{ResolveArgs, SharedPluginDriver}; /// A successful path resolution or an ignored path. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum ResolveResult { Resource(Resource), Ignored, @@ -20,7 +20,7 @@ pub enum ResolveResult { /// A successful path resolution. /// /// Contains the raw `package.json` value if there is one. -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Resource { pub path: PathBuf, pub query: Option, @@ -28,6 +28,19 @@ pub struct Resource { pub description_data: Option, } +impl fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.full_path()) + } +} + +impl PartialEq for Resource { + fn eq(&self, other: &Self) -> bool { + self.path == other.path && self.query == other.query && self.fragment == other.fragment + } +} +impl Eq for Resource {} + impl Resource { /// Get the full path with query and fragment attached. pub fn full_path(&self) -> PathBuf { @@ -43,8 +56,16 @@ impl Resource { } /// A runtime error message and an error for rspack stats. +#[derive(Debug)] pub struct ResolveError(pub String, pub Error); +impl PartialEq for ResolveError { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} +impl Eq for ResolveError {} + /// Main entry point for module resolution. pub async fn resolve( args: ResolveArgs<'_>, @@ -52,21 +73,24 @@ pub async fn resolve( ) -> Result { let mut args = args; - let resolver = plugin_driver - .resolver_factory - .get(ResolveOptionsWithDependencyType { - resolve_options: args.resolve_options.take(), - resolve_to_context: args.resolve_to_context, - dependency_type: args.dependency_type.clone(), - dependency_category: *args.dependency_category, - }); + let dep = ResolveOptionsWithDependencyType { + resolve_options: args.resolve_options.take(), + resolve_to_context: args.resolve_to_context, + dependency_type: args.dependency_type.clone(), + dependency_category: *args.dependency_category, + }; + + let base_dir = args.context.clone(); + let base_dir = base_dir.as_ref(); - let base_dir = args.context.as_ref(); - let result = resolver.resolve(base_dir, args.specifier); + let resolver = plugin_driver.resolver_factory.get(dep); + let result = resolver + .resolve(base_dir, args.specifier) + .map_err(|error| error.into_resolve_error(&args, plugin_driver)); let (file_dependencies, missing_dependencies) = resolver.dependencies(); args.file_dependencies.extend(file_dependencies); args.missing_dependencies.extend(missing_dependencies); - result.map_err(|error| error.into_resolve_error(args, plugin_driver)) + result } diff --git a/crates/rspack_core/src/resolver/resolver_impl.rs b/crates/rspack_core/src/resolver/resolver_impl.rs index 9385bd552d6c..47cf507cbdc3 100644 --- a/crates/rspack_core/src/resolver/resolver_impl.rs +++ b/crates/rspack_core/src/resolver/resolver_impl.rs @@ -1,4 +1,5 @@ use std::{ + fmt, path::{Path, PathBuf}, sync::Arc, }; @@ -16,14 +17,28 @@ use crate::{ #[derive(Debug)] pub enum ResolveInnerError { NodejsResolver(nodejs_resolver::Error), - // OxcResolver(oxc_resolver::ResolveError), + OxcResolver(oxc_resolver::ResolveError), } /// Proxy to [nodejs_resolver::Options] or [oxc_resolver::ResolveOptions] -#[derive(Debug)] pub enum ResolveInnerOptions<'a> { NodejsResolver(&'a nodejs_resolver::Options), - // OxcResolver(&'a oxc_resolver::ResolveOptions), + OxcResolver(&'a oxc_resolver::ResolveOptions), +} + +impl<'a> fmt::Debug for ResolveInnerOptions<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::NodejsResolver(options) => { + let mut options = (*options).clone(); + options.external_cache = None; + write!(f, "{:?}", options) + } + Self::OxcResolver(options) => { + write!(f, "{:?}", options) + } + } + } } impl<'a> ResolveInnerOptions<'a> { @@ -33,31 +48,31 @@ impl<'a> ResolveInnerOptions<'a> { options.enforce_extension, nodejs_resolver::EnforceExtension::Enabled ), - // Self::OxcResolver(options) => matches!( - // options.enforce_extension, - // oxc_resolver::EnforceExtension::Enabled - // ), + Self::OxcResolver(options) => matches!( + options.enforce_extension, + oxc_resolver::EnforceExtension::Enabled + ), } } pub fn extensions(&self) -> impl Iterator { match self { Self::NodejsResolver(options) => options.extensions.iter(), - // Self::OxcResolver(options) => options.extensions.iter(), + Self::OxcResolver(options) => options.extensions.iter(), } } pub fn main_files(&self) -> impl Iterator { match self { Self::NodejsResolver(options) => options.main_files.iter(), - // Self::OxcResolver(options) => options.main_files.iter(), + Self::OxcResolver(options) => options.main_files.iter(), } } pub fn modules(&self) -> impl Iterator { match self { Self::NodejsResolver(options) => options.modules.iter(), - // Self::OxcResolver(options) => options.modules.iter(), + Self::OxcResolver(options) => options.modules.iter(), } } } @@ -68,12 +83,16 @@ impl<'a> ResolveInnerOptions<'a> { #[derive(Debug)] pub enum Resolver { NodejsResolver(nodejs_resolver::Resolver, Arc), - // OxcResolver(oxc_resolver::Resolver), + OxcResolver(oxc_resolver::Resolver), } impl Resolver { - pub fn new(options: Resolve) -> Self { - Self::new_nodejs_resolver(options) + pub fn new(new_resolver: bool, options: Resolve) -> Self { + if new_resolver { + Self::new_oxc_resolver(options) + } else { + Self::new_nodejs_resolver(options) + } } fn new_nodejs_resolver(options: Resolve) -> Self { @@ -84,11 +103,17 @@ impl Resolver { Self::NodejsResolver(resolver, cache) } + fn new_oxc_resolver(options: Resolve) -> Self { + let options = to_oxc_resolver_options(options, false, DependencyCategory::Unknown); + let resolver = oxc_resolver::Resolver::new(options); + Self::OxcResolver(resolver) + } + /// Clear cache for all resolver instances pub fn clear_cache(&self) { match self { Self::NodejsResolver(_, cache) => cache.entries.clear(), - // Self::OxcResolver(resolver) => resolver.clear_cache(), + Self::OxcResolver(resolver) => resolver.clear_cache(), } } @@ -108,9 +133,16 @@ impl Resolver { ); let resolver = nodejs_resolver::Resolver::new(options); Self::NodejsResolver(resolver, cache.clone()) - } /* Self::OxcResolver(_resolver) => { - * unimplemented!() - * } */ + } + Self::OxcResolver(resolver) => { + let options = to_oxc_resolver_options( + options, + options_with_dependency_type.resolve_to_context, + options_with_dependency_type.dependency_category, + ); + let resolver = resolver.clone_with_options(options); + Self::OxcResolver(resolver) + } } } @@ -125,7 +157,7 @@ impl Resolver { pub fn options(&self) -> ResolveInnerOptions<'_> { match self { Self::NodejsResolver(resolver, _) => ResolveInnerOptions::NodejsResolver(&resolver.options), - // Self::OxcResolver(resolver) => ResolveInnerOptions::OxcResolver(resolver.options()), + Self::OxcResolver(resolver) => ResolveInnerOptions::OxcResolver(resolver.options()), } } @@ -146,9 +178,20 @@ impl Resolver { nodejs_resolver::ResolveResult::Ignored => ResolveResult::Ignored, }) .map_err(ResolveInnerError::NodejsResolver), - // Self::OxcResolver(_resolver) => { - // unimplemented!() - // } + Self::OxcResolver(resolver) => match resolver.resolve(path, request) { + Ok(r) => Ok(ResolveResult::Resource(Resource { + path: r.path().to_path_buf(), + query: r.query().map(ToString::to_string), + fragment: r.fragment().map(ToString::to_string), + description_data: r + .package_json() + .map(|d| DescriptionData::new(d.directory().to_path_buf(), Arc::clone(&d.raw_json))), + })), + Err(error) if matches!(error, oxc_resolver::ResolveError::Ignored(_)) => { + Ok(ResolveResult::Ignored) + } + Err(error) => Err(ResolveInnerError::OxcResolver(error)), + }, } } } @@ -156,14 +199,12 @@ impl Resolver { impl ResolveInnerError { pub fn into_resolve_error( self, - args: ResolveArgs<'_>, + args: &ResolveArgs<'_>, plugin_driver: &SharedPluginDriver, ) -> ResolveError { match self { Self::NodejsResolver(error) => map_nodejs_resolver_error(error, args, plugin_driver), - // Self::OxcResolver(_error) => { - // unimplemented!() - // } + Self::OxcResolver(error) => map_oxc_resolver_error(error, args, plugin_driver), } } } @@ -231,13 +272,109 @@ fn to_nodejs_resolver_options( } } +fn to_oxc_resolver_options( + options: Resolve, + resolve_to_context: bool, + dependency_type: DependencyCategory, +) -> oxc_resolver::ResolveOptions { + let options = options.merge_by_dependency(dependency_type); + let tsconfig = options.tsconfig; + let enforce_extension = oxc_resolver::EnforceExtension::Auto; + let description_files = vec!["package.json".to_string()]; + let extensions = options.extensions.unwrap_or_else(|| { + vec![".tsx", ".ts", ".jsx", ".js", ".mjs", ".json"] + .into_iter() + .map(|s| s.to_string()) + .collect() + }); + let alias = options + .alias + .unwrap_or_default() + .into_iter() + .map(|(key, value)| { + let value = value + .into_iter() + .map(|x| match x { + nodejs_resolver::AliasMap::Target(target) => oxc_resolver::AliasValue::Path(target), + nodejs_resolver::AliasMap::Ignored => oxc_resolver::AliasValue::Ignore, + }) + .collect(); + (key, value) + }) + .collect(); + let prefer_relative = options.prefer_relative.unwrap_or(false); + let symlinks = options.symlinks.unwrap_or(true); + let main_files = options + .main_files + .unwrap_or_else(|| vec![String::from("index")]); + let main_fields = options + .main_fields + .unwrap_or_else(|| vec![String::from("module"), String::from("main")]); + let alias_fields = (if options.browser_field.unwrap_or(true) { + vec!["browser".to_string()] + } else { + vec![] + }) + .into_iter() + .map(|x| vec![x]) + .collect(); + let condition_names = options + .condition_names + .unwrap_or_else(|| vec!["module".to_string(), "import".to_string()]); + let modules = options + .modules + .unwrap_or_else(|| vec!["node_modules".to_string()]); + let fallback = options + .fallback + .unwrap_or_default() + .into_iter() + .map(|(key, value)| { + let value = value + .into_iter() + .map(|x| match x { + nodejs_resolver::AliasMap::Target(target) => oxc_resolver::AliasValue::Path(target), + nodejs_resolver::AliasMap::Ignored => oxc_resolver::AliasValue::Ignore, + }) + .collect(); + (key, value) + }) + .collect(); + let fully_specified = options.fully_specified.unwrap_or_default(); + let exports_fields = options + .exports_field + .unwrap_or_else(|| vec![vec!["exports".to_string()]]); + let extension_alias = options.extension_alias.unwrap_or_default(); + oxc_resolver::ResolveOptions { + fallback, + modules, + extensions, + enforce_extension, + alias, + prefer_relative, + symlinks, + alias_fields, + description_files, + main_files, + main_fields, + condition_names, + tsconfig, + resolve_to_context, + fully_specified, + exports_fields, + extension_alias, + // not supported by rspack yet + prefer_absolute: false, + restrictions: vec![], + roots: vec![], + builtin_modules: false, + } +} + fn map_nodejs_resolver_error( error: nodejs_resolver::Error, - args: ResolveArgs<'_>, + args: &ResolveArgs<'_>, plugin_driver: &SharedPluginDriver, ) -> ResolveError { - let base_dir: &Path = args.context.as_ref(); - let importer = args.importer.map(|i| i.as_str()); match error { nodejs_resolver::Error::Io(error) => { ResolveError(error.to_string(), Error::Io { source: error }) @@ -262,68 +399,104 @@ fn map_nodejs_resolver_error( internal_error!("{} is not a tsconfig", path.display()), ), _ => { - if let Some(importer) = &importer { - let span = args.span.unwrap_or_default(); - // Use relative path in runtime for stable hashing - let (runtime_message, internal_message) = if let nodejs_resolver::Error::Overflow = error { - ( - format!( - "Can't resolve {:?} in {} , maybe it had cycle alias", - args.specifier, - Path::new(&importer) - .relative(&plugin_driver.options.context) - .display() - ), - format!( - "Can't resolve {:?} in {} , maybe it had cycle alias", - args.specifier, importer - ), - ) - } else { - ( - format!( - "Failed to resolve {} in {}", - args.specifier, - base_dir.display() - ), - format!("Failed to resolve {} in {}", args.specifier, importer), - ) - }; - ResolveError( - runtime_message, - TraceableError::from_real_file_path( - Path::new( - importer - .split_once('|') - .map(|(_, path)| path) - .unwrap_or(importer), - ), - span.start as usize, - span.end as usize, - "Resolve error".to_string(), - internal_message.clone(), - ) - .map(|e| { - if args.optional { - Error::TraceableError(e.with_severity(Severity::Warn)) - } else { - Error::TraceableError(e) - } - }) - .unwrap_or_else(|_| { - if args.optional { - Error::InternalError(InternalError::new(internal_message, Severity::Warn)) - } else { - internal_error!(internal_message) - } - }), - ) - } else { - ResolveError( - format!("Failed to resolve {} in project root", args.specifier), - internal_error!("Failed to resolve {} in project root", args.specifier), - ) - } + let is_recursion = matches!(error, nodejs_resolver::Error::Overflow); + map_resolver_error(is_recursion, args, plugin_driver) + } + } +} + +fn map_oxc_resolver_error( + error: oxc_resolver::ResolveError, + args: &ResolveArgs<'_>, + plugin_driver: &SharedPluginDriver, +) -> ResolveError { + match error { + oxc_resolver::ResolveError::InvalidPackageTarget(specifier) => { + let message = format!( + "Export should be relative path and start with \"./\", but got {}", + specifier + ); + ResolveError( + message.clone(), + Error::Anyhow { + source: anyhow::Error::msg(message), + }, + ) } + _ => { + let is_recursion = matches!(error, oxc_resolver::ResolveError::Recursion); + map_resolver_error(is_recursion, args, plugin_driver) + } + } +} + +fn map_resolver_error( + is_recursion: bool, + args: &ResolveArgs<'_>, + plugin_driver: &SharedPluginDriver, +) -> ResolveError { + let base_dir: &Path = args.context.as_ref(); + let importer = args.importer.map(|i| i.as_str()); + if let Some(importer) = &importer { + let span = args.span.unwrap_or_default(); + // Use relative path in runtime for stable hashing + let (runtime_message, internal_message) = if is_recursion { + ( + format!( + "Can't resolve {:?} in {} , maybe it had cycle alias", + args.specifier, + Path::new(importer) + .relative(&plugin_driver.options.context) + .display() + ), + format!( + "Can't resolve {:?} in {} , maybe it had cycle alias", + args.specifier, importer + ), + ) + } else { + ( + format!( + "Failed to resolve {} in {}", + args.specifier, + base_dir.display() + ), + format!("Failed to resolve {} in {}", args.specifier, importer), + ) + }; + ResolveError( + runtime_message, + TraceableError::from_real_file_path( + Path::new( + importer + .split_once('|') + .map(|(_, path)| path) + .unwrap_or(importer), + ), + span.start as usize, + span.end as usize, + "Resolve error".to_string(), + internal_message.clone(), + ) + .map(|e| { + if args.optional { + Error::TraceableError(e.with_severity(Severity::Warn)) + } else { + Error::TraceableError(e) + } + }) + .unwrap_or_else(|_| { + if args.optional { + Error::InternalError(InternalError::new(internal_message, Severity::Warn)) + } else { + internal_error!(internal_message) + } + }), + ) + } else { + ResolveError( + format!("Failed to resolve {} in project root", args.specifier), + internal_error!("Failed to resolve {} in project root", args.specifier), + ) } } diff --git a/packages/rspack/src/config/adapter.ts b/packages/rspack/src/config/adapter.ts index 3433715b2b6b..958a83255e40 100644 --- a/packages/rspack/src/config/adapter.ts +++ b/packages/rspack/src/config/adapter.ts @@ -725,7 +725,9 @@ function getRawExperiments( asyncWebAssembly, newSplitChunks, css, - rspackFuture + rspackFuture: { + newResolver: rspackFuture.newResolver ?? false + } }; } diff --git a/packages/rspack/src/config/types.ts b/packages/rspack/src/config/types.ts index 0dcf70712699..d357028993f3 100644 --- a/packages/rspack/src/config/types.ts +++ b/packages/rspack/src/config/types.ts @@ -655,7 +655,9 @@ export interface IncrementalRebuildOptions { make?: boolean; emitAsset?: boolean; } -export interface RspackFutureOptions {} +export interface RspackFutureOptions { + newResolver?: boolean; +} // TODO: discuss with webpack, should move to css generator options // export interface CssExperimentOptions { // exportsOnly?: boolean; diff --git a/packages/rspack/src/config/zod/experiments.ts b/packages/rspack/src/config/zod/experiments.ts index 86f4a332c2b2..20094f4a9cab 100644 --- a/packages/rspack/src/config/zod/experiments.ts +++ b/packages/rspack/src/config/zod/experiments.ts @@ -16,6 +16,10 @@ export function experiments() { outputModule: z.boolean().optional(), newSplitChunks: z.boolean().optional(), css: z.boolean().optional(), - rspackFuture: z.strictObject({}).optional() + rspackFuture: z + .strictObject({ + newResolver: z.boolean().optional() + }) + .optional() }); } diff --git a/packages/rspack/tests/configCases/resolve-new/alias/.gitignore b/packages/rspack/tests/configCases/resolve-new/alias/.gitignore new file mode 100644 index 000000000000..736e8ae58ad8 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/alias/.gitignore @@ -0,0 +1 @@ +!node_modules \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/alias/a.js b/packages/rspack/tests/configCases/resolve-new/alias/a.js new file mode 100644 index 000000000000..6cd1d0075d40 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/alias/a.js @@ -0,0 +1 @@ +module.exports = "a"; diff --git a/packages/rspack/tests/configCases/resolve-new/alias/index.js b/packages/rspack/tests/configCases/resolve-new/alias/index.js new file mode 100644 index 000000000000..bf155eea171b --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/alias/index.js @@ -0,0 +1,11 @@ +import value from "@b"; +import value2 from "xx"; +import value3 from "alias"; +import value4 from "ignored"; + +it("alias should work", () => { + expect(value).toBe("a"); + expect(value2).toBe("a"); + expect(value3).toBe("a"); + expect(value4).toStrictEqual({}); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/alias/node_modules/alias/index.js b/packages/rspack/tests/configCases/resolve-new/alias/node_modules/alias/index.js new file mode 100644 index 000000000000..62288897e28a --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/alias/node_modules/alias/index.js @@ -0,0 +1 @@ +module.exports = require('@b'); \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/alias/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/alias/webpack.config.js new file mode 100644 index 000000000000..3f4ca3220847 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/alias/webpack.config.js @@ -0,0 +1,28 @@ +const path = require("path"); +module.exports = { + entry: "./index.js", + resolve: { + alias: { + "@b": path.resolve(__dirname, "./a"), + xx: path.resolve(__dirname, "./a"), + ignored: path.resolve(__dirname, "./a") + } + }, + module: { + rules: [ + { + test: /\.js$/, + resolve: { + alias: { + ignored: false + } + } + } + ] + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/by-dependency/baz.js b/packages/rspack/tests/configCases/resolve-new/by-dependency/baz.js new file mode 100644 index 000000000000..c55720d996d4 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/by-dependency/baz.js @@ -0,0 +1 @@ +export default "baz"; diff --git a/packages/rspack/tests/configCases/resolve-new/by-dependency/foo.bar b/packages/rspack/tests/configCases/resolve-new/by-dependency/foo.bar new file mode 100644 index 000000000000..d02ba545bd3b --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/by-dependency/foo.bar @@ -0,0 +1 @@ +export default 'foo'; diff --git a/packages/rspack/tests/configCases/resolve-new/by-dependency/index.js b/packages/rspack/tests/configCases/resolve-new/by-dependency/index.js new file mode 100644 index 000000000000..ce785c88dc13 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/by-dependency/index.js @@ -0,0 +1,10 @@ +import foo from "./foo"; +import baz from "./baz"; + +it("should resolve 'foo.bar', byDependency '.bar' extension works", function () { + expect(foo).toBe("foo"); +}); + +it("should resolve 'baz.js', byDependency '...' extensions works", function () { + expect(baz).toBe("baz"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/by-dependency/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/by-dependency/webpack.config.js new file mode 100644 index 000000000000..4d9b6a85f179 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/by-dependency/webpack.config.js @@ -0,0 +1,15 @@ +module.exports = { + mode: "development", + resolve: { + byDependency: { + esm: { + extensions: [".bar", "..."] + } + } + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/.gitignore b/packages/rspack/tests/configCases/resolve-new/condition-exports/.gitignore new file mode 100644 index 000000000000..736e8ae58ad8 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/.gitignore @@ -0,0 +1 @@ +!node_modules \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/index.js b/packages/rspack/tests/configCases/resolve-new/condition-exports/index.js new file mode 100644 index 000000000000..eaba5bcdec33 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/index.js @@ -0,0 +1,13 @@ +import esmImport from "exports-conditional"; +import esmImport2 from "exports-conditional-2"; + +const cjsImport = require("exports-conditional"); +const cjsImport2 = require("exports-conditional-2"); + +it("conditional exports should works", () => { + expect(esmImport).toBe("esm"); + expect(cjsImport).toBe("cjs"); + + expect(esmImport2).toBe("esm"); + expect(cjsImport2).toBe("cjs"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.cjs b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.cjs new file mode 100644 index 000000000000..55bf5b1d7edd --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.cjs @@ -0,0 +1 @@ +module.exports = "cjs" \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.js b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.js new file mode 100644 index 000000000000..389852fd4348 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.js @@ -0,0 +1 @@ +export default "esm" \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/package.json b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/package.json new file mode 100644 index 000000000000..5779f368dec5 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/package.json @@ -0,0 +1,10 @@ +{ + "name": "exports-conditional-2", + "version": "1.0.0", + "exports": { + "import": "./lib.js", + "require": "./lib.cjs" + }, + "type": "module", + "sideEffects": false +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.cjs b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.cjs new file mode 100644 index 000000000000..55bf5b1d7edd --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.cjs @@ -0,0 +1 @@ +module.exports = "cjs" \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.mjs b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.mjs new file mode 100644 index 000000000000..389852fd4348 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.mjs @@ -0,0 +1 @@ +export default "esm" \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/package.json b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/package.json new file mode 100644 index 000000000000..f2cfa71d52e6 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/package.json @@ -0,0 +1,9 @@ +{ + "name": "exports-conditional", + "version": "1.0.0", + "exports": { + "import": "./lib.mjs", + "require": "./lib.cjs" + }, + "sideEffects": false +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/condition-exports/webpack.config.js new file mode 100644 index 000000000000..00c6f11bf8b4 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/webpack.config.js @@ -0,0 +1,8 @@ +module.exports = { + entry: "./index.js", + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/.gitignore b/packages/rspack/tests/configCases/resolve-new/conditions/.gitignore new file mode 100644 index 000000000000..736e8ae58ad8 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/.gitignore @@ -0,0 +1 @@ +!node_modules \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/index.js b/packages/rspack/tests/configCases/resolve-new/conditions/index.js new file mode 100644 index 000000000000..7cdec16e52ad --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/index.js @@ -0,0 +1,9 @@ +import mappedValue from "exports-field/dist/main"; +import entryValue from "exports-field"; + +it("conditionNames should works", () => { + expect(mappedValue).toBe("lib/lib2/main"); + expect(entryValue).toBe("x"); + const pkgValue = require("exports-field/package.json"); + expect(pkgValue.name).toBe("exports-field"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/index.js b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/index.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/lib2/main.js b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/lib2/main.js new file mode 100644 index 000000000000..898c9cad72c4 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/lib2/main.js @@ -0,0 +1 @@ +module.exports = "lib/lib2/main"; diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/main.js b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/main.js new file mode 100644 index 000000000000..010786ab1dbf --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/main.js @@ -0,0 +1 @@ +module.exports = "lib/main"; diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/package.json b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/package.json new file mode 100644 index 000000000000..87941561f6c2 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/package.json @@ -0,0 +1,19 @@ +{ + "name": "exports-field", + "version": "1.0.0", + "main": "./main.js", + "exports": { + ".": "./x.js", + "./dist/": { + "pack": [ + "./lib/lib2/", + "./lib/" + ], + "node": "./lib/", + "default": "./lib/" + }, + "./dist/a.js": "./../../a.js", + "./package.json": "./package.json" + }, + "sideEffects": false +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/x.js b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/x.js new file mode 100644 index 000000000000..7a5b647aa23c --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/x.js @@ -0,0 +1 @@ +module.exports = "x" \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/conditions/webpack.config.js new file mode 100644 index 000000000000..0305b7b748e0 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/webpack.config.js @@ -0,0 +1,11 @@ +module.exports = { + entry: "./index.js", + resolve: { + conditionNames: ["pack"] + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/exports-fields/.gitignore b/packages/rspack/tests/configCases/resolve-new/exports-fields/.gitignore new file mode 100644 index 000000000000..736e8ae58ad8 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/exports-fields/.gitignore @@ -0,0 +1 @@ +!node_modules \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/exports-fields/index.js b/packages/rspack/tests/configCases/resolve-new/exports-fields/index.js new file mode 100644 index 000000000000..56297d031f13 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/exports-fields/index.js @@ -0,0 +1,4 @@ +it("should resolve both alternatives", () => { + const b = require("exports-field"); + expect(b).toBe("b"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/b.js b/packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/b.js new file mode 100644 index 000000000000..b855beca7398 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/b.js @@ -0,0 +1 @@ +module.exports = 'b' diff --git a/packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/package.json b/packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/package.json new file mode 100644 index 000000000000..9f235c448020 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/package.json @@ -0,0 +1,5 @@ +{ + "name": "exports-field", + "version": "1.0.0", + "b": "./b.js" +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/exports-fields/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/exports-fields/webpack.config.js new file mode 100644 index 000000000000..d259bce369a0 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/exports-fields/webpack.config.js @@ -0,0 +1,11 @@ +module.exports = { + entry: "./index.js", + resolve: { + exportsFields: ["a", "b"] + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/extension-alias/index.js b/packages/rspack/tests/configCases/resolve-new/extension-alias/index.js new file mode 100644 index 000000000000..634bb57d0841 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/extension-alias/index.js @@ -0,0 +1,5 @@ +import value from "./src/index.mjs"; + +it("extension-alias should work", () => { + expect(value).toBe("in ts"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/extension-alias/src/index.mts b/packages/rspack/tests/configCases/resolve-new/extension-alias/src/index.mts new file mode 100644 index 000000000000..258a6f0a1aea --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/extension-alias/src/index.mts @@ -0,0 +1 @@ +module.exports = "in ts" diff --git a/packages/rspack/tests/configCases/resolve-new/extension-alias/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/extension-alias/webpack.config.js new file mode 100644 index 000000000000..b04b755aa55f --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/extension-alias/webpack.config.js @@ -0,0 +1,13 @@ +module.exports = { + entry: "./index.js", + resolve: { + extensionAlias: { + ".mjs": [".mts"] + } + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/fallback/#/a.js b/packages/rspack/tests/configCases/resolve-new/fallback/#/a.js new file mode 100644 index 000000000000..bd816eaba4ca --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/fallback/#/a.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/resolve-new/fallback/a/1.js b/packages/rspack/tests/configCases/resolve-new/fallback/a/1.js new file mode 100644 index 000000000000..bd816eaba4ca --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/fallback/a/1.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/resolve-new/fallback/a/2.js b/packages/rspack/tests/configCases/resolve-new/fallback/a/2.js new file mode 100644 index 000000000000..66a141057e00 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/fallback/a/2.js @@ -0,0 +1 @@ +module.exports = "not 2"; diff --git a/packages/rspack/tests/configCases/resolve-new/fallback/b/2.js b/packages/rspack/tests/configCases/resolve-new/fallback/b/2.js new file mode 100644 index 000000000000..4bbffde10442 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/fallback/b/2.js @@ -0,0 +1 @@ +module.exports = 2; diff --git a/packages/rspack/tests/configCases/resolve-new/fallback/index.js b/packages/rspack/tests/configCases/resolve-new/fallback/index.js new file mode 100644 index 000000000000..846ed16deb8e --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/fallback/index.js @@ -0,0 +1,14 @@ +it("ignores the fallback if an existing module is present", () => { + const two = require("./b/2"); + expect(two).toBe(2); +}); + +it("can fallback if the module does not exist", () => { + const one = require("./b/1"); + expect(one).toBe(1); +}); + +it("# alias should work", () => { + const one = require("#/a"); + expect(one).toBe(1); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/fallback/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/fallback/webpack.config.js new file mode 100644 index 000000000000..34f93ea3b9da --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/fallback/webpack.config.js @@ -0,0 +1,17 @@ +const path = require("path"); +/** @type {import("@rspack/core").Configuration} */ +module.exports = { + resolve: { + alias: { + "#": path.resolve(__dirname, "#") + }, + fallback: { + "./b": path.resolve(__dirname, "a") + } + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/modules/a/foo/a.js b/packages/rspack/tests/configCases/resolve-new/modules/a/foo/a.js new file mode 100644 index 000000000000..e94fef18587e --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/modules/a/foo/a.js @@ -0,0 +1 @@ +export default "a"; diff --git a/packages/rspack/tests/configCases/resolve-new/modules/a/foo/package.json b/packages/rspack/tests/configCases/resolve-new/modules/a/foo/package.json new file mode 100644 index 000000000000..fd262b44d2df --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/modules/a/foo/package.json @@ -0,0 +1,5 @@ +{ + "name": "a-foo", + "version": "1.0.0", + "module": "./a.js" +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/modules/b/foo/b.js b/packages/rspack/tests/configCases/resolve-new/modules/b/foo/b.js new file mode 100644 index 000000000000..eff703ff4657 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/modules/b/foo/b.js @@ -0,0 +1 @@ +export default "b"; diff --git a/packages/rspack/tests/configCases/resolve-new/modules/b/foo/package.json b/packages/rspack/tests/configCases/resolve-new/modules/b/foo/package.json new file mode 100644 index 000000000000..cea8dde66c0e --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/modules/b/foo/package.json @@ -0,0 +1,5 @@ +{ + "name": "b-foo", + "version": "1.0.0", + "module": "./b.js" +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/modules/index.js b/packages/rspack/tests/configCases/resolve-new/modules/index.js new file mode 100644 index 000000000000..c8d6b184314d --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/modules/index.js @@ -0,0 +1,7 @@ +import a from "foo/a"; +import b from "foo/b"; + +it("resolve modules should work fine", async () => { + expect(a).toEqual("a"); + expect(b).toEqual("b"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/modules/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/modules/webpack.config.js new file mode 100644 index 000000000000..e1dea9896648 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/modules/webpack.config.js @@ -0,0 +1,12 @@ +const path = require("path"); + +module.exports = { + resolve: { + modules: [path.resolve(__dirname, "a"), path.resolve(__dirname, "b")] + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/multi-alias/a/1.js b/packages/rspack/tests/configCases/resolve-new/multi-alias/a/1.js new file mode 100644 index 000000000000..bd816eaba4ca --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/multi-alias/a/1.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/resolve-new/multi-alias/b/2.js b/packages/rspack/tests/configCases/resolve-new/multi-alias/b/2.js new file mode 100644 index 000000000000..4bbffde10442 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/multi-alias/b/2.js @@ -0,0 +1 @@ +module.exports = 2; diff --git a/packages/rspack/tests/configCases/resolve-new/multi-alias/index.js b/packages/rspack/tests/configCases/resolve-new/multi-alias/index.js new file mode 100644 index 000000000000..b64161dbec46 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/multi-alias/index.js @@ -0,0 +1,6 @@ +it("should resolve both alternatives", () => { + const one = require("_/1"); + const two = require("_/2"); + expect(one).toBe(1); + expect(two).toBe(2); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/multi-alias/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/multi-alias/webpack.config.js new file mode 100644 index 000000000000..8cc9115cfe05 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/multi-alias/webpack.config.js @@ -0,0 +1,14 @@ +const path = require("path"); +/** @type {import("@rspack/core").Configuration} */ +module.exports = { + resolve: { + alias: { + _: [path.resolve(__dirname, "a"), path.resolve(__dirname, "b")] + } + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/bar.mjs b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/bar.mjs new file mode 100644 index 000000000000..4548a26ba14d --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/bar.mjs @@ -0,0 +1 @@ +export default 'bar' diff --git a/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/baz.js b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/baz.js new file mode 100644 index 000000000000..c55720d996d4 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/baz.js @@ -0,0 +1 @@ +export default "baz"; diff --git a/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/foo.bar b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/foo.bar new file mode 100644 index 000000000000..c44fd9f23452 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/foo.bar @@ -0,0 +1,3 @@ +import bar from './bar' + +export default bar; diff --git a/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/index.js b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/index.js new file mode 100644 index 000000000000..72cd80bba74b --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/index.js @@ -0,0 +1,10 @@ +import foo from "./foo"; +import baz from "./baz"; + +it("should resolve 'foo.bar', byDependency '.bar' extension works", function () { + expect(foo).toBe("bar"); +}); + +it("should resolve 'baz.js', byDependency '...' extensions works", function () { + expect(baz).toBe("baz"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/webpack.config.js new file mode 100644 index 000000000000..0f7c56ec7af1 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/webpack.config.js @@ -0,0 +1,31 @@ +module.exports = { + mode: "development", + resolve: { + byDependency: { + esm: { + extensions: [".bar", "..."] // enable resolve .bar + } + } + }, + module: { + rules: [ + { + test: /\.bar$/, + resolve: { + byDependency: { + // dependencyType of import is esm + esm: { + extensions: [".mjs", "..."], // enable resolve .mjs in .bar + fullySpecified: false // resolve .mjs without fullySpecified in .bar + } + } + } + } + ] + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/index.js b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/index.js new file mode 100644 index 000000000000..8ab1d3fdbeca --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/index.js @@ -0,0 +1,19 @@ +import fakeValue from "fake"; +import fileValue from "@/file"; +import baseUrlFileValue from "src/file"; +import fileValueWithQuery1 from "@/file?a=b"; +import fileValueWithQuery2 from "@/file?a=c"; + +it("should require real files by alias tsconfig paths", () => { + expect(fakeValue).toEqual("real"); + expect(fileValue).toStrictEqual({}); +}); + +it("absolute path relative base url should works", () => { + expect(baseUrlFileValue).toStrictEqual({}); + expect(baseUrlFileValue === fileValue).toBe(true); + expect(fileValueWithQuery1).toStrictEqual({}); + expect(fileValueWithQuery2).toStrictEqual({}); + expect(fileValueWithQuery1 !== fileValueWithQuery2).toBe(true); + expect(fileValueWithQuery1 !== baseUrlFileValue).toBe(true); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/real.js b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/real.js new file mode 100644 index 000000000000..c78545175574 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/real.js @@ -0,0 +1 @@ +module.exports = "real"; diff --git a/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/src/file.js b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/src/file.js new file mode 100644 index 000000000000..ff8b4c56321a --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/src/file.js @@ -0,0 +1 @@ +export default {}; diff --git a/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/tsconfig.json b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/tsconfig.json new file mode 100644 index 000000000000..d289e24a3f15 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "paths": { + "fake": ["./real.js"], + "@/*": ["./src/*"] + } + } +} diff --git a/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/webpack.config.js new file mode 100644 index 000000000000..b56c8cc06462 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/webpack.config.js @@ -0,0 +1,15 @@ +const path = require("path"); + +module.exports = { + entry: { + main: "./index.js" + }, + resolve: { + tsConfigPath: path.resolve(__dirname, "./tsconfig.json") + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; From 2892c96631a95d40a01f55267bc4a71a100109d3 Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY Date: Tue, 5 Sep 2023 20:39:03 +0800 Subject: [PATCH 04/30] =?UTF-8?q?chore:=20=F0=9F=A4=96=20align=20webpack?= =?UTF-8?q?=20factory=20meta=20(#4125)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 🤖 align webpack factory meta * chore: 🤖 fix type * chore: 🤖 update lock * chore: 🤖 revert main lock file --- crates/node_binding/binding.d.ts | 24 +++++++++++++++---- .../src/js_values/normal_module_factory.rs | 4 ++-- crates/rspack_core/src/module.rs | 2 +- crates/rspack_core/src/normal_module.rs | 4 ++-- .../rspack_core/src/normal_module_factory.rs | 4 +++- .../rspack_core/src/tree_shaking/visitor.rs | 4 ++-- packages/rspack/src/Compiler.ts | 2 +- 7 files changed, 31 insertions(+), 13 deletions(-) diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 03f97663ff0a..016cf0fab54b 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -133,7 +133,7 @@ export const enum BuiltinPluginKind { export function cleanupGlobalTrace(): void export interface FactoryMeta { - sideEffects?: boolean + sideEffectFree?: boolean } export interface JsAsset { @@ -432,6 +432,13 @@ export interface JsStatsWarning { formatted: string } +export interface NodeFS { + writeFile: (...args: any[]) => any + removeFile: (...args: any[]) => any + mkdir: (...args: any[]) => any + mkdirp: (...args: any[]) => any +} + export interface PathData { filename?: string hash?: string @@ -757,9 +764,10 @@ export interface RawModuleOptions { export interface RawModuleRule { /** - * A conditional match matching an absolute path + query + fragment - * This one is reserved as our escape hatch for those who - * relies on some single-thread runtime behaviors. + * A conditional match matching an absolute path + query + fragment. + * Note: + * This is a custom matching rule not initially designed by webpack. + * Only for single-threaded environment interoperation purpose. */ rspackResource?: RawRuleSetCondition /** A condition matcher matching an absolute path. */ @@ -1031,3 +1039,11 @@ export function registerGlobalTrace(filter: string, layer: "chrome" | "logger", /** Builtin loader runner */ export function runBuiltinLoader(builtin: string, options: string | undefined | null, loaderContext: JsLoaderContext): Promise +export interface ThreadsafeNodeFS { + writeFile: (...args: any[]) => any + removeFile: (...args: any[]) => any + mkdir: (...args: any[]) => any + mkdirp: (...args: any[]) => any + removeDirAll: (...args: any[]) => any +} + diff --git a/crates/node_binding/src/js_values/normal_module_factory.rs b/crates/node_binding/src/js_values/normal_module_factory.rs index a33afbaae08d..feb0296a2eb0 100644 --- a/crates/node_binding/src/js_values/normal_module_factory.rs +++ b/crates/node_binding/src/js_values/normal_module_factory.rs @@ -30,7 +30,7 @@ pub struct AfterResolveData { #[napi(object)] pub struct FactoryMeta { - pub side_effects: Option, + pub side_effect_free: Option, } #[napi(object)] @@ -98,7 +98,7 @@ impl From> for AfterResolveData { .map(|item| item.to_string_lossy().to_string()) .collect::>(), factory_meta: FactoryMeta { - side_effects: value.factory_meta.side_effects, + side_effect_free: value.factory_meta.side_effect_free, }, } } diff --git a/crates/rspack_core/src/module.rs b/crates/rspack_core/src/module.rs index f01b5471b879..a4eae53db8c2 100644 --- a/crates/rspack_core/src/module.rs +++ b/crates/rspack_core/src/module.rs @@ -127,7 +127,7 @@ pub struct BuildResult { #[derive(Debug, Default, Clone)] pub struct FactoryMeta { - pub side_effects: Option, + pub side_effect_free: Option, } pub type ModuleIdentifier = Identifier; diff --git a/crates/rspack_core/src/normal_module.rs b/crates/rspack_core/src/normal_module.rs index 3796cefe6eb2..edab59323ade 100644 --- a/crates/rspack_core/src/normal_module.rs +++ b/crates/rspack_core/src/normal_module.rs @@ -496,8 +496,8 @@ impl Module for NormalModule { module_chain: &mut HashSet, ) -> ConnectionState { if let Some(mgm) = module_graph.module_graph_module_by_identifier(&self.identifier()) { - if let Some(side_effect) = mgm.factory_meta.as_ref().and_then(|m| m.side_effects) { - return ConnectionState::Bool(side_effect); + if let Some(side_effect_free) = mgm.factory_meta.as_ref().and_then(|m| m.side_effect_free) { + return ConnectionState::Bool(!side_effect_free); } if let Some(side_effect_free) = mgm.build_meta.as_ref().and_then(|m| m.side_effect_free) && side_effect_free { // use module chain instead of is_evaluating_side_effects to mut module graph diff --git a/crates/rspack_core/src/normal_module_factory.rs b/crates/rspack_core/src/normal_module_factory.rs index a936dccb5461..29362a33f600 100644 --- a/crates/rspack_core/src/normal_module_factory.rs +++ b/crates/rspack_core/src/normal_module_factory.rs @@ -585,7 +585,9 @@ impl NormalModuleFactory { let (resolved_parser_options, resolved_generator_options) = self.calculate_parser_and_generator_options(&resolved_module_rules); let factory_meta = FactoryMeta { - side_effects: self.calculate_side_effects(&resolved_module_rules, &resource_data), + side_effect_free: self + .calculate_side_effects(&resolved_module_rules, &resource_data) + .map(|side_effects| !side_effects), }; let resolved_parser_and_generator = self diff --git a/crates/rspack_core/src/tree_shaking/visitor.rs b/crates/rspack_core/src/tree_shaking/visitor.rs index cc825d601306..d507d74465ea 100644 --- a/crates/rspack_core/src/tree_shaking/visitor.rs +++ b/crates/rspack_core/src/tree_shaking/visitor.rs @@ -1335,10 +1335,10 @@ impl<'a> ModuleRefAnalyze<'a> { // sideEffects in module.rule has higher priority, // we could early return if we match a rule. if let Some(FactoryMeta { - side_effects: Some(side_effects), + side_effect_free: Some(side_effect_free), }) = factory_meta { - return Some(SideEffectType::Configuration(*side_effects)); + return Some(SideEffectType::Configuration(!*side_effect_free)); } None } diff --git a/packages/rspack/src/Compiler.ts b/packages/rspack/src/Compiler.ts index 6aef519cba87..06eab2a2c1ed 100644 --- a/packages/rspack/src/Compiler.ts +++ b/packages/rspack/src/Compiler.ts @@ -834,7 +834,7 @@ class Compiler { (loaderContext: any) => { loaderContext._module = { factoryMeta: { - sideEffectFree: !resolveData.factoryMeta.sideEffects + sideEffectFree: !!resolveData.factoryMeta.sideEffectFree } }; } From 81d956e6a1c4618997f1c1f37161c205d75e5ded Mon Sep 17 00:00:00 2001 From: hardfist Date: Wed, 6 Sep 2023 11:58:32 +0800 Subject: [PATCH 05/30] chore: add documenation link notice of pull request template (#4106) Co-authored-by: Boshen --- .github/PULL_REQUEST_TEMPLATE.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8760e0f64373..3fd52875b2d9 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -17,3 +17,11 @@ ## Test Plan + +## Require Documentation? + +[ ] No + + + +[ ] Yes, the corresponding rspack-website PR is \_\_ From a233bb42d8835811f9eeae1a0c9cdf784f0331aa Mon Sep 17 00:00:00 2001 From: Hana Date: Wed, 6 Sep 2023 12:23:08 +0800 Subject: [PATCH 06/30] chore: use cross-env (#4130) * chore: use cross-env * chore: move to sub-package --- package.json | 2 +- packages/rspack/package.json | 3 ++- pnpm-lock.yaml | 23 +++-------------------- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index cb9bb50ef4b2..717e9aeaeb92 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "prepare": "is-ci || husky install", "pnpm:devPreinstall": "is-ci || node ./scripts/doctor.js", "test:example": "pnpm --filter \"example-*\" build", - "test:unit": "NO_COLOR=1 pnpm --filter \"@rspack/*\" test", + "test:unit": "pnpm --filter \"@rspack/*\" test", "test:e2e": "pnpm --filter \"@rspack-e2e/*\" test", "test:ci": "cross-env NODE_OPTIONS=--max_old_space_size=8192 pnpm run build:js && pnpm run test:example && pnpm run test:unit && pnpm test:webpack", "test:webpack": "pnpm --filter \"webpack-test\" test:metric" diff --git a/packages/rspack/package.json b/packages/rspack/package.json index 62d702075500..3df48cd10d04 100644 --- a/packages/rspack/package.json +++ b/packages/rspack/package.json @@ -16,7 +16,7 @@ "build": "tsc -b ./tsconfig.build.json", "prepare": "pnpm precompile-schema", "dev": "tsc -w", - "test": "node --expose-gc --max-old-space-size=8192 --experimental-vm-modules ../../node_modules/jest-cli/bin/jest --runInBand --logHeapUsage", + "test": "cross-env NO_COLOR=1 node --expose-gc --max-old-space-size=8192 --experimental-vm-modules ../../node_modules/jest-cli/bin/jest --runInBand --logHeapUsage", "precompile-schema": "node ./scripts/precompile-schema.js" }, "files": [ @@ -40,6 +40,7 @@ "babel-loader": "^9.1.0", "babel-plugin-import": "^1.13.5", "copy-webpack-plugin": "5", + "cross-env": "^7.0.3", "file-loader": "^6.2.0", "jest-serializer-path": "^0.1.15", "less": "4.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8ee021190c68..6d2786a675d6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -918,6 +918,7 @@ importers: browserslist: ^4.21.3 compare-versions: 6.0.0-rc.1 copy-webpack-plugin: '5' + cross-env: ^7.0.3 enhanced-resolve: 5.12.0 file-loader: ^6.2.0 graceful-fs: 4.2.10 @@ -972,6 +973,7 @@ importers: babel-loader: 9.1.2 babel-plugin-import: 1.13.5 copy-webpack-plugin: 5.1.2 + cross-env: 7.0.3 file-loader: 6.2.0 jest-serializer-path: 0.1.15 less: 4.1.3 @@ -12084,7 +12086,6 @@ packages: dependencies: webpack: 5.76.0_webpack-cli@4.10.0 webpack-cli: 4.10.0_webpack@5.76.0 - dev: true /@webpack-cli/info/1.5.0_webpack-cli@4.10.0: resolution: {integrity: sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==} @@ -12093,7 +12094,6 @@ packages: dependencies: envinfo: 7.8.1 webpack-cli: 4.10.0_webpack@5.76.0 - dev: true /@webpack-cli/serve/1.7.0_webpack-cli@4.10.0: resolution: {integrity: sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==} @@ -12105,7 +12105,6 @@ packages: optional: true dependencies: webpack-cli: 4.10.0_webpack@5.76.0 - dev: true /@xtuc/ieee754/1.2.0: resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -14018,7 +14017,6 @@ packages: is-plain-object: 2.0.4 kind-of: 6.0.3 shallow-clone: 3.0.1 - dev: true /clone-response/1.0.2: resolution: {integrity: sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==} @@ -15806,7 +15804,6 @@ packages: resolution: {integrity: sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==} engines: {node: '>=4'} hasBin: true - dev: true /err-code/2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} @@ -16511,7 +16508,6 @@ packages: /fastest-levenshtein/1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} - dev: true /fastq/1.13.0: resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} @@ -18146,7 +18142,6 @@ packages: dependencies: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 - dev: true /imurmurhash/0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} @@ -18284,7 +18279,6 @@ packages: /interpret/2.2.0: resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} engines: {node: '>= 0.10'} - dev: true /interpret/3.1.1: resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} @@ -18646,7 +18640,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: isobject: 3.0.1 - dev: true /is-plain-object/5.0.0: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} @@ -18837,7 +18830,6 @@ packages: /isobject/3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} - dev: true /isomorphic-fetch/2.2.1: resolution: {integrity: sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA==} @@ -19748,7 +19740,6 @@ packages: /kind-of/6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - dev: true /kleur/3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} @@ -23771,7 +23762,6 @@ packages: engines: {node: '>= 0.10'} dependencies: resolve: 1.22.2 - dev: true /rechoir/0.8.0: resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} @@ -24058,7 +24048,6 @@ packages: engines: {node: '>=8'} dependencies: resolve-from: 5.0.0 - dev: true /resolve-from/4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} @@ -24067,7 +24056,6 @@ packages: /resolve-from/5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - dev: true /resolve-pathname/3.0.0: resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==} @@ -24771,7 +24759,6 @@ packages: engines: {node: '>=8'} dependencies: kind-of: 6.0.3 - dev: true /shallowequal/1.1.0: resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} @@ -26275,7 +26262,7 @@ packages: schema-utils: 3.1.2 serialize-javascript: 6.0.1 terser: 5.17.1 - webpack: 5.76.0 + webpack: 5.76.0_webpack-cli@4.10.0 /terser/5.16.1: resolution: {integrity: sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==} @@ -27729,7 +27716,6 @@ packages: rechoir: 0.7.1 webpack: 5.76.0_webpack-cli@4.10.0 webpack-merge: 5.9.0 - dev: true /webpack-dev-middleware/5.3.3: resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==} @@ -28004,7 +27990,6 @@ packages: dependencies: clone-deep: 4.0.1 wildcard: 2.0.0 - dev: true /webpack-sources/2.3.1: resolution: {integrity: sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==} @@ -28119,7 +28104,6 @@ packages: - '@swc/core' - esbuild - uglify-js - dev: true /webpack/5.80.0_esbuild@0.17.18: resolution: {integrity: sha512-OIMiq37XK1rWO8mH9ssfFKZsXg4n6klTEDL7S8/HqbAOBBaiy8ABvXvz0dDCXeEF9gqwxSvVk611zFPjS8hJxA==} @@ -28287,7 +28271,6 @@ packages: /wildcard/2.0.0: resolution: {integrity: sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==} - dev: true /window-size/0.1.0: resolution: {integrity: sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==} From fdbe43e49514ca1916b460eff82b8ec3e1c08734 Mon Sep 17 00:00:00 2001 From: Gengkun Date: Wed, 6 Sep 2023 13:20:12 +0800 Subject: [PATCH 07/30] refactor: zod schema and normalized types (#4098) --- crates/rspack_core/src/options/externals.rs | 2 +- .../tests/normalizeOptions.test.ts | 15 +- packages/rspack/package.json | 5 +- packages/rspack/scripts/precompile-schema.js | 99 - .../src/builtin-plugin/ExternalsPlugin.ts | 8 +- .../rspack/src/builtin-plugin/HtmlPlugin.ts | 23 +- packages/rspack/src/builtin-plugin/base.ts | 4 +- packages/rspack/src/config/adapter.ts | 38 +- packages/rspack/src/config/adapterRuleUse.ts | 2 +- packages/rspack/src/config/defaults.ts | 30 +- packages/rspack/src/config/index.ts | 32 +- packages/rspack/src/config/normalization.ts | 185 +- packages/rspack/src/config/schema.js | 2335 ----------------- packages/rspack/src/config/target.js | 4 + packages/rspack/src/config/types.ts | 708 ----- packages/rspack/src/config/zod.ts | 1052 ++++++++ packages/rspack/src/config/zod/_rewrite.ts | 18 - packages/rspack/src/config/zod/builtins.ts | 46 - packages/rspack/src/config/zod/devtool.ts | 33 - packages/rspack/src/config/zod/entry.ts | 37 - packages/rspack/src/config/zod/experiments.ts | 25 - .../src/config/zod/externals-presets.ts | 14 - .../rspack/src/config/zod/externals-type.ts | 27 - packages/rspack/src/config/zod/externals.ts | 11 - packages/rspack/src/config/zod/index.ts | 55 - .../src/config/zod/infrastructure-logging.ts | 16 - packages/rspack/src/config/zod/module.ts | 0 packages/rspack/src/config/zod/node.ts | 22 - .../src/config/zod/optimization/index.ts | 37 - .../config/zod/optimization/split-chunks.ts | 53 - packages/rspack/src/config/zod/output.ts | 145 - packages/rspack/src/config/zod/plugins.ts | 17 - packages/rspack/src/config/zod/resolve.ts | 36 - packages/rspack/src/config/zod/snapshot.ts | 18 - packages/rspack/src/config/zod/stats.ts | 35 - packages/rspack/src/config/zod/target.ts | 80 - .../rspack/src/config/zod/watch-options.ts | 18 - packages/rspack/src/rspack.ts | 6 +- packages/rspack/src/rspackOptionsApply.ts | 5 +- packages/rspack/src/util/index.ts | 1 - packages/rspack/src/util/validate.ts | 29 + .../__snapshots__/Defaults.unittest.ts.snap | 4 +- .../max-size-casing/webpack.config.js | 2 +- pnpm-lock.yaml | 14 +- 44 files changed, 1351 insertions(+), 3995 deletions(-) delete mode 100644 packages/rspack/scripts/precompile-schema.js delete mode 100644 packages/rspack/src/config/schema.js delete mode 100644 packages/rspack/src/config/types.ts create mode 100644 packages/rspack/src/config/zod.ts delete mode 100644 packages/rspack/src/config/zod/_rewrite.ts delete mode 100644 packages/rspack/src/config/zod/builtins.ts delete mode 100644 packages/rspack/src/config/zod/devtool.ts delete mode 100644 packages/rspack/src/config/zod/entry.ts delete mode 100644 packages/rspack/src/config/zod/experiments.ts delete mode 100644 packages/rspack/src/config/zod/externals-presets.ts delete mode 100644 packages/rspack/src/config/zod/externals-type.ts delete mode 100644 packages/rspack/src/config/zod/externals.ts delete mode 100644 packages/rspack/src/config/zod/index.ts delete mode 100644 packages/rspack/src/config/zod/infrastructure-logging.ts delete mode 100644 packages/rspack/src/config/zod/module.ts delete mode 100644 packages/rspack/src/config/zod/node.ts delete mode 100644 packages/rspack/src/config/zod/optimization/index.ts delete mode 100644 packages/rspack/src/config/zod/optimization/split-chunks.ts delete mode 100644 packages/rspack/src/config/zod/output.ts delete mode 100644 packages/rspack/src/config/zod/plugins.ts delete mode 100644 packages/rspack/src/config/zod/resolve.ts delete mode 100644 packages/rspack/src/config/zod/snapshot.ts delete mode 100644 packages/rspack/src/config/zod/stats.ts delete mode 100644 packages/rspack/src/config/zod/target.ts delete mode 100644 packages/rspack/src/config/zod/watch-options.ts create mode 100644 packages/rspack/src/util/validate.ts diff --git a/crates/rspack_core/src/options/externals.rs b/crates/rspack_core/src/options/externals.rs index a4d41ff268fa..3f1f2f418a27 100644 --- a/crates/rspack_core/src/options/externals.rs +++ b/crates/rspack_core/src/options/externals.rs @@ -11,7 +11,7 @@ pub type Externals = Vec; pub enum ExternalItemValue { String(String), Bool(bool), - Array(Vec), // TODO: string[] | Record + Array(Vec), // TODO: Record } pub type ExternalItemObject = HashMap; diff --git a/packages/rspack-dev-server/tests/normalizeOptions.test.ts b/packages/rspack-dev-server/tests/normalizeOptions.test.ts index c72cccfd263a..7951141955d9 100644 --- a/packages/rspack-dev-server/tests/normalizeOptions.test.ts +++ b/packages/rspack-dev-server/tests/normalizeOptions.test.ts @@ -96,13 +96,7 @@ async function match(config: RspackOptions) { const compiler = createCompiler({ ...config, entry: ENTRY, - stats: "none", - infrastructureLogging: { - level: "info", - stream: { - write: () => {} - } - } + stats: "none" }); const server = new RspackDevServer( compiler.options.devServer ?? {}, @@ -123,12 +117,7 @@ async function matchAdditionEntries( const compiler = createCompiler({ ...config, stats: "none", - entry: ENTRY, - infrastructureLogging: { - stream: { - write: () => {} - } - } + entry: ENTRY }); const server = new RspackDevServer(serverConfig, compiler); diff --git a/packages/rspack/package.json b/packages/rspack/package.json index 3df48cd10d04..cf96f47f803b 100644 --- a/packages/rspack/package.json +++ b/packages/rspack/package.json @@ -14,10 +14,8 @@ }, "scripts": { "build": "tsc -b ./tsconfig.build.json", - "prepare": "pnpm precompile-schema", "dev": "tsc -w", - "test": "cross-env NO_COLOR=1 node --expose-gc --max-old-space-size=8192 --experimental-vm-modules ../../node_modules/jest-cli/bin/jest --runInBand --logHeapUsage", - "precompile-schema": "node ./scripts/precompile-schema.js" + "test": "cross-env NO_COLOR=1 node --expose-gc --max-old-space-size=8192 --experimental-vm-modules ../../node_modules/jest-cli/bin/jest --runInBand --logHeapUsage" }, "files": [ "dist" @@ -36,7 +34,6 @@ "@types/watchpack": "^2.4.0", "@types/webpack-sources": "3.2.0", "@types/ws": "8.5.3", - "ajv": "^8.12.0", "babel-loader": "^9.1.0", "babel-plugin-import": "^1.13.5", "copy-webpack-plugin": "5", diff --git a/packages/rspack/scripts/precompile-schema.js b/packages/rspack/scripts/precompile-schema.js deleted file mode 100644 index 2e4417b3406c..000000000000 --- a/packages/rspack/scripts/precompile-schema.js +++ /dev/null @@ -1,99 +0,0 @@ -const fs = require("fs"); -const { default: Ajv, _, Name } = require("ajv"); -const path = require("path"); -const standaloneCode = require("ajv/dist/standalone").default; -const terser = require("terser"); - -const configDir = path.resolve(__dirname, "../src/config"); -const configSchema = path.resolve(configDir, "./schema.js"); -const configCheck = path.resolve(configDir, "./schema.check.js"); - -const ajv = new Ajv({ - code: { source: true, optimize: true }, - strictNumbers: false, - logger: false -}); - -ajv.addKeyword({ - keyword: "instanceof", - schemaType: "string", - code(ctx) { - const { data, schema } = ctx; - ctx.fail(_`!(${data} instanceof ${new Name(schema)})`); - } -}); - -ajv.addKeyword({ - keyword: "absolutePath", - type: "string", - schemaType: "boolean", - - code(ctx) { - const { data, schema } = ctx; - ctx.fail( - _`${data}.includes("!") || (absolutePathRegExp.test(${data}) !== ${schema})` - ); - } -}); - -ajv.removeKeyword("minLength"); -ajv.addKeyword({ - keyword: "minLength", - type: "string", - schemaType: "number", - - code(ctx) { - const { data, schema } = ctx; - if (schema !== 1) - throw new Error("Schema precompilation only supports minLength: 1"); - ctx.fail(_`${data}.length < 1`); - } -}); - -ajv.removeKeyword("enum"); -ajv.addKeyword({ - keyword: "enum", - schemaType: "array", - $data: true, - - code(ctx) { - const { data, schema } = ctx; - for (const item of schema) { - if (typeof item === "object" && item !== null) { - throw new Error( - `Schema precompilation only supports primitive values in enum: ${JSON.stringify( - item, - null, - 2 - )}` - ); - } - } - ctx.fail( - schema.map(x => _`${data} !== ${x}`).reduce((a, b) => _`${a} && ${b}`) - ); - } -}); - -const validate = ajv.compile(require(configSchema)); -const code = standaloneCode(ajv, validate); -const generated = - "/** This file was automatically generated, Run `pnpm precompile-schema` to update */\n" + - code; -fs.writeFileSync(configCheck, generated); - -// terser -// .minify(code, { -// compress: { -// passes: 3 -// }, -// mangle: true, -// ecma: 2015, -// toplevel: true -// }) -// .then(minified => { -// const code = -// "/** This file was automatically generated, Run `pnpm precompile-schema` to update */\n" + -// minified.code; -// fs.promises.writeFile(configCheck, code); -// }); diff --git a/packages/rspack/src/builtin-plugin/ExternalsPlugin.ts b/packages/rspack/src/builtin-plugin/ExternalsPlugin.ts index d31c29775ac6..e40fdd03dd6a 100644 --- a/packages/rspack/src/builtin-plugin/ExternalsPlugin.ts +++ b/packages/rspack/src/builtin-plugin/ExternalsPlugin.ts @@ -21,9 +21,11 @@ export const ExternalsPlugin = create( function getRawExternalItem(item: ExternalItem): RawExternalItem { if (typeof item === "string") { return { type: "string", stringPayload: item }; - } else if (item instanceof RegExp) { + } + if (item instanceof RegExp) { return { type: "regexp", regexpPayload: item.source }; - } else if (typeof item === "function") { + } + if (typeof item === "function") { return { type: "function", fnPayload: async ctx => { @@ -34,7 +36,7 @@ function getRawExternalItem(item: ExternalItem): RawExternalItem { result: getRawExternalItemValueFormFnResult(result), external_type: type }); - }); + }) as Promise; if (promise && promise.then) { promise.then( result => diff --git a/packages/rspack/src/builtin-plugin/HtmlPlugin.ts b/packages/rspack/src/builtin-plugin/HtmlPlugin.ts index cd83422ae349..0a4f4471b68e 100644 --- a/packages/rspack/src/builtin-plugin/HtmlPlugin.ts +++ b/packages/rspack/src/builtin-plugin/HtmlPlugin.ts @@ -1,12 +1,29 @@ +import { z } from "zod"; import { RawHtmlPluginConfig } from "@rspack/binding"; import { BuiltinPluginKind, create } from "./base"; +import { validate } from "../util/validate"; -export type HtmlPluginOptions = Omit & { - meta?: Record>; -}; +const htmlPluginOptions = z.strictObject({ + filename: z.string().optional(), + template: z.string().optional(), + templateContent: z.string().optional(), + templateParameters: z.record(z.string()).optional(), + inject: z.enum(["head", "body"]).optional(), + publicPath: z.string().optional(), + scriptLoading: z.enum(["blocking", "defer", "module"]).optional(), + chunks: z.string().array().optional(), + excludedChunks: z.string().array().optional(), + sri: z.enum(["sha256", "sha384", "sha512"]).optional(), + minify: z.boolean().optional(), + title: z.string().optional(), + favicon: z.string().optional(), + meta: z.record(z.string().or(z.record(z.string()))).optional() +}); +export type HtmlPluginOptions = z.infer; export const HtmlPlugin = create( BuiltinPluginKind.Html, (c: HtmlPluginOptions): RawHtmlPluginConfig => { + validate(c, htmlPluginOptions); const meta: Record> = {}; for (const key in c.meta) { const value = c.meta[key]; diff --git a/packages/rspack/src/builtin-plugin/base.ts b/packages/rspack/src/builtin-plugin/base.ts index a02235fc74fc..cef64202d50f 100644 --- a/packages/rspack/src/builtin-plugin/base.ts +++ b/packages/rspack/src/builtin-plugin/base.ts @@ -1,5 +1,5 @@ import * as binding from "@rspack/binding"; -import { Compiler } from ".."; +import { Compiler, RspackPluginInstance } from ".."; // TODO: workaround for https://github.com/napi-rs/napi-rs/pull/1690 export enum BuiltinPluginKind { @@ -18,7 +18,7 @@ export enum BuiltinPluginKind { HttpExternals = "HttpExternals" } -export abstract class RspackBuiltinPlugin { +export abstract class RspackBuiltinPlugin implements RspackPluginInstance { abstract raw(): binding.BuiltinPlugin; apply(compiler: Compiler) { compiler.__internal__registerBuiltinPlugin(this); diff --git a/packages/rspack/src/config/adapter.ts b/packages/rspack/src/config/adapter.ts index 958a83255e40..b1154c57cb3c 100644 --- a/packages/rspack/src/config/adapter.ts +++ b/packages/rspack/src/config/adapter.ts @@ -14,12 +14,14 @@ import type { RawAssetResourceGeneratorOptions, RawIncrementalRebuild, RawModuleRuleUses, - RawFuncUseCtx + RawFuncUseCtx, + RawRspackFuture } from "@rspack/binding"; import assert from "assert"; import { Compiler } from "../Compiler"; import { normalizeStatsPreset } from "../Stats"; import { isNil } from "../util"; +import { parseResource } from "../util/identifier"; import { ComposeJsUseOptions, LoaderContext, @@ -27,14 +29,10 @@ import { } from "./adapterRuleUse"; import { CrossOriginLoading, - ExternalsPresets, LibraryOptions, - ModuleOptionsNormalized, Node, Optimization, - OutputNormalized, Resolve, - RspackOptionsNormalized, RuleSetCondition, RuleSetLogicalConditions, RuleSetRule, @@ -49,11 +47,18 @@ import { AssetParserOptions, ParserOptionsByModuleType, GeneratorOptionsByModuleType, + IncrementalRebuildOptions, + OptimizationSplitChunksOptions, + RspackFutureOptions +} from "./zod"; +import { ExperimentsNormalized, - IncrementalRebuildOptions -} from "./types"; -import { SplitChunksConfig } from "./zod/optimization/split-chunks"; -import { parseResource } from "../util/identifier"; + ModuleOptionsNormalized, + OutputNormalized, + RspackOptionsNormalized +} from "./normalization"; + +export type { LoaderContext }; export const getRawOptions = ( options: RspackOptionsNormalized, @@ -646,7 +651,7 @@ function getRawOptimization( } function toRawSplitChunksOptions( - sc?: SplitChunksConfig + sc?: OptimizationSplitChunksOptions ): RawOptions["optimization"]["splitChunks"] | undefined { if (!sc) { return; @@ -725,9 +730,16 @@ function getRawExperiments( asyncWebAssembly, newSplitChunks, css, - rspackFuture: { - newResolver: rspackFuture.newResolver ?? false - } + rspackFuture: getRawRspackFutureOptions(rspackFuture) + }; +} + +function getRawRspackFutureOptions( + future: RspackFutureOptions +): RawRspackFuture { + assert(!isNil(future.newResolver)); + return { + newResolver: future.newResolver }; } diff --git a/packages/rspack/src/config/adapterRuleUse.ts b/packages/rspack/src/config/adapterRuleUse.ts index a7297eee0c79..e1d5aa86a349 100644 --- a/packages/rspack/src/config/adapterRuleUse.ts +++ b/packages/rspack/src/config/adapterRuleUse.ts @@ -16,7 +16,7 @@ import { RuleSetUse, RuleSetUseItem, RuleSetLoaderWithOptions -} from "./types"; +} from "./zod"; import { parsePathQueryFragment } from "../loader-runner"; import { isNil } from "../util"; diff --git a/packages/rspack/src/config/defaults.ts b/packages/rspack/src/config/defaults.ts index 33684e8142dc..d24bb093faa8 100644 --- a/packages/rspack/src/config/defaults.ts +++ b/packages/rspack/src/config/defaults.ts @@ -13,39 +13,38 @@ import fs from "fs"; import path from "path"; import { isNil } from "../util"; import { cleverMerge } from "../util/cleverMerge"; -import { deprecated_resolveBuiltins } from "../builtin-plugin"; import { getDefaultTarget, getTargetProperties, getTargetsProperties } from "./target"; import type { - AvailableTarget, + Target, Context, - Entry, - EntryDescription, - EntryDescriptionNormalized, - EntryNormalized, - ExperimentsNormalized, ExternalsPresets, InfrastructureLogging, Mode, ModuleOptions, Node, Optimization, - OutputNormalized, ResolveOptions, - RspackOptionsNormalized, RuleSetRules, SnapshotOptions -} from "./types"; +} from "./zod"; +import { + EntryDescriptionNormalized, + EntryNormalized, + ExperimentsNormalized, + OutputNormalized, + RspackOptionsNormalized +} from "./normalization"; export const applyRspackOptionsDefaults = ( options: RspackOptionsNormalized ) => { F(options, "context", () => process.cwd()); F(options, "target", () => { - return getDefaultTarget(options.context!) as AvailableTarget; + return getDefaultTarget(options.context!); }); const { mode, target } = options; @@ -153,18 +152,16 @@ const applyExperimentsDefaults = ( experiments: ExperimentsNormalized, { cache }: { cache: boolean } ) => { - D(experiments, "incrementalRebuild", {}); D(experiments, "lazyCompilation", false); D(experiments, "asyncWebAssembly", false); D(experiments, "newSplitChunks", true); D(experiments, "css", true); // we not align with webpack about the default value for better DX - D(experiments, "rspackFuture", {}); + D(experiments, "incrementalRebuild", {}); if (typeof experiments.incrementalRebuild === "object") { D(experiments.incrementalRebuild, "make", true); D(experiments.incrementalRebuild, "emitAsset", true); } - if ( cache === false && experiments.incrementalRebuild && @@ -173,6 +170,11 @@ const applyExperimentsDefaults = ( experiments.incrementalRebuild.make = false; // TODO: use logger to warn user enable cache for incrementalRebuild.make } + + D(experiments, "rspackFuture", {}); + if (typeof experiments.rspackFuture === "object") { + D(experiments.rspackFuture, "newResolver", false); + } }; const applySnapshotDefaults = ( diff --git a/packages/rspack/src/config/index.ts b/packages/rspack/src/config/index.ts index 3cbb724a67d5..9c9dac3ac3c9 100644 --- a/packages/rspack/src/config/index.ts +++ b/packages/rspack/src/config/index.ts @@ -1,34 +1,4 @@ -import { configSchema } from "./zod"; -import { fromZodError } from "zod-validation-error"; - export * from "./normalization"; -export * from "./types"; +export * from "./zod"; export * from "./defaults"; export * from "./adapter"; - -export function validateConfig(opts: any) { - const res = configSchema().safeParse(opts); - if (!res.success) { - const strategy = process.env.RSPACK_CONFIG_VALIDATE ?? "strict"; - if (strategy === "loose-silent") return; - const issueSeparator = "$issue$"; - const prefixSeparator = "$prefix$"; - const validationErr = fromZodError(res.error, { - prefix: "Configuration error", - prefixSeparator, - issueSeparator - }); - // The output validationErr.message looks like - // `Configuration error$prefix$xxxx error$issue$yyy error$issue$zzz error` - const [prefix, reason] = validationErr.message.split(prefixSeparator); - const reasonItem = reason.split(issueSeparator); - const friendlyErr = new Error( - `${prefix}:\n${reasonItem.map(item => `- ${item}`).join("\n")}` - ); - if (strategy === "loose") { - console.error(friendlyErr.message); - } else { - throw friendlyErr; - } - } -} diff --git a/packages/rspack/src/config/normalization.ts b/packages/rspack/src/config/normalization.ts index 49f7dc6d93d9..c90ce038e8a5 100644 --- a/packages/rspack/src/config/normalization.ts +++ b/packages/rspack/src/config/normalization.ts @@ -8,17 +8,73 @@ * https://github.com/webpack/webpack/blob/main/LICENSE */ +import { Compilation } from ".."; import { deprecatedWarn } from "../util"; import type { + Context, + Dependencies, + Node, + DevTool, EntryStatic, - EntryStaticNormalized, + Externals, + ExternalsPresets, + ExternalsType, + InfrastructureLogging, LibraryOptions, + Mode, + Name, OptimizationRuntimeChunk, - OptimizationRuntimeChunkNormalized, + Resolve, RspackOptions, - RspackOptionsNormalized -} from "./types"; -import { OptimizationRuntimeChunkConfig } from "./zod/optimization"; + Target, + SnapshotOptions, + CacheOptions, + StatsValue, + Optimization, + Plugins, + Watch, + WatchOptions, + DevServer, + Profile, + Builtins, + EntryRuntime, + ChunkLoading, + PublicPath, + EntryFilename, + Path, + Clean, + Filename, + ChunkFilename, + CrossOriginLoading, + CssFilename, + CssChunkFilename, + HotUpdateMainFilename, + HotUpdateChunkFilename, + AssetModuleFilename, + UniqueName, + ChunkLoadingGlobal, + EnabledLibraryTypes, + OutputModule, + StrictModuleErrorHandling, + GlobalObject, + ImportFunctionName, + Iife, + WasmLoading, + EnabledWasmLoadingTypes, + WebassemblyModuleFilename, + TrustedTypes, + SourceMapFilename, + HashDigest, + HashDigestLength, + HashFunction, + HashSalt, + WorkerPublicPath, + RuleSetRules, + ParserOptionsByModuleType, + GeneratorOptionsByModuleType, + IncrementalRebuildOptions, + RspackFutureOptions +} from "./zod"; export const getNormalizedRspackOptions = ( config: RspackOptions @@ -28,9 +84,9 @@ export const getNormalizedRspackOptions = ( config.ignoreWarnings !== undefined ? config.ignoreWarnings.map(ignore => { if (typeof ignore === "function") { - return ignore as (...args: any[]) => boolean; + return ignore; } else { - return warning => { + return (warning: Error) => { return ignore.test(warning.message); }; } @@ -291,7 +347,7 @@ const getNormalizedEntryStatic = (entry: EntryStatic) => { }; const getNormalizedOptimizationRuntimeChunk = ( - runtimeChunk?: OptimizationRuntimeChunkConfig + runtimeChunk?: OptimizationRuntimeChunk ): OptimizationRuntimeChunkNormalized | undefined => { if (runtimeChunk === undefined) return undefined; if (runtimeChunk === false) return false; @@ -356,3 +412,116 @@ const keyedNestedConfig = ( } return result; }; + +export type EntryNormalized = EntryStaticNormalized; +export interface EntryStaticNormalized { + [k: string]: EntryDescriptionNormalized; +} +export interface EntryDescriptionNormalized { + import?: string[]; + runtime?: EntryRuntime; + chunkLoading?: ChunkLoading; + asyncChunks?: boolean; + publicPath?: PublicPath; + baseUri?: string; + filename?: EntryFilename; +} + +export interface OutputNormalized { + path?: Path; + clean?: Clean; + publicPath?: PublicPath; + filename?: Filename; + chunkFilename?: ChunkFilename; + crossOriginLoading?: CrossOriginLoading; + cssFilename?: CssFilename; + cssChunkFilename?: CssChunkFilename; + hotUpdateMainFilename?: HotUpdateMainFilename; + hotUpdateChunkFilename?: HotUpdateChunkFilename; + assetModuleFilename?: AssetModuleFilename; + uniqueName?: UniqueName; + chunkLoadingGlobal?: ChunkLoadingGlobal; + enabledLibraryTypes?: EnabledLibraryTypes; + library?: LibraryOptions; + module?: OutputModule; + strictModuleErrorHandling?: StrictModuleErrorHandling; + globalObject?: GlobalObject; + importFunctionName?: ImportFunctionName; + iife?: Iife; + wasmLoading?: WasmLoading; + enabledWasmLoadingTypes?: EnabledWasmLoadingTypes; + webassemblyModuleFilename?: WebassemblyModuleFilename; + chunkFormat?: string | false; + chunkLoading?: string | false; + enabledChunkLoadingTypes?: string[]; + trustedTypes?: TrustedTypes; + sourceMapFilename?: SourceMapFilename; + hashDigest?: HashDigest; + hashDigestLength?: HashDigestLength; + hashFunction?: HashFunction; + hashSalt?: HashSalt; + asyncChunks?: boolean; + workerChunkLoading?: ChunkLoading; + workerWasmLoading?: WasmLoading; + workerPublicPath?: WorkerPublicPath; +} + +export interface ModuleOptionsNormalized { + defaultRules?: RuleSetRules; + rules: RuleSetRules; + parser: ParserOptionsByModuleType; + generator: GeneratorOptionsByModuleType; +} + +export interface ExperimentsNormalized { + lazyCompilation?: boolean; + incrementalRebuild?: false | IncrementalRebuildOptions; + asyncWebAssembly?: boolean; + outputModule?: boolean; + newSplitChunks?: boolean; + css?: boolean; + futureDefaults?: boolean; + rspackFuture?: RspackFutureOptions; +} + +export type IgnoreWarningsNormalized = (( + warning: Error, + compilation: Compilation +) => boolean)[]; + +export type OptimizationRuntimeChunkNormalized = + | false + | { + name: (...args: any[]) => string | undefined; + }; + +export interface RspackOptionsNormalized { + name?: Name; + dependencies?: Dependencies; + context?: Context; + mode?: Mode; + entry: EntryNormalized; + output: OutputNormalized; + resolve: Resolve; + resolveLoader: Resolve; + module: ModuleOptionsNormalized; + target?: Target; + externals?: Externals; + externalsType?: ExternalsType; + externalsPresets: ExternalsPresets; + infrastructureLogging: InfrastructureLogging; + devtool?: DevTool; + node: Node; + snapshot: SnapshotOptions; + cache?: CacheOptions; + stats: StatsValue; + optimization: Optimization; + plugins: Plugins; + experiments: ExperimentsNormalized; + watch?: Watch; + watchOptions: WatchOptions; + devServer?: DevServer; + ignoreWarnings?: IgnoreWarningsNormalized; + profile?: Profile; + builtins: Builtins; +} diff --git a/packages/rspack/src/config/schema.js b/packages/rspack/src/config/schema.js deleted file mode 100644 index f8d2b0a8c7b1..000000000000 --- a/packages/rspack/src/config/schema.js +++ /dev/null @@ -1,2335 +0,0 @@ -module.exports = { - definitions: { - AssetModuleFilename: { - description: - "The filename of asset modules as relative path inside the 'output.path' directory.", - anyOf: [ - { - type: "string" - } - ] - }, - AssetParserDataUrlOptions: { - description: "Options object for DataUrl condition.", - type: "object", - additionalProperties: false, - properties: { - maxSize: { - description: - "Maximum size of asset that should be inline as modules. Default: 8kb.", - type: "number" - } - } - }, - AssetParserOptions: { - description: "Parser options for asset modules.", - type: "object", - additionalProperties: false, - properties: { - dataUrlCondition: { - description: "The condition for inlining the asset as DataUrl.", - anyOf: [ - { - $ref: "#/definitions/AssetParserDataUrlOptions" - } - ] - } - } - }, - AuxiliaryComment: { - description: "Add a comment in the UMD wrapper.", - anyOf: [ - { - description: "Append the same comment above each import style.", - type: "string" - }, - { - $ref: "#/definitions/LibraryCustomUmdCommentObject" - } - ] - }, - CacheOptions: { - description: - "Cache generated modules and chunks to improve performance for multiple incremental builds.", - type: "boolean" - }, - ChunkFilename: { - description: - "Specifies the filename template of output files of non-initial chunks on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - oneOf: [ - { - $ref: "#/definitions/FilenameTemplate" - } - ] - }, - ChunkFormat: { - description: - "The format of chunks (formats included by default are 'array-push' (web/WebWorker), 'commonjs' (node.js), 'module' (ESM), but others might be added by plugins).", - anyOf: [ - { - enum: ["array-push", "commonjs", "module", false] - }, - { - type: "string" - } - ] - }, - ChunkLoading: { - description: - "The method of loading chunks (methods included by default are 'jsonp' (web), 'import' (ESM), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).", - anyOf: [ - { - enum: [false] - }, - { - $ref: "#/definitions/ChunkLoadingType" - } - ] - }, - ChunkLoadingType: { - description: - "The method of loading chunks (methods included by default are 'jsonp' (web), 'import' (ESM), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).", - anyOf: [ - { - enum: ["jsonp", "import-scripts", "require", "async-node", "import"] - }, - { - type: "string" - } - ] - }, - CrossOriginLoading: { - description: "This option enables cross-origin loading of chunks.", - enum: [false, "anonymous", "use-credentials"] - }, - Context: { - description: - "The base directory (absolute path!) for resolving the `entry` option. If `output.pathinfo` is set, the included pathinfo is shortened to this directory.", - type: "string" - }, - CssChunkFilename: { - description: - "Specifies the filename template of non-initial output css files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - oneOf: [ - { - $ref: "#/definitions/FilenameTemplate" - } - ] - }, - CssFilename: { - description: - "Specifies the filename template of output css files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - oneOf: [ - { - $ref: "#/definitions/FilenameTemplate" - } - ] - }, - HotUpdateChunkFilename: { - description: - "The filename of the Hot Update Chunks. They are inside the output.path directory.", - type: "string", - absolutePath: false - }, - HotUpdateMainFilename: { - description: - "The filename of the Hot Update Main File. It is inside the 'output.path' directory.", - type: "string", - absolutePath: false - }, - HashDigest: { - description: "Digest type used for the hash.", - type: "string" - }, - HashDigestLength: { - description: "Number of chars which are used for the hash.", - type: "number", - minimum: 1 - }, - HashFunction: { - description: - "Algorithm used for generation the hash (see node.js crypto package).", - anyOf: [ - { - type: "string", - minLength: 1 - }, - { - instanceof: "Function" - } - ] - }, - HashSalt: { - description: "Any string which is added to the hash to salt it.", - type: "string", - minLength: 1 - }, - WebassemblyModuleFilename: { - description: - "The filename of WebAssembly modules as relative path inside the 'output.path' directory.", - type: "string" - }, - EnabledWasmLoadingTypes: { - description: - "List of wasm loading types enabled for use by entry points.", - type: "array", - items: { - $ref: "#/definitions/WasmLoadingType" - } - }, - EnabledChunkLoadingTypes: { - description: - "List of chunk loading types enabled for use by entry points.", - type: "array", - items: { - $ref: "#/definitions/ChunkLoadingType" - } - }, - WasmLoading: { - description: - "The method of loading WebAssembly Modules (methods included by default are 'fetch' (web/WebWorker), 'async-node' (node.js), but others might be added by plugins).", - anyOf: [ - { - enum: [false] - }, - { - $ref: "#/definitions/WasmLoadingType" - } - ] - }, - WasmLoadingType: { - description: - "The method of loading WebAssembly Modules (methods included by default are 'fetch' (web/WebWorker), 'async-node' (node.js), but others might be added by plugins).", - anyOf: [ - { - enum: ["fetch-streaming", "fetch", "async-node"] - }, - { - type: "string" - } - ] - }, - Dependencies: { - description: "References to other configurations to depend on.", - type: "array", - items: { - description: "References to another configuration to depend on.", - type: "string" - } - }, - DevServer: { - description: "Options for the rspack-dev-server.", - type: "object" - }, - DevTool: { - description: - "A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map).", - anyOf: [ - { - enum: [false] - }, - { - type: "string", - pattern: - "^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$" - } - ] - }, - EnabledLibraryTypes: { - description: "List of library types enabled for use by entry points.", - type: "array", - items: { - $ref: "#/definitions/LibraryType" - } - }, - Entry: { - description: "The entry point(s) of the compilation.", - anyOf: [ - { - $ref: "#/definitions/EntryStatic" - } - ] - }, - EntryDescription: { - description: "An object with entry point description.", - type: "object", - additionalProperties: false, - properties: { - import: { - $ref: "#/definitions/EntryItem" - }, - runtime: { - $ref: "#/definitions/EntryRuntime" - }, - wasmLoading: { - $ref: "#/definitions/WasmLoading" - } - }, - required: ["import"] - }, - EntryFilename: { - description: - "Specifies the filename of the output file on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - oneOf: [ - { - $ref: "#/definitions/FilenameTemplate" - } - ] - }, - EntryItem: { - description: "Module(s) that are loaded upon startup.", - anyOf: [ - { - description: - "All modules are loaded upon startup. The last one is exported.", - type: "array", - items: { - description: - "A module that is loaded upon startup. Only the last one is exported.", - type: "string", - minLength: 1 - }, - minItems: 1, - uniqueItems: true - }, - { - description: - "The string is resolved to a module which is loaded upon startup.", - type: "string", - minLength: 1 - } - ] - }, - EntryObject: { - description: - "Multiple entry bundles are created. The key is the entry name. The value can be a string, an array or an entry description object.", - type: "object", - additionalProperties: { - description: "An entry point with name.", - anyOf: [ - { - $ref: "#/definitions/EntryItem" - }, - { - $ref: "#/definitions/EntryDescription" - } - ] - } - }, - EntryRuntime: { - description: - "The name of the runtime chunk. If set a runtime chunk with this name is created or an existing entrypoint is used as runtime.", - anyOf: [ - { - enum: [false] - }, - { - type: "string", - minLength: 1 - } - ] - }, - EntryStatic: { - description: "A static entry description.", - anyOf: [ - { - $ref: "#/definitions/EntryObject" - }, - { - $ref: "#/definitions/EntryUnnamed" - } - ] - }, - EntryUnnamed: { - description: "An entry point without name.", - oneOf: [ - { - $ref: "#/definitions/EntryItem" - } - ] - }, - Experiments: { - description: - "Enables/Disables experiments (experimental features with relax SemVer compatibility).", - type: "object", - additionalProperties: false, - properties: { - asyncWebAssembly: { - description: "Support WebAssembly as asynchronous EcmaScript Module.", - type: "boolean" - }, - incrementalRebuild: { - description: "Rebuild incrementally", - anyOf: [ - { - type: "boolean" - }, - { - type: "object", - properties: { - make: { - description: "Make stage enable incremental rebuild", - type: "boolean" - }, - emitAsset: { - description: "Emit asset enable incremental rebuild", - type: "boolean" - } - } - } - ] - }, - lazyCompilation: { - description: - "Compile entrypoints and import()s only when they are accessed.", - anyOf: [ - { - type: "boolean" - } - ] - }, - outputModule: { - description: "Allow output javascript files as module source type.", - type: "boolean" - }, - newSplitChunks: { - description: "Enable new SplitChunksPlugin", - type: "boolean" - }, - css: { - description: "Enable native css support.", - type: "boolean" - }, - rspackFuture: { - description: - "Enable default behavior in the future version of Rspack", - type: "object", - additionalProperties: false, - properties: {} - } - } - }, - ExternalItem: { - description: - "Specify dependency that shouldn't be resolved by rspack, but should become dependencies of the resulting bundle. The kind of the dependency depends on `output.libraryTarget`.", - anyOf: [ - { - description: "Every matched dependency becomes external.", - instanceof: "RegExp" - }, - { - description: - "An exact matched dependency becomes external. The same string is used as external dependency.", - type: "string" - }, - { - description: - "If an dependency matches exactly a property of the object, the property value is used as dependency.", - type: "object", - additionalProperties: { - $ref: "#/definitions/ExternalItemValue" - } - }, - { - description: - "The function is called on each dependency (`function(context, request, callback(err, result))`).", - instanceof: "Function" - } - ] - }, - ExternalItemValue: { - description: "The dependency used for the external.", - anyOf: [ - { - type: "array", - items: { - description: "A part of the target of the external.", - type: "string", - minLength: 1 - } - }, - { - description: "The target of the external.", - type: "string" - }, - { - description: - "`true`: The dependency name is used as target of the external.", - type: "boolean" - } - ] - }, - Externals: { - description: - "Specify dependencies that shouldn't be resolved by rspack, but should become dependencies of the resulting bundle. The kind of the dependency depends on `output.libraryTarget`.", - anyOf: [ - { - type: "array", - items: { - $ref: "#/definitions/ExternalItem" - } - }, - { - $ref: "#/definitions/ExternalItem" - } - ] - }, - ExternalsPresets: { - description: "Enable presets of externals for specific targets.", - type: "object", - additionalProperties: false, - properties: { - node: { - description: - "Treat node.js built-in modules like fs, path or vm as external and load them via require() when used.", - type: "boolean" - }, - web: { - description: - "Treat references to 'http(s)://...' and 'std:...' as external and load them via import when used (Note that this changes execution order as externals are executed before any other code in the chunk).", - type: "boolean" - }, - electron: { - description: - "Treat common electron built-in modules in main and preload context like 'electron', 'ipc' or 'shell' as external and load them via require() when used.", - type: "boolean" - }, - electronMain: { - description: - "Treat electron built-in modules in the main context like 'app', 'ipc-main' or 'shell' as external and load them via require() when used.", - type: "boolean" - }, - electronPreload: { - description: - "Treat electron built-in modules in the preload context like 'web-frame', 'ipc-renderer' or 'shell' as external and load them via require() when used.", - type: "boolean" - }, - electronRenderer: { - description: - "Treat electron built-in modules in the renderer context like 'web-frame', 'ipc-renderer' or 'shell' as external and load them via require() when used.", - type: "boolean" - } - } - }, - ExternalsType: { - description: - "Specifies the default type of externals ('amd*', 'umd*', 'system' and 'jsonp' depend on output.libraryTarget set to the same value).", - enum: [ - "var", - "module", - "assign", - "this", - "window", - "self", - "global", - "commonjs", - "commonjs2", - "commonjs-module", - "commonjs-static", - "amd", - "amd-require", - "umd", - "umd2", - "jsonp", - "system", - "promise", - "import", - "script", - "node-commonjs" - ] - }, - Filename: { - description: - "Specifies the filename of output files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - oneOf: [ - { - $ref: "#/definitions/FilenameTemplate" - } - ] - }, - SourceMapFilename: { - description: - "Specifies the filename of output files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - oneOf: [ - { - $ref: "#/definitions/FilenameTemplate" - } - ] - }, - FilenameTemplate: { - description: - "Specifies the filename template of output files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - anyOf: [ - { - type: "string", - minLength: 1 - }, - { - instanceof: "Function" - } - ] - }, - FilterItemTypes: { - description: "Filtering value, regexp or function.", - anyOf: [ - { - instanceof: "RegExp" - }, - { - type: "string" - }, - { - instanceof: "Function" - } - ] - }, - FilterTypes: { - description: "Filtering values.", - anyOf: [ - { - type: "array", - items: { - description: "Rule to filter.", - oneOf: [ - { - $ref: "#/definitions/FilterItemTypes" - } - ] - } - }, - { - $ref: "#/definitions/FilterItemTypes" - } - ] - }, - GlobalObject: { - description: - "An expression which is used to address the global object/scope in runtime code.", - type: "string", - minLength: 1 - }, - ImportFunctionName: { - description: - "The name of the native import() function (can be exchanged for a polyfill).", - type: "string" - }, - InfrastructureLogging: { - description: "Options for infrastructure level logging.", - type: "object", - additionalProperties: false, - properties: { - appendOnly: { - description: - "Only appends lines to the output. Avoids updating existing output e. g. for status messages. This option is only used when no custom console is provided.", - type: "boolean" - }, - colors: { - description: - "Enables/Disables colorful output. This option is only used when no custom console is provided.", - type: "boolean" - }, - console: { - description: "Custom console used for logging." - }, - debug: { - description: "Enable debug logging for specific loggers.", - anyOf: [ - { - description: "Enable/Disable debug logging for all loggers.", - type: "boolean" - }, - { - $ref: "#/definitions/FilterTypes" - } - ] - }, - level: { - description: "Log level.", - enum: ["none", "error", "warn", "info", "log", "verbose"] - }, - stream: { - description: - "Stream used for logging output. Defaults to process.stderr. This option is only used when no custom console is provided." - } - } - }, - Library: { - description: - "Make the output files a library, exporting the exports of the entry point.", - anyOf: [ - { - $ref: "#/definitions/LibraryName" - }, - { - $ref: "#/definitions/LibraryOptions" - } - ] - }, - LibraryCustomUmdCommentObject: { - description: - "Set explicit comments for `commonjs`, `commonjs2`, `amd`, and `root`.", - type: "object", - additionalProperties: false, - properties: { - amd: { - description: "Set comment for `amd` section in UMD.", - type: "string" - }, - commonjs: { - description: "Set comment for `commonjs` (exports) section in UMD.", - type: "string" - }, - commonjs2: { - description: - "Set comment for `commonjs2` (module.exports) section in UMD.", - type: "string" - }, - root: { - description: - "Set comment for `root` (global variable) section in UMD.", - type: "string" - } - } - }, - LibraryCustomUmdObject: { - description: - "Description object for all UMD variants of the library name.", - type: "object", - additionalProperties: false, - properties: { - amd: { - description: "Name of the exposed AMD library in the UMD.", - type: "string", - minLength: 1 - }, - commonjs: { - description: "Name of the exposed commonjs export in the UMD.", - type: "string", - minLength: 1 - }, - root: { - description: - "Name of the property exposed globally by a UMD library.", - anyOf: [ - { - type: "array", - items: { - description: - "Part of the name of the property exposed globally by a UMD library.", - type: "string", - minLength: 1 - } - }, - { - type: "string", - minLength: 1 - } - ] - } - } - }, - LibraryExport: { - description: "Specify which export should be exposed as library.", - anyOf: [ - { - type: "array", - items: { - description: - "Part of the export that should be exposed as library.", - type: "string", - minLength: 1 - } - }, - { - type: "string", - minLength: 1 - } - ] - }, - LibraryName: { - description: - "The name of the library (some types allow unnamed libraries too).", - anyOf: [ - { - type: "array", - items: { - description: "A part of the library name.", - type: "string", - minLength: 1 - }, - minItems: 1 - }, - { - type: "string", - minLength: 1 - }, - { - $ref: "#/definitions/LibraryCustomUmdObject" - } - ] - }, - LibraryOptions: { - description: "Options for library.", - type: "object", - additionalProperties: false, - properties: { - auxiliaryComment: { - $ref: "#/definitions/AuxiliaryComment" - }, - export: { - $ref: "#/definitions/LibraryExport" - }, - name: { - $ref: "#/definitions/LibraryName" - }, - type: { - $ref: "#/definitions/LibraryType" - }, - umdNamedDefine: { - $ref: "#/definitions/UmdNamedDefine" - } - }, - required: ["type"] - }, - LibraryType: { - description: - "Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'commonjs-static', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).", - anyOf: [ - { - enum: [ - "var", - "module", - "assign", - "assign-properties", - "this", - "window", - "self", - "global", - "commonjs", - "commonjs2", - "commonjs-module", - "commonjs-static", - "amd", - "amd-require", - "umd", - "umd2", - "jsonp", - "system" - ] - }, - { - type: "string" - } - ] - }, - Mode: { - description: "Enable production optimizations or development hints.", - enum: ["development", "production", "none"] - }, - IgnoreWarnings: { - description: "ignore warnings based on pattern", - type: "array", - items: { - anyOf: [ - { - instanceof: "RegExp" - }, - { - instanceof: "Function" - } - ] - } - }, - ModuleOptions: { - description: - "Options affecting the normal modules (`NormalModuleFactory`).", - type: "object", - additionalProperties: false, - properties: { - defaultRules: { - description: "An array of rules applied by default for modules.", - oneOf: [ - { - $ref: "#/definitions/RuleSetRules" - } - ] - }, - parser: { - $ref: "#/definitions/ParserOptionsByModuleType" - }, - rules: { - description: "An array of rules applied for modules.", - oneOf: [ - { - $ref: "#/definitions/RuleSetRules" - } - ] - } - } - }, - Name: { - description: - "Name of the configuration. Used when loading multiple configurations.", - type: "string" - }, - Node: { - description: "Include polyfills or mocks for various node stuff.", - anyOf: [ - { - enum: [false] - }, - { - $ref: "#/definitions/NodeOptions" - } - ] - }, - NodeOptions: { - description: "Options object for node compatibility features.", - type: "object", - additionalProperties: false, - properties: { - __dirname: { - description: "Include a polyfill for the '__dirname' variable.", - enum: [false, true, "warn-mock", "mock", "eval-only"] - }, - __filename: { - description: "Include a polyfill for the '__filename' variable.", - enum: [false, true, "warn-mock", "mock", "eval-only"] - }, - global: { - description: "Include a polyfill for the 'global' variable.", - enum: [false, true, "warn"] - } - } - }, - Optimization: { - description: "Enables/Disables integrated optimizations.", - type: "object", - additionalProperties: false, - properties: { - chunkIds: { - description: - "Define the algorithm to choose chunk ids (named: readable ids for better debugging, deterministic: numeric hash ids for better long term caching, size: numeric ids focused on minimal initial download size, total-size: numeric ids focused on minimal total download size, false: no algorithm used, as custom one can be provided via plugin).", - enum: ["named", "deterministic"] - }, - minimize: { - description: - "Enable minimizing the output. Uses optimization.minimizer.", - type: "boolean" - }, - minimizer: { - description: "Minimizer(s) to use for minimizing the output.", - type: "array", - items: { - description: "Plugin of type object or instanceof Function.", - anyOf: [ - { - enum: ["..."] - }, - { - $ref: "#/definitions/RspackPluginInstance" - }, - { - $ref: "#/definitions/RspackPluginFunction" - } - ] - } - }, - moduleIds: { - description: - "Define the algorithm to choose module ids (natural: numeric ids in order of usage, named: readable ids for better debugging, hashed: (deprecated) short hashes as ids for better long term caching, deterministic: numeric hash ids for better long term caching, size: numeric ids focused on minimal initial download size, false: no algorithm used, as custom one can be provided via plugin).", - enum: ["named", "deterministic"] - }, - removeAvailableModules: { - description: - "Removes modules from chunks when these modules are already included in all parents.", - type: "boolean" - }, - removeEmptyChunks: { - description: "Remove chunks which are empty.", - type: "boolean" - }, - runtimeChunk: { - $ref: "#/definitions/OptimizationRuntimeChunk" - }, - sideEffects: { - description: - "Skip over modules which contain no side effects when exports are not used (false: disabled, 'flag': only use manually placed side effects flag, true: also analyse source code for side effects).", - anyOf: [ - { - enum: ["flag"] - }, - { - type: "boolean" - } - ] - }, - splitChunks: { - description: - "Optimize duplication and caching by splitting chunks by shared modules and cache group.", - anyOf: [ - { - enum: [false] - }, - { - $ref: "#/definitions/OptimizationSplitChunksOptions" - } - ] - }, - realContentHash: { - description: - "Use real [contenthash] based on final content of the assets.", - type: "boolean" - } - } - }, - OptimizationRuntimeChunk: { - description: - "Create an additional chunk which contains only the rspack runtime and chunk hash maps.", - anyOf: [ - { - enum: ["single", "multiple"] - }, - { - type: "boolean" - }, - { - type: "object", - additionalProperties: false, - properties: { - name: { - description: "The name or name factory for the runtime chunks.", - anyOf: [ - { - type: "string" - }, - { - instanceof: "Function" - } - ] - } - } - } - ] - }, - OptimizationSplitChunksCacheGroup: { - description: - "Options object for describing behavior of a cache group selecting modules that should be cached together.", - type: "object", - additionalProperties: false, - properties: { - chunks: { - description: - 'Select chunks for determining cache group content (defaults to "initial", "initial" and "all" requires adding these chunks to the HTML).', - anyOf: [ - { - enum: ["initial", "async", "all"] - }, - { - instanceof: "Function" - } - ] - }, - minChunks: { - description: - "Minimum number of times a module has to be duplicated until it's considered for splitting.", - type: "number", - minimum: 1 - }, - name: { - description: - "Give chunks for this cache group a name (chunks with equal name are merged).", - anyOf: [ - { - enum: [false] - }, - { - type: "string" - }, - { - instanceof: "Function" - } - ] - }, - priority: { - description: "Priority of this cache group.", - type: "number" - }, - reuseExistingChunk: { - description: - "Try to reuse existing chunk (with name) when it has matching modules.", - type: "boolean" - }, - enforce: { - description: - "ignore splitChunks.minSize, splitChunks.minChunks, splitChunks.maxAsyncRequests and splitChunks.maxInitialRequests options and always create chunks for this cache group.", - type: "boolean" - }, - hidePathInfo: { - type: "boolean" - }, - maxSize: { - type: "number" - }, - test: { - description: "Assign modules to a cache group by module name.", - anyOf: [ - { - instanceof: "RegExp" - } - ] - }, - minSize: { - description: "Minimal size for the created chunks.", - oneOf: [ - { - $ref: "#/definitions/OptimizationSplitChunksSizes" - } - ] - } - } - }, - OptimizationSplitChunksOptions: { - description: "Options object for splitting chunks into smaller chunks.", - type: "object", - additionalProperties: false, - properties: { - fallbackCacheGroup: { - type: "object", - properties: { - maxSize: { - type: "number" - }, - maxInitialSize: { - type: "number" - }, - maxAsyncSize: { - type: "number" - }, - minSize: { - type: "number" - } - } - }, - hidePathInfo: { - type: "boolean" - }, - name: { - description: "The name or name for chunks.", - anyOf: [ - { - type: "string" - } - ] - }, - cacheGroups: { - description: - "Assign modules to a cache group (modules from different cache groups are tried to keep in separate chunks, default categories: 'default', 'defaultVendors').", - type: "object", - additionalProperties: { - description: "Configuration for a cache group.", - anyOf: [ - { - $ref: "#/definitions/OptimizationSplitChunksCacheGroup" - } - ] - } - }, - chunks: { - description: - 'Select chunks for determining shared modules (defaults to "async", "initial" and "all" requires adding these chunks to the HTML).', - anyOf: [ - { - enum: ["initial", "async", "all"] - } - ] - }, - enforceSizeThreshold: { - description: - "Size threshold at which splitting is enforced and other restrictions (minRemainingSize, maxAsyncRequests, maxInitialRequests) are ignored.", - oneOf: [ - { - $ref: "#/definitions/OptimizationSplitChunksSizes" - } - ] - }, - maxAsyncRequests: { - description: - "Maximum number of requests which are accepted for on-demand loading.", - type: "number", - minimum: 1 - }, - maxInitialRequests: { - description: - "Maximum number of initial chunks which are accepted for an entry point.", - type: "number", - minimum: 1 - }, - minChunks: { - description: - "Minimum number of times a module has to be duplicated until it's considered for splitting.", - type: "number", - minimum: 1 - }, - minRemainingSize: { - description: - "Minimal size for the chunks the stay after moving the modules to a new chunk.", - oneOf: [ - { - $ref: "#/definitions/OptimizationSplitChunksSizes" - } - ] - }, - minSize: { - description: "Minimal size for the created chunks.", - oneOf: [ - { - $ref: "#/definitions/OptimizationSplitChunksSizes" - } - ] - }, - maxSize: { - type: "number" - }, - maxInitialSize: { - type: "number" - }, - maxAsyncSize: { - type: "number" - }, - reuseExistingChunk: { - description: - "If the current chunk contains modules already split out from the main bundle, it will be reused instead of a new one being generated. This can affect the resulting file name of the chunk.", - type: "boolean" - } - } - }, - OptimizationSplitChunksSizes: { - description: "Size description for limits.", - anyOf: [ - { - description: "Size of the javascript part of the chunk.", - type: "number", - minimum: 0 - } - ] - }, - Iife: { - description: - "Wrap javascript code into IIFE's to avoid leaking into global scope.", - type: "boolean" - }, - Clean: { - description: "Clears the output build directory", - type: "boolean" - }, - Output: { - description: - "Options affecting the output of the compilation. `output` options tell rspack how to write the compiled files to disk.", - type: "object", - additionalProperties: false, - properties: { - iife: { - $ref: "#/definitions/Iife" - }, - clean: { - $ref: "#/definitions/Clean" - }, - assetModuleFilename: { - $ref: "#/definitions/AssetModuleFilename" - }, - auxiliaryComment: { - oneOf: [ - { - $ref: "#/definitions/AuxiliaryComment" - } - ] - }, - chunkFormat: { - $ref: "#/definitions/ChunkFormat" - }, - chunkLoading: { - $ref: "#/definitions/ChunkLoading" - }, - enabledChunkLoadingTypes: { - $ref: "#/definitions/EnabledChunkLoadingTypes" - }, - chunkFilename: { - $ref: "#/definitions/ChunkFilename" - }, - crossOriginLoading: { - $ref: "#/definitions/CrossOriginLoading" - }, - cssChunkFilename: { - $ref: "#/definitions/CssChunkFilename" - }, - cssFilename: { - $ref: "#/definitions/CssFilename" - }, - hotUpdateChunkFilename: { - $ref: "#/definitions/HotUpdateChunkFilename" - }, - hotUpdateMainFilename: { - $ref: "#/definitions/HotUpdateMainFilename" - }, - enabledWasmLoadingTypes: { - $ref: "#/definitions/EnabledWasmLoadingTypes" - }, - wasmLoading: { - $ref: "#/definitions/WasmLoading" - }, - webassemblyModuleFilename: { - $ref: "#/definitions/WebassemblyModuleFilename" - }, - enabledLibraryTypes: { - $ref: "#/definitions/EnabledLibraryTypes" - }, - filename: { - $ref: "#/definitions/Filename" - }, - globalObject: { - $ref: "#/definitions/GlobalObject" - }, - importFunctionName: { - $ref: "#/definitions/ImportFunctionName" - }, - library: { - $ref: "#/definitions/Library" - }, - libraryExport: { - oneOf: [ - { - $ref: "#/definitions/LibraryExport" - } - ] - }, - libraryTarget: { - oneOf: [ - { - $ref: "#/definitions/LibraryType" - } - ] - }, - module: { - $ref: "#/definitions/OutputModule" - }, - path: { - $ref: "#/definitions/Path" - }, - publicPath: { - $ref: "#/definitions/PublicPath" - }, - strictModuleErrorHandling: { - $ref: "#/definitions/StrictModuleErrorHandling" - }, - umdNamedDefine: { - oneOf: [ - { - $ref: "#/definitions/UmdNamedDefine" - } - ] - }, - uniqueName: { - $ref: "#/definitions/UniqueName" - }, - chunkLoadingGlobal: { - $ref: "#/definitions/ChunkLoadingGlobal" - }, - trustedTypes: { - description: - "Use a Trusted Types policy to create urls for chunks. 'output.uniqueName' is used a default policy name. Passing a string sets a custom policy name.", - anyOf: [ - { - enum: [true] - }, - { - description: - "The name of the Trusted Types policy created by webpack to serve bundle chunks.", - type: "string", - minLength: 1 - }, - { - $ref: "#/definitions/TrustedTypes" - } - ] - }, - sourceMapFilename: { - $ref: "#/definitions/SourceMapFilename" - }, - hashDigest: { - $ref: "#/definitions/HashDigest" - }, - hashDigestLength: { - $ref: "#/definitions/HashDigestLength" - }, - hashFunction: { - $ref: "#/definitions/HashFunction" - }, - hashSalt: { - $ref: "#/definitions/HashSalt" - } - } - }, - OutputModule: { - description: "Output javascript files as module source type.", - type: "boolean" - }, - ParserOptionsByModuleType: { - description: "Specify options for each parser.", - type: "object", - additionalProperties: { - description: "Options for parsing.", - type: "object", - additionalProperties: true - }, - properties: { - asset: { - $ref: "#/definitions/AssetParserOptions" - } - } - }, - Path: { - description: "The output directory as **absolute path** (required).", - type: "string" - }, - Plugins: { - description: "Add additional plugins to the compiler.", - type: "array", - items: { - description: "Plugin of type object or instanceof Function.", - anyOf: [ - { - $ref: "#/definitions/RspackPluginInstance" - }, - { - $ref: "#/definitions/RspackPluginFunction" - } - ] - } - }, - PublicPath: { - description: - "The 'publicPath' specifies the public URL address of the output files when referenced in a browser.", - anyOf: [ - { - enum: ["auto"] - }, - { - $ref: "#/definitions/RawPublicPath" - } - ] - }, - RawPublicPath: { - description: - "The 'publicPath' specifies the public URL address of the output files when referenced in a browser.", - anyOf: [ - { - type: "string" - } - ] - }, - Resolve: { - description: "Options for the resolver.", - oneOf: [ - { - $ref: "#/definitions/ResolveOptions" - } - ] - }, - ResolveAlias: { - description: "Redirect module requests.", - anyOf: [ - { - type: "object", - additionalProperties: { - description: "New request.", - anyOf: [ - { - description: "Multiple alternative requests.", - type: "array", - items: { - description: "One choice of request.", - type: "string", - minLength: 1 - } - }, - { - description: "Ignore request (replace with empty module).", - enum: [false] - }, - { - description: "New request.", - type: "string", - minLength: 1 - } - ] - } - } - ] - }, - ResolveOptions: { - description: "Options object for resolving requests.", - type: "object", - additionalProperties: false, - properties: { - alias: { - $ref: "#/definitions/ResolveAlias" - }, - browserField: { - description: - "Fields in the description file (usually package.json) which are used to redirect requests inside the module.", - type: "boolean" - }, - conditionNames: { - description: "Condition names for exports field entry point.", - type: "array", - items: { - description: "Condition names for exports field entry point.", - type: "string" - } - }, - extensionAlias: { - description: "An object which maps extension to extension aliases.", - type: "object", - additionalProperties: { - description: "Extension alias.", - anyOf: [ - { - description: "Multiple extensions.", - type: "array", - items: { - description: "Aliased extension.", - type: "string", - minLength: 1 - } - }, - { - description: "Aliased extension.", - type: "string", - minLength: 1 - } - ] - } - }, - extensions: { - description: - "Extensions added to the request when trying to find the file.", - type: "array", - items: { - description: - "Extension added to the request when trying to find the file.", - type: "string" - } - }, - fallback: { - description: "Redirect module requests when normal resolving fails.", - oneOf: [ - { - $ref: "#/definitions/ResolveAlias" - } - ] - }, - fullySpecified: { - description: - "Treats the request specified by the user as fully specified, meaning no extensions are added and the mainFiles in directories are not resolved (This doesn't affect requests from mainFields, aliasFields or aliases).", - type: "boolean" - }, - mainFields: { - description: - "Field names from the description file (package.json) which are used to find the default entry point.", - type: "array", - items: { - description: - "Field name from the description file (package.json) which are used to find the default entry point.", - anyOf: [ - { - type: "array", - items: { - description: - "Part of the field path from the description file (package.json) which are used to find the default entry point.", - type: "string", - minLength: 1 - } - }, - { - type: "string", - minLength: 1 - } - ] - } - }, - mainFiles: { - description: - "Filenames used to find the default entry point if there is no description file or main field.", - type: "array", - items: { - description: - "Filename used to find the default entry point if there is no description file or main field.", - type: "string", - minLength: 1 - } - }, - modules: { - description: "Folder names or directory paths where to find modules.", - type: "array", - items: { - description: "Folder name or directory path where to find modules.", - type: "string", - minLength: 1 - } - }, - preferRelative: { - description: - "Prefer to resolve module requests as relative request and fallback to resolving as module.", - type: "boolean" - }, - byDependency: { - description: - 'Extra resolve options per dependency category. Typical categories are "commonjs", "amd", "esm".', - type: "object", - additionalProperties: { - description: "Options object for resolving requests.", - oneOf: [ - { - $ref: "#/definitions/ResolveOptions" - } - ] - } - }, - tsConfigPath: { - description: "Path to tsconfig.json", - type: "string" - }, - exportsFields: { - description: - "Fields in the description file (usually package.json) which are used to redirect requests inside the module.", - type: "array", - items: { - description: - "Field name from the description file (package.json) which are used to find the default entry point.", - type: "string" - } - } - } - }, - RuleSetCondition: { - description: "A condition matcher.", - anyOf: [ - { - instanceof: "RegExp" - }, - { - type: "string" - }, - { - instanceof: "Function" - }, - { - $ref: "#/definitions/RuleSetLogicalConditions" - }, - { - $ref: "#/definitions/RuleSetConditions" - } - ] - }, - RuleSetConditionOrConditions: { - description: "One or multiple rule conditions.", - anyOf: [ - { - $ref: "#/definitions/RuleSetCondition" - }, - { - $ref: "#/definitions/RuleSetConditions" - } - ] - }, - RuleSetConditions: { - description: "A list of rule conditions.", - type: "array", - items: { - description: "A rule condition.", - oneOf: [ - { - $ref: "#/definitions/RuleSetCondition" - } - ] - } - }, - RuleSetLoader: { - description: "A loader request.", - type: "string", - minLength: 1 - }, - RuleSetLoaderOptions: { - description: "Options passed to a loader.", - anyOf: [ - { - type: "string" - }, - { - type: "object" - } - ] - }, - RuleSetLogicalConditions: { - description: "Logic operators used in a condition matcher.", - type: "object", - additionalProperties: false, - properties: { - and: { - description: "Logical AND.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditions" - } - ] - }, - not: { - description: "Logical NOT.", - oneOf: [ - { - $ref: "#/definitions/RuleSetCondition" - } - ] - }, - or: { - description: "Logical OR.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditions" - } - ] - } - } - }, - RuleSetRule: { - description: - "A rule description with conditions and effects for modules.", - type: "object", - additionalProperties: false, - properties: { - enforce: { - description: "Enforce this rule as pre or post step.", - enum: ["pre", "post"] - }, - exclude: { - description: "Shortcut for resource.exclude.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - generator: { - description: "The options for the module generator.", - type: "object" - }, - include: { - description: "Shortcut for resource.include.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - issuer: { - description: - "Match the issuer of the module (The module pointing to this module).", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - dependency: { - description: "Match dependency type.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - descriptionData: { - description: - "Match values of properties in the description file (usually package.json).", - type: "object", - additionalProperties: { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - }, - oneOf: { - description: "Only execute the first matching rule in this array.", - type: "array", - items: { - description: "A rule.", - oneOf: [ - { - $ref: "#/definitions/RuleSetRule" - } - ] - } - }, - parser: { - description: "Options for parsing.", - type: "object", - additionalProperties: true - }, - resolve: { - description: "Options for the resolver.", - type: "object", - oneOf: [ - { - $ref: "#/definitions/ResolveOptions" - } - ] - }, - resource: { - description: "Match the resource path of the module.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - resourceFragment: { - description: "Match the resource fragment of the module.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - resourceQuery: { - description: "Match the resource query of the module.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - mimetype: { - description: "Match module mimetype when load from Data URI.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - loader: { - description: "Shortcut for use.loader.", - oneOf: [ - { - $ref: "#/definitions/RuleSetLoader" - } - ] - }, - options: { - description: "Shortcut for use.options.", - oneOf: [ - { - $ref: "#/definitions/RuleSetLoaderOptions" - } - ] - }, - scheme: { - description: "Match module scheme.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - rules: { - description: - "Match and execute these rules when this rule is matched.", - type: "array", - items: { - description: "A rule.", - oneOf: [ - { - $ref: "#/definitions/RuleSetRule" - } - ] - } - }, - sideEffects: { - description: "Flags a module as with or without side effects.", - type: "boolean" - }, - test: { - description: "Shortcut for resource.test.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - type: { - description: "Module type to use for the module.", - type: "string" - }, - use: { - description: "Modifiers applied to the module when rule is matched.", - oneOf: [ - { - $ref: "#/definitions/RuleSetUse" - } - ] - } - } - }, - RuleSetRules: { - description: "A list of rules.", - type: "array", - items: { - description: "A rule.", - anyOf: [ - { - enum: ["..."] - }, - { - $ref: "#/definitions/RuleSetRule" - } - ] - } - }, - RuleSetUse: { - description: "A list of descriptions of loaders applied.", - anyOf: [ - { - type: "array", - items: { - description: "An use item.", - oneOf: [ - { - $ref: "#/definitions/RuleSetUseItem" - } - ] - } - }, - { - $ref: "#/definitions/RuleSetUseItem" - } - ] - }, - RuleSetUseItem: { - description: "A description of an applied loader.", - anyOf: [ - { - type: "object", - additionalProperties: false, - properties: { - loader: { - description: "Loader name.", - oneOf: [ - { - $ref: "#/definitions/RuleSetLoader" - } - ] - }, - options: { - description: "Loader options.", - oneOf: [ - { - $ref: "#/definitions/RuleSetLoaderOptions" - } - ] - } - } - }, - { - $ref: "#/definitions/RuleSetLoader" - } - ] - }, - SnapshotOptions: { - description: - "Options affecting how file system snapshots are created and validated.", - type: "object", - additionalProperties: false, - properties: { - module: { - description: - "Options for snapshotting dependencies of modules to determine if they need to be built again.", - type: "object", - additionalProperties: false, - properties: { - hash: { - description: - "Use hashes of the content of the files/directories to determine invalidation.", - type: "boolean" - }, - timestamp: { - description: - "Use timestamps of the files/directories to determine invalidation.", - type: "boolean" - } - } - }, - resolve: { - description: - "Options for snapshotting dependencies of request resolving to determine if requests need to be re-resolved.", - type: "object", - additionalProperties: false, - properties: { - hash: { - description: - "Use hashes of the content of the files/directories to determine invalidation.", - type: "boolean" - }, - timestamp: { - description: - "Use timestamps of the files/directories to determine invalidation.", - type: "boolean" - } - } - } - } - }, - StatsOptions: { - description: "Stats options object.", - type: "object", - additionalProperties: true, - properties: { - all: { - description: - "Fallback value for stats options when an option is not defined (has precedence over local rspack defaults).", - type: "boolean" - }, - assets: { - description: "Add assets information.", - type: "boolean" - }, - chunkGroups: { - description: - "Display all chunk groups with the corresponding bundles.", - type: "boolean" - }, - chunks: { - description: "Add chunk information.", - type: "boolean" - }, - colors: { - description: "Enables/Disables colorful output.", - type: "boolean" - }, - entrypoints: { - description: - "Display the entry points with the corresponding bundles.", - anyOf: [ - { - enum: ["auto"] - }, - { - type: "boolean" - } - ] - }, - errors: { - description: "Add errors.", - type: "boolean" - }, - errorsCount: { - description: "Add errors count.", - type: "boolean" - }, - hash: { - description: "Add the hash of the compilation.", - type: "boolean" - }, - modules: { - description: "Add built modules information.", - type: "boolean" - }, - preset: { - description: "Preset for the default values.", - anyOf: [ - { - type: "boolean" - }, - { - type: "string" - } - ] - }, - publicPath: { - description: "Add public path information.", - type: "boolean" - }, - reasons: { - description: - "Add information about the reasons why modules are included.", - type: "boolean" - }, - warnings: { - description: "Add warnings.", - type: "boolean" - }, - warningsCount: { - description: "Add warnings count.", - type: "boolean" - }, - outputPath: { - description: "Add output path information.", - type: "boolean" - }, - chunkModules: { - description: "Add built modules information to chunk information.", - type: "boolean" - }, - chunkRelations: { - description: - "Add information about parent, children and sibling chunks to chunk information.", - type: "boolean" - }, - timings: { - description: "Add timing information.", - type: "boolean" - }, - builtAt: { - description: "Add built at time information.", - type: "boolean" - }, - nestedModules: { - description: - "Add information about modules nested in other modules (like with module concatenation).", - type: "boolean" - } - } - }, - StatsValue: { - description: "Stats options object or preset name.", - anyOf: [ - { - enum: ["none", "errors-only", "errors-warnings", "normal", "verbose"] - }, - { - type: "boolean" - }, - { - $ref: "#/definitions/StatsOptions" - } - ] - }, - StrictModuleErrorHandling: { - description: - "Handles error in module loading correctly at a performance cost. This will handle module error compatible with the EcmaScript Modules spec.", - type: "boolean" - }, - Target: { - description: - "Environment to build for. An array of environments to build for all of them when possible.", - anyOf: [ - { - type: "array", - items: { - description: "Environment to build for.", - type: "string", - minLength: 1 - }, - minItems: 1 - }, - { - enum: [false] - }, - { - type: "string", - minLength: 1 - } - ] - }, - TrustedTypes: { - description: "Use a Trusted Types policy to create urls for chunks.", - type: "object", - additionalProperties: false, - properties: { - policyName: { - description: - "The name of the Trusted Types policy created by webpack to serve bundle chunks.", - type: "string", - minLength: 1 - } - } - }, - UmdNamedDefine: { - description: - "If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.", - type: "boolean" - }, - UniqueName: { - description: - "A unique name of the rspack build to avoid multiple rspack runtimes to conflict when using globals.", - type: "string", - minLength: 1 - }, - ChunkLoadingGlobal: { - description: "The global variable used by rspack for loading of chunks.", - type: "string", - minLength: 1 - }, - Watch: { - description: "Enter watch mode, which rebuilds on file change.", - type: "boolean" - }, - WatchOptions: { - description: "Options for the watcher.", - type: "object", - additionalProperties: false, - properties: { - aggregateTimeout: { - description: - "Delay the rebuilt after the first change. Value is a time in ms.", - type: "number" - }, - followSymlinks: { - description: - "Resolve symlinks and watch symlink and real file. This is usually not needed as rspack already resolves symlinks ('resolve.symlinks').", - type: "boolean" - }, - ignored: { - description: - "Ignore some files from watching (glob pattern or regexp).", - anyOf: [ - { - type: "array", - items: { - description: - "A glob pattern for files that should be ignored from watching.", - type: "string", - minLength: 1 - } - }, - { - instanceof: "RegExp" - }, - { - description: - "A single glob pattern for files that should be ignored from watching.", - type: "string", - minLength: 1 - } - ] - }, - poll: { - description: "Enable polling mode for watching.", - anyOf: [ - { - description: "`number`: use polling with specified interval.", - type: "number" - }, - { - description: "`true`: use polling.", - type: "boolean" - } - ] - }, - stdin: { - description: "Stop watching when stdin stream has ended.", - type: "boolean" - } - } - }, - RspackPluginFunction: { - description: "Function acting as plugin.", - instanceof: "Function" - }, - RspackPluginInstance: { - description: "Plugin instance.", - type: "object", - additionalProperties: true, - properties: { - apply: { - description: "The run point of the plugin, required method.", - instanceof: "Function" - } - }, - required: ["apply"] - } - }, - title: "RspackOptions", - description: "Options object as provided by the user.", - type: "object", - additionalProperties: false, - properties: { - cache: { - $ref: "#/definitions/CacheOptions" - }, - context: { - $ref: "#/definitions/Context" - }, - dependencies: { - $ref: "#/definitions/Dependencies" - }, - devServer: { - $ref: "#/definitions/DevServer" - }, - devtool: { - $ref: "#/definitions/DevTool" - }, - entry: { - $ref: "#/definitions/Entry" - }, - experiments: { - $ref: "#/definitions/Experiments" - }, - externals: { - $ref: "#/definitions/Externals" - }, - externalsType: { - $ref: "#/definitions/ExternalsType" - }, - externalsPresets: { - $ref: "#/definitions/ExternalsPresets" - }, - infrastructureLogging: { - $ref: "#/definitions/InfrastructureLogging" - }, - mode: { - $ref: "#/definitions/Mode" - }, - module: { - $ref: "#/definitions/ModuleOptions" - }, - name: { - $ref: "#/definitions/Name" - }, - node: { - $ref: "#/definitions/Node" - }, - optimization: { - $ref: "#/definitions/Optimization" - }, - output: { - $ref: "#/definitions/Output" - }, - plugins: { - $ref: "#/definitions/Plugins" - }, - resolve: { - $ref: "#/definitions/Resolve" - }, - snapshot: { - $ref: "#/definitions/SnapshotOptions" - }, - stats: { - $ref: "#/definitions/StatsValue" - }, - target: { - $ref: "#/definitions/Target" - }, - watch: { - $ref: "#/definitions/Watch" - }, - watchOptions: { - $ref: "#/definitions/WatchOptions" - }, - builtins: { - description: "Builtins features in rspack", - type: "object", - additionalProperties: true - }, - ignoreWarnings: { - $ref: "#/definitions/IgnoreWarnings" - } - } -}; diff --git a/packages/rspack/src/config/target.js b/packages/rspack/src/config/target.js index 107852deccbf..d604e7fd3c03 100644 --- a/packages/rspack/src/config/target.js +++ b/packages/rspack/src/config/target.js @@ -14,6 +14,10 @@ const getBrowserslistTargetHandler = memoize(() => require("./browserslistTargetHandler") ); +/** + * @param {string} context the context directory + * @returns {import("..").Target} target properties + */ const getDefaultTarget = context => { // TODO: align with webpack // const browsers = getBrowserslistTargetHandler().load(null, context); diff --git a/packages/rspack/src/config/types.ts b/packages/rspack/src/config/types.ts deleted file mode 100644 index d357028993f3..000000000000 --- a/packages/rspack/src/config/types.ts +++ /dev/null @@ -1,708 +0,0 @@ -/** - * The following code is modified based on - * https://github.com/webpack/webpack/blob/4b4ca3b/declarations/WebpackOptions.d.ts - * - * MIT Licensed - * Author Tobias Koppers @sokra - * Copyright (c) JS Foundation and other contributors - * https://github.com/webpack/webpack/blob/main/LICENSE - */ - -import watchpack from "watchpack"; -import { Compiler } from "../Compiler"; -import * as oldBuiltins from "../builtin-plugin"; -import { Compilation } from ".."; -import type * as webpackDevServer from "webpack-dev-server"; -import type { Options as RspackOptions } from "./zod/_rewrite"; -import type { OptimizationConfig as Optimization } from "./zod/optimization"; -import type { RawFuncUseCtx } from "@rspack/binding"; -import { RspackBuiltinPlugin } from "../builtin-plugin"; -export type { RspackOptions, Optimization }; - -export type { - LoaderContext, - LoaderDefinitionFunction, - LoaderDefinition -} from "./adapterRuleUse"; - -export type Configuration = RspackOptions; - -export interface RspackOptionsNormalized { - name?: Name; - dependencies?: Dependencies; - context?: Context; - mode?: Mode; - entry: EntryNormalized; - output: OutputNormalized; - resolve: Resolve; - resolveLoader: Resolve; - module: ModuleOptionsNormalized; - target?: Target; - externals?: Externals; - externalsType?: ExternalsType; - externalsPresets: ExternalsPresets; - infrastructureLogging: InfrastructureLogging; - devtool?: DevTool; - node: Node; - snapshot: SnapshotOptions; - cache?: CacheOptions; - stats: StatsValue; - optimization: Optimization; - plugins: Plugins; - experiments: ExperimentsNormalized; - watch?: Watch; - watchOptions: WatchOptions; - devServer?: DevServer; - ignoreWarnings?: IgnoreWarningsNormalized; - profile?: Profile; - builtins: Builtins; -} - -///// Name ///// -export type Name = string; - -///// Dependencies ///// -export type Dependencies = Name[]; - -///// Context ///// -export type Context = string; - -///// Mode ///// -export type Mode = "development" | "production" | "none"; - -///// Entry ///// -export type Entry = EntryStatic; -export type EntryStatic = EntryObject | EntryUnnamed; -export type EntryUnnamed = EntryItem; -export type EntryRuntime = false | string; -export type EntryItem = string[] | string; -export type ChunkLoading = false | ChunkLoadingType; -export type ChunkLoadingType = - | ("jsonp" | "import-scripts" | "require" | "async-node" | "import") - | string; -export interface EntryObject { - [k: string]: EntryItem | EntryDescription; -} -export type EntryFilename = FilenameTemplate; -export interface EntryDescription { - import: EntryItem; - runtime?: EntryRuntime; - chunkLoading?: ChunkLoading; - asyncChunks?: boolean; - publicPath?: PublicPath; - baseUri?: string; - filename?: EntryFilename; -} - -export type EntryNormalized = EntryStaticNormalized; -export interface EntryStaticNormalized { - [k: string]: EntryDescriptionNormalized; -} -export interface EntryDescriptionNormalized { - import?: string[]; - runtime?: EntryRuntime; - chunkLoading?: ChunkLoading; - asyncChunks?: boolean; - publicPath?: PublicPath; - baseUri?: string; - filename?: EntryFilename; -} - -///// Output ///// -export interface Output { - path?: Path; - publicPath?: PublicPath; - clean?: Clean; - filename?: Filename; - chunkFilename?: ChunkFilename; - crossOriginLoading?: CrossOriginLoading; - cssFilename?: CssFilename; - cssChunkFilename?: CssChunkFilename; - assetModuleFilename?: AssetModuleFilename; - hotUpdateMainFilename?: HotUpdateMainFilename; - hotUpdateChunkFilename?: HotUpdateChunkFilename; - uniqueName?: UniqueName; - chunkLoadingGlobal?: ChunkLoadingGlobal; - enabledLibraryTypes?: EnabledLibraryTypes; - libraryExport?: LibraryExport; - libraryTarget?: LibraryType; - auxiliaryComment?: AuxiliaryComment; - umdNamedDefine?: UmdNamedDefine; - module?: OutputModule; - library?: Library; - strictModuleErrorHandling?: StrictModuleErrorHandling; - globalObject?: GlobalObject; - importFunctionName?: ImportFunctionName; - iife?: Iife; - wasmLoading?: WasmLoading; - enabledWasmLoadingTypes?: EnabledWasmLoadingTypes; - webassemblyModuleFilename?: WebassemblyModuleFilename; - chunkFormat?: string | false; - chunkLoading?: string | false; - enabledChunkLoadingTypes?: string[]; - trustedTypes?: true | string | TrustedTypes; - sourceMapFilename?: SourceMapFilename; - hashDigest?: HashDigest; - hashDigestLength?: HashDigestLength; - hashFunction?: HashFunction; - hashSalt?: HashSalt; - asyncChunks?: boolean; - workerChunkLoading?: ChunkLoading; - workerWasmLoading?: WasmLoading; - workerPublicPath?: WorkerPublicPath; -} -export type Path = string; -export type PublicPath = "auto" | RawPublicPath; -export type RawPublicPath = string; -export type AssetModuleFilename = string; -export type WebassemblyModuleFilename = string; -export type Filename = FilenameTemplate; -export type ChunkFilename = FilenameTemplate; -export type CrossOriginLoading = false | "anonymous" | "use-credentials"; -export type CssFilename = FilenameTemplate; -export type CssChunkFilename = FilenameTemplate; -export type HotUpdateChunkFilename = FilenameTemplate; -export type HotUpdateMainFilename = FilenameTemplate; -export type FilenameTemplate = string; -export type UniqueName = string; -export type ChunkLoadingGlobal = string; -export type Library = LibraryName | LibraryOptions; -export type StrictModuleErrorHandling = boolean; -export type OutputModule = boolean; -export type SourceMapFilename = FilenameTemplate; -export type Iife = boolean; -export type Clean = boolean; -export interface LibraryCustomUmdCommentObject { - amd?: string; - commonjs?: string; - commonjs2?: string; - root?: string; -} -export interface LibraryOptions { - auxiliaryComment?: AuxiliaryComment; - export?: LibraryExport; - name?: LibraryName; - type: LibraryType; - umdNamedDefine?: UmdNamedDefine; -} -export type LibraryName = string | string[] | LibraryCustomUmdObject; -export interface LibraryCustomUmdObject { - amd?: string; - commonjs?: string; - root?: string | string[]; -} -export type LibraryExport = string[] | string; -export type LibraryType = - | ( - | "var" - | "module" - | "assign" - | "assign-properties" - | "this" - | "window" - | "self" - | "global" - | "commonjs" - | "commonjs2" - | "commonjs-module" - | "commonjs-static" - | "amd" - | "amd-require" - | "umd" - | "umd2" - | "jsonp" - | "system" - ) - | string; -export type AuxiliaryComment = string | LibraryCustomUmdCommentObject; -export type UmdNamedDefine = boolean; -export type EnabledLibraryTypes = LibraryType[]; -export type GlobalObject = string; -export type ImportFunctionName = string; -export type WasmLoading = false | WasmLoadingType; -export type WasmLoadingType = - | ("fetch-streaming" | "fetch" | "async-node") - | string; -export type EnabledWasmLoadingTypes = WasmLoadingType[]; -export interface TrustedTypes { - policyName?: string; -} -export type HashDigest = string; -export type HashDigestLength = number; -export type HashFunction = string; -export type HashSalt = string; -export type WorkerPublicPath = string; -export interface OutputNormalized { - path?: Path; - clean?: Clean; - publicPath?: PublicPath; - filename?: Filename; - chunkFilename?: ChunkFilename; - crossOriginLoading?: CrossOriginLoading; - cssFilename?: CssFilename; - cssChunkFilename?: CssChunkFilename; - hotUpdateMainFilename?: HotUpdateMainFilename; - hotUpdateChunkFilename?: HotUpdateChunkFilename; - assetModuleFilename?: AssetModuleFilename; - uniqueName?: UniqueName; - chunkLoadingGlobal?: ChunkLoadingGlobal; - enabledLibraryTypes?: EnabledLibraryTypes; - library?: LibraryOptions; - module?: OutputModule; - strictModuleErrorHandling?: StrictModuleErrorHandling; - globalObject?: GlobalObject; - importFunctionName?: ImportFunctionName; - iife?: Iife; - wasmLoading?: WasmLoading; - enabledWasmLoadingTypes?: EnabledWasmLoadingTypes; - webassemblyModuleFilename?: WebassemblyModuleFilename; - chunkFormat?: string | false; - chunkLoading?: string | false; - enabledChunkLoadingTypes?: string[]; - trustedTypes?: TrustedTypes; - sourceMapFilename?: SourceMapFilename; - hashDigest?: HashDigest; - hashDigestLength?: HashDigestLength; - hashFunction?: HashFunction; - hashSalt?: HashSalt; - asyncChunks?: boolean; - workerChunkLoading?: ChunkLoading; - workerWasmLoading?: WasmLoading; - workerPublicPath?: WorkerPublicPath; -} - -///// Resolve ///// -export type Resolve = ResolveOptions; -export interface ResolveOptions { - alias?: ResolveAlias; - /** - * This is `aliasField: ["browser"]` in webpack, because no one - * uses aliasField other than "browser". ---@bvanjoi - */ - browserField?: boolean; - conditionNames?: string[]; - extensions?: string[]; - fallback?: ResolveAlias; - mainFields?: string[]; - mainFiles?: string[]; - modules?: string[]; - preferRelative?: boolean; - tsConfigPath?: string; - fullySpecified?: boolean; - exportsFields?: string[]; - extensionAlias?: Record; - byDependency?: { - [k: string]: ResolveOptions; - }; -} -export type ResolveAlias = { - [k: string]: false | string | Array; -}; - -///// Module ///// -export interface ModuleOptions { - defaultRules?: RuleSetRules; - rules?: RuleSetRules; - parser?: ParserOptionsByModuleType; - generator?: GeneratorOptionsByModuleType; -} -export type RuleSetRules = ("..." | RuleSetRule)[]; -export interface RuleSetRule { - test?: RuleSetCondition; - exclude?: RuleSetCondition; - include?: RuleSetCondition; - issuer?: RuleSetCondition; - dependency?: RuleSetCondition; - resource?: RuleSetCondition; - resourceFragment?: RuleSetCondition; - resourceQuery?: RuleSetCondition; - scheme?: RuleSetCondition; - mimetype?: RuleSetCondition; - descriptionData?: { - [k: string]: RuleSetCondition; - }; - oneOf?: RuleSetRule[]; - rules?: RuleSetRule[]; - type?: string; - loader?: RuleSetLoader; - options?: RuleSetLoaderOptions; - use?: RuleSetUse; - parser?: { - [k: string]: any; - }; - generator?: { - [k: string]: any; - }; - resolve?: ResolveOptions; - sideEffects?: boolean; - /** - * Specifies the category of the loader. No value means normal loader. - */ - enforce?: "pre" | "post"; -} -export type RuleSetCondition = - | RegExp - | string - | RuleSetConditions - | RuleSetLogicalConditions - | ((value: string) => boolean); -export type RuleSetConditions = RuleSetCondition[]; -export interface RuleSetLogicalConditions { - and?: RuleSetConditions; - or?: RuleSetConditions; - not?: RuleSetConditions; -} -export type RuleSetUse = - | RuleSetUseItem[] - | RuleSetUseItem - | ((funcUseCtx: RawFuncUseCtx) => RuleSetUseItem[]); -export type RuleSetUseItem = RuleSetLoaderWithOptions | RuleSetLoader; -export type RuleSetLoader = string; -export type RuleSetLoaderWithOptions = { - ident?: string; - loader: RuleSetLoader; - options?: RuleSetLoaderOptions; -}; -export type RuleSetLoaderOptions = - | string - | { - [k: string]: any; - }; -export type ParserOptionsByModuleType = ParserOptionsByModuleTypeKnown; -export interface ParserOptionsByModuleTypeKnown { - asset?: AssetParserOptions; -} -export interface AssetParserOptions { - dataUrlCondition?: AssetParserDataUrl; -} -export type AssetParserDataUrl = AssetParserDataUrlOptions; -export interface AssetParserDataUrlOptions { - maxSize?: number; -} -export type GeneratorOptionsByModuleType = GeneratorOptionsByModuleTypeKnown; -export interface GeneratorOptionsByModuleTypeKnown { - asset?: AssetGeneratorOptions; - "asset/inline"?: AssetInlineGeneratorOptions; - "asset/resource"?: AssetResourceGeneratorOptions; -} -export type AssetGeneratorOptions = AssetInlineGeneratorOptions & - AssetResourceGeneratorOptions; -export interface AssetInlineGeneratorOptions { - dataUrl?: AssetGeneratorDataUrl; -} -export type AssetGeneratorDataUrl = AssetGeneratorDataUrlOptions; -export interface AssetGeneratorDataUrlOptions { - encoding?: false | "base64"; - mimetype?: string; -} -export interface AssetResourceGeneratorOptions { - filename?: FilenameTemplate; - publicPath?: RawPublicPath; -} - -export interface ModuleOptionsNormalized { - defaultRules?: RuleSetRules; - rules: RuleSetRules; - parser: ParserOptionsByModuleType; - generator: GeneratorOptionsByModuleType; -} - -export type AvailableTarget = - | "async-node" - | "node" - | `node${number}` - | `node${number}.${number}` - | `async-node${number}.${number}` - | `async-node${number}` - | "electron-main" - | `electron${number}-main` - | `electron${number}.${number}-main` - | "electron-renderer" - | `electron${number}-renderer` - | `electron${number}.${number}-renderer` - | "electron-preload" - | `electron${number}-preload` - | `electron${number}.${number}-preload` - | "web" - | "webworker" - | `es${number}` - | "browserslist"; - -///// Target ///// -export type Target = false | AvailableTarget[] | AvailableTarget; - -///// Externals ///// -export type Externals = ExternalItem[] | ExternalItem; -export type ExternalItem = - | string - | RegExp - | ExternalItemObjectUnknown - | ( - | (( - data: ExternalItemFunctionData, - callback: ( - err?: Error, - result?: ExternalItemValue, - type?: ExternalsType - ) => void - ) => void) - | ((data: ExternalItemFunctionData) => Promise) - ); - -export interface ExternalItemFunctionData { - /** - * The directory in which the request is placed. - */ - context?: string; - /** - * Contextual information. - */ - // contextInfo?: import("../lib/ModuleFactory").ModuleFactoryCreateDataContextInfo; - /** - * The category of the referencing dependencies. - */ - dependencyType?: string; - /** - * Get a resolve function with the current resolver options. - */ - // getResolve?: ( - // options?: ResolveOptions - // ) => - // | (( - // context: string, - // request: string, - // callback: (err?: Error, result?: string) => void - // ) => void) - // | ((context: string, request: string) => Promise); - /** - * The request as written by the user in the require/import expression/statement. - */ - request?: string; -} -export interface ExternalItemObjectUnknown { - [k: string]: ExternalItemValue; -} -export type ExternalItemValue = string | boolean; - -///// ExternalsType ///// -export type ExternalsType = - | "var" - | "module" - | "assign" - | "this" - | "window" - | "self" - | "global" - | "commonjs" - | "commonjs2" - | "commonjs-module" - | "commonjs-static" - | "amd" - | "amd-require" - | "umd" - | "umd2" - | "jsonp" - | "system" - | "promise" - | "import" - | "script" - | "node-commonjs"; - -///// ExternalsPresets ///// -export interface ExternalsPresets { - node?: boolean; - web?: boolean; - electron?: boolean; - electronMain?: boolean; - electronPreload?: boolean; - electronRenderer?: boolean; -} - -///// InfrastructureLogging ///// -export interface InfrastructureLogging { - appendOnly?: boolean; - colors?: boolean; - console?: Console; - debug?: boolean | FilterTypes; - level?: "none" | "error" | "warn" | "info" | "log" | "verbose"; - stream?: NodeJS.WritableStream; -} -export type FilterTypes = FilterItemTypes[] | FilterItemTypes; -export type FilterItemTypes = RegExp | string | ((value: string) => boolean); - -///// DevTool ///// -export type DevTool = - | false - | "cheap-source-map" - | "cheap-module-source-map" - | "source-map" - | "inline-cheap-source-map" - | "inline-cheap-module-source-map" - | "inline-source-map" - | "inline-nosources-cheap-module-source-map" - | "inline-nosources-source-map" - | "nosources-cheap-source-map" - | "nosources-cheap-module-source-map" - | "nosources-source-map" - | "hidden-nosources-cheap-source-map" - | "hidden-nosources-cheap-module-source-map" - | "hidden-nosources-source-map" - | "hidden-cheap-source-map" - | "hidden-cheap-module-source-map" - | "hidden-source-map" - | "eval-cheap-source-map" - | "eval-cheap-module-source-map" - | "eval-source-map" - | "eval-nosources-cheap-source-map" - | "eval-nosources-cheap-module-source-map" - | "eval-nosources-source-map"; - -///// Node ///// -export type Node = false | NodeOptions; - -export interface NodeOptions { - __dirname?: false | true | "warn-mock" | "mock" | "eval-only"; - __filename?: false | true | "warn-mock" | "mock" | "eval-only"; - global?: boolean | "warn"; -} - -///// Snapshot ///// -export interface SnapshotOptions { - module?: { - hash?: boolean; - timestamp?: boolean; - }; - resolve?: { - hash?: boolean; - timestamp?: boolean; - }; -} - -///// Cache ///// -// TODO: align with webpack -export type CacheOptions = true | false; - -///// Stats ///// -export type StatsValue = - | ("none" | "errors-only" | "errors-warnings" | "normal" | "verbose") - | boolean - | StatsOptions; -export interface StatsOptions { - all?: boolean; - preset?: "normal" | "none" | "verbose" | "errors-only" | "errors-warnings"; - assets?: boolean; - chunks?: boolean; - modules?: boolean; - entrypoints?: boolean; - chunkGroups?: boolean; - warnings?: boolean; - warningsCount?: boolean; - errors?: boolean; - errorsCount?: boolean; - colors?: boolean; - hash?: boolean; - version?: boolean; - reasons?: boolean; - publicPath?: boolean; - outputPath?: boolean; - chunkModules?: boolean; - chunkRelations?: boolean; - timings?: boolean; - builtAt?: boolean; - moduleAssets?: boolean; - modulesSpace?: number; - nestedModules?: boolean; - source?: boolean; - logging?: ("none" | "error" | "warn" | "info" | "log" | "verbose") | boolean; - loggingDebug?: boolean | FilterTypes; - loggingTrace?: boolean; -} - -export type OptimizationRuntimeChunk = - | ("single" | "multiple") - | boolean - | { - name?: string | Function; - }; -export type OptimizationRuntimeChunkNormalized = - | false - | { - name: (...args: any[]) => string | undefined; - }; - -///// Plugins ///// -export type Plugins = ( - | RspackPluginInstance - | RspackPluginFunction - | RspackBuiltinPlugin -)[]; -export interface RspackPluginInstance { - apply: (compiler: Compiler) => void; - [k: string]: any; -} -export type RspackPluginFunction = (this: Compiler, compiler: Compiler) => void; - -///// Experiments ///// -export interface Experiments { - lazyCompilation?: boolean; - incrementalRebuild?: boolean | IncrementalRebuildOptions; - asyncWebAssembly?: boolean; - outputModule?: boolean; - newSplitChunks?: boolean; - css?: boolean; -} -export interface IncrementalRebuildOptions { - make?: boolean; - emitAsset?: boolean; -} -export interface RspackFutureOptions { - newResolver?: boolean; -} -// TODO: discuss with webpack, should move to css generator options -// export interface CssExperimentOptions { -// exportsOnly?: boolean; -// localsConvention?: -// | "asIs" -// | "camelCase" -// | "camelCaseOnly" -// | "dashes" -// | "dashesOnly"; -// localIdentName?: string; -// } -export interface ExperimentsNormalized { - lazyCompilation?: boolean; - incrementalRebuild?: false | IncrementalRebuildOptions; - asyncWebAssembly?: boolean; - outputModule?: boolean; - newSplitChunks?: boolean; - css?: boolean; - futureDefaults?: boolean; - rspackFuture?: RspackFutureOptions; -} - -///// Watch ///// -export type Watch = boolean; - -///// WatchOptions ///// -export type WatchOptions = watchpack.WatchOptions; - -///// DevServer ///// -export interface DevServer extends webpackDevServer.Configuration { - hot?: boolean; -} - -///// IgnoreWarnings ///// -export type IgnoreWarningsPattern = ( - | RegExp - | ((warning: Error, compilation: Compilation) => boolean) -)[]; -export type IgnoreWarningsNormalized = (( - warning: Error, - compilation: Compilation -) => boolean)[]; - -///// Profile ///// -export type Profile = boolean; - -///// Builtins ///// -export type Builtins = oldBuiltins.Builtins; diff --git a/packages/rspack/src/config/zod.ts b/packages/rspack/src/config/zod.ts new file mode 100644 index 000000000000..a292af730b25 --- /dev/null +++ b/packages/rspack/src/config/zod.ts @@ -0,0 +1,1052 @@ +import { RawFuncUseCtx } from "@rspack/binding"; +import { z } from "zod"; +import { Compilation, Compiler } from ".."; +import type * as oldBuiltins from "../builtin-plugin"; +import type * as webpackDevServer from "webpack-dev-server"; + +//#region Name +const name = z.string(); +export type Name = z.infer; +//#endregion + +//#region Dependencies +const dependencies = z.array(name); +export type Dependencies = z.infer; +//#endregion + +//#region Context +const context = z.string(); +export type Context = z.infer; +//#endregion + +//#region Mode +const mode = z.enum(["development", "production", "none"]); +export type Mode = z.infer; +//#endregion + +//#region Entry +const rawPublicPath = z.string(); +export type RawPublicPath = z.infer; + +const publicPath = z.literal("auto").or(rawPublicPath); +export type PublicPath = z.infer; + +const baseUri = z.string(); +export type BaseUri = z.infer; + +const chunkLoadingType = z + .enum(["jsonp", "import-scripts", "require", "async-node", "import"]) + .or(z.string()); +export type ChunkLoadingType = z.infer; + +const chunkLoading = z.literal(false).or(chunkLoadingType); +export type ChunkLoading = z.infer; + +const asyncChunks = z.boolean(); +export type AsyncChunks = z.infer; + +const wasmLoadingType = z + .enum(["fetch-streaming", "fetch", "async-node"]) + .or(z.string()); +export type WasmLoadingType = z.infer; + +const wasmLoading = z.literal(false).or(wasmLoadingType); +export type WasmLoading = z.infer; + +const filenameTemplate = z.string(); +export type FilenameTemplate = z.infer; + +const filename = filenameTemplate; +export type Filename = z.infer; + +const entryFilename = filenameTemplate; +export type EntryFilename = z.infer; + +const entryRuntime = z.literal(false).or(z.string()); +export type EntryRuntime = z.infer; + +const entryItem = z.string().or(z.array(z.string())); +export type EntryItem = z.infer; + +const entryDescription = z.strictObject({ + import: entryItem, + runtime: entryRuntime.optional(), + publicPath: publicPath.optional(), + baseUri: baseUri.optional(), + chunkLoading: chunkLoading.optional(), + asyncChunks: asyncChunks.optional(), + wasmLoading: wasmLoading.optional(), + filename: entryFilename.optional() +}); +export type EntryDescription = z.infer; + +const entryUnnamed = entryItem; +export type EntryUnnamed = z.infer; + +const entryObject = z.record(entryItem.or(entryDescription)); +export type EntryObject = z.infer; + +const entryStatic = entryObject.or(entryUnnamed); +export type EntryStatic = z.infer; + +const entry = entryStatic; +export type Entry = z.infer; +//#endregion + +//#region Output +const path = z.string(); +export type Path = z.infer; + +const assetModuleFilename = z.string(); +export type AssetModuleFilename = z.infer; + +const webassemblyModuleFilename = z.string(); +export type WebassemblyModuleFilename = z.infer< + typeof webassemblyModuleFilename +>; + +const chunkFilename = filenameTemplate; +export type ChunkFilename = z.infer; + +const crossOriginLoading = z + .literal(false) + .or(z.enum(["anonymous", "use-credentials"])); +export type CrossOriginLoading = z.infer; + +const cssFilename = filenameTemplate; +export type CssFilename = z.infer; + +const cssChunkFilename = filenameTemplate; +export type CssChunkFilename = z.infer; + +const hotUpdateChunkFilename = filenameTemplate; +export type HotUpdateChunkFilename = z.infer; + +const hotUpdateMainFilename = filenameTemplate; +export type HotUpdateMainFilename = z.infer; + +const uniqueName = z.string(); +export type UniqueName = z.infer; + +const chunkLoadingGlobal = z.string(); +export type ChunkLoadingGlobal = z.infer; + +const libraryCustomUmdObject = z.strictObject({ + amd: z.string().optional(), + commonjs: z.string().optional(), + root: z.string().or(z.array(z.string())).optional() +}); +export type LibraryCustomUmdObject = z.infer; + +const libraryName = z + .string() + .or(z.array(z.string())) + .or(libraryCustomUmdObject); +export type LibraryName = z.infer; + +const libraryCustomUmdCommentObject = z.strictObject({ + amd: z.string().optional(), + commonjs: z.string().optional(), + commonjs2: z.string().optional(), + root: z.string().optional() +}); +export type LibraryCustomUmdCommentObject = z.infer< + typeof libraryCustomUmdCommentObject +>; + +const auxiliaryComment = z.string().or(libraryCustomUmdCommentObject); +export type AuxiliaryComment = z.infer; + +const libraryExport = z.string().or(z.array(z.string())); +export type LibraryExport = z.infer; + +const libraryType = z + .enum([ + "var", + "module", + "assign", + "assign-properties", + "this", + "window", + "self", + "global", + "commonjs", + "commonjs2", + "commonjs-module", + "commonjs-static", + "amd", + "amd-require", + "umd", + "umd2", + "jsonp", + "system" + ]) + .or(z.string()); +export type LibraryType = z.infer; + +const umdNamedDefine = z.boolean(); +export type UmdNamedDefine = z.infer; + +const libraryOptions = z.strictObject({ + auxiliaryComment: auxiliaryComment.optional(), + export: libraryExport.optional(), + name: libraryName.optional(), + type: libraryType, + umdNamedDefine: umdNamedDefine.optional() +}); +export type LibraryOptions = z.infer; + +const enabledLibraryTypes = z.array(libraryType); +export type EnabledLibraryTypes = z.infer; + +const clean = z.boolean(); +export type Clean = z.infer; + +const outputModule = z.boolean(); +export type OutputModule = z.infer; + +const strictModuleErrorHandling = z.boolean(); +export type StrictModuleErrorHandling = z.infer< + typeof strictModuleErrorHandling +>; + +const globalObject = z.string(); +export type GlobalObject = z.infer; + +const enabledWasmLoadingTypes = z.array(wasmLoadingType); +export type EnabledWasmLoadingTypes = z.infer; + +const importFunctionName = z.string(); +export type ImportFunctionName = z.infer; + +const iife = z.boolean(); +export type Iife = z.infer; + +const enabledChunkLoadingTypes = z.array(chunkLoadingType); +export type EnabledChunkLoadingTypes = z.infer; + +const chunkFormat = z.literal(false).or(z.string()); +export type ChunkFormat = z.infer; + +const workerPublicPath = z.string(); +export type WorkerPublicPath = z.infer; + +const trustedTypes = z.strictObject({ + policyName: z.string().optional() +}); +export type TrustedTypes = z.infer; + +const hashDigest = z.string(); +export type HashDigest = z.infer; + +const hashDigestLength = z.number(); +export type HashDigestLength = z.infer; + +const hashFunction = z.string(); +export type HashFunction = z.infer; + +const hashSalt = z.string(); +export type HashSalt = z.infer; + +const sourceMapFilename = z.string(); +export type SourceMapFilename = z.infer; + +const output = z.strictObject({ + path: path.optional(), + clean: clean.optional(), + publicPath: publicPath.optional(), + filename: filename.optional(), + chunkFilename: chunkFilename.optional(), + crossOriginLoading: crossOriginLoading.optional(), + cssFilename: cssFilename.optional(), + cssChunkFilename: cssChunkFilename.optional(), + hotUpdateMainFilename: hotUpdateMainFilename.optional(), + hotUpdateChunkFilename: hotUpdateChunkFilename.optional(), + assetModuleFilename: assetModuleFilename.optional(), + uniqueName: uniqueName.optional(), + chunkLoadingGlobal: chunkLoadingGlobal.optional(), + enabledLibraryTypes: enabledLibraryTypes.optional(), + library: libraryName.or(libraryOptions).optional(), + libraryExport: libraryExport.optional(), + libraryTarget: libraryType.optional(), + umdNamedDefine: umdNamedDefine.optional(), + auxiliaryComment: auxiliaryComment.optional(), + module: outputModule.optional(), + strictModuleErrorHandling: strictModuleErrorHandling.optional(), + globalObject: globalObject.optional(), + importFunctionName: importFunctionName.optional(), + iife: iife.optional(), + wasmLoading: wasmLoading.optional(), + enabledWasmLoadingTypes: enabledWasmLoadingTypes.optional(), + webassemblyModuleFilename: webassemblyModuleFilename.optional(), + chunkFormat: chunkFormat.optional(), + chunkLoading: chunkLoading.optional(), + enabledChunkLoadingTypes: enabledChunkLoadingTypes.optional(), + trustedTypes: z.literal(true).or(z.string()).or(trustedTypes).optional(), + sourceMapFilename: sourceMapFilename.optional(), + hashDigest: hashDigest.optional(), + hashDigestLength: hashDigestLength.optional(), + hashFunction: hashFunction.optional(), + hashSalt: hashSalt.optional(), + asyncChunks: asyncChunks.optional(), + workerChunkLoading: chunkLoading.optional(), + workerWasmLoading: wasmLoading.optional(), + workerPublicPath: workerPublicPath.optional() +}); +export type Output = z.infer; +//#endregion + +//#region Resolve +const resolveAlias = z.record( + z + .literal(false) + .or(z.string()) + .or(z.array(z.string().or(z.literal(false)))) +); +export type ResolveAlias = z.infer; + +const baseResolveOptions = z.strictObject({ + alias: resolveAlias.optional(), + /** + * This is `aliasField: ["browser"]` in webpack, because no one + * uses aliasField other than "browser". ---@bvanjoi + */ + browserField: z.boolean().optional(), + conditionNames: z.array(z.string()).optional(), + extensions: z.array(z.string()).optional(), + fallback: resolveAlias.optional(), + mainFields: z.array(z.string()).optional(), + mainFiles: z.array(z.string()).optional(), + modules: z.array(z.string()).optional(), + preferRelative: z.boolean().optional(), + tsConfigPath: z.string().optional(), + fullySpecified: z.boolean().optional(), + exportsFields: z.array(z.string()).optional(), + extensionAlias: z.record(z.string().or(z.array(z.string()))).optional() +}); + +export type ResolveOptions = z.infer & { + byDependency?: Record; +}; +const resolveOptions: z.ZodType = baseResolveOptions.extend({ + byDependency: z.lazy(() => z.record(resolveOptions)).optional() +}); + +const resolve = resolveOptions; +export type Resolve = z.infer; +//#endregion + +//#region Module +export type RuleSetCondition = + | RegExp + | string + | RuleSetConditions + | RuleSetLogicalConditions + | ((value: string) => boolean); +const ruleSetCondition: z.ZodType = z + .instanceof(RegExp) + .or(z.string()) + .or(z.lazy(() => ruleSetConditions)) + .or(z.lazy(() => ruleSetLogicalConditions)) + .or(z.function().args(z.string()).returns(z.boolean())); + +export type RuleSetConditions = RuleSetCondition[]; +const ruleSetConditions: z.ZodType = z.lazy(() => + z.array(ruleSetCondition) +); + +export type RuleSetLogicalConditions = { + and?: RuleSetConditions; + or?: RuleSetConditions; + not?: RuleSetConditions; +}; +const ruleSetLogicalConditions: z.ZodType = + z.strictObject({ + and: ruleSetConditions.optional(), + or: ruleSetConditions.optional(), + not: ruleSetConditions.optional() + }); + +const ruleSetLoader = z.string(); +export type RuleSetLoader = z.infer; + +const ruleSetLoaderOptions = z.string().or(z.record(z.any())); +export type RuleSetLoaderOptions = z.infer; + +const ruleSetLoaderWithOptions = z.strictObject({ + ident: z.string().optional(), + loader: ruleSetLoader, + options: ruleSetLoaderOptions.optional() +}); +export type RuleSetLoaderWithOptions = z.infer; + +const ruleSetUseItem = ruleSetLoader.or(ruleSetLoaderWithOptions); +export type RuleSetUseItem = z.infer; + +const ruleSetUse = ruleSetUseItem + .or(ruleSetUseItem.array()) + .or( + z.function().args(z.custom()).returns(ruleSetUseItem.array()) + ); +export type RuleSetUse = z.infer; + +export type RuleSetRule = { + test?: RuleSetCondition; + exclude?: RuleSetCondition; + include?: RuleSetCondition; + issuer?: RuleSetCondition; + dependency?: RuleSetCondition; + resource?: RuleSetCondition; + resourceFragment?: RuleSetCondition; + resourceQuery?: RuleSetCondition; + scheme?: RuleSetCondition; + mimetype?: RuleSetCondition; + descriptionData?: { + [k: string]: RuleSetCondition; + }; + oneOf?: RuleSetRule[]; + rules?: RuleSetRule[]; + type?: string; + loader?: RuleSetLoader; + options?: RuleSetLoaderOptions; + use?: RuleSetUse; + parser?: { + [k: string]: any; + }; + generator?: { + [k: string]: any; + }; + resolve?: ResolveOptions; + sideEffects?: boolean; + enforce?: "pre" | "post"; +}; +const ruleSetRule: z.ZodType = z.strictObject({ + test: ruleSetCondition.optional(), + exclude: ruleSetCondition.optional(), + include: ruleSetCondition.optional(), + issuer: ruleSetCondition.optional(), + dependency: ruleSetCondition.optional(), + resource: ruleSetCondition.optional(), + resourceFragment: ruleSetCondition.optional(), + resourceQuery: ruleSetCondition.optional(), + scheme: ruleSetCondition.optional(), + mimetype: ruleSetCondition.optional(), + descriptionData: z.record(ruleSetCondition).optional(), + oneOf: z.lazy(() => ruleSetRule.array()).optional(), + rules: z.lazy(() => ruleSetRule.array()).optional(), + type: z.string().optional(), + loader: ruleSetLoader.optional(), + options: ruleSetLoaderOptions.optional(), + use: ruleSetUse.optional(), + parser: z.record(z.any()).optional(), + generator: z.record(z.any()).optional(), + resolve: resolveOptions.optional(), + sideEffects: z.boolean().optional(), + enforce: z.literal("pre").or(z.literal("post")).optional() +}); + +const ruleSetRules = z.array(z.literal("...").or(ruleSetRule)); +export type RuleSetRules = z.infer; + +const assetParserDataUrlOptions = z.strictObject({ + maxSize: z.number().optional() +}); +export type AssetParserDataUrlOptions = z.infer< + typeof assetParserDataUrlOptions +>; + +const assetParserDataUrl = assetParserDataUrlOptions; +export type AssetParserDataUrl = z.infer; + +const assetParserOptions = z.strictObject({ + dataUrlCondition: assetParserDataUrl.optional() +}); +export type AssetParserOptions = z.infer; + +const parserOptionsByModuleTypeKnown = z.strictObject({ + asset: assetParserOptions.optional() +}); +export type ParserOptionsByModuleTypeKnown = z.infer< + typeof parserOptionsByModuleTypeKnown +>; + +const parserOptionsByModuleTypeUnknown = z.record(z.record(z.any())); +export type ParserOptionsByModuleTypeUnknown = z.infer< + typeof parserOptionsByModuleTypeUnknown +>; + +const parserOptionsByModuleType = parserOptionsByModuleTypeKnown.or( + parserOptionsByModuleTypeUnknown +); +export type ParserOptionsByModuleType = z.infer< + typeof parserOptionsByModuleType +>; + +const assetGeneratorDataUrlOptions = z.strictObject({ + encoding: z.literal(false).or(z.literal("base64")).optional(), + mimetype: z.string().optional() +}); +export type AssetGeneratorDataUrlOptions = z.infer< + typeof assetGeneratorDataUrlOptions +>; + +const assetGeneratorDataUrl = assetGeneratorDataUrlOptions; +export type AssetGeneratorDataUrl = z.infer; + +const assetInlineGeneratorOptions = z.strictObject({ + dataUrl: assetGeneratorDataUrl.optional() +}); +export type AssetInlineGeneratorOptions = z.infer< + typeof assetInlineGeneratorOptions +>; + +const assetResourceGeneratorOptions = z.strictObject({ + filename: filenameTemplate.optional(), + publicPath: publicPath.optional() +}); +export type AssetResourceGeneratorOptions = z.infer< + typeof assetResourceGeneratorOptions +>; + +const assetGeneratorOptions = assetInlineGeneratorOptions.merge( + assetResourceGeneratorOptions +); +export type AssetGeneratorOptions = z.infer; + +const generatorOptionsByModuleTypeKnown = z.strictObject({ + asset: assetGeneratorOptions.optional(), + "asset/inline": assetInlineGeneratorOptions.optional(), + "asset/resource": assetResourceGeneratorOptions.optional() +}); +export type GeneratorOptionsByModuleTypeKnown = z.infer< + typeof generatorOptionsByModuleTypeKnown +>; + +const generatorOptionsByModuleTypeUnknown = z.record(z.record(z.any())); +export type GeneratorOptionsByModuleTypeUnknown = z.infer< + typeof generatorOptionsByModuleTypeUnknown +>; + +const generatorOptionsByModuleType = generatorOptionsByModuleTypeKnown.or( + generatorOptionsByModuleTypeUnknown +); +export type GeneratorOptionsByModuleType = z.infer< + typeof generatorOptionsByModuleType +>; + +const moduleOptions = z.strictObject({ + defaultRules: ruleSetRules.optional(), + rules: ruleSetRules.optional(), + parser: parserOptionsByModuleType.optional(), + generator: generatorOptionsByModuleType.optional() +}); +export type ModuleOptions = z.infer; +//#endregion + +//#region Target +const allowTarget = z + .enum([ + "web", + "webworker", + "es3", + "es5", + "es2015", + "es2016", + "es2017", + "es2018", + "es2019", + "es2020", + "es2021", + "es2022", + "browserslist" + ]) + .or(z.literal("node")) + .or(z.literal("async-node")) + .or( + z.custom<`node${number}`>( + value => typeof value === "string" && /^node\d+$/.test(value) + ) + ) + .or( + z.custom<`async-node${number}`>( + value => typeof value === "string" && /^async-node\d+$/.test(value) + ) + ) + .or( + z.custom<`node${number}.${number}`>( + value => typeof value === "string" && /^node\d+\.\d+$/.test(value) + ) + ) + .or( + z.custom<`async-node${number}.${number}`>( + value => typeof value === "string" && /^async-node\d+\.\d+$/.test(value) + ) + ) + .or(z.literal("electron-main")) + .or( + z.custom<`electron${number}-main`>( + value => typeof value === "string" && /^electron\d+-main$/.test(value) + ) + ) + .or( + z.custom<`electron${number}.${number}-main`>( + value => + typeof value === "string" && /^electron\d+\.\d+-main$/.test(value) + ) + ) + .or(z.literal("electron-renderer")) + .or( + z.custom<`electron${number}-renderer`>( + value => typeof value === "string" && /^electron\d+-renderer$/.test(value) + ) + ) + .or( + z.custom<`electron${number}.${number}-renderer`>( + value => + typeof value === "string" && /^electron\d+\.\d+-renderer$/.test(value) + ) + ) + .or(z.literal("electron-preload")) + .or( + z.custom<`electron${number}-preload`>( + value => typeof value === "string" && /^electron\d+-preload$/.test(value) + ) + ) + .or( + z.custom<`electron${number}.${number}-preload`>( + value => + typeof value === "string" && /^electron\d+\.\d+-preload$/.test(value) + ) + ); +const target = z.literal(false).or(allowTarget).or(allowTarget.array()); +export type Target = z.infer; +//#endregion + +//#region ExternalsType +const externalsType = z.enum([ + "var", + "module", + "assign", + "this", + "window", + "self", + "global", + "commonjs", + "commonjs2", + "commonjs-module", + "commonjs-static", + "amd", + "amd-require", + "umd", + "umd2", + "jsonp", + "system", + "promise", + "import", + "script", + "node-commonjs" +]); +export type ExternalsType = z.infer; +//#endregion + +//#region Externals +const externalItemValue = z.string().or(z.boolean()).or(z.string().array()); +export type ExternalItemValue = z.infer; + +const externalItemObjectUnknown = z.record(externalItemValue); +export type ExternalItemObjectUnknown = z.infer< + typeof externalItemObjectUnknown +>; + +const externalItemFunctionData = z.strictObject({ + context: z.string().optional(), + dependencyType: z.string().optional(), + request: z.string().optional() +}); +export type ExternalItemFunctionData = z.infer; + +const externalItem = z + .string() + .or(z.instanceof(RegExp)) + .or(externalItemObjectUnknown) + .or( + z + .function() + .args( + externalItemFunctionData, + z + .function() + .args( + z.instanceof(Error).optional(), + externalItemValue.optional(), + externalsType.optional() + ) + .returns(z.void()) + ) + ) + .or( + z + .function() + .args(externalItemFunctionData) + .returns(z.promise(externalItemValue)) + ); +export type ExternalItem = z.infer; + +const externals = externalItem.array().or(externalItem); +export type Externals = z.infer; +//#endregion + +//#region ExternalsPresets +const externalsPresets = z.strictObject({ + node: z.boolean().optional(), + web: z.boolean().optional(), + electron: z.boolean().optional(), + electronMain: z.boolean().optional(), + electronPreload: z.boolean().optional(), + electronRenderer: z.boolean().optional() +}); +export type ExternalsPresets = z.infer; +//#endregion + +//#region InfrastructureLogging +const filterItemTypes = z + .instanceof(RegExp) + .or(z.string()) + .or(z.function().args(z.string()).returns(z.boolean())); +export type FilterItemTypes = z.infer; + +const filterTypes = filterItemTypes.array().or(filterItemTypes); +export type FilterTypes = z.infer; + +const infrastructureLogging = z.strictObject({ + appendOnly: z.boolean().optional(), + colors: z.boolean().optional(), + console: z.custom().optional(), + debug: z.boolean().or(filterTypes).optional(), + level: z.enum(["none", "error", "warn", "info", "log", "verbose"]).optional(), + stream: z.custom().optional() +}); +export type InfrastructureLogging = z.infer; +//#endregion + +//#region DevTool +const devTool = z + .literal(false) + .or( + z.enum([ + "cheap-source-map", + "cheap-module-source-map", + "source-map", + "inline-cheap-source-map", + "inline-cheap-module-source-map", + "inline-source-map", + "inline-nosources-cheap-module-source-map", + "inline-nosources-source-map", + "nosources-cheap-source-map", + "nosources-cheap-module-source-map", + "nosources-source-map", + "hidden-nosources-cheap-source-map", + "hidden-nosources-cheap-module-source-map", + "hidden-nosources-source-map", + "hidden-cheap-source-map", + "hidden-cheap-module-source-map", + "hidden-source-map", + "eval-cheap-source-map", + "eval-cheap-module-source-map", + "eval-source-map", + "eval-nosources-cheap-source-map", + "eval-nosources-cheap-module-source-map", + "eval-nosources-source-map" + ]) + ); +export type DevTool = z.infer; +//#endregion + +//#region Node +const nodeOptions = z.strictObject({ + __dirname: z + .boolean() + .or(z.enum(["warn-mock", "mock", "eval-only"])) + .optional(), + __filename: z + .boolean() + .or(z.enum(["warn-mock", "mock", "eval-only"])) + .optional(), + global: z.boolean().or(z.literal("warn")).optional() +}); +export type NodeOptions = z.infer; + +const node = z.literal(false).or(nodeOptions); +export type Node = z.infer; +//#endregion + +//#region Snapshot +const snapshotOptions = z.strictObject({ + module: z + .strictObject({ + hash: z.boolean().optional(), + timestamp: z.boolean().optional() + }) + .optional(), + resolve: z + .strictObject({ + hash: z.boolean().optional(), + timestamp: z.boolean().optional() + }) + .optional() +}); +export type SnapshotOptions = z.infer; +//#endregion + +//#region Cache +const cacheOptions = z.boolean(); +export type CacheOptions = z.infer; +//#endregion + +//#region Stats +const statsOptions = z.strictObject({ + all: z.boolean().optional(), + preset: z + .enum(["normal", "none", "verbose", "errors-only", "errors-warnings"]) + .optional(), + assets: z.boolean().optional(), + chunks: z.boolean().optional(), + modules: z.boolean().optional(), + entrypoints: z.boolean().optional(), + chunkGroups: z.boolean().optional(), + warnings: z.boolean().optional(), + warningsCount: z.boolean().optional(), + errors: z.boolean().optional(), + errorsCount: z.boolean().optional(), + colors: z.boolean().optional(), + hash: z.boolean().optional(), + version: z.boolean().optional(), + reasons: z.boolean().optional(), + publicPath: z.boolean().optional(), + outputPath: z.boolean().optional(), + chunkModules: z.boolean().optional(), + chunkRelations: z.boolean().optional(), + timings: z.boolean().optional(), + builtAt: z.boolean().optional(), + moduleAssets: z.boolean().optional(), + modulesSpace: z.number().optional(), + nestedModules: z.boolean().optional(), + source: z.boolean().optional(), + logging: z + .enum(["none", "error", "warn", "info", "log", "verbose"]) + .or(z.boolean()) + .optional(), + loggingDebug: z.boolean().or(filterTypes).optional(), + loggingTrace: z.boolean().optional() +}); +export type StatsOptions = z.infer; + +const statsValue = z + .enum(["none", "errors-only", "errors-warnings", "normal", "verbose"]) + .or(z.boolean()) + .or(statsOptions); +export type StatsValue = z.infer; +//#endregion + +//#region Plugins +export interface RspackPluginInstance { + apply: (compiler: Compiler) => void; + [k: string]: any; +} +export type RspackPluginFunction = (this: Compiler, compiler: Compiler) => void; + +const plugin = z.union([ + z.custom(), + z.custom() +]); +const plugins = plugin.array(); +export type Plugins = z.infer; +//#endregion + +//#region Optimization +const optimizationRuntimeChunk = z + .enum(["single", "multiple"]) + .or(z.boolean()) + .or( + z.strictObject({ + name: z + .string() + .or(z.function().returns(z.string().or(z.undefined()))) + .optional() + }) + ); +export type OptimizationRuntimeChunk = z.infer; + +const optimizationSplitChunksName = z.string().or(z.literal(false)); +const optimizationSplitChunksChunks = z + .enum(["initial", "async", "all"]) + .or(z.instanceof(RegExp)); +const optimizationSplitChunksSizes = z.number(); +const sharedOptimizationSplitChunksCacheGroup = { + chunks: optimizationSplitChunksChunks.optional(), + minChunks: z.number().optional(), + name: optimizationSplitChunksName.optional(), + minSize: optimizationSplitChunksSizes.optional(), + maxSize: optimizationSplitChunksSizes.optional(), + maxAsyncSize: optimizationSplitChunksSizes.optional(), + maxInitialSize: optimizationSplitChunksSizes.optional() +}; +const optimizationSplitChunksCacheGroup = z.strictObject({ + test: z.string().or(z.instanceof(RegExp)).optional(), + priority: z.number().optional(), + enforce: z.boolean().optional(), + reuseExistingChunk: z.boolean().optional(), + type: z.string().or(z.instanceof(RegExp)).optional(), + idHint: z.string().optional(), + ...sharedOptimizationSplitChunksCacheGroup +}); +export type OptimizationSplitChunksCacheGroup = z.infer< + typeof optimizationSplitChunksCacheGroup +>; + +const optimizationSplitChunksOptions = z.strictObject({ + cacheGroups: z + .record(z.literal(false).or(optimizationSplitChunksCacheGroup)) + .optional(), + maxAsyncRequests: z.number().optional(), + maxInitialRequests: z.number().optional(), + fallbackCacheGroup: z + .strictObject({ + chunks: optimizationSplitChunksChunks.optional(), + minSize: z.number().optional(), + maxSize: z.number().optional(), + maxAsyncSize: z.number().optional(), + maxInitialSize: z.number().optional() + }) + .optional(), + ...sharedOptimizationSplitChunksCacheGroup +}); +export type OptimizationSplitChunksOptions = z.infer< + typeof optimizationSplitChunksOptions +>; + +const optimization = z.strictObject({ + moduleIds: z.enum(["named", "deterministic"]).optional(), + chunkIds: z.enum(["named", "deterministic"]).optional(), + minimize: z.boolean().optional(), + minimizer: z.literal("...").or(plugin).array().optional(), + splitChunks: optimizationSplitChunksOptions.optional(), + runtimeChunk: optimizationRuntimeChunk.optional(), + removeAvailableModules: z.boolean().optional(), + removeEmptyChunks: z.boolean().optional(), + realContentHash: z.boolean().optional(), + sideEffects: z.enum(["flag"]).or(z.boolean()).optional() +}); +export type Optimization = z.infer; +//#endregion + +//#region Experiments +const incrementalRebuildOptions = z.strictObject({ + make: z.boolean().optional(), + emitAsset: z.boolean().optional() +}); +export type IncrementalRebuildOptions = z.infer< + typeof incrementalRebuildOptions +>; + +const rspackFutureOptions = z.strictObject({ + newResolver: z.boolean().optional() +}); +export type RspackFutureOptions = z.infer; + +const experiments = z.strictObject({ + lazyCompilation: z.boolean().optional(), + incrementalRebuild: z.boolean().or(incrementalRebuildOptions).optional(), + asyncWebAssembly: z.boolean().optional(), + outputModule: z.boolean().optional(), + newSplitChunks: z.boolean().optional(), + css: z.boolean().optional(), + futureDefaults: z.boolean().optional(), + rspackFuture: rspackFutureOptions.optional() +}); +export type Experiments = z.infer; +//#endregion + +//#region Watch +const watch = z.boolean(); +export type Watch = z.infer; +//#endregion + +//#region WatchOptions +const watchOptions = z.strictObject({ + aggregateTimeout: z.number().optional(), + followSymlinks: z.boolean().optional(), + ignored: z + .string() + .array() + .or(z.instanceof(RegExp)) + .or(z.string()) + .optional(), + poll: z.number().or(z.boolean()).optional(), + stdin: z.boolean().optional() +}); +export type WatchOptions = z.infer; +//#endregion + +//#region DevServer +export interface DevServer extends webpackDevServer.Configuration { + hot?: boolean; +} +const devServer = z.custom(); +//#endregion + +//#region IgnoreWarnings +const ignoreWarnings = z + .instanceof(RegExp) + .or( + z + .function() + .args(z.instanceof(Error), z.custom()) + .returns(z.boolean()) + ) + .array(); +export type IgnoreWarnings = z.infer; +//#endregion + +//#region Profile +const profile = z.boolean(); +export type Profile = z.infer; +//#endregion + +//#region Builtins (deprecated) +const builtins = z.custom(); +export type Builtins = z.infer; +//#endregion + +export const rspackOptions = z.strictObject({ + name: name.optional(), + dependencies: dependencies.optional(), + entry: entry.optional(), + output: output.optional(), + target: target.optional(), + mode: mode.optional(), + experiments: experiments.optional(), + externals: externals.optional(), + externalsType: externalsType.optional(), + externalsPresets: externalsPresets.optional(), + infrastructureLogging: infrastructureLogging.optional(), + cache: cacheOptions.optional(), + context: context.optional(), + devtool: devTool.optional(), + node: node.optional(), + ignoreWarnings: ignoreWarnings.optional(), + watchOptions: watchOptions.optional(), + watch: watch.optional(), + stats: statsValue.optional(), + snapshot: snapshotOptions.optional(), + optimization: optimization.optional(), + resolve: resolve.optional(), + resolveLoader: resolve.optional(), + plugins: plugins.optional(), + devServer: devServer.optional(), + builtins: builtins.optional(), + module: moduleOptions.optional(), + profile: profile.optional() +}); +export type RspackOptions = z.infer; +export type Configuration = RspackOptions; diff --git a/packages/rspack/src/config/zod/_rewrite.ts b/packages/rspack/src/config/zod/_rewrite.ts deleted file mode 100644 index 221081d611ef..000000000000 --- a/packages/rspack/src/config/zod/_rewrite.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { z } from "zod"; -import { configSchema } from "./index"; -import type { Output, ModuleOptions, DevServer, Builtins } from "../types"; - -// The final goal is to infer the type using the schema without any rewriting. -// But currently there are some schema are loose, so we need to rewrite the `Config` -// type to expose the correct type to users. -type Config = z.infer>; - -type Rewritten = { - output?: Output; - module?: ModuleOptions; - builtins?: Omit> & - NonNullable; - devServer?: DevServer; -}; - -export type Options = Omit & Rewritten; diff --git a/packages/rspack/src/config/zod/builtins.ts b/packages/rspack/src/config/zod/builtins.ts deleted file mode 100644 index 1b6a93899c3a..000000000000 --- a/packages/rspack/src/config/zod/builtins.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { z } from "zod"; - -export function builtins() { - // TODO(hyf0): need to use `strictObject` mode when developer have time to finish the whole schema - return z.object({ - postcss: z - .strictObject({ - pxtorem: z - .strictObject({ - rootValue: z.number().optional(), - unitPrecision: z.number().optional(), - selectorBlackList: z.string().array().optional(), - propList: z.string().array().optional(), - replace: z.boolean().optional(), - mediaQuery: z.boolean().optional(), - minPixelValue: z.number().optional() - }) - .optional() - }) - .refine(() => { - console.warn( - "warn: `builtins.postcss` is removed in 0.3.0. Please use `postcss-loader` instead." - ); - return true; - }) - .optional(), - html: z - .object({ - title: z.string().optional(), - filename: z.string().optional(), - template: z.string().optional(), - templateParameters: z.record(z.string()).optional(), - inject: z.enum(["head", "body"]).optional(), - publicPath: z.string().optional(), - scriptLoading: z.enum(["blocking", "defer", "module"]).optional(), - chunks: z.string().array().optional(), - excludedChunks: z.string().array().optional(), - sri: z.enum(["sha256", "sha384", "sha512"]).optional(), - minify: z.boolean().optional(), - favicon: z.string().optional(), - meta: z.record(z.string().or(z.record(z.string()))).optional() - }) - .array() - .optional() - }); -} diff --git a/packages/rspack/src/config/zod/devtool.ts b/packages/rspack/src/config/zod/devtool.ts deleted file mode 100644 index 04ec6b2bb42e..000000000000 --- a/packages/rspack/src/config/zod/devtool.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { z } from "zod"; - -export function devtool() { - return z - .literal(false) - .or( - z.enum([ - "cheap-source-map", - "cheap-module-source-map", - "source-map", - "inline-cheap-source-map", - "inline-cheap-module-source-map", - "inline-source-map", - "inline-nosources-cheap-module-source-map", - "inline-nosources-source-map", - "nosources-cheap-source-map", - "nosources-cheap-module-source-map", - "nosources-source-map", - "hidden-nosources-cheap-source-map", - "hidden-nosources-cheap-module-source-map", - "hidden-nosources-source-map", - "hidden-cheap-source-map", - "hidden-cheap-module-source-map", - "hidden-source-map", - "eval-cheap-source-map", - "eval-cheap-module-source-map", - "eval-source-map", - "eval-nosources-cheap-source-map", - "eval-nosources-cheap-module-source-map", - "eval-nosources-source-map" - ]) - ); -} diff --git a/packages/rspack/src/config/zod/entry.ts b/packages/rspack/src/config/zod/entry.ts deleted file mode 100644 index e0d347eb98bc..000000000000 --- a/packages/rspack/src/config/zod/entry.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { z } from "zod"; -import { filename, publicPath } from "./output"; - -const entryItem = z - .string() - .min(1) - .describe("The string is resolved to a module which is loaded upon startup.") - .or(z.string().min(1).array().min(1)); - -const entryDescription = z - .object({ - import: entryItem, - runtime: z.literal(false).or(z.string().min(1)).optional(), - publicPath: publicPath().optional(), - baseUri: z.string().optional(), - chunkLoading: z - .literal(false) - .or( - z - .enum(["jsonp", "require", "async-node", "import", "import-scripts"]) - .or(z.string()) - .optional() - ), - asyncChunks: z.boolean().optional(), - wasmLoading: z - .literal(false) - .or(z.enum(["fetch-streaming", "fetch", "async-node"])) - .optional(), - filename: filename().optional() - }) - .strict(); - -const entryObject = z.record(entryItem.or(entryDescription)); - -export function entry() { - return entryItem.or(entryObject); -} diff --git a/packages/rspack/src/config/zod/experiments.ts b/packages/rspack/src/config/zod/experiments.ts deleted file mode 100644 index 20094f4a9cab..000000000000 --- a/packages/rspack/src/config/zod/experiments.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { z } from "zod"; - -export function experiments() { - return z.object({ - asyncWebAssembly: z.boolean().optional(), - incrementalRebuild: z - .boolean() - .or( - z.strictObject({ - make: z.boolean().optional(), - emitAsset: z.boolean().optional() - }) - ) - .optional(), - lazyCompilation: z.boolean().optional(), - outputModule: z.boolean().optional(), - newSplitChunks: z.boolean().optional(), - css: z.boolean().optional(), - rspackFuture: z - .strictObject({ - newResolver: z.boolean().optional() - }) - .optional() - }); -} diff --git a/packages/rspack/src/config/zod/externals-presets.ts b/packages/rspack/src/config/zod/externals-presets.ts deleted file mode 100644 index d860658c4dab..000000000000 --- a/packages/rspack/src/config/zod/externals-presets.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { z } from "zod"; - -export function externalsPresets() { - return z - .object({ - web: z.boolean().optional(), - node: z.boolean().optional(), - electron: z.boolean().optional(), - electronMain: z.boolean().optional(), - electronPreload: z.boolean().optional(), - electronRenderer: z.boolean().optional() - }) - .strict(); -} diff --git a/packages/rspack/src/config/zod/externals-type.ts b/packages/rspack/src/config/zod/externals-type.ts deleted file mode 100644 index db4eb556b0bc..000000000000 --- a/packages/rspack/src/config/zod/externals-type.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { z } from "zod"; - -export function externalsType() { - return z.enum([ - "var", - "module", - "assign", - "this", - "window", - "self", - "global", - "commonjs", - "commonjs2", - "commonjs-module", - "commonjs-static", - "amd", - "amd-require", - "umd", - "umd2", - "jsonp", - "system", - "promise", - "import", - "script", - "node-commonjs" - ]); -} diff --git a/packages/rspack/src/config/zod/externals.ts b/packages/rspack/src/config/zod/externals.ts deleted file mode 100644 index f92e2c115b23..000000000000 --- a/packages/rspack/src/config/zod/externals.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { z } from "zod"; - -const externalItem = z - .instanceof(RegExp) - .or(z.string()) - .or(z.function()) - .or(z.any()); - -export function externals() { - return externalItem.or(externalItem.array()); -} diff --git a/packages/rspack/src/config/zod/index.ts b/packages/rspack/src/config/zod/index.ts deleted file mode 100644 index 86436444ea82..000000000000 --- a/packages/rspack/src/config/zod/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { z } from "zod"; -import { entry } from "./entry"; -import { experiments } from "./experiments"; -import { externals } from "./externals"; -import { externalsType } from "./externals-type"; -import { externalsPresets } from "./externals-presets"; -import { infrastructureLogging } from "./infrastructure-logging"; -import { node } from "./node"; -import { builtins } from "./builtins"; -import { watchOptions } from "./watch-options"; -import { target } from "./target"; -import { stats } from "./stats"; -import { snapshot } from "./snapshot"; -import { output } from "./output"; -import { devtool } from "./devtool"; -import { optimization } from "./optimization"; -import { resolve } from "./resolve"; -import { plugins } from "./plugins"; - -export function configSchema() { - return z - .object({ - target: target().optional(), - mode: z.enum(["development", "production", "none"]).optional(), - entry: entry().optional(), - experiments: experiments().optional(), - externals: externals().optional(), - externalsType: externalsType().optional(), - externalsPresets: externalsPresets().optional(), - infrastructureLogging: infrastructureLogging().optional(), - cache: z.boolean().optional(), - context: z.string().optional(), - dependencies: z.string().array().optional(), - devtool: devtool().optional(), - node: node().optional(), - ignoreWarnings: z.instanceof(RegExp).or(z.function()).array().optional(), - watchOptions: watchOptions().optional(), - watch: z.boolean().optional(), - stats: stats().optional(), - snapshot: snapshot().optional(), - optimization: optimization().optional(), - resolve: resolve().optional(), - resolveLoader: resolve().optional(), - plugins: plugins().optional(), - // TODO(hyf0): what's the usage of this? - name: z.string().optional(), - // TODO - devServer: z.object({}).optional(), - output: output().optional(), - builtins: builtins().optional(), - module: z.any().optional(), - profile: z.boolean().optional() - }) - .strict(); -} diff --git a/packages/rspack/src/config/zod/infrastructure-logging.ts b/packages/rspack/src/config/zod/infrastructure-logging.ts deleted file mode 100644 index c2ca33d2eda3..000000000000 --- a/packages/rspack/src/config/zod/infrastructure-logging.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { z } from "zod"; - -export function infrastructureLogging() { - return z - .object({ - appendOnly: z.boolean().optional(), - colors: z.boolean().optional(), - console: z.boolean().optional(), - debug: z.boolean().or(z.any()).optional(), - level: z - .enum(["none", "error", "warn", "info", "log", "verbose"]) - .optional(), - stream: z.boolean().or(z.object({})).optional() - }) - .strict(); -} diff --git a/packages/rspack/src/config/zod/module.ts b/packages/rspack/src/config/zod/module.ts deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/rspack/src/config/zod/node.ts b/packages/rspack/src/config/zod/node.ts deleted file mode 100644 index a4a912db29f0..000000000000 --- a/packages/rspack/src/config/zod/node.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { z } from "zod"; - -export function node() { - return z.literal(false).or( - z - .object({ - __dirname: z - .boolean() - .or(z.enum(["warn-mock", "mock", "eval-only"])) - .optional(), - __filename: z - .boolean() - .or(z.enum(["warn-mock", "mock", "eval-only"])) - .optional(), - global: z - .boolean() - .or(z.enum(["warn"])) - .optional() - }) - .strict() - ); -} diff --git a/packages/rspack/src/config/zod/optimization/index.ts b/packages/rspack/src/config/zod/optimization/index.ts deleted file mode 100644 index 21bf01e733a5..000000000000 --- a/packages/rspack/src/config/zod/optimization/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { z } from "zod"; -import { Compiler } from "../../../Compiler"; -import { splitChunks } from "./split-chunks"; - -const rspackPluginInstance = z.any(); - -export function optimization() { - return z.strictObject({ - moduleIds: z.enum(["named", "deterministic"]).optional(), - chunkIds: z.enum(["named", "deterministic"]).optional(), - minimize: z.boolean().optional(), - minimizer: z.literal("...").or(rspackPluginInstance).array().optional(), - splitChunks: splitChunks().optional(), - runtimeChunk: z - .enum(["single", "multiple"]) - .or(z.boolean()) - .or( - z.strictObject({ - name: z - .string() - .or(z.function().returns(z.string().or(z.undefined()))) - .optional() - }) - ) - .optional(), - removeAvailableModules: z.boolean().optional(), - removeEmptyChunks: z.boolean().optional(), - realContentHash: z.boolean().optional(), - sideEffects: z.enum(["flag"]).or(z.boolean()).optional() - }); -} - -export type OptimizationConfig = z.TypeOf>; - -export type OptimizationRuntimeChunkConfig = NonNullable< - OptimizationConfig["runtimeChunk"] ->; diff --git a/packages/rspack/src/config/zod/optimization/split-chunks.ts b/packages/rspack/src/config/zod/optimization/split-chunks.ts deleted file mode 100644 index 83e4ef2955eb..000000000000 --- a/packages/rspack/src/config/zod/optimization/split-chunks.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { string, z } from "zod"; - -function chunks() { - return z.enum(["initial", "async", "all"]).or(z.instanceof(RegExp)); -} - -function name() { - return z.string().or(z.literal(false)); -} - -const sharedCacheGroupConfigPart = { - chunks: chunks().optional(), - minChunks: z.number().optional(), - name: name().optional(), - minSize: z.number().optional(), - maxSize: z.number().optional(), - maxAsyncSize: z.number().optional(), - maxInitialSize: z.number().optional() -}; - -const cacheGroupOptions = z.strictObject({ - test: z.string().or(z.instanceof(RegExp)).optional(), - priority: z.number().optional(), - enforce: z.boolean().optional(), - reuseExistingChunk: z.boolean().optional(), - type: z.string().or(z.instanceof(RegExp)).optional(), - idHint: z.string().optional(), - ...sharedCacheGroupConfigPart -}); - -export function splitChunks() { - return z.literal(false).or( - // We use loose object here to prevent breaking change on config - z.object({ - cacheGroups: z.record(z.literal(false).or(cacheGroupOptions)).optional(), - maxAsyncRequests: z.number().optional(), - maxInitialRequests: z.number().optional(), - fallbackCacheGroup: z - .strictObject({ - chunks: chunks().optional(), - minSize: z.number().optional(), - maxSize: z.number().optional(), - maxAsyncSize: z.number().optional(), - maxInitialSize: z.number().optional() - }) - .optional(), - ...sharedCacheGroupConfigPart - }) - ); -} - -export type SplitChunksConfig = z.TypeOf>; -export type CacheGroupOptionsConfig = z.TypeOf; diff --git a/packages/rspack/src/config/zod/output.ts b/packages/rspack/src/config/zod/output.ts deleted file mode 100644 index b68032dd874d..000000000000 --- a/packages/rspack/src/config/zod/output.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { z } from "zod"; - -function chunkLoadingType() { - return z - .enum(["jsonp", "import-scripts", "require", "async-node", "import"]) - .or(z.string()); -} - -function chunkLoading() { - return z.literal(false).or(chunkLoadingType()); -} - -function wasmLoadingType() { - return z.enum(["...", "fetch-streaming", "fetch", "async-node"]); -} - -function wasmLoading() { - return z.literal(false).or(wasmLoadingType()); -} - -export function publicPath() { - return z.literal("auto").or(z.string()); -} - -export function filename() { - return z.string(); -} - -function workerPublicPath() { - return z.string(); -} - -function libraryType() { - return z.enum([ - "...", - "var", - "module", - "assign", - "assign-properties", - "this", - "window", - "self", - "global", - "commonjs", - "commonjs2", - "commonjs-module", - "commonjs-static", - "amd", - "amd-require", - "umd", - "umd2", - "jsonp", - "system" - ]); -} - -const umdNamedDefine = z.boolean(); - -const auxiliaryComment = z.string().or( - z.strictObject({ - amd: z.string().optional(), - commonjs: z.string().optional(), - commonjs2: z.string().optional(), - root: z.string().optional() - }) -); - -const libraryName = z - .string() - .or(z.string().array()) - .or( - z.strictObject({ - amd: z.string().optional(), - commonjs: z.string().optional(), - root: z.string().or(z.string().array()).optional() - }) - ); - -const libraryOptions = z.strictObject({ - auxiliaryComment: auxiliaryComment.optional(), - export: z.string().array().or(z.string()).optional(), - name: libraryName.optional(), - type: libraryType().optional(), - umdNamedDefine: umdNamedDefine.optional() -}); - -export function output() { - return z.strictObject({ - iife: z.boolean().optional(), - clean: z.boolean().optional(), - assetModuleFilename: z.string().optional(), - auxiliaryComment: auxiliaryComment.optional(), - chunkFormat: z - .enum(["array-push", "commonjs", "module"]) - .or(z.literal(false)) - .optional(), - chunkLoading: chunkLoading().optional(), - enabledChunkLoadingTypes: chunkLoadingType().array().optional(), - chunkFilename: z.string().optional(), - cssChunkFilename: z.string().optional(), - cssFilename: z.string().optional(), - hotUpdateChunkFilename: z.string().optional(), - hotUpdateMainFilename: z.string().optional(), - webassemblyModuleFilename: z.string().optional(), - hashSalt: z.string().optional(), - filename: filename().optional(), - sourceMapFilename: z.string().optional(), - importFunctionName: z.string().optional(), - publicPath: publicPath().optional(), - uniqueName: z.string().optional(), - path: z.string().optional(), - crossOriginLoading: z - .literal(false) - .or(z.enum(["anonymous", "use-credentials"])) - .optional(), - enabledWasmLoadingTypes: wasmLoadingType().array().optional(), - wasmLoading: wasmLoading().optional(), - enabledLibraryTypes: libraryType().or(libraryType().array()).optional(), - globalObject: z.string().min(1).optional(), - libraryExport: z.string().min(1).or(z.string().min(1).array()).optional(), - libraryTarget: libraryType().optional(), - hashFunction: z.string().or(z.function()).optional(), - // TODO(hyf0) - module: z.any().optional(), - strictModuleErrorHandling: z.boolean().optional(), - umdNamedDefine: umdNamedDefine.optional(), - chunkLoadingGlobal: z.string().optional(), - trustedTypes: z - .literal(true) - .or(z.string()) - .or( - z.strictObject({ - policyName: z.string().optional() - }) - ) - .optional(), - hashDigest: z.string().optional(), - hashDigestLength: z.number().optional(), - library: libraryName.or(libraryOptions).optional(), - asyncChunks: z.boolean().optional(), - workerChunkLoading: chunkLoading().optional(), - workerWasmLoading: wasmLoading().optional(), - workerPublicPath: workerPublicPath().optional() - }); -} diff --git a/packages/rspack/src/config/zod/plugins.ts b/packages/rspack/src/config/zod/plugins.ts deleted file mode 100644 index c071fbb8c844..000000000000 --- a/packages/rspack/src/config/zod/plugins.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { z } from "zod"; -import { Compiler } from "../../Compiler"; - -const rspackPluginFunction = z - .function() - .args(z.instanceof(Compiler)) - .returns(z.void()); - -const rspackPluginInstance = z.object({ - name: z.string().optional(), - apply: rspackPluginFunction -}); - -export function plugins() { - const functionOrInstance = rspackPluginFunction.or(rspackPluginInstance); - return functionOrInstance.array(); -} diff --git a/packages/rspack/src/config/zod/resolve.ts b/packages/rspack/src/config/zod/resolve.ts deleted file mode 100644 index 0dbfb0cba78e..000000000000 --- a/packages/rspack/src/config/zod/resolve.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { z } from "zod"; - -const resolveAlias = z.record( - z - .literal(false) - .or(z.string()) - .or(z.array(z.literal(false).or(z.string()))) -); - -const baseResolve = z.strictObject({ - alias: resolveAlias.optional(), - browserField: z.boolean().optional(), - conditionNames: z.string().array().optional(), - extensions: z.string().array().optional(), - fallback: resolveAlias.optional(), - mainFields: z.string().array().optional(), - mainFiles: z.string().array().optional(), - modules: z.string().array().optional(), - preferRelative: z.boolean().optional(), - tsConfigPath: z.string().optional(), - fullySpecified: z.boolean().optional(), - exportsFields: z.string().array().optional(), - extensionAlias: z.record(z.string().or(z.string().array())).optional() -}); - -// Recursive types need to write this way. -// See https://zod.dev/?id=recursive-types -type ResolveConfig = z.infer & { - byDependency?: Record; -}; - -export function resolve(): z.ZodType { - return baseResolve.extend({ - byDependency: z.lazy(() => z.record(resolve())).optional() - }); -} diff --git a/packages/rspack/src/config/zod/snapshot.ts b/packages/rspack/src/config/zod/snapshot.ts deleted file mode 100644 index c65421779810..000000000000 --- a/packages/rspack/src/config/zod/snapshot.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { z } from "zod"; - -export function snapshot() { - return z.strictObject({ - module: z - .strictObject({ - hash: z.boolean().optional(), - timestamp: z.boolean().optional() - }) - .optional(), - resolve: z - .strictObject({ - hash: z.boolean().optional(), - timestamp: z.boolean().optional() - }) - .optional() - }); -} diff --git a/packages/rspack/src/config/zod/stats.ts b/packages/rspack/src/config/zod/stats.ts deleted file mode 100644 index ef0fc7e5b906..000000000000 --- a/packages/rspack/src/config/zod/stats.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { z } from "zod"; - -export function stats() { - return z - .enum(["none", "errors-only", "errors-warnings", "normal", "verbose"]) - .or(z.boolean()) - .or( - z.object({ - all: z.boolean().optional(), - assets: z.boolean().optional(), - chunkGroups: z.boolean().optional(), - chunks: z.boolean().optional(), - colors: z.boolean().optional(), - entrypoints: z.boolean().optional(), - errors: z.boolean().optional(), - errorsCount: z.boolean().optional(), - hash: z.boolean().optional(), - modules: z.boolean().optional(), - preset: z - .enum(["normal", "none", "verbose", "errors-only", "errors-warnings"]) - .optional(), - publicPath: z.boolean().optional(), - reasons: z.boolean().optional(), - warnings: z.boolean().optional(), - warningsCount: z.boolean().optional(), - outputPath: z.boolean().optional(), - chunkModules: z.boolean().optional(), - chunkRelations: z.boolean().optional(), - timings: z.boolean().optional(), - builtAt: z.boolean().optional(), - nestedModules: z.boolean().optional(), - source: z.boolean().optional() - }) - ); -} diff --git a/packages/rspack/src/config/zod/target.ts b/packages/rspack/src/config/zod/target.ts deleted file mode 100644 index 4eda8f5f7aab..000000000000 --- a/packages/rspack/src/config/zod/target.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { z } from "zod"; - -const allowTarget = z - .enum([ - "web", - "webworker", - "es3", - "es5", - "es2015", - "es2016", - "es2017", - "es2018", - "es2019", - "es2020", - "es2021", - "es2022", - "browserslist" - ]) - .or(z.literal("node")) - .or(z.literal("async-node")) - .or( - z.custom<`node${number}`>( - value => typeof value === "string" && /^node\d+$/.test(value) - ) - ) - .or( - z.custom<`async-node${number}`>( - value => typeof value === "string" && /^async-node\d+$/.test(value) - ) - ) - .or( - z.custom<`node${number}.${number}`>( - value => typeof value === "string" && /^node\d+\.\d+$/.test(value) - ) - ) - .or( - z.custom<`async-node${number}.${number}`>( - value => typeof value === "string" && /^async-node\d+\.\d+$/.test(value) - ) - ) - .or(z.literal("electron-main")) - .or( - z.custom<`electron${number}-main`>( - value => typeof value === "string" && /^electron\d+-main$/.test(value) - ) - ) - .or( - z.custom<`electron${number}.${number}-main`>( - value => - typeof value === "string" && /^electron\d+\.\d+-main$/.test(value) - ) - ) - .or(z.literal("electron-renderer")) - .or( - z.custom<`electron${number}-renderer`>( - value => typeof value === "string" && /^electron\d+-renderer$/.test(value) - ) - ) - .or( - z.custom<`electron${number}.${number}-renderer`>( - value => - typeof value === "string" && /^electron\d+\.\d+-renderer$/.test(value) - ) - ) - .or(z.literal("electron-preload")) - .or( - z.custom<`electron${number}-preload`>( - value => typeof value === "string" && /^electron\d+-preload$/.test(value) - ) - ) - .or( - z.custom<`electron${number}.${number}-preload`>( - value => - typeof value === "string" && /^electron\d+\.\d+-preload$/.test(value) - ) - ); - -export function target() { - return z.literal(false).or(allowTarget).or(allowTarget.array()); -} diff --git a/packages/rspack/src/config/zod/watch-options.ts b/packages/rspack/src/config/zod/watch-options.ts deleted file mode 100644 index 783c39fd7dd4..000000000000 --- a/packages/rspack/src/config/zod/watch-options.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { z } from "zod"; - -export function watchOptions() { - return z - .object({ - aggregateTimeout: z.number().optional(), - followSymlinks: z.boolean().optional(), - ignored: z - .string() - .array() - .or(z.instanceof(RegExp)) - .or(z.string()) - .optional(), - poll: z.number().or(z.boolean()).optional(), - stdin: z.boolean().optional() - }) - .strict(); -} diff --git a/packages/rspack/src/rspack.ts b/packages/rspack/src/rspack.ts index d0b00df9b76b..2ee3d6584753 100644 --- a/packages/rspack/src/rspack.ts +++ b/packages/rspack/src/rspack.ts @@ -13,7 +13,7 @@ import { applyRspackOptionsBaseDefaults, applyRspackOptionsDefaults, RspackPluginFunction, - validateConfig + rspackOptions } from "./config"; import { Compiler } from "./Compiler"; import { Stats } from "./Stats"; @@ -30,7 +30,7 @@ import { Callback } from "tapable"; import MultiStats from "./MultiStats"; import assert from "assert"; import { asArray, isNil } from "./util"; -import IgnoreWarningsPlugin from "./lib/ignoreWarningsPlugin"; +import { validate } from "./util/validate"; function createMultiCompiler(options: MultiRspackOptions): MultiCompiler { const compilers = options.map(createCompiler); @@ -113,7 +113,7 @@ function rspack( callback?: Callback | Callback ) { asArray(options).every(opts => { - validateConfig(opts); + validate(opts, rspackOptions); }); const create = () => { if (isMultiRspackOptions(options)) { diff --git a/packages/rspack/src/rspackOptionsApply.ts b/packages/rspack/src/rspackOptionsApply.ts index aa535748e73b..860164cada6a 100644 --- a/packages/rspack/src/rspackOptionsApply.ts +++ b/packages/rspack/src/rspackOptionsApply.ts @@ -10,7 +10,8 @@ import { RspackOptionsNormalized, Compiler, - OptimizationRuntimeChunkNormalized + OptimizationRuntimeChunkNormalized, + RspackPluginFunction } from "."; import fs from "graceful-fs"; @@ -96,7 +97,7 @@ export class RspackOptionsApply { if (minimize && minimizer) { for (const item of minimizer) { if (typeof item === "function") { - item.call(compiler, compiler); + (item as RspackPluginFunction).call(compiler, compiler); } else if (item !== "...") { item.apply(compiler); } diff --git a/packages/rspack/src/util/index.ts b/packages/rspack/src/util/index.ts index 520c0b046d2e..346ec580e181 100644 --- a/packages/rspack/src/util/index.ts +++ b/packages/rspack/src/util/index.ts @@ -1,7 +1,6 @@ import type { JsAssetInfo, JsStatsError } from "@rspack/binding"; import { AssetInfo } from "../Compilation"; import terminalLink from "terminal-link"; -import * as util from "util"; export function mapValues( record: Record, diff --git a/packages/rspack/src/util/validate.ts b/packages/rspack/src/util/validate.ts new file mode 100644 index 000000000000..e900d28cc3fb --- /dev/null +++ b/packages/rspack/src/util/validate.ts @@ -0,0 +1,29 @@ +import { z } from "zod"; +import { fromZodError } from "zod-validation-error"; + +export function validate(opts: any, schema: T) { + const res = schema.safeParse(opts); + if (!res.success) { + const strategy = process.env.RSPACK_CONFIG_VALIDATE ?? "strict"; + if (strategy === "loose-silent") return; + const issueSeparator = "$issue$"; + const prefixSeparator = "$prefix$"; + const validationErr = fromZodError(res.error, { + prefix: "Configuration error", + prefixSeparator, + issueSeparator + }); + // The output validationErr.message looks like + // `Configuration error$prefix$xxxx error$issue$yyy error$issue$zzz error` + const [prefix, reason] = validationErr.message.split(prefixSeparator); + const reasonItem = reason.split(issueSeparator); + const friendlyErr = new Error( + `${prefix}:\n${reasonItem.map(item => `- ${item}`).join("\n")}` + ); + if (strategy === "loose") { + console.error(friendlyErr.message); + } else { + throw friendlyErr; + } + } +} diff --git a/packages/rspack/tests/__snapshots__/Defaults.unittest.ts.snap b/packages/rspack/tests/__snapshots__/Defaults.unittest.ts.snap index 9eadbe140b17..728f86b31f83 100644 --- a/packages/rspack/tests/__snapshots__/Defaults.unittest.ts.snap +++ b/packages/rspack/tests/__snapshots__/Defaults.unittest.ts.snap @@ -24,7 +24,9 @@ exports[`snapshots should have the correct base config 1`] = ` }, "lazyCompilation": false, "newSplitChunks": true, - "rspackFuture": {}, + "rspackFuture": { + "newResolver": false, + }, }, "externals": undefined, "externalsPresets": { diff --git a/packages/rspack/tests/configCases/split-chunks/max-size-casing/webpack.config.js b/packages/rspack/tests/configCases/split-chunks/max-size-casing/webpack.config.js index 30a479d8b646..780a4088dfc5 100644 --- a/packages/rspack/tests/configCases/split-chunks/max-size-casing/webpack.config.js +++ b/packages/rspack/tests/configCases/split-chunks/max-size-casing/webpack.config.js @@ -8,7 +8,7 @@ module.exports = { }, optimization: { splitChunks: { - hidePathInfo: false, + // hidePathInfo: false, minSize: 50, maxSize: 100 } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6d2786a675d6..e1dd021ef764 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -912,7 +912,6 @@ importers: '@types/watchpack': ^2.4.0 '@types/webpack-sources': 3.2.0 '@types/ws': 8.5.3 - ajv: ^8.12.0 babel-loader: ^9.1.0 babel-plugin-import: ^1.13.5 browserslist: ^4.21.3 @@ -969,7 +968,6 @@ importers: '@types/watchpack': 2.4.0 '@types/webpack-sources': 3.2.0 '@types/ws': 8.5.3 - ajv: 8.12.0 babel-loader: 9.1.2 babel-plugin-import: 1.13.5 copy-webpack-plugin: 5.1.2 @@ -1509,7 +1507,7 @@ packages: optional: true dependencies: ajv: 8.12.0 - ajv-formats: 2.1.1_ajv@8.12.0 + ajv-formats: 2.1.1 jsonc-parser: 3.2.0 rxjs: 7.8.1 source-map: 0.7.4 @@ -1525,7 +1523,7 @@ packages: optional: true dependencies: ajv: 8.12.0 - ajv-formats: 2.1.1_ajv@8.12.0 + ajv-formats: 2.1.1 chokidar: 3.5.3 jsonc-parser: 3.2.0 rxjs: 7.8.1 @@ -12272,10 +12270,8 @@ packages: ajv: 6.12.6 dev: true - /ajv-formats/2.1.1_ajv@8.12.0: + /ajv-formats/2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 peerDependenciesMeta: ajv: optional: true @@ -24521,7 +24517,7 @@ packages: dependencies: '@types/json-schema': 7.0.11 ajv: 8.12.0 - ajv-formats: 2.1.1_ajv@8.12.0 + ajv-formats: 2.1.1 ajv-keywords: 5.1.0_ajv@8.12.0 /schema-utils/4.0.1: @@ -24530,7 +24526,7 @@ packages: dependencies: '@types/json-schema': 7.0.11 ajv: 8.12.0 - ajv-formats: 2.1.1_ajv@8.12.0 + ajv-formats: 2.1.1 ajv-keywords: 5.1.0_ajv@8.12.0 /scroll-into-view-if-needed/2.2.20: From d877cc474f52b23a68510b20e51944d8bd12d2a1 Mon Sep 17 00:00:00 2001 From: Hana Date: Wed, 6 Sep 2023 14:43:27 +0800 Subject: [PATCH 08/30] fix: test snapshot path (#4132) --- packages/rspack/tests/Stats.test.ts | 121 +++----------------- packages/rspack/tests/fixtures/abc-query.js | 2 +- 2 files changed, 14 insertions(+), 109 deletions(-) diff --git a/packages/rspack/tests/Stats.test.ts b/packages/rspack/tests/Stats.test.ts index f8a019e72bf1..ff20f79a398f 100644 --- a/packages/rspack/tests/Stats.test.ts +++ b/packages/rspack/tests/Stats.test.ts @@ -244,7 +244,7 @@ describe("Stats", () => { "hotModuleReplacement": false, }, "name": "main.js", - "size": 478, + "size": 394, "type": "asset", }, ], @@ -339,45 +339,6 @@ describe("Stats", () => { };", "type": "module", }, - { - "assets": [], - "chunks": [ - "main", - ], - "id": "53", - "identifier": "/tests/fixtures/b.js?b=2", - "issuer": "/tests/fixtures/abc-query.js", - "issuerId": "661", - "issuerName": "./fixtures/abc-query.js", - "issuerPath": [ - { - "id": "661", - "identifier": "/tests/fixtures/abc-query.js", - "name": "./fixtures/abc-query.js", - }, - ], - "moduleType": "javascript/auto", - "name": "./fixtures/b.js?b=2", - "nameForCondition": "/tests/fixtures/b.js", - "reasons": [ - { - "moduleId": "661", - "moduleIdentifier": "/tests/fixtures/abc-query.js", - "moduleName": "./fixtures/abc-query.js", - "type": "cjs require", - "userRequest": "./b?b=2", - }, - ], - "size": 94, - "source": "module.exports = function b() { - return "This is b"; - }; - - // Test CJS top-level return - return; - ", - "type": "module", - }, { "assets": [], "chunks": [ @@ -431,9 +392,9 @@ describe("Stats", () => { "userRequest": "./fixtures/abc-query", }, ], - "size": 96, + "size": 99, "source": "exports.a = require("./a?a=1"); - exports.b = require("./b?b=2"); + // exports.b = require("./b?b=2"); exports.c = require("./c?c=3"); ", "type": "module", @@ -444,7 +405,7 @@ describe("Stats", () => { ], "parents": [], "siblings": [], - "size": 372, + "size": 281, "type": "chunk", }, ], @@ -453,37 +414,20 @@ describe("Stats", () => { "assets": [ { "name": "main.js", - "size": 478, + "size": 394, }, ], - "assetsSize": 478, + "assetsSize": 394, "chunks": [ "main", ], "name": "main", }, }, - "errors": [ - { - "formatted": "error[javascript]: JavaScript parsing error - ┌─ tests/fixtures/b.js:6:1 - │ - 2 │ return "This is b"; - 3 │ }; - 4 │ - 5 │ // Test CJS top-level return - 6 │ return; - │ ^^^^^^^ Return statement is not allowed here - 7 │ - - ", - "message": "Return statement is not allowed here", - "title": "JavaScript parsing error", - }, - ], - "errorsCount": 1, + "errors": [], + "errorsCount": 0, "filteredModules": undefined, - "hash": "bee17f084d89e18f1263", + "hash": "5eccb83e369e53af1313", "logging": {}, "modules": [ { @@ -561,45 +505,6 @@ describe("Stats", () => { };", "type": "module", }, - { - "assets": [], - "chunks": [ - "main", - ], - "id": "53", - "identifier": "/tests/fixtures/b.js?b=2", - "issuer": "/tests/fixtures/abc-query.js", - "issuerId": "661", - "issuerName": "./fixtures/abc-query.js", - "issuerPath": [ - { - "id": "661", - "identifier": "/tests/fixtures/abc-query.js", - "name": "./fixtures/abc-query.js", - }, - ], - "moduleType": "javascript/auto", - "name": "./fixtures/b.js?b=2", - "nameForCondition": "/tests/fixtures/b.js", - "reasons": [ - { - "moduleId": "661", - "moduleIdentifier": "/tests/fixtures/abc-query.js", - "moduleName": "./fixtures/abc-query.js", - "type": "cjs require", - "userRequest": "./b?b=2", - }, - ], - "size": 94, - "source": "module.exports = function b() { - return "This is b"; - }; - - // Test CJS top-level return - return; - ", - "type": "module", - }, { "assets": [], "chunks": [ @@ -653,9 +558,9 @@ describe("Stats", () => { "userRequest": "./fixtures/abc-query", }, ], - "size": 96, + "size": 99, "source": "exports.a = require("./a?a=1"); - exports.b = require("./b?b=2"); + // exports.b = require("./b?b=2"); exports.c = require("./c?c=3"); ", "type": "module", @@ -666,10 +571,10 @@ describe("Stats", () => { "assets": [ { "name": "main.js", - "size": 478, + "size": 394, }, ], - "assetsSize": 478, + "assetsSize": 394, "chunks": [ "main", ], diff --git a/packages/rspack/tests/fixtures/abc-query.js b/packages/rspack/tests/fixtures/abc-query.js index f1ff8891c394..eb18ea7bf127 100644 --- a/packages/rspack/tests/fixtures/abc-query.js +++ b/packages/rspack/tests/fixtures/abc-query.js @@ -1,3 +1,3 @@ exports.a = require("./a?a=1"); -exports.b = require("./b?b=2"); +// exports.b = require("./b?b=2"); exports.c = require("./c?c=3"); From 0f61d3ec1f99ada0d4386869c37dbfab36ee5644 Mon Sep 17 00:00:00 2001 From: underfin Date: Wed, 6 Sep 2023 16:55:18 +0800 Subject: [PATCH 09/30] fix: use module.rules.include to avoid runtime inject it self (#4134) --- packages/rspack-dev-client/package.json | 2 +- .../src/runtimePathRegexp.js | 17 ----------------- .../rspack-dev-client/src/runtimePaths.js | 19 +++++++++++++++++++ packages/rspack-dev-server/src/server.ts | 5 +++-- 4 files changed, 23 insertions(+), 20 deletions(-) delete mode 100644 packages/rspack-dev-client/src/runtimePathRegexp.js create mode 100644 packages/rspack-dev-client/src/runtimePaths.js diff --git a/packages/rspack-dev-client/package.json b/packages/rspack-dev-client/package.json index cabc8667921e..2cc9a665514e 100644 --- a/packages/rspack-dev-client/package.json +++ b/packages/rspack-dev-client/package.json @@ -36,6 +36,6 @@ "exports": { "./react-refresh": "./src/reactRefresh.js", "./react-refresh-entry": "./src/reactRefreshEntry.js", - "./runtime-path-regexp": "./src/runtimePathRegexp.js" + "./runtime-paths": "./src/runtimePaths.js" } } \ No newline at end of file diff --git a/packages/rspack-dev-client/src/runtimePathRegexp.js b/packages/rspack-dev-client/src/runtimePathRegexp.js deleted file mode 100644 index c42904619f7b..000000000000 --- a/packages/rspack-dev-client/src/runtimePathRegexp.js +++ /dev/null @@ -1,17 +0,0 @@ -const path = require("path"); -const reactRefreshPath = require.resolve("./reactRefresh.js"); -const RefreshUtilsPath = require.resolve( - "@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils", - { - paths: [reactRefreshPath] - } -); -const RefreshRuntimeDirPath = path.dirname( - require.resolve("react-refresh", { - paths: [reactRefreshPath] - }) -); - -exports.runtimePathRegexp = new RegExp( - `${reactRefreshPath}|${RefreshUtilsPath}|^${RefreshRuntimeDirPath}` -); diff --git a/packages/rspack-dev-client/src/runtimePaths.js b/packages/rspack-dev-client/src/runtimePaths.js new file mode 100644 index 000000000000..8d23c16347e5 --- /dev/null +++ b/packages/rspack-dev-client/src/runtimePaths.js @@ -0,0 +1,19 @@ +const path = require('path') +const reactRefreshPath = require.resolve('./reactRefresh.js') +const RefreshUtilsPath = require.resolve( + '@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils', + { + paths: [reactRefreshPath] + } +) +const RefreshRuntimeDirPath = path.dirname( + require.resolve('react-refresh', { + paths: [reactRefreshPath] + }) +) + +exports.runtimePaths = [ + reactRefreshPath, + RefreshUtilsPath, + RefreshRuntimeDirPath +] diff --git a/packages/rspack-dev-server/src/server.ts b/packages/rspack-dev-server/src/server.ts index 5a5dc96988d3..409c0be53baf 100644 --- a/packages/rspack-dev-server/src/server.ts +++ b/packages/rspack-dev-server/src/server.ts @@ -17,7 +17,7 @@ import WebpackDevServer from "webpack-dev-server"; import type { ResolvedDevServer, DevServer } from "./config"; import { getRspackMemoryAssets } from "./middleware"; // @ts-expect-error -import { runtimePathRegexp } from "@rspack/dev-client/runtime-path-regexp"; +import { runtimePaths } from "@rspack/dev-client/runtime-paths"; export class RspackDevServer extends WebpackDevServer { /** @@ -352,8 +352,9 @@ export class RspackDevServer extends WebpackDevServer { this.addAdditionalEntries(compiler); if (this.options.hot) { + console.log(runtimePaths); compiler.options.module.rules.push({ - test: runtimePathRegexp, + include: runtimePaths, type: "js" }); } From ad727a95eb0fc5f7f61f53404c16e0d6ec51cd36 Mon Sep 17 00:00:00 2001 From: underfin Date: Wed, 6 Sep 2023 17:24:42 +0800 Subject: [PATCH 10/30] fix: css loading initial chunks (#4136) --- .../src/runtime_module/css_loading.rs | 50 ++++++++----------- .../src/runtime_module/get_chunk_filename.rs | 20 +++----- .../runtime/css_loading_with_loading.js | 15 ++++++ .../runtime/split-css-chunk-async/common.css | 3 ++ .../runtime/split-css-chunk-async/common.js | 1 + .../runtime/split-css-chunk-async/index1.js | 10 ++++ .../runtime/split-css-chunk-async/index2.js | 1 + .../runtime/split-css-chunk-async/index3.js | 1 + .../share.css} | 0 .../runtime/split-css-chunk-async/share.js | 4 ++ .../split-css-chunk-async/webpack.config.js | 29 +++++++++++ .../runtime/split-css-chunk/common.js | 2 + .../runtime/split-css-chunk/index1.js | 5 +- .../runtime/split-css-chunk/index2.js | 2 - 14 files changed, 96 insertions(+), 47 deletions(-) create mode 100644 packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.css create mode 100644 packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.js create mode 100644 packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index1.js create mode 100644 packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index2.js create mode 100644 packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index3.js rename packages/rspack/tests/runtimeCases/runtime/{split-css-chunk/other.css => split-css-chunk-async/share.css} (100%) create mode 100644 packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/share.js create mode 100644 packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/webpack.config.js diff --git a/crates/rspack_plugin_runtime/src/runtime_module/css_loading.rs b/crates/rspack_plugin_runtime/src/runtime_module/css_loading.rs index 676ef4b522bc..ce005b99aec7 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/css_loading.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/css_loading.rs @@ -46,35 +46,21 @@ impl RuntimeModule for CssLoadingRuntimeModule { .contains(RuntimeGlobals::ENSURE_CHUNK_HANDLERS); let initial_chunks = chunk.get_all_initial_chunks(&compilation.chunk_group_by_ukey); + let mut initial_chunk_ids_with_css = HashSet::default(); + let mut initial_chunk_ids_without_css = HashSet::default(); + for chunk_ukey in initial_chunks.iter() { + let chunk = compilation + .chunk_by_ukey + .get(chunk_ukey) + .expect("Chunk not found"); + if chunk_has_css(chunk_ukey, compilation) { + initial_chunk_ids_with_css.insert(chunk.expect_id().to_string()); + } else { + initial_chunk_ids_without_css.insert(chunk.expect_id().to_string()); + } + } - // `initial_chunk_ids_without_css` is render to `installedChunks` - // `initial_chunk_ids_with_css` is render to `loadCssChunkData`, it will be add to `installedChunks` - // so here direct use `initial_chunk_ids` - let initial_chunk_ids = initial_chunks - .iter() - .map(|chunk_ukey| { - let chunk = compilation - .chunk_by_ukey - .get(chunk_ukey) - .expect("Chunk not found"); - chunk.expect_id().to_string() - }) - .collect::>(); - // let mut initial_chunk_ids_with_css = HashSet::default(); - // let mut initial_chunk_ids_without_css = HashSet::default(); - // for chunk_ukey in initial_chunks.iter() { - // let chunk = compilation - // .chunk_by_ukey - // .get(chunk_ukey) - // .expect("Chunk not found"); - // if chunk_has_css(chunk_ukey, compilation) { - // initial_chunk_ids_with_css.insert(chunk.expect_id().to_string()); - // } else { - // initial_chunk_ids_without_css.insert(chunk.expect_id().to_string()); - // } - // } - - if !with_hmr && !with_loading && initial_chunk_ids.is_empty() { + if !with_hmr && !with_loading && initial_chunk_ids_with_css.is_empty() { return RawSource::from("").boxed(); } @@ -87,7 +73,7 @@ impl RuntimeModule for CssLoadingRuntimeModule { // only render chunk without css. See packages/rspack/tests/runtimeCases/runtime/split-css-chunk test. source.add(RawSource::from(format!( "var installedChunks = {};\n", - &stringify_chunks(&initial_chunk_ids, 0) + &stringify_chunks(&initial_chunk_ids_without_css, 0) ))); source.add(RawSource::from( @@ -102,8 +88,14 @@ impl RuntimeModule for CssLoadingRuntimeModule { compilation .chunk_graph .get_chunk_condition_map(&chunk_ukey, compilation, chunk_has_css); + let chunk_loading_global_expr = format!( + "{}['{}']", + &compilation.options.output.global_object, + &compilation.options.output.chunk_loading_global + ); source.add(RawSource::from( include_str!("runtime/css_loading_with_loading.js") + .replace("$CHUNK_LOADING_GLOBAL_EXPR$", &chunk_loading_global_expr) .replace("CSS_MATCHER", &render_condition_map(&condition_map)), )); } diff --git a/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_filename.rs b/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_filename.rs index 82705199de7e..d8b2720cd29a 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_filename.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_filename.rs @@ -6,7 +6,7 @@ use rspack_core::{ use rspack_identifier::Identifier; use rustc_hash::FxHashMap as HashMap; -use super::utils::{chunk_has_css, chunk_has_js}; +use super::utils::chunk_has_css; use crate::impl_runtime_module; #[derive(Debug, Eq)] @@ -79,17 +79,13 @@ impl RuntimeModule for GetChunkFilenameRuntimeModule { for chunk_ukey in chunks.iter() { if let Some(chunk) = compilation.chunk_by_ukey.get(chunk_ukey) { let filename_template = match self.source_type { - SourceType::JavaScript => { - if chunk_has_js(chunk_ukey, compilation) { - Some(get_js_chunk_filename_template( - chunk, - &compilation.options.output, - &compilation.chunk_group_by_ukey, - )) - } else { - None - } - } + // TODO webpack different + // css chunk will generate a js chunk, so here add it. + SourceType::JavaScript => Some(get_js_chunk_filename_template( + chunk, + &compilation.options.output, + &compilation.chunk_group_by_ukey, + )), SourceType::Css => { if chunk_has_css(chunk_ukey, compilation) { Some(get_css_chunk_filename_template( diff --git a/crates/rspack_plugin_runtime/src/runtime_module/runtime/css_loading_with_loading.js b/crates/rspack_plugin_runtime/src/runtime_module/runtime/css_loading_with_loading.js index 4cbc5916e925..1f2aecd3caf8 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/runtime/css_loading_with_loading.js +++ b/crates/rspack_plugin_runtime/src/runtime_module/runtime/css_loading_with_loading.js @@ -53,3 +53,18 @@ __webpack_require__.f.css = function (chunkId, promises) { } } }; +// TODO: diffrent with webpack +// webpack using `loadCssChunkData` and detect css variables to add install chunk. +// Because rspack the css chunk is always generate one js chunk, so here use js chunk to add install chunk. +var loadCssChunkCallback = function (parentChunkLoadingFunction, data) { + var chunkIds = data[0]; + if (parentChunkLoadingFunction) parentChunkLoadingFunction(data); + for (var i = 0; i < chunkIds.length; i++) { + installedChunks[chunkIds[i]] = 0; + } +}; +var chunkLoadingGlobal = $CHUNK_LOADING_GLOBAL_EXPR$ = $CHUNK_LOADING_GLOBAL_EXPR$ || []; +chunkLoadingGlobal.push = loadCssChunkCallback.bind( + null, + chunkLoadingGlobal.push.bind(chunkLoadingGlobal) +); \ No newline at end of file diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.css b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.css new file mode 100644 index 000000000000..c4845173e30c --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.css @@ -0,0 +1,3 @@ +#dynamic { + background: red; +} \ No newline at end of file diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.js new file mode 100644 index 000000000000..3b529e8a1005 --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.js @@ -0,0 +1 @@ +import "./common.css"; diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index1.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index1.js new file mode 100644 index 000000000000..47d51f290011 --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index1.js @@ -0,0 +1,10 @@ +it("should load css chunk", function (done) { + import("./share").then(module => { + expect(module.value).toBe(1); + // test is only for css loading + if (__webpack_require__.f.css) { + expect(document.getElementsByTagName("link").length).toBe(2); + } + done(); + }); +}); diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index2.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index2.js new file mode 100644 index 000000000000..a8a18775ed3b --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index2.js @@ -0,0 +1 @@ +import "./common"; diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index3.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index3.js new file mode 100644 index 000000000000..57e149a1413e --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index3.js @@ -0,0 +1 @@ +import "./share"; diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/other.css b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/share.css similarity index 100% rename from packages/rspack/tests/runtimeCases/runtime/split-css-chunk/other.css rename to packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/share.css diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/share.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/share.js new file mode 100644 index 000000000000..8dff76c666aa --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/share.js @@ -0,0 +1,4 @@ +import "./share.css"; +import "./common.css"; + +export const value = 1; diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/webpack.config.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/webpack.config.js new file mode 100644 index 000000000000..c45ae6cace95 --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/webpack.config.js @@ -0,0 +1,29 @@ +module.exports = { + entry: { + main: './index1.js', + main2: './index2.js', + main3: './index3.js' + }, + output: { + chunkFilename: '[id].[contenthash].js' + }, + optimization: { + splitChunks: { + cacheGroups: { + common: { + chunks: 'all', + test: /common/, + enforce: true, + name: 'common' + }, + share: { + chunks: 'all', + test: /share/, + enforce: true, + name: 'share' + } + } + }, + runtimeChunk: 'single' + } +} diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/common.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/common.js index 3b529e8a1005..0fc1dc430c49 100644 --- a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/common.js +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/common.js @@ -1 +1,3 @@ import "./common.css"; + +export const value = 1; diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index1.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index1.js index d8511e1e6195..d056a758b987 100644 --- a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index1.js +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index1.js @@ -1,5 +1,6 @@ it("should load css chunk", function (done) { import("./common").then(module => { + expect(module.value).toBe(1); // test is only for css loading if (__webpack_require__.f.css) { expect(document.getElementsByTagName("link").length).toBe(1); @@ -7,7 +8,3 @@ it("should load css chunk", function (done) { done(); }); }); - -import "./common.css"; - -// ./common.css is initial chunks and also be async chunks. diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index2.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index2.js index 77130c9452f6..a8a18775ed3b 100644 --- a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index2.js +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index2.js @@ -1,3 +1 @@ import "./common"; - -import("./other.css"); From 177aedbdcf7a4eb6b5f0ffb9d7c8bca5f49930c2 Mon Sep 17 00:00:00 2001 From: Gengkun Date: Wed, 6 Sep 2023 19:13:43 +0800 Subject: [PATCH 11/30] chore: format PR template (#4138) --- .github/PULL_REQUEST_TEMPLATE.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3fd52875b2d9..ff77bf5564dc 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -20,8 +20,7 @@ ## Require Documentation? -[ ] No - -[ ] Yes, the corresponding rspack-website PR is \_\_ +- [ ] No +- [ ] Yes, the corresponding rspack-website PR is \_\_ From 22ab0a9186f6cad5a4585e266f21cf97ff43e372 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Sep 2023 20:32:20 +0800 Subject: [PATCH 12/30] Release Packages:0.3.2 (#4113) Reelase Packages:0.3.2 Co-authored-by: github-actions[bot] --- crates/node_binding/package.json | 2 +- npm/darwin-arm64/package.json | 2 +- npm/darwin-x64/package.json | 2 +- npm/linux-x64-gnu/package.json | 2 +- npm/win32-x64-msvc/package.json | 2 +- package.json | 2 +- packages/create-rspack/package.json | 2 +- packages/rspack-cli/package.json | 2 +- packages/rspack-dev-client/package.json | 2 +- packages/rspack-dev-server/package.json | 2 +- packages/rspack-plugin-html/package.json | 2 +- packages/rspack-plugin-minify/package.json | 2 +- packages/rspack-plugin-node-polyfill/package.json | 2 +- packages/rspack/package.json | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/node_binding/package.json b/crates/node_binding/package.json index b4f2ed4d9f91..21cff5753499 100644 --- a/crates/node_binding/package.json +++ b/crates/node_binding/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/binding", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Node binding for rspack", "main": "binding.js", diff --git a/npm/darwin-arm64/package.json b/npm/darwin-arm64/package.json index 146f6985bc95..1ff4f4623b60 100644 --- a/npm/darwin-arm64/package.json +++ b/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/binding-darwin-arm64", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Node binding for rspack", "main": "rspack.darwin-arm64.node", diff --git a/npm/darwin-x64/package.json b/npm/darwin-x64/package.json index 4e13fb90dff6..488bec64304b 100644 --- a/npm/darwin-x64/package.json +++ b/npm/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/binding-darwin-x64", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Node binding for rspack", "main": "rspack.darwin-x64.node", diff --git a/npm/linux-x64-gnu/package.json b/npm/linux-x64-gnu/package.json index 899a89011ab8..b9f230c3a1ad 100644 --- a/npm/linux-x64-gnu/package.json +++ b/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/binding-linux-x64-gnu", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Node binding for rspack", "main": "rspack.linux-x64-gnu.node", diff --git a/npm/win32-x64-msvc/package.json b/npm/win32-x64-msvc/package.json index 4a484f34d374..ed6f8f80cc72 100644 --- a/npm/win32-x64-msvc/package.json +++ b/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/binding-win32-x64-msvc", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Node binding for rspack", "main": "rspack.win32-x64-msvc.node", diff --git a/package.json b/package.json index 717e9aeaeb92..084907596c38 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "monorepo", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "A Fast Rust-based web bundler", "private": true, diff --git a/packages/create-rspack/package.json b/packages/create-rspack/package.json index 2b24c5864e46..1fb2cb9f270f 100644 --- a/packages/create-rspack/package.json +++ b/packages/create-rspack/package.json @@ -1,6 +1,6 @@ { "name": "create-rspack", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "main": "index.js", "bin": { diff --git a/packages/rspack-cli/package.json b/packages/rspack-cli/package.json index 4941063453f3..2d6f2eca4e40 100644 --- a/packages/rspack-cli/package.json +++ b/packages/rspack-cli/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/cli", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "CLI for rspack", "bin": { diff --git a/packages/rspack-dev-client/package.json b/packages/rspack-dev-client/package.json index 2cc9a665514e..1e8b13c7d12c 100644 --- a/packages/rspack-dev-client/package.json +++ b/packages/rspack-dev-client/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/dev-client", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Development client for rspack", "scripts": { diff --git a/packages/rspack-dev-server/package.json b/packages/rspack-dev-server/package.json index deec53db4fc3..815aee3c3349 100644 --- a/packages/rspack-dev-server/package.json +++ b/packages/rspack-dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/dev-server", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Development server for rspack", "main": "./dist/index.js", diff --git a/packages/rspack-plugin-html/package.json b/packages/rspack-plugin-html/package.json index 0b70f3566091..18ed78ce0c43 100644 --- a/packages/rspack-plugin-html/package.json +++ b/packages/rspack-plugin-html/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/plugin-html", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "HTML plugin for rspack", "main": "index.cjs", diff --git a/packages/rspack-plugin-minify/package.json b/packages/rspack-plugin-minify/package.json index 088a31e5657a..95af1e1d0b72 100644 --- a/packages/rspack-plugin-minify/package.json +++ b/packages/rspack-plugin-minify/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/plugin-minify", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Minify plugin for rspack", "main": "src/index.js", diff --git a/packages/rspack-plugin-node-polyfill/package.json b/packages/rspack-plugin-node-polyfill/package.json index fce330c32e79..5754f26ca978 100644 --- a/packages/rspack-plugin-node-polyfill/package.json +++ b/packages/rspack-plugin-node-polyfill/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/plugin-node-polyfill", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Node polyfill plugin for rspack", "main": "src/index.js", diff --git a/packages/rspack/package.json b/packages/rspack/package.json index cf96f47f803b..b5a2f0c2a379 100644 --- a/packages/rspack/package.json +++ b/packages/rspack/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/core", - "version": "0.3.1", + "version": "0.3.2", "webpackVersion": "5.75.0", "license": "MIT", "description": "A Fast Rust-based Web Bundler", From 36b72b20bbaf5c6ea3b66ee561a1adad8994617f Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY Date: Wed, 6 Sep 2023 20:45:37 +0800 Subject: [PATCH 13/30] feat: flagDependencyUsagePlugin (#4114) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 🤖 init * chore: 🤖 ck point * chore: 🤖 finish set has use info * chore: 🤖 set used without info * chore: 🤖 flag * chore: 🤖 refactor get export info * chore: 🤖 refactor and get nested_exports info * chore: 🤖 update * chore: 🤖 process ref module * chore: 🤖 update * chore: 🤖 ck point * chore: 🤖 remove test file * chore: 🤖 finish process module ref * chore: 🤖 update get referenced exports * chore: 🤖 update ref * chore: 🤖 update * chore: 🤖 temp file * chore: 🤖 finish porting * chore: 🤖 remove temp file * chore: 🤖 lint * chore: 🤖 fix typos --- .../dependency/context_element_dependency.rs | 10 +- crates/rspack_core/src/dependency/mod.rs | 13 +- crates/rspack_core/src/exports_info.rs | 248 +++++++++++-- .../src/module_graph/connection.rs | 12 +- crates/rspack_core/src/module_graph/mod.rs | 26 +- .../commonjs/require_resolve_dependency.rs | 8 +- ...ny_export_imported_specifier_dependency.rs | 27 +- .../esm/harmony_import_dependency.rs | 13 +- .../harmony_import_specifier_dependency.rs | 22 +- .../src/dependency/esm/import_dependency.rs | 10 +- .../src/dependency/worker/mod.rs | 8 +- .../src/plugin/flag_usage_plugin.rs | 337 ++++++++++++++++++ .../src/plugin/mod.rs | 1 + .../src/plugin/provided_exports_plugin.rs | 29 +- crates/rspack_plugin_javascript/src/utils.rs | 14 +- crates/rspack_plugin_wasm/src/dependency.rs | 8 +- 16 files changed, 673 insertions(+), 113 deletions(-) create mode 100644 crates/rspack_plugin_javascript/src/plugin/flag_usage_plugin.rs diff --git a/crates/rspack_core/src/dependency/context_element_dependency.rs b/crates/rspack_core/src/dependency/context_element_dependency.rs index f583cac4339c..5c05084d12d3 100644 --- a/crates/rspack_core/src/dependency/context_element_dependency.rs +++ b/crates/rspack_core/src/dependency/context_element_dependency.rs @@ -2,7 +2,7 @@ use swc_core::ecma::atoms::JsWord; use crate::{ AsDependencyTemplate, Context, ContextMode, ContextOptions, Dependency, DependencyCategory, - DependencyId, DependencyType, ExportsReferencedType, ModuleDependency, ModuleGraph, + DependencyId, DependencyType, ExtendedReferencedExport, ModuleDependency, ModuleGraph, ReferencedExport, RuntimeSpec, }; @@ -72,12 +72,12 @@ impl ModuleDependency for ContextElementDependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { + _runtime: Option<&RuntimeSpec>, + ) -> Vec { if let Some(referenced_exports) = &self.referenced_exports { - vec![ReferencedExport::new(referenced_exports.clone(), false)].into() + vec![ReferencedExport::new(referenced_exports.clone(), false).into()] } else { - ExportsReferencedType::Object + vec![ExtendedReferencedExport::Array(vec![])] } } } diff --git a/crates/rspack_core/src/dependency/mod.rs b/crates/rspack_core/src/dependency/mod.rs index 29db60470114..eee0e88aefcf 100644 --- a/crates/rspack_core/src/dependency/mod.rs +++ b/crates/rspack_core/src/dependency/mod.rs @@ -31,7 +31,8 @@ use dyn_clone::{clone_trait_object, DynClone}; use crate::{ ChunkGroupOptionsKindRef, ConnectionState, Context, ContextMode, ContextOptions, ErrorSpan, - ModuleGraph, ModuleGraphConnection, ModuleIdentifier, ReferencedExport, RuntimeSpec, + ExtendedReferencedExport, ModuleGraph, ModuleGraphConnection, ModuleIdentifier, ReferencedExport, + RuntimeSpec, }; // Used to describe dependencies' types, see webpack's `type` getter in `Dependency` @@ -300,7 +301,7 @@ impl AsModuleDependency for T { pub type DependencyConditionFn = Box; pub trait Function: - Fn(&ModuleGraphConnection, &RuntimeSpec, &ModuleGraph) -> ConnectionState + Send + Sync + Fn(&ModuleGraphConnection, Option<&RuntimeSpec>, &ModuleGraph) -> ConnectionState + Send + Sync { fn clone_boxed(&self) -> Box; } @@ -309,7 +310,7 @@ pub trait Function: impl Function for T where T: 'static - + Fn(&ModuleGraphConnection, &RuntimeSpec, &ModuleGraph) -> ConnectionState + + Fn(&ModuleGraphConnection, Option<&RuntimeSpec>, &ModuleGraph) -> ConnectionState + Send + Sync + Clone, @@ -378,9 +379,9 @@ pub trait ModuleDependency: Dependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { - ExportsReferencedType::Object + _runtime: Option<&RuntimeSpec>, + ) -> Vec { + vec![ExtendedReferencedExport::Array(vec![])] } // an identifier to merge equal requests diff --git a/crates/rspack_core/src/exports_info.rs b/crates/rspack_core/src/exports_info.rs index 18853cc6fcc6..c14455badb1f 100644 --- a/crates/rspack_core/src/exports_info.rs +++ b/crates/rspack_core/src/exports_info.rs @@ -107,7 +107,7 @@ impl ExportsInfoId { if let Some(ref exclude_exports) = exclude_exports { for name in exclude_exports { - self.export_info_mut(name, mg); + self.get_export_info(name, mg); } } @@ -222,7 +222,7 @@ impl ExportsInfoId { unreachable!() } - pub fn export_info_mut(&self, name: &JsWord, mg: &mut ModuleGraph) -> ExportsInfoId { + pub fn get_export_info(&self, name: &JsWord, mg: &mut ModuleGraph) -> ExportInfoId { let exports_info = mg.get_exports_info_by_id(self); let mut cur = exports_info; // get redirect chain, because you can't pass a mut ref into a recursive call @@ -239,8 +239,8 @@ impl ExportsInfoId { let is_last = i == len - 1; let exports_info = mg.get_exports_info_by_id(&id); let other_exports_info = exports_info.other_exports_info; - if exports_info.exports.contains_key(name) { - return id; + if let Some(export_info_id) = exports_info.exports.get(name) { + return *export_info_id; } if is_last { let other_export_info = mg @@ -254,7 +254,7 @@ impl ExportsInfoId { let exports_info = mg.get_exports_info_mut_by_id(&id); exports_info._exports_are_ordered = false; exports_info.exports.insert(name.clone(), new_info_id); - return id; + return new_info_id; } } unreachable!() @@ -273,6 +273,77 @@ impl ExportsInfoId { } Some(*self) } + + pub fn set_has_use_info(&self, mg: &mut ModuleGraph) { + let exports_info = mg.get_exports_info_by_id(self); + let side_effects_only_info_id = exports_info._side_effects_only_info; + let redirect_to_id = exports_info.redirect_to; + let other_exports_info_id = exports_info.other_exports_info; + // this clone aiming to avoid use the mutable ref and immutable ref at the same time. + let export_id_list = exports_info.exports.values().cloned().collect::>(); + for export_info in export_id_list { + export_info.set_has_use_info(mg); + } + side_effects_only_info_id.set_has_use_info(mg); + if let Some(redirect) = redirect_to_id { + redirect.set_has_use_info(mg); + } else { + other_exports_info_id.set_has_use_info(mg); + let other_exports_info = mg.get_export_info_mut_by_id(&other_exports_info_id); + if other_exports_info.can_mangle_use.is_none() { + other_exports_info.can_mangle_use = Some(true); + } + } + } + + pub fn set_used_without_info(&self, mg: &mut ModuleGraph, runtime: Option<&RuntimeSpec>) -> bool { + let mut changed = false; + let exports_info = mg.get_exports_info_mut_by_id(self); + let redirect = exports_info.redirect_to; + let other_exports_info_id = exports_info.other_exports_info; + // avoid use ref and mut ref at the same time + let export_info_id_list = exports_info.exports.values().cloned().collect::>(); + for export_info_id in export_info_id_list { + let flag = export_info_id.set_used_without_info(mg, runtime); + changed |= flag; + } + if let Some(redirect_to) = redirect { + let flag = redirect_to.set_used_without_info(mg, runtime); + changed |= flag; + } else { + let flag = other_exports_info_id.set_used(mg, UsageState::NoInfo, None); + changed |= flag; + let other_export_info = mg.get_export_info_mut_by_id(&other_exports_info_id); + if !matches!(other_export_info.can_mangle_use, Some(false)) { + other_export_info.can_mangle_use = Some(false); + changed = true; + } + } + changed + } + + pub fn set_used_in_unknown_way( + &self, + _mg: &mut ModuleGraph, + _runtime: Option<&RuntimeSpec>, + ) -> bool { + todo!() + } + + pub fn set_used_for_side_effects_only( + &self, + mg: &mut ModuleGraph, + runtime: Option<&RuntimeSpec>, + ) -> bool { + let exports_info = mg.get_exports_info_by_id(self); + let side_effects_only_info_id = exports_info._side_effects_only_info; + side_effects_only_info_id.set_used_conditionally( + mg, + Box::new(|value| value == &UsageState::Unused), + UsageState::Used, + runtime, + ) + } } impl Default for ExportsInfoId { @@ -344,7 +415,7 @@ impl ExportsInfo { pub fn get_used( &self, name: UsedName, - runtime: &RuntimeSpec, + runtime: Option<&RuntimeSpec>, module_graph: &ModuleGraph, ) -> UsageState { match &name { @@ -438,6 +509,80 @@ impl ExportInfoId { pub fn new() -> Self { Self(EXPORT_INFO_ID.fetch_add(1, Relaxed)) } + + fn set_has_use_info(&self, mg: &mut ModuleGraph) { + let export_info = mg.get_export_info_mut_by_id(self); + if !export_info.has_use_in_runtime_info { + export_info.has_use_in_runtime_info = true; + } + if export_info.can_mangle_use.is_none() { + export_info.can_mangle_use = Some(true); + } + if let Some(exports_info) = export_info.exports_info { + exports_info.set_has_use_info(mg); + } + } + + fn set_used_without_info(&self, mg: &mut ModuleGraph, runtime: Option<&RuntimeSpec>) -> bool { + let mut changed = false; + let flag = self.set_used(mg, UsageState::NoInfo, runtime); + changed |= flag; + let export_info = mg.get_export_info_mut_by_id(self); + if !matches!(export_info.can_mangle_use, Some(false)) { + export_info.can_mangle_use = Some(false); + changed = true; + } + changed + } + + fn set_used( + &self, + mg: &mut ModuleGraph, + new_value: UsageState, + runtime: Option<&RuntimeSpec>, + ) -> bool { + if runtime.is_some() { + // TODO: runtime optimization + todo!() + } else { + let export_info = mg.get_export_info_mut_by_id(self); + if export_info.global_used != Some(new_value) { + export_info.global_used = Some(new_value); + return true; + } + } + false + } + + pub fn set_used_conditionally( + &self, + mg: &mut ModuleGraph, + condition: UsageFilterFnTy, + new_value: UsageState, + runtime: Option<&RuntimeSpec>, + ) -> bool { + if runtime.is_some() { + // TODO: runtime optimization + todo!() + } else { + let export_info = mg.get_export_info_mut_by_id(self); + if let Some(global_used) = export_info.global_used { + if global_used != new_value && condition(&global_used) { + export_info.global_used = Some(new_value); + return true; + } + } else { + export_info.global_used = Some(new_value); + return true; + } + } + false + } + + pub fn get_nested_exports_info(&self, mg: &ModuleGraph) -> Option { + let export_info = mg.get_export_info_by_id(self); + export_info.exports_info + } } impl Default for ExportInfoId { fn default() -> Self { @@ -477,6 +622,9 @@ pub struct ExportInfo { max_target_is_set: bool, pub exports_info: Option, pub exports_info_owned: bool, + pub has_use_in_runtime_info: bool, + pub can_mangle_use: Option, + pub global_used: Option, } impl ExportsHash for ExportInfo { @@ -513,6 +661,7 @@ pub enum ExportInfoProvided { Null, } +#[derive(Clone)] pub struct ResolvedExportInfoTarget { pub module: ModuleIdentifier, pub exports: Option>, @@ -529,23 +678,25 @@ pub enum ResolvedExportInfoTargetWithCircular { Circular, } -pub type ResolveFilterFnTy = Box; +pub type ResolveFilterFnTy = Box>; +pub type UsageFilterFnTy = Box>; -pub trait ResolveFilterFn: Fn(&ResolvedExportInfoTarget) -> bool + Send + Sync { - fn clone_boxed(&self) -> Box; +pub trait FilterFn: Fn(&T) -> bool + Send + Sync { + fn clone_boxed(&self) -> Box>; } /// Copy from https://github.com/rust-lang/rust/issues/24000#issuecomment-479425396 -impl ResolveFilterFn for T +impl FilterFn for T where - T: 'static + Fn(&ResolvedExportInfoTarget) -> bool + Send + Sync + Clone, + T: 'static + Fn(&F) -> bool + Send + Sync + Clone, + F: 'static, { - fn clone_boxed(&self) -> Box { + fn clone_boxed(&self) -> Box> { Box::new(self.clone()) } } -impl Clone for Box { +impl Clone for Box> { fn clone(&self) -> Self { self.clone_boxed() } @@ -553,7 +704,14 @@ impl Clone for Box { impl ExportInfo { // TODO: remove usage_state in the future - pub fn new(name: JsWord, usage_state: UsageState, _init_from: Option<&ExportInfo>) -> Self { + pub fn new(name: JsWord, usage_state: UsageState, init_from: Option<&ExportInfo>) -> Self { + let has_use_in_runtime_info = if let Some(init_from) = init_from { + init_from.has_use_in_runtime_info + } else { + false + }; + let can_mangle_use = init_from.and_then(|init_from| init_from.can_mangle_use); + Self { name, module_identifier: None, @@ -570,11 +728,14 @@ impl ExportInfo { exports_info: None, max_target: HashMap::default(), exports_info_owned: false, + has_use_in_runtime_info, + can_mangle_use, + global_used: None, } } // TODO - pub fn get_used(&self, _runtime: &RuntimeSpec) -> UsageState { + pub fn get_used(&self, _runtime: Option<&RuntimeSpec>) -> UsageState { UsageState::Unused } @@ -685,12 +846,7 @@ impl ExportInfo { .module_graph_module_by_identifier(&target.module) .expect("should have mgm") .exports; - let exports_info_id = id.export_info_mut(name, mg); - let exports_info = mg.get_exports_info_mut_by_id(&exports_info_id); - *exports_info - .exports - .get(name) - .expect("should have export info") + id.get_export_info(name, mg) }; if already_visited.contains(&export_info_id) { return Some(ResolvedExportInfoTargetWithCircular::Circular); @@ -927,29 +1083,67 @@ pub fn get_dependency_used_by_exports_condition( } } +/// refer https://github.com/webpack/webpack/blob/d15c73469fd71cf98734685225250148b68ddc79/lib/FlagDependencyUsagePlugin.js#L64 +#[derive(Clone)] +pub enum ExtendedReferencedExport { + Array(Vec), + Export(ReferencedExport), +} + +pub fn is_no_exports_referenced(exports: &[ExtendedReferencedExport]) -> bool { + exports.is_empty() +} + +pub fn is_exports_object_referenced(exports: &[ExtendedReferencedExport]) -> bool { + matches!(exports[..], [ExtendedReferencedExport::Array(ref arr)] if arr.is_empty()) +} + +pub fn create_no_exports_referenced() -> Vec { + vec![] +} + +pub fn create_exports_object_referenced() -> Vec { + vec![ExtendedReferencedExport::Array(vec![])] +} + +impl From> for ExtendedReferencedExport { + fn from(value: Vec) -> Self { + ExtendedReferencedExport::Array(value) + } +} +impl From for ExtendedReferencedExport { + fn from(value: ReferencedExport) -> Self { + ExtendedReferencedExport::Export(value) + } +} + +#[derive(Clone)] pub struct ReferencedExport { - _name: Vec, - _can_mangle: bool, + pub name: Vec, + pub can_mangle: bool, } impl ReferencedExport { pub fn new(_name: Vec, _can_mangle: bool) -> Self { - Self { _name, _can_mangle } + Self { + name: _name, + can_mangle: _can_mangle, + } } } impl Default for ReferencedExport { fn default() -> Self { Self { - _name: vec![], - _can_mangle: true, + name: vec![], + can_mangle: true, } } } pub fn process_export_info( module_graph: &ModuleGraph, - runtime: &RuntimeSpec, + runtime: Option<&RuntimeSpec>, referenced_export: &mut Vec>, prefix: Vec, export_info: Option<&ExportInfo>, diff --git a/crates/rspack_core/src/module_graph/connection.rs b/crates/rspack_core/src/module_graph/connection.rs index 57481709a872..f2b84606def9 100644 --- a/crates/rspack_core/src/module_graph/connection.rs +++ b/crates/rspack_core/src/module_graph/connection.rs @@ -83,7 +83,7 @@ impl ModuleGraphConnection { self.active = value; } - pub fn is_active(&self, module_graph: &ModuleGraph, runtime: &RuntimeSpec) -> bool { + pub fn is_active(&self, module_graph: &ModuleGraph, runtime: Option<&RuntimeSpec>) -> bool { if !self.conditional { return self.active; } @@ -92,7 +92,11 @@ impl ModuleGraphConnection { .is_not_false() } - pub fn is_target_active(&self, module_graph: &ModuleGraph, runtime: &RuntimeSpec) -> bool { + pub fn is_target_active( + &self, + module_graph: &ModuleGraph, + runtime: Option<&RuntimeSpec>, + ) -> bool { if !self.conditional { return self.active; } @@ -102,7 +106,7 @@ impl ModuleGraphConnection { pub fn get_active_state( &self, module_graph: &ModuleGraph, - runtime: &RuntimeSpec, + runtime: Option<&RuntimeSpec>, ) -> ConnectionState { if !self.conditional { return ConnectionState::Bool(self.active); @@ -117,7 +121,7 @@ impl ModuleGraphConnection { pub fn get_condition_state( &self, module_graph: &ModuleGraph, - runtime: &RuntimeSpec, + runtime: Option<&RuntimeSpec>, ) -> ConnectionState { match self.condition.as_ref().expect("should have condition") { DependencyCondition::False => ConnectionState::Bool(false), diff --git a/crates/rspack_core/src/module_graph/mod.rs b/crates/rspack_core/src/module_graph/mod.rs index 04aae0181c90..73614dc35528 100644 --- a/crates/rspack_core/src/module_graph/mod.rs +++ b/crates/rspack_core/src/module_graph/mod.rs @@ -548,12 +548,34 @@ impl ModuleGraph { // } pub fn get_exports_info_by_id(&self, id: &ExportsInfoId) -> &ExportsInfo { - let exports_info = self.exports_info_map.get(id).expect("should have mgm"); + let exports_info = self + .exports_info_map + .get(id) + .expect("should have exports_info"); exports_info } pub fn get_exports_info_mut_by_id(&mut self, id: &ExportsInfoId) -> &mut ExportsInfo { - let exports_info = self.exports_info_map.get_mut(id).expect("should have mgm"); + let exports_info = self + .exports_info_map + .get_mut(id) + .expect("should have exports_info"); + exports_info + } + + pub fn get_export_info_by_id(&self, id: &ExportInfoId) -> &ExportInfo { + let export_info = self + .export_info_map + .get(id) + .expect("should have export info"); + export_info + } + + pub fn get_export_info_mut_by_id(&mut self, id: &ExportInfoId) -> &mut ExportInfo { + let exports_info = self + .export_info_map + .get_mut(id) + .expect("should have export info"); exports_info } } diff --git a/crates/rspack_plugin_javascript/src/dependency/commonjs/require_resolve_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/commonjs/require_resolve_dependency.rs index f83577307153..678d36cacfeb 100644 --- a/crates/rspack_plugin_javascript/src/dependency/commonjs/require_resolve_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/commonjs/require_resolve_dependency.rs @@ -1,6 +1,6 @@ use rspack_core::{ module_id, ContextOptions, Dependency, DependencyCategory, DependencyId, DependencyTemplate, - DependencyType, ErrorSpan, ExportsReferencedType, ModuleDependency, ModuleGraph, RuntimeSpec, + DependencyType, ErrorSpan, ExtendedReferencedExport, ModuleDependency, ModuleGraph, RuntimeSpec, TemplateContext, TemplateReplaceSource, }; @@ -82,9 +82,9 @@ impl ModuleDependency for RequireResolveDependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { - ExportsReferencedType::No + _runtime: Option<&RuntimeSpec>, + ) -> Vec { + vec![] } } diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs index 712e94b21f19..9a6165fbc64f 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs @@ -1,7 +1,8 @@ use rspack_core::{ - export_from_import, get_exports_type, process_export_info, ConnectionState, Dependency, - DependencyCategory, DependencyId, DependencyTemplate, DependencyType, ErrorSpan, ExportInfo, - ExportsReferencedType, ExportsType, HarmonyExportInitFragment, ModuleDependency, ModuleGraph, + create_exports_object_referenced, create_no_exports_referenced, export_from_import, + get_exports_type, process_export_info, ConnectionState, Dependency, DependencyCategory, + DependencyId, DependencyTemplate, DependencyType, ErrorSpan, ExportInfo, ExportsType, + ExtendedReferencedExport, HarmonyExportInitFragment, ModuleDependency, ModuleGraph, ModuleIdentifier, RuntimeSpec, TemplateContext, TemplateReplaceSource, }; use rustc_hash::FxHashSet as HashSet; @@ -129,8 +130,8 @@ impl ModuleDependency for HarmonyExportImportedSpecifierDependency { fn get_referenced_exports( &self, module_graph: &ModuleGraph, - runtime: &RuntimeSpec, - ) -> ExportsReferencedType { + runtime: Option<&RuntimeSpec>, + ) -> Vec { let mode = get_mode( self.name.clone(), &self.ids.iter().map(|id| id.0.clone()).collect::>(), @@ -141,9 +142,9 @@ impl ModuleDependency for HarmonyExportImportedSpecifierDependency { ExportModeType::Missing | ExportModeType::Unused | ExportModeType::EmptyStar - | ExportModeType::ReexportUndefined => ExportsReferencedType::No, + | ExportModeType::ReexportUndefined => create_no_exports_referenced(), ExportModeType::ReexportDynamicDefault | ExportModeType::DynamicReexport => { - ExportsReferencedType::Object + create_exports_object_referenced() } ExportModeType::ReexportNamedDefault | ExportModeType::ReexportNamespaceObject @@ -159,9 +160,12 @@ impl ModuleDependency for HarmonyExportImportedSpecifierDependency { mode.kind == ExportModeType::ReexportFakeNamespaceObject, &mut Default::default(), ); - referenced_exports.into() + referenced_exports + .into_iter() + .map(ExtendedReferencedExport::Array) + .collect::>() } else { - ExportsReferencedType::Object + create_exports_object_referenced() } } ExportModeType::NormalReexport => { @@ -182,7 +186,10 @@ impl ModuleDependency for HarmonyExportImportedSpecifierDependency { ); } } - referenced_exports.into() + referenced_exports + .into_iter() + .map(ExtendedReferencedExport::Array) + .collect::>() } ExportModeType::Unset => { unreachable!("should not export mode unset"); diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_dependency.rs index 8e1a9abd72ac..bf8e91fd7566 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_dependency.rs @@ -2,10 +2,11 @@ use rspack_core::tree_shaking::symbol::{self, IndirectTopLevelSymbol}; use rspack_core::tree_shaking::visitor::SymbolRef; use rspack_core::{ import_statement, ConnectionState, Dependency, DependencyCategory, DependencyCondition, - DependencyId, DependencyTemplate, DependencyType, ErrorSpan, InitFragmentStage, ModuleDependency, - ModuleIdentifier, NormalInitFragment, RuntimeGlobals, TemplateContext, TemplateReplaceSource, + DependencyId, DependencyTemplate, DependencyType, ErrorSpan, ExtendedReferencedExport, + InitFragmentStage, ModuleDependency, ModuleIdentifier, NormalInitFragment, RuntimeGlobals, + TemplateContext, TemplateReplaceSource, }; -use rspack_core::{ExportsReferencedType, ModuleGraph, RuntimeSpec}; +use rspack_core::{ModuleGraph, RuntimeSpec}; use rustc_hash::FxHashSet as HashSet; use swc_core::ecma::atoms::JsWord; @@ -234,9 +235,9 @@ impl ModuleDependency for HarmonyImportDependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { - ExportsReferencedType::No + _runtime: Option<&RuntimeSpec>, + ) -> Vec { + vec![] } // It's from HarmonyImportSideEffectDependency. diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs index 28648a758142..3907d6336d14 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs @@ -1,10 +1,10 @@ +use rspack_core::{create_exports_object_referenced, ExtendedReferencedExport}; use rspack_core::{ export_from_import, get_dependency_used_by_exports_condition, get_exports_type, tree_shaking::symbol::DEFAULT_JS_WORD, Compilation, ConnectionState, Dependency, DependencyCategory, DependencyCondition, DependencyId, DependencyTemplate, DependencyType, - ErrorSpan, ExportsReferencedType, ExportsType, ModuleDependency, ModuleGraph, ModuleGraphModule, - ModuleIdentifier, ReferencedExport, RuntimeSpec, TemplateContext, TemplateReplaceSource, - UsedByExports, + ErrorSpan, ExportsType, ModuleDependency, ModuleGraph, ModuleGraphModule, ModuleIdentifier, + ReferencedExport, RuntimeSpec, TemplateContext, TemplateReplaceSource, UsedByExports, }; use rustc_hash::FxHashSet as HashSet; use swc_core::ecma::atoms::JsWord; @@ -92,7 +92,7 @@ impl HarmonyImportSpecifierDependency { pub fn get_referenced_exports_in_destructuring( &self, ids: Option<&Vec>, - ) -> ExportsReferencedType { + ) -> Vec { if let Some(referenced_properties) = &self.referenced_properties_in_destructuring { referenced_properties .iter() @@ -105,12 +105,12 @@ impl HarmonyImportSpecifierDependency { ReferencedExport::new(vec![prop.clone()], false) } }) + .map(ExtendedReferencedExport::Export) .collect::>() - .into() } else if let Some(v) = ids { - vec![ReferencedExport::new(v.clone(), true)].into() + vec![ReferencedExport::new(v.clone(), true).into()] } else { - ExportsReferencedType::Object + create_exports_object_referenced() } } } @@ -213,8 +213,8 @@ impl ModuleDependency for HarmonyImportSpecifierDependency { fn get_referenced_exports( &self, module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { + _runtime: Option<&RuntimeSpec>, + ) -> Vec { if self.ids.is_empty() { return self.get_referenced_exports_in_destructuring(None); } @@ -233,7 +233,7 @@ impl ModuleDependency for HarmonyImportSpecifierDependency { namespace_object_as_context = true; } ExportsType::Dynamic => { - return ExportsReferencedType::No; + return vec![] } _ => {} } @@ -241,7 +241,7 @@ impl ModuleDependency for HarmonyImportSpecifierDependency { if self.call && !self.direct_import && (namespace_object_as_context || ids.len() > 1) { if ids.len() == 1 { - return ExportsReferencedType::Object; + return create_exports_object_referenced(); } // remove last one ids.shrink_to(ids.len() - 1); diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs index 21e80410cdc5..ff0f4d00584e 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs @@ -1,7 +1,7 @@ use rspack_core::{ module_namespace_promise, ChunkGroupOptions, ChunkGroupOptionsKindRef, Dependency, DependencyCategory, DependencyId, DependencyTemplate, DependencyType, ErrorSpan, - ExportsReferencedType, ModuleDependency, ModuleGraph, ReferencedExport, RuntimeSpec, + ExtendedReferencedExport, ModuleDependency, ModuleGraph, ReferencedExport, RuntimeSpec, TemplateContext, TemplateReplaceSource, }; use swc_core::ecma::atoms::JsWord; @@ -78,12 +78,12 @@ impl ModuleDependency for ImportDependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { + _runtime: Option<&RuntimeSpec>, + ) -> Vec { if let Some(referenced_exports) = &self.referenced_exports { - vec![ReferencedExport::new(referenced_exports.clone(), false)].into() + vec![ReferencedExport::new(referenced_exports.clone(), false).into()] } else { - ExportsReferencedType::Object + vec![ExtendedReferencedExport::Array(vec![])] } } } diff --git a/crates/rspack_plugin_javascript/src/dependency/worker/mod.rs b/crates/rspack_plugin_javascript/src/dependency/worker/mod.rs index df00122736c5..7c81390a5bc6 100644 --- a/crates/rspack_plugin_javascript/src/dependency/worker/mod.rs +++ b/crates/rspack_plugin_javascript/src/dependency/worker/mod.rs @@ -1,6 +1,6 @@ use rspack_core::{ ChunkGroupOptionsKindRef, Dependency, DependencyCategory, DependencyId, DependencyTemplate, - DependencyType, EntryOptions, ErrorSpan, ExportsReferencedType, ModuleDependency, ModuleGraph, + DependencyType, EntryOptions, ErrorSpan, ExtendedReferencedExport, ModuleDependency, ModuleGraph, RuntimeGlobals, RuntimeSpec, TemplateContext, TemplateReplaceSource, }; @@ -74,9 +74,9 @@ impl ModuleDependency for WorkerDependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { - ExportsReferencedType::No + _runtime: Option<&RuntimeSpec>, + ) -> Vec { + vec![] } } diff --git a/crates/rspack_plugin_javascript/src/plugin/flag_usage_plugin.rs b/crates/rspack_plugin_javascript/src/plugin/flag_usage_plugin.rs new file mode 100644 index 000000000000..7cf5a905d755 --- /dev/null +++ b/crates/rspack_plugin_javascript/src/plugin/flag_usage_plugin.rs @@ -0,0 +1,337 @@ +use std::collections::hash_map::Entry; +use std::collections::VecDeque; + +use rspack_core::{ + is_exports_object_referenced, is_no_exports_referenced, BuildMetaExportsType, Compilation, + ConnectionState, DependencyId, ExportsInfoId, ExtendedReferencedExport, ModuleIdentifier, + ReferencedExport, RuntimeSpec, UsageState, +}; +use rspack_identifier::IdentifierMap; +use rustc_hash::FxHashMap as HashMap; + +use crate::utils::join_jsword; + +#[allow(unused)] +pub struct FlagDependencyUsagePlugin<'a> { + global: bool, + compilation: &'a mut Compilation, + exports_info_module_map: HashMap, +} + +#[allow(unused)] +impl<'a> FlagDependencyUsagePlugin<'a> { + pub fn new(global: bool, compilation: &'a mut Compilation) -> Self { + Self { + global, + compilation, + exports_info_module_map: HashMap::default(), + } + } + + fn apply(&mut self) { + for mgm in self + .compilation + .module_graph + .module_graph_modules() + .values() + { + self + .exports_info_module_map + .insert(mgm.exports, mgm.module_identifier); + } + let mut q = VecDeque::new(); + let mg = &mut self.compilation.module_graph; + for exports_info_id in self.exports_info_module_map.keys() { + exports_info_id.set_has_use_info(mg); + } + // SAFETY: we can make sure that entries will not be used other place at the same time, + // this take is aiming to avoid use self ref and mut ref at the same time; + let entries = std::mem::take(&mut self.compilation.entries); + for entry in entries.values() { + for &dep in entry.dependencies.iter() { + self.process_entry_dependency(dep, None, &mut q); + } + } + // TODO: compilation.globalEntry.dependencies, we don't have now https://github.com/webpack/webpack/blob/3f71468514ae2f179ff34c837ce82fcc8f97e24c/lib/FlagDependencyUsagePlugin.js#L328-L330 + _ = std::mem::replace(&mut self.compilation.entries, entries); + + while let Some((module_id, runtime)) = q.pop_front() { + self.process_module(module_id, runtime, false, &mut q); + } + } + + fn process_module( + &mut self, + root_module_id: ModuleIdentifier, + runtime: Option, + force_side_effects: bool, + q: &mut VecDeque<(ModuleIdentifier, Option)>, + ) { + enum ProcessModuleReferencedExports { + Map(HashMap), + ExtendRef(Vec), + } + + let mut map: IdentifierMap = IdentifierMap::default(); + let mut queue = VecDeque::new(); + queue.push_back(root_module_id); + while let Some(module_id) = queue.pop_front() { + // TODO: we don't have blocks.blocks https://github.com/webpack/webpack/blob/3f71468514ae2f179ff34c837ce82fcc8f97e24c/lib/FlagDependencyUsagePlugin.js#L180-L194 + let mgm = self + .compilation + .module_graph + .module_graph_module_by_identifier(&module_id) + .expect("should have module graph module"); + let dep_id_list = mgm.dependencies.clone(); + for dep_id in dep_id_list.into_iter() { + let connection = self + .compilation + .module_graph + .connection_by_dependency(&dep_id); + let connection = if let Some(connection) = connection { + connection + } else { + continue; + }; + let active_state = + connection.get_active_state(&self.compilation.module_graph, runtime.as_ref()); + match active_state { + ConnectionState::Bool(false) => { + continue; + } + ConnectionState::TransitiveOnly => { + self.process_module(connection.module_identifier, runtime.clone(), false, q); + continue; + } + _ => {} + } + let old_referenced_exports = map.remove(&connection.module_identifier); + let dep = self + .compilation + .module_graph + .dependency_by_id(&dep_id) + .expect("should have dep"); + let referenced_exports = if let Some(md) = dep.as_module_dependency() { + md.get_referenced_exports(&self.compilation.module_graph, runtime.as_ref()) + } else { + continue; + }; + if old_referenced_exports.is_none() + || matches!(old_referenced_exports.as_ref().expect("should be some"), ProcessModuleReferencedExports::ExtendRef(v) if is_no_exports_referenced(v)) + || is_exports_object_referenced(&referenced_exports) + { + map.insert( + connection.module_identifier, + ProcessModuleReferencedExports::ExtendRef(referenced_exports), + ); + } else if old_referenced_exports.is_some() && is_no_exports_referenced(&referenced_exports) + { + continue; + } else { + let mut exports_map = if let Some(old_referenced_exports) = old_referenced_exports { + match old_referenced_exports { + ProcessModuleReferencedExports::Map(map) => map, + ProcessModuleReferencedExports::ExtendRef(ref_items) => { + let mut exports_map = HashMap::default(); + for item in ref_items.iter() { + match item { + ExtendedReferencedExport::Array(arr) => { + exports_map.insert(join_jsword(arr, "\n"), item.clone()); + } + ExtendedReferencedExport::Export(export) => { + exports_map.insert(join_jsword(&export.name, "\n"), item.clone()); + } + } + } + exports_map + } + } + } else { + // in else branch above old_referenced_exports must be `Some(T)`, use if let Pattern + // just avoid rust clippy complain + unreachable!() + }; + for mut item in referenced_exports.into_iter() { + match item { + ExtendedReferencedExport::Array(ref arr) => { + let key = join_jsword(arr, "\n"); + exports_map.entry(key).or_insert(item); + } + ExtendedReferencedExport::Export(ref mut export) => { + let key = join_jsword(&export.name, "\n"); + match exports_map.entry(key) { + Entry::Occupied(mut occ) => { + let old_item = occ.get(); + match old_item { + ExtendedReferencedExport::Array(_) => { + occ.insert(item); + } + ExtendedReferencedExport::Export(old_item) => { + occ.insert(ExtendedReferencedExport::Export(ReferencedExport { + name: std::mem::take(&mut export.name), + can_mangle: export.can_mangle && old_item.can_mangle, + })); + } + } + } + Entry::Vacant(vac) => { + vac.insert(item); + } + } + } + } + } + } + } + } + for (module_id, referenced_exports) in map { + let normalized_refs = match referenced_exports { + ProcessModuleReferencedExports::Map(map) => map.into_values().collect::>(), + ProcessModuleReferencedExports::ExtendRef(extend_ref) => extend_ref, + }; + self.process_referenced_module( + module_id, + normalized_refs, + runtime.clone(), + force_side_effects, + q, + ); + } + } + + fn process_entry_dependency( + &mut self, + dep: DependencyId, + _runtime: Option, + queue: &mut VecDeque<(ModuleIdentifier, Option)>, + ) { + if let Some(module) = self + .compilation + .module_graph + .module_graph_module_by_dependency_id(&dep) + { + self.process_referenced_module(module.module_identifier, vec![], None, true, queue); + } + } + + /// TODO: currently we don't impl runtime optimization, runtime is always none + fn process_referenced_module( + &mut self, + module_id: ModuleIdentifier, + used_exports: Vec, + runtime: Option, + force_side_effects: bool, + queue: &mut VecDeque<(ModuleIdentifier, Option)>, + ) { + let mgm = self + .compilation + .module_graph + .module_graph_module_by_identifier(&module_id) + .expect("should have mgm"); + let mgm_exports_info_id = mgm.exports; + if !used_exports.is_empty() { + let need_insert = match mgm.build_meta { + Some(ref build_meta) => matches!(build_meta.exports_type, BuildMetaExportsType::Unset), + None => true, + }; + if need_insert { + let flag = mgm_exports_info_id + .set_used_without_info(&mut self.compilation.module_graph, runtime.as_ref()); + if flag { + queue.push_back((module_id, None)); + } + return; + } + for used_export_info in used_exports { + let (can_mangle, used_exports) = match used_export_info { + ExtendedReferencedExport::Array(used_exports) => (true, used_exports), + ExtendedReferencedExport::Export(export) => (export.can_mangle, export.name), + }; + if used_exports.is_empty() { + let flag = mgm_exports_info_id + .set_used_in_unknown_way(&mut self.compilation.module_graph, runtime.as_ref()); + if flag { + queue.push_back((module_id, runtime.clone())); + } + } else { + let mut current_exports_info_id = mgm_exports_info_id; + let len = used_exports.len(); + for (i, used_export) in used_exports.into_iter().enumerate() { + let export_info_id = current_exports_info_id + .get_export_info(&used_export, &mut self.compilation.module_graph); + let export_info = self + .compilation + .module_graph + .get_export_info_mut_by_id(&export_info_id); + if !can_mangle { + export_info.can_mangle_use = Some(false); + } + let last_one = i == len - 1; + if !last_one { + let nested_info = + export_info_id.get_nested_exports_info(&self.compilation.module_graph); + if let Some(nested_info) = nested_info { + let changed_flag = export_info_id.set_used_conditionally( + &mut self.compilation.module_graph, + Box::new(|used| used == &UsageState::Unused), + UsageState::OnlyPropertiesUsed, + runtime.as_ref(), + ); + if changed_flag { + let current_module = if current_exports_info_id == mgm_exports_info_id { + Some(module_id) + } else { + self + .exports_info_module_map + .get(¤t_exports_info_id) + .cloned() + }; + if let Some(current_module) = current_module { + queue.push_back((current_module, runtime.clone())); + } + } + current_exports_info_id = nested_info; + continue; + } + } + + let changed_flag = export_info_id.set_used_conditionally( + &mut self.compilation.module_graph, + Box::new(|v| v != &UsageState::Used), + UsageState::Used, + runtime.as_ref(), + ); + if changed_flag { + let current_module = if current_exports_info_id == mgm_exports_info_id { + Some(module_id) + } else { + self + .exports_info_module_map + .get(¤t_exports_info_id) + .cloned() + }; + if let Some(current_module) = current_module { + queue.push_back((current_module, runtime.clone())); + } + } + break; + } + } + } + } else { + if !force_side_effects + && match mgm.factory_meta { + Some(ref meta) => meta.side_effect_free.unwrap_or_default(), + None => false, + } + { + return; + } + let flag = mgm_exports_info_id + .set_used_for_side_effects_only(&mut self.compilation.module_graph, runtime.as_ref()); + if flag { + queue.push_back((module_id, runtime)); + } + } + } +} diff --git a/crates/rspack_plugin_javascript/src/plugin/mod.rs b/crates/rspack_plugin_javascript/src/plugin/mod.rs index 1d0cc6d91647..2085384fea60 100644 --- a/crates/rspack_plugin_javascript/src/plugin/mod.rs +++ b/crates/rspack_plugin_javascript/src/plugin/mod.rs @@ -1,3 +1,4 @@ +pub mod flag_usage_plugin; pub mod impl_plugin_for_js_plugin; pub mod infer_async_modules_plugin; pub mod provided_exports_plugin; diff --git a/crates/rspack_plugin_javascript/src/plugin/provided_exports_plugin.rs b/crates/rspack_plugin_javascript/src/plugin/provided_exports_plugin.rs index e6aaae2e0459..418d9018209a 100644 --- a/crates/rspack_plugin_javascript/src/plugin/provided_exports_plugin.rs +++ b/crates/rspack_plugin_javascript/src/plugin/provided_exports_plugin.rs @@ -91,16 +91,11 @@ impl<'a> ProvidedExportsPlugin<'a> { let export_dependencies = &export_desc.dependencies; if let Some(hide_export) = export_desc.hide_export { for name in hide_export.iter() { - let from_exports_info_id = exports_info_id.export_info_mut(name, self.mg); - let exports_info = self.mg.get_exports_info_mut_by_id(&from_exports_info_id); - let export_info_id = *exports_info - .exports - .get(name) - .expect("should have exports info"); + let from_exports_info_id = exports_info_id.get_export_info(name, self.mg); let export_info = self .mg .export_info_map - .get_mut(&export_info_id) + .get_mut(&from_exports_info_id) .expect("should have export info"); export_info.unuset_target(&dep_id); } @@ -189,12 +184,7 @@ impl<'a> ProvidedExportsPlugin<'a> { spec.hidden.unwrap_or(false), ), }; - let exports_info_id = exports_info.export_info_mut(&name, self.mg); - let exports_info = self.mg.get_exports_info_mut_by_id(&exports_info_id); - let export_info_id = *exports_info - .exports - .get(&name) - .expect("should have export info"); + let export_info_id = exports_info.get_export_info(&name, self.mg); let mut export_info = self .mg @@ -245,15 +235,10 @@ impl<'a> ProvidedExportsPlugin<'a> { // Recalculate target exportsInfo let target = export_info.get_target(self.mg, None); - let exports_info = self.mg.get_exports_info_by_id(&exports_info_id); - let export_info_old_id = *exports_info - .exports - .get(&name) - .expect("should have export info"); let export_info_old = self .mg .export_info_map - .get_mut(&export_info_old_id) + .get_mut(&export_info_id) .expect("should have export info"); _ = std::mem::replace(export_info_old, export_info); @@ -272,11 +257,7 @@ impl<'a> ProvidedExportsPlugin<'a> { } } } - let exports_info = self.mg.get_exports_info_by_id(&exports_info_id); - let export_info_id = *exports_info - .exports - .get(&name) - .expect("should have export info"); + let export_info = self .mg .export_info_map diff --git a/crates/rspack_plugin_javascript/src/utils.rs b/crates/rspack_plugin_javascript/src/utils.rs index b5f6f7ac1666..f78825336b7b 100644 --- a/crates/rspack_plugin_javascript/src/utils.rs +++ b/crates/rspack_plugin_javascript/src/utils.rs @@ -4,7 +4,7 @@ use rspack_core::{ErrorSpan, ModuleType}; use rspack_error::{DiagnosticKind, Error}; use swc_core::common::{SourceFile, Span, Spanned, SyntaxContext, DUMMY_SP}; use swc_core::ecma::ast::{CallExpr, Callee, Expr, ExprOrSpread, Ident, Lit, Str}; -use swc_core::ecma::atoms::js_word; +use swc_core::ecma::atoms::{js_word, JsWord}; use swc_core::ecma::parser::Syntax; use swc_core::ecma::parser::{EsConfig, TsConfig}; @@ -152,3 +152,15 @@ pub fn ecma_parse_error_to_rspack_error( .with_kind(diagnostic_kind); Error::TraceableError(traceable_error) } + +pub fn join_jsword(arr: &[JsWord], separator: &str) -> String { + let mut ret = String::new(); + if let Some(item) = arr.get(0) { + ret.push_str(item); + } + for item in arr.iter().skip(1) { + ret.push_str(separator); + ret.push_str(item); + } + ret +} diff --git a/crates/rspack_plugin_wasm/src/dependency.rs b/crates/rspack_plugin_wasm/src/dependency.rs index 2ddf82469895..59506941efc5 100644 --- a/crates/rspack_plugin_wasm/src/dependency.rs +++ b/crates/rspack_plugin_wasm/src/dependency.rs @@ -1,6 +1,6 @@ use rspack_core::{ AsDependencyTemplate, Dependency, DependencyCategory, DependencyId, DependencyType, ErrorSpan, - ExportsReferencedType, ModuleDependency, ModuleGraph, RuntimeSpec, + ExtendedReferencedExport, ModuleDependency, ModuleGraph, RuntimeSpec, }; use swc_core::ecma::atoms::JsWord; @@ -68,9 +68,9 @@ impl ModuleDependency for WasmImportDependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { - self.name.clone().into() + _runtime: Option<&RuntimeSpec>, + ) -> Vec { + vec![ExtendedReferencedExport::Array(vec![self.name.clone()])] } } From 1594bf90571324d4063c4791ba25d52417971850 Mon Sep 17 00:00:00 2001 From: hardfist Date: Thu, 7 Sep 2023 12:34:49 +0800 Subject: [PATCH 14/30] chore: disable sync document when closed merge request (#4142) --- .github/workflows/need-doc.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/need-doc.yml b/.github/workflows/need-doc.yml index 715ad59d5312..9dded26f4755 100644 --- a/.github/workflows/need-doc.yml +++ b/.github/workflows/need-doc.yml @@ -4,7 +4,7 @@ name: Need Documentation on: pull_request: - types: [closed, labeled] + types: [labeled] jobs: check_label: @@ -15,7 +15,7 @@ jobs: doc: name: Need Documentation runs-on: ubuntu-latest - if: ${{ (github.event.pull_request.merged == true || github.event.label.name == 'need documentation') && contains(github.event.pull_request.labels.*.name, 'need documentation') }} + if: ${{ (github.event.label.name == 'need documentation') && contains(github.event.pull_request.labels.*.name, 'need documentation') }} steps: - uses: actions/github-script@v6 with: From 78e848311999d1a499d0c4c1a12d1c9f6d044f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=95=B6?= Date: Thu, 7 Sep 2023 13:24:03 +0800 Subject: [PATCH 15/30] feat(core): add cache hits info to stats (#4140) feat(core): add cache hits info to stats and output when stats.logging=true --- crates/node_binding/src/js_values/stats.rs | 16 +++ .../src/cache/occasion/code_generate.rs | 8 +- .../src/cache/occasion/resolve_module.rs | 8 +- .../rspack_core/src/compiler/compilation.rs | 122 ++++++++++++----- crates/rspack_core/src/compiler/queue.rs | 4 + .../rspack_core/src/context_module_factory.rs | 10 +- crates/rspack_core/src/logger.rs | 41 ++++++ crates/rspack_core/src/module_factory.rs | 7 + .../rspack_core/src/normal_module_factory.rs | 55 +++++--- packages/rspack/src/logging/Logger.ts | 3 +- .../src/stats/DefaultStatsFactoryPlugin.ts | 3 +- .../src/stats/DefaultStatsPrinterPlugin.ts | 2 + packages/rspack/tests/Stats.test.ts | 127 +++++++++++++++++- 13 files changed, 338 insertions(+), 68 deletions(-) diff --git a/crates/node_binding/src/js_values/stats.rs b/crates/node_binding/src/js_values/stats.rs index 17ac542dce86..26d97f18288d 100644 --- a/crates/node_binding/src/js_values/stats.rs +++ b/crates/node_binding/src/js_values/stats.rs @@ -143,6 +143,22 @@ impl From<(String, rspack_core::LogType)> for JsStatsLogging { args: Some(vec![message]), trace: None, }, + rspack_core::LogType::Cache { label, hit, total } => Self { + name: value.0, + r#type: "cache".to_string(), + args: Some(vec![format!( + "{}: {:.1}% ({}/{})", + label, + if total == 0 { + 0 as f32 + } else { + hit as f32 / total as f32 * 100_f32 + }, + hit, + total, + )]), + trace: None, + }, } } } diff --git a/crates/rspack_core/src/cache/occasion/code_generate.rs b/crates/rspack_core/src/cache/occasion/code_generate.rs index 6a1efc5bbb71..6ba1d103cde0 100644 --- a/crates/rspack_core/src/cache/occasion/code_generate.rs +++ b/crates/rspack_core/src/cache/occasion/code_generate.rs @@ -21,14 +21,14 @@ impl CodeGenerateOccasion { module: &'a BoxModule, compilation: &Compilation, generator: G, - ) -> Result + ) -> Result<(CodeGenerationResult, bool)> where G: Fn(&'a BoxModule) -> Result, { let storage = match &self.storage { Some(s) => s, // no cache return directly - None => return generator(module), + None => return Ok((generator(module)?, false)), }; let mut cache_id = None; @@ -44,7 +44,7 @@ impl CodeGenerateOccasion { // currently no need to separate module hash by runtime if let Some(data) = storage.get(&id) { - return Ok(data); + return Ok((data, true)); } if matches!(normal_module.source(), NormalModuleSource::Unbuild) { @@ -59,6 +59,6 @@ impl CodeGenerateOccasion { if let Some(id) = cache_id { storage.set(id, data.clone()); } - Ok(data) + Ok((data, false)) } } diff --git a/crates/rspack_core/src/cache/occasion/resolve_module.rs b/crates/rspack_core/src/cache/occasion/resolve_module.rs index 57e13390f946..24cd981e2ea1 100644 --- a/crates/rspack_core/src/cache/occasion/resolve_module.rs +++ b/crates/rspack_core/src/cache/occasion/resolve_module.rs @@ -29,7 +29,7 @@ impl ResolveModuleOccasion { &self, args: ResolveArgs<'a>, generator: G, - ) -> Result + ) -> Result<(Result, bool), ResolveError> where G: Fn(ResolveArgs<'a>) -> F, F: Future>, @@ -37,7 +37,7 @@ impl ResolveModuleOccasion { let storage = match &self.storage { Some(s) => s, // no cache return directly - None => return generator(args).await, + None => return Ok((generator(args).await, false)), }; let id = ModuleIdentifier::from(format!( @@ -60,7 +60,7 @@ impl ResolveModuleOccasion { .unwrap_or(false); if valid { - return Ok(data); + return Ok((Ok(data), true)); } }; } @@ -78,6 +78,6 @@ impl ResolveModuleOccasion { .await .map_err(|err| ResolveError(err.to_string(), err))?; storage.set(id, (snapshot, data.clone())); - Ok(data) + Ok((Ok(data), false)) } } diff --git a/crates/rspack_core/src/compiler/compilation.rs b/crates/rspack_core/src/compiler/compilation.rs index 5776e297f6e3..8d80f389e794 100644 --- a/crates/rspack_core/src/compiler/compilation.rs +++ b/crates/rspack_core/src/compiler/compilation.rs @@ -35,15 +35,15 @@ use crate::{ is_source_equal, tree_shaking::{optimizer, visitor::SymbolRef, BailoutFlag, OptimizeDependencyResult}, AddQueue, AddTask, AddTaskResult, AdditionalChunkRuntimeRequirementsArgs, BoxDependency, - BoxModule, BuildQueue, BuildTask, BuildTaskResult, Chunk, ChunkByUkey, ChunkContentHash, - ChunkGraph, ChunkGroup, ChunkGroupUkey, ChunkHashArgs, ChunkKind, ChunkUkey, CleanQueue, - CleanTask, CleanTaskResult, CodeGenerationResult, CodeGenerationResults, CompilationLogger, - CompilationLogging, CompilerOptions, ContentHashArgs, DependencyId, Entry, EntryData, - EntryOptions, Entrypoint, FactorizeQueue, FactorizeTask, FactorizeTaskResult, Filename, Logger, - Module, ModuleGraph, ModuleIdentifier, ModuleProfile, ModuleType, PathData, ProcessAssetsArgs, - ProcessDependenciesQueue, ProcessDependenciesResult, ProcessDependenciesTask, RenderManifestArgs, - Resolve, ResolverFactory, RuntimeGlobals, RuntimeModule, RuntimeSpec, SharedPluginDriver, - SourceType, Stats, TaskResult, WorkerTask, + BoxModule, BuildQueue, BuildTask, BuildTaskResult, CacheCount, CacheOptions, Chunk, ChunkByUkey, + ChunkContentHash, ChunkGraph, ChunkGroup, ChunkGroupUkey, ChunkHashArgs, ChunkKind, ChunkUkey, + CleanQueue, CleanTask, CleanTaskResult, CodeGenerationResult, CodeGenerationResults, + CompilationLogger, CompilationLogging, CompilerOptions, ContentHashArgs, DependencyId, Entry, + EntryData, EntryOptions, Entrypoint, FactorizeQueue, FactorizeTask, FactorizeTaskResult, + Filename, Logger, Module, ModuleGraph, ModuleIdentifier, ModuleProfile, ModuleType, PathData, + ProcessAssetsArgs, ProcessDependenciesQueue, ProcessDependenciesResult, ProcessDependenciesTask, + RenderManifestArgs, Resolve, ResolverFactory, RuntimeGlobals, RuntimeModule, RuntimeSpec, + SharedPluginDriver, SourceType, Stats, TaskResult, WorkerTask, }; use crate::{tree_shaking::visitor::OptimizeAnalyzeResult, Context}; @@ -343,7 +343,7 @@ impl Compilation { #[instrument(name = "compilation:make", skip_all)] pub async fn make(&mut self, mut param: MakeParam) -> Result<()> { - let logger = self.get_logger("rspack.Compiler"); + let logger = self.get_logger("rspack.Compilation"); let start = logger.time("make hook"); if let Some(e) = self .plugin_driver @@ -387,7 +387,7 @@ impl Compilation { } async fn update_module_graph(&mut self, params: Vec) -> Result<()> { - let logger = self.get_logger("rspack.Compiler"); + let logger = self.get_logger("rspack.Compilation"); let deps_builder = RebuildDepsBuilder::new(params, &self.module_graph); let mut origin_module_deps = HashMap::default(); @@ -469,6 +469,15 @@ impl Compilation { let mut process_deps_time = logger.time_aggregate("module process dependencies task"); let mut factorize_time = logger.time_aggregate("module factorize task"); let mut build_time = logger.time_aggregate("module build task"); + + let mut build_cache_counter = None; + let mut factorize_cache_counter = None; + + if !(matches!(self.options.cache, CacheOptions::Disabled)) { + build_cache_counter = Some(logger.cache("module build cache")); + factorize_cache_counter = Some(logger.cache("module factorize cache")); + } + tokio::task::block_in_place(|| loop { let start = factorize_time.start(); while let Some(task) = factorize_queue.get_task() { @@ -617,7 +626,17 @@ impl Compilation { dependencies, current_profile, exports_info_related, + from_cache, } = task_result; + + if let Some(counter) = &mut factorize_cache_counter { + if from_cache { + counter.hit(); + } else { + counter.miss(); + } + } + let module_identifier = factory_result.module.identifier(); tracing::trace!("Module created: {}", &module_identifier); @@ -686,7 +705,17 @@ impl Compilation { build_result, diagnostics, current_profile, + from_cache, } = task_result; + + if let Some(counter) = &mut build_cache_counter { + if from_cache { + counter.hit(); + } else { + counter.miss(); + } + } + if self.options.builtins.tree_shaking.enable() { self .optimize_analyze_result_map @@ -778,6 +807,13 @@ impl Compilation { logger.time_aggregate_end(factorize_time); logger.time_aggregate_end(build_time); + if let Some(counter) = build_cache_counter { + logger.cache_end(counter); + } + if let Some(counter) = factorize_cache_counter { + logger.cache_end(counter); + } + // TODO @jerrykingxyz make update_module_graph a pure function self .make_failed_dependencies @@ -913,8 +949,15 @@ impl Compilation { #[instrument(name = "compilation:code_generation", skip(self))] async fn code_generation(&mut self) -> Result<()> { + let logger = self.get_logger("rspack.Compilation"); + let mut codegen_cache_counter = match self.options.cache { + CacheOptions::Disabled => None, + _ => Some(logger.cache("module code generation cache")), + }; + fn run_iteration( compilation: &mut Compilation, + codegen_cache_counter: &mut Option, filter_op: impl Fn(&(&ModuleIdentifier, &Box)) -> bool + Sync + Send, ) -> Result<()> { let results = compilation @@ -935,40 +978,53 @@ impl Compilation { .use_cache(module, compilation, |module| { module.code_generation(compilation) }) - .map(|result| (*module_identifier, result)) + .map(|(result, from_cache)| (*module_identifier, result, from_cache)) }) - .collect::>>()?; + .collect::>>()?; - results.into_iter().for_each(|(module_identifier, result)| { - compilation.code_generated_modules.insert(module_identifier); + results + .into_iter() + .for_each(|(module_identifier, result, from_cache)| { + if let Some(counter) = codegen_cache_counter { + if from_cache { + counter.hit(); + } else { + counter.miss(); + } + } + compilation.code_generated_modules.insert(module_identifier); - let runtimes = compilation - .chunk_graph - .get_module_runtimes(module_identifier, &compilation.chunk_by_ukey); - - compilation - .code_generation_results - .module_generation_result_map - .insert(module_identifier, result); - for runtime in runtimes.values() { - compilation.code_generation_results.add( - module_identifier, - runtime.clone(), - module_identifier, - ); - } - }); + let runtimes = compilation + .chunk_graph + .get_module_runtimes(module_identifier, &compilation.chunk_by_ukey); + + compilation + .code_generation_results + .module_generation_result_map + .insert(module_identifier, result); + for runtime in runtimes.values() { + compilation.code_generation_results.add( + module_identifier, + runtime.clone(), + module_identifier, + ); + } + }); Ok(()) } - run_iteration(self, |(_, module)| { + run_iteration(self, &mut codegen_cache_counter, |(_, module)| { module.get_code_generation_dependencies().is_none() })?; - run_iteration(self, |(_, module)| { + run_iteration(self, &mut codegen_cache_counter, |(_, module)| { module.get_code_generation_dependencies().is_some() })?; + if let Some(counter) = codegen_cache_counter { + logger.cache_end(counter); + } + Ok(()) } diff --git a/crates/rspack_core/src/compiler/queue.rs b/crates/rspack_core/src/compiler/queue.rs index 7d7fab5b9b0b..bcdd48157c00 100644 --- a/crates/rspack_core/src/compiler/queue.rs +++ b/crates/rspack_core/src/compiler/queue.rs @@ -59,6 +59,7 @@ pub struct FactorizeTaskResult { pub is_entry: bool, pub current_profile: Option>, pub exports_info_related: ExportsInfoRelated, + pub from_cache: bool, } #[async_trait::async_trait] @@ -134,6 +135,7 @@ impl WorkerTask for FactorizeTask { Ok(TaskResult::Factorize(Box::new(FactorizeTaskResult { is_entry: self.is_entry, original_module_identifier: self.original_module_identifier, + from_cache: result.from_cache, factory_result: result, module_graph_module: Box::new(mgm), dependencies: self.dependencies, @@ -257,6 +259,7 @@ pub struct BuildTaskResult { pub build_result: Box, pub diagnostics: Vec, pub current_profile: Option>, + pub from_cache: bool, } #[async_trait::async_trait] @@ -320,6 +323,7 @@ impl WorkerTask for BuildTask { build_result: Box::new(build_result), diagnostics, current_profile: self.current_profile, + from_cache: is_cache_valid, })) }) } diff --git a/crates/rspack_core/src/context_module_factory.rs b/crates/rspack_core/src/context_module_factory.rs index 05df0b6223bd..d5798ecbc15b 100644 --- a/crates/rspack_core/src/context_module_factory.rs +++ b/crates/rspack_core/src/context_module_factory.rs @@ -102,11 +102,16 @@ impl ContextModuleFactory { }; let plugin_driver = &self.plugin_driver; - let resource_data = self + let (resource_data, from_cache) = match self .cache .resolve_module_occasion .use_cache(resolve_args, |args| resolve(args, plugin_driver)) - .await; + .await + { + Ok(result) => result, + Err(err) => (Err(err), false), + }; + let module = match resource_data { Ok(ResolveResult::Resource(resource)) => Box::new(ContextModule::new( ContextModuleOptions { @@ -154,6 +159,7 @@ impl ContextModuleFactory { missing_dependencies, context_dependencies, factory_meta, + from_cache, } .with_empty_diagnostic(), ) diff --git a/crates/rspack_core/src/logger.rs b/crates/rspack_core/src/logger.rs index 36b1898cb2c3..b89354f2ce77 100644 --- a/crates/rspack_core/src/logger.rs +++ b/crates/rspack_core/src/logger.rs @@ -53,6 +53,11 @@ pub enum LogType { Status { message: String, }, + Cache { + label: &'static str, + hit: u32, + total: u32, + }, } impl LogType { @@ -72,6 +77,7 @@ impl LogType { LogType::Time { .. } => 1 << 11, LogType::Clear => 1 << 12, LogType::Status { .. } => 1 << 13, + LogType::Cache { .. } => 1 << 14, } } } @@ -208,6 +214,24 @@ pub trait Logger { subsec_nanos, }) } + + fn cache(&self, label: &'static str) -> CacheCount { + CacheCount { + label, + total: 0, + hit: 0, + } + } + + fn cache_end(&self, count: CacheCount) { + if count.total != 0 { + self.raw(LogType::Cache { + label: count.label, + hit: count.hit, + total: count.total, + }) + } + } } pub struct StartTime { @@ -246,6 +270,23 @@ impl StartTimeAggregate { } } +pub struct CacheCount { + label: &'static str, + hit: u32, + total: u32, +} + +impl CacheCount { + pub fn hit(&mut self) { + self.total += 1; + self.hit += 1; + } + + pub fn miss(&mut self) { + self.total += 1; + } +} + pub type CompilationLogging = Arc, BuildHasherDefault>>; pub struct CompilationLogger { diff --git a/crates/rspack_core/src/module_factory.rs b/crates/rspack_core/src/module_factory.rs index a8bac146b632..a9aa695ee145 100644 --- a/crates/rspack_core/src/module_factory.rs +++ b/crates/rspack_core/src/module_factory.rs @@ -19,6 +19,7 @@ pub struct ModuleFactoryResult { pub context_dependencies: HashSet, pub missing_dependencies: HashSet, pub factory_meta: FactoryMeta, + pub from_cache: bool, } impl ModuleFactoryResult { @@ -29,6 +30,7 @@ impl ModuleFactoryResult { context_dependencies: Default::default(), missing_dependencies: Default::default(), factory_meta: Default::default(), + from_cache: false, } } @@ -68,6 +70,11 @@ impl ModuleFactoryResult { self.factory_meta = factory_meta; self } + + pub fn from_cache(mut self, from_cache: bool) -> Self { + self.from_cache = from_cache; + self + } } #[async_trait::async_trait] diff --git a/crates/rspack_core/src/normal_module_factory.rs b/crates/rspack_core/src/normal_module_factory.rs index 29362a33f600..1cdfe180c321 100644 --- a/crates/rspack_core/src/normal_module_factory.rs +++ b/crates/rspack_core/src/normal_module_factory.rs @@ -183,16 +183,19 @@ impl NormalModuleFactory { let mut no_pre_post_auto_loaders = false; // with scheme, windows absolute path is considered scheme by `url` - let resource_data = if scheme != Scheme::None + let (resource_data, from_cache) = if scheme != Scheme::None && !Path::is_absolute(Path::new(request_without_match_resource)) { // resource with scheme - plugin_driver - .normal_module_factory_resolve_for_scheme(ResourceData::new( - request_without_match_resource.to_string(), - "".into(), - )) - .await? + ( + plugin_driver + .normal_module_factory_resolve_for_scheme(ResourceData::new( + request_without_match_resource.to_string(), + "".into(), + )) + .await?, + false, + ) } // TODO: resource within scheme, call resolveInScheme hook else { @@ -313,18 +316,25 @@ impl NormalModuleFactory { }; // default resolve - let resource_data = self + let (resource_data, from_cache) = match self .cache .resolve_module_occasion .use_cache(resolve_args, |args| resolve(args, plugin_driver)) - .await; + .await + { + Ok(result) => result, + Err(err) => (Err(err), false), + }; match resource_data { Ok(ResolveResult::Resource(resource)) => { let uri = resource.full_path().display().to_string(); - ResourceData::new(uri, resource.path) - .query_optional(resource.query) - .fragment_optional(resource.fragment) - .description_optional(resource.description_data) + ( + ResourceData::new(uri, resource.path) + .query_optional(resource.query) + .fragment_optional(resource.fragment) + .description_optional(resource.description_data), + from_cache, + ) } Ok(ResolveResult::Ignored) => { let ident = format!("{}/{}", &data.context, request_without_match_resource); @@ -340,7 +350,9 @@ impl NormalModuleFactory { self.context.module_type = Some(*raw_module.module_type()); return Ok(Some( - ModuleFactoryResult::new(raw_module).with_empty_diagnostic(), + ModuleFactoryResult::new(raw_module) + .from_cache(from_cache) + .with_empty_diagnostic(), )); } Err(ResolveError(runtime_error, internal_error)) => { @@ -350,6 +362,7 @@ impl NormalModuleFactory { let mut missing_dependencies = Default::default(); let diagnostics: Vec = internal_error.into(); let mut diagnostic = diagnostics[0].clone(); + let mut from_cache_result = from_cache; if !data .resolve_options .as_ref() @@ -373,11 +386,16 @@ impl NormalModuleFactory { missing_dependencies: &mut missing_dependencies, file_dependencies: &mut file_dependencies, }; - let resource_data = self + let (resource_data, from_cache) = match self .cache .resolve_module_occasion .use_cache(new_args, |args| resolve(args, plugin_driver)) - .await; + .await + { + Ok(result) => result, + Err(err) => (Err(err), false), + }; + from_cache_result = from_cache; if let Ok(ResolveResult::Resource(resource)) = resource_data { // TODO: Here windows resolver will return normalized path. // eg. D:\a\rspack\rspack\packages\rspack\tests\fixtures\errors\resolve-fail-esm\answer.js @@ -402,7 +420,9 @@ impl NormalModuleFactory { .boxed(); self.context.module_type = Some(*missing_module.module_type()); return Ok(Some( - ModuleFactoryResult::new(missing_module).with_diagnostic(vec![diagnostic]), + ModuleFactoryResult::new(missing_module) + .from_cache(from_cache_result) + .with_diagnostic(vec![diagnostic]), )); } } @@ -638,6 +658,7 @@ impl NormalModuleFactory { .file_dependencies(file_dependencies) .missing_dependencies(missing_dependencies) .factory_meta(factory_meta) + .from_cache(from_cache) .with_empty_diagnostic(), )) } diff --git a/packages/rspack/src/logging/Logger.ts b/packages/rspack/src/logging/Logger.ts index 8a112d3d94ee..5f46bfad3b56 100644 --- a/packages/rspack/src/logging/Logger.ts +++ b/packages/rspack/src/logging/Logger.ts @@ -27,7 +27,8 @@ export const LogType = Object.freeze({ time: /** @type {"time"} */ "time", // name, time as [seconds, nanoseconds] clear: /** @type {"clear"} */ "clear", // no arguments - status: /** @type {"status"} */ "status" // message, arguments + status: /** @type {"status"} */ "status", // message, arguments + cache: /** @type {"cache"} */ "cache" // [hit, total] }); export function getLogTypeBitFlag(type: LogTypeEnum) { diff --git a/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts b/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts index 528f9a032ef5..5243465552b1 100644 --- a/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts +++ b/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts @@ -416,7 +416,8 @@ const SIMPLE_EXTRACTORS: SimpleExtractors = { LogType.profileEnd, LogType.time, LogType.status, - LogType.clear + LogType.clear, + LogType.cache ]); collapsedGroups = true; } else if (logging === "log" || logging === true) { diff --git a/packages/rspack/src/stats/DefaultStatsPrinterPlugin.ts b/packages/rspack/src/stats/DefaultStatsPrinterPlugin.ts index bc9e96a35453..6e50b3742767 100644 --- a/packages/rspack/src/stats/DefaultStatsPrinterPlugin.ts +++ b/packages/rspack/src/stats/DefaultStatsPrinterPlugin.ts @@ -609,6 +609,8 @@ const SIMPLE_PRINTERS: Record< mapLines(message, x => `

${magenta(x)}`), "loggingEntry(time).loggingEntry.message": (message, { magenta }) => mapLines(message, x => ` ${magenta(x)}`), + "loggingEntry(cache).loggingEntry.message": (message, { magenta }) => + mapLines(message, x => ` ${magenta(x)}`), "loggingEntry(group).loggingEntry.message": (message, { cyan }) => mapLines(message, x => `<-> ${cyan(x)}`), "loggingEntry(groupCollapsed).loggingEntry.message": (message, { cyan }) => diff --git a/packages/rspack/tests/Stats.test.ts b/packages/rspack/tests/Stats.test.ts index ff20f79a398f..62e681f09aac 100644 --- a/packages/rspack/tests/Stats.test.ts +++ b/packages/rspack/tests/Stats.test.ts @@ -1,5 +1,6 @@ import * as util from "util"; -import { rspack, RspackOptions } from "../src"; +import path from "path"; +import { rspack, RspackOptions, Stats } from "../src"; import serializer from "jest-serializer-path"; expect.addSnapshotSerializer(serializer); @@ -628,6 +629,11 @@ describe("Stats", () => { .replace(/\d+ ms/g, "X ms") ).toMatchInlineSnapshot(` "LOG from rspack.Compilation + make hook: X ms + module add task: X ms + module process dependencies task: X ms + module factorize task: X ms + module build task: X ms finish modules: X ms optimize dependencies: X ms create chunks: X ms @@ -647,11 +653,6 @@ describe("Stats", () => { process assets: X ms LOG from rspack.Compiler - make hook: X ms - module add task: X ms - module process dependencies task: X ms - module factorize task: X ms - module build task: X ms make: X ms finish make hook: X ms finish compilation: X ms @@ -709,4 +710,118 @@ describe("Stats", () => { X ms (resolving: X ms, integration: X ms, building: X ms)" `); }); + + it("should have cache hits log when logging verbose and cache is enabled", async () => { + const compiler = rspack({ + context: __dirname, + entry: "./fixtures/abc", + cache: true, + experiments: { + incrementalRebuild: false + } + }); + await new Promise((resolve, reject) => { + compiler.build(err => { + if (err) { + return reject(err); + } + resolve(); + }); + }); + const stats = await new Promise((resolve, reject) => { + compiler.rebuild( + new Set([path.join(__dirname, "./fixtures/a")]), + new Set(), + err => { + if (err) { + return reject(err); + } + const stats = new Stats(compiler.compilation).toString({ + all: false, + logging: "verbose" + }); + resolve(stats); + } + ); + }); + expect(stats).toContain("module build cache: 100.0% (4/4)"); + expect(stats).toContain("module factorize cache: 100.0% (5/5)"); + expect(stats).toContain("module code generation cache: 100.0% (4/4)"); + }); + + it("should not have any cache hits log when cache is disabled", async () => { + const compiler = rspack({ + context: __dirname, + entry: "./fixtures/abc", + cache: false, + experiments: { + incrementalRebuild: false + } + }); + await new Promise((resolve, reject) => { + compiler.build(err => { + if (err) { + return reject(err); + } + resolve(); + }); + }); + const stats = await new Promise((resolve, reject) => { + compiler.rebuild( + new Set([path.join(__dirname, "./fixtures/a")]), + new Set(), + err => { + if (err) { + return reject(err); + } + const stats = new Stats(compiler.compilation).toString({ + all: false, + logging: "verbose" + }); + resolve(stats); + } + ); + }); + expect(stats).not.toContain("module build cache"); + expect(stats).not.toContain("module factorize cache"); + expect(stats).not.toContain("module code generation cache"); + }); + + it("should have any cache hits log of modules in incremental rebuild mode", async () => { + const compiler = rspack({ + context: __dirname, + entry: "./fixtures/abc", + cache: true, + experiments: { + incrementalRebuild: true + } + }); + await new Promise((resolve, reject) => { + compiler.build(err => { + if (err) { + return reject(err); + } + resolve(); + }); + }); + const stats = await new Promise((resolve, reject) => { + compiler.rebuild( + new Set([path.join(__dirname, "./fixtures/a")]), + new Set(), + err => { + if (err) { + return reject(err); + } + const stats = new Stats(compiler.compilation).toString({ + all: false, + logging: "verbose" + }); + resolve(stats); + } + ); + }); + expect(stats).toContain("module build cache: 100.0% (1/1)"); + expect(stats).toContain("module factorize cache: 100.0% (1/1)"); + expect(stats).toContain("module code generation cache: 100.0% (4/4)"); + }); }); From d279052ca736f889042891053677d270ecd91e83 Mon Sep 17 00:00:00 2001 From: Hana Date: Thu, 7 Sep 2023 14:02:39 +0800 Subject: [PATCH 16/30] fix: console should not appear here (#4145) --- packages/rspack-dev-server/src/server.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/rspack-dev-server/src/server.ts b/packages/rspack-dev-server/src/server.ts index 409c0be53baf..076b61fd212d 100644 --- a/packages/rspack-dev-server/src/server.ts +++ b/packages/rspack-dev-server/src/server.ts @@ -352,7 +352,6 @@ export class RspackDevServer extends WebpackDevServer { this.addAdditionalEntries(compiler); if (this.options.hot) { - console.log(runtimePaths); compiler.options.module.rules.push({ include: runtimePaths, type: "js" From d7c8c56aa0ef954741211a8e46a0d0f8917ccf65 Mon Sep 17 00:00:00 2001 From: hardfist Date: Thu, 7 Sep 2023 14:13:36 +0800 Subject: [PATCH 17/30] chore: takedown down monaco-editor example build in ci (#4146) --- examples/monaco-editor-js/package.json | 2 +- examples/monaco-editor-ts-react/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/monaco-editor-js/package.json b/examples/monaco-editor-js/package.json index d1517be976e8..d2b156f4bc72 100644 --- a/examples/monaco-editor-js/package.json +++ b/examples/monaco-editor-js/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "dev": "rspack serve", - "build": "rspack build" + "build:local": "rspack build" }, "license": "MIT", "dependencies": { diff --git a/examples/monaco-editor-ts-react/package.json b/examples/monaco-editor-ts-react/package.json index 1f7e51c4e0e3..af87a78ced3e 100644 --- a/examples/monaco-editor-ts-react/package.json +++ b/examples/monaco-editor-ts-react/package.json @@ -6,7 +6,7 @@ "private": true, "scripts": { "dev": "rspack serve", - "build": "rspack build" + "build:local": "rspack build" }, "keywords": [], "author": "", From d0e8595959d0fc33f43199c364e0157e9c2a0ba3 Mon Sep 17 00:00:00 2001 From: underfin Date: Thu, 7 Sep 2023 16:22:01 +0800 Subject: [PATCH 18/30] fix: vue3 hmr (#4149) --- .../rspack_core/src/compiler/compilation.rs | 5 +- packages/playground/cases/vue3/index.test.ts | 18 +++ .../playground/cases/vue3/rspack.config.js | 42 +++++++ packages/playground/cases/vue3/src/App.vue | 14 +++ packages/playground/cases/vue3/src/index.html | 12 ++ packages/playground/cases/vue3/src/main.js | 3 + packages/playground/package.json | 6 +- pnpm-lock.yaml | 107 +++++++++++------- 8 files changed, 160 insertions(+), 47 deletions(-) create mode 100644 packages/playground/cases/vue3/index.test.ts create mode 100644 packages/playground/cases/vue3/rspack.config.js create mode 100644 packages/playground/cases/vue3/src/App.vue create mode 100644 packages/playground/cases/vue3/src/index.html create mode 100644 packages/playground/cases/vue3/src/main.js diff --git a/crates/rspack_core/src/compiler/compilation.rs b/crates/rspack_core/src/compiler/compilation.rs index 8d80f389e794..8476e0f0bf2a 100644 --- a/crates/rspack_core/src/compiler/compilation.rs +++ b/crates/rspack_core/src/compiler/compilation.rs @@ -442,9 +442,8 @@ impl Compilation { .expect("dependency not found"); let parent_module = parent_module_identifier.and_then(|id| self.module_graph.module_by_identifier(&id)); - if parent_module_identifier.is_some() - && parent_module.is_none() - && dependency.as_module_dependency().is_none() + if (parent_module_identifier.is_some() && parent_module.is_none()) + || dependency.as_module_dependency().is_none() { return; } diff --git a/packages/playground/cases/vue3/index.test.ts b/packages/playground/cases/vue3/index.test.ts new file mode 100644 index 000000000000..0c111995b733 --- /dev/null +++ b/packages/playground/cases/vue3/index.test.ts @@ -0,0 +1,18 @@ +import { test, expect } from "@/fixtures"; + +test("should successfully render vue3", async ({ page }) => { + expect(await page.textContent("h1")).toBe("vue3"); +}); + +test("vue3 hmr", async ({ page, fileAction, rspack }) => { + await page.click("button"); + expect(await page.textContent("button")).toBe("1"); + fileAction.updateFile("src/App.vue", content => + content.replace("vue3", "vue3 hi") + ); + await rspack.waitingForHmr(async function () { + return (await page.textContent("h1")) === "vue3 hi"; + }); + // hmr should keep status + expect(await page.textContent("button")).toBe("1"); +}); diff --git a/packages/playground/cases/vue3/rspack.config.js b/packages/playground/cases/vue3/rspack.config.js new file mode 100644 index 000000000000..80f640340cd3 --- /dev/null +++ b/packages/playground/cases/vue3/rspack.config.js @@ -0,0 +1,42 @@ +const { VueLoaderPlugin } = require("vue-loader"); + +/** @type { import('@rspack/core').RspackOptions } */ +module.exports = { + context: __dirname, + mode: "development", + entry: "./src/main.js", + builtins: { + html: [ + { + template: "./src/index.html" + } + ], + define: { + __VUE_OPTIONS_API__: JSON.stringify(true), + __VUE_PROD_DEVTOOLS__: JSON.stringify(false) + } + }, + devServer: { + hot: true + }, + plugins: [new VueLoaderPlugin()], + module: { + rules: [ + { + test: /\.vue$/, + loader: "vue-loader", + options: { + experimentalInlineMatchResource: true + } + } + ] + }, + cache: false, + stats: "error", + infrastructureLogging: { + debug: false + }, + watchOptions: { + poll: 1000 + } +}; diff --git a/packages/playground/cases/vue3/src/App.vue b/packages/playground/cases/vue3/src/App.vue new file mode 100644 index 000000000000..335835d2ddca --- /dev/null +++ b/packages/playground/cases/vue3/src/App.vue @@ -0,0 +1,14 @@ + + + diff --git a/packages/playground/cases/vue3/src/index.html b/packages/playground/cases/vue3/src/index.html new file mode 100644 index 000000000000..127a457455b3 --- /dev/null +++ b/packages/playground/cases/vue3/src/index.html @@ -0,0 +1,12 @@ + + + + + + + Document + + +
+ + diff --git a/packages/playground/cases/vue3/src/main.js b/packages/playground/cases/vue3/src/main.js new file mode 100644 index 000000000000..a7a89ae0eafc --- /dev/null +++ b/packages/playground/cases/vue3/src/main.js @@ -0,0 +1,3 @@ +import { createApp } from "vue"; +import App from "./App.vue"; +createApp(App).mount("#root"); diff --git a/packages/playground/package.json b/packages/playground/package.json index 48d27ed4a950..ae3600fe8983 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -23,6 +23,8 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "tailwindcss": "^3.3.0", - "ws": "8.8.1" + "ws": "8.8.1", + "vue": "3.2.47", + "vue-loader": "^17.2.2" } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e1dd021ef764..71a192c262df 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -886,6 +886,8 @@ importers: react: ^18.2.0 react-dom: ^18.2.0 tailwindcss: ^3.3.0 + vue: 3.2.47 + vue-loader: ^17.2.2 ws: 8.8.1 devDependencies: '@playwright/test': 1.35.0 @@ -899,6 +901,8 @@ importers: react: 18.2.0 react-dom: 18.2.0_react@18.2.0 tailwindcss: 3.3.1_postcss@8.4.21 + vue: 3.2.47 + vue-loader: 17.2.2_vue@3.2.47 ws: 8.8.1 packages/rspack: @@ -1987,10 +1991,10 @@ packages: '@babel/generator': 7.21.5 '@babel/helper-module-transforms': 7.21.5 '@babel/helpers': 7.21.0 - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.5 '@babel/template': 7.20.7 '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 convert-source-map: 1.8.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -2151,7 +2155,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-explode-assignable-expression': 7.18.6 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/helper-compilation-targets/7.20.7: @@ -2458,7 +2462,7 @@ packages: resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/helper-function-name/7.21.0: @@ -2466,7 +2470,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.20.7 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 /@babel/helper-function-name/7.22.5: resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} @@ -2480,7 +2484,7 @@ packages: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 /@babel/helper-hoist-variables/7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} @@ -2493,14 +2497,14 @@ packages: resolution: {integrity: sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/helper-member-expression-to-functions/7.21.5: resolution: {integrity: sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/helper-member-expression-to-functions/7.22.5: @@ -2580,7 +2584,7 @@ packages: resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/helper-optimise-call-expression/7.22.5: @@ -2617,7 +2621,7 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2632,7 +2636,7 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2647,7 +2651,7 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2660,7 +2664,7 @@ packages: '@babel/helper-member-expression-to-functions': 7.18.9 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/traverse': 7.21.2 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2674,7 +2678,7 @@ packages: '@babel/helper-optimise-call-expression': 7.18.6 '@babel/template': 7.20.7 '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2697,7 +2701,7 @@ packages: resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 /@babel/helper-simple-access/7.21.5: resolution: {integrity: sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==} @@ -2717,7 +2721,7 @@ packages: resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/helper-skip-transparent-expression-wrappers/7.22.5: @@ -2781,7 +2785,7 @@ packages: '@babel/helper-function-name': 7.21.0 '@babel/template': 7.20.7 '@babel/traverse': 7.21.2 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -4760,7 +4764,7 @@ packages: '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.21.5 - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -4775,7 +4779,7 @@ packages: '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.21.5 - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -5036,7 +5040,7 @@ packages: '@babel/helper-module-imports': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.21.0 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/plugin-transform-react-pure-annotations/7.18.6_@babel+core@7.21.0: @@ -9850,7 +9854,7 @@ packages: resolution: {integrity: sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==} engines: {node: '>=10'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 entities: 4.4.0 dev: true @@ -11132,8 +11136,8 @@ packages: /@types/babel__core/7.1.19: resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} dependencies: - '@babel/parser': 7.21.8 - '@babel/types': 7.21.5 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.18.1 @@ -11142,20 +11146,20 @@ packages: /@types/babel__generator/7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@types/babel__template/7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.21.8 - '@babel/types': 7.21.5 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 dev: true /@types/babel__traverse/7.18.1: resolution: {integrity: sha512-FSdLaZh2UxaMuLp9lixWaHq/golWTRWOnRsAXzDTDSDOQLuZb1nsdCt6pJSPWSEQt2eFZ2YVk3oYhn+1kLMeMA==} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@types/body-parser/1.19.2: @@ -11667,7 +11671,7 @@ packages: /@vue/compiler-core/3.2.47: resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==} dependencies: - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.5 '@vue/shared': 3.2.47 estree-walker: 2.0.2 source-map: 0.6.1 @@ -11709,7 +11713,7 @@ packages: /@vue/compiler-sfc/3.2.47: resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==} dependencies: - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.5 '@vue/compiler-core': 3.2.47 '@vue/compiler-dom': 3.2.47 '@vue/compiler-ssr': 3.2.47 @@ -11813,7 +11817,7 @@ packages: /@vue/reactivity-transform/3.2.47: resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==} dependencies: - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.5 '@vue/compiler-core': 3.2.47 '@vue/shared': 3.2.47 estree-walker: 2.0.2 @@ -12084,6 +12088,7 @@ packages: dependencies: webpack: 5.76.0_webpack-cli@4.10.0 webpack-cli: 4.10.0_webpack@5.76.0 + dev: true /@webpack-cli/info/1.5.0_webpack-cli@4.10.0: resolution: {integrity: sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==} @@ -12092,6 +12097,7 @@ packages: dependencies: envinfo: 7.8.1 webpack-cli: 4.10.0_webpack@5.76.0 + dev: true /@webpack-cli/serve/1.7.0_webpack-cli@4.10.0: resolution: {integrity: sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==} @@ -12103,6 +12109,7 @@ packages: optional: true dependencies: webpack-cli: 4.10.0_webpack@5.76.0 + dev: true /@xtuc/ieee754/1.2.0: resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -12815,7 +12822,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/template': 7.20.7 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 '@types/babel__core': 7.1.19 '@types/babel__traverse': 7.18.1 dev: true @@ -13027,7 +13034,7 @@ packages: resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==} engines: {node: '>= 10.0.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /bail/1.0.5: @@ -14013,6 +14020,7 @@ packages: is-plain-object: 2.0.4 kind-of: 6.0.3 shallow-clone: 3.0.1 + dev: true /clone-response/1.0.2: resolution: {integrity: sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==} @@ -14435,8 +14443,8 @@ packages: /constantinople/4.0.1: resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} dependencies: - '@babel/parser': 7.21.8 - '@babel/types': 7.21.5 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 dev: true /constants-browserify/1.0.0: @@ -15800,6 +15808,7 @@ packages: resolution: {integrity: sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==} engines: {node: '>=4'} hasBin: true + dev: true /err-code/2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} @@ -16136,7 +16145,7 @@ packages: engines: {node: '>=8.3.0'} dependencies: '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 c8: 7.13.0 transitivePeerDependencies: - supports-color @@ -16504,6 +16513,7 @@ packages: /fastest-levenshtein/1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} + dev: true /fastq/1.13.0: resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} @@ -18138,6 +18148,7 @@ packages: dependencies: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 + dev: true /imurmurhash/0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} @@ -18275,6 +18286,7 @@ packages: /interpret/2.2.0: resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} engines: {node: '>= 0.10'} + dev: true /interpret/3.1.1: resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} @@ -18636,6 +18648,7 @@ packages: engines: {node: '>=0.10.0'} dependencies: isobject: 3.0.1 + dev: true /is-plain-object/5.0.0: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} @@ -18826,6 +18839,7 @@ packages: /isobject/3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} + dev: true /isomorphic-fetch/2.2.1: resolution: {integrity: sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA==} @@ -19310,7 +19324,7 @@ packages: '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.21.0 '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.21.0 '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 '@jest/expect-utils': 29.5.0 '@jest/transform': 29.5.0 '@jest/types': 29.5.0 @@ -19471,7 +19485,7 @@ packages: '@babel/preset-env': ^7.1.6 dependencies: '@babel/core': 7.21.0 - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.5 '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.21.0 '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.21.0 '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.21.0 @@ -19501,7 +19515,7 @@ packages: '@babel/preset-env': ^7.1.6 dependencies: '@babel/core': 7.21.0 - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.5 '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.21.0 '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.21.0 '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.21.0 @@ -19736,6 +19750,7 @@ packages: /kind-of/6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} + dev: true /kleur/3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} @@ -23310,7 +23325,7 @@ packages: dependencies: '@babel/core': 7.21.0 '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 '@types/babel__traverse': 7.18.1 '@types/doctrine': 0.0.5 '@types/resolve': 1.20.2 @@ -23758,6 +23773,7 @@ packages: engines: {node: '>= 0.10'} dependencies: resolve: 1.22.2 + dev: true /rechoir/0.8.0: resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} @@ -24044,6 +24060,7 @@ packages: engines: {node: '>=8'} dependencies: resolve-from: 5.0.0 + dev: true /resolve-from/4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} @@ -24052,6 +24069,7 @@ packages: /resolve-from/5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + dev: true /resolve-pathname/3.0.0: resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==} @@ -24755,6 +24773,7 @@ packages: engines: {node: '>=8'} dependencies: kind-of: 6.0.3 + dev: true /shallowequal/1.1.0: resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} @@ -26258,7 +26277,7 @@ packages: schema-utils: 3.1.2 serialize-javascript: 6.0.1 terser: 5.17.1 - webpack: 5.76.0_webpack-cli@4.10.0 + webpack: 5.76.0 /terser/5.16.1: resolution: {integrity: sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==} @@ -27712,6 +27731,7 @@ packages: rechoir: 0.7.1 webpack: 5.76.0_webpack-cli@4.10.0 webpack-merge: 5.9.0 + dev: true /webpack-dev-middleware/5.3.3: resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==} @@ -27986,6 +28006,7 @@ packages: dependencies: clone-deep: 4.0.1 wildcard: 2.0.0 + dev: true /webpack-sources/2.3.1: resolution: {integrity: sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==} @@ -28100,6 +28121,7 @@ packages: - '@swc/core' - esbuild - uglify-js + dev: true /webpack/5.80.0_esbuild@0.17.18: resolution: {integrity: sha512-OIMiq37XK1rWO8mH9ssfFKZsXg4n6klTEDL7S8/HqbAOBBaiy8ABvXvz0dDCXeEF9gqwxSvVk611zFPjS8hJxA==} @@ -28267,6 +28289,7 @@ packages: /wildcard/2.0.0: resolution: {integrity: sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==} + dev: true /window-size/0.1.0: resolution: {integrity: sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==} @@ -28277,8 +28300,8 @@ packages: resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==} engines: {node: '>= 10.0.0'} dependencies: - '@babel/parser': 7.21.8 - '@babel/types': 7.21.5 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 assert-never: 1.2.1 babel-walk: 3.0.0-canary-5 dev: true From bd64126ab2e6cf544dee3c4ce6d498eaf5084b4c Mon Sep 17 00:00:00 2001 From: underfin Date: Thu, 7 Sep 2023 18:40:18 +0800 Subject: [PATCH 19/30] feat: HarmonyExportImportedSpecifierDependency get mode (#4141) --- crates/rspack_core/src/exports_info.rs | 12 +- crates/rspack_core/src/module.rs | 8 +- ...ny_export_imported_specifier_dependency.rs | 476 ++++++++++++++---- .../harmony_export_specifier_dependency.rs | 19 +- .../harmony_import_specifier_dependency.rs | 15 +- .../harmony_export_dependency_scanner.rs | 42 +- .../harmony_import_dependency_scanner.rs | 89 ++-- .../src/visitors/dependency/mod.rs | 3 +- .../src/visitors/dependency/util.rs | 32 +- 9 files changed, 534 insertions(+), 162 deletions(-) diff --git a/crates/rspack_core/src/exports_info.rs b/crates/rspack_core/src/exports_info.rs index c14455badb1f..8630eb0f6a64 100644 --- a/crates/rspack_core/src/exports_info.rs +++ b/crates/rspack_core/src/exports_info.rs @@ -607,7 +607,7 @@ impl From for ExportInfoId { #[derive(Debug, Clone, Default)] #[allow(unused)] pub struct ExportInfo { - name: JsWord, + pub name: JsWord, module_identifier: Option, pub usage_state: UsageState, used_name: Option, @@ -1146,11 +1146,15 @@ pub fn process_export_info( runtime: Option<&RuntimeSpec>, referenced_export: &mut Vec>, prefix: Vec, - export_info: Option<&ExportInfo>, + export_info: Option, default_points_to_self: bool, already_visited: &mut HashSet, ) { - if let Some(export_info) = export_info { + if let Some(export_info_id) = export_info { + let export_info = module_graph + .export_info_map + .get(&export_info_id) + .expect("should have export info"); let used = export_info.get_used(runtime); if used == UsageState::Unused { return; @@ -1185,7 +1189,7 @@ pub fn process_export_info( value.push(export_info.name.clone()); value }, - Some(export_info), + Some(export_info.id), false, already_visited, ); diff --git a/crates/rspack_core/src/module.rs b/crates/rspack_core/src/module.rs index a4eae53db8c2..a7418582e988 100644 --- a/crates/rspack_core/src/module.rs +++ b/crates/rspack_core/src/module.rs @@ -10,12 +10,14 @@ use rspack_identifier::{Identifiable, Identifier}; use rspack_sources::Source; use rspack_util::ext::{AsAny, DynEq, DynHash}; use rustc_hash::FxHashSet as HashSet; +use swc_core::ecma::atoms::JsWord; use crate::tree_shaking::visitor::OptimizeAnalyzeResult; use crate::{ BoxDependency, ChunkUkey, CodeGenerationResult, Compilation, CompilerContext, CompilerOptions, - ConnectionState, Context, ContextModule, DependencyTemplate, ExternalModule, ModuleDependency, - ModuleGraph, ModuleType, NormalModule, RawModule, Resolve, SharedPluginDriver, SourceType, + ConnectionState, Context, ContextModule, DependencyId, DependencyTemplate, ExternalModule, + ModuleDependency, ModuleGraph, ModuleType, NormalModule, RawModule, Resolve, SharedPluginDriver, + SourceType, }; pub struct BuildContext<'a> { @@ -42,6 +44,8 @@ pub struct BuildInfo { pub missing_dependencies: HashSet, pub build_dependencies: HashSet, pub asset_filenames: HashSet, + pub harmony_named_exports: HashSet, + pub all_star_exports: Vec, } #[derive(Debug, Default, Clone, Hash)] diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs index 9a6165fbc64f..0d72b9c49663 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs @@ -1,9 +1,9 @@ use rspack_core::{ create_exports_object_referenced, create_no_exports_referenced, export_from_import, get_exports_type, process_export_info, ConnectionState, Dependency, DependencyCategory, - DependencyId, DependencyTemplate, DependencyType, ErrorSpan, ExportInfo, ExportsType, - ExtendedReferencedExport, HarmonyExportInitFragment, ModuleDependency, ModuleGraph, - ModuleIdentifier, RuntimeSpec, TemplateContext, TemplateReplaceSource, + DependencyId, DependencyTemplate, DependencyType, ErrorSpan, ExportInfoId, ExportInfoProvided, + ExportsType, ExtendedReferencedExport, HarmonyExportInitFragment, ModuleDependency, ModuleGraph, + ModuleIdentifier, RuntimeSpec, TemplateContext, TemplateReplaceSource, UsageState, }; use rustc_hash::FxHashSet as HashSet; use swc_core::ecma::atoms::JsWord; @@ -11,7 +11,9 @@ use swc_core::ecma::atoms::JsWord; use super::create_resource_identifier_for_esm_dependency; // Create _webpack_require__.d(__webpack_exports__, {}). -// import { a } from 'a'; export { a } +// case1: `import { a } from 'a'; export { a }` +// case2: `export { a } from 'a';` +// TODO case3: `export * from 'a'` #[derive(Debug, Clone)] pub struct HarmonyExportImportedSpecifierDependency { pub id: DependencyId, @@ -19,19 +21,353 @@ pub struct HarmonyExportImportedSpecifierDependency { pub ids: Vec<(JsWord, Option)>, name: Option, resource_identifier: String, + // Because it is shared by multiply HarmonyExportImportedSpecifierDependency, so put it to `BuildInfo` + // pub active_exports: HashSet, + // pub all_star_exports: Option>, + pub other_star_exports: Option>, // look like it is unused } impl HarmonyExportImportedSpecifierDependency { - pub fn new(request: JsWord, ids: Vec<(JsWord, Option)>) -> Self { + pub fn new(request: JsWord, ids: Vec<(JsWord, Option)>, name: Option) -> Self { let resource_identifier = create_resource_identifier_for_esm_dependency(&request); Self { id: DependencyId::new(), - name: None, + name, request, ids, resource_identifier, + other_star_exports: None, } } + + pub fn active_exports<'a>(&self, module_graph: &'a ModuleGraph) -> &'a HashSet { + let build_info = module_graph + .module_graph_module_by_dependency_id(&self.id) + .expect("should have mgm") + .build_info + .as_ref() + .expect("should have build info"); + &build_info.harmony_named_exports + } + + pub fn all_star_exports<'a>(&self, module_graph: &'a ModuleGraph) -> &'a Vec { + let build_info = module_graph + .module_graph_module_by_dependency_id(&self.id) + .expect("should have mgm") + .build_info + .as_ref() + .expect("should have build info"); + &build_info.all_star_exports + } + + // TODO cache get_mode result + #[allow(unused)] + pub fn get_mode( + &self, + name: Option, + ids: &Vec, + module_graph: &ModuleGraph, + id: &DependencyId, + runtime: Option<&RuntimeSpec>, + ) -> ExportMode { + let imported_module_identifier = if let Some(imported_module_identifier) = + module_graph.module_identifier_by_dependency_id(id) + { + imported_module_identifier + } else { + return ExportMode { + kind: ExportModeType::Missing, + ..Default::default() + }; + }; + + let parent_module = module_graph + .parent_module_by_dependency_id(id) + .expect("should have parent module"); + let exports_type = get_exports_type(module_graph, id, &parent_module); + let exports_info = module_graph.get_exports_info(&parent_module); + + // Special handling for reexporting the default export + // from non-namespace modules + if let Some(name) = name.as_ref() && !ids.is_empty() && let Some(id) = ids.get(0) && id == "default" { + match exports_type { + ExportsType::Dynamic => { + return ExportMode { kind: ExportModeType::ReexportDynamicDefault, name: Some(name.clone()), ..Default::default() } + }, + ExportsType::DefaultOnly | ExportsType::DefaultWithNamed => { + let export_info = exports_info.id.get_read_only_export_info(name, module_graph).id; + return ExportMode { kind: ExportModeType::ReexportNamedDefault, name: Some(name.clone()), partial_namespace_export_info: Some(export_info), ..Default::default() } + }, + _ => {} + } + } + + // reexporting with a fixed name + if let Some(name) = name { + let export_info = exports_info + .id + .get_read_only_export_info(&name, module_graph) + .id; + if !ids.is_empty() { + // export { name as name } + match exports_type { + ExportsType::DefaultOnly => { + return ExportMode { + kind: ExportModeType::ReexportUndefined, + name: Some(name), + ..Default::default() + } + } + _ => { + return ExportMode { + kind: ExportModeType::NormalReexport, + items: Some(vec![NormalReexportItem { + name, + ids: ids.to_vec(), + hidden: false, + checked: false, + export_info, + }]), + ..Default::default() + } + } + } + } else { + // export * as name + match exports_type { + ExportsType::DefaultOnly => { + return ExportMode { + kind: ExportModeType::ReexportFakeNamespaceObject, + name: Some(name), + partial_namespace_export_info: Some(export_info), + fake_type: Some(0), + ..Default::default() + } + } + ExportsType::DefaultWithNamed => { + return ExportMode { + kind: ExportModeType::ReexportFakeNamespaceObject, + name: Some(name), + partial_namespace_export_info: Some(export_info), + fake_type: Some(2), + ..Default::default() + } + } + _ => { + return ExportMode { + kind: ExportModeType::ReexportNamespaceObject, + name: Some(name), + partial_namespace_export_info: Some(export_info), + ..Default::default() + } + } + } + } + } + + let StarReexportsInfo { + exports, + checked, + ignored_exports, + hidden, + } = self.get_star_reexports(module_graph, runtime, imported_module_identifier); + + if let Some(exports) = exports { + if exports.is_empty() { + return ExportMode { + kind: ExportModeType::EmptyStar, + hidden, + ..Default::default() + }; + } + + let mut items = exports + .into_iter() + .map(|export_name| NormalReexportItem { + name: export_name.clone(), + ids: vec![export_name.clone()], + hidden: false, + checked: checked.as_ref().map(|c| c.contains(&export_name)).is_some(), + export_info: exports_info + .id + .get_read_only_export_info(&export_name, module_graph) + .id, + }) + .collect::>(); + + if let Some(hidden) = &hidden { + for export_name in hidden.iter() { + items.push(NormalReexportItem { + name: export_name.clone(), + ids: vec![export_name.clone()], + hidden: true, + checked: false, + export_info: exports_info + .id + .get_read_only_export_info(export_name, module_graph) + .id, + }); + } + } + + ExportMode { + kind: ExportModeType::NormalReexport, + items: Some(items), + ..Default::default() + } + } else { + ExportMode { + kind: ExportModeType::DynamicReexport, + ignored: Some(ignored_exports), + hidden, + ..Default::default() + } + } + } + + pub fn get_star_reexports( + &self, + module_graph: &ModuleGraph, + runtime: Option<&RuntimeSpec>, + imported_module_identifier: &ModuleIdentifier, + ) -> StarReexportsInfo { + let imported_exports_info = module_graph.get_exports_info(imported_module_identifier); + let other_export_info = module_graph + .export_info_map + .get(&imported_exports_info.other_exports_info) + .expect("should have export info"); + let no_extra_exports = matches!(other_export_info.provided, Some(ExportInfoProvided::False)); + let no_extra_imports = matches!(other_export_info.get_used(runtime), UsageState::Unused); + let ignored_exports: HashSet = { + let mut e = self.active_exports(module_graph).clone(); + e.insert("default".into()); + e + }; + let mut hidden_exports = self.discover_active_exports_from_other_star_exports(module_graph); + if !no_extra_exports && !no_extra_imports { + if let Some(hidden_exports) = hidden_exports.as_mut() { + for e in ignored_exports.iter() { + hidden_exports.remove(e); + } + } + return StarReexportsInfo { + ignored_exports, + hidden: hidden_exports, + ..Default::default() + }; + } + + let mut exports = HashSet::default(); + let mut checked = HashSet::default(); + let mut hidden = if hidden_exports.is_some() { + Some(HashSet::default()) + } else { + None + }; + + let parent_module = module_graph + .parent_module_by_dependency_id(&self.id) + .expect("should have parent module"); + let exports_info = module_graph.get_exports_info(&parent_module); + if no_extra_imports { + for export_info_id in exports_info.get_ordered_exports() { + let export_info = module_graph + .export_info_map + .get(export_info_id) + .expect("should have export info"); + if ignored_exports.contains(&export_info.name) + || matches!(export_info.get_used(runtime), UsageState::Unused) + { + continue; + } + let imported_export_info = imported_exports_info + .id + .get_read_only_export_info(&export_info.name, module_graph); + if matches!( + imported_export_info.provided, + Some(ExportInfoProvided::False) + ) { + continue; + } + if let Some(hidden) = hidden.as_mut() && hidden_exports.as_ref() + .map(|hidden_exports| hidden_exports.contains(&export_info.name)) + .is_some() + { + hidden.insert(export_info.name.clone()); + continue; + } + exports.insert(export_info.name.clone()); + if matches!( + imported_export_info.provided, + Some(ExportInfoProvided::True) + ) { + continue; + } + checked.insert(export_info.name.clone()); + } + } else if no_extra_exports { + for import_export_info_id in imported_exports_info.get_ordered_exports() { + let import_export_info = module_graph + .export_info_map + .get(import_export_info_id) + .expect("should have export info"); + if ignored_exports.contains(&import_export_info.name) + || matches!(import_export_info.provided, Some(ExportInfoProvided::False)) + { + continue; + } + let export_info = exports_info + .id + .get_read_only_export_info(&import_export_info.name, module_graph); + if matches!(export_info.get_used(runtime), UsageState::Unused) { + continue; + } + if let Some(hidden) = hidden.as_mut() && hidden_exports.as_ref() + .map(|hidden_exports| hidden_exports.contains(&import_export_info.name)) + .is_some() + { + hidden.insert(import_export_info.name.clone()); + continue; + } + exports.insert(import_export_info.name.clone()); + if matches!(import_export_info.provided, Some(ExportInfoProvided::True)) { + continue; + } + checked.insert(import_export_info.name.clone()); + } + } + + StarReexportsInfo { + ignored_exports, + exports: Some(exports), + checked: Some(checked), + hidden, + } + } + + pub fn discover_active_exports_from_other_star_exports( + &self, + module_graph: &ModuleGraph, + ) -> Option> { + if let Some(other_star_exports) = &self.other_star_exports { + if other_star_exports.is_empty() { + return None; + } + } + + let all_star_exports = self.all_star_exports(module_graph); + if !all_star_exports.is_empty() { + let names = determine_export_assignments(module_graph, all_star_exports.clone(), None); + return Some(names); + } + + if let Some(other_star_exports) = &self.other_star_exports { + let names = + determine_export_assignments(module_graph, other_star_exports.clone(), Some(self.id)); + return Some(names); + } + None + } } impl DependencyTemplate for HarmonyExportImportedSpecifierDependency { @@ -132,11 +468,12 @@ impl ModuleDependency for HarmonyExportImportedSpecifierDependency { module_graph: &ModuleGraph, runtime: Option<&RuntimeSpec>, ) -> Vec { - let mode = get_mode( + let mode = self.get_mode( self.name.clone(), &self.ids.iter().map(|id| id.0.clone()).collect::>(), module_graph, &self.id, + runtime, ); match mode.kind { ExportModeType::Missing @@ -156,7 +493,7 @@ impl ModuleDependency for HarmonyExportImportedSpecifierDependency { runtime, &mut referenced_exports, vec![], - Some(partial_namespace_export_info), + Some(*partial_namespace_export_info), mode.kind == ExportModeType::ReexportFakeNamespaceObject, &mut Default::default(), ); @@ -216,108 +553,61 @@ pub enum ExportModeType { } #[derive(Debug)] -pub struct NormalReexportItem<'a> { +pub struct NormalReexportItem { pub name: JsWord, pub ids: Vec, pub hidden: bool, pub checked: bool, - pub export_info: &'a ExportInfo, + pub export_info: ExportInfoId, } #[derive(Debug, Default)] -pub struct ExportMode<'a> { +pub struct ExportMode { pub kind: ExportModeType, - pub items: Option>>, + pub items: Option>, pub name: Option, pub fake_type: Option, - pub partial_namespace_export_info: Option, + pub partial_namespace_export_info: Option, + pub ignored: Option>, + pub hidden: Option>, } -// TODO cache get_mode result -#[allow(unused)] -pub fn get_mode<'a>( - name: Option, - ids: &Vec, - module_graph: &'a ModuleGraph, - id: &DependencyId, -) -> ExportMode<'a> { - let parent_module = module_graph - .parent_module_by_dependency_id(id) - .expect("should have parent module"); - let exports_type = get_exports_type(module_graph, id, &parent_module); - let exports_info = module_graph.get_exports_info(&parent_module); - if let Some(name) = name.as_ref() && !ids.is_empty() && let Some(id) = ids.get(0) && id == "default" { - match exports_type { - ExportsType::Dynamic => { - return ExportMode { kind: ExportModeType::ReexportDynamicDefault, name: Some(name.clone()), ..Default::default() } - }, - ExportsType::DefaultOnly | ExportsType::DefaultWithNamed => { - return ExportMode { kind: ExportModeType::ReexportNamedDefault, name: Some(name.clone()), ..Default::default() } - }, - _ => {} - } +#[derive(Debug, Default)] +pub struct StarReexportsInfo { + exports: Option>, + checked: Option>, + ignored_exports: HashSet, + hidden: Option>, +} + +fn determine_export_assignments( + module_graph: &ModuleGraph, + mut dependencies: Vec, + additional_dependency: Option, +) -> HashSet { + if let Some(additional_dependency) = additional_dependency { + dependencies.push(additional_dependency); } - if let Some(name) = name { - let export_info = exports_info - .id - .get_read_only_export_info(&name, module_graph); - if !ids.is_empty() { - match exports_type { - ExportsType::DefaultOnly => { - return ExportMode { - kind: ExportModeType::ReexportUndefined, - name: Some(name), - ..Default::default() - } - } - _ => { - return ExportMode { - kind: ExportModeType::NormalReexport, - items: Some(vec![NormalReexportItem { - name, - ids: ids.to_vec(), - hidden: false, - checked: false, - export_info, - }]), - ..Default::default() - } - } - } - } else { - match exports_type { - ExportsType::DefaultOnly => { - return ExportMode { - kind: ExportModeType::ReexportFakeNamespaceObject, - name: Some(name), - fake_type: Some(0), - ..Default::default() - } - } - ExportsType::DefaultWithNamed => { - return ExportMode { - kind: ExportModeType::ReexportFakeNamespaceObject, - name: Some(name), - fake_type: Some(2), - ..Default::default() - } - } - _ => { - return ExportMode { - kind: ExportModeType::ReexportNamespaceObject, - name: Some(name), - ..Default::default() - } + let mut names = HashSet::default(); + + for dependency in dependencies.iter() { + if let Some(module_identifier) = module_graph.module_identifier_by_dependency_id(dependency) { + let exports_info = module_graph.get_exports_info(module_identifier); + for export_info_id in exports_info.exports.values() { + let export_info = module_graph + .export_info_map + .get(export_info_id) + .expect("should have export info"); + if matches!(export_info.provided, Some(ExportInfoProvided::True)) + && &export_info.name != "default" + && !names.contains(&export_info.name) + { + names.insert(export_info.name.clone()); } } } } - // todo star reexporting - ExportMode { - kind: ExportModeType::NormalReexport, - items: Some(vec![]), - ..Default::default() - } + names } diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_specifier_dependency.rs index c22c6e437989..fc44da3b8e25 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_specifier_dependency.rs @@ -9,14 +9,16 @@ use swc_core::ecma::atoms::JsWord; #[derive(Debug, Clone)] pub struct HarmonyExportSpecifierDependency { id: DependencyId, - export: (JsWord, JsWord), + name: JsWord, + value: JsWord, // id } impl HarmonyExportSpecifierDependency { - pub fn new(export: (JsWord, JsWord)) -> Self { + pub fn new(name: JsWord, value: JsWord) -> Self { Self { id: DependencyId::new(), - export, + name, + value, } } } @@ -36,7 +38,7 @@ impl Dependency for HarmonyExportSpecifierDependency { fn get_exports(&self) -> Option { Some(ExportsSpec { - exports: ExportsOfExportsSpec::Array(vec![ExportNameOrSpec::String(self.export.0.clone())]), + exports: ExportsOfExportsSpec::Array(vec![ExportNameOrSpec::String(self.name.clone())]), priority: Some(1), can_mangle: None, terminal_binding: Some(true), @@ -68,14 +70,15 @@ impl DependencyTemplate for HarmonyExportSpecifierDependency { .module_graph .get_exports_info(&module.identifier()) .get_used_exports() - .contains(&self.export.0) + .contains(&self.name) } else { true }; if used { - init_fragments.push(Box::new(HarmonyExportInitFragment::new( - self.export.clone(), - ))); + init_fragments.push(Box::new(HarmonyExportInitFragment::new(( + self.name.clone(), + self.value.clone(), + )))); } } } diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs index 3907d6336d14..d9d1ca26ae4b 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs @@ -1,10 +1,11 @@ -use rspack_core::{create_exports_object_referenced, ExtendedReferencedExport}; use rspack_core::{ - export_from_import, get_dependency_used_by_exports_condition, get_exports_type, + create_exports_object_referenced, create_no_exports_referenced, export_from_import, + get_dependency_used_by_exports_condition, get_exports_type, tree_shaking::symbol::DEFAULT_JS_WORD, Compilation, ConnectionState, Dependency, DependencyCategory, DependencyCondition, DependencyId, DependencyTemplate, DependencyType, - ErrorSpan, ExportsType, ModuleDependency, ModuleGraph, ModuleGraphModule, ModuleIdentifier, - ReferencedExport, RuntimeSpec, TemplateContext, TemplateReplaceSource, UsedByExports, + ErrorSpan, ExportsType, ExtendedReferencedExport, ModuleDependency, ModuleGraph, + ModuleGraphModule, ModuleIdentifier, ReferencedExport, RuntimeSpec, TemplateContext, + TemplateReplaceSource, UsedByExports, }; use rustc_hash::FxHashSet as HashSet; use swc_core::ecma::atoms::JsWord; @@ -39,6 +40,7 @@ impl HarmonyImportSpecifierDependency { call: bool, direct_import: bool, specifier: Specifier, + referenced_properties_in_destructuring: Option>, ) -> Self { let resource_identifier = create_resource_identifier_for_esm_dependency(&request); Self { @@ -53,7 +55,7 @@ impl HarmonyImportSpecifierDependency { specifier, used_by_exports: UsedByExports::default(), namespace_object_as_context: false, - referenced_properties_in_destructuring: None, + referenced_properties_in_destructuring, resource_identifier, } } @@ -215,6 +217,7 @@ impl ModuleDependency for HarmonyImportSpecifierDependency { module_graph: &ModuleGraph, _runtime: Option<&RuntimeSpec>, ) -> Vec { + // namespace import if self.ids.is_empty() { return self.get_referenced_exports_in_destructuring(None); } @@ -233,7 +236,7 @@ impl ModuleDependency for HarmonyImportSpecifierDependency { namespace_object_as_context = true; } ExportsType::Dynamic => { - return vec![] + return create_no_exports_referenced(); } _ => {} } diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_export_dependency_scanner.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_export_dependency_scanner.rs index 5e4023ae100a..4582bde80246 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_export_dependency_scanner.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_export_dependency_scanner.rs @@ -1,6 +1,6 @@ use rspack_core::{ - tree_shaking::symbol::DEFAULT_JS_WORD, BoxDependency, BoxDependencyTemplate, ConstDependency, - SpanExt, + tree_shaking::symbol::DEFAULT_JS_WORD, BoxDependency, BoxDependencyTemplate, BuildInfo, + ConstDependency, SpanExt, }; use swc_core::{ common::Spanned, @@ -24,6 +24,7 @@ pub struct HarmonyExportDependencyScanner<'a> { pub dependencies: &'a mut Vec, pub presentational_dependencies: &'a mut Vec, pub import_map: &'a mut ImportMap, + pub build_info: &'a mut BuildInfo, } impl<'a> HarmonyExportDependencyScanner<'a> { @@ -31,11 +32,13 @@ impl<'a> HarmonyExportDependencyScanner<'a> { dependencies: &'a mut Vec, presentational_dependencies: &'a mut Vec, import_map: &'a mut ImportMap, + build_info: &'a mut BuildInfo, ) -> Self { Self { dependencies, presentational_dependencies, import_map, + build_info, } } } @@ -52,10 +55,14 @@ impl Visit for HarmonyExportDependencyScanner<'_> { Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => { self .dependencies - .push(Box::new(HarmonyExportSpecifierDependency::new(( + .push(Box::new(HarmonyExportSpecifierDependency::new( ident.sym.clone(), ident.sym.clone(), - )))); + ))); + self + .build_info + .harmony_named_exports + .insert(ident.sym.clone()); } Decl::Var(v) => { find_pat_ids::<_, Ident>(&v.decls) @@ -63,10 +70,11 @@ impl Visit for HarmonyExportDependencyScanner<'_> { .for_each(|ident| { self .dependencies - .push(Box::new(HarmonyExportSpecifierDependency::new(( + .push(Box::new(HarmonyExportSpecifierDependency::new( + ident.sym.clone(), ident.sym.clone(), - ident.sym, - )))) + ))); + self.build_info.harmony_named_exports.insert(ident.sym); }); } _ => {} @@ -96,15 +104,17 @@ impl Visit for HarmonyExportDependencyScanner<'_> { .dependencies .push(Box::new(HarmonyExportImportedSpecifierDependency::new( reference.request.clone(), - vec![(export, reference.names.clone())], + vec![(export.clone(), reference.names.clone())], + Some(export), ))); } else { self .dependencies - .push(Box::new(HarmonyExportSpecifierDependency::new(( - export, + .push(Box::new(HarmonyExportSpecifierDependency::new( + export.clone(), orig.sym.clone(), - )))); + ))); + self.build_info.harmony_named_exports.insert(export); } } } @@ -122,12 +132,13 @@ impl Visit for HarmonyExportDependencyScanner<'_> { } fn visit_export_default_expr(&mut self, export_default_expr: &'_ ExportDefaultExpr) { + // TODO this should be at `HarmonyExportExpressionDependency` self .dependencies - .push(Box::new(HarmonyExportSpecifierDependency::new(( + .push(Box::new(HarmonyExportSpecifierDependency::new( DEFAULT_JS_WORD.clone(), DEFAULT_EXPORT.into(), - )))); + ))); self .presentational_dependencies @@ -146,15 +157,16 @@ impl Visit for HarmonyExportDependencyScanner<'_> { _ => unreachable!(), }; + // TODO this should be at `HarmonyExportExpressionDependency` self .dependencies - .push(Box::new(HarmonyExportSpecifierDependency::new(( + .push(Box::new(HarmonyExportSpecifierDependency::new( DEFAULT_JS_WORD.clone(), match &ident { Some(ident) => ident.sym.clone(), None => DEFAULT_EXPORT.into(), }, - )))); + ))); self .presentational_dependencies diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_import_dependency_scanner.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_import_dependency_scanner.rs index d324a3a078d5..141df218b505 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_import_dependency_scanner.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_import_dependency_scanner.rs @@ -1,21 +1,23 @@ use indexmap::IndexMap; use rspack_core::{ - tree_shaking::symbol::DEFAULT_JS_WORD, BoxDependency, BoxDependencyTemplate, ConstDependency, - DependencyType, SpanExt, + tree_shaking::symbol::DEFAULT_JS_WORD, BoxDependency, BoxDependencyTemplate, BuildInfo, + ConstDependency, DependencyType, SpanExt, }; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; use swc_core::{ common::Span, ecma::{ ast::{ - Callee, ExportAll, ExportSpecifier, Expr, Id, Ident, ImportDecl, ImportSpecifier, Lit, - MemberExpr, MemberProp, ModuleExportName, NamedExport, Program, Prop, + AssignExpr, AssignOp, Callee, ExportAll, ExportSpecifier, Expr, Id, Ident, ImportDecl, + ImportSpecifier, Lit, MemberExpr, MemberProp, ModuleExportName, NamedExport, Pat, PatOrExpr, + Program, Prop, }, atoms::JsWord, visit::{noop_visit_type, Visit, VisitWith}, }, }; +use super::collect_destructuring_assignment_properties; use crate::dependency::{ HarmonyExportImportedSpecifierDependency, HarmonyImportDependency, HarmonyImportSpecifierDependency, Specifier, @@ -37,7 +39,7 @@ impl ImporterReferenceInfo { } } -pub type ImportMap = FxHashMap; +pub type ImportMap = HashMap; pub struct ImporterInfo { pub span: Span, @@ -62,6 +64,7 @@ pub struct HarmonyImportDependencyScanner<'a> { pub presentational_dependencies: &'a mut Vec, pub import_map: &'a mut ImportMap, pub imports: Imports, + pub build_info: &'a mut BuildInfo, } impl<'a> HarmonyImportDependencyScanner<'a> { @@ -69,12 +72,14 @@ impl<'a> HarmonyImportDependencyScanner<'a> { dependencies: &'a mut Vec, presentational_dependencies: &'a mut Vec, import_map: &'a mut ImportMap, + build_info: &'a mut BuildInfo, ) -> Self { Self { dependencies, presentational_dependencies, import_map, imports: Default::default(), + build_info, } } } @@ -85,41 +90,52 @@ impl Visit for HarmonyImportDependencyScanner<'_> { fn visit_program(&mut self, program: &Program) { // collect import map info program.visit_children_with(self); - for ((request, dependency_type), importer_info) in std::mem::take(&mut self.imports).into_iter() { if matches!(dependency_type, DependencyType::EsmExport) && !importer_info.specifiers.is_empty() { - let ids = importer_info + importer_info .specifiers .iter() - .map(|specifier| match specifier { - Specifier::Namespace(n) => (n.clone(), None), + .for_each(|specifier| match specifier { + Specifier::Namespace(n) => { + self + .dependencies + .push(Box::new(HarmonyExportImportedSpecifierDependency::new( + request.clone(), + vec![(n.clone(), None)], + Some(n.clone()), + ))); + self.build_info.harmony_named_exports.insert(n.clone()); + } Specifier::Default(_) => { unreachable!() } Specifier::Named(orig, exported) => { - (exported.clone().unwrap_or(orig.clone()), Some(orig.clone())) + let name = exported.clone().unwrap_or(orig.clone()); + self + .dependencies + .push(Box::new(HarmonyExportImportedSpecifierDependency::new( + request.clone(), + vec![(name.clone(), Some(orig.clone()))], + Some(name.clone()), + ))); + self.build_info.harmony_named_exports.insert(name); } - }) - .collect::>(); - self - .dependencies - .push(Box::new(HarmonyExportImportedSpecifierDependency::new( - request.clone(), - ids, - ))); + }); } - self - .dependencies - .push(Box::new(HarmonyImportDependency::new( - request.clone(), - Some(importer_info.span.into()), - importer_info.specifiers, - dependency_type, - importer_info.exports_all, - ))); + let dependency = HarmonyImportDependency::new( + request.clone(), + Some(importer_info.span.into()), + importer_info.specifiers, + dependency_type, + importer_info.exports_all, + ); + if importer_info.exports_all { + self.build_info.all_star_exports.push(dependency.id); + } + self.dependencies.push(Box::new(dependency)); } // collect import reference info @@ -265,6 +281,7 @@ pub struct HarmonyImportRefDependencyScanner<'a> { pub enter_callee: bool, pub import_map: &'a ImportMap, pub dependencies: &'a mut Vec, + pub properties_in_destructuring: HashMap>, } impl<'a> HarmonyImportRefDependencyScanner<'a> { @@ -273,6 +290,7 @@ impl<'a> HarmonyImportRefDependencyScanner<'a> { import_map, dependencies, enter_callee: false, + properties_in_destructuring: HashMap::default(), } } } @@ -280,6 +298,18 @@ impl<'a> HarmonyImportRefDependencyScanner<'a> { impl Visit for HarmonyImportRefDependencyScanner<'_> { noop_visit_type!(); + // collect referenced properties in destructuring + // import * as a from 'a'; + // const { value } = a; + fn visit_assign_expr(&mut self, assign_expr: &AssignExpr) { + if let PatOrExpr::Pat(box Pat::Object(object_pat)) = &assign_expr.left && assign_expr.op == AssignOp::Assign && let box Expr::Ident(ident) = &assign_expr.right && let Some(reference) = self.import_map.get(&ident.to_id()) && matches!(reference.specifier, Specifier::Namespace(_)) { + if let Some(value) = collect_destructuring_assignment_properties(object_pat) { + self.properties_in_destructuring.entry(ident.sym.clone()).and_modify(|v| v.extend(value.clone())).or_insert(value); + } + } + assign_expr.visit_children_with(self); + } + fn visit_prop(&mut self, n: &Prop) { match n { Prop::Shorthand(shorthand) => { @@ -295,6 +325,7 @@ impl Visit for HarmonyImportRefDependencyScanner<'_> { false, false, reference.specifier.clone(), + None, ))); } } @@ -315,6 +346,7 @@ impl Visit for HarmonyImportRefDependencyScanner<'_> { self.enter_callee, true, // x() reference.specifier.clone(), + self.properties_in_destructuring.remove(&ident.sym), ))); } } @@ -347,6 +379,7 @@ impl Visit for HarmonyImportRefDependencyScanner<'_> { self.enter_callee, !self.enter_callee, // x.xx() reference.specifier.clone(), + None, ))); return; } diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs index 66619f337d55..24c702aaf076 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs @@ -36,7 +36,6 @@ use self::{ node_stuff_scanner::NodeStuffScanner, require_context_scanner::RequireContextScanner, url_scanner::UrlScanner, worker_scanner::WorkerScanner, }; - pub type ScanDependenciesResult = (Vec, Vec); #[allow(clippy::too_many_arguments)] @@ -112,11 +111,13 @@ pub fn scan_dependencies( &mut dependencies, &mut presentational_dependencies, &mut import_map, + build_info, )); program.visit_with(&mut HarmonyExportDependencyScanner::new( &mut dependencies, &mut presentational_dependencies, &mut import_map, + build_info, )); let mut worker_syntax_scanner = rspack_core::needs_refactor::WorkerSyntaxScanner::new( rspack_core::needs_refactor::DEFAULT_WORKER_SYNTAX, diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/util.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/util.rs index fcae8389fae7..df2d34d28e88 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/util.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/util.rs @@ -1,13 +1,35 @@ +use rustc_hash::FxHashSet as HashSet; use swc_core::{ - common::{pass::AstNodePath, SyntaxContext}, + common::SyntaxContext, ecma::{ - ast::{CallExpr, Expr, MemberExpr}, - visit::{AstParentKind, AstParentNodeRef}, + ast::{CallExpr, Expr, MemberExpr, ObjectPat, ObjectPatProp, PropName}, + atoms::JsWord, }, }; -pub fn as_parent_path(ast_path: &AstNodePath>) -> Vec { - ast_path.iter().map(|n| n.kind()).collect() +pub fn collect_destructuring_assignment_properties( + object_pat: &ObjectPat, +) -> Option> { + let mut properties = HashSet::default(); + + for property in &object_pat.props { + match property { + ObjectPatProp::Assign(assign) => { + properties.insert(assign.key.sym.clone()); + } + ObjectPatProp::KeyValue(key_value) => { + if let PropName::Ident(ident) = &key_value.key { + properties.insert(ident.sym.clone()); + } + } + ObjectPatProp::Rest(_) => {} + } + } + + if properties.is_empty() { + return None; + } + Some(properties) } pub(crate) mod expr_matcher { From e3615d0d3eed7506186a2f35fc1d85c7b769bd18 Mon Sep 17 00:00:00 2001 From: Hana Date: Fri, 8 Sep 2023 14:20:08 +0800 Subject: [PATCH 20/30] fix: patch `sendStats` of webpack-dev-server (#4101) * fix: patch `sendStats` webpack-dev-server * chore: tweak comment --- packages/rspack-dev-server/src/patch.ts | 34 ++++++++++++++++++++++++ packages/rspack-dev-server/src/server.ts | 3 +++ packages/rspack/src/Stats.ts | 6 ++--- 3 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 packages/rspack-dev-server/src/patch.ts diff --git a/packages/rspack-dev-server/src/patch.ts b/packages/rspack-dev-server/src/patch.ts new file mode 100644 index 000000000000..b77fb0668aad --- /dev/null +++ b/packages/rspack-dev-server/src/patch.ts @@ -0,0 +1,34 @@ +import WebpackDevServer from "webpack-dev-server"; + +let old: InstanceType["sendStats"] | undefined; + +function restoreDevServerPatch() { + // @ts-expect-error private API + WebpackDevServer.prototype.sendStats = old; +} + +// Patch webpack-dev-server to prevent it from failing to send stats. +// See https://github.com/web-infra-dev/rspack/pull/4028 for details. +function applyDevServerPatch() { + if (old) return restoreDevServerPatch; + + // @ts-expect-error private API + old = WebpackDevServer.prototype.sendStats; + + // @ts-expect-error private API + WebpackDevServer.prototype.sendStats = function sendStats__rspack_patched( + ...args + ) { + let stats = args[1]; + + if (!stats) { + return; + } + + return old.apply(this, args); + }; + + return restoreDevServerPatch; +} + +export { applyDevServerPatch }; diff --git a/packages/rspack-dev-server/src/server.ts b/packages/rspack-dev-server/src/server.ts index 076b61fd212d..f7f6748e2174 100644 --- a/packages/rspack-dev-server/src/server.ts +++ b/packages/rspack-dev-server/src/server.ts @@ -16,9 +16,12 @@ import fs from "fs"; import WebpackDevServer from "webpack-dev-server"; import type { ResolvedDevServer, DevServer } from "./config"; import { getRspackMemoryAssets } from "./middleware"; +import { applyDevServerPatch } from "./patch"; // @ts-expect-error import { runtimePaths } from "@rspack/dev-client/runtime-paths"; +applyDevServerPatch(); + export class RspackDevServer extends WebpackDevServer { /** * resolved after `normalizedOptions` diff --git a/packages/rspack/src/Stats.ts b/packages/rspack/src/Stats.ts index 63eafc8b875e..c3a0a9885b12 100644 --- a/packages/rspack/src/Stats.ts +++ b/packages/rspack/src/Stats.ts @@ -41,9 +41,8 @@ export class Stats { const statsFactory = this.compilation.createStatsFactory(options); // FIXME: This is a really ugly workaround for avoid panic for accessing previous compilation. - // webpack-dev-server and Modern.js dev server will detect whether the returned stats is available. + // Modern.js dev server will detect whether the returned stats is available. // So this does not do harm to these frameworks. - // webpack-dev-server: https://github.com/webpack/webpack-dev-server/blob/540c43852ea33f9cb18820e1cef05d5ddb86cc3e/lib/Server.js#L3222 // Modern.js: https://github.com/web-infra-dev/modern.js/blob/63f916f882f7d16096949e264e119218c0ab8d7d/packages/server/server/src/dev-tools/dev-middleware/socketServer.ts#L172 let stats: StatsCompilation | null = null; try { @@ -69,9 +68,8 @@ export class Stats { const statsPrinter = this.compilation.createStatsPrinter(options); // FIXME: This is a really ugly workaround for avoid panic for accessing previous compilation. - // webpack-dev-server and Modern.js dev server will detect whether the returned stats is available. + // Modern.js dev server will detect whether the returned stats is available. // So this does not do harm to these frameworks. - // webpack-dev-server: https://github.com/webpack/webpack-dev-server/blob/540c43852ea33f9cb18820e1cef05d5ddb86cc3e/lib/Server.js#L3222 // Modern.js: https://github.com/web-infra-dev/modern.js/blob/63f916f882f7d16096949e264e119218c0ab8d7d/packages/server/server/src/dev-tools/dev-middleware/socketServer.ts#L172 let stats: StatsCompilation | null = null; try { From ee3ded1eaa384a41317e66ca9b00aaafc5f85b20 Mon Sep 17 00:00:00 2001 From: Kei Sakamoto <6201376+Thiry1@users.noreply.github.com> Date: Fri, 8 Sep 2023 15:37:46 +0900 Subject: [PATCH 21/30] fix(css/modules): bump swc version to fix #3875 and add test (#4144) * fix(css/modules): bump swc version to fix #3875 and add test * test(css): add snapshot of js part --- Cargo.lock | 180 +++++++++--------- Cargo.toml | 16 +- .../rspack_importer/snapshot/output.snap | 22 +++ .../fixtures/custom/composes/b.module.css | 6 + .../fixtures/custom/composes/d.module.css | 7 + .../fixtures/custom/composes/e.module.css | 7 + .../fixtures/custom/composes/f.module.css | 6 + .../tests/fixtures/custom/composes/index.js | 1 + .../custom/composes/snapshot/output.snap | 70 +++++++ .../fixtures/custom/composes/style.module.css | 19 ++ .../fixtures/custom/composes/test.config.json | 20 ++ .../custom/http_import/snapshot/output.snap | 20 ++ .../modules-ident-name/snapshot/output.snap | 24 +++ .../webpack/at-charset/snapshot/output.snap | 38 ++++ .../snapshot/output.snap | 20 ++ .../webpack/at-import/snapshot/output.snap | 38 ++++ .../default-options/snapshot/output.snap | 20 ++ .../multiple-entry/snapshot/output.snap | 85 +++++++++ .../webpack/nested/snapshot/output.snap | 34 ++++ .../webpack/output-iife/snapshot/output.snap | 20 ++ .../shared-import/snapshot/output.snap | 35 ++++ .../snapshot/output.snap | 62 ++++++ .../webpack/simple-async/snapshot/output.snap | 44 +++++ .../snapshot/output.snap | 17 ++ .../snapshot/output.snap | 20 ++ .../simple-multiple/snapshot/output.snap | 24 +++ .../webpack/simple/snapshot/output.snap | 20 ++ .../src/ast/stringify.rs | 10 +- crates/rspack_testing/src/run_fixture.rs | 5 +- 29 files changed, 785 insertions(+), 105 deletions(-) create mode 100644 crates/rspack_plugin_css/tests/fixtures/custom/composes/b.module.css create mode 100644 crates/rspack_plugin_css/tests/fixtures/custom/composes/d.module.css create mode 100644 crates/rspack_plugin_css/tests/fixtures/custom/composes/e.module.css create mode 100644 crates/rspack_plugin_css/tests/fixtures/custom/composes/f.module.css create mode 100644 crates/rspack_plugin_css/tests/fixtures/custom/composes/index.js create mode 100644 crates/rspack_plugin_css/tests/fixtures/custom/composes/snapshot/output.snap create mode 100644 crates/rspack_plugin_css/tests/fixtures/custom/composes/style.module.css create mode 100644 crates/rspack_plugin_css/tests/fixtures/custom/composes/test.config.json diff --git a/Cargo.lock b/Cargo.lock index 40b31cbc4608..01020bd2459a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3611,9 +3611,9 @@ dependencies = [ [[package]] name = "swc" -version = "0.265.9" +version = "0.266.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b459cd0cdd42afc8347f3dbbda137e9e4010523034dd550d647c99902cea7800" +checksum = "ee8d051cbb46162e3c7a7c2ffb43b0b2d9f1994a68dd84ef422afd752acd398c" dependencies = [ "anyhow", "base64", @@ -3686,9 +3686,9 @@ dependencies = [ [[package]] name = "swc_common" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cb7fcd56655c8ae7dcf2344f0be6cbff4d9c7cb401fe3ec8e56e1de8dfe582" +checksum = "9c84742fc22df1c293da5354c1cc8a5b45a045e9dc941005c1fd9cb4e9bdabc1" dependencies = [ "ahash 0.8.3", "ast_node", @@ -3742,9 +3742,9 @@ dependencies = [ [[package]] name = "swc_core" -version = "0.82.9" +version = "0.83.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb84a4863eec3f95ddc851ff0be49f8e53ccea3eab0f91e93baa4cb22d7ab83" +checksum = "b6b167dfadf8606ac1dc5ae13d22d057f195eed89bb0ecb42b468a317b1ed39e" dependencies = [ "swc", "swc_atoms", @@ -3778,9 +3778,9 @@ dependencies = [ [[package]] name = "swc_css_ast" -version = "0.139.0" +version = "0.139.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b183736d78583b5261e690833b0e8a1d5acee4ad99142d0e3aabdec10795fc5" +checksum = "fab824eff88884673de1d6b84cdb5d3d71c0b903fcef62a3ec1f44f40477433f" dependencies = [ "is-macro", "string_enum", @@ -3790,9 +3790,9 @@ dependencies = [ [[package]] name = "swc_css_codegen" -version = "0.149.0" +version = "0.149.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4a9af319764a33a6e7daf99a2d0f979d7f06e72e8a01947eec607cb46ddf7d" +checksum = "aef989abd4b9ccf3caf6a4ab0ceb9f9e7d6a27c08585a20a7fc7b9db6c73a341" dependencies = [ "auto_impl", "bitflags 2.3.3", @@ -3820,9 +3820,9 @@ dependencies = [ [[package]] name = "swc_css_compat" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c506282116236260f189f8f1eddd2ed582e52aac57e184dd9812d92a80915dd9" +checksum = "a1ee1b2b77e7daaf389237ca2656df01cf8c1a6f2d9b158459921b202a661f8a" dependencies = [ "bitflags 2.3.3", "once_cell", @@ -3837,9 +3837,9 @@ dependencies = [ [[package]] name = "swc_css_minifier" -version = "0.114.0" +version = "0.114.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cafbe90da565be736118b2d67855cc028f0c284e74e5fd1979822d5d5a2eec" +checksum = "21db6b6ef607d47d09a7e2fd0b8fd5ec29d05d1182f8d3d5eebef0f1b94c3f4d" dependencies = [ "serde", "swc_atoms", @@ -3851,9 +3851,9 @@ dependencies = [ [[package]] name = "swc_css_modules" -version = "0.27.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1a4caa2e6e5166ffb3e1d5400f261edad555dfdc3380371fe578ab496499ed8" +checksum = "ec7cf86bd0da899f551089bcb3fb122e821b0b860359e27875c1676ea0f3b98d" dependencies = [ "rustc-hash", "serde", @@ -3867,9 +3867,9 @@ dependencies = [ [[package]] name = "swc_css_parser" -version = "0.148.0" +version = "0.148.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c09f1b12f73eb425a38b2ecace95069e024da7bdf6c0b35ac9f0999c7d36bf" +checksum = "b02a3c11508487249aa571a908e673c540191de97d5139bb78ab03188dd57e26" dependencies = [ "lexical", "serde", @@ -3880,9 +3880,9 @@ dependencies = [ [[package]] name = "swc_css_prefixer" -version = "0.151.0" +version = "0.151.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff100f504e11804c290114290c0d7622fa9d4cc88d2fa013cc39a9d29f246ff9" +checksum = "274da87a8f0117ef86382b132812aa6a1b700b31c37ef95ce3bbde7e05f8c098" dependencies = [ "once_cell", "preset_env_base", @@ -3897,9 +3897,9 @@ dependencies = [ [[package]] name = "swc_css_utils" -version = "0.136.0" +version = "0.136.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2bb1a683a9ca9106a2c93c7266221f4e9d3ab85eb3eaee4ed78ea3b4dc820b" +checksum = "3eead47672e3c832e2e3fc3e523490c4822d80a7fc8c50e87a66f9ab7003b517" dependencies = [ "once_cell", "serde", @@ -3912,9 +3912,9 @@ dependencies = [ [[package]] name = "swc_css_visit" -version = "0.138.0" +version = "0.138.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52bde5bd8d92d993a3c69c809b51fadec06bb1c3c325fb951b79b481a74fd30a" +checksum = "83f01449a09b8a87ab4bd2ea6cbaaf74e39f9bfba3842a2918e998c5f9b428a4" dependencies = [ "serde", "swc_atoms", @@ -3925,9 +3925,9 @@ dependencies = [ [[package]] name = "swc_ecma_ast" -version = "0.109.0" +version = "0.109.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc2286cedd688a68f214faa1c19bb5cceab7c9c54d0cbe3273e4c1704e38f69" +checksum = "e063a1614daed3ea8be56e5dd8edb17003409088d2fc9ce4aca3378879812607" dependencies = [ "bitflags 2.3.3", "is-macro", @@ -3942,9 +3942,9 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "0.144.1" +version = "0.145.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e62ba2c0ed1f119fc1a76542d007f1b2c12854d54dea15f5491363227debe11" +checksum = "3387d6ec9e636999b76af7d604e430f62ac16aee7cff1ff9aa466d7387b59143" dependencies = [ "memchr", "num-bigint", @@ -3974,9 +3974,9 @@ dependencies = [ [[package]] name = "swc_ecma_ext_transforms" -version = "0.108.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57eb7bbfbd7d0b4c2d5abf6936efb16d5a228508246a19795d59c849dbff073e" +checksum = "d995f94740b4cde4919e6e03d982230f755f49dac9dac52f0218254a1fd69f2b" dependencies = [ "phf", "swc_atoms", @@ -3988,9 +3988,9 @@ dependencies = [ [[package]] name = "swc_ecma_lints" -version = "0.87.3" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5f56635e73df9d6f4fa1ecbc1c65e9175ad2593fada76b13b05c5d2895b8a1d" +checksum = "c79cd55fe7b36ba9399fa66609c97cc9220f09a8e7d12e86d721360df1f9c107" dependencies = [ "auto_impl", "dashmap", @@ -4008,9 +4008,9 @@ dependencies = [ [[package]] name = "swc_ecma_loader" -version = "0.44.2" +version = "0.44.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d7c322462657ae27ac090a2c89f7e456c94416284a2f5ecf66c43a6a3c19d1" +checksum = "d30d2ee2ea9207263f723ac0fe701c7c22200e5bec13d3a9b9a9189e97931081" dependencies = [ "anyhow", "dashmap", @@ -4029,9 +4029,9 @@ dependencies = [ [[package]] name = "swc_ecma_minifier" -version = "0.186.7" +version = "0.187.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c1ba3e299e769936b47bfcf68932305a32b5cdf9da2875a12fa1e8be6d523f" +checksum = "5d71b5b6d1f76782e3f91f0ded61364dce32ed70a2b1cf48edb02f7f753b2608" dependencies = [ "arrayvec", "indexmap 1.9.3", @@ -4064,9 +4064,9 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "0.139.0" +version = "0.140.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eab46cb863bc5cd61535464e07e5b74d5f792fa26a27b9f6fd4c8daca9903b7" +checksum = "3c968599841fcecfdc2e490188ad93251897a1bb912882547e6889e14a368399" dependencies = [ "either", "num-bigint", @@ -4084,9 +4084,9 @@ dependencies = [ [[package]] name = "swc_ecma_preset_env" -version = "0.200.6" +version = "0.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be8c666c0a51cf3210ac208dc5ec76f7e2c680e346e8fc2a0cea9547d66c362" +checksum = "17148615f8e209fa42f22a4cb893e91b7097c3b65bb5b6202657ba3583b65170" dependencies = [ "anyhow", "dashmap", @@ -4109,9 +4109,9 @@ dependencies = [ [[package]] name = "swc_ecma_quote_macros" -version = "0.50.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf676875aea532ce2c4cd479f89de52e0590048d71ec1790c8fe99f33788216" +checksum = "b028b0675ad45b79b163c70e192f25b59d72366a2864c5d369dce707a38a1597" dependencies = [ "anyhow", "pmutil", @@ -4127,9 +4127,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms" -version = "0.223.5" +version = "0.224.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "950df5006708476d43ffa815f7c00bb24460187395fed3dfbb42850676aebcb5" +checksum = "7bbb521b9cddc76c62d9f0243708dbb4797e957922a4953d9722aeef7a53d174" dependencies = [ "swc_atoms", "swc_common", @@ -4147,9 +4147,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "0.132.3" +version = "0.133.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e082ff07a2d31fd8e480dc590dd81efb75106e79bec24483dbece03630ca67b3" +checksum = "d7787d3607d628ad0cc2e7173770f6a43229ce46e55136e81e5fdeb0951dd6c9" dependencies = [ "better_scoped_tls", "bitflags 2.3.3", @@ -4171,9 +4171,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_classes" -version = "0.121.3" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71f54c8ce83b25e89dc60d09686ebc8c4d0376bc82c21ac1a14a1b0a339c714f" +checksum = "ad3878381c7a115528f90bd1df4a97ac82711b7013f60b1cbf43982ad31ec645" dependencies = [ "swc_atoms", "swc_common", @@ -4185,9 +4185,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_compat" -version = "0.158.4" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4da9073b7bfa9b2fd0a46f98fae9c1a205d1c7e8b91d8cd43c4077ce598c75b7" +checksum = "64d4fbea0d55e51492c6bedb37f42b57968df0b1469ef58a9d2532f978131aeb" dependencies = [ "arrayvec", "indexmap 1.9.3", @@ -4223,9 +4223,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_module" -version = "0.175.5" +version = "0.176.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c02853fd5e0a2301762acf29ce14789691e0794b085ccce0f04e6a40550c03" +checksum = "b040349682a36a0fb036351fdac4e99a6f1190186c1aed81db29ddb2ba1d0355" dependencies = [ "Inflector", "anyhow", @@ -4250,9 +4250,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_optimization" -version = "0.192.5" +version = "0.193.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89871886565a084262242caec0407222ff140c6ecaae6c8e97319c55920677e1" +checksum = "0128684058b111f809aa97af9c327523ad232cd4fea14c8f57ce1a3235a1ccf9" dependencies = [ "dashmap", "indexmap 1.9.3", @@ -4275,9 +4275,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_proposal" -version = "0.166.4" +version = "0.167.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2e61b71de4a1b403e6451d0c0fbeb66e27ef369c341a52b3827c9cf6df052c" +checksum = "00df76004234c6ee80a70a1d869cefe9b223d878b18b7bfd799b574112cd29bc" dependencies = [ "either", "rustc-hash", @@ -4295,9 +4295,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_react" -version = "0.178.5" +version = "0.179.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15bcdf31da1c05fa364b694cef09ec3daa128aa30a0322ae16c1d0e769883810" +checksum = "59b9c4a068b55238df8bd70730651d45b39d5c924fc4bf4ccbb912e48b406eac" dependencies = [ "base64", "dashmap", @@ -4319,9 +4319,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_typescript" -version = "0.182.5" +version = "0.183.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55df168be05f2ccdac3492168aa61bb8a18aef28059be33872593402781b6d01" +checksum = "8d7d39b89a675bc0a8e7b96135899faa2e5f1d29d3c628c16099bf3875ecf077" dependencies = [ "serde", "swc_atoms", @@ -4335,9 +4335,9 @@ dependencies = [ [[package]] name = "swc_ecma_usage_analyzer" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1aabf52dfd20abdbe5087107273d250d134f5b0fa2251cbb42bbfbec88404af" +checksum = "d71dc9b35f1f137c72badbadb705a2325d161ff603224ab0e07e6834774ea281" dependencies = [ "indexmap 1.9.3", "rustc-hash", @@ -4352,9 +4352,9 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11006a3398ffd4693c4d3b0a1b1a5030edbdc04228159f5301120a6178144708" +checksum = "5b6d6b59ebd31b25fe2692ff705c806961e7856de8b7e91fd0942328886cd315" dependencies = [ "indexmap 1.9.3", "num_cpus", @@ -4371,9 +4371,9 @@ dependencies = [ [[package]] name = "swc_ecma_visit" -version = "0.95.0" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f628ec196e76e67892441e14eef2e423a738543d32bffdabfeec20c29582117" +checksum = "2774848b306e17fa280c598ecb192cc2c72a1163942b02d48606514336e9e7c5" dependencies = [ "num-bigint", "swc_atoms", @@ -4385,9 +4385,9 @@ dependencies = [ [[package]] name = "swc_emotion" -version = "0.40.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7a80eb046e114d6af3e78c28ab870d1de411d91ba4ba2e9caca07627d3f4580" +checksum = "13abda43ddbc94149aca923fb331f13b90e70f7cc2fb54662ee4a2c51cb59b22" dependencies = [ "base64", "byteorder", @@ -4415,9 +4415,9 @@ dependencies = [ [[package]] name = "swc_error_reporters" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6530d0def50c33d14064a43837b7e3c1fe8716ee6c3495a478835793caae2c97" +checksum = "c76b479ad1a69bec65b261354b8e2dec8ed0f9ed43c7b54ab053dc4923e1c90e" dependencies = [ "anyhow", "miette", @@ -4428,9 +4428,9 @@ dependencies = [ [[package]] name = "swc_fast_graph" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a407fff2eb5ce3bee7513bdd9531a7be0285bc1213500b6d98ad235428d94cce" +checksum = "e2f7297cdefdb54d8d09e0294c1aec3826825b1feefd0c25978365aa7f447a1c" dependencies = [ "indexmap 1.9.3", "petgraph", @@ -4440,9 +4440,9 @@ dependencies = [ [[package]] name = "swc_html" -version = "0.130.7" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60835eea257c70751214952a559d695691668b12bdb5cd32f3b1b9dcc3bbb96b" +checksum = "5b727c5f63469fa67caf3ef4d051084f0ecec5ee9e083aa0e2ab380a8d26d941" dependencies = [ "swc_html_ast", "swc_html_codegen", @@ -4452,9 +4452,9 @@ dependencies = [ [[package]] name = "swc_html_ast" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ae5b940a04624f50f74d5f0364374e6d471b8bea9ca41ca5e2027265ea7a9a1" +checksum = "d38e530672c482fd2f05f3366edcd3779034e0b1cc90b24e5d6ce4090493075d" dependencies = [ "is-macro", "string_enum", @@ -4464,9 +4464,9 @@ dependencies = [ [[package]] name = "swc_html_codegen" -version = "0.41.0" +version = "0.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a25cf73cbad0bbfe4fc93dd15a28f0d56f37319ad2573b87b2690f39fa3dd2" +checksum = "7806bef46cc4889b8f5a4c2f860858d305ed3d080253929f3b8efd27e491c2ca" dependencies = [ "auto_impl", "bitflags 2.3.3", @@ -4493,9 +4493,9 @@ dependencies = [ [[package]] name = "swc_html_minifier" -version = "0.127.7" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4534419711311a306c38731245c62f5335784160f2e4ba8dca28dde63fbd9578" +checksum = "6241d0f1ad02b02b4c6171002edf80b57cad7c4acb88f10df1df71ae3849d430" dependencies = [ "once_cell", "serde", @@ -4522,9 +4522,9 @@ dependencies = [ [[package]] name = "swc_html_parser" -version = "0.38.0" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6457f6dcfb80749738cf57d068cf1c444a1746dd0f2be7da3398f93d4aee0d93" +checksum = "cdcd884e646add21950050313455abf910147b6a533f7a3c4386d9f74f9d5089" dependencies = [ "swc_atoms", "swc_common", @@ -4534,9 +4534,9 @@ dependencies = [ [[package]] name = "swc_html_utils" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21c0959bcc662d8199808e47b26c7f1314b1bdfa7c696de53ad53d84e667a761" +checksum = "15ce5b446cfa7ba0f5be9bc08535e8aa5bbcaf520249a62864b93350a4742b06" dependencies = [ "once_cell", "serde", @@ -4547,9 +4547,9 @@ dependencies = [ [[package]] name = "swc_html_visit" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f993bd3e860f2e6bf9f5d188be3ba1af3f40b95ef1562b515797700c560c97" +checksum = "6e783d8d07ea597d006ad91ce890e6406544209d6d89359bd6dd6935de986a4a" dependencies = [ "serde", "swc_atoms", @@ -4572,9 +4572,9 @@ dependencies = [ [[package]] name = "swc_node_comments" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73cee5dededc1e0d19e53dd0a41a343a43e21ed9b62c3df0fdd5801c11533bc9" +checksum = "b2b9597573f1ab8bae72329eef550d214ced0955c7a4f1b6b4ae5e216219e710" dependencies = [ "dashmap", "swc_atoms", @@ -4593,9 +4593,9 @@ dependencies = [ [[package]] name = "swc_timer" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95c548665c811d1c5def583d3d6ca0291e5397122c0de6362bb201cc2de8beff" +checksum = "b740ce6b402ed04176bd28dc4f4f92c764fe0defe8437c2f3b6e1b5818b4e10c" dependencies = [ "tracing", ] diff --git a/Cargo.toml b/Cargo.toml index 38b2b877519b..2f688daf1b3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,14 +54,14 @@ napi-build = { version = "=2.0.1" } napi-derive = { version = "=2.12.3" } napi-sys = { version = "=2.2.3" } swc_config = { version = "=0.1.7" } -swc_core = { version = "=0.82.9", default-features = false } -swc_css = { version = "=0.155.0" } -swc_ecma_minifier = { version = "=0.186.7", default-features = false } -swc_emotion = { version = "=0.40.0" } -swc_error_reporters = { version = "=0.16.0" } -swc_html = { version = "=0.130.7" } -swc_html_minifier = { version = "=0.127.7" } -swc_node_comments = { version = "=0.19.0" } +swc_core = { version = "=0.83.1", default-features = false } +swc_css = { version = "=0.155.2" } +swc_ecma_minifier = { version = "=0.187.0", default-features = false } +swc_emotion = { version = "=0.42.0" } +swc_error_reporters = { version = "=0.16.1" } +swc_html = { version = "=0.131.0" } +swc_html_minifier = { version = "=0.128.0" } +swc_node_comments = { version = "=0.19.1" } tikv-jemallocator = { version = "=0.5.4", features = ["disable_initial_exec_tls"] } [profile.dev] diff --git a/crates/rspack_loader_sass/tests/fixtures/rspack_importer/snapshot/output.snap b/crates/rspack_loader_sass/tests/fixtures/rspack_importer/snapshot/output.snap index 83ada6c249bc..dd516d0d22b8 100644 --- a/crates/rspack_loader_sass/tests/fixtures/rspack_importer/snapshot/output.snap +++ b/crates/rspack_loader_sass/tests/fixtures/rspack_importer/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 104 --- ```css title=main.css .color-red { @@ -42,3 +43,24 @@ nav a { } ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _scss_language_scss__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ../../scss/language.scss */"../../scss/language.scss"); + +}, +"../../scss/file.css": function (module, exports, __webpack_require__) { +}, +"../../scss/language.scss": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/b.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/composes/b.module.css new file mode 100644 index 000000000000..270e110cd9c1 --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/composes/b.module.css @@ -0,0 +1,6 @@ +.b-1 { + color: red; +} +.b { + composes: b-1; +} \ No newline at end of file diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/d.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/composes/d.module.css new file mode 100644 index 000000000000..8b0b000bab62 --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/composes/d.module.css @@ -0,0 +1,7 @@ +.d-1 { + color: green; +} + +.d { + composes: d-1; +} diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/e.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/composes/e.module.css new file mode 100644 index 000000000000..44640418b1ac --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/composes/e.module.css @@ -0,0 +1,7 @@ +.e-1 { + color: blue; +} + +.e { + composes: e-1; +} diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/f.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/composes/f.module.css new file mode 100644 index 000000000000..35793822fed8 --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/composes/f.module.css @@ -0,0 +1,6 @@ +.f-1 { + color: black; +} +.f { + composes: f-1; +} diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/index.js b/crates/rspack_plugin_css/tests/fixtures/custom/composes/index.js new file mode 100644 index 000000000000..d31bab07a9dc --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/composes/index.js @@ -0,0 +1 @@ +import './style.module.css' diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/custom/composes/snapshot/output.snap new file mode 100644 index 000000000000..fc4f198b438f --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/composes/snapshot/output.snap @@ -0,0 +1,70 @@ +--- +source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 +--- +```css title=main.css +._d-module__d-1---__c54e45bb853e1f4b-c54 { + color: green; +} +._d-module__d---___34b192ed7623f91e-_34 {} +._f-module__f-1---___85bca5b4a297a364-_85 { + color: black; +} +._f-module__f---___141af47168767e48-_14 {} +._b-module__b-1---___1d9724eea6aa131c-_1d { + color: red; +} +._b-module__b---__c156016816e95195-c15 {} +._style-module__chain2---___980d4cb026fb8411-_98 { + background: blue; +} +._style-module__chain1---___3a2c0a3269e6f63c-_3a { + background: green; +} +._style-module__root-class---__aae4264cfe73c127-aae { + background: red; +} + +``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _style_module_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.module.css */"./style.module.css"); + +}, +"./b.module.css": function (module, exports, __webpack_require__) { +module.exports = { + "b": "_b-module__b---__c156016816e95195-c15" + " " + "_b-module__b-1---___1d9724eea6aa131c-_1d", + "b-1": "_b-module__b-1---___1d9724eea6aa131c-_1d", +}; +}, +"./d.module.css": function (module, exports, __webpack_require__) { +module.exports = { + "d": "_d-module__d---___34b192ed7623f91e-_34" + " " + "_d-module__d-1---__c54e45bb853e1f4b-c54", + "d-1": "_d-module__d-1---__c54e45bb853e1f4b-c54", +}; +}, +"./f.module.css": function (module, exports, __webpack_require__) { +module.exports = { + "f": "_f-module__f---___141af47168767e48-_14" + " " + "_f-module__f-1---___85bca5b4a297a364-_85", + "f-1": "_f-module__f-1---___85bca5b4a297a364-_85", +}; +}, +"./style.module.css": function (module, exports, __webpack_require__) { +module.exports = { + "chain1": "_style-module__chain1---___3a2c0a3269e6f63c-_3a" + " " + "_style-module__chain2---___980d4cb026fb8411-_98" + " " + "c" + " " + __webpack_require__("./d.module.css")["d"] + " " + "e" + " " + __webpack_require__("./f.module.css")["f"], + "chain2": "_style-module__chain2---___980d4cb026fb8411-_98" + " " + "e" + " " + __webpack_require__("./f.module.css")["f"], + "root-class": "_style-module__root-class---__aae4264cfe73c127-aae" + " " + "_style-module__chain1---___3a2c0a3269e6f63c-_3a" + " " + "a" + " " + __webpack_require__("./b.module.css")["b"] + " " + "_style-module__chain2---___980d4cb026fb8411-_98" + " " + "c" + " " + __webpack_require__("./d.module.css")["d"] + " " + "e" + " " + __webpack_require__("./f.module.css")["f"], +}; +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/style.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/composes/style.module.css new file mode 100644 index 000000000000..67f65eb50d01 --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/composes/style.module.css @@ -0,0 +1,19 @@ +.chain2 { + composes: e from global; + composes: f from "./f.module.css"; + background: blue; +} + +.chain1 { + composes: chain2; + composes: c from global; + composes: d from "./d.module.css"; + background: green; +} + +.root-class { + composes: chain1; + composes: a from global; + composes: b from "./b.module.css"; + background: red; +} diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/test.config.json b/crates/rspack_plugin_css/tests/fixtures/custom/composes/test.config.json new file mode 100644 index 000000000000..0071292cce61 --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/composes/test.config.json @@ -0,0 +1,20 @@ +{ + "builtins": { + "css": { + "modules": { + "localIdentName": "[path]_[name]_[path]_[local]--/__[hash:42]<[hash:3]" + } + } + }, + "module": { + "rules": [ + { + "test": { + "type": "regexp", + "matcher": "\\.module\\.css$" + }, + "type": "css/module" + } + ] + } +} \ No newline at end of file diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/http_import/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/custom/http_import/snapshot/output.snap index e12ce854851d..7a1c48b5c00b 100644 --- a/crates/rspack_plugin_css/tests/fixtures/custom/http_import/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/custom/http_import/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css @import url("http://example.com/test.css"); @@ -11,3 +12,22 @@ body { ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _style_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.css */"./style.css"); + +}, +"./style.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/modules-ident-name/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/custom/modules-ident-name/snapshot/output.snap index 46648882420c..7b4070971fc9 100644 --- a/crates/rspack_plugin_css/tests/fixtures/custom/modules-ident-name/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/custom/modules-ident-name/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css ._style-module__foo---___6655ebe837449348-_66 { @@ -7,3 +8,26 @@ source: crates/rspack_testing/src/run_fixture.rs } ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _style_module_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.module.css */"./style.module.css"); + +console.log(_style_module_css__WEBPACK_IMPORTED_MODULE_0_); +}, +"./style.module.css": function (module, exports, __webpack_require__) { +module.exports = { + "foo": "_style-module__foo---___6655ebe837449348-_66", +}; +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/at-charset/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/at-charset/snapshot/output.snap index c0bf0608bcec..04ca3878a5bb 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/at-charset/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/at-charset/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css @charset "utf-8"; @@ -67,3 +68,40 @@ body { ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); +/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./b.css */"./b.css"); + + +}, +"./a.css": function (module, exports, __webpack_require__) { +}, +"./aa.css": function (module, exports, __webpack_require__) { +}, +"./ab.css": function (module, exports, __webpack_require__) { +}, +"./ac.css": function (module, exports, __webpack_require__) { +}, +"./ad.css": function (module, exports, __webpack_require__) { +}, +"./ae.css": function (module, exports, __webpack_require__) { +}, +"./b.css": function (module, exports, __webpack_require__) { +}, +"./ba.css": function (module, exports, __webpack_require__) { +}, +"./bb.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/at-import-in-the-middle/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/at-import-in-the-middle/snapshot/output.snap index abaa7ff33208..0829acd77418 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/at-import-in-the-middle/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/at-import-in-the-middle/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css body { @@ -32,4 +33,23 @@ body { +``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./a.css": function (module, exports, __webpack_require__) { +}, +"./b.css": function (module, exports, __webpack_require__) { +}, +"./c.css": function (module, exports, __webpack_require__) { +}, +"./index.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.css")); + +} +]); ``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/at-import/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/at-import/snapshot/output.snap index 6529bd62fc66..0835fc384bd2 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/at-import/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/at-import/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css .ae { @@ -49,3 +50,40 @@ body { ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); +/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./b.css */"./b.css"); + + +}, +"./a.css": function (module, exports, __webpack_require__) { +}, +"./aa.css": function (module, exports, __webpack_require__) { +}, +"./ab.css": function (module, exports, __webpack_require__) { +}, +"./ac.css": function (module, exports, __webpack_require__) { +}, +"./ad.css": function (module, exports, __webpack_require__) { +}, +"./ae.css": function (module, exports, __webpack_require__) { +}, +"./b.css": function (module, exports, __webpack_require__) { +}, +"./ba.css": function (module, exports, __webpack_require__) { +}, +"./bb.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/default-options/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/default-options/snapshot/output.snap index d21e48db5842..e0b4b99122bd 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/default-options/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/default-options/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css body { @@ -8,3 +9,22 @@ body { ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _style_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.css */"./style.css"); + +}, +"./style.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/multiple-entry/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/multiple-entry/snapshot/output.snap index 5d5e677a763c..e47710f62299 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/multiple-entry/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/multiple-entry/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=async-one.css body { @@ -48,3 +49,87 @@ body { ``` + +```js title=async-one.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["async-one"], { +"./async-one.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _c_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./c.css */"./c.css"); +/* harmony import */var _d_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./d.css */"./d.css"); + + +}, +"./c.css": function (module, exports, __webpack_require__) { +}, +"./d.css": function (module, exports, __webpack_require__) { +}, + +}]); +``` + +```js title=async-two.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["async-two"], { +"./async-two.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _d_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./d.css */"./d.css"); +/* harmony import */var _c_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./c.css */"./c.css"); + + +}, +"./c.css": function (module, exports, __webpack_require__) { +}, +"./d.css": function (module, exports, __webpack_require__) { +}, + +}]); +``` + +```js title=main-one.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main-one"], { +"./index-one.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); +/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./b.css */"./b.css"); + + +/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./async-one */"./async-one.js").then(__webpack_require__.bind(__webpack_require__, /* ./async-one */"./async-one.js")); +}, +"./a.css": function (module, exports, __webpack_require__) { +}, +"./b.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index-one.js")); + +} +]); +``` + +```js title=main-two.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main-two"], { +"./index-two.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./b.css */"./b.css"); +/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./a.css */"./a.css"); + + +/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./async-two */"./async-two.js").then(__webpack_require__.bind(__webpack_require__, /* ./async-two */"./async-two.js")); +}, +"./a.css": function (module, exports, __webpack_require__) { +}, +"./b.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index-two.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/nested/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/nested/snapshot/output.snap index 855ffb725143..b0d5a6904e7e 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/nested/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/nested/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css body { @@ -16,3 +17,36 @@ body { ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./component.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _component_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./component.css */"./component.css"); + +}, +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); +/* harmony import */var _component__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./component */"./component.js"); +/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_2_ = __webpack_require__(/* ./b.css */"./b.css"); + + + +}, +"./a.css": function (module, exports, __webpack_require__) { +}, +"./b.css": function (module, exports, __webpack_require__) { +}, +"./component.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/output-iife/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/output-iife/snapshot/output.snap index d21e48db5842..e0b4b99122bd 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/output-iife/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/output-iife/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css body { @@ -8,3 +9,22 @@ body { ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _style_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.css */"./style.css"); + +}, +"./style.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/shared-import/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/shared-import/snapshot/output.snap index c85681d9d19a..0212c1c2cb8f 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/shared-import/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/shared-import/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=c_css.css @@ -32,3 +33,37 @@ source: crates/rspack_testing/src/run_fixture.rs ``` + +```js title=c_css.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["c_css"], { +"./c.css": function (module, exports, __webpack_require__) { +}, + +}]); +``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); +/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./b.css */"./b.css"); + + +/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./c.css */"./c.css").then(__webpack_require__.t.bind(__webpack_require__, /* ./c.css */"./c.css", 17)); +}, +"./a.css": function (module, exports, __webpack_require__) { +}, +"./b.css": function (module, exports, __webpack_require__) { +}, +"./shared.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async-load-css/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async-load-css/snapshot/output.snap index aeabe434c42b..b9452adca357 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async-load-css/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async-load-css/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=async-one.css body { @@ -36,3 +37,64 @@ body { ``` + +```js title=async-one.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["async-one"], { +"./async-one.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _c_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./c.css */"./c.css"); +/* harmony import */var _d_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./d.css */"./d.css"); + + +}, +"./c.css": function (module, exports, __webpack_require__) { +}, +"./d.css": function (module, exports, __webpack_require__) { +}, + +}]); +``` + +```js title=async-two.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["async-two"], { +"./async-two.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _e_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./e.css */"./e.css"); +/* harmony import */var _f_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./f.css */"./f.css"); + + +}, +"./e.css": function (module, exports, __webpack_require__) { +}, +"./f.css": function (module, exports, __webpack_require__) { +}, + +}]); +``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); +/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./b.css */"./b.css"); + + +/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./async-one */"./async-one.js").then(__webpack_require__.bind(__webpack_require__, /* ./async-one */"./async-one.js")); +/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./async-two */"./async-two.js").then(__webpack_require__.bind(__webpack_require__, /* ./async-two */"./async-two.js")); +}, +"./a.css": function (module, exports, __webpack_require__) { +}, +"./b.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async/snapshot/output.snap index cf09b3f0ee37..5c32bae63a18 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=async_css.css .async { @@ -24,3 +25,46 @@ body { ``` + +```js title=async_css.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["async_css"], { +"./async.css": function (module, exports, __webpack_require__) { +}, + +}]); +``` + +```js title=async_js.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["async_js"], { +"./async.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _in_async_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./in-async.css */"./in-async.css"); + +}, +"./in-async.css": function (module, exports, __webpack_require__) { +}, + +}]); +``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _main_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./main.css */"./main.css"); + +/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./async */"./async.js").then(__webpack_require__.bind(__webpack_require__, /* ./async */"./async.js")); +/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./async.css */"./async.css").then(__webpack_require__.t.bind(__webpack_require__, /* ./async.css */"./async.css", 17)); +}, +"./main.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-commonjs-syntax/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-commonjs-syntax/snapshot/output.snap index d21e48db5842..7ababdd0f15b 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-commonjs-syntax/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-commonjs-syntax/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css body { @@ -8,3 +9,19 @@ body { ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, exports, __webpack_require__) { +__webpack_require__(/* ./style.css */"./style.css"); +}, +"./style.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-es-module-syntax/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-es-module-syntax/snapshot/output.snap index d21e48db5842..e0b4b99122bd 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-es-module-syntax/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-es-module-syntax/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css body { @@ -8,3 +9,22 @@ body { ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _style_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.css */"./style.css"); + +}, +"./style.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-multiple/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-multiple/snapshot/output.snap index dcc3065cc252..f0a7b946a346 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-multiple/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-multiple/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css body { @@ -12,3 +13,26 @@ body { ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); +/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./b.css */"./b.css"); + + +}, +"./a.css": function (module, exports, __webpack_require__) { +}, +"./b.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/simple/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/simple/snapshot/output.snap index d21e48db5842..e0b4b99122bd 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/simple/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/simple/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css body { @@ -8,3 +9,22 @@ body { ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _style_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.css */"./style.css"); + +}, +"./style.css": function (module, exports, __webpack_require__) { +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_javascript/src/ast/stringify.rs b/crates/rspack_plugin_javascript/src/ast/stringify.rs index 6e72c7b49a2b..1ff2a75e9133 100644 --- a/crates/rspack_plugin_javascript/src/ast/stringify.rs +++ b/crates/rspack_plugin_javascript/src/ast/stringify.rs @@ -77,12 +77,10 @@ pub fn print( } let mut emitter = Emitter { - cfg: codegen::Config { - minify, - target, - ascii_only, - ..Default::default() - }, + cfg: codegen::Config::default() + .with_minify(minify) + .with_target(target) + .with_ascii_only(ascii_only), comments, cm: source_map.clone(), wr, diff --git a/crates/rspack_testing/src/run_fixture.rs b/crates/rspack_testing/src/run_fixture.rs index 55b29365ecc0..16a40312f67d 100644 --- a/crates/rspack_testing/src/run_fixture.rs +++ b/crates/rspack_testing/src/run_fixture.rs @@ -37,7 +37,10 @@ pub async fn test_fixture_js(fixture_path: &Path) -> Compiler Compiler { - test_fixture_share(fixture_path, &|s| s.ends_with(".css")).await + test_fixture_share(fixture_path, &|s| { + s.ends_with(".css") || (s.ends_with(".js") && !s.contains("runtime.js")) + }) + .await } #[tokio::main] pub async fn test_fixture_insta( From 8f6f2b2c64edc67adb28642d4ee30412bc89555c Mon Sep 17 00:00:00 2001 From: gaoyuan <9aoyuao@gmail.com> Date: Fri, 8 Sep 2023 15:12:57 +0800 Subject: [PATCH 22/30] fix: ignoreWarnings should work when call stats.getJson (#4153) --- .../rspack/src/stats/DefaultStatsFactoryPlugin.ts | 6 +++++- .../tests/__snapshots__/StatsTestCases.test.ts.snap | 12 ++++++++++++ .../rspack/tests/statsCases/ignore-warning/index.js | 1 + .../tests/statsCases/ignore-warning/index.scss | 3 +++ .../statsCases/ignore-warning/webpack.config.js | 13 +++++++++++++ 5 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 packages/rspack/tests/statsCases/ignore-warning/index.js create mode 100644 packages/rspack/tests/statsCases/ignore-warning/index.scss create mode 100644 packages/rspack/tests/statsCases/ignore-warning/webpack.config.js diff --git a/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts b/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts index 5243465552b1..48313e1894cb 100644 --- a/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts +++ b/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts @@ -385,7 +385,11 @@ const SIMPLE_EXTRACTORS: SimpleExtractors = { } if (!context.cachedGetWarnings) { context.cachedGetWarnings = _compilation => { - return context._inner.getWarnings(); + const warnings = context._inner.getWarnings(); + + return compilation.hooks.processWarnings.call( + warnings as any + ) as unknown as typeof warnings; }; } if (compilation.name) { diff --git a/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap b/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap index 8eb0590f68b7..b6b9edb68ce5 100644 --- a/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap +++ b/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap @@ -908,6 +908,18 @@ exports[`StatsTestCases should print correct stats for ignore-plugin 2`] = ` /tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset } [247] {main}" `; +exports[`StatsTestCases should print correct stats for ignore-warning 1`] = ` +{ + "errors": [], + "errorsCount": 0, + "logging": {}, + "warnings": [], + "warningsCount": 0, +} +`; + +exports[`StatsTestCases should print correct stats for ignore-warning 2`] = `"rspack compiled successfully"`; + exports[`StatsTestCases should print correct stats for issue-3558 1`] = ` { "errors": [], diff --git a/packages/rspack/tests/statsCases/ignore-warning/index.js b/packages/rspack/tests/statsCases/ignore-warning/index.js new file mode 100644 index 000000000000..9724af412303 --- /dev/null +++ b/packages/rspack/tests/statsCases/ignore-warning/index.js @@ -0,0 +1 @@ +require("./index.scss"); diff --git a/packages/rspack/tests/statsCases/ignore-warning/index.scss b/packages/rspack/tests/statsCases/ignore-warning/index.scss new file mode 100644 index 000000000000..c15e450fd09c --- /dev/null +++ b/packages/rspack/tests/statsCases/ignore-warning/index.scss @@ -0,0 +1,3 @@ +.c { + width: (12px/4px); +} \ No newline at end of file diff --git a/packages/rspack/tests/statsCases/ignore-warning/webpack.config.js b/packages/rspack/tests/statsCases/ignore-warning/webpack.config.js new file mode 100644 index 000000000000..14a197b5eb72 --- /dev/null +++ b/packages/rspack/tests/statsCases/ignore-warning/webpack.config.js @@ -0,0 +1,13 @@ +module.exports = { + stats: "errors-warnings", + ignoreWarnings: [/Using \/ for division outside/], + module: { + rules: [ + { + test: /\.s[ac]ss$/i, + use: [{ loader: "sass-loader" }], + type: "css" + } + ] + } +}; From 8b5b5a670c75a3c3fe7ea29a1ffa21602a0c721a Mon Sep 17 00:00:00 2001 From: Gengkun Date: Fri, 8 Sep 2023 16:13:54 +0800 Subject: [PATCH 23/30] feat: support stats ids (#4148) * feat: support stats ids * fix --- packages/rspack/src/Compilation.ts | 2 + packages/rspack/src/config/zod.ts | 1 + .../src/stats/DefaultStatsFactoryPlugin.ts | 105 +++- .../rspack/src/stats/statsFactoryUtils.ts | 21 +- packages/rspack/tests/Stats.test.ts | 537 +--------------- .../tests/__snapshots__/Stats.test.ts.snap | 584 ++++++++++++++++++ .../__snapshots__/StatsTestCases.test.ts.snap | 227 ++++--- 7 files changed, 875 insertions(+), 602 deletions(-) create mode 100644 packages/rspack/tests/__snapshots__/Stats.test.ts.snap diff --git a/packages/rspack/src/Compilation.ts b/packages/rspack/src/Compilation.ts index ad490f96f0e5..14e761a0d449 100644 --- a/packages/rspack/src/Compilation.ts +++ b/packages/rspack/src/Compilation.ts @@ -304,6 +304,8 @@ export class Compilation { options.modulesSpace = options.modulesSpace || (context.forToString ? 15 : Infinity); + options.ids = optionOrLocalFallback(options.ids, !context.forToString); + return options; } diff --git a/packages/rspack/src/config/zod.ts b/packages/rspack/src/config/zod.ts index a292af730b25..b3e4ec531034 100644 --- a/packages/rspack/src/config/zod.ts +++ b/packages/rspack/src/config/zod.ts @@ -826,6 +826,7 @@ const statsOptions = z.strictObject({ outputPath: z.boolean().optional(), chunkModules: z.boolean().optional(), chunkRelations: z.boolean().optional(), + ids: z.boolean().optional(), timings: z.boolean().optional(), builtAt: z.boolean().optional(), moduleAssets: z.boolean().optional(), diff --git a/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts b/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts index 48313e1894cb..26e08e808a4b 100644 --- a/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts +++ b/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts @@ -713,12 +713,23 @@ const SIMPLE_EXTRACTORS: SimpleExtractors = { }, asset: { _: (object, asset, context: KnownStatsFactoryContext, options, factory) => { - Object.assign(object, asset); + object.type = asset.type; + object.name = asset.name; + object.size = asset.size; + object.emitted = asset.emitted; + object.info = asset.info; + Object.assign( + object, + factory.create(`${context.type}$visible`, asset, context) + ); } }, - chunk: { - _: (object, chunk) => { - Object.assign(object, chunk); + asset$visible: { + _: (object, asset) => { + object.chunkNames = asset.chunkNames; + }, + ids: (object, asset) => { + object.chunks = asset.chunks; } }, module: { @@ -730,11 +741,48 @@ const SIMPLE_EXTRACTORS: SimpleExtractors = { factory ) => { const { type } = context; - Object.assign(object, module); + object.type = module.type; + object.moduleType = module.moduleType; + object.size = module.size; + Object.assign(object, factory.create(`${type}$visible`, module, context)); + } + }, + module$visible: { + _: (object, module, context, options, factory) => { + const { type } = context; + object.identifier = module.identifier; + object.name = module.name; + object.nameForCondition = module.nameForCondition; + object.issuer = module.issuer; + object.issuerName = module.issuerName; + object.issuerPath = factory.create( + `${type.slice(0, -8)}.issuerPath`, + module.issuerPath, + context + ); const profile = module.profile; if (profile) { object.profile = factory.create(`${type}.profile`, profile, context); } + }, + ids: (object, module) => { + object.id = module.id; + object.issuerId = module.issuerId; + object.chunks = module.chunks; + }, + moduleAssets: (object, module) => { + object.assets = module.assets; + }, + reasons: (object, module, context, options, factory) => { + const { type } = context; + object.reasons = factory.create( + `${type.slice(0, -8)}.reasons`, + module.reasons, + context + ); + }, + source: (object, module) => { + object.source = module.source; } }, profile: { @@ -750,6 +798,53 @@ const SIMPLE_EXTRACTORS: SimpleExtractors = { }; Object.assign(object, statsProfile); } + }, + moduleIssuer: { + _: (object, module, context, options, factory) => { + object.identifier = module.identifier; + object.name = module.name; + }, + ids: (object, module) => { + object.id = module.id; + } + }, + moduleReason: { + _: (object, reason) => { + object.moduleIdentifier = reason.moduleIdentifier; + object.moduleName = reason.moduleName; + object.type = reason.type; + object.userRequest = reason.userRequest; + }, + ids: (object, reason) => { + object.moduleId = reason.moduleId; + } + }, + chunk: { + _: (object, chunk) => { + object.type = chunk.type; + object.initial = chunk.initial; + object.entry = chunk.entry; + object.size = chunk.size; + object.names = chunk.names; + object.files = chunk.files; + object.auxiliaryFiles = chunk.auxiliaryFiles; + }, + ids: (object, chunk) => { + object.id = chunk.id; + }, + chunkRelations: (object, chunk) => { + object.siblings = chunk.siblings; + object.parents = chunk.parents; + object.children = chunk.children; + }, + chunkModules: (object, chunk, context, options, factory) => { + const { type } = context; + object.modules = factory.create( + `${type}.modules`, + chunk.modules, + context + ); + } } }; diff --git a/packages/rspack/src/stats/statsFactoryUtils.ts b/packages/rspack/src/stats/statsFactoryUtils.ts index e36a4a84cce3..b03fb90a7d69 100644 --- a/packages/rspack/src/stats/statsFactoryUtils.ts +++ b/packages/rspack/src/stats/statsFactoryUtils.ts @@ -7,7 +7,7 @@ import { import type { Compilation } from "../Compilation"; import type { StatsOptions } from "../config"; -import type { StatsFactory } from "./StatsFactory"; +import type { StatsFactory, StatsFactoryContext } from "./StatsFactory"; export type KnownStatsChunkGroup = binding.JsStatsChunkGroup; @@ -36,6 +36,9 @@ export type KnownStatsProfile = { export type StatsModule = KnownStatsModule & Record; +export type StatsModuleIssuer = binding.JsStatsModuleIssuer & + Record; + type StatsError = binding.JsStatsError & Record; type StatsWarnings = binding.JsStatsWarning & Record; @@ -111,7 +114,7 @@ type ExtractorsByOption = { [x: string]: ( object: O, data: T, - context: any, + context: StatsFactoryContext, options: any, factory: StatsFactory ) => void; @@ -124,7 +127,7 @@ type PreprocessedAsset = StatsAsset & { export type SimpleExtractors = { compilation: ExtractorsByOption; - // asset$visible: ExtractorsByOption; + asset$visible: ExtractorsByOption; asset: ExtractorsByOption; // chunkGroup: ExtractorsByOption< // { @@ -134,10 +137,16 @@ export type SimpleExtractors = { // StatsChunkGroup // >; module: ExtractorsByOption; - // module$visible: ExtractorsByOption; - // moduleIssuer: ExtractorsByOption; + module$visible: ExtractorsByOption; + moduleIssuer: ExtractorsByOption< + binding.JsStatsModuleIssuer, + StatsModuleIssuer + >; profile: ExtractorsByOption; - // moduleReason: ExtractorsByOption; + moduleReason: ExtractorsByOption< + binding.JsStatsModuleReason, + StatsModuleReason + >; chunk: ExtractorsByOption; // chunkOrigin: ExtractorsByOption; // error: ExtractorsByOption; diff --git a/packages/rspack/tests/Stats.test.ts b/packages/rspack/tests/Stats.test.ts index 62e681f09aac..02fd5e195551 100644 --- a/packages/rspack/tests/Stats.test.ts +++ b/packages/rspack/tests/Stats.test.ts @@ -24,141 +24,7 @@ describe("Stats", () => { version: false }; expect(typeof stats?.hash).toBe("string"); - expect(stats?.toJson(statsOptions)).toMatchInlineSnapshot(` - { - "assets": [ - { - "chunkNames": [ - "main", - ], - "chunks": [ - "main", - ], - "emitted": true, - "info": { - "development": false, - "hotModuleReplacement": false, - }, - "name": "main.js", - "size": 215, - "type": "asset", - }, - ], - "assetsByChunkName": { - "main": [ - "main.js", - ], - }, - "chunks": [ - { - "auxiliaryFiles": [], - "children": [], - "entry": true, - "files": [ - "main.js", - ], - "id": "main", - "initial": true, - "modules": [ - { - "assets": [], - "chunks": [ - "main", - ], - "id": "876", - "identifier": "/tests/fixtures/a.js", - "issuerPath": [], - "moduleType": "javascript/auto", - "name": "./fixtures/a.js", - "nameForCondition": "/tests/fixtures/a.js", - "reasons": [ - { - "type": "entry", - "userRequest": "./fixtures/a", - }, - ], - "size": 55, - "source": "module.exports = function a() { - return "This is a"; - };", - "type": "module", - }, - ], - "names": [ - "main", - ], - "parents": [], - "siblings": [], - "size": 55, - "type": "chunk", - }, - ], - "entrypoints": { - "main": { - "assets": [ - { - "name": "main.js", - "size": 215, - }, - ], - "assetsSize": 215, - "chunks": [ - "main", - ], - "name": "main", - }, - }, - "errors": [], - "errorsCount": 0, - "filteredModules": undefined, - "hash": "b32fac08a5e8721cacff", - "logging": {}, - "modules": [ - { - "assets": [], - "chunks": [ - "main", - ], - "id": "876", - "identifier": "/tests/fixtures/a.js", - "issuerPath": [], - "moduleType": "javascript/auto", - "name": "./fixtures/a.js", - "nameForCondition": "/tests/fixtures/a.js", - "reasons": [ - { - "type": "entry", - "userRequest": "./fixtures/a", - }, - ], - "size": 55, - "source": "module.exports = function a() { - return "This is a"; - };", - "type": "module", - }, - ], - "namedChunkGroups": { - "main": { - "assets": [ - { - "name": "main.js", - "size": 215, - }, - ], - "assetsSize": 215, - "chunks": [ - "main", - ], - "name": "main", - }, - }, - "outputPath": "/dist", - "publicPath": "auto", - "warnings": [], - "warningsCount": 0, - } - `); + expect(stats?.toJson(statsOptions)).toMatchSnapshot(); expect(stats?.toString(statsOptions)).toMatchInlineSnapshot(` "PublicPath: auto asset main.js 215 bytes {main} [emitted] (name: main) @@ -193,12 +59,12 @@ describe("Stats", () => { stats?.toString({ timings: false, version: false }).replace(/\\/g, "/") ).toMatchInlineSnapshot(` "PublicPath: auto - asset main.js 419 bytes {main} [emitted] (name: main) + asset main.js 419 bytes [emitted] (name: main) Entrypoint main 419 bytes = main.js - ./fixtures/a.js [876] {main} - ./fixtures/b.js [211] {main} - ./fixtures/c.js [537] {main} - ./fixtures/abc.js [222] {main} + ./fixtures/a.js + ./fixtures/b.js + ./fixtures/c.js + ./fixtures/abc.js error[javascript]: JavaScript parsing error ┌─ tests/fixtures/b.js:6:1 @@ -229,365 +95,7 @@ describe("Stats", () => { builtAt: false, version: false }; - expect(stats?.toJson(statsOptions)).toMatchInlineSnapshot(` - { - "assets": [ - { - "chunkNames": [ - "main", - ], - "chunks": [ - "main", - ], - "emitted": true, - "info": { - "development": false, - "hotModuleReplacement": false, - }, - "name": "main.js", - "size": 394, - "type": "asset", - }, - ], - "assetsByChunkName": { - "main": [ - "main.js", - ], - }, - "chunks": [ - { - "auxiliaryFiles": [], - "children": [], - "entry": true, - "files": [ - "main.js", - ], - "id": "main", - "initial": true, - "modules": [ - { - "assets": [], - "chunks": [ - "main", - ], - "id": "876", - "identifier": "/tests/fixtures/a.js", - "issuer": "/tests/fixtures/c.js?c=3", - "issuerId": "970", - "issuerName": "./fixtures/c.js?c=3", - "issuerPath": [ - { - "id": "661", - "identifier": "/tests/fixtures/abc-query.js", - "name": "./fixtures/abc-query.js", - }, - { - "id": "970", - "identifier": "/tests/fixtures/c.js?c=3", - "name": "./fixtures/c.js?c=3", - }, - ], - "moduleType": "javascript/auto", - "name": "./fixtures/a.js", - "nameForCondition": "/tests/fixtures/a.js", - "reasons": [ - { - "moduleId": "970", - "moduleIdentifier": "/tests/fixtures/c.js?c=3", - "moduleName": "./fixtures/c.js?c=3", - "type": "cjs require", - "userRequest": "./a", - }, - ], - "size": 55, - "source": "module.exports = function a() { - return "This is a"; - };", - "type": "module", - }, - { - "assets": [], - "chunks": [ - "main", - ], - "id": "304", - "identifier": "/tests/fixtures/a.js?a=1", - "issuer": "/tests/fixtures/abc-query.js", - "issuerId": "661", - "issuerName": "./fixtures/abc-query.js", - "issuerPath": [ - { - "id": "661", - "identifier": "/tests/fixtures/abc-query.js", - "name": "./fixtures/abc-query.js", - }, - ], - "moduleType": "javascript/auto", - "name": "./fixtures/a.js?a=1", - "nameForCondition": "/tests/fixtures/a.js", - "reasons": [ - { - "moduleId": "661", - "moduleIdentifier": "/tests/fixtures/abc-query.js", - "moduleName": "./fixtures/abc-query.js", - "type": "cjs require", - "userRequest": "./a?a=1", - }, - ], - "size": 55, - "source": "module.exports = function a() { - return "This is a"; - };", - "type": "module", - }, - { - "assets": [], - "chunks": [ - "main", - ], - "id": "970", - "identifier": "/tests/fixtures/c.js?c=3", - "issuer": "/tests/fixtures/abc-query.js", - "issuerId": "661", - "issuerName": "./fixtures/abc-query.js", - "issuerPath": [ - { - "id": "661", - "identifier": "/tests/fixtures/abc-query.js", - "name": "./fixtures/abc-query.js", - }, - ], - "moduleType": "javascript/auto", - "name": "./fixtures/c.js?c=3", - "nameForCondition": "/tests/fixtures/c.js", - "reasons": [ - { - "moduleId": "661", - "moduleIdentifier": "/tests/fixtures/abc-query.js", - "moduleName": "./fixtures/abc-query.js", - "type": "cjs require", - "userRequest": "./c?c=3", - }, - ], - "size": 72, - "source": "module.exports = function b() { - require("./a"); - return "This is c"; - };", - "type": "module", - }, - { - "assets": [], - "chunks": [ - "main", - ], - "id": "661", - "identifier": "/tests/fixtures/abc-query.js", - "issuerPath": [], - "moduleType": "javascript/auto", - "name": "./fixtures/abc-query.js", - "nameForCondition": "/tests/fixtures/abc-query.js", - "reasons": [ - { - "type": "entry", - "userRequest": "./fixtures/abc-query", - }, - ], - "size": 99, - "source": "exports.a = require("./a?a=1"); - // exports.b = require("./b?b=2"); - exports.c = require("./c?c=3"); - ", - "type": "module", - }, - ], - "names": [ - "main", - ], - "parents": [], - "siblings": [], - "size": 281, - "type": "chunk", - }, - ], - "entrypoints": { - "main": { - "assets": [ - { - "name": "main.js", - "size": 394, - }, - ], - "assetsSize": 394, - "chunks": [ - "main", - ], - "name": "main", - }, - }, - "errors": [], - "errorsCount": 0, - "filteredModules": undefined, - "hash": "5eccb83e369e53af1313", - "logging": {}, - "modules": [ - { - "assets": [], - "chunks": [ - "main", - ], - "id": "876", - "identifier": "/tests/fixtures/a.js", - "issuer": "/tests/fixtures/c.js?c=3", - "issuerId": "970", - "issuerName": "./fixtures/c.js?c=3", - "issuerPath": [ - { - "id": "661", - "identifier": "/tests/fixtures/abc-query.js", - "name": "./fixtures/abc-query.js", - }, - { - "id": "970", - "identifier": "/tests/fixtures/c.js?c=3", - "name": "./fixtures/c.js?c=3", - }, - ], - "moduleType": "javascript/auto", - "name": "./fixtures/a.js", - "nameForCondition": "/tests/fixtures/a.js", - "reasons": [ - { - "moduleId": "970", - "moduleIdentifier": "/tests/fixtures/c.js?c=3", - "moduleName": "./fixtures/c.js?c=3", - "type": "cjs require", - "userRequest": "./a", - }, - ], - "size": 55, - "source": "module.exports = function a() { - return "This is a"; - };", - "type": "module", - }, - { - "assets": [], - "chunks": [ - "main", - ], - "id": "304", - "identifier": "/tests/fixtures/a.js?a=1", - "issuer": "/tests/fixtures/abc-query.js", - "issuerId": "661", - "issuerName": "./fixtures/abc-query.js", - "issuerPath": [ - { - "id": "661", - "identifier": "/tests/fixtures/abc-query.js", - "name": "./fixtures/abc-query.js", - }, - ], - "moduleType": "javascript/auto", - "name": "./fixtures/a.js?a=1", - "nameForCondition": "/tests/fixtures/a.js", - "reasons": [ - { - "moduleId": "661", - "moduleIdentifier": "/tests/fixtures/abc-query.js", - "moduleName": "./fixtures/abc-query.js", - "type": "cjs require", - "userRequest": "./a?a=1", - }, - ], - "size": 55, - "source": "module.exports = function a() { - return "This is a"; - };", - "type": "module", - }, - { - "assets": [], - "chunks": [ - "main", - ], - "id": "970", - "identifier": "/tests/fixtures/c.js?c=3", - "issuer": "/tests/fixtures/abc-query.js", - "issuerId": "661", - "issuerName": "./fixtures/abc-query.js", - "issuerPath": [ - { - "id": "661", - "identifier": "/tests/fixtures/abc-query.js", - "name": "./fixtures/abc-query.js", - }, - ], - "moduleType": "javascript/auto", - "name": "./fixtures/c.js?c=3", - "nameForCondition": "/tests/fixtures/c.js", - "reasons": [ - { - "moduleId": "661", - "moduleIdentifier": "/tests/fixtures/abc-query.js", - "moduleName": "./fixtures/abc-query.js", - "type": "cjs require", - "userRequest": "./c?c=3", - }, - ], - "size": 72, - "source": "module.exports = function b() { - require("./a"); - return "This is c"; - };", - "type": "module", - }, - { - "assets": [], - "chunks": [ - "main", - ], - "id": "661", - "identifier": "/tests/fixtures/abc-query.js", - "issuerPath": [], - "moduleType": "javascript/auto", - "name": "./fixtures/abc-query.js", - "nameForCondition": "/tests/fixtures/abc-query.js", - "reasons": [ - { - "type": "entry", - "userRequest": "./fixtures/abc-query", - }, - ], - "size": 99, - "source": "exports.a = require("./a?a=1"); - // exports.b = require("./b?b=2"); - exports.c = require("./c?c=3"); - ", - "type": "module", - }, - ], - "namedChunkGroups": { - "main": { - "assets": [ - { - "name": "main.js", - "size": 394, - }, - ], - "assetsSize": 394, - "chunks": [ - "main", - ], - "name": "main", - }, - }, - "outputPath": "/dist", - "publicPath": "auto", - "warnings": [], - "warningsCount": 0, - } - `); + expect(stats?.toJson(statsOptions)).toMatchSnapshot(); }); it("should output the specified number of modules when set stats.modulesSpace", async () => { @@ -697,16 +205,13 @@ describe("Stats", () => { .replace(/\\/g, "/") .replace(/\d+ ms/g, "X ms") ).toMatchInlineSnapshot(` - "./fixtures/a.js [876] {main} - [222] -> + "./fixtures/a.js X ms (resolving: X ms, integration: X ms, building: X ms) - ./fixtures/b.js [211] {main} - [222] -> + ./fixtures/b.js X ms (resolving: X ms, integration: X ms, building: X ms) - ./fixtures/c.js [537] {main} - [222] -> + ./fixtures/c.js X ms (resolving: X ms, integration: X ms, building: X ms) - ./fixtures/abc.js [222] {main} + ./fixtures/abc.js X ms (resolving: X ms, integration: X ms, building: X ms)" `); }); @@ -824,4 +329,24 @@ describe("Stats", () => { expect(stats).toContain("module factorize cache: 100.0% (1/1)"); expect(stats).toContain("module code generation cache: 100.0% (4/4)"); }); + + it("should have ids when ids is true", async () => { + const stats = await compile({ + context: __dirname, + entry: "./fixtures/a" + }); + const options = { + all: false, + assets: true, + modules: true, + chunks: true, + ids: true + }; + expect(stats?.toJson(options)).toMatchSnapshot(); + expect(stats?.toString(options).replace(/\\/g, "/")).toMatchInlineSnapshot(` + "asset main.js 215 bytes {main} [emitted] (name: main) + chunk {main} main.js (main) [entry] + ./fixtures/a.js [876] {main}" + `); + }); }); diff --git a/packages/rspack/tests/__snapshots__/Stats.test.ts.snap b/packages/rspack/tests/__snapshots__/Stats.test.ts.snap new file mode 100644 index 000000000000..10a53123a113 --- /dev/null +++ b/packages/rspack/tests/__snapshots__/Stats.test.ts.snap @@ -0,0 +1,584 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Stats should have ids when ids is true 1`] = ` +{ + "assets": [ + { + "chunkNames": [ + "main", + ], + "chunks": [ + "main", + ], + "emitted": true, + "info": { + "development": false, + "hotModuleReplacement": false, + }, + "name": "main.js", + "size": 215, + "type": "asset", + }, + ], + "assetsByChunkName": { + "main": [ + "main.js", + ], + }, + "chunks": [ + { + "auxiliaryFiles": [], + "entry": true, + "files": [ + "main.js", + ], + "id": "main", + "initial": true, + "names": [ + "main", + ], + "size": 55, + "type": "chunk", + }, + ], + "filteredModules": undefined, + "modules": [ + { + "chunks": [ + "main", + ], + "id": "876", + "identifier": "/tests/fixtures/a.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, + "issuerPath": [], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", + "size": 55, + "type": "module", + }, + ], +} +`; + +exports[`Stats should have stats 1`] = ` +{ + "assets": [ + { + "chunkNames": [ + "main", + ], + "chunks": [ + "main", + ], + "emitted": true, + "info": { + "development": false, + "hotModuleReplacement": false, + }, + "name": "main.js", + "size": 215, + "type": "asset", + }, + ], + "assetsByChunkName": { + "main": [ + "main.js", + ], + }, + "chunks": [ + { + "auxiliaryFiles": [], + "children": [], + "entry": true, + "files": [ + "main.js", + ], + "id": "main", + "initial": true, + "modules": [ + { + "assets": [], + "chunks": [ + "main", + ], + "id": "876", + "identifier": "/tests/fixtures/a.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, + "issuerPath": [], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, + "type": "entry", + "userRequest": "./fixtures/a", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; +};", + "type": "module", + }, + ], + "names": [ + "main", + ], + "parents": [], + "siblings": [], + "size": 55, + "type": "chunk", + }, + ], + "entrypoints": { + "main": { + "assets": [ + { + "name": "main.js", + "size": 215, + }, + ], + "assetsSize": 215, + "chunks": [ + "main", + ], + "name": "main", + }, + }, + "errors": [], + "errorsCount": 0, + "filteredModules": undefined, + "hash": "b32fac08a5e8721cacff", + "logging": {}, + "modules": [ + { + "assets": [], + "chunks": [ + "main", + ], + "id": "876", + "identifier": "/tests/fixtures/a.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, + "issuerPath": [], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, + "type": "entry", + "userRequest": "./fixtures/a", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; +};", + "type": "module", + }, + ], + "namedChunkGroups": { + "main": { + "assets": [ + { + "name": "main.js", + "size": 215, + }, + ], + "assetsSize": 215, + "chunks": [ + "main", + ], + "name": "main", + }, + }, + "outputPath": "/dist", + "publicPath": "auto", + "warnings": [], + "warningsCount": 0, +} +`; + +exports[`Stats should output stats with query 1`] = ` +{ + "assets": [ + { + "chunkNames": [ + "main", + ], + "chunks": [ + "main", + ], + "emitted": true, + "info": { + "development": false, + "hotModuleReplacement": false, + }, + "name": "main.js", + "size": 394, + "type": "asset", + }, + ], + "assetsByChunkName": { + "main": [ + "main.js", + ], + }, + "chunks": [ + { + "auxiliaryFiles": [], + "children": [], + "entry": true, + "files": [ + "main.js", + ], + "id": "main", + "initial": true, + "modules": [ + { + "assets": [], + "chunks": [ + "main", + ], + "id": "876", + "identifier": "/tests/fixtures/a.js", + "issuer": "/tests/fixtures/c.js?c=3", + "issuerId": "970", + "issuerName": "./fixtures/c.js?c=3", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + { + "id": "970", + "identifier": "/tests/fixtures/c.js?c=3", + "name": "./fixtures/c.js?c=3", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": "970", + "moduleIdentifier": "/tests/fixtures/c.js?c=3", + "moduleName": "./fixtures/c.js?c=3", + "type": "cjs require", + "userRequest": "./a", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; +};", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "304", + "identifier": "/tests/fixtures/a.js?a=1", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js?a=1", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./a?a=1", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; +};", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "970", + "identifier": "/tests/fixtures/c.js?c=3", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/c.js?c=3", + "nameForCondition": "/tests/fixtures/c.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./c?c=3", + }, + ], + "size": 72, + "source": "module.exports = function b() { + require("./a"); + return "This is c"; +};", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, + "issuerPath": [], + "moduleType": "javascript/auto", + "name": "./fixtures/abc-query.js", + "nameForCondition": "/tests/fixtures/abc-query.js", + "reasons": [ + { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, + "type": "entry", + "userRequest": "./fixtures/abc-query", + }, + ], + "size": 99, + "source": "exports.a = require("./a?a=1"); +// exports.b = require("./b?b=2"); +exports.c = require("./c?c=3"); +", + "type": "module", + }, + ], + "names": [ + "main", + ], + "parents": [], + "siblings": [], + "size": 281, + "type": "chunk", + }, + ], + "entrypoints": { + "main": { + "assets": [ + { + "name": "main.js", + "size": 394, + }, + ], + "assetsSize": 394, + "chunks": [ + "main", + ], + "name": "main", + }, + }, + "errors": [], + "errorsCount": 0, + "filteredModules": undefined, + "hash": "5eccb83e369e53af1313", + "logging": {}, + "modules": [ + { + "assets": [], + "chunks": [ + "main", + ], + "id": "876", + "identifier": "/tests/fixtures/a.js", + "issuer": "/tests/fixtures/c.js?c=3", + "issuerId": "970", + "issuerName": "./fixtures/c.js?c=3", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + { + "id": "970", + "identifier": "/tests/fixtures/c.js?c=3", + "name": "./fixtures/c.js?c=3", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": "970", + "moduleIdentifier": "/tests/fixtures/c.js?c=3", + "moduleName": "./fixtures/c.js?c=3", + "type": "cjs require", + "userRequest": "./a", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; +};", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "304", + "identifier": "/tests/fixtures/a.js?a=1", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js?a=1", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./a?a=1", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; +};", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "970", + "identifier": "/tests/fixtures/c.js?c=3", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/c.js?c=3", + "nameForCondition": "/tests/fixtures/c.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./c?c=3", + }, + ], + "size": 72, + "source": "module.exports = function b() { + require("./a"); + return "This is c"; +};", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, + "issuerPath": [], + "moduleType": "javascript/auto", + "name": "./fixtures/abc-query.js", + "nameForCondition": "/tests/fixtures/abc-query.js", + "reasons": [ + { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, + "type": "entry", + "userRequest": "./fixtures/abc-query", + }, + ], + "size": 99, + "source": "exports.a = require("./a?a=1"); +// exports.b = require("./b?b=2"); +exports.c = require("./c?c=3"); +", + "type": "module", + }, + ], + "namedChunkGroups": { + "main": { + "assets": [ + { + "name": "main.js", + "size": 394, + }, + ], + "assetsSize": 394, + "chunks": [ + "main", + ], + "name": "main", + }, + }, + "outputPath": "/dist", + "publicPath": "auto", + "warnings": [], + "warningsCount": 0, +} +`; diff --git a/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap b/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap index b6b9edb68ce5..affeb7677da5 100644 --- a/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap +++ b/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap @@ -106,12 +106,18 @@ exports[`StatsTestCases should print correct stats for auxiliary-files-test 1`] ], "id": "10", "identifier": "/tests/statsCases/auxiliary-files-test/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/auxiliary-files-test/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -236,12 +242,18 @@ import rawModule from './raw.png'", ], "id": "10", "identifier": "/tests/statsCases/auxiliary-files-test/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/auxiliary-files-test/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -440,12 +452,18 @@ exports[`StatsTestCases should print correct stats for filename 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/filename/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/filename/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -492,12 +510,18 @@ exports[`StatsTestCases should print correct stats for filename 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/filename/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/filename/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -624,12 +648,18 @@ exports[`StatsTestCases should print correct stats for hot+production 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/hot+production/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/hot+production/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index.js", }, @@ -677,12 +707,18 @@ exports[`StatsTestCases should print correct stats for hot+production 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/hot+production/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/hot+production/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index.js", }, @@ -789,11 +825,9 @@ exports[`StatsTestCases should print correct stats for ignore-plugin 1`] = ` "filteredModules": undefined, "modules": [ { - "chunks": [ - "main", - ], - "id": "10", "identifier": "/tests/statsCases/ignore-plugin/index.js", + "issuer": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", @@ -802,22 +836,15 @@ exports[`StatsTestCases should print correct stats for ignore-plugin 1`] = ` "type": "module", }, { - "chunks": [ - "main", - ], - "id": "208", "identifier": "/tests/statsCases/ignore-plugin/locals/en.js", "issuer": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", - "issuerId": "247", "issuerName": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", "issuerPath": [ { - "id": "10", "identifier": "/tests/statsCases/ignore-plugin/index.js", "name": "./index.js", }, { - "id": "247", "identifier": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", "name": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", }, @@ -829,70 +856,54 @@ exports[`StatsTestCases should print correct stats for ignore-plugin 1`] = ` "type": "module", }, { - "chunks": [ - "main", - ], - "id": "136", "identifier": "missing|/tests/statsCases/ignore-plugin/locals/./zh.js", "issuer": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", - "issuerId": "247", "issuerName": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", "issuerPath": [ { - "id": "10", "identifier": "/tests/statsCases/ignore-plugin/index.js", "name": "./index.js", }, { - "id": "247", "identifier": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", "name": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", }, ], "moduleType": "javascript/auto", "name": "/tests/statsCases/ignore-plugin/locals/./zh.js (missing)", + "nameForCondition": undefined, "size": 160, "type": "module", }, { - "chunks": [ - "main", - ], - "id": "239", "identifier": "missing|/tests/statsCases/ignore-plugin/./globalIndex.js", "issuer": "/tests/statsCases/ignore-plugin/index.js", - "issuerId": "10", "issuerName": "./index.js", "issuerPath": [ { - "id": "10", "identifier": "/tests/statsCases/ignore-plugin/index.js", "name": "./index.js", }, ], "moduleType": "javascript/auto", "name": "/tests/statsCases/ignore-plugin/./globalIndex.js (missing)", + "nameForCondition": undefined, "size": 160, "type": "module", }, { - "chunks": [ - "main", - ], - "id": "247", "identifier": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", "issuer": "/tests/statsCases/ignore-plugin/index.js", - "issuerId": "10", "issuerName": "./index.js", "issuerPath": [ { - "id": "10", "identifier": "/tests/statsCases/ignore-plugin/index.js", "name": "./index.js", }, ], "moduleType": "javascript/auto", "name": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", + "nameForCondition": undefined, "size": 160, "type": "module", }, @@ -901,11 +912,11 @@ exports[`StatsTestCases should print correct stats for ignore-plugin 1`] = ` `; exports[`StatsTestCases should print correct stats for ignore-plugin 2`] = ` -"./index.js [10] {main} -./locals/en.js [208] {main} -/tests/statsCases/ignore-plugin/locals/./zh.js (missing) [136] {main} -/tests/statsCases/ignore-plugin/./globalIndex.js (missing) [239] {main} -/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset } [247] {main}" +"./index.js +./locals/en.js +/tests/statsCases/ignore-plugin/locals/./zh.js (missing) +/tests/statsCases/ignore-plugin/./globalIndex.js (missing) +/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }" `; exports[`StatsTestCases should print correct stats for ignore-warning 1`] = ` @@ -1156,7 +1167,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e1.js", ], - "id": "e1", "initial": true, "names": [ "e1", @@ -1170,7 +1180,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e1~runtime.js", ], - "id": "e1~runtime", "initial": true, "names": [ "e1~runtime", @@ -1184,7 +1193,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e2.js", ], - "id": "e2", "initial": true, "names": [ "e2", @@ -1198,7 +1206,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e2~runtime.js", ], - "id": "e2~runtime", "initial": true, "names": [ "e2~runtime", @@ -1290,10 +1297,10 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun exports[`StatsTestCases should print correct stats for optimization-runtime-chunk 2`] = ` "Entrypoint e1 2.8 KiB = e1~runtime.js 2.4 KiB e1.js 412 bytes Entrypoint e2 2.8 KiB = e2~runtime.js 2.4 KiB e2.js 412 bytes -chunk {e1} e1.js (e1) [entry] -chunk {e1~runtime} e1~runtime.js (e1~runtime) [initial] -chunk {e2} e2.js (e2) [entry] -chunk {e2~runtime} e2~runtime.js (e2~runtime) [initial]" +chunk e1.js (e1) [entry] +chunk e1~runtime.js (e1~runtime) [initial] +chunk e2.js (e2) [entry] +chunk e2~runtime.js (e2~runtime) [initial]" `; exports[`StatsTestCases should print correct stats for optimization-runtime-chunk-multiple 1`] = ` @@ -1305,7 +1312,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e1.js", ], - "id": "e1", "initial": true, "names": [ "e1", @@ -1319,7 +1325,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e2.js", ], - "id": "e2", "initial": true, "names": [ "e2", @@ -1333,7 +1338,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "runtime~e1.js", ], - "id": "runtime~e1", "initial": true, "names": [ "runtime~e1", @@ -1347,7 +1351,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "runtime~e2.js", ], - "id": "runtime~e2", "initial": true, "names": [ "runtime~e2", @@ -1439,10 +1442,10 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun exports[`StatsTestCases should print correct stats for optimization-runtime-chunk-multiple 2`] = ` "Entrypoint e1 2.8 KiB = runtime~e1.js 2.4 KiB e1.js 406 bytes Entrypoint e2 2.8 KiB = runtime~e2.js 2.4 KiB e2.js 406 bytes -chunk {e1} e1.js (e1) [entry] -chunk {e2} e2.js (e2) [entry] -chunk {runtime~e1} runtime~e1.js (runtime~e1) [initial] -chunk {runtime~e2} runtime~e2.js (runtime~e2) [initial]" +chunk e1.js (e1) [entry] +chunk e2.js (e2) [entry] +chunk runtime~e1.js (runtime~e1) [initial] +chunk runtime~e2.js (runtime~e2) [initial]" `; exports[`StatsTestCases should print correct stats for optimization-runtime-chunk-single 1`] = ` @@ -1454,7 +1457,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e1.js", ], - "id": "e1", "initial": true, "names": [ "e1", @@ -1468,7 +1470,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e2.js", ], - "id": "e2", "initial": true, "names": [ "e2", @@ -1482,7 +1483,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "runtime.js", ], - "id": "runtime", "initial": true, "names": [ "runtime", @@ -1574,9 +1574,9 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun exports[`StatsTestCases should print correct stats for optimization-runtime-chunk-single 2`] = ` "Entrypoint e1 2.8 KiB = runtime.js 2.4 KiB e1.js 409 bytes Entrypoint e2 2.8 KiB = runtime.js 2.4 KiB e2.js 409 bytes -chunk {e1} e1.js (e1) [entry] -chunk {e2} e2.js (e2) [entry] -chunk {runtime} runtime.js (runtime) [initial]" +chunk e1.js (e1) [entry] +chunk e2.js (e2) [entry] +chunk runtime.js (runtime) [initial]" `; exports[`StatsTestCases should print correct stats for optimization-runtime-chunk-true 1`] = ` @@ -1588,7 +1588,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e1.js", ], - "id": "e1", "initial": true, "names": [ "e1", @@ -1602,7 +1601,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e2.js", ], - "id": "e2", "initial": true, "names": [ "e2", @@ -1616,7 +1614,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "runtime~e1.js", ], - "id": "runtime~e1", "initial": true, "names": [ "runtime~e1", @@ -1630,7 +1627,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "runtime~e2.js", ], - "id": "runtime~e2", "initial": true, "names": [ "runtime~e2", @@ -1722,10 +1718,10 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun exports[`StatsTestCases should print correct stats for optimization-runtime-chunk-true 2`] = ` "Entrypoint e1 2.8 KiB = runtime~e1.js 2.4 KiB e1.js 406 bytes Entrypoint e2 2.8 KiB = runtime~e2.js 2.4 KiB e2.js 406 bytes -chunk {e1} e1.js (e1) [entry] -chunk {e2} e2.js (e2) [entry] -chunk {runtime~e1} runtime~e1.js (runtime~e1) [initial] -chunk {runtime~e2} runtime~e2.js (runtime~e2) [initial]" +chunk e1.js (e1) [entry] +chunk e2.js (e2) [entry] +chunk runtime~e1.js (runtime~e1) [initial] +chunk runtime~e2.js (runtime~e2) [initial]" `; exports[`StatsTestCases should print correct stats for parse-error 1`] = ` @@ -1827,17 +1823,11 @@ exports[`StatsTestCases should print correct stats for reasons 1`] = ` "filteredModules": undefined, "modules": [ { - "chunks": [ - "main", - ], - "id": "847", "identifier": "/tests/statsCases/reasons/a.js", "issuer": "/tests/statsCases/reasons/index.js", - "issuerId": "10", "issuerName": "./index.js", "issuerPath": [ { - "id": "10", "identifier": "/tests/statsCases/reasons/index.js", "name": "./index.js", }, @@ -1847,7 +1837,6 @@ exports[`StatsTestCases should print correct stats for reasons 1`] = ` "nameForCondition": "/tests/statsCases/reasons/a.js", "reasons": [ { - "moduleId": "10", "moduleIdentifier": "/tests/statsCases/reasons/index.js", "moduleName": "./index.js", "type": "cjs require", @@ -1858,17 +1847,17 @@ exports[`StatsTestCases should print correct stats for reasons 1`] = ` "type": "module", }, { - "chunks": [ - "main", - ], - "id": "10", "identifier": "/tests/statsCases/reasons/index.js", + "issuer": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/reasons/index.js", "reasons": [ { + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -1881,9 +1870,9 @@ exports[`StatsTestCases should print correct stats for reasons 1`] = ` `; exports[`StatsTestCases should print correct stats for reasons 2`] = ` -"./a.js [847] {main} - cjs require ./a [10] -./index.js [10] {main} +"./a.js + cjs require ./a +./index.js entry ./index" `; @@ -1930,12 +1919,18 @@ exports[`StatsTestCases should print correct stats for resolve-overflow 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/resolve-overflow/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/resolve-overflow/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -1965,6 +1960,7 @@ console.log(a); ], "moduleType": "javascript/auto", "name": "/tests/statsCases/resolve-overflow/cycle-alias/a (missing)", + "nameForCondition": undefined, "reasons": [ { "moduleId": "10", @@ -1982,6 +1978,7 @@ console.log(a); }, ], "size": 160, + "source": undefined, "type": "module", }, ], @@ -2036,12 +2033,18 @@ console.log(a); ], "id": "10", "identifier": "/tests/statsCases/resolve-overflow/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/resolve-overflow/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2071,6 +2074,7 @@ console.log(a); ], "moduleType": "javascript/auto", "name": "/tests/statsCases/resolve-overflow/cycle-alias/a (missing)", + "nameForCondition": undefined, "reasons": [ { "moduleId": "10", @@ -2088,6 +2092,7 @@ console.log(a); }, ], "size": 160, + "source": undefined, "type": "module", }, ], @@ -2185,12 +2190,18 @@ exports[`StatsTestCases should print correct stats for resolve-unexpected-export ], "id": "10", "identifier": "/tests/statsCases/resolve-unexpected-exports-in-pkg/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/resolve-unexpected-exports-in-pkg/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2220,6 +2231,7 @@ console.log(a); ], "moduleType": "javascript/auto", "name": "/tests/statsCases/resolve-unexpected-exports-in-pkg/pkg-a (missing)", + "nameForCondition": undefined, "reasons": [ { "moduleId": "10", @@ -2237,6 +2249,7 @@ console.log(a); }, ], "size": 160, + "source": undefined, "type": "module", }, ], @@ -2285,12 +2298,18 @@ console.log(a); ], "id": "10", "identifier": "/tests/statsCases/resolve-unexpected-exports-in-pkg/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/resolve-unexpected-exports-in-pkg/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2320,6 +2339,7 @@ console.log(a); ], "moduleType": "javascript/auto", "name": "/tests/statsCases/resolve-unexpected-exports-in-pkg/pkg-a (missing)", + "nameForCondition": undefined, "reasons": [ { "moduleId": "10", @@ -2337,6 +2357,7 @@ console.log(a); }, ], "size": 160, + "source": undefined, "type": "module", }, ], @@ -2428,12 +2449,18 @@ exports[`StatsTestCases should print correct stats for simple 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/simple/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/simple/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2481,12 +2508,18 @@ exports[`StatsTestCases should print correct stats for simple 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/simple/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/simple/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2607,12 +2640,18 @@ exports[`StatsTestCases should print correct stats for simple-module-source 1`] ], "id": "10", "identifier": "/tests/statsCases/simple-module-source/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/simple-module-source/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2726,12 +2765,18 @@ import rawModule from './raw.png'", ], "id": "10", "identifier": "/tests/statsCases/simple-module-source/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/simple-module-source/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2799,11 +2844,11 @@ import rawModule from './raw.png'", exports[`StatsTestCases should print correct stats for simple-module-source 2`] = ` "PublicPath: (none) -asset bundle.js 631 bytes {main} [emitted] (name: main) +asset bundle.js 631 bytes [emitted] (name: main) Entrypoint main 631 bytes = bundle.js -./raw.png [98] {main} -./index.js [10] {main} -./stringModule.js [363] {main} +./raw.png +./index.js +./stringModule.js rspack compiled successfully (a563cef06d977112d3e1)" `; @@ -2851,12 +2896,18 @@ exports[`StatsTestCases should print correct stats for stats-hooks 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/stats-hooks/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/stats-hooks/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2902,12 +2953,18 @@ exports[`StatsTestCases should print correct stats for stats-hooks 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/stats-hooks/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", "nameForCondition": "/tests/statsCases/stats-hooks/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2940,9 +2997,9 @@ exports[`StatsTestCases should print correct stats for stats-hooks 1`] = ` exports[`StatsTestCases should print correct stats for stats-hooks 2`] = ` "PublicPath: (none) -asset bundle.js 589 bytes {main} [emitted111] (name: main) [testA: aaaaaa] +asset bundle.js 589 bytes [emitted111] (name: main) [testA: aaaaaa] Entrypoint main 589 bytes = bundle.js -./index.js [10] {main} +./index.js rspack compiled successfully (44e8a78970b06279cd26)" `; From 296f3905693e0f6b7135ac4f1e3c2b60cbf95c27 Mon Sep 17 00:00:00 2001 From: Hana Date: Fri, 8 Sep 2023 16:15:37 +0800 Subject: [PATCH 24/30] feat: support `builtin:swc-loader` experimental transformers (#4133) * feat: init * feat: add transformer * feat: save options * feat: migrate options * feat: cleanup * feat: cleanup again * feat: add swc loader experiments helper * chore: cleanup * test: migrate test * feat: make normalization happens naturally --- Cargo.lock | 22 ++- Cargo.toml | 1 + crates/node_binding/binding.d.ts | 4 +- crates/rspack_binding_options/Cargo.toml | 2 +- .../raw_builtins/raw_to_be_deprecated.rs | 14 +- .../src/options/raw_module/mod.rs | 19 +- crates/rspack_core/Cargo.toml | 1 + crates/rspack_core/src/options/builtins.rs | 48 +---- crates/rspack_hash/Cargo.toml | 2 +- crates/rspack_loader_swc/Cargo.toml | 5 + crates/rspack_loader_swc/src/lib.rs | 186 ++++++------------ crates/rspack_loader_swc/src/options.rs | 147 ++++++++++++++ crates/rspack_loader_swc/src/transformer.rs | 79 ++++++++ crates/rspack_loader_swc/tests/fixtures.rs | 2 +- crates/rspack_plugin_javascript/Cargo.toml | 3 +- .../src/visitors/mod.rs | 27 ++- .../src/visitors/plugin_import.rs | 11 -- .../src/visitors/swc_visitor/compat.rs | 9 +- .../src/visitors/swc_visitor/mod.rs | 9 - .../src/visitors/swc_visitor/typescript.rs | 4 +- crates/rspack_swc_visitors/Cargo.toml | 27 +++ .../src}/define.rs | 10 +- crates/rspack_swc_visitors/src/emotion.rs | 2 + crates/rspack_swc_visitors/src/import.rs | 70 +++++++ crates/rspack_swc_visitors/src/lib.rs | 17 ++ .../src}/provide.rs | 6 +- .../src}/react.rs | 61 +++++- .../src}/relay.rs | 45 ++++- crates/rspack_testing/src/loader.rs | 14 +- examples/builtin-swc-loader/rspack.config.js | 1 - examples/emotion/rspack.config.js | 28 ++- packages/rspack-cli/src/rspack-cli.ts | 2 +- packages/rspack/src/builtin-loader/index.ts | 1 + .../rspack/src/builtin-loader/swc/emotion.ts | 46 +++++ .../rspack/src/builtin-loader/swc/index.ts | 11 ++ .../src/builtin-loader/swc/pluginImport.ts | 48 +++++ .../rspack/src/builtin-loader/swc/react.ts | 10 + .../rspack/src/builtin-loader/swc/relay.ts | 60 ++++++ packages/rspack/src/builtin-plugin/index.ts | 142 ++----------- packages/rspack/src/config/adapter.ts | 4 +- packages/rspack/src/config/adapterRuleUse.ts | 72 +++++-- packages/rspack/tests/ConfigCase.template.ts | 6 +- .../builtin-swc-loader/plugin-import/index.js | 7 + .../plugin-import/src/bar/cc-dd/index.js | 1 + .../plugin-import/src/bar/cc-dd/style.css | 0 .../plugin-import/src/foo/aa-bb/index.js | 1 + .../src/foo/aa-bb/style/index.js | 1 + .../plugin-import/webpack.config.js | 34 ++++ .../react-refresh-false/index.jsx | 5 + .../react-refresh-false/webpack.config.js | 17 ++ .../react-runtime-automic/index.jsx | 5 + .../react-runtime-automic/webpack.config.js | 17 ++ .../react-runtime-classic/index.jsx | 7 + .../react-runtime-classic/webpack.config.js | 17 ++ .../custom/MyComponent.graphql.ts | 1 + .../relay-config-custom/index.jsx | 11 ++ .../relay-config-custom/webpack.config.js | 18 ++ .../custom/MyComponent.graphql.ts | 1 + .../relay-config-js/index.jsx | 11 ++ .../relay-config-js/relay.config.js | 4 + .../relay-config-js/webpack.config.js | 15 ++ .../custom/MyComponent.graphql.ts | 1 + .../relay-config-json/index.jsx | 11 ++ .../relay-config-json/relay.config.json | 4 + .../relay-config-json/webpack.config.js | 15 ++ .../__generated__/MyComponent.graphql.js | 1 + .../relay-default/index.jsx | 11 ++ .../relay-default/webpack.config.js | 15 ++ .../__generated__/MyComponent.graphql.js | 1 + .../builtin-swc-loader/relay-json/index.jsx | 11 ++ .../relay-json/package.json | 5 + .../relay-json/webpack.config.js | 15 ++ .../custom/MyComponent.graphql.ts | 1 + .../relay-webpack-require/index.js | 15 ++ .../relay-webpack-require/mock.js | 3 + .../relay-webpack-require/relay.config.js | 4 + .../relay-webpack-require/webpack.config.js | 25 +++ .../watch/_t2/directory/tempfile1.txt | 1 + .../watch/_t2/directory/tempfile2.txt | 1 + 79 files changed, 1190 insertions(+), 401 deletions(-) create mode 100644 crates/rspack_loader_swc/src/options.rs create mode 100644 crates/rspack_loader_swc/src/transformer.rs delete mode 100644 crates/rspack_plugin_javascript/src/visitors/plugin_import.rs create mode 100644 crates/rspack_swc_visitors/Cargo.toml rename crates/{rspack_plugin_javascript/src/visitors/swc_visitor => rspack_swc_visitors/src}/define.rs (87%) create mode 100644 crates/rspack_swc_visitors/src/emotion.rs create mode 100644 crates/rspack_swc_visitors/src/import.rs create mode 100644 crates/rspack_swc_visitors/src/lib.rs rename crates/{rspack_plugin_javascript/src/visitors/swc_visitor => rspack_swc_visitors/src}/provide.rs (98%) rename crates/{rspack_plugin_javascript/src/visitors/swc_visitor => rspack_swc_visitors/src}/react.rs (65%) rename crates/{rspack_plugin_javascript/src/visitors => rspack_swc_visitors/src}/relay.rs (82%) create mode 100644 packages/rspack/src/builtin-loader/index.ts create mode 100644 packages/rspack/src/builtin-loader/swc/emotion.ts create mode 100644 packages/rspack/src/builtin-loader/swc/index.ts create mode 100644 packages/rspack/src/builtin-loader/swc/pluginImport.ts create mode 100644 packages/rspack/src/builtin-loader/swc/react.ts create mode 100644 packages/rspack/src/builtin-loader/swc/relay.ts create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/index.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/bar/cc-dd/index.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/bar/cc-dd/style.css create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/index.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/style/index.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/webpack.config.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/index.jsx create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/webpack.config.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/index.jsx create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/webpack.config.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/index.jsx create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/webpack.config.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/custom/MyComponent.graphql.ts create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/index.jsx create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/webpack.config.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/custom/MyComponent.graphql.ts create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/index.jsx create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/relay.config.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/webpack.config.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/custom/MyComponent.graphql.ts create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/index.jsx create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/relay.config.json create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/webpack.config.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-default/__generated__/MyComponent.graphql.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-default/index.jsx create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-default/webpack.config.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-json/__generated__/MyComponent.graphql.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-json/index.jsx create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-json/package.json create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-json/webpack.config.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/custom/MyComponent.graphql.ts create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/index.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/mock.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/relay.config.js create mode 100644 packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/webpack.config.js create mode 100644 packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile1.txt create mode 100644 packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile2.txt diff --git a/Cargo.lock b/Cargo.lock index 01020bd2459a..650ed047ca3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2419,11 +2419,11 @@ dependencies = [ "rspack_plugin_wasm", "rspack_plugin_worker", "rspack_regex", + "rspack_swc_visitors", "rustc-hash", "serde", "serde_json", "swc_core", - "swc_plugin_import", "tokio", "tracing", ] @@ -2464,6 +2464,7 @@ dependencies = [ "rspack_loader_runner", "rspack_regex", "rspack_sources", + "rspack_swc_visitors", "rspack_util", "rustc-hash", "serde", @@ -2617,15 +2618,20 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "either", "indexmap 1.9.3", "rspack_core", "rspack_error", "rspack_loader_runner", + "rspack_swc_visitors", "rspack_testing", "serde", "serde_json", "swc_config", "swc_core", + "swc_emotion", + "swc_plugin_import", + "xxhash-rust", ] [[package]] @@ -2862,6 +2868,7 @@ dependencies = [ "rspack_hash", "rspack_identifier", "rspack_regex", + "rspack_swc_visitors", "rspack_testing", "rustc-hash", "serde_json", @@ -3077,6 +3084,19 @@ dependencies = [ "substring", ] +[[package]] +name = "rspack_swc_visitors" +version = "0.1.0" +dependencies = [ + "indexmap 1.9.3", + "once_cell", + "regex", + "serde", + "swc_core", + "swc_emotion", + "swc_plugin_import", +] + [[package]] name = "rspack_testing" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 2f688daf1b3d..77e46d4f9b5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ tracing-subscriber = { version = "0.3.17" } url = { version = "2.4.0" } urlencoding = { version = "2.1.2" } ustr = { version = "0.9.0" } +xxhash-rust = { version = "0.8.6" } # Pinned napi = { version = "=2.12.5" } diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 016cf0fab54b..0364982c4886 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -797,9 +797,7 @@ export interface RawModuleRule { } /** - * `loader` is for js side loader, `builtin_loader` is for rust side loader, - * which is mapped to real rust side loader by [get_builtin_loader]. - * + * `loader` is for both JS and Rust loaders. * `options` is * - a `None` on rust side and handled by js side `getOptions` when * using with `loader`. diff --git a/crates/rspack_binding_options/Cargo.toml b/crates/rspack_binding_options/Cargo.toml index cfd04e8cb0d4..9c9bd76b1a1d 100644 --- a/crates/rspack_binding_options/Cargo.toml +++ b/crates/rspack_binding_options/Cargo.toml @@ -41,6 +41,7 @@ rspack_plugin_swc_js_minimizer = { path = "../rspack_plugin_swc_js_mini rspack_plugin_wasm = { path = "../rspack_plugin_wasm" } rspack_plugin_worker = { path = "../rspack_plugin_worker" } rspack_regex = { path = "../rspack_regex" } +rspack_swc_visitors = { path = "../rspack_swc_visitors" } anyhow = { workspace = true, features = ["backtrace"] } async-trait = { workspace = true } @@ -53,6 +54,5 @@ rustc-hash = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } swc_core = { workspace = true, default-features = false, features = ["ecma_transforms_react"] } -swc_plugin_import = { path = "../swc_plugin_import" } tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "test-util", "parking_lot"] } tracing = { workspace = true } diff --git a/crates/rspack_binding_options/src/options/raw_builtins/raw_to_be_deprecated.rs b/crates/rspack_binding_options/src/options/raw_builtins/raw_to_be_deprecated.rs index cb5e1fbca4e0..33fd56731451 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/raw_to_be_deprecated.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/raw_to_be_deprecated.rs @@ -1,17 +1,17 @@ use std::{path::PathBuf, str::FromStr}; use napi_derive::napi; -use rspack_core::{ - Builtins, DecoratorOptions, PluginExt, PresetEnv, ReactOptions, RelayConfig, RelayLanguageConfig, -}; +use rspack_core::{Builtins, DecoratorOptions, PluginExt, PresetEnv}; use rspack_error::internal_error; use rspack_plugin_css::{ plugin::{CssConfig, LocalIdentName, LocalsConvention, ModulesConfig}, CssPlugin, }; use rspack_plugin_dev_friendly_split_chunks::DevFriendlySplitChunksPlugin; +use rspack_swc_visitors::{ + CustomTransform, ImportOptions, ReactOptions, RelayLanguageConfig, RelayOptions, StyleConfig, +}; use serde::{Deserialize, Serialize}; -use swc_plugin_import::{CustomTransform, PluginImportConfig, StyleConfig}; #[derive(Deserialize, Debug, Serialize, Default, Clone)] #[serde(rename_all = "camelCase")] @@ -70,7 +70,7 @@ pub struct RawPluginImportConfig { pub ignore_style_component: Option>, } -impl From for PluginImportConfig { +impl From for ImportOptions { fn from(plugin_import: RawPluginImportConfig) -> Self { let RawPluginImportConfig { library_name, @@ -154,7 +154,7 @@ impl From for ReactOptions { Some(Runtime::Automatic) }; - ReactOptions { + Self { runtime, import_source: value.import_source, pragma: value.pragma, @@ -177,7 +177,7 @@ pub struct RawRelayConfig { pub language: String, } -impl From for RelayConfig { +impl From for RelayOptions { fn from(raw_config: RawRelayConfig) -> Self { Self { artifact_directory: raw_config.artifact_directory.map(PathBuf::from), diff --git a/crates/rspack_binding_options/src/options/raw_module/mod.rs b/crates/rspack_binding_options/src/options/raw_module/mod.rs index 52c4a121b0fd..60024b410ada 100644 --- a/crates/rspack_binding_options/src/options/raw_module/mod.rs +++ b/crates/rspack_binding_options/src/options/raw_module/mod.rs @@ -35,20 +35,20 @@ pub fn get_builtin_loader(builtin: &str, options: Option<&str>) -> BoxLoader { } if builtin.starts_with(SWC_LOADER_IDENTIFIER) { - return Arc::new(rspack_loader_swc::SwcLoader::new( - serde_json::from_str(options.unwrap_or("{}")).unwrap_or_else(|e| { - panic!("Could not parse builtin:swc-loader options:{options:?},error: {e:?}") - }), - Some(builtin.into()), - )); + return Arc::new( + rspack_loader_swc::SwcLoader::new( + serde_json::from_str(options.unwrap_or("{}")).unwrap_or_else(|e| { + panic!("Could not parse builtin:swc-loader options:{options:?},error: {e:?}") + }), + ) + .with_identifier(builtin.into()), + ); } unreachable!("Unexpected builtin loader: {builtin}") } -/// `loader` is for js side loader, `builtin_loader` is for rust side loader, -/// which is mapped to real rust side loader by [get_builtin_loader]. -/// +/// `loader` is for both JS and Rust loaders. /// `options` is /// - a `None` on rust side and handled by js side `getOptions` when /// using with `loader`. @@ -59,7 +59,6 @@ pub fn get_builtin_loader(builtin: &str, options: Option<&str>) -> BoxLoader { #[serde(rename_all = "camelCase")] #[napi(object)] pub struct RawModuleRuleUse { - #[serde(skip_deserializing)] pub loader: String, pub options: Option, } diff --git a/crates/rspack_core/Cargo.toml b/crates/rspack_core/Cargo.toml index 078294cc2f52..4ca90bfb2a27 100644 --- a/crates/rspack_core/Cargo.toml +++ b/crates/rspack_core/Cargo.toml @@ -38,6 +38,7 @@ rspack_identifier = { path = "../rspack_identifier" } rspack_loader_runner = { path = "../rspack_loader_runner" } rspack_regex = { path = "../rspack_regex" } rspack_sources = { workspace = true } +rspack_swc_visitors = { path = "../rspack_swc_visitors" } rspack_util = { path = "../rspack_util" } rustc-hash = { workspace = true } serde = { workspace = true } diff --git a/crates/rspack_core/src/options/builtins.rs b/crates/rspack_core/src/options/builtins.rs index c5eee1f09b11..3dada906cdf5 100644 --- a/crates/rspack_core/src/options/builtins.rs +++ b/crates/rspack_core/src/options/builtins.rs @@ -1,15 +1,13 @@ use std::fmt::Debug; -use std::{collections::HashMap, fmt::Display, path::PathBuf}; +use std::{fmt::Display, path::PathBuf}; use glob::Pattern as GlobPattern; use rspack_error::Result; -use swc_core::ecma::transforms::react::Runtime; -use swc_plugin_import::PluginImportConfig; +pub use rspack_swc_visitors::{Define, Provide}; +use rspack_swc_visitors::{EmotionOptions, ImportOptions, ReactOptions, RelayOptions}; use crate::{ApplyContext, AssetInfo, CompilerOptions, Plugin, PluginContext}; -pub type Define = HashMap; - #[derive(Debug)] pub struct DefinePlugin { options: Define, @@ -36,8 +34,6 @@ impl Plugin for DefinePlugin { } } -pub type Provide = HashMap>; - #[derive(Debug)] pub struct ProvidePlugin { options: Provide, @@ -64,19 +60,6 @@ impl Plugin for ProvidePlugin { } } -#[derive(Debug, Clone, Default)] -pub struct ReactOptions { - pub runtime: Option, - pub import_source: Option, - pub pragma: Option, - pub pragma_frag: Option, - pub throw_if_namespace: Option, - pub development: Option, - pub use_builtins: Option, - pub use_spread: Option, - pub refresh: Option, -} - #[derive(Debug, Clone, Default)] pub struct DecoratorOptions { // https://swc.rs/docs/configuration/compilation#jsctransformlegacydecorator @@ -151,11 +134,11 @@ pub struct Builtins { // TODO: remove this when drop support for builtin options (0.6.0) pub no_emit_assets: bool, // TODO: migrate to builtin:swc-loader - pub emotion: Option, + pub emotion: Option, // TODO: migrate to builtin:swc-loader - pub plugin_import: Option>, + pub plugin_import: Option>, // TODO: migrate to builtin:swc-loader - pub relay: Option, + pub relay: Option, } #[derive(Debug, Clone)] @@ -212,22 +195,3 @@ pub struct PresetEnv { pub mode: Option, pub core_js: Option, } - -#[derive(Debug, Default, Clone)] -pub struct RelayConfig { - pub artifact_directory: Option, - pub language: RelayLanguageConfig, -} - -#[derive(Copy, Clone, Debug)] -pub enum RelayLanguageConfig { - JavaScript, - TypeScript, - Flow, -} - -impl Default for RelayLanguageConfig { - fn default() -> Self { - Self::Flow - } -} diff --git a/crates/rspack_hash/Cargo.toml b/crates/rspack_hash/Cargo.toml index c5477e364c2b..9a3f27e10825 100644 --- a/crates/rspack_hash/Cargo.toml +++ b/crates/rspack_hash/Cargo.toml @@ -10,4 +10,4 @@ version = "0.1.0" data-encoding = { version = "2.4.0" } md4 = "0.10.2" smol_str = { version = "*" } -xxhash-rust = { version = "0.8.6", features = ["xxh3"] } +xxhash-rust = { workspace = true, features = ["xxh3"] } diff --git a/crates/rspack_loader_swc/Cargo.toml b/crates/rspack_loader_swc/Cargo.toml index 86e871ff1333..a40371847a66 100644 --- a/crates/rspack_loader_swc/Cargo.toml +++ b/crates/rspack_loader_swc/Cargo.toml @@ -10,9 +10,11 @@ version = "0.1.0" [dependencies] anyhow = { workspace = true } async-trait = { workspace = true } +either = "1" rspack_core = { path = "../rspack_core" } rspack_error = { path = "../rspack_error" } rspack_loader_runner = { path = "../rspack_loader_runner" } +rspack_swc_visitors = { path = "../rspack_swc_visitors" } serde = { workspace = true, features = ["derive"] } serde_json = "1.0.100" swc_config = { workspace = true } @@ -24,6 +26,9 @@ swc_core = { workspace = true, features = [ # "plugin_transform_host_native_filesystem_cache", # "plugin_transform_host_native", ] } +swc_emotion = { workspace = true } +swc_plugin_import = { path = "../swc_plugin_import" } +xxhash-rust = { workspace = true, features = ["xxh32"] } [dev-dependencies] indexmap = { workspace = true } diff --git a/crates/rspack_loader_swc/src/lib.rs b/crates/rspack_loader_swc/src/lib.rs index 18727e388e3f..ef186a0c4886 100644 --- a/crates/rspack_loader_swc/src/lib.rs +++ b/crates/rspack_loader_swc/src/lib.rs @@ -6,141 +6,43 @@ use std::sync::Arc; use rspack_core::{rspack_sources::SourceMap, LoaderRunnerContext, Mode}; use rspack_error::{internal_error, Diagnostic, Result}; use rspack_loader_runner::{Identifiable, Identifier, Loader, LoaderContext}; -use serde::Deserialize; -use swc_config::config_types::{BoolConfig, MergingOption}; -use swc_config::merge::Merge; -use swc_core::base::config::{ - Config, ErrorConfig, FileMatcher, InputSourceMap, IsModule, JscConfig, ModuleConfig, Options, - SourceMapsConfig, TransformConfig, +use swc_config::{config_types::MergingOption, merge::Merge}; +use swc_core::base::{ + config::{InputSourceMap, TransformConfig}, + try_with_handler, Compiler, +}; +use swc_core::common::{ + comments::SingleThreadedComments, FileName, FilePathMapping, Mark, GLOBALS, }; -use swc_core::base::{try_with_handler, Compiler}; -use swc_core::common::comments::SingleThreadedComments; -use swc_core::common::{FileName, FilePathMapping, GLOBALS}; use swc_core::ecma::transforms::base::pass::noop; +use xxhash_rust::xxh32::xxh32; -pub const SOURCE_MAP_INLINE: &str = "inline"; - -#[derive(Debug, Default, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct SwcLoaderJsOptions { - #[serde(default)] - pub source_maps: Option, - - pub source_map: Option, - #[serde(default)] - pub env: Option, - - #[serde(default)] - pub test: Option, - - #[serde(default)] - pub exclude: Option, - - #[serde(default)] - pub jsc: JscConfig, - - #[serde(default)] - pub module: Option, - - #[serde(default)] - pub minify: BoolConfig, +mod options; +mod transformer; - #[serde(default)] - pub input_source_map: Option, - - #[serde(default)] - pub inline_sources_content: BoolConfig, - - #[serde(default)] - pub emit_source_map_columns: BoolConfig, - - #[serde(default)] - pub error: ErrorConfig, - - #[serde(default)] - pub is_module: Option, - - #[serde(rename = "$schema")] - pub schema: Option, -} - -#[derive(Debug, Default, Deserialize)] -#[serde(rename_all = "camelCase", default)] -struct SwcLoaderOptions { - pub config: Option, - pub source_maps: Option, -} - -impl From for Options { - fn from(value: SwcLoaderJsOptions) -> Self { - let SwcLoaderJsOptions { - source_maps, - source_map, - env, - test, - exclude, - jsc, - module, - minify, - input_source_map, - inline_sources_content, - emit_source_map_columns, - error, - is_module, - schema, - } = value; - let mut source_maps: Option = source_maps; - if source_maps.is_none() && source_map.is_some() { - source_maps = source_map - } - if let Some(SourceMapsConfig::Str(str)) = &source_maps { - if str == SOURCE_MAP_INLINE { - source_maps = Some(SourceMapsConfig::Bool(true)) - } - } - Options { - config: Config { - env, - test, - exclude, - jsc, - module, - minify, - input_source_map, - source_maps, - inline_sources_content, - emit_source_map_columns, - error, - is_module, - schema, - }, - ..Default::default() - } - } -} +use options::SwcCompilerOptionsWithAdditional; +pub use options::SwcLoaderJsOptions; #[derive(Debug)] pub struct SwcLoader { - options: Options, identifier: Identifier, + options_with_additional: SwcCompilerOptionsWithAdditional, } impl SwcLoader { - /// Panics: - /// Panics if `identifier` passed in is not starting with `builtin:swc-loader`. - pub fn new(options: SwcLoaderJsOptions, identifier: Option) -> Self { - // TODO: should stringify loader options to identifier - Self::validate_identifier(&identifier); + pub fn new(options: SwcLoaderJsOptions) -> Self { Self { - options: Options::from(options), - identifier: identifier.unwrap_or(SWC_LOADER_IDENTIFIER.into()), + identifier: SWC_LOADER_IDENTIFIER.into(), + options_with_additional: options.into(), } } - fn validate_identifier(identifier: &Option) { - if let Some(i) = identifier { - assert!(i.starts_with(SWC_LOADER_IDENTIFIER)); - } + /// Panics: + /// Panics if `identifier` passed in is not starting with `builtin:swc-loader`. + pub fn with_identifier(mut self, identifier: Identifier) -> Self { + assert!(identifier.starts_with(SWC_LOADER_IDENTIFIER)); + self.identifier = identifier; + self } } @@ -154,15 +56,15 @@ impl Loader for SwcLoader { return Err(internal_error!("Content should be available")) }; - let c: Compiler = Compiler::new(Arc::from(swc_core::common::SourceMap::new( + let c = Compiler::new(Arc::from(swc_core::common::SourceMap::new( FilePathMapping::empty(), ))); let default_development = matches!(loader_context.context.options.mode, Mode::Development); - let mut options = self.options.clone(); - if options.config.jsc.transform.as_ref().is_some() { + let mut swc_options = self.options_with_additional.swc_options.clone(); + if swc_options.config.jsc.transform.as_ref().is_some() { let mut transform = TransformConfig::default(); transform.react.development = Some(default_development); - options + swc_options .config .jsc .transform @@ -170,11 +72,11 @@ impl Loader for SwcLoader { } if let Some(pre_source_map) = std::mem::take(&mut loader_context.source_map) { if let Ok(source_map) = pre_source_map.to_json() { - options.config.input_source_map = Some(InputSourceMap::Str(source_map)) + swc_options.config.input_source_map = Some(InputSourceMap::Str(source_map)) } } - if options.config.jsc.experimental.plugins.is_some() { + if swc_options.config.jsc.experimental.plugins.is_some() { loader_context.emit_diagnostic(Diagnostic::warn( SWC_LOADER_IDENTIFIER.to_string(), "Experimental plugins are not currently supported.".to_string(), @@ -186,19 +88,43 @@ impl Loader for SwcLoader { GLOBALS.set(&Default::default(), || { try_with_handler(c.cm.clone(), Default::default(), |handler| { c.run(|| { + let top_level_mark = Mark::new(); + let unresolved_mark = Mark::new(); + swc_options.top_level_mark = Some(top_level_mark); + swc_options.unresolved_mark = Some(unresolved_mark); + let source = content.try_into_string()?; + let rspack_options = &*loader_context.context.options; + let source_content_hash = self + .options_with_additional + .rspack_experiments + .emotion + .as_ref() + .map(|_| xxh32(source.as_bytes(), 0)); + let fm = c .cm - .new_source_file(FileName::Real(resource_path), content.try_into_string()?); + .new_source_file(FileName::Real(resource_path.clone()), source); let comments = SingleThreadedComments::default(); let out = c.process_js_with_custom_pass( fm, None, handler, - &options, + &swc_options, comments, - |_a| noop(), - |_a| noop(), + |_| { + transformer::transform( + &resource_path, + rspack_options, + Some(c.comments()), + top_level_mark, + unresolved_mark, + c.cm.clone(), + source_content_hash, + &self.options_with_additional.rspack_experiments, + ) + }, + |_| noop(), )?; loader_context.content = Some(out.code.into()); loader_context.source_map = out.map.map(|m| SourceMap::from_json(&m)).transpose()?; diff --git a/crates/rspack_loader_swc/src/options.rs b/crates/rspack_loader_swc/src/options.rs new file mode 100644 index 000000000000..54a9d08c0cc1 --- /dev/null +++ b/crates/rspack_loader_swc/src/options.rs @@ -0,0 +1,147 @@ +use rspack_swc_visitors::{ + EmotionOptions, ImportOptions, RawEmotionOptions, RawImportOptions, RawReactOptions, + RawRelayOptions, ReactOptions, RelayOptions, +}; +use serde::Deserialize; +use swc_config::config_types::BoolConfig; +use swc_core::base::config::{ + Config, ErrorConfig, FileMatcher, InputSourceMap, IsModule, JscConfig, ModuleConfig, Options, + SourceMapsConfig, +}; + +#[derive(Default, Deserialize, Debug)] +#[serde(rename_all = "camelCase", default)] +pub struct RawRspackExperiments { + pub relay: Option, + pub react: Option, + pub import: Option>, + pub emotion: Option, +} + +#[derive(Default, Debug)] +pub(crate) struct RspackExperiments { + pub(crate) relay: Option, + pub(crate) react: Option, + pub(crate) import: Option>, + pub(crate) emotion: Option, +} + +impl From for RspackExperiments { + fn from(value: RawRspackExperiments) -> Self { + Self { + relay: value.relay.map(|v| v.into()), + react: value.react.map(|v| v.into()), + import: value + .import + .map(|i| i.into_iter().map(|v| v.into()).collect()), + emotion: value.emotion, + } + } +} + +#[derive(Debug, Default, Deserialize)] +#[serde(rename_all = "camelCase", default)] +pub struct SwcLoaderJsOptions { + #[serde(default)] + pub source_maps: Option, + + pub source_map: Option, + #[serde(default)] + pub env: Option, + + #[serde(default)] + pub test: Option, + + #[serde(default)] + pub exclude: Option, + + #[serde(default)] + pub jsc: JscConfig, + + #[serde(default)] + pub module: Option, + + #[serde(default)] + pub minify: BoolConfig, + + #[serde(default)] + pub input_source_map: Option, + + #[serde(default)] + pub inline_sources_content: BoolConfig, + + #[serde(default)] + pub emit_source_map_columns: BoolConfig, + + #[serde(default)] + pub error: ErrorConfig, + + #[serde(default)] + pub is_module: Option, + + #[serde(rename = "$schema")] + pub schema: Option, + + #[serde(default)] + pub rspack_experiments: Option, +} + +#[derive(Debug)] +pub(crate) struct SwcCompilerOptionsWithAdditional { + pub(crate) swc_options: Options, + pub(crate) rspack_experiments: RspackExperiments, +} + +const SOURCE_MAP_INLINE: &str = "inline"; + +impl From for SwcCompilerOptionsWithAdditional { + fn from(value: SwcLoaderJsOptions) -> Self { + let SwcLoaderJsOptions { + source_maps, + source_map, + env, + test, + exclude, + jsc, + module, + minify, + input_source_map, + inline_sources_content, + emit_source_map_columns, + error, + is_module, + schema, + rspack_experiments, + } = value; + let mut source_maps: Option = source_maps; + if source_maps.is_none() && source_map.is_some() { + source_maps = source_map + } + if let Some(SourceMapsConfig::Str(str)) = &source_maps { + if str == SOURCE_MAP_INLINE { + source_maps = Some(SourceMapsConfig::Bool(true)) + } + } + SwcCompilerOptionsWithAdditional { + swc_options: Options { + config: Config { + env, + test, + exclude, + jsc, + module, + minify, + input_source_map, + source_maps, + inline_sources_content, + emit_source_map_columns, + error, + is_module, + schema, + }, + ..Default::default() + }, + rspack_experiments: rspack_experiments.unwrap_or_default().into(), + } + } +} diff --git a/crates/rspack_loader_swc/src/transformer.rs b/crates/rspack_loader_swc/src/transformer.rs new file mode 100644 index 000000000000..1e7048740a21 --- /dev/null +++ b/crates/rspack_loader_swc/src/transformer.rs @@ -0,0 +1,79 @@ +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use either::Either; +use rspack_core::CompilerOptions; +use swc_core::common::{chain, comments::Comments, Mark, SourceMap}; +use swc_core::ecma::{ + transforms::base::pass::{noop, Optional}, + visit::Fold, +}; + +use crate::options::RspackExperiments; + +macro_rules! either { + ($config:expr, $f:expr) => { + if let Some(config) = &$config { + Either::Left($f(config)) + } else { + Either::Right(noop()) + } + }; + ($config:expr, $f:expr, $enabled:expr) => { + if $enabled { + either!($config, $f) + } else { + Either::Right(noop()) + } + }; +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn transform<'a>( + resource_path: &'a Path, + rspack_options: &'a CompilerOptions, + comments: Option<&'a dyn Comments>, + top_level_mark: Mark, + unresolved_mark: Mark, + cm: Arc, + content_hash: Option, + rspack_experiments: &'a RspackExperiments, +) -> impl Fold + 'a { + use rspack_swc_visitors::EmotionOptions; + + chain!( + either!(rspack_experiments.react, |options| { + rspack_swc_visitors::react(top_level_mark, comments, &cm, options, unresolved_mark) + }), + Optional::new( + rspack_swc_visitors::fold_react_refresh(unresolved_mark), + rspack_experiments + .react + .as_ref() + .and_then(|v| v.refresh) + .unwrap_or_default() + ), + either!(rspack_experiments.emotion, |options: &EmotionOptions| { + // SAFETY: Source content hash should always available if emotion is turned on. + let content_hash = content_hash.expect("Content hash should be available"); + rspack_swc_visitors::emotion( + options.clone(), + resource_path, + content_hash, + cm.clone(), + comments, + ) + }), + either!(rspack_experiments.relay, |options| { + rspack_swc_visitors::relay( + options, + resource_path, + PathBuf::from(AsRef::::as_ref(&rspack_options.context)), + unresolved_mark, + ) + }), + either!(rspack_experiments.import, |options| { + rspack_swc_visitors::import(options) + }), + ) +} diff --git a/crates/rspack_loader_swc/tests/fixtures.rs b/crates/rspack_loader_swc/tests/fixtures.rs index c2ca3dc687f8..de50e0c32d2d 100644 --- a/crates/rspack_loader_swc/tests/fixtures.rs +++ b/crates/rspack_loader_swc/tests/fixtures.rs @@ -27,7 +27,7 @@ async fn loader_test(actual: impl AsRef, expected: impl AsRef) { json!(null), )]); let (result, _) = run_loaders( - &[Arc::new(SwcLoader::new(options, None)) as Arc>], + &[Arc::new(SwcLoader::new(options)) as Arc>], &ResourceData::new(actual_path.to_string_lossy().to_string(), actual_path), &[], CompilerContext { diff --git a/crates/rspack_plugin_javascript/Cargo.toml b/crates/rspack_plugin_javascript/Cargo.toml index 1f12181f0cfb..b6bf7c3ede5c 100644 --- a/crates/rspack_plugin_javascript/Cargo.toml +++ b/crates/rspack_plugin_javascript/Cargo.toml @@ -24,6 +24,7 @@ rspack_error = { path = "../rspack_error" } rspack_hash = { path = "../rspack_hash" } rspack_identifier = { path = "../rspack_identifier" } rspack_regex = { path = "../rspack_regex" } +rspack_swc_visitors = { path = "../rspack_swc_visitors" } rustc-hash = { workspace = true } serde_json = { workspace = true } sourcemap = "6.2.3" @@ -44,4 +45,4 @@ swc_emotion = { workspace = true } swc_node_comments = { workspace = true } swc_plugin_import = { path = "../swc_plugin_import" } url = "2.4.0" -xxhash-rust = { version = "0.8.6", features = ["xxh32"] } +xxhash-rust = { workspace = true, features = ["xxh32"] } diff --git a/crates/rspack_plugin_javascript/src/visitors/mod.rs b/crates/rspack_plugin_javascript/src/visitors/mod.rs index 808f4fe8dc3c..6989e2d185f5 100644 --- a/crates/rspack_plugin_javascript/src/visitors/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/mod.rs @@ -7,21 +7,14 @@ use swc_core::common::comments::Comments; use xxhash_rust::xxh32::xxh32; mod clear_mark; -mod plugin_import; - use rspack_core::{BuildInfo, ModuleType}; use swc_core::ecma::transforms::base::Assumptions; -pub mod relay; pub mod swc_visitor; use rspack_core::{ast::javascript::Ast, CompilerOptions, ResourceData}; use rspack_error::Result; use swc_core::common::chain; use swc_core::ecma::parser::Syntax; use swc_core::ecma::transforms::base::pass::{noop, Optional}; -use swc_emotion::EmotionOptions; - -use crate::visitors::plugin_import::plugin_import; -use crate::visitors::relay::relay; macro_rules! either { ($config: expr, $f: expr) => { @@ -54,7 +47,7 @@ pub fn run_before_pass( ast.transform_with_handler(cm.clone(), |_handler, program, context| { let top_level_mark = context.top_level_mark; let unresolved_mark = context.unresolved_mark; - let comments = None; + let comments: Option<&dyn Comments> = None; let mut assumptions = Assumptions::default(); if syntax.typescript() { @@ -83,7 +76,7 @@ pub fn run_before_pass( syntax.typescript() ), Optional::new( - swc_visitor::react( + rspack_swc_visitors::react( top_level_mark, comments, &cm, @@ -93,7 +86,7 @@ pub fn run_before_pass( should_transform_by_react ), Optional::new( - swc_visitor::fold_react_refresh(unresolved_mark), + rspack_swc_visitors::fold_react_refresh(unresolved_mark), should_transform_by_react && options .builtins @@ -104,8 +97,8 @@ pub fn run_before_pass( ), either!( options.builtins.emotion, - |emotion_options: &EmotionOptions| { - swc_emotion::emotion( + |emotion_options: &rspack_swc_visitors::EmotionOptions| { + rspack_swc_visitors::emotion( emotion_options.clone(), &resource_data.resource_path, xxh32(source.as_bytes(), 0), @@ -115,22 +108,24 @@ pub fn run_before_pass( } ), either!(options.builtins.relay, |relay_option| { - relay( + rspack_swc_visitors::relay( relay_option, resource_data.resource_path.as_path(), PathBuf::from(AsRef::::as_ref(&options.context)), unresolved_mark, ) }), - plugin_import(options.builtins.plugin_import.as_ref()), + either!(options.builtins.plugin_import, |options| { + rspack_swc_visitors::import(options) + }), // enable if configurable // swc_visitor::const_modules(cm, globals), Optional::new( - swc_visitor::define(&options.builtins.define), + rspack_swc_visitors::define(&options.builtins.define), !options.builtins.define.is_empty() ), Optional::new( - swc_visitor::provide_builtin(&options.builtins.provide, unresolved_mark), + rspack_swc_visitors::provide_builtin(&options.builtins.provide, unresolved_mark), !options.builtins.provide.is_empty() ), Optional::new( diff --git a/crates/rspack_plugin_javascript/src/visitors/plugin_import.rs b/crates/rspack_plugin_javascript/src/visitors/plugin_import.rs deleted file mode 100644 index c43471778ee5..000000000000 --- a/crates/rspack_plugin_javascript/src/visitors/plugin_import.rs +++ /dev/null @@ -1,11 +0,0 @@ -use either::Either; -use swc_core::ecma::{transforms::base::pass::noop, visit::Fold}; -use swc_plugin_import::PluginImportConfig; - -pub fn plugin_import(config: Option<&Vec>) -> impl Fold + '_ { - if let Some(config) = config { - Either::Left(swc_plugin_import::plugin_import(config)) - } else { - Either::Right(noop()) - } -} diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/compat.rs b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/compat.rs index 0e6b4f26cdc1..fae2d7d8e21b 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/compat.rs +++ b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/compat.rs @@ -1,6 +1,7 @@ use either::Either; use rspack_core::PresetEnv; -use swc_core::common::{chain, comments::SingleThreadedComments, pass::Optional, Mark}; +use swc_core::common::comments::Comments; +use swc_core::common::{chain, pass::Optional, Mark}; use swc_core::ecma::ast::EsVersion; use swc_core::ecma::preset_env as swc_ecma_preset_env; use swc_core::ecma::transforms::base::{feature::FeatureFlag, pass::noop, Assumptions}; @@ -11,7 +12,7 @@ fn compat_by_preset_env( preset_env_config: Option, unresolved_mark: Mark, assumptions: Assumptions, - comments: Option<&SingleThreadedComments>, + comments: Option<&dyn Comments>, ) -> impl Fold + '_ { if let Some(PresetEnv { mode, targets, core_js }) = preset_env_config && !targets.is_empty() { let core_js = if let Some(core_js) = &core_js && let Ok(core_js) = core_js.parse() { @@ -47,7 +48,7 @@ fn compat_by_es_version( es_version: Option, unresolved_mark: Mark, assumptions: Assumptions, - comments: Option<&SingleThreadedComments>, + comments: Option<&dyn Comments>, is_typescript: bool, ) -> impl Fold + '_ { if let Some(es_version) = es_version { @@ -153,7 +154,7 @@ pub fn compat( assumptions: Assumptions, _top_level_mark: Mark, unresolved_mark: Mark, - comments: Option<&SingleThreadedComments>, + comments: Option<&dyn Comments>, is_typescript: bool, ) -> impl Fold + '_ { chain!( diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/mod.rs b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/mod.rs index 47508336f75d..814a3d796d18 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/mod.rs @@ -7,15 +7,6 @@ pub use decorator::decorator; mod typescript; pub use typescript::typescript; -mod react; -pub use react::{fold_react_refresh, react}; - -mod define; -pub use define::define; - -mod provide; -pub use provide::provide_builtin; - mod compat; pub use compat::compat; diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/typescript.rs b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/typescript.rs index 90838010ae28..aac9a8a839fb 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/typescript.rs +++ b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/typescript.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use swc_core::common::{comments::SingleThreadedComments, Mark, SourceMap}; +use swc_core::common::{comments::Comments, Mark, SourceMap}; use swc_core::ecma::transforms::base::Assumptions; use swc_core::ecma::transforms::{ react::{default_pragma, default_pragma_frag}, @@ -11,7 +11,7 @@ use swc_core::ecma::visit::Fold; pub fn typescript<'a>( assumptions: Assumptions, top_level_mark: Mark, - comments: Option<&'a SingleThreadedComments>, + comments: Option<&'a dyn Comments>, cm: &Arc, ) -> impl Fold + 'a { /* let import_export_assign_config = match module { diff --git a/crates/rspack_swc_visitors/Cargo.toml b/crates/rspack_swc_visitors/Cargo.toml new file mode 100644 index 000000000000..89755bb232fa --- /dev/null +++ b/crates/rspack_swc_visitors/Cargo.toml @@ -0,0 +1,27 @@ +[package] +edition = "2021" +license = "MIT" +name = "rspack_swc_visitors" +repository = "https://github.com/web-infra-dev/rspack" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +indexmap = { workspace = true } +once_cell = { workspace = true } +regex = { workspace = true } +serde = { workspace = true, features = ["derive"] } +swc_core = { workspace = true, features = ["ecma_ast"] } +swc_emotion = { workspace = true } +swc_plugin_import = { path = "../swc_plugin_import" } + +[dev-dependencies] +swc_core = { workspace = true, features = [ + "ecma_ast", + "__visit", + "__common", + "__parser", + "__utils", + "ecma_preset_env", +] } diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/define.rs b/crates/rspack_swc_visitors/src/define.rs similarity index 87% rename from crates/rspack_plugin_javascript/src/visitors/swc_visitor/define.rs rename to crates/rspack_swc_visitors/src/define.rs index eaa18e25e638..253aa30a27cc 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/define.rs +++ b/crates/rspack_swc_visitors/src/define.rs @@ -1,7 +1,8 @@ +use std::collections::HashMap; use std::sync::Arc; -use rspack_core::Define; use swc_core::common::collections::AHashMap; +use swc_core::ecma::ast::EsVersion; use swc_core::ecma::parser::EsConfig; use swc_core::ecma::transforms::optimization::inline_globals2; use swc_core::ecma::utils::NodeIgnoringSpan; @@ -11,6 +12,9 @@ use swc_core::{ ecma::parser::{parse_file_as_expr, Syntax}, }; +pub type Define = HashMap; +pub type RawDefine = Define; + pub fn define(opts: &Define) -> impl Fold { let cm: Arc = Default::default(); let defs = opts @@ -21,7 +25,7 @@ pub fn define(opts: &Define) -> impl Fold { parse_file_as_expr( &fm, Syntax::Es(EsConfig::default()), - rspack_core::EsVersion::EsNext, + EsVersion::EsNext, None, &mut vec![], ) @@ -32,7 +36,7 @@ pub fn define(opts: &Define) -> impl Fold { parse_file_as_expr( &fm, Syntax::Es(EsConfig::default()), - rspack_core::EsVersion::EsNext, + EsVersion::EsNext, None, &mut vec![], ) diff --git a/crates/rspack_swc_visitors/src/emotion.rs b/crates/rspack_swc_visitors/src/emotion.rs new file mode 100644 index 000000000000..5786cebba409 --- /dev/null +++ b/crates/rspack_swc_visitors/src/emotion.rs @@ -0,0 +1,2 @@ +pub use swc_emotion::{emotion, EmotionOptions}; +pub type RawEmotionOptions = swc_emotion::EmotionOptions; diff --git a/crates/rspack_swc_visitors/src/import.rs b/crates/rspack_swc_visitors/src/import.rs new file mode 100644 index 000000000000..51bd3123909e --- /dev/null +++ b/crates/rspack_swc_visitors/src/import.rs @@ -0,0 +1,70 @@ +use serde::Deserialize; +pub use swc_plugin_import::plugin_import as import; +pub use swc_plugin_import::{CustomTransform, PluginImportConfig as ImportOptions, StyleConfig}; + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RawStyleConfig { + pub style_library_directory: Option, + pub custom: Option, + pub css: Option, + pub bool: Option, +} + +impl From for StyleConfig { + fn from(raw_style_config: RawStyleConfig) -> Self { + if let Some(style_library_directory) = raw_style_config.style_library_directory { + Self::StyleLibraryDirectory(style_library_directory) + } else if let Some(custom) = raw_style_config.custom { + Self::Custom(CustomTransform::Tpl(custom)) + } else if raw_style_config.css.is_some() { + Self::Css + } else if let Some(bool) = raw_style_config.bool { + Self::Bool(bool) + } else { + Self::None + } + } +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RawImportOptions { + pub library_name: String, + pub library_directory: Option, // default to `lib` + pub custom_name: Option, + pub custom_style_name: Option, // If this is set, `style` option will be ignored + pub style: Option, + pub camel_to_dash_component_name: Option, // default to true + pub transform_to_default_import: Option, + pub ignore_es_component: Option>, + pub ignore_style_component: Option>, +} + +impl From for ImportOptions { + fn from(plugin_import: RawImportOptions) -> Self { + let RawImportOptions { + library_name, + library_directory, + custom_name, + custom_style_name, + style, + camel_to_dash_component_name, + transform_to_default_import, + ignore_es_component, + ignore_style_component, + } = plugin_import; + + Self { + library_name, + library_directory, + custom_name: custom_name.map(CustomTransform::Tpl), + custom_style_name: custom_style_name.map(CustomTransform::Tpl), + style: style.map(Into::into), + camel_to_dash_component_name, + transform_to_default_import, + ignore_es_component, + ignore_style_component, + } + } +} diff --git a/crates/rspack_swc_visitors/src/lib.rs b/crates/rspack_swc_visitors/src/lib.rs new file mode 100644 index 000000000000..cdf9c6643cc3 --- /dev/null +++ b/crates/rspack_swc_visitors/src/lib.rs @@ -0,0 +1,17 @@ +mod react; +pub use react::{fold_react_refresh, react, RawReactOptions, ReactOptions}; + +mod define; +pub use define::{define, Define, RawDefine}; + +mod provide; +pub use provide::{provide_builtin, Provide, RawProvide}; + +mod relay; +pub use relay::{relay, RawRelayOptions, RelayLanguageConfig, RelayOptions}; + +mod import; +pub use import::{import, CustomTransform, ImportOptions, RawImportOptions, StyleConfig}; + +mod emotion; +pub use emotion::{emotion, EmotionOptions, RawEmotionOptions}; diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/provide.rs b/crates/rspack_swc_visitors/src/provide.rs similarity index 98% rename from crates/rspack_plugin_javascript/src/visitors/swc_visitor/provide.rs rename to crates/rspack_swc_visitors/src/provide.rs index f2e6eff34217..27b4d9a686e7 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/provide.rs +++ b/crates/rspack_swc_visitors/src/provide.rs @@ -1,5 +1,6 @@ +use std::collections::HashMap; + use indexmap::IndexSet; -use rspack_core::Provide; use swc_core::common::util::take::Take; use swc_core::common::Span; use swc_core::common::{Mark, DUMMY_SP}; @@ -9,6 +10,9 @@ use swc_core::ecma::ast::{ }; use swc_core::ecma::visit::{as_folder, Fold, VisitMut, VisitMutWith}; +pub type Provide = HashMap>; +pub type RawProvide = Provide; + pub fn provide_builtin(opts: &Provide, unresolved_mark: Mark) -> impl Fold + '_ { as_folder(ProvideBuiltin::new(opts, unresolved_mark)) } diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/react.rs b/crates/rspack_swc_visitors/src/react.rs similarity index 65% rename from crates/rspack_plugin_javascript/src/visitors/swc_visitor/react.rs rename to crates/rspack_swc_visitors/src/react.rs index aa957e62d37a..e6e88d5b88fd 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/react.rs +++ b/crates/rspack_swc_visitors/src/react.rs @@ -1,18 +1,73 @@ use std::sync::Arc; -use rspack_core::ReactOptions; +use serde::Deserialize; +use swc_core::common::comments::Comments; use swc_core::common::DUMMY_SP; -use swc_core::common::{comments::SingleThreadedComments, Mark, SourceMap}; +use swc_core::common::{Mark, SourceMap}; use swc_core::ecma::ast::{BlockStmt, FnDecl, Function, Ident, ModuleItem, Program, Stmt}; use swc_core::ecma::transforms::react::RefreshOptions; +use swc_core::ecma::transforms::react::Runtime; use swc_core::ecma::transforms::react::{react as swc_react, Options}; use swc_core::ecma::utils::quote_ident; use swc_core::ecma::visit::Fold; use swc_core::quote; +#[derive(Debug, Clone, Default)] +pub struct ReactOptions { + pub runtime: Option, + pub import_source: Option, + pub pragma: Option, + pub pragma_frag: Option, + pub throw_if_namespace: Option, + pub development: Option, + pub use_builtins: Option, + pub use_spread: Option, + pub refresh: Option, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RawReactOptions { + pub runtime: Option, + pub import_source: Option, + pub pragma: Option, + pub pragma_frag: Option, + pub throw_if_namespace: Option, + pub development: Option, + pub use_builtins: Option, + pub use_spread: Option, + pub refresh: Option, +} + +impl From for ReactOptions { + fn from(value: RawReactOptions) -> Self { + let runtime = if let Some(runtime) = &value.runtime { + match runtime.as_str() { + "automatic" => Some(Runtime::Automatic), + "classic" => Some(Runtime::Classic), + _ => None, + } + } else { + Some(Runtime::Automatic) + }; + + ReactOptions { + runtime, + import_source: value.import_source, + pragma: value.pragma, + pragma_frag: value.pragma_frag, + throw_if_namespace: value.throw_if_namespace, + development: value.development, + use_builtins: value.use_builtins, + use_spread: value.use_spread, + refresh: value.refresh, + } + } +} + pub fn react<'a>( top_level_mark: Mark, - comments: Option<&'a SingleThreadedComments>, + comments: Option<&'a dyn Comments>, cm: &Arc, options: &ReactOptions, unresolved_mark: Mark, diff --git a/crates/rspack_plugin_javascript/src/visitors/relay.rs b/crates/rspack_swc_visitors/src/relay.rs similarity index 82% rename from crates/rspack_plugin_javascript/src/visitors/relay.rs rename to crates/rspack_swc_visitors/src/relay.rs index e34f80e400bc..3d6556bdd062 100644 --- a/crates/rspack_plugin_javascript/src/visitors/relay.rs +++ b/crates/rspack_swc_visitors/src/relay.rs @@ -20,7 +20,7 @@ use std::path::{Path, PathBuf}; use once_cell::sync::Lazy; use regex::Regex; -use rspack_core::{RelayConfig, RelayLanguageConfig}; +use serde::Deserialize; use swc_core::{ common::{Mark, DUMMY_SP}, ecma::{ @@ -35,7 +35,7 @@ struct Relay<'a> { unresolved_mark: Mark, root_dir: PathBuf, file_name: &'a Path, - config: &'a RelayConfig, + config: &'a RelayOptions, } fn pull_first_operation_name_from_tpl(tpl: &TaggedTpl) -> Option { @@ -167,8 +167,47 @@ impl<'a> Relay<'a> { } } +#[derive(Debug, Default, Clone)] +pub struct RelayOptions { + pub artifact_directory: Option, + pub language: RelayLanguageConfig, +} + +#[derive(Copy, Clone, Debug)] +pub enum RelayLanguageConfig { + JavaScript, + TypeScript, + Flow, +} + +impl Default for RelayLanguageConfig { + fn default() -> Self { + Self::Flow + } +} + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct RawRelayOptions { + pub artifact_directory: Option, + pub language: String, +} + +impl From for RelayOptions { + fn from(raw_config: RawRelayOptions) -> Self { + Self { + artifact_directory: raw_config.artifact_directory.map(PathBuf::from), + language: match raw_config.language.as_str() { + "typescript" => RelayLanguageConfig::TypeScript, + "flow" => RelayLanguageConfig::Flow, + _ => RelayLanguageConfig::JavaScript, + }, + } + } +} + pub fn relay<'a>( - config: &'a RelayConfig, + config: &'a RelayOptions, file_name: &'a Path, root_dir: PathBuf, unresolved_mark: Mark, diff --git a/crates/rspack_testing/src/loader.rs b/crates/rspack_testing/src/loader.rs index aed76195e438..8e50ab89447a 100644 --- a/crates/rspack_testing/src/loader.rs +++ b/crates/rspack_testing/src/loader.rs @@ -20,12 +20,14 @@ fn get_builtin_loader(builtin: &str, options: Option<&str>) -> BoxLoader { } if builtin.starts_with(SWC_LOADER_IDENTIFIER) { - return Arc::new(rspack_loader_swc::SwcLoader::new( - serde_json::from_str(options.unwrap_or("{}")).unwrap_or_else(|e| { - panic!("Could not parse builtin:swc-loader options:{options:?},error: {e:?}") - }), - Some(builtin.into()), - )); + return Arc::new( + rspack_loader_swc::SwcLoader::new( + serde_json::from_str(options.unwrap_or("{}")).unwrap_or_else(|e| { + panic!("Could not parse builtin:swc-loader options:{options:?},error: {e:?}") + }), + ) + .with_identifier(builtin.into()), + ); } unreachable!("Unexpected builtin loader: {builtin}") diff --git a/examples/builtin-swc-loader/rspack.config.js b/examples/builtin-swc-loader/rspack.config.js index 8568b5f480d7..60c31bfa10e1 100644 --- a/examples/builtin-swc-loader/rspack.config.js +++ b/examples/builtin-swc-loader/rspack.config.js @@ -1,4 +1,3 @@ -const path = require("path"); /** @type {import('@rspack/cli').Configuration} */ const config = { entry: { diff --git a/examples/emotion/rspack.config.js b/examples/emotion/rspack.config.js index 937a957a0b70..3c2ded6087dd 100644 --- a/examples/emotion/rspack.config.js +++ b/examples/emotion/rspack.config.js @@ -8,12 +8,28 @@ const config = { { template: "./index.html" } - ], - emotion: true, - react: { - importSource: "@emotion/react", - runtime: "automatic" - } + ] + }, + module: { + rules: [ + { + test: /\.jsx$/, + loader: "builtin:swc-loader", + options: { + jsc: { + parser: { + syntax: "ecmascript", + jsx: true + } + }, + rspackExperiments: { + emotion: true, + react: {} + } + }, + type: "javascript/auto" + } + ] } }; module.exports = config; diff --git a/packages/rspack-cli/src/rspack-cli.ts b/packages/rspack-cli/src/rspack-cli.ts index 19672e81a739..2d6cf358e95e 100644 --- a/packages/rspack-cli/src/rspack-cli.ts +++ b/packages/rspack-cli/src/rspack-cli.ts @@ -23,7 +23,7 @@ import { import { normalizeEnv } from "./utils/options"; import { loadRspackConfig } from "./utils/loadConfig"; import findConfig from "./utils/findConfig"; -import { RspackPluginInstance, RspackPluginFunction } from "@rspack/core"; +import type { RspackPluginInstance, RspackPluginFunction } from "@rspack/core"; import path from "path"; type Command = "serve" | "build"; diff --git a/packages/rspack/src/builtin-loader/index.ts b/packages/rspack/src/builtin-loader/index.ts new file mode 100644 index 000000000000..7bc26342b0d8 --- /dev/null +++ b/packages/rspack/src/builtin-loader/index.ts @@ -0,0 +1 @@ +export * from "./swc"; diff --git a/packages/rspack/src/builtin-loader/swc/emotion.ts b/packages/rspack/src/builtin-loader/swc/emotion.ts new file mode 100644 index 000000000000..09e7edf28ea9 --- /dev/null +++ b/packages/rspack/src/builtin-loader/swc/emotion.ts @@ -0,0 +1,46 @@ +type EmotionConfigImportMap = { + [packageName: string]: { + [exportName: string]: { + canonicalImport?: [string, string]; + }; + }; +}; + +type EmotionConfig = { + sourceMap?: boolean; + autoLabel?: "never" | "dev-only" | "always"; + labelFormat?: string; + importMap?: EmotionConfigImportMap; +}; + +type EmotionOptions = boolean | EmotionConfig | undefined; + +function resolveEmotion( + emotion: EmotionOptions, + isProduction: boolean +): EmotionConfig | undefined { + if (!emotion) { + return undefined; + } + + if (emotion === true) { + emotion = {}; + } + + const autoLabel = emotion?.autoLabel ?? "dev-only"; + + const emotionConfig: EmotionConfig = { + enabled: true, + // @ts-expect-error autoLabel is string for JavaScript interface, however is boolean for Rust interface + autoLabel: + autoLabel === "dev-only" ? !isProduction : autoLabel === "always", + importMap: emotion?.importMap, + labelFormat: emotion?.labelFormat ?? "[local]", + sourcemap: isProduction ? false : emotion?.sourceMap ?? true + }; + + return emotionConfig; +} + +export { resolveEmotion }; +export type { EmotionOptions }; diff --git a/packages/rspack/src/builtin-loader/swc/index.ts b/packages/rspack/src/builtin-loader/swc/index.ts new file mode 100644 index 000000000000..eaa67b591548 --- /dev/null +++ b/packages/rspack/src/builtin-loader/swc/index.ts @@ -0,0 +1,11 @@ +export { resolveEmotion } from "./emotion"; +export type { EmotionOptions } from "./emotion"; + +export { resolveReact } from "./react"; +export type { ReactOptions } from "./react"; + +export { resolveRelay } from "./relay"; +export type { RelayOptions } from "./relay"; + +export { resolvePluginImport } from "./pluginImport"; +export type { PluginImportOptions } from "./pluginImport"; diff --git a/packages/rspack/src/builtin-loader/swc/pluginImport.ts b/packages/rspack/src/builtin-loader/swc/pluginImport.ts new file mode 100644 index 000000000000..7050b3d09926 --- /dev/null +++ b/packages/rspack/src/builtin-loader/swc/pluginImport.ts @@ -0,0 +1,48 @@ +import { RawPluginImportConfig } from "@rspack/binding"; + +type PluginImportConfig = { + libraryName: string; + libraryDirectory?: string; + customName?: string; + customStyleName?: string; + style?: string | boolean; + styleLibraryDirectory?: string; + camelToDashComponentName?: boolean; + transformToDefaultImport?: boolean; + ignoreEsComponent?: Array; + ignoreStyleComponent?: Array; +}; + +type PluginImportOptions = PluginImportConfig[] | undefined; + +function resolvePluginImport( + pluginImport: PluginImportOptions +): RawPluginImportConfig[] | undefined { + if (!pluginImport) { + return undefined; + } + + return pluginImport.map(config => { + const rawConfig: RawPluginImportConfig = { + ...config, + style: {} // As babel-plugin-import style config is very flexible, we convert it to a more specific structure + }; + + if (typeof config.style === "boolean") { + rawConfig.style!.bool = config.style; + } else if (typeof config.style === "string") { + const isTpl = config.style.includes("{{"); + rawConfig.style![isTpl ? "custom" : "css"] = config.style; + } + + // This option will overrides the behavior of style + if (config.styleLibraryDirectory) { + rawConfig.style = { styleLibraryDirectory: config.styleLibraryDirectory }; + } + + return rawConfig; + }); +} + +export { resolvePluginImport }; +export type { PluginImportOptions }; diff --git a/packages/rspack/src/builtin-loader/swc/react.ts b/packages/rspack/src/builtin-loader/swc/react.ts new file mode 100644 index 000000000000..c2fd2d176bae --- /dev/null +++ b/packages/rspack/src/builtin-loader/swc/react.ts @@ -0,0 +1,10 @@ +import { RawReactOptions } from "@rspack/binding"; + +function resolveReact(react: ReactOptions): RawReactOptions { + return react ?? {}; +} + +type ReactOptions = RawReactOptions | undefined; + +export { resolveReact }; +export type { ReactOptions }; diff --git a/packages/rspack/src/builtin-loader/swc/relay.ts b/packages/rspack/src/builtin-loader/swc/relay.ts new file mode 100644 index 000000000000..696176d4c799 --- /dev/null +++ b/packages/rspack/src/builtin-loader/swc/relay.ts @@ -0,0 +1,60 @@ +import path from "node:path"; + +import { RawRelayConfig } from "@rspack/binding"; + +type RelayOptions = boolean | RawRelayConfig | undefined; + +function getRelayConfigFromProject( + rootDir: string +): RawRelayConfig | undefined { + for (const configName of [ + "relay.config.json", + "relay.config.js", + "package.json" + ]) { + const configPath = path.join(rootDir, configName); + try { + let config = require(configPath) as + | Partial + | { relay?: Partial } + | undefined; + + let finalConfig: Partial | undefined; + if (configName === "package.json") { + finalConfig = (config as { relay?: Partial })?.relay; + } else { + finalConfig = config as Partial | undefined; + } + + if (finalConfig) { + return { + language: finalConfig.language!, + artifactDirectory: finalConfig.artifactDirectory + }; + } + } catch (_) {} + } +} + +function resolveRelay( + relay: RelayOptions, + rootDir: string +): RawRelayConfig | undefined { + if (!relay) { + return undefined; + } + + // Search relay config based on + if (relay === true) { + return ( + getRelayConfigFromProject(rootDir) || { + language: "javascript" + } + ); + } else { + return relay; + } +} + +export { resolveRelay }; +export type { RelayOptions }; diff --git a/packages/rspack/src/builtin-plugin/index.ts b/packages/rspack/src/builtin-plugin/index.ts index bfafacb4e71c..2d3a43d7a90b 100644 --- a/packages/rspack/src/builtin-plugin/index.ts +++ b/packages/rspack/src/builtin-plugin/index.ts @@ -20,9 +20,6 @@ import { RawDecoratorOptions, RawPresetEnv, RawProgressPluginConfig, - RawReactOptions, - RawRelayConfig, - RawPluginImportConfig, RawBuiltins, RawCssModulesConfig } from "@rspack/binding"; @@ -44,7 +41,16 @@ import { RspackBuiltinPlugin } from "."; import { loadConfig } from "browserslist"; -import path from "path"; +import { + EmotionOptions, + PluginImportOptions, + ReactOptions, + RelayOptions, + resolveEmotion, + resolvePluginImport, + resolveReact, + resolveRelay +} from "../builtin-loader/swc"; type BuiltinsCssConfig = { modules?: Partial; @@ -78,8 +84,6 @@ type PluginImportConfig = { ignoreStyleComponent?: Array; }; -type RelayConfig = boolean | RawRelayConfig; - function resolveTreeShaking( treeShaking: Builtins["treeShaking"], production: boolean @@ -125,132 +129,24 @@ function resolveDecorator( ); } -function resolveEmotion( - emotion: Builtins["emotion"], - isProduction: boolean -): string | undefined { - if (!emotion) { - return undefined; - } - - if (emotion === true) { - emotion = {}; - } - - const autoLabel = emotion?.autoLabel ?? "dev-only"; - - const emotionConfig: Builtins["emotion"] = { - enabled: true, - // @ts-expect-error autoLabel is string for JavaScript interface, however is boolean for Rust interface - autoLabel: - autoLabel === "dev-only" ? !isProduction : autoLabel === "always", - importMap: emotion?.importMap, - labelFormat: emotion?.labelFormat ?? "[local]", - sourcemap: isProduction ? false : emotion?.sourceMap ?? true - }; - - return JSON.stringify(emotionConfig); -} - -function resolvePluginImport( - pluginImport?: PluginImportConfig[] -): RawPluginImportConfig[] | undefined { - if (!pluginImport) { - return undefined; - } - - return pluginImport.map(config => { - const rawConfig: RawPluginImportConfig = { - ...config, - style: {} // As babel-plugin-import style config is very flexible, we convert it to a more specific structure - }; - - if (typeof config.style === "boolean") { - rawConfig.style!.bool = config.style; - } else if (typeof config.style === "string") { - const isTpl = config.style.includes("{{"); - rawConfig.style![isTpl ? "custom" : "css"] = config.style; - } - - // This option will overrides the behavior of style - if (config.styleLibraryDirectory) { - rawConfig.style = { styleLibraryDirectory: config.styleLibraryDirectory }; - } - - return rawConfig; - }); -} - -function resolveRelay( - relay: RelayConfig, - rootDir: string -): RawRelayConfig | undefined { - if (!relay) { - return undefined; - } - - // Search relay config based on - if (relay === true) { - return ( - getRelayConfigFromProject(rootDir) || { - language: "javascript" - } - ); - } else { - return relay; - } -} - -function getRelayConfigFromProject( - rootDir: string -): RawRelayConfig | undefined { - for (const configName of [ - "relay.config.json", - "relay.config.js", - "package.json" - ]) { - const configPath = path.join(rootDir, configName); - try { - let config = require(configPath) as - | Partial - | { relay?: Partial } - | undefined; - - let finalConfig: Partial | undefined; - if (configName === "package.json") { - finalConfig = (config as { relay?: Partial })?.relay; - } else { - finalConfig = config as Partial | undefined; - } - - if (finalConfig) { - return { - language: finalConfig.language!, - artifactDirectory: finalConfig.artifactDirectory - }; - } - } catch (_) {} - } -} - export interface Builtins { css?: BuiltinsCssConfig; treeShaking?: boolean | "module"; progress?: boolean | RawProgressPluginConfig; - react?: RawReactOptions; noEmitAssets?: boolean; define?: Record; provide?: Record; html?: Array; decorator?: boolean | Partial; minifyOptions?: SwcJsMinimizerPluginOptions; - emotion?: boolean | EmotionConfig; presetEnv?: Partial; devFriendlySplitChunks?: boolean; copy?: CopyPluginOptions; banner?: BannerPluginOptions | BannerPluginOptions[]; - pluginImport?: PluginImportConfig[]; - relay?: boolean | RawRelayConfig; + react?: ReactOptions; + pluginImport?: PluginImportOptions; + emotion?: EmotionOptions; + relay?: RelayOptions; } export function deprecated_resolveBuiltins( @@ -377,15 +273,15 @@ export function deprecated_resolveBuiltins( } : undefined, treeShaking: resolveTreeShaking(builtins.treeShaking, production), - react: builtins.react ?? {}, noEmitAssets: noEmitAssets, presetEnv: resolvePresetEnv(builtins.presetEnv, contextPath), decorator: resolveDecorator(builtins.decorator), - emotion: resolveEmotion(builtins.emotion, production), devFriendlySplitChunks: builtins.devFriendlySplitChunks ?? false, + react: resolveReact(builtins.react), pluginImport: resolvePluginImport(builtins.pluginImport), - relay: builtins.relay - ? resolveRelay(builtins.relay, contextPath) - : undefined + emotion: builtins.emotion + ? JSON.stringify(resolveEmotion(builtins.emotion, production)) + : undefined, + relay: resolveRelay(builtins.relay, contextPath) }; } diff --git a/packages/rspack/src/config/adapter.ts b/packages/rspack/src/config/adapter.ts index b1154c57cb3c..fa2e7ad9cb88 100644 --- a/packages/rspack/src/config/adapter.ts +++ b/packages/rspack/src/config/adapter.ts @@ -74,8 +74,9 @@ export const getRawOptions = ( "context, devtool, cache should not be nil after defaults" ); const devtool = options.devtool === false ? "" : options.devtool; + const mode = options.mode; return { - mode: options.mode, + mode, target: getRawTarget(options.target), context: options.context, output: getRawOutput(options.output), @@ -84,6 +85,7 @@ export const getRawOptions = ( module: getRawModule(options.module, { compiler, devtool, + mode, context: options.context }), devtool, diff --git a/packages/rspack/src/config/adapterRuleUse.ts b/packages/rspack/src/config/adapterRuleUse.ts index e1d5aa86a349..682fc1428b2a 100644 --- a/packages/rspack/src/config/adapterRuleUse.ts +++ b/packages/rspack/src/config/adapterRuleUse.ts @@ -4,7 +4,6 @@ import type { RawModuleRuleUse, RawOptions } from "@rspack/binding"; -import assert from "assert"; import { ResolveRequest } from "enhanced-resolve"; import { Compiler } from "../Compiler"; @@ -19,12 +18,19 @@ import { } from "./zod"; import { parsePathQueryFragment } from "../loader-runner"; import { isNil } from "../util"; +import { + resolveEmotion, + resolvePluginImport, + resolveReact, + resolveRelay +} from "../builtin-loader"; const BUILTIN_LOADER_PREFIX = "builtin:"; export interface ComposeJsUseOptions { devtool: RawOptions["devtool"]; context: RawOptions["context"]; + mode: RawOptions["mode"]; compiler: Compiler; } @@ -220,6 +226,59 @@ export function createRawModuleRuleUses( return createRawModuleRuleUsesImpl(allUses, path, options); } +type GetLoaderOptions = ( + o: RuleSetLoaderWithOptions["options"], + options: ComposeJsUseOptions +) => RuleSetLoaderWithOptions["options"]; + +const getSassLoaderOptions: GetLoaderOptions = o => { + (o ??= {} as any).__exePath = require.resolve( + `sass-embedded-${process.platform}-${ + process.arch + }/dart-sass-embedded/dart-sass-embedded${ + process.platform === "win32" ? ".bat" : "" + }` + ); + return o; +}; + +const getSwcLoaderOptions: GetLoaderOptions = (o, options) => { + if (o && typeof o === "object" && o.rspackExperiments) { + let expr = o.rspackExperiments; + const contextPath = options.context!; + const production = options.mode === "production" || !options.mode; + if (expr.emotion) { + expr.emotion = resolveEmotion(expr.emotion, production); + } + if (expr.relay) { + expr.relay = resolveRelay(expr.relay, contextPath); + } + if (expr.import || expr.pluginImport) { + expr.import = resolvePluginImport(expr.import || expr.pluginImport); + } + if (expr.react) { + expr.react = resolveReact(expr.react); + } + } + return o; +}; + +function getBuiltinLoaderOptions( + identifier: string, + o: RuleSetLoaderWithOptions["options"], + options: ComposeJsUseOptions +): RuleSetLoaderWithOptions["options"] { + if (identifier.startsWith(`${BUILTIN_LOADER_PREFIX}sass-loader`)) { + return getSassLoaderOptions(o, options); + } + + if (identifier.startsWith(`${BUILTIN_LOADER_PREFIX}swc-loader`)) { + return getSwcLoaderOptions(o, options); + } + + return o; +} + function createRawModuleRuleUsesImpl( uses: RuleSetLoaderWithOptions[], path: string, @@ -232,16 +291,7 @@ function createRawModuleRuleUsesImpl( return uses.map((use, index) => { let o; if (use.loader.startsWith(BUILTIN_LOADER_PREFIX)) { - o = use.options; - if (use.loader === `${BUILTIN_LOADER_PREFIX}sass-loader`) { - (o ??= {} as any).__exePath = require.resolve( - `sass-embedded-${process.platform}-${ - process.arch - }/dart-sass-embedded/dart-sass-embedded${ - process.platform === "win32" ? ".bat" : "" - }` - ); - } + o = getBuiltinLoaderOptions(use.loader, use.options, options); o = isNil(o) ? undefined : typeof o === "string" ? o : JSON.stringify(o); } diff --git a/packages/rspack/tests/ConfigCase.template.ts b/packages/rspack/tests/ConfigCase.template.ts index 7eea966ae80f..4cc23fa0d542 100644 --- a/packages/rspack/tests/ConfigCase.template.ts +++ b/packages/rspack/tests/ConfigCase.template.ts @@ -209,10 +209,10 @@ export const describeCases = config => { return; }; it(`${testName} should compile`, _done => { - console.info("running:", testName); - console.time(testName); + // console.info("running:", testName); + // console.time(testName); const done = (...args: any[]) => { - console.timeEnd(testName); + // console.timeEnd(testName); return _done(...args); }; rimraf.sync(outputDirectory); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/index.js b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/index.js new file mode 100644 index 000000000000..07942dd5fe6f --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/index.js @@ -0,0 +1,7 @@ +import { AaBb } from "./src/foo"; +import { CcDd } from "./src/bar"; + +it("should resolve right", () => { + expect(AaBb).toBe("Foo"); + expect(CcDd).toBe("Bar"); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/bar/cc-dd/index.js b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/bar/cc-dd/index.js new file mode 100644 index 000000000000..91b83d550c75 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/bar/cc-dd/index.js @@ -0,0 +1 @@ +export default "Bar"; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/bar/cc-dd/style.css b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/bar/cc-dd/style.css new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/index.js b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/index.js new file mode 100644 index 000000000000..1b25fd8f4ba1 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/index.js @@ -0,0 +1 @@ +export default "Foo"; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/style/index.js b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/style/index.js new file mode 100644 index 000000000000..dd37ec7b3e1e --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/style/index.js @@ -0,0 +1 @@ +console.log("foo-style"); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/webpack.config.js new file mode 100644 index 000000000000..e4aaea82c43d --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/webpack.config.js @@ -0,0 +1,34 @@ +module.exports = { + module: { + rules: [ + { + test: /.css/, + type: "asset" + } + ] + }, + module: { + rules: [ + { + test: /\.js$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + import: [ + { + libraryName: "./src/foo", + customName: "./src/foo/{{ kebabCase member }}", + style: true + }, + { + libraryName: "./src/bar", + customName: "./src/bar/{{ kebabCase member }}", + style: `{{ member }}/style.css` + } + ] + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/index.jsx new file mode 100644 index 000000000000..1063695cabbc --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/index.jsx @@ -0,0 +1,5 @@ +function component () { + return
+} + +it('react refresh false should run', () => {}) diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/webpack.config.js new file mode 100644 index 000000000000..12de81331dc8 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/webpack.config.js @@ -0,0 +1,17 @@ +module.exports = { + module: { + rules: [ + { + test: /\.js$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + react: { + refresh: false + } + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/index.jsx new file mode 100644 index 000000000000..37406fa8adc6 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/index.jsx @@ -0,0 +1,5 @@ +const element =
; + +it("react automatic", () => { + expect(element.type).toBe("div"); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/webpack.config.js new file mode 100644 index 000000000000..a8957fc0bebc --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/webpack.config.js @@ -0,0 +1,17 @@ +module.exports = { + module: { + rules: [ + { + test: /\.js$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + react: { + runtime: "automatic" + } + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/index.jsx new file mode 100644 index 000000000000..00639c94454c --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/index.jsx @@ -0,0 +1,7 @@ +import React from 'react'; + +const element =
; + +it("react classic", () => { + expect(element.type).toBe("div"); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/webpack.config.js new file mode 100644 index 000000000000..f260c6f55f49 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/webpack.config.js @@ -0,0 +1,17 @@ +module.exports = { + module: { + rules: [ + { + test: /\.js$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + react: { + runtime: "classic" + } + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/custom/MyComponent.graphql.ts b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/custom/MyComponent.graphql.ts new file mode 100644 index 000000000000..bd816eaba4ca --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/custom/MyComponent.graphql.ts @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/index.jsx new file mode 100644 index 000000000000..428e75ae8077 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/index.jsx @@ -0,0 +1,11 @@ +const { graphql } = require("react-relay"); + +it("graphql", () => { + const query = graphql` + fragment MyComponent on Type { + field + } + `; + + expect(query).toBe(1); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/webpack.config.js new file mode 100644 index 000000000000..b602accac2c7 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/webpack.config.js @@ -0,0 +1,18 @@ +module.exports = { + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + relay: { + language: "typescript", + artifactDirectory: "./custom" + } + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/custom/MyComponent.graphql.ts b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/custom/MyComponent.graphql.ts new file mode 100644 index 000000000000..bd816eaba4ca --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/custom/MyComponent.graphql.ts @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/index.jsx new file mode 100644 index 000000000000..428e75ae8077 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/index.jsx @@ -0,0 +1,11 @@ +const { graphql } = require("react-relay"); + +it("graphql", () => { + const query = graphql` + fragment MyComponent on Type { + field + } + `; + + expect(query).toBe(1); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/relay.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/relay.config.js new file mode 100644 index 000000000000..cb368f17caf6 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/relay.config.js @@ -0,0 +1,4 @@ +module.exports = { + language: "typescript", + artifactDirectory: "./custom" +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/webpack.config.js new file mode 100644 index 000000000000..9a0f39cf0a44 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/webpack.config.js @@ -0,0 +1,15 @@ +module.exports = { + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + relay: true + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/custom/MyComponent.graphql.ts b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/custom/MyComponent.graphql.ts new file mode 100644 index 000000000000..bd816eaba4ca --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/custom/MyComponent.graphql.ts @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/index.jsx new file mode 100644 index 000000000000..428e75ae8077 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/index.jsx @@ -0,0 +1,11 @@ +const { graphql } = require("react-relay"); + +it("graphql", () => { + const query = graphql` + fragment MyComponent on Type { + field + } + `; + + expect(query).toBe(1); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/relay.config.json b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/relay.config.json new file mode 100644 index 000000000000..a80d534946bd --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/relay.config.json @@ -0,0 +1,4 @@ +{ + "language": "typescript", + "artifactDirectory": "./custom" +} diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/webpack.config.js new file mode 100644 index 000000000000..9a0f39cf0a44 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/webpack.config.js @@ -0,0 +1,15 @@ +module.exports = { + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + relay: true + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/__generated__/MyComponent.graphql.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/__generated__/MyComponent.graphql.js new file mode 100644 index 000000000000..bd816eaba4ca --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/__generated__/MyComponent.graphql.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/index.jsx new file mode 100644 index 000000000000..428e75ae8077 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/index.jsx @@ -0,0 +1,11 @@ +const { graphql } = require("react-relay"); + +it("graphql", () => { + const query = graphql` + fragment MyComponent on Type { + field + } + `; + + expect(query).toBe(1); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/webpack.config.js new file mode 100644 index 000000000000..9a0f39cf0a44 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/webpack.config.js @@ -0,0 +1,15 @@ +module.exports = { + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + relay: true + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/__generated__/MyComponent.graphql.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/__generated__/MyComponent.graphql.js new file mode 100644 index 000000000000..bd816eaba4ca --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/__generated__/MyComponent.graphql.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/index.jsx new file mode 100644 index 000000000000..428e75ae8077 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/index.jsx @@ -0,0 +1,11 @@ +const { graphql } = require("react-relay"); + +it("graphql", () => { + const query = graphql` + fragment MyComponent on Type { + field + } + `; + + expect(query).toBe(1); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/package.json b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/package.json new file mode 100644 index 000000000000..c7b2e5c18520 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/package.json @@ -0,0 +1,5 @@ +{ + "relay": { + "language": "javascript" + } +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/webpack.config.js new file mode 100644 index 000000000000..9a0f39cf0a44 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/webpack.config.js @@ -0,0 +1,15 @@ +module.exports = { + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + relay: true + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/custom/MyComponent.graphql.ts b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/custom/MyComponent.graphql.ts new file mode 100644 index 000000000000..0e35d0cbb39e --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/custom/MyComponent.graphql.ts @@ -0,0 +1 @@ +module.exports = new Error("should never resolved here"); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/index.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/index.js new file mode 100644 index 000000000000..8449c8457097 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/index.js @@ -0,0 +1,15 @@ +const { graphql } = require("react-relay"); + +it("graphql", () => { + // https://github.com/web-infra-dev/rspack/issues/2440 + // This will transformed to require('./custom/MyComponent.graphql.ts'), + // If succeed, `require` would become `__webpack_require__`, + // Then it will resolved to mock.js + const mock = graphql` + fragment MyComponent on Type { + field + } + `; + + expect(mock.found).toBeTruthy(); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/mock.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/mock.js new file mode 100644 index 000000000000..7911b6106eb1 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/mock.js @@ -0,0 +1,3 @@ +module.exports = { + found: true +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/relay.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/relay.config.js new file mode 100644 index 000000000000..5ec440eecb74 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/relay.config.js @@ -0,0 +1,4 @@ +module.exports = { + language: "typescript", + artifactDirectory: "custom" +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/webpack.config.js new file mode 100644 index 000000000000..399dc2be1f91 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/webpack.config.js @@ -0,0 +1,25 @@ +const { resolve } = require("path"); + +module.exports = { + resolve: { + alias: { + [resolve(__dirname, "./custom/MyComponent.graphql.ts")]: resolve( + __dirname, + "./mock.js" + ) + } + }, + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + relay: true + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile1.txt b/packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile1.txt new file mode 100644 index 000000000000..b3c554761809 --- /dev/null +++ b/packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile1.txt @@ -0,0 +1 @@ +file1contents \ No newline at end of file diff --git a/packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile2.txt b/packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile2.txt new file mode 100644 index 000000000000..26ebc2e98bd6 --- /dev/null +++ b/packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile2.txt @@ -0,0 +1 @@ +file2contents \ No newline at end of file From 4c72200c4bde9ccd6c37063e933a41465ca3d005 Mon Sep 17 00:00:00 2001 From: Gengkun Date: Fri, 8 Sep 2023 16:26:07 +0800 Subject: [PATCH 25/30] feat: support function for `BannerPlugin` (#4151) * feat: support function in * clean * lint --- Cargo.lock | 5 +- crates/node_binding/binding.d.ts | 15 +- crates/node_binding/src/js_values/mod.rs | 6 +- .../src}/chunk.rs | 5 +- crates/rspack_binding_options/src/lib.rs | 1 + .../src/options/raw_builtins/raw_banner.rs | 88 +++- crates/rspack_plugin_banner/Cargo.toml | 7 +- crates/rspack_plugin_banner/src/lib.rs | 126 +++--- .../rspack/src/builtin-plugin/BannerPlugin.ts | 100 +++-- .../src/builtin-plugin/ProgressPlugin.ts | 4 +- packages/rspack/src/index.ts | 6 +- packages/rspack/src/lib/BannerPlugin.js | 127 ------ .../rspack/src/lib/LoaderOptionsPlugin.js | 10 - packages/rspack/src/lib/Template.js | 419 ------------------ .../src/lib/util/create-schema-validation.js | 28 -- packages/rspack/src/lib/util/memoize.js | 32 -- .../src/schemas/plugins/BannerPlugin.check.js | 327 -------------- .../src/schemas/plugins/BannerPlugin.json | 107 ----- .../plugins/LoaderOptionsPlugin.check.js | 51 --- .../schemas/plugins/LoaderOptionsPlugin.json | 27 -- 20 files changed, 265 insertions(+), 1226 deletions(-) rename crates/{node_binding/src/js_values => rspack_binding_options/src}/chunk.rs (83%) delete mode 100644 packages/rspack/src/lib/BannerPlugin.js delete mode 100644 packages/rspack/src/lib/Template.js delete mode 100644 packages/rspack/src/lib/util/create-schema-validation.js delete mode 100644 packages/rspack/src/lib/util/memoize.js delete mode 100644 packages/rspack/src/schemas/plugins/BannerPlugin.check.js delete mode 100644 packages/rspack/src/schemas/plugins/BannerPlugin.json delete mode 100644 packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.check.js delete mode 100644 packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.json diff --git a/Cargo.lock b/Cargo.lock index 650ed047ca3d..21e3ad1af0eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2695,12 +2695,13 @@ version = "0.1.0" dependencies = [ "async-recursion", "async-trait", + "futures", + "once_cell", + "regex", "rspack_core", "rspack_error", "rspack_regex", - "rspack_testing", "rspack_util", - "testing_macros", ] [[package]] diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 0364982c4886..85ce7fd1df7b 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -188,6 +188,7 @@ export interface JsAssetInfoRelated { } export interface JsChunk { + name?: string files: Array } @@ -505,7 +506,7 @@ export interface RawBannerConditions { } export interface RawBannerConfig { - banner: string + banner: RawBannerContent entryOnly?: boolean footer?: boolean raw?: boolean @@ -514,6 +515,18 @@ export interface RawBannerConfig { exclude?: RawBannerConditions } +export interface RawBannerContent { + type: "string" | "function" + stringPayload?: string + fnPayload?: (...args: any[]) => any +} + +export interface RawBannerContentFnCtx { + hash: string + chunk: JsChunk + filename: string +} + export interface RawBuiltins { css?: RawCssPluginConfig presetEnv?: RawPresetEnv diff --git a/crates/node_binding/src/js_values/mod.rs b/crates/node_binding/src/js_values/mod.rs index 197cc5d9df94..c2959a499c8c 100644 --- a/crates/node_binding/src/js_values/mod.rs +++ b/crates/node_binding/src/js_values/mod.rs @@ -1,5 +1,9 @@ +mod chunk { + // TODO: should we merge rspack_binding_options and node_binding? + pub use rspack_binding_options::chunk::*; +} + mod asset; -mod chunk; mod chunk_group; mod compilation; mod hooks; diff --git a/crates/node_binding/src/js_values/chunk.rs b/crates/rspack_binding_options/src/chunk.rs similarity index 83% rename from crates/node_binding/src/js_values/chunk.rs rename to crates/rspack_binding_options/src/chunk.rs index e5872c86d950..eaefe9246335 100644 --- a/crates/node_binding/src/js_values/chunk.rs +++ b/crates/rspack_binding_options/src/chunk.rs @@ -1,15 +1,18 @@ +use napi_derive::napi; use rspack_core::ChunkAssetArgs; #[napi(object)] pub struct JsChunk { + pub name: Option, pub files: Vec, } impl JsChunk { pub fn from(chunk: &rspack_core::Chunk) -> Self { + let name = chunk.name.clone(); let mut files = Vec::from_iter(chunk.files.iter().cloned()); files.sort_unstable(); - Self { files } + Self { name, files } } } diff --git a/crates/rspack_binding_options/src/lib.rs b/crates/rspack_binding_options/src/lib.rs index 4e0c9dc1bb15..960c3f892069 100644 --- a/crates/rspack_binding_options/src/lib.rs +++ b/crates/rspack_binding_options/src/lib.rs @@ -1,2 +1,3 @@ +pub mod chunk; mod options; pub use options::*; diff --git a/crates/rspack_binding_options/src/options/raw_builtins/raw_banner.rs b/crates/rspack_binding_options/src/options/raw_builtins/raw_banner.rs index cc39698b2954..88bdfd82a2aa 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/raw_banner.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/raw_banner.rs @@ -1,10 +1,20 @@ -use std::fmt::Debug; +use std::{fmt::Debug, sync::Arc}; +use derivative::Derivative; +use napi::{Env, JsFunction}; use napi_derive::napi; use rspack_error::internal_error; -use rspack_plugin_banner::{BannerCondition, BannerConditions, BannerConfig}; +use rspack_napi_shared::{ + threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}, + NapiResultExt, NAPI_ENV, +}; +use rspack_plugin_banner::{ + BannerCondition, BannerConditions, BannerConfig, BannerContent, BannerContentFnCtx, +}; use serde::Deserialize; +use crate::chunk::JsChunk; + #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] #[napi(object)] @@ -26,11 +36,81 @@ pub struct RawBannerConditions { pub array_matcher: Option>, } +#[napi(object)] +pub struct RawBannerContentFnCtx { + pub hash: String, + pub chunk: JsChunk, + pub filename: String, +} + +impl<'a> From> for RawBannerContentFnCtx { + fn from(value: BannerContentFnCtx) -> Self { + Self { + hash: value.hash.to_string(), + chunk: JsChunk::from(value.chunk), + filename: value.filename.to_string(), + } + } +} + +#[derive(Derivative, Deserialize)] +#[derivative(Debug)] +#[serde(rename_all = "camelCase")] +#[napi(object)] +pub struct RawBannerContent { + #[napi(ts_type = r#""string" | "function""#)] + pub r#type: String, + pub string_payload: Option, + #[derivative(Debug = "ignore")] + #[serde(skip_deserializing)] + pub fn_payload: Option, +} + +impl TryFrom for BannerContent { + type Error = rspack_error::Error; + + fn try_from(value: RawBannerContent) -> Result { + match value.r#type.as_str() { + "string" => { + let s = value.string_payload.ok_or_else(|| { + internal_error!("should have a string_payload when RawBannerContent.type is \"string\"") + })?; + Ok(BannerContent::String(s)) + } + "function" => { + let func = value.fn_payload.ok_or_else(|| { + internal_error!("should have a fn_payload when RawBannerContent.type is \"function\"") + })?; + let func: ThreadsafeFunction = + NAPI_ENV.with(|env| -> anyhow::Result<_> { + let env = env.borrow().expect("Failed to get env with external"); + let func_use = rspack_binding_macros::js_fn_into_threadsafe_fn!(func, &Env::from(env)); + Ok(func_use) + })?; + let func = Arc::new(func); + Ok(BannerContent::Fn(Box::new( + move |ctx: BannerContentFnCtx| { + let func = func.clone(); + Box::pin(async move { + func + .call(ctx.into(), ThreadsafeFunctionCallMode::NonBlocking) + .into_rspack_result()? + .await + .map_err(|err| internal_error!("Failed to call rule.use function: {err}"))? + }) + }, + ))) + } + _ => unreachable!(), + } + } +} + #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] #[napi(object)] pub struct RawBannerConfig { - pub banner: String, + pub banner: RawBannerContent, pub entry_only: Option, pub footer: Option, pub raw: Option, @@ -117,7 +197,7 @@ impl TryFrom for BannerConfig { } Ok(BannerConfig { - banner: value.banner, + banner: value.banner.try_into()?, entry_only: value.entry_only, footer: value.footer, raw: value.raw, diff --git a/crates/rspack_plugin_banner/Cargo.toml b/crates/rspack_plugin_banner/Cargo.toml index bf8231567a77..f5361d5d0b7d 100644 --- a/crates/rspack_plugin_banner/Cargo.toml +++ b/crates/rspack_plugin_banner/Cargo.toml @@ -7,13 +7,12 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dev-dependencies] -rspack_testing = { path = "../rspack_testing" } -testing_macros = { workspace = true } - [dependencies] async-recursion = { workspace = true } async-trait = { workspace = true } +futures = { workspace = true } +once_cell = { workspace = true } +regex = { workspace = true } rspack_core = { path = "../rspack_core" } rspack_error = { path = "../rspack_error" } rspack_regex = { path = "../rspack_regex" } diff --git a/crates/rspack_plugin_banner/src/lib.rs b/crates/rspack_plugin_banner/src/lib.rs index 79bf49ae7861..9a252480e922 100644 --- a/crates/rspack_plugin_banner/src/lib.rs +++ b/crates/rspack_plugin_banner/src/lib.rs @@ -4,19 +4,24 @@ use std::fmt::{self, Debug}; use async_recursion::async_recursion; use async_trait::async_trait; +use futures::future::BoxFuture; +use once_cell::sync::Lazy; +use regex::Regex; use rspack_core::{ rspack_sources::{BoxSource, ConcatSource, RawSource, SourceExt}, - to_comment, Logger, Plugin, + to_comment, Chunk, Filename, Logger, PathData, Plugin, }; use rspack_error::Result; use rspack_regex::RspackRegex; use rspack_util::try_any; +#[derive(Debug)] pub enum BannerCondition { String(String), Regexp(RspackRegex), } +#[derive(Debug)] pub enum BannerConditions { String(String), Regexp(RspackRegex), @@ -46,52 +51,41 @@ impl BannerConditions { #[derive(Debug)] pub struct BannerConfig { - /** - * Specifies the banner. - */ - pub banner: String, - /** - * If true, the banner will only be added to the entry chunks. - */ + // Specifies the banner. + pub banner: BannerContent, + // If true, the banner will only be added to the entry chunks. pub entry_only: Option, - /** - * If true, banner will be placed at the end of the output. - */ + // If true, banner will be placed at the end of the output. pub footer: Option, - /** - * If true, banner will not be wrapped in a comment. - */ + // If true, banner will not be wrapped in a comment. pub raw: Option, - - /** - * Include all modules that pass test assertion. - */ + // Include all modules that pass test assertion. pub test: Option, - /** - * Include all modules matching any of these conditions. - */ + // Include all modules matching any of these conditions. pub include: Option, - /** - * Exclude all modules matching any of these conditions. - */ + // Exclude all modules matching any of these conditions. pub exclude: Option, } -impl fmt::Debug for BannerCondition { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::String(i) => i.fmt(f), - Self::Regexp(i) => i.fmt(f), - } - } +pub struct BannerContentFnCtx<'a> { + pub hash: &'a str, + pub chunk: &'a Chunk, + pub filename: &'a str, } -impl fmt::Debug for BannerConditions { +pub type BannerContentFn = + Box Fn(BannerContentFnCtx<'a>) -> BoxFuture<'a, Result> + Sync + Send>; + +pub enum BannerContent { + String(String), + Fn(BannerContentFn), +} + +impl fmt::Debug for BannerContent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::String(i) => i.fmt(f), - Self::Regexp(i) => i.fmt(f), - Self::Array(i) => i.fmt(f), + Self::String(arg0) => f.debug_tuple("String").field(arg0).finish(), + Self::Fn(_) => f.debug_tuple("Fn").finish(), } } } @@ -116,18 +110,21 @@ async fn match_object(obj: &BannerConfig, str: &str) -> Result { Ok(true) } +static TRIALING_WHITESPACE: Lazy = + Lazy::new(|| Regex::new(r"\s+\n").expect("invalid regexp")); + fn wrap_comment(str: &str) -> String { if !str.contains('\n') { return to_comment(str); } - let binding = str - .replace("* /", "*/") + let result = str + .replace("*/", "* /") .split('\n') .collect::>() - .join("\n * ") - .replace(|c: char| c.is_whitespace() && c != '\n', " "); - let result = binding.trim_end(); + .join("\n * "); + let result = TRIALING_WHITESPACE.replace_all(&result, "\n"); + let result = result.trim_end(); format!("/*!\n * {}\n */", result) } @@ -135,18 +132,19 @@ fn wrap_comment(str: &str) -> String { #[derive(Debug)] pub struct BannerPlugin { config: BannerConfig, - comment: String, } impl BannerPlugin { pub fn new(config: BannerConfig) -> Self { - let comment = if let Some(raw) = config.raw && raw { - config.banner.clone() - } else { - wrap_comment(&config.banner) - }; + Self { config } + } - Self { config, comment } + fn wrap_comment(&self, value: &str) -> String { + if let Some(true) = self.config.raw { + value.to_owned() + } else { + wrap_comment(value) + } } fn update_source(&self, comment: String, old: BoxSource, footer: Option) -> BoxSource { @@ -182,7 +180,7 @@ impl Plugin for BannerPlugin { let compilation = args.compilation; let logger = compilation.get_logger(self.name()); let start = logger.time("add banner"); - let mut chunk_files = vec![]; + let mut updates = vec![]; // filter file for chunk in compilation.chunk_by_ukey.values() { @@ -198,19 +196,41 @@ impl Plugin for BannerPlugin { if !is_match { continue; } - chunk_files.push(file.clone()); + // add comment to the matched file + let hash = compilation + .hash + .as_ref() + .expect("should have compilation.hash in process_assets hook") + .encoded() + .to_owned(); + // todo: support placeholder, such as [fullhash]、[chunkhash] + let banner = match &self.config.banner { + BannerContent::String(content) => self.wrap_comment(content), + BannerContent::Fn(func) => { + let res = func(BannerContentFnCtx { + hash: &hash, + chunk, + filename: file, + }) + .await?; + self.wrap_comment(&res) + } + }; + let comment = compilation.get_path( + &Filename::from(banner), + PathData::default().chunk(chunk).hash(&hash).filename(file), + ); + updates.push((file.clone(), comment)); } } - // add comment to the matched file - for file in chunk_files { - // todo: support placeholder, such as [fullhash]、[chunkhash] - let comment = self.comment.to_owned(); + for (file, comment) in updates { let _res = compilation.update_asset(file.as_str(), |old, info| { let new = self.update_source(comment, old, self.config.footer); Ok((new, info)) }); } + logger.time_end(start); Ok(()) diff --git a/packages/rspack/src/builtin-plugin/BannerPlugin.ts b/packages/rspack/src/builtin-plugin/BannerPlugin.ts index 7423d02eaeae..efe3deb332df 100644 --- a/packages/rspack/src/builtin-plugin/BannerPlugin.ts +++ b/packages/rspack/src/builtin-plugin/BannerPlugin.ts @@ -1,25 +1,49 @@ +import { z } from "zod"; import { + JsChunk, RawBannerCondition, RawBannerConditions, - RawBannerConfig + RawBannerConfig, + RawBannerContent } from "@rspack/binding"; import { BuiltinPluginKind, create } from "./base"; -type BannerCondition = string | RegExp; -type BannerConditions = BannerCondition | BannerCondition[]; -export type BannerPluginOptions = - | string - | { - banner: string; - entryOnly?: boolean; - footer?: boolean; - raw?: boolean; - test?: BannerConditions; - exclude?: BannerConditions; - include?: BannerConditions; - }; +const rule = z.string().or(z.instanceof(RegExp)); +export type Rule = z.infer; -function getBannerCondition(condition: BannerCondition): RawBannerCondition { +const rules = rule.or(rule.array()); +export type Rules = z.infer; + +const bannerFunction = z + .function() + .args( + z.object({ + hash: z.string(), + chunk: z.custom(), + filename: z.string() + }) + ) + .returns(z.string()); +export type BannerFunction = z.infer; + +const bannerContent = z.string().or(bannerFunction); +export type BannerContent = z.infer; + +const bannerPluginOptions = z.strictObject({ + banner: bannerContent, + entryOnly: z.boolean().optional(), + exclude: rules.optional(), + include: rules.optional(), + raw: z.boolean().optional(), + footer: z.boolean().optional(), + test: rules.optional() +}); +export type BannerPluginOptions = z.infer; + +const bannerPluginArgument = bannerContent.or(bannerPluginOptions); +export type BannerPluginArgument = z.infer; + +function getRawBannerRule(condition: Rule): RawBannerCondition { if (typeof condition === "string") { return { type: "string", @@ -35,35 +59,57 @@ function getBannerCondition(condition: BannerCondition): RawBannerCondition { throw new Error("unreachable: condition should be one of string, RegExp"); } -function getBannerConditions( - condition?: BannerConditions -): RawBannerConditions | undefined { +function getRawBannerRules(condition?: Rules): RawBannerConditions | undefined { if (!condition) return undefined; if (Array.isArray(condition)) { return { type: "array", - arrayMatcher: condition.map(i => getBannerCondition(i)) + arrayMatcher: condition.map(i => getRawBannerRule(i)) }; } - return getBannerCondition(condition); + return getRawBannerRule(condition); +} + +function getRawBannerContent(content: BannerContent): RawBannerContent { + if (typeof content === "string") { + return { + type: "string", + stringPayload: content + }; + } + if (typeof content === "function") { + return { + type: "function", + fnPayload: content + }; + } + throw new Error("BannerContent should be a string or function"); } export const BannerPlugin = create( BuiltinPluginKind.Banner, - (bannerConfig: BannerPluginOptions): RawBannerConfig => { - if (typeof bannerConfig === "string") { + (args: BannerPluginArgument): RawBannerConfig => { + if (typeof args === "string") { + return { + banner: getRawBannerContent(args) + }; + } + if (typeof args === "function") { return { - banner: bannerConfig + banner: getRawBannerContent(args) }; } return { - ...bannerConfig, - test: getBannerConditions(bannerConfig.test), - include: getBannerConditions(bannerConfig.include), - exclude: getBannerConditions(bannerConfig.exclude) + banner: getRawBannerContent(args.banner), + entryOnly: args.entryOnly, + footer: args.footer, + raw: args.raw, + test: getRawBannerRules(args.test), + include: getRawBannerRules(args.include), + exclude: getRawBannerRules(args.exclude) }; } ); diff --git a/packages/rspack/src/builtin-plugin/ProgressPlugin.ts b/packages/rspack/src/builtin-plugin/ProgressPlugin.ts index ff8463feb657..49b2d8612cd3 100644 --- a/packages/rspack/src/builtin-plugin/ProgressPlugin.ts +++ b/packages/rspack/src/builtin-plugin/ProgressPlugin.ts @@ -1,8 +1,8 @@ import { RawProgressPluginConfig } from "@rspack/binding"; import { BuiltinPluginKind, create } from "./base"; -export type ProgressPluginOptions = RawProgressPluginConfig | undefined; +export type ProgressPluginArgument = RawProgressPluginConfig | undefined; export const ProgressPlugin = create( BuiltinPluginKind.Progress, - (progress: ProgressPluginOptions = {}): RawProgressPluginConfig => progress + (progress: ProgressPluginArgument = {}): RawProgressPluginConfig => progress ); diff --git a/packages/rspack/src/index.ts b/packages/rspack/src/index.ts index c533891d5a6a..b4ec67621005 100644 --- a/packages/rspack/src/index.ts +++ b/packages/rspack/src/index.ts @@ -10,7 +10,6 @@ export * from "./MultiStats"; export * from "./ChunkGroup"; export * from "./NormalModuleFactory"; export { cachedCleverMerge as cleverMerge } from "./util/cleverMerge"; -export { BannerPlugin } from "./lib/BannerPlugin"; export { EnvironmentPlugin } from "./lib/EnvironmentPlugin"; export { LoaderOptionsPlugin } from "./lib/LoaderOptionsPlugin"; export { @@ -23,6 +22,7 @@ export type OptimizationSplitChunksOptions = NonNullable< Configuration["optimization"] >["splitChunks"]; export { + BannerPlugin, DefinePlugin, ProvidePlugin, ProgressPlugin, @@ -34,10 +34,10 @@ export { ExternalsPlugin } from "./builtin-plugin"; export type { + BannerPluginArgument, DefinePluginOptions, ProvidePluginOptions, - ProgressPluginOptions, - BannerPluginOptions, + ProgressPluginArgument, HtmlPluginOptions, SwcJsMinimizerPluginOptions, CopyPluginOptions, diff --git a/packages/rspack/src/lib/BannerPlugin.js b/packages/rspack/src/lib/BannerPlugin.js deleted file mode 100644 index 0f5fb6a6cd4b..000000000000 --- a/packages/rspack/src/lib/BannerPlugin.js +++ /dev/null @@ -1,127 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -"use strict"; - -const { ConcatSource } = require("webpack-sources"); -const Compilation = require("../Compilation"); -const ModuleFilenameHelpers = require("./ModuleFilenameHelpers"); -const Template = require("./Template"); -const createSchemaValidation = require("./util/create-schema-validation"); - -/** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginArgument} BannerPluginArgument */ -/** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginOptions} BannerPluginOptions */ -/** @typedef {import("./Compiler")} Compiler */ - -const validate = createSchemaValidation( - require("../schemas/plugins/BannerPlugin.check.js"), - () => require("../schemas/plugins/BannerPlugin.json"), - { - name: "Banner Plugin", - baseDataPath: "options" - } -); - -const wrapComment = str => { - if (!str.includes("\n")) { - return Template.toComment(str); - } - return `/*!\n * ${str - .replace(/\*\//g, "* /") - .split("\n") - .join("\n * ") - .replace(/\s+\n/g, "\n") - .trimEnd()}\n */`; -}; - -class BannerPlugin { - /** - * @param {BannerPluginArgument} options options object - */ - constructor(options) { - if (typeof options === "string" || typeof options === "function") { - options = { - banner: options - }; - } - - validate(options); - - this.options = options; - - const bannerOption = options.banner; - if (typeof bannerOption === "function") { - const getBanner = bannerOption; - this.banner = this.options.raw - ? getBanner - : data => wrapComment(getBanner(data)); - } else { - const banner = this.options.raw - ? bannerOption - : wrapComment(bannerOption); - this.banner = () => banner; - } - } - - /** - * Apply the plugin - * @param {Compiler} compiler the compiler instance - * @returns {void} - */ - apply(compiler) { - const options = this.options; - const banner = this.banner; - const matchObject = ModuleFilenameHelpers.matchObject.bind( - undefined, - options - ); - const cache = new WeakMap(); - - compiler.hooks.compilation.tap("BannerPlugin", compilation => { - compilation.hooks.processAssets.tap( - { - name: "BannerPlugin", - stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS - }, - () => { - for (const chunk of compilation.chunks) { - if (options.entryOnly && !chunk.canBeInitial()) { - continue; - } - - for (const file of chunk.files) { - if (!matchObject(file)) { - continue; - } - - const data = { - chunk, - filename: file - }; - - let normalizedBanner = - typeof banner === "function" ? banner({ chunk }) : banner; - const comment = compilation.getPath(normalizedBanner, data); - - compilation.updateAsset(file, old => { - let cached = cache.get(old); - if (!cached || cached.comment !== comment) { - const source = options.footer - ? new ConcatSource(old, "\n", comment) - : new ConcatSource(comment, "\n", old); - cache.set(old, { source, comment }); - return source; - } - return cached.source; - }); - } - } - } - ); - }); - } -} - -export { BannerPlugin }; diff --git a/packages/rspack/src/lib/LoaderOptionsPlugin.js b/packages/rspack/src/lib/LoaderOptionsPlugin.js index 7bc3670ab678..e24e730dc034 100644 --- a/packages/rspack/src/lib/LoaderOptionsPlugin.js +++ b/packages/rspack/src/lib/LoaderOptionsPlugin.js @@ -7,25 +7,15 @@ const ModuleFilenameHelpers = require("./ModuleFilenameHelpers"); const { NormalModule } = require("../NormalModule"); -const createSchemaValidation = require("./util/create-schema-validation"); /** @typedef {import("../declarations/plugins/LoaderOptionsPlugin").LoaderOptionsPluginOptions} LoaderOptionsPluginOptions */ /** @typedef {import("./Compiler")} Compiler */ -const validate = createSchemaValidation( - require("../schemas/plugins/LoaderOptionsPlugin.check.js"), - () => require("../schemas/plugins/LoaderOptionsPlugin.json"), - { - name: "Loader Options Plugin", - baseDataPath: "options" - } -); class LoaderOptionsPlugin { /** * @param {LoaderOptionsPluginOptions} options options object */ constructor(options = {}) { - validate(options); if (typeof options !== "object") options = {}; if (!options.test) { options.test = { diff --git a/packages/rspack/src/lib/Template.js b/packages/rspack/src/lib/Template.js deleted file mode 100644 index 35c17ec2b970..000000000000 --- a/packages/rspack/src/lib/Template.js +++ /dev/null @@ -1,419 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -"use strict"; - -const { ConcatSource, PrefixSource } = require("webpack-sources"); - -/** @typedef {import("webpack-sources").Source} Source */ -/** @typedef {import("../declarations/WebpackOptions").Output} OutputOptions */ -/** @typedef {import("./Chunk")} Chunk */ -/** @typedef {import("./ChunkGraph")} ChunkGraph */ -/** @typedef {import("./CodeGenerationResults")} CodeGenerationResults */ -/** @typedef {import("./Compilation").AssetInfo} AssetInfo */ -/** @typedef {import("./Compilation").PathData} PathData */ -/** @typedef {import("./DependencyTemplates")} DependencyTemplates */ -/** @typedef {import("./Module")} Module */ -/** @typedef {import("./ModuleGraph")} ModuleGraph */ -/** @typedef {import("./ModuleTemplate")} ModuleTemplate */ -/** @typedef {import("./RuntimeModule")} RuntimeModule */ -/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */ -/** @typedef {import("./javascript/JavascriptModulesPlugin").ChunkRenderContext} ChunkRenderContext */ -/** @typedef {import("./javascript/JavascriptModulesPlugin").RenderContext} RenderContext */ - -const START_LOWERCASE_ALPHABET_CODE = "a".charCodeAt(0); -const START_UPPERCASE_ALPHABET_CODE = "A".charCodeAt(0); -const DELTA_A_TO_Z = "z".charCodeAt(0) - START_LOWERCASE_ALPHABET_CODE + 1; -const NUMBER_OF_IDENTIFIER_START_CHARS = DELTA_A_TO_Z * 2 + 2; // a-z A-Z _ $ -const NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS = - NUMBER_OF_IDENTIFIER_START_CHARS + 10; // a-z A-Z _ $ 0-9 -const FUNCTION_CONTENT_REGEX = /^function\s?\(\)\s?\{\r?\n?|\r?\n?\}$/g; -const INDENT_MULTILINE_REGEX = /^\t/gm; -const LINE_SEPARATOR_REGEX = /\r?\n/g; -const IDENTIFIER_NAME_REPLACE_REGEX = /^([^a-zA-Z$_])/; -const IDENTIFIER_ALPHA_NUMERIC_NAME_REPLACE_REGEX = /[^a-zA-Z0-9$]+/g; -const COMMENT_END_REGEX = /\*\//g; -const PATH_NAME_NORMALIZE_REPLACE_REGEX = /[^a-zA-Z0-9_!§$()=\-^°]+/g; -const MATCH_PADDED_HYPHENS_REPLACE_REGEX = /^-|-$/g; - -/** - * @typedef {Object} RenderManifestOptions - * @property {Chunk} chunk the chunk used to render - * @property {string} hash - * @property {string} fullHash - * @property {OutputOptions} outputOptions - * @property {CodeGenerationResults} codeGenerationResults - * @property {{javascript: ModuleTemplate}} moduleTemplates - * @property {DependencyTemplates} dependencyTemplates - * @property {RuntimeTemplate} runtimeTemplate - * @property {ModuleGraph} moduleGraph - * @property {ChunkGraph} chunkGraph - */ - -/** @typedef {RenderManifestEntryTemplated | RenderManifestEntryStatic} RenderManifestEntry */ - -/** - * @typedef {Object} RenderManifestEntryTemplated - * @property {function(): Source} render - * @property {string | function(PathData, AssetInfo=): string} filenameTemplate - * @property {PathData=} pathOptions - * @property {AssetInfo=} info - * @property {string} identifier - * @property {string=} hash - * @property {boolean=} auxiliary - */ - -/** - * @typedef {Object} RenderManifestEntryStatic - * @property {function(): Source} render - * @property {string} filename - * @property {AssetInfo} info - * @property {string} identifier - * @property {string=} hash - * @property {boolean=} auxiliary - */ - -/** - * @typedef {Object} HasId - * @property {number | string} id - */ - -/** - * @typedef {function(Module, number): boolean} ModuleFilterPredicate - */ - -class Template { - /** - * - * @param {Function} fn a runtime function (.runtime.js) "template" - * @returns {string} the updated and normalized function string - */ - static getFunctionContent(fn) { - return fn - .toString() - .replace(FUNCTION_CONTENT_REGEX, "") - .replace(INDENT_MULTILINE_REGEX, "") - .replace(LINE_SEPARATOR_REGEX, "\n"); - } - - /** - * @param {string} str the string converted to identifier - * @returns {string} created identifier - */ - static toIdentifier(str) { - if (typeof str !== "string") return ""; - return str - .replace(IDENTIFIER_NAME_REPLACE_REGEX, "_$1") - .replace(IDENTIFIER_ALPHA_NUMERIC_NAME_REPLACE_REGEX, "_"); - } - /** - * - * @param {string} str string to be converted to commented in bundle code - * @returns {string} returns a commented version of string - */ - static toComment(str) { - if (!str) return ""; - return `/*! ${str.replace(COMMENT_END_REGEX, "* /")} */`; - } - - /** - * - * @param {string} str string to be converted to "normal comment" - * @returns {string} returns a commented version of string - */ - static toNormalComment(str) { - if (!str) return ""; - return `/* ${str.replace(COMMENT_END_REGEX, "* /")} */`; - } - - /** - * @param {string} str string path to be normalized - * @returns {string} normalized bundle-safe path - */ - static toPath(str) { - if (typeof str !== "string") return ""; - return str - .replace(PATH_NAME_NORMALIZE_REPLACE_REGEX, "-") - .replace(MATCH_PADDED_HYPHENS_REPLACE_REGEX, ""); - } - - // map number to a single character a-z, A-Z or multiple characters if number is too big - /** - * @param {number} n number to convert to ident - * @returns {string} returns single character ident - */ - static numberToIdentifier(n) { - if (n >= NUMBER_OF_IDENTIFIER_START_CHARS) { - // use multiple letters - return ( - Template.numberToIdentifier(n % NUMBER_OF_IDENTIFIER_START_CHARS) + - Template.numberToIdentifierContinuation( - Math.floor(n / NUMBER_OF_IDENTIFIER_START_CHARS) - ) - ); - } - - // lower case - if (n < DELTA_A_TO_Z) { - return String.fromCharCode(START_LOWERCASE_ALPHABET_CODE + n); - } - n -= DELTA_A_TO_Z; - - // upper case - if (n < DELTA_A_TO_Z) { - return String.fromCharCode(START_UPPERCASE_ALPHABET_CODE + n); - } - - if (n === DELTA_A_TO_Z) return "_"; - return "$"; - } - - /** - * @param {number} n number to convert to ident - * @returns {string} returns single character ident - */ - static numberToIdentifierContinuation(n) { - if (n >= NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS) { - // use multiple letters - return ( - Template.numberToIdentifierContinuation( - n % NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS - ) + - Template.numberToIdentifierContinuation( - Math.floor(n / NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS) - ) - ); - } - - // lower case - if (n < DELTA_A_TO_Z) { - return String.fromCharCode(START_LOWERCASE_ALPHABET_CODE + n); - } - n -= DELTA_A_TO_Z; - - // upper case - if (n < DELTA_A_TO_Z) { - return String.fromCharCode(START_UPPERCASE_ALPHABET_CODE + n); - } - n -= DELTA_A_TO_Z; - - // numbers - if (n < 10) { - return `${n}`; - } - - if (n === 10) return "_"; - return "$"; - } - - /** - * - * @param {string | string[]} s string to convert to identity - * @returns {string} converted identity - */ - static indent(s) { - if (Array.isArray(s)) { - return s.map(Template.indent).join("\n"); - } else { - const str = s.trimEnd(); - if (!str) return ""; - const ind = str[0] === "\n" ? "" : "\t"; - return ind + str.replace(/\n([^\n])/g, "\n\t$1"); - } - } - - /** - * - * @param {string|string[]} s string to create prefix for - * @param {string} prefix prefix to compose - * @returns {string} returns new prefix string - */ - static prefix(s, prefix) { - const str = Template.asString(s).trim(); - if (!str) return ""; - const ind = str[0] === "\n" ? "" : prefix; - return ind + str.replace(/\n([^\n])/g, "\n" + prefix + "$1"); - } - - /** - * - * @param {string|string[]} str string or string collection - * @returns {string} returns a single string from array - */ - static asString(str) { - if (Array.isArray(str)) { - return str.join("\n"); - } - return str; - } - - /** - * @typedef {Object} WithId - * @property {string|number} id - */ - - /** - * @param {WithId[]} modules a collection of modules to get array bounds for - * @returns {[number, number] | false} returns the upper and lower array bounds - * or false if not every module has a number based id - */ - static getModulesArrayBounds(modules) { - let maxId = -Infinity; - let minId = Infinity; - for (const module of modules) { - const moduleId = module.id; - if (typeof moduleId !== "number") return false; - if (maxId < moduleId) maxId = moduleId; - if (minId > moduleId) minId = moduleId; - } - if (minId < 16 + ("" + minId).length) { - // add minId x ',' instead of 'Array(minId).concat(…)' - minId = 0; - } - // start with -1 because the first module needs no comma - let objectOverhead = -1; - for (const module of modules) { - // module id + colon + comma - objectOverhead += `${module.id}`.length + 2; - } - // number of commas, or when starting non-zero the length of Array(minId).concat() - const arrayOverhead = minId === 0 ? maxId : 16 + `${minId}`.length + maxId; - return arrayOverhead < objectOverhead ? [minId, maxId] : false; - } - - /** - * @param {ChunkRenderContext} renderContext render context - * @param {Module[]} modules modules to render (should be ordered by identifier) - * @param {function(Module): Source} renderModule function to render a module - * @param {string=} prefix applying prefix strings - * @returns {Source} rendered chunk modules in a Source object - */ - static renderChunkModules(renderContext, modules, renderModule, prefix = "") { - const { chunkGraph } = renderContext; - var source = new ConcatSource(); - if (modules.length === 0) { - return null; - } - /** @type {{id: string|number, source: Source|string}[]} */ - const allModules = modules.map(module => { - return { - id: chunkGraph.getModuleId(module), - source: renderModule(module) || "false" - }; - }); - const bounds = Template.getModulesArrayBounds(allModules); - if (bounds) { - // Render a spare array - const minId = bounds[0]; - const maxId = bounds[1]; - if (minId !== 0) { - source.add(`Array(${minId}).concat(`); - } - source.add("[\n"); - /** @type {Map} */ - const modules = new Map(); - for (const module of allModules) { - modules.set(module.id, module); - } - for (let idx = minId; idx <= maxId; idx++) { - const module = modules.get(idx); - if (idx !== minId) { - source.add(",\n"); - } - source.add(`/* ${idx} */`); - if (module) { - source.add("\n"); - source.add(module.source); - } - } - source.add("\n" + prefix + "]"); - if (minId !== 0) { - source.add(")"); - } - } else { - // Render an object - source.add("{\n"); - for (let i = 0; i < allModules.length; i++) { - const module = allModules[i]; - if (i !== 0) { - source.add(",\n"); - } - source.add(`\n/***/ ${JSON.stringify(module.id)}:\n`); - source.add(module.source); - } - source.add(`\n\n${prefix}}`); - } - return source; - } - - /** - * @param {RuntimeModule[]} runtimeModules array of runtime modules in order - * @param {RenderContext & { codeGenerationResults?: CodeGenerationResults }} renderContext render context - * @returns {Source} rendered runtime modules in a Source object - */ - static renderRuntimeModules(runtimeModules, renderContext) { - const source = new ConcatSource(); - for (const module of runtimeModules) { - const codeGenerationResults = renderContext.codeGenerationResults; - let runtimeSource; - if (codeGenerationResults) { - runtimeSource = codeGenerationResults.getSource( - module, - renderContext.chunk.runtime, - "runtime" - ); - } else { - const codeGenResult = module.codeGeneration({ - chunkGraph: renderContext.chunkGraph, - dependencyTemplates: renderContext.dependencyTemplates, - moduleGraph: renderContext.moduleGraph, - runtimeTemplate: renderContext.runtimeTemplate, - runtime: renderContext.chunk.runtime, - codeGenerationResults - }); - if (!codeGenResult) continue; - runtimeSource = codeGenResult.sources.get("runtime"); - } - if (runtimeSource) { - source.add(Template.toNormalComment(module.identifier()) + "\n"); - if (!module.shouldIsolate()) { - source.add(runtimeSource); - source.add("\n\n"); - } else if (renderContext.runtimeTemplate.supportsArrowFunction()) { - source.add("(() => {\n"); - source.add(new PrefixSource("\t", runtimeSource)); - source.add("\n})();\n\n"); - } else { - source.add("!function() {\n"); - source.add(new PrefixSource("\t", runtimeSource)); - source.add("\n}();\n\n"); - } - } - } - return source; - } - - /** - * @param {RuntimeModule[]} runtimeModules array of runtime modules in order - * @param {RenderContext} renderContext render context - * @returns {Source} rendered chunk runtime modules in a Source object - */ - static renderChunkRuntimeModules(runtimeModules, renderContext) { - return new PrefixSource( - "/******/ ", - new ConcatSource( - "function(__webpack_require__) { // webpackRuntimeModules\n", - this.renderRuntimeModules(runtimeModules, renderContext), - "}\n" - ) - ); - } -} - -module.exports = Template; -module.exports.NUMBER_OF_IDENTIFIER_START_CHARS = - NUMBER_OF_IDENTIFIER_START_CHARS; -module.exports.NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS = - NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS; diff --git a/packages/rspack/src/lib/util/create-schema-validation.js b/packages/rspack/src/lib/util/create-schema-validation.js deleted file mode 100644 index 290eaf47d651..000000000000 --- a/packages/rspack/src/lib/util/create-schema-validation.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -"use strict"; - -const memoize = require("./memoize"); - -const getValidate = memoize(() => require("schema-utils").validate); - -const createSchemaValidation = (check, getSchema, options) => { - getSchema = memoize(getSchema); - return value => { - if (check && !check(value)) { - getValidate()(getSchema(), value, options); - if (check) { - require("util").deprecate( - () => {}, - "webpack bug: Pre-compiled schema reports error while real schema is happy. This has performance drawbacks.", - "DEP_WEBPACK_PRE_COMPILED_SCHEMA_INVALID" - )(); - } - } - }; -}; - -module.exports = createSchemaValidation; diff --git a/packages/rspack/src/lib/util/memoize.js b/packages/rspack/src/lib/util/memoize.js deleted file mode 100644 index 981b53188820..000000000000 --- a/packages/rspack/src/lib/util/memoize.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php -*/ - -"use strict"; - -/** @template T @typedef {function(): T} FunctionReturning */ - -/** - * @template T - * @param {FunctionReturning} fn memorized function - * @returns {FunctionReturning} new function - */ -const memoize = fn => { - let cache = false; - /** @type {T} */ - let result = undefined; - return () => { - if (cache) { - return result; - } else { - result = fn(); - cache = true; - // Allow to clean up memory for fn - // and all dependent resources - fn = undefined; - return result; - } - }; -}; - -module.exports = memoize; diff --git a/packages/rspack/src/schemas/plugins/BannerPlugin.check.js b/packages/rspack/src/schemas/plugins/BannerPlugin.check.js deleted file mode 100644 index 7e5578b8bd1d..000000000000 --- a/packages/rspack/src/schemas/plugins/BannerPlugin.check.js +++ /dev/null @@ -1,327 +0,0 @@ -/* - * This file was automatically generated. - * DO NOT MODIFY BY HAND. - * Run `yarn special-lint-fix` to update - */ -"use strict"; -function n( - t, - { - instancePath: l = "", - parentData: e, - parentDataProperty: s, - rootData: a = t - } = {} -) { - let r = null, - o = 0; - const u = o; - let i = !1; - const p = o; - if (o === p) - if (Array.isArray(t)) { - const n = t.length; - for (let l = 0; l < n; l++) { - let n = t[l]; - const e = o, - s = o; - let a = !1, - u = null; - const i = o, - p = o; - let f = !1; - const h = o; - if (!(n instanceof RegExp)) { - const n = { params: {} }; - null === r ? (r = [n]) : r.push(n), o++; - } - var c = h === o; - if (((f = f || c), !f)) { - const t = o; - if (o === t) - if ("string" == typeof n) { - if (n.length < 1) { - const n = { params: {} }; - null === r ? (r = [n]) : r.push(n), o++; - } - } else { - const n = { params: { type: "string" } }; - null === r ? (r = [n]) : r.push(n), o++; - } - (c = t === o), (f = f || c); - } - if (f) (o = p), null !== r && (p ? (r.length = p) : (r = null)); - else { - const n = { params: {} }; - null === r ? (r = [n]) : r.push(n), o++; - } - if ((i === o && ((a = !0), (u = 0)), a)) - (o = s), null !== r && (s ? (r.length = s) : (r = null)); - else { - const n = { params: { passingSchemas: u } }; - null === r ? (r = [n]) : r.push(n), o++; - } - if (e !== o) break; - } - } else { - const n = { params: { type: "array" } }; - null === r ? (r = [n]) : r.push(n), o++; - } - var f = p === o; - if (((i = i || f), !i)) { - const n = o, - l = o; - let e = !1; - const s = o; - if (!(t instanceof RegExp)) { - const n = { params: {} }; - null === r ? (r = [n]) : r.push(n), o++; - } - var h = s === o; - if (((e = e || h), !e)) { - const n = o; - if (o === n) - if ("string" == typeof t) { - if (t.length < 1) { - const n = { params: {} }; - null === r ? (r = [n]) : r.push(n), o++; - } - } else { - const n = { params: { type: "string" } }; - null === r ? (r = [n]) : r.push(n), o++; - } - (h = n === o), (e = e || h); - } - if (e) (o = l), null !== r && (l ? (r.length = l) : (r = null)); - else { - const n = { params: {} }; - null === r ? (r = [n]) : r.push(n), o++; - } - (f = n === o), (i = i || f); - } - if (!i) { - const t = { params: {} }; - return null === r ? (r = [t]) : r.push(t), o++, (n.errors = r), !1; - } - return ( - (o = u), - null !== r && (u ? (r.length = u) : (r = null)), - (n.errors = r), - 0 === o - ); -} -function t( - l, - { - instancePath: e = "", - parentData: s, - parentDataProperty: a, - rootData: r = l - } = {} -) { - let o = null, - u = 0; - const i = u; - let p = !1; - const c = u; - if (u === c) - if ("string" == typeof l) { - if (l.length < 1) { - const n = { params: {} }; - null === o ? (o = [n]) : o.push(n), u++; - } - } else { - const n = { params: { type: "string" } }; - null === o ? (o = [n]) : o.push(n), u++; - } - var f = c === u; - if (((p = p || f), !p)) { - const t = u; - if (u === t) - if (l && "object" == typeof l && !Array.isArray(l)) { - let t; - if (void 0 === l.banner && (t = "banner")) { - const n = { params: { missingProperty: t } }; - null === o ? (o = [n]) : o.push(n), u++; - } else { - const t = u; - for (const n in l) - if ( - "banner" !== n && - "entryOnly" !== n && - "exclude" !== n && - "footer" !== n && - "include" !== n && - "raw" !== n && - "test" !== n - ) { - const t = { params: { additionalProperty: n } }; - null === o ? (o = [t]) : o.push(t), u++; - break; - } - if (t === u) { - if (void 0 !== l.banner) { - let n = l.banner; - const t = u, - e = u; - let s = !1; - const a = u; - if ("string" != typeof n) { - const n = { params: { type: "string" } }; - null === o ? (o = [n]) : o.push(n), u++; - } - var h = a === u; - if (((s = s || h), !s)) { - const t = u; - if (!(n instanceof Function)) { - const n = { params: {} }; - null === o ? (o = [n]) : o.push(n), u++; - } - (h = t === u), (s = s || h); - } - if (s) (u = e), null !== o && (e ? (o.length = e) : (o = null)); - else { - const n = { params: {} }; - null === o ? (o = [n]) : o.push(n), u++; - } - var y = t === u; - } else y = !0; - if (y) { - if (void 0 !== l.entryOnly) { - const n = u; - if ("boolean" != typeof l.entryOnly) { - const n = { params: { type: "boolean" } }; - null === o ? (o = [n]) : o.push(n), u++; - } - y = n === u; - } else y = !0; - if (y) { - if (void 0 !== l.exclude) { - const t = u, - s = u; - let a = !1, - i = null; - const p = u; - if ( - (n(l.exclude, { - instancePath: e + "/exclude", - parentData: l, - parentDataProperty: "exclude", - rootData: r - }) || - ((o = null === o ? n.errors : o.concat(n.errors)), - (u = o.length)), - p === u && ((a = !0), (i = 0)), - a) - ) - (u = s), null !== o && (s ? (o.length = s) : (o = null)); - else { - const n = { params: { passingSchemas: i } }; - null === o ? (o = [n]) : o.push(n), u++; - } - y = t === u; - } else y = !0; - if (y) { - if (void 0 !== l.footer) { - const n = u; - if ("boolean" != typeof l.footer) { - const n = { params: { type: "boolean" } }; - null === o ? (o = [n]) : o.push(n), u++; - } - y = n === u; - } else y = !0; - if (y) { - if (void 0 !== l.include) { - const t = u, - s = u; - let a = !1, - i = null; - const p = u; - if ( - (n(l.include, { - instancePath: e + "/include", - parentData: l, - parentDataProperty: "include", - rootData: r - }) || - ((o = null === o ? n.errors : o.concat(n.errors)), - (u = o.length)), - p === u && ((a = !0), (i = 0)), - a) - ) - (u = s), - null !== o && (s ? (o.length = s) : (o = null)); - else { - const n = { params: { passingSchemas: i } }; - null === o ? (o = [n]) : o.push(n), u++; - } - y = t === u; - } else y = !0; - if (y) { - if (void 0 !== l.raw) { - const n = u; - if ("boolean" != typeof l.raw) { - const n = { params: { type: "boolean" } }; - null === o ? (o = [n]) : o.push(n), u++; - } - y = n === u; - } else y = !0; - if (y) - if (void 0 !== l.test) { - const t = u, - s = u; - let a = !1, - i = null; - const p = u; - if ( - (n(l.test, { - instancePath: e + "/test", - parentData: l, - parentDataProperty: "test", - rootData: r - }) || - ((o = null === o ? n.errors : o.concat(n.errors)), - (u = o.length)), - p === u && ((a = !0), (i = 0)), - a) - ) - (u = s), - null !== o && (s ? (o.length = s) : (o = null)); - else { - const n = { params: { passingSchemas: i } }; - null === o ? (o = [n]) : o.push(n), u++; - } - y = t === u; - } else y = !0; - } - } - } - } - } - } - } - } else { - const n = { params: { type: "object" } }; - null === o ? (o = [n]) : o.push(n), u++; - } - if (((f = t === u), (p = p || f), !p)) { - const n = u; - if (!(l instanceof Function)) { - const n = { params: {} }; - null === o ? (o = [n]) : o.push(n), u++; - } - (f = n === u), (p = p || f); - } - } - if (!p) { - const n = { params: {} }; - return null === o ? (o = [n]) : o.push(n), u++, (t.errors = o), !1; - } - return ( - (u = i), - null !== o && (i ? (o.length = i) : (o = null)), - (t.errors = o), - 0 === u - ); -} -(module.exports = t), (module.exports.default = t); diff --git a/packages/rspack/src/schemas/plugins/BannerPlugin.json b/packages/rspack/src/schemas/plugins/BannerPlugin.json deleted file mode 100644 index 2e6bae067f72..000000000000 --- a/packages/rspack/src/schemas/plugins/BannerPlugin.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "definitions": { - "BannerFunction": { - "description": "The banner as function, it will be wrapped in a comment.", - "instanceof": "Function", - "tsType": "(data: { hash: string, chunk: import('../../lib/Chunk'), filename: string }) => string" - }, - "Rule": { - "description": "Filtering rule as regex or string.", - "anyOf": [ - { - "instanceof": "RegExp", - "tsType": "RegExp" - }, - { - "type": "string", - "minLength": 1 - } - ] - }, - "Rules": { - "description": "Filtering rules.", - "anyOf": [ - { - "type": "array", - "items": { - "description": "A rule condition.", - "oneOf": [ - { - "$ref": "#/definitions/Rule" - } - ] - } - }, - { - "$ref": "#/definitions/Rule" - } - ] - } - }, - "title": "BannerPluginArgument", - "anyOf": [ - { - "description": "The banner as string, it will be wrapped in a comment.", - "type": "string", - "minLength": 1 - }, - { - "title": "BannerPluginOptions", - "type": "object", - "additionalProperties": false, - "properties": { - "banner": { - "description": "Specifies the banner.", - "anyOf": [ - { - "type": "string" - }, - { - "$ref": "#/definitions/BannerFunction" - } - ] - }, - "entryOnly": { - "description": "If true, the banner will only be added to the entry chunks.", - "type": "boolean" - }, - "exclude": { - "description": "Exclude all modules matching any of these conditions.", - "oneOf": [ - { - "$ref": "#/definitions/Rules" - } - ] - }, - "footer": { - "description": "If true, banner will be placed at the end of the output.", - "type": "boolean" - }, - "include": { - "description": "Include all modules matching any of these conditions.", - "oneOf": [ - { - "$ref": "#/definitions/Rules" - } - ] - }, - "raw": { - "description": "If true, banner will not be wrapped in a comment.", - "type": "boolean" - }, - "test": { - "description": "Include all modules that pass test assertion.", - "oneOf": [ - { - "$ref": "#/definitions/Rules" - } - ] - } - }, - "required": ["banner"] - }, - { - "$ref": "#/definitions/BannerFunction" - } - ] -} diff --git a/packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.check.js b/packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.check.js deleted file mode 100644 index d8cb9c6c3f1b..000000000000 --- a/packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.check.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file was automatically generated. - * DO NOT MODIFY BY HAND. - * Run `yarn special-lint-fix` to update - */ -const r = /^(?:[A-Za-z]:[\\/]|\\\\|\/)/; -function e( - t, - { - instancePath: o = "", - parentData: a, - parentDataProperty: i, - rootData: n = t - } = {} -) { - if (!t || "object" != typeof t || Array.isArray(t)) - return (e.errors = [{ params: { type: "object" } }]), !1; - if (void 0 !== t.debug) { - const r = 0; - if ("boolean" != typeof t.debug) - return (e.errors = [{ params: { type: "boolean" } }]), !1; - var s = 0 === r; - } else s = !0; - if (s) { - if (void 0 !== t.minimize) { - const r = 0; - if ("boolean" != typeof t.minimize) - return (e.errors = [{ params: { type: "boolean" } }]), !1; - s = 0 === r; - } else s = !0; - if (s) - if (void 0 !== t.options) { - let o = t.options; - const a = 0; - if (0 === a) { - if (!o || "object" != typeof o || Array.isArray(o)) - return (e.errors = [{ params: { type: "object" } }]), !1; - if (void 0 !== o.context) { - let t = o.context; - if ("string" != typeof t) - return (e.errors = [{ params: { type: "string" } }]), !1; - if (t.includes("!") || !0 !== r.test(t)) - return (e.errors = [{ params: {} }]), !1; - } - } - s = 0 === a; - } else s = !0; - } - return (e.errors = null), !0; -} -(module.exports = e), (module.exports.default = e); diff --git a/packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.json b/packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.json deleted file mode 100644 index 912095c59757..000000000000 --- a/packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "title": "LoaderOptionsPluginOptions", - "type": "object", - "additionalProperties": true, - "properties": { - "debug": { - "description": "Whether loaders should be in debug mode or not. debug will be removed as of webpack 3.", - "type": "boolean" - }, - "minimize": { - "description": "Where loaders can be switched to minimize mode.", - "type": "boolean" - }, - "options": { - "description": "A configuration object that can be used to configure older loaders.", - "type": "object", - "additionalProperties": true, - "properties": { - "context": { - "description": "The context that can be used to configure older loaders.", - "type": "string", - "absolutePath": true - } - } - } - } -} From 033f5a164bad4d64e2f82d2602ec5fac8594ee89 Mon Sep 17 00:00:00 2001 From: Gengkun Date: Fri, 8 Sep 2023 16:39:41 +0800 Subject: [PATCH 26/30] feat: react refresh plugin (#4135) * feat: react refresh plugin * dep dev-client * update snap * e2e dev-client --- .../basic-deprecated-dev-client/index.test.ts | 58 ++++++ .../react-refresh.js | 17 ++ .../rspack.config.js | 36 ++++ .../basic-deprecated-dev-client/src/App.jsx | 25 +++ .../src/CountProvider.jsx | 19 ++ .../src/ReactRefreshFinder.jsx | 10 + .../src/SameExportName1.jsx | 3 + .../src/SameExportName2.jsx | 3 + .../basic-deprecated-dev-client/src/index.css | 3 + .../src/index.html | 12 ++ .../basic-deprecated-dev-client/src/index.jsx | 11 + .../cases/react/basic/react-refresh.js | 2 +- .../cases/react/basic/rspack.config.js | 5 +- .../react/class-component/react-refresh.js | 2 +- .../react/class-component/rspack.config.js | 5 +- packages/playground/package.json | 1 + packages/rspack-dev-client/README.md | 2 + packages/rspack-dev-client/package.json | 12 +- .../rspack-dev-client/src/reactRefresh.js | 24 +-- .../src/reactRefreshEntry.js | 5 +- .../rspack-dev-client/src/runtimePaths.js | 19 -- packages/rspack-dev-server/package.json | 2 +- packages/rspack-dev-server/src/server.ts | 196 +----------------- .../normalizeOptions.test.ts.snap | 8 +- .../rspack-plugin-react-refresh/CHANGELOG.md | 1 + packages/rspack-plugin-react-refresh/LICENSE | 22 ++ .../rspack-plugin-react-refresh/README.md | 16 ++ .../client/reactRefresh.js | 23 ++ .../client/reactRefreshEntry.js | 4 + .../rspack-plugin-react-refresh/package.json | 40 ++++ .../rspack-plugin-react-refresh/src/index.js | 35 ++++ packages/rspack/package.json | 1 - pnpm-lock.yaml | 74 ++----- webpack-test/package.json | 1 - 34 files changed, 389 insertions(+), 308 deletions(-) create mode 100644 packages/playground/cases/react/basic-deprecated-dev-client/index.test.ts create mode 100644 packages/playground/cases/react/basic-deprecated-dev-client/react-refresh.js create mode 100644 packages/playground/cases/react/basic-deprecated-dev-client/rspack.config.js create mode 100644 packages/playground/cases/react/basic-deprecated-dev-client/src/App.jsx create mode 100644 packages/playground/cases/react/basic-deprecated-dev-client/src/CountProvider.jsx create mode 100644 packages/playground/cases/react/basic-deprecated-dev-client/src/ReactRefreshFinder.jsx create mode 100644 packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName1.jsx create mode 100644 packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName2.jsx create mode 100644 packages/playground/cases/react/basic-deprecated-dev-client/src/index.css create mode 100644 packages/playground/cases/react/basic-deprecated-dev-client/src/index.html create mode 100644 packages/playground/cases/react/basic-deprecated-dev-client/src/index.jsx delete mode 100644 packages/rspack-dev-client/src/runtimePaths.js create mode 100644 packages/rspack-plugin-react-refresh/CHANGELOG.md create mode 100644 packages/rspack-plugin-react-refresh/LICENSE create mode 100644 packages/rspack-plugin-react-refresh/README.md create mode 100644 packages/rspack-plugin-react-refresh/client/reactRefresh.js create mode 100644 packages/rspack-plugin-react-refresh/client/reactRefreshEntry.js create mode 100644 packages/rspack-plugin-react-refresh/package.json create mode 100644 packages/rspack-plugin-react-refresh/src/index.js diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/index.test.ts b/packages/playground/cases/react/basic-deprecated-dev-client/index.test.ts new file mode 100644 index 000000000000..a6a420c194bb --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/index.test.ts @@ -0,0 +1,58 @@ +import { test, expect } from "@/fixtures"; + +test("render should work", async ({ page }) => { + expect(await page.textContent(".header")).toBe("Hello World"); +}); + +test("hmr should work", async ({ page, fileAction, rspack }) => { + expect(await page.textContent("button")).toBe("10"); + await page.click("button"); + expect(await page.textContent("button")).toBe("11"); + expect(await page.textContent(".placeholder")).toBe("__PLACE_HOLDER__"); + fileAction.updateFile("src/App.jsx", content => + content.replace("__PLACE_HOLDER__", "__EDITED__") + ); + await rspack.waitingForHmr(async function () { + return (await page.textContent(".placeholder")) === "__EDITED__"; + }); + expect(await page.textContent("button")).toBe("11"); +}); + +test("context+component should work", async ({ page, fileAction, rspack }) => { + expect(await page.textContent("#context")).toBe("context-value"); + await page.click("#context"); + expect(await page.textContent("#context")).toBe("context-value-click"); + fileAction.updateFile("src/CountProvider.jsx", content => + content.replace("context-value", "context-value-update") + ); + await rspack.waitingForHmr(async function () { + return (await page.textContent("#context")) === "context-value-update"; + }); +}); + +test("ReactRefreshFinder should work", async ({ page }) => { + expect(await page.textContent("#nest-function")).toBe("nest-function"); +}); + +test("update same export name from different module should work", async ({ + page, + fileAction, + rspack +}) => { + expect(await page.textContent(".same-export-name1")).toBe("__NAME_1__"); + expect(await page.textContent(".same-export-name2")).toBe("__NAME_2__"); + fileAction.updateFile("src/SameExportName1.jsx", content => + content.replace("__NAME_1__", "__name_1__") + ); + await rspack.waitingForHmr(async function () { + return (await page.textContent(".same-export-name1")) === "__name_1__"; + }); + expect(await page.textContent(".same-export-name2")).toBe("__NAME_2__"); + fileAction.updateFile("src/SameExportName2.jsx", content => + content.replace("__NAME_2__", "__name_2__") + ); + await rspack.waitingForHmr(async function () { + return (await page.textContent(".same-export-name2")) === "__name_2__"; + }); + expect(await page.textContent(".same-export-name1")).toBe("__name_1__"); +}); diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/react-refresh.js b/packages/playground/cases/react/basic-deprecated-dev-client/react-refresh.js new file mode 100644 index 000000000000..e4b58b1fff8e --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/react-refresh.js @@ -0,0 +1,17 @@ +const reactRefresh = require("@rspack/plugin-react-refresh/react-refresh"); + +function shouldLooksLikeAModuleId(id) { + console.log(id); + if (typeof id === "string" && !id.includes("[object Object]")) { + return; + } + throw new Error(`Looks like ${id} is not a module.id`); +} + +module.exports = { + ...reactRefresh, + register(type, id) { + shouldLooksLikeAModuleId(id); + return reactRefresh.register(type, id); + } +}; diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/rspack.config.js b/packages/playground/cases/react/basic-deprecated-dev-client/rspack.config.js new file mode 100644 index 000000000000..e44d143da1f3 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/rspack.config.js @@ -0,0 +1,36 @@ +/** @type { import('@rspack/core').RspackOptions } */ +module.exports = { + context: __dirname, + mode: "development", + module: { + // add this to test react refresh runtime shouldn't inject runtime,see #3984 + rules: [ + { + test: /\.js$/, + type: "jsx" + } + ] + }, + entry: ["@rspack/dev-client/react-refresh-entry", "./src/index.jsx"], + devServer: { + hot: true + }, + cache: false, + stats: "none", + infrastructureLogging: { + debug: false + }, + builtins: { + provide: { + $ReactRefreshRuntime$: [require.resolve("./react-refresh.js")] + }, + html: [ + { + template: "./src/index.html" + } + ] + }, + watchOptions: { + poll: 1000 + } +}; diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/App.jsx b/packages/playground/cases/react/basic-deprecated-dev-client/src/App.jsx new file mode 100644 index 000000000000..229770f9d16c --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/App.jsx @@ -0,0 +1,25 @@ +import React from "react"; +import "./index.css"; +import { ContextComponent } from "./CountProvider"; +import { ReactRefreshFinder } from "./ReactRefreshFinder"; +import { SameExportName as SameExportName1 } from './SameExportName1' +import { SameExportName as SameExportName2 } from './SameExportName2' + +const Button = () => { + const [count, setCount] = React.useState(10); + return ; +}; + +export const App = () => { + return ( +
+
Hello World
+
+ ); +}; diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/CountProvider.jsx b/packages/playground/cases/react/basic-deprecated-dev-client/src/CountProvider.jsx new file mode 100644 index 000000000000..afbe1fcbe342 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/CountProvider.jsx @@ -0,0 +1,19 @@ +import React from 'react'; + +export const CountContext = React.createContext(); + +export function CountProvider({ children }) { + const [count, setCount] = React.useState('context-value'); + return ( + + {children} + + ); +} + +export function ContextComponent() { + const { count, setCount } = React.useContext(CountContext); + return
setCount((count) => count + '-click')}> + {count} +
+} \ No newline at end of file diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/ReactRefreshFinder.jsx b/packages/playground/cases/react/basic-deprecated-dev-client/src/ReactRefreshFinder.jsx new file mode 100644 index 000000000000..83b0509d747b --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/ReactRefreshFinder.jsx @@ -0,0 +1,10 @@ +import React, { useState } from 'react'; + +function CreateReactRefreshFinder() { + return function Component() { + useState(1); + return
nest-function
; + }; +} + +export const ReactRefreshFinder = CreateReactRefreshFinder() \ No newline at end of file diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName1.jsx b/packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName1.jsx new file mode 100644 index 000000000000..2a7f2b45f152 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName1.jsx @@ -0,0 +1,3 @@ +export function SameExportName() { + return
__NAME_1__
+} diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName2.jsx b/packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName2.jsx new file mode 100644 index 000000000000..8bf62ab6bb51 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName2.jsx @@ -0,0 +1,3 @@ +export function SameExportName() { + return
__NAME_2__
+} diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/index.css b/packages/playground/cases/react/basic-deprecated-dev-client/src/index.css new file mode 100644 index 000000000000..753f48a591f8 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/index.css @@ -0,0 +1,3 @@ +body { + background-color: rgba(0, 0, 0, 0); +} diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/index.html b/packages/playground/cases/react/basic-deprecated-dev-client/src/index.html new file mode 100644 index 000000000000..127a457455b3 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/index.html @@ -0,0 +1,12 @@ + + + + + + + Document + + +
+ + diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/index.jsx b/packages/playground/cases/react/basic-deprecated-dev-client/src/index.jsx new file mode 100644 index 000000000000..52e72c3334e5 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/index.jsx @@ -0,0 +1,11 @@ +import React from "react"; +import { createRoot } from "react-dom/client"; +import { App } from './App'; +import { CountProvider } from "./CountProvider"; + +const container = createRoot(document.getElementById("root")); +container.render( + + + +); diff --git a/packages/playground/cases/react/basic/react-refresh.js b/packages/playground/cases/react/basic/react-refresh.js index 17da450925d1..e4b58b1fff8e 100644 --- a/packages/playground/cases/react/basic/react-refresh.js +++ b/packages/playground/cases/react/basic/react-refresh.js @@ -1,4 +1,4 @@ -const reactRefresh = require("@rspack/dev-client/react-refresh"); +const reactRefresh = require("@rspack/plugin-react-refresh/react-refresh"); function shouldLooksLikeAModuleId(id) { console.log(id); diff --git a/packages/playground/cases/react/basic/rspack.config.js b/packages/playground/cases/react/basic/rspack.config.js index e44d143da1f3..2d75adbfe2db 100644 --- a/packages/playground/cases/react/basic/rspack.config.js +++ b/packages/playground/cases/react/basic/rspack.config.js @@ -11,7 +11,10 @@ module.exports = { } ] }, - entry: ["@rspack/dev-client/react-refresh-entry", "./src/index.jsx"], + entry: [ + "@rspack/plugin-react-refresh/react-refresh-entry", + "./src/index.jsx" + ], devServer: { hot: true }, diff --git a/packages/playground/cases/react/class-component/react-refresh.js b/packages/playground/cases/react/class-component/react-refresh.js index 17da450925d1..e4b58b1fff8e 100644 --- a/packages/playground/cases/react/class-component/react-refresh.js +++ b/packages/playground/cases/react/class-component/react-refresh.js @@ -1,4 +1,4 @@ -const reactRefresh = require("@rspack/dev-client/react-refresh"); +const reactRefresh = require("@rspack/plugin-react-refresh/react-refresh"); function shouldLooksLikeAModuleId(id) { console.log(id); diff --git a/packages/playground/cases/react/class-component/rspack.config.js b/packages/playground/cases/react/class-component/rspack.config.js index f8b513d6e682..3872f4f3a0e0 100644 --- a/packages/playground/cases/react/class-component/rspack.config.js +++ b/packages/playground/cases/react/class-component/rspack.config.js @@ -2,7 +2,10 @@ module.exports = { context: __dirname, mode: "development", - entry: ["@rspack/dev-client/react-refresh-entry", "./src/index.jsx"], + entry: [ + "@rspack/plugin-react-refresh/react-refresh-entry", + "./src/index.jsx" + ], devServer: { hot: true }, diff --git a/packages/playground/package.json b/packages/playground/package.json index ae3600fe8983..727b958a1857 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -14,6 +14,7 @@ "devDependencies": { "@playwright/test": "1.35.0", "@rspack/core": "workspace:*", + "@rspack/plugin-react-refresh": "workspace:*", "@rspack/dev-client": "workspace:*", "@rspack/dev-server": "workspace:*", "@types/fs-extra": "11.0.1", diff --git a/packages/rspack-dev-client/README.md b/packages/rspack-dev-client/README.md index 84ea27106ad2..e8cff60366bd 100644 --- a/packages/rspack-dev-client/README.md +++ b/packages/rspack-dev-client/README.md @@ -3,6 +3,8 @@ Rspack Banner +# ⚠️ DEPRECATED: use @rspack/plugin-react-refresh instead + # @rspack/dev-client Development client for rspack. diff --git a/packages/rspack-dev-client/package.json b/packages/rspack-dev-client/package.json index 1e8b13c7d12c..9d3317c5d4ec 100644 --- a/packages/rspack-dev-client/package.json +++ b/packages/rspack-dev-client/package.json @@ -18,12 +18,11 @@ "url": "https://github.com/web-infra-dev/rspack", "directory": "packages/rspack-dev-client" }, - "devDependencies": { - "react-refresh": "0.14.0", - "webpack": "5.76.0" - }, "dependencies": { - "@pmmmwh/react-refresh-webpack-plugin": "0.5.10" + "@rspack/plugin-react-refresh": "workspace:*" + }, + "devDependencies": { + "react-refresh": "0.14.0" }, "peerDependencies": { "react-refresh": ">=0.10.0 <1.0.0" @@ -35,7 +34,6 @@ }, "exports": { "./react-refresh": "./src/reactRefresh.js", - "./react-refresh-entry": "./src/reactRefreshEntry.js", - "./runtime-paths": "./src/runtimePaths.js" + "./react-refresh-entry": "./src/reactRefreshEntry.js" } } \ No newline at end of file diff --git a/packages/rspack-dev-client/src/reactRefresh.js b/packages/rspack-dev-client/src/reactRefresh.js index 7c24482448bb..939000d557cf 100644 --- a/packages/rspack-dev-client/src/reactRefresh.js +++ b/packages/rspack-dev-client/src/reactRefresh.js @@ -1,23 +1 @@ -// Thanks https://github.com/pmmmwh/react-refresh-webpack-plugin -const RefreshUtils = require("@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils"); -const RefreshRuntime = require("react-refresh/runtime"); - -// Port from https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/loader/utils/getRefreshModuleRuntime.js#L29 -function refresh(moduleId, webpackHot) { - const currentExports = RefreshUtils.getModuleExports(moduleId); - const fn = exports => { - RefreshUtils.executeRuntime(exports, moduleId, webpackHot); - }; - if (typeof Promise !== "undefined" && currentExports instanceof Promise) { - currentExports.then(fn); - } else { - fn(currentExports); - } -} - -module.exports = { - refresh, - register: RefreshRuntime.register, - createSignatureFunctionForTransform: - RefreshRuntime.createSignatureFunctionForTransform -}; +module.exports = require("@rspack/plugin-react-refresh/react-refresh"); diff --git a/packages/rspack-dev-client/src/reactRefreshEntry.js b/packages/rspack-dev-client/src/reactRefreshEntry.js index 26eb38307b34..8f77c1e7d783 100644 --- a/packages/rspack-dev-client/src/reactRefreshEntry.js +++ b/packages/rspack-dev-client/src/reactRefreshEntry.js @@ -1,4 +1 @@ -const RefreshRuntime = require("react-refresh/runtime"); - -RefreshRuntime.injectIntoGlobalHook(self); -self.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform; +require("@rspack/plugin-react-refresh/react-refresh-entry"); diff --git a/packages/rspack-dev-client/src/runtimePaths.js b/packages/rspack-dev-client/src/runtimePaths.js deleted file mode 100644 index 8d23c16347e5..000000000000 --- a/packages/rspack-dev-client/src/runtimePaths.js +++ /dev/null @@ -1,19 +0,0 @@ -const path = require('path') -const reactRefreshPath = require.resolve('./reactRefresh.js') -const RefreshUtilsPath = require.resolve( - '@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils', - { - paths: [reactRefreshPath] - } -) -const RefreshRuntimeDirPath = path.dirname( - require.resolve('react-refresh', { - paths: [reactRefreshPath] - }) -) - -exports.runtimePaths = [ - reactRefreshPath, - RefreshUtilsPath, - RefreshRuntimeDirPath -] diff --git a/packages/rspack-dev-server/package.json b/packages/rspack-dev-server/package.json index 815aee3c3349..047bbdcfd1f4 100644 --- a/packages/rspack-dev-server/package.json +++ b/packages/rspack-dev-server/package.json @@ -36,8 +36,8 @@ "puppeteer": "19.4.0" }, "dependencies": { - "@rspack/dev-client": "workspace:*", "@rspack/dev-server": "workspace:*", + "@rspack/plugin-react-refresh": "workspace:*", "chokidar": "3.5.3", "connect-history-api-fallback": "2.0.0", "express": "4.18.1", diff --git a/packages/rspack-dev-server/src/server.ts b/packages/rspack-dev-server/src/server.ts index f7f6748e2174..b77316653833 100644 --- a/packages/rspack-dev-server/src/server.ts +++ b/packages/rspack-dev-server/src/server.ts @@ -13,12 +13,11 @@ import type { FSWatcher } from "chokidar"; import rdm from "webpack-dev-middleware"; import type { Server } from "http"; import fs from "fs"; +import ReactRefreshPlugin from "@rspack/plugin-react-refresh"; import WebpackDevServer from "webpack-dev-server"; import type { ResolvedDevServer, DevServer } from "./config"; import { getRspackMemoryAssets } from "./middleware"; import { applyDevServerPatch } from "./patch"; -// @ts-expect-error -import { runtimePaths } from "@rspack/dev-client/runtime-paths"; applyDevServerPatch(); @@ -78,178 +77,6 @@ export class RspackDevServer extends WebpackDevServer { ); } - addAdditionalEntries(compiler: Compiler) { - const additionalEntries: string[] = []; - // @ts-expect-error - const isWebTarget = WebpackDevServer.isWebTarget(compiler); - // inject runtime first, avoid other additional entry after transfrom depend on it - const clientPath = require.resolve("webpack-dev-server/client/index.js"); - if (this.options.hot) { - if (compiler.options.builtins.react?.refresh) { - const reactRefreshEntryPath = require.resolve( - "@rspack/dev-client/react-refresh-entry" - ); - additionalEntries.push(reactRefreshEntryPath); - } - // #3356 make sure resolve webpack/hot/dev-server from webpack-dev-server - const hotUpdateEntryPath = require.resolve("webpack/hot/dev-server", { - paths: [clientPath] - }); - additionalEntries.push(hotUpdateEntryPath); - } - if (this.options.client && isWebTarget) { - let webSocketURLStr = ""; - - if (this.options.webSocketServer) { - const webSocketURL = this.options.client - .webSocketURL as WebpackDevServer.WebSocketURL; - const webSocketServer = this.options.webSocketServer; - const searchParams = new URLSearchParams(); - - let protocol: string; - - // We are proxying dev server and need to specify custom `hostname` - if (typeof webSocketURL.protocol !== "undefined") { - protocol = webSocketURL.protocol; - } else { - protocol = this.options.server.type === "http" ? "ws:" : "wss:"; - } - - searchParams.set("protocol", protocol); - - if (typeof webSocketURL.username !== "undefined") { - searchParams.set("username", webSocketURL.username); - } - - if (typeof webSocketURL.password !== "undefined") { - searchParams.set("password", webSocketURL.password); - } - - let hostname: string; - - // SockJS is not supported server mode, so `hostname` and `port` can't specified, let's ignore them - // TODO show warning about this - const isSockJSType = webSocketServer.type === "sockjs"; - - // We are proxying dev server and need to specify custom `hostname` - if (typeof webSocketURL.hostname !== "undefined") { - hostname = webSocketURL.hostname; - } - // Web socket server works on custom `hostname`, only for `ws` because `sock-js` is not support custom `hostname` - else if ( - typeof webSocketServer.options.host !== "undefined" && - !isSockJSType - ) { - hostname = webSocketServer.options.host; - } - // The `host` option is specified - else if (typeof this.options.host !== "undefined") { - hostname = this.options.host; - } - // The `port` option is not specified - else { - hostname = "0.0.0.0"; - } - - searchParams.set("hostname", hostname); - - let port: number | string; - - // We are proxying dev server and need to specify custom `port` - if (typeof webSocketURL.port !== "undefined") { - port = webSocketURL.port; - } - // Web socket server works on custom `port`, only for `ws` because `sock-js` is not support custom `port` - else if ( - typeof webSocketServer.options.port !== "undefined" && - !isSockJSType - ) { - port = webSocketServer.options.port; - } - // The `port` option is specified - else if (typeof this.options.port === "number") { - port = this.options.port; - } - // The `port` option is specified using `string` - else if ( - typeof this.options.port === "string" && - this.options.port !== "auto" - ) { - port = Number(this.options.port); - } - // The `port` option is not specified or set to `auto` - else { - port = "0"; - } - - searchParams.set("port", String(port)); - - let pathname = ""; - - // We are proxying dev server and need to specify custom `pathname` - if (typeof webSocketURL.pathname !== "undefined") { - pathname = webSocketURL.pathname; - } - // Web socket server works on custom `path` - else if ( - typeof webSocketServer.options.prefix !== "undefined" || - typeof webSocketServer.options.path !== "undefined" - ) { - pathname = - webSocketServer.options.prefix || webSocketServer.options.path; - } - - searchParams.set("pathname", pathname); - - const client = /** @type {ClientConfiguration} */ this.options.client; - - if (typeof client.logging !== "undefined") { - searchParams.set("logging", client.logging); - } - - if (typeof client.progress !== "undefined") { - searchParams.set("progress", String(client.progress)); - } - - if (typeof client.overlay !== "undefined") { - searchParams.set( - "overlay", - typeof client.overlay === "boolean" - ? String(client.overlay) - : JSON.stringify(client.overlay) - ); - } - - if (typeof client.reconnect !== "undefined") { - searchParams.set( - "reconnect", - typeof client.reconnect === "number" - ? String(client.reconnect) - : "10" - ); - } - - if (typeof this.options.hot !== "undefined") { - searchParams.set("hot", String(this.options.hot)); - } - - if (typeof this.options.liveReload !== "undefined") { - searchParams.set("live-reload", String(this.options.liveReload)); - } - - webSocketURLStr = searchParams.toString(); - } - - additionalEntries.push(`${clientPath}?${webSocketURLStr}`); - } - - for (const additionalEntry of additionalEntries) { - new compiler.webpack.EntryPlugin(compiler.context, additionalEntry, { - name: undefined - }).apply(compiler); - } - } - getClientTransport(): string { // WARNING: we can't use `super.getClientTransport`, // because we doesn't had same directory structure. @@ -332,10 +159,7 @@ export class RspackDevServer extends WebpackDevServer { compiler.options.builtins.react ??= {}; compiler.options.builtins.react.refresh ??= true; compiler.options.builtins.react.development ??= true; - compiler.options.builtins.provide ??= {}; - compiler.options.builtins.provide.$ReactRefreshRuntime$ ??= [ - require.resolve("@rspack/dev-client/react-refresh") - ]; + new ReactRefreshPlugin().apply(compiler); } else if (compiler.options.builtins.react.refresh) { if (mode === "production") { this.logger.warn( @@ -352,19 +176,11 @@ export class RspackDevServer extends WebpackDevServer { if (this.options.webSocketServer) { compilers.forEach(compiler => { + // @ts-expect-error: `addAdditionalEntries` is private function in base class. this.addAdditionalEntries(compiler); - - if (this.options.hot) { - compiler.options.module.rules.push({ - include: runtimePaths, - type: "js" - }); - } - - compiler.options.builtins.provide = { - ...compiler.options.builtins.provide, - __webpack_dev_server_client__: [this.getClientTransport()] - }; + new compiler.webpack.ProvidePlugin({ + __webpack_dev_server_client__: this.getClientTransport() + }).apply(compiler); }); } diff --git a/packages/rspack-dev-server/tests/__snapshots__/normalizeOptions.test.ts.snap b/packages/rspack-dev-server/tests/__snapshots__/normalizeOptions.test.ts.snap index 96ec4e57c972..77c7fc6b02aa 100644 --- a/packages/rspack-dev-server/tests/__snapshots__/normalizeOptions.test.ts.snap +++ b/packages/rspack-dev-server/tests/__snapshots__/normalizeOptions.test.ts.snap @@ -6,9 +6,9 @@ exports[`normalize options snapshot additional entires should added 1`] = ` "/./placeholder.js", ], "undefined": [ - "/rspack-dev-client/src/reactRefreshEntry.js", - "/webpack/hot/dev-server.js", + "/rspack-plugin-react-refresh/client/reactRefreshEntry.js", "/webpack-dev-server/client/index.js?protocol=ws%3A&hostname=0.0.0.0&&pathname=%2Fws&logging=info&overlay=true&reconnect=10&hot=true&live-reload=true", + "/webpack/hot/dev-server.js", ], } `; @@ -133,9 +133,9 @@ exports[`normalize options snapshot react-refresh client added when react/refres "/./placeholder.js", ], "undefined": [ - "/rspack-dev-client/src/reactRefreshEntry.js", - "/webpack/hot/dev-server.js", + "/rspack-plugin-react-refresh/client/reactRefreshEntry.js", "/webpack-dev-server/client/index.js?protocol=ws%3A&hostname=0.0.0.0&&pathname=%2Fws&logging=info&overlay=true&reconnect=10&hot=true&live-reload=true", + "/webpack/hot/dev-server.js", ], } `; diff --git a/packages/rspack-plugin-react-refresh/CHANGELOG.md b/packages/rspack-plugin-react-refresh/CHANGELOG.md new file mode 100644 index 000000000000..6c1499b0e1e1 --- /dev/null +++ b/packages/rspack-plugin-react-refresh/CHANGELOG.md @@ -0,0 +1 @@ +# @rspack/plugin-react-refresh diff --git a/packages/rspack-plugin-react-refresh/LICENSE b/packages/rspack-plugin-react-refresh/LICENSE new file mode 100644 index 000000000000..46310101ad8a --- /dev/null +++ b/packages/rspack-plugin-react-refresh/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2022-present Bytedance, Inc. and its affiliates. + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/rspack-plugin-react-refresh/README.md b/packages/rspack-plugin-react-refresh/README.md new file mode 100644 index 000000000000..35cdd301eed9 --- /dev/null +++ b/packages/rspack-plugin-react-refresh/README.md @@ -0,0 +1,16 @@ + + + Rspack Banner + + +# @rspack/plugin-react-refresh + +React refresh plugin for rspack. + +## Documentation + +See [https://rspack.dev](https://rspack.dev) for details. + +## License + +Rspack is [MIT licensed](https://github.com/web-infra-dev/rspack/blob/main/LICENSE). diff --git a/packages/rspack-plugin-react-refresh/client/reactRefresh.js b/packages/rspack-plugin-react-refresh/client/reactRefresh.js new file mode 100644 index 000000000000..7c24482448bb --- /dev/null +++ b/packages/rspack-plugin-react-refresh/client/reactRefresh.js @@ -0,0 +1,23 @@ +// Thanks https://github.com/pmmmwh/react-refresh-webpack-plugin +const RefreshUtils = require("@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils"); +const RefreshRuntime = require("react-refresh/runtime"); + +// Port from https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/loader/utils/getRefreshModuleRuntime.js#L29 +function refresh(moduleId, webpackHot) { + const currentExports = RefreshUtils.getModuleExports(moduleId); + const fn = exports => { + RefreshUtils.executeRuntime(exports, moduleId, webpackHot); + }; + if (typeof Promise !== "undefined" && currentExports instanceof Promise) { + currentExports.then(fn); + } else { + fn(currentExports); + } +} + +module.exports = { + refresh, + register: RefreshRuntime.register, + createSignatureFunctionForTransform: + RefreshRuntime.createSignatureFunctionForTransform +}; diff --git a/packages/rspack-plugin-react-refresh/client/reactRefreshEntry.js b/packages/rspack-plugin-react-refresh/client/reactRefreshEntry.js new file mode 100644 index 000000000000..26eb38307b34 --- /dev/null +++ b/packages/rspack-plugin-react-refresh/client/reactRefreshEntry.js @@ -0,0 +1,4 @@ +const RefreshRuntime = require("react-refresh/runtime"); + +RefreshRuntime.injectIntoGlobalHook(self); +self.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform; diff --git a/packages/rspack-plugin-react-refresh/package.json b/packages/rspack-plugin-react-refresh/package.json new file mode 100644 index 000000000000..ea2687ceffc8 --- /dev/null +++ b/packages/rspack-plugin-react-refresh/package.json @@ -0,0 +1,40 @@ +{ + "name": "@rspack/plugin-react-refresh", + "version": "0.3.1", + "license": "MIT", + "description": "React refresh plugin for rspack", + "main": "src/index.js", + "exports": { + ".": "./src/index.js", + "./react-refresh": "./client/reactRefresh.js", + "./react-refresh-entry": "./client/reactRefreshEntry.js" + }, + "scripts": { + "test": "echo success" + }, + "files": [ + "src" + ], + "access": "public", + "homepage": "https://rspack.dev", + "bugs": "https://github.com/web-infra-dev/rspack/issues", + "repository": { + "type": "git", + "url": "https://github.com/web-infra-dev/rspack", + "directory": "packages/rspack-plugin-react-refresh" + }, + "dependencies": { + "@pmmmwh/react-refresh-webpack-plugin": "0.5.10" + }, + "devDependencies": { + "react-refresh": "0.14.0" + }, + "peerDependencies": { + "react-refresh": ">=0.10.0 <1.0.0" + }, + "peerDependenciesMeta": { + "react-refresh": { + "optional": true + } + } +} diff --git a/packages/rspack-plugin-react-refresh/src/index.js b/packages/rspack-plugin-react-refresh/src/index.js new file mode 100644 index 000000000000..92b057de0ed0 --- /dev/null +++ b/packages/rspack-plugin-react-refresh/src/index.js @@ -0,0 +1,35 @@ +const path = require("path"); +const reactRefreshPath = require.resolve("../client/reactRefresh.js"); +const reactRefreshEntryPath = require.resolve("../client/reactRefreshEntry.js"); +const refreshUtilsPath = require.resolve( + "@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils", + { + paths: [reactRefreshPath] + } +); +const refreshRuntimeDirPath = path.dirname( + require.resolve("react-refresh", { + paths: [reactRefreshPath] + }) +); +const runtimePaths = [ + reactRefreshPath, + refreshUtilsPath, + refreshRuntimeDirPath +]; + +module.exports = class ReactRefreshRspackPlugin { + apply(compiler) { + new compiler.webpack.EntryPlugin(compiler.context, reactRefreshEntryPath, { + name: undefined + }).apply(compiler); + new compiler.webpack.ProvidePlugin({ + $ReactRefreshRuntime$: reactRefreshPath + }).apply(compiler); + + compiler.options.module.rules.push({ + include: runtimePaths, + type: "js" + }); + } +}; diff --git a/packages/rspack/package.json b/packages/rspack/package.json index b5a2f0c2a379..71fc60fd230c 100644 --- a/packages/rspack/package.json +++ b/packages/rspack/package.json @@ -54,7 +54,6 @@ }, "dependencies": { "@rspack/binding": "workspace:*", - "@rspack/dev-client": "workspace:*", "@swc/helpers": "0.5.1", "browserslist": "^4.21.3", "compare-versions": "6.0.0-rc.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 71a192c262df..98e19cce8ee8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -879,6 +879,7 @@ importers: '@rspack/core': workspace:* '@rspack/dev-client': workspace:* '@rspack/dev-server': workspace:* + '@rspack/plugin-react-refresh': workspace:* '@types/fs-extra': 11.0.1 fs-extra: 11.1.1 postcss: ^8.4.21 @@ -894,6 +895,7 @@ importers: '@rspack/core': link:../rspack '@rspack/dev-client': link:../rspack-dev-client '@rspack/dev-server': link:../rspack-dev-server + '@rspack/plugin-react-refresh': link:../rspack-plugin-react-refresh '@types/fs-extra': 11.0.1 fs-extra: 11.1.1 postcss: 8.4.21 @@ -909,7 +911,6 @@ importers: specifiers: '@rspack/binding': workspace:* '@rspack/core': workspace:* - '@rspack/dev-client': workspace:* '@rspack/plugin-minify': workspace:^ '@rspack/plugin-node-polyfill': workspace:^ '@swc/helpers': 0.5.1 @@ -949,7 +950,6 @@ importers: zod-validation-error: 1.2.0 dependencies: '@rspack/binding': link:../../crates/node_binding - '@rspack/dev-client': link:../rspack-dev-client '@swc/helpers': 0.5.1 browserslist: 4.21.4 compare-versions: 6.0.0-rc.1 @@ -1031,20 +1031,18 @@ importers: packages/rspack-dev-client: specifiers: - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10 + '@rspack/plugin-react-refresh': workspace:* react-refresh: 0.14.0 - webpack: 5.76.0 dependencies: - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10_kpkvch5jhx6s5tusmqrn4u64d4 + '@rspack/plugin-react-refresh': link:../rspack-plugin-react-refresh devDependencies: react-refresh: 0.14.0 - webpack: 5.76.0 packages/rspack-dev-server: specifiers: '@rspack/core': workspace:* - '@rspack/dev-client': workspace:* '@rspack/dev-server': workspace:* + '@rspack/plugin-react-refresh': workspace:* '@types/connect-history-api-fallback': 1.3.5 '@types/express': 4.17.14 '@types/mime-types': 2.1.1 @@ -1061,8 +1059,8 @@ importers: webpack-dev-server: 4.13.1 ws: 8.8.1 dependencies: - '@rspack/dev-client': link:../rspack-dev-client '@rspack/dev-server': 'link:' + '@rspack/plugin-react-refresh': link:../rspack-plugin-react-refresh chokidar: 3.5.3 connect-history-api-fallback: 2.0.0 express: 4.18.1 @@ -1174,6 +1172,15 @@ importers: util: 0.12.5 vm-browserify: 1.1.2 + packages/rspack-plugin-react-refresh: + specifiers: + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10 + react-refresh: 0.14.0 + dependencies: + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10_react-refresh@0.14.0 + devDependencies: + react-refresh: 0.14.0 + scripts: specifiers: '@actions/core': 1.10.0 @@ -1194,7 +1201,6 @@ importers: specifiers: '@rspack/binding': workspace:* '@rspack/core': workspace:* - '@rspack/dev-client': workspace:* '@rspack/plugin-minify': workspace:^ '@rspack/plugin-node-polyfill': workspace:^ '@swc/helpers': 0.5.1 @@ -1244,7 +1250,6 @@ importers: webpack-sources: 3.2.3 dependencies: '@rspack/binding': link:../crates/node_binding - '@rspack/dev-client': link:../packages/rspack-dev-client '@swc/helpers': 0.5.1 browserslist: 4.21.4 enhanced-resolve: 5.12.0 @@ -7732,49 +7737,10 @@ packages: find-up: 5.0.0 html-entities: 2.3.3 loader-utils: 2.0.4 - schema-utils: 3.1.1 + schema-utils: 3.1.2 source-map: 0.7.4 dev: true - /@pmmmwh/react-refresh-webpack-plugin/0.5.10_kpkvch5jhx6s5tusmqrn4u64d4: - resolution: {integrity: sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==} - engines: {node: '>= 10.13'} - peerDependencies: - '@types/webpack': 4.x || 5.x - react-refresh: '>=0.10.0 <1.0.0' - sockjs-client: ^1.4.0 - type-fest: '>=0.17.0 <4.0.0' - webpack: '>=4.43.0 <6.0.0' - webpack-dev-server: 3.x || 4.x - webpack-hot-middleware: 2.x - webpack-plugin-serve: 0.x || 1.x - peerDependenciesMeta: - '@types/webpack': - optional: true - sockjs-client: - optional: true - type-fest: - optional: true - webpack-dev-server: - optional: true - webpack-hot-middleware: - optional: true - webpack-plugin-serve: - optional: true - dependencies: - ansi-html-community: 0.0.8 - common-path-prefix: 3.0.0 - core-js-pure: 3.26.1 - error-stack-parser: 2.1.4 - find-up: 5.0.0 - html-entities: 2.3.3 - loader-utils: 2.0.4 - react-refresh: 0.14.0 - schema-utils: 3.1.1 - source-map: 0.7.4 - webpack: 5.76.0 - dev: false - /@pmmmwh/react-refresh-webpack-plugin/0.5.10_react-refresh@0.11.0: resolution: {integrity: sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==} engines: {node: '>= 10.13'} @@ -7809,7 +7775,7 @@ packages: html-entities: 2.3.3 loader-utils: 2.0.4 react-refresh: 0.11.0 - schema-utils: 3.1.1 + schema-utils: 3.1.2 source-map: 0.7.4 dev: true @@ -7847,9 +7813,8 @@ packages: html-entities: 2.3.3 loader-utils: 2.0.4 react-refresh: 0.14.0 - schema-utils: 3.1.1 + schema-utils: 3.1.2 source-map: 0.7.4 - dev: true /@pmmmwh/react-refresh-webpack-plugin/0.5.10_rywehdfw6hkk2xngu2c3zrnyba: resolution: {integrity: sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==} @@ -7885,7 +7850,7 @@ packages: html-entities: 2.3.3 loader-utils: 2.0.4 react-refresh: 0.14.0 - schema-utils: 3.1.1 + schema-utils: 3.1.2 source-map: 0.7.4 webpack-hot-middleware: 2.25.3 dev: true @@ -24520,6 +24485,7 @@ packages: '@types/json-schema': 7.0.11 ajv: 6.12.6 ajv-keywords: 3.5.2_ajv@6.12.6 + dev: true /schema-utils/3.1.2: resolution: {integrity: sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==} diff --git a/webpack-test/package.json b/webpack-test/package.json index c22b75f5a034..38c2c6d0a58a 100644 --- a/webpack-test/package.json +++ b/webpack-test/package.json @@ -55,7 +55,6 @@ }, "dependencies": { "@rspack/binding": "workspace:*", - "@rspack/dev-client": "workspace:*", "@swc/helpers": "0.5.1", "browserslist": "^4.21.3", "enhanced-resolve": "5.12.0", From e1df35d0e9761015aa7ebcf0c1a7f731cb135a7d Mon Sep 17 00:00:00 2001 From: Hana Date: Fri, 8 Sep 2023 16:51:17 +0800 Subject: [PATCH 27/30] test: snapshot css modules separately (#4155) --- .../rspack_importer/snapshot/output.snap | 22 ----- crates/rspack_plugin_css/tests/fixtures.rs | 9 +- .../custom/http_import/snapshot/output.snap | 20 ----- .../b.module.css | 0 .../d.module.css | 0 .../e.module.css | 0 .../f.module.css | 0 .../{composes => modules-composes}/index.js | 0 .../snapshot/output.snap | 0 .../style.module.css | 0 .../test.config.json | 0 .../webpack/at-charset/snapshot/output.snap | 38 --------- .../snapshot/output.snap | 20 ----- .../webpack/at-import/snapshot/output.snap | 38 --------- .../default-options/snapshot/output.snap | 20 ----- .../multiple-entry/snapshot/output.snap | 85 ------------------- .../webpack/nested/snapshot/output.snap | 34 -------- .../webpack/output-iife/snapshot/output.snap | 20 ----- .../shared-import/snapshot/output.snap | 35 -------- .../snapshot/output.snap | 62 -------------- .../webpack/simple-async/snapshot/output.snap | 44 ---------- .../snapshot/output.snap | 17 ---- .../snapshot/output.snap | 20 ----- .../simple-multiple/snapshot/output.snap | 24 ------ .../webpack/simple/snapshot/output.snap | 20 ----- crates/rspack_testing/src/lib.rs | 4 +- crates/rspack_testing/src/run_fixture.rs | 4 + 27 files changed, 13 insertions(+), 523 deletions(-) rename crates/rspack_plugin_css/tests/fixtures/custom/{composes => modules-composes}/b.module.css (100%) rename crates/rspack_plugin_css/tests/fixtures/custom/{composes => modules-composes}/d.module.css (100%) rename crates/rspack_plugin_css/tests/fixtures/custom/{composes => modules-composes}/e.module.css (100%) rename crates/rspack_plugin_css/tests/fixtures/custom/{composes => modules-composes}/f.module.css (100%) rename crates/rspack_plugin_css/tests/fixtures/custom/{composes => modules-composes}/index.js (100%) rename crates/rspack_plugin_css/tests/fixtures/custom/{composes => modules-composes}/snapshot/output.snap (100%) rename crates/rspack_plugin_css/tests/fixtures/custom/{composes => modules-composes}/style.module.css (100%) rename crates/rspack_plugin_css/tests/fixtures/custom/{composes => modules-composes}/test.config.json (100%) diff --git a/crates/rspack_loader_sass/tests/fixtures/rspack_importer/snapshot/output.snap b/crates/rspack_loader_sass/tests/fixtures/rspack_importer/snapshot/output.snap index dd516d0d22b8..83ada6c249bc 100644 --- a/crates/rspack_loader_sass/tests/fixtures/rspack_importer/snapshot/output.snap +++ b/crates/rspack_loader_sass/tests/fixtures/rspack_importer/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 104 --- ```css title=main.css .color-red { @@ -43,24 +42,3 @@ nav a { } ``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _scss_language_scss__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ../../scss/language.scss */"../../scss/language.scss"); - -}, -"../../scss/file.css": function (module, exports, __webpack_require__) { -}, -"../../scss/language.scss": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures.rs b/crates/rspack_plugin_css/tests/fixtures.rs index cc20d037d7bb..e1a59ca43357 100644 --- a/crates/rspack_plugin_css/tests/fixtures.rs +++ b/crates/rspack_plugin_css/tests/fixtures.rs @@ -1,13 +1,18 @@ use std::path::PathBuf; -use rspack_testing::{fixture, test_fixture_css}; +use rspack_testing::{fixture, test_fixture_css, test_fixture_css_modules}; #[fixture("tests/fixtures/webpack/*")] fn webpack_css(fixture_path: PathBuf) { test_fixture_css(&fixture_path); } -#[fixture("tests/fixtures/custom/*")] +#[fixture("tests/fixtures/custom/*", exclude("tests/fixtures/custom/modules-*"))] fn custom(fixture_path: PathBuf) { test_fixture_css(&fixture_path); } + +#[fixture("tests/fixtures/custom/modules-*")] +fn custom_modules(fixture_path: PathBuf) { + test_fixture_css_modules(&fixture_path); +} diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/http_import/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/custom/http_import/snapshot/output.snap index 7a1c48b5c00b..e12ce854851d 100644 --- a/crates/rspack_plugin_css/tests/fixtures/custom/http_import/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/custom/http_import/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=main.css @import url("http://example.com/test.css"); @@ -12,22 +11,3 @@ body { ``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _style_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.css */"./style.css"); - -}, -"./style.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/b.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/b.module.css similarity index 100% rename from crates/rspack_plugin_css/tests/fixtures/custom/composes/b.module.css rename to crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/b.module.css diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/d.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/d.module.css similarity index 100% rename from crates/rspack_plugin_css/tests/fixtures/custom/composes/d.module.css rename to crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/d.module.css diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/e.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/e.module.css similarity index 100% rename from crates/rspack_plugin_css/tests/fixtures/custom/composes/e.module.css rename to crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/e.module.css diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/f.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/f.module.css similarity index 100% rename from crates/rspack_plugin_css/tests/fixtures/custom/composes/f.module.css rename to crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/f.module.css diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/index.js b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/index.js similarity index 100% rename from crates/rspack_plugin_css/tests/fixtures/custom/composes/index.js rename to crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/index.js diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/snapshot/output.snap similarity index 100% rename from crates/rspack_plugin_css/tests/fixtures/custom/composes/snapshot/output.snap rename to crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/snapshot/output.snap diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/style.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/style.module.css similarity index 100% rename from crates/rspack_plugin_css/tests/fixtures/custom/composes/style.module.css rename to crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/style.module.css diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/composes/test.config.json b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/test.config.json similarity index 100% rename from crates/rspack_plugin_css/tests/fixtures/custom/composes/test.config.json rename to crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/test.config.json diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/at-charset/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/at-charset/snapshot/output.snap index 04ca3878a5bb..c0bf0608bcec 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/at-charset/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/at-charset/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=main.css @charset "utf-8"; @@ -68,40 +67,3 @@ body { ``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); -/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./b.css */"./b.css"); - - -}, -"./a.css": function (module, exports, __webpack_require__) { -}, -"./aa.css": function (module, exports, __webpack_require__) { -}, -"./ab.css": function (module, exports, __webpack_require__) { -}, -"./ac.css": function (module, exports, __webpack_require__) { -}, -"./ad.css": function (module, exports, __webpack_require__) { -}, -"./ae.css": function (module, exports, __webpack_require__) { -}, -"./b.css": function (module, exports, __webpack_require__) { -}, -"./ba.css": function (module, exports, __webpack_require__) { -}, -"./bb.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/at-import-in-the-middle/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/at-import-in-the-middle/snapshot/output.snap index 0829acd77418..abaa7ff33208 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/at-import-in-the-middle/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/at-import-in-the-middle/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=main.css body { @@ -33,23 +32,4 @@ body { -``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./a.css": function (module, exports, __webpack_require__) { -}, -"./b.css": function (module, exports, __webpack_require__) { -}, -"./c.css": function (module, exports, __webpack_require__) { -}, -"./index.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.css")); - -} -]); ``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/at-import/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/at-import/snapshot/output.snap index 0835fc384bd2..6529bd62fc66 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/at-import/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/at-import/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=main.css .ae { @@ -50,40 +49,3 @@ body { ``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); -/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./b.css */"./b.css"); - - -}, -"./a.css": function (module, exports, __webpack_require__) { -}, -"./aa.css": function (module, exports, __webpack_require__) { -}, -"./ab.css": function (module, exports, __webpack_require__) { -}, -"./ac.css": function (module, exports, __webpack_require__) { -}, -"./ad.css": function (module, exports, __webpack_require__) { -}, -"./ae.css": function (module, exports, __webpack_require__) { -}, -"./b.css": function (module, exports, __webpack_require__) { -}, -"./ba.css": function (module, exports, __webpack_require__) { -}, -"./bb.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/default-options/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/default-options/snapshot/output.snap index e0b4b99122bd..d21e48db5842 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/default-options/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/default-options/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=main.css body { @@ -9,22 +8,3 @@ body { ``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _style_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.css */"./style.css"); - -}, -"./style.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/multiple-entry/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/multiple-entry/snapshot/output.snap index e47710f62299..5d5e677a763c 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/multiple-entry/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/multiple-entry/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=async-one.css body { @@ -49,87 +48,3 @@ body { ``` - -```js title=async-one.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["async-one"], { -"./async-one.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _c_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./c.css */"./c.css"); -/* harmony import */var _d_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./d.css */"./d.css"); - - -}, -"./c.css": function (module, exports, __webpack_require__) { -}, -"./d.css": function (module, exports, __webpack_require__) { -}, - -}]); -``` - -```js title=async-two.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["async-two"], { -"./async-two.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _d_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./d.css */"./d.css"); -/* harmony import */var _c_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./c.css */"./c.css"); - - -}, -"./c.css": function (module, exports, __webpack_require__) { -}, -"./d.css": function (module, exports, __webpack_require__) { -}, - -}]); -``` - -```js title=main-one.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main-one"], { -"./index-one.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); -/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./b.css */"./b.css"); - - -/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./async-one */"./async-one.js").then(__webpack_require__.bind(__webpack_require__, /* ./async-one */"./async-one.js")); -}, -"./a.css": function (module, exports, __webpack_require__) { -}, -"./b.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index-one.js")); - -} -]); -``` - -```js title=main-two.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main-two"], { -"./index-two.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./b.css */"./b.css"); -/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./a.css */"./a.css"); - - -/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./async-two */"./async-two.js").then(__webpack_require__.bind(__webpack_require__, /* ./async-two */"./async-two.js")); -}, -"./a.css": function (module, exports, __webpack_require__) { -}, -"./b.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index-two.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/nested/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/nested/snapshot/output.snap index b0d5a6904e7e..855ffb725143 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/nested/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/nested/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=main.css body { @@ -17,36 +16,3 @@ body { ``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./component.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _component_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./component.css */"./component.css"); - -}, -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); -/* harmony import */var _component__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./component */"./component.js"); -/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_2_ = __webpack_require__(/* ./b.css */"./b.css"); - - - -}, -"./a.css": function (module, exports, __webpack_require__) { -}, -"./b.css": function (module, exports, __webpack_require__) { -}, -"./component.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/output-iife/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/output-iife/snapshot/output.snap index e0b4b99122bd..d21e48db5842 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/output-iife/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/output-iife/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=main.css body { @@ -9,22 +8,3 @@ body { ``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _style_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.css */"./style.css"); - -}, -"./style.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/shared-import/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/shared-import/snapshot/output.snap index 0212c1c2cb8f..c85681d9d19a 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/shared-import/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/shared-import/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=c_css.css @@ -33,37 +32,3 @@ assertion_line: 101 ``` - -```js title=c_css.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["c_css"], { -"./c.css": function (module, exports, __webpack_require__) { -}, - -}]); -``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); -/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./b.css */"./b.css"); - - -/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./c.css */"./c.css").then(__webpack_require__.t.bind(__webpack_require__, /* ./c.css */"./c.css", 17)); -}, -"./a.css": function (module, exports, __webpack_require__) { -}, -"./b.css": function (module, exports, __webpack_require__) { -}, -"./shared.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async-load-css/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async-load-css/snapshot/output.snap index b9452adca357..aeabe434c42b 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async-load-css/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async-load-css/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=async-one.css body { @@ -37,64 +36,3 @@ body { ``` - -```js title=async-one.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["async-one"], { -"./async-one.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _c_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./c.css */"./c.css"); -/* harmony import */var _d_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./d.css */"./d.css"); - - -}, -"./c.css": function (module, exports, __webpack_require__) { -}, -"./d.css": function (module, exports, __webpack_require__) { -}, - -}]); -``` - -```js title=async-two.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["async-two"], { -"./async-two.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _e_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./e.css */"./e.css"); -/* harmony import */var _f_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./f.css */"./f.css"); - - -}, -"./e.css": function (module, exports, __webpack_require__) { -}, -"./f.css": function (module, exports, __webpack_require__) { -}, - -}]); -``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); -/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./b.css */"./b.css"); - - -/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./async-one */"./async-one.js").then(__webpack_require__.bind(__webpack_require__, /* ./async-one */"./async-one.js")); -/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./async-two */"./async-two.js").then(__webpack_require__.bind(__webpack_require__, /* ./async-two */"./async-two.js")); -}, -"./a.css": function (module, exports, __webpack_require__) { -}, -"./b.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async/snapshot/output.snap index 5c32bae63a18..cf09b3f0ee37 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-async/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=async_css.css .async { @@ -25,46 +24,3 @@ body { ``` - -```js title=async_css.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["async_css"], { -"./async.css": function (module, exports, __webpack_require__) { -}, - -}]); -``` - -```js title=async_js.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["async_js"], { -"./async.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _in_async_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./in-async.css */"./in-async.css"); - -}, -"./in-async.css": function (module, exports, __webpack_require__) { -}, - -}]); -``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _main_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./main.css */"./main.css"); - -/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./async */"./async.js").then(__webpack_require__.bind(__webpack_require__, /* ./async */"./async.js")); -/* eslint-disable-next-line no-unused-expressions */ __webpack_require__.el(/* ./async.css */"./async.css").then(__webpack_require__.t.bind(__webpack_require__, /* ./async.css */"./async.css", 17)); -}, -"./main.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-commonjs-syntax/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-commonjs-syntax/snapshot/output.snap index 7ababdd0f15b..d21e48db5842 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-commonjs-syntax/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-commonjs-syntax/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=main.css body { @@ -9,19 +8,3 @@ body { ``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, exports, __webpack_require__) { -__webpack_require__(/* ./style.css */"./style.css"); -}, -"./style.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-es-module-syntax/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-es-module-syntax/snapshot/output.snap index e0b4b99122bd..d21e48db5842 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-es-module-syntax/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-es-module-syntax/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=main.css body { @@ -9,22 +8,3 @@ body { ``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _style_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.css */"./style.css"); - -}, -"./style.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-multiple/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-multiple/snapshot/output.snap index f0a7b946a346..dcc3065cc252 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/simple-multiple/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/simple-multiple/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=main.css body { @@ -13,26 +12,3 @@ body { ``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _a_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./a.css */"./a.css"); -/* harmony import */var _b_css__WEBPACK_IMPORTED_MODULE_1_ = __webpack_require__(/* ./b.css */"./b.css"); - - -}, -"./a.css": function (module, exports, __webpack_require__) { -}, -"./b.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_plugin_css/tests/fixtures/webpack/simple/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/webpack/simple/snapshot/output.snap index e0b4b99122bd..d21e48db5842 100644 --- a/crates/rspack_plugin_css/tests/fixtures/webpack/simple/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/webpack/simple/snapshot/output.snap @@ -1,6 +1,5 @@ --- source: crates/rspack_testing/src/run_fixture.rs -assertion_line: 101 --- ```css title=main.css body { @@ -9,22 +8,3 @@ body { ``` - -```js title=main.js -(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { -"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -'use strict'; -__webpack_require__.r(__webpack_exports__); -/* harmony import */var _style_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.css */"./style.css"); - -}, -"./style.css": function (module, exports, __webpack_require__) { -}, - -},function(__webpack_require__) { -var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } -var __webpack_exports__ = (__webpack_exec__("./index.js")); - -} -]); -``` diff --git a/crates/rspack_testing/src/lib.rs b/crates/rspack_testing/src/lib.rs index 57e117bce995..4a421d40e420 100644 --- a/crates/rspack_testing/src/lib.rs +++ b/crates/rspack_testing/src/lib.rs @@ -4,8 +4,8 @@ mod run_fixture; mod test_config; pub use eval_raw::evaluate_to_json; pub use run_fixture::{ - apply_from_fixture, test_fixture, test_fixture_css, test_fixture_html, test_fixture_insta, - test_fixture_js, test_rebuild_fixture, + apply_from_fixture, test_fixture, test_fixture_css, test_fixture_css_modules, test_fixture_html, + test_fixture_insta, test_fixture_js, test_rebuild_fixture, }; pub use test_config::TestConfig; pub use testing_macros::{self, fixture}; diff --git a/crates/rspack_testing/src/run_fixture.rs b/crates/rspack_testing/src/run_fixture.rs index 16a40312f67d..7b93d9fa2ece 100644 --- a/crates/rspack_testing/src/run_fixture.rs +++ b/crates/rspack_testing/src/run_fixture.rs @@ -37,6 +37,10 @@ pub async fn test_fixture_js(fixture_path: &Path) -> Compiler Compiler { + test_fixture_share(fixture_path, &|s| s.ends_with(".css")).await +} +#[tokio::main] +pub async fn test_fixture_css_modules(fixture_path: &Path) -> Compiler { test_fixture_share(fixture_path, &|s| { s.ends_with(".css") || (s.ends_with(".js") && !s.contains("runtime.js")) }) From 14ded15a1b43df44550cdfd8c734d53dfee9fcbf Mon Sep 17 00:00:00 2001 From: Xinxin He Date: Fri, 8 Sep 2023 17:46:47 +0800 Subject: [PATCH 28/30] feat: expose `keepFnNames` and `keepClassNames` options of builtin swc minfier (#4121) * feat(minfier): expose keepFnNames and keepClassNames options of builtin swc minfier * chore: change keep_class_names, keep_fn_names, and changed tests use field name * chore: remove unused code * Update crates/rspack_testing/test.config.scheme.json * Update crates/rspack_testing/test.config.scheme.json * chore: update snap * Apply suggestions from code review * chore: update snap * chore: format codebase --------- Co-authored-by: Hana --- crates/node_binding/binding.d.ts | 2 ++ .../raw_builtins/raw_swc_js_minimizer.rs | 4 +++ .../rspack_plugin_swc_js_minimizer/src/lib.rs | 14 ++++++-- crates/rspack_testing/test.config.scheme.json | 8 +++++ .../builtin-plugin/SwcJsMinimizerPlugin.ts | 4 +++ packages/rspack/tests/Stats.test.ts | 4 +-- .../tests/__snapshots__/Stats.test.ts.snap | 4 +-- .../__snapshots__/StatsTestCases.test.ts.snap | 32 +++++++++---------- .../builtins/minify-keep-classnames/index.js | 6 ++++ .../minify-keep-classnames/webpack.config.js | 10 ++++++ .../builtins/minify-keep-fn-names/index.js | 6 ++++ .../minify-keep-fn-names/webpack.config.js | 10 ++++++ 12 files changed, 82 insertions(+), 22 deletions(-) create mode 100644 packages/rspack/tests/configCases/builtins/minify-keep-classnames/index.js create mode 100644 packages/rspack/tests/configCases/builtins/minify-keep-classnames/webpack.config.js create mode 100644 packages/rspack/tests/configCases/builtins/minify-keep-fn-names/index.js create mode 100644 packages/rspack/tests/configCases/builtins/minify-keep-fn-names/webpack.config.js diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 85ce7fd1df7b..12b701a0f3b5 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -747,6 +747,8 @@ export interface RawLibraryOptions { export interface RawMinification { passes: number dropConsole: boolean + keepClassNames: boolean + keepFnNames: boolean comments: "all" | "some" | "false" asciiOnly: boolean pureFuncs: Array diff --git a/crates/rspack_binding_options/src/options/raw_builtins/raw_swc_js_minimizer.rs b/crates/rspack_binding_options/src/options/raw_builtins/raw_swc_js_minimizer.rs index 4be726006fdc..f834e6aeb80f 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/raw_swc_js_minimizer.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/raw_swc_js_minimizer.rs @@ -30,6 +30,8 @@ pub struct RawMinificationConditions { pub struct RawMinification { pub passes: u32, pub drop_console: bool, + pub keep_class_names: bool, + pub keep_fn_names: bool, #[napi(ts_type = r#""all" | "some" | "false""#)] pub comments: String, pub ascii_only: bool, @@ -59,6 +61,8 @@ impl TryFrom for Minification { Ok(Self { passes: value.passes as usize, drop_console: value.drop_console, + keep_class_names: value.keep_class_names, + keep_fn_names: value.keep_fn_names, pure_funcs: value.pure_funcs, ascii_only: value.ascii_only, comments: value.comments, diff --git a/crates/rspack_plugin_swc_js_minimizer/src/lib.rs b/crates/rspack_plugin_swc_js_minimizer/src/lib.rs index 588a2c3d07c6..ea3bda313ceb 100644 --- a/crates/rspack_plugin_swc_js_minimizer/src/lib.rs +++ b/crates/rspack_plugin_swc_js_minimizer/src/lib.rs @@ -29,6 +29,8 @@ use swc_ecma_minifier::option::{ pub struct Minification { pub passes: usize, pub drop_console: bool, + pub keep_class_names: bool, + pub keep_fn_names: bool, pub pure_funcs: Vec, pub extract_comments: Option, pub ascii_only: bool, @@ -115,6 +117,12 @@ impl Plugin for SwcJsMinimizerPlugin { ..Default::default() }; + let mangle = MangleOptions { + keep_class_names: minify_options.keep_class_names, + keep_fn_names: minify_options.keep_fn_names, + ..Default::default() + }; + let comments = match minify_options.comments.as_str() { "false" => JsMinifyCommentOption::False, "all" => JsMinifyCommentOption::PreserveAllComments, @@ -146,6 +154,7 @@ impl Plugin for SwcJsMinimizerPlugin { let input_source_map = original_source.map(&MapOptions::default()); let js_minify_options = JsMinifyOptions { compress: BoolOrDataConfig::from_obj(compress.clone()), + mangle: BoolOrDataConfig::from_obj(mangle.clone()), format: format.clone(), source_map: BoolOrDataConfig::from_bool(input_source_map.is_some()), inline_sources_content: true, /* Using true so original_source can be None in SourceMapSource */ @@ -153,6 +162,7 @@ impl Plugin for SwcJsMinimizerPlugin { module: is_module, ..Default::default() }; + let output = match minify( &js_minify_options, input, @@ -236,8 +246,8 @@ pub struct JsMinifyOptions { pub mangle: BoolOrDataConfig, pub format: JsMinifyFormatOptions, pub ecma: TerserEcmaVersion, - pub keep_classnames: bool, - pub keep_fnames: bool, + pub keep_class_names: bool, + pub keep_fn_names: bool, pub module: bool, pub safari10: bool, pub toplevel: bool, diff --git a/crates/rspack_testing/test.config.scheme.json b/crates/rspack_testing/test.config.scheme.json index 31cceb8d6cdd..539757d2b2db 100644 --- a/crates/rspack_testing/test.config.scheme.json +++ b/crates/rspack_testing/test.config.scheme.json @@ -346,6 +346,14 @@ "default": false, "type": "boolean" }, + "keepClassNames": { + "default": false, + "type": "boolean" + }, + "keepFnNames": { + "default": false, + "type": "boolean" + }, "extractComments": { "default": null, "type": [ diff --git a/packages/rspack/src/builtin-plugin/SwcJsMinimizerPlugin.ts b/packages/rspack/src/builtin-plugin/SwcJsMinimizerPlugin.ts index 53505f668af3..f4d5459882e2 100644 --- a/packages/rspack/src/builtin-plugin/SwcJsMinimizerPlugin.ts +++ b/packages/rspack/src/builtin-plugin/SwcJsMinimizerPlugin.ts @@ -10,6 +10,8 @@ type MinifyConditions = MinifyCondition | MinifyCondition[]; export type SwcJsMinimizerPluginOptions = { passes?: number; dropConsole?: boolean; + keepClassNames?: boolean; + keepFnNames?: boolean; pureFuncs?: Array; extractComments?: boolean | RegExp; comments?: false | "all" | "some"; @@ -58,6 +60,8 @@ export const SwcJsMinimizerPlugin = create( return { passes: options?.passes ?? 1, dropConsole: options?.dropConsole ?? false, + keepClassNames: options?.keepClassNames ?? false, + keepFnNames: options?.keepFnNames ?? false, pureFuncs: options?.pureFuncs ?? [], comments: options?.comments ? options.comments : "false", asciiOnly: options?.asciiOnly ?? false, diff --git a/packages/rspack/tests/Stats.test.ts b/packages/rspack/tests/Stats.test.ts index 02fd5e195551..dff1a5fd2f58 100644 --- a/packages/rspack/tests/Stats.test.ts +++ b/packages/rspack/tests/Stats.test.ts @@ -34,7 +34,7 @@ describe("Stats", () => { entry ./fixtures/a ./fixtures/a.js [876] {main} entry ./fixtures/a - rspack compiled successfully (b32fac08a5e8721cacff)" + rspack compiled successfully (31124a919605602fc9ae)" `); }); @@ -79,7 +79,7 @@ describe("Stats", () => { - rspack compiled with 1 error (27ec1c09308b67dcfd6f)" + rspack compiled with 1 error (18c9b1da202481673984)" `); }); diff --git a/packages/rspack/tests/__snapshots__/Stats.test.ts.snap b/packages/rspack/tests/__snapshots__/Stats.test.ts.snap index 10a53123a113..1f2652238337 100644 --- a/packages/rspack/tests/__snapshots__/Stats.test.ts.snap +++ b/packages/rspack/tests/__snapshots__/Stats.test.ts.snap @@ -156,7 +156,7 @@ exports[`Stats should have stats 1`] = ` "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "b32fac08a5e8721cacff", + "hash": "31124a919605602fc9ae", "logging": {}, "modules": [ { @@ -416,7 +416,7 @@ exports.c = require("./c?c=3"); "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "5eccb83e369e53af1313", + "hash": "089a36c7767bb55f0574", "logging": {}, "modules": [ { diff --git a/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap b/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap index affeb7677da5..d121012c97a4 100644 --- a/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap +++ b/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap @@ -188,7 +188,7 @@ import rawModule from './raw.png'", "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "31df38a93780ad25981d", + "hash": "94f12aa69d3989830e25", "logging": {}, "modules": [ { @@ -337,7 +337,7 @@ chunk {main} bundle.js (main) [entry] entry ./index ./stringModule.js [363] {main} esm import ./stringModule [10] -rspack compiled successfully (31df38a93780ad25981d)" +rspack compiled successfully (94f12aa69d3989830e25)" `; exports[`StatsTestCases should print correct stats for filename 1`] = ` @@ -500,7 +500,7 @@ exports[`StatsTestCases should print correct stats for filename 1`] = ` "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "7cdaa6ba61cec2cb7288", + "hash": "b1dbe683f73e6b1f7f2c", "logging": {}, "modules": [ { @@ -602,7 +602,7 @@ chunk {main} main.xxxx.js (main) >{dynamic_js}< [entry] entry ./index ./dynamic.js [426] {dynamic_js} dynamic import ./dynamic [10] -rspack compiled successfully (7cdaa6ba61cec2cb7288)" +rspack compiled successfully (b1dbe683f73e6b1f7f2c)" `; exports[`StatsTestCases should print correct stats for hot+production 1`] = ` @@ -697,7 +697,7 @@ exports[`StatsTestCases should print correct stats for hot+production 1`] = ` "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "d71f062b534cbc5af842", + "hash": "86548123ac24bd06a6bf", "logging": {}, "modules": [ { @@ -760,7 +760,7 @@ chunk {main} bundle.js (main) [entry] entry ./index.js ./index.js [10] {main} entry ./index.js -rspack compiled successfully (d71f062b534cbc5af842)" +rspack compiled successfully (86548123ac24bd06a6bf)" `; exports[`StatsTestCases should print correct stats for identifier-let-strict-mode 1`] = ` @@ -2023,7 +2023,7 @@ console.log(a); ], "errorsCount": 1, "filteredModules": undefined, - "hash": "7d2d2ee1a9384ad715fa", + "hash": "6ca407f06f94c982125d", "logging": {}, "modules": [ { @@ -2144,7 +2144,7 @@ error[internal]: Resolve error -rspack compiled with 1 error (7d2d2ee1a9384ad715fa)" +rspack compiled with 1 error (6ca407f06f94c982125d)" `; exports[`StatsTestCases should print correct stats for resolve-unexpected-exports-in-pkg 1`] = ` @@ -2288,7 +2288,7 @@ console.log(a); ], "errorsCount": 1, "filteredModules": undefined, - "hash": "d4ff2f3bc5aba8d564b3", + "hash": "a9093cd411993b4b9c01", "logging": {}, "modules": [ { @@ -2403,7 +2403,7 @@ error[internal]: Export should be relative path and start with "./", but got ../ -rspack compiled with 1 error (d4ff2f3bc5aba8d564b3)" +rspack compiled with 1 error (a9093cd411993b4b9c01)" `; exports[`StatsTestCases should print correct stats for simple 1`] = ` @@ -2498,7 +2498,7 @@ exports[`StatsTestCases should print correct stats for simple 1`] = ` "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "44e8a78970b06279cd26", + "hash": "7a27fed57156f35cc7b2", "logging": {}, "modules": [ { @@ -2561,7 +2561,7 @@ chunk {main} bundle.js (main) [entry] entry ./index ./index.js [10] {main} entry ./index -rspack compiled successfully (44e8a78970b06279cd26)" +rspack compiled successfully (7a27fed57156f35cc7b2)" `; exports[`StatsTestCases should print correct stats for simple-module-source 1`] = ` @@ -2722,7 +2722,7 @@ import rawModule from './raw.png'", "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "a563cef06d977112d3e1", + "hash": "ef78d0d53e6750a65525", "logging": {}, "modules": [ { @@ -2849,7 +2849,7 @@ Entrypoint main 631 bytes = bundle.js ./raw.png ./index.js ./stringModule.js -rspack compiled successfully (a563cef06d977112d3e1)" +rspack compiled successfully (ef78d0d53e6750a65525)" `; exports[`StatsTestCases should print correct stats for stats-hooks 1`] = ` @@ -2943,7 +2943,7 @@ exports[`StatsTestCases should print correct stats for stats-hooks 1`] = ` "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "44e8a78970b06279cd26", + "hash": "7a27fed57156f35cc7b2", "logging": {}, "modules": [ { @@ -3000,7 +3000,7 @@ exports[`StatsTestCases should print correct stats for stats-hooks 2`] = ` asset bundle.js 589 bytes [emitted111] (name: main) [testA: aaaaaa] Entrypoint main 589 bytes = bundle.js ./index.js -rspack compiled successfully (44e8a78970b06279cd26)" +rspack compiled successfully (7a27fed57156f35cc7b2)" `; exports[`StatsTestCases should print correct stats for try-require--module 1`] = ` diff --git a/packages/rspack/tests/configCases/builtins/minify-keep-classnames/index.js b/packages/rspack/tests/configCases/builtins/minify-keep-classnames/index.js new file mode 100644 index 000000000000..eff2802807ba --- /dev/null +++ b/packages/rspack/tests/configCases/builtins/minify-keep-classnames/index.js @@ -0,0 +1,6 @@ +class KeepClass {} + +it("should keep class names", () => { + const name = KeepClass.name; + expect(name).toBe("KeepClass"); +}); diff --git a/packages/rspack/tests/configCases/builtins/minify-keep-classnames/webpack.config.js b/packages/rspack/tests/configCases/builtins/minify-keep-classnames/webpack.config.js new file mode 100644 index 000000000000..5d6e446a18dd --- /dev/null +++ b/packages/rspack/tests/configCases/builtins/minify-keep-classnames/webpack.config.js @@ -0,0 +1,10 @@ +module.exports = { + builtins: { + minifyOptions: { + keepClassNames: true + } + }, + optimization: { + minimize: true + } +}; diff --git a/packages/rspack/tests/configCases/builtins/minify-keep-fn-names/index.js b/packages/rspack/tests/configCases/builtins/minify-keep-fn-names/index.js new file mode 100644 index 000000000000..482fb5f2b608 --- /dev/null +++ b/packages/rspack/tests/configCases/builtins/minify-keep-fn-names/index.js @@ -0,0 +1,6 @@ +function main() {} + +it("should keep fn names", () => { + const name = main.name; + expect(name).toBe("main"); +}); diff --git a/packages/rspack/tests/configCases/builtins/minify-keep-fn-names/webpack.config.js b/packages/rspack/tests/configCases/builtins/minify-keep-fn-names/webpack.config.js new file mode 100644 index 000000000000..22a5f464775e --- /dev/null +++ b/packages/rspack/tests/configCases/builtins/minify-keep-fn-names/webpack.config.js @@ -0,0 +1,10 @@ +module.exports = { + builtins: { + minifyOptions: { + keepFnNames: true + } + }, + optimization: { + minimize: true + } +}; From f25e0f27b7b44d96102032e1c5878ecaedffe8bd Mon Sep 17 00:00:00 2001 From: jinrui Date: Fri, 8 Sep 2023 19:34:57 +0800 Subject: [PATCH 29/30] perf: optimize process assets (#4116) --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21e3ad1af0eb..bd3f79ace165 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3068,9 +3068,9 @@ dependencies = [ [[package]] name = "rspack_sources" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945955c953f0a80eba0c6b9566012249a349df595a6d3ae10f562a99e35b2837" +checksum = "25a741e3d5c1b73996bad4beeefb8aa0bbffad40ba4f0c4b09080265c11bd7bd" dependencies = [ "dashmap", "dyn-clone", diff --git a/Cargo.toml b/Cargo.toml index 77e46d4f9b5d..94ad5d0d2555 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ preset_env_base = { version = "0.4.5" } rayon = { version = "1.7.0" } regex = { version = "1.9.1" } rkyv = { version = "0.7.42" } -rspack_sources = { version = "0.2.6" } +rspack_sources = { version = "0.2.7" } rustc-hash = { version = "1.1.0" } schemars = { version = "0.8.12" } serde = { version = "1.0.171" } From bcdcc4b60f89ed0419579f87efdfd79918eac683 Mon Sep 17 00:00:00 2001 From: hardfist Date: Sun, 10 Sep 2023 11:37:19 +0800 Subject: [PATCH 30/30] chore: fix publish problems of react-refresh (#4161) --- packages/rspack-plugin-react-refresh/package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/rspack-plugin-react-refresh/package.json b/packages/rspack-plugin-react-refresh/package.json index ea2687ceffc8..bde2c1bd0e8a 100644 --- a/packages/rspack-plugin-react-refresh/package.json +++ b/packages/rspack-plugin-react-refresh/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/plugin-react-refresh", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "React refresh plugin for rspack", "main": "src/index.js", @@ -15,7 +15,9 @@ "files": [ "src" ], - "access": "public", + "publishConfig": { + "access": "public" + }, "homepage": "https://rspack.dev", "bugs": "https://github.com/web-infra-dev/rspack/issues", "repository": {