@dxup/nuxt 0.3.0 → 0.3.2

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
@@ -60,9 +60,22 @@ useFetch("/api/foo");
60
60
  // Also `$fetch` and `useLazyFetch`.
61
61
  ```
62
62
 
63
+ ### 4. pageMeta
64
+
65
+ Go to definition for page metadata.
66
+
67
+ ```ts
68
+ definePageMeta({
69
+ layout: "admin",
70
+ // ^^^^^^^
71
+ middleware: ["auth"],
72
+ // ^^^^^^
73
+ });
74
+ ```
75
+
63
76
  It will fallback to resolve the URL from your `public` directory when no nitro routes match.
64
77
 
65
- ### 4. runtimeConfig
78
+ ### 5. runtimeConfig
66
79
 
67
80
  Go to definition for runtime config.
68
81
 
@@ -73,7 +86,20 @@ Go to definition for runtime config.
73
86
  </template>
74
87
  ```
75
88
 
76
- ### 5. unimport
89
+ ### 6. typedPages
90
+
91
+ Go to definition for typed pages.
92
+
93
+ ```vue
94
+ <template>
95
+ <nuxt-link :to="{ name: `about` }"/>
96
+ <!-- ^^^^^^^ -->
97
+ </template>
98
+ ```
99
+
100
+ It can be triggered on the `name` property of an object literal constrained by the `RouteLocationRaw` type.
101
+
102
+ ### 7. unimport
77
103
 
78
104
  Find references for SFC on `<template>`.
79
105
 
package/dist/module.d.mts CHANGED
@@ -18,11 +18,21 @@ interface ModuleOptions {
18
18
  * @default true
19
19
  */
20
20
  nitroRoutes?: boolean;
21
+ /**
22
+ * Whether to enable Go to Definition for page metadata.
23
+ * @default true
24
+ */
25
+ pageMeta?: boolean;
21
26
  /**
22
27
  * Whether to enable Go to Definition for runtime config.
23
28
  * @default true
24
29
  */
25
30
  runtimeConfig?: boolean;
31
+ /**
32
+ * Whether to enable Go to Definition for typed pages.
33
+ * @default true
34
+ */
35
+ typedPages?: boolean;
26
36
  /**
27
37
  * Whether to enable enhanced navigation for auto imported APIs.
28
38
  * @default true
@@ -40,7 +50,9 @@ declare const _default: _nuxt_schema0.NuxtModule<ModuleOptions, {
40
50
  components: true;
41
51
  importGlob: true;
42
52
  nitroRoutes: true;
53
+ pageMeta: true;
43
54
  runtimeConfig: true;
55
+ typedPages: true;
44
56
  unimport: true;
45
57
  };
46
58
  }, true>;
package/dist/module.mjs CHANGED
@@ -73,7 +73,9 @@ var module_default = defineNuxtModule().with({
73
73
  components: true,
74
74
  importGlob: true,
75
75
  nitroRoutes: true,
76
+ pageMeta: true,
76
77
  runtimeConfig: true,
78
+ typedPages: true,
77
79
  unimport: true
78
80
  } },
79
81
  async setup(options, nuxt) {
@@ -86,16 +88,29 @@ var module_default = defineNuxtModule().with({
86
88
  addTemplate({
87
89
  filename: "dxup/data.json",
88
90
  write: true,
89
- getContents({ nuxt: nuxt$1 }) {
91
+ getContents({ nuxt: nuxt$1, app }) {
92
+ const layouts = Object.fromEntries(Object.values(app.layouts).map((item) => [item.name, item.file]));
93
+ const middleware = app.middleware.reduce((acc, item) => {
94
+ if (!item.global) acc[item.name] = item.path;
95
+ return acc;
96
+ }, {});
90
97
  const nitroRoutes = useNitro().scannedHandlers.reduce((acc, item) => {
91
98
  if (item.route && item.method) (acc[item.route] ??= {})[item.method] = item.handler;
92
99
  return acc;
93
100
  }, {});
101
+ const typedPages = app.pages?.reduce(function reducer(acc, page) {
102
+ if (page.name && page.file) acc[page.name] = page.file;
103
+ if (page.children) for (const child of page.children) reducer(acc, child);
104
+ return acc;
105
+ }, {});
94
106
  const data = {
95
107
  buildDir: nuxt$1.options.buildDir,
96
108
  publicDir: nuxt$1.options.dir.public,
97
109
  configFiles: [...nuxt$1.options._nuxtConfigFiles, ...nuxt$1.options._layers.map((layer) => layer._configFile).filter(Boolean)],
110
+ layouts,
111
+ middleware,
98
112
  nitroRoutes,
113
+ typedPages,
99
114
  features: {
100
115
  ...options.features,
101
116
  unimport: { componentReferences: typeof options.features.unimport === "object" ? options.features.unimport.componentReferences : options.features.unimport }
@@ -36,12 +36,17 @@ const initialValue = {
36
36
  buildDir: "",
37
37
  publicDir: "",
38
38
  configFiles: [],
39
+ layouts: {},
40
+ middleware: {},
39
41
  nitroRoutes: {},
42
+ typedPages: {},
40
43
  features: {
41
44
  components: true,
42
45
  importGlob: true,
43
46
  nitroRoutes: true,
47
+ pageMeta: true,
44
48
  runtimeConfig: true,
49
+ typedPages: true,
45
50
  unimport: { componentReferences: true }
46
51
  }
47
52
  };
@@ -66,21 +71,22 @@ function createData(ts, info) {
66
71
 
67
72
  //#endregion
68
73
  //#region src/typescript/utils.ts
74
+ function createModuleDefinition(ts, path) {
75
+ return {
76
+ fileName: path,
77
+ textSpan: {
78
+ start: 0,
79
+ length: 0
80
+ },
81
+ kind: ts.ScriptElementKind.moduleElement,
82
+ name: `"${path}"`,
83
+ containerKind: ts.ScriptElementKind.unknown,
84
+ containerName: ""
85
+ };
86
+ }
69
87
  function isVueVirtualCode(code) {
70
88
  return code?.languageId === "vue";
71
89
  }
72
- function toSourceSpan(language, fileName, textSpan) {
73
- const sourceScript = language?.scripts.get(fileName);
74
- if (!sourceScript?.generated) return;
75
- const serviceScript = sourceScript.generated.languagePlugin.typescript?.getServiceScript(sourceScript.generated.root);
76
- if (!serviceScript) return;
77
- const map = language.maps.get(serviceScript.code, sourceScript);
78
- const leadingOffset = sourceScript.snapshot.getLength();
79
- for (const [start, end] of map.toSourceRange(textSpan.start - leadingOffset, textSpan.start + textSpan.length - leadingOffset, false)) return {
80
- start,
81
- length: end - start
82
- };
83
- }
84
90
  function withVirtualOffset(language, sourceScript, position, method) {
85
91
  const serviceScript = sourceScript.generated.languagePlugin.typescript?.getServiceScript(sourceScript.generated.root);
86
92
  if (!serviceScript) return;
@@ -183,6 +189,7 @@ const fetchFunctions = new Set([
183
189
  "useFetch",
184
190
  "useLazyFetch"
185
191
  ]);
192
+ const pageMetaKeys = new Set(["layout", "middleware"]);
186
193
  function postprocess(context, language, getDefinitionAndBoundSpan) {
187
194
  const { ts } = context;
188
195
  return (...args) => {
@@ -199,10 +206,10 @@ function postprocess(context, language, getDefinitionAndBoundSpan) {
199
206
  definitions: [{
200
207
  fileName: args[0],
201
208
  textSpan,
202
- kind: ts.ScriptElementKind.scriptElement,
203
- name: args[0],
209
+ kind: ts.ScriptElementKind.memberVariableElement,
210
+ name: "default",
204
211
  containerKind: ts.ScriptElementKind.unknown,
205
- containerName: ""
212
+ containerName: args[0]
206
213
  }]
207
214
  };
208
215
  return result;
@@ -219,12 +226,14 @@ function preprocess$1(context, getDefinitionAndBoundSpan) {
219
226
  const sourceFile = program$1.getSourceFile(args[0]);
220
227
  if (!sourceFile) return;
221
228
  const checker = program$1.getTypeChecker();
222
- let res;
229
+ let result$1;
223
230
  for (const node of forEachTouchingNode(ts, sourceFile, args[1])) {
224
- if (data.features.importGlob) res ??= visitImportGlob(ts, info, sourceFile, node, args[1]);
225
- if (data.features.nitroRoutes) res ??= visitNitroRoutes(ts, data, checker, sourceFile, node, args[1]);
231
+ if (data.features.importGlob) result$1 ??= visitImportGlob(ts, info, sourceFile, node, args[1]);
232
+ if (data.features.nitroRoutes) result$1 ??= visitNitroRoutes(ts, data, checker, sourceFile, node, args[1]);
233
+ if (data.features.pageMeta) result$1 ??= visitPageMeta(ts, data, sourceFile, node, args[1]);
234
+ if (data.features.typedPages) result$1 ??= visitTypedPages(ts, data, checker, sourceFile, node, args[1]);
226
235
  }
227
- if (res) return res;
236
+ if (result$1) return result$1;
228
237
  }
229
238
  if (!result?.definitions?.length) return result;
230
239
  const program = info.languageService.getProgram();
@@ -232,7 +241,7 @@ function preprocess$1(context, getDefinitionAndBoundSpan) {
232
241
  for (const definition of result.definitions) {
233
242
  const sourceFile = program.getSourceFile(definition.fileName);
234
243
  if (!sourceFile) continue;
235
- let result$1 = [];
244
+ let result$1;
236
245
  if (data.features.runtimeConfig && definition.fileName.endsWith("runtime-config.d.ts")) result$1 = visitRuntimeConfig(context, sourceFile, definition);
237
246
  if (result$1?.length) {
238
247
  for (const definition$1 of result$1) definitions.add(definition$1);
@@ -271,17 +280,7 @@ function visitImportGlob(ts, info, sourceFile, node, position) {
271
280
  start,
272
281
  length: end - start
273
282
  },
274
- definitions: fileNames.map((fileName) => ({
275
- fileName,
276
- textSpan: {
277
- start: 0,
278
- length: 0
279
- },
280
- kind: ts.ScriptElementKind.unknown,
281
- name: fileName,
282
- containerKind: ts.ScriptElementKind.unknown,
283
- containerName: ""
284
- }))
283
+ definitions: fileNames.map((fileName) => createModuleDefinition(ts, fileName))
285
284
  };
286
285
  }
287
286
  function visitNitroRoutes(ts, data, checker, sourceFile, node, position) {
@@ -322,17 +321,62 @@ function visitNitroRoutes(ts, data, checker, sourceFile, node, position) {
322
321
  start,
323
322
  length: end - start
324
323
  },
325
- definitions: paths.map((path) => ({
326
- fileName: path,
327
- textSpan: {
328
- start: 0,
329
- length: 0
330
- },
331
- kind: ts.ScriptElementKind.scriptElement,
332
- name: path,
333
- containerKind: ts.ScriptElementKind.unknown,
334
- containerName: ""
335
- }))
324
+ definitions: paths.map((path) => createModuleDefinition(ts, path))
325
+ };
326
+ }
327
+ function visitPageMeta(ts, data, sourceFile, node, position) {
328
+ if (!ts.isPropertyAssignment(node) || !ts.isIdentifier(node.name) || !pageMetaKeys.has(node.name.text) || !ts.isCallExpression(node.parent.parent) || !ts.isIdentifier(node.parent.parent.expression) || node.parent.parent.expression.text !== "definePageMeta") return;
329
+ switch (node.name.text) {
330
+ case "layout": {
331
+ if (!ts.isStringLiteralLike(node.initializer)) return;
332
+ const start = node.initializer.getStart(sourceFile);
333
+ const end = node.initializer.getEnd();
334
+ if (position < start || position > end) return;
335
+ const path = data.layouts[node.initializer.text];
336
+ if (path === void 0) return;
337
+ return {
338
+ textSpan: {
339
+ start,
340
+ length: end - start
341
+ },
342
+ definitions: [createModuleDefinition(ts, path)]
343
+ };
344
+ }
345
+ case "middleware": {
346
+ const literals = ts.isStringLiteralLike(node.initializer) ? [node.initializer] : ts.isArrayLiteralExpression(node.initializer) ? node.initializer.elements.filter(ts.isStringLiteralLike) : [];
347
+ for (const literal of literals) {
348
+ const start = literal.getStart(sourceFile);
349
+ const end = literal.getEnd();
350
+ if (position < start || position > end) continue;
351
+ const path = data.middleware[literal.text];
352
+ if (path === void 0) continue;
353
+ return {
354
+ textSpan: {
355
+ start,
356
+ length: end - start
357
+ },
358
+ definitions: [createModuleDefinition(ts, path)]
359
+ };
360
+ }
361
+ break;
362
+ }
363
+ }
364
+ }
365
+ function visitTypedPages(ts, data, checker, sourceFile, node, position) {
366
+ if (!ts.isPropertyAssignment(node) || !ts.isIdentifier(node.name) || node.name.text !== "name" || !ts.isStringLiteralLike(node.initializer)) return;
367
+ const start = node.initializer.getStart(sourceFile);
368
+ const end = node.initializer.getEnd();
369
+ if (position < start || position > end) return;
370
+ const contextualType = checker.getContextualType(node.parent)?.getNonNullableType();
371
+ if (contextualType?.aliasSymbol?.name !== "RouteLocationRaw" && (!contextualType?.isUnion() || contextualType.types.every((type) => type.symbol?.name !== "RouteLocationAsPathTyped"))) return;
372
+ const path = data.typedPages[node.initializer.text];
373
+ if (path === void 0) return;
374
+ return {
375
+ textSpan: {
376
+ start,
377
+ length: end - start
378
+ },
379
+ definitions: [createModuleDefinition(ts, path)]
336
380
  };
337
381
  }
338
382
  function visitRuntimeConfig(context, sourceFile, definition) {
@@ -418,7 +462,8 @@ function preprocess(context, getEditsForFileRename) {
418
462
  return (...args) => {
419
463
  const result = getEditsForFileRename(...args);
420
464
  if (!result?.length) return result;
421
- const program = info.languageService.getProgram();
465
+ const languageService = info.project.getLanguageService();
466
+ const program = languageService.getProgram();
422
467
  const references = {};
423
468
  for (const change of result) {
424
469
  const { fileName, textChanges } = change;
@@ -428,10 +473,10 @@ function preprocess(context, getEditsForFileRename) {
428
473
  for (const { span } of textChanges) for (const node of forEachTouchingNode(ts, sourceFile, span.start)) {
429
474
  if (!ts.isPropertySignature(node) && !ts.isVariableDeclaration(node)) continue;
430
475
  const position = node.name.getStart(sourceFile);
431
- const res = info.languageService.getReferencesAtPosition(fileName, position)?.filter((entry) => !entry.fileName.startsWith(data.buildDir));
476
+ const res = languageService.getReferencesAtPosition(fileName, position)?.filter((entry) => !entry.fileName.startsWith(data.buildDir))?.sort((a, b) => a.textSpan.start - b.textSpan.start);
432
477
  const lazy = node.type && ts.isTypeReferenceNode(node.type) && ts.isIdentifier(node.type.typeName) && node.type.typeName.text === "LazyComponent";
433
478
  for (const { fileName: fileName$1, textSpan } of res ?? []) (references[fileName$1] ??= []).push({
434
- textSpan: toSourceSpan(context.language, fileName$1, textSpan) ?? textSpan,
479
+ textSpan,
435
480
  lazy: lazy || void 0
436
481
  });
437
482
  break;
@@ -460,7 +505,7 @@ const plugin = (module$1) => {
460
505
  data,
461
506
  server: createEventServer(info)
462
507
  };
463
- setTimeout(() => {
508
+ queueMicrotask(() => {
464
509
  context.language = info.project.__vue__?.language;
465
510
  if (!context.language || !data.features.unimport.componentReferences) return;
466
511
  const languageService = info.project.getLanguageService();
@@ -477,7 +522,7 @@ const plugin = (module$1) => {
477
522
  return Reflect.set(...args);
478
523
  }
479
524
  });
480
- }, 0);
525
+ });
481
526
  for (const [key, method] of [
482
527
  ["findRenameLocations", findRenameLocations_exports],
483
528
  ["getDefinitionAndBoundSpan", getDefinitionAndBoundSpan_exports],
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dxup/nuxt",
3
3
  "type": "module",
4
- "version": "0.3.0",
4
+ "version": "0.3.2",
5
5
  "description": "TypeScript plugin for Nuxt",
6
6
  "author": "KazariEX",
7
7
  "license": "MIT",