@dxup/nuxt 0.0.4 → 0.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
@@ -4,12 +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 TypeScript plugin that improves Nuxt DX.
8
8
 
9
9
  ## Features
10
10
 
11
11
  - Update references when renaming auto imported component files
12
- - Go to definition for nitro routes in data fetching methods within Vue files
12
+ - Go to definition for nitro routes in data fetching methods
13
13
  - Go to definition for runtime config
14
14
  - [@dxup/unimport](/packages/unimport)
15
15
 
package/dist/module.js CHANGED
@@ -77,20 +77,18 @@ var module_default = defineNuxtModule({
77
77
  },
78
78
  async setup(options, nuxt) {
79
79
  const pluginsTs = [{ name: "@dxup/nuxt" }];
80
- const pluginsVue = [];
81
- if (options.nitroRoutes) pluginsVue.push("@dxup/nuxt/vue/nitro-routes");
82
80
  if (options.unimport) pluginsTs.unshift({ name: "@dxup/unimport" });
83
81
  append(pluginsTs, nuxt.options, "typescript", "tsConfig", "compilerOptions");
84
82
  append(pluginsTs, nuxt.options.nitro, "typescript", "tsConfig", "compilerOptions");
85
83
  append(pluginsTs, nuxt.options, "typescript", "sharedTsConfig", "compilerOptions");
86
84
  append(pluginsTs, nuxt.options, "typescript", "nodeTsConfig", "compilerOptions");
87
- append(pluginsVue, nuxt.options, "typescript", "tsConfig", "vueCompilerOptions");
88
85
  addTemplate({
89
86
  filename: "dxup/data.json",
90
87
  write: true,
91
88
  getContents() {
92
89
  const data = {
93
90
  buildDir: nuxt.options.buildDir,
91
+ serverDir: nuxt.options.serverDir,
94
92
  configFiles: [...nuxt.options._nuxtConfigFiles, ...nuxt.options._layers.map((layer) => layer._configFile).filter(Boolean)],
95
93
  components: options.components,
96
94
  nitroRoutes: options.nitroRoutes,
@@ -21,7 +21,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  }) : target, mod));
22
22
 
23
23
  //#endregion
24
- const require_src = require('./src-BfXMgjZD.cjs');
25
24
  let pathe = require("pathe");
26
25
  pathe = __toESM(pathe);
27
26
  let node_fs_promises = require("node:fs/promises");
@@ -30,49 +29,16 @@ node_fs_promises = __toESM(node_fs_promises);
30
29
  //#region src/event/server.ts
31
30
  function createEventServer(info) {
32
31
  const path = (0, pathe.join)(info.project.getCurrentDirectory(), "dxup/events.md");
33
- function write(key, data) {
34
- return (0, node_fs_promises.appendFile)(path, `\`\`\`json {${key}}\n${JSON.stringify(data, null, 2)}\n\`\`\`\n`).catch();
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 {}
35
36
  }
36
37
  return { write };
37
38
  }
38
39
 
39
40
  //#endregion
40
- //#region src/typescript/index.ts
41
- const plugin = (module$1) => {
42
- const { typescript: ts } = module$1;
43
- return { create(info) {
44
- const currentDirectory = info.languageServiceHost.getCurrentDirectory();
45
- const path = (0, pathe.join)(currentDirectory, "dxup/data.json");
46
- const data = {
47
- buildDir: currentDirectory,
48
- configFiles: [],
49
- components: true,
50
- nitroRoutes: true,
51
- runtimeConfig: true,
52
- ...JSON.parse(ts.sys.readFile(path) ?? "{}")
53
- };
54
- const server = createEventServer(info);
55
- const context = {
56
- ts,
57
- info,
58
- data,
59
- server
60
- };
61
- setTimeout(() => {
62
- context.language = (info.project.__vue__ ?? info.project["program"]?.__vue__)?.language;
63
- }, 500);
64
- for (const [key, method] of [
65
- ["findRenameLocations", findRenameLocations.bind(null, context)],
66
- ["getDefinitionAndBoundSpan", getDefinitionAndBoundSpan.bind(null, context)],
67
- ["getEditsForFileRename", getEditsForFileRename.bind(null, context)]
68
- ]) {
69
- const original = info.languageService[key];
70
- info.languageService[key] = method(original);
71
- }
72
- return info.languageService;
73
- } };
74
- };
75
- var typescript_default = plugin;
41
+ //#region src/typescript/features/findRenameLocations.ts
76
42
  function findRenameLocations(context, findRenameLocations$1) {
77
43
  const { data } = context;
78
44
  return (...args) => {
@@ -81,58 +47,109 @@ function findRenameLocations(context, findRenameLocations$1) {
81
47
  });
82
48
  };
83
49
  }
50
+
51
+ //#endregion
52
+ //#region ../shared/src/index.ts
53
+ function* forEachTouchingNode(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;
74
+ }
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\/)/;
84
86
  function getDefinitionAndBoundSpan(context, getDefinitionAndBoundSpan$1) {
85
- const { info, data } = context;
87
+ const { ts, info, data } = context;
86
88
  return (...args) => {
87
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 forEachTouchingNode(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, methodType] = (node.expression.text === "$fetch" ? typeArguments?.[2] && checker.getTypeArguments(typeArguments?.[2]) : typeArguments?.slice(2)) ?? [];
105
+ if (!routeType?.isStringLiteral() || !methodType?.isStringLiteral()) break;
106
+ const route = routeType.value.replace(nonApiRE, "/routes");
107
+ const method = methodType.value;
108
+ const path = (0, pathe.join)(data.serverDir, `${route}.${method}.ts`);
109
+ return {
110
+ textSpan: {
111
+ start,
112
+ length: end - start
113
+ },
114
+ definitions: [{
115
+ fileName: path,
116
+ textSpan: {
117
+ start: 0,
118
+ length: 0
119
+ },
120
+ kind: ts.ScriptElementKind.scriptElement,
121
+ name: path,
122
+ containerKind: ts.ScriptElementKind.unknown,
123
+ containerName: ""
124
+ }]
125
+ };
126
+ }
127
+ return;
128
+ }
88
129
  if (!result?.definitions?.length) return result;
89
130
  const program = info.languageService.getProgram();
90
131
  const definitions = new Set(result.definitions);
91
- const skippedDefinitions = [];
92
132
  for (const definition of result.definitions) {
93
133
  const sourceFile = program.getSourceFile(definition.fileName);
94
134
  if (!sourceFile) continue;
95
135
  let result$1 = [];
96
- if (data.nitroRoutes && definition.fileName.endsWith("nitro-routes.d.ts")) result$1 = visitNitroRoutes(context, sourceFile, definition, getDefinitionAndBoundSpan$1);
97
- else if (data.runtimeConfig && definition.fileName.endsWith("runtime-config.d.ts")) result$1 = visitRuntimeConfig(context, sourceFile, definition);
136
+ if (data.runtimeConfig && definition.fileName.endsWith("runtime-config.d.ts")) result$1 = visitRuntimeConfig(context, sourceFile, definition);
98
137
  if (result$1?.length) {
99
138
  for (const definition$1 of result$1) definitions.add(definition$1);
100
- skippedDefinitions.push(definition);
139
+ definitions.delete(definition);
101
140
  }
102
141
  }
103
- for (const definition of skippedDefinitions) definitions.delete(definition);
104
142
  return {
105
143
  definitions: [...definitions],
106
144
  textSpan: result.textSpan
107
145
  };
108
146
  };
109
147
  }
110
- function visitNitroRoutes(context, sourceFile, definition, getDefinitionAndBoundSpan$1) {
111
- const { ts } = context;
112
- const definitions = [];
113
- for (const node of require_src.forEachNode(ts, sourceFile)) {
114
- if (!ts.isPropertySignature(node) || !node.type || !ts.isTypeLiteralNode(node.type)) continue;
115
- const { textSpan } = definition;
116
- const start = node.name.getStart(sourceFile);
117
- const end = node.name.getEnd();
118
- if (start !== textSpan.start || end - start !== textSpan.length) continue;
119
- for (const member of node.type.members) {
120
- if (!ts.isPropertySignature(member)) continue;
121
- const pos = (((((member.type?.typeArguments?.[0])?.typeArguments?.[0])?.typeArguments?.[0])?.typeArguments?.[0])?.qualifier)?.getStart(sourceFile);
122
- if (pos !== void 0) {
123
- const res = getDefinitionAndBoundSpan$1(definition.fileName, pos);
124
- if (res?.definitions?.length) definitions.push(...res.definitions);
125
- }
126
- }
127
- break;
128
- }
129
- return definitions;
130
- }
131
148
  function visitRuntimeConfig(context, sourceFile, definition) {
132
149
  const { ts } = context;
133
150
  let definitions = [];
134
151
  const path = [];
135
- require_src.walkNodes(ts, sourceFile, (node, next) => {
152
+ for (const node of forEachTouchingNode(ts, sourceFile, definition.textSpan.start)) {
136
153
  let key;
137
154
  if (ts.isInterfaceDeclaration(node) && ts.isIdentifier(node.name)) key = node.name.text;
138
155
  else if (ts.isPropertySignature(node) && ts.isIdentifier(node.name)) {
@@ -142,17 +159,15 @@ function visitRuntimeConfig(context, sourceFile, definition) {
142
159
  const end = node.name.getEnd();
143
160
  if (start === textSpan.start && end - start === textSpan.length) {
144
161
  path.push(key);
145
- definitions = [...proxyRuntimeConfig(context, definition, path)];
146
- return;
162
+ definitions = [...forwardRuntimeConfig(context, definition, path)];
163
+ break;
147
164
  }
148
165
  }
149
166
  if (key !== void 0) path.push(key);
150
- next();
151
- if (key !== void 0) path.pop();
152
- });
167
+ }
153
168
  return definitions;
154
169
  }
155
- function* proxyRuntimeConfig(context, definition, path) {
170
+ function* forwardRuntimeConfig(context, definition, path) {
156
171
  const { ts, info, data } = context;
157
172
  switch (path[0]) {
158
173
  case "SharedRuntimeConfig":
@@ -207,60 +222,94 @@ function* proxyRuntimeConfig(context, definition, path) {
207
222
  }
208
223
  }
209
224
  }
225
+
226
+ //#endregion
227
+ //#region src/typescript/utils.ts
228
+ function toSourceSpan(language, fileName, textSpan) {
229
+ const sourceScript = language?.scripts.get(fileName);
230
+ if (!sourceScript?.generated) return;
231
+ const serviceScript = sourceScript.generated.languagePlugin.typescript?.getServiceScript(sourceScript.generated.root);
232
+ if (!serviceScript) return;
233
+ const map = language.maps.get(serviceScript.code, sourceScript);
234
+ const leadingOffset = sourceScript.snapshot.getLength();
235
+ for (const [start, end] of map.toSourceRange(textSpan.start - leadingOffset, textSpan.start + textSpan.length - leadingOffset, false)) return {
236
+ start,
237
+ length: end - start
238
+ };
239
+ }
240
+
241
+ //#endregion
242
+ //#region src/typescript/features/getEditsForFileRename.ts
210
243
  function getEditsForFileRename(context, getEditsForFileRename$1) {
211
244
  const { ts, info, data, server } = context;
212
245
  return (...args) => {
213
246
  const result = getEditsForFileRename$1(...args);
214
247
  if (!result?.length) return result;
215
248
  const program = info.languageService.getProgram();
216
- const changes = [];
217
249
  const references = {};
218
250
  for (const change of result) {
219
251
  const { fileName, textChanges } = change;
220
252
  if (data.components && fileName.endsWith("components.d.ts")) {
221
253
  const sourceFile = program.getSourceFile(fileName);
222
254
  if (!sourceFile) continue;
223
- const nodes = [];
224
- if (fileName.endsWith("types/components.d.ts")) {
225
- for (const node of require_src.forEachNode(ts, sourceFile)) if (ts.isPropertySignature(node)) nodes.push(node);
226
- } else for (const node of require_src.forEachNode(ts, sourceFile)) if (ts.isVariableDeclaration(node)) nodes.push(node);
227
- for (const node of nodes) {
228
- const start = node.getStart(sourceFile);
229
- const end = node.getEnd();
230
- if (textChanges.every(({ span }) => span.start < start || span.start + span.length > end)) continue;
231
- const symbols = info.languageService.findReferences(fileName, start);
255
+ for (const { span } of textChanges) for (const node of forEachTouchingNode(ts, sourceFile, span.start)) {
256
+ if (!ts.isPropertySignature(node) && !ts.isVariableDeclaration(node)) continue;
257
+ const position = node.name.getStart(sourceFile);
258
+ const res = info.languageService.getReferencesAtPosition(fileName, position)?.filter((entry) => !entry.fileName.startsWith(data.buildDir));
232
259
  const lazy = node.type && ts.isTypeReferenceNode(node.type) && ts.isIdentifier(node.type.typeName) && node.type.typeName.text === "LazyComponent";
233
- for (const reference of symbols?.flatMap(({ references: references$1 }) => references$1) ?? []) {
234
- if (reference.isDefinition) continue;
235
- const { fileName: fileName$1, textSpan } = reference;
236
- (references[fileName$1] ??= []).push({
237
- textSpan: toSourceSpan(context.language, fileName$1, textSpan) ?? textSpan,
238
- lazy: lazy || void 0
239
- });
240
- }
260
+ for (const { fileName: fileName$1, textSpan } of res ?? []) (references[fileName$1] ??= []).push({
261
+ textSpan: toSourceSpan(context.language, fileName$1, textSpan) ?? textSpan,
262
+ lazy: lazy || void 0
263
+ });
264
+ break;
241
265
  }
242
266
  }
243
- if (!fileName.startsWith(data.buildDir)) changes.push(change);
244
267
  }
245
268
  if (Object.keys(references).length) server.write("components:rename", {
246
269
  fileName: args[1],
247
270
  references
248
271
  });
249
- return changes;
250
- };
251
- }
252
- function toSourceSpan(language, fileName, textSpan) {
253
- const sourceScript = language?.scripts.get(fileName);
254
- if (!sourceScript?.generated) return;
255
- const serviceScript = sourceScript.generated.languagePlugin.typescript?.getServiceScript(sourceScript.generated.root);
256
- if (!serviceScript) return;
257
- const map = language.maps.get(serviceScript.code, sourceScript);
258
- const leadingOffset = sourceScript.snapshot.getLength();
259
- for (const [start, end] of map.toSourceRange(textSpan.start - leadingOffset, textSpan.start + textSpan.length - leadingOffset, false)) return {
260
- start,
261
- length: end - start
272
+ return result.filter((change) => {
273
+ return !change.fileName.startsWith(data.buildDir);
274
+ });
262
275
  };
263
276
  }
264
277
 
278
+ //#endregion
279
+ //#region src/typescript/index.ts
280
+ const plugin = (module$1) => {
281
+ const { typescript: ts } = module$1;
282
+ return { create(info) {
283
+ const currentDirectory = info.languageServiceHost.getCurrentDirectory();
284
+ const path = (0, pathe.join)(currentDirectory, "dxup/data.json");
285
+ const context = {
286
+ ts,
287
+ info,
288
+ data: {
289
+ buildDir: currentDirectory,
290
+ configFiles: [],
291
+ components: true,
292
+ nitroRoutes: true,
293
+ runtimeConfig: true,
294
+ ...JSON.parse(ts.sys.readFile(path) ?? "{}")
295
+ },
296
+ server: createEventServer(info)
297
+ };
298
+ setTimeout(() => {
299
+ context.language = (info.project.__vue__ ?? info.project["program"]?.__vue__)?.language;
300
+ }, 500);
301
+ for (const [key, method] of [
302
+ ["findRenameLocations", findRenameLocations.bind(null, context)],
303
+ ["getDefinitionAndBoundSpan", getDefinitionAndBoundSpan.bind(null, context)],
304
+ ["getEditsForFileRename", getEditsForFileRename.bind(null, context)]
305
+ ]) {
306
+ const original = info.languageService[key];
307
+ info.languageService[key] = method(original);
308
+ }
309
+ return info.languageService;
310
+ } };
311
+ };
312
+ var typescript_default = plugin;
313
+
265
314
  //#endregion
266
315
  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.4",
5
- "description": "TypeScript and Vue plugins for Nuxt",
4
+ "version": "0.1.0",
5
+ "description": "TypeScript plugin 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",
@@ -17,18 +16,18 @@
17
16
  "dist"
18
17
  ],
19
18
  "dependencies": {
20
- "@nuxt/kit": "^4.1.2",
19
+ "@nuxt/kit": "^4.1.3",
21
20
  "chokidar": "^4.0.3",
22
21
  "pathe": "2.0.3",
23
- "@dxup/unimport": "^0.0.1"
22
+ "@dxup/unimport": "^0.1.0"
24
23
  },
25
24
  "devDependencies": {
26
25
  "@dxup/shared": "",
27
26
  "@volar/language-core": "^2.4.23",
28
27
  "@volar/typescript": "^2.4.23",
29
- "@vue/language-core": "^3.0.8",
30
- "nuxt": "^4.1.2",
31
- "typescript": "^5.9.2"
28
+ "@vue/language-core": "^3.1.1",
29
+ "nuxt": "^4.1.3",
30
+ "typescript": "^5.9.3"
32
31
  },
33
32
  "scripts": {
34
33
  "build": "tsdown",
@@ -1,31 +0,0 @@
1
-
2
- //#region ../shared/src/index.ts
3
- function* forEachNode(ts, node) {
4
- yield node;
5
- const children = [];
6
- ts.forEachChild(node, (child) => {
7
- children.push(child);
8
- });
9
- for (const child of children) yield* forEachNode(ts, child);
10
- }
11
- function walkNodes(ts, node, callback) {
12
- callback(node, () => {
13
- ts.forEachChild(node, (child) => {
14
- walkNodes(ts, 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_src = require('../src-BfXMgjZD.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_src.forEachNode(ts, 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;