shakapacker 9.0.0.beta.0 → 9.0.0.beta.3

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/claude-code-review.yml +54 -0
  3. data/.github/workflows/claude.yml +50 -0
  4. data/.github/workflows/dummy.yml +3 -3
  5. data/.github/workflows/test-bundlers.yml +152 -0
  6. data/.rubocop.yml +1 -0
  7. data/CHANGELOG.md +15 -3
  8. data/CLAUDE.md +29 -0
  9. data/Gemfile.lock +1 -1
  10. data/README.md +42 -1
  11. data/Rakefile +39 -4
  12. data/TODO.md +51 -0
  13. data/TODO_v9.md +84 -0
  14. data/conductor-setup.sh +58 -0
  15. data/conductor.json +7 -0
  16. data/docs/cdn_setup.md +379 -0
  17. data/docs/css-modules-export-mode.md +216 -86
  18. data/docs/deployment.md +10 -1
  19. data/docs/rspack.md +7 -7
  20. data/docs/rspack_migration_guide.md +202 -0
  21. data/docs/using_esbuild_loader.md +3 -3
  22. data/docs/using_swc_loader.md +5 -3
  23. data/docs/v6_upgrade.md +10 -0
  24. data/docs/v9_upgrade.md +185 -0
  25. data/lib/install/bin/shakapacker +3 -17
  26. data/lib/install/config/shakapacker.yml +9 -4
  27. data/lib/shakapacker/configuration.rb +75 -3
  28. data/lib/shakapacker/dev_server_runner.rb +19 -9
  29. data/lib/shakapacker/manifest.rb +4 -3
  30. data/lib/shakapacker/rspack_runner.rb +4 -42
  31. data/lib/shakapacker/runner.rb +105 -11
  32. data/lib/shakapacker/utils/manager.rb +2 -0
  33. data/lib/shakapacker/version.rb +1 -1
  34. data/lib/shakapacker/version_checker.rb +1 -1
  35. data/lib/shakapacker/webpack_runner.rb +4 -42
  36. data/lib/tasks/shakapacker/install.rake +6 -2
  37. data/package/config.js +24 -0
  38. data/package/environments/base.js +12 -2
  39. data/package/environments/development.js +52 -12
  40. data/package/environments/production.js +8 -3
  41. data/package/environments/test.js +5 -3
  42. data/package/index.d.ts +69 -30
  43. data/package/index.js +1 -1
  44. data/package/optimization/rspack.js +9 -5
  45. data/package/plugins/rspack.js +12 -28
  46. data/package/rspack/index.js +57 -0
  47. data/package/rules/babel.js +2 -2
  48. data/package/rules/esbuild.js +2 -2
  49. data/package/rules/raw.js +5 -5
  50. data/package/rules/rspack.js +77 -7
  51. data/package/rules/swc.js +2 -2
  52. data/package/utils/debug.js +49 -0
  53. data/package/utils/getStyleRule.js +19 -10
  54. data/package/utils/requireOrError.js +1 -1
  55. data/package/utils/validateCssModulesConfig.js +91 -0
  56. data/package/utils/validateDependencies.js +61 -0
  57. data/package/webpackDevServerConfig.js +2 -0
  58. data/package-lock.json +11966 -0
  59. data/package.json +9 -2
  60. data/test/package/rules/esbuild.test.js +1 -1
  61. data/test/package/rules/swc.test.js +1 -1
  62. data/tools/README.md +124 -0
  63. data/tools/css-modules-v9-codemod.js +179 -0
  64. data/yarn.lock +199 -81
  65. metadata +20 -3
  66. data/lib/install/bin/shakapacker-rspack +0 -13
data/package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- declare module 'shakapacker' {
2
- import { Configuration, RuleSetRule } from 'webpack'
3
- import * as https from 'node:https';
1
+ declare module "shakapacker" {
2
+ import { Configuration, RuleSetRule } from "webpack"
3
+ import * as https from "node:https"
4
4
 
5
5
  export interface Config {
6
6
  source_path: string
@@ -9,20 +9,29 @@ declare module 'shakapacker' {
9
9
  css_extract_ignore_order_warnings: boolean
10
10
  public_root_path: string
11
11
  public_output_path: string
12
+ private_output_path?: string
12
13
  cache_path: string
13
14
  webpack_compile_output: boolean
14
15
  shakapacker_precompile: boolean
15
16
  additional_paths: string[]
16
17
  cache_manifest: boolean
17
- webpack_loader: string
18
+ javascript_transpiler: string
18
19
  ensure_consistent_versioning: boolean
19
20
  compiler_strategy: string
20
21
  useContentHash: boolean
21
- compile: boolean,
22
+ compile: boolean
22
23
  outputPath: string
23
24
  publicPath: string
24
25
  publicPathWithoutCDN: string
25
26
  manifestPath: string
27
+ manifest_path?: string
28
+ assets_bundler?: string
29
+ dev_server?: DevServerConfig
30
+ integrity?: {
31
+ enabled: boolean
32
+ cross_origin: string
33
+ hash_functions?: string[]
34
+ }
26
35
  }
27
36
 
28
37
  export interface Env {
@@ -33,9 +42,11 @@ declare module 'shakapacker' {
33
42
  runningWebpackDevServer: boolean
34
43
  }
35
44
 
36
- type Header = Array<{ key: string; value: string }> | Record<string, string | string[]>
37
- type ServerType = 'http' | 'https' | 'spdy'
38
- type WebSocketType = 'sockjs' | 'ws'
45
+ type Header =
46
+ | Array<{ key: string; value: string }>
47
+ | Record<string, string | string[]>
48
+ type ServerType = "http" | "https" | "spdy"
49
+ type WebSocketType = "sockjs" | "ws"
39
50
 
40
51
  /**
41
52
  * This has the same keys and behavior as https://webpack.js.org/configuration/dev-server/ except:
@@ -44,59 +55,87 @@ declare module 'shakapacker' {
44
55
  * @see {import('webpack-dev-server').Configuration}
45
56
  */
46
57
  interface DevServerConfig {
47
- allowed_hosts?: 'all' | 'auto' | string | string[]
58
+ allowed_hosts?: "all" | "auto" | string | string[]
48
59
  bonjour?: boolean | Record<string, unknown> // bonjour.BonjourOptions
49
60
  client?: Record<string, unknown> // Client
50
61
  compress?: boolean
51
62
  dev_middleware?: Record<string, unknown> // webpackDevMiddleware.Options
52
63
  headers?: Header | (() => Header)
53
64
  history_api_fallback?: boolean | Record<string, unknown> // HistoryApiFallbackOptions
54
- hmr?: 'only' | boolean
55
- host?: 'local-ip' | 'local-ipv4' | 'local-ipv6' | string
65
+ hmr?: "only" | boolean
66
+ host?: "local-ip" | "local-ipv4" | "local-ipv6" | string
56
67
  http2?: boolean
57
68
  https?: boolean | https.ServerOptions
58
69
  ipc?: boolean | string
59
70
  magic_html?: boolean
60
71
  live_reload?: boolean
61
- open?: boolean | string | string[] | Record<string, unknown> | Record<string, unknown>[]
62
- port?: 'auto' | string | number
72
+ inline_css?: boolean
73
+ env_prefix?: string
74
+ open?:
75
+ | boolean
76
+ | string
77
+ | string[]
78
+ | Record<string, unknown>
79
+ | Record<string, unknown>[]
80
+ port?: "auto" | string | number
63
81
  proxy?: unknown // ProxyConfigMap | ProxyConfigArray
64
82
  setup_exit_signals?: boolean
65
83
  static?: boolean | string | unknown // Static | Array<string | Static>
66
84
  watch_files?: string | string[] | unknown // WatchFiles | Array<WatchFiles | string>
67
- web_socket_server?: string | boolean | WebSocketType | { type?: string | boolean | WebSocketType, options?: Record<string, unknown> }
68
- server?: string | boolean | ServerType | { type?: string | boolean | ServerType, options?: https.ServerOptions }
85
+ web_socket_server?:
86
+ | string
87
+ | boolean
88
+ | WebSocketType
89
+ | {
90
+ type?: string | boolean | WebSocketType
91
+ options?: Record<string, unknown>
92
+ }
93
+ server?:
94
+ | string
95
+ | boolean
96
+ | ServerType
97
+ | { type?: string | boolean | ServerType; options?: https.ServerOptions }
69
98
  [otherWebpackDevServerConfigKey: string]: unknown
70
99
  }
71
100
 
72
101
  export const config: Config
73
102
  export const devServer: DevServerConfig
74
- export function generateWebpackConfig(extraConfig?: Configuration): Configuration
103
+ export function generateWebpackConfig(
104
+ extraConfig?: Configuration
105
+ ): Configuration
75
106
  export const baseConfig: Configuration
76
107
  export const env: Env
77
108
  export const rules: RuleSetRule[]
78
109
  export function moduleExists(packageName: string): boolean
79
- export function canProcess<T = unknown>(rule: string, fn: (modulePath: string) => T): T | null
110
+ export function canProcess<T = unknown>(
111
+ rule: string,
112
+ fn: (modulePath: string) => T
113
+ ): T | null
80
114
  export const inliningCss: boolean
81
- export * from 'webpack-merge'
115
+ export * from "webpack-merge"
82
116
  }
83
117
 
84
- declare module 'shakapacker/rspack' {
85
- import type { RspackOptions, RuleSetRule } from '@rspack/core'
86
-
87
- export const config: Config
88
- export function generateRspackConfig(extraConfig?: RspackOptions): RspackOptions
118
+ declare module "shakapacker/rspack" {
119
+ import type { RspackOptions } from "@rspack/core"
120
+
121
+ export const config: import("shakapacker").Config
122
+ export function generateRspackConfig(
123
+ extraConfig?: RspackOptions
124
+ ): RspackOptions
89
125
  export const baseConfig: RspackOptions
90
- export const env: Env
91
- export const rules: RuleSetRule[]
126
+ export const env: import("shakapacker").Env
127
+ export const rules: NonNullable<RspackOptions["module"]>["rules"]
92
128
  export function moduleExists(packageName: string): boolean
93
- export function canProcess<T = unknown>(rule: string, fn: (modulePath: string) => T): T | null
129
+ export function canProcess<T = unknown>(
130
+ rule: string,
131
+ fn: (modulePath: string) => T
132
+ ): T | null
94
133
  export const inliningCss: boolean
95
- export * from 'webpack-merge'
134
+ export * from "webpack-merge"
96
135
  }
97
136
 
98
- declare module 'shakapacker/package/babel/preset.js' {
99
- import { ConfigAPI, PluginItem, TransformOptions } from '@babel/core'
137
+ declare module "shakapacker/package/babel/preset.js" {
138
+ import { ConfigAPI, PluginItem, TransformOptions } from "@babel/core"
100
139
 
101
140
  interface RequiredTransformOptions {
102
141
  plugins: PluginItem[]
@@ -108,4 +147,4 @@ declare module 'shakapacker/package/babel/preset.js' {
108
147
  ) => TransformOptions & RequiredTransformOptions
109
148
 
110
149
  export = defaultConfigFunc
111
- }
150
+ }
data/package/index.js CHANGED
@@ -7,7 +7,7 @@ const { existsSync } = require("fs")
7
7
  const config = require("./config")
8
8
  const baseConfig = require("./environments/base")
9
9
 
10
- const rulesPath = resolve(__dirname, "rules", `${config.bundler}.js`)
10
+ const rulesPath = resolve(__dirname, "rules", `${config.assets_bundler}.js`)
11
11
  const rules = require(rulesPath)
12
12
  const devServer = require("./dev_server")
13
13
  const env = require("./env")
@@ -1,6 +1,7 @@
1
1
  const { requireOrError } = require("../utils/requireOrError")
2
+ const { error: logError } = require("../utils/debug")
2
3
 
3
- const { rspack } = requireOrError("@rspack/core")
4
+ const rspack = requireOrError("@rspack/core")
4
5
 
5
6
  const getOptimization = () => {
6
7
  // Use Rspack's built-in minification instead of terser-webpack-plugin
@@ -11,10 +12,13 @@ const getOptimization = () => {
11
12
  new rspack.LightningCssMinimizerRspackPlugin()
12
13
  ]
13
14
  } catch (error) {
14
- // eslint-disable-next-line no-console
15
- console.warn(
16
- "[SHAKAPACKER]: Warning: Could not configure Rspack minimizers:",
17
- error.message
15
+ // Log full error with stack trace
16
+ logError(
17
+ `Failed to configure Rspack minimizers: ${error.message}\n${error.stack}`
18
+ )
19
+ // Re-throw the error to properly propagate it
20
+ throw new Error(
21
+ `Could not configure Rspack minimizers: ${error.message}. Please check that @rspack/core is properly installed.`
18
22
  )
19
23
  }
20
24
  return result
@@ -1,4 +1,3 @@
1
- const { existsSync, readFileSync } = require("fs")
2
1
  const { requireOrError } = require("../utils/requireOrError")
3
2
 
4
3
  const { RspackManifestPlugin } = requireOrError("rspack-manifest-plugin")
@@ -16,27 +15,7 @@ const getPlugins = () => {
16
15
  writeToFileEmit: true,
17
16
  // rspack-manifest-plugin uses different option names than webpack-assets-manifest
18
17
  generate: (seed, files, entrypoints) => {
19
- let manifest = seed || {}
20
-
21
- // Load existing manifest if it exists to handle concurrent builds
22
- try {
23
- if (existsSync(config.manifestPath)) {
24
- const existingContent = readFileSync(config.manifestPath, "utf8")
25
- const parsed = JSON.parse(existingContent)
26
- if (parsed && typeof parsed === "object") {
27
- manifest = {
28
- ...manifest,
29
- ...parsed
30
- }
31
- }
32
- }
33
- } catch (error) {
34
- // eslint-disable-next-line no-console
35
- console.warn(
36
- "[SHAKAPACKER]: Warning: Could not read existing manifest.json:",
37
- String(error)
38
- )
39
- }
18
+ const manifest = seed || {}
40
19
 
41
20
  // Add files mapping first
42
21
  files.forEach((file) => {
@@ -47,12 +26,17 @@ const getPlugins = () => {
47
26
  const entrypointsManifest = {}
48
27
  Object.entries(entrypoints).forEach(
49
28
  ([entrypointName, entrypointFiles]) => {
50
- const jsFiles = entrypointFiles.filter((file) =>
51
- file.endsWith(".js")
52
- )
53
- const cssFiles = entrypointFiles.filter((file) =>
54
- file.endsWith(".css")
55
- )
29
+ const jsFiles = entrypointFiles
30
+ .filter(
31
+ (file) => file.endsWith(".js") && !file.includes(".hot-update.")
32
+ )
33
+ .map((file) => config.publicPathWithoutCDN + file)
34
+ const cssFiles = entrypointFiles
35
+ .filter(
36
+ (file) =>
37
+ file.endsWith(".css") && !file.includes(".hot-update.")
38
+ )
39
+ .map((file) => config.publicPathWithoutCDN + file)
56
40
 
57
41
  entrypointsManifest[entrypointName] = {
58
42
  assets: {
@@ -0,0 +1,57 @@
1
+ /* eslint global-require: 0 */
2
+ /* eslint import/no-dynamic-require: 0 */
3
+
4
+ const webpackMerge = require("webpack-merge")
5
+ const { resolve } = require("path")
6
+ const { existsSync } = require("fs")
7
+ const config = require("../config")
8
+ const baseConfig = require("../environments/base")
9
+
10
+ const rulesPath = resolve(__dirname, "../rules", "rspack.js")
11
+ const rules = require(rulesPath)
12
+ const devServer = require("../dev_server")
13
+ const env = require("../env")
14
+ const { moduleExists, canProcess } = require("../utils/helpers")
15
+ const inliningCss = require("../utils/inliningCss")
16
+ const { getPlugins } = require("../plugins/rspack")
17
+ const { getOptimization } = require("../optimization/rspack")
18
+ const { validateRspackDependencies } = require("../utils/validateDependencies")
19
+
20
+ const generateRspackConfig = (extraConfig = {}, ...extraArgs) => {
21
+ // Validate required dependencies first
22
+ validateRspackDependencies()
23
+ if (extraArgs.length > 0) {
24
+ throw new Error(
25
+ "Only one extra config may be passed here - use webpack-merge to merge configs before passing them to Shakapacker"
26
+ )
27
+ }
28
+
29
+ const { nodeEnv } = env
30
+ const path = resolve(__dirname, "../environments", `${nodeEnv}.js`)
31
+ const environmentConfig = existsSync(path) ? require(path) : baseConfig
32
+
33
+ // Create base rspack config
34
+ const rspackConfig = {
35
+ ...environmentConfig,
36
+ module: {
37
+ rules
38
+ },
39
+ plugins: getPlugins(),
40
+ optimization: getOptimization()
41
+ }
42
+
43
+ return webpackMerge.merge({}, rspackConfig, extraConfig)
44
+ }
45
+
46
+ module.exports = {
47
+ config, // shakapacker.yml
48
+ devServer,
49
+ generateRspackConfig,
50
+ baseConfig,
51
+ env,
52
+ rules,
53
+ moduleExists,
54
+ canProcess,
55
+ inliningCss,
56
+ ...webpackMerge
57
+ }
@@ -1,9 +1,9 @@
1
1
  const { loaderMatches } = require("../utils/helpers")
2
- const { webpack_loader: webpackLoader } = require("../config")
2
+ const { javascript_transpiler: javascriptTranspiler } = require("../config")
3
3
  const { isProduction } = require("../env")
4
4
  const jscommon = require("./jscommon")
5
5
 
6
- module.exports = loaderMatches(webpackLoader, "babel", () => ({
6
+ module.exports = loaderMatches(javascriptTranspiler, "babel", () => ({
7
7
  test: /\.(js|jsx|mjs|ts|tsx|coffee)?(\.erb)?$/,
8
8
  ...jscommon,
9
9
  use: [
@@ -1,9 +1,9 @@
1
1
  const { loaderMatches } = require("../utils/helpers")
2
2
  const { getEsbuildLoaderConfig } = require("../esbuild")
3
- const { webpack_loader: webpackLoader } = require("../config")
3
+ const { javascript_transpiler: javascriptTranspiler } = require("../config")
4
4
  const jscommon = require("./jscommon")
5
5
 
6
- module.exports = loaderMatches(webpackLoader, "esbuild", () => ({
6
+ module.exports = loaderMatches(javascriptTranspiler, "esbuild", () => ({
7
7
  test: /\.(ts|tsx|js|jsx|mjs|coffee)?(\.erb)?$/,
8
8
  ...jscommon,
9
9
  use: ({ resource }) => getEsbuildLoaderConfig(resource)
data/package/rules/raw.js CHANGED
@@ -1,15 +1,15 @@
1
1
  const config = require("../config")
2
2
 
3
- const rspackRawConfig = {
3
+ const rspackRawConfig = () => ({
4
4
  resourceQuery: /raw/,
5
5
  type: "asset/source"
6
- }
6
+ })
7
7
 
8
- const webpackRawConfig = {
8
+ const webpackRawConfig = () => ({
9
9
  test: /\.html$/,
10
10
  exclude: /\.(js|mjs|jsx|ts|tsx)$/,
11
11
  type: "asset/source"
12
- }
12
+ })
13
13
 
14
14
  module.exports =
15
- config.bundler === "rspack" ? rspackRawConfig : webpackRawConfig
15
+ config.assets_bundler === "rspack" ? rspackRawConfig() : webpackRawConfig()
@@ -1,13 +1,19 @@
1
1
  /* eslint global-require: 0 */
2
2
 
3
3
  const { moduleExists } = require("../utils/helpers")
4
+ const { debug, info, warn } = require("../utils/debug")
5
+
6
+ debug("Loading Rspack rules configuration...")
4
7
 
5
8
  const rules = []
6
9
 
7
10
  // Use Rspack's built-in SWC loader for JavaScript files
11
+ debug("Adding JavaScript rule with builtin:swc-loader")
8
12
  rules.push({
9
13
  test: /\.(js|jsx|mjs)$/,
10
14
  exclude: /node_modules/,
15
+ // The 'type' field is required for Rspack to properly handle JavaScript modules
16
+ // when using builtin loaders. It ensures correct module parsing and transformation.
11
17
  type: "javascript/auto",
12
18
  use: [
13
19
  {
@@ -30,9 +36,12 @@ rules.push({
30
36
  })
31
37
 
32
38
  // Use Rspack's built-in SWC loader for TypeScript files
39
+ debug("Adding TypeScript rule with builtin:swc-loader")
33
40
  rules.push({
34
41
  test: /\.(ts|tsx)$/,
35
42
  exclude: /node_modules/,
43
+ // The 'type' field is required for Rspack to properly handle TypeScript modules
44
+ // when using builtin loaders. It ensures correct module parsing and transformation.
36
45
  type: "javascript/auto",
37
46
  use: [
38
47
  {
@@ -55,42 +64,103 @@ rules.push({
55
64
  })
56
65
 
57
66
  // CSS rules using Rspack's built-in CSS handling
67
+ debug("Checking for CSS loader...")
58
68
  if (moduleExists("css-loader")) {
69
+ debug("css-loader found, loading CSS rule configuration...")
59
70
  const css = require("./css")
60
- rules.push(css)
71
+ if (css) {
72
+ debug("Successfully added CSS rule")
73
+ rules.push(css)
74
+ } else {
75
+ warn("css-loader found but rule configuration returned null")
76
+ }
77
+ } else {
78
+ info("Skipping CSS support - css-loader not installed")
61
79
  }
62
80
 
63
81
  // Sass rules
82
+ debug("Checking for Sass loader...")
64
83
  if (moduleExists("sass") && moduleExists("sass-loader")) {
84
+ debug("sass and sass-loader found, loading Sass rule configuration...")
65
85
  const sass = require("./sass")
66
- rules.push(sass)
86
+ if (sass) {
87
+ debug("Successfully added Sass rule")
88
+ rules.push(sass)
89
+ } else {
90
+ warn("sass and sass-loader found but rule configuration returned null")
91
+ }
92
+ } else if (!moduleExists("sass")) {
93
+ info("Skipping Sass support - sass not installed")
94
+ } else if (!moduleExists("sass-loader")) {
95
+ info("Skipping Sass support - sass-loader not installed")
67
96
  }
68
97
 
69
98
  // Less rules
99
+ debug("Checking for Less loader...")
70
100
  if (moduleExists("less") && moduleExists("less-loader")) {
101
+ debug("less and less-loader found, loading Less rule configuration...")
71
102
  const less = require("./less")
72
- rules.push(less)
103
+ if (less) {
104
+ debug("Successfully added Less rule")
105
+ rules.push(less)
106
+ } else {
107
+ warn("less and less-loader found but rule configuration returned null")
108
+ }
109
+ } else if (!moduleExists("less")) {
110
+ info("Skipping Less support - less not installed")
111
+ } else if (!moduleExists("less-loader")) {
112
+ info("Skipping Less support - less-loader not installed")
73
113
  }
74
114
 
75
115
  // Stylus rules
116
+ debug("Checking for Stylus loader...")
76
117
  if (moduleExists("stylus") && moduleExists("stylus-loader")) {
118
+ debug("stylus and stylus-loader found, loading Stylus rule configuration...")
77
119
  const stylus = require("./stylus")
78
- rules.push(stylus)
120
+ if (stylus) {
121
+ debug("Successfully added Stylus rule")
122
+ rules.push(stylus)
123
+ } else {
124
+ warn("stylus and stylus-loader found but rule configuration returned null")
125
+ }
126
+ } else if (!moduleExists("stylus")) {
127
+ info("Skipping Stylus support - stylus not installed")
128
+ } else if (!moduleExists("stylus-loader")) {
129
+ info("Skipping Stylus support - stylus-loader not installed")
79
130
  }
80
131
 
81
132
  // ERB template support
133
+ debug("Checking for ERB template support...")
82
134
  const erb = require("./erb")
83
135
 
84
- rules.push(erb)
136
+ if (erb) {
137
+ debug("Successfully added ERB rule")
138
+ rules.push(erb)
139
+ } else {
140
+ info("Skipping ERB support - rails-erb-loader not installed")
141
+ }
85
142
 
86
143
  // File/asset handling using Rspack's built-in asset modules
144
+ debug("Adding file/asset handling rule...")
87
145
  const file = require("./file")
88
146
 
89
- rules.push(file)
147
+ if (file) {
148
+ debug("Successfully added file/asset rule")
149
+ rules.push(file)
150
+ } else {
151
+ warn("file rule configuration returned null")
152
+ }
90
153
 
91
154
  // Raw file loading
155
+ debug("Adding raw file loading rule...")
92
156
  const raw = require("./raw")
93
157
 
94
- rules.push(raw)
158
+ if (raw) {
159
+ debug("Successfully added raw file rule")
160
+ rules.push(raw)
161
+ } else {
162
+ warn("raw rule configuration returned null")
163
+ }
95
164
 
165
+ debug(`Rspack rules configuration complete. Total rules: ${rules.length}`)
96
166
  module.exports = rules
data/package/rules/swc.js CHANGED
@@ -1,9 +1,9 @@
1
1
  const { loaderMatches } = require("../utils/helpers")
2
2
  const { getSwcLoaderConfig } = require("../swc")
3
- const { webpack_loader: webpackLoader } = require("../config")
3
+ const { javascript_transpiler: javascriptTranspiler } = require("../config")
4
4
  const jscommon = require("./jscommon")
5
5
 
6
- module.exports = loaderMatches(webpackLoader, "swc", () => ({
6
+ module.exports = loaderMatches(javascriptTranspiler, "swc", () => ({
7
7
  test: /\.(ts|tsx|js|jsx|mjs|coffee)?(\.erb)?$/,
8
8
  ...jscommon,
9
9
  use: ({ resource }) => getSwcLoaderConfig(resource)
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Debug utility for Shakapacker
3
+ * Provides conditional logging based on environment variables
4
+ */
5
+
6
+ const isDebugMode = () => {
7
+ // Explicitly check for debug mode being disabled
8
+ if (process.env.SHAKAPACKER_DEBUG === "false") {
9
+ return false
10
+ }
11
+
12
+ // Support both SHAKAPACKER_DEBUG (new) and DEBUG_SHAKAPACKER (legacy) for backwards compatibility
13
+ return (
14
+ process.env.SHAKAPACKER_DEBUG === "true" ||
15
+ process.env.DEBUG_SHAKAPACKER === "true"
16
+ )
17
+ }
18
+
19
+ const debug = (message, ...args) => {
20
+ if (isDebugMode()) {
21
+ // eslint-disable-next-line no-console
22
+ console.log(`[Shakapacker] ${message}`, ...args)
23
+ }
24
+ }
25
+
26
+ const warn = (message, ...args) => {
27
+ // eslint-disable-next-line no-console
28
+ console.warn(`[Shakapacker] WARNING: ${message}`, ...args)
29
+ }
30
+
31
+ const error = (message, ...args) => {
32
+ // eslint-disable-next-line no-console
33
+ console.error(`[Shakapacker] ERROR: ${message}`, ...args)
34
+ }
35
+
36
+ const info = (message, ...args) => {
37
+ if (isDebugMode()) {
38
+ // eslint-disable-next-line no-console
39
+ console.info(`[Shakapacker] INFO: ${message}`, ...args)
40
+ }
41
+ }
42
+
43
+ module.exports = {
44
+ debug,
45
+ warn,
46
+ error,
47
+ info,
48
+ isDebugMode
49
+ }
@@ -3,6 +3,7 @@ const { canProcess, moduleExists } = require("./helpers")
3
3
  const { requireOrError } = require("./requireOrError")
4
4
  const config = require("../config")
5
5
  const inliningCss = require("./inliningCss")
6
+ const { validateCssModulesConfig } = require("./validateCssModulesConfig")
6
7
 
7
8
  const getStyleRule = (test, preprocessors = []) => {
8
9
  if (moduleExists("css-loader")) {
@@ -15,21 +16,29 @@ const getStyleRule = (test, preprocessors = []) => {
15
16
  // style-loader is required when using css modules with HMR on the webpack-dev-server
16
17
 
17
18
  const extractionPlugin =
18
- config.bundle === "rspack"
19
+ config.assets_bundler === "rspack"
19
20
  ? requireOrError("@rspack/core").CssExtractRspackPlugin.loader
20
21
  : requireOrError("mini-css-extract-plugin").loader
21
22
 
23
+ const cssLoaderOptions = {
24
+ sourceMap: true,
25
+ importLoaders: 2,
26
+ modules: {
27
+ auto: true,
28
+ // v9 defaults: named exports with camelCase conversion
29
+ namedExport: true,
30
+ exportLocalsConvention: "camelCase"
31
+ }
32
+ }
33
+
34
+ // Validate CSS modules configuration
35
+ validateCssModulesConfig(cssLoaderOptions)
36
+
22
37
  const use = [
23
38
  inliningCss ? "style-loader" : extractionPlugin,
24
39
  {
25
40
  loader: require.resolve("css-loader"),
26
- options: {
27
- sourceMap: true,
28
- importLoaders: 2,
29
- modules: {
30
- auto: true
31
- }
32
- }
41
+ options: cssLoaderOptions
33
42
  },
34
43
  tryPostcss(),
35
44
  ...preprocessors
@@ -40,8 +49,8 @@ const getStyleRule = (test, preprocessors = []) => {
40
49
  use
41
50
  }
42
51
 
43
- if (config.bundle === "rspack") {
44
- result.type = "javascript/auto" // Required for rspack CSS extraction
52
+ if (config.assets_bundler === "rspack") {
53
+ result.type = "javascript/auto"
45
54
  }
46
55
 
47
56
  return result
@@ -7,7 +7,7 @@ const requireOrError = (moduleName) => {
7
7
  return require(moduleName)
8
8
  } catch (error) {
9
9
  throw new Error(
10
- `[SHAKAPACKER]: ${moduleName} is required for ${config.bundler} but is not installed. View Shakapacker's documented dependencies at https://github.com/shakacode/shakapacker/tree/main/docs/peer-dependencies.md`
10
+ `[SHAKAPACKER]: ${moduleName} is required for ${config.assets_bundler} but is not installed. View Shakapacker's documented dependencies at https://github.com/shakacode/shakapacker/tree/main/docs/peer-dependencies.md`
11
11
  )
12
12
  }
13
13
  }