@pajecawav/yamf 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/hooks/useHead.d.ts.map +1 -1
- package/dist/island/client.js +2 -2
- package/dist/island/client.js.map +1 -1
- package/dist/island/types.d.ts.map +1 -1
- package/dist/page.d.ts.map +1 -1
- package/dist/page.js +6 -1
- package/dist/page.js.map +1 -1
- package/dist/server/entry.d.mts.map +1 -1
- package/dist/server/entry.mjs +10 -1
- package/dist/server/entry.mjs.map +1 -1
- package/dist/server/island/server.mjs.map +1 -1
- package/dist/vite/index.d.mts.map +1 -1
- package/dist/vite/index.mjs +17 -1
- package/dist/vite/index.mjs.map +1 -1
- package/dist/vite/islands.mjs +38 -59
- package/dist/vite/islands.mjs.map +1 -1
- package/dist/vite/virtual-error-handler.mjs +23 -0
- package/dist/vite/virtual-error-handler.mjs.map +1 -0
- package/dist/vite/virtual-template.mjs +1 -0
- package/dist/vite/virtual-template.mjs.map +1 -1
- package/package.json +20 -26
- package/src/island/client.tsx +4 -4
- package/src/island/types.ts +7 -0
- package/src/page.tsx +4 -1
- package/src/server/entry.tsx +25 -3
- package/src/virtual.d.ts +8 -0
- package/src/vite/index.ts +19 -1
- package/src/vite/islands.ts +70 -175
- package/src/vite/virtual-error-handler.ts +40 -0
- package/src/vite/virtual-template.ts +1 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# yamf
|
|
2
2
|
|
|
3
|
-
SSR meta-framework on top of [Vite](https://vite.dev), [Nitro](https://nitro.build/), and [Hono JSX](https://hono.dev/docs/guides/jsx). File-based routing for HTML pages, islands architecture for client interactivity, head/SEO via [unhead](https://unhead.unjs.io/), and full Nitro feature set (presets, caching, middleware, API routes).
|
|
3
|
+
SSR meta-framework on top of [Vite](https://vite.dev), [Nitro](https://nitro.build/), and [Hono JSX](https://hono.dev/docs/guides/jsx). File-based routing for HTML pages, islands architecture for client interactivity, client-side routing via [wouter](https://github.com/molefrog/wouter), React-ecosystem compatibility through [@hono/react-compat](https://github.com/honojs/react-compat), head/SEO via [unhead](https://unhead.unjs.io/), and full Nitro feature set (presets, caching, middleware, API routes).
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -149,6 +149,37 @@ export default definePage({
|
|
|
149
149
|
|
|
150
150
|
Props are serialized with `devalue` (supports `Date`, `Map`, `Set`, `URL`, `RegExp`, `Error`, `BigInt`, cycles).
|
|
151
151
|
|
|
152
|
+
### React ecosystem compatibility
|
|
153
|
+
|
|
154
|
+
The Vite plugin aliases `react` and `react-dom` to [`@hono/react-compat`](https://github.com/honojs/react-compat), which reimplements the React API on top of `hono/jsx`. This means libraries from the React ecosystem (wouter, tanstack/react-query, etc.) work inside islands and the render tree without shipping React. `use-sync-external-store` is also aliased to `@hono/react-compat`.
|
|
155
|
+
|
|
156
|
+
## Routing
|
|
157
|
+
|
|
158
|
+
Every page renders inside a [wouter](https://github.com/molefrog/wouter) `<Router>`, seeded with the current request's `pathname` and `search` for SSR. After hydration, navigation is client-side — wouter hooks and components work inside islands and the root layout:
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
// src/components/Search.island.tsx
|
|
162
|
+
import { useSearchParams } from "wouter";
|
|
163
|
+
|
|
164
|
+
export const Search = () => {
|
|
165
|
+
const [params, setParams] = useSearchParams();
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<input
|
|
169
|
+
value={params.get("q") ?? ""}
|
|
170
|
+
onChange={e =>
|
|
171
|
+
setParams(prev => {
|
|
172
|
+
prev.set("q", e.target.value);
|
|
173
|
+
return prev;
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
/>
|
|
177
|
+
);
|
|
178
|
+
};
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
wouter's `Link`, `Route`, `useLocation`, `useRoute`, and `useSearchParams` are all available. Note that yamf's file-based routing (see [File routing](#file-routing)) handles full-page SSR routes, while wouter handles client-side navigation and query-param state within a page.
|
|
182
|
+
|
|
152
183
|
## Client entry
|
|
153
184
|
|
|
154
185
|
```ts
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHead.d.ts","names":[],"sources":["../../src/hooks/useHead.tsx"],"mappings":"
|
|
1
|
+
{"version":3,"file":"useHead.d.ts","names":[],"sources":["../../src/hooks/useHead.tsx"],"mappings":";;;QAMe;YACJ;IACT,aAAa;;;cAQF,UAAW,QAAQ;cAYnB,aAAc,QAAQ"}
|
package/dist/island/client.js
CHANGED
|
@@ -36,7 +36,7 @@ customElements.define("yamf-island", class extends HTMLElement {
|
|
|
36
36
|
__island_raw_import__(src).then(hydrateIsland);
|
|
37
37
|
};
|
|
38
38
|
switch (islandClient) {
|
|
39
|
-
case true:
|
|
39
|
+
case "true":
|
|
40
40
|
case "load":
|
|
41
41
|
initIsland();
|
|
42
42
|
break;
|
|
@@ -47,7 +47,7 @@ customElements.define("yamf-island", class extends HTMLElement {
|
|
|
47
47
|
if (this.firstElementChild) observe(this.firstElementChild, initIsland);
|
|
48
48
|
else initIsland();
|
|
49
49
|
break;
|
|
50
|
-
case false:
|
|
50
|
+
case "false":
|
|
51
51
|
case "skip": break;
|
|
52
52
|
default: throw new Error(`Invalid island-client value: ${islandClient}`);
|
|
53
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":[],"sources":["../../src/island/client.tsx"],"sourcesContent":["import { parse } from \"devalue\";\nimport type { FC } from \"hono/jsx\";\nimport { hydrateRoot } from \"hono/jsx/dom/client\";\nimport { withLeadingSlash } from \"ufo\";\nimport type {
|
|
1
|
+
{"version":3,"file":"client.js","names":[],"sources":["../../src/island/client.tsx"],"sourcesContent":["import { parse } from \"devalue\";\nimport type { FC } from \"hono/jsx\";\nimport { hydrateRoot } from \"hono/jsx/dom/client\";\nimport { withLeadingSlash } from \"ufo\";\nimport type { IslandClientDirectiveSerialized } from \"./types\";\n\ndeclare let __island_raw_import__: <T>(file: string) => Promise<T>;\n\nconst listeners = new WeakMap<Element, VoidFunction>();\n\nconst observer = new IntersectionObserver(entries => {\n\tfor (const entry of entries) {\n\t\tif (entry.isIntersecting) {\n\t\t\tlisteners.get(entry.target)?.();\n\t\t\tunobserve(entry.target);\n\t\t}\n\t}\n});\n\nconst observe = (target: Element, cb: VoidFunction) => {\n\tobserver.observe(target);\n\tlisteners.set(target, cb);\n};\n\nconst unobserve = (target: Element) => {\n\tobserver.unobserve(target);\n\tlisteners.delete(target);\n};\n\ncustomElements.define(\n\t\"yamf-island\",\n\tclass extends HTMLElement {\n\t\tpublic connectedCallback() {\n\t\t\tconst islandProps = parse(this.getAttribute(\"island-props\") ?? \"{}\");\n\t\t\tconst islandSrc = this.getAttribute(\"island-src\");\n\t\t\tconst islandEntry = this.getAttribute(\"island-entry\");\n\t\t\t// oxlint-disable-next-line typescript/no-unsafe-type-assertion\n\t\t\tconst islandClient = (this.getAttribute(\"island-client\") ??\n\t\t\t\t\"load\") as IslandClientDirectiveSerialized;\n\n\t\t\tif (!islandSrc) {\n\t\t\t\tthrow new Error(\"Missing island-src attribute\");\n\t\t\t}\n\n\t\t\tif (!islandEntry) {\n\t\t\t\tthrow new Error(\"Missing island-entry attribute\");\n\t\t\t}\n\n\t\t\tconst hydrateIsland = (mod: Record<string, FC>) => {\n\t\t\t\tconst Comp = mod[islandEntry];\n\n\t\t\t\tif (!Comp) {\n\t\t\t\t\tthrow new Error(`Missing island entry ${islandEntry} in ${islandSrc}`);\n\t\t\t\t}\n\n\t\t\t\thydrateRoot(this, <Comp {...islandProps} />);\n\t\t\t};\n\n\t\t\tconst initIsland = () => {\n\t\t\t\tconst src = withLeadingSlash(islandSrc);\n\t\t\t\tvoid __island_raw_import__<Record<string, FC>>(src).then(hydrateIsland);\n\t\t\t};\n\n\t\t\tswitch (islandClient) {\n\t\t\t\tcase \"true\":\n\t\t\t\tcase \"load\":\n\t\t\t\t\tinitIsland();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"idle\":\n\t\t\t\t\trequestIdleCallback(initIsland);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"visible\":\n\t\t\t\t\t// yamf-island has `display: contents` which breaks IntersectionObserver\n\t\t\t\t\t// so we have to observe the first child instead if it exists\n\t\t\t\t\tif (this.firstElementChild) {\n\t\t\t\t\t\tobserve(this.firstElementChild, initIsland);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tinitIsland();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"false\":\n\t\t\t\tcase \"skip\":\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tislandClient satisfies never;\n\t\t\t\t\t// oxlint-disable-next-line typescript/restrict-template-expressions\n\t\t\t\t\tthrow new Error(`Invalid island-client value: ${islandClient}`);\n\t\t\t}\n\t\t}\n\n\t\tpublic disconnectedCallback() {\n\t\t\tunobserve(this);\n\t\t}\n\t},\n);\n"],"mappings":";;;;;AAQA,MAAM,4BAAY,IAAI,QAA+B;AAErD,MAAM,WAAW,IAAI,sBAAqB,YAAW;CACpD,KAAK,MAAM,SAAS,SACnB,IAAI,MAAM,gBAAgB;EACzB,UAAU,IAAI,MAAM,MAAM,CAAC,GAAG;EAC9B,UAAU,MAAM,MAAM;CACvB;AAEF,CAAC;AAED,MAAM,WAAW,QAAiB,OAAqB;CACtD,SAAS,QAAQ,MAAM;CACvB,UAAU,IAAI,QAAQ,EAAE;AACzB;AAEA,MAAM,aAAa,WAAoB;CACtC,SAAS,UAAU,MAAM;CACzB,UAAU,OAAO,MAAM;AACxB;AAEA,eAAe,OACd,eACA,cAAc,YAAY;CACzB,oBAA2B;EAC1B,MAAM,cAAc,MAAM,KAAK,aAAa,cAAc,KAAK,IAAI;EACnE,MAAM,YAAY,KAAK,aAAa,YAAY;EAChD,MAAM,cAAc,KAAK,aAAa,cAAc;EAEpD,MAAM,eAAgB,KAAK,aAAa,eAAe,KACtD;EAED,IAAI,CAAC,WACJ,MAAM,IAAI,MAAM,8BAA8B;EAG/C,IAAI,CAAC,aACJ,MAAM,IAAI,MAAM,gCAAgC;EAGjD,MAAM,iBAAiB,QAA4B;GAClD,MAAM,OAAO,IAAI;GAEjB,IAAI,CAAC,MACJ,MAAM,IAAI,MAAM,wBAAwB,YAAY,MAAM,WAAW;GAGtE,YAAY,MAAM,oBAAC,MAAD,EAAM,GAAI,YAAc,CAAA,CAAC;EAC5C;EAEA,MAAM,mBAAmB;GACxB,MAAM,MAAM,iBAAiB,SAAS;GACtC,sBAA+C,GAAG,CAAC,CAAC,KAAK,aAAa;EACvE;EAEA,QAAQ,cAAR;GACC,KAAK;GACL,KAAK;IACJ,WAAW;IACX;GACD,KAAK;IACJ,oBAAoB,UAAU;IAC9B;GACD,KAAK;IAGJ,IAAI,KAAK,mBACR,QAAQ,KAAK,mBAAmB,UAAU;SAE1C,WAAW;IAEZ;GACD,KAAK;GACL,KAAK,QACJ;GACD,SAGC,MAAM,IAAI,MAAM,gCAAgC,cAAc;EAChE;CACD;CAEA,uBAA8B;EAC7B,UAAU,IAAI;CACf;AACD,CACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/island/types.ts"],"mappings":";KAAY;
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/island/types.ts"],"mappings":";KAAY;UASK;EAChB,gBAAgB"}
|
package/dist/page.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page.d.ts","names":[],"sources":["../src/page.tsx"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"page.d.ts","names":[],"sources":["../src/page.tsx"],"mappings":";;;;;;;KAiBY,eACX,OAAO,SACP;EACC,QAAQ;EACR,OAAO;MAEJ;KAEO,gBACX,OAAO,SACP;EACC,MAAM;EACN,UAAU,OAAO;MAEd,eAAe,QAAQ,QAAQ,QAAQ;UAElC;EACT,QAAQ;EACR;;cAGY,aAAc,SAAS,sBAAoB"}
|
package/dist/page.js
CHANGED
|
@@ -8,6 +8,7 @@ import { transformHtmlTemplate } from "unhead/server";
|
|
|
8
8
|
import { createStreamableHead, wrapStream } from "unhead/stream/server";
|
|
9
9
|
import { Root } from "virtual:yamf:root";
|
|
10
10
|
import { template } from "virtual:yamf:template";
|
|
11
|
+
import { Router } from "wouter";
|
|
11
12
|
import { jsx } from "hono/jsx/jsx-runtime";
|
|
12
13
|
//#region src/page.tsx
|
|
13
14
|
const definePage = (options) => {
|
|
@@ -39,7 +40,11 @@ const definePage = (options) => {
|
|
|
39
40
|
head,
|
|
40
41
|
event
|
|
41
42
|
},
|
|
42
|
-
children: /* @__PURE__ */ jsx(
|
|
43
|
+
children: /* @__PURE__ */ jsx(Router, {
|
|
44
|
+
ssrPath: event.url.pathname,
|
|
45
|
+
ssrSearch: event.url.search,
|
|
46
|
+
children: /* @__PURE__ */ jsx(Root$1, { children: content })
|
|
47
|
+
})
|
|
43
48
|
});
|
|
44
49
|
const responseInit = { headers: { "Content-Type": "text/html; charset=utf-8" } };
|
|
45
50
|
if (!options.stream) return new Hono().get("/", async (c) => withServerTiming(event, "#render", async () => {
|
package/dist/page.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page.js","names":["Root","RootComponent"],"sources":["../src/page.tsx"],"sourcesContent":["import { Fragment, type Child } from \"hono/jsx\";\nimport { renderToReadableStream } from \"hono/jsx/streaming\";\nimport { Hono } from \"hono/tiny\";\nimport type { EventHandlerResponse, H3Event } from \"nitro/h3\";\nimport { HTTPResponse, withServerTiming } from \"nitro/h3\";\nimport { useSeoMeta } from \"unhead\";\nimport type { Unhead } from \"unhead/server\";\nimport { transformHtmlTemplate } from \"unhead/server\";\nimport { createStreamableHead, wrapStream } from \"unhead/stream/server\";\nimport type { ResolvableLink, UseSeoMetaInput } from \"unhead/types\";\nimport { Root as RootComponent } from \"virtual:yamf:root\";\nimport { template } from \"virtual:yamf:template\";\nimport { SSRContext } from \"./context/ssr\";\nimport type { ImportAssetsResult } from \"./shared/assets\";\nimport { YamfHead } from \"./shared/head\";\n\nexport type PageHandler = (\n\tevent: H3Event,\n\tparams: {\n\t\tassets: ImportAssetsResult;\n\t\thead?: YamfHead;\n\t},\n) => EventHandlerResponse;\n\nexport type PageRenderer = (\n\tevent: H3Event,\n\tparams: {\n\t\thead: Unhead;\n\t\tseoHead: (input: UseSeoMetaInput) => void;\n\t},\n) => HTTPResponse | Child | Promise<Child | HTTPResponse>;\n\ninterface DefinePageOptions {\n\trender: PageRenderer;\n\tstream?: boolean;\n}\n\nexport const definePage = (options: DefinePageOptions): PageHandler => {\n\treturn async (event, { assets, head: headInit }) => {\n\t\tconst { head } = createStreamableHead({ init: [headInit] });\n\t\tconst seoHead = (input?: UseSeoMetaInput) => useSeoMeta(head, input);\n\t\tseoHead(headInit?.seo);\n\n\t\thead.push({\n\t\t\tlink: [\n\t\t\t\t...assets.js.map((attrs): ResolvableLink => ({ rel: \"modulepreload\", ...attrs })),\n\t\t\t\t...assets.css.map((attrs): ResolvableLink => ({ rel: \"stylesheet\", ...attrs })),\n\t\t\t],\n\t\t\tscript: [{ type: \"module\", src: assets.entry }],\n\t\t});\n\n\t\tconst content = await options.render(event, { head, seoHead });\n\n\t\tif (content instanceof HTTPResponse) {\n\t\t\treturn content;\n\t\t}\n\n\t\tconst Root = RootComponent ?? Fragment;\n\n\t\tconst App = async () => (\n\t\t\t<SSRContext value={{ head, event }}>\n\t\t\t\t<Root>{content}</Root>\n\t\t\t</SSRContext>\n\t\t);\n\n\t\tconst responseInit = {\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"text/html; charset=utf-8\",\n\t\t\t},\n\t\t};\n\n\t\tif (!options.stream) {\n\t\t\t// TODO: figure out how expensive this is\n\t\t\treturn new Hono()\n\t\t\t\t.get(\"/\", async c =>\n\t\t\t\t\twithServerTiming(event, \"#render\", async () => {\n\t\t\t\t\t\tconst response = await c.html(<App />);\n\n\t\t\t\t\t\tlet html = await response.text();\n\n\t\t\t\t\t\thtml = transformHtmlTemplate(\n\t\t\t\t\t\t\thead,\n\t\t\t\t\t\t\ttemplate.replace(\"<!--ssr-outlet-->\", html ?? \"\"),\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\treturn new Response(html, responseInit);\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\t.request(\"/\");\n\t\t}\n\n\t\tconst stream = wrapStream(head, renderToReadableStream(<App />), template);\n\n\t\treturn new Response(stream, responseInit);\n\t};\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"page.js","names":["Root","RootComponent"],"sources":["../src/page.tsx"],"sourcesContent":["import { Fragment, type Child } from \"hono/jsx\";\nimport { renderToReadableStream } from \"hono/jsx/streaming\";\nimport { Hono } from \"hono/tiny\";\nimport type { EventHandlerResponse, H3Event } from \"nitro/h3\";\nimport { HTTPResponse, withServerTiming } from \"nitro/h3\";\nimport { useSeoMeta } from \"unhead\";\nimport type { Unhead } from \"unhead/server\";\nimport { transformHtmlTemplate } from \"unhead/server\";\nimport { createStreamableHead, wrapStream } from \"unhead/stream/server\";\nimport type { ResolvableLink, UseSeoMetaInput } from \"unhead/types\";\nimport { Root as RootComponent } from \"virtual:yamf:root\";\nimport { template } from \"virtual:yamf:template\";\nimport { Router } from \"wouter\";\nimport { SSRContext } from \"./context/ssr\";\nimport type { ImportAssetsResult } from \"./shared/assets\";\nimport { YamfHead } from \"./shared/head\";\n\nexport type PageHandler = (\n\tevent: H3Event,\n\tparams: {\n\t\tassets: ImportAssetsResult;\n\t\thead?: YamfHead;\n\t},\n) => EventHandlerResponse;\n\nexport type PageRenderer = (\n\tevent: H3Event,\n\tparams: {\n\t\thead: Unhead;\n\t\tseoHead: (input: UseSeoMetaInput) => void;\n\t},\n) => HTTPResponse | Child | Promise<Child | HTTPResponse>;\n\ninterface DefinePageOptions {\n\trender: PageRenderer;\n\tstream?: boolean;\n}\n\nexport const definePage = (options: DefinePageOptions): PageHandler => {\n\treturn async (event, { assets, head: headInit }) => {\n\t\tconst { head } = createStreamableHead({ init: [headInit] });\n\t\tconst seoHead = (input?: UseSeoMetaInput) => useSeoMeta(head, input);\n\t\tseoHead(headInit?.seo);\n\n\t\thead.push({\n\t\t\tlink: [\n\t\t\t\t...assets.js.map((attrs): ResolvableLink => ({ rel: \"modulepreload\", ...attrs })),\n\t\t\t\t...assets.css.map((attrs): ResolvableLink => ({ rel: \"stylesheet\", ...attrs })),\n\t\t\t],\n\t\t\tscript: [{ type: \"module\", src: assets.entry }],\n\t\t});\n\n\t\tconst content = await options.render(event, { head, seoHead });\n\n\t\tif (content instanceof HTTPResponse) {\n\t\t\treturn content;\n\t\t}\n\n\t\tconst Root = RootComponent ?? Fragment;\n\n\t\tconst App = async () => (\n\t\t\t<SSRContext value={{ head, event }}>\n\t\t\t\t<Router ssrPath={event.url.pathname} ssrSearch={event.url.search}>\n\t\t\t\t\t<Root>{content}</Root>\n\t\t\t\t</Router>\n\t\t\t</SSRContext>\n\t\t);\n\n\t\tconst responseInit = {\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"text/html; charset=utf-8\",\n\t\t\t},\n\t\t};\n\n\t\tif (!options.stream) {\n\t\t\t// TODO: figure out how expensive this is\n\t\t\treturn new Hono()\n\t\t\t\t.get(\"/\", async c =>\n\t\t\t\t\twithServerTiming(event, \"#render\", async () => {\n\t\t\t\t\t\tconst response = await c.html(<App />);\n\n\t\t\t\t\t\tlet html = await response.text();\n\n\t\t\t\t\t\thtml = transformHtmlTemplate(\n\t\t\t\t\t\t\thead,\n\t\t\t\t\t\t\ttemplate.replace(\"<!--ssr-outlet-->\", html ?? \"\"),\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\treturn new Response(html, responseInit);\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\t.request(\"/\");\n\t\t}\n\n\t\tconst stream = wrapStream(head, renderToReadableStream(<App />), template);\n\n\t\treturn new Response(stream, responseInit);\n\t};\n};\n"],"mappings":";;;;;;;;;;;;;AAsCA,MAAa,cAAc,YAA4C;CACtE,OAAO,OAAO,OAAO,EAAE,QAAQ,MAAM,eAAe;EACnD,MAAM,EAAE,SAAS,qBAAqB,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;EAC1D,MAAM,WAAW,UAA4B,WAAW,MAAM,KAAK;EACnE,QAAQ,UAAU,GAAG;EAErB,KAAK,KAAK;GACT,MAAM,CACL,GAAG,OAAO,GAAG,KAAK,WAA2B;IAAE,KAAK;IAAiB,GAAG;GAAM,EAAE,GAChF,GAAG,OAAO,IAAI,KAAK,WAA2B;IAAE,KAAK;IAAc,GAAG;GAAM,EAAE,CAC/E;GACA,QAAQ,CAAC;IAAE,MAAM;IAAU,KAAK,OAAO;GAAM,CAAC;EAC/C,CAAC;EAED,MAAM,UAAU,MAAM,QAAQ,OAAO,OAAO;GAAE;GAAM;EAAQ,CAAC;EAE7D,IAAI,mBAAmB,cACtB,OAAO;EAGR,MAAMA,SAAOC,QAAiB;EAE9B,MAAM,MAAM,YACX,oBAAC,YAAD;GAAY,OAAO;IAAE;IAAM;GAAM;GAChC,UAAA,oBAAC,QAAD;IAAQ,SAAS,MAAM,IAAI;IAAU,WAAW,MAAM,IAAI;IACzD,UAAA,oBAACD,QAAD,EAAA,UAAO,QAAc,CAAA;GACd,CAAA;EACG,CAAA;EAGb,MAAM,eAAe,EACpB,SAAS,EACR,gBAAgB,2BACjB,EACD;EAEA,IAAI,CAAC,QAAQ,QAEZ,OAAO,IAAI,KAAK,CAAC,CACf,IAAI,KAAK,OAAM,MACf,iBAAiB,OAAO,WAAW,YAAY;GAG9C,IAAI,OAAO,OAAM,MAFM,EAAE,KAAK,oBAAC,KAAD,CAAM,CAAA,CAAC,EAAA,CAEX,KAAK;GAE/B,OAAO,sBACN,MACA,SAAS,QAAQ,qBAAqB,QAAQ,EAAE,CACjD;GAEA,OAAO,IAAI,SAAS,MAAM,YAAY;EACvC,CAAC,CACF,CAAC,CACA,QAAQ,GAAG;EAGd,MAAM,SAAS,WAAW,MAAM,uBAAuB,oBAAC,KAAD,CAAM,CAAA,CAAC,GAAG,QAAQ;EAEzE,OAAO,IAAI,SAAS,QAAQ,YAAY;CACzC;AACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.d.mts","names":[],"sources":["../../src/server/entry.tsx"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"entry.d.mts","names":[],"sources":["../../src/server/entry.tsx"],"mappings":";;;KAsDY,cAAc,sBAAsB,qBAAqB;UAEpD;EAChB,OAAO,aAAa,OAAO,YAAY;EACvC;;cAGY,oBAAqB,UAAU,6BAA2B"}
|
package/dist/server/entry.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { HTTPError, defineHandler, writeEarlyHints } from "nitro/h3";
|
|
|
3
3
|
import { addRoute, createRouter, findRoute } from "rou3";
|
|
4
4
|
import { withLeadingSlash, withoutTrailingSlash } from "ufo";
|
|
5
5
|
import { clientAssets } from "virtual:yamf:assets";
|
|
6
|
+
import { errorHandler } from "virtual:yamf:error-handler";
|
|
6
7
|
import { assets, pages } from "virtual:yamf:pages";
|
|
7
8
|
import { rootAssets } from "virtual:yamf:root";
|
|
8
9
|
//#region src/server/entry.tsx
|
|
@@ -19,7 +20,7 @@ for (const [relativePath, handler] of Object.entries(pages).toSorted((a, b) => a
|
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
22
|
const defineServerEntry = (options) => {
|
|
22
|
-
|
|
23
|
+
const rootHandler = async (event) => {
|
|
23
24
|
const route = findRoute(router, "GET", event.url.pathname);
|
|
24
25
|
if (!route) throw new HTTPError("Not found", { status: 404 });
|
|
25
26
|
const { handler, serverAssets } = route.data;
|
|
@@ -33,6 +34,14 @@ const defineServerEntry = (options) => {
|
|
|
33
34
|
assets,
|
|
34
35
|
head: typeof options?.head === "function" ? options.head(event) : options?.head
|
|
35
36
|
});
|
|
37
|
+
};
|
|
38
|
+
return defineHandler(async (event) => {
|
|
39
|
+
if (!errorHandler) return rootHandler(event);
|
|
40
|
+
try {
|
|
41
|
+
return await rootHandler(event);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return errorHandler(error instanceof HTTPError ? error : new HTTPError({ cause: error }), event);
|
|
44
|
+
}
|
|
36
45
|
});
|
|
37
46
|
};
|
|
38
47
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.mjs","names":["pagesServerAssets"],"sources":["../../src/server/entry.tsx"],"sourcesContent":["import path from \"node:path\";\nimport
|
|
1
|
+
{"version":3,"file":"entry.mjs","names":["pagesServerAssets"],"sources":["../../src/server/entry.tsx"],"sourcesContent":["import path from \"node:path\";\nimport {\n\tdefineHandler,\n\tEventHandler,\n\tH3Event,\n\tHTTPError,\n\twriteEarlyHints,\n\ttype EventHandlerRequest,\n\ttype EventHandlerWithFetch,\n} from \"nitro/h3\";\nimport { addRoute, createRouter, findRoute } from \"rou3\";\nimport { withLeadingSlash, withoutTrailingSlash } from \"ufo\";\nimport { clientAssets } from \"virtual:yamf:assets\";\nimport { errorHandler } from \"virtual:yamf:error-handler\";\nimport { pages, assets as pagesServerAssets } from \"virtual:yamf:pages\";\nimport { rootAssets } from \"virtual:yamf:root\";\nimport type { PageHandler } from \"#/page\";\nimport { YamfHead } from \"#/shared/head\";\n\ninterface Route {\n\thandler: () => Promise<PageHandler>;\n\tserverAssets?: ImportAssetsResult;\n}\n\nconst router = createRouter<Route>();\n\nfor (const [relativePath, handler] of Object.entries(pages).toSorted((a, b) =>\n\ta[0].localeCompare(b[0]),\n)) {\n\tconst ext = path.extname(relativePath);\n\n\tlet route = path.relative(\"/src/pages\", relativePath);\n\n\tif (ext.length) {\n\t\troute = route.slice(0, -ext.length);\n\t}\n\n\troute =\n\t\troute\n\t\t\t.replace(/\\.[A-Za-z]+$/, \"\")\n\t\t\t.replace(/\\(([^(/\\\\]+)\\)[/\\\\]/g, \"\")\n\t\t\t.replace(/\\[\\.{3}]/g, \"**\")\n\t\t\t.replace(/\\[\\.{3}([^\\]]+)]/g, (_, p: string) => \"**:\" + p.replace(/[^\\w-]/g, \"_\"))\n\t\t\t.replace(/\\[([^/\\]]+)]/g, (_, p: string) => \":\" + p.replace(/[^\\w-]/g, \"_\"))\n\t\t\t.replace(/(\\/|^)index$/, \"\") || \"/\";\n\n\troute = withLeadingSlash(withoutTrailingSlash(route));\n\n\taddRoute(router, \"GET\", route, {\n\t\thandler,\n\t\tserverAssets: pagesServerAssets[relativePath],\n\t});\n}\n\nexport type ServerEntry = EventHandlerWithFetch<EventHandlerRequest, Promise<unknown>>;\n\nexport interface DefineServerEntryOptions {\n\thead?: YamfHead | ((event: H3Event) => YamfHead);\n\tdisableEarlyHints?: boolean;\n}\n\nexport const defineServerEntry = (options?: DefineServerEntryOptions): ServerEntry => {\n\tconst rootHandler: EventHandler<EventHandlerRequest, Promise<unknown>> = async event => {\n\t\tconst route = findRoute(router, \"GET\", event.url.pathname);\n\n\t\tif (!route) {\n\t\t\tthrow new HTTPError(\"Not found\", { status: 404 });\n\t\t}\n\n\t\tconst { handler, serverAssets } = route.data;\n\t\tevent.context.params = route.params;\n\n\t\tconst assets = clientAssets.merge(...[rootAssets, serverAssets].filter(x => !!x));\n\n\t\tif (!options?.disableEarlyHints) {\n\t\t\tconst link = [\n\t\t\t\t...assets.js.map(script => `<${script.href}>; rel=modulepreload`),\n\t\t\t\t...assets.css.map(style => `<${style.href}>; rel=preload; as=style`),\n\t\t\t];\n\n\t\t\tif (link.length) {\n\t\t\t\tawait writeEarlyHints(event, { link });\n\t\t\t}\n\t\t}\n\n\t\treturn (await handler())(event, {\n\t\t\tassets,\n\t\t\thead: typeof options?.head === \"function\" ? options.head(event) : options?.head,\n\t\t});\n\t};\n\n\treturn defineHandler(async event => {\n\t\tif (!errorHandler) {\n\t\t\treturn rootHandler(event);\n\t\t}\n\n\t\ttry {\n\t\t\treturn await rootHandler(event);\n\t\t} catch (error) {\n\t\t\tconst httpError = error instanceof HTTPError ? error : new HTTPError({ cause: error });\n\n\t\t\treturn errorHandler(httpError, event);\n\t\t}\n\t});\n};\n"],"mappings":";;;;;;;;;AAwBA,MAAM,SAAS,aAAoB;AAEnC,KAAK,MAAM,CAAC,cAAc,YAAY,OAAO,QAAQ,KAAK,CAAC,CAAC,UAAU,GAAG,MACxE,EAAE,EAAE,CAAC,cAAc,EAAE,EAAE,CACxB,GAAG;CACF,MAAM,MAAM,KAAK,QAAQ,YAAY;CAErC,IAAI,QAAQ,KAAK,SAAS,cAAc,YAAY;CAEpD,IAAI,IAAI,QACP,QAAQ,MAAM,MAAM,GAAG,CAAC,IAAI,MAAM;CAGnC,QACC,MACE,QAAQ,gBAAgB,EAAE,CAAC,CAC3B,QAAQ,wBAAwB,EAAE,CAAC,CACnC,QAAQ,aAAa,IAAI,CAAC,CAC1B,QAAQ,sBAAsB,GAAG,MAAc,QAAQ,EAAE,QAAQ,WAAW,GAAG,CAAC,CAAC,CACjF,QAAQ,kBAAkB,GAAG,MAAc,MAAM,EAAE,QAAQ,WAAW,GAAG,CAAC,CAAC,CAC3E,QAAQ,gBAAgB,EAAE,KAAK;CAElC,QAAQ,iBAAiB,qBAAqB,KAAK,CAAC;CAEpD,SAAS,QAAQ,OAAO,OAAO;EAC9B;EACA,cAAcA,OAAkB;CACjC,CAAC;AACF;AASA,MAAa,qBAAqB,YAAoD;CACrF,MAAM,cAAmE,OAAM,UAAS;EACvF,MAAM,QAAQ,UAAU,QAAQ,OAAO,MAAM,IAAI,QAAQ;EAEzD,IAAI,CAAC,OACJ,MAAM,IAAI,UAAU,aAAa,EAAE,QAAQ,IAAI,CAAC;EAGjD,MAAM,EAAE,SAAS,iBAAiB,MAAM;EACxC,MAAM,QAAQ,SAAS,MAAM;EAE7B,MAAM,SAAS,aAAa,MAAM,GAAG,CAAC,YAAY,YAAY,CAAC,CAAC,QAAO,MAAK,CAAC,CAAC,CAAC,CAAC;EAEhF,IAAI,CAAC,SAAS,mBAAmB;GAChC,MAAM,OAAO,CACZ,GAAG,OAAO,GAAG,KAAI,WAAU,IAAI,OAAO,KAAK,qBAAqB,GAChE,GAAG,OAAO,IAAI,KAAI,UAAS,IAAI,MAAM,KAAK,yBAAyB,CACpE;GAEA,IAAI,KAAK,QACR,MAAM,gBAAgB,OAAO,EAAE,KAAK,CAAC;EAEvC;EAEA,QAAQ,MAAM,QAAQ,EAAA,CAAG,OAAO;GAC/B;GACA,MAAM,OAAO,SAAS,SAAS,aAAa,QAAQ,KAAK,KAAK,IAAI,SAAS;EAC5E,CAAC;CACF;CAEA,OAAO,cAAc,OAAM,UAAS;EACnC,IAAI,CAAC,cACJ,OAAO,YAAY,KAAK;EAGzB,IAAI;GACH,OAAO,MAAM,YAAY,KAAK;EAC/B,SAAS,OAAO;GAGf,OAAO,aAFW,iBAAiB,YAAY,QAAQ,IAAI,UAAU,EAAE,OAAO,MAAM,CAAC,GAEtD,KAAK;EACrC;CACD,CAAC;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.mjs","names":[],"sources":["../../../src/island/server.tsx"],"sourcesContent":["import { stringify } from \"devalue\";\nimport type { Child, FC } from \"hono/jsx\";\nimport type { ImportAssetsResultRaw } from \"#/shared/assets\";\nimport type { IslandClientDirective } from \"./types\";\n\ndeclare module \"hono/jsx\" {\n\tnamespace JSX {\n\t\tinterface IntrinsicElements {\n\t\t\t\"yamf-island\": {\n\t\t\t\t\"island-props\"?: string;\n\t\t\t\t\"island-src\": string;\n\t\t\t\t\"island-entry\": string;\n\t\t\t\t\"island-client\": IslandClientDirective;\n\t\t\t\tchildren: Child;\n\t\t\t\tstyle?: JSX.CSSProperties;\n\t\t\t};\n\t\t}\n\t}\n}\n\nexport const createIsland = (\n\tComponent: FC,\n\texportName: string,\n\tassets: ImportAssetsResultRaw,\n): FC => {\n\tconst ComponentWrapper: FC & { name: string } = props => {\n\t\tif (!assets.entry) {\n\t\t\tthrow new Error(`Missing island entry for island ${Component.name}`);\n\t\t}\n\n\t\treturn (\n\t\t\t<yamf-island\n\t\t\t\tisland-props={stringify(props)}\n\t\t\t\tisland-src={assets.entry}\n\t\t\t\tisland-entry={exportName}\n\t\t\t\t// oxlint-disable-next-line typescript/no-unsafe-type-assertion\n\t\t\t\tisland-client={props[\"yamf-client\"] as IslandClientDirective}\n\t\t\t\t// TODO: export CSS?\n\t\t\t\tstyle={{ display: \"contents\" }}\n\t\t\t>\n\t\t\t\t<Component {...props} />\n\t\t\t</yamf-island>\n\t\t);\n\t};\n\n\tObject.defineProperty(ComponentWrapper, \"name\", {\n\t\tvalue: Component.name,\n\t});\n\n\treturn ComponentWrapper;\n};\n"],"mappings":";;;AAoBA,MAAa,gBACZ,WACA,YACA,WACQ;CACR,MAAM,oBAA0C,UAAS;EACxD,IAAI,CAAC,OAAO,OACX,MAAM,IAAI,MAAM,mCAAmC,UAAU,MAAM;EAGpE,OACC,oBAAC,eAAD;GACC,gBAAc,UAAU,KAAK;GAC7B,cAAY,OAAO;GACnB,gBAAc;GAEd,iBAAe,MAAM;GAErB,OAAO,EAAE,SAAS,WAAW;
|
|
1
|
+
{"version":3,"file":"server.mjs","names":[],"sources":["../../../src/island/server.tsx"],"sourcesContent":["import { stringify } from \"devalue\";\nimport type { Child, FC } from \"hono/jsx\";\nimport type { ImportAssetsResultRaw } from \"#/shared/assets\";\nimport type { IslandClientDirective } from \"./types\";\n\ndeclare module \"hono/jsx\" {\n\tnamespace JSX {\n\t\tinterface IntrinsicElements {\n\t\t\t\"yamf-island\": {\n\t\t\t\t\"island-props\"?: string;\n\t\t\t\t\"island-src\": string;\n\t\t\t\t\"island-entry\": string;\n\t\t\t\t\"island-client\": IslandClientDirective;\n\t\t\t\tchildren: Child;\n\t\t\t\tstyle?: JSX.CSSProperties;\n\t\t\t};\n\t\t}\n\t}\n}\n\nexport const createIsland = (\n\tComponent: FC,\n\texportName: string,\n\tassets: ImportAssetsResultRaw,\n): FC => {\n\tconst ComponentWrapper: FC & { name: string } = props => {\n\t\tif (!assets.entry) {\n\t\t\tthrow new Error(`Missing island entry for island ${Component.name}`);\n\t\t}\n\n\t\treturn (\n\t\t\t<yamf-island\n\t\t\t\tisland-props={stringify(props)}\n\t\t\t\tisland-src={assets.entry}\n\t\t\t\tisland-entry={exportName}\n\t\t\t\t// oxlint-disable-next-line typescript/no-unsafe-type-assertion\n\t\t\t\tisland-client={props[\"yamf-client\"] as IslandClientDirective}\n\t\t\t\t// TODO: export CSS?\n\t\t\t\tstyle={{ display: \"contents\" }}\n\t\t\t>\n\t\t\t\t<Component {...props} />\n\t\t\t</yamf-island>\n\t\t);\n\t};\n\n\tObject.defineProperty(ComponentWrapper, \"name\", {\n\t\tvalue: Component.name,\n\t});\n\n\treturn ComponentWrapper;\n};\n"],"mappings":";;;AAoBA,MAAa,gBACZ,WACA,YACA,WACQ;CACR,MAAM,oBAA0C,UAAS;EACxD,IAAI,CAAC,OAAO,OACX,MAAM,IAAI,MAAM,mCAAmC,UAAU,MAAM;EAGpE,OACC,oBAAC,eAAD;GACC,gBAAc,UAAU,KAAK;GAC7B,cAAY,OAAO;GACnB,gBAAc;GAEd,iBAAe,MAAM;GAErB,OAAO,EAAE,SAAS,WAAW;GAE7B,UAAA,oBAAC,WAAD,EAAW,GAAI,MAAQ,CAAA;EACX,CAAA;CAEf;CAEA,OAAO,eAAe,kBAAkB,QAAQ,EAC/C,OAAO,UAAU,KAClB,CAAC;CAED,OAAO;AACR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/vite/index.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/vite/index.ts"],"mappings":";;;UAWiB;EAChB,QAAQ;;cAGH,OAAQ,UAAU,gBAAc"}
|
package/dist/vite/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { islands } from "./islands.mjs";
|
|
2
2
|
import { virtualAssets } from "./virtual-assets.mjs";
|
|
3
|
+
import { virtualErrorHandler } from "./virtual-error-handler.mjs";
|
|
3
4
|
import { virtualPages } from "./virtual-pages.mjs";
|
|
4
5
|
import { virtualRoot } from "./virtual-root.mjs";
|
|
5
6
|
import { virtualTemplate } from "./virtual-template.mjs";
|
|
@@ -12,7 +13,7 @@ const yamf = (options) => {
|
|
|
12
13
|
name: "yamf:config",
|
|
13
14
|
config() {
|
|
14
15
|
return {
|
|
15
|
-
ssr: { noExternal:
|
|
16
|
+
ssr: { noExternal: true },
|
|
16
17
|
optimizeDeps: { include: [
|
|
17
18
|
"hono",
|
|
18
19
|
"hono/jsx/dom/client",
|
|
@@ -20,6 +21,20 @@ const yamf = (options) => {
|
|
|
20
21
|
"devalue",
|
|
21
22
|
"ufo"
|
|
22
23
|
] },
|
|
24
|
+
resolve: { alias: [
|
|
25
|
+
{
|
|
26
|
+
find: "react",
|
|
27
|
+
replacement: "@hono/react-compat"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
find: "react-dom",
|
|
31
|
+
replacement: "@hono/react-compat"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
find: /^use-sync-external-store(?:\/shim(?:\/.*)?)?$/,
|
|
35
|
+
replacement: "@hono/react-compat"
|
|
36
|
+
}
|
|
37
|
+
] },
|
|
23
38
|
environments: {
|
|
24
39
|
...existsSync("./src/server.tsx") ? { ssr: { build: {
|
|
25
40
|
cssCodeSplit: false,
|
|
@@ -31,6 +46,7 @@ const yamf = (options) => {
|
|
|
31
46
|
}
|
|
32
47
|
});
|
|
33
48
|
plugins.push(islands());
|
|
49
|
+
plugins.push(virtualErrorHandler(options));
|
|
34
50
|
plugins.push(virtualAssets());
|
|
35
51
|
plugins.push(virtualPages());
|
|
36
52
|
plugins.push(virtualTemplate());
|
package/dist/vite/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/vite/index.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport type { NitroPluginConfig } from \"nitro/vite\";\nimport { nitro } from \"nitro/vite\";\nimport type { EnvironmentOptions, PluginOption } from \"vite\";\nimport { islands } from \"./islands\";\nimport { virtualAssets } from \"./virtual-assets\";\nimport { virtualPages } from \"./virtual-pages\";\nimport { virtualRoot } from \"./virtual-root\";\nimport { virtualTemplate } from \"./virtual-template\";\n\nexport interface YamfOptions {\n\tnitro?: NitroPluginConfig;\n}\n\nconst yamf = (options?: YamfOptions): PluginOption[] => {\n\tconst plugins: PluginOption[] = [];\n\n\t// TODO: extendable config\n\tplugins.push({\n\t\tname: \"yamf:config\",\n\t\tconfig() {\n\t\t\tconst ssrEnv: EnvironmentOptions = {\n\t\t\t\tbuild: {\n\t\t\t\t\t// TODO: figure out if this should be true\n\t\t\t\t\tcssCodeSplit: false,\n\t\t\t\t\trolldownOptions: {\n\t\t\t\t\t\tinput: \"./src/server.tsx\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\n\t\t\treturn {\n\t\t\t\tssr: {\n\t\t\t\t\t// we need to inline because otherwise fullstack plugin fails to build manifest for assets imports\n\t\t\t\t\tnoExternal:
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/vite/index.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport type { NitroPluginConfig } from \"nitro/vite\";\nimport { nitro } from \"nitro/vite\";\nimport type { EnvironmentOptions, PluginOption } from \"vite\";\nimport { islands } from \"./islands\";\nimport { virtualAssets } from \"./virtual-assets\";\nimport { virtualErrorHandler } from \"./virtual-error-handler\";\nimport { virtualPages } from \"./virtual-pages\";\nimport { virtualRoot } from \"./virtual-root\";\nimport { virtualTemplate } from \"./virtual-template\";\n\nexport interface YamfOptions {\n\tnitro?: NitroPluginConfig;\n}\n\nconst yamf = (options?: YamfOptions): PluginOption[] => {\n\tconst plugins: PluginOption[] = [];\n\n\t// TODO: extendable config\n\tplugins.push({\n\t\tname: \"yamf:config\",\n\t\tconfig() {\n\t\t\tconst ssrEnv: EnvironmentOptions = {\n\t\t\t\tbuild: {\n\t\t\t\t\t// TODO: figure out if this should be true\n\t\t\t\t\tcssCodeSplit: false,\n\t\t\t\t\trolldownOptions: {\n\t\t\t\t\t\tinput: \"./src/server.tsx\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\n\t\t\treturn {\n\t\t\t\tssr: {\n\t\t\t\t\t// we need to inline because otherwise fullstack plugin fails to build manifest for assets imports\n\t\t\t\t\t// also, we need to bundle everything so that the `react` -> `@hono/react-compat` alias\n\t\t\t\t\t// is applied to all dependencies (e.g. wouter) in dev mode. Externalized deps are\n\t\t\t\t\t// loaded natively by Node, bypassing Vite's resolve.alias.\n\t\t\t\t\tnoExternal: true,\n\t\t\t\t},\n\t\t\t\toptimizeDeps: {\n\t\t\t\t\tinclude: [\n\t\t\t\t\t\t\"hono\",\n\t\t\t\t\t\t\"hono/jsx/dom/client\",\n\t\t\t\t\t\t\"hono/jsx/jsx-runtime\",\n\t\t\t\t\t\t\"devalue\",\n\t\t\t\t\t\t\"ufo\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tresolve: {\n\t\t\t\t\talias: [\n\t\t\t\t\t\t{ find: \"react\", replacement: \"@hono/react-compat\" },\n\t\t\t\t\t\t{ find: \"react-dom\", replacement: \"@hono/react-compat\" },\n\t\t\t\t\t\t// use-sync-external-store is a CJS package that requires(\"react\").\n\t\t\t\t\t\t// Vite's SSR module runner can't process CJS, so we alias it to\n\t\t\t\t\t\t// @hono/react-compat which exports useSyncExternalStore from hono/jsx.\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfind: /^use-sync-external-store(?:\\/shim(?:\\/.*)?)?$/,\n\t\t\t\t\t\t\treplacement: \"@hono/react-compat\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tenvironments: {\n\t\t\t\t\t...(existsSync(\"./src/server.tsx\") ? { ssr: ssrEnv } : {}),\n\t\t\t\t\tclient: {\n\t\t\t\t\t\tbuild: {\n\t\t\t\t\t\t\trolldownOptions: {\n\t\t\t\t\t\t\t\tinput: \"./src/client/index.ts\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t});\n\n\tplugins.push(islands());\n\n\tplugins.push(virtualErrorHandler(options));\n\tplugins.push(virtualAssets());\n\tplugins.push(virtualPages());\n\tplugins.push(virtualTemplate());\n\tplugins.push(virtualRoot());\n\n\tplugins.push(\n\t\tnitro({\n\t\t\tserverDir: \"./src\",\n\t\t\trenderer: false,\n\t\t\t...options?.nitro,\n\t\t\tcompressPublicAssets: {\n\t\t\t\tgzip: true,\n\t\t\t\tbrotli: true,\n\t\t\t},\n\t\t\tpublicAssets: [\n\t\t\t\t{\n\t\t\t\t\tbaseURL: \"assets\",\n\t\t\t\t\tdir: \"./public/assets\",\n\t\t\t\t\tmaxAge: 365 * 24 * 60 * 60,\n\t\t\t\t},\n\t\t\t],\n\t\t}),\n\t);\n\n\treturn plugins;\n};\n\nexport default yamf;\n"],"mappings":";;;;;;;;;AAeA,MAAM,QAAQ,YAA0C;CACvD,MAAM,UAA0B,CAAC;CAGjC,QAAQ,KAAK;EACZ,MAAM;EACN,SAAS;GAWR,OAAO;IACN,KAAK,EAKJ,YAAY,KACb;IACA,cAAc,EACb,SAAS;KACR;KACA;KACA;KACA;KACA;IACD,EACD;IACA,SAAS,EACR,OAAO;KACN;MAAE,MAAM;MAAS,aAAa;KAAqB;KACnD;MAAE,MAAM;MAAa,aAAa;KAAqB;KAIvD;MACC,MAAM;MACN,aAAa;KACd;IACD,EACD;IACA,cAAc;KACb,GAAI,WAAW,kBAAkB,IAAI,EAAE,KAAK,EAxC7C,OAAO;MAEN,cAAc;MACd,iBAAiB,EAChB,OAAO,mBACR;KACD,EAkCkD,EAAE,IAAI,CAAC;KACxD,QAAQ,EACP,OAAO,EACN,iBAAiB,EAChB,OAAO,wBACR,EACD,EACD;IACD;GACD;EACD;CACD,CAAC;CAED,QAAQ,KAAK,QAAQ,CAAC;CAEtB,QAAQ,KAAK,oBAAoB,OAAO,CAAC;CACzC,QAAQ,KAAK,cAAc,CAAC;CAC5B,QAAQ,KAAK,aAAa,CAAC;CAC3B,QAAQ,KAAK,gBAAgB,CAAC;CAC9B,QAAQ,KAAK,YAAY,CAAC;CAE1B,QAAQ,KACP,MAAM;EACL,WAAW;EACX,UAAU;EACV,GAAG,SAAS;EACZ,sBAAsB;GACrB,MAAM;GACN,QAAQ;EACT;EACA,cAAc,CACb;GACC,SAAS;GACT,KAAK;GACL,QAAQ,MAAM,KAAK,KAAK;EACzB,CACD;CACD,CAAC,CACF;CAEA,OAAO;AACR"}
|
package/dist/vite/islands.mjs
CHANGED
|
@@ -1,81 +1,60 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import _traverse from "@babel/traverse";
|
|
4
|
-
import { callExpression, exportDefaultDeclaration, exportNamedDeclaration, functionDeclaration, functionExpression, identifier, importDeclaration, importDefaultSpecifier, importNamespaceSpecifier, memberExpression, stringLiteral, variableDeclaration, variableDeclarator } from "@babel/types";
|
|
1
|
+
import { withMagicString } from "rolldown-string";
|
|
2
|
+
import { Visitor } from "vite";
|
|
5
3
|
//#region src/vite/islands.ts
|
|
6
|
-
const traverse = _traverse.default ?? _traverse;
|
|
7
4
|
const ISLAND_REGEX = /\.island\.(j|t)sx?$/;
|
|
5
|
+
function langFromId(id) {
|
|
6
|
+
if (id.endsWith(".tsx")) return "tsx";
|
|
7
|
+
if (id.endsWith(".ts")) return "ts";
|
|
8
|
+
if (id.endsWith(".jsx")) return "jsx";
|
|
9
|
+
return "js";
|
|
10
|
+
}
|
|
8
11
|
const islands = () => {
|
|
9
12
|
return [{
|
|
10
13
|
name: "yamf:islands",
|
|
11
14
|
transform: {
|
|
12
15
|
filter: { id: ISLAND_REGEX },
|
|
13
|
-
handler(
|
|
16
|
+
handler: withMagicString(function(s, id) {
|
|
14
17
|
if (this.environment.name !== "ssr") return;
|
|
18
|
+
const program = this.parse(s.original, { lang: langFromId(id) });
|
|
19
|
+
s.prepend(`import * as __runtime from "@pajecawav/yamf/server";\n`);
|
|
20
|
+
s.prepend(`import __assets from "${id}?assets=client";\n`);
|
|
15
21
|
const seenExports = /* @__PURE__ */ new Set();
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
path.unshiftContainer("body", importDeclaration([importNamespaceSpecifier(identifier("__runtime"))], stringLiteral("@pajecawav/yamf/server")));
|
|
23
|
-
path.unshiftContainer("body", importDeclaration([importDefaultSpecifier(identifier("__assets"))], stringLiteral(`${id}?assets=client`)));
|
|
22
|
+
new Visitor({
|
|
23
|
+
ExportDefaultDeclaration(node) {
|
|
24
|
+
const declaration = node.declaration;
|
|
25
|
+
if (declaration.type !== "FunctionDeclaration" && declaration.type !== "FunctionExpression" && declaration.type !== "ArrowFunctionExpression") return;
|
|
26
|
+
s.overwrite(node.start, declaration.start, "const __ISLAND__ = ");
|
|
27
|
+
s.appendRight(declaration.end, `; export default __runtime.createIsland(__ISLAND__, "default", __assets)`);
|
|
24
28
|
},
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
path.replaceWith(exportDefaultDeclaration(callExpression(memberExpression(identifier("__runtime"), identifier("createIsland")), [
|
|
32
|
-
islandIdentifier,
|
|
33
|
-
stringLiteral("default"),
|
|
34
|
-
identifier("__assets")
|
|
35
|
-
])));
|
|
36
|
-
},
|
|
37
|
-
ExportNamedDeclaration(path) {
|
|
38
|
-
if (path.node.declaration?.type === "VariableDeclaration") {
|
|
39
|
-
const declaration = path.node.declaration.declarations.at(0);
|
|
40
|
-
if (!declaration || declaration.id.type !== "Identifier" || seenExports.has(declaration.id.name)) return;
|
|
41
|
-
const exportName = declaration.id.name;
|
|
29
|
+
ExportNamedDeclaration(node) {
|
|
30
|
+
const declaration = node.declaration;
|
|
31
|
+
if (declaration?.type === "VariableDeclaration") {
|
|
32
|
+
const declarator = declaration.declarations[0];
|
|
33
|
+
if (!declarator || declarator.id.type !== "Identifier" || !declarator.init || seenExports.has(declarator.id.name)) return;
|
|
34
|
+
const exportName = declarator.id.name;
|
|
42
35
|
seenExports.add(exportName);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
]))])));
|
|
50
|
-
} else if (path.node.declaration?.type === "FunctionDeclaration") {
|
|
51
|
-
const declaration = path.node.declaration;
|
|
52
|
-
if (!declaration || declaration.id?.type !== "Identifier" || seenExports.has(declaration.id.name)) return;
|
|
53
|
-
const exportName = declaration.id.name;
|
|
36
|
+
s.overwrite(node.start, declarator.init.start, `const __wrap_${exportName} = `);
|
|
37
|
+
s.appendRight(declarator.init.end, `; export const ${exportName} = __runtime.createIsland(__wrap_${exportName}, "${exportName}", __assets)`);
|
|
38
|
+
} else if (declaration?.type === "FunctionDeclaration") {
|
|
39
|
+
const fnId = declaration.id;
|
|
40
|
+
if (!fnId || fnId.type !== "Identifier" || seenExports.has(fnId.name)) return;
|
|
41
|
+
const exportName = fnId.name;
|
|
54
42
|
seenExports.add(exportName);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
islandIdentifier,
|
|
59
|
-
stringLiteral(exportName),
|
|
60
|
-
identifier("__assets")
|
|
61
|
-
]))])));
|
|
43
|
+
s.remove(node.start, declaration.start);
|
|
44
|
+
s.overwrite(fnId.start, fnId.end, `__wrap_${exportName}`);
|
|
45
|
+
s.appendRight(declaration.end, `; export const ${exportName} = __runtime.createIsland(__wrap_${exportName}, "${exportName}", __assets)`);
|
|
62
46
|
}
|
|
63
47
|
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
code: result.code,
|
|
68
|
-
map: result.map
|
|
69
|
-
};
|
|
70
|
-
}
|
|
48
|
+
}).visit(program);
|
|
49
|
+
})
|
|
71
50
|
}
|
|
72
51
|
}, {
|
|
73
52
|
name: "yamf:islands:raw-import",
|
|
74
53
|
transform: {
|
|
75
54
|
order: "post",
|
|
76
|
-
handler(
|
|
77
|
-
if (
|
|
78
|
-
}
|
|
55
|
+
handler: withMagicString(function(s) {
|
|
56
|
+
if (s.original.includes("__island_raw_import__")) s.replaceAll("__island_raw_import__", "import");
|
|
57
|
+
})
|
|
79
58
|
}
|
|
80
59
|
}];
|
|
81
60
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"islands.mjs","names":[],"sources":["../../src/vite/islands.ts"],"sourcesContent":["// reference https://github.com/hi-ogawa/vite-plugin-fullstack/blob/28e9540a68529c58842e9a3bf17d2193a065d524/examples/island/src/framework/island/plugin.ts\nimport { generate } from \"@babel/generator\";\nimport { parse } from \"@babel/parser\";\nimport _traverse from \"@babel/traverse\";\nimport {\n\tcallExpression,\n\texportDefaultDeclaration,\n\texportNamedDeclaration,\n\tfunctionDeclaration,\n\tfunctionExpression,\n\tidentifier,\n\timportDeclaration,\n\timportDefaultSpecifier,\n\timportNamespaceSpecifier,\n\tmemberExpression,\n\tstringLiteral,\n\tvariableDeclaration,\n\tvariableDeclarator,\n} from \"@babel/types\";\nimport type { Plugin } from \"vite\";\n\n// @ts-ignore\n// oxlint-disable-next-line typescript/no-unsafe-type-assertion\nconst traverse = (_traverse.default as typeof _traverse) ?? _traverse;\n\nconst ISLAND_REGEX = /\\.island\\.(j|t)sx?$/;\n\nexport const islands = (): Plugin[] => {\n\treturn [\n\t\t{\n\t\t\tname: \"yamf:islands\",\n\t\t\ttransform: {\n\t\t\t\tfilter: { id: ISLAND_REGEX },\n\t\t\t\thandler(code, id) {\n\t\t\t\t\tif (this.environment.name !== \"ssr\") {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst seenExports = new Set<string>();\n\n\t\t\t\t\tconst ast = parse(code, {\n\t\t\t\t\t\tsourceType: \"module\",\n\t\t\t\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t\t\t\t});\n\n\t\t\t\t\ttraverse(ast, {\n\t\t\t\t\t\tProgram(path) {\n\t\t\t\t\t\t\t// prepends server island runtime\n\t\t\t\t\t\t\t// import * as __runtime from \"yamf/server\";\n\t\t\t\t\t\t\tpath.unshiftContainer(\n\t\t\t\t\t\t\t\t\"body\",\n\t\t\t\t\t\t\t\timportDeclaration(\n\t\t\t\t\t\t\t\t\t[importNamespaceSpecifier(identifier(\"__runtime\"))],\n\t\t\t\t\t\t\t\t\tstringLiteral(\"@pajecawav/yamf/server\"),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t// prepends asset imports for the island:\n\t\t\t\t\t\t\t// import __assets from \"MODULE_ID?assets=client\";\n\t\t\t\t\t\t\tpath.unshiftContainer(\n\t\t\t\t\t\t\t\t\"body\",\n\t\t\t\t\t\t\t\timportDeclaration(\n\t\t\t\t\t\t\t\t\t[importDefaultSpecifier(identifier(\"__assets\"))],\n\t\t\t\t\t\t\t\t\tstringLiteral(`${id}?assets=client`),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tExportDefaultDeclaration(path) {\n\t\t\t\t\t\t\tconst declarationType = path.node.declaration.type;\n\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!(\n\t\t\t\t\t\t\t\t\tdeclarationType === \"FunctionDeclaration\" ||\n\t\t\t\t\t\t\t\t\tdeclarationType === \"FunctionExpression\" ||\n\t\t\t\t\t\t\t\t\tdeclarationType === \"ArrowFunctionExpression\"\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst originalFunction =\n\t\t\t\t\t\t\t\tpath.node.declaration.type === \"FunctionExpression\" ||\n\t\t\t\t\t\t\t\tpath.node.declaration.type === \"ArrowFunctionExpression\"\n\t\t\t\t\t\t\t\t\t? path.node.declaration\n\t\t\t\t\t\t\t\t\t: functionExpression(\n\t\t\t\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t\t\t\tpath.node.declaration.params,\n\t\t\t\t\t\t\t\t\t\t\tpath.node.declaration.body,\n\t\t\t\t\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t\t\t\t\tpath.node.declaration.async,\n\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tconst islandIdentifier = identifier(\"__ISLAND__\");\n\n\t\t\t\t\t\t\tpath.insertBefore(\n\t\t\t\t\t\t\t\tvariableDeclaration(\"const\", [\n\t\t\t\t\t\t\t\t\tvariableDeclarator(islandIdentifier, originalFunction),\n\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tpath.replaceWith(\n\t\t\t\t\t\t\t\texportDefaultDeclaration(\n\t\t\t\t\t\t\t\t\tcallExpression(\n\t\t\t\t\t\t\t\t\t\tmemberExpression(\n\t\t\t\t\t\t\t\t\t\t\tidentifier(\"__runtime\"),\n\t\t\t\t\t\t\t\t\t\t\tidentifier(\"createIsland\"),\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\tislandIdentifier,\n\t\t\t\t\t\t\t\t\t\t\tstringLiteral(\"default\"),\n\t\t\t\t\t\t\t\t\t\t\tidentifier(\"__assets\"),\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tExportNamedDeclaration(path) {\n\t\t\t\t\t\t\tif (path.node.declaration?.type === \"VariableDeclaration\") {\n\t\t\t\t\t\t\t\tconst declaration = path.node.declaration.declarations.at(0);\n\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t!declaration ||\n\t\t\t\t\t\t\t\t\tdeclaration.id.type !== \"Identifier\" ||\n\t\t\t\t\t\t\t\t\tseenExports.has(declaration.id.name)\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst exportName = declaration.id.name;\n\t\t\t\t\t\t\t\tseenExports.add(exportName);\n\n\t\t\t\t\t\t\t\tconst islandIdentifier = identifier(`__wrap_${exportName}`);\n\n\t\t\t\t\t\t\t\tpath.insertBefore(\n\t\t\t\t\t\t\t\t\tvariableDeclaration(\"const\", [\n\t\t\t\t\t\t\t\t\t\tvariableDeclarator(islandIdentifier, declaration.init),\n\t\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tpath.replaceWith(\n\t\t\t\t\t\t\t\t\texportNamedDeclaration(\n\t\t\t\t\t\t\t\t\t\tvariableDeclaration(\"const\", [\n\t\t\t\t\t\t\t\t\t\t\tvariableDeclarator(\n\t\t\t\t\t\t\t\t\t\t\t\tidentifier(exportName),\n\t\t\t\t\t\t\t\t\t\t\t\tcallExpression(\n\t\t\t\t\t\t\t\t\t\t\t\t\tmemberExpression(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tidentifier(\"__runtime\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tidentifier(\"createIsland\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tislandIdentifier,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstringLiteral(exportName),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tidentifier(\"__assets\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else if (path.node.declaration?.type === \"FunctionDeclaration\") {\n\t\t\t\t\t\t\t\tconst declaration = path.node.declaration;\n\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t!declaration ||\n\t\t\t\t\t\t\t\t\tdeclaration.id?.type !== \"Identifier\" ||\n\t\t\t\t\t\t\t\t\tseenExports.has(declaration.id.name)\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst exportName = declaration.id.name;\n\t\t\t\t\t\t\t\tseenExports.add(exportName);\n\n\t\t\t\t\t\t\t\tconst islandIdentifier = identifier(`__wrap_${exportName}`);\n\n\t\t\t\t\t\t\t\tpath.insertBefore(\n\t\t\t\t\t\t\t\t\tfunctionDeclaration(\n\t\t\t\t\t\t\t\t\t\tislandIdentifier,\n\t\t\t\t\t\t\t\t\t\tdeclaration.params,\n\t\t\t\t\t\t\t\t\t\tdeclaration.body,\n\t\t\t\t\t\t\t\t\t\tdeclaration.generator,\n\t\t\t\t\t\t\t\t\t\tdeclaration.async,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tpath.replaceWith(\n\t\t\t\t\t\t\t\t\texportNamedDeclaration(\n\t\t\t\t\t\t\t\t\t\tvariableDeclaration(\"const\", [\n\t\t\t\t\t\t\t\t\t\t\tvariableDeclarator(\n\t\t\t\t\t\t\t\t\t\t\t\tidentifier(exportName),\n\t\t\t\t\t\t\t\t\t\t\t\tcallExpression(\n\t\t\t\t\t\t\t\t\t\t\t\t\tmemberExpression(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tidentifier(\"__runtime\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tidentifier(\"createIsland\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tislandIdentifier,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstringLiteral(exportName),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tidentifier(\"__assets\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\tconst result = generate(ast);\n\n\t\t\t\t\treturn { code: result.code, map: result.map };\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"yamf:islands:raw-import\",\n\t\t\ttransform: {\n\t\t\t\torder: \"post\",\n\t\t\t\thandler(code) {\n\t\t\t\t\tif (code.includes(\"__island_raw_import__\")) {\n\t\t\t\t\t\treturn code.replaceAll(\"__island_raw_import__\", \"import\");\n\t\t\t\t\t}\n\n\t\t\t\t\treturn undefined;\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t];\n};\n"],"mappings":";;;;;AAuBA,MAAM,WAAY,UAAU,WAAgC;AAE5D,MAAM,eAAe;AAErB,MAAa,gBAA0B;CACtC,OAAO,CACN;EACC,MAAM;EACN,WAAW;GACV,QAAQ,EAAE,IAAI,aAAa;GAC3B,QAAQ,MAAM,IAAI;IACjB,IAAI,KAAK,YAAY,SAAS,OAC7B;IAGD,MAAM,8BAAc,IAAI,IAAY;IAEpC,MAAM,MAAM,MAAM,MAAM;KACvB,YAAY;KACZ,SAAS,CAAC,cAAc,KAAK;IAC9B,CAAC;IAED,SAAS,KAAK;KACb,QAAQ,MAAM;MAGb,KAAK,iBACJ,QACA,kBACC,CAAC,yBAAyB,WAAW,WAAW,CAAC,CAAC,GAClD,cAAc,wBAAwB,CACvC,CACD;MAIA,KAAK,iBACJ,QACA,kBACC,CAAC,uBAAuB,WAAW,UAAU,CAAC,CAAC,GAC/C,cAAc,GAAG,GAAG,eAAe,CACpC,CACD;KACD;KACA,yBAAyB,MAAM;MAC9B,MAAM,kBAAkB,KAAK,KAAK,YAAY;MAE9C,IACC,EACC,oBAAoB,yBACpB,oBAAoB,wBACpB,oBAAoB,4BAGrB;MAGD,MAAM,mBACL,KAAK,KAAK,YAAY,SAAS,wBAC/B,KAAK,KAAK,YAAY,SAAS,4BAC5B,KAAK,KAAK,cACV,mBACA,MACA,KAAK,KAAK,YAAY,QACtB,KAAK,KAAK,YAAY,MACtB,KAAA,GACA,KAAK,KAAK,YAAY,KACvB;MAEH,MAAM,mBAAmB,WAAW,YAAY;MAEhD,KAAK,aACJ,oBAAoB,SAAS,CAC5B,mBAAmB,kBAAkB,gBAAgB,CACtD,CAAC,CACF;MAEA,KAAK,YACJ,yBACC,eACC,iBACC,WAAW,WAAW,GACtB,WAAW,cAAc,CAC1B,GACA;OACC;OACA,cAAc,SAAS;OACvB,WAAW,UAAU;MACtB,CACD,CACD,CACD;KACD;KACA,uBAAuB,MAAM;MAC5B,IAAI,KAAK,KAAK,aAAa,SAAS,uBAAuB;OAC1D,MAAM,cAAc,KAAK,KAAK,YAAY,aAAa,GAAG,CAAC;OAE3D,IACC,CAAC,eACD,YAAY,GAAG,SAAS,gBACxB,YAAY,IAAI,YAAY,GAAG,IAAI,GAEnC;OAED,MAAM,aAAa,YAAY,GAAG;OAClC,YAAY,IAAI,UAAU;OAE1B,MAAM,mBAAmB,WAAW,UAAU,YAAY;OAE1D,KAAK,aACJ,oBAAoB,SAAS,CAC5B,mBAAmB,kBAAkB,YAAY,IAAI,CACtD,CAAC,CACF;OAEA,KAAK,YACJ,uBACC,oBAAoB,SAAS,CAC5B,mBACC,WAAW,UAAU,GACrB,eACC,iBACC,WAAW,WAAW,GACtB,WAAW,cAAc,CAC1B,GACA;QACC;QACA,cAAc,UAAU;QACxB,WAAW,UAAU;OACtB,CACD,CACD,CACD,CAAC,CACF,CACD;MACD,OAAO,IAAI,KAAK,KAAK,aAAa,SAAS,uBAAuB;OACjE,MAAM,cAAc,KAAK,KAAK;OAE9B,IACC,CAAC,eACD,YAAY,IAAI,SAAS,gBACzB,YAAY,IAAI,YAAY,GAAG,IAAI,GAEnC;OAED,MAAM,aAAa,YAAY,GAAG;OAClC,YAAY,IAAI,UAAU;OAE1B,MAAM,mBAAmB,WAAW,UAAU,YAAY;OAE1D,KAAK,aACJ,oBACC,kBACA,YAAY,QACZ,YAAY,MACZ,YAAY,WACZ,YAAY,KACb,CACD;OAEA,KAAK,YACJ,uBACC,oBAAoB,SAAS,CAC5B,mBACC,WAAW,UAAU,GACrB,eACC,iBACC,WAAW,WAAW,GACtB,WAAW,cAAc,CAC1B,GACA;QACC;QACA,cAAc,UAAU;QACxB,WAAW,UAAU;OACtB,CACD,CACD,CACD,CAAC,CACF,CACD;MACD;KACD;IACD,CAAC;IAED,MAAM,SAAS,SAAS,GAAG;IAE3B,OAAO;KAAE,MAAM,OAAO;KAAM,KAAK,OAAO;IAAI;GAC7C;EACD;CACD,GACA;EACC,MAAM;EACN,WAAW;GACV,OAAO;GACP,QAAQ,MAAM;IACb,IAAI,KAAK,SAAS,uBAAuB,GACxC,OAAO,KAAK,WAAW,yBAAyB,QAAQ;GAI1D;EACD;CACD,CACD;AACD"}
|
|
1
|
+
{"version":3,"file":"islands.mjs","names":[],"sources":["../../src/vite/islands.ts"],"sourcesContent":["// reference https://github.com/hi-ogawa/vite-plugin-fullstack/blob/28e9540a68529c58842e9a3bf17d2193a065d524/examples/island/src/framework/island/plugin.ts\nimport { withMagicString } from \"rolldown-string\";\nimport type { ParserOptions, Plugin } from \"vite\";\nimport { Visitor } from \"vite\";\n\nconst ISLAND_REGEX = /\\.island\\.(j|t)sx?$/;\n\ntype Lang = NonNullable<ParserOptions[\"lang\"]>;\n\nfunction langFromId(id: string): Lang {\n\tif (id.endsWith(\".tsx\")) return \"tsx\";\n\tif (id.endsWith(\".ts\")) return \"ts\";\n\tif (id.endsWith(\".jsx\")) return \"jsx\";\n\treturn \"js\";\n}\n\nexport const islands = (): Plugin[] => {\n\treturn [\n\t\t{\n\t\t\tname: \"yamf:islands\",\n\t\t\ttransform: {\n\t\t\t\tfilter: { id: ISLAND_REGEX },\n\t\t\t\thandler: withMagicString(function (this, s, id) {\n\t\t\t\t\tif (this.environment.name !== \"ssr\") {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst program = this.parse(s.original, {\n\t\t\t\t\t\tlang: langFromId(id),\n\t\t\t\t\t});\n\n\t\t\t\t\t// prepend server island runtime + asset imports:\n\t\t\t\t\t// import * as __runtime from \"@pajecawav/yamf/server\";\n\t\t\t\t\t// import __assets from \"<id>?assets=client\";\n\t\t\t\t\ts.prepend(`import * as __runtime from \"@pajecawav/yamf/server\";\\n`);\n\t\t\t\t\ts.prepend(`import __assets from \"${id}?assets=client\";\\n`);\n\n\t\t\t\t\tconst seenExports = new Set<string>();\n\n\t\t\t\t\tnew Visitor({\n\t\t\t\t\t\tExportDefaultDeclaration(node) {\n\t\t\t\t\t\t\tconst declaration = node.declaration;\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tdeclaration.type !== \"FunctionDeclaration\" &&\n\t\t\t\t\t\t\t\tdeclaration.type !== \"FunctionExpression\" &&\n\t\t\t\t\t\t\t\tdeclaration.type !== \"ArrowFunctionExpression\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// export default <fn> ->\n\t\t\t\t\t\t\t// const __ISLAND__ = <fn>; export default __runtime.createIsland(__ISLAND__, \"default\", __assets)\n\t\t\t\t\t\t\ts.overwrite(node.start, declaration.start, \"const __ISLAND__ = \");\n\t\t\t\t\t\t\ts.appendRight(\n\t\t\t\t\t\t\t\tdeclaration.end,\n\t\t\t\t\t\t\t\t`; export default __runtime.createIsland(__ISLAND__, \"default\", __assets)`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tExportNamedDeclaration(node) {\n\t\t\t\t\t\t\tconst declaration = node.declaration;\n\t\t\t\t\t\t\tif (declaration?.type === \"VariableDeclaration\") {\n\t\t\t\t\t\t\t\tconst declarator = declaration.declarations[0];\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t!declarator ||\n\t\t\t\t\t\t\t\t\tdeclarator.id.type !== \"Identifier\" ||\n\t\t\t\t\t\t\t\t\t!declarator.init ||\n\t\t\t\t\t\t\t\t\tseenExports.has(declarator.id.name)\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst exportName = declarator.id.name;\n\t\t\t\t\t\t\t\tseenExports.add(exportName);\n\n\t\t\t\t\t\t\t\t// export const <name> = <init> ->\n\t\t\t\t\t\t\t\t// const __wrap_<name> = <init>; export const <name> = __runtime.createIsland(__wrap_<name>, \"<name>\", __assets)\n\t\t\t\t\t\t\t\ts.overwrite(\n\t\t\t\t\t\t\t\t\tnode.start,\n\t\t\t\t\t\t\t\t\tdeclarator.init.start,\n\t\t\t\t\t\t\t\t\t`const __wrap_${exportName} = `,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\ts.appendRight(\n\t\t\t\t\t\t\t\t\tdeclarator.init.end,\n\t\t\t\t\t\t\t\t\t`; export const ${exportName} = __runtime.createIsland(__wrap_${exportName}, \"${exportName}\", __assets)`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else if (declaration?.type === \"FunctionDeclaration\") {\n\t\t\t\t\t\t\t\tconst fnId = declaration.id;\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t!fnId ||\n\t\t\t\t\t\t\t\t\tfnId.type !== \"Identifier\" ||\n\t\t\t\t\t\t\t\t\tseenExports.has(fnId.name)\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst exportName = fnId.name;\n\t\t\t\t\t\t\t\tseenExports.add(exportName);\n\n\t\t\t\t\t\t\t\t// export function <name>() {} ->\n\t\t\t\t\t\t\t\t// function __wrap_<name>() {}; export const <name> = __runtime.createIsland(__wrap_<name>, \"<name>\", __assets)\n\t\t\t\t\t\t\t\ts.remove(node.start, declaration.start);\n\t\t\t\t\t\t\t\ts.overwrite(fnId.start, fnId.end, `__wrap_${exportName}`);\n\t\t\t\t\t\t\t\ts.appendRight(\n\t\t\t\t\t\t\t\t\tdeclaration.end,\n\t\t\t\t\t\t\t\t\t`; export const ${exportName} = __runtime.createIsland(__wrap_${exportName}, \"${exportName}\", __assets)`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t}).visit(program);\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"yamf:islands:raw-import\",\n\t\t\ttransform: {\n\t\t\t\torder: \"post\",\n\t\t\t\thandler: withMagicString(function (s) {\n\t\t\t\t\tif (s.original.includes(\"__island_raw_import__\")) {\n\t\t\t\t\t\ts.replaceAll(\"__island_raw_import__\", \"import\");\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t];\n};\n"],"mappings":";;;AAKA,MAAM,eAAe;AAIrB,SAAS,WAAW,IAAkB;CACrC,IAAI,GAAG,SAAS,MAAM,GAAG,OAAO;CAChC,IAAI,GAAG,SAAS,KAAK,GAAG,OAAO;CAC/B,IAAI,GAAG,SAAS,MAAM,GAAG,OAAO;CAChC,OAAO;AACR;AAEA,MAAa,gBAA0B;CACtC,OAAO,CACN;EACC,MAAM;EACN,WAAW;GACV,QAAQ,EAAE,IAAI,aAAa;GAC3B,SAAS,gBAAgB,SAAgB,GAAG,IAAI;IAC/C,IAAI,KAAK,YAAY,SAAS,OAC7B;IAGD,MAAM,UAAU,KAAK,MAAM,EAAE,UAAU,EACtC,MAAM,WAAW,EAAE,EACpB,CAAC;IAKD,EAAE,QAAQ,wDAAwD;IAClE,EAAE,QAAQ,yBAAyB,GAAG,mBAAmB;IAEzD,MAAM,8BAAc,IAAI,IAAY;IAEpC,IAAI,QAAQ;KACX,yBAAyB,MAAM;MAC9B,MAAM,cAAc,KAAK;MACzB,IACC,YAAY,SAAS,yBACrB,YAAY,SAAS,wBACrB,YAAY,SAAS,2BAErB;MAKD,EAAE,UAAU,KAAK,OAAO,YAAY,OAAO,qBAAqB;MAChE,EAAE,YACD,YAAY,KACZ,0EACD;KACD;KACA,uBAAuB,MAAM;MAC5B,MAAM,cAAc,KAAK;MACzB,IAAI,aAAa,SAAS,uBAAuB;OAChD,MAAM,aAAa,YAAY,aAAa;OAC5C,IACC,CAAC,cACD,WAAW,GAAG,SAAS,gBACvB,CAAC,WAAW,QACZ,YAAY,IAAI,WAAW,GAAG,IAAI,GAElC;OAED,MAAM,aAAa,WAAW,GAAG;OACjC,YAAY,IAAI,UAAU;OAI1B,EAAE,UACD,KAAK,OACL,WAAW,KAAK,OAChB,gBAAgB,WAAW,IAC5B;OACA,EAAE,YACD,WAAW,KAAK,KAChB,kBAAkB,WAAW,mCAAmC,WAAW,KAAK,WAAW,aAC5F;MACD,OAAO,IAAI,aAAa,SAAS,uBAAuB;OACvD,MAAM,OAAO,YAAY;OACzB,IACC,CAAC,QACD,KAAK,SAAS,gBACd,YAAY,IAAI,KAAK,IAAI,GAEzB;OAED,MAAM,aAAa,KAAK;OACxB,YAAY,IAAI,UAAU;OAI1B,EAAE,OAAO,KAAK,OAAO,YAAY,KAAK;OACtC,EAAE,UAAU,KAAK,OAAO,KAAK,KAAK,UAAU,YAAY;OACxD,EAAE,YACD,YAAY,KACZ,kBAAkB,WAAW,mCAAmC,WAAW,KAAK,WAAW,aAC5F;MACD;KACD;IACD,CAAC,CAAC,CAAC,MAAM,OAAO;GACjB,CAAC;EACF;CACD,GACA;EACC,MAAM;EACN,WAAW;GACV,OAAO;GACP,SAAS,gBAAgB,SAAU,GAAG;IACrC,IAAI,EAAE,SAAS,SAAS,uBAAuB,GAC9C,EAAE,WAAW,yBAAyB,QAAQ;GAEhD,CAAC;EACF;CACD,CACD;AACD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { js } from "./shared/utils.mjs";
|
|
2
|
+
//#region src/vite/virtual-error-handler.ts
|
|
3
|
+
const virtualErrorHandler = (options) => {
|
|
4
|
+
const virtualModuleId = "virtual:yamf:error-handler";
|
|
5
|
+
const resolvedVirtualModuleId = "\0virtual:yamf:error-handler";
|
|
6
|
+
return {
|
|
7
|
+
name: "yamf:virtual-error-handler",
|
|
8
|
+
resolveId(id) {
|
|
9
|
+
if (id === virtualModuleId) return resolvedVirtualModuleId;
|
|
10
|
+
},
|
|
11
|
+
load(id) {
|
|
12
|
+
if (id !== resolvedVirtualModuleId) return;
|
|
13
|
+
const errorHandler = options?.nitro?.errorHandler;
|
|
14
|
+
if (!errorHandler) return js`export const errorHandler = null;`;
|
|
15
|
+
if (Array.isArray(errorHandler)) throw new Error("Multiple error handlers are not supported");
|
|
16
|
+
return js`export { default as errorHandler } from "${errorHandler}";`;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { virtualErrorHandler };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=virtual-error-handler.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"virtual-error-handler.mjs","names":[],"sources":["../../src/vite/virtual-error-handler.ts"],"sourcesContent":["import type { NitroPluginConfig } from \"nitro/vite\";\nimport type { Plugin } from \"vite\";\nimport { js } from \"../shared/utils\";\n\nexport interface VirtualErrorHandlerOptions {\n\tnitro?: NitroPluginConfig;\n}\n\nexport const virtualErrorHandler = (options?: VirtualErrorHandlerOptions): Plugin => {\n\tconst virtualModuleId = \"virtual:yamf:error-handler\";\n\tconst resolvedVirtualModuleId = \"\\0\" + virtualModuleId;\n\n\treturn {\n\t\tname: \"yamf:virtual-error-handler\",\n\t\tresolveId(id) {\n\t\t\tif (id === virtualModuleId) {\n\t\t\t\treturn resolvedVirtualModuleId;\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t},\n\t\tload(id) {\n\t\t\tif (id !== resolvedVirtualModuleId) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst errorHandler = options?.nitro?.errorHandler;\n\n\t\t\tif (!errorHandler) {\n\t\t\t\treturn js`export const errorHandler = null;`;\n\t\t\t}\n\n\t\t\tif (Array.isArray(errorHandler)) {\n\t\t\t\tthrow new Error(\"Multiple error handlers are not supported\");\n\t\t\t}\n\n\t\t\treturn js`export { default as errorHandler } from \"${errorHandler}\";`;\n\t\t},\n\t};\n};\n"],"mappings":";;AAQA,MAAa,uBAAuB,YAAiD;CACpF,MAAM,kBAAkB;CACxB,MAAM,0BAA0B;CAEhC,OAAO;EACN,MAAM;EACN,UAAU,IAAI;GACb,IAAI,OAAO,iBACV,OAAO;EAIT;EACA,KAAK,IAAI;GACR,IAAI,OAAO,yBACV;GAGD,MAAM,eAAe,SAAS,OAAO;GAErC,IAAI,CAAC,cACJ,OAAO,EAAE;GAGV,IAAI,MAAM,QAAQ,YAAY,GAC7B,MAAM,IAAI,MAAM,2CAA2C;GAG5D,OAAO,EAAE,4CAA4C,aAAa;EACnE;CACD;AACD"}
|
|
@@ -34,6 +34,7 @@ const virtualTemplate = () => {
|
|
|
34
34
|
if (relative(file, TEMPLATE_PATH) === "") {
|
|
35
35
|
const mod = server.moduleGraph.getModuleById(resolvedVirtualModuleId);
|
|
36
36
|
if (mod) server.moduleGraph.invalidateModule(mod);
|
|
37
|
+
server.ws.send({ type: "full-reload" });
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"virtual-template.mjs","names":[],"sources":["../../src/vite/virtual-template.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { relative } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { js } from \"../shared/utils\";\n\nconst TEMPLATE_PATH = \"./src/template.html\";\n\nconst DEFAULT_TEMPLATE = /* html */ `\n<!DOCTYPE html>\n<html>\n <head></head>\n <body>\n <!--ssr-outlet-->\n </body>\n</html>\n`.trim();\n\nexport const virtualTemplate = (): Plugin => {\n\tconst virtualModuleId = \"virtual:yamf:template\";\n\tconst resolvedVirtualModuleId = \"\\0\" + virtualModuleId;\n\n\treturn {\n\t\tname: \"yamf:virtual-template\",\n\t\tresolveId(id) {\n\t\t\tif (id === virtualModuleId) {\n\t\t\t\treturn resolvedVirtualModuleId;\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t},\n\t\tasync load(id) {\n\t\t\tif (id !== resolvedVirtualModuleId) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst template = await readFile(TEMPLATE_PATH, \"utf8\");\n\n\t\t\t\treturn js`export const template = ${JSON.stringify(template)};`;\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n\t\t\t\t\treturn js`export const template = ${JSON.stringify(DEFAULT_TEMPLATE)};`;\n\t\t\t\t}\n\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\thandleHotUpdate({ file, server }) {\n\t\t\tif (relative(file, TEMPLATE_PATH) === \"\") {\n\t\t\t\tconst mod = server.moduleGraph.getModuleById(resolvedVirtualModuleId);\n\n\t\t\t\tif (mod) {\n\t\t\t\t\tserver.moduleGraph.invalidateModule(mod);\n\t\t\t\t}\n\n\t\t\t\
|
|
1
|
+
{"version":3,"file":"virtual-template.mjs","names":[],"sources":["../../src/vite/virtual-template.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { relative } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { js } from \"../shared/utils\";\n\nconst TEMPLATE_PATH = \"./src/template.html\";\n\nconst DEFAULT_TEMPLATE = /* html */ `\n<!DOCTYPE html>\n<html>\n <head></head>\n <body>\n <!--ssr-outlet-->\n </body>\n</html>\n`.trim();\n\nexport const virtualTemplate = (): Plugin => {\n\tconst virtualModuleId = \"virtual:yamf:template\";\n\tconst resolvedVirtualModuleId = \"\\0\" + virtualModuleId;\n\n\treturn {\n\t\tname: \"yamf:virtual-template\",\n\t\tresolveId(id) {\n\t\t\tif (id === virtualModuleId) {\n\t\t\t\treturn resolvedVirtualModuleId;\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t},\n\t\tasync load(id) {\n\t\t\tif (id !== resolvedVirtualModuleId) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst template = await readFile(TEMPLATE_PATH, \"utf8\");\n\n\t\t\t\treturn js`export const template = ${JSON.stringify(template)};`;\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n\t\t\t\t\treturn js`export const template = ${JSON.stringify(DEFAULT_TEMPLATE)};`;\n\t\t\t\t}\n\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\thandleHotUpdate({ file, server }) {\n\t\t\tif (relative(file, TEMPLATE_PATH) === \"\") {\n\t\t\t\tconst mod = server.moduleGraph.getModuleById(resolvedVirtualModuleId);\n\n\t\t\t\tif (mod) {\n\t\t\t\t\tserver.moduleGraph.invalidateModule(mod);\n\t\t\t\t}\n\n\t\t\t\tserver.ws.send({ type: \"full-reload\" });\n\t\t\t}\n\t\t},\n\t};\n};\n"],"mappings":";;;;AAKA,MAAM,gBAAgB;AAEtB,MAAM,mBAA8B;;;;;;;;EAQlC,KAAK;AAEP,MAAa,wBAAgC;CAC5C,MAAM,kBAAkB;CACxB,MAAM,0BAA0B;CAEhC,OAAO;EACN,MAAM;EACN,UAAU,IAAI;GACb,IAAI,OAAO,iBACV,OAAO;EAIT;EACA,MAAM,KAAK,IAAI;GACd,IAAI,OAAO,yBACV;GAGD,IAAI;IACH,MAAM,WAAW,MAAM,SAAS,eAAe,MAAM;IAErD,OAAO,EAAE,2BAA2B,KAAK,UAAU,QAAQ,EAAE;GAC9D,SAAS,OAAO;IACf,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAC/D,OAAO,EAAE,2BAA2B,KAAK,UAAU,gBAAgB,EAAE;IAGtE,MAAM;GACP;EACD;EACA,gBAAgB,EAAE,MAAM,UAAU;GACjC,IAAI,SAAS,MAAM,aAAa,MAAM,IAAI;IACzC,MAAM,MAAM,OAAO,YAAY,cAAc,uBAAuB;IAEpE,IAAI,KACH,OAAO,YAAY,iBAAiB,GAAG;IAGxC,OAAO,GAAG,KAAK,EAAE,MAAM,cAAc,CAAC;GACvC;EACD;CACD;AACD"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pajecawav/yamf",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.7",
|
|
5
5
|
"description": "Yet another meta framework",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://github.com/pajecawav/yamf#readme",
|
|
@@ -30,40 +30,36 @@
|
|
|
30
30
|
"access": "public"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"hono": "^4.12.
|
|
34
|
-
"vite": "^8.
|
|
33
|
+
"hono": "^4.12.30",
|
|
34
|
+
"vite": "^8.1.5"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@
|
|
38
|
-
"
|
|
39
|
-
"@babel/traverse": "^7.29.7",
|
|
40
|
-
"@babel/types": "^7.29.7",
|
|
41
|
-
"devalue": "^5.8.1",
|
|
37
|
+
"@hono/react-compat": "^0.0.3",
|
|
38
|
+
"devalue": "^5.8.2",
|
|
42
39
|
"exsolve": "^1.1.0",
|
|
43
40
|
"nitro": "3.0.260610-beta",
|
|
41
|
+
"rolldown-string": "^0.3.1",
|
|
44
42
|
"rou3": "^0.9.1",
|
|
45
43
|
"ufo": "^1.6.4",
|
|
46
|
-
"unhead": "^3.
|
|
44
|
+
"unhead": "^3.2.3",
|
|
45
|
+
"wouter": "^3.10.0"
|
|
47
46
|
},
|
|
48
47
|
"devDependencies": {
|
|
49
|
-
"@
|
|
50
|
-
"@
|
|
51
|
-
"@
|
|
48
|
+
"@hono/react-compat": "^0.0.3",
|
|
49
|
+
"@pajecawav/tools": "^0.0.7",
|
|
50
|
+
"@playwright/test": "^1.61.1",
|
|
52
51
|
"@types/node": "^24.13.3",
|
|
53
|
-
"@vitest/coverage-v8": "^4.1.10",
|
|
54
|
-
"@vitest/ui": "^4.1.10",
|
|
55
52
|
"cross-env": "^10.1.0",
|
|
56
|
-
"hono": "^4.12.
|
|
53
|
+
"hono": "^4.12.32",
|
|
57
54
|
"husky": "^9.1.7",
|
|
58
55
|
"npm-run-all2": "^9.0.2",
|
|
59
|
-
"oxfmt": "^0.
|
|
60
|
-
"oxlint": "^1.
|
|
61
|
-
"oxlint-tsgolint": "^0.
|
|
62
|
-
"publint": "^0.3.
|
|
63
|
-
"tsdown": "0.22.
|
|
56
|
+
"oxfmt": "^0.60.0",
|
|
57
|
+
"oxlint": "^1.75.0",
|
|
58
|
+
"oxlint-tsgolint": "^7.0.2001",
|
|
59
|
+
"publint": "^0.3.22",
|
|
60
|
+
"tsdown": "0.22.14",
|
|
64
61
|
"typescript": "~7.0.2",
|
|
65
|
-
"vite": "^8.
|
|
66
|
-
"vitest": "^4.1.10"
|
|
62
|
+
"vite": "^8.1.5"
|
|
67
63
|
},
|
|
68
64
|
"scripts": {
|
|
69
65
|
"build": "tsdown",
|
|
@@ -71,10 +67,8 @@
|
|
|
71
67
|
"play": "pnpm --filter=playground dev",
|
|
72
68
|
"play:build": "pnpm --filter=playground build",
|
|
73
69
|
"play:preview": "pnpm --filter=playground preview",
|
|
74
|
-
"test": "
|
|
75
|
-
"test:
|
|
76
|
-
"test:coverage": "vitest run --coverage",
|
|
77
|
-
"test:ui": "vitest --ui",
|
|
70
|
+
"test": "playwright test",
|
|
71
|
+
"test:ui": "playwright test --ui",
|
|
78
72
|
"lint": "cross-env FORCE_COLOR=1 run-p -l lint:*",
|
|
79
73
|
"lint:oxlint": "oxlint .",
|
|
80
74
|
"lint:tsc": "tsc -b --noEmit",
|
package/src/island/client.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { parse } from "devalue";
|
|
|
2
2
|
import type { FC } from "hono/jsx";
|
|
3
3
|
import { hydrateRoot } from "hono/jsx/dom/client";
|
|
4
4
|
import { withLeadingSlash } from "ufo";
|
|
5
|
-
import type {
|
|
5
|
+
import type { IslandClientDirectiveSerialized } from "./types";
|
|
6
6
|
|
|
7
7
|
declare let __island_raw_import__: <T>(file: string) => Promise<T>;
|
|
8
8
|
|
|
@@ -36,7 +36,7 @@ customElements.define(
|
|
|
36
36
|
const islandEntry = this.getAttribute("island-entry");
|
|
37
37
|
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
38
38
|
const islandClient = (this.getAttribute("island-client") ??
|
|
39
|
-
"load") as
|
|
39
|
+
"load") as IslandClientDirectiveSerialized;
|
|
40
40
|
|
|
41
41
|
if (!islandSrc) {
|
|
42
42
|
throw new Error("Missing island-src attribute");
|
|
@@ -62,7 +62,7 @@ customElements.define(
|
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
switch (islandClient) {
|
|
65
|
-
case true:
|
|
65
|
+
case "true":
|
|
66
66
|
case "load":
|
|
67
67
|
initIsland();
|
|
68
68
|
break;
|
|
@@ -78,7 +78,7 @@ customElements.define(
|
|
|
78
78
|
initIsland();
|
|
79
79
|
}
|
|
80
80
|
break;
|
|
81
|
-
case false:
|
|
81
|
+
case "false":
|
|
82
82
|
case "skip":
|
|
83
83
|
break;
|
|
84
84
|
default:
|
package/src/island/types.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export type IslandClientDirective = "load" | "idle" | "visible" | "skip" | boolean;
|
|
2
|
+
export type IslandClientDirectiveSerialized =
|
|
3
|
+
| "load"
|
|
4
|
+
| "idle"
|
|
5
|
+
| "visible"
|
|
6
|
+
| "skip"
|
|
7
|
+
| "true"
|
|
8
|
+
| "false";
|
|
2
9
|
|
|
3
10
|
export interface IslandProps {
|
|
4
11
|
"yamf-client"?: IslandClientDirective;
|
package/src/page.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import { createStreamableHead, wrapStream } from "unhead/stream/server";
|
|
|
10
10
|
import type { ResolvableLink, UseSeoMetaInput } from "unhead/types";
|
|
11
11
|
import { Root as RootComponent } from "virtual:yamf:root";
|
|
12
12
|
import { template } from "virtual:yamf:template";
|
|
13
|
+
import { Router } from "wouter";
|
|
13
14
|
import { SSRContext } from "./context/ssr";
|
|
14
15
|
import type { ImportAssetsResult } from "./shared/assets";
|
|
15
16
|
import { YamfHead } from "./shared/head";
|
|
@@ -59,7 +60,9 @@ export const definePage = (options: DefinePageOptions): PageHandler => {
|
|
|
59
60
|
|
|
60
61
|
const App = async () => (
|
|
61
62
|
<SSRContext value={{ head, event }}>
|
|
62
|
-
<
|
|
63
|
+
<Router ssrPath={event.url.pathname} ssrSearch={event.url.search}>
|
|
64
|
+
<Root>{content}</Root>
|
|
65
|
+
</Router>
|
|
63
66
|
</SSRContext>
|
|
64
67
|
);
|
|
65
68
|
|
package/src/server/entry.tsx
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
defineHandler,
|
|
4
|
+
EventHandler,
|
|
5
|
+
H3Event,
|
|
6
|
+
HTTPError,
|
|
7
|
+
writeEarlyHints,
|
|
8
|
+
type EventHandlerRequest,
|
|
9
|
+
type EventHandlerWithFetch,
|
|
10
|
+
} from "nitro/h3";
|
|
4
11
|
import { addRoute, createRouter, findRoute } from "rou3";
|
|
5
12
|
import { withLeadingSlash, withoutTrailingSlash } from "ufo";
|
|
6
13
|
import { clientAssets } from "virtual:yamf:assets";
|
|
14
|
+
import { errorHandler } from "virtual:yamf:error-handler";
|
|
7
15
|
import { pages, assets as pagesServerAssets } from "virtual:yamf:pages";
|
|
8
16
|
import { rootAssets } from "virtual:yamf:root";
|
|
9
17
|
import type { PageHandler } from "#/page";
|
|
@@ -52,7 +60,7 @@ export interface DefineServerEntryOptions {
|
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
export const defineServerEntry = (options?: DefineServerEntryOptions): ServerEntry => {
|
|
55
|
-
|
|
63
|
+
const rootHandler: EventHandler<EventHandlerRequest, Promise<unknown>> = async event => {
|
|
56
64
|
const route = findRoute(router, "GET", event.url.pathname);
|
|
57
65
|
|
|
58
66
|
if (!route) {
|
|
@@ -79,5 +87,19 @@ export const defineServerEntry = (options?: DefineServerEntryOptions): ServerEnt
|
|
|
79
87
|
assets,
|
|
80
88
|
head: typeof options?.head === "function" ? options.head(event) : options?.head,
|
|
81
89
|
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return defineHandler(async event => {
|
|
93
|
+
if (!errorHandler) {
|
|
94
|
+
return rootHandler(event);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
return await rootHandler(event);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
const httpError = error instanceof HTTPError ? error : new HTTPError({ cause: error });
|
|
101
|
+
|
|
102
|
+
return errorHandler(httpError, event);
|
|
103
|
+
}
|
|
82
104
|
});
|
|
83
105
|
};
|
package/src/virtual.d.ts
CHANGED
|
@@ -18,3 +18,11 @@ declare module "virtual:yamf:root" {
|
|
|
18
18
|
|
|
19
19
|
export { Root, rootAssets };
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
declare module "virtual:yamf:error-handler" {
|
|
23
|
+
import type { H3Event, HTTPError } from "nitro/h3";
|
|
24
|
+
|
|
25
|
+
type ErrorHandler = (error: HTTPError, event: H3Event) => Promise<Response | undefined>;
|
|
26
|
+
|
|
27
|
+
export const errorHandler: ErrorHandler | null;
|
|
28
|
+
}
|
package/src/vite/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { nitro } from "nitro/vite";
|
|
|
4
4
|
import type { EnvironmentOptions, PluginOption } from "vite";
|
|
5
5
|
import { islands } from "./islands";
|
|
6
6
|
import { virtualAssets } from "./virtual-assets";
|
|
7
|
+
import { virtualErrorHandler } from "./virtual-error-handler";
|
|
7
8
|
import { virtualPages } from "./virtual-pages";
|
|
8
9
|
import { virtualRoot } from "./virtual-root";
|
|
9
10
|
import { virtualTemplate } from "./virtual-template";
|
|
@@ -32,7 +33,10 @@ const yamf = (options?: YamfOptions): PluginOption[] => {
|
|
|
32
33
|
return {
|
|
33
34
|
ssr: {
|
|
34
35
|
// we need to inline because otherwise fullstack plugin fails to build manifest for assets imports
|
|
35
|
-
|
|
36
|
+
// also, we need to bundle everything so that the `react` -> `@hono/react-compat` alias
|
|
37
|
+
// is applied to all dependencies (e.g. wouter) in dev mode. Externalized deps are
|
|
38
|
+
// loaded natively by Node, bypassing Vite's resolve.alias.
|
|
39
|
+
noExternal: true,
|
|
36
40
|
},
|
|
37
41
|
optimizeDeps: {
|
|
38
42
|
include: [
|
|
@@ -43,6 +47,19 @@ const yamf = (options?: YamfOptions): PluginOption[] => {
|
|
|
43
47
|
"ufo",
|
|
44
48
|
],
|
|
45
49
|
},
|
|
50
|
+
resolve: {
|
|
51
|
+
alias: [
|
|
52
|
+
{ find: "react", replacement: "@hono/react-compat" },
|
|
53
|
+
{ find: "react-dom", replacement: "@hono/react-compat" },
|
|
54
|
+
// use-sync-external-store is a CJS package that requires("react").
|
|
55
|
+
// Vite's SSR module runner can't process CJS, so we alias it to
|
|
56
|
+
// @hono/react-compat which exports useSyncExternalStore from hono/jsx.
|
|
57
|
+
{
|
|
58
|
+
find: /^use-sync-external-store(?:\/shim(?:\/.*)?)?$/,
|
|
59
|
+
replacement: "@hono/react-compat",
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
46
63
|
environments: {
|
|
47
64
|
...(existsSync("./src/server.tsx") ? { ssr: ssrEnv } : {}),
|
|
48
65
|
client: {
|
|
@@ -59,6 +76,7 @@ const yamf = (options?: YamfOptions): PluginOption[] => {
|
|
|
59
76
|
|
|
60
77
|
plugins.push(islands());
|
|
61
78
|
|
|
79
|
+
plugins.push(virtualErrorHandler(options));
|
|
62
80
|
plugins.push(virtualAssets());
|
|
63
81
|
plugins.push(virtualPages());
|
|
64
82
|
plugins.push(virtualTemplate());
|
package/src/vite/islands.ts
CHANGED
|
@@ -1,227 +1,122 @@
|
|
|
1
1
|
// reference https://github.com/hi-ogawa/vite-plugin-fullstack/blob/28e9540a68529c58842e9a3bf17d2193a065d524/examples/island/src/framework/island/plugin.ts
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
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
|
-
// @ts-ignore
|
|
23
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
24
|
-
const traverse = (_traverse.default as typeof _traverse) ?? _traverse;
|
|
2
|
+
import { withMagicString } from "rolldown-string";
|
|
3
|
+
import type { ParserOptions, Plugin } from "vite";
|
|
4
|
+
import { Visitor } from "vite";
|
|
25
5
|
|
|
26
6
|
const ISLAND_REGEX = /\.island\.(j|t)sx?$/;
|
|
27
7
|
|
|
8
|
+
type Lang = NonNullable<ParserOptions["lang"]>;
|
|
9
|
+
|
|
10
|
+
function langFromId(id: string): Lang {
|
|
11
|
+
if (id.endsWith(".tsx")) return "tsx";
|
|
12
|
+
if (id.endsWith(".ts")) return "ts";
|
|
13
|
+
if (id.endsWith(".jsx")) return "jsx";
|
|
14
|
+
return "js";
|
|
15
|
+
}
|
|
16
|
+
|
|
28
17
|
export const islands = (): Plugin[] => {
|
|
29
18
|
return [
|
|
30
19
|
{
|
|
31
20
|
name: "yamf:islands",
|
|
32
21
|
transform: {
|
|
33
22
|
filter: { id: ISLAND_REGEX },
|
|
34
|
-
handler(
|
|
23
|
+
handler: withMagicString(function (this, s, id) {
|
|
35
24
|
if (this.environment.name !== "ssr") {
|
|
36
25
|
return;
|
|
37
26
|
}
|
|
38
27
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const ast = parse(code, {
|
|
42
|
-
sourceType: "module",
|
|
43
|
-
plugins: ["typescript", "jsx"],
|
|
28
|
+
const program = this.parse(s.original, {
|
|
29
|
+
lang: langFromId(id),
|
|
44
30
|
});
|
|
45
31
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"body",
|
|
52
|
-
importDeclaration(
|
|
53
|
-
[importNamespaceSpecifier(identifier("__runtime"))],
|
|
54
|
-
stringLiteral("@pajecawav/yamf/server"),
|
|
55
|
-
),
|
|
56
|
-
);
|
|
32
|
+
// prepend server island runtime + asset imports:
|
|
33
|
+
// import * as __runtime from "@pajecawav/yamf/server";
|
|
34
|
+
// import __assets from "<id>?assets=client";
|
|
35
|
+
s.prepend(`import * as __runtime from "@pajecawav/yamf/server";\n`);
|
|
36
|
+
s.prepend(`import __assets from "${id}?assets=client";\n`);
|
|
57
37
|
|
|
58
|
-
|
|
59
|
-
// import __assets from "MODULE_ID?assets=client";
|
|
60
|
-
path.unshiftContainer(
|
|
61
|
-
"body",
|
|
62
|
-
importDeclaration(
|
|
63
|
-
[importDefaultSpecifier(identifier("__assets"))],
|
|
64
|
-
stringLiteral(`${id}?assets=client`),
|
|
65
|
-
),
|
|
66
|
-
);
|
|
67
|
-
},
|
|
68
|
-
ExportDefaultDeclaration(path) {
|
|
69
|
-
const declarationType = path.node.declaration.type;
|
|
38
|
+
const seenExports = new Set<string>();
|
|
70
39
|
|
|
40
|
+
new Visitor({
|
|
41
|
+
ExportDefaultDeclaration(node) {
|
|
42
|
+
const declaration = node.declaration;
|
|
71
43
|
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
declarationType === "ArrowFunctionExpression"
|
|
76
|
-
)
|
|
44
|
+
declaration.type !== "FunctionDeclaration" &&
|
|
45
|
+
declaration.type !== "FunctionExpression" &&
|
|
46
|
+
declaration.type !== "ArrowFunctionExpression"
|
|
77
47
|
) {
|
|
78
48
|
return;
|
|
79
49
|
}
|
|
80
50
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
path.node.declaration.params,
|
|
88
|
-
path.node.declaration.body,
|
|
89
|
-
undefined,
|
|
90
|
-
path.node.declaration.async,
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const islandIdentifier = identifier("__ISLAND__");
|
|
94
|
-
|
|
95
|
-
path.insertBefore(
|
|
96
|
-
variableDeclaration("const", [
|
|
97
|
-
variableDeclarator(islandIdentifier, originalFunction),
|
|
98
|
-
]),
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
path.replaceWith(
|
|
102
|
-
exportDefaultDeclaration(
|
|
103
|
-
callExpression(
|
|
104
|
-
memberExpression(
|
|
105
|
-
identifier("__runtime"),
|
|
106
|
-
identifier("createIsland"),
|
|
107
|
-
),
|
|
108
|
-
[
|
|
109
|
-
islandIdentifier,
|
|
110
|
-
stringLiteral("default"),
|
|
111
|
-
identifier("__assets"),
|
|
112
|
-
],
|
|
113
|
-
),
|
|
114
|
-
),
|
|
51
|
+
// export default <fn> ->
|
|
52
|
+
// const __ISLAND__ = <fn>; export default __runtime.createIsland(__ISLAND__, "default", __assets)
|
|
53
|
+
s.overwrite(node.start, declaration.start, "const __ISLAND__ = ");
|
|
54
|
+
s.appendRight(
|
|
55
|
+
declaration.end,
|
|
56
|
+
`; export default __runtime.createIsland(__ISLAND__, "default", __assets)`,
|
|
115
57
|
);
|
|
116
58
|
},
|
|
117
|
-
ExportNamedDeclaration(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
59
|
+
ExportNamedDeclaration(node) {
|
|
60
|
+
const declaration = node.declaration;
|
|
61
|
+
if (declaration?.type === "VariableDeclaration") {
|
|
62
|
+
const declarator = declaration.declarations[0];
|
|
121
63
|
if (
|
|
122
|
-
!
|
|
123
|
-
|
|
124
|
-
|
|
64
|
+
!declarator ||
|
|
65
|
+
declarator.id.type !== "Identifier" ||
|
|
66
|
+
!declarator.init ||
|
|
67
|
+
seenExports.has(declarator.id.name)
|
|
125
68
|
) {
|
|
126
69
|
return;
|
|
127
70
|
}
|
|
128
|
-
const exportName =
|
|
71
|
+
const exportName = declarator.id.name;
|
|
129
72
|
seenExports.add(exportName);
|
|
130
73
|
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
74
|
+
// export const <name> = <init> ->
|
|
75
|
+
// const __wrap_<name> = <init>; export const <name> = __runtime.createIsland(__wrap_<name>, "<name>", __assets)
|
|
76
|
+
s.overwrite(
|
|
77
|
+
node.start,
|
|
78
|
+
declarator.init.start,
|
|
79
|
+
`const __wrap_${exportName} = `,
|
|
137
80
|
);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
variableDeclaration("const", [
|
|
142
|
-
variableDeclarator(
|
|
143
|
-
identifier(exportName),
|
|
144
|
-
callExpression(
|
|
145
|
-
memberExpression(
|
|
146
|
-
identifier("__runtime"),
|
|
147
|
-
identifier("createIsland"),
|
|
148
|
-
),
|
|
149
|
-
[
|
|
150
|
-
islandIdentifier,
|
|
151
|
-
stringLiteral(exportName),
|
|
152
|
-
identifier("__assets"),
|
|
153
|
-
],
|
|
154
|
-
),
|
|
155
|
-
),
|
|
156
|
-
]),
|
|
157
|
-
),
|
|
81
|
+
s.appendRight(
|
|
82
|
+
declarator.init.end,
|
|
83
|
+
`; export const ${exportName} = __runtime.createIsland(__wrap_${exportName}, "${exportName}", __assets)`,
|
|
158
84
|
);
|
|
159
|
-
} else if (
|
|
160
|
-
const
|
|
161
|
-
|
|
85
|
+
} else if (declaration?.type === "FunctionDeclaration") {
|
|
86
|
+
const fnId = declaration.id;
|
|
162
87
|
if (
|
|
163
|
-
!
|
|
164
|
-
|
|
165
|
-
seenExports.has(
|
|
88
|
+
!fnId ||
|
|
89
|
+
fnId.type !== "Identifier" ||
|
|
90
|
+
seenExports.has(fnId.name)
|
|
166
91
|
) {
|
|
167
92
|
return;
|
|
168
93
|
}
|
|
169
|
-
const exportName =
|
|
94
|
+
const exportName = fnId.name;
|
|
170
95
|
seenExports.add(exportName);
|
|
171
96
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
declaration.generator,
|
|
180
|
-
declaration.async,
|
|
181
|
-
),
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
path.replaceWith(
|
|
185
|
-
exportNamedDeclaration(
|
|
186
|
-
variableDeclaration("const", [
|
|
187
|
-
variableDeclarator(
|
|
188
|
-
identifier(exportName),
|
|
189
|
-
callExpression(
|
|
190
|
-
memberExpression(
|
|
191
|
-
identifier("__runtime"),
|
|
192
|
-
identifier("createIsland"),
|
|
193
|
-
),
|
|
194
|
-
[
|
|
195
|
-
islandIdentifier,
|
|
196
|
-
stringLiteral(exportName),
|
|
197
|
-
identifier("__assets"),
|
|
198
|
-
],
|
|
199
|
-
),
|
|
200
|
-
),
|
|
201
|
-
]),
|
|
202
|
-
),
|
|
97
|
+
// export function <name>() {} ->
|
|
98
|
+
// function __wrap_<name>() {}; export const <name> = __runtime.createIsland(__wrap_<name>, "<name>", __assets)
|
|
99
|
+
s.remove(node.start, declaration.start);
|
|
100
|
+
s.overwrite(fnId.start, fnId.end, `__wrap_${exportName}`);
|
|
101
|
+
s.appendRight(
|
|
102
|
+
declaration.end,
|
|
103
|
+
`; export const ${exportName} = __runtime.createIsland(__wrap_${exportName}, "${exportName}", __assets)`,
|
|
203
104
|
);
|
|
204
105
|
}
|
|
205
106
|
},
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const result = generate(ast);
|
|
209
|
-
|
|
210
|
-
return { code: result.code, map: result.map };
|
|
211
|
-
},
|
|
107
|
+
}).visit(program);
|
|
108
|
+
}),
|
|
212
109
|
},
|
|
213
110
|
},
|
|
214
111
|
{
|
|
215
112
|
name: "yamf:islands:raw-import",
|
|
216
113
|
transform: {
|
|
217
114
|
order: "post",
|
|
218
|
-
handler(
|
|
219
|
-
if (
|
|
220
|
-
|
|
115
|
+
handler: withMagicString(function (s) {
|
|
116
|
+
if (s.original.includes("__island_raw_import__")) {
|
|
117
|
+
s.replaceAll("__island_raw_import__", "import");
|
|
221
118
|
}
|
|
222
|
-
|
|
223
|
-
return undefined;
|
|
224
|
-
},
|
|
119
|
+
}),
|
|
225
120
|
},
|
|
226
121
|
},
|
|
227
122
|
];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { NitroPluginConfig } from "nitro/vite";
|
|
2
|
+
import type { Plugin } from "vite";
|
|
3
|
+
import { js } from "../shared/utils";
|
|
4
|
+
|
|
5
|
+
export interface VirtualErrorHandlerOptions {
|
|
6
|
+
nitro?: NitroPluginConfig;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const virtualErrorHandler = (options?: VirtualErrorHandlerOptions): Plugin => {
|
|
10
|
+
const virtualModuleId = "virtual:yamf:error-handler";
|
|
11
|
+
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
name: "yamf:virtual-error-handler",
|
|
15
|
+
resolveId(id) {
|
|
16
|
+
if (id === virtualModuleId) {
|
|
17
|
+
return resolvedVirtualModuleId;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return undefined;
|
|
21
|
+
},
|
|
22
|
+
load(id) {
|
|
23
|
+
if (id !== resolvedVirtualModuleId) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const errorHandler = options?.nitro?.errorHandler;
|
|
28
|
+
|
|
29
|
+
if (!errorHandler) {
|
|
30
|
+
return js`export const errorHandler = null;`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (Array.isArray(errorHandler)) {
|
|
34
|
+
throw new Error("Multiple error handlers are not supported");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return js`export { default as errorHandler } from "${errorHandler}";`;
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
};
|