@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.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/dist/client/index.d.ts +1 -0
  3. package/dist/client/index.js +1 -0
  4. package/dist/components/Head.d.ts +8 -0
  5. package/dist/components/Head.d.ts.map +1 -0
  6. package/dist/components/Head.js +9 -0
  7. package/dist/components/Head.js.map +1 -0
  8. package/dist/context/ssr.d.ts +13 -0
  9. package/dist/context/ssr.d.ts.map +1 -0
  10. package/dist/context/ssr.js +10 -0
  11. package/dist/context/ssr.js.map +1 -0
  12. package/dist/hooks/useEvent.d.ts +8 -0
  13. package/dist/hooks/useEvent.d.ts.map +1 -0
  14. package/dist/hooks/useEvent.js +11 -0
  15. package/dist/hooks/useEvent.js.map +1 -0
  16. package/dist/hooks/useHead.d.ts +13 -0
  17. package/dist/hooks/useHead.d.ts.map +1 -0
  18. package/dist/hooks/useHead.js +15 -0
  19. package/dist/hooks/useHead.js.map +1 -0
  20. package/dist/index.d.ts +8 -0
  21. package/dist/index.js +6 -0
  22. package/dist/island/client.js +59 -0
  23. package/dist/island/client.js.map +1 -0
  24. package/dist/island/types.d.ts +8 -0
  25. package/dist/island/types.d.ts.map +1 -0
  26. package/dist/page.d.ts +24 -0
  27. package/dist/page.d.ts.map +1 -0
  28. package/dist/page.js +51 -0
  29. package/dist/page.js.map +1 -0
  30. package/dist/server/entry.d.mts +14 -0
  31. package/dist/server/entry.d.mts.map +1 -0
  32. package/dist/server/entry.mjs +38 -0
  33. package/dist/server/entry.mjs.map +1 -0
  34. package/dist/server/index.d.mts +3 -0
  35. package/dist/server/index.mjs +3 -0
  36. package/dist/server/island/server.d.mts +23 -0
  37. package/dist/server/island/server.d.mts.map +1 -0
  38. package/dist/server/island/server.mjs +22 -0
  39. package/dist/server/island/server.mjs.map +1 -0
  40. package/dist/server/island/types.d.mts +5 -0
  41. package/dist/server/island/types.d.mts.map +1 -0
  42. package/dist/server/shared/assets.d.mts +14 -0
  43. package/dist/server/shared/assets.d.mts.map +1 -0
  44. package/dist/shared/assets.d.ts +17 -0
  45. package/dist/shared/assets.d.ts.map +1 -0
  46. package/dist/vite/index.d.mts +11 -0
  47. package/dist/vite/index.d.mts.map +1 -0
  48. package/dist/vite/index.mjs +54 -0
  49. package/dist/vite/index.mjs.map +1 -0
  50. package/dist/vite/islands.mjs +85 -0
  51. package/dist/vite/islands.mjs.map +1 -0
  52. package/dist/vite/shared/utils.mjs +10 -0
  53. package/dist/vite/shared/utils.mjs.map +1 -0
  54. package/dist/vite/virtual-assets.mjs +25 -0
  55. package/dist/vite/virtual-assets.mjs.map +1 -0
  56. package/dist/vite/virtual-pages.mjs +35 -0
  57. package/dist/vite/virtual-pages.mjs.map +1 -0
  58. package/dist/vite/virtual-template.mjs +45 -0
  59. package/dist/vite/virtual-template.mjs.map +1 -0
  60. package/package.json +84 -0
  61. package/src/client/index.ts +1 -0
  62. package/src/components/Head.tsx +8 -0
  63. package/src/context/ssr.ts +17 -0
  64. package/src/hooks/useEvent.tsx +13 -0
  65. package/src/hooks/useHead.tsx +27 -0
  66. package/src/index.ts +12 -0
  67. package/src/island/client.tsx +92 -0
  68. package/src/island/server.tsx +51 -0
  69. package/src/island/types.ts +5 -0
  70. package/src/page.tsx +92 -0
  71. package/src/server/entry.tsx +80 -0
  72. package/src/server/index.ts +4 -0
  73. package/src/shared/assets.ts +10 -0
  74. package/src/shared/utils.ts +5 -0
  75. package/src/virtual.d.ts +14 -0
  76. package/src/vite/index.ts +83 -0
  77. package/src/vite/islands.ts +229 -0
  78. package/src/vite/virtual-assets.ts +30 -0
  79. package/src/vite/virtual-pages.ts +40 -0
  80. package/src/vite/virtual-template.ts +62 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/vite/index.ts"],"sourcesContent":["import type { NitroPluginConfig } from \"nitro/vite\";\nimport { nitro } from \"nitro/vite\";\nimport { existsSync } from \"node:fs\";\nimport type { EnvironmentOptions, PluginOption } from \"vite\";\nimport { islands } from \"./islands\";\nimport { virtualAssets } from \"./virtual-assets\";\nimport { virtualPages } from \"./virtual-pages\";\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\trollupOptions: {\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\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\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(virtualAssets());\n\tplugins.push(virtualPages());\n\tplugins.push(virtualTemplate());\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":";;;;;;;AAaA,MAAM,QAAQ,YAA0C;CACvD,MAAM,UAA0B,EAAE;AAGlC,SAAQ,KAAK;EACZ,MAAM;EACN,SAAS;AAWR,UAAO;IACN,cAAc,EACb,SAAS;KACR;KACA;KACA;KACA;KACA;KACA,EACD;IACD,cAAc;KACb,GAAI,WAAW,mBAAmB,GAAG,EAAE,KArBN,EAClC,OAAO;MAEN,cAAc;MACd,eAAe,EACd,OAAO,oBACP;MACD,EACD,EAaqD,GAAG,EAAE;KACzD,QAAQ,EACP,OAAO,EACN,iBAAiB,EAChB,OAAO,yBACP,EACD,EACD;KACD;IACD;;EAEF,CAAC;AAEF,SAAQ,KAAK,SAAS,CAAC;AAEvB,SAAQ,KAAK,eAAe,CAAC;AAC7B,SAAQ,KAAK,cAAc,CAAC;AAC5B,SAAQ,KAAK,iBAAiB,CAAC;AAE/B,SAAQ,KACP,MAAM;EACL,WAAW;EACX,UAAU;EACV,GAAG,SAAS;EACZ,sBAAsB;GACrB,MAAM;GACN,QAAQ;GACR;EACD,cAAc,CACb;GACC,SAAS;GACT,KAAK;GACL,QAAQ,MAAM,KAAK,KAAK;GACxB,CACD;EACD,CAAC,CACF;AAED,QAAO"}
@@ -0,0 +1,85 @@
1
+ import { generate } from "@babel/generator";
2
+ import { parse } from "@babel/parser";
3
+ import _traverse from "@babel/traverse";
4
+ import { callExpression, exportDefaultDeclaration, exportNamedDeclaration, functionDeclaration, functionExpression, identifier, importDeclaration, importDefaultSpecifier, importNamespaceSpecifier, memberExpression, stringLiteral, variableDeclaration, variableDeclarator } from "@babel/types";
5
+ //#region src/vite/islands.ts
6
+ const traverse = _traverse.default ?? _traverse;
7
+ const ISLAND_REGEX = /\.island\.(j|t)sx?$/;
8
+ const islands = () => {
9
+ return [{
10
+ name: "yamf:islands",
11
+ transform: {
12
+ filter: { id: ISLAND_REGEX },
13
+ handler(code, id) {
14
+ if (this.environment.name !== "ssr") return;
15
+ const seenExports = /* @__PURE__ */ new Set();
16
+ const ast = parse(code, {
17
+ sourceType: "module",
18
+ plugins: ["typescript", "jsx"]
19
+ });
20
+ traverse(ast, {
21
+ Program(path) {
22
+ path.unshiftContainer("body", importDeclaration([importNamespaceSpecifier(identifier("__runtime"))], stringLiteral("@pajecawav/yamf/server")));
23
+ path.unshiftContainer("body", importDeclaration([importDefaultSpecifier(identifier("__assets"))], stringLiteral(`${id}?assets=client`)));
24
+ },
25
+ ExportDefaultDeclaration(path) {
26
+ const declarationType = path.node.declaration.type;
27
+ if (!(declarationType === "FunctionDeclaration" || declarationType === "FunctionExpression" || declarationType === "ArrowFunctionExpression")) return;
28
+ const originalFunction = path.node.declaration.type === "FunctionExpression" || path.node.declaration.type === "ArrowFunctionExpression" ? path.node.declaration : functionExpression(null, path.node.declaration.params, path.node.declaration.body, void 0, path.node.declaration.async);
29
+ const islandIdentifier = identifier("__ISLAND__");
30
+ path.insertBefore(variableDeclaration("const", [variableDeclarator(islandIdentifier, originalFunction)]));
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;
42
+ seenExports.add(exportName);
43
+ const islandIdentifier = identifier(`__wrap_${exportName}`);
44
+ path.insertBefore(variableDeclaration("const", [variableDeclarator(islandIdentifier, declaration.init)]));
45
+ path.replaceWith(exportNamedDeclaration(variableDeclaration("const", [variableDeclarator(identifier(exportName), callExpression(memberExpression(identifier("__runtime"), identifier("createIsland")), [
46
+ islandIdentifier,
47
+ stringLiteral(exportName),
48
+ identifier("__assets")
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;
54
+ seenExports.add(exportName);
55
+ const islandIdentifier = identifier(`__wrap_${exportName}`);
56
+ path.insertBefore(functionDeclaration(islandIdentifier, declaration.params, declaration.body, declaration.generator, declaration.async));
57
+ path.replaceWith(exportNamedDeclaration(variableDeclaration("const", [variableDeclarator(identifier(exportName), callExpression(memberExpression(identifier("__runtime"), identifier("createIsland")), [
58
+ islandIdentifier,
59
+ stringLiteral(exportName),
60
+ identifier("__assets")
61
+ ]))])));
62
+ }
63
+ }
64
+ });
65
+ const result = generate(ast);
66
+ return {
67
+ code: result.code,
68
+ map: result.map
69
+ };
70
+ }
71
+ }
72
+ }, {
73
+ name: "yamf:islands:raw-import",
74
+ transform: {
75
+ order: "post",
76
+ handler(code) {
77
+ if (code.includes("__island_raw_import__")) return code.replaceAll("__island_raw_import__", "import");
78
+ }
79
+ }
80
+ }];
81
+ };
82
+ //#endregion
83
+ export { islands };
84
+
85
+ //# sourceMappingURL=islands.mjs.map
@@ -0,0 +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// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore\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\t// enforce: \"pre\",\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;AACtC,QAAO,CACN;EACC,MAAM;EAEN,WAAW;GACV,QAAQ,EAAE,IAAI,cAAc;GAC5B,QAAQ,MAAM,IAAI;AACjB,QAAI,KAAK,YAAY,SAAS,MAC7B;IAGD,MAAM,8BAAc,IAAI,KAAa;IAErC,MAAM,MAAM,MAAM,MAAM;KACvB,YAAY;KACZ,SAAS,CAAC,cAAc,MAAM;KAC9B,CAAC;AAEF,aAAS,KAAK;KACb,QAAQ,MAAM;AAGb,WAAK,iBACJ,QACA,kBACC,CAAC,yBAAyB,WAAW,YAAY,CAAC,CAAC,EACnD,cAAc,yBAAyB,CACvC,CACD;AAID,WAAK,iBACJ,QACA,kBACC,CAAC,uBAAuB,WAAW,WAAW,CAAC,CAAC,EAChD,cAAc,GAAG,GAAG,gBAAgB,CACpC,CACD;;KAEF,yBAAyB,MAAM;MAC9B,MAAM,kBAAkB,KAAK,KAAK,YAAY;AAE9C,UACC,EACC,oBAAoB,yBACpB,oBAAoB,wBACpB,oBAAoB,2BAGrB;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,MACtB;MAEJ,MAAM,mBAAmB,WAAW,aAAa;AAEjD,WAAK,aACJ,oBAAoB,SAAS,CAC5B,mBAAmB,kBAAkB,iBAAiB,CACtD,CAAC,CACF;AAED,WAAK,YACJ,yBACC,eACC,iBACC,WAAW,YAAY,EACvB,WAAW,eAAe,CAC1B,EACD;OACC;OACA,cAAc,UAAU;OACxB,WAAW,WAAW;OACtB,CACD,CACD,CACD;;KAEF,uBAAuB,MAAM;AAC5B,UAAI,KAAK,KAAK,aAAa,SAAS,uBAAuB;OAC1D,MAAM,cAAc,KAAK,KAAK,YAAY,aAAa,GAAG,EAAE;AAE5D,WACC,CAAC,eACD,YAAY,GAAG,SAAS,gBACxB,YAAY,IAAI,YAAY,GAAG,KAAK,CAEpC;OAED,MAAM,aAAa,YAAY,GAAG;AAClC,mBAAY,IAAI,WAAW;OAE3B,MAAM,mBAAmB,WAAW,UAAU,aAAa;AAE3D,YAAK,aACJ,oBAAoB,SAAS,CAC5B,mBAAmB,kBAAkB,YAAY,KAAK,CACtD,CAAC,CACF;AAED,YAAK,YACJ,uBACC,oBAAoB,SAAS,CAC5B,mBACC,WAAW,WAAW,EACtB,eACC,iBACC,WAAW,YAAY,EACvB,WAAW,eAAe,CAC1B,EACD;QACC;QACA,cAAc,WAAW;QACzB,WAAW,WAAW;QACtB,CACD,CACD,CACD,CAAC,CACF,CACD;iBACS,KAAK,KAAK,aAAa,SAAS,uBAAuB;OACjE,MAAM,cAAc,KAAK,KAAK;AAE9B,WACC,CAAC,eACD,YAAY,IAAI,SAAS,gBACzB,YAAY,IAAI,YAAY,GAAG,KAAK,CAEpC;OAED,MAAM,aAAa,YAAY,GAAG;AAClC,mBAAY,IAAI,WAAW;OAE3B,MAAM,mBAAmB,WAAW,UAAU,aAAa;AAE3D,YAAK,aACJ,oBACC,kBACA,YAAY,QACZ,YAAY,MACZ,YAAY,WACZ,YAAY,MACZ,CACD;AAED,YAAK,YACJ,uBACC,oBAAoB,SAAS,CAC5B,mBACC,WAAW,WAAW,EACtB,eACC,iBACC,WAAW,YAAY,EACvB,WAAW,eAAe,CAC1B,EACD;QACC;QACA,cAAc,WAAW;QACzB,WAAW,WAAW;QACtB,CACD,CACD,CACD,CAAC,CACF,CACD;;;KAGH,CAAC;IAEF,MAAM,SAAS,SAAS,IAAI;AAE5B,WAAO;KAAE,MAAM,OAAO;KAAM,KAAK,OAAO;KAAK;;GAE9C;EACD,EACD;EACC,MAAM;EACN,WAAW;GACV,OAAO;GACP,QAAQ,MAAM;AACb,QAAI,KAAK,SAAS,wBAAwB,CACzC,QAAO,KAAK,WAAW,yBAAyB,SAAS;;GAK3D;EACD,CACD"}
@@ -0,0 +1,10 @@
1
+ //#region src/shared/utils.ts
2
+ const js = (str, ...args) => {
3
+ return str.reduce((acc, cur, i) => {
4
+ return acc + cur + String(args[i]);
5
+ }, "");
6
+ };
7
+ //#endregion
8
+ export { js };
9
+
10
+ //# sourceMappingURL=utils.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.mjs","names":[],"sources":["../../../src/shared/utils.ts"],"sourcesContent":["export const js = (str: TemplateStringsArray, ...args: unknown[]): string => {\n\treturn str.reduce((acc, cur, i) => {\n\t\treturn acc + cur + String(args[i]);\n\t}, \"\");\n};\n"],"mappings":";AAAA,MAAa,MAAM,KAA2B,GAAG,SAA4B;AAC5E,QAAO,IAAI,QAAQ,KAAK,KAAK,MAAM;AAClC,SAAO,MAAM,MAAM,OAAO,KAAK,GAAG;IAChC,GAAG"}
@@ -0,0 +1,25 @@
1
+ import { js } from "./shared/utils.mjs";
2
+ //#region src/vite/virtual-assets.ts
3
+ const virtualAssets = () => {
4
+ const virtualModuleId = "virtual:yamf:assets";
5
+ const resolvedVirtualModuleId = "\0" + virtualModuleId;
6
+ return {
7
+ name: "yamf:virtual-assets",
8
+ enforce: "pre",
9
+ resolveId(id) {
10
+ if (id === virtualModuleId) return resolvedVirtualModuleId;
11
+ },
12
+ load(id) {
13
+ if (id !== resolvedVirtualModuleId) return;
14
+ return js`
15
+ import clientAssets from "./src/client/index.ts?assets=client";
16
+
17
+ export { clientAssets };
18
+ `.trim();
19
+ }
20
+ };
21
+ };
22
+ //#endregion
23
+ export { virtualAssets };
24
+
25
+ //# sourceMappingURL=virtual-assets.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"virtual-assets.mjs","names":[],"sources":["../../src/vite/virtual-assets.ts"],"sourcesContent":["import type { Plugin } from \"vite\";\nimport { js } from \"../shared/utils\";\n\nexport const virtualAssets = (): Plugin => {\n\tconst virtualModuleId = \"virtual:yamf:assets\";\n\tconst resolvedVirtualModuleId = \"\\0\" + virtualModuleId;\n\n\treturn {\n\t\tname: \"yamf:virtual-assets\",\n\t\tenforce: \"pre\",\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\treturn js`\nimport clientAssets from \"./src/client/index.ts?assets=client\";\n\nexport { clientAssets };\n`.trim();\n\t\t},\n\t};\n};\n"],"mappings":";;AAGA,MAAa,sBAA8B;CAC1C,MAAM,kBAAkB;CACxB,MAAM,0BAA0B,OAAO;AAEvC,QAAO;EACN,MAAM;EACN,SAAS;EACT,UAAU,IAAI;AACb,OAAI,OAAO,gBACV,QAAO;;EAKT,KAAK,IAAI;AACR,OAAI,OAAO,wBACV;AAGD,UAAO,EAAE;;;;EAIV,MAAM;;EAEN"}
@@ -0,0 +1,35 @@
1
+ import { js } from "./shared/utils.mjs";
2
+ //#region src/vite/virtual-pages.ts
3
+ const virtualPages = () => {
4
+ const virtualModuleId = "virtual:yamf:pages";
5
+ const resolvedVirtualModuleId = "\0" + virtualModuleId;
6
+ return {
7
+ name: "yamf:virtual-pages",
8
+ enforce: "pre",
9
+ resolveId(id) {
10
+ if (id === virtualModuleId) return resolvedVirtualModuleId;
11
+ },
12
+ load(id) {
13
+ if (id !== resolvedVirtualModuleId) return;
14
+ return js`
15
+ const pages = import.meta.glob("/src/pages/**/*.{js,mjs,cjs,ts,mts,cts,tsx,jsx}", {
16
+ import: "default",
17
+ });
18
+ const assets = import.meta.glob(
19
+ "/src/pages/**/*.{js,mjs,cjs,ts,mts,cts,tsx,jsx}",
20
+ {
21
+ import: "default",
22
+ query: "?assets=ssr",
23
+ eager: true,
24
+ },
25
+ );
26
+
27
+ export { pages, assets };
28
+ `.trim();
29
+ }
30
+ };
31
+ };
32
+ //#endregion
33
+ export { virtualPages };
34
+
35
+ //# sourceMappingURL=virtual-pages.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"virtual-pages.mjs","names":[],"sources":["../../src/vite/virtual-pages.ts"],"sourcesContent":["import type { Plugin } from \"vite\";\nimport { js } from \"../shared/utils\";\n\nexport const virtualPages = (): Plugin => {\n\tconst virtualModuleId = \"virtual:yamf:pages\";\n\tconst resolvedVirtualModuleId = \"\\0\" + virtualModuleId;\n\n\treturn {\n\t\tname: \"yamf:virtual-pages\",\n\t\tenforce: \"pre\",\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\treturn js`\nconst pages = import.meta.glob(\"/src/pages/**/*.{js,mjs,cjs,ts,mts,cts,tsx,jsx}\", {\n\timport: \"default\",\n});\nconst assets = import.meta.glob(\n\t\"/src/pages/**/*.{js,mjs,cjs,ts,mts,cts,tsx,jsx}\",\n\t{\n\t\timport: \"default\",\n\t\tquery: \"?assets=ssr\",\n\t\teager: true,\n\t},\n);\n\nexport { pages, assets };\n`.trim();\n\t\t},\n\t};\n};\n"],"mappings":";;AAGA,MAAa,qBAA6B;CACzC,MAAM,kBAAkB;CACxB,MAAM,0BAA0B,OAAO;AAEvC,QAAO;EACN,MAAM;EACN,SAAS;EACT,UAAU,IAAI;AACb,OAAI,OAAO,gBACV,QAAO;;EAKT,KAAK,IAAI;AACR,OAAI,OAAO,wBACV;AAGD,UAAO,EAAE;;;;;;;;;;;;;;EAcV,MAAM;;EAEN"}
@@ -0,0 +1,45 @@
1
+ import { js } from "./shared/utils.mjs";
2
+ import { readFile } from "node:fs/promises";
3
+ import { relative } from "node:path";
4
+ //#region src/vite/virtual-template.ts
5
+ const TEMPLATE_PATH = "./src/template.html";
6
+ const DEFAULT_TEMPLATE = `
7
+ <!DOCTYPE html>
8
+ <html>
9
+ <head></head>
10
+ <body>
11
+ <!--ssr-outlet-->
12
+ </body>
13
+ </html>
14
+ `.trim();
15
+ const virtualTemplate = () => {
16
+ const virtualModuleId = "virtual:yamf:template";
17
+ const resolvedVirtualModuleId = "\0" + virtualModuleId;
18
+ return {
19
+ name: "yamf:virtual-template",
20
+ enforce: "pre",
21
+ resolveId(id) {
22
+ if (id === virtualModuleId) return resolvedVirtualModuleId;
23
+ },
24
+ async load(id) {
25
+ if (id !== resolvedVirtualModuleId) return;
26
+ try {
27
+ const template = await readFile(TEMPLATE_PATH, "utf8");
28
+ return js`export const template = ${JSON.stringify(template)};`;
29
+ } catch (error) {
30
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return js`export const template = ${JSON.stringify(DEFAULT_TEMPLATE)};`;
31
+ throw error;
32
+ }
33
+ },
34
+ handleHotUpdate({ file, server }) {
35
+ if (relative(file, TEMPLATE_PATH) === "") {
36
+ const mod = server.moduleGraph.getModuleById(resolvedVirtualModuleId);
37
+ if (mod) server.moduleGraph.invalidateModule(mod);
38
+ }
39
+ }
40
+ };
41
+ };
42
+ //#endregion
43
+ export { virtualTemplate };
44
+
45
+ //# sourceMappingURL=virtual-template.mjs.map
@@ -0,0 +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\tenforce: \"pre\",\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\t// TODO: should reload?\n\t\t\t\t// server.ws.send({ type: \"full-reload\" });\n\t\t\t}\n\t\t},\n\t};\n};\n"],"mappings":";;;;AAKA,MAAM,gBAAgB;AAEtB,MAAM,mBAA8B;;;;;;;;EAQlC,MAAM;AAER,MAAa,wBAAgC;CAC5C,MAAM,kBAAkB;CACxB,MAAM,0BAA0B,OAAO;AAEvC,QAAO;EACN,MAAM;EACN,SAAS;EACT,UAAU,IAAI;AACb,OAAI,OAAO,gBACV,QAAO;;EAKT,MAAM,KAAK,IAAI;AACd,OAAI,OAAO,wBACV;AAGD,OAAI;IACH,MAAM,WAAW,MAAM,SAAS,eAAe,OAAO;AAEtD,WAAO,EAAE,2BAA2B,KAAK,UAAU,SAAS,CAAC;YACrD,OAAO;AACf,QAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,SAC/D,QAAO,EAAE,2BAA2B,KAAK,UAAU,iBAAiB,CAAC;AAGtE,UAAM;;;EAGR,gBAAgB,EAAE,MAAM,UAAU;AACjC,OAAI,SAAS,MAAM,cAAc,KAAK,IAAI;IACzC,MAAM,MAAM,OAAO,YAAY,cAAc,wBAAwB;AAErE,QAAI,IACH,QAAO,YAAY,iBAAiB,IAAI;;;EAO3C"}
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@pajecawav/yamf",
3
+ "type": "module",
4
+ "version": "0.0.1",
5
+ "description": "Yet another meta framework",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/pajecawav/yamf#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/pajecawav/yamf.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/pajecawav/yamf/issues"
14
+ },
15
+ "exports": {
16
+ ".": "./dist/index.js",
17
+ "./vite": "./dist/vite/index.mjs",
18
+ "./client": "./dist/client/index.js",
19
+ "./server": "./dist/server/index.mjs",
20
+ "./package.json": "./package.json"
21
+ },
22
+ "main": "./dist/index.js",
23
+ "module": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "files": [
26
+ "dist",
27
+ "src"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "peerDependencies": {
33
+ "hono": "^4.12.12",
34
+ "vite": "^8.0.0"
35
+ },
36
+ "dependencies": {
37
+ "@babel/generator": "^7.29.1",
38
+ "@babel/parser": "^7.29.2",
39
+ "@babel/traverse": "^7.29.0",
40
+ "@babel/types": "^7.29.0",
41
+ "devalue": "^5.7.1",
42
+ "nitro": "3.0.260429-beta",
43
+ "rou3": "^0.8.1",
44
+ "ufo": "^1.6.3",
45
+ "unhead": "^3.1.0"
46
+ },
47
+ "devDependencies": {
48
+ "@commitlint/cli": "^20.5.0",
49
+ "@commitlint/config-conventional": "^20.5.0",
50
+ "@eslint/js": "^10.0.1",
51
+ "@pajecawav/prettier-config": "^1.0.0",
52
+ "@types/babel__generator": "^7.27.0",
53
+ "@types/babel__traverse": "^7.28.0",
54
+ "@types/node": "^24.12.2",
55
+ "@vitest/coverage-v8": "^4.1.5",
56
+ "@vitest/ui": "^4.1.5",
57
+ "cross-env": "^10.1.0",
58
+ "eslint": "^10.2.1",
59
+ "husky": "^9.1.7",
60
+ "lint-staged": "^16.4.0",
61
+ "npm-run-all2": "^8.0.4",
62
+ "prettier": "^3.8.3",
63
+ "publint": "^0.3.18",
64
+ "tsdown": "0.21.9",
65
+ "typescript": "^6.0.3",
66
+ "typescript-eslint": "^8.59.0",
67
+ "vite": "^8.0.0",
68
+ "vitest": "^4.1.5"
69
+ },
70
+ "scripts": {
71
+ "build": "tsdown",
72
+ "dev": "tsdown --watch",
73
+ "test": "vitest run --passWithNoTests",
74
+ "test:watch": "vitest watch",
75
+ "test:coverage": "vitest run --coverage",
76
+ "test:ui": "vitest --ui",
77
+ "lint": "run-p -l lint:*",
78
+ "lint:eslint": "eslint .",
79
+ "lint:tsc": "tsc -b --noEmit",
80
+ "lint:format": "prettier . --check",
81
+ "lint:package": "publint",
82
+ "format": "prettier . --write"
83
+ }
84
+ }
@@ -0,0 +1 @@
1
+ import "../island/client";
@@ -0,0 +1,8 @@
1
+ import { useHead } from "#/hooks/useHead";
2
+ import type { ResolvableHead } from "unhead/types";
3
+
4
+ export type HeadProps = ResolvableHead;
5
+
6
+ export const Head = (props: HeadProps): void => {
7
+ useHead(props);
8
+ };
@@ -0,0 +1,17 @@
1
+ import { type Context, createContext, useContext } from "hono/jsx";
2
+ import type { H3Event } from "nitro";
3
+ import type { EventHandlerRequest } from "nitro/h3";
4
+ import type { ServerUnhead } from "unhead/server";
5
+
6
+ interface SSRContextValue {
7
+ head: ServerUnhead;
8
+ event: H3Event<EventHandlerRequest>;
9
+ }
10
+
11
+ export const SSRContext: Context<SSRContextValue | null> = createContext<SSRContextValue | null>(
12
+ null,
13
+ );
14
+
15
+ export const useSSRContext = (): SSRContextValue | null => {
16
+ return useContext(SSRContext);
17
+ };
@@ -0,0 +1,13 @@
1
+ import { useSSRContext } from "#/context/ssr";
2
+ import type { H3Event } from "nitro";
3
+ import type { EventHandlerRequest } from "nitro/h3";
4
+
5
+ export const useEvent = (): H3Event<EventHandlerRequest> => {
6
+ const ctx = useSSRContext();
7
+
8
+ if (!ctx) {
9
+ throw new Error("SSR context not found");
10
+ }
11
+
12
+ return ctx.event;
13
+ };
@@ -0,0 +1,27 @@
1
+ import { useSSRContext } from "#/context/ssr";
2
+ import { useHead as _useHead } from "unhead";
3
+ import type { ClientUnhead } from "unhead/client";
4
+ import { createHead } from "unhead/client";
5
+ import type { ResolvableHead } from "unhead/types";
6
+
7
+ declare global {
8
+ interface Window {
9
+ __UNHEAD__?: ClientUnhead;
10
+ }
11
+ }
12
+
13
+ if (!import.meta.env.SSR) {
14
+ window.__UNHEAD__ = createHead();
15
+ }
16
+
17
+ export const useHead = (input?: ResolvableHead): void => {
18
+ if (import.meta.env.SSR) {
19
+ const ctx = useSSRContext();
20
+
21
+ if (ctx?.head) {
22
+ _useHead(ctx.head, input);
23
+ }
24
+ } else if (window.__UNHEAD__) {
25
+ _useHead(window.__UNHEAD__, input);
26
+ }
27
+ };
package/src/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ export { definePage } from "./page";
2
+ export type { PageHandler } from "./page";
3
+
4
+ export { useSSRContext } from "./context/ssr";
5
+ export { useEvent } from "./hooks/useEvent";
6
+ export { useHead } from "./hooks/useHead";
7
+
8
+ export type { IslandClientDirective, IslandProps } from "./island/types";
9
+
10
+ export type { ImportAssetsResult, ImportAssetsResultRaw } from "./shared/assets";
11
+
12
+ export { Head, type HeadProps } from "./components/Head";
@@ -0,0 +1,92 @@
1
+ import { parse } from "devalue";
2
+ import type { FC } from "hono/jsx";
3
+ import { hydrateRoot } from "hono/jsx/dom/client";
4
+ import { withLeadingSlash } from "ufo";
5
+ import type { IslandClientDirective } from "./types";
6
+
7
+ declare let __island_raw_import__: <T>(file: string) => Promise<T>;
8
+
9
+ const listeners = new WeakMap<Element, VoidFunction>();
10
+
11
+ const observer = new IntersectionObserver(entries => {
12
+ for (const entry of entries) {
13
+ if (entry.isIntersecting) {
14
+ listeners.get(entry.target)?.();
15
+ unobserve(entry.target);
16
+ }
17
+ }
18
+ });
19
+
20
+ const observe = (target: Element, cb: VoidFunction) => {
21
+ observer.observe(target);
22
+ listeners.set(target, cb);
23
+ };
24
+
25
+ const unobserve = (target: Element) => {
26
+ observer.unobserve(target);
27
+ listeners.delete(target);
28
+ };
29
+
30
+ customElements.define(
31
+ "yamf-island",
32
+ class extends HTMLElement {
33
+ public connectedCallback() {
34
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
35
+ const islandProps = parse(this.getAttribute("island-props") ?? "{}");
36
+ const islandSrc = this.getAttribute("island-src");
37
+ const islandEntry = this.getAttribute("island-entry");
38
+ const islandClient = (this.getAttribute("island-client") ??
39
+ "load") as IslandClientDirective;
40
+
41
+ if (!islandSrc) {
42
+ throw new Error("Missing island-src attribute");
43
+ }
44
+
45
+ if (!islandEntry) {
46
+ throw new Error("Missing island-entry attribute");
47
+ }
48
+
49
+ const hydrateIsland = (mod: Record<string, FC>) => {
50
+ const Comp = mod[islandEntry];
51
+
52
+ if (!Comp) {
53
+ throw new Error(`Missing island entry ${islandEntry} in ${islandSrc}`);
54
+ }
55
+
56
+ hydrateRoot(this, <Comp {...islandProps} />);
57
+ };
58
+
59
+ const initIsland = () => {
60
+ const src = withLeadingSlash(islandSrc);
61
+ void __island_raw_import__<Record<string, FC>>(src).then(hydrateIsland);
62
+ };
63
+
64
+ switch (islandClient) {
65
+ case "load":
66
+ initIsland();
67
+ break;
68
+ case "idle":
69
+ requestIdleCallback(initIsland);
70
+ break;
71
+ case "visible":
72
+ // yamf-island has `display: contents` which breaks IntersectionObserver
73
+ // so we have to observe the first child instead if it exists
74
+ if (this.firstElementChild) {
75
+ observe(this.firstElementChild, initIsland);
76
+ } else {
77
+ initIsland();
78
+ }
79
+ break;
80
+ case "skip":
81
+ break;
82
+ default:
83
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
84
+ throw new Error(`Invalid island-client value: ${islandClient}`);
85
+ }
86
+ }
87
+
88
+ public disconnectedCallback() {
89
+ unobserve(this);
90
+ }
91
+ },
92
+ );
@@ -0,0 +1,51 @@
1
+ import type { ImportAssetsResultRaw } from "#/shared/assets";
2
+ import { stringify } from "devalue";
3
+ import type { Child, FC } from "hono/jsx";
4
+ import type { IslandClientDirective } from "./types";
5
+
6
+ declare module "hono/jsx" {
7
+ // eslint-disable-next-line @typescript-eslint/no-namespace
8
+ namespace JSX {
9
+ interface IntrinsicElements {
10
+ "yamf-island": {
11
+ "island-props"?: string;
12
+ "island-src": string;
13
+ "island-entry": string;
14
+ "island-client": IslandClientDirective;
15
+ children: Child;
16
+ style?: JSX.CSSProperties;
17
+ };
18
+ }
19
+ }
20
+ }
21
+
22
+ export const createIsland = (
23
+ Component: FC,
24
+ exportName: string,
25
+ assets: ImportAssetsResultRaw,
26
+ ): FC => {
27
+ const ComponentWrapper: FC & { name: string } = props => {
28
+ if (!assets.entry) {
29
+ throw new Error(`Missing island entry for island ${Component.name}`);
30
+ }
31
+
32
+ return (
33
+ <yamf-island
34
+ island-props={stringify(props)}
35
+ island-src={assets.entry}
36
+ island-entry={exportName}
37
+ island-client={props["yamf-client"] as IslandClientDirective}
38
+ // TODO: export CSS?
39
+ style={{ display: "contents" }}
40
+ >
41
+ <Component {...props} />
42
+ </yamf-island>
43
+ );
44
+ };
45
+
46
+ Object.defineProperty(ComponentWrapper, "name", {
47
+ value: Component.name,
48
+ });
49
+
50
+ return ComponentWrapper;
51
+ };
@@ -0,0 +1,5 @@
1
+ export type IslandClientDirective = "load" | "idle" | "visible" | "skip";
2
+
3
+ export interface IslandProps {
4
+ "yamf-client"?: IslandClientDirective;
5
+ }