@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 +44 -7
- package/dist/module.d.ts +27 -20
- package/dist/module.js +7 -7
- package/dist/typescript.cjs +110 -53
- package/package.json +7 -6
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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
|
}
|
package/dist/typescript.cjs
CHANGED
|
@@ -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
|
|
76
|
-
return textSpan.start + textSpan.length
|
|
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
|
|
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 (
|
|
97
|
-
|
|
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 (
|
|
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
|
|
301
|
-
["getDefinitionAndBoundSpan", getDefinitionAndBoundSpan
|
|
302
|
-
["getEditsForFileRename", getEditsForFileRename
|
|
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
|
-
|
|
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.
|
|
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.
|
|
19
|
+
"@nuxt/kit": "^4.2.0",
|
|
20
20
|
"chokidar": "^4.0.3",
|
|
21
|
-
"pathe": "2.0.3",
|
|
22
|
-
"
|
|
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.
|
|
29
|
-
"nuxt": "^4.
|
|
29
|
+
"@vue/language-core": "^3.1.3",
|
|
30
|
+
"nuxt": "^4.2.0",
|
|
30
31
|
"typescript": "^5.9.3"
|
|
31
32
|
},
|
|
32
33
|
"scripts": {
|