shakapacker 8.4.0 → 9.0.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.
Files changed (166) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintignore +1 -0
  3. data/.eslintrc.fast.js +40 -0
  4. data/.eslintrc.js +48 -0
  5. data/.github/STATUS.md +1 -0
  6. data/.github/workflows/claude-code-review.yml +54 -0
  7. data/.github/workflows/claude.yml +50 -0
  8. data/.github/workflows/dummy.yml +8 -4
  9. data/.github/workflows/generator.yml +17 -14
  10. data/.github/workflows/node.yml +23 -1
  11. data/.github/workflows/ruby.yml +11 -0
  12. data/.github/workflows/test-bundlers.yml +170 -0
  13. data/.gitignore +17 -0
  14. data/.husky/pre-commit +2 -0
  15. data/.npmignore +56 -0
  16. data/.prettierignore +3 -0
  17. data/.rubocop.yml +1 -0
  18. data/.yalcignore +26 -0
  19. data/CHANGELOG.md +156 -18
  20. data/CLAUDE.md +29 -0
  21. data/CONTRIBUTING.md +138 -20
  22. data/Gemfile.lock +3 -3
  23. data/README.md +130 -5
  24. data/Rakefile +39 -4
  25. data/TODO.md +50 -0
  26. data/TODO_v9.md +87 -0
  27. data/conductor-setup.sh +70 -0
  28. data/conductor.json +7 -0
  29. data/docs/cdn_setup.md +379 -0
  30. data/docs/css-modules-export-mode.md +512 -0
  31. data/docs/deployment.md +10 -1
  32. data/docs/optional-peer-dependencies.md +198 -0
  33. data/docs/peer-dependencies.md +60 -0
  34. data/docs/rspack.md +190 -0
  35. data/docs/rspack_migration_guide.md +202 -0
  36. data/docs/transpiler-migration.md +188 -0
  37. data/docs/transpiler-performance.md +179 -0
  38. data/docs/troubleshooting.md +5 -0
  39. data/docs/typescript-migration.md +378 -0
  40. data/docs/typescript.md +99 -0
  41. data/docs/using_esbuild_loader.md +3 -3
  42. data/docs/using_swc_loader.md +5 -3
  43. data/docs/v6_upgrade.md +10 -0
  44. data/docs/v9_upgrade.md +413 -0
  45. data/lib/install/bin/shakapacker +3 -5
  46. data/lib/install/config/rspack/rspack.config.js +6 -0
  47. data/lib/install/config/rspack/rspack.config.ts +7 -0
  48. data/lib/install/config/shakapacker.yml +12 -2
  49. data/lib/install/config/webpack/webpack.config.ts +7 -0
  50. data/lib/install/package.json +38 -0
  51. data/lib/install/template.rb +194 -44
  52. data/lib/shakapacker/configuration.rb +141 -0
  53. data/lib/shakapacker/dev_server_runner.rb +25 -5
  54. data/lib/shakapacker/doctor.rb +844 -0
  55. data/lib/shakapacker/manifest.rb +4 -2
  56. data/lib/shakapacker/rspack_runner.rb +19 -0
  57. data/lib/shakapacker/runner.rb +144 -4
  58. data/lib/shakapacker/swc_migrator.rb +376 -0
  59. data/lib/shakapacker/utils/manager.rb +2 -0
  60. data/lib/shakapacker/version.rb +1 -1
  61. data/lib/shakapacker/version_checker.rb +1 -1
  62. data/lib/shakapacker/webpack_runner.rb +4 -42
  63. data/lib/shakapacker.rb +2 -1
  64. data/lib/tasks/shakapacker/doctor.rake +8 -0
  65. data/lib/tasks/shakapacker/install.rake +12 -2
  66. data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
  67. data/lib/tasks/shakapacker.rake +1 -0
  68. data/package/.npmignore +4 -0
  69. data/package/babel/preset.ts +56 -0
  70. data/package/config.ts +175 -0
  71. data/package/{dev_server.js → dev_server.ts} +8 -5
  72. data/package/env.ts +92 -0
  73. data/package/environments/base.ts +138 -0
  74. data/package/environments/development.ts +90 -0
  75. data/package/environments/production.ts +80 -0
  76. data/package/environments/test.ts +53 -0
  77. data/package/environments/types.ts +90 -0
  78. data/package/esbuild/index.ts +42 -0
  79. data/package/index.d.ts +3 -97
  80. data/package/index.ts +52 -0
  81. data/package/loaders.d.ts +28 -0
  82. data/package/optimization/rspack.ts +36 -0
  83. data/package/optimization/webpack.ts +57 -0
  84. data/package/plugins/rspack.ts +103 -0
  85. data/package/plugins/webpack.ts +62 -0
  86. data/package/rspack/index.ts +64 -0
  87. data/package/rules/{babel.js → babel.ts} +2 -2
  88. data/package/rules/{coffee.js → coffee.ts} +1 -1
  89. data/package/rules/css.ts +3 -0
  90. data/package/rules/{erb.js → erb.ts} +1 -1
  91. data/package/rules/esbuild.ts +10 -0
  92. data/package/rules/file.ts +40 -0
  93. data/package/rules/{jscommon.js → jscommon.ts} +4 -4
  94. data/package/rules/{less.js → less.ts} +4 -4
  95. data/package/rules/raw.ts +25 -0
  96. data/package/rules/rspack.ts +176 -0
  97. data/package/rules/{sass.js → sass.ts} +7 -3
  98. data/package/rules/{stylus.js → stylus.ts} +4 -8
  99. data/package/rules/swc.ts +10 -0
  100. data/package/rules/{index.js → webpack.ts} +1 -1
  101. data/package/swc/index.ts +54 -0
  102. data/package/types/README.md +87 -0
  103. data/package/types/index.ts +60 -0
  104. data/package/types.ts +108 -0
  105. data/package/utils/configPath.ts +6 -0
  106. data/package/utils/debug.ts +49 -0
  107. data/package/utils/defaultConfigPath.ts +4 -0
  108. data/package/utils/errorCodes.ts +219 -0
  109. data/package/utils/errorHelpers.ts +143 -0
  110. data/package/utils/getStyleRule.ts +64 -0
  111. data/package/utils/helpers.ts +85 -0
  112. data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
  113. data/package/utils/pathValidation.ts +139 -0
  114. data/package/utils/requireOrError.ts +15 -0
  115. data/package/utils/snakeToCamelCase.ts +5 -0
  116. data/package/utils/typeGuards.ts +342 -0
  117. data/package/utils/validateDependencies.ts +61 -0
  118. data/package/webpack-types.d.ts +33 -0
  119. data/package/webpackDevServerConfig.ts +117 -0
  120. data/package.json +134 -9
  121. data/scripts/remove-use-strict.js +45 -0
  122. data/scripts/type-check-no-emit.js +27 -0
  123. data/test/package/config.test.js +3 -0
  124. data/test/package/env.test.js +42 -7
  125. data/test/package/environments/base.test.js +5 -1
  126. data/test/package/rules/babel.test.js +16 -0
  127. data/test/package/rules/esbuild.test.js +1 -1
  128. data/test/package/rules/raw.test.js +40 -7
  129. data/test/package/rules/swc.test.js +1 -1
  130. data/test/package/rules/webpack.test.js +35 -0
  131. data/test/package/staging.test.js +4 -3
  132. data/test/package/transpiler-defaults.test.js +127 -0
  133. data/test/peer-dependencies.sh +85 -0
  134. data/test/scripts/remove-use-strict.test.js +125 -0
  135. data/test/typescript/build.test.js +118 -0
  136. data/test/typescript/environments.test.js +107 -0
  137. data/test/typescript/pathValidation.test.js +142 -0
  138. data/test/typescript/securityValidation.test.js +182 -0
  139. data/tools/README.md +124 -0
  140. data/tools/css-modules-v9-codemod.js +179 -0
  141. data/tsconfig.eslint.json +16 -0
  142. data/tsconfig.json +38 -0
  143. data/yarn.lock +2704 -767
  144. metadata +111 -41
  145. data/package/babel/preset.js +0 -48
  146. data/package/config.js +0 -56
  147. data/package/env.js +0 -48
  148. data/package/environments/base.js +0 -171
  149. data/package/environments/development.js +0 -13
  150. data/package/environments/production.js +0 -88
  151. data/package/environments/test.js +0 -3
  152. data/package/esbuild/index.js +0 -40
  153. data/package/index.js +0 -40
  154. data/package/rules/css.js +0 -3
  155. data/package/rules/esbuild.js +0 -10
  156. data/package/rules/file.js +0 -29
  157. data/package/rules/raw.js +0 -5
  158. data/package/rules/swc.js +0 -10
  159. data/package/swc/index.js +0 -50
  160. data/package/utils/configPath.js +0 -4
  161. data/package/utils/defaultConfigPath.js +0 -2
  162. data/package/utils/getStyleRule.js +0 -40
  163. data/package/utils/helpers.js +0 -62
  164. data/package/utils/snakeToCamelCase.js +0 -5
  165. data/package/webpackDevServerConfig.js +0 -71
  166. data/test/package/rules/index.test.js +0 -16
@@ -0,0 +1,62 @@
1
+ const { requireOrError } = require("../utils/requireOrError")
2
+ // TODO: Change to `const { WebpackAssetsManifest }` when dropping 'webpack-assets-manifest < 6.0.0' (Node >=20.10.0) support
3
+ const WebpackAssetsManifest = requireOrError("webpack-assets-manifest")
4
+ const webpack = requireOrError("webpack")
5
+ const config = require("../config")
6
+ const { isProduction } = require("../env")
7
+ const { moduleExists } = require("../utils/helpers")
8
+
9
+ const getPlugins = (): unknown[] => {
10
+ // TODO: Remove WebpackAssetsManifestConstructor workaround when dropping 'webpack-assets-manifest < 6.0.0' (Node >=20.10.0) support
11
+ const WebpackAssetsManifestConstructor =
12
+ "WebpackAssetsManifest" in WebpackAssetsManifest
13
+ ? WebpackAssetsManifest.WebpackAssetsManifest
14
+ : WebpackAssetsManifest
15
+ const plugins = [
16
+ new webpack.EnvironmentPlugin(process.env),
17
+ new WebpackAssetsManifestConstructor({
18
+ entrypoints: true,
19
+ writeToDisk: true,
20
+ output: config.manifestPath,
21
+ entrypointsUseAssets: true,
22
+ publicPath: config.publicPathWithoutCDN,
23
+ integrity: config.integrity.enabled,
24
+ integrityHashes: config.integrity.hash_functions
25
+ })
26
+ ]
27
+
28
+ if (moduleExists("css-loader") && moduleExists("mini-css-extract-plugin")) {
29
+ const hash = isProduction || config.useContentHash ? "-[contenthash:8]" : ""
30
+ const MiniCssExtractPlugin = requireOrError("mini-css-extract-plugin")
31
+ plugins.push(
32
+ new MiniCssExtractPlugin({
33
+ filename: `css/[name]${hash}.css`,
34
+ chunkFilename: `css/[id]${hash}.css`,
35
+ // For projects where css ordering has been mitigated through consistent use of scoping or naming conventions,
36
+ // the css order warnings can be disabled by setting the ignoreOrder flag.
37
+ ignoreOrder: config.css_extract_ignore_order_warnings
38
+ })
39
+ )
40
+ }
41
+
42
+ if (
43
+ config.integrity.enabled &&
44
+ moduleExists("webpack-subresource-integrity")
45
+ ) {
46
+ const SubresourceIntegrityPlugin = requireOrError(
47
+ "webpack-subresource-integrity"
48
+ )
49
+ plugins.push(
50
+ new SubresourceIntegrityPlugin({
51
+ hashFuncNames: config.integrity.hash_functions,
52
+ enabled: isProduction
53
+ })
54
+ )
55
+ }
56
+
57
+ return plugins
58
+ }
59
+
60
+ export = {
61
+ getPlugins
62
+ }
@@ -0,0 +1,64 @@
1
+ /* eslint global-require: 0 */
2
+ /* eslint import/no-dynamic-require: 0 */
3
+
4
+ // Mixed require/import syntax:
5
+ // - Using require() for compiled JS modules that may not have proper ES module exports
6
+ // - Using import for type-only imports and Node.js built-in modules
7
+ const webpackMerge = require("webpack-merge")
8
+ import { resolve } from "path"
9
+ import { existsSync } from "fs"
10
+ import type { RspackConfigWithDevServer } from "../environments/types"
11
+ const config = require("../config")
12
+ const baseConfig = require("../environments/base")
13
+ const devServer = require("../dev_server")
14
+ const env = require("../env")
15
+ const { moduleExists, canProcess } = require("../utils/helpers")
16
+ const inliningCss = require("../utils/inliningCss")
17
+ const { getPlugins } = require("../plugins/rspack")
18
+ const { getOptimization } = require("../optimization/rspack")
19
+ const { validateRspackDependencies } = require("../utils/validateDependencies")
20
+
21
+ const rulesPath = resolve(__dirname, "../rules", "rspack.js")
22
+ const rules = require(rulesPath)
23
+
24
+ const generateRspackConfig = (extraConfig: RspackConfigWithDevServer = {}, ...extraArgs: unknown[]): RspackConfigWithDevServer => {
25
+ // Validate required dependencies first
26
+ validateRspackDependencies()
27
+ if (extraArgs.length > 0) {
28
+ throw new Error(
29
+ "Only one extra config may be passed here - use webpack-merge to merge configs before passing them to Shakapacker"
30
+ )
31
+ }
32
+
33
+ const { nodeEnv } = env
34
+ const path = resolve(__dirname, "../environments", `${nodeEnv}.js`)
35
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
36
+ const environmentConfig = existsSync(path) ? require(path) : baseConfig
37
+
38
+ // Create base rspack config
39
+ const rspackConfig: RspackConfigWithDevServer = {
40
+ ...environmentConfig,
41
+ module: {
42
+ rules
43
+ },
44
+ plugins: getPlugins(),
45
+ optimization: getOptimization()
46
+ }
47
+
48
+ return webpackMerge.merge({}, rspackConfig, extraConfig)
49
+ }
50
+
51
+ // Re-export webpack-merge utilities for backward compatibility
52
+ export { merge, mergeWithCustomize, mergeWithRules, unique } from "webpack-merge"
53
+
54
+ export {
55
+ config, // shakapacker.yml
56
+ devServer,
57
+ generateRspackConfig,
58
+ baseConfig,
59
+ env,
60
+ rules,
61
+ moduleExists,
62
+ canProcess,
63
+ inliningCss
64
+ }
@@ -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
+ export = loaderMatches(javascriptTranspiler, "babel", () => ({
7
7
  test: /\.(js|jsx|mjs|ts|tsx|coffee)?(\.erb)?$/,
8
8
  ...jscommon,
9
9
  use: [
@@ -1,6 +1,6 @@
1
1
  const { canProcess } = require("../utils/helpers")
2
2
 
3
- module.exports = canProcess("coffee-loader", (resolvedPath) => ({
3
+ export = canProcess("coffee-loader", (resolvedPath: string) => ({
4
4
  test: /\.coffee(\.erb)?$/,
5
5
  use: [{ loader: resolvedPath }]
6
6
  }))
@@ -0,0 +1,3 @@
1
+ const { getStyleRule } = require("../utils/getStyleRule")
2
+
3
+ export = getStyleRule(/\.(css)$/i)
@@ -2,7 +2,7 @@ const { canProcess } = require("../utils/helpers")
2
2
 
3
3
  const runner = /^win/.test(process.platform) ? "ruby " : ""
4
4
 
5
- module.exports = canProcess("rails-erb-loader", (resolvedPath) => ({
5
+ export = canProcess("rails-erb-loader", (resolvedPath: string) => ({
6
6
  test: /\.erb$/,
7
7
  enforce: "pre",
8
8
  exclude: /node_modules/,
@@ -0,0 +1,10 @@
1
+ const { loaderMatches } = require("../utils/helpers")
2
+ const { getEsbuildLoaderConfig } = require("../esbuild")
3
+ const { javascript_transpiler: javascriptTranspiler } = require("../config")
4
+ const jscommon = require("./jscommon")
5
+
6
+ export = loaderMatches(javascriptTranspiler, "esbuild", () => ({
7
+ test: /\.(ts|tsx|js|jsx|mjs|coffee)?(\.erb)?$/,
8
+ ...jscommon,
9
+ use: ({ resource }: { resource: string }) => getEsbuildLoaderConfig(resource)
10
+ }))
@@ -0,0 +1,40 @@
1
+ import { dirname, sep, normalize } from "path"
2
+ const {
3
+ additional_paths: additionalPaths,
4
+ source_path: sourcePath
5
+ } = require("../config")
6
+
7
+ export = {
8
+ test: /\.(bmp|gif|jpe?g|png|tiff|ico|avif|webp|eot|otf|ttf|woff|woff2|svg)$/,
9
+ exclude: /\.(js|mjs|jsx|ts|tsx)$/,
10
+ type: "asset/resource",
11
+ generator: {
12
+ filename: (pathData: { filename?: string }) => {
13
+ // Guard against null/undefined pathData or filename
14
+ if (!pathData || !pathData.filename) {
15
+ return `static/[name]-[hash][ext][query]`
16
+ }
17
+ const path = normalize(dirname(pathData.filename))
18
+ const stripPaths = [...additionalPaths, sourcePath].map((p: string) =>
19
+ normalize(p)
20
+ )
21
+
22
+ const selectedStripPath = stripPaths.find((includePath: string) =>
23
+ path.startsWith(includePath)
24
+ )
25
+
26
+ if (!selectedStripPath) {
27
+ return `static/[name]-[hash][ext][query]`
28
+ }
29
+
30
+ // Split on both forward and backward slashes for cross-platform compatibility
31
+ const folders = path
32
+ .replace(selectedStripPath, "")
33
+ .split(/[\\/]/)
34
+ .filter(Boolean)
35
+
36
+ const foldersWithStatic = ["static", ...folders].join("/")
37
+ return `${foldersWithStatic}/[name]-[hash][ext][query]`
38
+ }
39
+ }
40
+ }
@@ -1,11 +1,11 @@
1
- const { resolve } = require("path")
2
- const { realpathSync } = require("fs")
1
+ import { resolve } from "path"
2
+ import { realpathSync } from "fs"
3
3
  const {
4
4
  source_path: sourcePath,
5
5
  additional_paths: additionalPaths
6
6
  } = require("../config")
7
7
 
8
- const inclusions = [sourcePath, ...additionalPaths].map((p) => {
8
+ const inclusions = [sourcePath, ...additionalPaths].map((p: string) => {
9
9
  try {
10
10
  return realpathSync(p)
11
11
  } catch (e) {
@@ -13,7 +13,7 @@ const inclusions = [sourcePath, ...additionalPaths].map((p) => {
13
13
  }
14
14
  })
15
15
 
16
- module.exports = {
16
+ export = {
17
17
  include: inclusions,
18
18
  exclude: [
19
19
  {
@@ -1,19 +1,19 @@
1
- const path = require("path")
2
1
  const { canProcess } = require("../utils/helpers")
3
- const getStyleRule = require("../utils/getStyleRule")
2
+ const { getStyleRule } = require("../utils/getStyleRule")
4
3
 
5
4
  const {
6
5
  additional_paths: paths,
7
6
  source_path: sourcePath
8
7
  } = require("../config")
9
8
 
10
- module.exports = canProcess("less-loader", (resolvedPath) =>
9
+ export = canProcess("less-loader", (resolvedPath: string) =>
11
10
  getStyleRule(/\.(less)(\.erb)?$/i, [
12
11
  {
13
12
  loader: resolvedPath,
14
13
  options: {
15
14
  lessOptions: {
16
- paths: [path.resolve(__dirname, "node_modules"), sourcePath, ...paths]
15
+ // Additional paths for Less imports (node_modules is resolved automatically)
16
+ paths: [sourcePath, ...paths]
17
17
  },
18
18
  sourceMap: true
19
19
  }
@@ -0,0 +1,25 @@
1
+ const config = require("../config")
2
+
3
+ const rspackRawConfig = () => ({
4
+ resourceQuery: /raw/,
5
+ type: "asset/source"
6
+ })
7
+
8
+ const webpackRawConfig = () => ({
9
+ oneOf: [
10
+ {
11
+ // Match any file with ?raw query parameter
12
+ resourceQuery: /raw/,
13
+ type: "asset/source"
14
+ },
15
+ {
16
+ // Fallback: match .html files without query
17
+ test: /\.html$/,
18
+ exclude: /\.(js|mjs|jsx|ts|tsx)$/,
19
+ type: "asset/source"
20
+ }
21
+ ]
22
+ })
23
+
24
+ export =
25
+ config.assets_bundler === "rspack" ? rspackRawConfig() : webpackRawConfig()
@@ -0,0 +1,176 @@
1
+ /* eslint global-require: 0 */
2
+
3
+ const { moduleExists } = require("../utils/helpers")
4
+ const { debug, info, warn } = require("../utils/debug")
5
+
6
+ debug("Loading Rspack rules configuration...")
7
+
8
+ const rules = []
9
+
10
+ // Use Rspack's built-in SWC loader for JavaScript files
11
+ debug("Adding JavaScript rule with builtin:swc-loader")
12
+ rules.push({
13
+ test: /\.(js|jsx|mjs)$/,
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.
17
+ type: "javascript/auto",
18
+ use: [
19
+ {
20
+ loader: "builtin:swc-loader",
21
+ options: {
22
+ jsc: {
23
+ parser: {
24
+ syntax: "ecmascript",
25
+ jsx: true
26
+ },
27
+ transform: {
28
+ react: {
29
+ runtime: "automatic"
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ ]
36
+ })
37
+
38
+ // Use Rspack's built-in SWC loader for TypeScript files
39
+ debug("Adding TypeScript rule with builtin:swc-loader")
40
+ rules.push({
41
+ test: /\.(ts|tsx)$/,
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.
45
+ type: "javascript/auto",
46
+ use: [
47
+ {
48
+ loader: "builtin:swc-loader",
49
+ options: {
50
+ jsc: {
51
+ parser: {
52
+ syntax: "typescript",
53
+ tsx: true
54
+ },
55
+ transform: {
56
+ react: {
57
+ runtime: "automatic"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ ]
64
+ })
65
+
66
+ // CSS rules using Rspack's built-in CSS handling
67
+ debug("Checking for CSS loader...")
68
+ if (moduleExists("css-loader")) {
69
+ debug("css-loader found, loading CSS rule configuration...")
70
+ const css = require("./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")
79
+ }
80
+
81
+ // Sass rules
82
+ debug("Checking for Sass loader...")
83
+ if (moduleExists("sass") && moduleExists("sass-loader")) {
84
+ debug("sass and sass-loader found, loading Sass rule configuration...")
85
+ const sass = require("./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")
96
+ }
97
+
98
+ // Less rules
99
+ debug("Checking for Less loader...")
100
+ if (moduleExists("less") && moduleExists("less-loader")) {
101
+ debug("less and less-loader found, loading Less rule configuration...")
102
+ const less = require("./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")
113
+ }
114
+
115
+ // Stylus rules
116
+ debug("Checking for Stylus loader...")
117
+ if (moduleExists("stylus") && moduleExists("stylus-loader")) {
118
+ debug("stylus and stylus-loader found, loading Stylus rule configuration...")
119
+ const stylus = require("./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")
130
+ }
131
+
132
+ // ERB template support
133
+ debug("Checking for ERB template support...")
134
+ const erb = require("./erb")
135
+
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
+ }
142
+
143
+ // File/asset handling using Rspack's built-in asset modules
144
+ // This is a critical rule required for proper asset handling
145
+ debug("Adding file/asset handling rule...")
146
+ const file = require("./file")
147
+
148
+ if (!file) {
149
+ throw new Error(
150
+ "CRITICAL: file rule configuration returned null. " +
151
+ "Asset handling is required for proper bundling. " +
152
+ "Please ensure the file rule module exports a valid rule configuration."
153
+ )
154
+ }
155
+
156
+ debug("Successfully added file/asset rule")
157
+ rules.push(file)
158
+
159
+ // Raw file loading
160
+ // This is a critical rule required for raw file imports
161
+ debug("Adding raw file loading rule...")
162
+ const raw = require("./raw")
163
+
164
+ if (!raw) {
165
+ throw new Error(
166
+ "CRITICAL: raw rule configuration returned null. " +
167
+ "Raw file loading is required for proper bundling. " +
168
+ "Please ensure the raw rule module exports a valid rule configuration."
169
+ )
170
+ }
171
+
172
+ debug("Successfully added raw file rule")
173
+ rules.push(raw)
174
+
175
+ debug(`Rspack rules configuration complete. Total rules: ${rules.length}`)
176
+ export = rules
@@ -1,17 +1,21 @@
1
1
  /* eslint global-require: 0 */
2
2
 
3
- const getStyleRule = require("../utils/getStyleRule")
3
+ const { getStyleRule } = require("../utils/getStyleRule")
4
4
  const { canProcess, packageMajorVersion } = require("../utils/helpers")
5
5
  const { additional_paths: extraPaths } = require("../config")
6
6
 
7
- module.exports = canProcess("sass-loader", (resolvedPath) => {
7
+ export = canProcess("sass-loader", (resolvedPath: string) => {
8
8
  const optionKey =
9
9
  packageMajorVersion("sass-loader") > 15 ? "loadPaths" : "includePaths"
10
10
  return getStyleRule(/\.(scss|sass)(\.erb)?$/i, [
11
11
  {
12
12
  loader: resolvedPath,
13
13
  options: {
14
- sassOptions: { [optionKey]: extraPaths }
14
+ sourceMap: true,
15
+ sassOptions: {
16
+ [optionKey]: extraPaths,
17
+ quietDeps: true
18
+ }
15
19
  }
16
20
  }
17
21
  ])
@@ -1,23 +1,19 @@
1
- const path = require("path")
2
1
  const { canProcess } = require("../utils/helpers")
3
- const getStyleRule = require("../utils/getStyleRule")
2
+ const { getStyleRule } = require("../utils/getStyleRule")
4
3
 
5
4
  const {
6
5
  additional_paths: paths,
7
6
  source_path: sourcePath
8
7
  } = require("../config")
9
8
 
10
- module.exports = canProcess("stylus-loader", (resolvedPath) =>
9
+ export = canProcess("stylus-loader", (resolvedPath: string) =>
11
10
  getStyleRule(/\.(styl(us)?)(\.erb)?$/i, [
12
11
  {
13
12
  loader: resolvedPath,
14
13
  options: {
15
14
  stylusOptions: {
16
- include: [
17
- path.resolve(__dirname, "node_modules"),
18
- sourcePath,
19
- ...paths
20
- ]
15
+ // Additional paths for Stylus imports (node_modules is resolved automatically)
16
+ include: [sourcePath, ...paths]
21
17
  },
22
18
  sourceMap: true
23
19
  }
@@ -0,0 +1,10 @@
1
+ const { loaderMatches } = require("../utils/helpers")
2
+ const { getSwcLoaderConfig } = require("../swc")
3
+ const { javascript_transpiler: javascriptTranspiler } = require("../config")
4
+ const jscommon = require("./jscommon")
5
+
6
+ export = loaderMatches(javascriptTranspiler, "swc", () => ({
7
+ test: /\.(ts|tsx|js|jsx|mjs|coffee)?(\.erb)?$/,
8
+ ...jscommon,
9
+ use: ({ resource }: { resource: string }) => getSwcLoaderConfig(resource)
10
+ }))
@@ -1,7 +1,7 @@
1
1
  /* eslint global-require: 0 */
2
2
  /* eslint import/no-dynamic-require: 0 */
3
3
 
4
- module.exports = [
4
+ export = [
5
5
  require("./raw"),
6
6
  require("./file"),
7
7
  require("./css"),
@@ -0,0 +1,54 @@
1
+ /* eslint global-require: 0 */
2
+ /* eslint import/no-dynamic-require: 0 */
3
+
4
+ import { resolve } from "path"
5
+ import { existsSync } from "fs"
6
+ import { merge } from "webpack-merge"
7
+ import type { RuleSetRule } from "webpack"
8
+
9
+ const JSX_FILE_REGEX = /\.(jsx|tsx)(\.erb)?$/
10
+ const TYPESCRIPT_FILE_REGEX = /\.(ts|tsx)(\.erb)?$/
11
+
12
+ const isJsxFile = (filename: string): boolean => !!filename.match(JSX_FILE_REGEX)
13
+
14
+ const isTypescriptFile = (filename: string): boolean => !!filename.match(TYPESCRIPT_FILE_REGEX)
15
+
16
+ const getCustomConfig = (): Partial<RuleSetRule> => {
17
+ const path = resolve("config", "swc.config.js")
18
+ if (existsSync(path)) {
19
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
20
+ return require(path)
21
+ }
22
+ return {}
23
+ }
24
+
25
+ const getSwcLoaderConfig = (filenameToProcess: string): RuleSetRule => {
26
+ const customConfig = getCustomConfig()
27
+ const isTs = isTypescriptFile(filenameToProcess)
28
+ const isJsx = isJsxFile(filenameToProcess)
29
+ const jsxKey = isTs ? "tsx" : "jsx"
30
+
31
+ const defaultConfig: RuleSetRule = {
32
+ loader: require.resolve("swc-loader"),
33
+ options: {
34
+ jsc: {
35
+ parser: {
36
+ dynamicImport: true,
37
+ syntax: isTs ? "typescript" : "ecmascript",
38
+ [jsxKey]: isJsx
39
+ },
40
+ loose: true
41
+ },
42
+ sourceMaps: true,
43
+ env: {
44
+ coreJs: 3,
45
+ exclude: ["transform-typeof-symbol"],
46
+ mode: "entry"
47
+ }
48
+ }
49
+ }
50
+
51
+ return merge(defaultConfig, customConfig)
52
+ }
53
+
54
+ export { getSwcLoaderConfig }