@pajecawav/yamf 0.0.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/LICENSE +21 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/components/Head.d.ts +8 -0
- package/dist/components/Head.d.ts.map +1 -0
- package/dist/components/Head.js +9 -0
- package/dist/components/Head.js.map +1 -0
- package/dist/context/ssr.d.ts +13 -0
- package/dist/context/ssr.d.ts.map +1 -0
- package/dist/context/ssr.js +10 -0
- package/dist/context/ssr.js.map +1 -0
- package/dist/hooks/useEvent.d.ts +8 -0
- package/dist/hooks/useEvent.d.ts.map +1 -0
- package/dist/hooks/useEvent.js +11 -0
- package/dist/hooks/useEvent.js.map +1 -0
- package/dist/hooks/useHead.d.ts +13 -0
- package/dist/hooks/useHead.d.ts.map +1 -0
- package/dist/hooks/useHead.js +15 -0
- package/dist/hooks/useHead.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -0
- package/dist/island/client.js +59 -0
- package/dist/island/client.js.map +1 -0
- package/dist/island/types.d.ts +8 -0
- package/dist/island/types.d.ts.map +1 -0
- package/dist/page.d.ts +24 -0
- package/dist/page.d.ts.map +1 -0
- package/dist/page.js +51 -0
- package/dist/page.js.map +1 -0
- package/dist/server/entry.d.mts +14 -0
- package/dist/server/entry.d.mts.map +1 -0
- package/dist/server/entry.mjs +38 -0
- package/dist/server/entry.mjs.map +1 -0
- package/dist/server/index.d.mts +3 -0
- package/dist/server/index.mjs +3 -0
- package/dist/server/island/server.d.mts +23 -0
- package/dist/server/island/server.d.mts.map +1 -0
- package/dist/server/island/server.mjs +22 -0
- package/dist/server/island/server.mjs.map +1 -0
- package/dist/server/island/types.d.mts +5 -0
- package/dist/server/island/types.d.mts.map +1 -0
- package/dist/server/shared/assets.d.mts +14 -0
- package/dist/server/shared/assets.d.mts.map +1 -0
- package/dist/shared/assets.d.ts +17 -0
- package/dist/shared/assets.d.ts.map +1 -0
- package/dist/vite/index.d.mts +11 -0
- package/dist/vite/index.d.mts.map +1 -0
- package/dist/vite/index.mjs +54 -0
- package/dist/vite/index.mjs.map +1 -0
- package/dist/vite/islands.mjs +85 -0
- package/dist/vite/islands.mjs.map +1 -0
- package/dist/vite/shared/utils.mjs +10 -0
- package/dist/vite/shared/utils.mjs.map +1 -0
- package/dist/vite/virtual-assets.mjs +25 -0
- package/dist/vite/virtual-assets.mjs.map +1 -0
- package/dist/vite/virtual-pages.mjs +35 -0
- package/dist/vite/virtual-pages.mjs.map +1 -0
- package/dist/vite/virtual-template.mjs +45 -0
- package/dist/vite/virtual-template.mjs.map +1 -0
- package/package.json +84 -0
- package/src/client/index.ts +1 -0
- package/src/components/Head.tsx +8 -0
- package/src/context/ssr.ts +17 -0
- package/src/hooks/useEvent.tsx +13 -0
- package/src/hooks/useHead.tsx +27 -0
- package/src/index.ts +12 -0
- package/src/island/client.tsx +92 -0
- package/src/island/server.tsx +51 -0
- package/src/island/types.ts +5 -0
- package/src/page.tsx +92 -0
- package/src/server/entry.tsx +80 -0
- package/src/server/index.ts +4 -0
- package/src/shared/assets.ts +10 -0
- package/src/shared/utils.ts +5 -0
- package/src/virtual.d.ts +14 -0
- package/src/vite/index.ts +83 -0
- package/src/vite/islands.ts +229 -0
- package/src/vite/virtual-assets.ts +30 -0
- package/src/vite/virtual-pages.ts +40 -0
- package/src/vite/virtual-template.ts +62 -0
package/src/page.tsx
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { Child, FC, PropsWithChildren } from "hono/jsx";
|
|
2
|
+
import { Fragment } from "hono/jsx";
|
|
3
|
+
import { renderToReadableStream } from "hono/jsx/streaming";
|
|
4
|
+
import { Hono } from "hono/tiny";
|
|
5
|
+
import type { EventHandlerRequest, EventHandlerResponse, H3Event } from "nitro/h3";
|
|
6
|
+
import { HTTPResponse, withServerTiming } from "nitro/h3";
|
|
7
|
+
import type { Unhead } from "unhead/server";
|
|
8
|
+
import { transformHtmlTemplate } from "unhead/server";
|
|
9
|
+
import { createStreamableHead, wrapStream } from "unhead/stream/server";
|
|
10
|
+
import type { ResolvableHead, ResolvableLink } from "unhead/types";
|
|
11
|
+
import { clientAssets } from "virtual:yamf:assets";
|
|
12
|
+
import { template } from "virtual:yamf:template";
|
|
13
|
+
import { SSRContext } from "./context/ssr";
|
|
14
|
+
import type { ImportAssetsResult } from "./shared/assets";
|
|
15
|
+
|
|
16
|
+
export type PageHandler = (
|
|
17
|
+
event: H3Event<EventHandlerRequest>,
|
|
18
|
+
params: {
|
|
19
|
+
serverAssets?: ImportAssetsResult;
|
|
20
|
+
head?: ResolvableHead;
|
|
21
|
+
Layout?: FC<PropsWithChildren>;
|
|
22
|
+
},
|
|
23
|
+
) => EventHandlerResponse;
|
|
24
|
+
|
|
25
|
+
export type PageRenderer = (
|
|
26
|
+
event: H3Event<EventHandlerRequest>,
|
|
27
|
+
params: { head: Unhead },
|
|
28
|
+
) => HTTPResponse | Promise<HTTPResponse> | Child | Promise<Child>;
|
|
29
|
+
|
|
30
|
+
interface DefinePageOptions {
|
|
31
|
+
render: PageRenderer;
|
|
32
|
+
stream?: boolean;
|
|
33
|
+
Layout?: FC<PropsWithChildren>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const definePage = (options: DefinePageOptions): PageHandler => {
|
|
37
|
+
return async (event, { serverAssets, head: headInit, Layout = options.Layout ?? Fragment }) => {
|
|
38
|
+
const assets = serverAssets ? clientAssets.merge(serverAssets) : clientAssets;
|
|
39
|
+
|
|
40
|
+
const { head } = createStreamableHead({ init: [headInit] });
|
|
41
|
+
|
|
42
|
+
head.push({
|
|
43
|
+
link: [
|
|
44
|
+
...assets.js.map((attrs): ResolvableLink => ({ rel: "modulepreload", ...attrs })),
|
|
45
|
+
...assets.css.map((attrs): ResolvableLink => ({ rel: "stylesheet", ...attrs })),
|
|
46
|
+
],
|
|
47
|
+
script: [{ type: "module", src: assets.entry }],
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const content = await options.render(event, { head });
|
|
51
|
+
|
|
52
|
+
if (content instanceof HTTPResponse) {
|
|
53
|
+
return content;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const App = async () => (
|
|
57
|
+
<SSRContext value={{ head, event }}>
|
|
58
|
+
<Layout>{content}</Layout>
|
|
59
|
+
</SSRContext>
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const responseInit = {
|
|
63
|
+
headers: {
|
|
64
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (!options.stream) {
|
|
69
|
+
// TODO: figure out how expensive this is
|
|
70
|
+
return new Hono()
|
|
71
|
+
.get("/", async c =>
|
|
72
|
+
withServerTiming(event, "#render", async () => {
|
|
73
|
+
const response = await c.html(<App />);
|
|
74
|
+
|
|
75
|
+
let html = await response.text();
|
|
76
|
+
|
|
77
|
+
html = transformHtmlTemplate(
|
|
78
|
+
head,
|
|
79
|
+
template.replace("<!--ssr-outlet-->", html ?? ""),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return new Response(html, responseInit);
|
|
83
|
+
}),
|
|
84
|
+
)
|
|
85
|
+
.request("/");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const stream = wrapStream(head, renderToReadableStream(<App />), template);
|
|
89
|
+
|
|
90
|
+
return new Response(stream, responseInit);
|
|
91
|
+
};
|
|
92
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { PageHandler } from "#/page";
|
|
2
|
+
import type { FC, PropsWithChildren } from "hono/jsx";
|
|
3
|
+
import type { EventHandlerRequest, EventHandlerWithFetch, H3Event } from "nitro/h3";
|
|
4
|
+
import { defineHandler, HTTPError, writeEarlyHints } from "nitro/h3";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { addRoute, createRouter, findRoute } from "rou3";
|
|
7
|
+
import { withLeadingSlash, withoutTrailingSlash } from "ufo";
|
|
8
|
+
import type { ResolvableHead } from "unhead/types";
|
|
9
|
+
import { clientAssets } from "virtual:yamf:assets";
|
|
10
|
+
import { pages, assets as serverAssets } from "virtual:yamf:pages";
|
|
11
|
+
|
|
12
|
+
interface Route {
|
|
13
|
+
handler: () => Promise<PageHandler>;
|
|
14
|
+
serverAssets?: ImportAssetsResult;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const router = createRouter<Route>();
|
|
18
|
+
|
|
19
|
+
for (const [relativePath, handler] of Object.entries(pages).toSorted((a, b) =>
|
|
20
|
+
a[0].localeCompare(b[0]),
|
|
21
|
+
)) {
|
|
22
|
+
const ext = path.extname(relativePath);
|
|
23
|
+
|
|
24
|
+
let route = path.relative("/src/pages", relativePath);
|
|
25
|
+
|
|
26
|
+
if (ext.length) {
|
|
27
|
+
route = route.slice(0, -ext.length);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
route =
|
|
31
|
+
route
|
|
32
|
+
.replace(/\.[A-Za-z]+$/, "")
|
|
33
|
+
.replace(/\(([^(/\\]+)\)[/\\]/g, "")
|
|
34
|
+
.replace(/\[\.{3}]/g, "**")
|
|
35
|
+
.replace(/\[\.{3}([^\]]+)]/g, (_, p: string) => "**:" + p.replace(/[^\w-]/g, "_"))
|
|
36
|
+
.replace(/\[([^/\]]+)]/g, (_, p: string) => ":" + p.replace(/[^\w-]/g, "_"))
|
|
37
|
+
.replace(/(\/|^)index$/, "") || "/";
|
|
38
|
+
|
|
39
|
+
route = withLeadingSlash(withoutTrailingSlash(route));
|
|
40
|
+
|
|
41
|
+
addRoute(router, "GET", route, {
|
|
42
|
+
handler,
|
|
43
|
+
serverAssets: serverAssets[relativePath],
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type ServerEntry = EventHandlerWithFetch<EventHandlerRequest, Promise<unknown>>;
|
|
48
|
+
|
|
49
|
+
export interface DefineServerEntryOptions {
|
|
50
|
+
head?: ResolvableHead | ((event: H3Event<EventHandlerRequest>) => ResolvableHead);
|
|
51
|
+
Layout?: FC<PropsWithChildren>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const defineServerEntry = (options?: DefineServerEntryOptions): ServerEntry => {
|
|
55
|
+
return defineHandler(async event => {
|
|
56
|
+
const route = findRoute(router, "GET", event.url.pathname);
|
|
57
|
+
|
|
58
|
+
if (!route) {
|
|
59
|
+
throw new HTTPError("Not found", { status: 404 });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const { handler, serverAssets } = route.data;
|
|
63
|
+
event.context.params = route.params;
|
|
64
|
+
|
|
65
|
+
const assets = serverAssets ? serverAssets.merge(clientAssets) : clientAssets;
|
|
66
|
+
|
|
67
|
+
await writeEarlyHints(event, {
|
|
68
|
+
link: [
|
|
69
|
+
...assets.js.map(script => `<${script.href}>; rel=modulepreload`),
|
|
70
|
+
...assets.css.map(style => `<${style.href}>; rel=preload; as=style`),
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return (await handler())(event, {
|
|
75
|
+
serverAssets,
|
|
76
|
+
head: typeof options?.head === "function" ? options.head(event) : options?.head,
|
|
77
|
+
Layout: options?.Layout,
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// https://github.com/nitrojs/nitro/blob/bfbb207c720ce10ec7ad7887c7a8269d493eec55/lib/vite.types.d.mts
|
|
2
|
+
export type ImportAssetsResult = ImportAssetsResultRaw & {
|
|
3
|
+
merge(...args: ImportAssetsResultRaw[]): ImportAssetsResult;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type ImportAssetsResultRaw = {
|
|
7
|
+
entry?: string;
|
|
8
|
+
js: { href: string }[];
|
|
9
|
+
css: { href: string; "data-vite-dev-id"?: string }[];
|
|
10
|
+
};
|
package/src/virtual.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare module "virtual:yamf:assets" {
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
3
|
+
export const clientAssets: import("./shared/assets").ImportAssetsResult;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
declare module "virtual:yamf:pages" {
|
|
7
|
+
export const pages: Record<string, () => Promise<PageHandler>>;
|
|
8
|
+
|
|
9
|
+
export const assets: Record<string, ImportAssetsResult>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare module "virtual:yamf:template" {
|
|
13
|
+
export const template: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { NitroPluginConfig } from "nitro/vite";
|
|
2
|
+
import { nitro } from "nitro/vite";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import type { EnvironmentOptions, PluginOption } from "vite";
|
|
5
|
+
import { islands } from "./islands";
|
|
6
|
+
import { virtualAssets } from "./virtual-assets";
|
|
7
|
+
import { virtualPages } from "./virtual-pages";
|
|
8
|
+
import { virtualTemplate } from "./virtual-template";
|
|
9
|
+
|
|
10
|
+
export interface YamfOptions {
|
|
11
|
+
nitro?: NitroPluginConfig;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const yamf = (options?: YamfOptions): PluginOption[] => {
|
|
15
|
+
const plugins: PluginOption[] = [];
|
|
16
|
+
|
|
17
|
+
// TODO: extendable config
|
|
18
|
+
plugins.push({
|
|
19
|
+
name: "yamf:config",
|
|
20
|
+
config() {
|
|
21
|
+
const ssrEnv: EnvironmentOptions = {
|
|
22
|
+
build: {
|
|
23
|
+
// TODO: figure out if this should be true
|
|
24
|
+
cssCodeSplit: false,
|
|
25
|
+
rollupOptions: {
|
|
26
|
+
input: "./src/server.tsx",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
optimizeDeps: {
|
|
33
|
+
include: [
|
|
34
|
+
"hono",
|
|
35
|
+
"hono/jsx/dom/client",
|
|
36
|
+
"hono/jsx/jsx-runtime",
|
|
37
|
+
"devalue",
|
|
38
|
+
"ufo",
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
environments: {
|
|
42
|
+
...(existsSync("./src/server.tsx") ? { ssr: ssrEnv } : {}),
|
|
43
|
+
client: {
|
|
44
|
+
build: {
|
|
45
|
+
rolldownOptions: {
|
|
46
|
+
input: "./src/client/index.ts",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
plugins.push(islands());
|
|
56
|
+
|
|
57
|
+
plugins.push(virtualAssets());
|
|
58
|
+
plugins.push(virtualPages());
|
|
59
|
+
plugins.push(virtualTemplate());
|
|
60
|
+
|
|
61
|
+
plugins.push(
|
|
62
|
+
nitro({
|
|
63
|
+
serverDir: "./src",
|
|
64
|
+
renderer: false,
|
|
65
|
+
...options?.nitro,
|
|
66
|
+
compressPublicAssets: {
|
|
67
|
+
gzip: true,
|
|
68
|
+
brotli: true,
|
|
69
|
+
},
|
|
70
|
+
publicAssets: [
|
|
71
|
+
{
|
|
72
|
+
baseURL: "assets",
|
|
73
|
+
dir: "./public/assets",
|
|
74
|
+
maxAge: 365 * 24 * 60 * 60,
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return plugins;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export default yamf;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// reference https://github.com/hi-ogawa/vite-plugin-fullstack/blob/28e9540a68529c58842e9a3bf17d2193a065d524/examples/island/src/framework/island/plugin.ts
|
|
2
|
+
import { generate } from "@babel/generator";
|
|
3
|
+
import { parse } from "@babel/parser";
|
|
4
|
+
import _traverse from "@babel/traverse";
|
|
5
|
+
import {
|
|
6
|
+
callExpression,
|
|
7
|
+
exportDefaultDeclaration,
|
|
8
|
+
exportNamedDeclaration,
|
|
9
|
+
functionDeclaration,
|
|
10
|
+
functionExpression,
|
|
11
|
+
identifier,
|
|
12
|
+
importDeclaration,
|
|
13
|
+
importDefaultSpecifier,
|
|
14
|
+
importNamespaceSpecifier,
|
|
15
|
+
memberExpression,
|
|
16
|
+
stringLiteral,
|
|
17
|
+
variableDeclaration,
|
|
18
|
+
variableDeclarator,
|
|
19
|
+
} from "@babel/types";
|
|
20
|
+
import type { Plugin } from "vite";
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
const traverse = (_traverse.default as typeof _traverse) ?? _traverse;
|
|
25
|
+
|
|
26
|
+
const ISLAND_REGEX = /\.island\.(j|t)sx?$/;
|
|
27
|
+
|
|
28
|
+
export const islands = (): Plugin[] => {
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
name: "yamf:islands",
|
|
32
|
+
// enforce: "pre",
|
|
33
|
+
transform: {
|
|
34
|
+
filter: { id: ISLAND_REGEX },
|
|
35
|
+
handler(code, id) {
|
|
36
|
+
if (this.environment.name !== "ssr") {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const seenExports = new Set<string>();
|
|
41
|
+
|
|
42
|
+
const ast = parse(code, {
|
|
43
|
+
sourceType: "module",
|
|
44
|
+
plugins: ["typescript", "jsx"],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
traverse(ast, {
|
|
48
|
+
Program(path) {
|
|
49
|
+
// prepends server island runtime
|
|
50
|
+
// import * as __runtime from "yamf/server";
|
|
51
|
+
path.unshiftContainer(
|
|
52
|
+
"body",
|
|
53
|
+
importDeclaration(
|
|
54
|
+
[importNamespaceSpecifier(identifier("__runtime"))],
|
|
55
|
+
stringLiteral("@pajecawav/yamf/server"),
|
|
56
|
+
),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// prepends asset imports for the island:
|
|
60
|
+
// import __assets from "MODULE_ID?assets=client";
|
|
61
|
+
path.unshiftContainer(
|
|
62
|
+
"body",
|
|
63
|
+
importDeclaration(
|
|
64
|
+
[importDefaultSpecifier(identifier("__assets"))],
|
|
65
|
+
stringLiteral(`${id}?assets=client`),
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
},
|
|
69
|
+
ExportDefaultDeclaration(path) {
|
|
70
|
+
const declarationType = path.node.declaration.type;
|
|
71
|
+
|
|
72
|
+
if (
|
|
73
|
+
!(
|
|
74
|
+
declarationType === "FunctionDeclaration" ||
|
|
75
|
+
declarationType === "FunctionExpression" ||
|
|
76
|
+
declarationType === "ArrowFunctionExpression"
|
|
77
|
+
)
|
|
78
|
+
) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const originalFunction =
|
|
83
|
+
path.node.declaration.type === "FunctionExpression" ||
|
|
84
|
+
path.node.declaration.type === "ArrowFunctionExpression"
|
|
85
|
+
? path.node.declaration
|
|
86
|
+
: functionExpression(
|
|
87
|
+
null,
|
|
88
|
+
path.node.declaration.params,
|
|
89
|
+
path.node.declaration.body,
|
|
90
|
+
undefined,
|
|
91
|
+
path.node.declaration.async,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const islandIdentifier = identifier("__ISLAND__");
|
|
95
|
+
|
|
96
|
+
path.insertBefore(
|
|
97
|
+
variableDeclaration("const", [
|
|
98
|
+
variableDeclarator(islandIdentifier, originalFunction),
|
|
99
|
+
]),
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
path.replaceWith(
|
|
103
|
+
exportDefaultDeclaration(
|
|
104
|
+
callExpression(
|
|
105
|
+
memberExpression(
|
|
106
|
+
identifier("__runtime"),
|
|
107
|
+
identifier("createIsland"),
|
|
108
|
+
),
|
|
109
|
+
[
|
|
110
|
+
islandIdentifier,
|
|
111
|
+
stringLiteral("default"),
|
|
112
|
+
identifier("__assets"),
|
|
113
|
+
],
|
|
114
|
+
),
|
|
115
|
+
),
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
ExportNamedDeclaration(path) {
|
|
119
|
+
if (path.node.declaration?.type === "VariableDeclaration") {
|
|
120
|
+
const declaration = path.node.declaration.declarations.at(0);
|
|
121
|
+
|
|
122
|
+
if (
|
|
123
|
+
!declaration ||
|
|
124
|
+
declaration.id.type !== "Identifier" ||
|
|
125
|
+
seenExports.has(declaration.id.name)
|
|
126
|
+
) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const exportName = declaration.id.name;
|
|
130
|
+
seenExports.add(exportName);
|
|
131
|
+
|
|
132
|
+
const islandIdentifier = identifier(`__wrap_${exportName}`);
|
|
133
|
+
|
|
134
|
+
path.insertBefore(
|
|
135
|
+
variableDeclaration("const", [
|
|
136
|
+
variableDeclarator(islandIdentifier, declaration.init),
|
|
137
|
+
]),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
path.replaceWith(
|
|
141
|
+
exportNamedDeclaration(
|
|
142
|
+
variableDeclaration("const", [
|
|
143
|
+
variableDeclarator(
|
|
144
|
+
identifier(exportName),
|
|
145
|
+
callExpression(
|
|
146
|
+
memberExpression(
|
|
147
|
+
identifier("__runtime"),
|
|
148
|
+
identifier("createIsland"),
|
|
149
|
+
),
|
|
150
|
+
[
|
|
151
|
+
islandIdentifier,
|
|
152
|
+
stringLiteral(exportName),
|
|
153
|
+
identifier("__assets"),
|
|
154
|
+
],
|
|
155
|
+
),
|
|
156
|
+
),
|
|
157
|
+
]),
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
} else if (path.node.declaration?.type === "FunctionDeclaration") {
|
|
161
|
+
const declaration = path.node.declaration;
|
|
162
|
+
|
|
163
|
+
if (
|
|
164
|
+
!declaration ||
|
|
165
|
+
declaration.id?.type !== "Identifier" ||
|
|
166
|
+
seenExports.has(declaration.id.name)
|
|
167
|
+
) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const exportName = declaration.id.name;
|
|
171
|
+
seenExports.add(exportName);
|
|
172
|
+
|
|
173
|
+
const islandIdentifier = identifier(`__wrap_${exportName}`);
|
|
174
|
+
|
|
175
|
+
path.insertBefore(
|
|
176
|
+
functionDeclaration(
|
|
177
|
+
islandIdentifier,
|
|
178
|
+
declaration.params,
|
|
179
|
+
declaration.body,
|
|
180
|
+
declaration.generator,
|
|
181
|
+
declaration.async,
|
|
182
|
+
),
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
path.replaceWith(
|
|
186
|
+
exportNamedDeclaration(
|
|
187
|
+
variableDeclaration("const", [
|
|
188
|
+
variableDeclarator(
|
|
189
|
+
identifier(exportName),
|
|
190
|
+
callExpression(
|
|
191
|
+
memberExpression(
|
|
192
|
+
identifier("__runtime"),
|
|
193
|
+
identifier("createIsland"),
|
|
194
|
+
),
|
|
195
|
+
[
|
|
196
|
+
islandIdentifier,
|
|
197
|
+
stringLiteral(exportName),
|
|
198
|
+
identifier("__assets"),
|
|
199
|
+
],
|
|
200
|
+
),
|
|
201
|
+
),
|
|
202
|
+
]),
|
|
203
|
+
),
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const result = generate(ast);
|
|
210
|
+
|
|
211
|
+
return { code: result.code, map: result.map };
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: "yamf:islands:raw-import",
|
|
217
|
+
transform: {
|
|
218
|
+
order: "post",
|
|
219
|
+
handler(code) {
|
|
220
|
+
if (code.includes("__island_raw_import__")) {
|
|
221
|
+
return code.replaceAll("__island_raw_import__", "import");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return undefined;
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
];
|
|
229
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import { js } from "../shared/utils";
|
|
3
|
+
|
|
4
|
+
export const virtualAssets = (): Plugin => {
|
|
5
|
+
const virtualModuleId = "virtual:yamf:assets";
|
|
6
|
+
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
name: "yamf:virtual-assets",
|
|
10
|
+
enforce: "pre",
|
|
11
|
+
resolveId(id) {
|
|
12
|
+
if (id === virtualModuleId) {
|
|
13
|
+
return resolvedVirtualModuleId;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return undefined;
|
|
17
|
+
},
|
|
18
|
+
load(id) {
|
|
19
|
+
if (id !== resolvedVirtualModuleId) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return js`
|
|
24
|
+
import clientAssets from "./src/client/index.ts?assets=client";
|
|
25
|
+
|
|
26
|
+
export { clientAssets };
|
|
27
|
+
`.trim();
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import { js } from "../shared/utils";
|
|
3
|
+
|
|
4
|
+
export const virtualPages = (): Plugin => {
|
|
5
|
+
const virtualModuleId = "virtual:yamf:pages";
|
|
6
|
+
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
name: "yamf:virtual-pages",
|
|
10
|
+
enforce: "pre",
|
|
11
|
+
resolveId(id) {
|
|
12
|
+
if (id === virtualModuleId) {
|
|
13
|
+
return resolvedVirtualModuleId;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return undefined;
|
|
17
|
+
},
|
|
18
|
+
load(id) {
|
|
19
|
+
if (id !== resolvedVirtualModuleId) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return js`
|
|
24
|
+
const pages = import.meta.glob("/src/pages/**/*.{js,mjs,cjs,ts,mts,cts,tsx,jsx}", {
|
|
25
|
+
import: "default",
|
|
26
|
+
});
|
|
27
|
+
const assets = import.meta.glob(
|
|
28
|
+
"/src/pages/**/*.{js,mjs,cjs,ts,mts,cts,tsx,jsx}",
|
|
29
|
+
{
|
|
30
|
+
import: "default",
|
|
31
|
+
query: "?assets=ssr",
|
|
32
|
+
eager: true,
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export { pages, assets };
|
|
37
|
+
`.trim();
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { relative } from "node:path";
|
|
3
|
+
import type { Plugin } from "vite";
|
|
4
|
+
import { js } from "../shared/utils";
|
|
5
|
+
|
|
6
|
+
const TEMPLATE_PATH = "./src/template.html";
|
|
7
|
+
|
|
8
|
+
const DEFAULT_TEMPLATE = /* html */ `
|
|
9
|
+
<!DOCTYPE html>
|
|
10
|
+
<html>
|
|
11
|
+
<head></head>
|
|
12
|
+
<body>
|
|
13
|
+
<!--ssr-outlet-->
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
16
|
+
`.trim();
|
|
17
|
+
|
|
18
|
+
export const virtualTemplate = (): Plugin => {
|
|
19
|
+
const virtualModuleId = "virtual:yamf:template";
|
|
20
|
+
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
name: "yamf:virtual-template",
|
|
24
|
+
enforce: "pre",
|
|
25
|
+
resolveId(id) {
|
|
26
|
+
if (id === virtualModuleId) {
|
|
27
|
+
return resolvedVirtualModuleId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return undefined;
|
|
31
|
+
},
|
|
32
|
+
async load(id) {
|
|
33
|
+
if (id !== resolvedVirtualModuleId) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const template = await readFile(TEMPLATE_PATH, "utf8");
|
|
39
|
+
|
|
40
|
+
return js`export const template = ${JSON.stringify(template)};`;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
43
|
+
return js`export const template = ${JSON.stringify(DEFAULT_TEMPLATE)};`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
handleHotUpdate({ file, server }) {
|
|
50
|
+
if (relative(file, TEMPLATE_PATH) === "") {
|
|
51
|
+
const mod = server.moduleGraph.getModuleById(resolvedVirtualModuleId);
|
|
52
|
+
|
|
53
|
+
if (mod) {
|
|
54
|
+
server.moduleGraph.invalidateModule(mod);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// TODO: should reload?
|
|
58
|
+
// server.ws.send({ type: "full-reload" });
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
};
|