@corespeed/webpack 0.0.0 → 0.1.0-beta.1
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.
- package/dist/index.cjs +689 -0
- package/dist/index.d.ts +228 -0
- package/dist/index.mjs +675 -0
- package/package.json +84 -8
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
import process$1 from 'node:process';
|
|
2
|
+
import { configDotenv } from 'dotenv';
|
|
3
|
+
import __node_cjsModule, { createRequire } from 'node:module';
|
|
4
|
+
import readBrowserslist, { loadConfig } from 'browserslist';
|
|
5
|
+
import { getPort } from 'get-port-please';
|
|
6
|
+
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
|
|
7
|
+
import ReactRefreshPlugin from '@pmmmwh/react-refresh-webpack-plugin';
|
|
8
|
+
import { DefinePlugin } from 'webpack';
|
|
9
|
+
import CopyWebpackPlugin from 'copy-webpack-plugin';
|
|
10
|
+
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
|
11
|
+
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
|
12
|
+
import WebpackBarProgressPlugin from 'webpackbar';
|
|
13
|
+
import { appendArrayInPlace } from 'foxts/append-array-in-place';
|
|
14
|
+
import MiniCssExtractPlugin, { loader } from 'mini-css-extract-plugin';
|
|
15
|
+
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
|
|
16
|
+
import isCI from 'is-ci';
|
|
17
|
+
import { confirm } from '@clack/prompts';
|
|
18
|
+
import { installPackage } from '@antfu/install-pkg';
|
|
19
|
+
import { castArray } from 'foxts/cast-array';
|
|
20
|
+
import LightningCSS, { browserslistToTargets } from 'lightningcss';
|
|
21
|
+
import { defineReactCompilerLoaderOption, reactCompilerLoader } from 'react-compiler-webpack';
|
|
22
|
+
import TerserPlugin from 'terser-webpack-plugin';
|
|
23
|
+
import { LightningCssMinifyPlugin } from 'lightningcss-loader';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
|
|
26
|
+
function getSupportedBrowsers(browserlists, dir, isDevelopment) {
|
|
27
|
+
if (browserlists) {
|
|
28
|
+
return readBrowserslist(browserlists);
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
return loadConfig({
|
|
32
|
+
path: dir,
|
|
33
|
+
env: isDevelopment ? 'development' : 'production'
|
|
34
|
+
});
|
|
35
|
+
} catch {}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getSwcOptions(isDevelopment, useTypeScript, supportedBrowsers, coreJsVersion) {
|
|
39
|
+
return {
|
|
40
|
+
jsc: {
|
|
41
|
+
parser: useTypeScript ? {
|
|
42
|
+
syntax: 'typescript',
|
|
43
|
+
tsx: true
|
|
44
|
+
} : {
|
|
45
|
+
syntax: 'ecmascript',
|
|
46
|
+
jsx: true,
|
|
47
|
+
importAttributes: true
|
|
48
|
+
},
|
|
49
|
+
externalHelpers: true,
|
|
50
|
+
loose: false,
|
|
51
|
+
transform: {
|
|
52
|
+
react: {
|
|
53
|
+
runtime: 'automatic',
|
|
54
|
+
refresh: isDevelopment,
|
|
55
|
+
development: isDevelopment
|
|
56
|
+
},
|
|
57
|
+
optimizer: {
|
|
58
|
+
simplify: true,
|
|
59
|
+
globals: {
|
|
60
|
+
// typeofs: {
|
|
61
|
+
// window: 'object'
|
|
62
|
+
// },
|
|
63
|
+
envs: {
|
|
64
|
+
NODE_ENV: isDevelopment ? '"development"' : '"production"'
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
env: {
|
|
71
|
+
// swc-loader don't read browserslist config file, manually specify targets
|
|
72
|
+
targets: supportedBrowsers?.length ? supportedBrowsers : 'defaults, chrome > 70, edge >= 79, firefox esr, safari >= 11, not dead, not ie > 0, not ie_mob > 0, not OperaMini all',
|
|
73
|
+
mode: 'usage',
|
|
74
|
+
loose: false,
|
|
75
|
+
coreJs: coreJsVersion,
|
|
76
|
+
shippedProposals: false
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const output = ({ output }, { jsFilename, cssFilename, assetFilename })=>(config)=>{
|
|
82
|
+
const { path: outputPath, library, crossOriginLoading } = output;
|
|
83
|
+
config.output ??= {};
|
|
84
|
+
config.output.asyncChunks = true;
|
|
85
|
+
config.output.crossOriginLoading ??= crossOriginLoading;
|
|
86
|
+
config.output.hashFunction = 'xxhash64';
|
|
87
|
+
config.output.hashDigestLength = 16;
|
|
88
|
+
config.output.path ??= outputPath;
|
|
89
|
+
config.output.library ??= library;
|
|
90
|
+
config.output.filename ??= jsFilename;
|
|
91
|
+
config.output.chunkFilename ??= jsFilename;
|
|
92
|
+
config.output.cssFilename ??= cssFilename;
|
|
93
|
+
config.output.cssChunkFilename ??= cssFilename;
|
|
94
|
+
config.output.assetModuleFilename ??= assetFilename;
|
|
95
|
+
config.output.hotUpdateChunkFilename = 'webpack/[id].[fullhash].hot-update.js';
|
|
96
|
+
config.output.hotUpdateMainFilename = 'webpack/[fullhash].[runtime].hot-update.json';
|
|
97
|
+
return config;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const base = ({ development: isDevelopment, entry, webpackExperimentalBuiltinCssSupport })=>(config)=>{
|
|
101
|
+
config.mode ??= isDevelopment ? 'development' : 'production';
|
|
102
|
+
config.entry ??= entry;
|
|
103
|
+
config.experiments ??= {};
|
|
104
|
+
config.experiments.css ??= webpackExperimentalBuiltinCssSupport;
|
|
105
|
+
config.experiments.cacheUnaffected ??= true;
|
|
106
|
+
return config;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const sourcemap = ({ development: isDevelopment, sourcemap })=>(config)=>{
|
|
110
|
+
const { development: sourceMapDevelopment = 'eval-source-map', production: sourceMapProduction = 'hidden-source-map' } = sourcemap;
|
|
111
|
+
config.devtool ??= isDevelopment ? sourceMapDevelopment : sourceMapProduction;
|
|
112
|
+
return config;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const devserver = ({ devServerPort, spa, publicDir })=>async (config)=>{
|
|
116
|
+
const { fallbackPort: port = process.env.PORT ? Number(process.env.PORT || 3000) : 3000, portRange = [
|
|
117
|
+
3000,
|
|
118
|
+
3100
|
|
119
|
+
], ports = [] } = devServerPort;
|
|
120
|
+
const finalPort = await getPort({
|
|
121
|
+
port,
|
|
122
|
+
ports,
|
|
123
|
+
portRange
|
|
124
|
+
});
|
|
125
|
+
config.devServer ??= {};
|
|
126
|
+
config.devServer.port ??= finalPort;
|
|
127
|
+
config.devServer.hot ??= true;
|
|
128
|
+
config.devServer.historyApiFallback ??= spa;
|
|
129
|
+
config.devServer.static ??= {};
|
|
130
|
+
if (typeof config.devServer.static === 'object' && !Array.isArray(config.devServer.static)) {
|
|
131
|
+
config.devServer.static.directory ??= publicDir;
|
|
132
|
+
}
|
|
133
|
+
// in any case being a string, array, boolean, we do not override it
|
|
134
|
+
config.devServer.client ??= {};
|
|
135
|
+
if (typeof config.devServer.client === 'object') {
|
|
136
|
+
config.devServer.client.overlay ??= {};
|
|
137
|
+
if (typeof config.devServer.client.overlay === 'object') {
|
|
138
|
+
config.devServer.client.overlay.errors ??= true;
|
|
139
|
+
config.devServer.client.overlay.warnings ??= false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return config;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const plugins = ({ development: isDevelopment, htmlTemplatePath, analyze, webpackExperimentalBuiltinCssSupport, plugins: customPlugins }, { cssFilename })=>(config)=>{
|
|
146
|
+
config.plugins ??= [];
|
|
147
|
+
if (!isDevelopment) {
|
|
148
|
+
config.plugins.push(new CleanWebpackPlugin(), new WebpackBarProgressPlugin());
|
|
149
|
+
}
|
|
150
|
+
if (isDevelopment) {
|
|
151
|
+
config.plugins.push(new ReactRefreshPlugin());
|
|
152
|
+
}
|
|
153
|
+
if (!webpackExperimentalBuiltinCssSupport) {
|
|
154
|
+
config.plugins.push(new MiniCssExtractPlugin({
|
|
155
|
+
filename: cssFilename
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
// env inline
|
|
159
|
+
const publicEnv = Object.entries(process.env).reduce((acc, [key, value])=>{
|
|
160
|
+
if (key.startsWith('PUBLIC_') || key.startsWith('NEXT_PUBLIC_')) {
|
|
161
|
+
acc[`process.env.${key}`] = JSON.stringify(value);
|
|
162
|
+
}
|
|
163
|
+
return acc;
|
|
164
|
+
}, {});
|
|
165
|
+
config.plugins.push(new DefinePlugin({
|
|
166
|
+
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || (isDevelopment ? 'development' : 'production')),
|
|
167
|
+
'import.meta.env.DEV': isDevelopment.toString(),
|
|
168
|
+
'import.meta.env.PROD': (!isDevelopment).toString(),
|
|
169
|
+
'typeof window': JSON.stringify('object'),
|
|
170
|
+
'process.env': JSON.stringify(publicEnv),
|
|
171
|
+
...publicEnv
|
|
172
|
+
}), new CopyWebpackPlugin({
|
|
173
|
+
patterns: [
|
|
174
|
+
{
|
|
175
|
+
from: 'public',
|
|
176
|
+
to: '.',
|
|
177
|
+
// globOptions: {
|
|
178
|
+
// ignore: ['**/index.html']
|
|
179
|
+
// },
|
|
180
|
+
noErrorOnMissing: true
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
}));
|
|
184
|
+
if (htmlTemplatePath) {
|
|
185
|
+
config.plugins.push(new HtmlWebpackPlugin({
|
|
186
|
+
template: htmlTemplatePath
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
if (analyze) {
|
|
190
|
+
config.plugins.push(new BundleAnalyzerPlugin(typeof analyze === 'object' ? analyze : {
|
|
191
|
+
analyzerMode: 'static'
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
appendArrayInPlace(config.plugins, customPlugins);
|
|
195
|
+
return config;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
async function ensurePackage(packages, cwd, dev) {
|
|
199
|
+
const globalRequire = createRequire(cwd);
|
|
200
|
+
const missingPackages = new Set();
|
|
201
|
+
for (const packageName of castArray(packages)){
|
|
202
|
+
try {
|
|
203
|
+
globalRequire.resolve(packageName, {
|
|
204
|
+
paths: [
|
|
205
|
+
cwd
|
|
206
|
+
]
|
|
207
|
+
});
|
|
208
|
+
} catch (e) {
|
|
209
|
+
if (typeof e === 'object' && e && 'code' in e && e.code === 'MODULE_NOT_FOUND') {
|
|
210
|
+
missingPackages.add(packageName);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (missingPackages.size === 0) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (isCI) {
|
|
218
|
+
throw new Error(`Missing required packages: ${[
|
|
219
|
+
...missingPackages
|
|
220
|
+
].join(', ')}. Please ensure all required packages are installed.`);
|
|
221
|
+
}
|
|
222
|
+
const result = await confirm({
|
|
223
|
+
message: ` The following packages are required but not installed: ${[
|
|
224
|
+
...missingPackages
|
|
225
|
+
].join(', ')}. Do you want to install them now?`,
|
|
226
|
+
initialValue: true
|
|
227
|
+
});
|
|
228
|
+
if (result) {
|
|
229
|
+
await installPackage([
|
|
230
|
+
...missingPackages
|
|
231
|
+
], {
|
|
232
|
+
dev
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const resolve = ({ cwd, lodashTreeShaking })=>async (config)=>{
|
|
238
|
+
config.resolve ??= {};
|
|
239
|
+
const extensions = [
|
|
240
|
+
'.ts',
|
|
241
|
+
'.tsx',
|
|
242
|
+
'.jsx',
|
|
243
|
+
'.mjs',
|
|
244
|
+
'.cjs',
|
|
245
|
+
'.js',
|
|
246
|
+
'.json'
|
|
247
|
+
];
|
|
248
|
+
config.resolve.extensions = extensions;
|
|
249
|
+
config.resolve.cache = true;
|
|
250
|
+
config.resolve.unsafeCache = false;
|
|
251
|
+
config.resolve.conditionNames = [
|
|
252
|
+
'import',
|
|
253
|
+
'module',
|
|
254
|
+
'require',
|
|
255
|
+
'default'
|
|
256
|
+
];
|
|
257
|
+
config.resolve.plugins ??= [];
|
|
258
|
+
config.resolve.plugins.push(new TsconfigPathsPlugin({
|
|
259
|
+
// tsconfig-paths-webpack-plugin can't access `resolve.extensions`
|
|
260
|
+
// have to provide again
|
|
261
|
+
extensions
|
|
262
|
+
}));
|
|
263
|
+
const globalRequire = createRequire(cwd);
|
|
264
|
+
if (lodashTreeShaking) {
|
|
265
|
+
await ensurePackage('lodash-es', cwd, true);
|
|
266
|
+
config.resolve.alias ??= {};
|
|
267
|
+
if (!Array.isArray(config.resolve.alias)) {
|
|
268
|
+
config.resolve.alias.lodash = 'lodash-es';
|
|
269
|
+
}
|
|
270
|
+
config.resolve.fallback ??= {};
|
|
271
|
+
if (!Array.isArray(config.resolve.fallback)) {
|
|
272
|
+
config.resolve.fallback['lodash-es/fp'] = globalRequire.resolve('lodash/fp');
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return config;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const external = ({ externals })=>(config)=>{
|
|
279
|
+
const finalExternals = [];
|
|
280
|
+
if (config.externals) {
|
|
281
|
+
if (Array.isArray(config.externals)) {
|
|
282
|
+
appendArrayInPlace(finalExternals, config.externals);
|
|
283
|
+
} else {
|
|
284
|
+
finalExternals.push(config.externals);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
finalExternals.push(externals);
|
|
288
|
+
config.externals = finalExternals;
|
|
289
|
+
return config;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
__node_cjsModule.createRequire(import.meta.url);
|
|
293
|
+
|
|
294
|
+
const regexLikeCss = /\.(css|scss|sass)$/;
|
|
295
|
+
const $require = typeof globalThis.require === 'function' ? globalThis.require : createRequire(import.meta.url);
|
|
296
|
+
const loaders = ({ cwd, postcss, svgr, reactCompiler, webpackExperimentalBuiltinCssSupport, browserlists }, { swcTypeScriptOptions, swcJavaScriptOptions, supportedBrowsers })=>async (config)=>{
|
|
297
|
+
await ensurePackage([
|
|
298
|
+
'core-js',
|
|
299
|
+
'@swc/helpers'
|
|
300
|
+
], cwd, false);
|
|
301
|
+
const blocks = [];
|
|
302
|
+
config.module ??= {};
|
|
303
|
+
config.module.rules ??= [];
|
|
304
|
+
// Automatically transform references to files (i.e. url()) into URLs
|
|
305
|
+
loader$1({
|
|
306
|
+
oneOf: [
|
|
307
|
+
{
|
|
308
|
+
// This should only be applied to CSS files
|
|
309
|
+
issuer: regexLikeCss,
|
|
310
|
+
// Exclude extensions that webpack handles by default
|
|
311
|
+
exclude: [
|
|
312
|
+
/\.(js|mjs|jsx|ts|tsx)$/,
|
|
313
|
+
/\.html$/,
|
|
314
|
+
/\.json$/,
|
|
315
|
+
/\.webpack\[[^\]]+]$/
|
|
316
|
+
],
|
|
317
|
+
// `asset/resource` always emits a URL reference, where `asset`
|
|
318
|
+
// might inline the asset as a data URI
|
|
319
|
+
type: 'asset/resource'
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
});
|
|
323
|
+
// CSS
|
|
324
|
+
if (postcss) {
|
|
325
|
+
await ensurePackage([
|
|
326
|
+
'postcss',
|
|
327
|
+
'postcss-loader'
|
|
328
|
+
], cwd, true);
|
|
329
|
+
}
|
|
330
|
+
const globalRequire = createRequire(cwd);
|
|
331
|
+
if (!webpackExperimentalBuiltinCssSupport || postcss) {
|
|
332
|
+
loader$1({
|
|
333
|
+
test: /\.css$/,
|
|
334
|
+
use: [
|
|
335
|
+
!webpackExperimentalBuiltinCssSupport && {
|
|
336
|
+
loader: loader
|
|
337
|
+
},
|
|
338
|
+
!webpackExperimentalBuiltinCssSupport && {
|
|
339
|
+
loader: $require.resolve('css-loader')
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
loader: $require.resolve('lightningcss-loader'),
|
|
343
|
+
options: {
|
|
344
|
+
implementation: LightningCSS,
|
|
345
|
+
// lightningcss-loader will read targets and parsed via browserlists
|
|
346
|
+
// https://github.com/fz6m/lightningcss-loader/blob/7f3601abea4479deb31db713610c5a09e8a5d576/src/loader.ts#L44
|
|
347
|
+
// https://github.com/fz6m/lightningcss-loader/blob/7f3601abea4479deb31db713610c5a09e8a5d576/src/minify.ts#L111
|
|
348
|
+
// https://github.com/fz6m/lightningcss-loader/blob/7f3601abea4479deb31db713610c5a09e8a5d576/src/utils.ts#L39
|
|
349
|
+
targets: browserlists ?? supportedBrowsers
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
postcss && globalRequire.resolve('postcss-loader')
|
|
353
|
+
]
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
// SVGR
|
|
357
|
+
if (svgr) {
|
|
358
|
+
await ensurePackage('@svgr/webpack', cwd, true);
|
|
359
|
+
loader$1({
|
|
360
|
+
test: /\.svg$/i,
|
|
361
|
+
type: 'asset',
|
|
362
|
+
resourceQuery: /url/ // *.svg?url
|
|
363
|
+
});
|
|
364
|
+
loader$1({
|
|
365
|
+
test: /\.svg$/i,
|
|
366
|
+
issuer: /\.[jt]sx?$/,
|
|
367
|
+
resourceQuery: {
|
|
368
|
+
not: [
|
|
369
|
+
/url/
|
|
370
|
+
]
|
|
371
|
+
},
|
|
372
|
+
use: [
|
|
373
|
+
{
|
|
374
|
+
loader: $require.resolve('swc-loader'),
|
|
375
|
+
options: swcTypeScriptOptions
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
loader: globalRequire.resolve('@svgr/webpack'),
|
|
379
|
+
options: {
|
|
380
|
+
babel: false
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
]
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
// Assets
|
|
387
|
+
loader$1({
|
|
388
|
+
oneOf: [
|
|
389
|
+
{
|
|
390
|
+
test: /\.(woff|woff2|eot|ttf|otf)$/i,
|
|
391
|
+
type: 'asset/resource'
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
test: /assets\//,
|
|
395
|
+
type: 'asset/resource',
|
|
396
|
+
generator: {
|
|
397
|
+
filename: '_assets/[hash][ext][query]'
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
]
|
|
401
|
+
});
|
|
402
|
+
// TypeScript/JavaScript
|
|
403
|
+
if (reactCompiler) {
|
|
404
|
+
await ensurePackage('babel-plugin-react-compiler', cwd, true);
|
|
405
|
+
}
|
|
406
|
+
loader$1({
|
|
407
|
+
test: /\.[cm]?tsx?$/,
|
|
408
|
+
exclude: [
|
|
409
|
+
/node_modules/
|
|
410
|
+
],
|
|
411
|
+
use: [
|
|
412
|
+
{
|
|
413
|
+
loader: $require.resolve('swc-loader'),
|
|
414
|
+
options: swcTypeScriptOptions
|
|
415
|
+
},
|
|
416
|
+
reactCompiler && {
|
|
417
|
+
loader: reactCompilerLoader,
|
|
418
|
+
options: defineReactCompilerLoaderOption(typeof reactCompiler === 'object' ? reactCompiler : {})
|
|
419
|
+
}
|
|
420
|
+
]
|
|
421
|
+
});
|
|
422
|
+
loader$1({
|
|
423
|
+
test: /\.[cm]?jsx?$/,
|
|
424
|
+
exclude: [
|
|
425
|
+
/node_modules/
|
|
426
|
+
],
|
|
427
|
+
use: [
|
|
428
|
+
{
|
|
429
|
+
loader: $require.resolve('swc-loader'),
|
|
430
|
+
options: swcJavaScriptOptions
|
|
431
|
+
},
|
|
432
|
+
reactCompiler && {
|
|
433
|
+
loader: reactCompilerLoader,
|
|
434
|
+
options: defineReactCompilerLoaderOption(typeof reactCompiler === 'object' ? reactCompiler : {})
|
|
435
|
+
}
|
|
436
|
+
]
|
|
437
|
+
});
|
|
438
|
+
return blocks.reduce(async (cfg, next)=>next(await cfg), config);
|
|
439
|
+
function loader$1(rule) {
|
|
440
|
+
blocks.push((config)=>{
|
|
441
|
+
if (rule.oneOf) {
|
|
442
|
+
const existing = config.module.rules.find((arrayRule)=>arrayRule && typeof arrayRule === 'object' && arrayRule.oneOf);
|
|
443
|
+
if (existing && typeof existing === 'object') {
|
|
444
|
+
existing.oneOf.push(...rule.oneOf);
|
|
445
|
+
return config;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
config.module.rules.push(rule);
|
|
449
|
+
return config;
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
// function unshiftLoader(
|
|
453
|
+
// rule: WebpackRuleSetRule
|
|
454
|
+
// ) {
|
|
455
|
+
// blocks.push((config: WebpackConfiguration) => {
|
|
456
|
+
// if (rule.oneOf) {
|
|
457
|
+
// const existing = config.module!.rules!.find(
|
|
458
|
+
// (arrayRule) => arrayRule && typeof arrayRule === 'object' && arrayRule.oneOf
|
|
459
|
+
// );
|
|
460
|
+
// if (existing && typeof existing === 'object') {
|
|
461
|
+
// existing.oneOf?.unshift(...rule.oneOf);
|
|
462
|
+
// return config;
|
|
463
|
+
// }
|
|
464
|
+
// }
|
|
465
|
+
// config.module!.rules!.unshift(rule);
|
|
466
|
+
// return config;
|
|
467
|
+
// });
|
|
468
|
+
// };
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
const optimization = ({ development: isDevelopment, browserlists }, { supportedBrowsers })=>(config)=>{
|
|
472
|
+
config.optimization ??= {};
|
|
473
|
+
config.optimization.emitOnErrors = !isDevelopment;
|
|
474
|
+
config.optimization.checkWasmTypes = false;
|
|
475
|
+
config.optimization.nodeEnv = false; // we manually bring our own DefinePlugin
|
|
476
|
+
config.optimization.minimizer ??= [];
|
|
477
|
+
config.optimization.minimizer.push(new TerserPlugin({
|
|
478
|
+
minify: TerserPlugin.swcMinify,
|
|
479
|
+
terserOptions: {
|
|
480
|
+
compress: {
|
|
481
|
+
ecma: 2018,
|
|
482
|
+
comparisons: false,
|
|
483
|
+
inline: 2 // https://github.com/vercel/next.js/issues/7178#issuecomment-493048965
|
|
484
|
+
},
|
|
485
|
+
mangle: {
|
|
486
|
+
safari10: true
|
|
487
|
+
},
|
|
488
|
+
format: {
|
|
489
|
+
// use ecma 2015+ to enable minify like shorthand object
|
|
490
|
+
ecma: 2018,
|
|
491
|
+
safari10: true,
|
|
492
|
+
comments: false,
|
|
493
|
+
// Fixes usage of Emoji and certain Regex
|
|
494
|
+
ascii_only: true
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}), new LightningCssMinifyPlugin({
|
|
498
|
+
implementation: LightningCSS,
|
|
499
|
+
// lightningcss-loader will read targets and parsed via browserlists
|
|
500
|
+
// https://github.com/fz6m/lightningcss-loader/blob/7f3601abea4479deb31db713610c5a09e8a5d576/src/loader.ts#L44
|
|
501
|
+
// https://github.com/fz6m/lightningcss-loader/blob/7f3601abea4479deb31db713610c5a09e8a5d576/src/minify.ts#L111
|
|
502
|
+
// https://github.com/fz6m/lightningcss-loader/blob/7f3601abea4479deb31db713610c5a09e8a5d576/src/utils.ts#L39
|
|
503
|
+
targets: browserlists ?? supportedBrowsers
|
|
504
|
+
}));
|
|
505
|
+
return config;
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
function getTopLevelFrameworkPaths(frameworkPackages, dir) {
|
|
509
|
+
const globalRequire = createRequire(dir);
|
|
510
|
+
// Only top-level packages are included, e.g. nested copies like
|
|
511
|
+
// 'node_modules/meow/node_modules/react' are not included.
|
|
512
|
+
const topLevelFrameworkPaths = [];
|
|
513
|
+
const visitedFrameworkPackages = new Set();
|
|
514
|
+
// Adds package-paths of dependencies recursively
|
|
515
|
+
const addPackagePath = (packageName, relativeToPath)=>{
|
|
516
|
+
try {
|
|
517
|
+
if (visitedFrameworkPackages.has(packageName)) return;
|
|
518
|
+
visitedFrameworkPackages.add(packageName);
|
|
519
|
+
const packageJsonPath = globalRequire.resolve(`${packageName}/package.json`, {
|
|
520
|
+
paths: [
|
|
521
|
+
relativeToPath
|
|
522
|
+
]
|
|
523
|
+
});
|
|
524
|
+
// Include a trailing slash so that a `.startsWith(packagePath)` check avoids false positives
|
|
525
|
+
// when one package name starts with the full name of a different package.
|
|
526
|
+
// For example:
|
|
527
|
+
// "node_modules/react-slider".startsWith("node_modules/react") // true
|
|
528
|
+
// "node_modules/react-slider".startsWith("node_modules/react/") // false
|
|
529
|
+
const directory = path.join(packageJsonPath, '../');
|
|
530
|
+
// Returning from the function in case the directory has already been added and traversed
|
|
531
|
+
if (topLevelFrameworkPaths.includes(directory)) return;
|
|
532
|
+
topLevelFrameworkPaths.push(directory);
|
|
533
|
+
const dependencies = globalRequire(packageJsonPath).dependencies || {};
|
|
534
|
+
for (const name of Object.keys(dependencies)){
|
|
535
|
+
addPackagePath(name, directory);
|
|
536
|
+
}
|
|
537
|
+
} catch {
|
|
538
|
+
// don't error on failing to resolve framework packages
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
for (const packageName of frameworkPackages){
|
|
542
|
+
addPackagePath(packageName, dir);
|
|
543
|
+
}
|
|
544
|
+
return topLevelFrameworkPaths;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const splitChunks = ({ cwd, topLevelFrameworkPackages })=>(config)=>{
|
|
548
|
+
config.optimization ??= {};
|
|
549
|
+
if (config.optimization.runtimeChunk === 'single') {
|
|
550
|
+
config.optimization.runtimeChunk = {
|
|
551
|
+
name: 'webpack'
|
|
552
|
+
};
|
|
553
|
+
} else {
|
|
554
|
+
config.optimization.runtimeChunk ??= {
|
|
555
|
+
name: 'webpack'
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
config.optimization.splitChunks ||= {};
|
|
559
|
+
config.optimization.splitChunks.maxInitialRequests = 25;
|
|
560
|
+
config.optimization.splitChunks.minSize = 20000;
|
|
561
|
+
config.optimization.splitChunks.cacheGroups ??= {};
|
|
562
|
+
const topLevelFrameworkPaths = getTopLevelFrameworkPaths(topLevelFrameworkPackages, cwd);
|
|
563
|
+
config.optimization.splitChunks.cacheGroups.framework ||= {
|
|
564
|
+
chunks: 'all',
|
|
565
|
+
name: 'framework',
|
|
566
|
+
test (module) {
|
|
567
|
+
const resource = module.nameForCondition();
|
|
568
|
+
return resource ? topLevelFrameworkPaths.some((pkgPath)=>resource.startsWith(pkgPath)) : false;
|
|
569
|
+
},
|
|
570
|
+
priority: 40,
|
|
571
|
+
enforce: true
|
|
572
|
+
};
|
|
573
|
+
return config;
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
__node_cjsModule.createRequire(import.meta.url);
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Usage:
|
|
580
|
+
*
|
|
581
|
+
* ```js
|
|
582
|
+
* // webpack.config.js
|
|
583
|
+
* const { createWebpck } = require('@corespeed/webpack');
|
|
584
|
+
* const path = require('node:path');
|
|
585
|
+
*
|
|
586
|
+
* module.exports = createWebpack({
|
|
587
|
+
* // your options here
|
|
588
|
+
* }, {
|
|
589
|
+
* // your custom webpack config here
|
|
590
|
+
* });
|
|
591
|
+
* ```
|
|
592
|
+
*/ async function createWebpack(options, customWebpackOptions = {}) {
|
|
593
|
+
const ctx = {
|
|
594
|
+
output: {},
|
|
595
|
+
spa: true,
|
|
596
|
+
publicDir: './public',
|
|
597
|
+
development: process$1.env.NODE_ENV === 'development',
|
|
598
|
+
// entry,
|
|
599
|
+
htmlTemplatePath: './src/index.html',
|
|
600
|
+
sourcemap: {},
|
|
601
|
+
devServerPort: {},
|
|
602
|
+
externals: {},
|
|
603
|
+
webpackExperimentalBuiltinCssSupport: false,
|
|
604
|
+
postcss: false,
|
|
605
|
+
svgr: false,
|
|
606
|
+
topLevelFrameworkPackages: [
|
|
607
|
+
'react',
|
|
608
|
+
'react-dom',
|
|
609
|
+
'wouter',
|
|
610
|
+
'react-router',
|
|
611
|
+
'react-router-dom'
|
|
612
|
+
],
|
|
613
|
+
reactCompiler: false,
|
|
614
|
+
analyze: process$1.env.ANALYZE === 'true',
|
|
615
|
+
plugins: [],
|
|
616
|
+
dotenv: {},
|
|
617
|
+
lodashTreeShaking: false,
|
|
618
|
+
...options
|
|
619
|
+
};
|
|
620
|
+
// dotenv
|
|
621
|
+
if (ctx.dotenv) {
|
|
622
|
+
if (typeof ctx.dotenv === 'boolean') {
|
|
623
|
+
configDotenv();
|
|
624
|
+
} else {
|
|
625
|
+
configDotenv(ctx.dotenv);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
// Output
|
|
629
|
+
const { filenameContainChunkName: outputChunkFileFormatContainChunkName = false, filenamePrefix: _outputFilenamePrefix = '/_assets/static/' } = ctx.output;
|
|
630
|
+
const prodOnlyChunkName = outputChunkFileFormatContainChunkName ? '[name].' : '';
|
|
631
|
+
const outputFilenamePrefix = ensureFilenamePrefix(_outputFilenamePrefix);
|
|
632
|
+
// MiniCssExtractPlugin do not read output.cssFilename
|
|
633
|
+
// https://github.com/webpack/mini-css-extract-plugin/issues/1041
|
|
634
|
+
// So we need to pass cssFilename to both output.cssFilename and new MiniCssExtractPlugin({ filename: ... })
|
|
635
|
+
// So we calculate them ahead here and pass via block context
|
|
636
|
+
const jsFilename = outputFilenamePrefix + 'js/' + (ctx.development ? '[name].js' : prodOnlyChunkName + '[contenthash].js');
|
|
637
|
+
const cssFilename = outputFilenamePrefix + 'css/' + (ctx.development ? '[name].css' : prodOnlyChunkName + '[contenthash].css');
|
|
638
|
+
const assetFilename = outputFilenamePrefix + 'assets/' + (ctx.development ? '[name].[hash][ext][query]' : prodOnlyChunkName + '[hash][ext][query]');
|
|
639
|
+
// Misc
|
|
640
|
+
const supportedBrowsers = getSupportedBrowsers(ctx.browserlists, ctx.cwd, ctx.development);
|
|
641
|
+
supportedBrowsers ? browserslistToTargets(supportedBrowsers) : undefined;
|
|
642
|
+
const globalRequire = createRequire(ctx.cwd);
|
|
643
|
+
const coreJsVersion = globalRequire('core-js/package.json').version;
|
|
644
|
+
const blockCtx = {
|
|
645
|
+
swcJavaScriptOptions: getSwcOptions(ctx.development, false, supportedBrowsers, coreJsVersion),
|
|
646
|
+
swcTypeScriptOptions: getSwcOptions(ctx.development, true, supportedBrowsers, coreJsVersion),
|
|
647
|
+
supportedBrowsers,
|
|
648
|
+
jsFilename,
|
|
649
|
+
cssFilename,
|
|
650
|
+
assetFilename
|
|
651
|
+
};
|
|
652
|
+
return [
|
|
653
|
+
base(ctx),
|
|
654
|
+
external(ctx),
|
|
655
|
+
output(ctx, blockCtx),
|
|
656
|
+
sourcemap(ctx),
|
|
657
|
+
devserver(ctx),
|
|
658
|
+
loaders(ctx, blockCtx),
|
|
659
|
+
plugins(ctx, blockCtx),
|
|
660
|
+
optimization(ctx, blockCtx),
|
|
661
|
+
splitChunks(ctx),
|
|
662
|
+
resolve(ctx)
|
|
663
|
+
].reduce(async (config, next)=>next(await config), customWebpackOptions);
|
|
664
|
+
}
|
|
665
|
+
function ensureFilenamePrefix(path) {
|
|
666
|
+
if (path.startsWith('/')) {
|
|
667
|
+
path = path.slice(1);
|
|
668
|
+
}
|
|
669
|
+
if (!path.endsWith('/')) {
|
|
670
|
+
path = path + '/';
|
|
671
|
+
}
|
|
672
|
+
return path;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
export { createWebpack };
|