shakapacker 9.0.0.beta.4 → 9.0.0.beta.5

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dummy.yml +4 -0
  3. data/.github/workflows/generator.yml +7 -0
  4. data/.github/workflows/node.yml +22 -0
  5. data/.github/workflows/ruby.yml +11 -0
  6. data/.github/workflows/test-bundlers.yml +18 -0
  7. data/.gitignore +20 -0
  8. data/.yalcignore +26 -0
  9. data/CHANGELOG.md +58 -40
  10. data/Gemfile.lock +1 -1
  11. data/README.md +3 -1
  12. data/docs/typescript.md +99 -0
  13. data/docs/v9_upgrade.md +14 -1
  14. data/lib/install/template.rb +8 -1
  15. data/lib/shakapacker/configuration.rb +58 -1
  16. data/lib/shakapacker/doctor.rb +752 -0
  17. data/lib/shakapacker/swc_migrator.rb +292 -0
  18. data/lib/shakapacker/version.rb +1 -1
  19. data/lib/shakapacker.rb +1 -0
  20. data/lib/tasks/shakapacker/doctor.rake +8 -0
  21. data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
  22. data/lib/tasks/shakapacker.rake +1 -0
  23. data/package/config.ts +162 -0
  24. data/package/{dev_server.js → dev_server.ts} +8 -5
  25. data/package/env.ts +67 -0
  26. data/package/environments/base.js +21 -31
  27. data/package/environments/base.ts +137 -0
  28. data/package/index.d.ts +3 -150
  29. data/package/{index.js → index.ts} +17 -8
  30. data/package/loaders.d.ts +27 -0
  31. data/package/types.ts +108 -0
  32. data/package/utils/configPath.ts +6 -0
  33. data/package/utils/{debug.js → debug.ts} +7 -7
  34. data/package/utils/defaultConfigPath.ts +4 -0
  35. data/package/utils/errorHelpers.ts +77 -0
  36. data/package/utils/{getStyleRule.js → getStyleRule.ts} +17 -20
  37. data/package/utils/helpers.ts +85 -0
  38. data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
  39. data/package/utils/{requireOrError.js → requireOrError.ts} +2 -2
  40. data/package/utils/snakeToCamelCase.ts +5 -0
  41. data/package/utils/typeGuards.ts +228 -0
  42. data/package/utils/{validateDependencies.js → validateDependencies.ts} +4 -4
  43. data/package/webpack-types.d.ts +32 -0
  44. data/package/webpackDevServerConfig.ts +117 -0
  45. data/package.json +6 -2
  46. data/test/typescript/build.test.js +117 -0
  47. data/tsconfig.json +39 -0
  48. data/yarn.lock +1 -1
  49. metadata +31 -17
  50. data/package/config.js +0 -80
  51. data/package/env.js +0 -48
  52. data/package/utils/configPath.js +0 -4
  53. data/package/utils/defaultConfigPath.js +0 -2
  54. data/package/utils/helpers.js +0 -127
  55. data/package/utils/snakeToCamelCase.js +0 -5
  56. data/package/utils/validateCssModulesConfig.js +0 -91
  57. data/package/webpackDevServerConfig.js +0 -73
@@ -0,0 +1,117 @@
1
+ /* eslint-env jest */
2
+ const { execSync } = require("child_process")
3
+ const { existsSync, readFileSync } = require("fs")
4
+ const { join } = require("path")
5
+
6
+ describe("typescript build", () => {
7
+ const rootPath = join(__dirname, "..", "..")
8
+
9
+ describe("typescript compilation", () => {
10
+ it("should compile TypeScript files without errors", () => {
11
+ expect(() => {
12
+ execSync("npx tsc --noEmit", { cwd: rootPath, stdio: "pipe" })
13
+ }).not.toThrow()
14
+ })
15
+
16
+ it("should generate JavaScript files from TypeScript", () => {
17
+ // Check that key TypeScript files compile to JavaScript
18
+ const tsFiles = ["config", "env", "index", "dev_server"]
19
+
20
+ tsFiles.forEach((file) => {
21
+ const jsPath = join(rootPath, "package", `${file}.js`)
22
+ const tsPath = join(rootPath, "package", `${file}.ts`)
23
+
24
+ expect(existsSync(tsPath)).toBe(true)
25
+ expect(existsSync(jsPath)).toBe(true)
26
+
27
+ // Verify JS file is newer than TS file (has been compiled)
28
+ const jsContent = readFileSync(jsPath, "utf8")
29
+ expect(jsContent).toContain("use strict")
30
+ })
31
+ })
32
+
33
+ it("should generate type definition files", () => {
34
+ const dtsFiles = ["config", "env", "index", "types", "dev_server"]
35
+
36
+ dtsFiles.forEach((file) => {
37
+ const dtsPath = join(rootPath, "package", `${file}.d.ts`)
38
+ expect(existsSync(dtsPath)).toBe(true)
39
+ })
40
+ })
41
+ })
42
+
43
+ describe("commonJS compatibility", () => {
44
+ it("should export modules using CommonJS format", () => {
45
+ const config = require("../../package/config")
46
+ const env = require("../../package/env")
47
+ const helpers = require("../../package/utils/helpers")
48
+
49
+ expect(config).toBeDefined()
50
+ expect(env.railsEnv).toBeDefined()
51
+ expect(helpers.moduleExists).toBeDefined()
52
+ })
53
+
54
+ it("should maintain backward compatibility", () => {
55
+ const index = require("../../package/index")
56
+
57
+ // Check all expected exports are present
58
+ expect(index.config).toBeDefined()
59
+ expect(index.env).toBeDefined()
60
+ expect(index.generateWebpackConfig).toBeInstanceOf(Function)
61
+ expect(index.moduleExists).toBeInstanceOf(Function)
62
+ expect(index.canProcess).toBeInstanceOf(Function)
63
+ })
64
+ })
65
+
66
+ describe("type guards", () => {
67
+ it("should have runtime type validation functions", () => {
68
+ const typeGuards = require("../../package/utils/typeGuards")
69
+
70
+ expect(typeGuards.isValidConfig).toBeInstanceOf(Function)
71
+ expect(typeGuards.isValidDevServerConfig).toBeInstanceOf(Function)
72
+ expect(typeGuards.isValidYamlConfig).toBeInstanceOf(Function)
73
+ expect(typeGuards.isPartialConfig).toBeInstanceOf(Function)
74
+ })
75
+
76
+ it("should validate config objects correctly", () => {
77
+ const { isPartialConfig } = require("../../package/utils/typeGuards")
78
+
79
+ const validPartial = {
80
+ source_path: "app/javascript",
81
+ nested_entries: true
82
+ }
83
+
84
+ const invalidPartial = {
85
+ source_path: 123, // Should be string
86
+ nested_entries: "yes" // Should be boolean
87
+ }
88
+
89
+ expect(isPartialConfig(validPartial)).toBe(true)
90
+ expect(isPartialConfig(invalidPartial)).toBe(false)
91
+ })
92
+ })
93
+
94
+ describe("error helpers", () => {
95
+ it("should have error handling utilities", () => {
96
+ const errorHelpers = require("../../package/utils/errorHelpers")
97
+
98
+ expect(errorHelpers.isFileNotFoundError).toBeInstanceOf(Function)
99
+ expect(errorHelpers.isModuleNotFoundError).toBeInstanceOf(Function)
100
+ expect(errorHelpers.getErrorMessage).toBeInstanceOf(Function)
101
+ })
102
+
103
+ it("should correctly identify ENOENT errors", () => {
104
+ const {
105
+ isFileNotFoundError
106
+ } = require("../../package/utils/errorHelpers")
107
+
108
+ const enoentError = new Error("File not found")
109
+ enoentError.code = "ENOENT"
110
+
111
+ const otherError = new Error("Other error")
112
+
113
+ expect(isFileNotFoundError(enoentError)).toBe(true)
114
+ expect(isFileNotFoundError(otherError)).toBe(false)
115
+ })
116
+ })
117
+ })
data/tsconfig.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "typeRoots": ["./node_modules/@types", "node_modules/@types", "../node_modules/@types"],
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "outDir": "./package",
10
+ "rootDir": "./package",
11
+ "strict": true,
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "resolveJsonModule": true,
16
+ "moduleResolution": "node",
17
+ "allowJs": true,
18
+ "checkJs": false,
19
+ "noImplicitAny": true,
20
+ "strictNullChecks": true,
21
+ "strictFunctionTypes": true,
22
+ "strictBindCallApply": true,
23
+ "strictPropertyInitialization": false,
24
+ "noImplicitThis": true,
25
+ "alwaysStrict": false,
26
+ "allowSyntheticDefaultImports": true,
27
+ "preserveConstEnums": true,
28
+ "isolatedModules": true,
29
+ "removeComments": false
30
+ },
31
+ "include": [
32
+ "package/**/*.ts"
33
+ ],
34
+ "exclude": [
35
+ "node_modules",
36
+ "package/**/*.test.ts",
37
+ "package/**/*.spec.ts"
38
+ ]
39
+ }
data/yarn.lock CHANGED
@@ -5997,7 +5997,7 @@ webpack-merge@*:
5997
5997
 
5998
5998
  webpack-merge@^5.8.0:
5999
5999
  version "5.10.0"
6000
- resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz"
6000
+ resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177"
6001
6001
  integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==
6002
6002
  dependencies:
6003
6003
  clone-deep "^4.0.1"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shakapacker
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.0.0.beta.4
4
+ version: 9.0.0.beta.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
@@ -149,6 +149,7 @@ files:
149
149
  - ".node-version"
150
150
  - ".rspec"
151
151
  - ".rubocop.yml"
152
+ - ".yalcignore"
152
153
  - CHANGELOG.md
153
154
  - CLAUDE.md
154
155
  - CONTRIBUTING.md
@@ -180,6 +181,7 @@ files:
180
181
  - docs/subresource_integrity.md
181
182
  - docs/transpiler-performance.md
182
183
  - docs/troubleshooting.md
184
+ - docs/typescript.md
183
185
  - docs/using_esbuild_loader.md
184
186
  - docs/using_swc_loader.md
185
187
  - docs/v6_upgrade.md
@@ -214,6 +216,7 @@ files:
214
216
  - lib/shakapacker/dev_server_proxy.rb
215
217
  - lib/shakapacker/dev_server_runner.rb
216
218
  - lib/shakapacker/digest_strategy.rb
219
+ - lib/shakapacker/doctor.rb
217
220
  - lib/shakapacker/env.rb
218
221
  - lib/shakapacker/helper.rb
219
222
  - lib/shakapacker/instance.rb
@@ -222,6 +225,7 @@ files:
222
225
  - lib/shakapacker/railtie.rb
223
226
  - lib/shakapacker/rspack_runner.rb
224
227
  - lib/shakapacker/runner.rb
228
+ - lib/shakapacker/swc_migrator.rb
225
229
  - lib/shakapacker/utils/manager.rb
226
230
  - lib/shakapacker/utils/misc.rb
227
231
  - lib/shakapacker/utils/version_syntax_converter.rb
@@ -236,22 +240,26 @@ files:
236
240
  - lib/tasks/shakapacker/clean.rake
237
241
  - lib/tasks/shakapacker/clobber.rake
238
242
  - lib/tasks/shakapacker/compile.rake
243
+ - lib/tasks/shakapacker/doctor.rake
239
244
  - lib/tasks/shakapacker/info.rake
240
245
  - lib/tasks/shakapacker/install.rake
246
+ - lib/tasks/shakapacker/migrate_to_swc.rake
241
247
  - lib/tasks/shakapacker/verify_config.rake
242
248
  - lib/tasks/shakapacker/verify_install.rake
243
249
  - package.json
244
250
  - package/babel/preset.js
245
- - package/config.js
246
- - package/dev_server.js
247
- - package/env.js
251
+ - package/config.ts
252
+ - package/dev_server.ts
253
+ - package/env.ts
248
254
  - package/environments/base.js
255
+ - package/environments/base.ts
249
256
  - package/environments/development.js
250
257
  - package/environments/production.js
251
258
  - package/environments/test.js
252
259
  - package/esbuild/index.js
253
260
  - package/index.d.ts
254
- - package/index.js
261
+ - package/index.ts
262
+ - package/loaders.d.ts
255
263
  - package/optimization/rspack.js
256
264
  - package/optimization/webpack.js
257
265
  - package/plugins/rspack.js
@@ -272,17 +280,20 @@ files:
272
280
  - package/rules/swc.js
273
281
  - package/rules/webpack.js
274
282
  - package/swc/index.js
275
- - package/utils/configPath.js
276
- - package/utils/debug.js
277
- - package/utils/defaultConfigPath.js
278
- - package/utils/getStyleRule.js
279
- - package/utils/helpers.js
280
- - package/utils/inliningCss.js
281
- - package/utils/requireOrError.js
282
- - package/utils/snakeToCamelCase.js
283
- - package/utils/validateCssModulesConfig.js
284
- - package/utils/validateDependencies.js
285
- - package/webpackDevServerConfig.js
283
+ - package/types.ts
284
+ - package/utils/configPath.ts
285
+ - package/utils/debug.ts
286
+ - package/utils/defaultConfigPath.ts
287
+ - package/utils/errorHelpers.ts
288
+ - package/utils/getStyleRule.ts
289
+ - package/utils/helpers.ts
290
+ - package/utils/inliningCss.ts
291
+ - package/utils/requireOrError.ts
292
+ - package/utils/snakeToCamelCase.ts
293
+ - package/utils/typeGuards.ts
294
+ - package/utils/validateDependencies.ts
295
+ - package/webpack-types.d.ts
296
+ - package/webpackDevServerConfig.ts
286
297
  - prettier.config.js
287
298
  - shakapacker.gemspec
288
299
  - test/helpers.js
@@ -307,14 +318,16 @@ files:
307
318
  - test/package/staging.test.js
308
319
  - test/package/test.test.js
309
320
  - test/resolver.js
321
+ - test/typescript/build.test.js
310
322
  - tools/README.md
311
323
  - tools/css-modules-v9-codemod.js
324
+ - tsconfig.json
312
325
  - yarn.lock
313
326
  homepage: https://github.com/shakacode/shakapacker
314
327
  licenses:
315
328
  - MIT
316
329
  metadata:
317
- source_code_uri: https://github.com/shakacode/shakapacker/tree/v9.0.0.beta.4
330
+ source_code_uri: https://github.com/shakacode/shakapacker/tree/v9.0.0.beta.5
318
331
  rdoc_options: []
319
332
  require_paths:
320
333
  - lib
@@ -355,3 +368,4 @@ test_files:
355
368
  - test/package/staging.test.js
356
369
  - test/package/test.test.js
357
370
  - test/resolver.js
371
+ - test/typescript/build.test.js
data/package/config.js DELETED
@@ -1,80 +0,0 @@
1
- const { resolve } = require("path")
2
- const { load } = require("js-yaml")
3
- const { existsSync, readFileSync } = require("fs")
4
-
5
- const { merge } = require("webpack-merge")
6
- const { ensureTrailingSlash } = require("./utils/helpers")
7
- const { railsEnv } = require("./env")
8
- const configPath = require("./utils/configPath")
9
-
10
- const defaultConfigPath = require("./utils/defaultConfigPath")
11
-
12
- const getDefaultConfig = () => {
13
- const defaultConfig = load(readFileSync(defaultConfigPath), "utf8")
14
- return defaultConfig[railsEnv] || defaultConfig.production
15
- }
16
-
17
- const defaults = getDefaultConfig()
18
- let config
19
-
20
- if (existsSync(configPath)) {
21
- const appYmlObject = load(readFileSync(configPath), "utf8")
22
- const envAppConfig = appYmlObject[railsEnv]
23
-
24
- if (!envAppConfig) {
25
- /* eslint no-console:0 */
26
- console.warn(
27
- `Warning: ${railsEnv} key not found in the configuration file. Using production configuration as a fallback.`
28
- )
29
- }
30
-
31
- config = merge(defaults, envAppConfig || {})
32
- } else {
33
- config = merge(defaults, {})
34
- }
35
-
36
- config.outputPath = resolve(config.public_root_path, config.public_output_path)
37
-
38
- // Ensure that the publicPath includes our asset host so dynamic imports
39
- // (code-splitting chunks and static assets) load from the CDN instead of a relative path.
40
- const getPublicPath = () => {
41
- const rootUrl = ensureTrailingSlash(process.env.SHAKAPACKER_ASSET_HOST || "/")
42
- return `${rootUrl}${config.public_output_path}/`
43
- }
44
-
45
- config.publicPath = getPublicPath()
46
- config.publicPathWithoutCDN = `/${config.public_output_path}/`
47
-
48
- if (config.manifest_path) {
49
- config.manifestPath = resolve(config.manifest_path)
50
- } else {
51
- config.manifestPath = resolve(config.outputPath, "manifest.json")
52
- }
53
- // Ensure no duplicate hash functions exist in the returned config object
54
- config.integrity.hash_functions = [...new Set(config.integrity.hash_functions)]
55
-
56
- // Allow ENV variable to override assets_bundler
57
- if (process.env.SHAKAPACKER_ASSETS_BUNDLER) {
58
- config.assets_bundler = process.env.SHAKAPACKER_ASSETS_BUNDLER
59
- }
60
-
61
- // Define clear defaults
62
- // SWC is now the default transpiler for both webpack and rspack
63
- const DEFAULT_JAVASCRIPT_TRANSPILER = "swc"
64
-
65
- // Backward compatibility: Add webpack_loader property that maps to javascript_transpiler
66
- // Show deprecation warning if webpack_loader is used
67
- if (config.webpack_loader && !config.javascript_transpiler) {
68
- console.warn(
69
- "⚠️ DEPRECATION WARNING: The 'webpack_loader' configuration option is deprecated. Please use 'javascript_transpiler' instead as it better reflects its purpose of configuring JavaScript transpilation regardless of the bundler used."
70
- )
71
- config.javascript_transpiler = config.webpack_loader
72
- } else if (!config.javascript_transpiler) {
73
- config.javascript_transpiler =
74
- config.webpack_loader || DEFAULT_JAVASCRIPT_TRANSPILER
75
- }
76
-
77
- // Ensure webpack_loader is always available for backward compatibility
78
- config.webpack_loader = config.javascript_transpiler
79
-
80
- module.exports = config
data/package/env.js DELETED
@@ -1,48 +0,0 @@
1
- const { load } = require("js-yaml")
2
- const { readFileSync } = require("fs")
3
- const defaultConfigPath = require("./utils/defaultConfigPath")
4
-
5
- const NODE_ENVIRONMENTS = ["development", "production", "test"]
6
- const DEFAULT = "production"
7
- const configPath = require("./utils/configPath")
8
-
9
- const railsEnv = process.env.RAILS_ENV
10
- const rawNodeEnv = process.env.NODE_ENV
11
- const nodeEnv =
12
- rawNodeEnv && NODE_ENVIRONMENTS.includes(rawNodeEnv) ? rawNodeEnv : DEFAULT
13
- const isProduction = nodeEnv === "production"
14
- const isDevelopment = nodeEnv === "development"
15
-
16
- let config
17
- try {
18
- config = load(readFileSync(configPath), "utf8")
19
- } catch (error) {
20
- if (error.code === "ENOENT") {
21
- // File not found, use default configuration
22
- config = load(readFileSync(defaultConfigPath), "utf8")
23
- } else {
24
- throw error
25
- }
26
- }
27
-
28
- const availableEnvironments = Object.keys(config).join("|")
29
- const regex = new RegExp(`^(${availableEnvironments})$`, "g")
30
-
31
- const runningWebpackDevServer = process.env.WEBPACK_SERVE === "true"
32
-
33
- const validatedRailsEnv = railsEnv && railsEnv.match(regex) ? railsEnv : DEFAULT
34
-
35
- if (railsEnv && validatedRailsEnv !== railsEnv) {
36
- /* eslint no-console:0 */
37
- console.warn(
38
- `Warning: '${railsEnv}' environment not found in the configuration. Using '${DEFAULT}' configuration as a fallback.`
39
- )
40
- }
41
-
42
- module.exports = {
43
- railsEnv: validatedRailsEnv,
44
- nodeEnv,
45
- isProduction,
46
- isDevelopment,
47
- runningWebpackDevServer
48
- }
@@ -1,4 +0,0 @@
1
- const { resolve } = require("path")
2
-
3
- module.exports =
4
- process.env.SHAKAPACKER_CONFIG || resolve("config", "shakapacker.yml")
@@ -1,2 +0,0 @@
1
- const path = require.resolve("../../lib/install/config/shakapacker.yml")
2
- module.exports = path
@@ -1,127 +0,0 @@
1
- const isBoolean = (str) => /^true/.test(str) || /^false/.test(str)
2
-
3
- const ensureTrailingSlash = (path) => (path.endsWith("/") ? path : `${path}/`)
4
-
5
- const resolvedPath = (packageName) => {
6
- try {
7
- return require.resolve(packageName)
8
- } catch (e) {
9
- if (e.code !== "MODULE_NOT_FOUND") {
10
- throw e
11
- }
12
- return null
13
- }
14
- }
15
-
16
- const moduleExists = (packageName) => !!resolvedPath(packageName)
17
-
18
- const canProcess = (rule, fn) => {
19
- const modulePath = resolvedPath(rule)
20
-
21
- if (modulePath) {
22
- return fn(modulePath)
23
- }
24
-
25
- return null
26
- }
27
-
28
- const validateBabelDependencies = () => {
29
- // Only validate core dependencies that are absolutely required
30
- // Additional packages like presets are optional and project-specific
31
- const coreRequiredPackages = ["@babel/core", "babel-loader"]
32
-
33
- const missingCorePackages = coreRequiredPackages.filter(
34
- (pkg) => !moduleExists(pkg)
35
- )
36
-
37
- if (missingCorePackages.length > 0) {
38
- const installCommand = `npm install --save-dev ${missingCorePackages.join(" ")}`
39
-
40
- // Check for commonly needed packages and suggest them
41
- const suggestedPackages = [
42
- "@babel/preset-env",
43
- "@babel/plugin-transform-runtime",
44
- "@babel/runtime"
45
- ]
46
-
47
- const missingSuggested = suggestedPackages.filter(
48
- (pkg) => !moduleExists(pkg)
49
- )
50
- let additionalHelp = ""
51
-
52
- if (missingSuggested.length > 0) {
53
- additionalHelp =
54
- `\n\nYou may also need: ${missingSuggested.join(", ")}\n` +
55
- `Install with: npm install --save-dev ${missingSuggested.join(" ")}`
56
- }
57
-
58
- throw new Error(
59
- `Babel is configured but core packages are missing: ${missingCorePackages.join(", ")}\n\n` +
60
- `To fix this, run:\n ${installCommand}${additionalHelp}\n\n` +
61
- `💡 Tip: Consider migrating to SWC for 20x faster compilation:\n` +
62
- ` 1. Set javascript_transpiler: 'swc' in config/shakapacker.yml\n` +
63
- ` 2. Run: npm install @swc/core swc-loader`
64
- )
65
- }
66
- }
67
-
68
- const loaderMatches = (configLoader, loaderToCheck, fn) => {
69
- if (configLoader !== loaderToCheck) {
70
- return null
71
- }
72
-
73
- const loaderName = `${configLoader}-loader`
74
-
75
- // Special validation for babel to check all dependencies
76
- if (configLoader === "babel") {
77
- validateBabelDependencies()
78
- }
79
-
80
- if (!moduleExists(loaderName)) {
81
- let installCommand = ""
82
- let migrationHelp = ""
83
-
84
- if (configLoader === "babel") {
85
- installCommand =
86
- "npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/runtime"
87
- migrationHelp =
88
- "\n\n💡 Tip: Consider migrating to SWC for 20x faster compilation:\n" +
89
- " 1. Set javascript_transpiler: 'swc' in config/shakapacker.yml\n" +
90
- " 2. Run: npm install @swc/core swc-loader"
91
- } else if (configLoader === "swc") {
92
- installCommand = "npm install --save-dev @swc/core swc-loader"
93
- migrationHelp =
94
- "\n\n✨ SWC is 20x faster than Babel with zero configuration!"
95
- } else if (configLoader === "esbuild") {
96
- installCommand = "npm install --save-dev esbuild esbuild-loader"
97
- }
98
-
99
- throw new Error(
100
- `Your Shakapacker config specified using ${configLoader}, but ${loaderName} package is not installed.\n\n` +
101
- `To fix this, run:\n ${installCommand}${migrationHelp}`
102
- )
103
- }
104
-
105
- return fn()
106
- }
107
-
108
- const packageFullVersion = (packageName) => {
109
- // eslint-disable-next-line import/no-dynamic-require
110
- const packageJsonPath = require.resolve(`${packageName}/package.json`)
111
- // eslint-disable-next-line import/no-dynamic-require, global-require
112
- return require(packageJsonPath).version
113
- }
114
-
115
- const packageMajorVersion = (packageName) =>
116
- packageFullVersion(packageName).match(/^\d+/)[0]
117
-
118
- module.exports = {
119
- isBoolean,
120
- ensureTrailingSlash,
121
- canProcess,
122
- moduleExists,
123
- validateBabelDependencies,
124
- loaderMatches,
125
- packageFullVersion,
126
- packageMajorVersion
127
- }
@@ -1,5 +0,0 @@
1
- function snakeToCamelCase(s) {
2
- return s.replace(/(_\w)/g, (match) => match[1].toUpperCase())
3
- }
4
-
5
- module.exports = snakeToCamelCase
@@ -1,91 +0,0 @@
1
- /* eslint global-require: 0 */
2
- const { warn } = require("./debug")
3
-
4
- /**
5
- * Validates CSS modules configuration and warns about potential issues
6
- * with v9 defaults or conflicting settings.
7
- */
8
- const validateCssModulesConfig = (cssLoaderOptions) => {
9
- // Skip validation in production by default for performance
10
- if (
11
- process.env.NODE_ENV === "production" &&
12
- process.env.SHAKAPACKER_VALIDATE_CSS_MODULES !== "true"
13
- ) {
14
- return
15
- }
16
-
17
- if (!cssLoaderOptions || !cssLoaderOptions.modules) {
18
- return
19
- }
20
-
21
- const { modules } = cssLoaderOptions
22
-
23
- // Check for conflicting namedExport and esModule settings
24
- if (modules.namedExport === true && modules.esModule === false) {
25
- warn(
26
- "⚠️ CSS Modules Configuration Warning:\n" +
27
- " namedExport: true with esModule: false may cause issues.\n" +
28
- " Consider setting esModule: true or removing it (defaults to true)."
29
- )
30
- }
31
-
32
- // Check for v8-style configuration with v9
33
- if (modules.namedExport === false) {
34
- warn(
35
- "ℹ️ CSS Modules Configuration Note:\n" +
36
- " You are using namedExport: false (v8 behavior).\n" +
37
- " Shakapacker v9 defaults to namedExport: true for better tree-shaking.\n" +
38
- " See docs/css-modules-export-mode.md for migration instructions."
39
- )
40
- }
41
-
42
- // Check for inconsistent exportLocalsConvention with namedExport
43
- if (
44
- modules.namedExport === true &&
45
- modules.exportLocalsConvention === "asIs"
46
- ) {
47
- warn(
48
- "⚠️ CSS Modules Configuration Warning:\n" +
49
- " Using namedExport: true with exportLocalsConvention: 'asIs' may cause issues\n" +
50
- " with kebab-case class names (e.g., 'my-button').\n" +
51
- " Consider using exportLocalsConvention: 'camelCase' (v9 default)."
52
- )
53
- }
54
-
55
- // Check for deprecated localIdentName pattern
56
- if (
57
- modules.localIdentName &&
58
- modules.localIdentName.includes("[hash:base64]")
59
- ) {
60
- warn(
61
- "⚠️ CSS Modules Configuration Warning:\n" +
62
- " [hash:base64] is deprecated in css-loader v6+.\n" +
63
- " Use [hash] instead for better compatibility."
64
- )
65
- }
66
-
67
- // Check for potential TypeScript issues
68
- if (
69
- modules.namedExport === true &&
70
- process.env.SHAKAPACKER_ASSET_COMPILER_TYPESCRIPT === "true"
71
- ) {
72
- warn(
73
- "ℹ️ TypeScript CSS Modules Note:\n" +
74
- " With namedExport: true, TypeScript projects should use:\n" +
75
- " import * as styles from './styles.module.css'\n" +
76
- " instead of: import styles from './styles.module.css'\n" +
77
- " See docs/css-modules-export-mode.md for TypeScript setup."
78
- )
79
- }
80
-
81
- // Check for auto: true with getLocalIdent (potential conflict)
82
- if (modules.auto === true && modules.getLocalIdent) {
83
- warn(
84
- "⚠️ CSS Modules Configuration Warning:\n" +
85
- " Using both 'auto: true' and 'getLocalIdent' may cause conflicts.\n" +
86
- " The 'auto' option determines which files are treated as CSS modules."
87
- )
88
- }
89
- }
90
-
91
- module.exports = { validateCssModulesConfig }