shakapacker 8.4.0 → 9.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/commands/address-review.md +206 -0
- data/.claude/commands/update-changelog.md +354 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +6 -9
- data/.github/ISSUE_TEMPLATE/feature_request.md +6 -8
- data/.github/STATUS.md +1 -0
- data/.github/actionlint-matcher.json +17 -0
- data/.github/workflows/claude-code-review.yml +45 -0
- data/.github/workflows/claude.yml +55 -0
- data/.github/workflows/dummy.yml +18 -5
- data/.github/workflows/eslint-validation.yml +46 -0
- data/.github/workflows/generator.yml +38 -22
- data/.github/workflows/node.yml +116 -2
- data/.github/workflows/ruby.yml +57 -15
- data/.github/workflows/test-bundlers.yml +180 -0
- data/.gitignore +27 -0
- data/.husky/pre-commit +2 -0
- data/.npmignore +56 -0
- data/.prettierignore +7 -0
- data/.rubocop.yml +2 -0
- data/.yalcignore +26 -0
- data/CHANGELOG.md +487 -19
- data/CLAUDE.md +63 -0
- data/CONTRIBUTING.md +268 -21
- data/ESLINT_TECHNICAL_DEBT.md +165 -0
- data/README.md +497 -137
- data/Rakefile +44 -4
- data/TODO.md +58 -0
- data/TODO_v9.md +97 -0
- data/bin/conductor-exec +24 -0
- data/bin/shakapacker-config +11 -0
- data/conductor-setup.sh +147 -0
- data/conductor.json +9 -0
- data/docs/api-reference.md +519 -0
- data/docs/cdn_setup.md +384 -0
- data/docs/common-upgrades.md +695 -0
- data/docs/configuration.md +845 -0
- data/docs/css-modules-export-mode.md +566 -0
- data/docs/customizing_babel_config.md +16 -16
- data/docs/deployment.md +78 -7
- data/docs/developing_shakapacker.md +6 -0
- data/docs/early_hints.md +433 -0
- data/docs/early_hints_manual_api.md +454 -0
- data/docs/feature_testing.md +492 -0
- data/docs/node_package_api.md +70 -0
- data/docs/optional-peer-dependencies.md +203 -0
- data/docs/peer-dependencies.md +71 -0
- data/docs/precompile_hook.md +486 -0
- data/docs/preventing_fouc.md +132 -0
- data/docs/react.md +58 -48
- data/docs/releasing.md +288 -0
- data/docs/rspack.md +218 -0
- data/docs/rspack_migration_guide.md +862 -0
- data/docs/sprockets.md +1 -0
- data/docs/style_loader_vs_mini_css.md +12 -12
- data/docs/subresource_integrity.md +13 -7
- data/docs/transpiler-migration.md +212 -0
- data/docs/transpiler-performance.md +200 -0
- data/docs/troubleshooting.md +272 -24
- data/docs/typescript-migration.md +388 -0
- data/docs/typescript.md +103 -0
- data/docs/using_esbuild_loader.md +12 -12
- data/docs/using_swc_loader.md +121 -16
- data/docs/v6_upgrade.md +42 -19
- data/docs/v7_upgrade.md +8 -6
- data/docs/v8_upgrade.md +13 -12
- data/docs/v9_upgrade.md +616 -0
- data/eslint.config.fast.js +254 -0
- data/eslint.config.js +309 -0
- data/jest.config.js +8 -1
- data/knip.ts +61 -0
- data/lib/install/bin/shakapacker +4 -6
- data/lib/install/bin/shakapacker-config +11 -0
- data/lib/install/bin/shakapacker-dev-server +1 -1
- data/lib/install/binstubs.rb +6 -2
- data/lib/install/config/rspack/rspack.config.js +6 -0
- data/lib/install/config/rspack/rspack.config.ts +7 -0
- data/lib/install/config/shakapacker.yml +75 -12
- data/lib/install/config/webpack/webpack.config.ts +7 -0
- data/lib/install/package.json +38 -0
- data/lib/install/template.rb +207 -45
- data/lib/shakapacker/build_config_loader.rb +147 -0
- data/lib/shakapacker/bundler_switcher.rb +415 -0
- data/lib/shakapacker/compiler.rb +87 -0
- data/lib/shakapacker/configuration.rb +475 -6
- data/lib/shakapacker/dev_server.rb +88 -1
- data/lib/shakapacker/dev_server_runner.rb +240 -6
- data/lib/shakapacker/doctor.rb +1191 -0
- data/lib/shakapacker/env.rb +19 -3
- data/lib/shakapacker/helper.rb +411 -14
- data/lib/shakapacker/install/env.rb +33 -0
- data/lib/shakapacker/instance.rb +93 -4
- data/lib/shakapacker/manifest.rb +167 -30
- data/lib/shakapacker/railtie.rb +4 -0
- data/lib/shakapacker/rspack_runner.rb +19 -0
- data/lib/shakapacker/runner.rb +668 -9
- data/lib/shakapacker/swc_migrator.rb +384 -0
- data/lib/shakapacker/utils/manager.rb +2 -0
- data/lib/shakapacker/utils/version_syntax_converter.rb +1 -1
- data/lib/shakapacker/version.rb +1 -1
- data/lib/shakapacker/version_checker.rb +1 -1
- data/lib/shakapacker/webpack_runner.rb +4 -42
- data/lib/shakapacker.rb +159 -1
- data/lib/tasks/shakapacker/binstubs.rake +4 -2
- data/lib/tasks/shakapacker/check_binstubs.rake +2 -2
- data/lib/tasks/shakapacker/doctor.rake +48 -0
- data/lib/tasks/shakapacker/export_bundler_config.rake +68 -0
- data/lib/tasks/shakapacker/install.rake +16 -4
- data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
- data/lib/tasks/shakapacker/switch_bundler.rake +72 -0
- data/lib/tasks/shakapacker.rake +2 -0
- data/package/.npmignore +4 -0
- data/package/babel/preset.ts +59 -0
- data/package/config.ts +189 -0
- data/package/configExporter/buildValidator.ts +906 -0
- data/package/configExporter/cli.ts +1748 -0
- data/package/configExporter/configDocs.ts +102 -0
- data/package/configExporter/configFile.ts +663 -0
- data/package/configExporter/fileWriter.ts +112 -0
- data/package/configExporter/index.ts +15 -0
- data/package/configExporter/types.ts +159 -0
- data/package/configExporter/yamlSerializer.ts +391 -0
- data/package/dev_server.ts +27 -0
- data/package/env.ts +92 -0
- data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +36 -0
- data/package/environments/base.ts +147 -0
- data/package/environments/development.ts +88 -0
- data/package/environments/production.ts +82 -0
- data/package/environments/test.ts +55 -0
- data/package/environments/types.ts +98 -0
- data/package/esbuild/index.ts +40 -0
- data/package/index.d.ts +68 -93
- data/package/index.d.ts.template +72 -0
- data/package/index.ts +104 -0
- data/package/loaders.d.ts +28 -0
- data/package/optimization/rspack.ts +36 -0
- data/package/optimization/webpack.ts +55 -0
- data/package/plugins/envFilter.ts +82 -0
- data/package/plugins/rspack.ts +119 -0
- data/package/plugins/webpack.ts +82 -0
- data/package/rspack/index.ts +91 -0
- data/package/rules/{babel.js → babel.ts} +2 -2
- data/package/rules/{coffee.js → coffee.ts} +1 -1
- data/package/rules/css.ts +3 -0
- data/package/rules/{erb.js → erb.ts} +1 -1
- data/package/rules/esbuild.ts +10 -0
- data/package/rules/file.ts +41 -0
- data/package/rules/{jscommon.js → jscommon.ts} +5 -4
- data/package/rules/{less.js → less.ts} +4 -4
- data/package/rules/raw.ts +28 -0
- data/package/rules/rspack.ts +174 -0
- data/package/rules/sass.ts +21 -0
- data/package/rules/{stylus.js → stylus.ts} +4 -8
- data/package/rules/swc.ts +10 -0
- data/package/rules/{index.js → webpack.ts} +1 -2
- data/package/swc/index.ts +54 -0
- data/package/types/README.md +90 -0
- data/package/types/index.ts +69 -0
- data/package/types.ts +105 -0
- data/package/utils/bundlerUtils.ts +232 -0
- data/package/utils/configPath.ts +6 -0
- data/package/utils/debug.ts +45 -0
- data/package/utils/defaultConfigPath.ts +7 -0
- data/package/utils/ensureManifestExists.ts +17 -0
- data/package/utils/errorCodes.ts +249 -0
- data/package/utils/errorHelpers.ts +152 -0
- data/package/utils/getStyleRule.ts +75 -0
- data/package/utils/helpers.ts +99 -0
- data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
- data/package/utils/pathValidation.ts +207 -0
- data/package/utils/requireOrError.ts +24 -0
- data/package/utils/snakeToCamelCase.ts +5 -0
- data/package/utils/typeGuards.ts +388 -0
- data/package/utils/validateDependencies.ts +61 -0
- data/package/webpack-types.d.ts +33 -0
- data/package/webpackDevServerConfig.ts +130 -0
- data/package.json +157 -18
- data/scripts/remove-use-strict.js +44 -0
- data/scripts/type-check-no-emit.js +27 -0
- data/shakapacker.gemspec +4 -2
- data/sig/shakapacker/commands.rbs +35 -0
- data/sig/shakapacker/compiler.rbs +65 -0
- data/sig/shakapacker/compiler_strategy.rbs +41 -0
- data/sig/shakapacker/configuration.rbs +140 -0
- data/sig/shakapacker/dev_server.rbs +56 -0
- data/sig/shakapacker/env.rbs +25 -0
- data/sig/shakapacker/helper.rbs +98 -0
- data/sig/shakapacker/instance.rbs +46 -0
- data/sig/shakapacker/manifest.rbs +69 -0
- data/sig/shakapacker/version.rbs +4 -0
- data/sig/shakapacker.rbs +66 -0
- data/test/configExporter/buildValidator.test.js +1295 -0
- data/test/configExporter/configFile.test.js +393 -0
- data/test/configExporter/integration.test.js +262 -0
- data/test/helpers.js +1 -1
- data/test/package/bundlerUtils.rspack.test.js +145 -0
- data/test/package/bundlerUtils.test.js +97 -0
- data/test/package/config.test.js +14 -0
- data/test/package/configExporter/cli.test.js +440 -0
- data/test/package/configExporter/types.test.js +163 -0
- data/test/package/configExporter.test.js +491 -0
- data/test/package/env.test.js +42 -7
- data/test/package/environments/base.test.js +14 -4
- data/test/package/helpers.test.js +2 -2
- data/test/package/plugins/envFiltering.test.js +453 -0
- data/test/package/plugins/webpackSubresourceIntegrity.test.js +89 -0
- data/test/package/rspack/index.test.js +293 -0
- data/test/package/rspack/optimization.test.js +86 -0
- data/test/package/rspack/plugins.test.js +185 -0
- data/test/package/rspack/rules.test.js +229 -0
- data/test/package/rules/babel.test.js +65 -38
- data/test/package/rules/esbuild.test.js +13 -4
- data/test/package/rules/file.test.js +7 -1
- data/test/package/rules/raw.test.js +40 -7
- data/test/package/rules/sass-version-parsing.test.js +71 -0
- data/test/package/rules/sass.test.js +11 -6
- data/test/package/rules/sass1.test.js +8 -5
- data/test/package/rules/sass16.test.js +24 -0
- data/test/package/rules/swc.test.js +50 -39
- data/test/package/rules/webpack.test.js +35 -0
- data/test/package/staging.test.js +4 -3
- data/test/package/transpiler-defaults.test.js +169 -0
- data/test/package/utils/ensureManifestExists.test.js +51 -0
- data/test/package/yamlSerializer.test.js +204 -0
- data/test/peer-dependencies.sh +85 -0
- data/test/resolver.js +34 -3
- data/test/scripts/remove-use-strict.test.js +125 -0
- data/test/typescript/build.test.js +118 -0
- data/test/typescript/environments.test.js +107 -0
- data/test/typescript/pathValidation.test.js +186 -0
- data/test/typescript/requireOrError.test.js +49 -0
- data/test/typescript/securityValidation.test.js +182 -0
- data/tools/README.md +134 -0
- data/tools/css-modules-v9-codemod.js +179 -0
- data/tsconfig.eslint.json +9 -0
- data/tsconfig.json +38 -0
- data/yarn.lock +3202 -1097
- metadata +212 -44
- data/.eslintignore +0 -4
- data/.eslintrc.js +0 -36
- data/Gemfile.lock +0 -251
- data/package/babel/preset.js +0 -48
- data/package/config.js +0 -56
- data/package/dev_server.js +0 -23
- data/package/env.js +0 -48
- data/package/environments/base.js +0 -171
- data/package/environments/development.js +0 -13
- data/package/environments/production.js +0 -88
- data/package/environments/test.js +0 -3
- data/package/esbuild/index.js +0 -40
- data/package/index.js +0 -40
- data/package/rules/css.js +0 -3
- data/package/rules/esbuild.js +0 -10
- data/package/rules/file.js +0 -29
- data/package/rules/raw.js +0 -5
- data/package/rules/sass.js +0 -18
- data/package/rules/swc.js +0 -10
- data/package/swc/index.js +0 -50
- data/package/utils/configPath.js +0 -4
- data/package/utils/defaultConfigPath.js +0 -2
- data/package/utils/getStyleRule.js +0 -40
- data/package/utils/helpers.js +0 -62
- data/package/utils/snakeToCamelCase.js +0 -5
- data/package/webpackDevServerConfig.js +0 -71
- data/test/package/rules/index.test.js +0 -16
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/* eslint-disable func-names, jest/no-conditional-in-test */
|
|
2
|
+
|
|
3
|
+
const { chdirTestApp } = require("../../helpers")
|
|
4
|
+
|
|
5
|
+
const rootPath = process.cwd()
|
|
6
|
+
chdirTestApp()
|
|
7
|
+
|
|
8
|
+
// Mock config to ensure assets_bundler is set to rspack
|
|
9
|
+
jest.mock("../../../package/config", () => {
|
|
10
|
+
const actual = jest.requireActual("../../../package/config")
|
|
11
|
+
return {
|
|
12
|
+
...actual,
|
|
13
|
+
assets_bundler: "rspack"
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
// Mock helpers before requiring the rspack module
|
|
18
|
+
jest.mock("../../../package/utils/helpers", () => {
|
|
19
|
+
const original = jest.requireActual("../../../package/utils/helpers")
|
|
20
|
+
const moduleExists = jest.fn(() => true)
|
|
21
|
+
return {
|
|
22
|
+
...original,
|
|
23
|
+
moduleExists
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// Mock validateDependencies to prevent actual validation
|
|
28
|
+
jest.mock("../../../package/utils/validateDependencies", () => ({
|
|
29
|
+
validateRspackDependencies: jest.fn()
|
|
30
|
+
}))
|
|
31
|
+
|
|
32
|
+
// Mock requireOrError to provide a fake @rspack/core (v2 is pure ESM, can't be require()'d by Jest)
|
|
33
|
+
jest.mock("../../../package/utils/requireOrError", () => ({
|
|
34
|
+
requireOrError: (moduleName) => {
|
|
35
|
+
if (moduleName === "@rspack/core") {
|
|
36
|
+
const CssExtractRspackPlugin = jest.fn(function (options) {
|
|
37
|
+
this.options = options
|
|
38
|
+
})
|
|
39
|
+
CssExtractRspackPlugin.loader = "css-extract-rspack-loader"
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
DefinePlugin: jest.fn(function (definitions) {
|
|
43
|
+
this.definitions = definitions
|
|
44
|
+
}),
|
|
45
|
+
EnvironmentPlugin: jest.fn(function (env) {
|
|
46
|
+
this.env = env
|
|
47
|
+
}),
|
|
48
|
+
ProvidePlugin: jest.fn(function (definitions) {
|
|
49
|
+
this.definitions = definitions
|
|
50
|
+
}),
|
|
51
|
+
HotModuleReplacementPlugin: jest.fn(),
|
|
52
|
+
ProgressPlugin: jest.fn(),
|
|
53
|
+
CssExtractRspackPlugin,
|
|
54
|
+
SubresourceIntegrityPlugin: jest.fn(function (options) {
|
|
55
|
+
this.options = options
|
|
56
|
+
}),
|
|
57
|
+
SwcJsMinimizerRspackPlugin: jest.fn(),
|
|
58
|
+
LightningCssMinimizerRspackPlugin: jest.fn()
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (moduleName === "rspack-manifest-plugin") {
|
|
62
|
+
return {
|
|
63
|
+
RspackManifestPlugin: jest.fn(function (options) {
|
|
64
|
+
this.options = options
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return jest
|
|
69
|
+
.requireActual("../../../package/utils/requireOrError")
|
|
70
|
+
.requireOrError(moduleName)
|
|
71
|
+
}
|
|
72
|
+
}))
|
|
73
|
+
|
|
74
|
+
describe("rspack/index", () => {
|
|
75
|
+
let rspackIndex
|
|
76
|
+
let validateRspackDependencies
|
|
77
|
+
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
jest.resetModules()
|
|
80
|
+
rspackIndex = require("../../../package/rspack/index")
|
|
81
|
+
;({
|
|
82
|
+
validateRspackDependencies
|
|
83
|
+
} = require("../../../package/utils/validateDependencies"))
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
afterAll(() => process.chdir(rootPath))
|
|
87
|
+
|
|
88
|
+
describe("exports", () => {
|
|
89
|
+
test("exports webpack-merge v5 functions", () => {
|
|
90
|
+
expect(rspackIndex.merge).toBeInstanceOf(Function)
|
|
91
|
+
expect(rspackIndex.mergeWithRules).toBeInstanceOf(Function)
|
|
92
|
+
expect(rspackIndex.mergeWithCustomize).toBeInstanceOf(Function)
|
|
93
|
+
expect(rspackIndex.unique).toBeInstanceOf(Function)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test("exports config object", () => {
|
|
97
|
+
expect(rspackIndex.config).toHaveProperty("source_path")
|
|
98
|
+
expect(rspackIndex.config).toHaveProperty("public_output_path")
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test("exports devServer object", () => {
|
|
102
|
+
expect(rspackIndex.devServer).toBeDefined()
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test("exports generateRspackConfig function", () => {
|
|
106
|
+
expect(rspackIndex.generateRspackConfig).toBeInstanceOf(Function)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test("exports baseConfig object", () => {
|
|
110
|
+
expect(rspackIndex.baseConfig).toBeDefined()
|
|
111
|
+
expect(rspackIndex.baseConfig).toHaveProperty("mode")
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test("exports env object", () => {
|
|
115
|
+
expect(rspackIndex.env).toHaveProperty("railsEnv")
|
|
116
|
+
expect(rspackIndex.env).toHaveProperty("nodeEnv")
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test("exports rules array", () => {
|
|
120
|
+
expect(Array.isArray(rspackIndex.rules)).toBe(true)
|
|
121
|
+
expect(rspackIndex.rules.length).toBeGreaterThan(0)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test("exports moduleExists function", () => {
|
|
125
|
+
expect(typeof rspackIndex.moduleExists).toBe("function")
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
test("exports canProcess function", () => {
|
|
129
|
+
expect(rspackIndex.canProcess).toBeInstanceOf(Function)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test("exports inliningCss value", () => {
|
|
133
|
+
expect(typeof rspackIndex.inliningCss).toBe("boolean")
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
describe("generateRspackConfig", () => {
|
|
138
|
+
test("returns a valid rspack config object", () => {
|
|
139
|
+
const config = rspackIndex.generateRspackConfig()
|
|
140
|
+
|
|
141
|
+
expect(config).toBeDefined()
|
|
142
|
+
expect(config).toHaveProperty("mode")
|
|
143
|
+
expect(config).toHaveProperty("module")
|
|
144
|
+
expect(config).toHaveProperty("plugins")
|
|
145
|
+
expect(config).toHaveProperty("optimization")
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test("returns a new top-level config object on each call", () => {
|
|
149
|
+
const config1 = rspackIndex.generateRspackConfig()
|
|
150
|
+
const config2 = rspackIndex.generateRspackConfig()
|
|
151
|
+
|
|
152
|
+
expect(config1).not.toBe(config2)
|
|
153
|
+
config1.newKey = "new value"
|
|
154
|
+
|
|
155
|
+
expect(config2).not.toHaveProperty("newKey")
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test("merges extra config", () => {
|
|
159
|
+
const config = rspackIndex.generateRspackConfig({
|
|
160
|
+
newKey: "new value",
|
|
161
|
+
output: {
|
|
162
|
+
path: "new path"
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
expect(config).toHaveProperty("newKey", "new value")
|
|
167
|
+
expect(config).toHaveProperty("output.path", "new path")
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
test("includes module rules in config", () => {
|
|
171
|
+
const config = rspackIndex.generateRspackConfig()
|
|
172
|
+
|
|
173
|
+
expect(config.module).toBeDefined()
|
|
174
|
+
expect(config.module.rules).toBeDefined()
|
|
175
|
+
expect(Array.isArray(config.module.rules)).toBe(true)
|
|
176
|
+
// The exact number of rules depends on which optional loaders are installed,
|
|
177
|
+
// so we only verify that at least some rules exist
|
|
178
|
+
expect(config.module.rules.length).toBeGreaterThan(0)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
test("includes plugins in config", () => {
|
|
182
|
+
const config = rspackIndex.generateRspackConfig()
|
|
183
|
+
|
|
184
|
+
expect(config.plugins).toBeDefined()
|
|
185
|
+
expect(Array.isArray(config.plugins)).toBe(true)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
test("includes optimization in config", () => {
|
|
189
|
+
const config = rspackIndex.generateRspackConfig()
|
|
190
|
+
|
|
191
|
+
expect(config.optimization).toBeDefined()
|
|
192
|
+
expect(config.optimization).toHaveProperty("minimize")
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
test("errors if multiple configs are provided", () => {
|
|
196
|
+
expect(() => rspackIndex.generateRspackConfig({}, {})).toThrow(
|
|
197
|
+
"use webpack-merge to merge configs before passing them to Shakapacker"
|
|
198
|
+
)
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test("validates rspack dependencies on generation", () => {
|
|
202
|
+
rspackIndex.generateRspackConfig()
|
|
203
|
+
expect(validateRspackDependencies).toHaveBeenCalledTimes(1)
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
describe("rules", () => {
|
|
208
|
+
test("includes JavaScript/JSX rule with builtin:swc-loader", () => {
|
|
209
|
+
const jsRule = rspackIndex.rules.find(
|
|
210
|
+
(rule) => rule.test && rule.test.toString().includes("js|jsx|mjs")
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
expect(jsRule).toBeDefined()
|
|
214
|
+
expect(jsRule.type).toBe("javascript/auto")
|
|
215
|
+
expect(jsRule.use).toBeDefined()
|
|
216
|
+
expect(Array.isArray(jsRule.use)).toBe(true)
|
|
217
|
+
expect(jsRule.use[0].loader).toBe("builtin:swc-loader")
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
test("includes TypeScript rule with builtin:swc-loader", () => {
|
|
221
|
+
const tsRule = rspackIndex.rules.find(
|
|
222
|
+
(rule) => rule.test && rule.test.toString().includes("ts|tsx")
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
expect(tsRule).toBeDefined()
|
|
226
|
+
expect(tsRule.type).toBe("javascript/auto")
|
|
227
|
+
expect(tsRule.use).toBeDefined()
|
|
228
|
+
expect(Array.isArray(tsRule.use)).toBe(true)
|
|
229
|
+
expect(tsRule.use[0].loader).toBe("builtin:swc-loader")
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
test("includes file/asset handling rule", () => {
|
|
233
|
+
const fileRule = rspackIndex.rules.find(
|
|
234
|
+
(rule) =>
|
|
235
|
+
rule.test &&
|
|
236
|
+
(rule.test.toString().includes("png") ||
|
|
237
|
+
rule.test.toString().includes("jpg") ||
|
|
238
|
+
rule.test.toString().includes("svg"))
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
expect(fileRule).toBeDefined()
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
test("includes raw file loading rule", () => {
|
|
245
|
+
const rawRule = rspackIndex.rules.find(
|
|
246
|
+
(rule) => rule.type === "asset/source"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
expect(rawRule).toBeDefined()
|
|
250
|
+
})
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
describe("helper functions", () => {
|
|
254
|
+
test("moduleExists returns boolean", () => {
|
|
255
|
+
const result = rspackIndex.moduleExists("some-module")
|
|
256
|
+
expect(typeof result).toBe("boolean")
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
test("canProcess invokes callback when module resolves", () => {
|
|
260
|
+
const callback = jest.fn((modulePath) => ({
|
|
261
|
+
processed: true,
|
|
262
|
+
path: modulePath
|
|
263
|
+
}))
|
|
264
|
+
const result = rspackIndex.canProcess("path", callback)
|
|
265
|
+
|
|
266
|
+
expect(callback).toHaveBeenCalledWith(expect.any(String))
|
|
267
|
+
expect(result).toHaveProperty("processed", true)
|
|
268
|
+
expect(result).toHaveProperty("path")
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
test("canProcess returns null and does not invoke callback when module is missing", () => {
|
|
272
|
+
const callback = jest.fn()
|
|
273
|
+
const result = rspackIndex.canProcess(
|
|
274
|
+
"__definitely_not_a_real_package_name__",
|
|
275
|
+
callback
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
expect(result).toBeNull()
|
|
279
|
+
expect(callback).not.toHaveBeenCalled()
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
describe("environment integration", () => {
|
|
284
|
+
test("uses correct environment config based on NODE_ENV", () => {
|
|
285
|
+
const config = rspackIndex.generateRspackConfig()
|
|
286
|
+
const { nodeEnv } = rspackIndex.env
|
|
287
|
+
|
|
288
|
+
const expectedMode =
|
|
289
|
+
nodeEnv === "production" ? "production" : "development"
|
|
290
|
+
expect(config.mode).toBe(expectedMode)
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
})
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* eslint-disable func-names */
|
|
2
|
+
|
|
3
|
+
// Mock requireOrError to prevent actual module loading
|
|
4
|
+
jest.mock("../../../package/utils/requireOrError", () => ({
|
|
5
|
+
requireOrError: (moduleName) => {
|
|
6
|
+
if (moduleName === "@rspack/core") {
|
|
7
|
+
return {
|
|
8
|
+
SwcJsMinimizerRspackPlugin: jest.fn(function () {
|
|
9
|
+
this.name = "SwcJsMinimizerRspackPlugin"
|
|
10
|
+
}),
|
|
11
|
+
LightningCssMinimizerRspackPlugin: jest.fn(function () {
|
|
12
|
+
this.name = "LightningCssMinimizerRspackPlugin"
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
throw new Error(`Module ${moduleName} not found`)
|
|
17
|
+
}
|
|
18
|
+
}))
|
|
19
|
+
|
|
20
|
+
// Mock debug logger
|
|
21
|
+
jest.mock("../../../package/utils/debug", () => ({
|
|
22
|
+
error: jest.fn(),
|
|
23
|
+
warn: jest.fn(),
|
|
24
|
+
info: jest.fn(),
|
|
25
|
+
debug: jest.fn()
|
|
26
|
+
}))
|
|
27
|
+
|
|
28
|
+
describe("rspack/optimization", () => {
|
|
29
|
+
let getOptimization
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
jest.resetModules()
|
|
33
|
+
const optimizationModule = require("../../../package/optimization/rspack")
|
|
34
|
+
getOptimization = optimizationModule.getOptimization
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
jest.clearAllMocks()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe("getOptimization", () => {
|
|
42
|
+
test("returns an optimization config object", () => {
|
|
43
|
+
const optimization = getOptimization()
|
|
44
|
+
|
|
45
|
+
expect(optimization).toBeDefined()
|
|
46
|
+
expect(optimization).toHaveProperty("minimize")
|
|
47
|
+
expect(optimization).toHaveProperty("minimizer")
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test("sets minimize to true", () => {
|
|
51
|
+
const optimization = getOptimization()
|
|
52
|
+
|
|
53
|
+
expect(optimization.minimize).toBe(true)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test("includes SwcJsMinimizerRspackPlugin", () => {
|
|
57
|
+
const optimization = getOptimization()
|
|
58
|
+
|
|
59
|
+
expect(Array.isArray(optimization.minimizer)).toBe(true)
|
|
60
|
+
const jsMinimizer = optimization.minimizer.find(
|
|
61
|
+
(m) => m.name === "SwcJsMinimizerRspackPlugin"
|
|
62
|
+
)
|
|
63
|
+
expect(jsMinimizer).toBeDefined()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test("includes LightningCssMinimizerRspackPlugin", () => {
|
|
67
|
+
const optimization = getOptimization()
|
|
68
|
+
|
|
69
|
+
expect(Array.isArray(optimization.minimizer)).toBe(true)
|
|
70
|
+
const cssMinimizer = optimization.minimizer.find(
|
|
71
|
+
(m) => m.name === "LightningCssMinimizerRspackPlugin"
|
|
72
|
+
)
|
|
73
|
+
expect(cssMinimizer).toBeDefined()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test("includes both minimizers in correct order", () => {
|
|
77
|
+
const optimization = getOptimization()
|
|
78
|
+
|
|
79
|
+
expect(optimization.minimizer).toHaveLength(2)
|
|
80
|
+
expect(optimization.minimizer[0].name).toBe("SwcJsMinimizerRspackPlugin")
|
|
81
|
+
expect(optimization.minimizer[1].name).toBe(
|
|
82
|
+
"LightningCssMinimizerRspackPlugin"
|
|
83
|
+
)
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
})
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/* eslint-disable func-names, jest/prefer-strict-equal */
|
|
2
|
+
|
|
3
|
+
// Mock helpers before requiring the plugins module
|
|
4
|
+
jest.mock("../../../package/utils/helpers", () => {
|
|
5
|
+
const original = jest.requireActual("../../../package/utils/helpers")
|
|
6
|
+
return {
|
|
7
|
+
...original,
|
|
8
|
+
moduleExists: jest.fn(() => true)
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
// Mock requireOrError to prevent actual module loading
|
|
13
|
+
jest.mock("../../../package/utils/requireOrError", () => ({
|
|
14
|
+
requireOrError: (moduleName) => {
|
|
15
|
+
if (moduleName === "rspack-manifest-plugin") {
|
|
16
|
+
return {
|
|
17
|
+
RspackManifestPlugin: jest.fn(function (options) {
|
|
18
|
+
this.options = options
|
|
19
|
+
this.name = "RspackManifestPlugin"
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (moduleName === "@rspack/core") {
|
|
24
|
+
return {
|
|
25
|
+
EnvironmentPlugin: jest.fn(function (env) {
|
|
26
|
+
this.env = env
|
|
27
|
+
this.name = "EnvironmentPlugin"
|
|
28
|
+
}),
|
|
29
|
+
CssExtractRspackPlugin: jest.fn(function (options) {
|
|
30
|
+
this.options = options
|
|
31
|
+
this.name = "CssExtractRspackPlugin"
|
|
32
|
+
}),
|
|
33
|
+
SubresourceIntegrityPlugin: jest.fn(function (options) {
|
|
34
|
+
this.options = options
|
|
35
|
+
this.name = "SubresourceIntegrityPlugin"
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`Module ${moduleName} not found`)
|
|
40
|
+
}
|
|
41
|
+
}))
|
|
42
|
+
|
|
43
|
+
describe("rspack/plugins", () => {
|
|
44
|
+
let getPlugins
|
|
45
|
+
let moduleExists
|
|
46
|
+
let config
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
jest.resetModules()
|
|
50
|
+
config = require("../../../package/config")
|
|
51
|
+
moduleExists = require("../../../package/utils/helpers").moduleExists
|
|
52
|
+
const pluginsModule = require("../../../package/plugins/rspack")
|
|
53
|
+
getPlugins = pluginsModule.getPlugins
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
afterEach(() => {
|
|
57
|
+
jest.clearAllMocks()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe("getPlugins", () => {
|
|
61
|
+
test("returns an array", () => {
|
|
62
|
+
const plugins = getPlugins()
|
|
63
|
+
expect(Array.isArray(plugins)).toBe(true)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test("includes EnvironmentPlugin with filtered env", () => {
|
|
67
|
+
const plugins = getPlugins()
|
|
68
|
+
const envPlugin = plugins.find((p) => p.name === "EnvironmentPlugin")
|
|
69
|
+
expect(envPlugin).toBeDefined()
|
|
70
|
+
// EnvironmentPlugin receives getFilteredEnv() - a security-filtered version of process.env
|
|
71
|
+
// that only includes allowlisted environment variables
|
|
72
|
+
expect(envPlugin.env).toBeDefined()
|
|
73
|
+
expect(typeof envPlugin.env).toBe("object")
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test("includes RspackManifestPlugin", () => {
|
|
77
|
+
const plugins = getPlugins()
|
|
78
|
+
const manifestPlugin = plugins.find(
|
|
79
|
+
(p) => p.name === "RspackManifestPlugin"
|
|
80
|
+
)
|
|
81
|
+
expect(manifestPlugin).toBeDefined()
|
|
82
|
+
expect(manifestPlugin.options).toBeDefined()
|
|
83
|
+
expect(manifestPlugin.options.writeToFileEmit).toBe(true)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test("rspackManifestPlugin has generate function", () => {
|
|
87
|
+
const plugins = getPlugins()
|
|
88
|
+
const manifestPlugin = plugins.find(
|
|
89
|
+
(p) => p.name === "RspackManifestPlugin"
|
|
90
|
+
)
|
|
91
|
+
expect(manifestPlugin.options.generate).toBeInstanceOf(Function)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test("rspackManifestPlugin generate creates proper manifest structure", () => {
|
|
95
|
+
const plugins = getPlugins()
|
|
96
|
+
const manifestPlugin = plugins.find(
|
|
97
|
+
(p) => p.name === "RspackManifestPlugin"
|
|
98
|
+
)
|
|
99
|
+
const { publicPath } = manifestPlugin.options
|
|
100
|
+
|
|
101
|
+
const files = [
|
|
102
|
+
{ name: "app.js", path: `${publicPath}app-123.js` },
|
|
103
|
+
{ name: "app.css", path: `${publicPath}app-456.css` }
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
const entrypoints = {
|
|
107
|
+
app: ["app-123.js", "app-456.css"]
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const manifest = manifestPlugin.options.generate(null, files, entrypoints)
|
|
111
|
+
|
|
112
|
+
expect(manifest["app.js"]).toBe(`${publicPath}app-123.js`)
|
|
113
|
+
expect(manifest["app.css"]).toBe(`${publicPath}app-456.css`)
|
|
114
|
+
expect(manifest).toHaveProperty("entrypoints")
|
|
115
|
+
expect(manifest.entrypoints).toHaveProperty("app")
|
|
116
|
+
expect(manifest.entrypoints.app).toHaveProperty("assets")
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test("rspackManifestPlugin filters hot-update files", () => {
|
|
120
|
+
const plugins = getPlugins()
|
|
121
|
+
const manifestPlugin = plugins.find(
|
|
122
|
+
(p) => p.name === "RspackManifestPlugin"
|
|
123
|
+
)
|
|
124
|
+
const { publicPath } = manifestPlugin.options
|
|
125
|
+
|
|
126
|
+
const files = []
|
|
127
|
+
const entrypoints = {
|
|
128
|
+
app: [
|
|
129
|
+
"app-123.js",
|
|
130
|
+
"app.hot-update.js",
|
|
131
|
+
"app-456.css",
|
|
132
|
+
"app.hot-update.css"
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const manifest = manifestPlugin.options.generate(null, files, entrypoints)
|
|
137
|
+
|
|
138
|
+
expect(manifest.entrypoints.app.assets.js).toEqual([
|
|
139
|
+
`${publicPath}app-123.js`
|
|
140
|
+
])
|
|
141
|
+
expect(manifest.entrypoints.app.assets.css).toEqual([
|
|
142
|
+
`${publicPath}app-456.css`
|
|
143
|
+
])
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
test("includes CssExtractRspackPlugin when css-loader exists", () => {
|
|
147
|
+
moduleExists.mockReturnValue(true)
|
|
148
|
+
|
|
149
|
+
const plugins = getPlugins()
|
|
150
|
+
const cssPlugin = plugins.find((p) => p.name === "CssExtractRspackPlugin")
|
|
151
|
+
expect(cssPlugin).toBeDefined()
|
|
152
|
+
expect(cssPlugin.options.filename).toMatch(/^css\//)
|
|
153
|
+
expect(cssPlugin.options.emit).toBe(true)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
test("does not include CssExtractRspackPlugin when css-loader is missing", () => {
|
|
157
|
+
moduleExists.mockReturnValue(false)
|
|
158
|
+
|
|
159
|
+
const plugins = getPlugins()
|
|
160
|
+
const cssPlugin = plugins.find((p) => p.name === "CssExtractRspackPlugin")
|
|
161
|
+
expect(cssPlugin).toBeUndefined()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
test("includes SubresourceIntegrityPlugin when integrity is enabled", () => {
|
|
165
|
+
const originalIntegrity = config.integrity
|
|
166
|
+
config.integrity = {
|
|
167
|
+
...originalIntegrity,
|
|
168
|
+
enabled: true,
|
|
169
|
+
hash_functions: ["sha256"]
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const plugins = getPlugins()
|
|
174
|
+
const sriPlugin = plugins.find(
|
|
175
|
+
(p) => p.name === "SubresourceIntegrityPlugin"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
expect(sriPlugin).toBeDefined()
|
|
179
|
+
expect(sriPlugin.options.hashFuncNames).toEqual(["sha256"])
|
|
180
|
+
} finally {
|
|
181
|
+
config.integrity = originalIntegrity
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
})
|