shakapacker 9.0.0.beta.4 → 9.0.0.beta.6

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintignore +1 -0
  3. data/.github/workflows/claude-code-review.yml +1 -1
  4. data/.github/workflows/dummy.yml +4 -0
  5. data/.github/workflows/generator.yml +7 -0
  6. data/.github/workflows/node.yml +22 -0
  7. data/.github/workflows/ruby.yml +11 -0
  8. data/.github/workflows/test-bundlers.yml +27 -9
  9. data/.gitignore +20 -0
  10. data/.yalcignore +26 -0
  11. data/CHANGELOG.md +58 -40
  12. data/CONTRIBUTING.md +64 -0
  13. data/Gemfile.lock +1 -1
  14. data/README.md +80 -1
  15. data/docs/optional-peer-dependencies.md +198 -0
  16. data/docs/typescript.md +99 -0
  17. data/docs/v9_upgrade.md +79 -2
  18. data/lib/install/template.rb +8 -1
  19. data/lib/shakapacker/configuration.rb +58 -1
  20. data/lib/shakapacker/doctor.rb +751 -0
  21. data/lib/shakapacker/swc_migrator.rb +292 -0
  22. data/lib/shakapacker/version.rb +1 -1
  23. data/lib/shakapacker.rb +1 -0
  24. data/lib/tasks/shakapacker/doctor.rake +8 -0
  25. data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
  26. data/lib/tasks/shakapacker.rake +1 -0
  27. data/package/config.ts +162 -0
  28. data/package/{dev_server.js → dev_server.ts} +8 -5
  29. data/package/env.ts +67 -0
  30. data/package/environments/base.js +94 -117
  31. data/package/environments/base.ts +138 -0
  32. data/package/index.d.ts +3 -150
  33. data/package/{index.js → index.ts} +18 -8
  34. data/package/loaders.d.ts +28 -0
  35. data/package/types.ts +108 -0
  36. data/package/utils/configPath.ts +6 -0
  37. data/package/utils/{debug.js → debug.ts} +7 -7
  38. data/package/utils/defaultConfigPath.ts +4 -0
  39. data/package/utils/errorHelpers.ts +77 -0
  40. data/package/utils/{getStyleRule.js → getStyleRule.ts} +17 -20
  41. data/package/utils/helpers.ts +85 -0
  42. data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
  43. data/package/utils/{requireOrError.js → requireOrError.ts} +2 -2
  44. data/package/utils/snakeToCamelCase.ts +5 -0
  45. data/package/utils/typeGuards.ts +228 -0
  46. data/package/utils/{validateDependencies.js → validateDependencies.ts} +4 -4
  47. data/package/webpack-types.d.ts +33 -0
  48. data/package/webpackDevServerConfig.ts +117 -0
  49. data/package.json +112 -4
  50. data/test/peer-dependencies.sh +85 -0
  51. data/test/typescript/build.test.js +117 -0
  52. data/tsconfig.json +39 -0
  53. data/yarn.lock +1 -1
  54. metadata +34 -17
  55. data/package/config.js +0 -80
  56. data/package/env.js +0 -48
  57. data/package/utils/configPath.js +0 -4
  58. data/package/utils/defaultConfigPath.js +0 -2
  59. data/package/utils/helpers.js +0 -127
  60. data/package/utils/snakeToCamelCase.js +0 -5
  61. data/package/utils/validateCssModulesConfig.js +0 -91
  62. data/package/webpackDevServerConfig.js +0 -73
@@ -1,126 +1,103 @@
1
+ "use strict";
1
2
  /* eslint global-require: 0 */
2
3
  /* eslint import/no-dynamic-require: 0 */
3
-
4
- const { basename, dirname, join, relative, resolve } = require("path")
5
- const { existsSync, readdirSync } = require("fs")
6
- const extname = require("path-complete-extname")
7
- const config = require("../config")
8
- const { isProduction } = require("../env")
9
-
10
- const pluginsPath = resolve(
11
- __dirname,
12
- "..",
13
- "plugins",
14
- `${config.assets_bundler}.js`
15
- )
16
- const { getPlugins } = require(pluginsPath)
17
- const rulesPath = resolve(
18
- __dirname,
19
- "..",
20
- "rules",
21
- `${config.assets_bundler}.js`
22
- )
23
- const rules = require(rulesPath)
24
-
4
+ const { basename, dirname, join, relative, resolve } = require("path");
5
+ const { existsSync, readdirSync } = require("fs");
6
+ const extname = require("path-complete-extname");
7
+ const config = require("../config");
8
+ const { isProduction } = require("../env");
9
+ const pluginsPath = resolve(__dirname, "..", "plugins", `${config.assets_bundler}.js`);
10
+ const { getPlugins } = require(pluginsPath);
11
+ const rulesPath = resolve(__dirname, "..", "rules", `${config.assets_bundler}.js`);
12
+ const rules = require(rulesPath);
25
13
  // Don't use contentHash except for production for performance
26
14
  // https://webpack.js.org/guides/build-performance/#avoid-production-specific-tooling
27
- const hash = isProduction || config.useContentHash ? "-[contenthash]" : ""
28
-
15
+ const hash = isProduction || config.useContentHash ? "-[contenthash]" : "";
29
16
  const getFilesInDirectory = (dir, includeNested) => {
30
- if (!existsSync(dir)) {
31
- return []
32
- }
33
-
34
- return readdirSync(dir, { withFileTypes: true }).flatMap((dirent) => {
35
- const filePath = join(dir, dirent.name)
36
-
37
- if (dirent.isDirectory() && includeNested) {
38
- return getFilesInDirectory(filePath, includeNested)
17
+ if (!existsSync(dir)) {
18
+ return [];
39
19
  }
40
- if (dirent.isFile()) {
41
- return filePath
42
- }
43
- return []
44
- })
45
- }
46
-
20
+ return readdirSync(dir, { withFileTypes: true }).flatMap((dirent) => {
21
+ const filePath = join(dir, dirent.name);
22
+ if (dirent.isDirectory() && includeNested) {
23
+ return getFilesInDirectory(filePath, includeNested);
24
+ }
25
+ if (dirent.isFile()) {
26
+ return filePath;
27
+ }
28
+ return [];
29
+ });
30
+ };
47
31
  const getEntryObject = () => {
48
- const entries = {}
49
- const rootPath = join(config.source_path, config.source_entry_path)
50
- if (config.source_entry_path === "/" && config.nested_entries) {
51
- throw new Error(
52
- "Your shakapacker config specified using a source_entry_path of '/' with 'nested_entries' == " +
53
- "'true'. Doing this would result in packs for every one of your source files"
54
- )
55
- }
56
-
57
- getFilesInDirectory(rootPath, config.nested_entries).forEach((path) => {
58
- const namespace = relative(join(rootPath), dirname(path))
59
- const name = join(namespace, basename(path, extname(path)))
60
- let assetPaths = resolve(path)
61
-
62
- // Allows for multiple filetypes per entry (https://webpack.js.org/guides/entry-advanced/)
63
- // Transforms the config object value to an array with all values under the same name
64
- let previousPaths = entries[name]
65
- if (previousPaths) {
66
- previousPaths = Array.isArray(previousPaths)
67
- ? previousPaths
68
- : [previousPaths]
69
- previousPaths.push(assetPaths)
70
- assetPaths = previousPaths
32
+ const entries = {};
33
+ const rootPath = join(config.source_path, config.source_entry_path);
34
+ if (config.source_entry_path === "/" && config.nested_entries) {
35
+ throw new Error(`Invalid Shakapacker configuration detected!\n\n` +
36
+ `You have set source_entry_path to '/' with nested_entries enabled.\n` +
37
+ `This would create webpack entry points for EVERY file in your source directory,\n` +
38
+ `which would severely impact build performance.\n\n` +
39
+ `To fix this issue, either:\n` +
40
+ `1. Set 'nested_entries: false' in your shakapacker.yml\n` +
41
+ `2. Change 'source_entry_path' to a specific subdirectory (e.g., 'packs')\n` +
42
+ `3. Or use both options for better organization of your entry points`);
71
43
  }
72
-
73
- entries[name] = assetPaths
74
- })
75
-
76
- return entries
77
- }
78
-
44
+ getFilesInDirectory(rootPath, config.nested_entries).forEach((path) => {
45
+ const namespace = relative(join(rootPath), dirname(path));
46
+ const name = join(namespace, basename(path, extname(path)));
47
+ const assetPath = resolve(path);
48
+ // Allows for multiple filetypes per entry (https://webpack.js.org/guides/entry-advanced/)
49
+ // Transforms the config object value to an array with all values under the same name
50
+ const previousPaths = entries[name];
51
+ if (previousPaths) {
52
+ const pathArray = Array.isArray(previousPaths)
53
+ ? previousPaths
54
+ : [previousPaths];
55
+ pathArray.push(assetPath);
56
+ entries[name] = pathArray;
57
+ }
58
+ else {
59
+ entries[name] = assetPath;
60
+ }
61
+ });
62
+ return entries;
63
+ };
79
64
  const getModulePaths = () => {
80
- const result = [resolve(config.source_path)]
81
-
82
- if (config.additional_paths) {
83
- config.additional_paths.forEach((path) => result.push(resolve(path)))
84
- }
85
- result.push("node_modules")
86
-
87
- return result
88
- }
89
-
90
- module.exports = {
91
- mode: "production",
92
- output: {
93
- filename: `js/[name]${hash}.js`,
94
- chunkFilename: `js/[name]${hash}.chunk.js`,
95
-
96
- // https://webpack.js.org/configuration/output/#outputhotupdatechunkfilename
97
- hotUpdateChunkFilename: "js/[id].[fullhash].hot-update.js",
98
- path: config.outputPath,
99
- publicPath: config.publicPath,
100
-
101
- // This is required for SRI to work.
102
- crossOriginLoading: config.integrity.enabled
103
- ? config.integrity.cross_origin
104
- : false
105
- },
106
- entry: getEntryObject(),
107
- resolve: {
108
- extensions: [".js", ".jsx", ".mjs", ".ts", ".tsx", ".coffee"],
109
- modules: getModulePaths()
110
- },
111
-
112
- plugins: getPlugins(),
113
-
114
- resolveLoader: {
115
- modules: ["node_modules"]
116
- },
117
-
118
- optimization: {
119
- splitChunks: { chunks: "all" },
120
- runtimeChunk: "single"
121
- },
122
-
123
- module: {
124
- rules
125
- }
126
- }
65
+ const result = [resolve(config.source_path)];
66
+ if (config.additional_paths) {
67
+ config.additional_paths.forEach((path) => result.push(resolve(path)));
68
+ }
69
+ result.push("node_modules");
70
+ return result;
71
+ };
72
+ const baseConfig = {
73
+ mode: "production",
74
+ output: {
75
+ filename: `js/[name]${hash}.js`,
76
+ chunkFilename: `js/[name]${hash}.chunk.js`,
77
+ // https://webpack.js.org/configuration/output/#outputhotupdatechunkfilename
78
+ hotUpdateChunkFilename: "js/[id].[fullhash].hot-update.js",
79
+ path: config.outputPath,
80
+ publicPath: config.publicPath,
81
+ // This is required for SRI to work.
82
+ crossOriginLoading: config.integrity && config.integrity.enabled
83
+ ? config.integrity.cross_origin
84
+ : false
85
+ },
86
+ entry: getEntryObject(),
87
+ resolve: {
88
+ extensions: [".js", ".jsx", ".mjs", ".ts", ".tsx", ".coffee"],
89
+ modules: getModulePaths()
90
+ },
91
+ plugins: getPlugins(),
92
+ resolveLoader: {
93
+ modules: ["node_modules"]
94
+ },
95
+ optimization: {
96
+ splitChunks: { chunks: "all" },
97
+ runtimeChunk: "single"
98
+ },
99
+ module: {
100
+ rules
101
+ }
102
+ };
103
+ module.exports = baseConfig;
@@ -0,0 +1,138 @@
1
+ /* eslint global-require: 0 */
2
+ /* eslint import/no-dynamic-require: 0 */
3
+
4
+ const { basename, dirname, join, relative, resolve } = require("path")
5
+ const { existsSync, readdirSync } = require("fs")
6
+ import { Dirent } from "fs"
7
+ const extname = require("path-complete-extname")
8
+ // @ts-ignore: webpack is an optional peer dependency (using type-only import)
9
+ import type { Configuration, Entry } from "webpack"
10
+ const config = require("../config")
11
+ const { isProduction } = require("../env")
12
+
13
+ const pluginsPath = resolve(
14
+ __dirname,
15
+ "..",
16
+ "plugins",
17
+ `${config.assets_bundler}.js`
18
+ )
19
+ const { getPlugins } = require(pluginsPath)
20
+ const rulesPath = resolve(
21
+ __dirname,
22
+ "..",
23
+ "rules",
24
+ `${config.assets_bundler}.js`
25
+ )
26
+ const rules = require(rulesPath)
27
+
28
+ // Don't use contentHash except for production for performance
29
+ // https://webpack.js.org/guides/build-performance/#avoid-production-specific-tooling
30
+ const hash = isProduction || config.useContentHash ? "-[contenthash]" : ""
31
+
32
+ const getFilesInDirectory = (dir: string, includeNested: boolean): string[] => {
33
+ if (!existsSync(dir)) {
34
+ return []
35
+ }
36
+
37
+ return readdirSync(dir, { withFileTypes: true }).flatMap((dirent: Dirent) => {
38
+ const filePath = join(dir, dirent.name)
39
+
40
+ if (dirent.isDirectory() && includeNested) {
41
+ return getFilesInDirectory(filePath, includeNested)
42
+ }
43
+ if (dirent.isFile()) {
44
+ return filePath
45
+ }
46
+ return []
47
+ })
48
+ }
49
+
50
+ const getEntryObject = (): Entry => {
51
+ const entries: Entry = {}
52
+ const rootPath = join(config.source_path, config.source_entry_path)
53
+ if (config.source_entry_path === "/" && config.nested_entries) {
54
+ throw new Error(
55
+ `Invalid Shakapacker configuration detected!\n\n` +
56
+ `You have set source_entry_path to '/' with nested_entries enabled.\n` +
57
+ `This would create webpack entry points for EVERY file in your source directory,\n` +
58
+ `which would severely impact build performance.\n\n` +
59
+ `To fix this issue, either:\n` +
60
+ `1. Set 'nested_entries: false' in your shakapacker.yml\n` +
61
+ `2. Change 'source_entry_path' to a specific subdirectory (e.g., 'packs')\n` +
62
+ `3. Or use both options for better organization of your entry points`
63
+ )
64
+ }
65
+
66
+ getFilesInDirectory(rootPath, config.nested_entries).forEach((path) => {
67
+ const namespace = relative(join(rootPath), dirname(path))
68
+ const name = join(namespace, basename(path, extname(path)))
69
+ const assetPath: string = resolve(path)
70
+
71
+ // Allows for multiple filetypes per entry (https://webpack.js.org/guides/entry-advanced/)
72
+ // Transforms the config object value to an array with all values under the same name
73
+ const previousPaths = entries[name]
74
+ if (previousPaths) {
75
+ const pathArray = Array.isArray(previousPaths)
76
+ ? previousPaths as string[]
77
+ : [previousPaths as string]
78
+ pathArray.push(assetPath)
79
+ entries[name] = pathArray
80
+ } else {
81
+ entries[name] = assetPath
82
+ }
83
+ })
84
+
85
+ return entries
86
+ }
87
+
88
+ const getModulePaths = (): string[] => {
89
+ const result = [resolve(config.source_path)]
90
+
91
+ if (config.additional_paths) {
92
+ config.additional_paths.forEach((path: string) => result.push(resolve(path)))
93
+ }
94
+ result.push("node_modules")
95
+
96
+ return result
97
+ }
98
+
99
+ const baseConfig: Configuration = {
100
+ mode: "production",
101
+ output: {
102
+ filename: `js/[name]${hash}.js`,
103
+ chunkFilename: `js/[name]${hash}.chunk.js`,
104
+
105
+ // https://webpack.js.org/configuration/output/#outputhotupdatechunkfilename
106
+ hotUpdateChunkFilename: "js/[id].[fullhash].hot-update.js",
107
+ path: config.outputPath,
108
+ publicPath: config.publicPath,
109
+
110
+ // This is required for SRI to work.
111
+ crossOriginLoading: config.integrity && config.integrity.enabled
112
+ ? (config.integrity.cross_origin as "anonymous" | "use-credentials" | false)
113
+ : false
114
+ },
115
+ entry: getEntryObject(),
116
+ resolve: {
117
+ extensions: [".js", ".jsx", ".mjs", ".ts", ".tsx", ".coffee"],
118
+ modules: getModulePaths()
119
+ },
120
+
121
+ plugins: getPlugins(),
122
+
123
+ resolveLoader: {
124
+ modules: ["node_modules"]
125
+ },
126
+
127
+ optimization: {
128
+ splitChunks: { chunks: "all" },
129
+ runtimeChunk: "single"
130
+ },
131
+
132
+ module: {
133
+ rules
134
+ }
135
+ }
136
+
137
+ export = baseConfig
138
+
data/package/index.d.ts CHANGED
@@ -1,150 +1,3 @@
1
- declare module "shakapacker" {
2
- import { Configuration, RuleSetRule } from "webpack"
3
- import * as https from "node:https"
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
- private_output_path?: string
13
- cache_path: string
14
- webpack_compile_output: boolean
15
- shakapacker_precompile: boolean
16
- additional_paths: string[]
17
- cache_manifest: boolean
18
- javascript_transpiler: string
19
- ensure_consistent_versioning: boolean
20
- compiler_strategy: string
21
- useContentHash: boolean
22
- compile: boolean
23
- outputPath: string
24
- publicPath: string
25
- publicPathWithoutCDN: string
26
- manifestPath: string
27
- manifest_path?: string
28
- assets_bundler?: string
29
- dev_server?: DevServerConfig
30
- integrity?: {
31
- enabled: boolean
32
- cross_origin: string
33
- hash_functions?: string[]
34
- }
35
- }
36
-
37
- export interface Env {
38
- railsEnv: string
39
- nodeEnv: string
40
- isProduction: boolean
41
- isDevelopment: boolean
42
- runningWebpackDevServer: boolean
43
- }
44
-
45
- type Header =
46
- | Array<{ key: string; value: string }>
47
- | Record<string, string | string[]>
48
- type ServerType = "http" | "https" | "spdy"
49
- type WebSocketType = "sockjs" | "ws"
50
-
51
- /**
52
- * This has the same keys and behavior as https://webpack.js.org/configuration/dev-server/ except:
53
- * 1. `hot` is replaced by `hmr`;
54
- * 2. Camel-cased properties are replaced by snake-cased ones.
55
- * @see {import('webpack-dev-server').Configuration}
56
- */
57
- interface DevServerConfig {
58
- allowed_hosts?: "all" | "auto" | string | string[]
59
- bonjour?: boolean | Record<string, unknown> // bonjour.BonjourOptions
60
- client?: Record<string, unknown> // Client
61
- compress?: boolean
62
- dev_middleware?: Record<string, unknown> // webpackDevMiddleware.Options
63
- headers?: Header | (() => Header)
64
- history_api_fallback?: boolean | Record<string, unknown> // HistoryApiFallbackOptions
65
- hmr?: "only" | boolean
66
- host?: "local-ip" | "local-ipv4" | "local-ipv6" | string
67
- http2?: boolean
68
- https?: boolean | https.ServerOptions
69
- ipc?: boolean | string
70
- magic_html?: boolean
71
- live_reload?: boolean
72
- inline_css?: boolean
73
- env_prefix?: string
74
- open?:
75
- | boolean
76
- | string
77
- | string[]
78
- | Record<string, unknown>
79
- | Record<string, unknown>[]
80
- port?: "auto" | string | number
81
- proxy?: unknown // ProxyConfigMap | ProxyConfigArray
82
- setup_exit_signals?: boolean
83
- static?: boolean | string | unknown // Static | Array<string | Static>
84
- watch_files?: string | string[] | unknown // WatchFiles | Array<WatchFiles | string>
85
- web_socket_server?:
86
- | string
87
- | boolean
88
- | WebSocketType
89
- | {
90
- type?: string | boolean | WebSocketType
91
- options?: Record<string, unknown>
92
- }
93
- server?:
94
- | string
95
- | boolean
96
- | ServerType
97
- | { type?: string | boolean | ServerType; options?: https.ServerOptions }
98
- [otherWebpackDevServerConfigKey: string]: unknown
99
- }
100
-
101
- export const config: Config
102
- export const devServer: DevServerConfig
103
- export function generateWebpackConfig(
104
- extraConfig?: Configuration
105
- ): Configuration
106
- export const baseConfig: Configuration
107
- export const env: Env
108
- export const rules: RuleSetRule[]
109
- export function moduleExists(packageName: string): boolean
110
- export function canProcess<T = unknown>(
111
- rule: string,
112
- fn: (modulePath: string) => T
113
- ): T | null
114
- export const inliningCss: boolean
115
- export * from "webpack-merge"
116
- }
117
-
118
- declare module "shakapacker/rspack" {
119
- import type { RspackOptions } from "@rspack/core"
120
-
121
- export const config: import("shakapacker").Config
122
- export function generateRspackConfig(
123
- extraConfig?: RspackOptions
124
- ): RspackOptions
125
- export const baseConfig: RspackOptions
126
- export const env: import("shakapacker").Env
127
- export const rules: NonNullable<RspackOptions["module"]>["rules"]
128
- export function moduleExists(packageName: string): boolean
129
- export function canProcess<T = unknown>(
130
- rule: string,
131
- fn: (modulePath: string) => T
132
- ): T | null
133
- export const inliningCss: boolean
134
- export * from "webpack-merge"
135
- }
136
-
137
- declare module "shakapacker/package/babel/preset.js" {
138
- import { ConfigAPI, PluginItem, TransformOptions } from "@babel/core"
139
-
140
- interface RequiredTransformOptions {
141
- plugins: PluginItem[]
142
- presets: PluginItem[]
143
- }
144
-
145
- const defaultConfigFunc: (
146
- api: ConfigAPI
147
- ) => TransformOptions & RequiredTransformOptions
148
-
149
- export = defaultConfigFunc
150
- }
1
+ declare const _default: any;
2
+ export = _default;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -2,22 +2,32 @@
2
2
  /* eslint import/no-dynamic-require: 0 */
3
3
 
4
4
  const webpackMerge = require("webpack-merge")
5
- const { resolve } = require("path")
6
- const { existsSync } = require("fs")
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"
7
9
  const config = require("./config")
8
10
  const baseConfig = require("./environments/base")
9
-
10
- const rulesPath = resolve(__dirname, "rules", `${config.assets_bundler}.js`)
11
- const rules = require(rulesPath)
12
11
  const devServer = require("./dev_server")
13
12
  const env = require("./env")
14
13
  const { moduleExists, canProcess } = require("./utils/helpers")
15
14
  const inliningCss = require("./utils/inliningCss")
16
15
 
17
- const generateWebpackConfig = (extraConfig = {}, ...extraArgs) => {
16
+ const rulesPath = resolve(__dirname, "rules", `${config.assets_bundler}.js`)
17
+ const rules = require(rulesPath)
18
+
19
+ const generateWebpackConfig = (extraConfig: Configuration = {}, ...extraArgs: any[]): Configuration => {
18
20
  if (extraArgs.length > 0) {
19
21
  throw new Error(
20
- "Only one extra config may be passed here - use webpack-merge to merge configs before passing them to Shakapacker"
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))`
21
31
  )
22
32
  }
23
33
 
@@ -28,7 +38,7 @@ const generateWebpackConfig = (extraConfig = {}, ...extraArgs) => {
28
38
  return webpackMerge.merge({}, environmentConfig, extraConfig)
29
39
  }
30
40
 
31
- module.exports = {
41
+ export = {
32
42
  config, // shakapacker.yml
33
43
  devServer,
34
44
  generateWebpackConfig,
@@ -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