fe-stack 0.0.15 → 0.0.16
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/package.json +1 -1
- package/vite-plugin-rollup-dts.d.ts +12 -0
- package/vite-plugin-rollup-dts.js +240 -70
- package/vite.config.base.js +14 -3
package/package.json
CHANGED
|
@@ -20,6 +20,18 @@ export interface RollupDtsPluginOptions {
|
|
|
20
20
|
entries?: Entry[];
|
|
21
21
|
/** tsconfig.json 文件路径,默认为 './tsconfig.json' */
|
|
22
22
|
tsconfigPath?: string;
|
|
23
|
+
/**
|
|
24
|
+
* 共享类型入口配置(多入口时使用)
|
|
25
|
+
* 指定一个共享类型的入口,会被 API Extractor 单独处理
|
|
26
|
+
* 其他入口会自动 import 这个共享类型文件
|
|
27
|
+
* @example { input: './dist/types.d.ts', output: './dist/shared.d.ts' }
|
|
28
|
+
*/
|
|
29
|
+
sharedTypes?: {
|
|
30
|
+
/** 共享类型的 .d.ts 入口文件路径 */
|
|
31
|
+
input: string;
|
|
32
|
+
/** 合并后的输出路径 */
|
|
33
|
+
output: string;
|
|
34
|
+
} | null;
|
|
23
35
|
}
|
|
24
36
|
|
|
25
37
|
/**
|
|
@@ -94,8 +94,195 @@ function getEntriesFromConfig(config) {
|
|
|
94
94
|
}));
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* 使用 API Extractor 处理单个入口
|
|
99
|
+
*/
|
|
100
|
+
function rollupEntry(entry, projectFolder, tsconfigPath) {
|
|
101
|
+
const mainEntryPointFilePath = path.resolve(
|
|
102
|
+
projectFolder,
|
|
103
|
+
entry.mainEntryPoint,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
if (!fs.existsSync(mainEntryPointFilePath)) {
|
|
107
|
+
console.warn(
|
|
108
|
+
`[rollup-dts] ⚠️ Skipping ${entry.name}: ${entry.mainEntryPoint} not found`,
|
|
109
|
+
);
|
|
110
|
+
return { success: false, skipped: true };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
console.log(`[rollup-dts] 📦 Rolling up types for: ${entry.name}`);
|
|
114
|
+
|
|
115
|
+
const config = {
|
|
116
|
+
...baseConfig,
|
|
117
|
+
compiler: {
|
|
118
|
+
tsconfigFilePath: `<projectFolder>/${tsconfigPath}`,
|
|
119
|
+
},
|
|
120
|
+
mainEntryPointFilePath: `<projectFolder>/${entry.mainEntryPoint}`,
|
|
121
|
+
dtsRollup: {
|
|
122
|
+
enabled: true,
|
|
123
|
+
untrimmedFilePath: `<projectFolder>/${entry.output}`,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const safeName = entry.name.replace(/\//g, '-');
|
|
128
|
+
const tempConfigPath = path.resolve(
|
|
129
|
+
projectFolder,
|
|
130
|
+
`api-extractor.${safeName}.tmp.json`,
|
|
131
|
+
);
|
|
132
|
+
fs.writeFileSync(tempConfigPath, JSON.stringify(config, null, 2));
|
|
133
|
+
|
|
134
|
+
let success = false;
|
|
135
|
+
try {
|
|
136
|
+
const extractorConfig = ExtractorConfig.loadFileAndPrepare(tempConfigPath);
|
|
137
|
+
const extractorResult = Extractor.invoke(extractorConfig, {
|
|
138
|
+
localBuild: true,
|
|
139
|
+
showVerboseMessages: false,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
if (extractorResult.succeeded) {
|
|
143
|
+
console.log(
|
|
144
|
+
`[rollup-dts] ✅ ${entry.name}: types rolled up successfully`,
|
|
145
|
+
);
|
|
146
|
+
success = true;
|
|
147
|
+
} else {
|
|
148
|
+
console.warn(
|
|
149
|
+
`[rollup-dts] ⚠️ ${entry.name}: API Extractor completed with warnings/errors`,
|
|
150
|
+
);
|
|
151
|
+
if (fs.existsSync(path.resolve(projectFolder, entry.output))) {
|
|
152
|
+
success = true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.warn(
|
|
157
|
+
`[rollup-dts] ⚠️ ${entry.name}: Error during rollup, keeping original .d.ts files`,
|
|
158
|
+
);
|
|
159
|
+
console.warn(` ${error instanceof Error ? error.message : error}`);
|
|
160
|
+
} finally {
|
|
161
|
+
if (fs.existsSync(tempConfigPath)) {
|
|
162
|
+
fs.unlinkSync(tempConfigPath);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { success, skipped: false };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 从 .d.ts 文件中提取导出的类型名称
|
|
171
|
+
*/
|
|
172
|
+
function extractExportedTypeNames(filePath) {
|
|
173
|
+
if (!fs.existsSync(filePath)) return [];
|
|
174
|
+
|
|
175
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
176
|
+
const names = [];
|
|
177
|
+
|
|
178
|
+
// 匹配 export declare interface/type/enum/class/namespace Name
|
|
179
|
+
const regex =
|
|
180
|
+
/export\s+(?:declare\s+)?(?:interface|type|enum|class|namespace)\s+(\w+)/g;
|
|
181
|
+
let match;
|
|
182
|
+
// biome-ignore lint/suspicious/noAssignInExpressions: <explanation>
|
|
183
|
+
while ((match = regex.exec(content)) !== null) {
|
|
184
|
+
names.push(match[1]);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return names;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 在入口文件中添加对共享类型的 import,并移除重复的类型定义
|
|
192
|
+
*/
|
|
193
|
+
function addSharedTypeImports(entryOutputPath, sharedTypesOutput, typeNames) {
|
|
194
|
+
if (!fs.existsSync(entryOutputPath) || typeNames.length === 0) return;
|
|
195
|
+
|
|
196
|
+
let content = fs.readFileSync(entryOutputPath, 'utf-8');
|
|
197
|
+
|
|
198
|
+
// 检查文件中使用了哪些共享类型
|
|
199
|
+
const usedTypes = typeNames.filter((name) => {
|
|
200
|
+
// 检查是否在文件中使用了这个类型
|
|
201
|
+
const useRegex = new RegExp(`\\b${name}\\b`);
|
|
202
|
+
return useRegex.test(content);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
if (usedTypes.length === 0) return;
|
|
206
|
+
|
|
207
|
+
// 移除文件中重复的类型定义(这些类型已经在 shared.d.ts 中定义了)
|
|
208
|
+
for (const typeName of usedTypes) {
|
|
209
|
+
// 使用更可靠的方式:按行处理,找到类型定义的开始和结束
|
|
210
|
+
const lines = content.split('\n');
|
|
211
|
+
const newLines = [];
|
|
212
|
+
let skipUntilClosingBrace = false;
|
|
213
|
+
let braceCount = 0;
|
|
214
|
+
|
|
215
|
+
for (let i = 0; i < lines.length; i++) {
|
|
216
|
+
const line = lines[i];
|
|
217
|
+
|
|
218
|
+
// 检查是否是要移除的类型定义的开始
|
|
219
|
+
const typeDefStart = new RegExp(
|
|
220
|
+
`^export\\s+declare\\s+(?:interface|type|enum|class|namespace)\\s+${typeName}\\b`,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
if (typeDefStart.test(line) && !skipUntilClosingBrace) {
|
|
224
|
+
// 检查是否是单行定义(type alias)
|
|
225
|
+
if (line.includes('=') && line.includes(';')) {
|
|
226
|
+
// 单行 type alias,直接跳过
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 多行定义,开始跳过
|
|
231
|
+
skipUntilClosingBrace = true;
|
|
232
|
+
braceCount =
|
|
233
|
+
(line.match(/{/g) || []).length - (line.match(/}/g) || []).length;
|
|
234
|
+
|
|
235
|
+
// 如果这一行就闭合了
|
|
236
|
+
if (braceCount <= 0 && line.includes('}')) {
|
|
237
|
+
skipUntilClosingBrace = false;
|
|
238
|
+
}
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (skipUntilClosingBrace) {
|
|
243
|
+
braceCount +=
|
|
244
|
+
(line.match(/{/g) || []).length - (line.match(/}/g) || []).length;
|
|
245
|
+
if (braceCount <= 0) {
|
|
246
|
+
skipUntilClosingBrace = false;
|
|
247
|
+
}
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
newLines.push(line);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
content = newLines.join('\n');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// 清理多余的空行
|
|
258
|
+
content = content.replace(/\n{3,}/g, '\n\n').trim();
|
|
259
|
+
|
|
260
|
+
// 计算相对路径
|
|
261
|
+
const outputDir = path.dirname(entryOutputPath);
|
|
262
|
+
const sharedFilePath = path.resolve(process.cwd(), sharedTypesOutput);
|
|
263
|
+
let relativePath = path
|
|
264
|
+
.relative(outputDir, sharedFilePath)
|
|
265
|
+
.replace(/\\/g, '/');
|
|
266
|
+
|
|
267
|
+
if (!relativePath.startsWith('.')) {
|
|
268
|
+
relativePath = './' + relativePath;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// 添加 import 和 re-export 语句
|
|
272
|
+
const importStatement = `import type { ${usedTypes.join(', ')} } from '${relativePath}';\nexport type { ${usedTypes.join(', ')} } from '${relativePath}';\n\n`;
|
|
273
|
+
content = importStatement + content;
|
|
274
|
+
|
|
275
|
+
fs.writeFileSync(entryOutputPath, content, 'utf-8');
|
|
276
|
+
console.log(
|
|
277
|
+
`[rollup-dts] 🔗 Added shared type imports to ${path.basename(entryOutputPath)}: ${usedTypes.join(', ')}`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
97
281
|
export function rollupDtsPlugin(options = {}) {
|
|
98
|
-
const {
|
|
282
|
+
const {
|
|
283
|
+
tsconfigPath = './tsconfig.json',
|
|
284
|
+
sharedTypes = null, // { input: './dist/types.d.ts', output: './dist/shared.d.ts' }
|
|
285
|
+
} = options;
|
|
99
286
|
let resolvedConfig;
|
|
100
287
|
let entries = [];
|
|
101
288
|
|
|
@@ -118,83 +305,63 @@ export function rollupDtsPlugin(options = {}) {
|
|
|
118
305
|
|
|
119
306
|
const projectFolder = process.cwd();
|
|
120
307
|
const results = [];
|
|
308
|
+
const keepFiles = [];
|
|
121
309
|
|
|
122
310
|
console.log('\n[rollup-dts] Starting to bundle declaration files...');
|
|
123
311
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
312
|
+
// 第一步:如果有共享类型配置,先处理共享类型
|
|
313
|
+
let sharedTypeNames = [];
|
|
314
|
+
if (sharedTypes) {
|
|
315
|
+
console.log('[rollup-dts] 📦 Processing shared types first...');
|
|
316
|
+
const sharedEntry = {
|
|
317
|
+
name: 'shared',
|
|
318
|
+
mainEntryPoint: sharedTypes.input,
|
|
319
|
+
output: sharedTypes.output,
|
|
320
|
+
};
|
|
321
|
+
const result = rollupEntry(sharedEntry, projectFolder, tsconfigPath);
|
|
322
|
+
if (result.success) {
|
|
323
|
+
keepFiles.push(path.resolve(projectFolder, sharedTypes.output));
|
|
324
|
+
// 提取共享类型文件中导出的类型名称
|
|
325
|
+
sharedTypeNames = extractExportedTypeNames(
|
|
326
|
+
path.resolve(projectFolder, sharedTypes.output),
|
|
327
|
+
);
|
|
328
|
+
console.log(
|
|
329
|
+
`[rollup-dts] 📝 Shared types exported: ${sharedTypeNames.join(', ') || 'none'}`,
|
|
133
330
|
);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// 第二步:处理所有入口
|
|
335
|
+
for (const entry of entries) {
|
|
336
|
+
// 跳过共享类型入口(如果它也在 entries 中)
|
|
337
|
+
if (sharedTypes && entry.mainEntryPoint === sharedTypes.input) {
|
|
134
338
|
continue;
|
|
135
339
|
}
|
|
136
340
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const config = {
|
|
140
|
-
...baseConfig,
|
|
141
|
-
compiler: {
|
|
142
|
-
tsconfigFilePath: `<projectFolder>/${tsconfigPath}`,
|
|
143
|
-
},
|
|
144
|
-
mainEntryPointFilePath: `<projectFolder>/${entry.mainEntryPoint}`,
|
|
145
|
-
dtsRollup: {
|
|
146
|
-
enabled: true,
|
|
147
|
-
untrimmedFilePath: `<projectFolder>/${entry.output}`,
|
|
148
|
-
},
|
|
149
|
-
};
|
|
341
|
+
const result = rollupEntry(entry, projectFolder, tsconfigPath);
|
|
342
|
+
results.push({ entry, ...result });
|
|
150
343
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
`api-extractor.${safeName}.tmp.json`,
|
|
156
|
-
);
|
|
157
|
-
fs.writeFileSync(tempConfigPath, JSON.stringify(config, null, 2));
|
|
158
|
-
|
|
159
|
-
let success = false;
|
|
160
|
-
try {
|
|
161
|
-
const extractorConfig =
|
|
162
|
-
ExtractorConfig.loadFileAndPrepare(tempConfigPath);
|
|
163
|
-
const extractorResult = Extractor.invoke(extractorConfig, {
|
|
164
|
-
localBuild: true,
|
|
165
|
-
showVerboseMessages: false,
|
|
166
|
-
});
|
|
344
|
+
if (result.success) {
|
|
345
|
+
keepFiles.push(path.resolve(projectFolder, entry.output));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
167
348
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
349
|
+
// 第三步:为每个入口添加共享类型的 import
|
|
350
|
+
if (sharedTypes && sharedTypeNames.length > 0) {
|
|
351
|
+
console.log('[rollup-dts] 🔗 Adding shared type imports...');
|
|
352
|
+
for (const { entry, success } of results) {
|
|
353
|
+
if (success) {
|
|
354
|
+
addSharedTypeImports(
|
|
355
|
+
path.resolve(projectFolder, entry.output),
|
|
356
|
+
sharedTypes.output,
|
|
357
|
+
sharedTypeNames,
|
|
176
358
|
);
|
|
177
|
-
if (fs.existsSync(path.resolve(projectFolder, entry.output))) {
|
|
178
|
-
success = true;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
} catch (error) {
|
|
182
|
-
console.warn(
|
|
183
|
-
`[rollup-dts] ⚠️ ${entry.name}: Error during rollup, keeping original .d.ts files`,
|
|
184
|
-
);
|
|
185
|
-
console.warn(` ${error instanceof Error ? error.message : error}`);
|
|
186
|
-
} finally {
|
|
187
|
-
if (fs.existsSync(tempConfigPath)) {
|
|
188
|
-
fs.unlinkSync(tempConfigPath);
|
|
189
359
|
}
|
|
190
360
|
}
|
|
191
|
-
|
|
192
|
-
results.push({ entry, success });
|
|
193
361
|
}
|
|
194
362
|
|
|
195
|
-
//
|
|
363
|
+
// 第四步:清理中间文件
|
|
196
364
|
const dirsToKeep = new Set();
|
|
197
|
-
|
|
198
365
|
for (const { entry, success } of results) {
|
|
199
366
|
if (!success) {
|
|
200
367
|
const entryDir = path.dirname(
|
|
@@ -207,15 +374,11 @@ export function rollupDtsPlugin(options = {}) {
|
|
|
207
374
|
}
|
|
208
375
|
}
|
|
209
376
|
|
|
210
|
-
// Clean up only if all entries succeeded
|
|
211
377
|
if (dirsToKeep.size === 0) {
|
|
212
378
|
console.log('[rollup-dts] 🧹 Cleaning up intermediate files...');
|
|
213
379
|
const outDir = resolvedConfig?.build?.outDir || 'dist';
|
|
214
380
|
const distDir = path.resolve(projectFolder, outDir);
|
|
215
|
-
cleanupIntermediateFiles(
|
|
216
|
-
distDir,
|
|
217
|
-
entries.map((e) => path.resolve(projectFolder, e.output)),
|
|
218
|
-
);
|
|
381
|
+
cleanupIntermediateFiles(distDir, keepFiles);
|
|
219
382
|
} else {
|
|
220
383
|
console.log(
|
|
221
384
|
'[rollup-dts] ⚠️ Some entries failed, keeping intermediate files',
|
|
@@ -223,8 +386,15 @@ export function rollupDtsPlugin(options = {}) {
|
|
|
223
386
|
}
|
|
224
387
|
|
|
225
388
|
console.log('[rollup-dts] 📊 Summary:');
|
|
226
|
-
|
|
227
|
-
console.log(`
|
|
389
|
+
if (sharedTypes) {
|
|
390
|
+
console.log(` ✅ shared (${sharedTypeNames.length} types)`);
|
|
391
|
+
}
|
|
392
|
+
for (const { entry, success, skipped } of results) {
|
|
393
|
+
if (skipped) {
|
|
394
|
+
console.log(` ⏭️ ${entry.name} (skipped)`);
|
|
395
|
+
} else {
|
|
396
|
+
console.log(` ${success ? '✅' : '❌'} ${entry.name}`);
|
|
397
|
+
}
|
|
228
398
|
}
|
|
229
399
|
},
|
|
230
400
|
};
|
package/vite.config.base.js
CHANGED
|
@@ -11,9 +11,12 @@ import { rollupDtsPlugin } from './vite-plugin-rollup-dts.js';
|
|
|
11
11
|
/**
|
|
12
12
|
* @typedef {import('vite').UserConfig} UserConfig
|
|
13
13
|
* @typedef {import('vite-plugin-dts').PluginOptions} DtsPluginOptions
|
|
14
|
+
* @typedef {Object} RollupDtsOptions
|
|
15
|
+
* @property {string} [tsconfigPath] - tsconfig.json 路径
|
|
16
|
+
* @property {{ input: string, output: string }} [sharedTypes] - 共享类型配置
|
|
14
17
|
* @typedef {Object} CreateBaseConfigOptions
|
|
15
18
|
* @property {'app' | 'lib'} [mode] - 构建模式:'app' 为 Vue 应用,'lib' 为库打包
|
|
16
|
-
* @property {Partial<DtsPluginOptions>} [dtsConfig] - vite-plugin-dts 配置(仅 lib 模式),会与默认配置合并
|
|
19
|
+
* @property {Partial<DtsPluginOptions> & RollupDtsOptions} [dtsConfig] - vite-plugin-dts 配置(仅 lib 模式),会与默认配置合并
|
|
17
20
|
* @property {UserConfig} [viteConfig] - 额外的 Vite 配置,会与基础配置合并
|
|
18
21
|
*/
|
|
19
22
|
|
|
@@ -67,6 +70,9 @@ export function createBaseConfig(dirname, options = {}) {
|
|
|
67
70
|
|
|
68
71
|
// Lib mode: 提供默认配置
|
|
69
72
|
if (mode === 'lib') {
|
|
73
|
+
// 提取 rollupDts 专用配置
|
|
74
|
+
const { sharedTypes, tsconfigPath, ...dtsOnlyConfig } = dtsConfig;
|
|
75
|
+
|
|
70
76
|
baseConfig.build = {
|
|
71
77
|
copyPublicDir: false,
|
|
72
78
|
lib: {
|
|
@@ -81,12 +87,17 @@ export function createBaseConfig(dirname, options = {}) {
|
|
|
81
87
|
dts({
|
|
82
88
|
include: ['src/**/*.ts'],
|
|
83
89
|
root: dirname,
|
|
84
|
-
...
|
|
90
|
+
...dtsOnlyConfig,
|
|
85
91
|
rollupTypes: false,
|
|
86
92
|
}),
|
|
87
93
|
);
|
|
88
94
|
if (dtsConfig.rollupTypes) {
|
|
89
|
-
baseConfig.plugins.push(
|
|
95
|
+
baseConfig.plugins.push(
|
|
96
|
+
rollupDtsPlugin({
|
|
97
|
+
tsconfigPath,
|
|
98
|
+
sharedTypes,
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
90
101
|
}
|
|
91
102
|
}
|
|
92
103
|
|