void-plugin-hono-jsx 0.1.0-alpha.3 → 0.1.0-alpha.4
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/CHANGELOG.md +52 -0
- package/README.md +41 -12
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/dist/browser/client.d.ts +2 -1
- package/dist/browser/client.d.ts.map +1 -1
- package/dist/browser/client.js +76 -22
- package/dist/browser/client.js.map +1 -1
- package/dist/browser/document-head.d.ts +1 -1
- package/dist/browser/document-head.d.ts.map +1 -1
- package/dist/browser/document-head.js +22 -1
- package/dist/browser/document-head.js.map +1 -1
- package/dist/browser/form-view.d.ts +10 -0
- package/dist/browser/form-view.d.ts.map +1 -0
- package/dist/browser/form-view.js +16 -0
- package/dist/browser/form-view.js.map +1 -0
- package/dist/browser/form.d.ts +28 -12
- package/dist/browser/form.d.ts.map +1 -1
- package/dist/browser/form.js +84 -23
- package/dist/browser/form.js.map +1 -1
- package/dist/browser/markdown.d.ts +15 -0
- package/dist/browser/markdown.d.ts.map +1 -0
- package/dist/browser/markdown.js +6 -0
- package/dist/browser/markdown.js.map +1 -0
- package/dist/browser/navigation.d.ts +13 -0
- package/dist/browser/navigation.d.ts.map +1 -0
- package/dist/browser/navigation.js +83 -0
- package/dist/browser/navigation.js.map +1 -0
- package/dist/browser/pages.d.ts +33 -3
- package/dist/browser/pages.d.ts.map +1 -1
- package/dist/browser/pages.js +76 -13
- package/dist/browser/pages.js.map +1 -1
- package/dist/plugins/hono-islands.d.ts +1 -1
- package/dist/plugins/hono-islands.d.ts.map +1 -1
- package/dist/plugins/hono-islands.js +156 -24
- package/dist/plugins/hono-islands.js.map +1 -1
- package/dist/plugins/hono-markdown.d.ts +4 -0
- package/dist/plugins/hono-markdown.d.ts.map +1 -0
- package/dist/plugins/hono-markdown.js +91 -0
- package/dist/plugins/hono-markdown.js.map +1 -0
- package/dist/plugins/hono-pages.d.ts +9 -1
- package/dist/plugins/hono-pages.d.ts.map +1 -1
- package/dist/plugins/hono-pages.js +51 -15
- package/dist/plugins/hono-pages.js.map +1 -1
- package/dist/plugins/markdown-api.d.ts +10 -0
- package/dist/plugins/markdown-api.d.ts.map +1 -0
- package/dist/plugins/markdown-api.js +13 -0
- package/dist/plugins/markdown-api.js.map +1 -0
- package/dist/plugins/markdown-compiler.d.ts +43 -0
- package/dist/plugins/markdown-compiler.d.ts.map +1 -0
- package/dist/plugins/markdown-compiler.js +91 -0
- package/dist/plugins/markdown-compiler.js.map +1 -0
- package/dist/plugins/resolve-component.d.ts +3 -0
- package/dist/plugins/resolve-component.d.ts.map +1 -0
- package/dist/plugins/resolve-component.js +15 -0
- package/dist/plugins/resolve-component.js.map +1 -0
- package/dist/plugins/runtime-paths.d.ts +1 -0
- package/dist/plugins/runtime-paths.d.ts.map +1 -1
- package/dist/plugins/runtime-paths.js +2 -0
- package/dist/plugins/runtime-paths.js.map +1 -1
- package/dist/server/form-view.d.ts +10 -0
- package/dist/server/form-view.d.ts.map +1 -0
- package/dist/server/form-view.js +16 -0
- package/dist/server/form-view.js.map +1 -0
- package/dist/server/form.d.ts +28 -12
- package/dist/server/form.d.ts.map +1 -1
- package/dist/server/form.js +84 -23
- package/dist/server/form.js.map +1 -1
- package/dist/server/markdown.d.ts +15 -0
- package/dist/server/markdown.d.ts.map +1 -0
- package/dist/server/markdown.js +6 -0
- package/dist/server/markdown.js.map +1 -0
- package/dist/server/pages.d.ts +33 -3
- package/dist/server/pages.d.ts.map +1 -1
- package/dist/server/pages.js +76 -13
- package/dist/server/pages.js.map +1 -1
- package/package.json +21 -3
- package/src/plugins/hono-islands.ts +121 -23
- package/src/plugins/hono-markdown.ts +78 -0
- package/src/plugins/hono-pages.ts +50 -14
- package/src/plugins/markdown-api.ts +17 -0
- package/src/plugins/markdown-compiler.ts +77 -0
- package/src/plugins/resolve-component.ts +12 -0
- package/src/plugins/runtime-paths.ts +2 -0
- package/src/runtime/client.tsx +66 -24
- package/src/runtime/document-head.ts +20 -1
- package/src/runtime/form-view.tsx +23 -0
- package/src/runtime/form.ts +82 -41
- package/src/runtime/markdown.ts +7 -0
- package/src/runtime/navigation.ts +72 -0
- package/src/runtime/pages.tsx +77 -15
|
@@ -1,30 +1,49 @@
|
|
|
1
1
|
import { readFileSync, realpathSync } from 'node:fs';
|
|
2
2
|
import { join, relative } from 'node:path';
|
|
3
3
|
import { normalizePath, parseSync } from 'vite';
|
|
4
|
-
import { scanPages, resolvePagesDir } from 'void/pages';
|
|
5
|
-
import { findIslandImports, editIslandSource,
|
|
6
|
-
|
|
4
|
+
import { scanPages, resolveEffectiveLayoutChain, resolvePagesDir } from 'void/pages';
|
|
5
|
+
import { findIslandImports, editIslandSource, computePagesNeedingClientJS, ISLANDS_RENDERER_ID, ISLANDS_CLIENT_ENTRY_ID } from 'void/pages-islands-plugin';
|
|
6
|
+
import { resolveComponent } from './resolve-component.js';
|
|
7
|
+
import { markdownApi, markdownDiagnostic } from './markdown-api.js';
|
|
8
|
+
/** Hono mounting through Void's shared island scheduler. */
|
|
7
9
|
export function honoIslands() {
|
|
8
10
|
const root = process.cwd();
|
|
9
11
|
const pagesDir = resolvePagesDir(root);
|
|
10
12
|
const islands = new Map();
|
|
11
13
|
const pageFiles = new Set();
|
|
14
|
+
const graph = new Map();
|
|
15
|
+
const directlyInteractive = new Set();
|
|
16
|
+
const interactivePages = new Set();
|
|
17
|
+
const scriptOnlyPages = new Set();
|
|
12
18
|
let scan;
|
|
19
|
+
let markdown;
|
|
13
20
|
function islandImports(code, filename) {
|
|
14
21
|
const found = findIslandImports(code, filename);
|
|
15
22
|
const attributed = parseSync(filename, code).program.body.filter((node) => node.type === 'ImportDeclaration' && node.attributes?.some(({ key }) => ('name' in key && key.name === 'island') || ('value' in key && key.value === 'island')));
|
|
16
23
|
if (attributed.length !== found.length)
|
|
17
|
-
throw new Error(`Hono islands: use one default import with exactly one island
|
|
24
|
+
throw new Error(`Hono islands: use one default import with exactly one island attribute using load, visible, idle or media:query in ${filename}.`);
|
|
18
25
|
return found;
|
|
19
26
|
}
|
|
20
27
|
function imports(code, filename) {
|
|
21
28
|
const found = islandImports(code, filename);
|
|
22
29
|
for (const entry of found) {
|
|
23
|
-
if (
|
|
24
|
-
|
|
30
|
+
if (!['load', 'visible', 'idle'].includes(entry.hydrate)) {
|
|
31
|
+
const query = entry.hydrate.startsWith('media:') ? entry.hydrate.slice(6).trim() : '';
|
|
32
|
+
let balance = 0;
|
|
33
|
+
for (const character of query) {
|
|
34
|
+
if (character === '(')
|
|
35
|
+
balance++;
|
|
36
|
+
if (character === ')')
|
|
37
|
+
balance--;
|
|
38
|
+
if (balance < 0)
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
if (!query || balance !== 0 || /[{};]/.test(query))
|
|
42
|
+
throw new Error(`Hono islands: unsupported policy or invalid media query '${entry.hydrate}' in ${filename}.`);
|
|
43
|
+
}
|
|
25
44
|
if (!entry.specifier.startsWith('.'))
|
|
26
45
|
throw new Error(`Hono islands: use a relative TSX/JSX component import in ${filename}.`);
|
|
27
|
-
const path =
|
|
46
|
+
const path = resolveComponent(entry.specifier, join(filename, '..'), ['.tsx', '.jsx']);
|
|
28
47
|
if (!path || !/\.[jt]sx$/.test(path))
|
|
29
48
|
throw new Error(`Hono islands: cannot resolve TSX/JSX component ${entry.specifier} in ${filename}.`);
|
|
30
49
|
const canonical = realpathSync(path);
|
|
@@ -35,21 +54,107 @@ export function honoIslands() {
|
|
|
35
54
|
}
|
|
36
55
|
async function rescan() {
|
|
37
56
|
scan = await scanPages(root, { pagesDir });
|
|
57
|
+
// Never pass Markdown text to the JavaScript static-graph parser.
|
|
58
|
+
for (const file of [...scan.pages.map(page => page.componentPath), ...scan.layouts.map(layout => layout.filePath), ...scan.namedLayouts.map(layout => layout.filePath)]) {
|
|
59
|
+
if (/\.md$/.test(file) && !markdown)
|
|
60
|
+
throw new Error(`${markdownDiagnostic} ${file}`);
|
|
61
|
+
}
|
|
38
62
|
islands.clear();
|
|
39
63
|
pageFiles.clear();
|
|
64
|
+
graph.clear();
|
|
65
|
+
directlyInteractive.clear();
|
|
66
|
+
interactivePages.clear();
|
|
67
|
+
scriptOnlyPages.clear();
|
|
68
|
+
const visit = (filename) => {
|
|
69
|
+
const canonical = realpathSync(filename);
|
|
70
|
+
if (graph.has(canonical))
|
|
71
|
+
return;
|
|
72
|
+
const dependencies = new Set();
|
|
73
|
+
graph.set(canonical, dependencies);
|
|
74
|
+
pageFiles.add(canonical);
|
|
75
|
+
const code = canonical.endsWith('.md') && markdown ? markdown.getGraphSource(canonical) : readFileSync(canonical, 'utf8');
|
|
76
|
+
const found = imports(code, canonical);
|
|
77
|
+
if (found.length) {
|
|
78
|
+
if (!/\.[jt]sx$/.test(canonical) && !(canonical.endsWith('.md') && markdown))
|
|
79
|
+
throw new Error('Hono islands: attributed imports require a static TSX/JSX module.');
|
|
80
|
+
directlyInteractive.add(canonical);
|
|
81
|
+
}
|
|
82
|
+
for (const node of parseSync(canonical, code).program.body) {
|
|
83
|
+
const source = node.type === 'ImportDeclaration' ? node.source : node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration' ? node.source : undefined;
|
|
84
|
+
const typeOnly = node.type === 'ImportDeclaration' ? node.importKind === 'type' || (node.specifiers.length > 0 && node.specifiers.every((entry) => entry.type === 'ImportSpecifier' && entry.importKind === 'type')) : (node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration') && node.exportKind === 'type';
|
|
85
|
+
if (!source || typeOnly || !source.value.startsWith('.') || found.some((entry) => entry.specifier === source.value))
|
|
86
|
+
continue;
|
|
87
|
+
const target = resolveComponent(source.value, join(canonical, '..'), ['.tsx', '.jsx', '.ts', '.js']);
|
|
88
|
+
if (!target || !/\.[jt]sx?$/.test(target))
|
|
89
|
+
continue;
|
|
90
|
+
dependencies.add(realpathSync(target));
|
|
91
|
+
visit(target);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
for (const entry of [...scan.pages.filter((page) => page.island).map((page) => page.componentPath), ...scan.layouts.filter((layout) => layout.island).map((layout) => layout.filePath), ...scan.namedLayouts.filter((layout) => layout.island).map((layout) => layout.filePath)])
|
|
95
|
+
visit(join(pagesDir, entry));
|
|
96
|
+
const validateInteractive = (filename, seen = new Set()) => {
|
|
97
|
+
const canonical = realpathSync(filename);
|
|
98
|
+
if (seen.has(canonical))
|
|
99
|
+
return;
|
|
100
|
+
seen.add(canonical);
|
|
101
|
+
const code = canonical.endsWith('.md') && markdown ? markdown.getGraphSource(canonical) : readFileSync(canonical, 'utf8');
|
|
102
|
+
if (islandImports(code, canonical).length)
|
|
103
|
+
throw new Error('Hono islands: attributed imports inside an interactive component are unsupported; use ordinary component imports there.');
|
|
104
|
+
for (const node of parseSync(canonical, code).program.body) {
|
|
105
|
+
const source = node.type === 'ImportDeclaration' ? node.source : node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration' ? node.source : undefined;
|
|
106
|
+
const typeOnly = node.type === 'ImportDeclaration' ? node.importKind === 'type' || (node.specifiers.length > 0 && node.specifiers.every((entry) => entry.type === 'ImportSpecifier' && entry.importKind === 'type')) : (node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration') && node.exportKind === 'type';
|
|
107
|
+
if (!source || typeOnly || !source.value.startsWith('.'))
|
|
108
|
+
continue;
|
|
109
|
+
const target = resolveComponent(source.value, join(canonical, '..'), ['.tsx', '.jsx', '.ts', '.js']);
|
|
110
|
+
if (target && /\.[jt]sx?$/.test(target))
|
|
111
|
+
validateInteractive(target, seen);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
for (const target of islands.values())
|
|
115
|
+
validateInteractive(target);
|
|
116
|
+
for (const page of scan.pages.filter((page) => !page.island)) {
|
|
117
|
+
for (const file of [page.componentPath, ...resolveEffectiveLayoutChain(page, scan.layouts, scan.namedLayouts).map((layout) => layout.filePath)])
|
|
118
|
+
validateInteractive(join(pagesDir, file));
|
|
119
|
+
}
|
|
120
|
+
const needsJS = new Set(directlyInteractive);
|
|
121
|
+
let changed = true;
|
|
122
|
+
while (changed) {
|
|
123
|
+
changed = false;
|
|
124
|
+
for (const [file, dependencies] of graph)
|
|
125
|
+
if (!needsJS.has(file) && [...dependencies].some((target) => needsJS.has(target))) {
|
|
126
|
+
needsJS.add(file);
|
|
127
|
+
changed = true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
40
130
|
for (const page of scan.pages.filter((page) => page.island)) {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
131
|
+
const files = [page.componentPath, ...resolveEffectiveLayoutChain(page, scan.layouts, scan.namedLayouts).map((layout) => layout.filePath)];
|
|
132
|
+
const hasIslands = files.some(file => needsJS.has(realpathSync(join(pagesDir, file))));
|
|
133
|
+
const hasScript = markdown?.getClientScripts().has(page.componentId);
|
|
134
|
+
if (hasIslands || hasScript)
|
|
135
|
+
interactivePages.add(page.componentId);
|
|
136
|
+
if (hasScript && !hasIslands)
|
|
137
|
+
scriptOnlyPages.add(page.componentId);
|
|
44
138
|
}
|
|
45
139
|
}
|
|
46
140
|
return {
|
|
47
141
|
name: 'void-hono:islands',
|
|
48
142
|
enforce: 'pre',
|
|
143
|
+
configResolved(config) { markdown = markdownApi(config.plugins); },
|
|
49
144
|
buildStart: rescan,
|
|
50
|
-
async hotUpdate({ file }) {
|
|
51
|
-
|
|
52
|
-
|
|
145
|
+
async hotUpdate({ file, modules, timestamp }) {
|
|
146
|
+
const inPages = normalizePath(file).startsWith(normalizePath(pagesDir) + '/');
|
|
147
|
+
if (!inPages && ![...pageFiles, ...islands.values()].some((path) => normalizePath(path) === normalizePath(file)))
|
|
148
|
+
return;
|
|
149
|
+
await rescan();
|
|
150
|
+
const invalidated = new Set();
|
|
151
|
+
for (const id of [ISLANDS_RENDERER_ID, ISLANDS_CLIENT_ENTRY_ID, 'virtual:void-pages-renderer']) {
|
|
152
|
+
const module = this.environment.moduleGraph.getModuleById(`\0${id}`);
|
|
153
|
+
if (module)
|
|
154
|
+
this.environment.moduleGraph.invalidateModule(module, invalidated, timestamp, true);
|
|
155
|
+
}
|
|
156
|
+
if (!inPages && this.environment.config.consumer === 'client')
|
|
157
|
+
this.environment.hot.send({ type: 'full-reload' });
|
|
53
158
|
},
|
|
54
159
|
config() {
|
|
55
160
|
return { build: { rollupOptions: { input: { 'islands-client': ISLANDS_CLIENT_ENTRY_ID } } } };
|
|
@@ -63,47 +168,74 @@ export function honoIslands() {
|
|
|
63
168
|
if (!/\.[jt]sx$/.test(filename) || filename.includes('/node_modules/'))
|
|
64
169
|
return;
|
|
65
170
|
const canonical = realpathSync(filename);
|
|
66
|
-
if ((pageFiles.has(canonical) || [...islands.values()].includes(canonical)) && parseSync(filename, code).program.body.some((node) => node.type === 'ImportDeclaration' && node.
|
|
67
|
-
throw new Error('Hono islands:
|
|
171
|
+
if ((pageFiles.has(canonical) || [...islands.values()].includes(canonical)) && parseSync(filename, code).program.body.some((node) => node.type === 'ImportDeclaration' && node.importKind !== 'type' && ['void-plugin-hono-jsx/form', 'void-plugin-hono-jsx/pages'].includes(node.source.value) && node.specifiers.some((entry) => entry.type !== 'ImportSpecifier' ? node.source.value.endsWith('/form') : entry.importKind !== 'type' && ['useForm', 'action', 'setActionRouter'].includes('name' in entry.imported ? entry.imported.name : entry.imported.value)))) {
|
|
172
|
+
throw new Error('Hono islands: useForm/action require full Pages; use named useIslandForm/Form imports for islands.');
|
|
68
173
|
}
|
|
69
174
|
const found = islandImports(code, filename);
|
|
70
175
|
if (!found.length)
|
|
71
176
|
return;
|
|
72
|
-
if (!pageFiles.has(
|
|
73
|
-
throw new Error('Hono islands: attributed imports
|
|
177
|
+
if (!pageFiles.has(canonical))
|
|
178
|
+
throw new Error('Hono islands: attributed imports require a static island page, layout or component graph; use ordinary imports inside interactive components.');
|
|
74
179
|
imports(code, filename);
|
|
180
|
+
// Void requests static modules in the client environment to collect CSS.
|
|
181
|
+
// Actual interactive entry graphs were validated above; strip attributes
|
|
182
|
+
// for that traversal rather than generating server-only marker wrappers.
|
|
183
|
+
if (this.environment?.config.consumer === 'client')
|
|
184
|
+
return editIslandSource(code, found.map((entry) => ({ start: entry.start, end: entry.end, text: `import ${entry.importName} from ${entry.specifierRaw};` })));
|
|
75
185
|
return editIslandSource(code, found.map((entry) => {
|
|
76
|
-
const path =
|
|
186
|
+
const path = resolveComponent(entry.specifier, join(filename, '..'), ['.tsx', '.jsx']);
|
|
77
187
|
if (!path)
|
|
78
188
|
throw new Error(`Hono islands: missing component ${entry.specifier}.`);
|
|
79
189
|
const islandId = normalizePath(relative(root, realpathSync(path)));
|
|
80
190
|
const component = `__honoIslandComponent${entry.start}`;
|
|
81
191
|
return { start: entry.start, end: entry.end, text: `import ${component} from ${entry.specifierRaw};
|
|
82
|
-
const ${entry.importName} = (props) => __honoIslandJsx('div', { 'data-island': ${JSON.stringify(islandId)}, 'data-hydrate':
|
|
192
|
+
const ${entry.importName} = (props) => __honoIslandJsx('div', { 'data-island': ${JSON.stringify(islandId)}, 'data-hydrate': ${JSON.stringify(entry.hydrate)}, 'data-props': JSON.stringify(props), children: __honoIslandJsx(${component}, props) });` };
|
|
83
193
|
}), { prepend: "import { jsx as __honoIslandJsx } from 'hono/jsx/jsx-runtime';\n" });
|
|
84
194
|
},
|
|
85
195
|
load(id) {
|
|
86
196
|
if (id === `\0${ISLANDS_RENDERER_ID}`) {
|
|
87
197
|
if (!scan)
|
|
88
198
|
throw new Error('Hono islands: page discovery has not completed.');
|
|
89
|
-
const interactive = [...computePagesNeedingClientJS(scan, pagesDir, null)];
|
|
199
|
+
const interactive = [...new Set([...computePagesNeedingClientJS(scan, pagesDir, null), ...interactivePages])];
|
|
90
200
|
return `import { renderPage } from 'virtual:void-pages-renderer';
|
|
91
201
|
const interactive = new Set(${JSON.stringify(interactive)});
|
|
202
|
+
const scriptOnly = new Set(${JSON.stringify([...scriptOnlyPages])});
|
|
203
|
+
const markdownScripts = new Set(${JSON.stringify([...(markdown?.getClientScripts().keys() ?? [])])});
|
|
92
204
|
export function renderIslandPage(page, assets) {
|
|
93
205
|
if (page.deferred?.length || page.deferredKeys) throw new Error('Hono islands: deferred props are unsupported.');
|
|
94
|
-
|
|
206
|
+
if (!markdownScripts.has(page.component)) assets = { ...assets,
|
|
207
|
+
body: assets.body.replace(/<script\\b[^>]*src=["'][^"']*(?:void-hono-markdown-client|markdown-client-)[^"']*["'][^>]*>\\s*<\\/script>/g, ''),
|
|
208
|
+
preloads: assets.preloads.replace(/<link\\b[^>]*href=["'][^"']*markdown-client-[^"']*["'][^>]*>/g, ''),
|
|
209
|
+
};
|
|
210
|
+
if (!interactive.has(page.component)) return renderPage(page, { ...assets, preloads: '', body: '' });
|
|
211
|
+
if (scriptOnly.has(page.component)) {
|
|
212
|
+
// Keep the Markdown entry; its dynamic import loads only this page's code.
|
|
213
|
+
// Framework preloads and the island scheduler have no work on this chain.
|
|
214
|
+
const body = assets.body.replace(/<script\\b[^>]*src=["'][^"']*(?:void-islands-client|islands-client-)[^"']*["'][^>]*>\\s*<\\/script>/g, '');
|
|
215
|
+
return renderPage(page, { ...assets, preloads: '', body });
|
|
216
|
+
}
|
|
217
|
+
return renderPage(page, assets);
|
|
95
218
|
}`;
|
|
96
219
|
}
|
|
97
220
|
if (id === `\0${ISLANDS_CLIENT_ENTRY_ID}`) {
|
|
98
|
-
if (!islands.size)
|
|
221
|
+
if (!islands.size || !interactivePages.size)
|
|
99
222
|
return 'export {};';
|
|
100
223
|
const loaders = [...islands].map(([name, path]) => `${JSON.stringify(name)}: () => import(${JSON.stringify(path)})`).join(',\n');
|
|
101
|
-
return
|
|
224
|
+
return `${markdown ? "import { MarkdownContext } from 'void-plugin-hono-jsx/markdown';" : ''}
|
|
225
|
+
import { initIslands } from 'void/pages-client';
|
|
102
226
|
import { jsx } from 'hono/jsx/dom/jsx-runtime';
|
|
103
227
|
import { render } from 'hono/jsx/dom';
|
|
228
|
+
for (const element of document.querySelectorAll('[data-hydrate^="media:"]')) {
|
|
229
|
+
const query = element.getAttribute('data-hydrate').slice(6).trim();
|
|
230
|
+
if (matchMedia(query).media === 'not all' && query !== 'not all') throw new Error('Hono islands: invalid or unsupported media query ' + query);
|
|
231
|
+
}
|
|
104
232
|
initIslands({ ${loaders} }, (element, module, props) => {
|
|
105
233
|
if (typeof module.default !== 'function') throw new Error('Hono islands: a component must export a default function.');
|
|
106
|
-
|
|
234
|
+
let content = jsx(module.default, props);
|
|
235
|
+
${markdown ? `const metadata = JSON.parse(document.getElementById('app')?.getAttribute('data-hono-markdown') || '{}');
|
|
236
|
+
const page = JSON.parse(document.getElementById('__VOID_PAGE_DATA__')?.textContent || '{}');
|
|
237
|
+
content = jsx(MarkdownContext.Provider, { value: { frontmatter: metadata.frontmatter ?? {}, headings: metadata.headings ?? [], props: page.props ?? {} }, children: content });` : ''}
|
|
238
|
+
render(content, element);
|
|
107
239
|
element.setAttribute('data-hono-island', 'mounted');
|
|
108
240
|
});`;
|
|
109
241
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono-islands.js","sourceRoot":"","sources":["../../src/plugins/hono-islands.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync, realpathSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport { normalizePath, parseSync, type Plugin } from 'vite';\nimport { scanPages, resolvePagesDir, type PageScanResult } from 'void/pages';\nimport { findIslandImports, editIslandSource, resolveSpecifier, computePagesNeedingClientJS, ISLANDS_RENDERER_ID, ISLANDS_CLIENT_ENTRY_ID } from 'void/pages-islands-plugin';\n\n/** Narrow adapter for Void's load-on-page-load island convention. */\nexport function honoIslands(): Plugin {\n const root = process.cwd();\n const pagesDir = resolvePagesDir(root);\n const islands = new Map<string, string>();\n const pageFiles = new Set<string>();\n let scan: PageScanResult | undefined;\n\n function islandImports(code: string, filename: string) {\n const found = findIslandImports(code, filename);\n const attributed = parseSync(filename, code).program.body.filter((node) => node.type === 'ImportDeclaration' && node.attributes?.some(({ key }) => ('name' in key && key.name === 'island') || ('value' in key && key.value === 'island')));\n if (attributed.length !== found.length) throw new Error(`Hono islands: use one default import with exactly one island: 'load' attribute in ${filename}.`);\n return found;\n }\n\n function imports(code: string, filename: string) {\n const found = islandImports(code, filename);\n for (const entry of found) {\n if (entry.hydrate !== 'load') throw new Error(`Hono islands: unsupported policy '${entry.hydrate}' in ${filename}; use island: 'load'.`);\n if (!entry.specifier.startsWith('.')) throw new Error(`Hono islands: use a relative TSX/JSX component import in ${filename}.`);\n const path = resolveSpecifier(entry.specifier, join(filename, '..'), ['.tsx', '.jsx']);\n if (!path || !/\\.[jt]sx$/.test(path)) throw new Error(`Hono islands: cannot resolve TSX/JSX component ${entry.specifier} in ${filename}.`);\n const canonical = realpathSync(path);\n const id = normalizePath(relative(root, canonical));\n islands.set(id, canonical);\n }\n return found;\n }\n\n async function rescan() {\n scan = await scanPages(root, { pagesDir });\n islands.clear();\n pageFiles.clear();\n for (const page of scan.pages.filter((page) => page.island)) {\n const filename = realpathSync(join(pagesDir, page.componentPath));\n pageFiles.add(filename);\n imports(readFileSync(filename, 'utf8'), filename);\n }\n }\n\n return {\n name: 'void-hono:islands',\n enforce: 'pre',\n buildStart: rescan,\n async hotUpdate({ file }) {\n if (normalizePath(file).startsWith(normalizePath(pagesDir) + '/')) await rescan();\n },\n config() {\n return { build: { rollupOptions: { input: { 'islands-client': ISLANDS_CLIENT_ENTRY_ID } } } };\n },\n resolveId(id) {\n if (id === ISLANDS_RENDERER_ID || id === ISLANDS_CLIENT_ENTRY_ID) return `\\0${id}`;\n },\n transform(code, id) {\n const filename = id.split('?')[0];\n if (!/\\.[jt]sx$/.test(filename) || filename.includes('/node_modules/')) return;\n const canonical = realpathSync(filename);\n if ((pageFiles.has(canonical) || [...islands.values()].includes(canonical)) && parseSync(filename, code).program.body.some((node) => node.type === 'ImportDeclaration' && node.source.value === 'void-plugin-hono-jsx/form')) {\n throw new Error('Hono islands: form helpers are unsupported; use a full Pages component.');\n }\n const found = islandImports(code, filename);\n if (!found.length) return;\n if (!pageFiles.has(realpathSync(filename))) throw new Error('Hono islands: attributed imports are supported only directly in .island.tsx/.island.jsx pages.');\n imports(code, filename);\n return editIslandSource(code, found.map((entry) => {\n const path = resolveSpecifier(entry.specifier, join(filename, '..'), ['.tsx', '.jsx']);\n if (!path) throw new Error(`Hono islands: missing component ${entry.specifier}.`);\n const islandId = normalizePath(relative(root, realpathSync(path)));\n const component = `__honoIslandComponent${entry.start}`;\n return { start: entry.start, end: entry.end, text: `import ${component} from ${entry.specifierRaw};\nconst ${entry.importName} = (props) => __honoIslandJsx('div', { 'data-island': ${JSON.stringify(islandId)}, 'data-hydrate': 'load', 'data-props': JSON.stringify(props), children: __honoIslandJsx(${component}, props) });` };\n }), { prepend: \"import { jsx as __honoIslandJsx } from 'hono/jsx/jsx-runtime';\\n\" });\n },\n load(id) {\n if (id === `\\0${ISLANDS_RENDERER_ID}`) {\n if (!scan) throw new Error('Hono islands: page discovery has not completed.');\n const interactive = [...computePagesNeedingClientJS(scan, pagesDir, null)];\n return `import { renderPage } from 'virtual:void-pages-renderer';\nconst interactive = new Set(${JSON.stringify(interactive)});\nexport function renderIslandPage(page, assets) {\n if (page.deferred?.length || page.deferredKeys) throw new Error('Hono islands: deferred props are unsupported.');\n return renderPage(page, interactive.has(page.component) ? assets : { ...assets, preloads: '', body: '' });\n}`;\n }\n if (id === `\\0${ISLANDS_CLIENT_ENTRY_ID}`) {\n if (!islands.size) return 'export {};';\n const loaders = [...islands].map(([name, path]) => `${JSON.stringify(name)}: () => import(${JSON.stringify(path)})`).join(',\\n');\n return `import { initIslands } from 'void/pages-client';\nimport { jsx } from 'hono/jsx/dom/jsx-runtime';\nimport { render } from 'hono/jsx/dom';\ninitIslands({ ${loaders} }, (element, module, props) => {\n if (typeof module.default !== 'function') throw new Error('Hono islands: a component must export a default function.');\n render(jsx(module.default, props), element);\n element.setAttribute('data-hono-island', 'mounted');\n});`;\n }\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"hono-islands.js","sourceRoot":"","sources":["../../src/plugins/hono-islands.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync, realpathSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport { normalizePath, parseSync, type Plugin } from 'vite';\nimport { scanPages, resolveEffectiveLayoutChain, resolvePagesDir, type PageScanResult } from 'void/pages';\nimport { findIslandImports, editIslandSource, computePagesNeedingClientJS, ISLANDS_RENDERER_ID, ISLANDS_CLIENT_ENTRY_ID } from 'void/pages-islands-plugin';\n\nimport { resolveComponent } from './resolve-component.ts';\n\nimport { markdownApi, markdownDiagnostic, type HonoMarkdownApi } from './markdown-api.ts';\n\n/** Hono mounting through Void's shared island scheduler. */\nexport function honoIslands(): Plugin {\n const root = process.cwd();\n const pagesDir = resolvePagesDir(root);\n const islands = new Map<string, string>();\n const pageFiles = new Set<string>();\n const graph = new Map<string, Set<string>>();\n const directlyInteractive = new Set<string>();\n const interactivePages = new Set<string>();\n const scriptOnlyPages = new Set<string>();\n let scan: PageScanResult | undefined;\n let markdown: HonoMarkdownApi | undefined;\n\n function islandImports(code: string, filename: string) {\n const found = findIslandImports(code, filename);\n const attributed = parseSync(filename, code).program.body.filter((node) => node.type === 'ImportDeclaration' && node.attributes?.some(({ key }) => ('name' in key && key.name === 'island') || ('value' in key && key.value === 'island')));\n if (attributed.length !== found.length) throw new Error(`Hono islands: use one default import with exactly one island attribute using load, visible, idle or media:query in ${filename}.`);\n return found;\n }\n\n function imports(code: string, filename: string) {\n const found = islandImports(code, filename);\n for (const entry of found) {\n if (!['load', 'visible', 'idle'].includes(entry.hydrate)) {\n const query = entry.hydrate.startsWith('media:') ? entry.hydrate.slice(6).trim() : '';\n let balance = 0;\n for (const character of query) { if (character === '(') balance++; if (character === ')') balance--; if (balance < 0) break; }\n if (!query || balance !== 0 || /[{};]/.test(query)) throw new Error(`Hono islands: unsupported policy or invalid media query '${entry.hydrate}' in ${filename}.`);\n }\n if (!entry.specifier.startsWith('.')) throw new Error(`Hono islands: use a relative TSX/JSX component import in ${filename}.`);\n const path = resolveComponent(entry.specifier, join(filename, '..'), ['.tsx', '.jsx']);\n if (!path || !/\\.[jt]sx$/.test(path)) throw new Error(`Hono islands: cannot resolve TSX/JSX component ${entry.specifier} in ${filename}.`);\n const canonical = realpathSync(path);\n const id = normalizePath(relative(root, canonical));\n islands.set(id, canonical);\n }\n return found;\n }\n\n async function rescan() {\n scan = await scanPages(root, { pagesDir });\n // Never pass Markdown text to the JavaScript static-graph parser.\n for (const file of [...scan.pages.map(page => page.componentPath), ...scan.layouts.map(layout => layout.filePath), ...scan.namedLayouts.map(layout => layout.filePath)]) {\n if (/\\.md$/.test(file) && !markdown) throw new Error(`${markdownDiagnostic} ${file}`);\n }\n islands.clear(); pageFiles.clear(); graph.clear(); directlyInteractive.clear(); interactivePages.clear(); scriptOnlyPages.clear();\n const visit = (filename: string) => {\n const canonical = realpathSync(filename);\n if (graph.has(canonical)) return;\n const dependencies = new Set<string>(); graph.set(canonical, dependencies); pageFiles.add(canonical);\n const code = canonical.endsWith('.md') && markdown ? markdown.getGraphSource(canonical) : readFileSync(canonical, 'utf8');\n const found = imports(code, canonical);\n if (found.length) {\n if (!/\\.[jt]sx$/.test(canonical) && !(canonical.endsWith('.md') && markdown)) throw new Error('Hono islands: attributed imports require a static TSX/JSX module.');\n directlyInteractive.add(canonical);\n }\n for (const node of parseSync(canonical, code).program.body) {\n const source = node.type === 'ImportDeclaration' ? node.source : node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration' ? node.source : undefined;\n const typeOnly = node.type === 'ImportDeclaration' ? node.importKind === 'type' || (node.specifiers.length > 0 && node.specifiers.every((entry) => entry.type === 'ImportSpecifier' && entry.importKind === 'type')) : (node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration') && node.exportKind === 'type';\n if (!source || typeOnly || !source.value.startsWith('.') || found.some((entry) => entry.specifier === source.value)) continue;\n const target = resolveComponent(source.value, join(canonical, '..'), ['.tsx', '.jsx', '.ts', '.js']);\n if (!target || !/\\.[jt]sx?$/.test(target)) continue;\n dependencies.add(realpathSync(target)); visit(target);\n }\n\n };\n for (const entry of [...scan.pages.filter((page) => page.island).map((page) => page.componentPath), ...scan.layouts.filter((layout) => layout.island).map((layout) => layout.filePath), ...scan.namedLayouts.filter((layout) => layout.island).map((layout) => layout.filePath)]) visit(join(pagesDir, entry));\n const validateInteractive = (filename: string, seen = new Set<string>()) => {\n const canonical = realpathSync(filename);\n if (seen.has(canonical)) return; seen.add(canonical);\n const code = canonical.endsWith('.md') && markdown ? markdown.getGraphSource(canonical) : readFileSync(canonical, 'utf8');\n if (islandImports(code, canonical).length) throw new Error('Hono islands: attributed imports inside an interactive component are unsupported; use ordinary component imports there.');\n for (const node of parseSync(canonical, code).program.body) {\n const source = node.type === 'ImportDeclaration' ? node.source : node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration' ? node.source : undefined;\n const typeOnly = node.type === 'ImportDeclaration' ? node.importKind === 'type' || (node.specifiers.length > 0 && node.specifiers.every((entry) => entry.type === 'ImportSpecifier' && entry.importKind === 'type')) : (node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration') && node.exportKind === 'type';\n if (!source || typeOnly || !source.value.startsWith('.')) continue;\n const target = resolveComponent(source.value, join(canonical, '..'), ['.tsx', '.jsx', '.ts', '.js']);\n if (target && /\\.[jt]sx?$/.test(target)) validateInteractive(target, seen);\n }\n };\n for (const target of islands.values()) validateInteractive(target);\n for (const page of scan.pages.filter((page) => !page.island)) {\n for (const file of [page.componentPath, ...resolveEffectiveLayoutChain(page, scan.layouts, scan.namedLayouts).map((layout) => layout.filePath)]) validateInteractive(join(pagesDir, file));\n }\n const needsJS = new Set(directlyInteractive);\n let changed = true;\n while (changed) {\n changed = false;\n for (const [file, dependencies] of graph) if (!needsJS.has(file) && [...dependencies].some((target) => needsJS.has(target))) { needsJS.add(file); changed = true; }\n }\n for (const page of scan.pages.filter((page) => page.island)) {\n const files = [page.componentPath, ...resolveEffectiveLayoutChain(page, scan.layouts, scan.namedLayouts).map((layout) => layout.filePath)];\n const hasIslands = files.some(file => needsJS.has(realpathSync(join(pagesDir, file))));\n const hasScript = markdown?.getClientScripts().has(page.componentId);\n if (hasIslands || hasScript) interactivePages.add(page.componentId);\n if (hasScript && !hasIslands) scriptOnlyPages.add(page.componentId);\n }\n }\n\n return {\n name: 'void-hono:islands',\n enforce: 'pre',\n configResolved(config) { markdown = markdownApi(config.plugins); },\n buildStart: rescan,\n async hotUpdate({ file, modules, timestamp }) {\n const inPages = normalizePath(file).startsWith(normalizePath(pagesDir) + '/');\n if (!inPages && ![...pageFiles, ...islands.values()].some((path) => normalizePath(path) === normalizePath(file))) return;\n await rescan();\n const invalidated = new Set<(typeof modules)[number]>();\n for (const id of [ISLANDS_RENDERER_ID, ISLANDS_CLIENT_ENTRY_ID, 'virtual:void-pages-renderer']) {\n const module = this.environment.moduleGraph.getModuleById(`\\0${id}`);\n if (module) this.environment.moduleGraph.invalidateModule(module, invalidated, timestamp, true);\n }\n if (!inPages && this.environment.config.consumer === 'client') this.environment.hot.send({ type: 'full-reload' });\n },\n config() {\n return { build: { rollupOptions: { input: { 'islands-client': ISLANDS_CLIENT_ENTRY_ID } } } };\n },\n resolveId(id) {\n if (id === ISLANDS_RENDERER_ID || id === ISLANDS_CLIENT_ENTRY_ID) return `\\0${id}`;\n },\n transform(code, id) {\n const filename = id.split('?')[0];\n if (!/\\.[jt]sx$/.test(filename) || filename.includes('/node_modules/')) return;\n const canonical = realpathSync(filename);\n if ((pageFiles.has(canonical) || [...islands.values()].includes(canonical)) && parseSync(filename, code).program.body.some((node) => node.type === 'ImportDeclaration' && node.importKind !== 'type' && ['void-plugin-hono-jsx/form', 'void-plugin-hono-jsx/pages'].includes(node.source.value) && node.specifiers.some((entry) => entry.type !== 'ImportSpecifier' ? node.source.value.endsWith('/form') : entry.importKind !== 'type' && ['useForm', 'action', 'setActionRouter'].includes('name' in entry.imported ? entry.imported.name : entry.imported.value)))) {\n throw new Error('Hono islands: useForm/action require full Pages; use named useIslandForm/Form imports for islands.');\n }\n const found = islandImports(code, filename);\n if (!found.length) return;\n if (!pageFiles.has(canonical)) throw new Error('Hono islands: attributed imports require a static island page, layout or component graph; use ordinary imports inside interactive components.');\n imports(code, filename);\n // Void requests static modules in the client environment to collect CSS.\n // Actual interactive entry graphs were validated above; strip attributes\n // for that traversal rather than generating server-only marker wrappers.\n if (this.environment?.config.consumer === 'client') return editIslandSource(code, found.map((entry) => ({ start: entry.start, end: entry.end, text: `import ${entry.importName} from ${entry.specifierRaw};` })));\n return editIslandSource(code, found.map((entry) => {\n const path = resolveComponent(entry.specifier, join(filename, '..'), ['.tsx', '.jsx']);\n if (!path) throw new Error(`Hono islands: missing component ${entry.specifier}.`);\n const islandId = normalizePath(relative(root, realpathSync(path)));\n const component = `__honoIslandComponent${entry.start}`;\n return { start: entry.start, end: entry.end, text: `import ${component} from ${entry.specifierRaw};\nconst ${entry.importName} = (props) => __honoIslandJsx('div', { 'data-island': ${JSON.stringify(islandId)}, 'data-hydrate': ${JSON.stringify(entry.hydrate)}, 'data-props': JSON.stringify(props), children: __honoIslandJsx(${component}, props) });` };\n }), { prepend: \"import { jsx as __honoIslandJsx } from 'hono/jsx/jsx-runtime';\\n\" });\n },\n load(id) {\n if (id === `\\0${ISLANDS_RENDERER_ID}`) {\n if (!scan) throw new Error('Hono islands: page discovery has not completed.');\n const interactive = [...new Set([...computePagesNeedingClientJS(scan, pagesDir, null), ...interactivePages])];\n return `import { renderPage } from 'virtual:void-pages-renderer';\nconst interactive = new Set(${JSON.stringify(interactive)});\nconst scriptOnly = new Set(${JSON.stringify([...scriptOnlyPages])});\nconst markdownScripts = new Set(${JSON.stringify([...(markdown?.getClientScripts().keys() ?? [])])});\nexport function renderIslandPage(page, assets) {\n if (page.deferred?.length || page.deferredKeys) throw new Error('Hono islands: deferred props are unsupported.');\n if (!markdownScripts.has(page.component)) assets = { ...assets,\n body: assets.body.replace(/<script\\\\b[^>]*src=[\"'][^\"']*(?:void-hono-markdown-client|markdown-client-)[^\"']*[\"'][^>]*>\\\\s*<\\\\/script>/g, ''),\n preloads: assets.preloads.replace(/<link\\\\b[^>]*href=[\"'][^\"']*markdown-client-[^\"']*[\"'][^>]*>/g, ''),\n };\n if (!interactive.has(page.component)) return renderPage(page, { ...assets, preloads: '', body: '' });\n if (scriptOnly.has(page.component)) {\n // Keep the Markdown entry; its dynamic import loads only this page's code.\n // Framework preloads and the island scheduler have no work on this chain.\n const body = assets.body.replace(/<script\\\\b[^>]*src=[\"'][^\"']*(?:void-islands-client|islands-client-)[^\"']*[\"'][^>]*>\\\\s*<\\\\/script>/g, '');\n return renderPage(page, { ...assets, preloads: '', body });\n }\n return renderPage(page, assets);\n}`;\n }\n if (id === `\\0${ISLANDS_CLIENT_ENTRY_ID}`) {\n if (!islands.size || !interactivePages.size) return 'export {};';\n const loaders = [...islands].map(([name, path]) => `${JSON.stringify(name)}: () => import(${JSON.stringify(path)})`).join(',\\n');\n return `${markdown ? \"import { MarkdownContext } from 'void-plugin-hono-jsx/markdown';\" : ''}\nimport { initIslands } from 'void/pages-client';\nimport { jsx } from 'hono/jsx/dom/jsx-runtime';\nimport { render } from 'hono/jsx/dom';\nfor (const element of document.querySelectorAll('[data-hydrate^=\"media:\"]')) {\n const query = element.getAttribute('data-hydrate').slice(6).trim();\n if (matchMedia(query).media === 'not all' && query !== 'not all') throw new Error('Hono islands: invalid or unsupported media query ' + query);\n}\ninitIslands({ ${loaders} }, (element, module, props) => {\n if (typeof module.default !== 'function') throw new Error('Hono islands: a component must export a default function.');\n let content = jsx(module.default, props);\n ${markdown ? `const metadata = JSON.parse(document.getElementById('app')?.getAttribute('data-hono-markdown') || '{}');\n const page = JSON.parse(document.getElementById('__VOID_PAGE_DATA__')?.textContent || '{}');\n content = jsx(MarkdownContext.Provider, { value: { frontmatter: metadata.frontmatter ?? {}, headings: metadata.headings ?? [], props: page.props ?? {} }, children: content });` : ''}\n render(content, element);\n element.setAttribute('data-hono-island', 'mounted');\n});`;\n }\n },\n };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono-markdown.d.ts","sourceRoot":"","sources":["../../src/plugins/hono-markdown.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAUlD,kFAAkF;AAClF,wBAAgB,gBAAgB,IAAI,MAAM,CAgEzC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, existsSync, realpathSync } from 'node:fs';
|
|
2
|
+
import { join, relative, dirname } from 'node:path';
|
|
3
|
+
import { normalizePath } from 'vite';
|
|
4
|
+
import { resolvePagesDir } from 'void/pages';
|
|
5
|
+
import { resolveComponent } from './resolve-component.js';
|
|
6
|
+
import { compileMarkdown, extractMarkdown, renderChunks } from './markdown-compiler.js';
|
|
7
|
+
const clientId = 'virtual:void-hono-markdown-client';
|
|
8
|
+
const scriptQuery = '?hono-markdown-script';
|
|
9
|
+
const literal = (value) => `JSON.parse(${JSON.stringify(JSON.stringify(value))})`;
|
|
10
|
+
/** Optional authored Markdown compiler; independent of @void/md private files. */
|
|
11
|
+
export function voidHonoMarkdown() {
|
|
12
|
+
let root = process.cwd();
|
|
13
|
+
let pagesDir = resolvePagesDir(root);
|
|
14
|
+
const scripts = new Map();
|
|
15
|
+
const files = new Map();
|
|
16
|
+
function rescan() {
|
|
17
|
+
scripts.clear();
|
|
18
|
+
files.clear();
|
|
19
|
+
if (!existsSync(pagesDir))
|
|
20
|
+
return;
|
|
21
|
+
for (const entry of readdirSync(pagesDir, { recursive: true, withFileTypes: true })) {
|
|
22
|
+
if (!entry.isFile() || !entry.name.endsWith('.md'))
|
|
23
|
+
continue;
|
|
24
|
+
const file = join(entry.parentPath, entry.name);
|
|
25
|
+
const id = normalizePath(relative(pagesDir, file)).replace(/\.md$/, '').replace(/\.island$/, '').replace(/\/index$/, '').replace(/^index$/, '') || 'index';
|
|
26
|
+
const { clientCode } = extractMarkdown(readFileSync(file, 'utf8'));
|
|
27
|
+
if (clientCode) {
|
|
28
|
+
scripts.set(id, clientCode);
|
|
29
|
+
files.set(id, file);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const api = { adapter: 'hono', clientEntryId: clientId, getClientScripts() { rescan(); return scripts; }, getGraphSource(file) { return extractMarkdown(readFileSync(file, 'utf8')).graphSource; } };
|
|
34
|
+
return {
|
|
35
|
+
// Void's Markdown entry/asset convention is shared with @void/md.
|
|
36
|
+
name: 'void-md', enforce: 'pre', api,
|
|
37
|
+
config() { return { build: { rollupOptions: { input: { 'markdown-client': clientId } } } }; },
|
|
38
|
+
configResolved(config) { if (!config.plugins.some(plugin => plugin.name === 'void-hono:pages'))
|
|
39
|
+
throw new Error('Hono Markdown: configure voidHonoJsx() before voidHonoMarkdown().'); root = realpathSync(config.root); pagesDir = resolvePagesDir(root); rescan(); },
|
|
40
|
+
buildStart() { rescan(); },
|
|
41
|
+
hotUpdate({ file, timestamp }) {
|
|
42
|
+
if (!file.endsWith('.md') || !normalizePath(file).startsWith(normalizePath(pagesDir) + '/'))
|
|
43
|
+
return;
|
|
44
|
+
rescan();
|
|
45
|
+
const entry = this.environment.moduleGraph.getModuleById(`\0${clientId}`);
|
|
46
|
+
if (entry)
|
|
47
|
+
this.environment.moduleGraph.invalidateModule(entry, new Set(), timestamp, true);
|
|
48
|
+
},
|
|
49
|
+
resolveId(id) { if (id === clientId)
|
|
50
|
+
return `\0${id}`; },
|
|
51
|
+
load(id) {
|
|
52
|
+
if (id.endsWith(scriptQuery))
|
|
53
|
+
return extractMarkdown(readFileSync(id.slice(0, -scriptQuery.length), 'utf8')).clientCode || 'export {};';
|
|
54
|
+
if (id !== `\0${clientId}`)
|
|
55
|
+
return;
|
|
56
|
+
rescan();
|
|
57
|
+
if (!files.size)
|
|
58
|
+
return 'export {};';
|
|
59
|
+
return `const scripts = {${[...files].map(([id, file]) => `${JSON.stringify(id)}: () => import(${JSON.stringify(normalizePath(file) + scriptQuery)})`).join(',')}};
|
|
60
|
+
const id = document.getElementById('app')?.getAttribute('data-md-script');
|
|
61
|
+
if (id && scripts[id]) await scripts[id]();`;
|
|
62
|
+
},
|
|
63
|
+
transform: {
|
|
64
|
+
filter: { id: /\.md$/ },
|
|
65
|
+
handler(source, id) {
|
|
66
|
+
const result = compileMarkdown(source);
|
|
67
|
+
const imports = result.imports.map((entry, index) => {
|
|
68
|
+
const target = resolveComponent(entry.specifier, dirname(id), ['.tsx', '.jsx']);
|
|
69
|
+
if (!target || !entry.specifier.startsWith('.'))
|
|
70
|
+
throw new Error(`Hono Markdown: cannot resolve relative TSX/JSX island ${entry.specifier} in ${id}.`);
|
|
71
|
+
return { ...entry, alias: `__island${index}`, islandId: normalizePath(relative(root, realpathSync(target))) };
|
|
72
|
+
});
|
|
73
|
+
const chunks = renderChunks(result.html, new Set(imports.map(entry => entry.importName)));
|
|
74
|
+
const children = chunks.map(chunk => {
|
|
75
|
+
if ('html' in chunk)
|
|
76
|
+
return `raw(${JSON.stringify(chunk.html)})`;
|
|
77
|
+
const entry = imports.find(entry => entry.importName === chunk.name);
|
|
78
|
+
const props = literal(chunk.props);
|
|
79
|
+
return `jsx('div', { 'data-island': ${JSON.stringify(entry.islandId)}, 'data-hydrate': ${JSON.stringify(entry.hydrate)}, 'data-props': JSON.stringify(${props}), children: jsx(${entry.alias}, ${props}) })`;
|
|
80
|
+
});
|
|
81
|
+
return { code: `import { jsx, Fragment } from 'hono/jsx/jsx-runtime';
|
|
82
|
+
import { raw } from 'hono/html';
|
|
83
|
+
${imports.map(entry => `import ${entry.alias} from ${JSON.stringify(entry.specifier)};`).join('\n')}
|
|
84
|
+
export const frontmatter = ${literal(result.frontmatter)};
|
|
85
|
+
export const headings = ${literal(result.headings)};
|
|
86
|
+
export default function MarkdownPage() { return jsx(Fragment, { children: [${children.join(',')}] }); }`, map: null };
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=hono-markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono-markdown.js","sourceRoot":"","sources":["../../src/plugins/hono-markdown.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync, readdirSync, existsSync, realpathSync } from 'node:fs';\nimport { join, relative, dirname } from 'node:path';\nimport { normalizePath, type Plugin } from 'vite';\nimport { resolvePagesDir } from 'void/pages';\nimport { resolveComponent } from './resolve-component.ts';\nimport { compileMarkdown, extractMarkdown, renderChunks } from './markdown-compiler.ts';\nimport type { HonoMarkdownApi } from './markdown-api.ts';\n\nconst clientId = 'virtual:void-hono-markdown-client';\nconst scriptQuery = '?hono-markdown-script';\nconst literal = (value: unknown) => `JSON.parse(${JSON.stringify(JSON.stringify(value))})`;\n\n/** Optional authored Markdown compiler; independent of @void/md private files. */\nexport function voidHonoMarkdown(): Plugin {\n let root = process.cwd();\n let pagesDir = resolvePagesDir(root);\n const scripts = new Map<string, string>();\n const files = new Map<string, string>();\n function rescan() {\n scripts.clear(); files.clear();\n if (!existsSync(pagesDir)) return;\n for (const entry of readdirSync(pagesDir, { recursive: true, withFileTypes: true })) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const file = join(entry.parentPath, entry.name);\n const id = normalizePath(relative(pagesDir, file)).replace(/\\.md$/, '').replace(/\\.island$/, '').replace(/\\/index$/, '').replace(/^index$/, '') || 'index';\n const { clientCode } = extractMarkdown(readFileSync(file, 'utf8'));\n if (clientCode) { scripts.set(id, clientCode); files.set(id, file); }\n }\n }\n const api: HonoMarkdownApi = { adapter: 'hono', clientEntryId: clientId, getClientScripts() { rescan(); return scripts; }, getGraphSource(file) { return extractMarkdown(readFileSync(file, 'utf8')).graphSource; } };\n return {\n // Void's Markdown entry/asset convention is shared with @void/md.\n name: 'void-md', enforce: 'pre', api,\n config() { return { build: { rollupOptions: { input: { 'markdown-client': clientId } } } }; },\n configResolved(config) { if (!config.plugins.some(plugin => plugin.name === 'void-hono:pages')) throw new Error('Hono Markdown: configure voidHonoJsx() before voidHonoMarkdown().'); root = realpathSync(config.root); pagesDir = resolvePagesDir(root); rescan(); },\n buildStart() { rescan(); },\n hotUpdate({ file, timestamp }) {\n if (!file.endsWith('.md') || !normalizePath(file).startsWith(normalizePath(pagesDir) + '/')) return;\n rescan();\n const entry = this.environment.moduleGraph.getModuleById(`\\0${clientId}`);\n if (entry) this.environment.moduleGraph.invalidateModule(entry, new Set(), timestamp, true);\n },\n resolveId(id) { if (id === clientId) return `\\0${id}`; },\n load(id) {\n if (id.endsWith(scriptQuery)) return extractMarkdown(readFileSync(id.slice(0, -scriptQuery.length), 'utf8')).clientCode || 'export {};';\n if (id !== `\\0${clientId}`) return;\n rescan();\n if (!files.size) return 'export {};';\n return `const scripts = {${[...files].map(([id, file]) => `${JSON.stringify(id)}: () => import(${JSON.stringify(normalizePath(file) + scriptQuery)})`).join(',')}};\nconst id = document.getElementById('app')?.getAttribute('data-md-script');\nif (id && scripts[id]) await scripts[id]();`;\n },\n transform: {\n filter: { id: /\\.md$/ },\n handler(source, id) {\n const result = compileMarkdown(source);\n const imports = result.imports.map((entry, index) => {\n const target = resolveComponent(entry.specifier, dirname(id), ['.tsx', '.jsx']);\n if (!target || !entry.specifier.startsWith('.')) throw new Error(`Hono Markdown: cannot resolve relative TSX/JSX island ${entry.specifier} in ${id}.`);\n return { ...entry, alias: `__island${index}`, islandId: normalizePath(relative(root, realpathSync(target))) };\n });\n const chunks = renderChunks(result.html, new Set(imports.map(entry => entry.importName)));\n const children = chunks.map(chunk => {\n if ('html' in chunk) return `raw(${JSON.stringify(chunk.html)})`;\n const entry = imports.find(entry => entry.importName === chunk.name)!;\n const props = literal(chunk.props);\n return `jsx('div', { 'data-island': ${JSON.stringify(entry.islandId)}, 'data-hydrate': ${JSON.stringify(entry.hydrate)}, 'data-props': JSON.stringify(${props}), children: jsx(${entry.alias}, ${props}) })`;\n });\n return { code: `import { jsx, Fragment } from 'hono/jsx/jsx-runtime';\nimport { raw } from 'hono/html';\n${imports.map(entry => `import ${entry.alias} from ${JSON.stringify(entry.specifier)};`).join('\\n')}\nexport const frontmatter = ${literal(result.frontmatter)};\nexport const headings = ${literal(result.headings)};\nexport default function MarkdownPage() { return jsx(Fragment, { children: [${children.join(',')}] }); }`, map: null };\n },\n },\n };\n}\n"]}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
|
+
import { type CacheForInput } from 'void/pages-prefetch';
|
|
3
|
+
export type HonoJsxOptions = {
|
|
4
|
+
viewTransitions?: boolean;
|
|
5
|
+
prefetch?: {
|
|
6
|
+
hoverDelay?: number;
|
|
7
|
+
cacheFor?: CacheForInput;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
2
10
|
/** Experimental Pages contract verified against Void 0.26.1 and 0.27.0. */
|
|
3
|
-
export declare function
|
|
11
|
+
export declare function voidHonoJsx(options?: HonoJsxOptions): Plugin[];
|
|
4
12
|
//# sourceMappingURL=hono-pages.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono-pages.d.ts","sourceRoot":"","sources":["../../src/plugins/hono-pages.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"hono-pages.d.ts","sourceRoot":"","sources":["../../src/plugins/hono-pages.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAexE,MAAM,MAAM,cAAc,GAAG;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC;CAC9D,CAAC;AAcF,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,OAAO,GAAE,cAAmB,GAAG,MAAM,EAAE,CA2IlE"}
|