@void/vue 0.7.12 → 0.8.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/dist/plugin.d.mts +1 -1
- package/dist/plugin.mjs +76 -59
- package/package.json +3 -3
package/dist/plugin.d.mts
CHANGED
|
@@ -6,6 +6,6 @@ type VoidVueOptions = Readonly<{
|
|
|
6
6
|
vue?: Parameters<typeof vue>[0];
|
|
7
7
|
viewTransitions?: boolean;
|
|
8
8
|
}>;
|
|
9
|
-
declare function voidVue(options?: VoidVueOptions):
|
|
9
|
+
declare function voidVue(options?: VoidVueOptions): Array<Plugin>;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { VoidVueOptions, voidVue };
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import vue from "@vitejs/plugin-vue";
|
|
2
|
+
import { generateComponentManifest, resolveEffectiveLayoutChain, resolvePagesDir, resolveStaticPageDataBuildOptions, scanPages } from "void/pages";
|
|
2
3
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
3
4
|
import { normalizePath } from "vite";
|
|
4
|
-
import { generateComponentManifest, resolveEffectiveLayoutChain, resolveStaticPageDataBuildOptions, scanPages } from "void/pages";
|
|
5
5
|
import { ISLANDS_CLIENT_ENTRY_ID, ISLANDS_RENDERER_ID, computePagesNeedingClientJS, needsIslandClientBundle, resolveSpecifier, rewriteCssImportForVirtualModule, watchPagesForRescan } from "void/pages-islands-plugin";
|
|
6
6
|
//#region ../../node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/shared/pathe.M-eThtNZ.mjs
|
|
7
7
|
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
@@ -9,42 +9,9 @@ function normalizeWindowsPath(input = "") {
|
|
|
9
9
|
if (!input) return input;
|
|
10
10
|
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
|
|
11
11
|
}
|
|
12
|
-
const _UNC_REGEX = /^[/\\]{2}/;
|
|
13
12
|
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
|
|
14
13
|
const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
|
|
15
14
|
const _ROOT_FOLDER_RE = /^\/([A-Za-z]:)?$/;
|
|
16
|
-
const normalize = function(path) {
|
|
17
|
-
if (path.length === 0) return ".";
|
|
18
|
-
path = normalizeWindowsPath(path);
|
|
19
|
-
const isUNCPath = path.match(_UNC_REGEX);
|
|
20
|
-
const isPathAbsolute = isAbsolute(path);
|
|
21
|
-
const trailingSeparator = path[path.length - 1] === "/";
|
|
22
|
-
path = normalizeString(path, !isPathAbsolute);
|
|
23
|
-
if (path.length === 0) {
|
|
24
|
-
if (isPathAbsolute) return "/";
|
|
25
|
-
return trailingSeparator ? "./" : ".";
|
|
26
|
-
}
|
|
27
|
-
if (trailingSeparator) path += "/";
|
|
28
|
-
if (_DRIVE_LETTER_RE.test(path)) path += "/";
|
|
29
|
-
if (isUNCPath) {
|
|
30
|
-
if (!isPathAbsolute) return `//./${path}`;
|
|
31
|
-
return `//${path}`;
|
|
32
|
-
}
|
|
33
|
-
return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
|
|
34
|
-
};
|
|
35
|
-
const join = function(...segments) {
|
|
36
|
-
let path = "";
|
|
37
|
-
for (const seg of segments) {
|
|
38
|
-
if (!seg) continue;
|
|
39
|
-
if (path.length > 0) {
|
|
40
|
-
const pathTrailing = path[path.length - 1] === "/";
|
|
41
|
-
const segLeading = seg[0] === "/";
|
|
42
|
-
if (pathTrailing && segLeading) path += seg.slice(1);
|
|
43
|
-
else path += pathTrailing || segLeading ? seg : `/${seg}`;
|
|
44
|
-
} else path += seg;
|
|
45
|
-
}
|
|
46
|
-
return normalize(path);
|
|
47
|
-
};
|
|
48
15
|
function cwd() {
|
|
49
16
|
if (typeof process !== "undefined" && typeof process.cwd === "function") return process.cwd().replace(/\\/g, "/");
|
|
50
17
|
return "/";
|
|
@@ -142,6 +109,23 @@ const COMPONENT_EXTENSIONS = [
|
|
|
142
109
|
".ts",
|
|
143
110
|
".js"
|
|
144
111
|
];
|
|
112
|
+
const ISLAND_IMPORT_RE = /import\s+([$_A-Za-z][$_\w]*)\s+from\s+("[^"]+"|'[^']+')\s+with\s*\{\s*island\s*:\s*("[^"]+"|'[^']+')\s*,?\s*\}/g;
|
|
113
|
+
function findIslandImports(code) {
|
|
114
|
+
const imports = [];
|
|
115
|
+
let match;
|
|
116
|
+
ISLAND_IMPORT_RE.lastIndex = 0;
|
|
117
|
+
while ((match = ISLAND_IMPORT_RE.exec(code)) !== null) {
|
|
118
|
+
const specifierRaw = match[2];
|
|
119
|
+
imports.push({
|
|
120
|
+
statement: match[0],
|
|
121
|
+
importName: match[1],
|
|
122
|
+
specifierRaw,
|
|
123
|
+
specifier: specifierRaw.slice(1, -1),
|
|
124
|
+
hydrate: match[3].slice(1, -1)
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return imports;
|
|
128
|
+
}
|
|
145
129
|
function extractCss(filePath) {
|
|
146
130
|
const code = readFileSync(filePath, "utf-8");
|
|
147
131
|
const parts = [];
|
|
@@ -157,9 +141,9 @@ function extractCss(filePath) {
|
|
|
157
141
|
}
|
|
158
142
|
return parts.join("\n");
|
|
159
143
|
}
|
|
160
|
-
function islandsPlugin(pagesDir) {
|
|
144
|
+
function islandsPlugin(pagesDir, options) {
|
|
161
145
|
let pageScan = null;
|
|
162
|
-
const root = dirname(pagesDir);
|
|
146
|
+
const root = options?.root ?? dirname(pagesDir);
|
|
163
147
|
const islandManifest = /* @__PURE__ */ new Map();
|
|
164
148
|
const islandCssFiles = /* @__PURE__ */ new Set();
|
|
165
149
|
let isDev = false;
|
|
@@ -184,8 +168,7 @@ function islandsPlugin(pagesDir) {
|
|
|
184
168
|
},
|
|
185
169
|
async buildStart() {
|
|
186
170
|
if (existsSync(pagesDir)) {
|
|
187
|
-
pageScan = await scanPages(root);
|
|
188
|
-
const importAttrRe = /import\s+\w+\s+from\s+("[^"]+"|'[^']+')\s+with\s*\{\s*island\s*:\s*("[^"]+"|'[^']+')\s*\}/g;
|
|
171
|
+
pageScan = await scanPages(root, { pagesDir });
|
|
189
172
|
const filesToScan = [
|
|
190
173
|
...pageScan.pages.map((page) => ({
|
|
191
174
|
filePath: page.componentPath,
|
|
@@ -205,11 +188,9 @@ function islandsPlugin(pagesDir) {
|
|
|
205
188
|
const filePath = resolve(pagesDir, entry.filePath);
|
|
206
189
|
const code = readFileSync(filePath, "utf-8");
|
|
207
190
|
const importerDir = dirname(filePath);
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const spec = importMatch[1].slice(1, -1);
|
|
212
|
-
const strategy = importMatch[2].slice(1, -1);
|
|
191
|
+
for (const islandImport of findIslandImports(code)) {
|
|
192
|
+
const spec = islandImport.specifier;
|
|
193
|
+
const strategy = islandImport.hydrate;
|
|
213
194
|
const absPath = resolveSpecifier(spec, importerDir, COMPONENT_EXTENSIONS);
|
|
214
195
|
if (absPath) {
|
|
215
196
|
const islandId = normalizePath(relative(root, absPath)).replace(/\.[^.]+$/, "");
|
|
@@ -267,18 +248,15 @@ function islandsPlugin(pagesDir) {
|
|
|
267
248
|
filter: { id: /\.island\.vue$/ },
|
|
268
249
|
handler(code, id) {
|
|
269
250
|
if (/import\s+\{[^}]*\buseForm\b/.test(code)) this.warn(`islands: useForm cannot be imported in island page '${id}'. Use useIslandForm instead.`);
|
|
270
|
-
const importAttrRe = /import\s+(\w+)\s+from\s+("[^"]+"|'[^']+')\s+with\s*\{\s*island\s*:\s*("[^"]+"|'[^']+')\s*\}/g;
|
|
271
251
|
const importerDir = dirname(id);
|
|
272
252
|
let transformed = code;
|
|
273
253
|
const wrappers = [];
|
|
274
254
|
let idx = 0;
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
const
|
|
279
|
-
const
|
|
280
|
-
const spec = specRaw.slice(1, -1);
|
|
281
|
-
const strategy = strategyRaw.slice(1, -1);
|
|
255
|
+
for (const islandImport of findIslandImports(code)) {
|
|
256
|
+
const name = islandImport.importName;
|
|
257
|
+
const specRaw = islandImport.specifierRaw;
|
|
258
|
+
const strategy = islandImport.hydrate;
|
|
259
|
+
const spec = islandImport.specifier;
|
|
282
260
|
const rawName = `_raw_island_${idx++}`;
|
|
283
261
|
const absPath = resolveSpecifier(spec, importerDir, COMPONENT_EXTENSIONS);
|
|
284
262
|
if (absPath) {
|
|
@@ -291,7 +269,7 @@ function islandsPlugin(pagesDir) {
|
|
|
291
269
|
}
|
|
292
270
|
const islandId = absPath ? normalizePath(relative(root, absPath)).replace(/\.[^.]+$/, "") : spec;
|
|
293
271
|
const replacement = `import ${rawName} from ${specRaw}`;
|
|
294
|
-
transformed = transformed.replace(
|
|
272
|
+
transformed = transformed.replace(islandImport.statement, replacement);
|
|
295
273
|
wrappers.push(`const ${name} = { inheritAttrs: false, render() { const props = this.$attrs; return _h_island("div", { "data-island": ${JSON.stringify(islandId)}, "data-props": JSON.stringify(props), "data-hydrate": ${JSON.stringify(strategy)} }, _h_island(${rawName}, props)); } };`);
|
|
296
274
|
}
|
|
297
275
|
if (wrappers.length > 0) {
|
|
@@ -472,8 +450,26 @@ function hasNonIslandPages(dir) {
|
|
|
472
450
|
}
|
|
473
451
|
function pagesPlugin(pagesDir, options) {
|
|
474
452
|
let pageScan = null;
|
|
453
|
+
let isDev = false;
|
|
475
454
|
let viteConfig;
|
|
476
|
-
const root = dirname(pagesDir);
|
|
455
|
+
const root = options?.root ?? dirname(pagesDir);
|
|
456
|
+
const normalizedPagesDir = normalizePath(pagesDir);
|
|
457
|
+
let pendingRescan = null;
|
|
458
|
+
const rescanPages = async () => {
|
|
459
|
+
pageScan = existsSync(pagesDir) ? await scanPages(root, { pagesDir }) : null;
|
|
460
|
+
};
|
|
461
|
+
const runRescan = () => {
|
|
462
|
+
pendingRescan ??= rescanPages().finally(() => {
|
|
463
|
+
pendingRescan = null;
|
|
464
|
+
});
|
|
465
|
+
return pendingRescan;
|
|
466
|
+
};
|
|
467
|
+
const invalidatePageModules = (server) => {
|
|
468
|
+
for (const env of Object.values(server.environments)) for (const id of ["\0" + RENDERER_ID, "\0" + CLIENT_ENTRY_ID]) {
|
|
469
|
+
const mod = env.moduleGraph.getModuleById(id);
|
|
470
|
+
if (mod) env.moduleGraph.invalidateModule(mod);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
477
473
|
return [{
|
|
478
474
|
name: "void-vue:pages-transform",
|
|
479
475
|
enforce: "pre",
|
|
@@ -503,7 +499,7 @@ function pagesPlugin(pagesDir, options) {
|
|
|
503
499
|
}, {
|
|
504
500
|
name: "void-vue:pages",
|
|
505
501
|
async buildStart() {
|
|
506
|
-
|
|
502
|
+
await runRescan();
|
|
507
503
|
},
|
|
508
504
|
resolveId: {
|
|
509
505
|
filter: { id: /^virtual:void-pages-(renderer|client)$/ },
|
|
@@ -513,7 +509,9 @@ function pagesPlugin(pagesDir, options) {
|
|
|
513
509
|
},
|
|
514
510
|
load: {
|
|
515
511
|
filter: { id: /^\0virtual:void-pages-(renderer|client)$/ },
|
|
516
|
-
handler(id) {
|
|
512
|
+
async handler(id) {
|
|
513
|
+
if (isDev && pendingRescan) await pendingRescan;
|
|
514
|
+
if (!pageScan && existsSync(pagesDir)) await runRescan();
|
|
517
515
|
if (id === "\0" + RENDERER_ID) {
|
|
518
516
|
if (!pageScan) return "export function renderPage() { throw new Error('No pages found'); }";
|
|
519
517
|
return generateRendererModule(pageScan, pagesDir);
|
|
@@ -524,12 +522,27 @@ function pagesPlugin(pagesDir, options) {
|
|
|
524
522
|
}
|
|
525
523
|
}
|
|
526
524
|
},
|
|
525
|
+
configureServer(server) {
|
|
526
|
+
const handlePagesChange = async (path, event) => {
|
|
527
|
+
const normalizedPath = normalizePath(path);
|
|
528
|
+
if (normalizedPath !== normalizedPagesDir && !normalizedPath.startsWith(normalizedPagesDir + "/")) return;
|
|
529
|
+
if (event === "change" && !normalizedPath.endsWith(".md")) return;
|
|
530
|
+
await runRescan();
|
|
531
|
+
invalidatePageModules(server);
|
|
532
|
+
};
|
|
533
|
+
server.watcher.on("add", (path) => handlePagesChange(path, "add"));
|
|
534
|
+
server.watcher.on("change", (path) => handlePagesChange(path, "change"));
|
|
535
|
+
server.watcher.on("unlink", (path) => handlePagesChange(path, "unlink"));
|
|
536
|
+
},
|
|
527
537
|
configResolved(config) {
|
|
528
538
|
viteConfig = config;
|
|
529
539
|
},
|
|
530
540
|
config(_, env) {
|
|
531
541
|
if (!existsSync(pagesDir)) return;
|
|
532
|
-
if (env.command === "serve")
|
|
542
|
+
if (env.command === "serve") {
|
|
543
|
+
isDev = true;
|
|
544
|
+
return { optimizeDeps: { include: ["vue"] } };
|
|
545
|
+
}
|
|
533
546
|
if (!hasNonIslandPages(pagesDir)) return;
|
|
534
547
|
return { environments: { client: { build: {
|
|
535
548
|
manifest: true,
|
|
@@ -702,11 +715,15 @@ start();
|
|
|
702
715
|
//#endregion
|
|
703
716
|
//#region src/plugin.ts
|
|
704
717
|
function voidVue(options) {
|
|
705
|
-
const
|
|
718
|
+
const root = process.cwd();
|
|
719
|
+
const pagesDir = resolvePagesDir(root);
|
|
706
720
|
return [
|
|
707
721
|
vue(options?.vue),
|
|
708
|
-
...pagesPlugin(pagesDir, {
|
|
709
|
-
|
|
722
|
+
...pagesPlugin(pagesDir, {
|
|
723
|
+
root,
|
|
724
|
+
viewTransitions: options?.viewTransitions
|
|
725
|
+
}),
|
|
726
|
+
...islandsPlugin(pagesDir, { root })
|
|
710
727
|
];
|
|
711
728
|
}
|
|
712
729
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@void/vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/voidzero-dev/void.git",
|
|
@@ -44,12 +44,12 @@
|
|
|
44
44
|
"vite": "npm:@voidzero-dev/vite-plus-core@0.1.20",
|
|
45
45
|
"vite-plus": "^0.1.20",
|
|
46
46
|
"vue": "^3.5.34",
|
|
47
|
-
"void": "0.
|
|
47
|
+
"void": "0.8.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"vite": "^8.0.0",
|
|
51
51
|
"vue": "^3.5.0",
|
|
52
|
-
"void": "0.
|
|
52
|
+
"void": "0.8.0"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "vp pack",
|