@dxup/nuxt 0.1.1 → 0.2.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
@@ -6,13 +6,6 @@
6
6
 
7
7
  This is a TypeScript plugin that improves Nuxt DX.
8
8
 
9
- ## Features
10
-
11
- - Update references when renaming auto imported component files
12
- - Go to definition for nitro routes in data fetching methods
13
- - Go to definition for runtime config
14
- - [@dxup/unimport](/packages/unimport)
15
-
16
9
  ## Installation
17
10
 
18
11
  ```bash
@@ -30,3 +23,47 @@ export default defineNuxtConfig({
30
23
  ],
31
24
  });
32
25
  ```
26
+
27
+ ## Features
28
+
29
+ ### components
30
+
31
+ Update references when renaming auto imported component files.
32
+
33
+ ### importGlob
34
+
35
+ Go to definition for dynamic imports with glob patterns.
36
+
37
+ ```ts
38
+ import(`~/assets/${name}.webp`);
39
+ // ^^^^^^^^^^^^^^^^^^^^^^^
40
+ import.meta.glob("~/assets/*.webp");
41
+ // ^^^^^^^^^^^^^^^^^
42
+ ```
43
+
44
+ ### nitroRoutes
45
+
46
+ Go to definition for nitro routes in data fetching methods.
47
+
48
+ ```ts
49
+ useFetch("/api/foo");
50
+ // ^^^^^^^^^^
51
+ // Also `$fetch` and `useLazyFetch`.
52
+ ```
53
+
54
+ It will fallback to resolve the URL from your `public` directory when no nitro routes match.
55
+
56
+ ### runtimeConfig
57
+
58
+ Go to definition for runtime config.
59
+
60
+ ```vue
61
+ <template>
62
+ {{ $config.public.domain }}
63
+ <!-- ^^^^^^ -->
64
+ </template>
65
+ ```
66
+
67
+ ### unimport
68
+
69
+ Please refer to the [@dxup/unimport](/packages/unimport) package for more details.
package/dist/module.d.ts CHANGED
@@ -2,26 +2,33 @@ 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
- */
9
- components?: boolean;
10
- /**
11
- * Whether to enable Go to Definition for nitro routes in data fetching methods.
12
- * @default true
13
- */
14
- nitroRoutes?: boolean;
15
- /**
16
- * Whether to enable Go to Definition for runtime config.
17
- * @default true
18
- */
19
- runtimeConfig?: boolean;
20
- /**
21
- * Whether to enable enhanced navigation for auto imported APIs.
22
- * @default true
23
- */
24
- unimport?: boolean;
5
+ features?: {
6
+ /**
7
+ * Whether to update references when renaming auto imported component files.
8
+ * @default true
9
+ */
10
+ components?: boolean;
11
+ /**
12
+ * Whether to enable Go to Definition for dynamic imports with glob patterns.
13
+ * @default true
14
+ */
15
+ importGlob?: boolean;
16
+ /**
17
+ * Whether to enable Go to Definition for nitro routes in data fetching methods.
18
+ * @default true
19
+ */
20
+ nitroRoutes?: boolean;
21
+ /**
22
+ * Whether to enable Go to Definition for runtime config.
23
+ * @default true
24
+ */
25
+ runtimeConfig?: boolean;
26
+ /**
27
+ * Whether to enable enhanced navigation for auto imported APIs.
28
+ * @default true
29
+ */
30
+ unimport?: boolean;
31
+ };
25
32
  }
26
33
  declare const _default: _nuxt_schema0.NuxtModule<ModuleOptions, ModuleOptions, false>;
27
34
  //#endregion
package/dist/module.js CHANGED
@@ -69,15 +69,16 @@ var module_default = defineNuxtModule({
69
69
  name,
70
70
  configKey: "dxup"
71
71
  },
72
- defaults: {
72
+ defaults: { features: {
73
73
  components: true,
74
+ importGlob: true,
74
75
  nitroRoutes: true,
75
76
  runtimeConfig: true,
76
77
  unimport: true
77
- },
78
+ } },
78
79
  async setup(options, nuxt) {
79
80
  const pluginsTs = [{ name: "@dxup/nuxt" }];
80
- if (options.unimport) pluginsTs.unshift({ name: "@dxup/unimport" });
81
+ if (options.features?.unimport) pluginsTs.unshift({ name: "@dxup/unimport" });
81
82
  append(pluginsTs, nuxt.options, "typescript", "tsConfig", "compilerOptions");
82
83
  append(pluginsTs, nuxt.options.nitro, "typescript", "tsConfig", "compilerOptions");
83
84
  append(pluginsTs, nuxt.options, "typescript", "sharedTsConfig", "compilerOptions");
@@ -87,13 +88,12 @@ var module_default = defineNuxtModule({
87
88
  write: true,
88
89
  getContents({ nuxt: nuxt$1 }) {
89
90
  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]));
91
91
  const data = {
92
92
  buildDir: nuxt$1.options.buildDir,
93
+ publicDir: nuxt$1.options.dir.public,
93
94
  configFiles: [...nuxt$1.options._nuxtConfigFiles, ...nuxt$1.options._layers.map((layer) => layer._configFile).filter(Boolean)],
94
- components: options.components,
95
- nitroRoutes,
96
- runtimeConfig: options.runtimeConfig
95
+ nitroRoutes: Object.fromEntries(nitro.scannedHandlers.filter((item) => item.route).map((item) => [`${item.route}+${item.method ?? "get"}`, item.handler])),
96
+ features: options.features
97
97
  };
98
98
  return JSON.stringify(data, null, 2);
99
99
  }
@@ -25,6 +25,8 @@ let pathe = require("pathe");
25
25
  pathe = __toESM(pathe);
26
26
  let node_fs_promises = require("node:fs/promises");
27
27
  node_fs_promises = __toESM(node_fs_promises);
28
+ let tinyglobby = require("tinyglobby");
29
+ tinyglobby = __toESM(tinyglobby);
28
30
 
29
31
  //#region src/event/server.ts
30
32
  function createEventServer(info) {
@@ -72,8 +74,8 @@ function* binaryVisit(ts, sourceFile, node, position) {
72
74
  }
73
75
  }
74
76
  }
75
- function isTextSpanEqual(node, textSpan, sourceFile) {
76
- return textSpan.start + textSpan.length === node.getEnd() && textSpan.start === node.getStart(sourceFile);
77
+ function isTextSpanWithin(node, textSpan, sourceFile) {
78
+ return textSpan.start + textSpan.length <= node.getEnd() && textSpan.start >= node.getStart(sourceFile);
77
79
  }
78
80
 
79
81
  //#endregion
@@ -87,54 +89,17 @@ function getDefinitionAndBoundSpan(context, getDefinitionAndBoundSpan$1) {
87
89
  const { ts, info, data } = context;
88
90
  return (...args) => {
89
91
  const result = getDefinitionAndBoundSpan$1(...args);
90
- if (!result && data.nitroRoutes) {
92
+ if (!result) {
91
93
  const program$1 = info.languageService.getProgram();
92
94
  const sourceFile = program$1.getSourceFile(args[0]);
93
95
  if (!sourceFile) return;
94
96
  const checker = program$1.getTypeChecker();
97
+ let res;
95
98
  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) continue;
103
- const typeArguments = checker.getTypeArgumentsForResolvedSignature(resolvedSignature);
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
- }
120
- return {
121
- textSpan: {
122
- start,
123
- length: end - start
124
- },
125
- definitions: paths.map((path) => ({
126
- fileName: path,
127
- textSpan: {
128
- start: 0,
129
- length: 0
130
- },
131
- kind: ts.ScriptElementKind.scriptElement,
132
- name: path,
133
- containerKind: ts.ScriptElementKind.unknown,
134
- containerName: ""
135
- }))
136
- };
99
+ if (data.features.importGlob) res ??= visitImportGlob(ts, info, sourceFile, node, args[1]);
100
+ if (data.features.nitroRoutes) res ??= visitNitroRoutes(ts, data, checker, sourceFile, node, args[1]);
137
101
  }
102
+ if (res) return res;
138
103
  }
139
104
  if (!result?.definitions?.length) return result;
140
105
  const program = info.languageService.getProgram();
@@ -143,7 +108,7 @@ function getDefinitionAndBoundSpan(context, getDefinitionAndBoundSpan$1) {
143
108
  const sourceFile = program.getSourceFile(definition.fileName);
144
109
  if (!sourceFile) continue;
145
110
  let result$1 = [];
146
- if (data.runtimeConfig && definition.fileName.endsWith("runtime-config.d.ts")) result$1 = visitRuntimeConfig(context, sourceFile, definition);
111
+ if (data.features.runtimeConfig && definition.fileName.endsWith("runtime-config.d.ts")) result$1 = visitRuntimeConfig(context, sourceFile, definition);
147
112
  if (result$1?.length) {
148
113
  for (const definition$1 of result$1) definitions.add(definition$1);
149
114
  definitions.delete(definition);
@@ -155,6 +120,93 @@ function getDefinitionAndBoundSpan(context, getDefinitionAndBoundSpan$1) {
155
120
  };
156
121
  };
157
122
  }
123
+ function visitImportGlob(ts, info, sourceFile, node, position) {
124
+ if (!ts.isCallExpression(node) || !node.arguments.length) return;
125
+ const firstArg = node.arguments[0];
126
+ const start = firstArg.getStart(sourceFile);
127
+ const end = firstArg.getEnd();
128
+ if (position < start || position > end) return;
129
+ let pattern;
130
+ const callText = node.expression.getText(sourceFile);
131
+ if (callText === "import" && ts.isTemplateExpression(firstArg)) pattern = [firstArg.head.text, ...firstArg.templateSpans.map((span) => span.literal.text)].join("*");
132
+ else if (callText === "import.meta.glob" && ts.isStringLiteral(firstArg)) pattern = firstArg.text;
133
+ if (pattern === void 0) return;
134
+ const resolved = ts.resolveModuleName(pattern, sourceFile.fileName, info.languageServiceHost.getCompilationSettings(), {
135
+ fileExists: () => true,
136
+ readFile: () => ""
137
+ });
138
+ if (!resolved?.resolvedModule) return;
139
+ const extension = (0, pathe.extname)(pattern);
140
+ const arbitrary = `.d${extension}.ts`;
141
+ pattern = resolved.resolvedModule.resolvedFileName;
142
+ if (resolved.resolvedModule.extension === arbitrary) pattern = pattern.slice(0, -arbitrary.length) + extension;
143
+ const fileNames = (0, tinyglobby.globSync)(pattern, { absolute: true });
144
+ return {
145
+ textSpan: {
146
+ start,
147
+ length: end - start
148
+ },
149
+ definitions: fileNames.map((fileName) => ({
150
+ fileName,
151
+ textSpan: {
152
+ start: 0,
153
+ length: 0
154
+ },
155
+ kind: ts.ScriptElementKind.unknown,
156
+ name: fileName,
157
+ containerKind: ts.ScriptElementKind.unknown,
158
+ containerName: ""
159
+ }))
160
+ };
161
+ }
162
+ function visitNitroRoutes(ts, data, checker, sourceFile, node, position) {
163
+ if (!ts.isCallExpression(node) || !ts.isIdentifier(node.expression) || !fetchFunctions.has(node.expression.text) || !node.arguments.length || !ts.isStringLiteralLike(node.arguments[0])) return;
164
+ const firstArg = node.arguments[0];
165
+ const start = firstArg.getStart(sourceFile);
166
+ const end = firstArg.getEnd();
167
+ if (position < start || position > end) return;
168
+ const resolvedSignature = checker.getResolvedSignature(node);
169
+ if (!resolvedSignature) return;
170
+ const typeArguments = checker.getTypeArgumentsForResolvedSignature(resolvedSignature);
171
+ let routeType;
172
+ let methodType;
173
+ if (node.expression.text === "$fetch") {
174
+ routeType = typeArguments?.[1];
175
+ const symbol = typeArguments?.[2].getProperty("method");
176
+ methodType = symbol ? checker.getTypeOfSymbol(symbol) : void 0;
177
+ } else {
178
+ routeType = typeArguments?.[2];
179
+ methodType = typeArguments?.[3];
180
+ }
181
+ const paths = [];
182
+ if (routeType?.isStringLiteral()) {
183
+ for (const type of methodType?.isUnion() ? methodType.types : [methodType]) if (type?.isStringLiteral()) {
184
+ const path = data.nitroRoutes[`${routeType.value}+${type.value}`];
185
+ if (path !== void 0) paths.push(path);
186
+ }
187
+ }
188
+ if (!paths.length && firstArg.text.startsWith("/")) {
189
+ const fallback = (0, pathe.join)(data.publicDir, firstArg.text);
190
+ if (ts.sys.fileExists(fallback)) paths.push(fallback);
191
+ }
192
+ return {
193
+ textSpan: {
194
+ start,
195
+ length: end - start
196
+ },
197
+ definitions: paths.map((path) => ({
198
+ fileName: path,
199
+ textSpan: {
200
+ start: 0,
201
+ length: 0
202
+ },
203
+ kind: ts.ScriptElementKind.scriptElement,
204
+ name: path,
205
+ containerKind: ts.ScriptElementKind.unknown,
206
+ containerName: ""
207
+ }))
208
+ };
209
+ }
158
210
  function visitRuntimeConfig(context, sourceFile, definition) {
159
211
  const { ts } = context;
160
212
  let definitions = [];
@@ -164,7 +216,7 @@ function visitRuntimeConfig(context, sourceFile, definition) {
164
216
  if (ts.isInterfaceDeclaration(node) && ts.isIdentifier(node.name)) key = node.name.text;
165
217
  else if (ts.isPropertySignature(node) && ts.isIdentifier(node.name)) {
166
218
  key = node.name.text;
167
- if (isTextSpanEqual(node.name, definition.textSpan, sourceFile)) {
219
+ if (isTextSpanWithin(node.name, definition.textSpan, sourceFile)) {
168
220
  path.push(key);
169
221
  definitions = [...forwardRuntimeConfig(context, definition, path)];
170
222
  break;
@@ -256,7 +308,7 @@ function getEditsForFileRename(context, getEditsForFileRename$1) {
256
308
  const references = {};
257
309
  for (const change of result) {
258
310
  const { fileName, textChanges } = change;
259
- if (data.components && fileName.endsWith("components.d.ts")) {
311
+ if (data.features.components && fileName.endsWith("components.d.ts")) {
260
312
  const sourceFile = program.getSourceFile(fileName);
261
313
  if (!sourceFile) continue;
262
314
  for (const { span } of textChanges) for (const node of forEachTouchingNode(ts, sourceFile, span.start)) {
@@ -297,12 +349,12 @@ const plugin = (module$1) => {
297
349
  context.language = (info.project.__vue__ ?? info.project["program"]?.__vue__)?.language;
298
350
  }, 500);
299
351
  for (const [key, method] of [
300
- ["findRenameLocations", findRenameLocations.bind(null, context)],
301
- ["getDefinitionAndBoundSpan", getDefinitionAndBoundSpan.bind(null, context)],
302
- ["getEditsForFileRename", getEditsForFileRename.bind(null, context)]
352
+ ["findRenameLocations", findRenameLocations],
353
+ ["getDefinitionAndBoundSpan", getDefinitionAndBoundSpan],
354
+ ["getEditsForFileRename", getEditsForFileRename]
303
355
  ]) {
304
356
  const original = info.languageService[key];
305
- info.languageService[key] = method(original);
357
+ info.languageService[key] = method(context, original);
306
358
  }
307
359
  return info.languageService;
308
360
  } };
@@ -311,10 +363,15 @@ var typescript_default = plugin;
311
363
  function createData(ts, info) {
312
364
  const initialValue = {
313
365
  buildDir: "",
366
+ publicDir: "",
314
367
  configFiles: [],
315
- components: true,
316
368
  nitroRoutes: {},
317
- runtimeConfig: true
369
+ features: {
370
+ components: true,
371
+ importGlob: true,
372
+ nitroRoutes: true,
373
+ runtimeConfig: true
374
+ }
318
375
  };
319
376
  const path = (0, pathe.join)(info.languageServiceHost.getCurrentDirectory(), "dxup/data.json");
320
377
  const data = {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dxup/nuxt",
3
3
  "type": "module",
4
- "version": "0.1.1",
4
+ "version": "0.2.1",
5
5
  "description": "TypeScript plugin for Nuxt",
6
6
  "author": "KazariEX",
7
7
  "license": "MIT",
@@ -16,17 +16,18 @@
16
16
  "dist"
17
17
  ],
18
18
  "dependencies": {
19
- "@nuxt/kit": "^4.1.3",
19
+ "@nuxt/kit": "^4.2.0",
20
20
  "chokidar": "^4.0.3",
21
- "pathe": "2.0.3",
22
- "@dxup/unimport": "^0.1.0"
21
+ "pathe": "^2.0.3",
22
+ "tinyglobby": "^0.2.15",
23
+ "@dxup/unimport": "^0.1.1"
23
24
  },
24
25
  "devDependencies": {
25
26
  "@dxup/shared": "",
26
27
  "@volar/language-core": "^2.4.23",
27
28
  "@volar/typescript": "^2.4.23",
28
- "@vue/language-core": "^3.1.1",
29
- "nuxt": "^4.1.3",
29
+ "@vue/language-core": "^3.1.3",
30
+ "nuxt": "^4.2.0",
30
31
  "typescript": "^5.9.3"
31
32
  },
32
33
  "scripts": {