shakapacker 9.3.0 → 9.3.1

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/update-changelog.md +224 -0
  3. data/.github/actionlint-matcher.json +17 -0
  4. data/.github/workflows/dummy.yml +9 -0
  5. data/.github/workflows/generator.yml +13 -0
  6. data/.github/workflows/node.yml +83 -0
  7. data/.github/workflows/ruby.yml +11 -0
  8. data/.github/workflows/test-bundlers.yml +10 -0
  9. data/CHANGELOG.md +15 -8
  10. data/CLAUDE.md +6 -10
  11. data/CONTRIBUTING.md +57 -0
  12. data/Gemfile.lock +1 -1
  13. data/README.md +58 -33
  14. data/docs/api-reference.md +519 -0
  15. data/docs/configuration.md +11 -5
  16. data/docs/css-modules-export-mode.md +40 -6
  17. data/docs/transpiler-migration.md +12 -9
  18. data/docs/using_swc_loader.md +13 -10
  19. data/docs/v9_upgrade.md +11 -2
  20. data/eslint.config.fast.js +120 -8
  21. data/eslint.config.js +50 -31
  22. data/lib/install/config/shakapacker.yml +14 -1
  23. data/lib/shakapacker/configuration.rb +47 -4
  24. data/lib/shakapacker/dev_server_runner.rb +4 -0
  25. data/lib/shakapacker/doctor.rb +1 -1
  26. data/lib/shakapacker/version.rb +1 -1
  27. data/package/config.ts +2 -3
  28. data/package/configExporter/cli.ts +29 -24
  29. data/package/dev_server.ts +2 -2
  30. data/package/env.ts +1 -1
  31. data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +6 -6
  32. data/package/environments/base.ts +2 -2
  33. data/package/environments/development.ts +3 -6
  34. data/package/environments/production.ts +2 -2
  35. data/package/environments/test.ts +2 -1
  36. data/package/esbuild/index.ts +0 -2
  37. data/package/index.d.ts +1 -0
  38. data/package/index.d.ts.template +1 -0
  39. data/package/index.ts +0 -1
  40. data/package/plugins/rspack.ts +3 -1
  41. data/package/plugins/webpack.ts +5 -3
  42. data/package/rspack/index.ts +3 -3
  43. data/package/rules/file.ts +1 -1
  44. data/package/rules/raw.ts +3 -1
  45. data/package/rules/rspack.ts +0 -2
  46. data/package/rules/sass.ts +0 -2
  47. data/package/rules/webpack.ts +0 -1
  48. data/package/swc/index.ts +0 -2
  49. data/package/types.ts +8 -11
  50. data/package/utils/debug.ts +0 -4
  51. data/package/utils/getStyleRule.ts +17 -9
  52. data/package/utils/helpers.ts +8 -3
  53. data/package/utils/pathValidation.ts +10 -11
  54. data/package/utils/requireOrError.ts +4 -3
  55. data/package/webpackDevServerConfig.ts +4 -4
  56. data/package.json +1 -1
  57. data/test/package/transpiler-defaults.test.js +42 -0
  58. metadata +5 -2
@@ -18,24 +18,20 @@ const isDebugMode = (): boolean => {
18
18
 
19
19
  const debug = (message: string, ...args: unknown[]): void => {
20
20
  if (isDebugMode()) {
21
- // eslint-disable-next-line no-console
22
21
  console.log(`[Shakapacker] ${message}`, ...args)
23
22
  }
24
23
  }
25
24
 
26
25
  const warn = (message: string, ...args: unknown[]): void => {
27
- // eslint-disable-next-line no-console
28
26
  console.warn(`[Shakapacker] WARNING: ${message}`, ...args)
29
27
  }
30
28
 
31
29
  const error = (message: string, ...args: unknown[]): void => {
32
- // eslint-disable-next-line no-console
33
30
  console.error(`[Shakapacker] ERROR: ${message}`, ...args)
34
31
  }
35
32
 
36
33
  const info = (message: string, ...args: unknown[]): void => {
37
34
  if (isDebugMode()) {
38
- // eslint-disable-next-line no-console
39
35
  console.info(`[Shakapacker] INFO: ${message}`, ...args)
40
36
  }
41
37
  }
@@ -1,18 +1,19 @@
1
- /* eslint global-require: 0 */
1
+ import type { Config } from "../types"
2
+
2
3
  const { canProcess, moduleExists } = require("./helpers")
3
4
  const { requireOrError } = require("./requireOrError")
4
- const config = require("../config")
5
+ const config = require("../config") as Config
5
6
  const inliningCss = require("./inliningCss")
6
7
 
7
8
  interface StyleRule {
8
9
  test: RegExp
9
- use: any[]
10
+ use: unknown[]
10
11
  type?: string
11
12
  }
12
13
 
13
14
  const getStyleRule = (
14
15
  test: RegExp,
15
- preprocessors: any[] = []
16
+ preprocessors: unknown[] = []
16
17
  ): StyleRule | null => {
17
18
  if (moduleExists("css-loader")) {
18
19
  const tryPostcss = () =>
@@ -28,6 +29,11 @@ const getStyleRule = (
28
29
  ? requireOrError("@rspack/core").CssExtractRspackPlugin.loader
29
30
  : requireOrError("mini-css-extract-plugin").loader
30
31
 
32
+ // Determine CSS Modules export mode based on configuration
33
+ // 'named' (default): Use named exports with camelCaseOnly (v9 behavior)
34
+ // 'default': Use default exports with camelCase (v8 behavior)
35
+ const useNamedExports = config.css_modules_export_mode !== "default"
36
+
31
37
  const use = [
32
38
  inliningCss ? "style-loader" : extractionPlugin,
33
39
  {
@@ -37,11 +43,13 @@ const getStyleRule = (
37
43
  importLoaders: 2,
38
44
  modules: {
39
45
  auto: true,
40
- // v9 defaults: Use named exports with camelCase conversion
41
- // Note: css-loader requires 'camelCaseOnly' or 'dashesOnly' when namedExport is true
42
- // Using 'camelCase' with namedExport: true causes a build error
43
- namedExport: true,
44
- exportLocalsConvention: "camelCaseOnly"
46
+ // Use named exports for v9 (default), or default exports for v8 compatibility
47
+ namedExport: useNamedExports,
48
+ // 'camelCaseOnly' with namedExport: true (v9 default)
49
+ // 'camelCase' with namedExport: false (v8 behavior - exports both original and camelCase)
50
+ exportLocalsConvention: useNamedExports
51
+ ? "camelCaseOnly"
52
+ : "camelCase"
45
53
  }
46
54
  }
47
55
  },
@@ -38,6 +38,11 @@ const loaderMatches = <T = unknown>(
38
38
  loaderToCheck: string,
39
39
  fn: () => T
40
40
  ): T | null => {
41
+ // If transpiler is set to 'none', skip all transpiler rules (for custom webpack configs)
42
+ if (configLoader === "none") {
43
+ return null
44
+ }
45
+
41
46
  if (configLoader !== loaderToCheck) {
42
47
  return null
43
48
  }
@@ -60,13 +65,13 @@ const loaderMatches = <T = unknown>(
60
65
  const packageFullVersion = (packageName: string): string => {
61
66
  try {
62
67
  const packageJsonPath = require.resolve(`${packageName}/package.json`)
63
- // eslint-disable-next-line import/no-dynamic-require, global-require
68
+ // eslint-disable-next-line import/no-dynamic-require
64
69
  const packageJson = require(packageJsonPath) as { version: string }
65
70
  return packageJson.version
66
- } catch (error: any) {
71
+ } catch (error: unknown) {
67
72
  // Re-throw the error with proper code to maintain compatibility with babel preset
68
73
  // The preset expects MODULE_NOT_FOUND errors to handle missing core-js gracefully
69
- if (error.code === "MODULE_NOT_FOUND") {
74
+ if ((error as NodeJS.ErrnoException).code === "MODULE_NOT_FOUND") {
70
75
  throw error
71
76
  }
72
77
  // For other errors, warn and re-throw
@@ -138,17 +138,16 @@ export function validatePaths(paths: string[], basePath: string): string[] {
138
138
  console.warn(
139
139
  `[SHAKAPACKER WARNING] Skipping potentially unsafe path: ${userPath}`
140
140
  )
141
- continue
142
- }
143
-
144
- try {
145
- const safePath = safeResolvePath(basePath, userPath)
146
- validatedPaths.push(safePath)
147
- } catch (error) {
148
- console.warn(
149
- `[SHAKAPACKER WARNING] Invalid path configuration: ${userPath}\n` +
150
- `Error: ${error instanceof Error ? error.message : String(error)}`
151
- )
141
+ } else {
142
+ try {
143
+ const safePath = safeResolvePath(basePath, userPath)
144
+ validatedPaths.push(safePath)
145
+ } catch (error) {
146
+ console.warn(
147
+ `[SHAKAPACKER WARNING] Invalid path configuration: ${userPath}\n` +
148
+ `Error: ${error instanceof Error ? error.message : String(error)}`
149
+ )
150
+ }
152
151
  }
153
152
  }
154
153
 
@@ -1,12 +1,13 @@
1
- /* eslint global-require: 0 */
2
1
  /* eslint import/no-dynamic-require: 0 */
3
- const config = require("../config")
2
+ import type { Config } from "../types"
3
+
4
+ const config = require("../config") as Config
4
5
 
5
6
  interface ErrorWithCause extends Error {
6
7
  cause?: unknown
7
8
  }
8
9
 
9
- const requireOrError = (moduleName: string): any => {
10
+ const requireOrError = (moduleName: string): unknown => {
10
11
  try {
11
12
  return require(moduleName)
12
13
  } catch (originalError: unknown) {
@@ -25,11 +25,11 @@ interface WebpackDevServerConfig {
25
25
  [key: string]: unknown
26
26
  }
27
27
  client?: Record<string, unknown>
28
- allowedHosts?: "all" | "auto" | string | string[]
28
+ allowedHosts?: string | string[]
29
29
  bonjour?: boolean | Record<string, unknown>
30
30
  compress?: boolean
31
31
  headers?: Record<string, unknown> | (() => Record<string, unknown>)
32
- host?: "local-ip" | "local-ipv4" | "local-ipv6" | string
32
+ host?: string
33
33
  http2?: boolean
34
34
  https?: boolean | Record<string, unknown>
35
35
  ipc?: boolean | string
@@ -42,12 +42,12 @@ interface WebpackDevServerConfig {
42
42
  | string[]
43
43
  | Record<string, unknown>
44
44
  | Record<string, unknown>[]
45
- port?: "auto" | string | number
45
+ port?: string | number
46
46
  proxy?: unknown
47
47
  server?: string | boolean | Record<string, unknown>
48
48
  setupExitSignals?: boolean
49
49
  setupMiddlewares?: (middlewares: unknown[], devServer: unknown) => unknown[]
50
- watchFiles?: string | string[] | unknown
50
+ watchFiles?: unknown
51
51
  webSocketServer?: string | boolean | Record<string, unknown>
52
52
  [key: string]: unknown
53
53
  }
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shakapacker",
3
- "version": "9.3.0",
3
+ "version": "9.3.1",
4
4
  "description": "Use webpack to manage app-like JavaScript modules in Rails",
5
5
  "homepage": "https://github.com/shakacode/shakapacker",
6
6
  "bugs": {
@@ -124,4 +124,46 @@ describe("JavaScript Transpiler Defaults", () => {
124
124
  expect(config.javascript_transpiler).toBe("swc")
125
125
  })
126
126
  })
127
+
128
+ describe("none transpiler option", () => {
129
+ it("accepts 'none' as a valid transpiler value", () => {
130
+ process.env.SHAKAPACKER_JAVASCRIPT_TRANSPILER = "none"
131
+
132
+ jest.resetModules()
133
+ const config = require("../../package/config")
134
+
135
+ expect(config.javascript_transpiler).toBe("none")
136
+ })
137
+
138
+ it("'none' option doesn't throw errors for missing transpiler packages", () => {
139
+ process.env.SHAKAPACKER_JAVASCRIPT_TRANSPILER = "none"
140
+
141
+ jest.resetModules()
142
+
143
+ // Should not throw even though there's no 'none-loader' package
144
+ expect(() => {
145
+ require("../../package/config")
146
+ }).not.toThrow()
147
+ })
148
+
149
+ it("'none' option results in no transpiler rules being applied", () => {
150
+ process.env.SHAKAPACKER_JAVASCRIPT_TRANSPILER = "none"
151
+ delete process.env.SHAKAPACKER_ASSETS_BUNDLER
152
+
153
+ jest.resetModules()
154
+
155
+ // Load the webpack rules - should not include babel, swc, or esbuild rules
156
+ const rules = require("../../package/rules/webpack")
157
+
158
+ // Verify no transpiler rules are present by checking rules array
159
+ // When transpiler is 'none', loaderMatches returns null for all transpilers
160
+ // which means babel.ts, swc.ts, and esbuild.ts all export null
161
+ // These get filtered out, so rules array should not contain them
162
+ const rulesJson = JSON.stringify(rules)
163
+
164
+ expect(rulesJson).not.toContain("babel-loader")
165
+ expect(rulesJson).not.toContain("swc-loader")
166
+ expect(rulesJson).not.toContain("esbuild-loader")
167
+ })
168
+ })
127
169
  })
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.3.0
4
+ version: 9.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
@@ -131,11 +131,13 @@ executables: []
131
131
  extensions: []
132
132
  extra_rdoc_files: []
133
133
  files:
134
+ - ".claude/commands/update-changelog.md"
134
135
  - ".github/FUNDING.yml"
135
136
  - ".github/ISSUE_TEMPLATE/bug_report.md"
136
137
  - ".github/ISSUE_TEMPLATE/feature_request.md"
137
138
  - ".github/PULL_REQUEST_TEMPLATE.md"
138
139
  - ".github/STATUS.md"
140
+ - ".github/actionlint-matcher.json"
139
141
  - ".github/workflows/claude-code-review.yml"
140
142
  - ".github/workflows/claude.yml"
141
143
  - ".github/workflows/dummy.yml"
@@ -171,6 +173,7 @@ files:
171
173
  - conductor.json
172
174
  - config/README.md
173
175
  - config/shakapacker.yml
176
+ - docs/api-reference.md
174
177
  - docs/cdn_setup.md
175
178
  - docs/common-upgrades.md
176
179
  - docs/configuration.md
@@ -386,7 +389,7 @@ homepage: https://github.com/shakacode/shakapacker
386
389
  licenses:
387
390
  - MIT
388
391
  metadata:
389
- source_code_uri: https://github.com/shakacode/shakapacker/tree/v9.3.0
392
+ source_code_uri: https://github.com/shakacode/shakapacker/tree/v9.3.1
390
393
  rdoc_options: []
391
394
  require_paths:
392
395
  - lib