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.
- checksums.yaml +4 -4
- data/.eslintignore +1 -0
- data/.eslintrc.fast.js +40 -0
- data/.eslintrc.js +48 -0
- data/.github/STATUS.md +1 -0
- data/.github/workflows/claude-code-review.yml +54 -0
- data/.github/workflows/claude.yml +50 -0
- data/.github/workflows/dummy.yml +8 -4
- data/.github/workflows/generator.yml +17 -14
- data/.github/workflows/node.yml +23 -1
- data/.github/workflows/ruby.yml +11 -0
- data/.github/workflows/test-bundlers.yml +170 -0
- data/.gitignore +17 -0
- data/.husky/pre-commit +2 -0
- data/.npmignore +56 -0
- data/.prettierignore +3 -0
- data/.rubocop.yml +1 -0
- data/.yalcignore +26 -0
- data/CHANGELOG.md +156 -18
- data/CLAUDE.md +29 -0
- data/CONTRIBUTING.md +138 -20
- data/Gemfile.lock +3 -3
- data/README.md +130 -5
- data/Rakefile +39 -4
- data/TODO.md +50 -0
- data/TODO_v9.md +87 -0
- data/conductor-setup.sh +70 -0
- data/conductor.json +7 -0
- data/docs/cdn_setup.md +379 -0
- data/docs/css-modules-export-mode.md +512 -0
- data/docs/deployment.md +10 -1
- data/docs/optional-peer-dependencies.md +198 -0
- data/docs/peer-dependencies.md +60 -0
- data/docs/rspack.md +190 -0
- data/docs/rspack_migration_guide.md +202 -0
- data/docs/transpiler-migration.md +188 -0
- data/docs/transpiler-performance.md +179 -0
- data/docs/troubleshooting.md +5 -0
- data/docs/typescript-migration.md +378 -0
- data/docs/typescript.md +99 -0
- data/docs/using_esbuild_loader.md +3 -3
- data/docs/using_swc_loader.md +5 -3
- data/docs/v6_upgrade.md +10 -0
- data/docs/v9_upgrade.md +413 -0
- data/lib/install/bin/shakapacker +3 -5
- data/lib/install/config/rspack/rspack.config.js +6 -0
- data/lib/install/config/rspack/rspack.config.ts +7 -0
- data/lib/install/config/shakapacker.yml +12 -2
- data/lib/install/config/webpack/webpack.config.ts +7 -0
- data/lib/install/package.json +38 -0
- data/lib/install/template.rb +194 -44
- data/lib/shakapacker/configuration.rb +141 -0
- data/lib/shakapacker/dev_server_runner.rb +25 -5
- data/lib/shakapacker/doctor.rb +844 -0
- data/lib/shakapacker/manifest.rb +4 -2
- data/lib/shakapacker/rspack_runner.rb +19 -0
- data/lib/shakapacker/runner.rb +144 -4
- data/lib/shakapacker/swc_migrator.rb +376 -0
- data/lib/shakapacker/utils/manager.rb +2 -0
- data/lib/shakapacker/version.rb +1 -1
- data/lib/shakapacker/version_checker.rb +1 -1
- data/lib/shakapacker/webpack_runner.rb +4 -42
- data/lib/shakapacker.rb +2 -1
- data/lib/tasks/shakapacker/doctor.rake +8 -0
- data/lib/tasks/shakapacker/install.rake +12 -2
- data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
- data/lib/tasks/shakapacker.rake +1 -0
- data/package/.npmignore +4 -0
- data/package/babel/preset.ts +56 -0
- data/package/config.ts +175 -0
- data/package/{dev_server.js → dev_server.ts} +8 -5
- data/package/env.ts +92 -0
- data/package/environments/base.ts +138 -0
- data/package/environments/development.ts +90 -0
- data/package/environments/production.ts +80 -0
- data/package/environments/test.ts +53 -0
- data/package/environments/types.ts +90 -0
- data/package/esbuild/index.ts +42 -0
- data/package/index.d.ts +3 -97
- data/package/index.ts +52 -0
- data/package/loaders.d.ts +28 -0
- data/package/optimization/rspack.ts +36 -0
- data/package/optimization/webpack.ts +57 -0
- data/package/plugins/rspack.ts +103 -0
- data/package/plugins/webpack.ts +62 -0
- data/package/rspack/index.ts +64 -0
- data/package/rules/{babel.js → babel.ts} +2 -2
- data/package/rules/{coffee.js → coffee.ts} +1 -1
- data/package/rules/css.ts +3 -0
- data/package/rules/{erb.js → erb.ts} +1 -1
- data/package/rules/esbuild.ts +10 -0
- data/package/rules/file.ts +40 -0
- data/package/rules/{jscommon.js → jscommon.ts} +4 -4
- data/package/rules/{less.js → less.ts} +4 -4
- data/package/rules/raw.ts +25 -0
- data/package/rules/rspack.ts +176 -0
- data/package/rules/{sass.js → sass.ts} +7 -3
- data/package/rules/{stylus.js → stylus.ts} +4 -8
- data/package/rules/swc.ts +10 -0
- data/package/rules/{index.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/types.ts +108 -0
- data/package/utils/configPath.ts +6 -0
- data/package/utils/debug.ts +49 -0
- data/package/utils/defaultConfigPath.ts +4 -0
- data/package/utils/errorCodes.ts +219 -0
- data/package/utils/errorHelpers.ts +143 -0
- data/package/utils/getStyleRule.ts +64 -0
- data/package/utils/helpers.ts +85 -0
- data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
- data/package/utils/pathValidation.ts +139 -0
- data/package/utils/requireOrError.ts +15 -0
- data/package/utils/snakeToCamelCase.ts +5 -0
- data/package/utils/typeGuards.ts +342 -0
- data/package/utils/validateDependencies.ts +61 -0
- data/package/webpack-types.d.ts +33 -0
- data/package/webpackDevServerConfig.ts +117 -0
- data/package.json +134 -9
- data/scripts/remove-use-strict.js +45 -0
- data/scripts/type-check-no-emit.js +27 -0
- data/test/package/config.test.js +3 -0
- data/test/package/env.test.js +42 -7
- data/test/package/environments/base.test.js +5 -1
- data/test/package/rules/babel.test.js +16 -0
- data/test/package/rules/esbuild.test.js +1 -1
- data/test/package/rules/raw.test.js +40 -7
- data/test/package/rules/swc.test.js +1 -1
- data/test/package/rules/webpack.test.js +35 -0
- data/test/package/staging.test.js +4 -3
- data/test/package/transpiler-defaults.test.js +127 -0
- data/test/peer-dependencies.sh +85 -0
- data/test/scripts/remove-use-strict.test.js +125 -0
- data/test/typescript/build.test.js +118 -0
- 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/tools/README.md +124 -0
- data/tools/css-modules-v9-codemod.js +179 -0
- data/tsconfig.eslint.json +16 -0
- data/tsconfig.json +38 -0
- data/yarn.lock +2704 -767
- metadata +111 -41
- data/package/babel/preset.js +0 -48
- data/package/config.js +0 -56
- data/package/env.js +0 -48
- data/package/environments/base.js +0 -171
- data/package/environments/development.js +0 -13
- data/package/environments/production.js +0 -88
- data/package/environments/test.js +0 -3
- data/package/esbuild/index.js +0 -40
- data/package/index.js +0 -40
- data/package/rules/css.js +0 -3
- data/package/rules/esbuild.js +0 -10
- data/package/rules/file.js +0 -29
- data/package/rules/raw.js +0 -5
- data/package/rules/swc.js +0 -10
- data/package/swc/index.js +0 -50
- data/package/utils/configPath.js +0 -4
- data/package/utils/defaultConfigPath.js +0 -2
- data/package/utils/getStyleRule.js +0 -40
- data/package/utils/helpers.js +0 -62
- data/package/utils/snakeToCamelCase.js +0 -5
- data/package/webpackDevServerConfig.js +0 -71
- data/test/package/rules/index.test.js +0 -16
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production environment configuration for webpack and rspack bundlers
|
|
3
|
+
* @module environments/production
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* eslint global-require: 0 */
|
|
7
|
+
/* eslint import/no-dynamic-require: 0 */
|
|
8
|
+
|
|
9
|
+
const { resolve } = require("path")
|
|
10
|
+
const { merge } = require("webpack-merge")
|
|
11
|
+
const baseConfig = require("./base")
|
|
12
|
+
const { moduleExists } = require("../utils/helpers")
|
|
13
|
+
const config = require("../config")
|
|
14
|
+
import type { Configuration as WebpackConfiguration, WebpackPluginInstance } from "webpack"
|
|
15
|
+
import type { CompressionPluginConstructor } from "./types"
|
|
16
|
+
|
|
17
|
+
const optimizationPath = resolve(
|
|
18
|
+
__dirname,
|
|
19
|
+
"..",
|
|
20
|
+
"optimization",
|
|
21
|
+
`${config.assets_bundler}.js`
|
|
22
|
+
)
|
|
23
|
+
const { getOptimization } = require(optimizationPath)
|
|
24
|
+
|
|
25
|
+
let CompressionPlugin: CompressionPluginConstructor | null = null
|
|
26
|
+
if (moduleExists("compression-webpack-plugin")) {
|
|
27
|
+
// eslint-disable-next-line global-require
|
|
28
|
+
CompressionPlugin = require("compression-webpack-plugin")
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Generate production plugins including compression
|
|
33
|
+
* @returns Array of webpack plugins for production
|
|
34
|
+
*/
|
|
35
|
+
const getPlugins = (): WebpackPluginInstance[] => {
|
|
36
|
+
const plugins: WebpackPluginInstance[] = []
|
|
37
|
+
|
|
38
|
+
if (CompressionPlugin) {
|
|
39
|
+
plugins.push(
|
|
40
|
+
new CompressionPlugin({
|
|
41
|
+
filename: "[path][base].gz[query]",
|
|
42
|
+
algorithm: "gzip",
|
|
43
|
+
test: /\.(js|css|html|json|ico|svg|eot|otf|ttf|map)$/
|
|
44
|
+
})
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if ("brotli" in process.versions) {
|
|
48
|
+
plugins.push(
|
|
49
|
+
new CompressionPlugin({
|
|
50
|
+
filename: "[path][base].br[query]",
|
|
51
|
+
algorithm: "brotliCompress",
|
|
52
|
+
test: /\.(js|css|html|json|ico|svg|eot|otf|ttf|map)$/
|
|
53
|
+
})
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return plugins
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Production configuration with optimizations and compression
|
|
63
|
+
*/
|
|
64
|
+
const productionConfig: Partial<WebpackConfiguration> = {
|
|
65
|
+
devtool: "source-map",
|
|
66
|
+
stats: "normal",
|
|
67
|
+
bail: true,
|
|
68
|
+
plugins: getPlugins(),
|
|
69
|
+
optimization: getOptimization()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (config.useContentHash === false) {
|
|
73
|
+
// eslint-disable-next-line no-console
|
|
74
|
+
console.warn(`⚠️ WARNING
|
|
75
|
+
Setting 'useContentHash' to 'false' in the production environment (specified by NODE_ENV environment variable) is not allowed!
|
|
76
|
+
Content hashes get added to the filenames regardless of setting useContentHash in 'shakapacker.yml' to false.
|
|
77
|
+
`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = merge(baseConfig, productionConfig)
|
|
@@ -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 }
|
data/package/index.d.ts
CHANGED
|
@@ -1,97 +1,3 @@
|
|
|
1
|
-
declare
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export interface Config {
|
|
6
|
-
source_path: string
|
|
7
|
-
source_entry_path: string
|
|
8
|
-
nested_entries: boolean
|
|
9
|
-
css_extract_ignore_order_warnings: boolean
|
|
10
|
-
public_root_path: string
|
|
11
|
-
public_output_path: string
|
|
12
|
-
cache_path: string
|
|
13
|
-
webpack_compile_output: boolean
|
|
14
|
-
shakapacker_precompile: boolean
|
|
15
|
-
additional_paths: string[]
|
|
16
|
-
cache_manifest: boolean
|
|
17
|
-
webpack_loader: string
|
|
18
|
-
ensure_consistent_versioning: boolean
|
|
19
|
-
compiler_strategy: string
|
|
20
|
-
useContentHash: boolean
|
|
21
|
-
compile: boolean,
|
|
22
|
-
outputPath: string
|
|
23
|
-
publicPath: string
|
|
24
|
-
publicPathWithoutCDN: string
|
|
25
|
-
manifestPath: string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface Env {
|
|
29
|
-
railsEnv: string
|
|
30
|
-
nodeEnv: string
|
|
31
|
-
isProduction: boolean
|
|
32
|
-
isDevelopment: boolean
|
|
33
|
-
runningWebpackDevServer: boolean
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
type Header = Array<{ key: string; value: string }> | Record<string, string | string[]>
|
|
37
|
-
type ServerType = 'http' | 'https' | 'spdy'
|
|
38
|
-
type WebSocketType = 'sockjs' | 'ws'
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* This has the same keys and behavior as https://webpack.js.org/configuration/dev-server/ except:
|
|
42
|
-
* 1. `hot` is replaced by `hmr`;
|
|
43
|
-
* 2. Camel-cased properties are replaced by snake-cased ones.
|
|
44
|
-
* @see {import('webpack-dev-server').Configuration}
|
|
45
|
-
*/
|
|
46
|
-
interface DevServerConfig {
|
|
47
|
-
allowed_hosts?: 'all' | 'auto' | string | string[]
|
|
48
|
-
bonjour?: boolean | Record<string, unknown> // bonjour.BonjourOptions
|
|
49
|
-
client?: Record<string, unknown> // Client
|
|
50
|
-
compress?: boolean
|
|
51
|
-
dev_middleware?: Record<string, unknown> // webpackDevMiddleware.Options
|
|
52
|
-
headers?: Header | (() => Header)
|
|
53
|
-
history_api_fallback?: boolean | Record<string, unknown> // HistoryApiFallbackOptions
|
|
54
|
-
hmr?: 'only' | boolean
|
|
55
|
-
host?: 'local-ip' | 'local-ipv4' | 'local-ipv6' | string
|
|
56
|
-
http2?: boolean
|
|
57
|
-
https?: boolean | https.ServerOptions
|
|
58
|
-
ipc?: boolean | string
|
|
59
|
-
magic_html?: boolean
|
|
60
|
-
live_reload?: boolean
|
|
61
|
-
open?: boolean | string | string[] | Record<string, unknown> | Record<string, unknown>[]
|
|
62
|
-
port?: 'auto' | string | number
|
|
63
|
-
proxy?: unknown // ProxyConfigMap | ProxyConfigArray
|
|
64
|
-
setup_exit_signals?: boolean
|
|
65
|
-
static?: boolean | string | unknown // Static | Array<string | Static>
|
|
66
|
-
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 }
|
|
69
|
-
[otherWebpackDevServerConfigKey: string]: unknown
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export const config: Config
|
|
73
|
-
export const devServer: DevServerConfig
|
|
74
|
-
export function generateWebpackConfig(extraConfig?: Configuration): Configuration
|
|
75
|
-
export const baseConfig: Configuration
|
|
76
|
-
export const env: Env
|
|
77
|
-
export const rules: RuleSetRule[]
|
|
78
|
-
export function moduleExists(packageName: string): boolean
|
|
79
|
-
export function canProcess<T = unknown>(rule: string, fn: (modulePath: string) => T): T | null
|
|
80
|
-
export const inliningCss: boolean
|
|
81
|
-
export * from 'webpack-merge'
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
declare module 'shakapacker/package/babel/preset.js' {
|
|
85
|
-
import { ConfigAPI, PluginItem, TransformOptions } from '@babel/core'
|
|
86
|
-
|
|
87
|
-
interface RequiredTransformOptions {
|
|
88
|
-
plugins: PluginItem[]
|
|
89
|
-
presets: PluginItem[]
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const defaultConfigFunc: (
|
|
93
|
-
api: ConfigAPI
|
|
94
|
-
) => TransformOptions & RequiredTransformOptions
|
|
95
|
-
|
|
96
|
-
export = defaultConfigFunc
|
|
97
|
-
}
|
|
1
|
+
declare const _default: any;
|
|
2
|
+
export = _default;
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
data/package/index.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* eslint global-require: 0 */
|
|
2
|
+
/* eslint import/no-dynamic-require: 0 */
|
|
3
|
+
|
|
4
|
+
const webpackMerge = require("webpack-merge")
|
|
5
|
+
import { resolve } from "path"
|
|
6
|
+
import { existsSync } from "fs"
|
|
7
|
+
// @ts-ignore: webpack is an optional peer dependency (using type-only import)
|
|
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")
|
|
15
|
+
|
|
16
|
+
const rulesPath = resolve(__dirname, "rules", `${config.assets_bundler}.js`)
|
|
17
|
+
const rules = require(rulesPath)
|
|
18
|
+
|
|
19
|
+
const generateWebpackConfig = (extraConfig: Configuration = {}, ...extraArgs: any[]): Configuration => {
|
|
20
|
+
if (extraArgs.length > 0) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Invalid usage: generateWebpackConfig() accepts only one configuration object.\n\n` +
|
|
23
|
+
`You passed ${extraArgs.length + 1} arguments. Only one extra config may be passed here - use webpack-merge to merge configs before passing them to Shakapacker.\n\n` +
|
|
24
|
+
`Example:\n` +
|
|
25
|
+
` const { merge } = require('webpack-merge')\n` +
|
|
26
|
+
` const mergedConfig = merge(config1, config2, config3)\n` +
|
|
27
|
+
` const finalConfig = generateWebpackConfig(mergedConfig)\n\n` +
|
|
28
|
+
`Or if using ES6:\n` +
|
|
29
|
+
` import { merge } from 'webpack-merge'\n` +
|
|
30
|
+
` const finalConfig = generateWebpackConfig(merge(config1, config2))`
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { nodeEnv } = env
|
|
35
|
+
const path = resolve(__dirname, "environments", `${nodeEnv}.js`)
|
|
36
|
+
const environmentConfig = existsSync(path) ? require(path) : baseConfig
|
|
37
|
+
|
|
38
|
+
return webpackMerge.merge({}, environmentConfig, extraConfig)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export = {
|
|
42
|
+
config, // shakapacker.yml
|
|
43
|
+
devServer,
|
|
44
|
+
generateWebpackConfig,
|
|
45
|
+
baseConfig,
|
|
46
|
+
env,
|
|
47
|
+
rules,
|
|
48
|
+
moduleExists,
|
|
49
|
+
canProcess,
|
|
50
|
+
inliningCss,
|
|
51
|
+
...webpackMerge
|
|
52
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// @ts-ignore: webpack is an optional peer dependency (using type-only import)
|
|
2
|
+
import type { LoaderDefinitionFunction } from 'webpack'
|
|
3
|
+
|
|
4
|
+
export interface ShakapackerLoaderOptions {
|
|
5
|
+
[key: string]: any
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ShakapackerLoader {
|
|
9
|
+
loader: string
|
|
10
|
+
options?: ShakapackerLoaderOptions
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type LoaderResolver = (name: string) => string
|
|
14
|
+
|
|
15
|
+
export interface LoaderConfig {
|
|
16
|
+
test: RegExp | ((value: string) => boolean)
|
|
17
|
+
use: Array<string | ShakapackerLoader | LoaderDefinitionFunction>
|
|
18
|
+
exclude?: RegExp | string | Array<string>
|
|
19
|
+
include?: RegExp | string | Array<string>
|
|
20
|
+
type?: string
|
|
21
|
+
generator?: {
|
|
22
|
+
filename?: string
|
|
23
|
+
publicPath?: string
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function resolveLoader(name: string): string
|
|
28
|
+
export function createLoader(config: LoaderConfig): LoaderConfig
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const { requireOrError } = require("../utils/requireOrError")
|
|
2
|
+
|
|
3
|
+
const TerserPlugin = requireOrError("terser-webpack-plugin")
|
|
4
|
+
const { moduleExists } = require("../utils/helpers")
|
|
5
|
+
|
|
6
|
+
const tryCssMinimizer = (): unknown | null => {
|
|
7
|
+
if (
|
|
8
|
+
moduleExists("css-loader") &&
|
|
9
|
+
moduleExists("css-minimizer-webpack-plugin")
|
|
10
|
+
) {
|
|
11
|
+
const CssMinimizerPlugin = requireOrError("css-minimizer-webpack-plugin")
|
|
12
|
+
return new CssMinimizerPlugin()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface OptimizationConfig {
|
|
19
|
+
minimizer: unknown[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const getOptimization = (): OptimizationConfig => {
|
|
23
|
+
return {
|
|
24
|
+
minimizer: [
|
|
25
|
+
tryCssMinimizer(),
|
|
26
|
+
new TerserPlugin({
|
|
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,
|
|
32
|
+
terserOptions: {
|
|
33
|
+
parse: {
|
|
34
|
+
// Let terser parse ecma 8 code but always output
|
|
35
|
+
// ES5 compliant code for older browsers
|
|
36
|
+
ecma: 8
|
|
37
|
+
},
|
|
38
|
+
compress: {
|
|
39
|
+
ecma: 5,
|
|
40
|
+
warnings: false,
|
|
41
|
+
comparisons: false
|
|
42
|
+
},
|
|
43
|
+
mangle: { safari10: true },
|
|
44
|
+
output: {
|
|
45
|
+
ecma: 5,
|
|
46
|
+
comments: false,
|
|
47
|
+
ascii_only: true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
].filter(Boolean)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export = {
|
|
56
|
+
getOptimization
|
|
57
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const { requireOrError } = require("../utils/requireOrError")
|
|
2
|
+
|
|
3
|
+
const { RspackManifestPlugin } = requireOrError("rspack-manifest-plugin")
|
|
4
|
+
const rspack = requireOrError("@rspack/core")
|
|
5
|
+
const config = require("../config")
|
|
6
|
+
const { isProduction } = require("../env")
|
|
7
|
+
const { moduleExists } = require("../utils/helpers")
|
|
8
|
+
|
|
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[] => {
|
|
25
|
+
const plugins = [
|
|
26
|
+
new rspack.EnvironmentPlugin(process.env),
|
|
27
|
+
new RspackManifestPlugin({
|
|
28
|
+
fileName: config.manifestPath.split("/").pop(), // Get just the filename
|
|
29
|
+
publicPath: config.publicPathWithoutCDN,
|
|
30
|
+
writeToFileEmit: true,
|
|
31
|
+
// rspack-manifest-plugin uses different option names than webpack-assets-manifest
|
|
32
|
+
generate: (seed: Manifest | null, files: ManifestFile[], entrypoints: Record<string, string[]>) => {
|
|
33
|
+
const manifest: Manifest = seed || {}
|
|
34
|
+
|
|
35
|
+
// Add files mapping first
|
|
36
|
+
files.forEach((file) => {
|
|
37
|
+
manifest[file.name] = file.path
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// Add entrypoints information compatible with Shakapacker expectations
|
|
41
|
+
const entrypointsManifest: Record<string, { assets: EntrypointAssets }> = {}
|
|
42
|
+
Object.entries(entrypoints).forEach(
|
|
43
|
+
([entrypointName, entrypointFiles]) => {
|
|
44
|
+
const jsFiles = entrypointFiles
|
|
45
|
+
.filter(
|
|
46
|
+
(file) => file.endsWith(".js") && !file.includes(".hot-update.")
|
|
47
|
+
)
|
|
48
|
+
.map((file) => config.publicPathWithoutCDN + file)
|
|
49
|
+
const cssFiles = entrypointFiles
|
|
50
|
+
.filter(
|
|
51
|
+
(file) =>
|
|
52
|
+
file.endsWith(".css") && !file.includes(".hot-update.")
|
|
53
|
+
)
|
|
54
|
+
.map((file) => config.publicPathWithoutCDN + file)
|
|
55
|
+
|
|
56
|
+
entrypointsManifest[entrypointName] = {
|
|
57
|
+
assets: {
|
|
58
|
+
js: jsFiles,
|
|
59
|
+
css: cssFiles
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
manifest.entrypoints = entrypointsManifest
|
|
65
|
+
|
|
66
|
+
return manifest
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
if (moduleExists("css-loader")) {
|
|
72
|
+
const hash = isProduction || config.useContentHash ? "-[contenthash:8]" : ""
|
|
73
|
+
// Use Rspack's built-in CSS extraction
|
|
74
|
+
const { CssExtractRspackPlugin } = rspack
|
|
75
|
+
plugins.push(
|
|
76
|
+
new CssExtractRspackPlugin({
|
|
77
|
+
filename: `css/[name]${hash}.css`,
|
|
78
|
+
chunkFilename: `css/[id]${hash}.css`,
|
|
79
|
+
// For projects where css ordering has been mitigated through consistent use of scoping or naming conventions,
|
|
80
|
+
// the css order warnings can be disabled by setting the ignoreOrder flag.
|
|
81
|
+
ignoreOrder: config.css_extract_ignore_order_warnings,
|
|
82
|
+
// Force writing CSS files to disk in development for Rails compatibility
|
|
83
|
+
emit: true
|
|
84
|
+
})
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Use Rspack's built-in SubresourceIntegrityPlugin
|
|
89
|
+
if (config.integrity.enabled) {
|
|
90
|
+
plugins.push(
|
|
91
|
+
new rspack.SubresourceIntegrityPlugin({
|
|
92
|
+
hashFuncNames: config.integrity.hash_functions,
|
|
93
|
+
enabled: isProduction
|
|
94
|
+
})
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return plugins
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export = {
|
|
102
|
+
getPlugins
|
|
103
|
+
}
|