@rsc-kit/core 0.8.0 → 0.9.0
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/metadata.d.ts +69 -0
- package/dist/metadata.js +17 -0
- package/dist/metadata.js.map +1 -0
- package/dist/vite.js +42 -15
- package/dist/vite.js.map +1 -1
- package/package.json +6 -6
- package/dist/types.d.ts +0 -82
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export interface IconDescriptor {
|
|
2
|
+
url: string | URL;
|
|
3
|
+
type?: string;
|
|
4
|
+
sizes?: string;
|
|
5
|
+
color?: string;
|
|
6
|
+
rel?: string;
|
|
7
|
+
media?: string;
|
|
8
|
+
fetchPriority?: 'high' | 'low' | 'auto';
|
|
9
|
+
}
|
|
10
|
+
export type IconURL = string | URL;
|
|
11
|
+
export interface Icons {
|
|
12
|
+
icon?: IconURL | IconDescriptor | (IconURL | IconDescriptor)[];
|
|
13
|
+
apple?: IconURL | IconDescriptor | (IconURL | IconDescriptor)[];
|
|
14
|
+
shortcut?: IconURL | IconDescriptor | (IconURL | IconDescriptor)[];
|
|
15
|
+
other?: IconDescriptor | IconDescriptor[];
|
|
16
|
+
}
|
|
17
|
+
/** A layout's title, wrapping the titles of the pages beneath it. */
|
|
18
|
+
export interface TitleTemplate {
|
|
19
|
+
/** `%s` stands in for the page's own title. */
|
|
20
|
+
template?: string;
|
|
21
|
+
/** Used by a page that exports no title of its own. */
|
|
22
|
+
default?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface Metadata {
|
|
25
|
+
/** A string on a page; a template on a layout, applied to the pages below it. */
|
|
26
|
+
title?: string | TitleTemplate;
|
|
27
|
+
description?: string;
|
|
28
|
+
keywords?: string | string[];
|
|
29
|
+
author?: string;
|
|
30
|
+
robots?: string;
|
|
31
|
+
icons?: IconURL | (IconURL | IconDescriptor)[] | Icons | null;
|
|
32
|
+
'og:title'?: string;
|
|
33
|
+
'og:description'?: string;
|
|
34
|
+
'og:image'?: string;
|
|
35
|
+
'og:url'?: string;
|
|
36
|
+
'og:type'?: string;
|
|
37
|
+
'og:site_name'?: string;
|
|
38
|
+
'twitter:card'?: string;
|
|
39
|
+
'twitter:title'?: string;
|
|
40
|
+
'twitter:description'?: string;
|
|
41
|
+
'twitter:image'?: string;
|
|
42
|
+
'twitter:site'?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Any other meta tag, by name.
|
|
45
|
+
*
|
|
46
|
+
* other: { 'fb:app_id': '123', 'theme-color': '#000' }
|
|
47
|
+
*
|
|
48
|
+
* Here rather than alongside the named keys, and that is what makes the rest
|
|
49
|
+
* of this interface worth annotating. An index signature on the interface
|
|
50
|
+
* itself made every key legal — so `titel` was accepted in silence, and an
|
|
51
|
+
* editor offered no completions at all, because with any identifier valid
|
|
52
|
+
* TypeScript reads an unfinished key as a shorthand property and goes looking
|
|
53
|
+
* for a variable by that name.
|
|
54
|
+
*/
|
|
55
|
+
other?: Record<string, string | string[] | null | undefined>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Metadata that depends on the request.
|
|
59
|
+
*
|
|
60
|
+
* Receives the same awaitables a page does, so one shape is learned rather
|
|
61
|
+
* than two:
|
|
62
|
+
*
|
|
63
|
+
* export const generateMetadata: GenerateMetadata<{ slug: string }> =
|
|
64
|
+
* async ({ params }) => ({ title: (await params).slug })
|
|
65
|
+
*/
|
|
66
|
+
export type GenerateMetadata<P = Record<string, string>> = (args: {
|
|
67
|
+
params: Promise<P>;
|
|
68
|
+
searchParams: Promise<URLSearchParams>;
|
|
69
|
+
}) => Metadata | Promise<Metadata>;
|
package/dist/metadata.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// What a page says about itself, as importable types.
|
|
2
|
+
//
|
|
3
|
+
// import type { Metadata } from '@rsc-kit/core/metadata'
|
|
4
|
+
//
|
|
5
|
+
// export const metadata: Metadata = { title: 'Orders' }
|
|
6
|
+
//
|
|
7
|
+
// Imported rather than ambient, and that is the whole point of the move. An
|
|
8
|
+
// ambient declaration has to be COPIED into the project, which means it is not
|
|
9
|
+
// there until the build has run once — so a freshly cloned app reports "Cannot
|
|
10
|
+
// find name 'Metadata'" on every page until someone runs the dev server. An
|
|
11
|
+
// import resolves from node_modules the moment dependencies are installed.
|
|
12
|
+
//
|
|
13
|
+
// The ambient names still work. `.rsc-kit/rsc-types.d.ts` now aliases these
|
|
14
|
+
// rather than restating them, so there is one definition and two ways to reach
|
|
15
|
+
// it.
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,EAAE;AACF,6DAA6D;AAC7D,EAAE;AACF,4DAA4D;AAC5D,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,+EAA+E;AAC/E,4EAA4E;AAC5E,2EAA2E;AAC3E,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,MAAM","sourcesContent":["// What a page says about itself, as importable types.\n//\n// import type { Metadata } from '@rsc-kit/core/metadata'\n//\n// export const metadata: Metadata = { title: 'Orders' }\n//\n// Imported rather than ambient, and that is the whole point of the move. An\n// ambient declaration has to be COPIED into the project, which means it is not\n// there until the build has run once — so a freshly cloned app reports \"Cannot\n// find name 'Metadata'\" on every page until someone runs the dev server. An\n// import resolves from node_modules the moment dependencies are installed.\n//\n// The ambient names still work. `.rsc-kit/rsc-types.d.ts` now aliases these\n// rather than restating them, so there is one definition and two ways to reach\n// it.\n\nexport interface IconDescriptor {\n url: string | URL\n type?: string\n sizes?: string\n color?: string\n rel?: string\n media?: string\n fetchPriority?: 'high' | 'low' | 'auto'\n}\n\nexport type IconURL = string | URL\n\nexport interface Icons {\n icon?: IconURL | IconDescriptor | (IconURL | IconDescriptor)[]\n apple?: IconURL | IconDescriptor | (IconURL | IconDescriptor)[]\n shortcut?: IconURL | IconDescriptor | (IconURL | IconDescriptor)[]\n other?: IconDescriptor | IconDescriptor[]\n}\n\n/** A layout's title, wrapping the titles of the pages beneath it. */\nexport interface TitleTemplate {\n /** `%s` stands in for the page's own title. */\n template?: string\n /** Used by a page that exports no title of its own. */\n default?: string\n}\n\nexport interface Metadata {\n /** A string on a page; a template on a layout, applied to the pages below it. */\n title?: string | TitleTemplate\n description?: string\n keywords?: string | string[]\n author?: string\n robots?: string\n icons?: IconURL | (IconURL | IconDescriptor)[] | Icons | null\n 'og:title'?: string\n 'og:description'?: string\n 'og:image'?: string\n 'og:url'?: string\n 'og:type'?: string\n 'og:site_name'?: string\n 'twitter:card'?: string\n 'twitter:title'?: string\n 'twitter:description'?: string\n 'twitter:image'?: string\n 'twitter:site'?: string\n\n /**\n * Any other meta tag, by name.\n *\n * other: { 'fb:app_id': '123', 'theme-color': '#000' }\n *\n * Here rather than alongside the named keys, and that is what makes the rest\n * of this interface worth annotating. An index signature on the interface\n * itself made every key legal — so `titel` was accepted in silence, and an\n * editor offered no completions at all, because with any identifier valid\n * TypeScript reads an unfinished key as a shorthand property and goes looking\n * for a variable by that name.\n */\n other?: Record<string, string | string[] | null | undefined>\n}\n\n/**\n * Metadata that depends on the request.\n *\n * Receives the same awaitables a page does, so one shape is learned rather\n * than two:\n *\n * export const generateMetadata: GenerateMetadata<{ slug: string }> =\n * async ({ params }) => ({ title: (await params).slug })\n */\nexport type GenerateMetadata<P = Record<string, string>> = (args: {\n params: Promise<P>\n searchParams: Promise<URLSearchParams>\n}) => Metadata | Promise<Metadata>\n"]}
|
package/dist/vite.js
CHANGED
|
@@ -413,10 +413,19 @@ function warnIfTypesUnreachable() {
|
|
|
413
413
|
const include = JSON.parse(text).include;
|
|
414
414
|
if (!Array.isArray(include))
|
|
415
415
|
return;
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
416
|
+
const covers = (what) => include.some((entry) => typeof entry === 'string' && entry.includes(what));
|
|
417
|
+
if (!covers('.rsc-kit')) {
|
|
418
|
+
log(`tsconfig.json does not include .rsc-kit, where the generated types are written.\n` +
|
|
419
|
+
` Add ".rsc-kit/**/*" to "include", or typed routes and rpc() fall back to string.`);
|
|
420
|
+
}
|
|
421
|
+
// Only when there is something there to check. An app with no api routes
|
|
422
|
+
// has nothing to be warned about, and a warning it cannot act on is one it
|
|
423
|
+
// learns to scroll past.
|
|
424
|
+
if (existsSync(join(projectRoot, 'server')) && !covers('server')) {
|
|
425
|
+
log(`tsconfig.json does not include server/, where your api handlers live.\n` +
|
|
426
|
+
` Add "server/**/*" to "include", or they are not type-checked at all — ` +
|
|
427
|
+
`a handler returning the wrong shape builds and deploys without complaint.`);
|
|
428
|
+
}
|
|
420
429
|
}
|
|
421
430
|
catch {
|
|
422
431
|
// An unparseable tsconfig is the project's own problem, not this one's.
|
|
@@ -451,10 +460,6 @@ function writeHostBindings(manifest) {
|
|
|
451
460
|
// and a typecheck cannot see it. Written whether or not there are actions:
|
|
452
461
|
// server components call it directly too.
|
|
453
462
|
writeFileSync(join(typesDir, 'rsc-env.d.ts'), renderHostGlobalTypes());
|
|
454
|
-
// The engine's own ambient types, copied where the app's typechecker will
|
|
455
|
-
// see them. Deliberately a separate file from the one above: this one is
|
|
456
|
-
// the engine's and identical everywhere, that one is generated from how
|
|
457
|
-
// this host is configured.
|
|
458
463
|
// The urls this build found, so a link to a page that does not exist fails
|
|
459
464
|
// the typecheck instead of the browser.
|
|
460
465
|
writeFileSync(join(typesDir, 'rsc-routes.d.ts'), renderRouteTypes(manifest));
|
|
@@ -463,10 +468,6 @@ function writeHostBindings(manifest) {
|
|
|
463
468
|
// an app-authored one goes stale — the first version named only RscEngine,
|
|
464
469
|
// which typechecks a server and fails a prerender script.
|
|
465
470
|
writeFileSync(join(typesDir, 'rsc-engine.d.ts'), ENGINE_TYPES);
|
|
466
|
-
const engineTypes = join(packageDir, 'types.d.ts');
|
|
467
|
-
if (existsSync(engineTypes)) {
|
|
468
|
-
writeFileSync(join(typesDir, 'rsc-types.d.ts'), readFileSync(engineTypes, 'utf-8'));
|
|
469
|
-
}
|
|
470
471
|
warnIfTypesUnreachable();
|
|
471
472
|
const target = join(sourceDir, 'server-actions.generated.ts');
|
|
472
473
|
// A host with no functions of its own leaves no file behind: kept, its
|
|
@@ -1492,7 +1493,14 @@ async function renderTree(
|
|
|
1492
1493
|
if (bootstrap) head.push(createElement(DocumentTitle, { key: '__ts', title: String(md.title) }))
|
|
1493
1494
|
}
|
|
1494
1495
|
if (md.description != null) head.push(createElement('meta', { key: '__d', name: 'description', content: String(md.description) }))
|
|
1495
|
-
|
|
1496
|
+
// other is flattened in beside the named keys, because it is a place to put
|
|
1497
|
+
// meta tags rather than a meta tag by that name. A key at the top level
|
|
1498
|
+
// still renders — the type no longer invites one, but an app written
|
|
1499
|
+
// against the old shape must not silently lose its tags.
|
|
1500
|
+
const named = Object.entries(md).filter(([k]) => k !== 'other')
|
|
1501
|
+
const extra = Object.entries((md.other ?? {}) as Record<string, unknown>)
|
|
1502
|
+
|
|
1503
|
+
for (const [k, v] of [...named, ...extra]) {
|
|
1496
1504
|
if (k === 'title' || k === 'description' || v == null) continue
|
|
1497
1505
|
head.push(createElement('meta', { key: '__m_' + k, name: k, content: String(v) }))
|
|
1498
1506
|
}
|
|
@@ -2017,12 +2025,31 @@ export async function resolveMetadata(
|
|
|
2017
2025
|
: {}
|
|
2018
2026
|
|
|
2019
2027
|
// Non-title metadata: layout defaults (outer→inner), page overrides.
|
|
2028
|
+
//
|
|
2029
|
+
// other merges per key rather than being replaced, so a page adding one
|
|
2030
|
+
// custom tag keeps the ones its layout set. Assigning it like any other key
|
|
2031
|
+
// would mean a root layout's theme-color disappearing from every page that
|
|
2032
|
+
// happened to declare one of its own.
|
|
2020
2033
|
const merged: Record<string, unknown> = {}
|
|
2034
|
+
const other: Record<string, unknown> = {}
|
|
2035
|
+
|
|
2036
|
+
const take = (from: Record<string, unknown>) => {
|
|
2037
|
+
for (const [k, v] of Object.entries(from)) {
|
|
2038
|
+
if (k === 'title') continue
|
|
2039
|
+
if (k === 'other') Object.assign(other, v as Record<string, unknown>)
|
|
2040
|
+
else merged[k] = v
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2021
2044
|
for (const l of layouts) {
|
|
2022
2045
|
const s = metadataMap[l.component]?.static
|
|
2023
|
-
|
|
2046
|
+
|
|
2047
|
+
if (s) take(s as Record<string, unknown>)
|
|
2024
2048
|
}
|
|
2025
|
-
|
|
2049
|
+
|
|
2050
|
+
take(page)
|
|
2051
|
+
|
|
2052
|
+
if (Object.keys(other).length > 0) merged.other = other
|
|
2026
2053
|
|
|
2027
2054
|
// Title: the page title with the NEAREST layout title.template applied; if the
|
|
2028
2055
|
// page has no title, the nearest layout default/string title.
|