vite-plugin-vanjs 0.0.10 → 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 +19 -221
- package/client/global.d.ts +3 -3
- package/client/index.mjs +141 -7
- package/client/types.d.ts +3 -3
- package/jsx/jsx.d.ts +2 -0
- package/jsx/jsx.mjs +4 -4
- package/meta/Head.mjs +3 -3
- package/meta/helpers.mjs +3 -5
- package/package.json +19 -19
- package/plugin/helpers.mjs +271 -0
- package/plugin/index.mjs +206 -0
- package/plugin/types.d.ts +23 -0
- package/router/a.mjs +18 -19
- package/router/cache.mjs +4 -3
- package/router/global.d.ts +74 -28
- package/router/helpers.mjs +32 -27
- package/router/lazy.mjs +22 -11
- package/router/router.mjs +28 -21
- package/router/routes.mjs +38 -9
- package/router/state.mjs +3 -3
- package/router/types.d.ts +103 -29
- package/server/index.mjs +32 -9
- package/server/types.d.ts +56 -0
- package/setup/global.d.ts +41 -39
- package/setup/helpers.mjs +7 -0
- package/setup/index-debug.mjs +8 -20
- package/setup/index-ssr.mjs +5 -0
- package/setup/index.mjs +4 -20
- package/setup/isServer.mjs +2 -0
- package/setup/package.json +16 -0
- package/setup/van-debug.mjs +2 -2
- package/setup/van-ssr..d.ts +1 -0
- package/setup/van-ssr.mjs +51 -0
- package/setup/van.mjs +44 -2
- package/setup/vanX-ssr.d.ts +1 -0
- package/setup/vanX-ssr.mjs +12 -0
- package/setup/vanX.mjs +23 -4
- package/tsconfig.json +1 -1
- package/src/index.mjs +0 -79
- package/src/types.d.ts +0 -28
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/** @typedef {import("./types").RouteConfig} RouteConfig */
|
|
2
|
+
/** @typedef {import("./types").PageFile} PageFile */
|
|
3
|
+
/** @typedef {import("./types").LayoutFile} LayoutFile */
|
|
4
|
+
/** @typedef {import("./types").RouteFile} RouteFile */
|
|
5
|
+
|
|
6
|
+
// import { normalizePath } from "vite";
|
|
7
|
+
import { dirname, join, posix, win32 } from "node:path";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { readdir } from "node:fs/promises";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get the file most probable route path for a given potential route.
|
|
13
|
+
* @type {typeof import("./types").fileToRoute}
|
|
14
|
+
*/
|
|
15
|
+
export const fileToRoute = (file, routesDir) => {
|
|
16
|
+
const cleanPath = file
|
|
17
|
+
.slice(routesDir.length + 1) // also remove initial slash
|
|
18
|
+
.replace(/\.(jsx|tsx|ts|js)$/, "")
|
|
19
|
+
.replace(/index$/, "")
|
|
20
|
+
.replace(/\(.*\)$/, "") // Remove (file_name) from path
|
|
21
|
+
.replace(/\([^)]+\)\/?/g, "") // Remove (folder_name) from path
|
|
22
|
+
.replace(/\[\.\.\.[^\]]+\]/g, "*")
|
|
23
|
+
.replace(/\[([^\]]+)\]/g, ":$1");
|
|
24
|
+
const slashPath = cleanPath.endsWith("/")
|
|
25
|
+
? cleanPath.slice(0, -1)
|
|
26
|
+
: cleanPath;
|
|
27
|
+
const path = slashPath === "*"
|
|
28
|
+
? slashPath
|
|
29
|
+
: slashPath?.length > 0
|
|
30
|
+
? `/${slashPath}`
|
|
31
|
+
: "/";
|
|
32
|
+
|
|
33
|
+
return path;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Identify all files in a folder.
|
|
38
|
+
* @type {typeof import("./types").globFiles}
|
|
39
|
+
*/
|
|
40
|
+
export const globFiles = async (dir, extensions) => {
|
|
41
|
+
/** @type {string[]} */
|
|
42
|
+
const files = [];
|
|
43
|
+
|
|
44
|
+
/** @param {string} directory */
|
|
45
|
+
async function scan(directory) {
|
|
46
|
+
if (!existsSync(directory)) {
|
|
47
|
+
// console.log('🍦 @vanjs/router: the "routes" folder does not exist.');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
51
|
+
if (!entries.length) {
|
|
52
|
+
// console.warn('🍦 @vanjs/router: the "routes" folder is empty.');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
const fullPath = join(directory, entry.name);
|
|
58
|
+
|
|
59
|
+
// istanbul ignore else
|
|
60
|
+
if (entry.isDirectory()) {
|
|
61
|
+
await scan(fullPath);
|
|
62
|
+
} else if (entry.isFile()) {
|
|
63
|
+
// Check if file has allowed extension
|
|
64
|
+
// istanbul ignore else
|
|
65
|
+
if (extensions.some((ext) => entry.name.endsWith(ext))) {
|
|
66
|
+
files.push(fullPath);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await scan(dir);
|
|
73
|
+
return files;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const normalizePathRegExp = new RegExp(`\\${win32.sep}`, "g");
|
|
77
|
+
/** @param {string} filename */
|
|
78
|
+
function normalizePath(filename) {
|
|
79
|
+
return filename.replace(normalizePathRegExp, posix.sep);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Scan routes directory and generate routes.
|
|
84
|
+
* @type {typeof import("./types").scanRoutes}
|
|
85
|
+
*/
|
|
86
|
+
export const scanRoutes = async (config, pluginConfig) => {
|
|
87
|
+
const { routesDir, extensions } = pluginConfig;
|
|
88
|
+
const routesPath = join(config.root, routesDir);
|
|
89
|
+
const files = await globFiles(routesPath, extensions);
|
|
90
|
+
|
|
91
|
+
if (!files?.length) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Filter out duplicate routes and layout files that are already used
|
|
96
|
+
const routes = files.map((file) => ({
|
|
97
|
+
path: normalizePath(file),
|
|
98
|
+
routePath: fileToRoute(file, routesPath),
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
// Remove duplicate routes (prefer non-layout files)
|
|
102
|
+
/** @type {typeof routes} */
|
|
103
|
+
const uniqueRoutes = routes.reduce(
|
|
104
|
+
/**
|
|
105
|
+
* @param {typeof routes} acc
|
|
106
|
+
* @param {typeof routes[0]} route
|
|
107
|
+
* @returns {typeof routes}
|
|
108
|
+
*/
|
|
109
|
+
(acc, route) => {
|
|
110
|
+
const existing = acc.find((r) => r.routePath === route.routePath);
|
|
111
|
+
if (
|
|
112
|
+
!existing || (existing.path.includes("(") && !route.path.includes("("))
|
|
113
|
+
) {
|
|
114
|
+
// Remove the existing route if this is a better match
|
|
115
|
+
// istanbul ignore if - should not be possible
|
|
116
|
+
if (existing) {
|
|
117
|
+
acc = acc.filter((r) => r !== existing);
|
|
118
|
+
}
|
|
119
|
+
acc.push(route);
|
|
120
|
+
}
|
|
121
|
+
return acc;
|
|
122
|
+
},
|
|
123
|
+
[],
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
return uniqueRoutes;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Find all layout files for a given route.
|
|
131
|
+
* @type {typeof import("./types").findLayouts}
|
|
132
|
+
*/
|
|
133
|
+
export const findLayouts = (routePath, config, pluginConfig) => {
|
|
134
|
+
const { routesDir, extensions } = pluginConfig;
|
|
135
|
+
const layouts = [];
|
|
136
|
+
let dir = dirname(routePath);
|
|
137
|
+
const routesPath = join(config.root, routesDir);
|
|
138
|
+
|
|
139
|
+
// Walk up the directory tree looking for layout files
|
|
140
|
+
while (dir.startsWith(routesPath) && dir !== routesPath) { // Stop at routes dir
|
|
141
|
+
let layoutFile = null;
|
|
142
|
+
const dirName = dir.split(/[/\\]/).pop();
|
|
143
|
+
|
|
144
|
+
// istanbul ignore else
|
|
145
|
+
if (dirName) {
|
|
146
|
+
// Look for a layout file in the current directory
|
|
147
|
+
for (const ext of extensions) {
|
|
148
|
+
const layoutPaths = [
|
|
149
|
+
join(dirname(dir), `${dirName}${ext}`),
|
|
150
|
+
join(dirname(dir), `(${dirName.replace(/^\((.*)\)$/, "$1")})${ext}`),
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
for (const path of layoutPaths) {
|
|
154
|
+
if (existsSync(path)) {
|
|
155
|
+
layoutFile = path;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// istanbul ignore else
|
|
163
|
+
if (layoutFile && layoutFile !== routePath) {
|
|
164
|
+
layouts.unshift({
|
|
165
|
+
id: `Layout${layouts.length}`,
|
|
166
|
+
path: layoutFile,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
dir = dirname(dir);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return layouts;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Process routes and identify their layouts
|
|
178
|
+
* @type {typeof import("./types").processLayoutRoutes}
|
|
179
|
+
*/
|
|
180
|
+
export const processLayoutRoutes = (routes, config, pluginConfig) => {
|
|
181
|
+
if (!routes.length) return [];
|
|
182
|
+
|
|
183
|
+
return routes.map((route) => {
|
|
184
|
+
const layouts = findLayouts(route.path, config, pluginConfig);
|
|
185
|
+
return {
|
|
186
|
+
...route,
|
|
187
|
+
layouts,
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Scan and process routes and return them
|
|
194
|
+
* @type {typeof import("./types").getRoutes}
|
|
195
|
+
*/
|
|
196
|
+
// export const scanRoutes = async (config, pluginConfig) => {}
|
|
197
|
+
export const getRoutes = async (config, pluginConfig) => {
|
|
198
|
+
const routes = await scanRoutes(config, pluginConfig);
|
|
199
|
+
return processLayoutRoutes(routes, config, pluginConfig);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/** @type {(route: RouteFile) => string} */
|
|
203
|
+
export const generateRouteProloaders = (route) => {
|
|
204
|
+
const moduleName = "PageModule";
|
|
205
|
+
const layoutName = "Module";
|
|
206
|
+
|
|
207
|
+
return `{
|
|
208
|
+
preload: async (params) => {
|
|
209
|
+
${
|
|
210
|
+
route.layouts.map((layout) =>
|
|
211
|
+
`if (${layout.id + layoutName}?.route?.preload) await ${
|
|
212
|
+
layout.id + layoutName
|
|
213
|
+
}?.route?.preload(params);`
|
|
214
|
+
).join("\n ")
|
|
215
|
+
}
|
|
216
|
+
if (${moduleName}?.route?.preload) await ${moduleName}?.route?.preload(params);
|
|
217
|
+
},
|
|
218
|
+
load: async (params) => {
|
|
219
|
+
${
|
|
220
|
+
route.layouts.map((layout) =>
|
|
221
|
+
`if (${layout.id + layoutName}?.route?.load) await ${
|
|
222
|
+
layout.id + layoutName
|
|
223
|
+
}?.route?.load(params);`
|
|
224
|
+
).join("\n ")
|
|
225
|
+
}
|
|
226
|
+
if (${moduleName}?.route?.load) await ${moduleName}?.route?.load(params);
|
|
227
|
+
}
|
|
228
|
+
}`;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/** @type {(route: RouteFile) => string} */
|
|
232
|
+
export const generateComponentRoute = (route) => {
|
|
233
|
+
if (route.layouts?.length > 0) {
|
|
234
|
+
// Only generate imports for unique layouts
|
|
235
|
+
const layoutImports = route.layouts.map(
|
|
236
|
+
(layout) =>
|
|
237
|
+
`const ${layout.id}Module = await import('${layout.path}');\n` +
|
|
238
|
+
`const ${layout.id}Page = ${layout.id}Module.Layout || ${layout.id}Module.Page || ${layout.id}Module.default;`,
|
|
239
|
+
).join("\n");
|
|
240
|
+
|
|
241
|
+
// Use both shared and unique layouts for the component chain
|
|
242
|
+
const pageComponent = route.layouts.reduce(
|
|
243
|
+
(acc, layout) => `${layout.id}Page({ children: ${acc} })`,
|
|
244
|
+
"Page()",
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
return `lazy(() => {
|
|
248
|
+
const importFn = async () => {
|
|
249
|
+
${layoutImports}
|
|
250
|
+
const PageModule = await import('${route.path}');
|
|
251
|
+
const Page = PageModule?.Page || PageModule?.default;
|
|
252
|
+
|
|
253
|
+
return Promise.resolve({
|
|
254
|
+
route: ${generateRouteProloaders(route)},
|
|
255
|
+
Page: () => ${pageComponent},
|
|
256
|
+
});
|
|
257
|
+
};
|
|
258
|
+
return importFn();
|
|
259
|
+
})`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return `lazy(() => import('${route.path}'))`;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/** @type {(route: RouteFile) => string} */
|
|
266
|
+
export const generateRoute = (route) => {
|
|
267
|
+
return `Route({
|
|
268
|
+
path: "${route.routePath}",
|
|
269
|
+
component: ${generateComponentRoute(route)},
|
|
270
|
+
});`;
|
|
271
|
+
};
|
package/plugin/index.mjs
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/** @typedef {import("vite").ResolvedConfig} ResolvedConfig */
|
|
2
|
+
/** @typedef {import("./types").PageFile} PageFile */
|
|
3
|
+
/** @typedef {import("./types").RouteFile} RouteFile */
|
|
4
|
+
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { dirname, join, resolve } from "node:path";
|
|
7
|
+
import process from "node:process";
|
|
8
|
+
import { transformWithEsbuild } from "vite";
|
|
9
|
+
import { routes } from "../router/routes.mjs";
|
|
10
|
+
import { generateRoute, getRoutes } from "./helpers.mjs";
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
/** @param {string} p */
|
|
15
|
+
const toAbsolute = (p) => resolve(__dirname, p);
|
|
16
|
+
|
|
17
|
+
const pluginDefaults = {
|
|
18
|
+
routesDir: "src/routes",
|
|
19
|
+
extensions: [".tsx", ".jsx", ".ts", ".js"],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default function VitePluginVanJS(options = {}) {
|
|
23
|
+
const pluginConfig = { ...pluginDefaults, ...options };
|
|
24
|
+
const { routesDir } = pluginConfig;
|
|
25
|
+
|
|
26
|
+
/** @type {ResolvedConfig} */
|
|
27
|
+
let config;
|
|
28
|
+
/** @type {RouteFile[] | null} */
|
|
29
|
+
let routeCache = null;
|
|
30
|
+
|
|
31
|
+
const virtualModuleId = "virtual:@vanjs/routes";
|
|
32
|
+
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
name: "vanjs",
|
|
36
|
+
enforce: "pre",
|
|
37
|
+
config() {
|
|
38
|
+
return {
|
|
39
|
+
optimizeDeps: {
|
|
40
|
+
noDiscovery: true,
|
|
41
|
+
include: [
|
|
42
|
+
"vanjs-core",
|
|
43
|
+
"vanjs-ext",
|
|
44
|
+
"mini-van-plate/van-plate",
|
|
45
|
+
"mini-van-plate/shared",
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
ssr: {
|
|
49
|
+
noExternal: ["vanjs-*", "*-vanjs", "@vanjs/*"],
|
|
50
|
+
},
|
|
51
|
+
resolve: {
|
|
52
|
+
alias: {
|
|
53
|
+
"@vanjs/setup": toAbsolute("../setup/index"),
|
|
54
|
+
"@vanjs/van": toAbsolute("../setup/van"),
|
|
55
|
+
"@vanjs/vanX": toAbsolute("../setup/vanX"),
|
|
56
|
+
"@vanjs/client": toAbsolute("../client"),
|
|
57
|
+
"@vanjs/server": toAbsolute("../server"),
|
|
58
|
+
"@vanjs/meta": toAbsolute("../meta"),
|
|
59
|
+
"@vanjs/router": toAbsolute("../router"),
|
|
60
|
+
"@vanjs/jsx": toAbsolute("../jsx"),
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
esbuild: {
|
|
64
|
+
jsx: "automatic",
|
|
65
|
+
jsxImportSource: "@vanjs/jsx",
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
/** @param {import("vite").ResolvedConfig} resolvedConfig */
|
|
70
|
+
configResolved(resolvedConfig) {
|
|
71
|
+
config = resolvedConfig;
|
|
72
|
+
},
|
|
73
|
+
/** @param {import("vite").ViteDevServer} server */
|
|
74
|
+
configureServer(server) {
|
|
75
|
+
// Watch routes directory
|
|
76
|
+
const pagesPath = join(config.root, routesDir);
|
|
77
|
+
/** @param {string} file */
|
|
78
|
+
const changeHandler = (file) => {
|
|
79
|
+
// istanbul ignore else
|
|
80
|
+
if (file.startsWith(pagesPath)) {
|
|
81
|
+
routes.length = 0;
|
|
82
|
+
routeCache = null;
|
|
83
|
+
const module = server.moduleGraph.getModuleById(
|
|
84
|
+
resolvedVirtualModuleId,
|
|
85
|
+
);
|
|
86
|
+
// istanbul ignore else
|
|
87
|
+
if (module) {
|
|
88
|
+
server.moduleGraph.invalidateModule(module);
|
|
89
|
+
}
|
|
90
|
+
server.ws.send({ type: "full-reload" });
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
server.watcher.add(pagesPath);
|
|
94
|
+
|
|
95
|
+
// Handle file changes in pages directory
|
|
96
|
+
// 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir'
|
|
97
|
+
server.watcher.on("add", changeHandler);
|
|
98
|
+
server.watcher.on("addDir", changeHandler);
|
|
99
|
+
server.watcher.on("unlink", changeHandler);
|
|
100
|
+
server.watcher.on("unlinkDir", changeHandler);
|
|
101
|
+
server.watcher.on("change", changeHandler);
|
|
102
|
+
},
|
|
103
|
+
/** @type {(source: string, importer: string | undefined, ops: { ssr: boolean }) => string | null} */
|
|
104
|
+
resolveId(source, importer, ops) {
|
|
105
|
+
// istanbul ignore else
|
|
106
|
+
if (source === virtualModuleId) {
|
|
107
|
+
return resolvedVirtualModuleId;
|
|
108
|
+
}
|
|
109
|
+
const isVanXFile = importer &&
|
|
110
|
+
/vanjs-ext[\/\\]src[\/\\]van-x/.test(importer);
|
|
111
|
+
const isSetupFile = importer &&
|
|
112
|
+
/vite-plugin-vanjs[\/\\]setup/.test(importer);
|
|
113
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
114
|
+
const isTest = process.env.NODE_ENV === "test";
|
|
115
|
+
const isJSXImport = source.includes("/vite-plugin-vanjs/jsx/jsx") ||
|
|
116
|
+
importer?.includes("/vite-plugin-vanjs/jsx/jsx.mjs");
|
|
117
|
+
|
|
118
|
+
const resolvedVan = toAbsolute(
|
|
119
|
+
ops.ssr ? "../setup/van-ssr.mjs" : (isJSXImport || isProduction ||
|
|
120
|
+
isTest)
|
|
121
|
+
? "../setup/van.mjs"
|
|
122
|
+
: "../setup/van-debug.mjs",
|
|
123
|
+
);
|
|
124
|
+
const resolvedVanX = toAbsolute(
|
|
125
|
+
ops.ssr ? "../setup/vanX-ssr.mjs" : "../setup/vanX.mjs",
|
|
126
|
+
);
|
|
127
|
+
const setupResolved = toAbsolute(
|
|
128
|
+
ops.ssr
|
|
129
|
+
? "../setup/index-ssr.mjs"
|
|
130
|
+
: (isProduction || isTest)
|
|
131
|
+
? "../setup/index.mjs"
|
|
132
|
+
: "../setup/index-debug.mjs",
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// Resolve early when source already resolved. EG: @vanjs/van
|
|
136
|
+
if (source === setupResolved || setupResolved.includes(source)) {
|
|
137
|
+
return setupResolved;
|
|
138
|
+
}
|
|
139
|
+
if (source === resolvedVan || resolvedVan.includes(source)) {
|
|
140
|
+
return resolvedVan;
|
|
141
|
+
}
|
|
142
|
+
if (source === resolvedVanX || resolvedVanX.includes(source)) {
|
|
143
|
+
return resolvedVanX;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// istanbul ignore else
|
|
147
|
+
if (!isSetupFile && !isVanXFile) {
|
|
148
|
+
if (source === "@vanjs/setup") {
|
|
149
|
+
return setupResolved;
|
|
150
|
+
}
|
|
151
|
+
if (importer?.endsWith("debug.js") && source.endsWith("/van.js")) {
|
|
152
|
+
return toAbsolute("../setup/van.mjs");
|
|
153
|
+
}
|
|
154
|
+
if (source === "vanjs-core" || source === "@vanjs/van") {
|
|
155
|
+
return resolvedVan;
|
|
156
|
+
}
|
|
157
|
+
if (source === "vanjs-ext" || source === "@vanjs/vanX") {
|
|
158
|
+
return resolvedVanX;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return null;
|
|
163
|
+
},
|
|
164
|
+
/** @type {(id: string, ops: { ssr: boolean }) => Promise<({ code: string, map: null } | null)>} */
|
|
165
|
+
async load(id, ops) {
|
|
166
|
+
// istanbul ignore else
|
|
167
|
+
if (id === resolvedVirtualModuleId) {
|
|
168
|
+
const currentRoutes = routeCache ||
|
|
169
|
+
await getRoutes(config, pluginConfig);
|
|
170
|
+
if (!currentRoutes || !currentRoutes.length) {
|
|
171
|
+
// don't crash the server if no routes are found
|
|
172
|
+
// devs might not use file system router
|
|
173
|
+
return { code: "", map: null };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const routesScript = `
|
|
177
|
+
import { Route, routes } from "@vanjs/router/routes.mjs";
|
|
178
|
+
import { lazy } from "@vanjs/router/lazy.mjs";
|
|
179
|
+
|
|
180
|
+
// Reset current routes
|
|
181
|
+
routes.length = 0;
|
|
182
|
+
|
|
183
|
+
// Register routes
|
|
184
|
+
${currentRoutes.map(generateRoute).join("\n")}
|
|
185
|
+
${
|
|
186
|
+
ops.ssr && currentRoutes.length
|
|
187
|
+
? `console.log(\`🍦 @vanjs/router registered ${currentRoutes.length} routes.\`)`
|
|
188
|
+
: /* istanbul ignore next - satisfied */ ""
|
|
189
|
+
}
|
|
190
|
+
`;
|
|
191
|
+
|
|
192
|
+
const result = await transformWithEsbuild(
|
|
193
|
+
routesScript,
|
|
194
|
+
id,
|
|
195
|
+
{ loader: "js" },
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
code: result.code,
|
|
200
|
+
map: null,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// plugin/types.ts
|
|
2
|
+
export * from "../jsx/types";
|
|
3
|
+
export * from "../setup/types";
|
|
4
|
+
export * from "../router/types";
|
|
5
|
+
export * from "../meta/types";
|
|
6
|
+
export * from "../server/types";
|
|
7
|
+
export * from "../client/types";
|
|
8
|
+
export * from "../parser/types";
|
|
9
|
+
import type { Plugin } from "vite";
|
|
10
|
+
|
|
11
|
+
export type VanJSPluginOptions = {
|
|
12
|
+
routesDir: string;
|
|
13
|
+
extensions: string[];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type VanJSPlugin = (
|
|
17
|
+
options?: Partial<VanJSPluginOptions>,
|
|
18
|
+
) => Plugin;
|
|
19
|
+
|
|
20
|
+
// This is what your plugin actually returns, so declare it as a Plugin type
|
|
21
|
+
declare const VitePluginVanJS: VanJSPlugin;
|
|
22
|
+
|
|
23
|
+
export default VitePluginVanJS;
|
package/router/a.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// router/a.mjs
|
|
2
2
|
import van from "vanjs-core";
|
|
3
|
-
import setup from "../setup/index.mjs";
|
|
4
3
|
import { matchRoute } from "./routes.mjs";
|
|
5
4
|
import { executeLifecycle, isCurrentPage, navigate } from "./helpers.mjs";
|
|
6
5
|
|
|
@@ -20,37 +19,37 @@ export const A = (
|
|
|
20
19
|
const newProps = {
|
|
21
20
|
href,
|
|
22
21
|
...props,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
van.derive(() => {
|
|
26
|
-
if (isCurrentPage(href)) {
|
|
27
|
-
newProps["aria-current"] = "page";
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const anchor = van.tags.a(newProps, [...(children || []), ...otherChildren]);
|
|
32
|
-
/* istanbul ignore else */
|
|
33
|
-
if (!setup.isServer) {
|
|
34
|
-
anchor.addEventListener("click", async (e) => {
|
|
22
|
+
onclick: async (e) => {
|
|
35
23
|
e.preventDefault();
|
|
36
24
|
/* istanbul ignore next */
|
|
37
25
|
if (isCurrentPage(href)) return;
|
|
38
26
|
|
|
27
|
+
// istanbul ignore else
|
|
28
|
+
if (props.onclick) {
|
|
29
|
+
await props.onclick(e);
|
|
30
|
+
}
|
|
31
|
+
|
|
39
32
|
const route = matchRoute(href);
|
|
40
33
|
const module = await route.component();
|
|
41
34
|
await executeLifecycle(module, route.params);
|
|
42
35
|
|
|
43
36
|
navigate(href);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
anchor.addEventListener("mouseenter", () => {
|
|
37
|
+
},
|
|
38
|
+
onmouseenter: () => {
|
|
47
39
|
const route = matchRoute(href);
|
|
48
40
|
|
|
49
41
|
/* istanbul ignore else */
|
|
50
42
|
if (route?.component) {
|
|
51
43
|
route.component();
|
|
52
44
|
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
van.derive(() => {
|
|
49
|
+
if (isCurrentPage(href)) {
|
|
50
|
+
newProps["aria-current"] = "page";
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return van.tags.a(newProps, children || otherChildren);
|
|
56
55
|
};
|
package/router/cache.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/** @typedef {import("./types").ComponentModule} ComponentModule */
|
|
2
|
-
/** @typedef {import("./types").
|
|
3
|
-
/** @typedef {import("./types").
|
|
2
|
+
/** @typedef {import("./types").ImportFn} ImportFn */
|
|
3
|
+
/** @typedef {typeof import("./types").getCached} GetCachedRoute */
|
|
4
|
+
/** @typedef {typeof import("./types").cache} CacheRoute */
|
|
4
5
|
|
|
5
|
-
/** @type {Map<
|
|
6
|
+
/** @type {Map<ImportFn, ComponentModule>} */
|
|
6
7
|
const routeCache = new Map();
|
|
7
8
|
|
|
8
9
|
/** @type {GetCachedRoute} */
|