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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +6 -9
- data/.github/ISSUE_TEMPLATE/feature_request.md +6 -8
- data/.github/workflows/claude-code-review.yml +4 -5
- data/.github/workflows/claude.yml +1 -2
- data/.github/workflows/dummy.yml +4 -4
- data/.github/workflows/generator.yml +9 -9
- data/.github/workflows/node.yml +11 -2
- data/.github/workflows/ruby.yml +16 -16
- data/.github/workflows/test-bundlers.yml +9 -9
- data/.gitignore +7 -0
- data/CHANGELOG.md +50 -4
- data/CLAUDE.md +6 -1
- data/CONTRIBUTING.md +0 -1
- data/Gemfile.lock +1 -1
- data/README.md +35 -14
- data/TODO.md +10 -2
- data/TODO_v9.md +13 -3
- data/bin/export-bundler-config +11 -0
- data/conductor-setup.sh +1 -1
- data/conductor.json +1 -1
- data/docs/cdn_setup.md +13 -8
- data/docs/common-upgrades.md +2 -1
- data/docs/configuration.md +630 -0
- data/docs/css-modules-export-mode.md +120 -100
- data/docs/customizing_babel_config.md +16 -16
- data/docs/deployment.md +68 -6
- data/docs/developing_shakapacker.md +6 -0
- data/docs/optional-peer-dependencies.md +9 -4
- data/docs/peer-dependencies.md +17 -6
- data/docs/precompile_hook.md +342 -0
- data/docs/react.md +57 -47
- data/docs/releasing.md +195 -0
- data/docs/rspack.md +25 -21
- data/docs/rspack_migration_guide.md +363 -8
- data/docs/sprockets.md +1 -0
- data/docs/style_loader_vs_mini_css.md +12 -12
- data/docs/subresource_integrity.md +13 -7
- data/docs/transpiler-performance.md +40 -19
- data/docs/troubleshooting.md +122 -23
- data/docs/typescript-migration.md +48 -39
- data/docs/typescript.md +12 -8
- data/docs/using_esbuild_loader.md +10 -10
- data/docs/v6_upgrade.md +33 -20
- data/docs/v7_upgrade.md +8 -6
- data/docs/v8_upgrade.md +13 -12
- data/docs/v9_upgrade.md +2 -1
- data/eslint.config.fast.js +134 -0
- data/eslint.config.js +140 -0
- data/knip.ts +54 -0
- data/lib/install/bin/export-bundler-config +11 -0
- data/lib/install/bin/shakapacker +1 -1
- data/lib/install/bin/shakapacker-dev-server +1 -1
- data/lib/install/config/shakapacker.yml +16 -5
- data/lib/shakapacker/bundler_switcher.rb +7 -0
- data/lib/shakapacker/compiler.rb +80 -0
- data/lib/shakapacker/configuration.rb +56 -2
- data/lib/shakapacker/dev_server_runner.rb +140 -1
- data/lib/shakapacker/doctor.rb +302 -57
- data/lib/shakapacker/instance.rb +8 -3
- data/lib/shakapacker/rspack_runner.rb +1 -1
- data/lib/shakapacker/runner.rb +245 -9
- data/lib/shakapacker/version.rb +1 -1
- data/lib/shakapacker/webpack_runner.rb +1 -1
- data/lib/shakapacker.rb +10 -0
- data/lib/tasks/shakapacker/doctor.rake +42 -2
- data/lib/tasks/shakapacker/export_bundler_config.rake +72 -0
- data/package/babel/preset.ts +7 -4
- data/package/config.ts +42 -30
- data/package/configExporter/cli.ts +1274 -0
- data/package/configExporter/configDocs.ts +102 -0
- data/package/configExporter/configFile.ts +520 -0
- data/package/configExporter/fileWriter.ts +96 -0
- data/package/configExporter/index.ts +13 -0
- data/package/configExporter/types.ts +70 -0
- data/package/configExporter/yamlSerializer.ts +280 -0
- data/package/dev_server.ts +1 -1
- data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +11 -5
- data/package/environments/base.ts +18 -13
- data/package/environments/development.ts +1 -1
- data/package/environments/production.ts +4 -1
- data/package/index.d.ts +50 -3
- data/package/index.d.ts.template +50 -0
- data/package/index.ts +7 -7
- data/package/loaders.d.ts +2 -2
- data/package/optimization/rspack.ts +1 -1
- data/package/plugins/rspack.ts +15 -4
- data/package/plugins/webpack.ts +7 -3
- data/package/rspack/index.ts +10 -2
- data/package/rules/raw.ts +3 -2
- data/package/rules/sass.ts +1 -1
- data/package/types/README.md +15 -13
- data/package/types/index.ts +5 -5
- data/package/types.ts +0 -1
- data/package/utils/defaultConfigPath.ts +4 -1
- data/package/utils/errorCodes.ts +129 -100
- data/package/utils/errorHelpers.ts +34 -29
- data/package/utils/getStyleRule.ts +5 -2
- data/package/utils/helpers.ts +21 -11
- data/package/utils/pathValidation.ts +43 -35
- data/package/utils/requireOrError.ts +1 -1
- data/package/utils/snakeToCamelCase.ts +1 -1
- data/package/utils/typeGuards.ts +132 -83
- data/package/utils/validateDependencies.ts +1 -1
- data/package/webpack-types.d.ts +3 -3
- data/package/webpackDevServerConfig.ts +22 -10
- data/package-lock.json +2 -2
- data/package.json +37 -28
- data/scripts/type-check-no-emit.js +1 -1
- data/test/configExporter/configFile.test.js +392 -0
- data/test/configExporter/integration.test.js +275 -0
- data/test/helpers.js +1 -1
- data/test/package/configExporter.test.js +154 -0
- data/test/package/helpers.test.js +2 -2
- data/test/package/rules/sass-version-parsing.test.js +71 -0
- data/test/package/rules/sass.test.js +2 -4
- data/test/package/rules/sass1.test.js +1 -3
- data/test/package/rules/sass16.test.js +23 -0
- data/tools/README.md +15 -5
- data/tsconfig.eslint.json +2 -9
- data/yarn.lock +1635 -1442
- metadata +29 -3
- data/.eslintignore +0 -5
data/docs/v8_upgrade.md
CHANGED
|
@@ -18,6 +18,7 @@ If you are not using CDN, then this change will have no effect on your setup.
|
|
|
18
18
|
If you are using CDN and your CDN host is static, `config.asset_host` setting in Rails will be respected during compilation and when referencing assets through view helpers.
|
|
19
19
|
|
|
20
20
|
If your host might differ, between various environments for example, you will either need to:
|
|
21
|
+
|
|
21
22
|
- Ensure the assets are specifically rebuilt for each environment (Heroku pipeline promote feature for example does not do that by default).
|
|
22
23
|
- Make sure the assets are compiled with `SHAKAPACKER_ASSET_HOST=''` ENV variable to avoid hardcording URLs in packs output.
|
|
23
24
|
|
|
@@ -131,41 +132,41 @@ The function will return the same object with less risk:
|
|
|
131
132
|
|
|
132
133
|
```js
|
|
133
134
|
// before
|
|
134
|
-
const { globalMutableWebpackConfig, merge } = require(
|
|
135
|
+
const { globalMutableWebpackConfig, merge } = require("shakapacker")
|
|
135
136
|
|
|
136
137
|
const customConfig = {
|
|
137
138
|
module: {
|
|
138
139
|
rules: [
|
|
139
140
|
{
|
|
140
|
-
test: require.resolve(
|
|
141
|
-
loader:
|
|
142
|
-
options: { exposes: [
|
|
141
|
+
test: require.resolve("jquery"),
|
|
142
|
+
loader: "expose-loader",
|
|
143
|
+
options: { exposes: ["$", "jQuery"] }
|
|
143
144
|
}
|
|
144
145
|
]
|
|
145
146
|
}
|
|
146
|
-
}
|
|
147
|
+
}
|
|
147
148
|
|
|
148
|
-
module.exports = merge(globalMutableWebpackConfig, customConfig)
|
|
149
|
+
module.exports = merge(globalMutableWebpackConfig, customConfig)
|
|
149
150
|
```
|
|
150
151
|
|
|
151
152
|
```js
|
|
152
153
|
// after
|
|
153
|
-
const { generateWebpackConfig, merge } = require(
|
|
154
|
+
const { generateWebpackConfig, merge } = require("shakapacker")
|
|
154
155
|
|
|
155
156
|
const customConfig = {
|
|
156
157
|
module: {
|
|
157
158
|
rules: [
|
|
158
159
|
{
|
|
159
|
-
test: require.resolve(
|
|
160
|
-
loader:
|
|
161
|
-
options: { exposes: [
|
|
160
|
+
test: require.resolve("jquery"),
|
|
161
|
+
loader: "expose-loader",
|
|
162
|
+
options: { exposes: ["$", "jQuery"] }
|
|
162
163
|
}
|
|
163
164
|
]
|
|
164
165
|
}
|
|
165
|
-
}
|
|
166
|
+
}
|
|
166
167
|
|
|
167
168
|
// you can also pass your config directly to the generator function to have it merged in!
|
|
168
|
-
module.exports = merge(generateWebpackConfig(), customConfig)
|
|
169
|
+
module.exports = merge(generateWebpackConfig(), customConfig)
|
|
169
170
|
```
|
|
170
171
|
|
|
171
172
|
## `additional_paths` are now stripped just like with `source_path`
|
data/docs/v9_upgrade.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
This guide outlines new features, breaking changes, and migration steps for upgrading from Shakapacker v8 to v9.
|
|
4
4
|
|
|
5
|
+
**📖 For detailed configuration options, see the [Configuration Guide](./configuration.md)**
|
|
6
|
+
|
|
5
7
|
> **⚠️ Important for v9.1.0 Users:** If you're upgrading to v9.1.0 or later, please note the [SWC Configuration Breaking Change](#swc-loose-mode-breaking-change-v910) below. This affects users who previously configured SWC in v9.0.0.
|
|
6
8
|
|
|
7
9
|
## New Features
|
|
@@ -130,7 +132,6 @@ import * as styles from './Component.module.css';
|
|
|
130
132
|
**Migration Options:**
|
|
131
133
|
|
|
132
134
|
1. **Update your code** (Recommended):
|
|
133
|
-
|
|
134
135
|
- JavaScript: Change to named imports (`import { className }`)
|
|
135
136
|
- TypeScript: Change to namespace imports (`import * as styles`)
|
|
136
137
|
- Kebab-case class names are automatically converted to camelCase
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Fast ESLint config for quick development feedback
|
|
2
|
+
// Skips type-aware rules that require TypeScript compilation
|
|
3
|
+
|
|
4
|
+
const { FlatCompat } = require("@eslint/eslintrc")
|
|
5
|
+
const js = require("@eslint/js")
|
|
6
|
+
const typescriptParser = require("@typescript-eslint/parser")
|
|
7
|
+
const typescriptPlugin = require("@typescript-eslint/eslint-plugin")
|
|
8
|
+
const jestPlugin = require("eslint-plugin-jest")
|
|
9
|
+
const prettierConfig = require("eslint-config-prettier")
|
|
10
|
+
|
|
11
|
+
const compat = new FlatCompat({
|
|
12
|
+
baseDirectory: __dirname,
|
|
13
|
+
recommendedConfig: js.configs.recommended
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
module.exports = [
|
|
17
|
+
// Global ignores (replaces .eslintignore)
|
|
18
|
+
{
|
|
19
|
+
ignores: [
|
|
20
|
+
"lib/**",
|
|
21
|
+
"**/node_modules/**",
|
|
22
|
+
"vendor/**",
|
|
23
|
+
"spec/**",
|
|
24
|
+
"package/**" // TODO: Remove after issue #644 is resolved (lints package/ TS source files)
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
// Base config for all JS files
|
|
29
|
+
...compat.extends("airbnb"),
|
|
30
|
+
{
|
|
31
|
+
languageOptions: {
|
|
32
|
+
ecmaVersion: 2020,
|
|
33
|
+
sourceType: "module",
|
|
34
|
+
globals: {
|
|
35
|
+
// Browser globals
|
|
36
|
+
window: "readonly",
|
|
37
|
+
document: "readonly",
|
|
38
|
+
navigator: "readonly",
|
|
39
|
+
console: "readonly",
|
|
40
|
+
// Node globals
|
|
41
|
+
process: "readonly",
|
|
42
|
+
__dirname: "readonly",
|
|
43
|
+
__filename: "readonly",
|
|
44
|
+
module: "readonly",
|
|
45
|
+
require: "readonly",
|
|
46
|
+
exports: "readonly",
|
|
47
|
+
global: "readonly",
|
|
48
|
+
Buffer: "readonly"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
rules: {
|
|
52
|
+
// Webpack handles module resolution, not ESLint
|
|
53
|
+
"import/no-unresolved": "off",
|
|
54
|
+
// Allow importing devDependencies in config/test files
|
|
55
|
+
"import/no-extraneous-dependencies": "off",
|
|
56
|
+
// TypeScript handles extensions, not needed for JS imports
|
|
57
|
+
"import/extensions": "off",
|
|
58
|
+
indent: ["error", 2]
|
|
59
|
+
},
|
|
60
|
+
settings: {
|
|
61
|
+
react: {
|
|
62
|
+
// Suppress "react package not installed" warning
|
|
63
|
+
// This project doesn't use React but airbnb config requires react-plugin
|
|
64
|
+
version: "999.999.999"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// Jest test files
|
|
70
|
+
{
|
|
71
|
+
files: ["test/**"],
|
|
72
|
+
plugins: {
|
|
73
|
+
jest: jestPlugin
|
|
74
|
+
},
|
|
75
|
+
languageOptions: {
|
|
76
|
+
globals: {
|
|
77
|
+
...jestPlugin.environments.globals.globals
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
rules: {
|
|
81
|
+
...jestPlugin.configs.recommended.rules,
|
|
82
|
+
...jestPlugin.configs.style.rules,
|
|
83
|
+
"global-require": "off",
|
|
84
|
+
"jest/prefer-called-with": "error",
|
|
85
|
+
"jest/no-conditional-in-test": "error",
|
|
86
|
+
"jest/no-test-return-statement": "error",
|
|
87
|
+
"jest/prefer-expect-resolves": "error",
|
|
88
|
+
"jest/require-to-throw-message": "error",
|
|
89
|
+
"jest/require-top-level-describe": "error",
|
|
90
|
+
"jest/prefer-hooks-on-top": "error",
|
|
91
|
+
"jest/prefer-lowercase-title": [
|
|
92
|
+
"error",
|
|
93
|
+
{ ignoreTopLevelDescribe: true }
|
|
94
|
+
],
|
|
95
|
+
"jest/prefer-spy-on": "error",
|
|
96
|
+
"jest/prefer-strict-equal": "error",
|
|
97
|
+
"jest/prefer-todo": "error"
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
// TypeScript files - fast mode without type-aware linting
|
|
102
|
+
{
|
|
103
|
+
files: ["**/*.ts", "**/*.tsx"],
|
|
104
|
+
languageOptions: {
|
|
105
|
+
parser: typescriptParser,
|
|
106
|
+
parserOptions: {
|
|
107
|
+
// No project specified - disables type-aware linting
|
|
108
|
+
ecmaVersion: 2020,
|
|
109
|
+
sourceType: "module"
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
plugins: {
|
|
113
|
+
"@typescript-eslint": typescriptPlugin
|
|
114
|
+
},
|
|
115
|
+
rules: {
|
|
116
|
+
...typescriptPlugin.configs.recommended.rules,
|
|
117
|
+
// Same rules as main config minus type-aware ones
|
|
118
|
+
"import/no-unresolved": "off",
|
|
119
|
+
"import/no-extraneous-dependencies": "off",
|
|
120
|
+
"import/extensions": "off",
|
|
121
|
+
"no-use-before-define": "off",
|
|
122
|
+
"@typescript-eslint/no-use-before-define": ["error"],
|
|
123
|
+
"@typescript-eslint/no-unused-vars": [
|
|
124
|
+
"error",
|
|
125
|
+
{ argsIgnorePattern: "^_" }
|
|
126
|
+
],
|
|
127
|
+
"@typescript-eslint/no-explicit-any": "error",
|
|
128
|
+
"@typescript-eslint/explicit-module-boundary-types": "off"
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// Prettier config must be last to override other configs
|
|
133
|
+
prettierConfig
|
|
134
|
+
]
|
data/eslint.config.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
const { FlatCompat } = require("@eslint/eslintrc")
|
|
2
|
+
const js = require("@eslint/js")
|
|
3
|
+
const typescriptParser = require("@typescript-eslint/parser")
|
|
4
|
+
const typescriptPlugin = require("@typescript-eslint/eslint-plugin")
|
|
5
|
+
const jestPlugin = require("eslint-plugin-jest")
|
|
6
|
+
const prettierConfig = require("eslint-config-prettier")
|
|
7
|
+
|
|
8
|
+
const compat = new FlatCompat({
|
|
9
|
+
baseDirectory: __dirname,
|
|
10
|
+
recommendedConfig: js.configs.recommended
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
module.exports = [
|
|
14
|
+
// Global ignores (replaces .eslintignore)
|
|
15
|
+
{
|
|
16
|
+
ignores: [
|
|
17
|
+
"lib/**",
|
|
18
|
+
"**/node_modules/**",
|
|
19
|
+
"vendor/**",
|
|
20
|
+
"spec/**",
|
|
21
|
+
"package/**" // TODO: Remove after issue #644 is resolved (lints package/ TS source files)
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
// Base config for all JS files
|
|
26
|
+
...compat.extends("airbnb"),
|
|
27
|
+
{
|
|
28
|
+
languageOptions: {
|
|
29
|
+
ecmaVersion: 2020,
|
|
30
|
+
sourceType: "module",
|
|
31
|
+
globals: {
|
|
32
|
+
// Browser globals
|
|
33
|
+
window: "readonly",
|
|
34
|
+
document: "readonly",
|
|
35
|
+
navigator: "readonly",
|
|
36
|
+
console: "readonly",
|
|
37
|
+
// Node globals
|
|
38
|
+
process: "readonly",
|
|
39
|
+
__dirname: "readonly",
|
|
40
|
+
__filename: "readonly",
|
|
41
|
+
module: "readonly",
|
|
42
|
+
require: "readonly",
|
|
43
|
+
exports: "readonly",
|
|
44
|
+
global: "readonly",
|
|
45
|
+
Buffer: "readonly"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
rules: {
|
|
49
|
+
// Webpack handles module resolution, not ESLint
|
|
50
|
+
"import/no-unresolved": "off",
|
|
51
|
+
// Allow importing devDependencies in config/test files
|
|
52
|
+
"import/no-extraneous-dependencies": "off",
|
|
53
|
+
// TypeScript handles extensions, not needed for JS imports
|
|
54
|
+
"import/extensions": "off",
|
|
55
|
+
indent: ["error", 2]
|
|
56
|
+
},
|
|
57
|
+
settings: {
|
|
58
|
+
react: {
|
|
59
|
+
// Suppress "react package not installed" warning
|
|
60
|
+
// This project doesn't use React but airbnb config requires react-plugin
|
|
61
|
+
version: "999.999.999"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// Jest test files
|
|
67
|
+
{
|
|
68
|
+
files: ["test/**"],
|
|
69
|
+
plugins: {
|
|
70
|
+
jest: jestPlugin
|
|
71
|
+
},
|
|
72
|
+
languageOptions: {
|
|
73
|
+
globals: {
|
|
74
|
+
...jestPlugin.environments.globals.globals
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
rules: {
|
|
78
|
+
...jestPlugin.configs.recommended.rules,
|
|
79
|
+
...jestPlugin.configs.style.rules,
|
|
80
|
+
"global-require": "off",
|
|
81
|
+
"jest/prefer-called-with": "error",
|
|
82
|
+
"jest/no-conditional-in-test": "error",
|
|
83
|
+
"jest/no-test-return-statement": "error",
|
|
84
|
+
"jest/prefer-expect-resolves": "error",
|
|
85
|
+
"jest/require-to-throw-message": "error",
|
|
86
|
+
"jest/require-top-level-describe": "error",
|
|
87
|
+
"jest/prefer-hooks-on-top": "error",
|
|
88
|
+
"jest/prefer-lowercase-title": [
|
|
89
|
+
"error",
|
|
90
|
+
{ ignoreTopLevelDescribe: true }
|
|
91
|
+
],
|
|
92
|
+
"jest/prefer-spy-on": "error",
|
|
93
|
+
"jest/prefer-strict-equal": "error",
|
|
94
|
+
"jest/prefer-todo": "error"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
// TypeScript files
|
|
99
|
+
{
|
|
100
|
+
files: ["**/*.ts", "**/*.tsx"],
|
|
101
|
+
languageOptions: {
|
|
102
|
+
parser: typescriptParser,
|
|
103
|
+
parserOptions: {
|
|
104
|
+
// Enables type-aware linting for better type safety
|
|
105
|
+
// Note: This can slow down linting on large codebases
|
|
106
|
+
// Consider using --cache flag with ESLint if performance degrades
|
|
107
|
+
project: "./tsconfig.eslint.json",
|
|
108
|
+
tsconfigRootDir: __dirname
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
plugins: {
|
|
112
|
+
"@typescript-eslint": typescriptPlugin
|
|
113
|
+
},
|
|
114
|
+
rules: {
|
|
115
|
+
...typescriptPlugin.configs.recommended.rules,
|
|
116
|
+
...typescriptPlugin.configs["recommended-requiring-type-checking"].rules,
|
|
117
|
+
// TypeScript compiler handles module resolution
|
|
118
|
+
"import/no-unresolved": "off",
|
|
119
|
+
// Allow importing devDependencies in TypeScript files
|
|
120
|
+
"import/no-extraneous-dependencies": "off",
|
|
121
|
+
// TypeScript handles file extensions via moduleResolution
|
|
122
|
+
"import/extensions": "off",
|
|
123
|
+
// Disable base rule in favor of TypeScript version
|
|
124
|
+
"no-use-before-define": "off",
|
|
125
|
+
"@typescript-eslint/no-use-before-define": ["error"],
|
|
126
|
+
// Allow unused vars if they start with underscore (convention for ignored params)
|
|
127
|
+
"@typescript-eslint/no-unused-vars": [
|
|
128
|
+
"error",
|
|
129
|
+
{ argsIgnorePattern: "^_" }
|
|
130
|
+
],
|
|
131
|
+
// Strict: no 'any' types allowed - use 'unknown' or specific types instead
|
|
132
|
+
"@typescript-eslint/no-explicit-any": "error",
|
|
133
|
+
// Allow implicit return types - TypeScript can infer them
|
|
134
|
+
"@typescript-eslint/explicit-module-boundary-types": "off"
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
// Prettier config must be last to override other configs
|
|
139
|
+
prettierConfig
|
|
140
|
+
]
|
data/knip.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { KnipConfig } from "knip"
|
|
2
|
+
|
|
3
|
+
const config: KnipConfig = {
|
|
4
|
+
project: ["package/**/*.{ts,js}", "test/**/*.{ts,js}", "scripts/**/*.js"],
|
|
5
|
+
ignore: [
|
|
6
|
+
"package/**/*.d.ts",
|
|
7
|
+
"package/**/*.js",
|
|
8
|
+
"package/**/*.js.map",
|
|
9
|
+
"package/**/*.d.ts.map",
|
|
10
|
+
"test/fixtures/**",
|
|
11
|
+
"test/helpers.js", // Test utility file used by jest
|
|
12
|
+
"spec/**",
|
|
13
|
+
"gemfiles/**"
|
|
14
|
+
],
|
|
15
|
+
ignoreBinaries: ["sed"],
|
|
16
|
+
ignoreDependencies: [
|
|
17
|
+
// These are peer dependencies that may not be directly imported
|
|
18
|
+
"@babel/core",
|
|
19
|
+
"@types/babel__core",
|
|
20
|
+
"@types/webpack",
|
|
21
|
+
"webpack-dev-server",
|
|
22
|
+
// Test/build tooling
|
|
23
|
+
"memory-fs",
|
|
24
|
+
"thenify",
|
|
25
|
+
// Used in type tests but not directly imported
|
|
26
|
+
"@rspack/plugin-react-refresh",
|
|
27
|
+
// CLI tools used by developers
|
|
28
|
+
"@rspack/cli",
|
|
29
|
+
"webpack-cli",
|
|
30
|
+
"husky",
|
|
31
|
+
// Optional dependencies used in webpack/rspack configs
|
|
32
|
+
"mini-css-extract-plugin",
|
|
33
|
+
"webpack-assets-manifest",
|
|
34
|
+
"webpack-subresource-integrity",
|
|
35
|
+
"rspack-manifest-plugin",
|
|
36
|
+
"sass-loader",
|
|
37
|
+
// Package merger utility
|
|
38
|
+
"@types/webpack-merge",
|
|
39
|
+
// Optional runtime dependencies
|
|
40
|
+
"ts-node",
|
|
41
|
+
"@pmmmwh/react-refresh-webpack-plugin",
|
|
42
|
+
// Optional peer dependencies referenced in code
|
|
43
|
+
"@rspack/core",
|
|
44
|
+
"@swc/core",
|
|
45
|
+
"babel-loader",
|
|
46
|
+
"compression-webpack-plugin",
|
|
47
|
+
"css-loader",
|
|
48
|
+
"esbuild-loader",
|
|
49
|
+
"swc-loader",
|
|
50
|
+
"webpack"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default config
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Minimal shim - all logic is in the TypeScript module
|
|
4
|
+
const { run } = require("shakapacker/configExporter")
|
|
5
|
+
|
|
6
|
+
run(process.argv.slice(2))
|
|
7
|
+
.then((exitCode) => process.exit(exitCode))
|
|
8
|
+
.catch((error) => {
|
|
9
|
+
console.error(error.message)
|
|
10
|
+
process.exit(1)
|
|
11
|
+
})
|
data/lib/install/bin/shakapacker
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
3
|
ENV["RAILS_ENV"] ||= "development"
|
|
4
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("
|
|
4
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
|
5
5
|
ENV["APP_ROOT"] ||= File.expand_path("..", __dir__)
|
|
6
6
|
|
|
7
7
|
require "bundler/setup"
|
|
@@ -43,11 +43,22 @@ default: &default
|
|
|
43
43
|
# Select JavaScript transpiler to use
|
|
44
44
|
# Available options: 'swc' (default, 20x faster), 'babel', or 'esbuild'
|
|
45
45
|
# Note: When using rspack, swc is used automatically regardless of this setting
|
|
46
|
-
javascript_transpiler:
|
|
46
|
+
javascript_transpiler: "swc"
|
|
47
47
|
|
|
48
48
|
# Select assets bundler to use
|
|
49
49
|
# Available options: 'webpack' (default) or 'rspack'
|
|
50
|
-
assets_bundler:
|
|
50
|
+
assets_bundler: "webpack"
|
|
51
|
+
|
|
52
|
+
# Path to the directory containing webpack/rspack config files
|
|
53
|
+
# Defaults to 'config/webpack' for webpack or 'config/rspack' for rspack
|
|
54
|
+
# Use '.' to specify the root directory of your project
|
|
55
|
+
# assets_bundler_config_path: config/webpack
|
|
56
|
+
|
|
57
|
+
# Hook to run before webpack compilation (e.g., for generating dynamic entry points)
|
|
58
|
+
# SECURITY: Only reference trusted scripts within your project. The hook command will be
|
|
59
|
+
# validated to ensure it points to a file within the project root.
|
|
60
|
+
# Example: precompile_hook: 'bin/shakapacker-precompile-hook'
|
|
61
|
+
# precompile_hook: ~
|
|
51
62
|
|
|
52
63
|
# Raises an error if there is a mismatch in the shakapacker gem and npm package being used
|
|
53
64
|
ensure_consistent_versioning: true
|
|
@@ -112,14 +123,14 @@ development:
|
|
|
112
123
|
# Should we use gzip compression?
|
|
113
124
|
compress: true
|
|
114
125
|
# Note that apps that do not check the host are vulnerable to DNS rebinding attacks
|
|
115
|
-
allowed_hosts:
|
|
126
|
+
allowed_hosts: "auto"
|
|
116
127
|
# Shows progress and colorizes output of bin/shakapacker[-dev-server]
|
|
117
128
|
pretty: true
|
|
118
129
|
headers:
|
|
119
|
-
|
|
130
|
+
"Access-Control-Allow-Origin": "*"
|
|
120
131
|
static:
|
|
121
132
|
watch:
|
|
122
|
-
ignored:
|
|
133
|
+
ignored: "**/node_modules/**"
|
|
123
134
|
|
|
124
135
|
test:
|
|
125
136
|
<<: *default
|
|
@@ -241,6 +241,13 @@ module Shakapacker
|
|
|
241
241
|
raise "Failed to install prod dependencies"
|
|
242
242
|
end
|
|
243
243
|
end
|
|
244
|
+
|
|
245
|
+
# Run a full install to ensure optional dependencies (like native bindings) are properly resolved
|
|
246
|
+
# This is especially important for packages like @rspack/core that use platform-specific native modules
|
|
247
|
+
unless package_json.manager.install
|
|
248
|
+
puts "❌ Failed to run full install to resolve optional dependencies"
|
|
249
|
+
raise "Failed to run full install"
|
|
250
|
+
end
|
|
244
251
|
end
|
|
245
252
|
|
|
246
253
|
def get_package_json
|
data/lib/shakapacker/compiler.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "open3"
|
|
2
2
|
require "fileutils"
|
|
3
|
+
require "shellwords"
|
|
3
4
|
|
|
4
5
|
require_relative "compiler_strategy"
|
|
5
6
|
|
|
@@ -26,6 +27,7 @@ class Shakapacker::Compiler
|
|
|
26
27
|
true
|
|
27
28
|
else
|
|
28
29
|
acquire_ipc_lock do
|
|
30
|
+
run_precompile_hook if config.precompile_hook
|
|
29
31
|
run_webpack.tap do |success|
|
|
30
32
|
after_compile_hook
|
|
31
33
|
end
|
|
@@ -78,6 +80,84 @@ class Shakapacker::Compiler
|
|
|
78
80
|
/ruby/.match?(first_line) ? RbConfig.ruby : ""
|
|
79
81
|
end
|
|
80
82
|
|
|
83
|
+
def run_precompile_hook
|
|
84
|
+
hook_command = config.precompile_hook
|
|
85
|
+
hook_spec = validate_precompile_hook(hook_command)
|
|
86
|
+
|
|
87
|
+
logger.info "Running precompile hook: #{hook_command}"
|
|
88
|
+
|
|
89
|
+
runtime_env = webpack_env.merge(hook_spec[:env])
|
|
90
|
+
stdout, stderr, status = Open3.capture3(
|
|
91
|
+
runtime_env,
|
|
92
|
+
hook_spec[:executable],
|
|
93
|
+
*hook_spec[:args],
|
|
94
|
+
chdir: File.expand_path(config.root_path)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if status.success?
|
|
98
|
+
logger.info "Precompile hook completed successfully"
|
|
99
|
+
logger.info stdout unless stdout.empty?
|
|
100
|
+
logger.warn stderr unless stderr.empty?
|
|
101
|
+
else
|
|
102
|
+
non_empty_streams = [stdout, stderr].delete_if(&:empty?)
|
|
103
|
+
logger.error "\nPRECOMPILE HOOK FAILED:\nEXIT STATUS: #{status.exitstatus}\nCOMMAND: #{hook_command}\nOUTPUTS:\n#{non_empty_streams.join("\n\n")}"
|
|
104
|
+
logger.error "\nTo fix this:"
|
|
105
|
+
logger.error " 1. Check that the hook script exists and is executable"
|
|
106
|
+
logger.error " 2. Test the hook command manually: #{hook_command}"
|
|
107
|
+
logger.error " 3. Review the error output above for details"
|
|
108
|
+
logger.error " 4. You can disable the hook temporarily by commenting out 'precompile_hook' in shakapacker.yml"
|
|
109
|
+
raise "Precompile hook '#{hook_command}' failed with exit status #{status.exitstatus}"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def validate_precompile_hook(hook_command)
|
|
114
|
+
hook_tokens = begin
|
|
115
|
+
Shellwords.shellsplit(hook_command)
|
|
116
|
+
rescue ArgumentError => e
|
|
117
|
+
raise "Shakapacker configuration error: Invalid precompile_hook command syntax: #{e.message}. Check for unmatched quotes in: #{hook_command}"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
env_assignments = {}
|
|
121
|
+
while hook_tokens.first&.match?(/\A[A-Za-z_][A-Za-z0-9_]*=/)
|
|
122
|
+
key, value = hook_tokens.shift.split("=", 2)
|
|
123
|
+
env_assignments[key] = value
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
executable = hook_tokens.shift
|
|
127
|
+
if executable.nil? || executable.empty?
|
|
128
|
+
raise "Shakapacker configuration error: precompile_hook must include an executable command. Got: #{hook_command}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
executable_path = config.root_path.join(executable)
|
|
132
|
+
|
|
133
|
+
# Security: Resolve symlinks and verify the hook points to a file within the project
|
|
134
|
+
# This prevents symlink bypass attacks and path traversal attacks
|
|
135
|
+
begin
|
|
136
|
+
resolved_path = executable_path.realpath
|
|
137
|
+
resolved_root = config.root_path.realpath
|
|
138
|
+
rescue Errno::ENOENT
|
|
139
|
+
# If file doesn't exist, use cleanpath for basic validation
|
|
140
|
+
resolved_path = executable_path.cleanpath
|
|
141
|
+
resolved_root = config.root_path.cleanpath
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Verify path is within project root with proper separator check
|
|
145
|
+
# Using File::SEPARATOR prevents partial path matches (e.g., /project vs /project-evil)
|
|
146
|
+
unless resolved_path.to_s.start_with?(resolved_root.to_s + File::SEPARATOR)
|
|
147
|
+
raise "Security Error: precompile_hook must reference a script within the project root. " \
|
|
148
|
+
"Got: #{hook_command} (resolved to: #{resolved_path})"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Warn if the executable doesn't exist within the project
|
|
152
|
+
unless File.exist?(executable_path)
|
|
153
|
+
logger.warn "⚠️ Warning: precompile_hook executable not found: #{executable_path}"
|
|
154
|
+
logger.warn " The hook command is configured but the script does not exist within the project root."
|
|
155
|
+
logger.warn " Please ensure the script exists or remove 'precompile_hook' from your shakapacker.yml configuration."
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
{ env: env_assignments, executable: executable, args: hook_tokens }
|
|
159
|
+
end
|
|
160
|
+
|
|
81
161
|
def run_webpack
|
|
82
162
|
logger.info "Compiling..."
|
|
83
163
|
|