@dxup/nuxt 0.3.1 → 0.4.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
@@ -62,7 +62,20 @@ useFetch("/api/foo");
62
62
 
63
63
  It will fallback to resolve the URL from your `public` directory when no nitro routes match.
64
64
 
65
- ### 4. runtimeConfig
65
+ ### 4. pageMeta
66
+
67
+ Go to definition for page metadata.
68
+
69
+ ```ts
70
+ definePageMeta({
71
+ layout: "admin",
72
+ // ^^^^^^^
73
+ middleware: ["auth"],
74
+ // ^^^^^^
75
+ });
76
+ ```
77
+
78
+ ### 5. runtimeConfig
66
79
 
67
80
  Go to definition for runtime config.
68
81
 
@@ -73,7 +86,7 @@ Go to definition for runtime config.
73
86
  </template>
74
87
  ```
75
88
 
76
- ### 5. typedPages
89
+ ### 6. typedPages
77
90
 
78
91
  Go to definition for typed pages.
79
92
 
@@ -86,7 +99,11 @@ Go to definition for typed pages.
86
99
 
87
100
  It can be triggered on the `name` property of an object literal constrained by the `RouteLocationRaw` type.
88
101
 
89
- ### 6. unimport
102
+ ### 7. unimport
103
+
104
+ Please refer to the [@dxup/unimport](/packages/unimport) package for more details.
105
+
106
+ ### 8. unofficial
90
107
 
91
108
  Find references for SFC on `<template>`.
92
109
 
@@ -95,5 +112,3 @@ Find references for SFC on `<template>`.
95
112
  <!-- ^^^^^^^^ -->
96
113
  </template>
97
114
  ```
98
-
99
- Please refer to the [@dxup/unimport](/packages/unimport) package for more details.
package/dist/module.d.mts CHANGED
@@ -18,6 +18,11 @@ 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
@@ -32,12 +37,13 @@ interface ModuleOptions {
32
37
  * Whether to enable enhanced navigation for auto imported APIs.
33
38
  * @default true
34
39
  */
35
- unimport?: boolean | {
36
- /**
37
- * Whether to enable Find References for SFC on `<template>`.
38
- */
39
- componentReferences: boolean;
40
- };
40
+ unimport?: boolean;
41
+ /**
42
+ * Whether to enable unofficial features for Vue itself.
43
+ * - find references for SFC on `<template>`
44
+ * @default true
45
+ */
46
+ unofficial?: boolean;
41
47
  };
42
48
  }
43
49
  declare const _default: _nuxt_schema0.NuxtModule<ModuleOptions, {
@@ -45,9 +51,11 @@ declare const _default: _nuxt_schema0.NuxtModule<ModuleOptions, {
45
51
  components: true;
46
52
  importGlob: true;
47
53
  nitroRoutes: true;
54
+ pageMeta: true;
48
55
  runtimeConfig: true;
49
56
  typedPages: true;
50
57
  unimport: true;
58
+ unofficial: true;
51
59
  };
52
60
  }, true>;
53
61
  //#endregion
package/dist/module.mjs CHANGED
@@ -73,9 +73,11 @@ var module_default = defineNuxtModule().with({
73
73
  components: true,
74
74
  importGlob: true,
75
75
  nitroRoutes: true,
76
+ pageMeta: true,
76
77
  runtimeConfig: true,
77
78
  typedPages: true,
78
- unimport: true
79
+ unimport: true,
80
+ unofficial: true
79
81
  } },
80
82
  async setup(options, nuxt) {
81
83
  const pluginsTs = [{ name: "@dxup/nuxt" }];
@@ -88,20 +90,29 @@ var module_default = defineNuxtModule().with({
88
90
  filename: "dxup/data.json",
89
91
  write: true,
90
92
  getContents({ nuxt: nuxt$1, app }) {
93
+ const layouts = Object.fromEntries(Object.values(app.layouts).map((item) => [item.name, item.file]));
94
+ const middleware = app.middleware.reduce((acc, item) => {
95
+ if (!item.global) acc[item.name] = item.path;
96
+ return acc;
97
+ }, {});
91
98
  const nitroRoutes = useNitro().scannedHandlers.reduce((acc, item) => {
92
99
  if (item.route && item.method) (acc[item.route] ??= {})[item.method] = item.handler;
93
100
  return acc;
94
101
  }, {});
102
+ const typedPages = app.pages?.reduce(function reducer(acc, page) {
103
+ if (page.name && page.file) acc[page.name] = page.file;
104
+ if (page.children) for (const child of page.children) reducer(acc, child);
105
+ return acc;
106
+ }, {});
95
107
  const data = {
96
108
  buildDir: nuxt$1.options.buildDir,
97
109
  publicDir: nuxt$1.options.dir.public,
98
110
  configFiles: [...nuxt$1.options._nuxtConfigFiles, ...nuxt$1.options._layers.map((layer) => layer._configFile).filter(Boolean)],
111
+ layouts,
112
+ middleware,
99
113
  nitroRoutes,
100
- typedPages: Object.fromEntries(app.pages?.map((page) => [page.name, page.file]) ?? []),
101
- features: {
102
- ...options.features,
103
- unimport: { componentReferences: typeof options.features.unimport === "object" ? options.features.unimport.componentReferences : options.features.unimport }
104
- }
114
+ typedPages,
115
+ features: options.features
105
116
  };
106
117
  return JSON.stringify(data, null, 2);
107
118
  }
@@ -36,15 +36,18 @@ const initialValue = {
36
36
  buildDir: "",
37
37
  publicDir: "",
38
38
  configFiles: [],
39
+ layouts: {},
40
+ middleware: {},
39
41
  nitroRoutes: {},
40
42
  typedPages: {},
41
43
  features: {
42
44
  components: true,
43
45
  importGlob: true,
44
46
  nitroRoutes: true,
47
+ pageMeta: true,
45
48
  runtimeConfig: true,
46
49
  typedPages: true,
47
- unimport: { componentReferences: true }
50
+ unofficial: true
48
51
  }
49
52
  };
50
53
  const callbacks = {};
@@ -84,18 +87,6 @@ function createModuleDefinition(ts, path) {
84
87
  function isVueVirtualCode(code) {
85
88
  return code?.languageId === "vue";
86
89
  }
87
- function toSourceSpan(language, fileName, textSpan) {
88
- const sourceScript = language?.scripts.get(fileName);
89
- if (!sourceScript?.generated) return;
90
- const serviceScript = sourceScript.generated.languagePlugin.typescript?.getServiceScript(sourceScript.generated.root);
91
- if (!serviceScript) return;
92
- const map = language.maps.get(serviceScript.code, sourceScript);
93
- const leadingOffset = sourceScript.snapshot.getLength();
94
- for (const [start, end] of map.toSourceRange(textSpan.start - leadingOffset, textSpan.start + textSpan.length - leadingOffset, false)) return {
95
- start,
96
- length: end - start
97
- };
98
- }
99
90
  function withVirtualOffset(language, sourceScript, position, method) {
100
91
  const serviceScript = sourceScript.generated.languagePlugin.typescript?.getServiceScript(sourceScript.generated.root);
101
92
  if (!serviceScript) return;
@@ -133,7 +124,7 @@ function postprocess$1(context, language, findReferences) {
133
124
  return (...args) => {
134
125
  const result = findReferences(...args);
135
126
  if (!result?.length) {
136
- const sourceScript = language.scripts.get(args[0]);
127
+ const sourceScript = language.scripts.get(args[0].replaceAll("\\", "/"));
137
128
  const root = sourceScript?.generated?.root;
138
129
  if (!isVueVirtualCode(root)) return;
139
130
  const start = (root.sfc.template?.start ?? Infinity) + 1;
@@ -198,12 +189,13 @@ const fetchFunctions = new Set([
198
189
  "useFetch",
199
190
  "useLazyFetch"
200
191
  ]);
192
+ const pageMetaKeys = new Set(["layout", "middleware"]);
201
193
  function postprocess(context, language, getDefinitionAndBoundSpan) {
202
194
  const { ts } = context;
203
195
  return (...args) => {
204
196
  const result = getDefinitionAndBoundSpan(...args);
205
197
  if (!result?.definitions?.length) {
206
- const root = language.scripts.get(args[0])?.generated?.root;
198
+ const root = language.scripts.get(args[0].replaceAll("\\", "/"))?.generated?.root;
207
199
  if (!isVueVirtualCode(root)) return result;
208
200
  const textSpan = {
209
201
  start: (root.sfc.template?.start ?? Infinity) + 1,
@@ -238,6 +230,7 @@ function preprocess$1(context, getDefinitionAndBoundSpan) {
238
230
  for (const node of forEachTouchingNode(ts, sourceFile, args[1])) {
239
231
  if (data.features.importGlob) result$1 ??= visitImportGlob(ts, info, sourceFile, node, args[1]);
240
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]);
241
234
  if (data.features.typedPages) result$1 ??= visitTypedPages(ts, data, checker, sourceFile, node, args[1]);
242
235
  }
243
236
  if (result$1) return result$1;
@@ -331,12 +324,51 @@ function visitNitroRoutes(ts, data, checker, sourceFile, node, position) {
331
324
  definitions: paths.map((path) => createModuleDefinition(ts, path))
332
325
  };
333
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
+ }
334
365
  function visitTypedPages(ts, data, checker, sourceFile, node, position) {
335
366
  if (!ts.isPropertyAssignment(node) || !ts.isIdentifier(node.name) || node.name.text !== "name" || !ts.isStringLiteralLike(node.initializer)) return;
336
367
  const start = node.initializer.getStart(sourceFile);
337
368
  const end = node.initializer.getEnd();
338
369
  if (position < start || position > end) return;
339
- if (checker.getContextualType(node.parent)?.getNonNullableType().aliasSymbol?.name !== "RouteLocationRaw") 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;
340
372
  const path = data.typedPages[node.initializer.text];
341
373
  if (path === void 0) return;
342
374
  return {
@@ -430,30 +462,31 @@ function preprocess(context, getEditsForFileRename) {
430
462
  return (...args) => {
431
463
  const result = getEditsForFileRename(...args);
432
464
  if (!result?.length) return result;
433
- const program = info.languageService.getProgram();
434
- const references = {};
435
- for (const change of result) {
436
- const { fileName, textChanges } = change;
437
- if (data.features.components && fileName.endsWith("components.d.ts")) {
465
+ if (data.features.components) {
466
+ const languageService = info.project.getLanguageService();
467
+ const program = languageService.getProgram();
468
+ const references = {};
469
+ for (const { fileName, textChanges } of result) {
470
+ if (!fileName.endsWith("components.d.ts")) continue;
438
471
  const sourceFile = program.getSourceFile(fileName);
439
472
  if (!sourceFile) continue;
440
473
  for (const { span } of textChanges) for (const node of forEachTouchingNode(ts, sourceFile, span.start)) {
441
474
  if (!ts.isPropertySignature(node) && !ts.isVariableDeclaration(node)) continue;
442
475
  const position = node.name.getStart(sourceFile);
443
- 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);
444
477
  const lazy = node.type && ts.isTypeReferenceNode(node.type) && ts.isIdentifier(node.type.typeName) && node.type.typeName.text === "LazyComponent";
445
478
  for (const { fileName: fileName$1, textSpan } of res ?? []) (references[fileName$1] ??= []).push({
446
- textSpan: toSourceSpan(context.language, fileName$1, textSpan) ?? textSpan,
479
+ textSpan,
447
480
  lazy: lazy || void 0
448
481
  });
449
482
  break;
450
483
  }
451
484
  }
485
+ if (Object.keys(references).length) server.write("components:rename", {
486
+ fileName: args[1],
487
+ references
488
+ });
452
489
  }
453
- if (Object.keys(references).length) server.write("components:rename", {
454
- fileName: args[1],
455
- references
456
- });
457
490
  return result.filter((change) => {
458
491
  return !change.fileName.startsWith(data.buildDir);
459
492
  });
@@ -474,7 +507,7 @@ const plugin = (module$1) => {
474
507
  };
475
508
  queueMicrotask(() => {
476
509
  context.language = info.project.__vue__?.language;
477
- if (!context.language || !data.features.unimport.componentReferences) return;
510
+ if (!context.language || !data.features.unofficial) return;
478
511
  const languageService = info.project.getLanguageService();
479
512
  const methods = {};
480
513
  for (const [key, method] of [["findReferences", findReferences_exports], ["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.1",
4
+ "version": "0.4.0",
5
5
  "description": "TypeScript plugin for Nuxt",
6
6
  "author": "KazariEX",
7
7
  "license": "MIT",
@@ -15,6 +15,9 @@
15
15
  "files": [
16
16
  "dist"
17
17
  ],
18
+ "peerDependencies": {
19
+ "typescript": "*"
20
+ },
18
21
  "dependencies": {
19
22
  "@nuxt/kit": "^4.2.2",
20
23
  "chokidar": "^5.0.0",
@@ -24,9 +27,9 @@
24
27
  },
25
28
  "devDependencies": {
26
29
  "@dxup/shared": "",
27
- "@volar/language-core": "^2.4.26",
28
- "@volar/typescript": "^2.4.26",
29
- "@vue/language-core": "^3.1.8",
30
+ "@volar/language-core": "^2.4.27",
31
+ "@volar/typescript": "^2.4.27",
32
+ "@vue/language-core": "^3.2.1",
30
33
  "nuxt": "^4.2.2",
31
34
  "typescript": "^5.9.3"
32
35
  },