@dxup/nuxt 0.0.3 → 0.0.5

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
@@ -4,11 +4,12 @@
4
4
  [![downloads](https://img.shields.io/npm/dm/@dxup/nuxt?color=007EC7&label=downloads)](https://www.npmjs.com/package/@dxup/nuxt)
5
5
  [![license](https://img.shields.io/npm/l/@dxup/nuxt?color=007EC7&label=license)](/LICENSE)
6
6
 
7
- This is a collection of TypeScript and Vue plugins that improves Nuxt DX.
7
+ This is a collection of TypeScript plugins that improves Nuxt DX.
8
8
 
9
9
  ## Features
10
10
 
11
- - Go to definition for nitro routes in data fetching methods within Vue files
11
+ - Update references when renaming auto imported component files
12
+ - Go to definition for nitro routes in data fetching methods
12
13
  - Go to definition for runtime config
13
14
  - [@dxup/unimport](/packages/unimport)
14
15
 
package/dist/module.d.ts CHANGED
@@ -2,6 +2,7 @@ import * as _nuxt_schema0 from "@nuxt/schema";
2
2
 
3
3
  //#region src/module/index.d.ts
4
4
  interface ModuleOptions {
5
+ components?: boolean;
5
6
  nitroRoutes?: boolean;
6
7
  runtimeConfig?: boolean;
7
8
  unimport?: boolean;
package/dist/module.js CHANGED
@@ -1,8 +1,67 @@
1
1
  import { addTemplate, defineNuxtModule } from "@nuxt/kit";
2
+ import { Buffer } from "node:buffer";
3
+ import EventEmitter from "node:events";
4
+ import { mkdir, open, readFile, writeFile } from "node:fs/promises";
5
+ import { watch } from "chokidar";
6
+ import { dirname, join } from "pathe";
2
7
 
3
8
  //#region package.json
4
9
  var name = "@dxup/nuxt";
5
10
 
11
+ //#endregion
12
+ //#region src/event/client.ts
13
+ const responseRE = /^```json \{(?<key>.*)\}\n(?<value>[\s\S]*?)\n```$/;
14
+ async function createEventClient(nuxt) {
15
+ const path = join(nuxt.options.buildDir, "dxup/events.md");
16
+ await mkdir(dirname(path), { recursive: true });
17
+ await writeFile(path, "");
18
+ const fd = await open(path, "r");
19
+ const watcher = watch(path, { ignoreInitial: true });
20
+ nuxt.hook("close", async () => {
21
+ await fd.close();
22
+ await watcher.close();
23
+ });
24
+ const client = new EventEmitter();
25
+ let offset = 0;
26
+ watcher.on("change", async (path$1, stats) => {
27
+ if (!stats || stats.size <= offset) return;
28
+ const pos = offset;
29
+ offset = stats.size;
30
+ const buffer = Buffer.alloc(offset - pos);
31
+ await fd.read(buffer, 0, buffer.length, pos);
32
+ const match = buffer.toString("utf-8").trim().match(responseRE);
33
+ if (match) {
34
+ const { key, value } = match.groups;
35
+ client.emit(key, JSON.parse(value));
36
+ }
37
+ });
38
+ return client;
39
+ }
40
+
41
+ //#endregion
42
+ //#region src/module/events.ts
43
+ const uppercaseRE = /[A-Z]/;
44
+ async function onComponentsRename(nuxt, { fileName, references }) {
45
+ const component = Object.values(nuxt.apps).flatMap((app) => app.components).find((c) => c.filePath === fileName);
46
+ if (!component) return;
47
+ const tasks = Object.entries(references).map(async ([fileName$1, references$1]) => {
48
+ const code = await readFile(fileName$1, "utf-8");
49
+ const chunks = [];
50
+ let offset = 0;
51
+ for (const { textSpan, lazy } of references$1) {
52
+ const start = textSpan.start;
53
+ const end = start + textSpan.length;
54
+ const oldName = code.slice(start, end);
55
+ const newName = uppercaseRE.test(oldName) ? lazy ? "Lazy" + component.pascalName : component.pascalName : lazy ? "lazy-" + component.kebabName : component.kebabName;
56
+ chunks.push(code.slice(offset, start), newName);
57
+ offset = end;
58
+ }
59
+ chunks.push(code.slice(offset));
60
+ await writeFile(fileName$1, chunks.join(""));
61
+ });
62
+ await Promise.all(tasks);
63
+ }
64
+
6
65
  //#endregion
7
66
  //#region src/module/index.ts
8
67
  var module_default = defineNuxtModule({
@@ -11,32 +70,34 @@ var module_default = defineNuxtModule({
11
70
  configKey: "dxup"
12
71
  },
13
72
  defaults: {
73
+ components: true,
14
74
  nitroRoutes: true,
15
75
  runtimeConfig: true,
16
76
  unimport: true
17
77
  },
18
78
  async setup(options, nuxt) {
19
79
  const pluginsTs = [{ name: "@dxup/nuxt" }];
20
- const pluginsVue = [];
21
- if (options.nitroRoutes) pluginsVue.push("@dxup/nuxt/vue/nitro-routes");
22
- if (options.unimport) pluginsTs.push({ name: "@dxup/unimport" });
80
+ if (options.unimport) pluginsTs.unshift({ name: "@dxup/unimport" });
23
81
  append(pluginsTs, nuxt.options, "typescript", "tsConfig", "compilerOptions");
24
82
  append(pluginsTs, nuxt.options.nitro, "typescript", "tsConfig", "compilerOptions");
25
83
  append(pluginsTs, nuxt.options, "typescript", "sharedTsConfig", "compilerOptions");
26
84
  append(pluginsTs, nuxt.options, "typescript", "nodeTsConfig", "compilerOptions");
27
- append(pluginsVue, nuxt.options, "typescript", "tsConfig", "vueCompilerOptions");
28
85
  addTemplate({
29
- filename: "dxup.json",
86
+ filename: "dxup/data.json",
30
87
  write: true,
31
88
  getContents() {
32
89
  const data = {
90
+ buildDir: nuxt.options.buildDir,
91
+ serverDir: nuxt.options.serverDir,
92
+ configFiles: [...nuxt.options._nuxtConfigFiles, ...nuxt.options._layers.map((layer) => layer._configFile).filter(Boolean)],
93
+ components: options.components,
33
94
  nitroRoutes: options.nitroRoutes,
34
- runtimeConfig: options.runtimeConfig,
35
- configFiles: [...nuxt.options._nuxtConfigFiles, ...nuxt.options._layers.map((layer) => layer._configFile).filter(Boolean)]
95
+ runtimeConfig: options.runtimeConfig
36
96
  };
37
97
  return JSON.stringify(data, null, 2);
38
98
  }
39
99
  });
100
+ (await createEventClient(nuxt)).on("components:rename", (data) => onComponentsRename(nuxt, data));
40
101
  }
41
102
  });
42
103
  function append(plugins, target, ...keys) {
@@ -21,38 +21,112 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  }) : target, mod));
22
22
 
23
23
  //#endregion
24
- const require_ast = require('./ast-DJW3KREB.cjs');
25
- let node_path = require("node:path");
26
- node_path = __toESM(node_path);
24
+ let pathe = require("pathe");
25
+ pathe = __toESM(pathe);
26
+ let node_fs_promises = require("node:fs/promises");
27
+ node_fs_promises = __toESM(node_fs_promises);
27
28
 
28
- //#region src/typescript/index.ts
29
- const plugin = (module$1) => {
30
- const { typescript: ts } = module$1;
31
- return { create(info) {
32
- const path = (0, node_path.join)(info.languageServiceHost.getCurrentDirectory(), "dxup.json");
33
- const data = {
34
- nitroRoutes: true,
35
- runtimeConfig: true,
36
- configFiles: [],
37
- ...JSON.parse(ts.sys.readFile(path) ?? "{}")
38
- };
39
- const context = {
40
- ts,
41
- info,
42
- data
43
- };
44
- for (const [key, method] of [["getDefinitionAndBoundSpan", getDefinitionAndBoundSpan.bind(null, context)]]) {
45
- const original = info.languageService[key];
46
- info.languageService[key] = method(original);
29
+ //#region src/event/server.ts
30
+ function createEventServer(info) {
31
+ const path = (0, pathe.join)(info.project.getCurrentDirectory(), "dxup/events.md");
32
+ async function write(key, data) {
33
+ try {
34
+ await (0, node_fs_promises.appendFile)(path, `\`\`\`json {${key}}\n${JSON.stringify(data, null, 2)}\n\`\`\`\n`);
35
+ } catch {}
36
+ }
37
+ return { write };
38
+ }
39
+
40
+ //#endregion
41
+ //#region src/typescript/features/findRenameLocations.ts
42
+ function findRenameLocations(context, findRenameLocations$1) {
43
+ const { data } = context;
44
+ return (...args) => {
45
+ return findRenameLocations$1(...args)?.filter((edit) => {
46
+ return !edit.fileName.startsWith(data.buildDir);
47
+ });
48
+ };
49
+ }
50
+
51
+ //#endregion
52
+ //#region ../shared/src/index.ts
53
+ function* forEachTouchNode(ts, sourceFile, position) {
54
+ yield* binaryVisit(ts, sourceFile, sourceFile, position);
55
+ }
56
+ function* binaryVisit(ts, sourceFile, node, position) {
57
+ const nodes = [];
58
+ ts.forEachChild(node, (child) => {
59
+ nodes.push(child);
60
+ });
61
+ let left = 0;
62
+ let right = nodes.length - 1;
63
+ while (left <= right) {
64
+ const mid = Math.floor((left + right) / 2);
65
+ const node$1 = nodes[mid];
66
+ const start = node$1.getStart(sourceFile);
67
+ const end = node$1.getEnd();
68
+ if (position < start) right = mid - 1;
69
+ else if (position > end) left = mid + 1;
70
+ else {
71
+ yield node$1;
72
+ yield* binaryVisit(ts, sourceFile, node$1, position);
73
+ return;
47
74
  }
48
- return info.languageService;
49
- } };
50
- };
51
- var typescript_default = plugin;
75
+ }
76
+ }
77
+
78
+ //#endregion
79
+ //#region src/typescript/features/getDefinitionAndBoundSpan.ts
80
+ const fetchFunctions = new Set([
81
+ "$fetch",
82
+ "useFetch",
83
+ "useLazyFetch"
84
+ ]);
85
+ const nonApiRE = /^(?!\/api\/)/;
52
86
  function getDefinitionAndBoundSpan(context, getDefinitionAndBoundSpan$1) {
53
- const { info, data } = context;
54
- return (fileName, position) => {
55
- const result = getDefinitionAndBoundSpan$1(fileName, position);
87
+ const { ts, info, data } = context;
88
+ return (...args) => {
89
+ const result = getDefinitionAndBoundSpan$1(...args);
90
+ if (!result && data.nitroRoutes) {
91
+ const program$1 = info.languageService.getProgram();
92
+ const sourceFile = program$1.getSourceFile(args[0]);
93
+ if (!sourceFile) return;
94
+ const checker = program$1.getTypeChecker();
95
+ for (const node of forEachTouchNode(ts, sourceFile, args[1])) {
96
+ if (!ts.isCallExpression(node) || !ts.isIdentifier(node.expression) || !fetchFunctions.has(node.expression.text) || !node.arguments.length) continue;
97
+ const firstArg = node.arguments[0];
98
+ const start = firstArg.getStart(sourceFile);
99
+ const end = firstArg.getEnd();
100
+ if (args[1] < start || args[1] > end) continue;
101
+ const resolvedSignature = checker.getResolvedSignature(node);
102
+ if (!resolvedSignature) break;
103
+ const typeArguments = checker.getTypeArgumentsForResolvedSignature(resolvedSignature);
104
+ const routeType = typeArguments?.[2];
105
+ const methodtype = typeArguments?.[3];
106
+ if (!routeType?.isStringLiteral() || !methodtype?.isStringLiteral()) break;
107
+ const route = routeType.value.replace(nonApiRE, "/routes");
108
+ const method = methodtype.value;
109
+ const path = (0, pathe.join)(data.serverDir, `${route}.${method}.ts`);
110
+ return {
111
+ textSpan: {
112
+ start,
113
+ length: end - start
114
+ },
115
+ definitions: [{
116
+ fileName: path,
117
+ textSpan: {
118
+ start: 0,
119
+ length: 0
120
+ },
121
+ kind: ts.ScriptElementKind.scriptElement,
122
+ name: path,
123
+ containerKind: ts.ScriptElementKind.unknown,
124
+ containerName: ""
125
+ }]
126
+ };
127
+ }
128
+ return;
129
+ }
56
130
  if (!result?.definitions?.length) return result;
57
131
  const program = info.languageService.getProgram();
58
132
  const definitions = new Set(result.definitions);
@@ -61,8 +135,7 @@ function getDefinitionAndBoundSpan(context, getDefinitionAndBoundSpan$1) {
61
135
  const sourceFile = program.getSourceFile(definition.fileName);
62
136
  if (!sourceFile) continue;
63
137
  let result$1 = [];
64
- if (data.nitroRoutes && definition.fileName.endsWith("nitro-routes.d.ts")) result$1 = visitNitroRoutes(context, sourceFile, definition, getDefinitionAndBoundSpan$1);
65
- else if (data.runtimeConfig && definition.fileName.endsWith("runtime-config.d.ts")) result$1 = visitRuntimeConfig(context, sourceFile, definition);
138
+ if (data.runtimeConfig && definition.fileName.endsWith("runtime-config.d.ts")) result$1 = visitRuntimeConfig(context, sourceFile, definition);
66
139
  if (result$1?.length) {
67
140
  for (const definition$1 of result$1) definitions.add(definition$1);
68
141
  skippedDefinitions.push(definition);
@@ -75,32 +148,11 @@ function getDefinitionAndBoundSpan(context, getDefinitionAndBoundSpan$1) {
75
148
  };
76
149
  };
77
150
  }
78
- function visitNitroRoutes(context, sourceFile, definition, getDefinitionAndBoundSpan$1) {
79
- const { ts } = context;
80
- const definitions = [];
81
- for (const node of require_ast.forEachNode(sourceFile)) {
82
- if (!ts.isPropertySignature(node) || !node.type || !ts.isTypeLiteralNode(node.type)) continue;
83
- const { textSpan } = definition;
84
- const start = node.name.getStart(sourceFile);
85
- const end = node.name.getEnd();
86
- if (start !== textSpan.start || end - start !== textSpan.length) continue;
87
- for (const member of node.type.members) {
88
- if (!ts.isPropertySignature(member)) continue;
89
- const pos = (((((member.type?.typeArguments?.[0])?.typeArguments?.[0])?.typeArguments?.[0])?.typeArguments?.[0])?.qualifier)?.getStart(sourceFile);
90
- if (pos !== void 0) {
91
- const res = getDefinitionAndBoundSpan$1(definition.fileName, pos);
92
- if (res?.definitions?.length) definitions.push(...res.definitions);
93
- }
94
- }
95
- break;
96
- }
97
- return definitions;
98
- }
99
151
  function visitRuntimeConfig(context, sourceFile, definition) {
100
152
  const { ts } = context;
101
153
  let definitions = [];
102
154
  const path = [];
103
- require_ast.walkNodes(sourceFile, (node, next) => {
155
+ for (const node of forEachTouchNode(ts, sourceFile, definition.textSpan.start)) {
104
156
  let key;
105
157
  if (ts.isInterfaceDeclaration(node) && ts.isIdentifier(node.name)) key = node.name.text;
106
158
  else if (ts.isPropertySignature(node) && ts.isIdentifier(node.name)) {
@@ -111,13 +163,11 @@ function visitRuntimeConfig(context, sourceFile, definition) {
111
163
  if (start === textSpan.start && end - start === textSpan.length) {
112
164
  path.push(key);
113
165
  definitions = [...proxyRuntimeConfig(context, definition, path)];
114
- return;
166
+ break;
115
167
  }
116
168
  }
117
169
  if (key !== void 0) path.push(key);
118
- next();
119
- if (key !== void 0) path.pop();
120
- });
170
+ }
121
171
  return definitions;
122
172
  }
123
173
  function* proxyRuntimeConfig(context, definition, path) {
@@ -176,5 +226,95 @@ function* proxyRuntimeConfig(context, definition, path) {
176
226
  }
177
227
  }
178
228
 
229
+ //#endregion
230
+ //#region src/typescript/utils.ts
231
+ function toSourceSpan(language, fileName, textSpan) {
232
+ const sourceScript = language?.scripts.get(fileName);
233
+ if (!sourceScript?.generated) return;
234
+ const serviceScript = sourceScript.generated.languagePlugin.typescript?.getServiceScript(sourceScript.generated.root);
235
+ if (!serviceScript) return;
236
+ const map = language.maps.get(serviceScript.code, sourceScript);
237
+ const leadingOffset = sourceScript.snapshot.getLength();
238
+ for (const [start, end] of map.toSourceRange(textSpan.start - leadingOffset, textSpan.start + textSpan.length - leadingOffset, false)) return {
239
+ start,
240
+ length: end - start
241
+ };
242
+ }
243
+
244
+ //#endregion
245
+ //#region src/typescript/features/getEditsForFileRename.ts
246
+ function getEditsForFileRename(context, getEditsForFileRename$1) {
247
+ const { ts, info, data, server } = context;
248
+ return (...args) => {
249
+ const result = getEditsForFileRename$1(...args);
250
+ if (!result?.length) return result;
251
+ const program = info.languageService.getProgram();
252
+ const changes = [];
253
+ const references = {};
254
+ for (const change of result) {
255
+ const { fileName, textChanges } = change;
256
+ if (data.components && fileName.endsWith("components.d.ts")) {
257
+ const sourceFile = program.getSourceFile(fileName);
258
+ if (!sourceFile) continue;
259
+ for (const { span } of textChanges) for (const node of forEachTouchNode(ts, sourceFile, span.start)) {
260
+ if (!ts.isPropertySignature(node) && !ts.isVariableDeclaration(node)) continue;
261
+ const position = node.name.getStart(sourceFile);
262
+ const res = info.languageService.getReferencesAtPosition(fileName, position)?.filter((entry) => !entry.fileName.startsWith(data.buildDir));
263
+ const lazy = node.type && ts.isTypeReferenceNode(node.type) && ts.isIdentifier(node.type.typeName) && node.type.typeName.text === "LazyComponent";
264
+ for (const { fileName: fileName$1, textSpan } of res ?? []) (references[fileName$1] ??= []).push({
265
+ textSpan: toSourceSpan(context.language, fileName$1, textSpan) ?? textSpan,
266
+ lazy: lazy || void 0
267
+ });
268
+ break;
269
+ }
270
+ }
271
+ if (!fileName.startsWith(data.buildDir)) changes.push(change);
272
+ }
273
+ if (Object.keys(references).length) server.write("components:rename", {
274
+ fileName: args[1],
275
+ references
276
+ });
277
+ return changes;
278
+ };
279
+ }
280
+
281
+ //#endregion
282
+ //#region src/typescript/index.ts
283
+ const plugin = (module$1) => {
284
+ const { typescript: ts } = module$1;
285
+ return { create(info) {
286
+ const currentDirectory = info.languageServiceHost.getCurrentDirectory();
287
+ const path = (0, pathe.join)(currentDirectory, "dxup/data.json");
288
+ const data = {
289
+ buildDir: currentDirectory,
290
+ configFiles: [],
291
+ components: true,
292
+ nitroRoutes: true,
293
+ runtimeConfig: true,
294
+ ...JSON.parse(ts.sys.readFile(path) ?? "{}")
295
+ };
296
+ const server = createEventServer(info);
297
+ const context = {
298
+ ts,
299
+ info,
300
+ data,
301
+ server
302
+ };
303
+ setTimeout(() => {
304
+ context.language = (info.project.__vue__ ?? info.project["program"]?.__vue__)?.language;
305
+ }, 500);
306
+ for (const [key, method] of [
307
+ ["findRenameLocations", findRenameLocations.bind(null, context)],
308
+ ["getDefinitionAndBoundSpan", getDefinitionAndBoundSpan.bind(null, context)],
309
+ ["getEditsForFileRename", getEditsForFileRename.bind(null, context)]
310
+ ]) {
311
+ const original = info.languageService[key];
312
+ info.languageService[key] = method(original);
313
+ }
314
+ return info.languageService;
315
+ } };
316
+ };
317
+ var typescript_default = plugin;
318
+
179
319
  //#endregion
180
320
  module.exports = typescript_default;
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "@dxup/nuxt",
3
3
  "type": "module",
4
- "version": "0.0.3",
5
- "description": "TypeScript and Vue plugins for Nuxt",
4
+ "version": "0.0.5",
5
+ "description": "TypeScript plugins for Nuxt",
6
6
  "author": "KazariEX",
7
7
  "license": "MIT",
8
8
  "repository": "KazariEX/dxup",
9
9
  "exports": {
10
10
  ".": "./dist/module.js",
11
- "./vue/nitro-routes": "./dist/vue/nitro-routes.cjs",
12
11
  "./package.json": "./package.json"
13
12
  },
14
13
  "main": "./dist/typescript.cjs",
@@ -18,9 +17,14 @@
18
17
  ],
19
18
  "dependencies": {
20
19
  "@nuxt/kit": "^4.1.2",
20
+ "chokidar": "^4.0.3",
21
+ "pathe": "2.0.3",
21
22
  "@dxup/unimport": "^0.0.1"
22
23
  },
23
24
  "devDependencies": {
25
+ "@dxup/shared": "",
26
+ "@volar/language-core": "^2.4.23",
27
+ "@volar/typescript": "^2.4.23",
24
28
  "@vue/language-core": "^3.0.8",
25
29
  "nuxt": "^4.1.2",
26
30
  "typescript": "^5.9.2"
@@ -28,6 +32,6 @@
28
32
  "scripts": {
29
33
  "build": "tsdown",
30
34
  "dev": "tsdown -w --sourcemap",
31
- "release": "bumpp"
35
+ "release": "node ../../scripts/release.ts"
32
36
  }
33
37
  }
@@ -1,31 +0,0 @@
1
-
2
- //#region src/utils/ast.ts
3
- function* forEachNode(node) {
4
- yield node;
5
- const children = [];
6
- node.forEachChild((child) => {
7
- children.push(child);
8
- });
9
- for (const child of children) yield* forEachNode(child);
10
- }
11
- function walkNodes(node, callback) {
12
- callback(node, () => {
13
- node.forEachChild((child) => {
14
- walkNodes(child, callback);
15
- });
16
- });
17
- }
18
-
19
- //#endregion
20
- Object.defineProperty(exports, 'forEachNode', {
21
- enumerable: true,
22
- get: function () {
23
- return forEachNode;
24
- }
25
- });
26
- Object.defineProperty(exports, 'walkNodes', {
27
- enumerable: true,
28
- get: function () {
29
- return walkNodes;
30
- }
31
- });
@@ -1,33 +0,0 @@
1
- const require_ast = require('../ast-DJW3KREB.cjs');
2
-
3
- //#region src/vue/nitro-routes.ts
4
- const functionNames = new Set([
5
- "$fetch",
6
- "useFetch",
7
- "useLazyFetch"
8
- ]);
9
- const plugin = ({ modules: { typescript: ts } }) => {
10
- return {
11
- version: 2.2,
12
- resolveEmbeddedCode(fileName, sfc, embeddedFile) {
13
- if (!embeddedFile.id.startsWith("script_")) return;
14
- const { scriptSetup } = sfc;
15
- if (!scriptSetup) return;
16
- const codes = [];
17
- for (const node of require_ast.forEachNode(scriptSetup.ast)) if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && functionNames.has(node.expression.text) && node.arguments.length && ts.isStringLiteralLike(node.arguments[0])) {
18
- const arg = node.arguments[0];
19
- codes.push(`/** @type {__VLS_InternalApi[`, [
20
- arg.getText(scriptSetup.ast),
21
- sfc.scriptSetup.name,
22
- arg.getStart(scriptSetup.ast),
23
- { navigation: true }
24
- ], `]} */;\n`);
25
- }
26
- if (codes.length) embeddedFile.content.push(`import type { InternalApi as __VLS_InternalApi } from 'nitropack/types';\n`, ...codes);
27
- }
28
- };
29
- };
30
- var nitro_routes_default = plugin;
31
-
32
- //#endregion
33
- module.exports = nitro_routes_default;
@@ -1,5 +0,0 @@
1
- import { VueLanguagePlugin } from "@vue/language-core";
2
-
3
- //#region src/vue/nitro-routes.d.ts
4
- declare const plugin: VueLanguagePlugin;
5
- export = plugin;