shakapacker 9.1.0 → 9.3.0.beta.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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +6 -9
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +6 -8
  4. data/.github/workflows/claude-code-review.yml +4 -5
  5. data/.github/workflows/claude.yml +1 -2
  6. data/.github/workflows/dummy.yml +4 -4
  7. data/.github/workflows/generator.yml +9 -9
  8. data/.github/workflows/node.yml +11 -2
  9. data/.github/workflows/ruby.yml +16 -16
  10. data/.github/workflows/test-bundlers.yml +9 -9
  11. data/.gitignore +7 -0
  12. data/CHANGELOG.md +50 -4
  13. data/CLAUDE.md +6 -1
  14. data/CONTRIBUTING.md +0 -1
  15. data/Gemfile.lock +1 -1
  16. data/README.md +35 -14
  17. data/TODO.md +10 -2
  18. data/TODO_v9.md +13 -3
  19. data/bin/export-bundler-config +11 -0
  20. data/conductor-setup.sh +1 -1
  21. data/conductor.json +1 -1
  22. data/docs/cdn_setup.md +13 -8
  23. data/docs/common-upgrades.md +2 -1
  24. data/docs/configuration.md +630 -0
  25. data/docs/css-modules-export-mode.md +120 -100
  26. data/docs/customizing_babel_config.md +16 -16
  27. data/docs/deployment.md +68 -6
  28. data/docs/developing_shakapacker.md +6 -0
  29. data/docs/optional-peer-dependencies.md +9 -4
  30. data/docs/peer-dependencies.md +17 -6
  31. data/docs/precompile_hook.md +342 -0
  32. data/docs/react.md +57 -47
  33. data/docs/releasing.md +195 -0
  34. data/docs/rspack.md +25 -21
  35. data/docs/rspack_migration_guide.md +363 -8
  36. data/docs/sprockets.md +1 -0
  37. data/docs/style_loader_vs_mini_css.md +12 -12
  38. data/docs/subresource_integrity.md +13 -7
  39. data/docs/transpiler-performance.md +40 -19
  40. data/docs/troubleshooting.md +122 -23
  41. data/docs/typescript-migration.md +48 -39
  42. data/docs/typescript.md +12 -8
  43. data/docs/using_esbuild_loader.md +10 -10
  44. data/docs/v6_upgrade.md +33 -20
  45. data/docs/v7_upgrade.md +8 -6
  46. data/docs/v8_upgrade.md +13 -12
  47. data/docs/v9_upgrade.md +2 -1
  48. data/eslint.config.fast.js +134 -0
  49. data/eslint.config.js +140 -0
  50. data/knip.ts +54 -0
  51. data/lib/install/bin/export-bundler-config +11 -0
  52. data/lib/install/bin/shakapacker +1 -1
  53. data/lib/install/bin/shakapacker-dev-server +1 -1
  54. data/lib/install/config/shakapacker.yml +16 -5
  55. data/lib/shakapacker/bundler_switcher.rb +7 -0
  56. data/lib/shakapacker/compiler.rb +80 -0
  57. data/lib/shakapacker/configuration.rb +56 -2
  58. data/lib/shakapacker/dev_server_runner.rb +140 -1
  59. data/lib/shakapacker/doctor.rb +302 -57
  60. data/lib/shakapacker/instance.rb +8 -3
  61. data/lib/shakapacker/rspack_runner.rb +1 -1
  62. data/lib/shakapacker/runner.rb +245 -9
  63. data/lib/shakapacker/version.rb +1 -1
  64. data/lib/shakapacker/webpack_runner.rb +1 -1
  65. data/lib/shakapacker.rb +10 -0
  66. data/lib/tasks/shakapacker/doctor.rake +42 -2
  67. data/lib/tasks/shakapacker/export_bundler_config.rake +72 -0
  68. data/package/babel/preset.ts +7 -4
  69. data/package/config.ts +42 -30
  70. data/package/configExporter/cli.ts +1274 -0
  71. data/package/configExporter/configDocs.ts +102 -0
  72. data/package/configExporter/configFile.ts +520 -0
  73. data/package/configExporter/fileWriter.ts +96 -0
  74. data/package/configExporter/index.ts +13 -0
  75. data/package/configExporter/types.ts +70 -0
  76. data/package/configExporter/yamlSerializer.ts +280 -0
  77. data/package/dev_server.ts +1 -1
  78. data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +11 -5
  79. data/package/environments/base.ts +18 -13
  80. data/package/environments/development.ts +1 -1
  81. data/package/environments/production.ts +4 -1
  82. data/package/index.d.ts +50 -3
  83. data/package/index.d.ts.template +50 -0
  84. data/package/index.ts +7 -7
  85. data/package/loaders.d.ts +2 -2
  86. data/package/optimization/rspack.ts +1 -1
  87. data/package/plugins/rspack.ts +15 -4
  88. data/package/plugins/webpack.ts +7 -3
  89. data/package/rspack/index.ts +10 -2
  90. data/package/rules/raw.ts +3 -2
  91. data/package/rules/sass.ts +1 -1
  92. data/package/types/README.md +15 -13
  93. data/package/types/index.ts +5 -5
  94. data/package/types.ts +0 -1
  95. data/package/utils/defaultConfigPath.ts +4 -1
  96. data/package/utils/errorCodes.ts +129 -100
  97. data/package/utils/errorHelpers.ts +34 -29
  98. data/package/utils/getStyleRule.ts +5 -2
  99. data/package/utils/helpers.ts +21 -11
  100. data/package/utils/pathValidation.ts +43 -35
  101. data/package/utils/requireOrError.ts +1 -1
  102. data/package/utils/snakeToCamelCase.ts +1 -1
  103. data/package/utils/typeGuards.ts +132 -83
  104. data/package/utils/validateDependencies.ts +1 -1
  105. data/package/webpack-types.d.ts +3 -3
  106. data/package/webpackDevServerConfig.ts +22 -10
  107. data/package-lock.json +2 -2
  108. data/package.json +37 -28
  109. data/scripts/type-check-no-emit.js +1 -1
  110. data/test/configExporter/configFile.test.js +392 -0
  111. data/test/configExporter/integration.test.js +275 -0
  112. data/test/helpers.js +1 -1
  113. data/test/package/configExporter.test.js +154 -0
  114. data/test/package/helpers.test.js +2 -2
  115. data/test/package/rules/sass-version-parsing.test.js +71 -0
  116. data/test/package/rules/sass.test.js +2 -4
  117. data/test/package/rules/sass1.test.js +1 -3
  118. data/test/package/rules/sass16.test.js +23 -0
  119. data/tools/README.md +15 -5
  120. data/tsconfig.eslint.json +2 -9
  121. data/yarn.lock +1635 -1442
  122. metadata +29 -3
  123. data/.eslintignore +0 -5
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Manual type definitions for Shakapacker package exports.
3
+ *
4
+ * This file is manually maintained because TypeScript cannot infer types
5
+ * from the `export =` syntax with dynamic require() calls in index.ts.
6
+ *
7
+ * When adding/modifying exports in index.ts, update this file accordingly.
8
+ */
9
+
10
+ import type { Configuration, RuleSetRule } from "webpack"
11
+ import type { Config, DevServerConfig, Env } from "./types"
12
+
13
+ /**
14
+ * The shape of the Shakapacker module exports.
15
+ * This interface represents the object exported via CommonJS `export =`.
16
+ */
17
+ interface ShakapackerExports {
18
+ /** Shakapacker configuration from shakapacker.yml */
19
+ config: Config
20
+ /** Development server configuration */
21
+ devServer: DevServerConfig
22
+ /** Base webpack/rspack configuration */
23
+ baseConfig: Configuration
24
+ /** Environment configuration (railsEnv, nodeEnv, etc.) */
25
+ env: Env
26
+ /** Array of webpack/rspack loader rules */
27
+ rules: RuleSetRule[]
28
+ /** Check if a module exists in node_modules */
29
+ moduleExists: (packageName: string) => boolean
30
+ /** Process a file if a specific loader is available */
31
+ canProcess: <T = unknown>(
32
+ rule: string,
33
+ fn: (modulePath: string) => T
34
+ ) => T | null
35
+ /** Whether CSS should be inlined (dev server with HMR) */
36
+ inliningCss: boolean
37
+ /** Generate webpack configuration with optional custom config */
38
+ generateWebpackConfig: (extraConfig?: Configuration) => Configuration
39
+ /** webpack-merge's merge function */
40
+ merge: typeof import("webpack-merge").merge
41
+ /** webpack-merge's mergeWithCustomize function */
42
+ mergeWithCustomize: typeof import("webpack-merge").mergeWithCustomize
43
+ /** webpack-merge's mergeWithRules function */
44
+ mergeWithRules: typeof import("webpack-merge").mergeWithRules
45
+ /** webpack-merge's unique function */
46
+ unique: typeof import("webpack-merge").unique
47
+ }
48
+
49
+ declare const shakapacker: ShakapackerExports
50
+ export = shakapacker
data/package/index.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  /* eslint global-require: 0 */
2
2
  /* eslint import/no-dynamic-require: 0 */
3
3
 
4
- const webpackMerge = require("webpack-merge")
4
+ import * as webpackMerge from "webpack-merge"
5
5
  import { resolve } from "path"
6
6
  import { existsSync } from "fs"
7
7
  // @ts-ignore: webpack is an optional peer dependency (using type-only import)
8
8
  import type { Configuration } from "webpack"
9
- const config = require("./config")
10
- const baseConfig = require("./environments/base")
11
- const devServer = require("./dev_server")
12
- const env = require("./env")
13
- const { moduleExists, canProcess } = require("./utils/helpers")
14
- const inliningCss = require("./utils/inliningCss")
9
+ import config from "./config"
10
+ import baseConfig from "./environments/base"
11
+ import devServer from "./dev_server"
12
+ import env from "./env"
13
+ import { moduleExists, canProcess } from "./utils/helpers"
14
+ import inliningCss from "./utils/inliningCss"
15
15
 
16
16
  const rulesPath = resolve(__dirname, "rules", `${config.assets_bundler}.js`)
17
17
  const rules = require(rulesPath)
data/package/loaders.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // @ts-ignore: webpack is an optional peer dependency (using type-only import)
2
- import type { LoaderDefinitionFunction } from 'webpack'
2
+ import type { LoaderDefinitionFunction } from "webpack"
3
3
 
4
4
  export interface ShakapackerLoaderOptions {
5
5
  [key: string]: any
@@ -25,4 +25,4 @@ export interface LoaderConfig {
25
25
  }
26
26
 
27
27
  export function resolveLoader(name: string): string
28
- export function createLoader(config: LoaderConfig): LoaderConfig
28
+ export function createLoader(config: LoaderConfig): LoaderConfig
@@ -18,7 +18,7 @@ const getOptimization = (): OptimizationConfig => {
18
18
  ]
19
19
  } catch (error: unknown) {
20
20
  const errorMessage = error instanceof Error ? error.message : String(error)
21
- const errorStack = error instanceof Error ? error.stack : ''
21
+ const errorStack = error instanceof Error ? error.stack : ""
22
22
  // Log full error with stack trace
23
23
  logError(
24
24
  `Failed to configure Rspack minimizers: ${errorMessage}\n${errorStack}`
@@ -18,7 +18,11 @@ interface EntrypointAssets {
18
18
 
19
19
  interface Manifest {
20
20
  entrypoints?: Record<string, { assets: EntrypointAssets }>
21
- [key: string]: string | { assets: EntrypointAssets } | Record<string, { assets: EntrypointAssets }> | undefined
21
+ [key: string]:
22
+ | string
23
+ | { assets: EntrypointAssets }
24
+ | Record<string, { assets: EntrypointAssets }>
25
+ | undefined
22
26
  }
23
27
 
24
28
  const getPlugins = (): unknown[] => {
@@ -29,7 +33,11 @@ const getPlugins = (): unknown[] => {
29
33
  publicPath: config.publicPathWithoutCDN,
30
34
  writeToFileEmit: true,
31
35
  // rspack-manifest-plugin uses different option names than webpack-assets-manifest
32
- generate: (seed: Manifest | null, files: ManifestFile[], entrypoints: Record<string, string[]>) => {
36
+ generate: (
37
+ seed: Manifest | null,
38
+ files: ManifestFile[],
39
+ entrypoints: Record<string, string[]>
40
+ ) => {
33
41
  const manifest: Manifest = seed || {}
34
42
 
35
43
  // Add files mapping first
@@ -38,7 +46,10 @@ const getPlugins = (): unknown[] => {
38
46
  })
39
47
 
40
48
  // Add entrypoints information compatible with Shakapacker expectations
41
- const entrypointsManifest: Record<string, { assets: EntrypointAssets }> = {}
49
+ const entrypointsManifest: Record<
50
+ string,
51
+ { assets: EntrypointAssets }
52
+ > = {}
42
53
  Object.entries(entrypoints).forEach(
43
54
  ([entrypointName, entrypointFiles]) => {
44
55
  const jsFiles = entrypointFiles
@@ -86,7 +97,7 @@ const getPlugins = (): unknown[] => {
86
97
  }
87
98
 
88
99
  // Use Rspack's built-in SubresourceIntegrityPlugin
89
- if (config.integrity.enabled) {
100
+ if (config.integrity?.enabled) {
90
101
  plugins.push(
91
102
  new rspack.SubresourceIntegrityPlugin({
92
103
  hashFuncNames: config.integrity.hash_functions,
@@ -20,8 +20,12 @@ const getPlugins = (): unknown[] => {
20
20
  output: config.manifestPath,
21
21
  entrypointsUseAssets: true,
22
22
  publicPath: config.publicPathWithoutCDN,
23
- integrity: config.integrity.enabled,
24
- integrityHashes: config.integrity.hash_functions
23
+ ...(config.integrity
24
+ ? {
25
+ integrity: config.integrity.enabled,
26
+ integrityHashes: config.integrity.hash_functions
27
+ }
28
+ : {})
25
29
  })
26
30
  ]
27
31
 
@@ -40,7 +44,7 @@ const getPlugins = (): unknown[] => {
40
44
  }
41
45
 
42
46
  if (
43
- config.integrity.enabled &&
47
+ config.integrity?.enabled &&
44
48
  moduleExists("webpack-subresource-integrity")
45
49
  ) {
46
50
  const SubresourceIntegrityPlugin = requireOrError(
@@ -21,7 +21,10 @@ const { validateRspackDependencies } = require("../utils/validateDependencies")
21
21
  const rulesPath = resolve(__dirname, "../rules", "rspack.js")
22
22
  const rules = require(rulesPath)
23
23
 
24
- const generateRspackConfig = (extraConfig: RspackConfigWithDevServer = {}, ...extraArgs: unknown[]): RspackConfigWithDevServer => {
24
+ const generateRspackConfig = (
25
+ extraConfig: RspackConfigWithDevServer = {},
26
+ ...extraArgs: unknown[]
27
+ ): RspackConfigWithDevServer => {
25
28
  // Validate required dependencies first
26
29
  validateRspackDependencies()
27
30
  if (extraArgs.length > 0) {
@@ -49,7 +52,12 @@ const generateRspackConfig = (extraConfig: RspackConfigWithDevServer = {}, ...ex
49
52
  }
50
53
 
51
54
  // Re-export webpack-merge utilities for backward compatibility
52
- export { merge, mergeWithCustomize, mergeWithRules, unique } from "webpack-merge"
55
+ export {
56
+ merge,
57
+ mergeWithCustomize,
58
+ mergeWithRules,
59
+ unique
60
+ } from "webpack-merge"
53
61
 
54
62
  export {
55
63
  config, // shakapacker.yml
data/package/rules/raw.ts CHANGED
@@ -21,5 +21,6 @@ const webpackRawConfig = () => ({
21
21
  ]
22
22
  })
23
23
 
24
- export =
25
- config.assets_bundler === "rspack" ? rspackRawConfig() : webpackRawConfig()
24
+ export = config.assets_bundler === "rspack"
25
+ ? rspackRawConfig()
26
+ : webpackRawConfig()
@@ -6,7 +6,7 @@ const { additional_paths: extraPaths } = require("../config")
6
6
 
7
7
  export = canProcess("sass-loader", (resolvedPath: string) => {
8
8
  const optionKey =
9
- packageMajorVersion("sass-loader") > 15 ? "loadPaths" : "includePaths"
9
+ packageMajorVersion("sass-loader") >= 16 ? "loadPaths" : "includePaths"
10
10
  return getStyleRule(/\.(scss|sass)(\.erb)?$/i, [
11
11
  {
12
12
  loader: resolvedPath,
@@ -8,8 +8,8 @@ Instead of importing types from deep paths:
8
8
 
9
9
  ```typescript
10
10
  // ❌ Old way - importing from multiple deep paths
11
- import type { Config } from 'shakapacker/package/types'
12
- import type { WebpackConfigWithDevServer } from 'shakapacker/package/environments/types'
11
+ import type { Config } from "shakapacker/package/types"
12
+ import type { WebpackConfigWithDevServer } from "shakapacker/package/environments/types"
13
13
  ```
14
14
 
15
15
  You can now import all types from a single location:
@@ -20,12 +20,13 @@ import type {
20
20
  Config,
21
21
  WebpackConfigWithDevServer,
22
22
  RspackConfigWithDevServer
23
- } from 'shakapacker/types'
23
+ } from "shakapacker/types"
24
24
  ```
25
25
 
26
26
  ## Available Types
27
27
 
28
28
  ### Core Configuration Types
29
+
29
30
  - `Config` - Main Shakapacker configuration interface
30
31
  - `YamlConfig` - YAML configuration structure
31
32
  - `LegacyConfig` - Legacy configuration with deprecated options
@@ -33,12 +34,14 @@ import type {
33
34
  - `DevServerConfig` - Development server configuration
34
35
 
35
36
  ### Loader Types
37
+
36
38
  - `ShakapackerLoader` - Loader interface
37
39
  - `ShakapackerLoaderOptions` - Loader options interface
38
40
  - `LoaderResolver` - Function type for resolving loaders
39
41
  - `LoaderConfig` - Loader configuration interface
40
42
 
41
43
  ### Webpack/Rspack Types
44
+
42
45
  - `WebpackConfigWithDevServer` - Webpack config with dev server
43
46
  - `RspackConfigWithDevServer` - Rspack config with dev server
44
47
  - `RspackPluginInstance` - Rspack plugin instance type
@@ -50,12 +53,14 @@ import type {
50
53
  - `ReactRefreshRspackPlugin` - React refresh plugin for Rspack
51
54
 
52
55
  ### Webpack-Specific Types
56
+
53
57
  - `ShakapackerWebpackConfig` - Extended Webpack configuration
54
58
  - `ShakapackerRule` - Extended Webpack rule
55
59
  - `LoaderType` - String or loader object type
56
60
  - `LoaderUtils` - Loader utility functions
57
61
 
58
62
  ### Re-exported Types
63
+
59
64
  - `WebpackConfiguration` - From 'webpack'
60
65
  - `WebpackPluginInstance` - From 'webpack'
61
66
  - `RuleSetRule` - From 'webpack'
@@ -64,25 +69,22 @@ import type {
64
69
  ## Example Usage
65
70
 
66
71
  ```typescript
67
- import type {
68
- Config,
69
- WebpackConfigWithDevServer
70
- } from 'shakapacker/types'
72
+ import type { Config, WebpackConfigWithDevServer } from "shakapacker/types"
71
73
 
72
74
  const config: Config = {
73
- source_path: 'app/javascript',
74
- source_entry_path: 'packs',
75
- public_root_path: 'public',
76
- public_output_path: 'packs',
75
+ source_path: "app/javascript",
76
+ source_entry_path: "packs",
77
+ public_root_path: "public",
78
+ public_output_path: "packs"
77
79
  // ... other config
78
80
  }
79
81
 
80
82
  const webpackConfig: WebpackConfigWithDevServer = {
81
- mode: 'development',
83
+ mode: "development",
82
84
  devServer: {
83
85
  hot: true,
84
86
  port: 3035
85
87
  }
86
88
  // ... other webpack config
87
89
  }
88
- ```
90
+ ```
@@ -17,7 +17,7 @@ export type {
17
17
  LegacyConfig,
18
18
  Env,
19
19
  DevServerConfig
20
- } from '../types'
20
+ } from "../types"
21
21
 
22
22
  // Loader types
23
23
  export type {
@@ -25,7 +25,7 @@ export type {
25
25
  ShakapackerLoaderOptions,
26
26
  LoaderResolver,
27
27
  LoaderConfig
28
- } from '../loaders'
28
+ } from "../loaders"
29
29
 
30
30
  // Webpack-specific types
31
31
  export type {
@@ -35,7 +35,7 @@ export type {
35
35
  ShakapackerLoader as WebpackLoader,
36
36
  LoaderType,
37
37
  LoaderUtils
38
- } from '../webpack-types'
38
+ } from "../webpack-types"
39
39
 
40
40
  // Environment configuration types
41
41
  export type {
@@ -48,7 +48,7 @@ export type {
48
48
  CompressionPluginConstructor,
49
49
  ReactRefreshWebpackPlugin,
50
50
  ReactRefreshRspackPlugin
51
- } from '../environments/types'
51
+ } from "../environments/types"
52
52
 
53
53
  // Node.js error type (re-exported for convenience)
54
54
  export type NodeJSError = NodeJS.ErrnoException
@@ -58,4 +58,4 @@ export type {
58
58
  Configuration as WebpackConfiguration,
59
59
  WebpackPluginInstance,
60
60
  RuleSetRule
61
- } from 'webpack'
61
+ } from "webpack"
data/package/types.ts CHANGED
@@ -105,4 +105,3 @@ export interface DevServerConfig {
105
105
  | { type?: string | boolean | ServerType; options?: https.ServerOptions }
106
106
  [otherWebpackDevServerConfigKey: string]: unknown
107
107
  }
108
-
@@ -1,4 +1,7 @@
1
1
  import { resolve } from "path"
2
2
 
3
- const path: string = resolve(__dirname, "../../lib/install/config/shakapacker.yml")
3
+ const path: string = resolve(
4
+ __dirname,
5
+ "../../lib/install/config/shakapacker.yml"
6
+ )
4
7
  export = path