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