@dxup/nuxt 0.0.5 → 0.1.1

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,7 +4,7 @@
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 plugins that improves Nuxt DX.
7
+ This is a TypeScript plugin that improves Nuxt DX.
8
8
 
9
9
  ## Features
10
10
 
package/dist/module.d.ts CHANGED
@@ -2,9 +2,25 @@ import * as _nuxt_schema0 from "@nuxt/schema";
2
2
 
3
3
  //#region src/module/index.d.ts
4
4
  interface ModuleOptions {
5
+ /**
6
+ * Whether to update references when renaming auto imported component files.
7
+ * @default true
8
+ */
5
9
  components?: boolean;
10
+ /**
11
+ * Whether to enable Go to Definition for nitro routes in data fetching methods.
12
+ * @default true
13
+ */
6
14
  nitroRoutes?: boolean;
15
+ /**
16
+ * Whether to enable Go to Definition for runtime config.
17
+ * @default true
18
+ */
7
19
  runtimeConfig?: boolean;
20
+ /**
21
+ * Whether to enable enhanced navigation for auto imported APIs.
22
+ * @default true
23
+ */
8
24
  unimport?: boolean;
9
25
  }
10
26
  declare const _default: _nuxt_schema0.NuxtModule<ModuleOptions, ModuleOptions, false>;
package/dist/module.js CHANGED
@@ -1,4 +1,4 @@
1
- import { addTemplate, defineNuxtModule } from "@nuxt/kit";
1
+ import { addTemplate, defineNuxtModule, useNitro } from "@nuxt/kit";
2
2
  import { Buffer } from "node:buffer";
3
3
  import EventEmitter from "node:events";
4
4
  import { mkdir, open, readFile, writeFile } from "node:fs/promises";
@@ -85,13 +85,14 @@ var module_default = defineNuxtModule({
85
85
  addTemplate({
86
86
  filename: "dxup/data.json",
87
87
  write: true,
88
- getContents() {
88
+ getContents({ nuxt: nuxt$1 }) {
89
+ const nitro = useNitro();
90
+ const nitroRoutes = options.nitroRoutes && Object.fromEntries(nitro.scannedHandlers.filter((item) => item.route).map((item) => [`${item.route}+${item.method ?? "get"}`, item.handler]));
89
91
  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)],
92
+ buildDir: nuxt$1.options.buildDir,
93
+ configFiles: [...nuxt$1.options._nuxtConfigFiles, ...nuxt$1.options._layers.map((layer) => layer._configFile).filter(Boolean)],
93
94
  components: options.components,
94
- nitroRoutes: options.nitroRoutes,
95
+ nitroRoutes,
95
96
  runtimeConfig: options.runtimeConfig
96
97
  };
97
98
  return JSON.stringify(data, null, 2);
@@ -50,7 +50,7 @@ function findRenameLocations(context, findRenameLocations$1) {
50
50
 
51
51
  //#endregion
52
52
  //#region ../shared/src/index.ts
53
- function* forEachTouchNode(ts, sourceFile, position) {
53
+ function* forEachTouchingNode(ts, sourceFile, position) {
54
54
  yield* binaryVisit(ts, sourceFile, sourceFile, position);
55
55
  }
56
56
  function* binaryVisit(ts, sourceFile, node, position) {
@@ -63,10 +63,8 @@ function* binaryVisit(ts, sourceFile, node, position) {
63
63
  while (left <= right) {
64
64
  const mid = Math.floor((left + right) / 2);
65
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;
66
+ if (position > node$1.getEnd()) left = mid + 1;
67
+ else if (position < node$1.getStart(sourceFile)) right = mid - 1;
70
68
  else {
71
69
  yield node$1;
72
70
  yield* binaryVisit(ts, sourceFile, node$1, position);
@@ -74,6 +72,9 @@ function* binaryVisit(ts, sourceFile, node, position) {
74
72
  }
75
73
  }
76
74
  }
75
+ function isTextSpanEqual(node, textSpan, sourceFile) {
76
+ return textSpan.start + textSpan.length === node.getEnd() && textSpan.start === node.getStart(sourceFile);
77
+ }
77
78
 
78
79
  //#endregion
79
80
  //#region src/typescript/features/getDefinitionAndBoundSpan.ts
@@ -82,7 +83,6 @@ const fetchFunctions = new Set([
82
83
  "useFetch",
83
84
  "useLazyFetch"
84
85
  ]);
85
- const nonApiRE = /^(?!\/api\/)/;
86
86
  function getDefinitionAndBoundSpan(context, getDefinitionAndBoundSpan$1) {
87
87
  const { ts, info, data } = context;
88
88
  return (...args) => {
@@ -92,27 +92,37 @@ function getDefinitionAndBoundSpan(context, getDefinitionAndBoundSpan$1) {
92
92
  const sourceFile = program$1.getSourceFile(args[0]);
93
93
  if (!sourceFile) return;
94
94
  const checker = program$1.getTypeChecker();
95
- for (const node of forEachTouchNode(ts, sourceFile, args[1])) {
95
+ for (const node of forEachTouchingNode(ts, sourceFile, args[1])) {
96
96
  if (!ts.isCallExpression(node) || !ts.isIdentifier(node.expression) || !fetchFunctions.has(node.expression.text) || !node.arguments.length) continue;
97
97
  const firstArg = node.arguments[0];
98
98
  const start = firstArg.getStart(sourceFile);
99
99
  const end = firstArg.getEnd();
100
100
  if (args[1] < start || args[1] > end) continue;
101
101
  const resolvedSignature = checker.getResolvedSignature(node);
102
- if (!resolvedSignature) break;
102
+ if (!resolvedSignature) continue;
103
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`);
104
+ let routeType;
105
+ let methodType;
106
+ if (node.expression.text === "$fetch") {
107
+ routeType = typeArguments?.[1];
108
+ const symbol = typeArguments?.[2].getProperty("method");
109
+ methodType = symbol ? checker.getTypeOfSymbol(symbol) : void 0;
110
+ } else {
111
+ routeType = typeArguments?.[2];
112
+ methodType = typeArguments?.[3];
113
+ }
114
+ if (!routeType?.isStringLiteral()) continue;
115
+ const paths = [];
116
+ for (const type of methodType?.isUnion() ? methodType.types : [methodType]) if (type?.isStringLiteral()) {
117
+ const path = data.nitroRoutes[`${routeType.value}+${type.value}`];
118
+ if (path !== void 0) paths.push(path);
119
+ }
110
120
  return {
111
121
  textSpan: {
112
122
  start,
113
123
  length: end - start
114
124
  },
115
- definitions: [{
125
+ definitions: paths.map((path) => ({
116
126
  fileName: path,
117
127
  textSpan: {
118
128
  start: 0,
@@ -122,15 +132,13 @@ function getDefinitionAndBoundSpan(context, getDefinitionAndBoundSpan$1) {
122
132
  name: path,
123
133
  containerKind: ts.ScriptElementKind.unknown,
124
134
  containerName: ""
125
- }]
135
+ }))
126
136
  };
127
137
  }
128
- return;
129
138
  }
130
139
  if (!result?.definitions?.length) return result;
131
140
  const program = info.languageService.getProgram();
132
141
  const definitions = new Set(result.definitions);
133
- const skippedDefinitions = [];
134
142
  for (const definition of result.definitions) {
135
143
  const sourceFile = program.getSourceFile(definition.fileName);
136
144
  if (!sourceFile) continue;
@@ -138,10 +146,9 @@ function getDefinitionAndBoundSpan(context, getDefinitionAndBoundSpan$1) {
138
146
  if (data.runtimeConfig && definition.fileName.endsWith("runtime-config.d.ts")) result$1 = visitRuntimeConfig(context, sourceFile, definition);
139
147
  if (result$1?.length) {
140
148
  for (const definition$1 of result$1) definitions.add(definition$1);
141
- skippedDefinitions.push(definition);
149
+ definitions.delete(definition);
142
150
  }
143
151
  }
144
- for (const definition of skippedDefinitions) definitions.delete(definition);
145
152
  return {
146
153
  definitions: [...definitions],
147
154
  textSpan: result.textSpan
@@ -152,17 +159,14 @@ function visitRuntimeConfig(context, sourceFile, definition) {
152
159
  const { ts } = context;
153
160
  let definitions = [];
154
161
  const path = [];
155
- for (const node of forEachTouchNode(ts, sourceFile, definition.textSpan.start)) {
162
+ for (const node of forEachTouchingNode(ts, sourceFile, definition.textSpan.start)) {
156
163
  let key;
157
164
  if (ts.isInterfaceDeclaration(node) && ts.isIdentifier(node.name)) key = node.name.text;
158
165
  else if (ts.isPropertySignature(node) && ts.isIdentifier(node.name)) {
159
166
  key = node.name.text;
160
- const { textSpan } = definition;
161
- const start = node.name.getStart(sourceFile);
162
- const end = node.name.getEnd();
163
- if (start === textSpan.start && end - start === textSpan.length) {
167
+ if (isTextSpanEqual(node.name, definition.textSpan, sourceFile)) {
164
168
  path.push(key);
165
- definitions = [...proxyRuntimeConfig(context, definition, path)];
169
+ definitions = [...forwardRuntimeConfig(context, definition, path)];
166
170
  break;
167
171
  }
168
172
  }
@@ -170,7 +174,7 @@ function visitRuntimeConfig(context, sourceFile, definition) {
170
174
  }
171
175
  return definitions;
172
176
  }
173
- function* proxyRuntimeConfig(context, definition, path) {
177
+ function* forwardRuntimeConfig(context, definition, path) {
174
178
  const { ts, info, data } = context;
175
179
  switch (path[0]) {
176
180
  case "SharedRuntimeConfig":
@@ -249,14 +253,13 @@ function getEditsForFileRename(context, getEditsForFileRename$1) {
249
253
  const result = getEditsForFileRename$1(...args);
250
254
  if (!result?.length) return result;
251
255
  const program = info.languageService.getProgram();
252
- const changes = [];
253
256
  const references = {};
254
257
  for (const change of result) {
255
258
  const { fileName, textChanges } = change;
256
259
  if (data.components && fileName.endsWith("components.d.ts")) {
257
260
  const sourceFile = program.getSourceFile(fileName);
258
261
  if (!sourceFile) continue;
259
- for (const { span } of textChanges) for (const node of forEachTouchNode(ts, sourceFile, span.start)) {
262
+ for (const { span } of textChanges) for (const node of forEachTouchingNode(ts, sourceFile, span.start)) {
260
263
  if (!ts.isPropertySignature(node) && !ts.isVariableDeclaration(node)) continue;
261
264
  const position = node.name.getStart(sourceFile);
262
265
  const res = info.languageService.getReferencesAtPosition(fileName, position)?.filter((entry) => !entry.fileName.startsWith(data.buildDir));
@@ -268,13 +271,14 @@ function getEditsForFileRename(context, getEditsForFileRename$1) {
268
271
  break;
269
272
  }
270
273
  }
271
- if (!fileName.startsWith(data.buildDir)) changes.push(change);
272
274
  }
273
275
  if (Object.keys(references).length) server.write("components:rename", {
274
276
  fileName: args[1],
275
277
  references
276
278
  });
277
- return changes;
279
+ return result.filter((change) => {
280
+ return !change.fileName.startsWith(data.buildDir);
281
+ });
278
282
  };
279
283
  }
280
284
 
@@ -283,22 +287,11 @@ function getEditsForFileRename(context, getEditsForFileRename$1) {
283
287
  const plugin = (module$1) => {
284
288
  const { typescript: ts } = module$1;
285
289
  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
290
  const context = {
298
291
  ts,
299
292
  info,
300
- data,
301
- server
293
+ data: createData(ts, info),
294
+ server: createEventServer(info)
302
295
  };
303
296
  setTimeout(() => {
304
297
  context.language = (info.project.__vue__ ?? info.project["program"]?.__vue__)?.language;
@@ -315,6 +308,27 @@ const plugin = (module$1) => {
315
308
  } };
316
309
  };
317
310
  var typescript_default = plugin;
311
+ function createData(ts, info) {
312
+ const initialValue = {
313
+ buildDir: "",
314
+ configFiles: [],
315
+ components: true,
316
+ nitroRoutes: {},
317
+ runtimeConfig: true
318
+ };
319
+ const path = (0, pathe.join)(info.languageServiceHost.getCurrentDirectory(), "dxup/data.json");
320
+ const data = {};
321
+ update();
322
+ ts.sys.watchFile?.(path, update);
323
+ return data;
324
+ function update() {
325
+ const text = ts.sys.readFile(path);
326
+ Object.assign(data, {
327
+ ...initialValue,
328
+ ...text ? JSON.parse(text) : {}
329
+ });
330
+ }
331
+ }
318
332
 
319
333
  //#endregion
320
334
  module.exports = typescript_default;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@dxup/nuxt",
3
3
  "type": "module",
4
- "version": "0.0.5",
5
- "description": "TypeScript plugins for Nuxt",
4
+ "version": "0.1.1",
5
+ "description": "TypeScript plugin for Nuxt",
6
6
  "author": "KazariEX",
7
7
  "license": "MIT",
8
8
  "repository": "KazariEX/dxup",
@@ -16,18 +16,18 @@
16
16
  "dist"
17
17
  ],
18
18
  "dependencies": {
19
- "@nuxt/kit": "^4.1.2",
19
+ "@nuxt/kit": "^4.1.3",
20
20
  "chokidar": "^4.0.3",
21
21
  "pathe": "2.0.3",
22
- "@dxup/unimport": "^0.0.1"
22
+ "@dxup/unimport": "^0.1.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@dxup/shared": "",
26
26
  "@volar/language-core": "^2.4.23",
27
27
  "@volar/typescript": "^2.4.23",
28
- "@vue/language-core": "^3.0.8",
29
- "nuxt": "^4.1.2",
30
- "typescript": "^5.9.2"
28
+ "@vue/language-core": "^3.1.1",
29
+ "nuxt": "^4.1.3",
30
+ "typescript": "^5.9.3"
31
31
  },
32
32
  "scripts": {
33
33
  "build": "tsdown",