shakapacker 9.0.0.beta.6 → 9.0.0.beta.8
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.
- checksums.yaml +4 -4
- data/.eslintrc.fast.js +40 -0
- data/.eslintrc.js +48 -0
- data/.github/workflows/generator.yml +6 -0
- data/.gitignore +1 -4
- data/.npmignore +56 -0
- data/CHANGELOG.md +64 -1
- data/CONTRIBUTING.md +75 -21
- data/Gemfile.lock +1 -1
- data/README.md +4 -0
- data/TODO.md +15 -16
- data/docs/transpiler-migration.md +191 -0
- data/docs/typescript-migration.md +378 -0
- data/lib/install/template.rb +54 -7
- data/lib/shakapacker/version.rb +1 -1
- data/package/.npmignore +4 -0
- data/package/babel/preset.ts +56 -0
- data/package/config.ts +23 -10
- data/package/env.ts +15 -2
- data/package/environments/{development.js → development.ts} +30 -8
- data/package/environments/{production.js → production.ts} +18 -4
- data/package/environments/test.ts +53 -0
- data/package/environments/types.ts +90 -0
- data/package/esbuild/index.ts +42 -0
- data/package/optimization/rspack.ts +36 -0
- data/package/optimization/{webpack.js → webpack.ts} +12 -4
- data/package/plugins/{rspack.js → rspack.ts} +20 -5
- data/package/plugins/{webpack.js → webpack.ts} +2 -2
- data/package/rspack/{index.js → index.ts} +17 -10
- data/package/rules/{babel.js → babel.ts} +1 -1
- data/package/rules/{coffee.js → coffee.ts} +1 -1
- data/package/rules/{css.js → css.ts} +1 -1
- data/package/rules/{erb.js → erb.ts} +1 -1
- data/package/rules/{esbuild.js → esbuild.ts} +2 -2
- data/package/rules/{file.js → file.ts} +11 -6
- data/package/rules/{jscommon.js → jscommon.ts} +4 -4
- data/package/rules/{less.js → less.ts} +3 -3
- data/package/rules/raw.ts +25 -0
- data/package/rules/{rspack.js → rspack.ts} +21 -11
- data/package/rules/{sass.js → sass.ts} +1 -1
- data/package/rules/{stylus.js → stylus.ts} +3 -7
- data/package/rules/{swc.js → swc.ts} +2 -2
- data/package/rules/{webpack.js → webpack.ts} +1 -1
- data/package/swc/index.ts +54 -0
- data/package/types/README.md +87 -0
- data/package/types/index.ts +60 -0
- data/package/utils/errorCodes.ts +219 -0
- data/package/utils/errorHelpers.ts +68 -2
- data/package/utils/pathValidation.ts +139 -0
- data/package/utils/typeGuards.ts +161 -47
- data/package.json +26 -4
- data/scripts/remove-use-strict.js +45 -0
- data/scripts/type-check-no-emit.js +27 -0
- data/test/package/rules/raw.test.js +40 -7
- data/test/package/rules/webpack.test.js +21 -2
- data/test/package/transpiler-defaults.test.js +127 -0
- data/test/scripts/remove-use-strict.test.js +125 -0
- data/test/typescript/build.test.js +3 -2
- data/test/typescript/environments.test.js +107 -0
- data/test/typescript/pathValidation.test.js +142 -0
- data/test/typescript/securityValidation.test.js +182 -0
- data/tsconfig.eslint.json +16 -0
- data/tsconfig.json +9 -10
- data/yarn.lock +415 -6
- metadata +50 -28
- data/package/babel/preset.js +0 -48
- data/package/environments/base.js +0 -103
- data/package/environments/test.js +0 -19
- data/package/esbuild/index.js +0 -40
- data/package/optimization/rspack.js +0 -29
- data/package/rules/raw.js +0 -15
- data/package/swc/index.js +0 -50
data/package/config.ts
CHANGED
@@ -125,33 +125,46 @@ if (config.integrity?.hash_functions) {
|
|
125
125
|
config.integrity.hash_functions = [...new Set(config.integrity.hash_functions)]
|
126
126
|
}
|
127
127
|
|
128
|
-
//
|
128
|
+
// Ensure assets_bundler has a default value
|
129
|
+
if (!config.assets_bundler) {
|
130
|
+
config.assets_bundler = "webpack"
|
131
|
+
}
|
132
|
+
|
133
|
+
// Allow ENV variable to override assets_bundler
|
129
134
|
if (process.env.SHAKAPACKER_ASSETS_BUNDLER) {
|
130
135
|
config.assets_bundler = process.env.SHAKAPACKER_ASSETS_BUNDLER
|
131
136
|
}
|
132
137
|
|
133
138
|
// Define clear defaults
|
134
|
-
|
139
|
+
// Keep Babel as default for webpack to maintain backward compatibility
|
140
|
+
// Use SWC for rspack as it's a newer bundler where we can set modern defaults
|
141
|
+
const DEFAULT_JAVASCRIPT_TRANSPILER =
|
135
142
|
config.assets_bundler === "rspack" ? "swc" : "babel"
|
136
143
|
|
137
|
-
// Backward compatibility:
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
144
|
+
// Backward compatibility: Check for webpack_loader using proper type guard
|
145
|
+
function hasWebpackLoader(obj: unknown): obj is Config & { webpack_loader: string } {
|
146
|
+
return (
|
147
|
+
typeof obj === 'object' &&
|
148
|
+
obj !== null &&
|
149
|
+
'webpack_loader' in obj &&
|
150
|
+
typeof (obj as Record<string, unknown>).webpack_loader === 'string'
|
151
|
+
)
|
152
|
+
}
|
142
153
|
|
143
|
-
|
154
|
+
// Allow environment variable to override javascript_transpiler
|
155
|
+
if (process.env.SHAKAPACKER_JAVASCRIPT_TRANSPILER) {
|
156
|
+
config.javascript_transpiler = process.env.SHAKAPACKER_JAVASCRIPT_TRANSPILER
|
157
|
+
} else if (hasWebpackLoader(config) && !config.javascript_transpiler) {
|
144
158
|
console.warn(
|
145
159
|
"[SHAKAPACKER DEPRECATION] The 'webpack_loader' configuration option is deprecated.\n" +
|
146
160
|
"Please use 'javascript_transpiler' instead as it better reflects its purpose of configuring JavaScript transpilation regardless of the bundler used."
|
147
161
|
)
|
148
|
-
config.javascript_transpiler =
|
162
|
+
config.javascript_transpiler = config.webpack_loader
|
149
163
|
} else if (!config.javascript_transpiler) {
|
150
164
|
config.javascript_transpiler = DEFAULT_JAVASCRIPT_TRANSPILER
|
151
165
|
}
|
152
166
|
|
153
167
|
// Ensure webpack_loader is always available for backward compatibility
|
154
|
-
// Use property assignment instead of type assertion
|
155
168
|
Object.defineProperty(config, 'webpack_loader', {
|
156
169
|
value: config.javascript_transpiler,
|
157
170
|
writable: true,
|
data/package/env.ts
CHANGED
@@ -3,14 +3,27 @@ import { readFileSync } from "fs"
|
|
3
3
|
const defaultConfigPath = require("./utils/defaultConfigPath")
|
4
4
|
const configPath = require("./utils/configPath")
|
5
5
|
const { isFileNotFoundError } = require("./utils/errorHelpers")
|
6
|
+
const { sanitizeEnvValue } = require("./utils/pathValidation")
|
6
7
|
|
7
8
|
const NODE_ENVIRONMENTS = ["development", "production", "test"] as const
|
8
9
|
const DEFAULT = "production"
|
9
10
|
|
10
|
-
|
11
|
-
const
|
11
|
+
// Sanitize environment variables to prevent injection
|
12
|
+
const initialRailsEnv = sanitizeEnvValue(process.env.RAILS_ENV)
|
13
|
+
const rawNodeEnv = sanitizeEnvValue(process.env.NODE_ENV)
|
14
|
+
|
15
|
+
// Validate NODE_ENV strictly
|
12
16
|
const nodeEnv =
|
13
17
|
rawNodeEnv && NODE_ENVIRONMENTS.includes(rawNodeEnv as typeof NODE_ENVIRONMENTS[number]) ? rawNodeEnv : DEFAULT
|
18
|
+
|
19
|
+
// Log warning if NODE_ENV was invalid
|
20
|
+
if (rawNodeEnv && !NODE_ENVIRONMENTS.includes(rawNodeEnv as typeof NODE_ENVIRONMENTS[number])) {
|
21
|
+
console.warn(
|
22
|
+
`[SHAKAPACKER WARNING] Invalid NODE_ENV value: ${rawNodeEnv}. ` +
|
23
|
+
`Valid values are: ${NODE_ENVIRONMENTS.join(', ')}. Using default: ${DEFAULT}`
|
24
|
+
)
|
25
|
+
}
|
26
|
+
|
14
27
|
const isProduction = nodeEnv === "production"
|
15
28
|
const isDevelopment = nodeEnv === "development"
|
16
29
|
|
@@ -1,17 +1,35 @@
|
|
1
|
+
/**
|
2
|
+
* Development environment configuration for webpack and rspack bundlers
|
3
|
+
* @module environments/development
|
4
|
+
*/
|
5
|
+
|
1
6
|
const { merge } = require("webpack-merge")
|
2
7
|
const config = require("../config")
|
3
8
|
const baseConfig = require("./base")
|
4
9
|
const webpackDevServerConfig = require("../webpackDevServerConfig")
|
5
10
|
const { runningWebpackDevServer } = require("../env")
|
6
11
|
const { moduleExists } = require("../utils/helpers")
|
12
|
+
import type {
|
13
|
+
WebpackConfigWithDevServer,
|
14
|
+
RspackConfigWithDevServer,
|
15
|
+
ReactRefreshWebpackPlugin,
|
16
|
+
ReactRefreshRspackPlugin
|
17
|
+
} from "./types"
|
7
18
|
|
19
|
+
/**
|
20
|
+
* Base development configuration shared between webpack and rspack
|
21
|
+
*/
|
8
22
|
const baseDevConfig = {
|
9
|
-
mode: "development",
|
10
|
-
devtool: "cheap-module-source-map"
|
23
|
+
mode: "development" as const,
|
24
|
+
devtool: "cheap-module-source-map" as const
|
11
25
|
}
|
12
26
|
|
13
|
-
|
14
|
-
|
27
|
+
/**
|
28
|
+
* Generate webpack-specific development configuration
|
29
|
+
* @returns Webpack configuration with dev server settings
|
30
|
+
*/
|
31
|
+
const webpackDevConfig = (): WebpackConfigWithDevServer => {
|
32
|
+
const webpackConfig: WebpackConfigWithDevServer = {
|
15
33
|
...baseDevConfig,
|
16
34
|
...(runningWebpackDevServer && { devServer: webpackDevServerConfig() })
|
17
35
|
}
|
@@ -33,15 +51,19 @@ const webpackDevConfig = () => {
|
|
33
51
|
return webpackConfig
|
34
52
|
}
|
35
53
|
|
36
|
-
|
54
|
+
/**
|
55
|
+
* Generate rspack-specific development configuration
|
56
|
+
* @returns Rspack configuration with dev server settings
|
57
|
+
*/
|
58
|
+
const rspackDevConfig = (): RspackConfigWithDevServer => {
|
37
59
|
const devServerConfig = webpackDevServerConfig()
|
38
|
-
const rspackConfig = {
|
60
|
+
const rspackConfig: RspackConfigWithDevServer = {
|
39
61
|
...baseDevConfig,
|
40
62
|
devServer: {
|
41
63
|
...devServerConfig,
|
42
64
|
devMiddleware: {
|
43
|
-
...devServerConfig.devMiddleware,
|
44
|
-
writeToDisk: (filePath) => !filePath.includes(".hot-update.")
|
65
|
+
...(devServerConfig.devMiddleware || {}),
|
66
|
+
writeToDisk: (filePath: string) => !filePath.includes(".hot-update.")
|
45
67
|
}
|
46
68
|
}
|
47
69
|
}
|
@@ -1,3 +1,8 @@
|
|
1
|
+
/**
|
2
|
+
* Production environment configuration for webpack and rspack bundlers
|
3
|
+
* @module environments/production
|
4
|
+
*/
|
5
|
+
|
1
6
|
/* eslint global-require: 0 */
|
2
7
|
/* eslint import/no-dynamic-require: 0 */
|
3
8
|
|
@@ -6,6 +11,8 @@ const { merge } = require("webpack-merge")
|
|
6
11
|
const baseConfig = require("./base")
|
7
12
|
const { moduleExists } = require("../utils/helpers")
|
8
13
|
const config = require("../config")
|
14
|
+
import type { Configuration as WebpackConfiguration, WebpackPluginInstance } from "webpack"
|
15
|
+
import type { CompressionPluginConstructor } from "./types"
|
9
16
|
|
10
17
|
const optimizationPath = resolve(
|
11
18
|
__dirname,
|
@@ -15,14 +22,18 @@ const optimizationPath = resolve(
|
|
15
22
|
)
|
16
23
|
const { getOptimization } = require(optimizationPath)
|
17
24
|
|
18
|
-
let CompressionPlugin = null
|
25
|
+
let CompressionPlugin: CompressionPluginConstructor | null = null
|
19
26
|
if (moduleExists("compression-webpack-plugin")) {
|
20
27
|
// eslint-disable-next-line global-require
|
21
28
|
CompressionPlugin = require("compression-webpack-plugin")
|
22
29
|
}
|
23
30
|
|
24
|
-
|
25
|
-
|
31
|
+
/**
|
32
|
+
* Generate production plugins including compression
|
33
|
+
* @returns Array of webpack plugins for production
|
34
|
+
*/
|
35
|
+
const getPlugins = (): WebpackPluginInstance[] => {
|
36
|
+
const plugins: WebpackPluginInstance[] = []
|
26
37
|
|
27
38
|
if (CompressionPlugin) {
|
28
39
|
plugins.push(
|
@@ -47,7 +58,10 @@ const getPlugins = () => {
|
|
47
58
|
return plugins
|
48
59
|
}
|
49
60
|
|
50
|
-
|
61
|
+
/**
|
62
|
+
* Production configuration with optimizations and compression
|
63
|
+
*/
|
64
|
+
const productionConfig: Partial<WebpackConfiguration> = {
|
51
65
|
devtool: "source-map",
|
52
66
|
stats: "normal",
|
53
67
|
bail: true,
|
@@ -0,0 +1,53 @@
|
|
1
|
+
/**
|
2
|
+
* Test environment configuration for webpack and rspack bundlers
|
3
|
+
* @module environments/test
|
4
|
+
*/
|
5
|
+
|
6
|
+
const { merge } = require("webpack-merge")
|
7
|
+
const config = require("../config")
|
8
|
+
const baseConfig = require("./base")
|
9
|
+
import type { Configuration as WebpackConfiguration } from "webpack"
|
10
|
+
|
11
|
+
interface TestConfig {
|
12
|
+
mode: "development" | "production" | "none"
|
13
|
+
devtool: string | false
|
14
|
+
watchOptions?: {
|
15
|
+
ignored: RegExp
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Shared test configuration for both webpack and rspack
|
21
|
+
* Ensures consistent test behavior across bundlers
|
22
|
+
*/
|
23
|
+
const sharedTestConfig: TestConfig = {
|
24
|
+
mode: "development",
|
25
|
+
devtool: "cheap-module-source-map",
|
26
|
+
// Disable file watching in test mode
|
27
|
+
watchOptions: {
|
28
|
+
ignored: /node_modules/
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Generate rspack-specific test configuration
|
34
|
+
* @returns Rspack configuration optimized for testing
|
35
|
+
*/
|
36
|
+
const rspackTestConfig = (): TestConfig => ({
|
37
|
+
...sharedTestConfig
|
38
|
+
// Add any rspack-specific overrides here if needed
|
39
|
+
})
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Generate webpack-specific test configuration
|
43
|
+
* @returns Webpack configuration for testing with same settings as rspack
|
44
|
+
*/
|
45
|
+
const webpackTestConfig = (): Partial<WebpackConfiguration> => ({
|
46
|
+
...sharedTestConfig
|
47
|
+
// Add any webpack-specific overrides here if needed
|
48
|
+
})
|
49
|
+
|
50
|
+
const bundlerConfig =
|
51
|
+
config.assets_bundler === "rspack" ? rspackTestConfig() : webpackTestConfig()
|
52
|
+
|
53
|
+
module.exports = merge(baseConfig, bundlerConfig)
|
@@ -0,0 +1,90 @@
|
|
1
|
+
/**
|
2
|
+
* Type definitions for environment configurations
|
3
|
+
* These types are exported for consumer use
|
4
|
+
*/
|
5
|
+
|
6
|
+
import type { Configuration as WebpackConfiguration, WebpackPluginInstance } from "webpack"
|
7
|
+
import type { Configuration as DevServerConfiguration } from "webpack-dev-server"
|
8
|
+
|
9
|
+
/**
|
10
|
+
* Webpack configuration extended with dev server support
|
11
|
+
*/
|
12
|
+
export interface WebpackConfigWithDevServer extends WebpackConfiguration {
|
13
|
+
devServer?: DevServerConfiguration
|
14
|
+
plugins?: WebpackPluginInstance[]
|
15
|
+
}
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Rspack plugin interface
|
19
|
+
* Rspack plugins follow a similar pattern to webpack but may have different internals
|
20
|
+
*/
|
21
|
+
export interface RspackPlugin {
|
22
|
+
new(...args: any[]): {
|
23
|
+
apply(compiler: any): void
|
24
|
+
[key: string]: any
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
/**
|
29
|
+
* Rspack dev server configuration
|
30
|
+
* Similar to webpack-dev-server but with some rspack-specific options
|
31
|
+
*/
|
32
|
+
export interface RspackDevServerConfig {
|
33
|
+
port?: number | string | "auto"
|
34
|
+
host?: string
|
35
|
+
hot?: boolean | "only"
|
36
|
+
historyApiFallback?: boolean | Record<string, unknown>
|
37
|
+
headers?: Record<string, string | string[]>
|
38
|
+
proxy?: unknown
|
39
|
+
static?: boolean | string | Array<string | Record<string, unknown>>
|
40
|
+
devMiddleware?: {
|
41
|
+
writeToDisk?: boolean | ((filePath: string) => boolean)
|
42
|
+
publicPath?: string
|
43
|
+
[key: string]: unknown
|
44
|
+
}
|
45
|
+
[key: string]: unknown
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Rspack configuration with dev server support
|
50
|
+
*/
|
51
|
+
export interface RspackConfigWithDevServer {
|
52
|
+
mode?: "development" | "production" | "none"
|
53
|
+
devtool?: string | false
|
54
|
+
devServer?: RspackDevServerConfig
|
55
|
+
plugins?: RspackPlugin[]
|
56
|
+
module?: WebpackConfiguration["module"]
|
57
|
+
resolve?: WebpackConfiguration["resolve"]
|
58
|
+
entry?: WebpackConfiguration["entry"]
|
59
|
+
output?: WebpackConfiguration["output"]
|
60
|
+
optimization?: WebpackConfiguration["optimization"]
|
61
|
+
[key: string]: unknown
|
62
|
+
}
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Compression plugin options interface
|
66
|
+
*/
|
67
|
+
export interface CompressionPluginOptions {
|
68
|
+
filename: string
|
69
|
+
algorithm: string | "gzip" | "brotliCompress"
|
70
|
+
test: RegExp
|
71
|
+
threshold?: number
|
72
|
+
minRatio?: number
|
73
|
+
deleteOriginalAssets?: boolean
|
74
|
+
}
|
75
|
+
|
76
|
+
/**
|
77
|
+
* Compression plugin constructor type
|
78
|
+
*/
|
79
|
+
export type CompressionPluginConstructor = new (options: CompressionPluginOptions) => WebpackPluginInstance
|
80
|
+
|
81
|
+
/**
|
82
|
+
* React Refresh plugin types
|
83
|
+
*/
|
84
|
+
export interface ReactRefreshWebpackPlugin {
|
85
|
+
new(options?: Record<string, unknown>): WebpackPluginInstance
|
86
|
+
}
|
87
|
+
|
88
|
+
export interface ReactRefreshRspackPlugin {
|
89
|
+
new(options?: Record<string, unknown>): RspackPlugin
|
90
|
+
}
|
@@ -0,0 +1,42 @@
|
|
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 LOADER_EXT_REGEX = /\.([jt]sx?)(\.erb)?$/
|
10
|
+
|
11
|
+
const getLoaderExtension = (filename: string): string => {
|
12
|
+
const matchData = filename.match(LOADER_EXT_REGEX)
|
13
|
+
|
14
|
+
if (!matchData) {
|
15
|
+
return "js"
|
16
|
+
}
|
17
|
+
|
18
|
+
return matchData[1] ?? "js"
|
19
|
+
}
|
20
|
+
|
21
|
+
const getCustomConfig = (): Partial<RuleSetRule> => {
|
22
|
+
const path = resolve("config", "esbuild.config.js")
|
23
|
+
if (existsSync(path)) {
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
25
|
+
return require(path)
|
26
|
+
}
|
27
|
+
return {}
|
28
|
+
}
|
29
|
+
|
30
|
+
const getEsbuildLoaderConfig = (filenameToProcess: string): RuleSetRule => {
|
31
|
+
const customConfig = getCustomConfig()
|
32
|
+
const defaultConfig: RuleSetRule = {
|
33
|
+
loader: require.resolve("esbuild-loader"),
|
34
|
+
options: {
|
35
|
+
loader: getLoaderExtension(filenameToProcess)
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
return merge(defaultConfig, customConfig)
|
40
|
+
}
|
41
|
+
|
42
|
+
export { getEsbuildLoaderConfig }
|
@@ -0,0 +1,36 @@
|
|
1
|
+
const { requireOrError } = require("../utils/requireOrError")
|
2
|
+
const { error: logError } = require("../utils/debug")
|
3
|
+
|
4
|
+
const rspack = requireOrError("@rspack/core")
|
5
|
+
|
6
|
+
interface OptimizationConfig {
|
7
|
+
minimize: boolean
|
8
|
+
minimizer?: unknown[]
|
9
|
+
}
|
10
|
+
|
11
|
+
const getOptimization = (): OptimizationConfig => {
|
12
|
+
// Use Rspack's built-in minification instead of terser-webpack-plugin
|
13
|
+
const result: OptimizationConfig = { minimize: true }
|
14
|
+
try {
|
15
|
+
result.minimizer = [
|
16
|
+
new rspack.SwcJsMinimizerRspackPlugin(),
|
17
|
+
new rspack.LightningCssMinimizerRspackPlugin()
|
18
|
+
]
|
19
|
+
} catch (error: unknown) {
|
20
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
21
|
+
const errorStack = error instanceof Error ? error.stack : ''
|
22
|
+
// Log full error with stack trace
|
23
|
+
logError(
|
24
|
+
`Failed to configure Rspack minimizers: ${errorMessage}\n${errorStack}`
|
25
|
+
)
|
26
|
+
// Re-throw the error to properly propagate it
|
27
|
+
throw new Error(
|
28
|
+
`Could not configure Rspack minimizers: ${errorMessage}. Please check that @rspack/core is properly installed.`
|
29
|
+
)
|
30
|
+
}
|
31
|
+
return result
|
32
|
+
}
|
33
|
+
|
34
|
+
export = {
|
35
|
+
getOptimization
|
36
|
+
}
|
@@ -3,7 +3,7 @@ const { requireOrError } = require("../utils/requireOrError")
|
|
3
3
|
const TerserPlugin = requireOrError("terser-webpack-plugin")
|
4
4
|
const { moduleExists } = require("../utils/helpers")
|
5
5
|
|
6
|
-
const tryCssMinimizer = () => {
|
6
|
+
const tryCssMinimizer = (): unknown | null => {
|
7
7
|
if (
|
8
8
|
moduleExists("css-loader") &&
|
9
9
|
moduleExists("css-minimizer-webpack-plugin")
|
@@ -15,12 +15,20 @@ const tryCssMinimizer = () => {
|
|
15
15
|
return null
|
16
16
|
}
|
17
17
|
|
18
|
-
|
18
|
+
interface OptimizationConfig {
|
19
|
+
minimizer: unknown[]
|
20
|
+
}
|
21
|
+
|
22
|
+
const getOptimization = (): OptimizationConfig => {
|
19
23
|
return {
|
20
24
|
minimizer: [
|
21
25
|
tryCssMinimizer(),
|
22
26
|
new TerserPlugin({
|
23
|
-
|
27
|
+
// SHAKAPACKER_PARALLEL env var: number of parallel workers, or true for auto (os.cpus().length - 1)
|
28
|
+
// If not set or invalid, defaults to true (automatic parallelization)
|
29
|
+
parallel: process.env.SHAKAPACKER_PARALLEL
|
30
|
+
? Number.parseInt(process.env.SHAKAPACKER_PARALLEL, 10) || true
|
31
|
+
: true,
|
24
32
|
terserOptions: {
|
25
33
|
parse: {
|
26
34
|
// Let terser parse ecma 8 code but always output
|
@@ -44,6 +52,6 @@ const getOptimization = () => {
|
|
44
52
|
}
|
45
53
|
}
|
46
54
|
|
47
|
-
|
55
|
+
export = {
|
48
56
|
getOptimization
|
49
57
|
}
|
@@ -6,7 +6,22 @@ const config = require("../config")
|
|
6
6
|
const { isProduction } = require("../env")
|
7
7
|
const { moduleExists } = require("../utils/helpers")
|
8
8
|
|
9
|
-
|
9
|
+
interface ManifestFile {
|
10
|
+
name: string
|
11
|
+
path: string
|
12
|
+
}
|
13
|
+
|
14
|
+
interface EntrypointAssets {
|
15
|
+
js: string[]
|
16
|
+
css: string[]
|
17
|
+
}
|
18
|
+
|
19
|
+
interface Manifest {
|
20
|
+
entrypoints?: Record<string, { assets: EntrypointAssets }>
|
21
|
+
[key: string]: string | { assets: EntrypointAssets } | Record<string, { assets: EntrypointAssets }> | undefined
|
22
|
+
}
|
23
|
+
|
24
|
+
const getPlugins = (): unknown[] => {
|
10
25
|
const plugins = [
|
11
26
|
new rspack.EnvironmentPlugin(process.env),
|
12
27
|
new RspackManifestPlugin({
|
@@ -14,8 +29,8 @@ const getPlugins = () => {
|
|
14
29
|
publicPath: config.publicPathWithoutCDN,
|
15
30
|
writeToFileEmit: true,
|
16
31
|
// rspack-manifest-plugin uses different option names than webpack-assets-manifest
|
17
|
-
generate: (seed, files, entrypoints) => {
|
18
|
-
const manifest = seed || {}
|
32
|
+
generate: (seed: Manifest | null, files: ManifestFile[], entrypoints: Record<string, string[]>) => {
|
33
|
+
const manifest: Manifest = seed || {}
|
19
34
|
|
20
35
|
// Add files mapping first
|
21
36
|
files.forEach((file) => {
|
@@ -23,7 +38,7 @@ const getPlugins = () => {
|
|
23
38
|
})
|
24
39
|
|
25
40
|
// Add entrypoints information compatible with Shakapacker expectations
|
26
|
-
const entrypointsManifest = {}
|
41
|
+
const entrypointsManifest: Record<string, { assets: EntrypointAssets }> = {}
|
27
42
|
Object.entries(entrypoints).forEach(
|
28
43
|
([entrypointName, entrypointFiles]) => {
|
29
44
|
const jsFiles = entrypointFiles
|
@@ -83,6 +98,6 @@ const getPlugins = () => {
|
|
83
98
|
return plugins
|
84
99
|
}
|
85
100
|
|
86
|
-
|
101
|
+
export = {
|
87
102
|
getPlugins
|
88
103
|
}
|
@@ -6,7 +6,7 @@ const config = require("../config")
|
|
6
6
|
const { isProduction } = require("../env")
|
7
7
|
const { moduleExists } = require("../utils/helpers")
|
8
8
|
|
9
|
-
const getPlugins = () => {
|
9
|
+
const getPlugins = (): unknown[] => {
|
10
10
|
// TODO: Remove WebpackAssetsManifestConstructor workaround when dropping 'webpack-assets-manifest < 6.0.0' (Node >=20.10.0) support
|
11
11
|
const WebpackAssetsManifestConstructor =
|
12
12
|
"WebpackAssetsManifest" in WebpackAssetsManifest
|
@@ -57,6 +57,6 @@ const getPlugins = () => {
|
|
57
57
|
return plugins
|
58
58
|
}
|
59
59
|
|
60
|
-
|
60
|
+
export = {
|
61
61
|
getPlugins
|
62
62
|
}
|
@@ -1,14 +1,15 @@
|
|
1
1
|
/* eslint global-require: 0 */
|
2
2
|
/* eslint import/no-dynamic-require: 0 */
|
3
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
|
4
7
|
const webpackMerge = require("webpack-merge")
|
5
|
-
|
6
|
-
|
8
|
+
import { resolve } from "path"
|
9
|
+
import { existsSync } from "fs"
|
10
|
+
import type { RspackConfigWithDevServer } from "../environments/types"
|
7
11
|
const config = require("../config")
|
8
12
|
const baseConfig = require("../environments/base")
|
9
|
-
|
10
|
-
const rulesPath = resolve(__dirname, "../rules", "rspack.js")
|
11
|
-
const rules = require(rulesPath)
|
12
13
|
const devServer = require("../dev_server")
|
13
14
|
const env = require("../env")
|
14
15
|
const { moduleExists, canProcess } = require("../utils/helpers")
|
@@ -17,7 +18,10 @@ const { getPlugins } = require("../plugins/rspack")
|
|
17
18
|
const { getOptimization } = require("../optimization/rspack")
|
18
19
|
const { validateRspackDependencies } = require("../utils/validateDependencies")
|
19
20
|
|
20
|
-
const
|
21
|
+
const rulesPath = resolve(__dirname, "../rules", "rspack.js")
|
22
|
+
const rules = require(rulesPath)
|
23
|
+
|
24
|
+
const generateRspackConfig = (extraConfig: RspackConfigWithDevServer = {}, ...extraArgs: unknown[]): RspackConfigWithDevServer => {
|
21
25
|
// Validate required dependencies first
|
22
26
|
validateRspackDependencies()
|
23
27
|
if (extraArgs.length > 0) {
|
@@ -28,10 +32,11 @@ const generateRspackConfig = (extraConfig = {}, ...extraArgs) => {
|
|
28
32
|
|
29
33
|
const { nodeEnv } = env
|
30
34
|
const path = resolve(__dirname, "../environments", `${nodeEnv}.js`)
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
31
36
|
const environmentConfig = existsSync(path) ? require(path) : baseConfig
|
32
37
|
|
33
38
|
// Create base rspack config
|
34
|
-
const rspackConfig = {
|
39
|
+
const rspackConfig: RspackConfigWithDevServer = {
|
35
40
|
...environmentConfig,
|
36
41
|
module: {
|
37
42
|
rules
|
@@ -43,7 +48,10 @@ const generateRspackConfig = (extraConfig = {}, ...extraArgs) => {
|
|
43
48
|
return webpackMerge.merge({}, rspackConfig, extraConfig)
|
44
49
|
}
|
45
50
|
|
46
|
-
|
51
|
+
// Re-export webpack-merge utilities for backward compatibility
|
52
|
+
export { merge, mergeWithCustomize, mergeWithRules, unique } from "webpack-merge"
|
53
|
+
|
54
|
+
export {
|
47
55
|
config, // shakapacker.yml
|
48
56
|
devServer,
|
49
57
|
generateRspackConfig,
|
@@ -52,6 +60,5 @@ module.exports = {
|
|
52
60
|
rules,
|
53
61
|
moduleExists,
|
54
62
|
canProcess,
|
55
|
-
inliningCss
|
56
|
-
...webpackMerge
|
63
|
+
inliningCss
|
57
64
|
}
|
@@ -3,7 +3,7 @@ const { javascript_transpiler: javascriptTranspiler } = require("../config")
|
|
3
3
|
const { isProduction } = require("../env")
|
4
4
|
const jscommon = require("./jscommon")
|
5
5
|
|
6
|
-
|
6
|
+
export = loaderMatches(javascriptTranspiler, "babel", () => ({
|
7
7
|
test: /\.(js|jsx|mjs|ts|tsx|coffee)?(\.erb)?$/,
|
8
8
|
...jscommon,
|
9
9
|
use: [
|
@@ -2,7 +2,7 @@ const { canProcess } = require("../utils/helpers")
|
|
2
2
|
|
3
3
|
const runner = /^win/.test(process.platform) ? "ruby " : ""
|
4
4
|
|
5
|
-
|
5
|
+
export = canProcess("rails-erb-loader", (resolvedPath: string) => ({
|
6
6
|
test: /\.erb$/,
|
7
7
|
enforce: "pre",
|
8
8
|
exclude: /node_modules/,
|
@@ -3,8 +3,8 @@ const { getEsbuildLoaderConfig } = require("../esbuild")
|
|
3
3
|
const { javascript_transpiler: javascriptTranspiler } = require("../config")
|
4
4
|
const jscommon = require("./jscommon")
|
5
5
|
|
6
|
-
|
6
|
+
export = loaderMatches(javascriptTranspiler, "esbuild", () => ({
|
7
7
|
test: /\.(ts|tsx|js|jsx|mjs|coffee)?(\.erb)?$/,
|
8
8
|
...jscommon,
|
9
|
-
use: ({ resource }) => getEsbuildLoaderConfig(resource)
|
9
|
+
use: ({ resource }: { resource: string }) => getEsbuildLoaderConfig(resource)
|
10
10
|
}))
|