@infly/vue2-vite 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +307 -0
- package/bin/infly-vue2-vite.mjs +12 -0
- package/bin/vue-cli-service.mjs +135 -0
- package/package.json +71 -0
- package/src/cli/arguments.mjs +197 -0
- package/src/cli/node-version.mjs +46 -0
- package/src/cli/run.mjs +122 -0
- package/src/compat/assets.mjs +165 -0
- package/src/compat/commonjs.mjs +93 -0
- package/src/compat/empty-stub.mjs +58 -0
- package/src/compat/html.mjs +244 -0
- package/src/compat/import-interop.mjs +200 -0
- package/src/compat/jsx.mjs +142 -0
- package/src/compat/mock.mjs +125 -0
- package/src/compat/require-context.mjs +157 -0
- package/src/compat/router.mjs +62 -0
- package/src/compat/sass-export.mjs +112 -0
- package/src/compat/sass-importer.mjs +30 -0
- package/src/compat/svg-icons.mjs +35 -0
- package/src/config/discover.mjs +261 -0
- package/src/config/env.mjs +148 -0
- package/src/config/merge.mjs +65 -0
- package/src/config/project-config.mjs +87 -0
- package/src/config/registered.mjs +152 -0
- package/src/config/validation.mjs +122 -0
- package/src/config/vue-cli-reader.mjs +207 -0
- package/src/factory/build-policy.mjs +39 -0
- package/src/factory/create-vite-config.mjs +648 -0
- package/src/factory/manual-chunks.mjs +55 -0
- package/src/factory/output-names.mjs +62 -0
- package/src/index.mjs +55 -0
- package/src/runner/build.mjs +43 -0
- package/src/runner/serve.mjs +44 -0
- package/src/runner/signals.mjs +29 -0
- package/src/runner/test.mjs +190 -0
- package/src/runner/workspace.cjs +346 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { spawn } = require('node:child_process');
|
|
4
|
+
|
|
5
|
+
function resolvePackageViteBin() {
|
|
6
|
+
try {
|
|
7
|
+
const packageJsonPath = require.resolve('vite/package.json', {
|
|
8
|
+
paths: [__dirname],
|
|
9
|
+
});
|
|
10
|
+
const viteBin = path.join(path.dirname(packageJsonPath), 'bin', 'vite.js');
|
|
11
|
+
if (!fs.existsSync(viteBin)) {
|
|
12
|
+
throw new Error(`Vite CLI 不存在: ${viteBin}`);
|
|
13
|
+
}
|
|
14
|
+
return viteBin;
|
|
15
|
+
} catch (error) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`无法从 @infly/vue2-vite 的直接依赖解析 Vite CLI;请重新运行 pnpm install。`
|
|
18
|
+
+ ` ${error.message}`,
|
|
19
|
+
{ cause: error },
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function createWorkspaceRunner({
|
|
25
|
+
registry,
|
|
26
|
+
getTargetConfig,
|
|
27
|
+
createConfigPath,
|
|
28
|
+
defaultRootDir = process.cwd(),
|
|
29
|
+
resolveViteBin = resolvePackageViteBin,
|
|
30
|
+
spawnProcess = spawn,
|
|
31
|
+
}) {
|
|
32
|
+
if (!Array.isArray(registry)) throw new TypeError('registry 必须是数组');
|
|
33
|
+
if (typeof getTargetConfig !== 'function') {
|
|
34
|
+
throw new TypeError('getTargetConfig 必须是函数');
|
|
35
|
+
}
|
|
36
|
+
if (typeof createConfigPath !== 'function') {
|
|
37
|
+
throw new TypeError('createConfigPath 必须是函数');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function validateOutputDir(app, outputDir, rootDir = defaultRootDir) {
|
|
41
|
+
if (!app.outputRoot) throw new Error(`应用 ${app.id} 未登记 outputRoot`);
|
|
42
|
+
const resolvedRoot = path.resolve(rootDir, app.outputRoot);
|
|
43
|
+
const resolvedOutput = path.resolve(rootDir, outputDir);
|
|
44
|
+
const relative = path.relative(resolvedRoot, resolvedOutput);
|
|
45
|
+
if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
46
|
+
throw new Error(`拒绝未登记或不安全的输出目录: ${resolvedOutput}`);
|
|
47
|
+
}
|
|
48
|
+
const registered = Object.values(app.targets).some(
|
|
49
|
+
(targetConfig) => path.resolve(rootDir, targetConfig.outputDir) === resolvedOutput,
|
|
50
|
+
);
|
|
51
|
+
if (!registered) {
|
|
52
|
+
throw new Error(`输出目录不属于 ${app.id} 的注册 target: ${resolvedOutput}`);
|
|
53
|
+
}
|
|
54
|
+
return resolvedOutput;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function matchAppPackage(pkg) {
|
|
58
|
+
const packageDir = (pkg.dir || '').replace(/\\/g, '/');
|
|
59
|
+
return registry.find((app) => {
|
|
60
|
+
const appDir = app.subProjectPath.replace(/\\/g, '/');
|
|
61
|
+
return packageDir === appDir || pkg.name === app.id || packageDir.endsWith(appDir);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function resolveConfigPath(app, rootDir) {
|
|
66
|
+
const configPath = path.resolve(createConfigPath(app, rootDir));
|
|
67
|
+
if (!fs.existsSync(configPath)) {
|
|
68
|
+
throw new Error(`Vite 配置文件不存在: ${configPath}`);
|
|
69
|
+
}
|
|
70
|
+
return configPath;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function resolveApplicationVersion(app, rootDir) {
|
|
74
|
+
const packageJsonPath = path.resolve(rootDir, app.subProjectPath, 'package.json');
|
|
75
|
+
let packageJson;
|
|
76
|
+
try {
|
|
77
|
+
packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
78
|
+
} catch (error) {
|
|
79
|
+
throw new Error(`无法读取应用 package.json: ${packageJsonPath}`, { cause: error });
|
|
80
|
+
}
|
|
81
|
+
if (typeof packageJson.version !== 'string' || packageJson.version.length === 0) {
|
|
82
|
+
throw new Error(`应用 package.json 缺少有效 version: ${packageJsonPath}`);
|
|
83
|
+
}
|
|
84
|
+
return packageJson.version;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function spawnViteDev(app, options = {}) {
|
|
88
|
+
const rootDir = options.rootDir || defaultRootDir;
|
|
89
|
+
const target = options.target || app.defaultTarget;
|
|
90
|
+
const targetConfig = getTargetConfig(app, target);
|
|
91
|
+
const mode = options.mode || 'development';
|
|
92
|
+
const configPath = resolveConfigPath(app, rootDir);
|
|
93
|
+
const appVersion = resolveApplicationVersion(app, rootDir);
|
|
94
|
+
const env = {
|
|
95
|
+
...process.env,
|
|
96
|
+
INFLY_VITE_APP_ID: app.id,
|
|
97
|
+
INFLY_VITE_APP_VERSION: appVersion,
|
|
98
|
+
VUE_APP_PLATFORM: target,
|
|
99
|
+
NODE_ENV: mode,
|
|
100
|
+
ENV: mode,
|
|
101
|
+
};
|
|
102
|
+
const args = [
|
|
103
|
+
resolveViteBin(),
|
|
104
|
+
'--config',
|
|
105
|
+
configPath,
|
|
106
|
+
'--mode',
|
|
107
|
+
mode,
|
|
108
|
+
'--port',
|
|
109
|
+
String(options.port || targetConfig.port),
|
|
110
|
+
];
|
|
111
|
+
if (options.host) {
|
|
112
|
+
args.push('--host', String(options.host));
|
|
113
|
+
}
|
|
114
|
+
return spawnProcess(process.execPath, args, {
|
|
115
|
+
cwd: rootDir,
|
|
116
|
+
stdio: 'inherit',
|
|
117
|
+
env,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function spawnViteBuild(app, options = {}) {
|
|
122
|
+
return new Promise((resolve, reject) => {
|
|
123
|
+
const rootDir = options.rootDir || defaultRootDir;
|
|
124
|
+
const target = options.target || app.defaultTarget;
|
|
125
|
+
const mode = options.mode || 'production';
|
|
126
|
+
const viteMode = mode === 'prod'
|
|
127
|
+
? 'production'
|
|
128
|
+
: mode === 'stage'
|
|
129
|
+
? 'staging'
|
|
130
|
+
: mode;
|
|
131
|
+
let configPath;
|
|
132
|
+
try {
|
|
133
|
+
configPath = resolveConfigPath(app, rootDir);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
reject(error);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const targetConfig = getTargetConfig(app, target);
|
|
140
|
+
let appVersion;
|
|
141
|
+
try {
|
|
142
|
+
appVersion = resolveApplicationVersion(app, rootDir);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
reject(error);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const outputDir = options.outputDir || targetConfig.outputDir;
|
|
148
|
+
let safeOutputDir;
|
|
149
|
+
try {
|
|
150
|
+
safeOutputDir = validateOutputDir(app, outputDir, rootDir);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
reject(error);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const env = {
|
|
157
|
+
...process.env,
|
|
158
|
+
INFLY_VITE_APP_ID: app.id,
|
|
159
|
+
INFLY_VITE_APP_VERSION: appVersion,
|
|
160
|
+
VUE_APP_PLATFORM: target,
|
|
161
|
+
NODE_ENV: viteMode === 'development' ? 'development' : 'production',
|
|
162
|
+
ENV: mode,
|
|
163
|
+
};
|
|
164
|
+
const args = [
|
|
165
|
+
resolveViteBin(),
|
|
166
|
+
'build',
|
|
167
|
+
'--config',
|
|
168
|
+
configPath,
|
|
169
|
+
'--mode',
|
|
170
|
+
viteMode,
|
|
171
|
+
'--outDir',
|
|
172
|
+
safeOutputDir,
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
if (options.dryRun) {
|
|
176
|
+
console.log(`[dry-run] @infly/vue2-vite build: ${args.join(' ')}`);
|
|
177
|
+
console.log(
|
|
178
|
+
`[dry-run] env: INFLY_VITE_APP_ID=${app.id}`
|
|
179
|
+
+ ` INFLY_VITE_APP_VERSION=${appVersion}`
|
|
180
|
+
+ ` VUE_APP_PLATFORM=${target} NODE_ENV=${env.NODE_ENV}`,
|
|
181
|
+
);
|
|
182
|
+
resolve({ code: 0, signal: null });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const child = spawnProcess(process.execPath, args, {
|
|
187
|
+
cwd: rootDir,
|
|
188
|
+
stdio: 'inherit',
|
|
189
|
+
env,
|
|
190
|
+
});
|
|
191
|
+
buildProcesses.add(child);
|
|
192
|
+
child.on('exit', (code, signal) => {
|
|
193
|
+
buildProcesses.delete(child);
|
|
194
|
+
if (code === 0) {
|
|
195
|
+
resolve({ code, signal });
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
reject(new Error(
|
|
199
|
+
`Vite build 失败 (${app.id}, target=${target}): exit code ${code}`
|
|
200
|
+
+ `${signal ? `, signal ${signal}` : ''}`,
|
|
201
|
+
));
|
|
202
|
+
});
|
|
203
|
+
child.on('error', (error) => {
|
|
204
|
+
buildProcesses.delete(child);
|
|
205
|
+
reject(new Error(`Vite build 进程错误 (${app.id}): ${error.message}`));
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 构建期活跃子进程(信号处理用)
|
|
211
|
+
const buildProcesses = new Set();
|
|
212
|
+
|
|
213
|
+
function killAll(processes) {
|
|
214
|
+
for (const child of processes) {
|
|
215
|
+
if (!child.killed && child.exitCode === null) {
|
|
216
|
+
child._killed = true;
|
|
217
|
+
child.kill('SIGTERM');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function viteDev(packages, options = {}) {
|
|
223
|
+
const processes = [];
|
|
224
|
+
for (const pkg of packages) {
|
|
225
|
+
const app = matchAppPackage(pkg);
|
|
226
|
+
if (!app) {
|
|
227
|
+
console.warn(`[vite-dev] 跳过未注册的应用: ${pkg.name}`);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const targetLabel = pkg.target || options.target;
|
|
231
|
+
console.log(`[vite-dev] 启动 ${app.id}${targetLabel ? ` (target=${targetLabel})` : ''} (@infly/vue2-vite)`);
|
|
232
|
+
const child = spawnViteDev(app, { ...options, target: targetLabel });
|
|
233
|
+
processes.push(child);
|
|
234
|
+
child.on('exit', (code, signal) => {
|
|
235
|
+
if (code !== 0 && !child._killed) {
|
|
236
|
+
console.error(
|
|
237
|
+
`[vite-dev] ${app.id} 异常退出 (code=${code}, signal=${signal}),终止所有进程`,
|
|
238
|
+
);
|
|
239
|
+
killAll(processes);
|
|
240
|
+
process.exit(code || 1);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
|
|
246
|
+
process.on(signal, () => {
|
|
247
|
+
killAll(processes);
|
|
248
|
+
process.exit(0);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
return processes;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function viteBuild(packages, options = {}) {
|
|
255
|
+
const mode = options.mode || 'stage';
|
|
256
|
+
const concurrency = options.preset === 'all' ? 1 : 2;
|
|
257
|
+
|
|
258
|
+
// 信号处理:Ctrl+C 时终止构建子进程并立即退出(与 dev 链一致)
|
|
259
|
+
const onBuildSignal = (signal) => {
|
|
260
|
+
killAll([...buildProcesses]);
|
|
261
|
+
process.exit(signal === 'SIGINT' ? 130 : 143);
|
|
262
|
+
};
|
|
263
|
+
for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
264
|
+
if (!process.listeners(signal).includes(onBuildSignal)) {
|
|
265
|
+
process.on(signal, onBuildSignal);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const tasks = [];
|
|
269
|
+
for (const pkg of packages) {
|
|
270
|
+
const app = matchAppPackage(pkg);
|
|
271
|
+
if (!app) {
|
|
272
|
+
console.warn(`[vite-build] 跳过未注册的应用: ${pkg.name}`);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
const pkgTarget = pkg.target || options.target;
|
|
276
|
+
if (pkgTarget) {
|
|
277
|
+
getTargetConfig(app, pkgTarget);
|
|
278
|
+
tasks.push({ app, target: pkgTarget });
|
|
279
|
+
} else if (options.preset) {
|
|
280
|
+
tasks.push(...Object.keys(app.targets).map((target) => ({ app, target })));
|
|
281
|
+
} else {
|
|
282
|
+
tasks.push({ app, target: app.defaultTarget });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (tasks.length === 0) {
|
|
287
|
+
console.log('[vite-build] 没有需要 Vite 构建的任务');
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const groups = new Map();
|
|
292
|
+
for (const task of tasks) {
|
|
293
|
+
if (!groups.has(task.app.id)) groups.set(task.app.id, []);
|
|
294
|
+
groups.get(task.app.id).push(task);
|
|
295
|
+
}
|
|
296
|
+
const appList = [...groups.values()];
|
|
297
|
+
const errors = [];
|
|
298
|
+
let index = 0;
|
|
299
|
+
|
|
300
|
+
async function runNext() {
|
|
301
|
+
const appTasks = appList[index++];
|
|
302
|
+
if (!appTasks) return;
|
|
303
|
+
try {
|
|
304
|
+
for (const task of appTasks) {
|
|
305
|
+
const targetConfig = getTargetConfig(task.app, task.target);
|
|
306
|
+
console.log(`[vite-build] 构建 ${task.app.id} (target=${task.target})`);
|
|
307
|
+
await spawnViteBuild(task.app, {
|
|
308
|
+
...options,
|
|
309
|
+
target: task.target,
|
|
310
|
+
mode,
|
|
311
|
+
outputDir: targetConfig.outputDir,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
} catch (error) {
|
|
315
|
+
errors.push(error.message);
|
|
316
|
+
}
|
|
317
|
+
await runNext();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
await Promise.all(
|
|
321
|
+
Array.from(
|
|
322
|
+
{ length: Math.min(concurrency, appList.length) },
|
|
323
|
+
() => runNext(),
|
|
324
|
+
),
|
|
325
|
+
);
|
|
326
|
+
if (errors.length > 0) {
|
|
327
|
+
throw new Error(`Vite build 失败:\n${errors.join('\n')}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
matchAppPackage,
|
|
333
|
+
spawnViteDev,
|
|
334
|
+
spawnViteBuild,
|
|
335
|
+
viteDev,
|
|
336
|
+
viteBuild,
|
|
337
|
+
killAll,
|
|
338
|
+
resolveViteBin,
|
|
339
|
+
validateOutputDir,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
module.exports = {
|
|
344
|
+
createWorkspaceRunner,
|
|
345
|
+
resolvePackageViteBin,
|
|
346
|
+
};
|