@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,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vue.config.js 标准字段读取器
|
|
3
|
+
*
|
|
4
|
+
* 从项目 vue.config.js 提取可自动转换的标准字段。
|
|
5
|
+
* 旧 Vue CLI 配置解析的唯一公共实现。调用方只传入项目目录,不读取根仓注册表。
|
|
6
|
+
*
|
|
7
|
+
* 提取字段(规范 §9.4):
|
|
8
|
+
* publicPath → base
|
|
9
|
+
* outputDir → build.outDir
|
|
10
|
+
* assetsDir → build.assetsDir
|
|
11
|
+
* productionSourceMap → build.sourcemap
|
|
12
|
+
* devServer.port → server.port
|
|
13
|
+
* devServer.proxy → server.proxy
|
|
14
|
+
* css.loaderOptions.scss/sass.additionalData → css.preprocessorOptions
|
|
15
|
+
* configureWebpack.resolve.alias → resolve.alias(仅限普通对象)
|
|
16
|
+
* configureWebpack.name → HTML title
|
|
17
|
+
* transpileDependencies → 兼容转换 include
|
|
18
|
+
*
|
|
19
|
+
* 不能自动转换的字段(规范 §9.4):
|
|
20
|
+
* chainWebpack、函数形式 configureWebpack、webpack plugin、loader rule
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import fs from 'node:fs';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
import { createRequire } from 'node:module';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 读取并解析项目的 vue.config.js,提取标准配置字段
|
|
29
|
+
*
|
|
30
|
+
* @param {string} projectRoot - 项目根目录
|
|
31
|
+
* @param {string} mode - 构建模式
|
|
32
|
+
* @returns {object} { vueConfig, warnings }
|
|
33
|
+
*/
|
|
34
|
+
export function loadVueCliConfig(projectRoot, mode) {
|
|
35
|
+
const vueConfigPath = path.join(projectRoot, 'vue.config.js');
|
|
36
|
+
const warnings = [];
|
|
37
|
+
|
|
38
|
+
const result = {
|
|
39
|
+
base: '/',
|
|
40
|
+
outputDir: null,
|
|
41
|
+
assetsDir: 'static',
|
|
42
|
+
productionSourceMap: false,
|
|
43
|
+
server: { port: undefined, proxy: {} },
|
|
44
|
+
css: { additionalData: '' },
|
|
45
|
+
resolve: { alias: {} },
|
|
46
|
+
html: { title: undefined },
|
|
47
|
+
transpileDependencies: [],
|
|
48
|
+
// 兼容性标记
|
|
49
|
+
hasChainWebpack: false,
|
|
50
|
+
hasFunctionConfigureWebpack: false,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
if (!fs.existsSync(vueConfigPath)) {
|
|
54
|
+
return { vueConfig: result, warnings: ['vue.config.js 不存在'] };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let rawConfig;
|
|
58
|
+
try {
|
|
59
|
+
const projectRequire = createRequire(path.join(projectRoot, 'package.json'));
|
|
60
|
+
rawConfig = projectRequire(vueConfigPath);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
warnings.push(`无法加载 vue.config.js: ${err.message}`);
|
|
63
|
+
return { vueConfig: result, warnings };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!rawConfig || typeof rawConfig !== 'object') {
|
|
67
|
+
warnings.push('vue.config.js 未导出配置对象');
|
|
68
|
+
return { vueConfig: result, warnings };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// publicPath → base
|
|
72
|
+
if (typeof rawConfig.publicPath === 'string') {
|
|
73
|
+
result.base = rawConfig.publicPath;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// outputDir
|
|
77
|
+
if (typeof rawConfig.outputDir === 'string') {
|
|
78
|
+
result.outputDir = rawConfig.outputDir;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// assetsDir
|
|
82
|
+
if (typeof rawConfig.assetsDir === 'string') {
|
|
83
|
+
result.assetsDir = rawConfig.assetsDir;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// productionSourceMap
|
|
87
|
+
if (typeof rawConfig.productionSourceMap === 'boolean') {
|
|
88
|
+
result.productionSourceMap = rawConfig.productionSourceMap;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// devServer
|
|
92
|
+
if (rawConfig.devServer && typeof rawConfig.devServer === 'object') {
|
|
93
|
+
if (typeof rawConfig.devServer.port === 'number') {
|
|
94
|
+
result.server.port = rawConfig.devServer.port;
|
|
95
|
+
} else if (typeof rawConfig.devServer.port === 'string') {
|
|
96
|
+
const parsed = parseInt(rawConfig.devServer.port, 10);
|
|
97
|
+
if (!isNaN(parsed)) result.server.port = parsed;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (rawConfig.devServer.proxy && typeof rawConfig.devServer.proxy === 'object') {
|
|
101
|
+
result.server.proxy = normalizeProxyConfig(rawConfig.devServer.proxy);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// css.loaderOptions
|
|
106
|
+
if (rawConfig.css && rawConfig.css.loaderOptions) {
|
|
107
|
+
const sassOptions = rawConfig.css.loaderOptions.scss || rawConfig.css.loaderOptions.sass;
|
|
108
|
+
if (sassOptions && typeof sassOptions.additionalData === 'string') {
|
|
109
|
+
result.css.additionalData = sassOptions.additionalData;
|
|
110
|
+
} else if (sassOptions && typeof sassOptions.additionalData === 'function') {
|
|
111
|
+
// 函数形式:尝试调用获取字符串
|
|
112
|
+
warnings.push('css.loaderOptions.scss.additionalData 是函数,无法自动转换。请在 infly.vite.config.js 中显式提供。');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// configureWebpack
|
|
117
|
+
if (rawConfig.configureWebpack !== undefined) {
|
|
118
|
+
if (typeof rawConfig.configureWebpack === 'function') {
|
|
119
|
+
result.hasFunctionConfigureWebpack = true;
|
|
120
|
+
warnings.push('configureWebpack 是函数,无法自动转换。请在 infly.vite.config.js 中显式提供 Vite 配置。');
|
|
121
|
+
} else if (typeof rawConfig.configureWebpack === 'object' && rawConfig.configureWebpack !== null) {
|
|
122
|
+
const cw = rawConfig.configureWebpack;
|
|
123
|
+
|
|
124
|
+
// resolve.alias
|
|
125
|
+
if (cw.resolve && cw.resolve.alias && typeof cw.resolve.alias === 'object') {
|
|
126
|
+
for (const [key, value] of Object.entries(cw.resolve.alias)) {
|
|
127
|
+
result.resolve.alias[key] = value;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// name → HTML title
|
|
132
|
+
if (typeof cw.name === 'string') {
|
|
133
|
+
result.html.title = cw.name;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// chainWebpack:仅记录标记(严格兼容模式校验使用)。
|
|
139
|
+
// 其行为已由 @infly/vue2-vite 内置默认能力覆盖(SVG sprite、whitespace、
|
|
140
|
+
// 资源内联、分包、drop console 等),非严格模式不再产生警告。
|
|
141
|
+
if (typeof rawConfig.chainWebpack === 'function') {
|
|
142
|
+
result.hasChainWebpack = true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// transpileDependencies
|
|
146
|
+
if (Array.isArray(rawConfig.transpileDependencies)) {
|
|
147
|
+
result.transpileDependencies = rawConfig.transpileDependencies;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return { vueConfig: result, warnings };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 标准化代理配置(pathRewrite → rewrite 函数)
|
|
155
|
+
*/
|
|
156
|
+
function normalizeProxyConfig(proxy) {
|
|
157
|
+
const result = {};
|
|
158
|
+
for (const [key, entry] of Object.entries(proxy)) {
|
|
159
|
+
if (typeof entry === 'string') {
|
|
160
|
+
result[key] = { target: entry, changeOrigin: true };
|
|
161
|
+
} else if (entry && typeof entry === 'object') {
|
|
162
|
+
const normalized = { ...entry };
|
|
163
|
+
if (normalized.pathRewrite) {
|
|
164
|
+
const rewriteRules = normalized.pathRewrite;
|
|
165
|
+
normalized.rewrite = (p) => {
|
|
166
|
+
let r = p;
|
|
167
|
+
for (const [pattern, replacement] of Object.entries(rewriteRules)) {
|
|
168
|
+
r = r.replace(new RegExp(pattern), replacement);
|
|
169
|
+
}
|
|
170
|
+
return r;
|
|
171
|
+
};
|
|
172
|
+
delete normalized.pathRewrite;
|
|
173
|
+
}
|
|
174
|
+
result[key] = normalized;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* 从 vue.config.js 源码中提取无法通过 require 获取的信息
|
|
182
|
+
* (用于增强 loadVueCliConfig 的结果,提取正则匹配的额外信息)
|
|
183
|
+
*
|
|
184
|
+
* @param {string} projectRoot
|
|
185
|
+
* @returns {object}
|
|
186
|
+
*/
|
|
187
|
+
export function extractAdditionalVueConfigInfo(projectRoot) {
|
|
188
|
+
const vueConfigPath = path.join(projectRoot, 'vue.config.js');
|
|
189
|
+
if (!fs.existsSync(vueConfigPath)) return {};
|
|
190
|
+
|
|
191
|
+
const code = fs.readFileSync(vueConfigPath, 'utf8');
|
|
192
|
+
const result = {};
|
|
193
|
+
|
|
194
|
+
// 检测 devServer.setupMiddlewares 中的 mock-server 引用
|
|
195
|
+
const mockMatch = code.match(/require\(['"]\.\/mock\/mock-server(?:\.js)?['"]\)/);
|
|
196
|
+
if (mockMatch) {
|
|
197
|
+
result.mockEntry = 'mock/mock-server.js';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 检测 configureWebpack 是否包含 favicon
|
|
201
|
+
const faviconMatch = code.match(/favicon\s*:\s*['"]([^'"]+)['"]/);
|
|
202
|
+
if (faviconMatch) {
|
|
203
|
+
result.favicon = faviconMatch[1];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 构建策略
|
|
3
|
+
*
|
|
4
|
+
* 根据 mode 决定 minify/sourcemap 策略。
|
|
5
|
+
* 规范 §12 构建策略矩阵。
|
|
6
|
+
*
|
|
7
|
+
* 从 factory.js resolveBuildPolicy 迁移。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const FORMAL_BUILD_MODES = new Set(['staging', 'production', 'release']);
|
|
11
|
+
/** @type {Record<string, { minify: false|'esbuild', sourcemap: boolean }>} */
|
|
12
|
+
const BUILT_IN_POLICIES = {
|
|
13
|
+
development: { minify: false, sourcemap: true },
|
|
14
|
+
staging: { minify: 'esbuild', sourcemap: false },
|
|
15
|
+
stage: { minify: 'esbuild', sourcemap: false },
|
|
16
|
+
production: { minify: 'esbuild', sourcemap: false },
|
|
17
|
+
prod: { minify: 'esbuild', sourcemap: false },
|
|
18
|
+
release: { minify: 'esbuild', sourcemap: false },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 解析现代构建策略。
|
|
23
|
+
*
|
|
24
|
+
* @param {string} mode - 构建模式
|
|
25
|
+
* @param {object} [overrides] - { minify?, sourcemap? }
|
|
26
|
+
* @returns {{ formal: boolean, minify: false|'esbuild', sourcemap: boolean, reportCompressedSize: boolean }}
|
|
27
|
+
*/
|
|
28
|
+
export function resolveBuildPolicy(mode, overrides = {}) {
|
|
29
|
+
const normalizedMode = mode === 'stage' ? 'staging' : mode === 'prod' ? 'production' : mode;
|
|
30
|
+
const policy = BUILT_IN_POLICIES[normalizedMode] || BUILT_IN_POLICIES.development; // fallback for unknown modes
|
|
31
|
+
|
|
32
|
+
const formal = FORMAL_BUILD_MODES.has(normalizedMode);
|
|
33
|
+
return {
|
|
34
|
+
formal,
|
|
35
|
+
minify: overrides.minify ?? policy.minify,
|
|
36
|
+
sourcemap: overrides.sourcemap ?? policy.sourcemap,
|
|
37
|
+
reportCompressedSize: !formal,
|
|
38
|
+
};
|
|
39
|
+
}
|