@pajecawav/yamf 0.0.7 → 0.0.9
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 +63 -7
- package/dist/client/index.js +39 -1
- package/dist/client/index.js.map +1 -0
- package/dist/components/Head.d.ts +3 -4
- package/dist/components/Head.d.ts.map +1 -1
- package/dist/components/Head.js.map +1 -1
- package/dist/context/ssr.d.ts +1 -2
- package/dist/context/ssr.d.ts.map +1 -1
- package/dist/hooks/isPrerendering.d.ts +12 -0
- package/dist/hooks/isPrerendering.d.ts.map +1 -0
- package/dist/hooks/isPrerendering.js +17 -0
- package/dist/hooks/isPrerendering.js.map +1 -0
- package/dist/hooks/useEvent.d.ts +1 -2
- package/dist/hooks/useEvent.d.ts.map +1 -1
- package/dist/hooks/useHead.d.ts +10 -5
- package/dist/hooks/useHead.d.ts.map +1 -1
- package/dist/hooks/useHead.js +46 -4
- package/dist/hooks/useHead.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +3 -1
- package/dist/island/client.js +44 -36
- package/dist/island/client.js.map +1 -1
- package/dist/island/types.d.ts +2 -3
- package/dist/island/types.d.ts.map +1 -1
- package/dist/page.d.ts +32 -6
- package/dist/page.d.ts.map +1 -1
- package/dist/page.js +60 -6
- package/dist/page.js.map +1 -1
- package/dist/safeAsync.d.ts +19 -0
- package/dist/safeAsync.d.ts.map +1 -0
- package/dist/safeAsync.js +34 -0
- package/dist/safeAsync.js.map +1 -0
- package/dist/server/entry.d.mts +12 -4
- package/dist/server/entry.d.mts.map +1 -1
- package/dist/server/entry.mjs +9 -3
- package/dist/server/entry.mjs.map +1 -1
- package/dist/server/island/server.d.mts +2 -3
- package/dist/server/island/server.d.mts.map +1 -1
- package/dist/server/island/server.mjs +13 -3
- package/dist/server/island/server.mjs.map +1 -1
- package/dist/server/island/types.d.mts +1 -2
- package/dist/server/island/types.d.mts.map +1 -1
- package/dist/server/shared/assets.d.mts +1 -2
- package/dist/server/shared/assets.d.mts.map +1 -1
- package/dist/server/shared/head.d.mts +1 -2
- package/dist/server/shared/head.d.mts.map +1 -1
- package/dist/shared/assets.d.ts +2 -3
- package/dist/shared/assets.d.ts.map +1 -1
- package/dist/shared/head.d.ts +1 -2
- package/dist/shared/head.d.ts.map +1 -1
- package/dist/vite/index.d.mts +2 -2
- package/dist/vite/index.d.mts.map +1 -1
- package/dist/vite/index.mjs +13 -5
- package/dist/vite/index.mjs.map +1 -1
- package/dist/vite/islands.mjs +34 -5
- package/dist/vite/islands.mjs.map +1 -1
- package/dist/vite/prerender.mjs +16 -0
- package/dist/vite/prerender.mjs.map +1 -0
- package/dist/vite/virtual-assets.mjs +1 -1
- package/dist/vite/virtual-assets.mjs.map +1 -1
- package/dist/vite/virtual-error-handler.mjs +1 -1
- package/dist/vite/virtual-error-handler.mjs.map +1 -1
- package/dist/vite/virtual-pages.mjs +1 -1
- package/dist/vite/virtual-pages.mjs.map +1 -1
- package/dist/vite/virtual-root.mjs +12 -5
- package/dist/vite/virtual-root.mjs.map +1 -1
- package/dist/vite/virtual-template.mjs +9 -4
- package/dist/vite/virtual-template.mjs.map +1 -1
- package/package.json +27 -20
- package/src/client/index.ts +51 -1
- package/src/components/Head.tsx +2 -2
- package/src/hooks/isPrerendering.ts +13 -0
- package/src/hooks/useHead.tsx +98 -9
- package/src/index.ts +10 -1
- package/src/island/client.tsx +73 -65
- package/src/island/server.tsx +31 -6
- package/src/page.tsx +176 -11
- package/src/safeAsync.tsx +46 -0
- package/src/server/entry.tsx +25 -1
- package/src/vite/index.ts +30 -3
- package/src/vite/islands.ts +68 -8
- package/src/vite/prerender.ts +27 -0
- package/src/vite/virtual-root.ts +14 -4
- package/src/vite/virtual-template.ts +15 -3
package/src/vite/islands.ts
CHANGED
|
@@ -5,12 +5,36 @@ import { Visitor } from "vite";
|
|
|
5
5
|
|
|
6
6
|
const ISLAND_REGEX = /\.island\.(j|t)sx?$/;
|
|
7
7
|
|
|
8
|
+
// inits that can plausibly evaluate to a component; anything else (object,
|
|
9
|
+
// array, literal, class…) is left as a plain export with a warning
|
|
10
|
+
const WRAPPABLE_INIT_TYPES = new Set([
|
|
11
|
+
"ArrowFunctionExpression",
|
|
12
|
+
"FunctionExpression",
|
|
13
|
+
"CallExpression",
|
|
14
|
+
"OptionalCallExpression",
|
|
15
|
+
"Identifier",
|
|
16
|
+
"ConditionalExpression",
|
|
17
|
+
"SequenceExpression",
|
|
18
|
+
"TSAsExpression",
|
|
19
|
+
"NonNullExpression",
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
const isWrappableInit = (init: { type: string }): boolean => {
|
|
23
|
+
return WRAPPABLE_INIT_TYPES.has(init.type);
|
|
24
|
+
};
|
|
25
|
+
|
|
8
26
|
type Lang = NonNullable<ParserOptions["lang"]>;
|
|
9
27
|
|
|
10
28
|
function langFromId(id: string): Lang {
|
|
11
|
-
if (id.endsWith(".tsx"))
|
|
12
|
-
|
|
13
|
-
|
|
29
|
+
if (id.endsWith(".tsx")) {
|
|
30
|
+
return "tsx";
|
|
31
|
+
}
|
|
32
|
+
if (id.endsWith(".ts")) {
|
|
33
|
+
return "ts";
|
|
34
|
+
}
|
|
35
|
+
if (id.endsWith(".jsx")) {
|
|
36
|
+
return "jsx";
|
|
37
|
+
}
|
|
14
38
|
return "js";
|
|
15
39
|
}
|
|
16
40
|
|
|
@@ -36,6 +60,7 @@ export const islands = (): Plugin[] => {
|
|
|
36
60
|
s.prepend(`import __assets from "${id}?assets=client";\n`);
|
|
37
61
|
|
|
38
62
|
const seenExports = new Set<string>();
|
|
63
|
+
const warnings: string[] = [];
|
|
39
64
|
|
|
40
65
|
new Visitor({
|
|
41
66
|
ExportDefaultDeclaration(node) {
|
|
@@ -45,6 +70,9 @@ export const islands = (): Plugin[] => {
|
|
|
45
70
|
declaration.type !== "FunctionExpression" &&
|
|
46
71
|
declaration.type !== "ArrowFunctionExpression"
|
|
47
72
|
) {
|
|
73
|
+
warnings.push(
|
|
74
|
+
`${id}: export default <${declaration.type === "Identifier" ? "identifier" : declaration.type}> is not wrapped as an island — only function components are supported`,
|
|
75
|
+
);
|
|
48
76
|
return;
|
|
49
77
|
}
|
|
50
78
|
|
|
@@ -58,8 +86,20 @@ export const islands = (): Plugin[] => {
|
|
|
58
86
|
},
|
|
59
87
|
ExportNamedDeclaration(node) {
|
|
60
88
|
const declaration = node.declaration;
|
|
61
|
-
|
|
62
|
-
|
|
89
|
+
|
|
90
|
+
if (!declaration) {
|
|
91
|
+
if (node.exportKind !== "type") {
|
|
92
|
+
warnings.push(
|
|
93
|
+
`${id}: export { … } statements are not wrapped as islands — export the component with \`export const\` or \`export function\``,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (declaration.type === "VariableDeclaration") {
|
|
100
|
+
const declarators = declaration.declarations;
|
|
101
|
+
const declarator = declarators[0];
|
|
102
|
+
|
|
63
103
|
if (
|
|
64
104
|
!declarator ||
|
|
65
105
|
declarator.id.type !== "Identifier" ||
|
|
@@ -68,7 +108,22 @@ export const islands = (): Plugin[] => {
|
|
|
68
108
|
) {
|
|
69
109
|
return;
|
|
70
110
|
}
|
|
111
|
+
|
|
71
112
|
const exportName = declarator.id.name;
|
|
113
|
+
|
|
114
|
+
if (!isWrappableInit(declarator.init)) {
|
|
115
|
+
warnings.push(
|
|
116
|
+
`${id}: export "${exportName}" does not look like a component — left as a plain export, not an island`,
|
|
117
|
+
);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (declarators.length > 1) {
|
|
122
|
+
warnings.push(
|
|
123
|
+
`${id}: only the first declarator of a multi-declarator export is wrapped as an island ("${exportName}") — split the declaration`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
72
127
|
seenExports.add(exportName);
|
|
73
128
|
|
|
74
129
|
// export const <name> = <init> ->
|
|
@@ -82,7 +137,7 @@ export const islands = (): Plugin[] => {
|
|
|
82
137
|
declarator.init.end,
|
|
83
138
|
`; export const ${exportName} = __runtime.createIsland(__wrap_${exportName}, "${exportName}", __assets)`,
|
|
84
139
|
);
|
|
85
|
-
} else if (declaration
|
|
140
|
+
} else if (declaration.type === "FunctionDeclaration") {
|
|
86
141
|
const fnId = declaration.id;
|
|
87
142
|
if (
|
|
88
143
|
!fnId ||
|
|
@@ -105,6 +160,10 @@ export const islands = (): Plugin[] => {
|
|
|
105
160
|
}
|
|
106
161
|
},
|
|
107
162
|
}).visit(program);
|
|
163
|
+
|
|
164
|
+
for (const warning of warnings) {
|
|
165
|
+
this.warn(warning);
|
|
166
|
+
}
|
|
108
167
|
}),
|
|
109
168
|
},
|
|
110
169
|
},
|
|
@@ -113,8 +172,9 @@ export const islands = (): Plugin[] => {
|
|
|
113
172
|
transform: {
|
|
114
173
|
order: "post",
|
|
115
174
|
handler: withMagicString(function (s) {
|
|
116
|
-
|
|
117
|
-
|
|
175
|
+
// the token is namespaced to avoid colliding with user code
|
|
176
|
+
if (s.original.includes("__yamf_raw_import__")) {
|
|
177
|
+
s.replaceAll("__yamf_raw_import__", "import");
|
|
118
178
|
}
|
|
119
179
|
}),
|
|
120
180
|
},
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { NitroModule } from "nitro/types";
|
|
2
|
+
|
|
3
|
+
// Nitro's prerenderer is a standalone rolldown build (`nitro-prerender` preset)
|
|
4
|
+
// that cannot run yamf's vite plugins, so `virtual:yamf:*` modules, the islands
|
|
5
|
+
// transform, and the vite asset manifests do not exist there. Its auto-detected
|
|
6
|
+
// serverEntry (src/server.tsx) would be bundled by rolldown and crash the
|
|
7
|
+
// worker at init (`Received protocol 'virtual:'`). Disabling it lets prerender
|
|
8
|
+
// requests fall through to the inherited renderer — nitro's vite ssr-renderer —
|
|
9
|
+
// which dispatches them into the vite-built ssr service bundle: the exact code
|
|
10
|
+
// path the production server uses.
|
|
11
|
+
export const prerenderModule: NitroModule = {
|
|
12
|
+
name: "yamf:prerender",
|
|
13
|
+
setup(nitro) {
|
|
14
|
+
nitro.hooks.hook("prerender:config", config => {
|
|
15
|
+
config.serverEntry = false;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
nitro.hooks.hook("prerender:generate", route => {
|
|
19
|
+
// static hosts (github-pages & friends) expect the not-found document
|
|
20
|
+
// at exactly /404.html; nitro would write /404 as 404/index.html
|
|
21
|
+
// (autoSubfolderIndex)
|
|
22
|
+
if (route.route === "/404" && !route.error && !route.skip) {
|
|
23
|
+
route.fileName = "404.html";
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
};
|
package/src/vite/virtual-root.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
1
2
|
import { resolveModulePath } from "exsolve";
|
|
2
3
|
import type { Plugin } from "vite";
|
|
3
4
|
import { js } from "../shared/utils";
|
|
@@ -8,8 +9,15 @@ export const virtualRoot = (): Plugin => {
|
|
|
8
9
|
const virtualModuleId = "virtual:yamf:root";
|
|
9
10
|
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
|
10
11
|
|
|
12
|
+
// resolved against the vite root (not cwd) so running vite from another
|
|
13
|
+
// working directory does not silently skip the root layout
|
|
14
|
+
let root = process.cwd();
|
|
15
|
+
|
|
11
16
|
return {
|
|
12
17
|
name: "yamf:virtual-root",
|
|
18
|
+
configResolved(config) {
|
|
19
|
+
root = config.root;
|
|
20
|
+
},
|
|
13
21
|
resolveId(id) {
|
|
14
22
|
if (id === virtualModuleId) {
|
|
15
23
|
return resolvedVirtualModuleId;
|
|
@@ -22,13 +30,15 @@ export const virtualRoot = (): Plugin => {
|
|
|
22
30
|
return;
|
|
23
31
|
}
|
|
24
32
|
|
|
25
|
-
const
|
|
33
|
+
const rootPath = resolve(root, ROOT_PATH);
|
|
34
|
+
|
|
35
|
+
const rootModule = resolveModulePath(rootPath, {
|
|
26
36
|
try: true,
|
|
27
37
|
suffixes: ["", "/index"],
|
|
28
38
|
extensions: [".tsx", ".jsx", ".ts", ".js", ".mjs", ".cjs", ".mts", ".cts"],
|
|
29
39
|
});
|
|
30
40
|
|
|
31
|
-
if (!
|
|
41
|
+
if (!rootModule) {
|
|
32
42
|
return js`
|
|
33
43
|
const Root = null;
|
|
34
44
|
const rootAssets = null;
|
|
@@ -38,8 +48,8 @@ export { Root, rootAssets };
|
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
return js`
|
|
41
|
-
import Root from "${
|
|
42
|
-
import rootAssets from "${
|
|
51
|
+
import Root from "${rootModule}";
|
|
52
|
+
import rootAssets from "${rootModule}?assets=ssr";
|
|
43
53
|
|
|
44
54
|
export { Root, rootAssets };
|
|
45
55
|
`.trim();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { relative } from "node:path";
|
|
2
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
3
3
|
import type { Plugin } from "vite";
|
|
4
4
|
import { js } from "../shared/utils";
|
|
5
5
|
|
|
@@ -19,8 +19,16 @@ export const virtualTemplate = (): Plugin => {
|
|
|
19
19
|
const virtualModuleId = "virtual:yamf:template";
|
|
20
20
|
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
|
21
21
|
|
|
22
|
+
// resolved against the vite root (not cwd) so running vite from another
|
|
23
|
+
// working directory — monorepo tooling, turbo, etc — does not silently
|
|
24
|
+
// fall back to the default template
|
|
25
|
+
let root = process.cwd();
|
|
26
|
+
|
|
22
27
|
return {
|
|
23
28
|
name: "yamf:virtual-template",
|
|
29
|
+
configResolved(config) {
|
|
30
|
+
root = config.root;
|
|
31
|
+
},
|
|
24
32
|
resolveId(id) {
|
|
25
33
|
if (id === virtualModuleId) {
|
|
26
34
|
return resolvedVirtualModuleId;
|
|
@@ -34,7 +42,7 @@ export const virtualTemplate = (): Plugin => {
|
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
try {
|
|
37
|
-
const template = await readFile(TEMPLATE_PATH, "utf8");
|
|
45
|
+
const template = await readFile(resolve(root, TEMPLATE_PATH), "utf8");
|
|
38
46
|
|
|
39
47
|
return js`export const template = ${JSON.stringify(template)};`;
|
|
40
48
|
} catch (error) {
|
|
@@ -46,7 +54,11 @@ export const virtualTemplate = (): Plugin => {
|
|
|
46
54
|
}
|
|
47
55
|
},
|
|
48
56
|
handleHotUpdate({ file, server }) {
|
|
49
|
-
if (
|
|
57
|
+
if (!isAbsolute(file)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (relative(resolve(root, TEMPLATE_PATH), file) === "") {
|
|
50
62
|
const mod = server.moduleGraph.getModuleById(resolvedVirtualModuleId);
|
|
51
63
|
|
|
52
64
|
if (mod) {
|