@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.
@@ -0,0 +1,533 @@
1
+ import { parseSync } from "oxc-parser";
2
+ //#region src/page-meta.ts
3
+ const ABSENT = {
4
+ origin: { kind: "absent" },
5
+ prefixSegments: [],
6
+ suffixSegments: []
7
+ };
8
+ function walk(node, visit) {
9
+ if (!node || typeof node !== "object") return;
10
+ if (Array.isArray(node)) {
11
+ for (const child of node) walk(child, visit);
12
+ return;
13
+ }
14
+ const n = node;
15
+ if (typeof n.type === "string") visit(n);
16
+ for (const key in n) {
17
+ if (key === "type" || key === "start" || key === "end") continue;
18
+ const value = n[key];
19
+ if (value && typeof value === "object") walk(value, visit);
20
+ }
21
+ }
22
+ /** Value of a JSX attribute that is a plain string, e.g. `name="description"`. */
23
+ function jsxAttributeString(element, attributeName) {
24
+ const opening = element.openingElement;
25
+ for (const attr of opening?.attributes ?? []) {
26
+ if (attr.type !== "JSXAttribute") continue;
27
+ const name = attr.name;
28
+ if (name?.type !== "JSXIdentifier" || name.name !== attributeName) continue;
29
+ const value = attr.value;
30
+ if (value?.type === "Literal" && typeof value.value === "string") return value.value;
31
+ return null;
32
+ }
33
+ return null;
34
+ }
35
+ function jsxAttributeNode(element, attributeName) {
36
+ const opening = element.openingElement;
37
+ for (const attr of opening?.attributes ?? []) {
38
+ if (attr.type !== "JSXAttribute") continue;
39
+ const name = attr.name;
40
+ if (name?.type === "JSXIdentifier" && name.name === attributeName) return attr;
41
+ }
42
+ return null;
43
+ }
44
+ /** A string literal node → an editable origin covering the text between its quotes. */
45
+ function literalOrigin(code, node) {
46
+ const quote = code[node.start];
47
+ const quoted = quote === "\"" || quote === "'" || quote === "`";
48
+ return {
49
+ kind: "literal",
50
+ start: quoted ? node.start + 1 : node.start,
51
+ end: quoted ? node.end - 1 : node.end,
52
+ value: node.value,
53
+ quote: quoted ? quote : "\""
54
+ };
55
+ }
56
+ /**
57
+ * Read `t("key")` / `i18n.t("ns:key", { ns })`. Returns null when the call is not a
58
+ * translation lookup we can address (dynamic key, interpolated values…).
59
+ */
60
+ function readTranslationCall(node) {
61
+ if (node.type !== "CallExpression") return null;
62
+ const callee = node.callee;
63
+ if (!(callee.type === "Identifier" && callee.name === "t" || callee.type === "MemberExpression" && !callee.computed && callee.property?.type === "Identifier" && callee.property.name === "t")) return null;
64
+ const args = node.arguments ?? [];
65
+ const first = args[0];
66
+ if (first?.type !== "Literal" || typeof first.value !== "string") return null;
67
+ let namespace = "translation";
68
+ let key = first.value;
69
+ const colon = key.indexOf(":");
70
+ if (colon > 0) {
71
+ namespace = key.slice(0, colon);
72
+ key = key.slice(colon + 1);
73
+ }
74
+ const second = args[1];
75
+ if (second) {
76
+ if (second.type !== "ObjectExpression") return null;
77
+ const props = second.properties ?? [];
78
+ if (props.length !== 1) return null;
79
+ const prop = props[0];
80
+ if (prop.type !== "Property" || prop.computed) return null;
81
+ const propKey = prop.key;
82
+ const propValue = prop.value;
83
+ if ((propKey.type === "Identifier" ? propKey.name : propKey.value) !== "ns" || propValue.type !== "Literal" || typeof propValue.value !== "string") return null;
84
+ namespace = propValue.value;
85
+ }
86
+ return {
87
+ kind: "i18n",
88
+ key,
89
+ namespace,
90
+ keyStart: first.start + 1,
91
+ keyEnd: first.end - 1
92
+ };
93
+ }
94
+ /** Initializer of a plain `const x = …` declaration, when the module has one. */
95
+ function localDeclaration(ctx, identifier) {
96
+ let found = null;
97
+ walk(ctx.program, (node) => {
98
+ if (found || node.type !== "VariableDeclarator") return;
99
+ const id = node.id;
100
+ if (id?.type === "Identifier" && id.name === identifier) found = node.init ?? null;
101
+ });
102
+ return found;
103
+ }
104
+ /** The key an object pattern binds to `identifier`, following `{ a: b }` and `{ a = 1 }`. */
105
+ function patternKeyFor(pattern, identifier) {
106
+ for (const prop of pattern.properties ?? []) {
107
+ if (prop.type !== "Property") continue;
108
+ const key = prop.key;
109
+ let value = prop.value;
110
+ if (value?.type === "AssignmentPattern") value = value.left;
111
+ if (value?.type === "Identifier" && value.name === identifier && key?.type === "Identifier") return key.name;
112
+ }
113
+ return null;
114
+ }
115
+ /** Property name an identifier was destructured from, e.g. `const { title } = loaderData`. */
116
+ function destructuredSourceName(ctx, identifier) {
117
+ let found = null;
118
+ walk(ctx.program, (node) => {
119
+ if (found || node.type !== "VariableDeclarator") return;
120
+ const id = node.id;
121
+ if (id?.type !== "ObjectPattern") return;
122
+ found = patternKeyFor(id, identifier);
123
+ });
124
+ return found;
125
+ }
126
+ /**
127
+ * Prop name an identifier is bound to by a component's signature, e.g.
128
+ * `function SeoHead({ title }: Props)`. Props arrive as a parameter pattern, not as a
129
+ * variable declaration, so they need their own lookup.
130
+ */
131
+ function parameterPropName(ctx, identifier) {
132
+ let found = null;
133
+ walk(ctx.program, (node) => {
134
+ if (found) return;
135
+ if (node.type !== "FunctionDeclaration" && node.type !== "ArrowFunctionExpression" && node.type !== "FunctionExpression") return;
136
+ const first = (node.params ?? [])[0];
137
+ if (first?.type === "ObjectPattern") found = patternKeyFor(first, identifier);
138
+ });
139
+ return found;
140
+ }
141
+ /** The route's exported `loader` function, whatever declaration form it uses. */
142
+ function findLoader(ctx) {
143
+ const body = ctx.program.body ?? [];
144
+ for (const statement of body) {
145
+ if (statement.type !== "ExportNamedDeclaration") continue;
146
+ const declaration = statement.declaration;
147
+ if (!declaration) continue;
148
+ if (declaration.type === "FunctionDeclaration") {
149
+ const id = declaration.id;
150
+ if (id?.type === "Identifier" && id.name === "loader") return declaration;
151
+ } else if (declaration.type === "VariableDeclaration") for (const decl of declaration.declarations ?? []) {
152
+ const id = decl.id;
153
+ if (id?.type === "Identifier" && id.name === "loader") return decl.init ?? null;
154
+ }
155
+ }
156
+ return null;
157
+ }
158
+ /** Value the loader returns for `propertyName`, across every `return { … }` it contains. */
159
+ function loaderReturnValue(loader, propertyName) {
160
+ let found = null;
161
+ walk(loader, (node) => {
162
+ if (found || node.type !== "ReturnStatement") return;
163
+ const argument = node.argument;
164
+ if (argument?.type !== "ObjectExpression") return;
165
+ for (const prop of argument.properties ?? []) {
166
+ if (prop.type !== "Property" || prop.computed) continue;
167
+ const key = prop.key;
168
+ if ((key?.type === "Identifier" ? key.name : key?.type === "Literal" ? key.value : null) === propertyName) {
169
+ found = prop.value;
170
+ return;
171
+ }
172
+ }
173
+ });
174
+ return found;
175
+ }
176
+ const unsupportedElement = (reason) => ({
177
+ origin: {
178
+ kind: "unsupported",
179
+ reason
180
+ },
181
+ prefixSegments: [],
182
+ suffixSegments: []
183
+ });
184
+ /**
185
+ * Follow a component identifier back to the loader value that feeds it. The loader may
186
+ * itself compose several translations, so this returns a full element rather than a single
187
+ * origin.
188
+ */
189
+ function traceIdentifier(ctx, identifier, depth) {
190
+ if (depth > 4) return unsupportedElement("This value is computed by code");
191
+ const local = localDeclaration(ctx, identifier);
192
+ if (local) return resolveExpression(ctx, local, depth + 1);
193
+ if (ctx.mode === "component") {
194
+ const propName = parameterPropName(ctx, identifier) ?? destructuredSourceName(ctx, identifier);
195
+ if (!propName) return unsupportedElement("This value is computed by code");
196
+ return {
197
+ origin: {
198
+ kind: "prop",
199
+ name: propName
200
+ },
201
+ prefixSegments: [],
202
+ suffixSegments: []
203
+ };
204
+ }
205
+ const sourceName = destructuredSourceName(ctx, identifier);
206
+ if (!sourceName) return unsupportedElement("This value is computed by code");
207
+ const loader = findLoader(ctx);
208
+ if (!loader) return unsupportedElement("This value is computed by code");
209
+ const value = loaderReturnValue(loader, sourceName);
210
+ if (!value) return unsupportedElement("This value is computed by code");
211
+ const translation = readTranslationCall(value);
212
+ if (translation) return {
213
+ origin: translation,
214
+ prefixSegments: [],
215
+ suffixSegments: []
216
+ };
217
+ if (value.type === "Literal" && typeof value.value === "string") return {
218
+ origin: literalOrigin(ctx.code, value),
219
+ prefixSegments: [],
220
+ suffixSegments: []
221
+ };
222
+ if (value.type === "TemplateLiteral") return resolveExpression(ctx, value, depth + 1);
223
+ return unsupportedElement("This value is built from other content");
224
+ }
225
+ /** Resolve any expression used as a meta value, keeping the read-only parts around it. */
226
+ function resolveExpression(ctx, expression, depth = 0) {
227
+ if (expression.type === "Literal" && typeof expression.value === "string") return {
228
+ origin: literalOrigin(ctx.code, expression),
229
+ prefixSegments: [],
230
+ suffixSegments: []
231
+ };
232
+ if (expression.type === "Identifier") return traceIdentifier(ctx, expression.name, depth);
233
+ const translation = readTranslationCall(expression);
234
+ if (translation) return {
235
+ origin: translation,
236
+ prefixSegments: [],
237
+ suffixSegments: []
238
+ };
239
+ if (expression.type === "TemplateLiteral") {
240
+ const quasis = expression.quasis ?? [];
241
+ const expressions = expression.expressions ?? [];
242
+ if (expressions.length === 0 && quasis.length === 1) {
243
+ const quasi = quasis[0];
244
+ const cooked = quasi.value?.cooked ?? "";
245
+ return {
246
+ origin: {
247
+ kind: "literal",
248
+ start: quasi.start,
249
+ end: quasi.end,
250
+ value: cooked,
251
+ quote: "`"
252
+ },
253
+ prefixSegments: [],
254
+ suffixSegments: []
255
+ };
256
+ }
257
+ const segments = [];
258
+ quasis.forEach((quasi, index) => {
259
+ segments.push({ text: quasi.value?.cooked ?? "" });
260
+ const interpolated = expressions[index];
261
+ if (!interpolated) return;
262
+ const resolved = resolveExpression(ctx, interpolated, depth + 1);
263
+ segments.push(...resolved.prefixSegments, { origin: resolved.origin }, ...resolved.suffixSegments);
264
+ });
265
+ const editableAt = segments.findIndex((segment) => "origin" in segment && (segment.origin.kind === "i18n" || segment.origin.kind === "literal" || segment.origin.kind === "prop"));
266
+ if (editableAt === -1) return {
267
+ origin: {
268
+ kind: "unsupported",
269
+ reason: "This value is computed by code"
270
+ },
271
+ prefixSegments: [],
272
+ suffixSegments: []
273
+ };
274
+ return {
275
+ origin: segments[editableAt].origin,
276
+ prefixSegments: segments.slice(0, editableAt),
277
+ suffixSegments: segments.slice(editableAt + 1)
278
+ };
279
+ }
280
+ return {
281
+ origin: {
282
+ kind: "unsupported",
283
+ reason: "This value is computed by code"
284
+ },
285
+ prefixSegments: [],
286
+ suffixSegments: []
287
+ };
288
+ }
289
+ /** Read `<title>…</title>` children. */
290
+ function analyzeTitleElement(ctx, element) {
291
+ const children = (element.children ?? []).filter((child) => child.type !== "JSXText" || String(child.value ?? "").trim() !== "");
292
+ if (children.length === 1) {
293
+ const child = children[0];
294
+ if (child.type === "JSXText") return {
295
+ origin: {
296
+ kind: "literal",
297
+ start: child.start,
298
+ end: child.end,
299
+ value: String(child.value ?? ""),
300
+ quote: "\""
301
+ },
302
+ prefixSegments: [],
303
+ suffixSegments: []
304
+ };
305
+ if (child.type === "JSXExpressionContainer") return resolveExpression(ctx, child.expression);
306
+ }
307
+ return {
308
+ origin: {
309
+ kind: "unsupported",
310
+ reason: "This title is computed by code"
311
+ },
312
+ prefixSegments: [],
313
+ suffixSegments: []
314
+ };
315
+ }
316
+ /** Read the `content` attribute of a `<meta …/>` element. */
317
+ function analyzeMetaElement(ctx, element) {
318
+ const value = jsxAttributeNode(element, "content")?.value;
319
+ if (!value) return {
320
+ origin: {
321
+ kind: "unsupported",
322
+ reason: "This tag has no content"
323
+ },
324
+ prefixSegments: [],
325
+ suffixSegments: []
326
+ };
327
+ if (value.type === "Literal" && typeof value.value === "string") return {
328
+ origin: literalOrigin(ctx.code, value),
329
+ prefixSegments: [],
330
+ suffixSegments: []
331
+ };
332
+ if (value.type === "JSXExpressionContainer") return resolveExpression(ctx, value.expression);
333
+ return {
334
+ origin: {
335
+ kind: "unsupported",
336
+ reason: "This value is computed by code"
337
+ },
338
+ prefixSegments: [],
339
+ suffixSegments: []
340
+ };
341
+ }
342
+ /**
343
+ * Locate and resolve the four meta values a route can render as JSX.
344
+ */
345
+ /** Import specifier a local identifier (a component name) was imported from. */
346
+ function importSpecifierOf(program, name) {
347
+ for (const statement of program.body ?? []) {
348
+ if (statement.type !== "ImportDeclaration") continue;
349
+ for (const spec of statement.specifiers ?? []) {
350
+ const local = spec.local;
351
+ if (local?.type === "Identifier" && local.name === name) {
352
+ const source = statement.source;
353
+ return typeof source?.value === "string" ? source.value : null;
354
+ }
355
+ }
356
+ }
357
+ return null;
358
+ }
359
+ /** JSX attributes of an element, by name, ignoring spreads. */
360
+ function jsxAttributes(element) {
361
+ const attributes = /* @__PURE__ */ new Map();
362
+ const opening = element.openingElement;
363
+ for (const attr of opening?.attributes ?? []) {
364
+ if (attr.type !== "JSXAttribute") continue;
365
+ const name = attr.name;
366
+ if (name?.type === "JSXIdentifier") attributes.set(name.name, attr);
367
+ }
368
+ return attributes;
369
+ }
370
+ /** Rendered components, in source order, that could stand in for the meta tags. */
371
+ function findComponentElements(program) {
372
+ const found = [];
373
+ walk(program, (node) => {
374
+ if (node.type !== "JSXElement") return;
375
+ const name = node.openingElement?.name;
376
+ if (name?.type === "JSXIdentifier" && /^[A-Z]/.test(name.name)) found.push(node);
377
+ });
378
+ return found;
379
+ }
380
+ /**
381
+ * Resolve the value the route passes for one of the component's props. The value is read in
382
+ * the ROUTE's context, so a prop fed by the loader still resolves to its translation key.
383
+ */
384
+ function resolveProp(routeCtx, attributes, name) {
385
+ const value = attributes.get(name)?.value;
386
+ if (!value) return unsupportedElement("This value is set by the page's SEO component");
387
+ if (value.type === "Literal" && typeof value.value === "string") return {
388
+ origin: literalOrigin(routeCtx.code, value),
389
+ prefixSegments: [],
390
+ suffixSegments: []
391
+ };
392
+ if (value.type === "JSXExpressionContainer") return resolveExpression(routeCtx, value.expression, 0);
393
+ return unsupportedElement("This value is computed by code");
394
+ }
395
+ /** Replace every `prop` origin with what the route passes, keeping the surrounding parts. */
396
+ function substituteProps(element, routeCtx, attributes) {
397
+ const substituteSegments = (segments) => segments.flatMap((segment) => {
398
+ if (!("origin" in segment) || segment.origin.kind !== "prop") return [segment];
399
+ const resolved = resolveProp(routeCtx, attributes, segment.origin.name);
400
+ return [
401
+ ...resolved.prefixSegments,
402
+ { origin: resolved.origin },
403
+ ...resolved.suffixSegments
404
+ ];
405
+ });
406
+ const prefixSegments = substituteSegments(element.prefixSegments);
407
+ const suffixSegments = substituteSegments(element.suffixSegments);
408
+ if (element.origin.kind !== "prop") {
409
+ const origin = element.origin.kind === "literal" ? {
410
+ kind: "unsupported",
411
+ reason: "This text comes from the page's SEO component"
412
+ } : element.origin;
413
+ return {
414
+ ...element,
415
+ origin,
416
+ prefixSegments,
417
+ suffixSegments
418
+ };
419
+ }
420
+ const resolved = resolveProp(routeCtx, attributes, element.origin.name);
421
+ return {
422
+ ...element,
423
+ origin: resolved.origin,
424
+ prefixSegments: [...prefixSegments, ...resolved.prefixSegments],
425
+ suffixSegments: [...resolved.suffixSegments, ...suffixSegments]
426
+ };
427
+ }
428
+ /**
429
+ * Follow a component the route delegates its metadata to, and express its meta values in
430
+ * terms of what the route passes it.
431
+ */
432
+ function analyzeDelegate(routeCtx, program, loadModule) {
433
+ for (const element of findComponentElements(program)) {
434
+ const componentName = element.openingElement.name.name;
435
+ const specifier = importSpecifierOf(program, componentName);
436
+ if (!specifier) continue;
437
+ const module = loadModule(specifier);
438
+ if (!module) continue;
439
+ const moduleProgram = parseSync(module.filePath, module.code, { sourceType: "module" }).program;
440
+ const moduleElements = findMetaElements(moduleProgram);
441
+ if (!moduleElements.title && !moduleElements.description) continue;
442
+ const moduleCtx = {
443
+ code: module.code,
444
+ program: moduleProgram,
445
+ mode: "component"
446
+ };
447
+ const attributes = jsxAttributes(element);
448
+ const read = (node, analyze) => node ? substituteProps(analyze(moduleCtx, node), routeCtx, attributes) : ABSENT;
449
+ const routeElements = findMetaElements(program);
450
+ const routeRead = (node) => node ? {
451
+ ...analyzeMetaElement(routeCtx, node),
452
+ elementStart: node.start,
453
+ elementEnd: node.end
454
+ } : ABSENT;
455
+ const lineStart = routeCtx.code.lastIndexOf("\n", element.start) + 1;
456
+ return {
457
+ title: read(moduleElements.title, analyzeTitleElement),
458
+ description: read(moduleElements.description, analyzeMetaElement),
459
+ keywords: routeRead(routeElements.keywords),
460
+ robots: routeRead(routeElements.robots),
461
+ hasJsxMeta: true,
462
+ insertOffset: element.end,
463
+ insertIndent: routeCtx.code.slice(lineStart, element.start).match(/^[ \t]*/)?.[0] ?? ""
464
+ };
465
+ }
466
+ return null;
467
+ }
468
+ /** The four meta elements a module renders, if any. */
469
+ function findMetaElements(program) {
470
+ const elements = {};
471
+ walk(program, (node) => {
472
+ if (node.type !== "JSXElement") return;
473
+ const name = node.openingElement?.name;
474
+ if (name?.type !== "JSXIdentifier") return;
475
+ if (name.name === "title") {
476
+ elements.title ??= node;
477
+ return;
478
+ }
479
+ if (name.name !== "meta") return;
480
+ const metaName = jsxAttributeString(node, "name");
481
+ if (metaName === "description" || metaName === "keywords" || metaName === "robots") elements[metaName] ??= node;
482
+ });
483
+ return elements;
484
+ }
485
+ function analyzeRouteMeta(code, filePath, loadModule) {
486
+ const program = parseSync(filePath, code, { sourceType: "module" }).program;
487
+ const ctx = {
488
+ code,
489
+ program,
490
+ mode: "route"
491
+ };
492
+ const elements = findMetaElements(program);
493
+ const build = (node, read) => {
494
+ if (!node) return ABSENT;
495
+ return {
496
+ ...read(ctx, node),
497
+ elementStart: node.start,
498
+ elementEnd: node.end
499
+ };
500
+ };
501
+ const title = build(elements.title, analyzeTitleElement);
502
+ const description = build(elements.description, analyzeMetaElement);
503
+ const keywords = build(elements.keywords, analyzeMetaElement);
504
+ const robots = build(elements.robots, analyzeMetaElement);
505
+ const delegated = !title.elementStart && !description.elementStart && loadModule ? analyzeDelegate(ctx, program, loadModule) : null;
506
+ if (delegated) return delegated;
507
+ const present = [
508
+ title,
509
+ description,
510
+ keywords,
511
+ robots
512
+ ].filter((el) => el.elementEnd !== void 0);
513
+ const insertOffset = present.length ? Math.max(...present.map((el) => el.elementEnd)) : null;
514
+ let insertIndent = "";
515
+ if (insertOffset !== null) {
516
+ const anchor = Math.min(...present.map((el) => el.elementStart));
517
+ const lineStart = code.lastIndexOf("\n", anchor) + 1;
518
+ insertIndent = code.slice(lineStart, anchor).match(/^[ \t]*/)?.[0] ?? "";
519
+ }
520
+ return {
521
+ title,
522
+ description,
523
+ keywords,
524
+ robots,
525
+ hasJsxMeta: present.length > 0,
526
+ insertOffset,
527
+ insertIndent
528
+ };
529
+ }
530
+ //#endregion
531
+ export { analyzeRouteMeta };
532
+
533
+ //# sourceMappingURL=page-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-meta.js","names":[],"sources":["../src/page-meta.ts"],"sourcesContent":["import { parseSync } from \"oxc-parser\";\n\n/**\n * Static analysis of a route module's page metadata (browser-tab title, description,\n * keywords, robots indexing).\n *\n * Routes generated by the AI assistant render their meta with React 19's native hoisting\n * and take the text from i18next, e.g.:\n *\n * export async function loader({ context }) {\n * const i18n = getInstance(context);\n * return { title: i18n.t(\"about.meta.title\"), description: i18n.t(\"about.meta.description\") };\n * }\n * export default function AboutPage({ loaderData }) {\n * const { title, description } = loaderData;\n * return (<><title>{title}</title><meta name=\"description\" content={description} />…\n *\n * so editing \"the page title\" really means editing a translation key. This module resolves\n * each meta value back to its origin — an i18n key, a plain string literal, or something we\n * refuse to touch — and locates the byte ranges the editor needs to rewrite.\n */\n\nexport interface AstNode {\n type: string;\n start: number;\n end: number;\n [key: string]: unknown;\n}\n\n/** Where a meta value ultimately comes from. */\nexport type MetaValueOrigin =\n /** `i18n.t(\"key\")` in the route's loader — the text lives in the locale files. */\n | {\n kind: \"i18n\";\n key: string;\n namespace: string;\n /** Bounds of the key string, quotes excluded, so it can be repointed to another key. */\n keyStart: number;\n keyEnd: number;\n }\n /** A string literal in the source (JSX attribute, JSX text or loader value). */\n | { kind: \"literal\"; start: number; end: number; value: string; quote: string }\n /** The element is not in the route at all. */\n | { kind: \"absent\" }\n /**\n * A prop of the component being analyzed. Only produced while looking inside a shared\n * SEO component; the caller substitutes the value the route passes for it.\n */\n | { kind: \"prop\"; name: string }\n /** Computed at runtime (database row, several keys combined…) — read-only. */\n | { kind: \"unsupported\"; reason: string };\n\n/**\n * A piece of a composed value: either literal text from the template, or another dynamic\n * part that the caller resolves for display (e.g. the site name appended to every title).\n */\nexport type MetaSegment = { text: string } | { origin: MetaValueOrigin };\n\nexport interface MetaElement {\n origin: MetaValueOrigin;\n /** What is rendered around the editable part, e.g. `{`${title} - ${tagline}`}`. */\n prefixSegments: MetaSegment[];\n suffixSegments: MetaSegment[];\n /** Bounds of the whole JSX element, when the route has one. */\n elementStart?: number;\n elementEnd?: number;\n}\n\nexport interface RouteMetaAnalysis {\n title: MetaElement;\n description: MetaElement;\n keywords: MetaElement;\n robots: MetaElement;\n /** True when the route renders at least one meta element as JSX. */\n hasJsxMeta: boolean;\n /**\n * Offset a new `<meta …/>` sibling can be inserted at (right after the last existing\n * meta element), plus the indentation of the line it sits on.\n */\n insertOffset: number | null;\n insertIndent: string;\n}\n\nconst ABSENT: MetaElement = { origin: { kind: \"absent\" }, prefixSegments: [], suffixSegments: [] };\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\n/** Value of a JSX attribute that is a plain string, e.g. `name=\"description\"`. */\nfunction jsxAttributeString(element: AstNode, attributeName: string): string | null {\n const opening = element.openingElement as AstNode | undefined;\n for (const attr of (opening?.attributes as AstNode[]) ?? []) {\n if (attr.type !== \"JSXAttribute\") continue;\n const name = attr.name as AstNode;\n if (name?.type !== \"JSXIdentifier\" || name.name !== attributeName) continue;\n const value = attr.value as AstNode | null;\n if (value?.type === \"Literal\" && typeof value.value === \"string\") return value.value;\n return null;\n }\n return null;\n}\n\nfunction jsxAttributeNode(element: AstNode, attributeName: string): AstNode | null {\n const opening = element.openingElement as AstNode | undefined;\n for (const attr of (opening?.attributes as AstNode[]) ?? []) {\n if (attr.type !== \"JSXAttribute\") continue;\n const name = attr.name as AstNode;\n if (name?.type === \"JSXIdentifier\" && name.name === attributeName) return attr;\n }\n return null;\n}\n\n/** A string literal node → an editable origin covering the text between its quotes. */\nfunction literalOrigin(code: string, node: AstNode): MetaValueOrigin {\n const quote = code[node.start];\n const quoted = quote === '\"' || quote === \"'\" || quote === \"`\";\n return {\n kind: \"literal\",\n start: quoted ? node.start + 1 : node.start,\n end: quoted ? node.end - 1 : node.end,\n value: node.value as string,\n quote: quoted ? quote : '\"',\n };\n}\n\n/**\n * Read `t(\"key\")` / `i18n.t(\"ns:key\", { ns })`. Returns null when the call is not a\n * translation lookup we can address (dynamic key, interpolated values…).\n */\nfunction readTranslationCall(node: AstNode): MetaValueOrigin | null {\n if (node.type !== \"CallExpression\") return null;\n const callee = node.callee as AstNode;\n const isT =\n (callee.type === \"Identifier\" && callee.name === \"t\") ||\n (callee.type === \"MemberExpression\" &&\n !callee.computed &&\n (callee.property as AstNode)?.type === \"Identifier\" &&\n ((callee.property as AstNode).name as string) === \"t\");\n if (!isT) return null;\n\n const args = (node.arguments as AstNode[]) ?? [];\n const first = args[0];\n if (first?.type !== \"Literal\" || typeof first.value !== \"string\") return null;\n\n let namespace = \"translation\";\n let key = first.value;\n const colon = key.indexOf(\":\");\n if (colon > 0) {\n namespace = key.slice(0, colon);\n key = key.slice(colon + 1);\n }\n // A second argument is only acceptable when it merely selects the namespace: anything\n // else (interpolation values, counts) means the stored string is not what is displayed.\n const second = args[1];\n if (second) {\n if (second.type !== \"ObjectExpression\") return null;\n const props = (second.properties as AstNode[]) ?? [];\n if (props.length !== 1) return null;\n const prop = props[0];\n if (prop.type !== \"Property\" || prop.computed) return null;\n const propKey = prop.key as AstNode;\n const propValue = prop.value as AstNode;\n const propName = propKey.type === \"Identifier\" ? propKey.name : propKey.value;\n if (propName !== \"ns\" || propValue.type !== \"Literal\" || typeof propValue.value !== \"string\") {\n return null;\n }\n namespace = propValue.value;\n }\n\n return { kind: \"i18n\", key, namespace, keyStart: first.start + 1, keyEnd: first.end - 1 };\n}\n\ninterface TraceContext {\n code: string;\n program: AstNode;\n /**\n * \"route\": identifiers come from the loader through `loaderData`.\n * \"component\": identifiers are local constants or props of the component itself.\n */\n mode: \"route\" | \"component\";\n}\n\n/** Initializer of a plain `const x = …` declaration, when the module has one. */\nfunction localDeclaration(ctx: TraceContext, identifier: string): AstNode | null {\n let found: AstNode | null = null;\n walk(ctx.program, (node) => {\n if (found || node.type !== \"VariableDeclarator\") return;\n const id = node.id as AstNode;\n if (id?.type === \"Identifier\" && id.name === identifier) found = (node.init as AstNode) ?? null;\n });\n return found;\n}\n\n/** The key an object pattern binds to `identifier`, following `{ a: b }` and `{ a = 1 }`. */\nfunction patternKeyFor(pattern: AstNode, identifier: string): string | null {\n for (const prop of (pattern.properties as AstNode[]) ?? []) {\n if (prop.type !== \"Property\") continue;\n const key = prop.key as AstNode;\n let value = prop.value as AstNode;\n // `{ image = \"/default.png\" }` binds through an assignment pattern.\n if (value?.type === \"AssignmentPattern\") value = value.left as AstNode;\n if (value?.type === \"Identifier\" && value.name === identifier && key?.type === \"Identifier\") {\n return key.name as string;\n }\n }\n return null;\n}\n\n/** Property name an identifier was destructured from, e.g. `const { title } = loaderData`. */\nfunction destructuredSourceName(ctx: TraceContext, identifier: string): string | null {\n let found: string | null = null;\n walk(ctx.program, (node) => {\n if (found || node.type !== \"VariableDeclarator\") return;\n const id = node.id as AstNode;\n if (id?.type !== \"ObjectPattern\") return;\n found = patternKeyFor(id, identifier);\n });\n return found;\n}\n\n/**\n * Prop name an identifier is bound to by a component's signature, e.g.\n * `function SeoHead({ title }: Props)`. Props arrive as a parameter pattern, not as a\n * variable declaration, so they need their own lookup.\n */\nfunction parameterPropName(ctx: TraceContext, identifier: string): string | null {\n let found: string | null = null;\n walk(ctx.program, (node) => {\n if (found) return;\n if (\n node.type !== \"FunctionDeclaration\" &&\n node.type !== \"ArrowFunctionExpression\" &&\n node.type !== \"FunctionExpression\"\n ) {\n return;\n }\n const first = ((node.params as AstNode[]) ?? [])[0];\n if (first?.type === \"ObjectPattern\") found = patternKeyFor(first, identifier);\n });\n return found;\n}\n\n/** The route's exported `loader` function, whatever declaration form it uses. */\nfunction findLoader(ctx: TraceContext): AstNode | null {\n const body = (ctx.program.body as AstNode[]) ?? [];\n for (const statement of body) {\n if (statement.type !== \"ExportNamedDeclaration\") continue;\n const declaration = statement.declaration as AstNode | null;\n if (!declaration) continue;\n if (declaration.type === \"FunctionDeclaration\") {\n const id = declaration.id as AstNode | null;\n if (id?.type === \"Identifier\" && id.name === \"loader\") 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 === \"loader\") return (decl.init as AstNode) ?? null;\n }\n }\n }\n return null;\n}\n\n/** Value the loader returns for `propertyName`, across every `return { … }` it contains. */\nfunction loaderReturnValue(loader: AstNode, propertyName: string): AstNode | null {\n let found: AstNode | null = null;\n walk(loader, (node) => {\n if (found || node.type !== \"ReturnStatement\") return;\n const argument = node.argument as AstNode | null;\n if (argument?.type !== \"ObjectExpression\") return;\n for (const prop of (argument.properties as AstNode[]) ?? []) {\n if (prop.type !== \"Property\" || prop.computed) continue;\n const key = prop.key as AstNode;\n const name = key?.type === \"Identifier\" ? key.name : key?.type === \"Literal\" ? key.value : null;\n if (name === propertyName) {\n found = prop.value as AstNode;\n return;\n }\n }\n });\n return found;\n}\n\nconst unsupportedElement = (reason: string): MetaElement => ({\n origin: { kind: \"unsupported\", reason },\n prefixSegments: [],\n suffixSegments: [],\n});\n\n/**\n * Follow a component identifier back to the loader value that feeds it. The loader may\n * itself compose several translations, so this returns a full element rather than a single\n * origin.\n */\nfunction traceIdentifier(ctx: TraceContext, identifier: string, depth: number): MetaElement {\n // Guards against a self-referencing declaration chain.\n if (depth > 4) return unsupportedElement(\"This value is computed by code\");\n\n // `const fullTitle = `${title} — Acme`` and friends.\n const local = localDeclaration(ctx, identifier);\n if (local) return resolveExpression(ctx, local, depth + 1);\n\n // Inside a shared SEO component the value is a prop: hand it back to the caller, which\n // knows what the route passes for it.\n if (ctx.mode === \"component\") {\n const propName = parameterPropName(ctx, identifier) ?? destructuredSourceName(ctx, identifier);\n if (!propName) return unsupportedElement(\"This value is computed by code\");\n return { origin: { kind: \"prop\", name: propName }, prefixSegments: [], suffixSegments: [] };\n }\n\n const sourceName = destructuredSourceName(ctx, identifier);\n if (!sourceName) return unsupportedElement(\"This value is computed by code\");\n\n const loader = findLoader(ctx);\n if (!loader) return unsupportedElement(\"This value is computed by code\");\n const value = loaderReturnValue(loader, sourceName);\n if (!value) return unsupportedElement(\"This value is computed by code\");\n\n const translation = readTranslationCall(value);\n if (translation) return { origin: translation, prefixSegments: [], suffixSegments: [] };\n if (value.type === \"Literal\" && typeof value.value === \"string\") {\n return { origin: literalOrigin(ctx.code, value), prefixSegments: [], suffixSegments: [] };\n }\n if (value.type === \"TemplateLiteral\") return resolveExpression(ctx, value, depth + 1);\n return unsupportedElement(\"This value is built from other content\");\n}\n\n/** Resolve any expression used as a meta value, keeping the read-only parts around it. */\nfunction resolveExpression(ctx: TraceContext, expression: AstNode, depth = 0): MetaElement {\n if (expression.type === \"Literal\" && typeof expression.value === \"string\") {\n return { origin: literalOrigin(ctx.code, expression), prefixSegments: [], suffixSegments: [] };\n }\n if (expression.type === \"Identifier\") {\n return traceIdentifier(ctx, expression.name as string, depth);\n }\n // `i18n.t(\"key\")` used inline, e.g. in a title composed inside the loader.\n const translation = readTranslationCall(expression);\n if (translation) return { origin: translation, prefixSegments: [], suffixSegments: [] };\n if (expression.type === \"TemplateLiteral\") {\n const quasis = (expression.quasis as AstNode[]) ?? [];\n const expressions = (expression.expressions as AstNode[]) ?? [];\n if (expressions.length === 0 && quasis.length === 1) {\n const quasi = quasis[0];\n const cooked = (quasi.value as { cooked?: string })?.cooked ?? \"\";\n return {\n origin: { kind: \"literal\", start: quasi.start, end: quasi.end, value: cooked, quote: \"`\" },\n prefixSegments: [],\n suffixSegments: [],\n };\n }\n\n // Interleave the literal chunks with the interpolated values, then take the first part\n // we can actually address as the editable one. A title built from several translations\n // (`${pageTitle} - ${siteTagline}`) still has one part that belongs to this page; the\n // rest becomes read-only context so the user sees the whole rendered string.\n const segments: MetaSegment[] = [];\n quasis.forEach((quasi, index) => {\n segments.push({ text: (quasi.value as { cooked?: string })?.cooked ?? \"\" });\n const interpolated = expressions[index];\n if (!interpolated) return;\n // Each interpolation can itself be composed — flatten its own parts in place.\n const resolved = resolveExpression(ctx, interpolated, depth + 1);\n segments.push(...resolved.prefixSegments, { origin: resolved.origin }, ...resolved.suffixSegments);\n });\n\n const editableAt = segments.findIndex(\n (segment) =>\n \"origin\" in segment &&\n (segment.origin.kind === \"i18n\" ||\n segment.origin.kind === \"literal\" ||\n segment.origin.kind === \"prop\"),\n );\n if (editableAt === -1) {\n return {\n origin: { kind: \"unsupported\", reason: \"This value is computed by code\" },\n prefixSegments: [],\n suffixSegments: [],\n };\n }\n return {\n origin: (segments[editableAt] as { origin: MetaValueOrigin }).origin,\n prefixSegments: segments.slice(0, editableAt),\n suffixSegments: segments.slice(editableAt + 1),\n };\n }\n return {\n origin: { kind: \"unsupported\", reason: \"This value is computed by code\" },\n prefixSegments: [],\n suffixSegments: [],\n };\n}\n\n/** Read `<title>…</title>` children. */\nfunction analyzeTitleElement(ctx: TraceContext, element: AstNode): MetaElement {\n const children = ((element.children as AstNode[]) ?? []).filter(\n (child) => child.type !== \"JSXText\" || String(child.value ?? \"\").trim() !== \"\",\n );\n if (children.length === 1) {\n const child = children[0];\n if (child.type === \"JSXText\") {\n return {\n origin: {\n kind: \"literal\",\n start: child.start,\n end: child.end,\n value: String(child.value ?? \"\"),\n quote: '\"',\n },\n prefixSegments: [],\n suffixSegments: [],\n };\n }\n if (child.type === \"JSXExpressionContainer\") {\n return resolveExpression(ctx, child.expression as AstNode);\n }\n }\n return {\n origin: { kind: \"unsupported\", reason: \"This title is computed by code\" },\n prefixSegments: [],\n suffixSegments: [],\n };\n}\n\n/** Read the `content` attribute of a `<meta …/>` element. */\nfunction analyzeMetaElement(ctx: TraceContext, element: AstNode): MetaElement {\n const attribute = jsxAttributeNode(element, \"content\");\n const value = attribute?.value as AstNode | null | undefined;\n if (!value) {\n return {\n origin: { kind: \"unsupported\", reason: \"This tag has no content\" },\n prefixSegments: [],\n suffixSegments: [],\n };\n }\n if (value.type === \"Literal\" && typeof value.value === \"string\") {\n return { origin: literalOrigin(ctx.code, value), prefixSegments: [], suffixSegments: [] };\n }\n if (value.type === \"JSXExpressionContainer\") {\n return resolveExpression(ctx, value.expression as AstNode);\n }\n return {\n origin: { kind: \"unsupported\", reason: \"This value is computed by code\" },\n prefixSegments: [],\n suffixSegments: [],\n };\n}\n\n/**\n * Locate and resolve the four meta values a route can render as JSX.\n */\n\n/** Import specifier a local identifier (a component name) was imported from. */\nfunction importSpecifierOf(program: AstNode, name: string): string | null {\n for (const statement of (program.body as AstNode[]) ?? []) {\n if (statement.type !== \"ImportDeclaration\") continue;\n for (const spec of (statement.specifiers as AstNode[]) ?? []) {\n const local = spec.local as AstNode | undefined;\n if (local?.type === \"Identifier\" && local.name === name) {\n const source = statement.source as AstNode;\n return typeof source?.value === \"string\" ? source.value : null;\n }\n }\n }\n return null;\n}\n\n/** JSX attributes of an element, by name, ignoring spreads. */\nfunction jsxAttributes(element: AstNode): Map<string, AstNode> {\n const attributes = new Map<string, AstNode>();\n const opening = element.openingElement as AstNode | undefined;\n for (const attr of (opening?.attributes as AstNode[]) ?? []) {\n if (attr.type !== \"JSXAttribute\") continue;\n const name = attr.name as AstNode;\n if (name?.type === \"JSXIdentifier\") attributes.set(name.name as string, attr);\n }\n return attributes;\n}\n\n/** Rendered components, in source order, that could stand in for the meta tags. */\nfunction findComponentElements(program: AstNode): AstNode[] {\n const found: AstNode[] = [];\n walk(program, (node) => {\n if (node.type !== \"JSXElement\") return;\n const name = (node.openingElement as AstNode)?.name as AstNode | undefined;\n // Components are capitalized; lowercase names are plain HTML tags.\n if (name?.type === \"JSXIdentifier\" && /^[A-Z]/.test(name.name as string)) found.push(node);\n });\n return found;\n}\n\n/**\n * Resolve the value the route passes for one of the component's props. The value is read in\n * the ROUTE's context, so a prop fed by the loader still resolves to its translation key.\n */\nfunction resolveProp(routeCtx: TraceContext, attributes: Map<string, AstNode>, name: string): MetaElement {\n const attribute = attributes.get(name);\n const value = attribute?.value as AstNode | null | undefined;\n if (!value) return unsupportedElement(\"This value is set by the page's SEO component\");\n if (value.type === \"Literal\" && typeof value.value === \"string\") {\n return { origin: literalOrigin(routeCtx.code, value), prefixSegments: [], suffixSegments: [] };\n }\n if (value.type === \"JSXExpressionContainer\") {\n return resolveExpression(routeCtx, value.expression as AstNode, 0);\n }\n return unsupportedElement(\"This value is computed by code\");\n}\n\n/** Replace every `prop` origin with what the route passes, keeping the surrounding parts. */\nfunction substituteProps(\n element: MetaElement,\n routeCtx: TraceContext,\n attributes: Map<string, AstNode>,\n): MetaElement {\n const substituteSegments = (segments: MetaSegment[]): MetaSegment[] =>\n segments.flatMap((segment) => {\n if (!(\"origin\" in segment) || segment.origin.kind !== \"prop\") return [segment];\n const resolved = resolveProp(routeCtx, attributes, segment.origin.name);\n return [...resolved.prefixSegments, { origin: resolved.origin }, ...resolved.suffixSegments];\n });\n\n const prefixSegments = substituteSegments(element.prefixSegments);\n const suffixSegments = substituteSegments(element.suffixSegments);\n\n if (element.origin.kind !== \"prop\") {\n // The value lives in the shared component, so it is the same on every page: showing it\n // as editable here would silently rewrite the whole site.\n const origin: MetaValueOrigin =\n element.origin.kind === \"literal\"\n ? { kind: \"unsupported\", reason: \"This text comes from the page's SEO component\" }\n : element.origin;\n return { ...element, origin, prefixSegments, suffixSegments };\n }\n\n const resolved = resolveProp(routeCtx, attributes, element.origin.name);\n return {\n ...element,\n origin: resolved.origin,\n prefixSegments: [...prefixSegments, ...resolved.prefixSegments],\n suffixSegments: [...resolved.suffixSegments, ...suffixSegments],\n };\n}\n\n/**\n * Follow a component the route delegates its metadata to, and express its meta values in\n * terms of what the route passes it.\n */\nfunction analyzeDelegate(\n routeCtx: TraceContext,\n program: AstNode,\n loadModule: ModuleLoader,\n): RouteMetaAnalysis | null {\n for (const element of findComponentElements(program)) {\n const componentName = ((element.openingElement as AstNode).name as AstNode).name as string;\n const specifier = importSpecifierOf(program, componentName);\n if (!specifier) continue;\n const module = loadModule(specifier);\n if (!module) continue;\n\n const moduleProgram = parseSync(module.filePath, module.code, { sourceType: \"module\" })\n .program as unknown as AstNode;\n const moduleElements = findMetaElements(moduleProgram);\n if (!moduleElements.title && !moduleElements.description) continue;\n\n const moduleCtx: TraceContext = { code: module.code, program: moduleProgram, mode: \"component\" };\n const attributes = jsxAttributes(element);\n const read = (\n node: AstNode | undefined,\n analyze: (ctx: TraceContext, el: AstNode) => MetaElement,\n ): MetaElement => (node ? substituteProps(analyze(moduleCtx, node), routeCtx, attributes) : ABSENT);\n\n // Keywords and robots are page-specific, so they are read from — and written to — the\n // route itself, next to the component, rather than to the shared component.\n const routeElements = findMetaElements(program);\n const routeRead = (node: AstNode | undefined): MetaElement =>\n node\n ? { ...analyzeMetaElement(routeCtx, node), elementStart: node.start, elementEnd: node.end }\n : ABSENT;\n\n const lineStart = routeCtx.code.lastIndexOf(\"\\n\", element.start) + 1;\n return {\n title: read(moduleElements.title, analyzeTitleElement),\n description: read(moduleElements.description, analyzeMetaElement),\n keywords: routeRead(routeElements.keywords),\n robots: routeRead(routeElements.robots),\n hasJsxMeta: true,\n insertOffset: element.end,\n insertIndent: routeCtx.code.slice(lineStart, element.start).match(/^[ \\t]*/)?.[0] ?? \"\",\n };\n }\n return null;\n}\n\n/** The four meta elements a module renders, if any. */\nfunction findMetaElements(program: AstNode) {\n const elements: Partial<Record<\"title\" | \"description\" | \"keywords\" | \"robots\", AstNode>> = {};\n walk(program, (node) => {\n if (node.type !== \"JSXElement\") return;\n const name = (node.openingElement as AstNode)?.name as AstNode | undefined;\n if (name?.type !== \"JSXIdentifier\") return;\n if (name.name === \"title\") {\n elements.title ??= node;\n return;\n }\n if (name.name !== \"meta\") return;\n const metaName = jsxAttributeString(node, \"name\");\n if (metaName === \"description\" || metaName === \"keywords\" || metaName === \"robots\") {\n elements[metaName] ??= node;\n }\n });\n return elements;\n}\n\n/** Reads a module (by import specifier) so a shared SEO component can be followed. */\nexport type ModuleLoader = (specifier: string) => { code: string; filePath: string } | null;\n\nexport function analyzeRouteMeta(\n code: string,\n filePath: string,\n loadModule?: ModuleLoader,\n): RouteMetaAnalysis {\n const ast = parseSync(filePath, code, { sourceType: \"module\" });\n const program = ast.program as unknown as AstNode;\n const ctx: TraceContext = { code, program, mode: \"route\" };\n\n const elements = findMetaElements(program);\n\n const build = (\n node: AstNode | undefined,\n read: (ctx: TraceContext, element: AstNode) => MetaElement,\n ): MetaElement => {\n if (!node) return ABSENT;\n return { ...read(ctx, node), elementStart: node.start, elementEnd: node.end };\n };\n\n const title = build(elements.title, analyzeTitleElement);\n const description = build(elements.description, analyzeMetaElement);\n const keywords = build(elements.keywords, analyzeMetaElement);\n const robots = build(elements.robots, analyzeMetaElement);\n\n // Nothing inline: the route may hand its metadata to a shared component\n // (`<SeoHead title={title} description={description} />`), so follow it.\n const delegated =\n !title.elementStart && !description.elementStart && loadModule\n ? analyzeDelegate(ctx, program, loadModule)\n : null;\n if (delegated) return delegated;\n\n // New tags go after the last meta element already there, so they stay grouped.\n const present = [title, description, keywords, robots].filter((el) => el.elementEnd !== undefined);\n const insertOffset = present.length ? Math.max(...present.map((el) => el.elementEnd as number)) : null;\n let insertIndent = \"\";\n if (insertOffset !== null) {\n const anchor = Math.min(...present.map((el) => el.elementStart as number));\n const lineStart = code.lastIndexOf(\"\\n\", anchor) + 1;\n insertIndent = code.slice(lineStart, anchor).match(/^[ \\t]*/)?.[0] ?? \"\";\n }\n\n return {\n title,\n description,\n keywords,\n robots,\n hasJsxMeta: present.length > 0,\n insertOffset,\n insertIndent,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ //#region src/site-meta.d.ts
2
+ type EnsureRootResult = {
3
+ ok: true;
4
+ code: string;
5
+ changed: boolean;
6
+ } | {
7
+ ok: false;
8
+ reason: string;
9
+ };
10
+ /**
11
+ * Icon files left untouched: an ICO is already small and an SVG scales on its own. Every
12
+ * other image is downscaled to a square PNG, because a favicon is drawn at about 16px and
13
+ * WebP is not accepted as an icon everywhere.
14
+ */
15
+ declare const FAVICON_PASS_THROUGH_TYPES: string[];
16
+ /** Extensions matching {@link FAVICON_PASS_THROUGH_TYPES}, for callers working from a filename. */
17
+ declare const FAVICON_PASS_THROUGH_EXTENSIONS: string[];
18
+ /** Square size a favicon is downscaled to. */
19
+ declare const FAVICON_SIZE = 64;
20
+ /**
21
+ * Name of the downscaled copy of a favicon source. Sources are often already called
22
+ * "favicon", so the suffix is only added when it would say something new.
23
+ */
24
+ declare function faviconFileName(sourceName: string): string;
25
+ declare function ensureRootSiteMeta(code: string, filePath: string): EnsureRootResult;
26
+ //#endregion
27
+ export { EnsureRootResult, FAVICON_PASS_THROUGH_EXTENSIONS, FAVICON_PASS_THROUGH_TYPES, FAVICON_SIZE, ensureRootSiteMeta, faviconFileName };
28
+ //# sourceMappingURL=site-meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-meta.d.ts","names":[],"sources":["../src/site-meta.ts"],"mappings":";KAoBY,gBAAA;EAAqB,EAAA;EAAU,IAAA;EAAc,OAAA;AAAA;EAAuB,EAAA;EAAW,MAAA;AAAA;;;;;AAO3F;cAAa,0BAAA;;cAEA,+BAAA;;cAEA,YAAA;;;;;iBAMG,eAAA,CAAgB,UAAA;AAAA,iBAiFhB,kBAAA,CAAmB,IAAA,UAAc,QAAA,WAAmB,gBAAA"}