@upstart.gg/vite-plugins 0.1.61 → 0.1.62
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/dist/page-meta.js +533 -0
- package/dist/page-meta.js.map +1 -0
- package/dist/site-meta.d.ts +28 -0
- package/dist/site-meta.d.ts.map +1 -0
- package/dist/site-meta.js +207 -0
- package/dist/site-meta.js.map +1 -0
- package/dist/upstart-editor-api.d.ts +135 -1
- package/dist/upstart-editor-api.d.ts.map +1 -1
- package/dist/upstart-editor-api.js +756 -1
- package/dist/upstart-editor-api.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.js +35 -0
- package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +3 -0
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
- package/package.json +8 -3
- package/src/page-meta.ts +678 -0
- package/src/site-meta.ts +226 -0
- package/src/tests/site-meta.test.ts +158 -0
- package/src/tests/upstart-editor-api-page-meta.test.ts +635 -0
- package/src/upstart-editor-api.ts +941 -0
- package/src/vite-plugin-upstart-editor/runtime/index.ts +38 -0
- package/src/vite-plugin-upstart-editor/runtime/types.ts +2 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import MagicString from "magic-string";
|
|
2
|
+
import { parseSync } from "oxc-parser";
|
|
3
|
+
//#region src/site-meta.ts
|
|
4
|
+
/**
|
|
5
|
+
* Icon files left untouched: an ICO is already small and an SVG scales on its own. Every
|
|
6
|
+
* other image is downscaled to a square PNG, because a favicon is drawn at about 16px and
|
|
7
|
+
* WebP is not accepted as an icon everywhere.
|
|
8
|
+
*/
|
|
9
|
+
const FAVICON_PASS_THROUGH_TYPES = [
|
|
10
|
+
"image/svg+xml",
|
|
11
|
+
"image/x-icon",
|
|
12
|
+
"image/vnd.microsoft.icon"
|
|
13
|
+
];
|
|
14
|
+
/** Extensions matching {@link FAVICON_PASS_THROUGH_TYPES}, for callers working from a filename. */
|
|
15
|
+
const FAVICON_PASS_THROUGH_EXTENSIONS = [".svg", ".ico"];
|
|
16
|
+
/** Square size a favicon is downscaled to. */
|
|
17
|
+
const FAVICON_SIZE = 64;
|
|
18
|
+
/**
|
|
19
|
+
* Name of the downscaled copy of a favicon source. Sources are often already called
|
|
20
|
+
* "favicon", so the suffix is only added when it would say something new.
|
|
21
|
+
*/
|
|
22
|
+
function faviconFileName(sourceName) {
|
|
23
|
+
const stem = sourceName.replace(/\.[^.]+$/, "");
|
|
24
|
+
return /favicon$/i.test(stem) ? `${stem}.png` : `${stem}-favicon.png`;
|
|
25
|
+
}
|
|
26
|
+
/** Marker proving the file already renders the site settings. */
|
|
27
|
+
const MARKER = "siteAttributes.socialImage";
|
|
28
|
+
const SITE_ATTRIBUTES_DECL = "/** Site-wide settings edited from the editor's settings panel (favicon, social preview). */\nconst siteAttributes = siteConfig as unknown as SiteAttributes;\n\n";
|
|
29
|
+
const SOCIAL_META = `{/* Preview card shown when a page is shared on social networks or messaging apps.
|
|
30
|
+
React hoists these into <head>. Pages provide the title and description
|
|
31
|
+
themselves; crawlers fall back to <title> and <meta name="description">. */}
|
|
32
|
+
{siteAttributes.socialImage && (
|
|
33
|
+
<>
|
|
34
|
+
<meta property="og:image" content={\`\${origin}\${siteAttributes.socialImage}\`} />
|
|
35
|
+
<meta property="og:type" content="website" />
|
|
36
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
37
|
+
</>
|
|
38
|
+
)}`;
|
|
39
|
+
function walk(node, visit) {
|
|
40
|
+
if (!node || typeof node !== "object") return;
|
|
41
|
+
if (Array.isArray(node)) {
|
|
42
|
+
for (const child of node) walk(child, visit);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const n = node;
|
|
46
|
+
if (typeof n.type === "string") visit(n);
|
|
47
|
+
for (const key in n) {
|
|
48
|
+
if (key === "type" || key === "start" || key === "end") continue;
|
|
49
|
+
const value = n[key];
|
|
50
|
+
if (value && typeof value === "object") walk(value, visit);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const propertyName = (prop) => {
|
|
54
|
+
const key = prop.key;
|
|
55
|
+
if (key?.type === "Identifier") return key.name;
|
|
56
|
+
if (key?.type === "Literal" && typeof key.value === "string") return key.value;
|
|
57
|
+
return null;
|
|
58
|
+
};
|
|
59
|
+
/** Top-level `export …` declaration named `name`, whatever form it takes. */
|
|
60
|
+
function findExport(program, name) {
|
|
61
|
+
for (const statement of program.body ?? []) {
|
|
62
|
+
const isDefault = statement.type === "ExportDefaultDeclaration";
|
|
63
|
+
if (statement.type !== "ExportNamedDeclaration" && !isDefault) continue;
|
|
64
|
+
const declaration = statement.declaration;
|
|
65
|
+
if (!declaration) continue;
|
|
66
|
+
if (isDefault && name === "default") return declaration;
|
|
67
|
+
if (declaration.type === "FunctionDeclaration") {
|
|
68
|
+
const id = declaration.id;
|
|
69
|
+
if (id?.type === "Identifier" && id.name === name) return declaration;
|
|
70
|
+
} else if (declaration.type === "VariableDeclaration") for (const decl of declaration.declarations ?? []) {
|
|
71
|
+
const id = decl.id;
|
|
72
|
+
if (id?.type === "Identifier" && id.name === name) return decl.init ?? null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
/** Add a property to a destructuring pattern, unless it is already there. */
|
|
78
|
+
function addToObjectPattern(s, code, pattern, name) {
|
|
79
|
+
const properties = pattern.properties ?? [];
|
|
80
|
+
if (properties.some((prop) => prop.type === "Property" && propertyName(prop) === name)) return false;
|
|
81
|
+
const last = properties[properties.length - 1];
|
|
82
|
+
if (!last) return false;
|
|
83
|
+
const multiline = code.slice(last.end, pattern.end).includes("\n");
|
|
84
|
+
const indent = multiline ? code.slice(code.lastIndexOf("\n", last.start) + 1, last.start).match(/^[ \t]*/)?.[0] ?? "" : "";
|
|
85
|
+
s.appendLeft(last.end, multiline ? `,\n${indent}${name}` : `, ${name}`);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
function ensureRootSiteMeta(code, filePath) {
|
|
89
|
+
if (code.includes(MARKER)) return {
|
|
90
|
+
ok: true,
|
|
91
|
+
code,
|
|
92
|
+
changed: false
|
|
93
|
+
};
|
|
94
|
+
if (!code.includes("./config/site.json") || !code.includes("SiteAttributes")) return {
|
|
95
|
+
ok: false,
|
|
96
|
+
reason: "root.tsx does not read the site configuration"
|
|
97
|
+
};
|
|
98
|
+
const program = parseSync(filePath, code, { sourceType: "module" }).program;
|
|
99
|
+
const s = new MagicString(code);
|
|
100
|
+
const linksExport = findExport(program, "links");
|
|
101
|
+
if (!linksExport) return {
|
|
102
|
+
ok: false,
|
|
103
|
+
reason: "root.tsx has no links() export"
|
|
104
|
+
};
|
|
105
|
+
const linksStatement = (program.body ?? []).find((statement) => statement.start <= linksExport.start && statement.end >= linksExport.end);
|
|
106
|
+
s.appendLeft(linksStatement?.start ?? linksExport.start, SITE_ATTRIBUTES_DECL);
|
|
107
|
+
let faviconPatched = false;
|
|
108
|
+
walk(linksExport, (node) => {
|
|
109
|
+
if (faviconPatched || node.type !== "ObjectExpression") return;
|
|
110
|
+
const properties = node.properties ?? [];
|
|
111
|
+
const relValue = properties.find((prop) => propertyName(prop) === "rel")?.value;
|
|
112
|
+
if (relValue?.type !== "Literal" || relValue.value !== "icon") return;
|
|
113
|
+
const hrefValue = properties.find((prop) => propertyName(prop) === "href")?.value;
|
|
114
|
+
if (hrefValue?.type !== "Literal" || typeof hrefValue.value !== "string") return;
|
|
115
|
+
s.overwrite(hrefValue.start, hrefValue.end, `siteAttributes.favicon ?? "${hrefValue.value}"`);
|
|
116
|
+
faviconPatched = true;
|
|
117
|
+
});
|
|
118
|
+
if (!faviconPatched) return {
|
|
119
|
+
ok: false,
|
|
120
|
+
reason: "root.tsx has no favicon link to update"
|
|
121
|
+
};
|
|
122
|
+
const loader = findExport(program, "loader");
|
|
123
|
+
if (!loader) return {
|
|
124
|
+
ok: false,
|
|
125
|
+
reason: "root.tsx has no loader"
|
|
126
|
+
};
|
|
127
|
+
const loaderParam = (loader.params ?? [])[0];
|
|
128
|
+
if (loaderParam?.type !== "ObjectPattern") return {
|
|
129
|
+
ok: false,
|
|
130
|
+
reason: "root.tsx's loader does not destructure its arguments"
|
|
131
|
+
};
|
|
132
|
+
addToObjectPattern(s, code, loaderParam, "request");
|
|
133
|
+
let originAdded = false;
|
|
134
|
+
walk(loader, (node) => {
|
|
135
|
+
if (originAdded || node.type !== "ReturnStatement") return;
|
|
136
|
+
let returned = node.argument;
|
|
137
|
+
if (returned?.type === "CallExpression") returned = (returned.arguments ?? [])[0] ?? null;
|
|
138
|
+
if (returned?.type !== "ObjectExpression") return;
|
|
139
|
+
const properties = returned.properties ?? [];
|
|
140
|
+
const last = properties[properties.length - 1];
|
|
141
|
+
if (!last) return;
|
|
142
|
+
s.appendLeft(last.end, ",\n // Social crawlers need absolute image URLs, and only the request knows the host.\n origin: new URL(request.url).origin");
|
|
143
|
+
originAdded = true;
|
|
144
|
+
});
|
|
145
|
+
if (!originAdded) return {
|
|
146
|
+
ok: false,
|
|
147
|
+
reason: "root.tsx's loader does not return an object"
|
|
148
|
+
};
|
|
149
|
+
const app = findExport(program, "default");
|
|
150
|
+
if (!app || app.type !== "FunctionDeclaration" && app.type !== "ArrowFunctionExpression") return {
|
|
151
|
+
ok: false,
|
|
152
|
+
reason: "root.tsx has no default component"
|
|
153
|
+
};
|
|
154
|
+
const appParam = (app.params ?? [])[0];
|
|
155
|
+
if (appParam?.type !== "ObjectPattern") return {
|
|
156
|
+
ok: false,
|
|
157
|
+
reason: "root.tsx's component does not destructure its props"
|
|
158
|
+
};
|
|
159
|
+
const loaderDataProp = (appParam.properties ?? []).find((prop) => propertyName(prop) === "loaderData");
|
|
160
|
+
const loaderDataPattern = loaderDataProp?.value;
|
|
161
|
+
if (loaderDataPattern?.type === "ObjectPattern") addToObjectPattern(s, code, loaderDataPattern, "origin");
|
|
162
|
+
else if (loaderDataProp) return {
|
|
163
|
+
ok: false,
|
|
164
|
+
reason: "root.tsx's component does not destructure its loader data"
|
|
165
|
+
};
|
|
166
|
+
else {
|
|
167
|
+
addToObjectPattern(s, code, appParam, "loaderData");
|
|
168
|
+
return {
|
|
169
|
+
ok: false,
|
|
170
|
+
reason: "root.tsx's component does not receive loader data"
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
let wrapped = false;
|
|
174
|
+
walk(app.body, (node) => {
|
|
175
|
+
if (wrapped || node.type !== "ReturnStatement") return;
|
|
176
|
+
const returned = node.argument;
|
|
177
|
+
if (!returned) return;
|
|
178
|
+
s.appendLeft(returned.start, `(\n <>\n ${SOCIAL_META}\n `);
|
|
179
|
+
s.appendRight(returned.end, "\n </>\n )");
|
|
180
|
+
wrapped = true;
|
|
181
|
+
});
|
|
182
|
+
if (!wrapped) return {
|
|
183
|
+
ok: false,
|
|
184
|
+
reason: "root.tsx's component returns nothing"
|
|
185
|
+
};
|
|
186
|
+
const next = s.toString();
|
|
187
|
+
try {
|
|
188
|
+
if (parseSync(filePath, next, { sourceType: "module" }).errors.length > 0) return {
|
|
189
|
+
ok: false,
|
|
190
|
+
reason: "the upgraded root.tsx would not parse"
|
|
191
|
+
};
|
|
192
|
+
} catch {
|
|
193
|
+
return {
|
|
194
|
+
ok: false,
|
|
195
|
+
reason: "the upgraded root.tsx would not parse"
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
ok: true,
|
|
200
|
+
code: next,
|
|
201
|
+
changed: true
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
export { FAVICON_PASS_THROUGH_EXTENSIONS, FAVICON_PASS_THROUGH_TYPES, FAVICON_SIZE, ensureRootSiteMeta, faviconFileName };
|
|
206
|
+
|
|
207
|
+
//# sourceMappingURL=site-meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site-meta.js","names":[],"sources":["../src/site-meta.ts"],"sourcesContent":["import MagicString from \"magic-string\";\nimport { parseSync } from \"oxc-parser\";\n\n/**\n * Brings an existing site's `app/root.tsx` up to the version that renders the site-wide\n * settings edited from the editor: the favicon and the social preview image.\n *\n * Sites created from the template already contain this code; sites created before it need a\n * one-off upgrade. `root.tsx` is template-owned (the assistant never edits it), so the shapes\n * matched here are stable — but every step still asserts what it found and the result is\n * re-parsed, so a customized file is reported instead of being mangled.\n */\n\ninterface AstNode {\n type: string;\n start: number;\n end: number;\n [key: string]: unknown;\n}\n\nexport type EnsureRootResult = { ok: true; code: string; changed: boolean } | { ok: false; reason: string };\n\n/**\n * Icon files left untouched: an ICO is already small and an SVG scales on its own. Every\n * other image is downscaled to a square PNG, because a favicon is drawn at about 16px and\n * WebP is not accepted as an icon everywhere.\n */\nexport const FAVICON_PASS_THROUGH_TYPES = [\"image/svg+xml\", \"image/x-icon\", \"image/vnd.microsoft.icon\"];\n/** Extensions matching {@link FAVICON_PASS_THROUGH_TYPES}, for callers working from a filename. */\nexport const FAVICON_PASS_THROUGH_EXTENSIONS = [\".svg\", \".ico\"];\n/** Square size a favicon is downscaled to. */\nexport const FAVICON_SIZE = 64;\n\n/**\n * Name of the downscaled copy of a favicon source. Sources are often already called\n * \"favicon\", so the suffix is only added when it would say something new.\n */\nexport function faviconFileName(sourceName: string): string {\n const stem = sourceName.replace(/\\.[^.]+$/, \"\");\n return /favicon$/i.test(stem) ? `${stem}.png` : `${stem}-favicon.png`;\n}\n\n/** Marker proving the file already renders the site settings. */\nconst MARKER = \"siteAttributes.socialImage\";\n\nconst SITE_ATTRIBUTES_DECL =\n \"/** Site-wide settings edited from the editor's settings panel (favicon, social preview). */\\n\" +\n \"const siteAttributes = siteConfig as unknown as SiteAttributes;\\n\\n\";\n\nconst SOCIAL_META = `{/* Preview card shown when a page is shared on social networks or messaging apps.\n React hoists these into <head>. Pages provide the title and description\n themselves; crawlers fall back to <title> and <meta name=\"description\">. */}\n {siteAttributes.socialImage && (\n <>\n <meta property=\"og:image\" content={\\`\\${origin}\\${siteAttributes.socialImage}\\`} />\n <meta property=\"og:type\" content=\"website\" />\n <meta name=\"twitter:card\" content=\"summary_large_image\" />\n </>\n )}`;\n\nfunction walk(node: unknown, visit: (n: AstNode) => void): void {\n if (!node || typeof node !== \"object\") return;\n if (Array.isArray(node)) {\n for (const child of node) walk(child, visit);\n return;\n }\n const n = node as AstNode;\n if (typeof n.type === \"string\") visit(n);\n for (const key in n) {\n if (key === \"type\" || key === \"start\" || key === \"end\") continue;\n const value = n[key];\n if (value && typeof value === \"object\") walk(value, visit);\n }\n}\n\nconst propertyName = (prop: AstNode): string | null => {\n const key = prop.key as AstNode | undefined;\n if (key?.type === \"Identifier\") return key.name as string;\n if (key?.type === \"Literal\" && typeof key.value === \"string\") return key.value;\n return null;\n};\n\n/** Top-level `export …` declaration named `name`, whatever form it takes. */\nfunction findExport(program: AstNode, name: string): AstNode | null {\n for (const statement of (program.body as AstNode[]) ?? []) {\n const isDefault = statement.type === \"ExportDefaultDeclaration\";\n if (statement.type !== \"ExportNamedDeclaration\" && !isDefault) continue;\n const declaration = statement.declaration as AstNode | null;\n if (!declaration) continue;\n if (isDefault && name === \"default\") return declaration;\n if (declaration.type === \"FunctionDeclaration\") {\n const id = declaration.id as AstNode | null;\n if (id?.type === \"Identifier\" && id.name === name) return declaration;\n } else if (declaration.type === \"VariableDeclaration\") {\n for (const decl of (declaration.declarations as AstNode[]) ?? []) {\n const id = decl.id as AstNode;\n if (id?.type === \"Identifier\" && id.name === name) return (decl.init as AstNode) ?? null;\n }\n }\n }\n return null;\n}\n\n/** Add a property to a destructuring pattern, unless it is already there. */\nfunction addToObjectPattern(s: MagicString, code: string, pattern: AstNode, name: string): boolean {\n const properties = (pattern.properties as AstNode[]) ?? [];\n if (properties.some((prop) => prop.type === \"Property\" && propertyName(prop) === name)) return false;\n const last = properties[properties.length - 1];\n if (!last) return false;\n // Keep the source's layout: one property per line stays one property per line.\n const multiline = code.slice(last.end, pattern.end).includes(\"\\n\");\n const indent = multiline\n ? (code.slice(code.lastIndexOf(\"\\n\", last.start) + 1, last.start).match(/^[ \\t]*/)?.[0] ?? \"\")\n : \"\";\n s.appendLeft(last.end, multiline ? `,\\n${indent}${name}` : `, ${name}`);\n return true;\n}\n\nexport function ensureRootSiteMeta(code: string, filePath: string): EnsureRootResult {\n if (code.includes(MARKER)) return { ok: true, code, changed: false };\n if (!code.includes(\"./config/site.json\") || !code.includes(\"SiteAttributes\")) {\n return { ok: false, reason: \"root.tsx does not read the site configuration\" };\n }\n\n const program = parseSync(filePath, code, { sourceType: \"module\" }).program as unknown as AstNode;\n const s = new MagicString(code);\n\n // 1. Module-level view of site.json, shared by links() and the social preview.\n const linksExport = findExport(program, \"links\");\n if (!linksExport) return { ok: false, reason: \"root.tsx has no links() export\" };\n const linksStatement = ((program.body as AstNode[]) ?? []).find(\n (statement) => statement.start <= linksExport.start && statement.end >= linksExport.end,\n );\n s.appendLeft(linksStatement?.start ?? linksExport.start, SITE_ATTRIBUTES_DECL);\n\n // 2. Favicon: drive the existing `rel: \"icon\"` entry from the config.\n let faviconPatched = false;\n walk(linksExport, (node) => {\n if (faviconPatched || node.type !== \"ObjectExpression\") return;\n const properties = (node.properties as AstNode[]) ?? [];\n const rel = properties.find((prop) => propertyName(prop) === \"rel\");\n const relValue = rel?.value as AstNode | undefined;\n if (relValue?.type !== \"Literal\" || relValue.value !== \"icon\") return;\n const href = properties.find((prop) => propertyName(prop) === \"href\");\n const hrefValue = href?.value as AstNode | undefined;\n if (hrefValue?.type !== \"Literal\" || typeof hrefValue.value !== \"string\") return;\n s.overwrite(hrefValue.start, hrefValue.end, `siteAttributes.favicon ?? \"${hrefValue.value}\"`);\n faviconPatched = true;\n });\n if (!faviconPatched) return { ok: false, reason: \"root.tsx has no favicon link to update\" };\n\n // 3. The loader must expose the request origin: social crawlers need absolute image URLs.\n const loader = findExport(program, \"loader\");\n if (!loader) return { ok: false, reason: \"root.tsx has no loader\" };\n const loaderParam = ((loader.params as AstNode[]) ?? [])[0];\n if (loaderParam?.type !== \"ObjectPattern\") {\n return { ok: false, reason: \"root.tsx's loader does not destructure its arguments\" };\n }\n addToObjectPattern(s, code, loaderParam, \"request\");\n\n let originAdded = false;\n walk(loader, (node) => {\n if (originAdded || node.type !== \"ReturnStatement\") return;\n let returned = node.argument as AstNode | null;\n // The template returns `data({ … }, { headers })`; a plain object works too.\n if (returned?.type === \"CallExpression\") {\n returned = ((returned.arguments as AstNode[]) ?? [])[0] ?? null;\n }\n if (returned?.type !== \"ObjectExpression\") return;\n const properties = (returned.properties as AstNode[]) ?? [];\n const last = properties[properties.length - 1];\n if (!last) return;\n s.appendLeft(\n last.end,\n \",\\n // Social crawlers need absolute image URLs, and only the request knows the host.\\n\" +\n \" origin: new URL(request.url).origin\",\n );\n originAdded = true;\n });\n if (!originAdded) return { ok: false, reason: \"root.tsx's loader does not return an object\" };\n\n // 4. Render the preview tags from the default export, which receives the loader data.\n const app = findExport(program, \"default\");\n if (!app || (app.type !== \"FunctionDeclaration\" && app.type !== \"ArrowFunctionExpression\")) {\n return { ok: false, reason: \"root.tsx has no default component\" };\n }\n const appParam = ((app.params as AstNode[]) ?? [])[0];\n if (appParam?.type !== \"ObjectPattern\") {\n return { ok: false, reason: \"root.tsx's component does not destructure its props\" };\n }\n const loaderDataProp = ((appParam.properties as AstNode[]) ?? []).find(\n (prop) => propertyName(prop) === \"loaderData\",\n );\n const loaderDataPattern = loaderDataProp?.value as AstNode | undefined;\n if (loaderDataPattern?.type === \"ObjectPattern\") {\n addToObjectPattern(s, code, loaderDataPattern, \"origin\");\n } else if (loaderDataProp) {\n return { ok: false, reason: \"root.tsx's component does not destructure its loader data\" };\n } else {\n addToObjectPattern(s, code, appParam, \"loaderData\");\n return { ok: false, reason: \"root.tsx's component does not receive loader data\" };\n }\n\n let wrapped = false;\n walk(app.body as AstNode, (node) => {\n if (wrapped || node.type !== \"ReturnStatement\") return;\n const returned = node.argument as AstNode | null;\n if (!returned) return;\n // `return (` keeps the expression on the return's own line, so no semicolon is inserted.\n s.appendLeft(returned.start, `(\\n <>\\n ${SOCIAL_META}\\n `);\n s.appendRight(returned.end, \"\\n </>\\n )\");\n wrapped = true;\n });\n if (!wrapped) return { ok: false, reason: \"root.tsx's component returns nothing\" };\n\n const next = s.toString();\n try {\n const check = parseSync(filePath, next, { sourceType: \"module\" });\n if (check.errors.length > 0) {\n return { ok: false, reason: \"the upgraded root.tsx would not parse\" };\n }\n } catch {\n return { ok: false, reason: \"the upgraded root.tsx would not parse\" };\n }\n return { ok: true, code: next, changed: true };\n}\n"],"mappings":"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"}
|
|
@@ -9,6 +9,13 @@ import z from "zod";
|
|
|
9
9
|
* The surrounding quotes themselves are NOT included.
|
|
10
10
|
*/
|
|
11
11
|
declare function escapeStringLiteralBody(value: string, quote: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Read a key from a locale document. i18next accepts both a flat key ("a.b" as a literal
|
|
14
|
+
* property) and a nested path, so both are tried — flat first, as the generated sites use it.
|
|
15
|
+
*/
|
|
16
|
+
declare function readLocaleValue(data: Record<string, unknown>, key: string): string | null;
|
|
17
|
+
/** Write a key, keeping the shape it already has; new keys are created flat. */
|
|
18
|
+
declare function writeLocaleValue(data: Record<string, unknown>, key: string, value: string): void;
|
|
12
19
|
declare const payloadEditText: z.ZodObject<{
|
|
13
20
|
action: z.ZodLiteral<"editText">;
|
|
14
21
|
language: z.ZodString;
|
|
@@ -53,6 +60,91 @@ declare const payloadArraySet: z.ZodObject<{
|
|
|
53
60
|
items: z.ZodArray<z.ZodString>;
|
|
54
61
|
}, z.core.$strip>;
|
|
55
62
|
type PayloadArraySet = z.infer<typeof payloadArraySet>;
|
|
63
|
+
declare const payloadGetPageMeta: z.ZodObject<{
|
|
64
|
+
action: z.ZodLiteral<"getPageMeta">;
|
|
65
|
+
routeId: z.ZodString;
|
|
66
|
+
language: z.ZodOptional<z.ZodString>;
|
|
67
|
+
}, z.core.$strip>;
|
|
68
|
+
type PayloadGetPageMeta = z.infer<typeof payloadGetPageMeta>;
|
|
69
|
+
declare const payloadSetPageMeta: z.ZodObject<{
|
|
70
|
+
action: z.ZodLiteral<"setPageMeta">;
|
|
71
|
+
routeId: z.ZodString;
|
|
72
|
+
language: z.ZodOptional<z.ZodString>;
|
|
73
|
+
title: z.ZodDefault<z.ZodString>;
|
|
74
|
+
description: z.ZodDefault<z.ZodString>;
|
|
75
|
+
keywords: z.ZodDefault<z.ZodString>;
|
|
76
|
+
robotsIndexing: z.ZodDefault<z.ZodBoolean>;
|
|
77
|
+
}, z.core.$strip>;
|
|
78
|
+
type PayloadSetPageMeta = z.infer<typeof payloadSetPageMeta>;
|
|
79
|
+
declare const payloadGetSiteMeta: z.ZodObject<{
|
|
80
|
+
action: z.ZodLiteral<"getSiteMeta">;
|
|
81
|
+
}, z.core.$strip>;
|
|
82
|
+
type PayloadGetSiteMeta = z.infer<typeof payloadGetSiteMeta>;
|
|
83
|
+
declare const payloadSetSiteMeta: z.ZodObject<{
|
|
84
|
+
action: z.ZodLiteral<"setSiteMeta">;
|
|
85
|
+
favicon: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
86
|
+
socialImage: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
87
|
+
}, z.core.$strip>;
|
|
88
|
+
type PayloadSetSiteMeta = z.infer<typeof payloadSetSiteMeta>;
|
|
89
|
+
type GetSiteMetaResult = {
|
|
90
|
+
success: true;
|
|
91
|
+
favicon: string | null;
|
|
92
|
+
socialImage: string | null; /** False when root.tsx was customized and can no longer be upgraded automatically. */
|
|
93
|
+
editable: boolean;
|
|
94
|
+
reason?: string;
|
|
95
|
+
} | {
|
|
96
|
+
success: false;
|
|
97
|
+
error: string;
|
|
98
|
+
};
|
|
99
|
+
type SetSiteMetaResult = {
|
|
100
|
+
success: true;
|
|
101
|
+
filePaths: string[];
|
|
102
|
+
} | {
|
|
103
|
+
success: false;
|
|
104
|
+
error: string;
|
|
105
|
+
};
|
|
106
|
+
interface PageMetaField {
|
|
107
|
+
value: string;
|
|
108
|
+
editable: boolean;
|
|
109
|
+
/** Why the field is read-only (dynamic value, computed title…). */
|
|
110
|
+
reason?: string;
|
|
111
|
+
/** True when the text lives in the locale files rather than in the route source. */
|
|
112
|
+
translated: boolean;
|
|
113
|
+
i18nKey?: string;
|
|
114
|
+
/**
|
|
115
|
+
* True when the translation key is also used elsewhere (typically as a heading on the
|
|
116
|
+
* page). Saving then moves this page's meta onto its own key instead of rewriting the
|
|
117
|
+
* shared one.
|
|
118
|
+
*/
|
|
119
|
+
shared: boolean;
|
|
120
|
+
/** Static text rendered around the value, e.g. `{`${title} | Acme`}`. */
|
|
121
|
+
prefix: string;
|
|
122
|
+
suffix: string;
|
|
123
|
+
}
|
|
124
|
+
type GetPageMetaResult = {
|
|
125
|
+
success: true;
|
|
126
|
+
filePath: string;
|
|
127
|
+
relativeFile: string; /** "jsx" for React-rendered meta tags, "meta-export" for a `export const meta` route. */
|
|
128
|
+
mode: "jsx" | "meta-export";
|
|
129
|
+
languages: string[];
|
|
130
|
+
language: string;
|
|
131
|
+
title: PageMetaField;
|
|
132
|
+
description: PageMetaField;
|
|
133
|
+
keywords: PageMetaField;
|
|
134
|
+
robotsIndexing: boolean;
|
|
135
|
+
robotsEditable: boolean;
|
|
136
|
+
robotsReason?: string;
|
|
137
|
+
} | {
|
|
138
|
+
success: false;
|
|
139
|
+
error: string;
|
|
140
|
+
};
|
|
141
|
+
type SetPageMetaResult = {
|
|
142
|
+
success: true;
|
|
143
|
+
filePaths: string[];
|
|
144
|
+
} | {
|
|
145
|
+
success: false;
|
|
146
|
+
error: string;
|
|
147
|
+
};
|
|
56
148
|
interface EditableRegistry {
|
|
57
149
|
version: number;
|
|
58
150
|
generatedAt: string;
|
|
@@ -124,6 +216,48 @@ declare class UpstartEditorAPI {
|
|
|
124
216
|
* batched ✓ apply so a whole add/delete session is a single edit + rebuild.
|
|
125
217
|
*/
|
|
126
218
|
arraySet(params: PayloadArraySet): Promise<EditResult>;
|
|
219
|
+
/**
|
|
220
|
+
* Read the page metadata shown in the browser tab and in search results.
|
|
221
|
+
*
|
|
222
|
+
* Two shapes are supported, in this order: React-rendered tags (`<title>{title}</title>`
|
|
223
|
+
* fed by the loader, the shape the AI assistant generates — the text then lives in the
|
|
224
|
+
* locale files), and a static `export const meta` array.
|
|
225
|
+
*/
|
|
226
|
+
getPageMeta(params: PayloadGetPageMeta): Promise<GetPageMetaResult>;
|
|
227
|
+
/**
|
|
228
|
+
* Write the page metadata back. Values backed by a translation key are written to the
|
|
229
|
+
* locale file of `language`; everything else is written into the route source. Returns
|
|
230
|
+
* every file that changed so the caller can commit them together.
|
|
231
|
+
*/
|
|
232
|
+
setPageMeta(params: PayloadSetPageMeta): Promise<SetPageMetaResult>;
|
|
233
|
+
/**
|
|
234
|
+
* Read the site-wide settings rendered by `app/root.tsx`: the browser-tab icon and the
|
|
235
|
+
* image shown when a page is shared on social networks.
|
|
236
|
+
*/
|
|
237
|
+
getSiteMeta(params: PayloadGetSiteMeta): Promise<GetSiteMetaResult>;
|
|
238
|
+
/**
|
|
239
|
+
* Write the site-wide settings to `app/config/site.json`, upgrading `app/root.tsx` to
|
|
240
|
+
* render them if it does not already. The image files themselves are copied into the
|
|
241
|
+
* workspace by the caller, which has bucket access.
|
|
242
|
+
*/
|
|
243
|
+
setSiteMeta(params: PayloadSetSiteMeta): Promise<SetSiteMetaResult>;
|
|
244
|
+
private readSiteConfig;
|
|
245
|
+
private readRootFile;
|
|
246
|
+
private setMetaExport;
|
|
247
|
+
private renderSegments;
|
|
248
|
+
private describeField;
|
|
249
|
+
private listLanguages;
|
|
250
|
+
private resolveLanguage;
|
|
251
|
+
private localePath;
|
|
252
|
+
private readLocale;
|
|
253
|
+
private readAppSources;
|
|
254
|
+
/**
|
|
255
|
+
* Resolve the import specifiers a route can use for a local component — "~/components/X"
|
|
256
|
+
* (the template's alias for `app/`) and relative paths — against the files already read.
|
|
257
|
+
*/
|
|
258
|
+
private createModuleLoader;
|
|
259
|
+
private allocateMetaKey;
|
|
260
|
+
private resolveRouteFile;
|
|
127
261
|
/** Parse "<relativeFile>:<offset>" into an absolute path + numeric offset. */
|
|
128
262
|
private resolveArrayId;
|
|
129
263
|
private applyEdit;
|
|
@@ -142,5 +276,5 @@ declare class UpstartEditorAPI {
|
|
|
142
276
|
getElementsByFile(file: string): Record<string, EditableEntry>;
|
|
143
277
|
}
|
|
144
278
|
//#endregion
|
|
145
|
-
export { EditResult, EditableRegistry, PayloadArrayItemAdd, PayloadArrayItemDelete, PayloadArraySet, PayloadEditClassName, PayloadEditImage, PayloadEditText, PayloadEditTextDirect, UpstartEditorAPI, escapeStringLiteralBody, payloadArrayItemAdd, payloadArrayItemDelete, payloadArraySet, payloadEditClassName, payloadEditImage, payloadEditText, payloadEditTextDirect };
|
|
279
|
+
export { EditResult, EditableRegistry, GetPageMetaResult, GetSiteMetaResult, PageMetaField, PayloadArrayItemAdd, PayloadArrayItemDelete, PayloadArraySet, PayloadEditClassName, PayloadEditImage, PayloadEditText, PayloadEditTextDirect, PayloadGetPageMeta, PayloadGetSiteMeta, PayloadSetPageMeta, PayloadSetSiteMeta, SetPageMetaResult, SetSiteMetaResult, UpstartEditorAPI, escapeStringLiteralBody, payloadArrayItemAdd, payloadArrayItemDelete, payloadArraySet, payloadEditClassName, payloadEditImage, payloadEditText, payloadEditTextDirect, payloadGetPageMeta, payloadGetSiteMeta, payloadSetPageMeta, payloadSetSiteMeta, readLocaleValue, writeLocaleValue };
|
|
146
280
|
//# sourceMappingURL=upstart-editor-api.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstart-editor-api.d.ts","names":[],"sources":["../src/upstart-editor-api.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"upstart-editor-api.d.ts","names":[],"sources":["../src/upstart-editor-api.ts"],"mappings":"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"}
|