vite-plugin-dts 3.0.3 → 3.1.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/README.md CHANGED
@@ -106,9 +106,30 @@ This is an existing issue when TypeScript infers types from packages located in
106
106
  import type ts from 'typescript'
107
107
  import type { LogLevel } from 'vite'
108
108
 
109
- interface TransformWriteFile {
110
- filePath?: string
111
- content?: string
109
+ type MaybePromise<T> = T | Promise<T>
110
+
111
+ export interface Resolver {
112
+ /**
113
+ * The name of the resolver
114
+ *
115
+ * The later resolver with the same name will overwrite the earlier
116
+ */
117
+ name: string,
118
+ /**
119
+ * Determine whether the resolve supports the file
120
+ */
121
+ supports: (id: string) => void | boolean,
122
+ /**
123
+ * Transform the source file to declaration files
124
+ */
125
+ transform: (payload: {
126
+ id: string,
127
+ code: string,
128
+ root: string,
129
+ host: ts.CompilerHost,
130
+ program: ts.Program,
131
+ service: ts.LanguageService
132
+ }) => MaybePromise<{ path: string, content: string }[]>
112
133
  }
113
134
 
114
135
  export interface PluginOptions {
@@ -117,7 +138,7 @@ export interface PluginOptions {
117
138
  *
118
139
  * By Default it base on 'root' of your Vite config, or `process.cwd()` if using Rollup
119
140
  */
120
- root?: string
141
+ root?: string,
121
142
 
122
143
  /**
123
144
  * Specify declaration files output directory
@@ -126,7 +147,7 @@ export interface PluginOptions {
126
147
  *
127
148
  * By Default it base on 'build.outDir' of your Vite config, or `outDir` of tsconfig.json if using Rollup
128
149
  */
129
- outDir?: string | string[]
150
+ outDir?: string | string[],
130
151
 
131
152
  /**
132
153
  * Manually set the root path of the entry files, useful in monorepo
@@ -135,7 +156,7 @@ export interface PluginOptions {
135
156
  *
136
157
  * By Default it is the smallest public path for all files
137
158
  */
138
- entryRoot?: string
159
+ entryRoot?: string,
139
160
 
140
161
  /**
141
162
  * Strictly restrict declaration files output inside `outDir`
@@ -144,14 +165,14 @@ export interface PluginOptions {
144
165
  *
145
166
  * @default true
146
167
  */
147
- strictOutput?: boolean
168
+ strictOutput?: boolean,
148
169
 
149
170
  /**
150
171
  * Specify a CompilerOptions to override
151
172
  *
152
173
  * @default null
153
174
  */
154
- compilerOptions?: ts.CompilerOptions | null
175
+ compilerOptions?: ts.CompilerOptions | null,
155
176
 
156
177
  /**
157
178
  * Specify tsconfig.json path
@@ -160,7 +181,14 @@ export interface PluginOptions {
160
181
  *
161
182
  * By default plugin will find config form root if not specify
162
183
  */
163
- tsconfigPath?: string
184
+ tsconfigPath?: string,
185
+
186
+ /**
187
+ * Specify custom resolvers
188
+ *
189
+ * @default []
190
+ */
191
+ resolvers?: Resolver[],
164
192
 
165
193
  /**
166
194
  * Set which paths should exclude when transform aliases
@@ -169,14 +197,14 @@ export interface PluginOptions {
169
197
  *
170
198
  * @default []
171
199
  */
172
- aliasesExclude?: (string | RegExp)[]
200
+ aliasesExclude?: (string | RegExp)[],
173
201
 
174
202
  /**
175
203
  * Whether transform file name '.vue.d.ts' to '.d.ts'
176
204
  *
177
205
  * @default false
178
206
  */
179
- cleanVueFileName?: boolean
207
+ cleanVueFileName?: boolean,
180
208
 
181
209
  /**
182
210
  * Whether transform dynamic import to static
@@ -187,28 +215,28 @@ export interface PluginOptions {
187
215
  *
188
216
  * @default false
189
217
  */
190
- staticImport?: boolean
218
+ staticImport?: boolean,
191
219
 
192
220
  /**
193
221
  * Manual set include glob
194
222
  *
195
223
  * By Default it base on `include` option of the tsconfig.json
196
224
  */
197
- include?: string | string[]
225
+ include?: string | string[],
198
226
 
199
227
  /**
200
228
  * Manual set exclude glob
201
229
  *
202
230
  * By Default it base on `exclude` option of the tsconfig.json, be `'node_module/**'` when empty
203
231
  */
204
- exclude?: string | string[]
232
+ exclude?: string | string[],
205
233
 
206
234
  /**
207
235
  * Whether remove those `import 'xxx'`
208
236
  *
209
237
  * @default true
210
238
  */
211
- clearPureImport?: boolean
239
+ clearPureImport?: boolean,
212
240
 
213
241
  /**
214
242
  * Whether generate types entry file
@@ -219,7 +247,7 @@ export interface PluginOptions {
219
247
  *
220
248
  * @default false
221
249
  */
222
- insertTypesEntry?: boolean
250
+ insertTypesEntry?: boolean,
223
251
 
224
252
  /**
225
253
  * Set whether rollup declaration files after emit
@@ -228,7 +256,7 @@ export interface PluginOptions {
228
256
  *
229
257
  * @default false
230
258
  */
231
- rollupTypes?: boolean
259
+ rollupTypes?: boolean,
232
260
 
233
261
  /**
234
262
  * Bundled packages for `@microsoft/api-extractor`
@@ -236,7 +264,7 @@ export interface PluginOptions {
236
264
  * @default []
237
265
  * @see https://api-extractor.com/pages/configs/api-extractor_json/#bundledpackages
238
266
  */
239
- bundledPackages?: string[]
267
+ bundledPackages?: string[],
240
268
 
241
269
  /**
242
270
  * Whether copy .d.ts source files into `outDir`
@@ -244,14 +272,14 @@ export interface PluginOptions {
244
272
  * @default false
245
273
  * @remarks Before 2.0 it defaults to true
246
274
  */
247
- copyDtsFiles?: boolean
275
+ copyDtsFiles?: boolean,
248
276
 
249
277
  /**
250
278
  * Specify the log level of plugin
251
279
  *
252
280
  * By Default it base on 'logLevel' option of your Vite config
253
281
  */
254
- logLevel?: LogLevel
282
+ logLevel?: LogLevel,
255
283
 
256
284
  /**
257
285
  * Hook after diagnostic emitted
@@ -260,7 +288,7 @@ export interface PluginOptions {
260
288
  *
261
289
  * @default () => {}
262
290
  */
263
- afterDiagnostic?: (diagnostics: readonly ts.Diagnostic[]) => void | Promise<void>
291
+ afterDiagnostic?: (diagnostics: readonly ts.Diagnostic[]) => MaybePromise<void>,
264
292
 
265
293
  /**
266
294
  * Hook before each declaration file is written
@@ -271,7 +299,16 @@ export interface PluginOptions {
271
299
  *
272
300
  * @default () => {}
273
301
  */
274
- beforeWriteFile?: (filePath: string, content: string) => void | false | TransformWriteFile
302
+ beforeWriteFile?: (
303
+ filePath: string,
304
+ content: string
305
+ ) =>
306
+ | void
307
+ | false
308
+ | {
309
+ filePath?: string,
310
+ content?: string
311
+ },
275
312
 
276
313
  /**
277
314
  * Hook after built
@@ -280,7 +317,7 @@ export interface PluginOptions {
280
317
  *
281
318
  * @default () => {}
282
319
  */
283
- afterBuild?: () => void | Promise<void>
320
+ afterBuild?: () => MaybePromise<void>
284
321
  }
285
322
  ```
286
323
 
package/README.zh-CN.md CHANGED
@@ -106,9 +106,30 @@ defineProps<{
106
106
  import type ts from 'typescript'
107
107
  import type { LogLevel } from 'vite'
108
108
 
109
- interface TransformWriteFile {
110
- filePath?: string
111
- content?: string
109
+ type MaybePromise<T> = T | Promise<T>
110
+
111
+ export interface Resolver {
112
+ /**
113
+ * 解析器的名称
114
+ *
115
+ * 靠后的同名解析器将会覆盖靠前的
116
+ */
117
+ name: string,
118
+ /**
119
+ * 决定是否要解析文件
120
+ */
121
+ supports: (id: string) => void | boolean,
122
+ /**
123
+ * 将源文件转换为类型文件
124
+ */
125
+ transform: (payload: {
126
+ id: string,
127
+ code: string,
128
+ root: string,
129
+ host: ts.CompilerHost,
130
+ program: ts.Program,
131
+ service: ts.LanguageService
132
+ }) => MaybePromise<{ path: string, content: string }[]>
112
133
  }
113
134
 
114
135
  export interface PluginOptions {
@@ -117,7 +138,7 @@ export interface PluginOptions {
117
138
  *
118
139
  * 默认基于 Vite 配置的 'root',使用 Rollup 时基于 `process.cwd()`
119
140
  */
120
- root?: string
141
+ root?: string,
121
142
 
122
143
  /**
123
144
  * 指定输出目录
@@ -126,7 +147,7 @@ export interface PluginOptions {
126
147
  *
127
148
  * 默认基于 Vite 配置的 'build.outDir',使用 Rollup 时基于 tsconfig.json 的 `outDir`
128
149
  */
129
- outDir?: string | string[]
150
+ outDir?: string | string[],
130
151
 
131
152
  /**
132
153
  * 用于手动设置入口文件的根路径,通常用在 monorepo
@@ -135,7 +156,7 @@ export interface PluginOptions {
135
156
  *
136
157
  * 默认为所有文件的最小公共路径
137
158
  */
138
- entryRoot?: string
159
+ entryRoot?: string,
139
160
 
140
161
  /**
141
162
  * 严格限制类型文件生产在 `outDir` 内
@@ -144,14 +165,14 @@ export interface PluginOptions {
144
165
  *
145
166
  * @default true
146
167
  */
147
- strictOutput?: boolean
168
+ strictOutput?: boolean,
148
169
 
149
170
  /**
150
171
  * 指定一个用于覆写的 CompilerOptions
151
172
  *
152
173
  * @default null
153
174
  */
154
- compilerOptions?: ts.CompilerOptions | null
175
+ compilerOptions?: ts.CompilerOptions | null,
155
176
 
156
177
  /**
157
178
  * 指定 tsconfig.json 的路径
@@ -160,21 +181,28 @@ export interface PluginOptions {
160
181
  *
161
182
  * 未指定时插件默认从根目录寻找配置
162
183
  */
163
- tsconfigPath?: string
184
+ tsconfigPath?: string,
185
+
186
+ /**
187
+ * 指定自定义的解析器
188
+ *
189
+ * @default []
190
+ */
191
+ resolvers?: Resolver[],
164
192
 
165
193
  /**
166
194
  * 设置在转换别名时哪些路径需要排除
167
195
  *
168
196
  * @default []
169
197
  */
170
- aliasesExclude?: (string | RegExp)[]
198
+ aliasesExclude?: (string | RegExp)[],
171
199
 
172
200
  /**
173
201
  * 是否将 '.vue.d.ts' 文件名转换为 '.d.ts'
174
202
  *
175
203
  * @default false
176
204
  */
177
- cleanVueFileName?: boolean
205
+ cleanVueFileName?: boolean,
178
206
 
179
207
  /**
180
208
  * 是否将动态引入转换为静态
@@ -185,28 +213,28 @@ export interface PluginOptions {
185
213
  *
186
214
  * @default false
187
215
  */
188
- staticImport?: boolean
216
+ staticImport?: boolean,
189
217
 
190
218
  /**
191
219
  * 手动设置包含路径的 glob
192
220
  *
193
221
  * 默认基于 tsconfig.json 的 `include` 选项
194
222
  */
195
- include?: string | string[]
223
+ include?: string | string[],
196
224
 
197
225
  /**
198
226
  * 手动设置排除路径的 glob
199
227
  *
200
228
  * 默认基于 tsconfig.json 的 `exclude` 选线,未设置时为 `'node_module/**'`
201
229
  */
202
- exclude?: string | string[]
230
+ exclude?: string | string[],
203
231
 
204
232
  /**
205
233
  * 是否移除那些 `import 'xxx'`
206
234
  *
207
235
  * @default true
208
236
  */
209
- clearPureImport?: boolean
237
+ clearPureImport?: boolean,
210
238
 
211
239
  /**
212
240
  * 是否生成类型声明入口
@@ -217,7 +245,7 @@ export interface PluginOptions {
217
245
  *
218
246
  * @default false
219
247
  */
220
- insertTypesEntry?: boolean
248
+ insertTypesEntry?: boolean,
221
249
 
222
250
  /**
223
251
  * 设置是否在发出类型文件后将其打包
@@ -226,7 +254,7 @@ export interface PluginOptions {
226
254
  *
227
255
  * @default false
228
256
  */
229
- rollupTypes?: boolean
257
+ rollupTypes?: boolean,
230
258
 
231
259
  /**
232
260
  * 设置 `@microsoft/api-extractor` 的 `bundledPackages` 选项
@@ -234,7 +262,7 @@ export interface PluginOptions {
234
262
  * @default []
235
263
  * @see https://api-extractor.com/pages/configs/api-extractor_json/#bundledpackages
236
264
  */
237
- bundledPackages?: string[]
265
+ bundledPackages?: string[],
238
266
 
239
267
  /**
240
268
  * 是否将源码里的 .d.ts 文件复制到 `outDir`
@@ -242,14 +270,14 @@ export interface PluginOptions {
242
270
  * @default false
243
271
  * @remarks 在 2.0 之前它默认为 true
244
272
  */
245
- copyDtsFiles?: boolean
273
+ copyDtsFiles?: boolean,
246
274
 
247
275
  /**
248
276
  * 指定插件的输出等级
249
277
  *
250
278
  * 默认基于 Vite 配置的 'logLevel' 选项
251
279
  */
252
- logLevel?: LogLevel
280
+ logLevel?: LogLevel,
253
281
 
254
282
  /**
255
283
  * 获取诊断信息后的钩子
@@ -258,7 +286,7 @@ export interface PluginOptions {
258
286
  *
259
287
  * @default () => {}
260
288
  */
261
- afterDiagnostic?: (diagnostics: Diagnostic[]) => void | Promise<void>
289
+ afterDiagnostic?: (diagnostics: readonly ts.Diagnostic[]) => MaybePromise<void>,
262
290
 
263
291
  /**
264
292
  * 类型声明文件被写入前的钩子
@@ -269,7 +297,16 @@ export interface PluginOptions {
269
297
  *
270
298
  * @default () => {}
271
299
  */
272
- beforeWriteFile?: (filePath: string, content: string) => void | false | TransformWriteFile
300
+ beforeWriteFile?: (
301
+ filePath: string,
302
+ content: string
303
+ ) =>
304
+ | void
305
+ | false
306
+ | {
307
+ filePath?: string,
308
+ content?: string
309
+ },
273
310
 
274
311
  /**
275
312
  * 构建后回调钩子
@@ -278,7 +315,7 @@ export interface PluginOptions {
278
315
  *
279
316
  * @default () => {}
280
317
  */
281
- afterBuild?: () => void | Promise<void>
318
+ afterBuild?: () => MaybePromise<void>
282
319
  }
283
320
  ```
284
321
 
package/dist/index.cjs CHANGED
@@ -4,9 +4,9 @@ const node_path = require('node:path');
4
4
  const node_fs = require('node:fs');
5
5
  const promises = require('node:fs/promises');
6
6
  const node_os = require('node:os');
7
+ const languageCore = require('@vue/language-core');
7
8
  const ts = require('typescript');
8
9
  const pluginutils = require('@rollup/pluginutils');
9
- const languageCore = require('@vue/language-core');
10
10
  const vueTsc = require('vue-tsc');
11
11
  const debug = require('debug');
12
12
  const kolorist = require('kolorist');
@@ -114,13 +114,33 @@ function rollupDeclarationFiles({
114
114
  );
115
115
  }
116
116
 
117
- const windowsSlashRE = /\\/g;
117
+ const svelteRE = /\.svelte$/;
118
+ function SvelteResolver() {
119
+ return {
120
+ name: "svelte",
121
+ supports(id) {
122
+ return svelteRE.test(id);
123
+ },
124
+ transform({ id }) {
125
+ return [
126
+ {
127
+ path: `${id}.d.ts`,
128
+ content: "export { SvelteComponentTyped as default } from 'svelte';"
129
+ }
130
+ ];
131
+ }
132
+ };
133
+ }
134
+
135
+ const windowsSlashRE = /\\+/g;
118
136
  function slash(p) {
119
137
  return p.replace(windowsSlashRE, "/");
120
138
  }
121
- const isWindows = node_os.platform() === "win32";
122
139
  function normalizePath(id) {
123
- return node_path.posix.normalize(isWindows ? slash(id) : id);
140
+ return node_path.posix.normalize(slash(id));
141
+ }
142
+ function resolve(...paths) {
143
+ return normalizePath(node_path.resolve(...paths));
124
144
  }
125
145
  function isNativeObj(value) {
126
146
  return Object.prototype.toString.call(value) === "[object Object]";
@@ -132,7 +152,7 @@ function isPromise(value) {
132
152
  return !!value && (typeof value === "function" || typeof value === "object") && typeof value.then === "function";
133
153
  }
134
154
  function ensureAbsolute(path, root) {
135
- return normalizePath(path ? node_path.isAbsolute(path) ? path : node_path.resolve(root, path) : root);
155
+ return normalizePath(path ? node_path.isAbsolute(path) ? path : resolve(root, path) : root);
136
156
  }
137
157
  function ensureArray(value) {
138
158
  return Array.isArray(value) ? value : value ? [value] : [];
@@ -197,7 +217,7 @@ function removeDirIfEmpty(dir) {
197
217
  }
198
218
  let onlyHasDir = true;
199
219
  for (const file of node_fs.readdirSync(dir)) {
200
- const abs = node_path.resolve(dir, file);
220
+ const abs = resolve(dir, file);
201
221
  if (node_fs.lstatSync(abs).isDirectory()) {
202
222
  if (!removeDirIfEmpty(abs)) {
203
223
  onlyHasDir = false;
@@ -212,6 +232,38 @@ function removeDirIfEmpty(dir) {
212
232
  return onlyHasDir;
213
233
  }
214
234
 
235
+ const vueRE = /\.vue$/;
236
+ function VueResolver() {
237
+ return {
238
+ name: "vue",
239
+ supports(id) {
240
+ return vueRE.test(id);
241
+ },
242
+ transform({ id, root, program, service }) {
243
+ let sourceFile = program.getSourceFile(id);
244
+ if (!sourceFile && vueRE.test(id)) {
245
+ sourceFile = program.getSourceFile(id + ".ts") || program.getSourceFile(id + ".js") || program.getSourceFile(id + ".tsx") || program.getSourceFile(id + ".jsx");
246
+ }
247
+ if (!sourceFile)
248
+ return [];
249
+ return service.getEmitOutput(sourceFile.fileName, true).outputFiles.map((file) => {
250
+ return {
251
+ path: resolve(root, file.name),
252
+ content: file.text
253
+ };
254
+ });
255
+ }
256
+ };
257
+ }
258
+
259
+ function parseResolvers(resolvers) {
260
+ const nameMap = /* @__PURE__ */ new Map();
261
+ for (const resolver of resolvers) {
262
+ resolver.name && nameMap.set(resolver.name, resolver);
263
+ }
264
+ return Array.from(nameMap.values());
265
+ }
266
+
215
267
  const globSuffixRE = /^((?:.*\.[^.]+)|(?:\*+))$/;
216
268
  function normalizeGlob(path) {
217
269
  if (/[\\/]$/.test(path)) {
@@ -310,7 +362,6 @@ function removePureImport(content) {
310
362
  return content.replace(pureImportRE, "");
311
363
  }
312
364
 
313
- const vueRE = /\.vue$/;
314
365
  const jsRE = /\.(m|c)?jsx?$/;
315
366
  const tsRE = /\.(m|c)?tsx?$/;
316
367
  const dtsRE = /\.d\.(m|c)?tsx?$/;
@@ -318,7 +369,6 @@ const tjsRE = /\.(m|c)?(t|j)sx?$/;
318
369
  const mtjsRE = /\.m(t|j)sx?$/;
319
370
  const ctjsRE = /\.c(t|j)sx?$/;
320
371
  const fullRelativeRE = /^\.\.?\//;
321
- const watchExtensionRE = /\.(vue|(m|c)?(t|j)sx?)$/;
322
372
  const defaultIndex = "index.d.ts";
323
373
  const logPrefix = kolorist.cyan("[vite:dts]");
324
374
  const bundleDebug = debug__default("vite-plugin-dts:bundle");
@@ -336,7 +386,6 @@ const fixedCompilerOptions = {
336
386
  const noop = () => {
337
387
  };
338
388
  const extPrefix = (file) => mtjsRE.test(file) ? "m" : ctjsRE.test(file) ? "c" : "";
339
- const resolve = (...paths) => normalizePath(node_path.resolve(...paths));
340
389
  function dtsPlugin(options = {}) {
341
390
  const {
342
391
  tsconfigPath,
@@ -373,6 +422,7 @@ function dtsPlugin(options = {}) {
373
422
  let filter;
374
423
  let bundled = false;
375
424
  let timeRecord = 0;
425
+ const resolvers = parseResolvers([VueResolver(), SvelteResolver(), ...options.resolvers || []]);
376
426
  const rootFiles = /* @__PURE__ */ new Set();
377
427
  const outputFiles = /* @__PURE__ */ new Map();
378
428
  return {
@@ -503,37 +553,51 @@ ${kolorist.cyan(
503
553
  bundleDebug("create ts program");
504
554
  timeRecord += Date.now() - startTime;
505
555
  },
506
- transform(_, id) {
507
- if (!program || !filter(id) || id.includes(".vue?vue") || !tjsRE.test(id) && !vueRE.test(id)) {
556
+ async transform(code, id) {
557
+ let resolver;
558
+ id = normalizePath(id).split("?")[0];
559
+ if (!host || !program || !filter(id) || !(resolver = resolvers.find((r) => r.supports(id))) && !tjsRE.test(id)) {
508
560
  return;
509
561
  }
510
562
  const startTime = Date.now();
511
- id = normalizePath(id);
512
- rootFiles.delete(id);
513
- let sourceFile = program.getSourceFile(normalizePath(id));
514
- if (!sourceFile && vueRE.test(id)) {
515
- sourceFile = program.getSourceFile(id + ".ts") || program.getSourceFile(id + ".js") || program.getSourceFile(id + ".tsx") || program.getSourceFile(id + ".jsx");
516
- }
517
- if (!sourceFile)
518
- return;
519
563
  const service = program.__vue.languageService;
520
- for (const outputFile of service.getEmitOutput(sourceFile.fileName, true).outputFiles) {
521
- outputFiles.set(resolve(publicRoot, outputFile.name), outputFile.text);
564
+ rootFiles.delete(id);
565
+ if (resolver) {
566
+ const result = await resolver.transform({
567
+ id,
568
+ code,
569
+ root: publicRoot,
570
+ host,
571
+ program,
572
+ service
573
+ });
574
+ for (const { path, content } of result) {
575
+ outputFiles.set(ensureAbsolute(path, publicRoot), content);
576
+ }
577
+ } else {
578
+ const sourceFile = program.getSourceFile(id);
579
+ if (sourceFile) {
580
+ for (const outputFile of service.getEmitOutput(sourceFile.fileName, true).outputFiles) {
581
+ outputFiles.set(resolve(publicRoot, outputFile.name), outputFile.text);
582
+ }
583
+ }
522
584
  }
523
- const dtsId = id.replace(tjsRE, ".d.ts");
585
+ const dtsId = id.replace(tjsRE, "") + ".d.ts";
524
586
  const dtsSourceFile = program.getSourceFile(dtsId);
525
587
  dtsSourceFile && filter(dtsSourceFile.fileName) && outputFiles.set(normalizePath(dtsSourceFile.fileName), dtsSourceFile.getFullText());
526
588
  timeRecord += Date.now() - startTime;
527
589
  },
528
590
  watchChange(id) {
529
- if (host && program && watchExtensionRE.test(id)) {
530
- const sourceFile = host.getSourceFile(normalizePath(id), ts__default.ScriptTarget.ESNext);
531
- if (sourceFile && filter(sourceFile.fileName)) {
532
- !vueRE.test(id) && rootFiles.add(sourceFile.fileName);
533
- program.__vue.projectVersion++;
534
- bundled = false;
535
- timeRecord = 0;
536
- }
591
+ id = normalizePath(id).split("?")[0];
592
+ if (!host || !program || !filter(id) || !resolvers.find((r) => r.supports(id)) && !tjsRE.test(id)) {
593
+ return;
594
+ }
595
+ const sourceFile = host.getSourceFile(normalizePath(id), ts__default.ScriptTarget.ESNext);
596
+ if (sourceFile) {
597
+ rootFiles.add(sourceFile.fileName);
598
+ program.__vue.projectVersion++;
599
+ bundled = false;
600
+ timeRecord = 0;
537
601
  }
538
602
  },
539
603
  async writeBundle() {
package/dist/index.d.ts CHANGED
@@ -2,9 +2,32 @@ import * as vite from 'vite';
2
2
  import { LogLevel } from 'vite';
3
3
  import ts from 'typescript';
4
4
 
5
- interface TransformWriteFile {
6
- filePath?: string;
7
- content?: string;
5
+ type MaybePromise<T> = T | Promise<T>;
6
+ interface Resolver {
7
+ /**
8
+ * The name of the resolver
9
+ *
10
+ * The later resolver with the same name will overwrite the earlier
11
+ */
12
+ name: string;
13
+ /**
14
+ * Determine whether the resolve supports the file
15
+ */
16
+ supports: (id: string) => void | boolean;
17
+ /**
18
+ * Transform the source file to declaration files
19
+ */
20
+ transform: (payload: {
21
+ id: string;
22
+ code: string;
23
+ root: string;
24
+ host: ts.CompilerHost;
25
+ program: ts.Program;
26
+ service: ts.LanguageService;
27
+ }) => MaybePromise<{
28
+ path: string;
29
+ content: string;
30
+ }[]>;
8
31
  }
9
32
  interface PluginOptions {
10
33
  /**
@@ -51,6 +74,12 @@ interface PluginOptions {
51
74
  * By default plugin will find config form root if not specify
52
75
  */
53
76
  tsconfigPath?: string;
77
+ /**
78
+ * Specify custom resolvers
79
+ *
80
+ * @default []
81
+ */
82
+ resolvers?: Resolver[];
54
83
  /**
55
84
  * Set which paths should exclude when transform aliases
56
85
  *
@@ -138,7 +167,7 @@ interface PluginOptions {
138
167
  *
139
168
  * @default () => {}
140
169
  */
141
- afterDiagnostic?: (diagnostics: readonly ts.Diagnostic[]) => void | Promise<void>;
170
+ afterDiagnostic?: (diagnostics: readonly ts.Diagnostic[]) => MaybePromise<void>;
142
171
  /**
143
172
  * Hook before each declaration file is written
144
173
  *
@@ -148,7 +177,10 @@ interface PluginOptions {
148
177
  *
149
178
  * @default () => {}
150
179
  */
151
- beforeWriteFile?: (filePath: string, content: string) => void | false | TransformWriteFile;
180
+ beforeWriteFile?: (filePath: string, content: string) => void | false | {
181
+ filePath?: string;
182
+ content?: string;
183
+ };
152
184
  /**
153
185
  * Hook after built
154
186
  *
@@ -156,7 +188,7 @@ interface PluginOptions {
156
188
  *
157
189
  * @default () => {}
158
190
  */
159
- afterBuild?: () => void | Promise<void>;
191
+ afterBuild?: () => MaybePromise<void>;
160
192
  }
161
193
 
162
194
  declare function dtsPlugin(options?: PluginOptions): vite.Plugin;
package/dist/index.mjs CHANGED
@@ -8,10 +8,10 @@ const require = __cjs_mod__.createRequire(import.meta.url);
8
8
  import { resolve as resolve$1, posix, isAbsolute, dirname, normalize, sep, relative, basename } from 'node:path';
9
9
  import { existsSync, readdirSync, lstatSync, rmdirSync } from 'node:fs';
10
10
  import { readFile, mkdir, writeFile, unlink } from 'node:fs/promises';
11
- import { platform, cpus } from 'node:os';
11
+ import { cpus } from 'node:os';
12
+ import { createParsedCommandLine } from '@vue/language-core';
12
13
  import ts from 'typescript';
13
14
  import { createFilter } from '@rollup/pluginutils';
14
- import { createParsedCommandLine } from '@vue/language-core';
15
15
  import { createProgram } from 'vue-tsc';
16
16
  import debug from 'debug';
17
17
  import { cyan, yellow, green } from 'kolorist';
@@ -114,13 +114,33 @@ function rollupDeclarationFiles({
114
114
  );
115
115
  }
116
116
 
117
- const windowsSlashRE = /\\/g;
117
+ const svelteRE = /\.svelte$/;
118
+ function SvelteResolver() {
119
+ return {
120
+ name: "svelte",
121
+ supports(id) {
122
+ return svelteRE.test(id);
123
+ },
124
+ transform({ id }) {
125
+ return [
126
+ {
127
+ path: `${id}.d.ts`,
128
+ content: "export { SvelteComponentTyped as default } from 'svelte';"
129
+ }
130
+ ];
131
+ }
132
+ };
133
+ }
134
+
135
+ const windowsSlashRE = /\\+/g;
118
136
  function slash(p) {
119
137
  return p.replace(windowsSlashRE, "/");
120
138
  }
121
- const isWindows = platform() === "win32";
122
139
  function normalizePath(id) {
123
- return posix.normalize(isWindows ? slash(id) : id);
140
+ return posix.normalize(slash(id));
141
+ }
142
+ function resolve(...paths) {
143
+ return normalizePath(resolve$1(...paths));
124
144
  }
125
145
  function isNativeObj(value) {
126
146
  return Object.prototype.toString.call(value) === "[object Object]";
@@ -132,7 +152,7 @@ function isPromise(value) {
132
152
  return !!value && (typeof value === "function" || typeof value === "object") && typeof value.then === "function";
133
153
  }
134
154
  function ensureAbsolute(path, root) {
135
- return normalizePath(path ? isAbsolute(path) ? path : resolve$1(root, path) : root);
155
+ return normalizePath(path ? isAbsolute(path) ? path : resolve(root, path) : root);
136
156
  }
137
157
  function ensureArray(value) {
138
158
  return Array.isArray(value) ? value : value ? [value] : [];
@@ -197,7 +217,7 @@ function removeDirIfEmpty(dir) {
197
217
  }
198
218
  let onlyHasDir = true;
199
219
  for (const file of readdirSync(dir)) {
200
- const abs = resolve$1(dir, file);
220
+ const abs = resolve(dir, file);
201
221
  if (lstatSync(abs).isDirectory()) {
202
222
  if (!removeDirIfEmpty(abs)) {
203
223
  onlyHasDir = false;
@@ -212,6 +232,38 @@ function removeDirIfEmpty(dir) {
212
232
  return onlyHasDir;
213
233
  }
214
234
 
235
+ const vueRE = /\.vue$/;
236
+ function VueResolver() {
237
+ return {
238
+ name: "vue",
239
+ supports(id) {
240
+ return vueRE.test(id);
241
+ },
242
+ transform({ id, root, program, service }) {
243
+ let sourceFile = program.getSourceFile(id);
244
+ if (!sourceFile && vueRE.test(id)) {
245
+ sourceFile = program.getSourceFile(id + ".ts") || program.getSourceFile(id + ".js") || program.getSourceFile(id + ".tsx") || program.getSourceFile(id + ".jsx");
246
+ }
247
+ if (!sourceFile)
248
+ return [];
249
+ return service.getEmitOutput(sourceFile.fileName, true).outputFiles.map((file) => {
250
+ return {
251
+ path: resolve(root, file.name),
252
+ content: file.text
253
+ };
254
+ });
255
+ }
256
+ };
257
+ }
258
+
259
+ function parseResolvers(resolvers) {
260
+ const nameMap = /* @__PURE__ */ new Map();
261
+ for (const resolver of resolvers) {
262
+ resolver.name && nameMap.set(resolver.name, resolver);
263
+ }
264
+ return Array.from(nameMap.values());
265
+ }
266
+
215
267
  const globSuffixRE = /^((?:.*\.[^.]+)|(?:\*+))$/;
216
268
  function normalizeGlob(path) {
217
269
  if (/[\\/]$/.test(path)) {
@@ -310,7 +362,6 @@ function removePureImport(content) {
310
362
  return content.replace(pureImportRE, "");
311
363
  }
312
364
 
313
- const vueRE = /\.vue$/;
314
365
  const jsRE = /\.(m|c)?jsx?$/;
315
366
  const tsRE = /\.(m|c)?tsx?$/;
316
367
  const dtsRE = /\.d\.(m|c)?tsx?$/;
@@ -318,7 +369,6 @@ const tjsRE = /\.(m|c)?(t|j)sx?$/;
318
369
  const mtjsRE = /\.m(t|j)sx?$/;
319
370
  const ctjsRE = /\.c(t|j)sx?$/;
320
371
  const fullRelativeRE = /^\.\.?\//;
321
- const watchExtensionRE = /\.(vue|(m|c)?(t|j)sx?)$/;
322
372
  const defaultIndex = "index.d.ts";
323
373
  const logPrefix = cyan("[vite:dts]");
324
374
  const bundleDebug = debug("vite-plugin-dts:bundle");
@@ -336,7 +386,6 @@ const fixedCompilerOptions = {
336
386
  const noop = () => {
337
387
  };
338
388
  const extPrefix = (file) => mtjsRE.test(file) ? "m" : ctjsRE.test(file) ? "c" : "";
339
- const resolve = (...paths) => normalizePath(resolve$1(...paths));
340
389
  function dtsPlugin(options = {}) {
341
390
  const {
342
391
  tsconfigPath,
@@ -373,6 +422,7 @@ function dtsPlugin(options = {}) {
373
422
  let filter;
374
423
  let bundled = false;
375
424
  let timeRecord = 0;
425
+ const resolvers = parseResolvers([VueResolver(), SvelteResolver(), ...options.resolvers || []]);
376
426
  const rootFiles = /* @__PURE__ */ new Set();
377
427
  const outputFiles = /* @__PURE__ */ new Map();
378
428
  return {
@@ -503,37 +553,51 @@ ${cyan(
503
553
  bundleDebug("create ts program");
504
554
  timeRecord += Date.now() - startTime;
505
555
  },
506
- transform(_, id) {
507
- if (!program || !filter(id) || id.includes(".vue?vue") || !tjsRE.test(id) && !vueRE.test(id)) {
556
+ async transform(code, id) {
557
+ let resolver;
558
+ id = normalizePath(id).split("?")[0];
559
+ if (!host || !program || !filter(id) || !(resolver = resolvers.find((r) => r.supports(id))) && !tjsRE.test(id)) {
508
560
  return;
509
561
  }
510
562
  const startTime = Date.now();
511
- id = normalizePath(id);
512
- rootFiles.delete(id);
513
- let sourceFile = program.getSourceFile(normalizePath(id));
514
- if (!sourceFile && vueRE.test(id)) {
515
- sourceFile = program.getSourceFile(id + ".ts") || program.getSourceFile(id + ".js") || program.getSourceFile(id + ".tsx") || program.getSourceFile(id + ".jsx");
516
- }
517
- if (!sourceFile)
518
- return;
519
563
  const service = program.__vue.languageService;
520
- for (const outputFile of service.getEmitOutput(sourceFile.fileName, true).outputFiles) {
521
- outputFiles.set(resolve(publicRoot, outputFile.name), outputFile.text);
564
+ rootFiles.delete(id);
565
+ if (resolver) {
566
+ const result = await resolver.transform({
567
+ id,
568
+ code,
569
+ root: publicRoot,
570
+ host,
571
+ program,
572
+ service
573
+ });
574
+ for (const { path, content } of result) {
575
+ outputFiles.set(ensureAbsolute(path, publicRoot), content);
576
+ }
577
+ } else {
578
+ const sourceFile = program.getSourceFile(id);
579
+ if (sourceFile) {
580
+ for (const outputFile of service.getEmitOutput(sourceFile.fileName, true).outputFiles) {
581
+ outputFiles.set(resolve(publicRoot, outputFile.name), outputFile.text);
582
+ }
583
+ }
522
584
  }
523
- const dtsId = id.replace(tjsRE, ".d.ts");
585
+ const dtsId = id.replace(tjsRE, "") + ".d.ts";
524
586
  const dtsSourceFile = program.getSourceFile(dtsId);
525
587
  dtsSourceFile && filter(dtsSourceFile.fileName) && outputFiles.set(normalizePath(dtsSourceFile.fileName), dtsSourceFile.getFullText());
526
588
  timeRecord += Date.now() - startTime;
527
589
  },
528
590
  watchChange(id) {
529
- if (host && program && watchExtensionRE.test(id)) {
530
- const sourceFile = host.getSourceFile(normalizePath(id), ts.ScriptTarget.ESNext);
531
- if (sourceFile && filter(sourceFile.fileName)) {
532
- !vueRE.test(id) && rootFiles.add(sourceFile.fileName);
533
- program.__vue.projectVersion++;
534
- bundled = false;
535
- timeRecord = 0;
536
- }
591
+ id = normalizePath(id).split("?")[0];
592
+ if (!host || !program || !filter(id) || !resolvers.find((r) => r.supports(id)) && !tjsRE.test(id)) {
593
+ return;
594
+ }
595
+ const sourceFile = host.getSourceFile(normalizePath(id), ts.ScriptTarget.ESNext);
596
+ if (sourceFile) {
597
+ rootFiles.add(sourceFile.fileName);
598
+ program.__vue.projectVersion++;
599
+ bundled = false;
600
+ timeRecord = 0;
537
601
  }
538
602
  },
539
603
  async writeBundle() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-dts",
3
- "version": "3.0.3",
3
+ "version": "3.1.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "qmhc",
@@ -9,17 +9,16 @@
9
9
  "build": "tsx scripts/build.ts",
10
10
  "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s --commit-path .",
11
11
  "dev": "unbuild --stub",
12
- "lint": "pnpm run lint:src && pnpm run lint:example",
13
- "lint:src": "eslint --fix --ext .js,.jsx,.ts,.tsx,.vue src/**",
14
- "lint:example": "eslint --fix --ext .js,.jsx,.ts,.tsx,.vue example/{src,components}/**",
12
+ "lint": "eslint --ext .js,.jsx,.ts,.tsx \"{src,tests}/**\"",
15
13
  "_postinstall": "is-ci || husky install",
16
14
  "postpublish": "pinst --enable",
17
15
  "precommit": "lint-staged -c ./.husky/.lintstagedrc -q",
18
16
  "prepublishOnly": "pinst --disable",
19
- "prettier": "pretty-quick --staged",
17
+ "prettier": "pretty-quick --staged && pnpm run lint",
20
18
  "release": "tsx scripts/release.ts",
21
19
  "test": "vitest run",
22
20
  "test:react": "pnpm -C examples/react build",
21
+ "test:svelte": "pnpm -C examples/svelte build",
23
22
  "test:ts": "pnpm -C examples/ts build",
24
23
  "test:vue": "pnpm -C examples/vue build"
25
24
  },
@@ -72,15 +71,14 @@
72
71
  "@types/semver": "^7.5.0",
73
72
  "@typescript-eslint/eslint-plugin": "^5.60.0",
74
73
  "@typescript-eslint/parser": "^5.60.0",
74
+ "@vexip-ui/commitlint-config": "^0.1.0",
75
+ "@vexip-ui/eslint-config": "^0.6.3",
76
+ "@vexip-ui/prettier-config": "^0.2.0",
75
77
  "@vue/eslint-config-standard": "^8.0.1",
76
78
  "@vue/eslint-config-typescript": "^11.0.3",
77
79
  "conventional-changelog-cli": "^3.0.0",
78
80
  "cross-env": "^7.0.3",
79
81
  "eslint": "^8.43.0",
80
- "eslint-plugin-import": "^2.27.5",
81
- "eslint-plugin-node": "^11.1.0",
82
- "eslint-plugin-promise": "^6.1.1",
83
- "eslint-plugin-vue": "^9.15.1",
84
82
  "execa": "^7.1.1",
85
83
  "husky": "^8.0.3",
86
84
  "is-ci": "^3.0.1",
@@ -93,7 +91,7 @@
93
91
  "rimraf": "^5.0.1",
94
92
  "semver": "^7.5.3",
95
93
  "tsx": "^3.12.7",
96
- "typescript": "^5.1.3",
94
+ "typescript": "5.0.4",
97
95
  "unbuild": "^1.2.1",
98
96
  "vite": "^4.3.9",
99
97
  "vitest": "^0.32.2"