fe-stack 0.0.10 → 0.0.12

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fe-stack",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "共享的配置文件集合,用于 Vite、Tailwind、Biome、Prettier 和 TypeScript",
5
5
  "type": "module",
6
6
  "exports": {
@@ -21,6 +21,7 @@
21
21
  "tsconfig.node.json",
22
22
  "vite.config.base.js",
23
23
  "vite.config.base.d.ts",
24
+ "vite-plugin-rollup-dts.ts",
24
25
  "README.md"
25
26
  ],
26
27
  "keywords": [
@@ -49,9 +50,9 @@
49
50
  "@vitejs/plugin-vue": "^6.0.3",
50
51
  "@vue/tsconfig": "^0.7.0",
51
52
  "unplugin-auto-import": "^20.3.0",
52
- "unplugin-dts": "1.0.0-beta.6",
53
53
  "unplugin-vue-components": "^30.0.0",
54
- "unplugin-vue-router": "^0.19.2"
54
+ "unplugin-vue-router": "^0.19.2",
55
+ "vite-plugin-dts": "^4.5.4"
55
56
  },
56
57
  "peerDependencies": {
57
58
  "@biomejs/biome": "^2.3.11",
@@ -0,0 +1,246 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { Extractor, ExtractorConfig } from '@microsoft/api-extractor';
4
+ import type { Plugin, ResolvedConfig } from 'vite';
5
+
6
+ interface Entry {
7
+ name: string;
8
+ mainEntryPoint: string;
9
+ output: string;
10
+ }
11
+
12
+ interface RollupDtsPluginOptions {
13
+ /** 手动指定入口,如果不指定则从 vite config 的 build.lib.entry 自动获取 */
14
+ entries?: Entry[];
15
+ tsconfigPath?: string;
16
+ }
17
+
18
+ const baseConfig = {
19
+ $schema:
20
+ 'https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json',
21
+ projectFolder: '.',
22
+ apiReport: { enabled: false },
23
+ docModel: { enabled: false },
24
+ tsdocMetadata: { enabled: false },
25
+ dtsRollup: { enabled: true },
26
+ messages: {
27
+ compilerMessageReporting: { default: { logLevel: 'warning' as const } },
28
+ extractorMessageReporting: {
29
+ default: { logLevel: 'warning' as const },
30
+ 'ae-missing-release-tag': { logLevel: 'none' as const },
31
+ 'ae-unresolved-link': { logLevel: 'none' as const },
32
+ },
33
+ tsdocMessageReporting: { default: { logLevel: 'none' as const } },
34
+ },
35
+ };
36
+
37
+ function cleanupIntermediateFiles(dir: string, keepFiles: string[]) {
38
+ if (!fs.existsSync(dir)) return;
39
+
40
+ const items = fs.readdirSync(dir, { withFileTypes: true });
41
+
42
+ for (const item of items) {
43
+ const fullPath = path.join(dir, item.name);
44
+
45
+ if (item.isDirectory()) {
46
+ const hasKeepFile = keepFiles.some(
47
+ (f) => f.startsWith(fullPath + path.sep) || f === fullPath,
48
+ );
49
+ if (hasKeepFile) {
50
+ cleanupIntermediateFiles(fullPath, keepFiles);
51
+ const remaining = fs.readdirSync(fullPath);
52
+ if (remaining.length === 0) {
53
+ fs.rmdirSync(fullPath);
54
+ }
55
+ } else {
56
+ const allDts = fs
57
+ .readdirSync(fullPath, { recursive: true })
58
+ .every((f) => {
59
+ if (typeof f !== 'string') return true;
60
+ const fp = path.join(fullPath, f);
61
+ return fs.statSync(fp).isDirectory() || f.endsWith('.d.ts');
62
+ });
63
+ if (allDts) {
64
+ fs.rmSync(fullPath, { recursive: true });
65
+ }
66
+ }
67
+ } else if (item.name.endsWith('.d.ts') && !keepFiles.includes(fullPath)) {
68
+ fs.unlinkSync(fullPath);
69
+ }
70
+ }
71
+ }
72
+
73
+ /**
74
+ * 从 vite config 的 build.lib.entry 自动生成 entries
75
+ */
76
+ function getEntriesFromConfig(config: ResolvedConfig): Entry[] {
77
+ const lib = config.build?.lib;
78
+ if (!lib) return [];
79
+
80
+ const outDir = config.build?.outDir || 'dist';
81
+ const entry = lib.entry;
82
+
83
+ // entry 可以是 string | string[] | Record<string, string>
84
+ if (typeof entry === 'string') {
85
+ return [
86
+ {
87
+ name: 'index',
88
+ mainEntryPoint: `./${outDir}/index.d.ts`,
89
+ output: `./${outDir}/index.d.ts`,
90
+ },
91
+ ];
92
+ }
93
+
94
+ if (Array.isArray(entry)) {
95
+ return entry.map((_, i) => ({
96
+ name: i === 0 ? 'index' : `entry${i}`,
97
+ mainEntryPoint: `./${outDir}/${i === 0 ? 'index' : `entry${i}`}.d.ts`,
98
+ output: `./${outDir}/${i === 0 ? 'index' : `entry${i}`}.d.ts`,
99
+ }));
100
+ }
101
+
102
+ // Record<string, string>
103
+ return Object.keys(entry).map((name) => ({
104
+ name,
105
+ mainEntryPoint: `./${outDir}/${name}.d.ts`,
106
+ output: `./${outDir}/${name}.d.ts`,
107
+ }));
108
+ }
109
+
110
+ export function rollupDtsPlugin(options: RollupDtsPluginOptions = {}): Plugin {
111
+ const { tsconfigPath = './tsconfig.json' } = options;
112
+ let resolvedConfig: ResolvedConfig;
113
+ let entries: Entry[] = [];
114
+
115
+ return {
116
+ name: 'vite-plugin-rollup-dts',
117
+ apply: 'build',
118
+ enforce: 'post',
119
+
120
+ configResolved(config) {
121
+ resolvedConfig = config;
122
+ // 如果手动指定了 entries,使用手动配置;否则从 config 自动生成
123
+ entries = options.entries || getEntriesFromConfig(config);
124
+ },
125
+
126
+ async closeBundle() {
127
+ if (entries.length === 0) {
128
+ console.log('[rollup-dts] No entries found, skipping...');
129
+ return;
130
+ }
131
+
132
+ const projectFolder = process.cwd();
133
+ const results: { entry: Entry; success: boolean }[] = [];
134
+
135
+ console.log('\n[rollup-dts] Starting to bundle declaration files...');
136
+
137
+ for (const entry of entries) {
138
+ const mainEntryPointFilePath = path.resolve(
139
+ projectFolder,
140
+ entry.mainEntryPoint,
141
+ );
142
+
143
+ if (!fs.existsSync(mainEntryPointFilePath)) {
144
+ console.warn(
145
+ `[rollup-dts] ⚠️ Skipping ${entry.name}: ${entry.mainEntryPoint} not found`,
146
+ );
147
+ continue;
148
+ }
149
+
150
+ console.log(`[rollup-dts] 📦 Rolling up types for: ${entry.name}`);
151
+
152
+ const config = {
153
+ ...baseConfig,
154
+ compiler: {
155
+ tsconfigFilePath: `<projectFolder>/${tsconfigPath}`,
156
+ },
157
+ mainEntryPointFilePath: `<projectFolder>/${entry.mainEntryPoint}`,
158
+ dtsRollup: {
159
+ enabled: true,
160
+ untrimmedFilePath: `<projectFolder>/${entry.output}`,
161
+ },
162
+ };
163
+
164
+ // 用安全的文件名(将 / 替换为 -)
165
+ const safeName = entry.name.replace(/\//g, '-');
166
+ const tempConfigPath = path.resolve(
167
+ projectFolder,
168
+ `api-extractor.${safeName}.tmp.json`,
169
+ );
170
+ fs.writeFileSync(tempConfigPath, JSON.stringify(config, null, 2));
171
+
172
+ let success = false;
173
+ try {
174
+ const extractorConfig =
175
+ ExtractorConfig.loadFileAndPrepare(tempConfigPath);
176
+ const extractorResult = Extractor.invoke(extractorConfig, {
177
+ localBuild: true,
178
+ showVerboseMessages: false,
179
+ });
180
+
181
+ if (extractorResult.succeeded) {
182
+ console.log(
183
+ `[rollup-dts] ✅ ${entry.name}: types rolled up successfully`,
184
+ );
185
+ success = true;
186
+ } else {
187
+ console.warn(
188
+ `[rollup-dts] ⚠️ ${entry.name}: API Extractor completed with warnings/errors`,
189
+ );
190
+ if (fs.existsSync(path.resolve(projectFolder, entry.output))) {
191
+ success = true;
192
+ }
193
+ }
194
+ } catch (error) {
195
+ console.warn(
196
+ `[rollup-dts] ⚠️ ${entry.name}: Error during rollup, keeping original .d.ts files`,
197
+ );
198
+ console.warn(` ${error instanceof Error ? error.message : error}`);
199
+ } finally {
200
+ if (fs.existsSync(tempConfigPath)) {
201
+ fs.unlinkSync(tempConfigPath);
202
+ }
203
+ }
204
+
205
+ results.push({ entry, success });
206
+ }
207
+
208
+ // Determine which directories to keep
209
+ const dirsToKeep = new Set<string>();
210
+
211
+ for (const { entry, success } of results) {
212
+ if (!success) {
213
+ const entryDir = path.dirname(
214
+ path.resolve(projectFolder, entry.mainEntryPoint),
215
+ );
216
+ dirsToKeep.add(entryDir);
217
+ console.log(
218
+ `[rollup-dts] 📁 Keeping directory structure for: ${entry.name}`,
219
+ );
220
+ }
221
+ }
222
+
223
+ // Clean up only if all entries succeeded
224
+ if (dirsToKeep.size === 0) {
225
+ console.log('[rollup-dts] 🧹 Cleaning up intermediate files...');
226
+ const outDir = resolvedConfig?.build?.outDir || 'dist';
227
+ const distDir = path.resolve(projectFolder, outDir);
228
+ cleanupIntermediateFiles(
229
+ distDir,
230
+ entries.map((e) => path.resolve(projectFolder, e.output)),
231
+ );
232
+ } else {
233
+ console.log(
234
+ '[rollup-dts] ⚠️ Some entries failed, keeping intermediate files',
235
+ );
236
+ }
237
+
238
+ console.log('[rollup-dts] 📊 Summary:');
239
+ for (const { entry, success } of results) {
240
+ console.log(` ${success ? '✅' : '❌'} ${entry.name}`);
241
+ }
242
+ },
243
+ };
244
+ }
245
+
246
+ export default rollupDtsPlugin;
@@ -1,16 +1,16 @@
1
- import type { PluginOptions as DtsPluginOptions } from 'unplugin-dts';
2
1
  import type { UserConfig } from 'vite';
2
+ import type { PluginOptions as DtsPluginOptions } from 'vite-plugin-dts';
3
3
 
4
4
  export interface CreateBaseConfigOptions {
5
5
  /**
6
6
  * 构建模式
7
7
  * - 'app': Vue 应用模式,包含 Vue Router、Auto Import 等
8
- * - 'lib': 库打包模式,配置 unplugin-dts/vite
8
+ * - 'lib': 库打包模式,配置 vite-plugin-dts
9
9
  */
10
10
  mode?: 'app' | 'lib';
11
11
 
12
12
  /**
13
- * unplugin-dts/vite 配置(仅 lib 模式)
13
+ * vite-plugin-dts 配置(仅 lib 模式)
14
14
  * 会与默认配置合并,可以覆盖 include、exclude 等选项
15
15
  */
16
16
  dtsConfig?: Partial<DtsPluginOptions>;
@@ -2,17 +2,18 @@ import path from 'node:path';
2
2
  import tailwindcss from '@tailwindcss/vite';
3
3
  import vue from '@vitejs/plugin-vue';
4
4
  import AutoImport from 'unplugin-auto-import/vite';
5
- import dts from 'unplugin-dts/vite';
6
5
  import Components from 'unplugin-vue-components/vite';
7
6
  import VueRouter from 'unplugin-vue-router/vite';
8
7
  import { defineConfig, mergeConfig } from 'vite';
8
+ import dts from 'vite-plugin-dts';
9
+ import { rollupDtsPlugin } from './scripts/vite-plugin-rollup-dts';
9
10
 
10
11
  /**
11
12
  * @typedef {import('vite').UserConfig} UserConfig
12
- * @typedef {import('unplugin-dts').PluginOptions} DtsPluginOptions
13
+ * @typedef {import('vite-plugin-dts').PluginOptions} DtsPluginOptions
13
14
  * @typedef {Object} CreateBaseConfigOptions
14
15
  * @property {'app' | 'lib'} [mode] - 构建模式:'app' 为 Vue 应用,'lib' 为库打包
15
- * @property {Partial<DtsPluginOptions>} [dtsConfig] - unplugin-dts 配置(仅 lib 模式),会与默认配置合并
16
+ * @property {Partial<DtsPluginOptions>} [dtsConfig] - vite-plugin-dts 配置(仅 lib 模式),会与默认配置合并
16
17
  * @property {UserConfig} [viteConfig] - 额外的 Vite 配置,会与基础配置合并
17
18
  */
18
19
 
@@ -80,10 +81,13 @@ export function createBaseConfig(dirname, options = {}) {
80
81
  dts({
81
82
  include: ['src/**/*.ts'],
82
83
  root: dirname,
83
- bundleTypes: false,
84
84
  ...dtsConfig,
85
+ rollupTypes: false,
85
86
  }),
86
87
  );
88
+ if (dts.rollupTypes) {
89
+ baseConfig.plugins.push(rollupDtsPlugin());
90
+ }
87
91
  }
88
92
 
89
93
  // 使用 mergeConfig 合并用户自定义配置