coda-builder 1.0.1 → 1.2.2

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/build/index.mjs CHANGED
@@ -1 +1,825 @@
1
- var c=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,i)=>(typeof require<"u"?require:e)[i]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});import W from"webpack-node-externals";import{BundleAnalyzerPlugin as O}from"webpack-bundle-analyzer";import x from"path";import S from"os";import A from"webpackbar";var u={js:/\.(ts|js)$/,css:/\.css$/,image:/\.(png|jpg|jpeg|gif|svg)$/,font:/\.(woff|woff2|eot|ttf|otf)$/,media:/\.(mp4|webm|ogg|mp3|wav)$/},b=(t={},e="development")=>{let i=S.cpus().length||4;return{name:t.name||"ts-thread",workers:t.workers||(e==="development"?2:Math.max(1,Math.floor((i-2)/2))),workerParallelJobs:t.workerParallelJobs||(e==="development"?40:20),workerNodeArgs:Array.isArray(t.workerNodeArgs)?t.workerNodeArgs:[t.workerNodeArgs||(e==="development"?"--max-old-space-size=512":"--max-old-space-size=256")],poolRespawn:t.poolRespawn??e!=="development",poolTimeout:t.poolTimeout||500,poolParallelJobs:t.poolParallelJobs||(e==="development"?200:100)}},P=(t=!1,e)=>{let i=[];if(e?.thread){let o=b(e.workerOptions,e.mode);i.push({loader:"thread-loader",options:o})}return i.push({loader:"ts-loader",options:{compilerOptions:{declaration:!1,declarationMap:!1}}}),{mode:"none",output:{path:x.resolve(__dirname,"dist"),clean:!0},resolve:{extensions:[".ts",".js"],modules:["node_modules"]},module:{rules:[{test:u.js,use:i,exclude:/node_modules/},{test:u.css,use:[t?{loader:c("mini-css-extract-plugin").loader}:"style-loader",{loader:"css-loader",options:{sourceMap:!0,importLoaders:1}},"postcss-loader"]},{test:u.image,type:"asset/resource",generator:{filename:"assets/img/[hash][ext]"}},{test:u.font,type:"asset/resource",generator:{filename:"assets/font/[name][ext]"}},{test:u.media,type:"asset/resource",generator:{filename:"assets/media/[name][ext]"}}]},plugins:[new A({name:"coda-builder",color:"#0052cc"})],devServer:{historyApiFallback:!0,port:8080,compress:!1,open:!1,hot:!0,static:{directory:x.join(process.cwd(),"dist")}},stats:"minimal"}};var h=class{config;constructor(e){this.config={...e}}setMode(e){return this.config.mode=e,this}setEntry(e){return this.config.entry=e,this}setContext(e){return e!==void 0&&(this.config.context=e),this}setOutput(e){return e?(this.config.output={...this.config.output,...e},this):this}setDevtool(e){return e!==void 0&&(this.config.devtool=e),this}setTarget(e){return e!==void 0&&(this.config.target=e),this}setCache(e){return e!==void 0&&(this.config.cache=e),this}setResolve(e,i){let o=i?.modules,s=i?.mainFields,n=i?.alias,r=i?.extensions,a=i?.extensionAlias;return this.config.resolve={...this.config.resolve,...o?{modules:o}:{},...s?{mainFields:s}:{},...n?{alias:n}:{},...r?{extensions:r}:{},...a?{extensionAlias:a}:{},...e},this}setModule(e,i){let o=this.config.module?.rules||[],s=i?.rules||[],n=i?.noParse;return this.config.module={...this.config.module||{},...n?{noParse:n}:{},rules:[...o,...s],...e},this}setOptimization(e,i,o,s,n,r,a){let l=i;l===void 0&&(s==="web"||s==="chrome-ext")&&(l={chunks:"all",minSize:2e4,maxSize:2e5,minChunks:1,maxAsyncRequests:6,maxInitialRequests:6,cacheGroups:{libs:{name:"chunk-libs",test:/[\\/]node_modules[\\/]/,chunks:"initial",priority:20,reuseExistingChunk:!0},common:{name:"chunk-common",minChunks:2,priority:2,reuseExistingChunk:!0}}});let m=r!==void 0?r:n==="production"||n==="analyzer",p=e?.minimizer||this.config.optimization?.minimizer;if(m&&!p)try{let y=c("terser-webpack-plugin"),C=c("css-minimizer-webpack-plugin"),z=o!==void 0?o:{parse:{ecma:8},compress:{ecma:5,comparisons:!1,inline:2,warnings:!1},mangle:!0,module:!1,output:{ecma:5,comments:!1},toplevel:!1,ie8:!1,safari10:!1},v=a||2;p=[new y({parallel:v,terserOptions:z}),new C({parallel:v})]}catch{}let f=e?.runtimeChunk||this.config.optimization?.runtimeChunk;return l!==void 0&&!f&&(f={name:"manifest"}),this.config.optimization={...this.config.optimization||{},minimize:m,...l!==void 0?{splitChunks:l}:{},...p!==void 0?{minimizer:p}:{},...f!==void 0?{runtimeChunk:f}:{},...e},this}setStats(e){return e!==void 0&&(this.config.stats=e),this}setDevServer(e){if(!e)return this;let i=this.config.devServer||{};return this.config.devServer={...i,...e,...e.static?{static:typeof e.static=="object"&&typeof i.static=="object"?{...i.static,...e.static}:e.static}:{}},this}setWatch(e){return e!==void 0&&(this.config.watch=e),this}setWatchOptions(e){return e?(this.config.watchOptions={...this.config.watchOptions,...e},this):this}setExternals(e){return e!==void 0&&(this.config.externals=e),this}addPlugin(e){return this.config.plugins=this.config.plugins||[],this.config.plugins.push(e),this}addPlugins(e){return!e||!Array.isArray(e)?this:(e.forEach(i=>{i&&this.addPlugin(i)}),this)}setMaxChunks(e){if(e===void 0)return this;let i=c("webpack");return this.addPlugin(new i.optimize.LimitChunkCountPlugin({maxChunks:e})),this}getConfig(){return this.config}setConfig(e){return this.config={...this.config,...e},this}};import d from"fs";import k from"path";var g=class{rules;constructor(e=[]){this.rules=e}apply(e){e.hooks.thisCompilation.tap("CodaCopyWebpackPlugin",i=>{i.hooks.processAssets.tap({name:"CodaCopyWebpackPlugin",stage:e.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS},()=>{this.rules.forEach(o=>{d.existsSync(o.from)&&(o.type==="single"?this.processSingleFile(i,o.from,o.to):o.type==="directory"?this.processDirectory(i,o.from,o.to,o.ignore||[]):o.type==="template"&&this.processTemplate(i,o.from,o.to,o.data))})})})}processSingleFile(e,i,o){let s=d.readFileSync(i);e.emitAsset(o,new e.compiler.webpack.sources.RawSource(s))}processDirectory(e,i,o,s){d.readdirSync(i,{recursive:!0}).forEach(r=>{let a=k.join(i,r);if(d.statSync(a).isDirectory())return;let l=k.relative(i,a);if(s.some(p=>l.includes(p)))return;let w=k.join(o,l).replace(/\\/g,"/"),m=d.readFileSync(a);e.emitAsset(w,new e.compiler.webpack.sources.RawSource(m))})}processTemplate(e,i,o,s){let n=d.readFileSync(i,"utf-8");Object.keys(s).forEach(r=>{let a=r.replace(/{{\s*|\s*}}/g,""),l=new RegExp(`{{\\s*${a}\\s*}}`,"g");n=n.replace(l,String(s[r]))}),e.emitAsset(o,new e.compiler.webpack.sources.RawSource(Buffer.from(n)))}};function M(t){if(!t.techRoute)throw new Error("[coda-builder] Missing required config: techRoute");if(!t.platformTarget)throw new Error("[coda-builder] Missing required config: platformTarget");if(!t.mode)throw new Error("[coda-builder] Missing required config: mode");if(!t.entry)throw new Error("[coda-builder] Missing required config: entry")}function E(t){let e=t.mode==="production"||t.mode==="analyzer";return P(e,t)}function R(t,e){let{platformTarget:i}=t;["nodejs-cli","node-server","vscode-ext"].includes(i)?(e.setTarget("node"),i==="vscode-ext"?e.setExternals([W(),{vscode:"commonjs vscode"}]):e.setExternals([W()])):i==="chrome-ext"&&e.setTarget("web")}function j(t,e){let i=t.mode==="analyzer"?"production":t.mode;e.setMode(i).setEntry(t.entry).setContext(t.context).setOutput(t.output).setDevtool(t.devtool).setCache(t.cache).setMaxChunks(t.maxChunks).setStats(t.stats).setDevServer(t.devServer).setWatch(t.watch).setWatchOptions(t.watchOptions),e.setOptimization(t.optimization,t.splitChunks,t.terserOptions,t.platformTarget,i,t.minimize,t.parallel),e.setResolve(t.resolve,{alias:t.alias,extensions:t.extensions,extensionAlias:t.extensionAlias,modules:t.modules,mainFields:t.mainFields}),e.setModule(t.module,{noParse:t.noParse,rules:t.rules}),t.target&&e.setTarget(t.target),t.externals&&e.setExternals(t.externals)}function q(t,e){let i=t.mode==="production"||t.mode==="analyzer";if(t.copyRules&&t.copyRules.length>0&&e.addPlugin(new g(t.copyRules)),t.html)try{let o=c("html-webpack-plugin"),s=typeof t.html=="object"?t.html:{};e.addPlugin(new o(s))}catch{throw new Error('[coda-builder] html-webpack-plugin is required when using "html" config. Please install it.')}if(i)try{let o=c("mini-css-extract-plugin");e.addPlugin(new o({filename:"css/[name].[contenthash:8].css",chunkFilename:"css/[name].[contenthash:8].chunk.css"}))}catch{throw new Error("[coda-builder] mini-css-extract-plugin is required in production mode. Please install it.")}if(t.gzip!==!1&&(t.gzip||i))try{let o=c("compression-webpack-plugin"),s={filename:"[path][base].gz",algorithm:"gzip",test:/\.(js|css|png|jpg|jpeg|svg)(\?.*)?$/i,threshold:8192,minRatio:.8,deleteOriginalAssets:!1,exclude:[/node_modules/]},n=typeof t.gzip=="object"?{...s,...t.gzip}:s;e.addPlugin(new o(n))}catch{throw new Error('[coda-builder] compression-webpack-plugin is required when using "gzip" config. Please install it.')}t.plugins&&t.plugins.length>0&&e.addPlugins(t.plugins),t.mode==="analyzer"&&e.addPlugin(new O({analyzerMode:"server"}))}function T(t,e){if(t.configureWebpack&&typeof t.configureWebpack=="function"){let i=t.configureWebpack(e,t.mode);if(i)return i}return e}function N(t,e){if(M(t),t.thread&&t.mode==="development")try{let n=c("thread-loader"),r=b(t.workerOptions||{name:"ts-thread",workers:2,workerParallelJobs:40,workerNodeArgs:"--max-old-space-size=512",poolRespawn:!1,poolParallelJobs:200},t.mode);n.warmup({workerBabelArgs:r},["ts-loader"])}catch{}let i=E(t),o=new h(i);o.setConfig({context:process.cwd()}),R(t,o),j(t,o),q(t,o);let s=o.getConfig();if(s=T(t,s),t.speedMeasure&&(t.mode==="development"||t.mode==="analyzer"))try{let n=c("speed-measure-webpack-v5-plugin");s=new n().wrap(s)}catch{throw new Error('[coda-builder] speed-measure-webpack-v5-plugin is required when using "speedMeasure" config. Please install it.')}return e&&(console.log("webpack\u914D\u7F6E:"),console.dir(i,{depth:null})),s}export{N as resolveWebpackConfig};
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/pipeline.ts
9
+ import nodeExternals from "webpack-node-externals";
10
+ import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
11
+
12
+ // src/default.config.ts
13
+ import path from "path";
14
+ import os from "os";
15
+ import WebpackBar from "webpackbar";
16
+ var ruleMap = {
17
+ js: /\.(ts|js)$/,
18
+ css: /\.css$/,
19
+ image: /\.(png|jpg|jpeg|gif|svg)$/,
20
+ font: /\.(woff|woff2|eot|ttf|otf)$/,
21
+ media: /\.(mp4|webm|ogg|mp3|wav)$/
22
+ };
23
+ var setWorkerOptions = (option = {}, mode = "development") => {
24
+ const CPULen = os.cpus().length || 4;
25
+ return {
26
+ name: option.name || "ts-thread",
27
+ workers: option.workers || (mode === "development" ? 2 : Math.max(1, Math.floor((CPULen - 2) / 2))),
28
+ workerParallelJobs: option.workerParallelJobs || (mode === "development" ? 40 : 20),
29
+ workerNodeArgs: Array.isArray(option.workerNodeArgs) ? option.workerNodeArgs : [
30
+ option.workerNodeArgs || (mode === "development" ? "--max-old-space-size=512" : "--max-old-space-size=256")
31
+ ],
32
+ poolRespawn: option.poolRespawn ?? (mode === "development" ? false : true),
33
+ poolTimeout: option.poolTimeout || 500,
34
+ poolParallelJobs: option.poolParallelJobs || (mode === "development" ? 200 : 100)
35
+ };
36
+ };
37
+ var getDefaultWebpackConfig = (isProduction = false, userConfig) => {
38
+ const techRoute = userConfig?.techRoute;
39
+ const isReact = techRoute === "react";
40
+ const isVue = techRoute === "vue";
41
+ const jsTestRule = isReact || isVue ? /\.(ts|tsx|js|jsx)$/ : /\.(ts|js)$/;
42
+ const defaultExtensions = isReact ? [".tsx", ".ts", ".jsx", ".js"] : isVue ? [".vue", ".tsx", ".ts", ".jsx", ".js"] : [".ts", ".js"];
43
+ const tsLoaderUse = [];
44
+ if (userConfig?.thread) {
45
+ const workerOptions = setWorkerOptions(userConfig.workerOptions, userConfig.mode);
46
+ tsLoaderUse.push({
47
+ loader: "thread-loader",
48
+ options: workerOptions
49
+ });
50
+ }
51
+ const tsLoaderCompilerOptions = {
52
+ declaration: false,
53
+ declarationMap: false
54
+ };
55
+ if (isReact) {
56
+ tsLoaderCompilerOptions.jsx = "react-jsx";
57
+ }
58
+ tsLoaderUse.push({
59
+ loader: "ts-loader",
60
+ options: {
61
+ //强制关闭 Webpack 打包时生成 .d.ts 声明文件,避免多余的目录嵌套
62
+ compilerOptions: tsLoaderCompilerOptions
63
+ }
64
+ });
65
+ const rules = [
66
+ {
67
+ test: jsTestRule,
68
+ use: tsLoaderUse,
69
+ exclude: /node_modules/
70
+ }
71
+ ];
72
+ if (isVue) {
73
+ let vueLoaderPath = "vue-loader";
74
+ try {
75
+ vueLoaderPath = __require.resolve("vue-loader", { paths: [process.cwd()] });
76
+ } catch (e) {
77
+ }
78
+ rules.push({
79
+ test: /\.vue$/,
80
+ loader: vueLoaderPath
81
+ });
82
+ }
83
+ rules.push(
84
+ {
85
+ test: ruleMap.css,
86
+ use: [
87
+ // 生产/分析模式下使用 MiniCssExtractPlugin 提取 CSS,开发模式使用 style-loader 注入 DOM
88
+ isProduction ? {
89
+ loader: __require("mini-css-extract-plugin").loader
90
+ } : "style-loader",
91
+ {
92
+ loader: "css-loader",
93
+ options: {
94
+ sourceMap: true,
95
+ importLoaders: 1
96
+ }
97
+ },
98
+ "postcss-loader"
99
+ ]
100
+ },
101
+ {
102
+ test: ruleMap.image,
103
+ type: "asset/resource",
104
+ generator: {
105
+ filename: "assets/img/[hash][ext]"
106
+ }
107
+ },
108
+ {
109
+ test: ruleMap.font,
110
+ type: "asset/resource",
111
+ generator: {
112
+ filename: "assets/font/[name][ext]"
113
+ }
114
+ },
115
+ {
116
+ test: ruleMap.media,
117
+ type: "asset/resource",
118
+ generator: {
119
+ filename: "assets/media/[name][ext]"
120
+ }
121
+ }
122
+ );
123
+ return {
124
+ mode: "none",
125
+ output: {
126
+ path: path.resolve(__dirname, "dist"),
127
+ clean: true
128
+ },
129
+ resolve: {
130
+ // 限制 extensions 数量以提升编译性能
131
+ extensions: defaultExtensions,
132
+ modules: ["node_modules"]
133
+ },
134
+ module: {
135
+ rules
136
+ },
137
+ plugins: [
138
+ new WebpackBar({
139
+ name: "coda-builder",
140
+ color: "#0052cc"
141
+ })
142
+ ],
143
+ devServer: {
144
+ historyApiFallback: true,
145
+ port: 8080,
146
+ compress: false,
147
+ open: false,
148
+ hot: true,
149
+ static: {
150
+ directory: path.join(process.cwd(), "dist")
151
+ }
152
+ },
153
+ stats: "minimal",
154
+ devtool: userConfig?.mode === "development" ? "source-map" : "cheap-module-source-map"
155
+ };
156
+ };
157
+
158
+ // src/mutator.ts
159
+ var WebpackConfigMutator = class {
160
+ config;
161
+ /**
162
+ * 构造函数
163
+ * @param {WebpackConfiguration} baseConfig 基础 Webpack 配置对象
164
+ */
165
+ constructor(baseConfig) {
166
+ this.config = { ...baseConfig };
167
+ }
168
+ /**
169
+ * 设置运行模式
170
+ * @param {WebpackConfiguration['mode']} mode 运行模式 (development | production | none)
171
+ * @returns {this} 返回当前实例支持链式调用
172
+ */
173
+ setMode(mode) {
174
+ this.config.mode = mode;
175
+ return this;
176
+ }
177
+ /**
178
+ * 设置脚本入口 entry
179
+ * @param {WebpackConfiguration['entry']} entry 入口配置
180
+ * @returns {this} 返回当前实例
181
+ */
182
+ setEntry(entry) {
183
+ this.config.entry = entry;
184
+ return this;
185
+ }
186
+ /**
187
+ * 设置webpack执行的根目录
188
+ * @param {WebpackConfiguration['context']} context 执行目录
189
+ * @returns {this} 返回当前实例
190
+ */
191
+ setContext(context) {
192
+ if (context !== void 0) this.config.context = context;
193
+ return this;
194
+ }
195
+ /**
196
+ * 设置构建输出 output
197
+ * @param {WebpackConfiguration['output']} [output] 输出配置选项
198
+ * @returns {this} 返回当前实例
199
+ */
200
+ setOutput(output) {
201
+ if (!output) return this;
202
+ this.config.output = {
203
+ ...this.config.output,
204
+ ...output
205
+ };
206
+ return this;
207
+ }
208
+ /**
209
+ * 设置 source map 生成方式 devtool
210
+ * @param {WebpackConfiguration['devtool']} [devtool] devtool 属性
211
+ * @returns {this} 返回当前实例
212
+ */
213
+ setDevtool(devtool) {
214
+ if (devtool !== void 0) this.config.devtool = devtool;
215
+ return this;
216
+ }
217
+ /**
218
+ * 设置编译目标环境 target
219
+ * @param {WebpackConfiguration['target']} [target] 目标环境
220
+ * @returns {this} 返回当前实例
221
+ */
222
+ setTarget(target) {
223
+ if (target !== void 0) this.config.target = target;
224
+ return this;
225
+ }
226
+ /**
227
+ * 设置缓存配置 cache
228
+ * @param {WebpackConfiguration['cache']} [cache] 缓存设置
229
+ * @returns {this} 返回当前实例
230
+ */
231
+ setCache(cache) {
232
+ if (cache !== void 0) this.config.cache = cache;
233
+ return this;
234
+ }
235
+ /**
236
+ * 设置 Webpack 原生 resolve,同时兼容合并快捷属性 alias/extensions/extensionAlias/modules/mainFields
237
+ * 规则:Webpack 原生属性优先,快捷属性作为补充兜底
238
+ */
239
+ setResolve(resolve, shortcuts) {
240
+ const resolvedModules = shortcuts?.modules;
241
+ const resolvedMainFields = shortcuts?.mainFields;
242
+ const resolvedAlias = shortcuts?.alias;
243
+ const resolvedExtensions = shortcuts?.extensions;
244
+ const resolvedExtensionAlias = shortcuts?.extensionAlias;
245
+ this.config.resolve = {
246
+ ...this.config.resolve,
247
+ ...resolvedModules ? { modules: resolvedModules } : {},
248
+ ...resolvedMainFields ? { mainFields: resolvedMainFields } : {},
249
+ ...resolvedAlias ? { alias: resolvedAlias } : {},
250
+ ...resolvedExtensions ? { extensions: resolvedExtensions } : {},
251
+ ...resolvedExtensionAlias ? { extensionAlias: resolvedExtensionAlias } : {},
252
+ ...resolve
253
+ // <--- 原生 resolve 放在最后,确保“以 Webpack 自身属性为主”
254
+ };
255
+ return this;
256
+ }
257
+ /**
258
+ * 设置 Webpack 原生 module,同时兼容合并 noParse 与 自定义 rules
259
+ * 规则:Webpack 原生 module 优先
260
+ */
261
+ setModule(moduleConfig, shortcuts) {
262
+ const defaultRules = this.config.module?.rules || [];
263
+ const shortcutRules = shortcuts?.rules || [];
264
+ const shortcutNoParse = shortcuts?.noParse;
265
+ this.config.module = {
266
+ ...this.config.module || {},
267
+ ...shortcutNoParse ? { noParse: shortcutNoParse } : {},
268
+ // 用户自定义 rules 应当排在默认 rules 前面,确保用户规则优先匹配
269
+ rules: [...shortcutRules, ...defaultRules],
270
+ ...moduleConfig
271
+ // <--- 原生 module 放在最后,确保“以 Webpack 自身属性为主”
272
+ };
273
+ return this;
274
+ }
275
+ /**
276
+ * 设置代码优化配置 optimization(集成 splitChunks 智能平台适配、JS/CSS 双重压缩与 runtimeChunk 映射)
277
+ * 规则:Webpack 原生 optimization 优先,其次是快捷属性,最后是平台智能兜底
278
+ */
279
+ setOptimization(optimization, shortcutSplitChunks, shortcutTerserOptions, techRoute, mode, minimize, parallel) {
280
+ let finalSplitChunks = shortcutSplitChunks;
281
+ if (finalSplitChunks === void 0) {
282
+ if (techRoute && ["sdk", "react", "vue", "chrome-ext"].includes(techRoute)) {
283
+ finalSplitChunks = {
284
+ chunks: "all",
285
+ minSize: 2e4,
286
+ maxSize: 2e5,
287
+ minChunks: 1,
288
+ maxAsyncRequests: 6,
289
+ maxInitialRequests: 6,
290
+ cacheGroups: {
291
+ libs: {
292
+ name: "chunk-libs",
293
+ test: /[\\/]node_modules[\\/]/,
294
+ chunks: "initial",
295
+ priority: 20,
296
+ reuseExistingChunk: true
297
+ },
298
+ common: {
299
+ name: "chunk-common",
300
+ minChunks: 2,
301
+ priority: 2,
302
+ reuseExistingChunk: true
303
+ }
304
+ }
305
+ };
306
+ }
307
+ }
308
+ const isProdOrAnalyzer = mode === "production" || mode === "analyzer";
309
+ const shouldMinimize = minimize !== void 0 ? minimize : isProdOrAnalyzer;
310
+ let minimizer = optimization?.minimizer || this.config.optimization?.minimizer;
311
+ if (shouldMinimize && !minimizer) {
312
+ try {
313
+ const TerserPlugin = __require("terser-webpack-plugin");
314
+ const CssMinimizerPlugin = __require("css-minimizer-webpack-plugin");
315
+ const defaultTerserOptions = {
316
+ parse: { ecma: 8 },
317
+ compress: {
318
+ ecma: 5,
319
+ comparisons: false,
320
+ inline: 2,
321
+ warnings: false
322
+ },
323
+ mangle: true,
324
+ module: false,
325
+ output: {
326
+ ecma: 5,
327
+ comments: false
328
+ },
329
+ toplevel: false,
330
+ ie8: false,
331
+ safari10: false
332
+ };
333
+ const finalTerserOptions = shortcutTerserOptions !== void 0 ? shortcutTerserOptions : defaultTerserOptions;
334
+ const cpuParallel = parallel || 2;
335
+ minimizer = [
336
+ new TerserPlugin({
337
+ parallel: cpuParallel,
338
+ terserOptions: finalTerserOptions
339
+ }),
340
+ new CssMinimizerPlugin({
341
+ parallel: cpuParallel
342
+ })
343
+ ];
344
+ } catch (e) {
345
+ }
346
+ }
347
+ let runtimeChunk = optimization?.runtimeChunk || this.config.optimization?.runtimeChunk;
348
+ if (finalSplitChunks !== void 0 && !runtimeChunk) {
349
+ runtimeChunk = { name: "manifest" };
350
+ }
351
+ this.config.optimization = {
352
+ ...this.config.optimization || {},
353
+ minimize: shouldMinimize,
354
+ ...finalSplitChunks !== void 0 ? { splitChunks: finalSplitChunks } : {},
355
+ ...minimizer !== void 0 ? { minimizer } : {},
356
+ ...runtimeChunk !== void 0 ? { runtimeChunk } : {},
357
+ ...optimization
358
+ // <--- 原生 optimization 放在最后,确保“以 Webpack 自身属性为主”
359
+ };
360
+ return this;
361
+ }
362
+ /**
363
+ * 设置构建日志输出控制 stats
364
+ * @param {WebpackConfiguration['stats']} [stats] 日志输出控制配置
365
+ * @returns {this} 返回当前实例支持链式调用
366
+ */
367
+ setStats(stats) {
368
+ if (stats !== void 0) this.config.stats = stats;
369
+ return this;
370
+ }
371
+ /**
372
+ * 设置Dev Server(支持深度合并嵌套对象如 static)
373
+ * @param {DevServerConfiguration} [devServer] devServer 配置
374
+ * @returns {this} 返回当前实例支持链式调用
375
+ */
376
+ setDevServer(devServer) {
377
+ if (!devServer) return this;
378
+ const existingDevServer = this.config.devServer || {};
379
+ this.config.devServer = {
380
+ ...existingDevServer,
381
+ ...devServer,
382
+ // 精准深度合并 static 配置对象,防止外部覆盖时丢失默认目录
383
+ ...devServer.static ? {
384
+ static: typeof devServer.static === "object" && typeof existingDevServer.static === "object" ? {
385
+ ...existingDevServer.static,
386
+ ...devServer.static
387
+ } : devServer.static
388
+ } : {}
389
+ };
390
+ return this;
391
+ }
392
+ /**
393
+ * 设置监听文件变化
394
+ * @param {WebpackConfiguration[watch]} [watch] 监听文件变化
395
+ * @returns {this} 返回当前实例支持链式调用
396
+ */
397
+ setWatch(watch) {
398
+ if (watch !== void 0) this.config.watch = watch;
399
+ return this;
400
+ }
401
+ /**
402
+ * 设置监听项
403
+ * @param {WebpackConfiguration[watch]} [watchOptions] 监听项
404
+ * @returns {this} 返回当前实例支持链式调用
405
+ */
406
+ setWatchOptions(watchOptions) {
407
+ if (!watchOptions) return this;
408
+ this.config.watchOptions = {
409
+ ...this.config.watchOptions,
410
+ ...watchOptions
411
+ };
412
+ return this;
413
+ }
414
+ /**
415
+ * 设置外部依赖处理 externals
416
+ * @param {WebpackConfiguration['externals']} [externals] 外部依赖项
417
+ * @returns {this} 返回当前实例
418
+ */
419
+ setExternals(externals) {
420
+ if (externals !== void 0) {
421
+ this.config.externals = externals;
422
+ }
423
+ return this;
424
+ }
425
+ /**
426
+ * 添加 Webpack 插件
427
+ * @param {WebpackPluginInstance} plugin 插件实例
428
+ * @returns {this} 返回当前实例
429
+ */
430
+ addPlugin(plugin) {
431
+ this.config.plugins = this.config.plugins || [];
432
+ this.config.plugins.push(plugin);
433
+ return this;
434
+ }
435
+ /**
436
+ * 批量添加自定义插件
437
+ * @param {NonNullable<WebpackConfiguration['plugins']>} [plugins] 插件数组
438
+ * @returns {this} 返回当前实例支持链式调用
439
+ */
440
+ addPlugins(plugins) {
441
+ if (!plugins || !Array.isArray(plugins)) return this;
442
+ plugins.forEach((plugin) => {
443
+ if (plugin) {
444
+ this.addPlugin(plugin);
445
+ }
446
+ });
447
+ return this;
448
+ }
449
+ /**
450
+ * 限制每个入口生成的最大 Chunk 数
451
+ * @param {number} [maxChunks] 最大 Chunk 数量
452
+ * @returns {this} 返回当前实例
453
+ */
454
+ setMaxChunks(maxChunks) {
455
+ if (maxChunks === void 0) return this;
456
+ const webpack = __require("webpack");
457
+ this.addPlugin(
458
+ new webpack.optimize.LimitChunkCountPlugin({
459
+ maxChunks
460
+ })
461
+ );
462
+ return this;
463
+ }
464
+ /**
465
+ * 获取最终组装好的 Webpack 配置对象
466
+ * @returns {WebpackConfiguration} 完整的 Webpack 配置
467
+ */
468
+ getConfig() {
469
+ return this.config;
470
+ }
471
+ /**
472
+ * 批量或直接合并设置 Webpack 原始配置对象属性
473
+ * @param {Partial<WebpackConfiguration>} partialConfig 部分配置对象
474
+ * @returns {this} 返回当前实例
475
+ */
476
+ setConfig(partialConfig) {
477
+ this.config = {
478
+ ...this.config,
479
+ ...partialConfig
480
+ };
481
+ return this;
482
+ }
483
+ };
484
+
485
+ // src/webpack.copy.plugin.ts
486
+ import fs from "fs";
487
+ import path2 from "path";
488
+ var CodaCopyWebpackPlugin = class {
489
+ rules;
490
+ /**
491
+ * 构造函数
492
+ * @param {CopyRuleItem[]} [rules=[]] 复制规则数组
493
+ */
494
+ constructor(rules = []) {
495
+ this.rules = rules;
496
+ }
497
+ /**
498
+ * Webpack 插件应用入口
499
+ * @param {Compiler} compiler Webpack 编译器实例
500
+ */
501
+ apply(compiler) {
502
+ compiler.hooks.thisCompilation.tap("CodaCopyWebpackPlugin", (compilation) => {
503
+ compilation.hooks.processAssets.tap(
504
+ {
505
+ name: "CodaCopyWebpackPlugin",
506
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
507
+ },
508
+ () => {
509
+ this.rules.forEach((rule) => {
510
+ if (!fs.existsSync(rule.from)) return;
511
+ if (rule.type === "single") {
512
+ this.processSingleFile(compilation, rule.from, rule.to);
513
+ } else if (rule.type === "directory") {
514
+ this.processDirectory(compilation, rule.from, rule.to, rule.ignore || []);
515
+ } else if (rule.type === "template") {
516
+ this.processTemplate(compilation, rule.from, rule.to, rule.data);
517
+ }
518
+ });
519
+ }
520
+ );
521
+ });
522
+ }
523
+ /**
524
+ * 处理单文件复制规则
525
+ * @private
526
+ * @param {Compilation} compilation Webpack 编译对象
527
+ * @param {string} from 源文件绝对路径
528
+ * @param {string} to 目标输出相对路径
529
+ */
530
+ processSingleFile(compilation, from, to) {
531
+ const content = fs.readFileSync(from);
532
+ compilation.emitAsset(to, new compilation.compiler.webpack.sources.RawSource(content));
533
+ }
534
+ /**
535
+ * 处理目录递归复制规则
536
+ * @private
537
+ * @param {Compilation} compilation Webpack 编译对象
538
+ * @param {string} srcDir 源目录路径
539
+ * @param {string} destDir 目标输出目录路径
540
+ * @param {string[]} ignore 忽略匹配模式数组
541
+ */
542
+ processDirectory(compilation, srcDir, destDir, ignore) {
543
+ const files = fs.readdirSync(srcDir, { recursive: true });
544
+ files.forEach((file) => {
545
+ const srcPath = path2.join(srcDir, file);
546
+ if (fs.statSync(srcPath).isDirectory()) return;
547
+ const relativePath = path2.relative(srcDir, srcPath);
548
+ if (ignore.some((pattern) => relativePath.includes(pattern))) return;
549
+ const targetPath = path2.join(destDir, relativePath).replace(/\\/g, "/");
550
+ const content = fs.readFileSync(srcPath);
551
+ compilation.emitAsset(
552
+ targetPath,
553
+ new compilation.compiler.webpack.sources.RawSource(content)
554
+ );
555
+ });
556
+ }
557
+ /**
558
+ * 处理模板文件占位符替换规则
559
+ * @private
560
+ * @param {Compilation} compilation Webpack 编译对象
561
+ * @param {string} from 模板文件源路径
562
+ * @param {string} to 目标输出路径
563
+ * @param {Record<string, string | number>} data 占位符键值对数据
564
+ */
565
+ processTemplate(compilation, from, to, data) {
566
+ let content = fs.readFileSync(from, "utf-8");
567
+ Object.keys(data).forEach((rawKey) => {
568
+ const cleanKey = rawKey.replace(/{{\s*|\s*}}/g, "");
569
+ const escapedKey = cleanKey.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
570
+ const reg = new RegExp(`{{\\s*${escapedKey}\\s*}}`, "g");
571
+ content = content.replace(reg, String(data[rawKey]));
572
+ });
573
+ compilation.emitAsset(
574
+ to,
575
+ new compilation.compiler.webpack.sources.RawSource(Buffer.from(content))
576
+ );
577
+ }
578
+ };
579
+
580
+ // src/pipeline.ts
581
+ function stageValidate(userConfig) {
582
+ if (!userConfig.techRoute) throw new Error("[coda-builder] Missing required config: techRoute");
583
+ if (!userConfig.mode) throw new Error("[coda-builder] Missing required config: mode");
584
+ if (!userConfig.entry) throw new Error("[coda-builder] Missing required config: entry");
585
+ try {
586
+ const webpack = __require("webpack");
587
+ const version = webpack.version || "0.0.0";
588
+ const [major, minor] = version.split(".").map(Number);
589
+ if (major < 5 || major === 5 && minor < 91) {
590
+ throw new Error(
591
+ `[coda-builder] Webpack version 5.91.0 or higher is required, but detected v${version}. Please upgrade your project's webpack.`
592
+ );
593
+ }
594
+ } catch (e) {
595
+ if (e.message && e.message.includes("[coda-builder]")) throw e;
596
+ throw new Error(
597
+ "[coda-builder] Failed to detect webpack version. Please ensure webpack is installed."
598
+ );
599
+ }
600
+ }
601
+ function stageGetBaseConfig(userConfig) {
602
+ const isProduction = userConfig.mode === "production" || userConfig.mode === "analyzer";
603
+ return getDefaultWebpackConfig(isProduction, userConfig);
604
+ }
605
+ function stageSetPlatformParam(userConfig, mutator) {
606
+ const { techRoute } = userConfig;
607
+ if (["node-server", "node-cli", "vscode-ext"].includes(techRoute)) {
608
+ mutator.setTarget("node");
609
+ if (techRoute === "vscode-ext") {
610
+ mutator.setExternals([nodeExternals(), { vscode: "commonjs vscode" }]);
611
+ } else {
612
+ mutator.setExternals([nodeExternals()]);
613
+ }
614
+ } else if (["sdk", "react", "vue", "chrome-ext"].includes(techRoute)) {
615
+ mutator.setTarget("web");
616
+ }
617
+ }
618
+ function stageSetFeatures(userConfig, mutator) {
619
+ const webpackMode = userConfig.mode === "analyzer" ? "production" : userConfig.mode;
620
+ mutator.setMode(webpackMode).setEntry(userConfig.entry).setContext(userConfig.context).setOutput(userConfig.output).setDevtool(userConfig.devtool).setCache(userConfig.cache).setMaxChunks(userConfig.maxChunks).setStats(userConfig.stats).setDevServer(userConfig.devServer).setWatch(userConfig.watch).setWatchOptions(userConfig.watchOptions);
621
+ mutator.setOptimization(
622
+ userConfig.optimization,
623
+ userConfig.splitChunks,
624
+ userConfig.terserOptions,
625
+ userConfig.techRoute,
626
+ webpackMode,
627
+ userConfig.minimize,
628
+ userConfig.parallel
629
+ );
630
+ mutator.setResolve(userConfig.resolve, {
631
+ alias: userConfig.alias,
632
+ extensions: userConfig.extensions,
633
+ extensionAlias: userConfig.extensionAlias,
634
+ modules: userConfig.modules,
635
+ mainFields: userConfig.mainFields
636
+ });
637
+ mutator.setModule(userConfig.module, {
638
+ noParse: userConfig.noParse,
639
+ rules: userConfig.rules
640
+ });
641
+ if (userConfig.target) {
642
+ mutator.setTarget(userConfig.target);
643
+ }
644
+ if (userConfig.externals) {
645
+ mutator.setExternals(userConfig.externals);
646
+ }
647
+ }
648
+ function stageSetPlugins(userConfig, mutator) {
649
+ const isProductionOrAnalyzer = userConfig.mode === "production" || userConfig.mode === "analyzer";
650
+ if (userConfig.copyRules && userConfig.copyRules.length > 0) {
651
+ mutator.addPlugin(new CodaCopyWebpackPlugin(userConfig.copyRules));
652
+ }
653
+ if (userConfig.html) {
654
+ try {
655
+ const HtmlWebpackPlugin = __require("html-webpack-plugin");
656
+ const htmlOptions = typeof userConfig.html === "object" ? userConfig.html : {};
657
+ mutator.addPlugin(new HtmlWebpackPlugin(htmlOptions));
658
+ } catch (e) {
659
+ throw new Error(
660
+ '[coda-builder] html-webpack-plugin is required when using "html" config. Please install it.'
661
+ );
662
+ }
663
+ }
664
+ if (isProductionOrAnalyzer) {
665
+ try {
666
+ const MiniCssExtractPlugin = __require("mini-css-extract-plugin");
667
+ mutator.addPlugin(
668
+ new MiniCssExtractPlugin({
669
+ filename: "css/[name].[contenthash:8].css",
670
+ chunkFilename: "css/[name].[contenthash:8].chunk.css"
671
+ })
672
+ );
673
+ } catch (e) {
674
+ throw new Error(
675
+ "[coda-builder] mini-css-extract-plugin is required in production mode. Please install it."
676
+ );
677
+ }
678
+ }
679
+ if (userConfig.techRoute === "vue") {
680
+ try {
681
+ const fs2 = __require("fs");
682
+ const path3 = __require("path");
683
+ const pkgPath = path3.resolve(process.cwd(), "package.json");
684
+ let isVue2 = false;
685
+ if (fs2.existsSync(pkgPath)) {
686
+ const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
687
+ const vueVersion = pkg.dependencies?.vue || pkg.devDependencies?.vue || "3.0.0";
688
+ isVue2 = typeof vueVersion === "string" && (vueVersion.startsWith("2") || vueVersion.includes("^2"));
689
+ }
690
+ const paths = [process.cwd()];
691
+ const vueLoader = __require(__require.resolve("vue-loader", { paths }));
692
+ let vueLoaderPluginInstance;
693
+ if (isVue2) {
694
+ const VueLoaderPlugin = __require(__require.resolve("vue-loader/lib/plugin", { paths }));
695
+ vueLoaderPluginInstance = new VueLoaderPlugin();
696
+ } else {
697
+ const VueLoaderPlugin = vueLoader.VueLoaderPlugin || vueLoader.default?.VueLoaderPlugin;
698
+ vueLoaderPluginInstance = new VueLoaderPlugin();
699
+ }
700
+ mutator.addPlugin(vueLoaderPluginInstance);
701
+ } catch (e) {
702
+ throw new Error(
703
+ `[coda-builder] vue-loader is required when using techRoute: "vue". Please ensure "vue" and "vue-loader" are installed in your project. Details: ${e.message}`
704
+ );
705
+ }
706
+ }
707
+ if (userConfig.gzip !== false && (userConfig.gzip || isProductionOrAnalyzer)) {
708
+ try {
709
+ const CompressionPlugin = __require("compression-webpack-plugin");
710
+ const defaultGzipOptions = {
711
+ filename: "[path][base].gz",
712
+ algorithm: "gzip",
713
+ test: /\.(js|css|png|jpg|jpeg|svg)(\?.*)?$/i,
714
+ threshold: 8192,
715
+ minRatio: 0.8,
716
+ deleteOriginalAssets: false,
717
+ exclude: [/node_modules/]
718
+ };
719
+ const gzipOptions = typeof userConfig.gzip === "object" ? { ...defaultGzipOptions, ...userConfig.gzip } : defaultGzipOptions;
720
+ mutator.addPlugin(new CompressionPlugin(gzipOptions));
721
+ } catch (e) {
722
+ throw new Error(
723
+ '[coda-builder] compression-webpack-plugin is required when using "gzip" config. Please install it.'
724
+ );
725
+ }
726
+ }
727
+ if (userConfig.plugins && userConfig.plugins.length > 0) {
728
+ mutator.addPlugins(userConfig.plugins);
729
+ }
730
+ if (userConfig.mode === "analyzer") {
731
+ mutator.addPlugin(new BundleAnalyzerPlugin({ analyzerMode: "server" }));
732
+ }
733
+ if (userConfig.techRoute === "node-cli") {
734
+ try {
735
+ const fs2 = __require("fs");
736
+ const path3 = __require("path");
737
+ const webpack = __require("webpack");
738
+ const pkgPath = path3.resolve(process.cwd(), "package.json");
739
+ if (fs2.existsSync(pkgPath)) {
740
+ const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
741
+ if (pkg.bin) {
742
+ let binPaths = [];
743
+ if (typeof pkg.bin === "string") {
744
+ binPaths = [pkg.bin];
745
+ } else if (typeof pkg.bin === "object" && pkg.bin !== null) {
746
+ binPaths = Object.values(pkg.bin);
747
+ }
748
+ binPaths.forEach((binPath) => {
749
+ const cleanPath = binPath.replace(/^\.\//, "");
750
+ const parts = cleanPath.split("/");
751
+ const fileName = parts.pop() || "index.js";
752
+ const targetRegex = new RegExp(`(^|[\\\\/])${fileName}$`);
753
+ mutator.addPlugin(
754
+ new webpack.BannerPlugin({
755
+ banner: "#!/usr/bin/env node",
756
+ raw: true,
757
+ // 作为原始文本置顶插入,不加注释符
758
+ test: targetRegex
759
+ // 精准匹配,绝不误伤其他子模块
760
+ })
761
+ );
762
+ });
763
+ }
764
+ }
765
+ } catch (e) {
766
+ }
767
+ }
768
+ }
769
+ function stageSetHooks(userConfig, config) {
770
+ if (userConfig.configureWebpack && typeof userConfig.configureWebpack === "function") {
771
+ const result = userConfig.configureWebpack(config, userConfig.mode);
772
+ return result || config;
773
+ }
774
+ return config;
775
+ }
776
+ function resolveWebpackConfig(userConfig, isLogger) {
777
+ stageValidate(userConfig);
778
+ if (userConfig.thread && userConfig.mode === "development") {
779
+ try {
780
+ const threadLoader = __require("thread-loader");
781
+ const workerOptions = setWorkerOptions(
782
+ userConfig.workerOptions || {
783
+ name: "ts-thread",
784
+ workers: 2,
785
+ workerParallelJobs: 40,
786
+ workerNodeArgs: "--max-old-space-size=512",
787
+ poolRespawn: false,
788
+ poolParallelJobs: 200
789
+ },
790
+ userConfig.mode
791
+ );
792
+ threadLoader.warmup(workerOptions, ["ts-loader"]);
793
+ } catch (e) {
794
+ }
795
+ }
796
+ const baseConfig = stageGetBaseConfig(userConfig);
797
+ const mutator = new WebpackConfigMutator(baseConfig);
798
+ mutator.setConfig({
799
+ context: process.cwd()
800
+ });
801
+ stageSetPlatformParam(userConfig, mutator);
802
+ stageSetFeatures(userConfig, mutator);
803
+ stageSetPlugins(userConfig, mutator);
804
+ let finalConfig = mutator.getConfig();
805
+ finalConfig = stageSetHooks(userConfig, finalConfig);
806
+ if (userConfig.speedMeasure && (userConfig.mode === "development" || userConfig.mode === "analyzer")) {
807
+ try {
808
+ const SpeedMeasurePlugin = __require("speed-measure-webpack-v5-plugin");
809
+ const smp = new SpeedMeasurePlugin();
810
+ finalConfig = smp.wrap(finalConfig);
811
+ } catch (e) {
812
+ throw new Error(
813
+ '[coda-builder] speed-measure-webpack-v5-plugin is required when using "speedMeasure" config. Please install it.'
814
+ );
815
+ }
816
+ }
817
+ if (isLogger) {
818
+ console.log("webpack\u914D\u7F6E:");
819
+ console.dir(baseConfig, { depth: null });
820
+ }
821
+ return finalConfig;
822
+ }
823
+ export {
824
+ resolveWebpackConfig
825
+ };