fe-stack 0.0.16 → 0.0.17
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 +5 -10
- package/vite-plugin-rollup-dts.js +202 -128
- package/vite.config.base.js +3 -3
package/package.json
CHANGED
|
@@ -21,17 +21,12 @@ export interface RollupDtsPluginOptions {
|
|
|
21
21
|
/** tsconfig.json 文件路径,默认为 './tsconfig.json' */
|
|
22
22
|
tsconfigPath?: string;
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
24
|
+
* 共享类型输出路径(多入口时使用)
|
|
25
|
+
* 插件会自动分析各入口输出,找出在所有入口中都存在的类型声明
|
|
26
|
+
* 将这些共享类型提取到指定文件,并更新各入口文件的 import
|
|
27
|
+
* @example './dist/shared.d.ts'
|
|
28
28
|
*/
|
|
29
|
-
|
|
30
|
-
/** 共享类型的 .d.ts 入口文件路径 */
|
|
31
|
-
input: string;
|
|
32
|
-
/** 合并后的输出路径 */
|
|
33
|
-
output: string;
|
|
34
|
-
} | null;
|
|
29
|
+
sharedTypesOutput?: string | null;
|
|
35
30
|
}
|
|
36
31
|
|
|
37
32
|
/**
|
|
@@ -167,121 +167,206 @@ function rollupEntry(entry, projectFolder, tsconfigPath) {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
/**
|
|
170
|
-
* 从 .d.ts
|
|
170
|
+
* 从 .d.ts 内容中解析类型声明
|
|
171
|
+
* 返回 Map<typeName, { declaration: string, kind: string }>
|
|
171
172
|
*/
|
|
172
|
-
function
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
173
|
+
function parseTypeDeclarations(content) {
|
|
174
|
+
const declarations = new Map();
|
|
175
|
+
const lines = content.split('\n');
|
|
176
|
+
|
|
177
|
+
let i = 0;
|
|
178
|
+
while (i < lines.length) {
|
|
179
|
+
const line = lines[i];
|
|
180
|
+
|
|
181
|
+
// 匹配类型声明开始
|
|
182
|
+
const match = line.match(
|
|
183
|
+
/^export\s+declare\s+(interface|type|enum|class|namespace)\s+(\w+)/,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
if (match) {
|
|
187
|
+
const kind = match[1];
|
|
188
|
+
const name = match[2];
|
|
189
|
+
const declLines = [line];
|
|
190
|
+
|
|
191
|
+
// type alias 可能是单行
|
|
192
|
+
if (kind === 'type' && line.includes('=') && line.includes(';')) {
|
|
193
|
+
declarations.set(name, { declaration: line, kind });
|
|
194
|
+
i++;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 多行声明,收集直到闭合
|
|
199
|
+
let braceCount =
|
|
200
|
+
(line.match(/{/g) || []).length - (line.match(/}/g) || []).length;
|
|
201
|
+
i++;
|
|
202
|
+
|
|
203
|
+
while (i < lines.length && braceCount > 0) {
|
|
204
|
+
declLines.push(lines[i]);
|
|
205
|
+
braceCount +=
|
|
206
|
+
(lines[i].match(/{/g) || []).length -
|
|
207
|
+
(lines[i].match(/}/g) || []).length;
|
|
208
|
+
i++;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
declarations.set(name, { declaration: declLines.join('\n'), kind });
|
|
212
|
+
} else {
|
|
213
|
+
i++;
|
|
214
|
+
}
|
|
185
215
|
}
|
|
186
216
|
|
|
187
|
-
return
|
|
217
|
+
return declarations;
|
|
188
218
|
}
|
|
189
219
|
|
|
190
220
|
/**
|
|
191
|
-
*
|
|
221
|
+
* 从多个输出文件中找出共享的类型声明
|
|
192
222
|
*/
|
|
193
|
-
function
|
|
194
|
-
if (
|
|
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
|
-
);
|
|
223
|
+
function findSharedTypeDeclarations(outputs) {
|
|
224
|
+
if (outputs.length < 2) return new Map();
|
|
222
225
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
226
|
+
// 解析所有文件的类型声明
|
|
227
|
+
const allDeclarations = outputs.map((output) => ({
|
|
228
|
+
name: output.name,
|
|
229
|
+
declarations: parseTypeDeclarations(output.content),
|
|
230
|
+
}));
|
|
229
231
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
(line.match(/{/g) || []).length - (line.match(/}/g) || []).length;
|
|
232
|
+
// 找出在所有文件中都出现的类型名称
|
|
233
|
+
const firstDecls = allDeclarations[0].declarations;
|
|
234
|
+
const sharedTypes = new Map();
|
|
234
235
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
236
|
+
for (const [typeName, typeInfo] of firstDecls) {
|
|
237
|
+
// 检查是否在所有其他文件中都存在
|
|
238
|
+
const existsInAll = allDeclarations
|
|
239
|
+
.slice(1)
|
|
240
|
+
.every((file) => file.declarations.has(typeName));
|
|
241
|
+
|
|
242
|
+
if (existsInAll) {
|
|
243
|
+
sharedTypes.set(typeName, typeInfo);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return sharedTypes;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 从文件内容中移除指定的类型声明
|
|
252
|
+
*/
|
|
253
|
+
function removeTypeDeclarations(content, typeNames) {
|
|
254
|
+
const lines = content.split('\n');
|
|
255
|
+
const newLines = [];
|
|
256
|
+
|
|
257
|
+
let i = 0;
|
|
258
|
+
while (i < lines.length) {
|
|
259
|
+
const line = lines[i];
|
|
260
|
+
|
|
261
|
+
// 检查是否是要移除的类型声明
|
|
262
|
+
const match = line.match(
|
|
263
|
+
/^export\s+declare\s+(interface|type|enum|class|namespace)\s+(\w+)/,
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
if (match && typeNames.includes(match[2])) {
|
|
267
|
+
const kind = match[1];
|
|
268
|
+
|
|
269
|
+
// type alias 单行
|
|
270
|
+
if (kind === 'type' && line.includes('=') && line.includes(';')) {
|
|
271
|
+
i++;
|
|
239
272
|
continue;
|
|
240
273
|
}
|
|
241
274
|
|
|
242
|
-
|
|
275
|
+
// 多行声明,跳过直到闭合
|
|
276
|
+
let braceCount =
|
|
277
|
+
(line.match(/{/g) || []).length - (line.match(/}/g) || []).length;
|
|
278
|
+
i++;
|
|
279
|
+
|
|
280
|
+
while (i < lines.length && braceCount > 0) {
|
|
243
281
|
braceCount +=
|
|
244
|
-
(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
continue;
|
|
282
|
+
(lines[i].match(/{/g) || []).length -
|
|
283
|
+
(lines[i].match(/}/g) || []).length;
|
|
284
|
+
i++;
|
|
249
285
|
}
|
|
250
|
-
|
|
286
|
+
} else {
|
|
251
287
|
newLines.push(line);
|
|
288
|
+
i++;
|
|
252
289
|
}
|
|
253
|
-
|
|
254
|
-
content = newLines.join('\n');
|
|
255
290
|
}
|
|
256
291
|
|
|
257
|
-
|
|
258
|
-
|
|
292
|
+
return newLines.join('\n').replace(/\n{3,}/g, '\n\n').trim();
|
|
293
|
+
}
|
|
259
294
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
295
|
+
/**
|
|
296
|
+
* 提取共享类型到单独文件,并更新各入口文件
|
|
297
|
+
*/
|
|
298
|
+
function extractSharedTypes(outputs, projectFolder, sharedTypesOutput) {
|
|
299
|
+
// 找出共享的类型声明
|
|
300
|
+
const sharedTypes = findSharedTypeDeclarations(outputs);
|
|
266
301
|
|
|
267
|
-
if (
|
|
268
|
-
|
|
302
|
+
if (sharedTypes.size === 0) {
|
|
303
|
+
console.log('[rollup-dts] ℹ️ No shared types found across entries');
|
|
304
|
+
return { sharedTypeNames: [], sharedFilePath: null };
|
|
269
305
|
}
|
|
270
306
|
|
|
271
|
-
|
|
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');
|
|
307
|
+
const sharedTypeNames = Array.from(sharedTypes.keys());
|
|
276
308
|
console.log(
|
|
277
|
-
`[rollup-dts]
|
|
309
|
+
`[rollup-dts] 📝 Found ${sharedTypes.size} shared types: ${sharedTypeNames.join(', ')}`,
|
|
278
310
|
);
|
|
311
|
+
|
|
312
|
+
// 生成共享类型文件内容
|
|
313
|
+
const sharedContent = Array.from(sharedTypes.values())
|
|
314
|
+
.map((t) => t.declaration)
|
|
315
|
+
.join('\n\n');
|
|
316
|
+
|
|
317
|
+
const sharedFilePath = path.resolve(projectFolder, sharedTypesOutput);
|
|
318
|
+
|
|
319
|
+
// 确保目录存在
|
|
320
|
+
const sharedDir = path.dirname(sharedFilePath);
|
|
321
|
+
if (!fs.existsSync(sharedDir)) {
|
|
322
|
+
fs.mkdirSync(sharedDir, { recursive: true });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 写入共享类型文件
|
|
326
|
+
fs.writeFileSync(sharedFilePath, sharedContent + '\n\nexport { }\n', 'utf-8');
|
|
327
|
+
console.log(`[rollup-dts] ✅ Shared types written to: ${sharedTypesOutput}`);
|
|
328
|
+
|
|
329
|
+
// 更新各入口文件
|
|
330
|
+
for (const output of outputs) {
|
|
331
|
+
// 移除共享类型声明
|
|
332
|
+
let newContent = removeTypeDeclarations(output.content, sharedTypeNames);
|
|
333
|
+
|
|
334
|
+
// 找出这个文件中实际使用的共享类型
|
|
335
|
+
const usedTypes = sharedTypeNames.filter((name) => {
|
|
336
|
+
const regex = new RegExp(`\\b${name}\\b`);
|
|
337
|
+
return regex.test(newContent);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
if (usedTypes.length > 0) {
|
|
341
|
+
// 计算相对路径
|
|
342
|
+
const outputDir = path.dirname(output.path);
|
|
343
|
+
let relativePath = path
|
|
344
|
+
.relative(outputDir, sharedFilePath)
|
|
345
|
+
.replace(/\\/g, '/');
|
|
346
|
+
|
|
347
|
+
if (!relativePath.startsWith('.')) {
|
|
348
|
+
relativePath = './' + relativePath;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// 添加 import 和 re-export
|
|
352
|
+
const importExport = `import type { ${usedTypes.join(', ')} } from '${relativePath}';\nexport type { ${usedTypes.join(', ')} } from '${relativePath}';\n\n`;
|
|
353
|
+
newContent = importExport + newContent;
|
|
354
|
+
|
|
355
|
+
console.log(
|
|
356
|
+
`[rollup-dts] 🔗 Updated ${output.name}: imports ${usedTypes.join(', ')}`,
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
fs.writeFileSync(output.path, newContent, 'utf-8');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return { sharedTypeNames, sharedFilePath };
|
|
279
364
|
}
|
|
280
365
|
|
|
281
366
|
export function rollupDtsPlugin(options = {}) {
|
|
282
367
|
const {
|
|
283
368
|
tsconfigPath = './tsconfig.json',
|
|
284
|
-
|
|
369
|
+
sharedTypesOutput = null, // 例如: './dist/shared.d.ts'
|
|
285
370
|
} = options;
|
|
286
371
|
let resolvedConfig;
|
|
287
372
|
let entries = [];
|
|
@@ -309,35 +394,8 @@ export function rollupDtsPlugin(options = {}) {
|
|
|
309
394
|
|
|
310
395
|
console.log('\n[rollup-dts] Starting to bundle declaration files...');
|
|
311
396
|
|
|
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'}`,
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// 第二步:处理所有入口
|
|
397
|
+
// 第一步:处理所有入口
|
|
335
398
|
for (const entry of entries) {
|
|
336
|
-
// 跳过共享类型入口(如果它也在 entries 中)
|
|
337
|
-
if (sharedTypes && entry.mainEntryPoint === sharedTypes.input) {
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
399
|
const result = rollupEntry(entry, projectFolder, tsconfigPath);
|
|
342
400
|
results.push({ entry, ...result });
|
|
343
401
|
|
|
@@ -346,21 +404,37 @@ export function rollupDtsPlugin(options = {}) {
|
|
|
346
404
|
}
|
|
347
405
|
}
|
|
348
406
|
|
|
349
|
-
//
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
407
|
+
// 第二步:如果有多个入口且指定了共享类型输出,提取共享类型
|
|
408
|
+
let sharedTypeNames = [];
|
|
409
|
+
if (sharedTypesOutput && results.filter((r) => r.success).length > 1) {
|
|
410
|
+
console.log('[rollup-dts] 🔍 Analyzing shared types across entries...');
|
|
411
|
+
|
|
412
|
+
// 读取所有成功的入口输出
|
|
413
|
+
const outputs = results
|
|
414
|
+
.filter((r) => r.success)
|
|
415
|
+
.map((r) => ({
|
|
416
|
+
name: r.entry.name,
|
|
417
|
+
path: path.resolve(projectFolder, r.entry.output),
|
|
418
|
+
content: fs.readFileSync(
|
|
419
|
+
path.resolve(projectFolder, r.entry.output),
|
|
420
|
+
'utf-8',
|
|
421
|
+
),
|
|
422
|
+
}));
|
|
423
|
+
|
|
424
|
+
const { sharedTypeNames: names, sharedFilePath } = extractSharedTypes(
|
|
425
|
+
outputs,
|
|
426
|
+
projectFolder,
|
|
427
|
+
sharedTypesOutput,
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
sharedTypeNames = names;
|
|
431
|
+
|
|
432
|
+
if (sharedFilePath) {
|
|
433
|
+
keepFiles.push(sharedFilePath);
|
|
360
434
|
}
|
|
361
435
|
}
|
|
362
436
|
|
|
363
|
-
//
|
|
437
|
+
// 第三步:清理中间文件
|
|
364
438
|
const dirsToKeep = new Set();
|
|
365
439
|
for (const { entry, success } of results) {
|
|
366
440
|
if (!success) {
|
|
@@ -386,9 +460,6 @@ export function rollupDtsPlugin(options = {}) {
|
|
|
386
460
|
}
|
|
387
461
|
|
|
388
462
|
console.log('[rollup-dts] 📊 Summary:');
|
|
389
|
-
if (sharedTypes) {
|
|
390
|
-
console.log(` ✅ shared (${sharedTypeNames.length} types)`);
|
|
391
|
-
}
|
|
392
463
|
for (const { entry, success, skipped } of results) {
|
|
393
464
|
if (skipped) {
|
|
394
465
|
console.log(` ⏭️ ${entry.name} (skipped)`);
|
|
@@ -396,6 +467,9 @@ export function rollupDtsPlugin(options = {}) {
|
|
|
396
467
|
console.log(` ${success ? '✅' : '❌'} ${entry.name}`);
|
|
397
468
|
}
|
|
398
469
|
}
|
|
470
|
+
if (sharedTypeNames.length > 0) {
|
|
471
|
+
console.log(` 📦 shared: ${sharedTypeNames.length} types extracted`);
|
|
472
|
+
}
|
|
399
473
|
},
|
|
400
474
|
};
|
|
401
475
|
}
|
package/vite.config.base.js
CHANGED
|
@@ -13,7 +13,7 @@ import { rollupDtsPlugin } from './vite-plugin-rollup-dts.js';
|
|
|
13
13
|
* @typedef {import('vite-plugin-dts').PluginOptions} DtsPluginOptions
|
|
14
14
|
* @typedef {Object} RollupDtsOptions
|
|
15
15
|
* @property {string} [tsconfigPath] - tsconfig.json 路径
|
|
16
|
-
* @property {
|
|
16
|
+
* @property {string} [sharedTypesOutput] - 共享类型输出路径(多入口时)
|
|
17
17
|
* @typedef {Object} CreateBaseConfigOptions
|
|
18
18
|
* @property {'app' | 'lib'} [mode] - 构建模式:'app' 为 Vue 应用,'lib' 为库打包
|
|
19
19
|
* @property {Partial<DtsPluginOptions> & RollupDtsOptions} [dtsConfig] - vite-plugin-dts 配置(仅 lib 模式),会与默认配置合并
|
|
@@ -71,7 +71,7 @@ export function createBaseConfig(dirname, options = {}) {
|
|
|
71
71
|
// Lib mode: 提供默认配置
|
|
72
72
|
if (mode === 'lib') {
|
|
73
73
|
// 提取 rollupDts 专用配置
|
|
74
|
-
const {
|
|
74
|
+
const { sharedTypesOutput, tsconfigPath, ...dtsOnlyConfig } = dtsConfig;
|
|
75
75
|
|
|
76
76
|
baseConfig.build = {
|
|
77
77
|
copyPublicDir: false,
|
|
@@ -95,7 +95,7 @@ export function createBaseConfig(dirname, options = {}) {
|
|
|
95
95
|
baseConfig.plugins.push(
|
|
96
96
|
rollupDtsPlugin({
|
|
97
97
|
tsconfigPath,
|
|
98
|
-
|
|
98
|
+
sharedTypesOutput,
|
|
99
99
|
}),
|
|
100
100
|
);
|
|
101
101
|
}
|