diff --git a/apps/router-e2e/__e2e__/static-rendering/app/metadata-async/[id].tsx b/apps/router-e2e/__e2e__/static-rendering/app/metadata-async/[id].tsx new file mode 100644 index 00000000000000..235f28667df49a --- /dev/null +++ b/apps/router-e2e/__e2e__/static-rendering/app/metadata-async/[id].tsx @@ -0,0 +1,15 @@ +import type { GenerateMetadataFunction, Metadata } from 'expo-router/server'; +import { Text } from 'react-native'; + +export const generateMetadata: GenerateMetadataFunction = async (request, params) => { + const pathname = new URL(request.url).pathname; + + return { + title: `Async Metadata ${params.id}`, + description: `Async metadata for ${pathname}`, + } satisfies Metadata; +}; + +export default function AsyncMetadataPage() { + return Async Metadata; +} diff --git a/apps/router-e2e/__e2e__/static-rendering/app/metadata.tsx b/apps/router-e2e/__e2e__/static-rendering/app/metadata.tsx new file mode 100644 index 00000000000000..22030074c705cd --- /dev/null +++ b/apps/router-e2e/__e2e__/static-rendering/app/metadata.tsx @@ -0,0 +1,31 @@ +import Head from 'expo-router/head'; +import { Text } from 'react-native'; +import type { GenerateMetadataFunction, Metadata } from 'expo-router/server'; + +export const generateMetadata: GenerateMetadataFunction = async () => { + return { + title: 'Metadata Page', + description: 'Page with generateMetadata', + keywords: ['metadata', 'e2e'], + openGraph: { + title: 'Metadata OG Title', + description: 'Metadata OG Description', + }, + twitter: { + card: 'summary', + title: 'Metadata Twitter Title', + }, + } satisfies Metadata; +}; + +export default function MetadataPage() { + return ( + <> + {/* The component is here to check that the app doesn't crash when using it with `generateMetadata()` */} + + + + Metadata + + ); +} diff --git a/packages/@expo/cli/CHANGELOG.md b/packages/@expo/cli/CHANGELOG.md index 7a061838cbcc11..0c50068cd59ce6 100644 --- a/packages/@expo/cli/CHANGELOG.md +++ b/packages/@expo/cli/CHANGELOG.md @@ -15,6 +15,8 @@ - Enable parallel CocoaPods code signing to speed up device builds. ([#43529](https://github.com/expo/expo/pull/43529) by [@evanbacon](https://github.com/evanbacon)) - Prompt before clearing native folders when we detect project as a native module ([#44458](https://github.com/expo/expo/pull/44458) by [@kitten](https://github.com/kitten)) - Rewrite @react-navigation/core to expo-router for library compatibility ([#45039](https://github.com/expo/expo/pull/45039) by [@Ubax](https://github.com/Ubax)) +- Use stream rendering in SSR ([#43963](https://github.com/expo/expo/pull/43963) by [@hassankhan](https://github.com/hassankhan)) +- Add support for metadata in streaming SSR ([#44731](https://github.com/expo/expo/pull/44731) by [@hassankhan](https://github.com/hassankhan)) ### 🐛 Bug fixes diff --git a/packages/@expo/cli/e2e/__tests__/export/__snapshots__/server-rendering.test.ts.snap b/packages/@expo/cli/e2e/__tests__/export/__snapshots__/server-rendering.test.ts.snap index b26f06adfd563b..441214f607ac52 100644 --- a/packages/@expo/cli/e2e/__tests__/export/__snapshots__/server-rendering.test.ts.snap +++ b/packages/@expo/cli/e2e/__tests__/export/__snapshots__/server-rendering.test.ts.snap @@ -1,7 +1,31 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`exports server expo serve requests injects \`generateMetadata()\` result into the initial server HTML 1`] = ` +[ + "", + "", + "", + "", + "", + "", + "", + "Metadata Page", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", +] +`; + exports[`exports server expo serve requests supports usePathname in +html files 1`] = ` -" - - - -

index

Index
-" +@keyframes r-t2lo5v{0%{opacity:1;}100%{opacity:0;}} + + +

index

Index
" +`; + +exports[`exports server workerd requests injects \`generateMetadata()\` result into the initial server HTML 1`] = ` +[ + "", + "", + "", + "", + "", + "", + "", + "Metadata Page", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", +] `; exports[`exports server workerd requests supports usePathname in +html files 1`] = ` -" - - - -

index

Index
-" +@keyframes r-t2lo5v{0%{opacity:1;}100%{opacity:0;}} + + +

index

Index
" `; diff --git a/packages/@expo/cli/e2e/__tests__/export/server-rendering.test.ts b/packages/@expo/cli/e2e/__tests__/export/server-rendering.test.ts index 1b163a39656338..0d601d52564bc8 100644 --- a/packages/@expo/cli/e2e/__tests__/export/server-rendering.test.ts +++ b/packages/@expo/cli/e2e/__tests__/export/server-rendering.test.ts @@ -34,6 +34,11 @@ describe('exports server', () => { )('$name requests', (config) => { const server = setupServer(config); + it('sets the `Transfer-Encoding: chunked` header', async () => { + const res = await server.fetchAsync('/'); + expect(res.headers.get('Transfer-Encoding')).toEqual('chunked'); + }); + it(`can serve up index html`, async () => { const html = getHtml(await server.fetchAsync('/').then((res) => res.text())); expect(html.querySelector('[data-testid="index-text"]')?.textContent).toEqual('Index'); @@ -214,7 +219,25 @@ describe('exports server', () => { it('injects hydration assets into SSR response', async () => { const html = await server.fetchAsync('/').then((res) => res.text()); - expect(html).toMatch(/' + ); + const bootstrapScriptIndex = html.search( + /`; } +/** + * Returns a synchronous inline ``; +} const HELMET_HEAD_KEYS = ['title', 'priority', 'meta', 'link', 'script', 'style']; /** * Extracts head tags and document attributes from a `react-helmet-async` helmet instance. diff --git a/packages/@expo/router-server/build/utils/html.js.map b/packages/@expo/router-server/build/utils/html.js.map index 4bac30af60d653..07fad9b168c740 100644 --- a/packages/@expo/router-server/build/utils/html.js.map +++ b/packages/@expo/router-server/build/utils/html.js.map @@ -1 +1 @@ -{"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/utils/html.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAqBH,wDAEC;AASD,8DAOC;AAQD,oEAEC;AASD,wDAEC;AAUD,sDAsBC;AA1FD,mIAAmI;AACnI,sGAAsG;AAEtG,iEAAiE;AACjE,uGAAuG;AAEvG,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AACrD,MAAM,kBAAkB,GAAgC;IACtD,GAAG,EAAE,SAAS;IACd,GAAG,EAAE,SAAS;IACd,GAAG,EAAE,SAAS;IACd,QAAQ,EAAE,SAAS;IACnB,QAAQ,EAAE,SAAS;CACpB,CAAC;AAEF;;;GAGG;AACH,SAAgB,sBAAsB,CAAC,GAAW;IAChD,OAAO,GAAG,CAAC,OAAO,CAAC,uBAAuB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC;AAC7F,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,yBAAyB,CAAC,KAAe;IACvD,OAAO,KAAK;SACT,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACjB,6BAA6B,IAAI,eAAe;QAChD,gCAAgC,IAAI,IAAI;KACzC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,4BAA4B,CAAC,IAAc;IACzD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,GAAG,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,sBAAsB;IACpC,OAAO,yEAAyE,CAAC;AACnF,CAAC;AAED,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAU,CAAC;AAE3F;;;;;GAKG;AACH,SAAgB,qBAAqB,CAAC,MAAW;IAK/C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;IAClE,CAAC;IAED,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC;QACvC,IAAI,MAAM,EAAE,CAAC;YACX,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,EAAE,IAAI,EAAE;QACvD,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,EAAE,IAAI,EAAE;KACxD,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Copyright © 2023 650 Industries.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// See: https://github.com/urql-graphql/urql/blob/ad0276ae616b2b2f2cd01a527b4217ae35c3fa2d/packages/next-urql/src/htmlescape.ts#L10\n// License: https://github.com/urql-graphql/urql/blob/ad0276ae616b2b2f2cd01a527b4217ae35c3fa2d/LICENSE\n\n// This utility is based on https://github.com/zertosh/htmlescape\n// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE\n\nconst UNSAFE_CHARACTERS_REGEX = /[&><\\u2028\\u2029]/g;\nconst ESCAPED_CHARACTERS: { [match: string]: string } = {\n '&': '\\\\u0026',\n '>': '\\\\u003e',\n '<': '\\\\u003c',\n '\\u2028': '\\\\u2028',\n '\\u2029': '\\\\u2029',\n};\n\n/**\n * Replaces unsafe characters in a string with their escaped equivalents. This is to safely\n * embed data in an HTML context to prevent XSS.\n */\nexport function escapeUnsafeCharacters(str: string): string {\n return str.replace(UNSAFE_CHARACTERS_REGEX, (match) => ESCAPED_CHARACTERS[match] ?? match);\n}\n\n/**\n * Returns a newline-separated `` and `` pair for each\n * CSS href.\n *\n * Used by both `renderStaticContent()` and `serializeHtml()` to inject CSS bundles into the HTML\n * document's `` element.\n */\nexport function createInjectedCssElements(hrefs: string[]): string {\n return hrefs\n .flatMap((href) => [\n ``,\n ``,\n ])\n .join('\\n');\n}\n\n/**\n * Returns newline-separated ``).join('\\n');\n}\n\n/**\n * Returns a module script that sets the `__EXPO_ROUTER_HYDRATE__` global flag, which tells the\n * client-side Expo Router entrypoint to hydrate the server-rendered markup instead of performing\n * a full client render.\n *\n * @see packages/expo/src/launch/registerRootComponent.tsx\n */\nexport function getHydrationFlagScript(): string {\n return ``;\n}\n\nconst HELMET_HEAD_KEYS = ['title', 'priority', 'meta', 'link', 'script', 'style'] as const;\n\n/**\n * Extracts head tags and document attributes from a `react-helmet-async` helmet instance.\n *\n * `` keys are serialized in document order: title, priority, meta, link, script, style.\n * Returns empty strings when `helmet` is `null`/`undefined`.\n */\nexport function serializeHelmetToHtml(helmet: any): {\n headTags: string;\n htmlAttributes: string;\n bodyAttributes: string;\n} {\n if (!helmet) {\n return { headTags: '', htmlAttributes: '', bodyAttributes: '' };\n }\n\n const headParts: string[] = [];\n for (const key of HELMET_HEAD_KEYS) {\n const result = helmet[key]?.toString();\n if (result) {\n headParts.push(result);\n }\n }\n\n return {\n headTags: headParts.join(''),\n htmlAttributes: helmet.htmlAttributes?.toString() ?? '',\n bodyAttributes: helmet.bodyAttributes?.toString() ?? '',\n };\n}\n"]} \ No newline at end of file +{"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/utils/html.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAqBH,wDAEC;AASD,8DAOC;AAQD,oEAEC;AASD,wDAEC;AASD,wDAGC;AAUD,sDAsBC;AAtGD,mIAAmI;AACnI,sGAAsG;AAEtG,iEAAiE;AACjE,uGAAuG;AAEvG,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AACrD,MAAM,kBAAkB,GAAgC;IACtD,GAAG,EAAE,SAAS;IACd,GAAG,EAAE,SAAS;IACd,GAAG,EAAE,SAAS;IACd,QAAQ,EAAE,SAAS;IACnB,QAAQ,EAAE,SAAS;CACpB,CAAC;AAEF;;;GAGG;AACH,SAAgB,sBAAsB,CAAC,GAAW;IAChD,OAAO,GAAG,CAAC,OAAO,CAAC,uBAAuB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC;AAC7F,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,yBAAyB,CAAC,KAAe;IACvD,OAAO,KAAK;SACT,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACjB,6BAA6B,IAAI,eAAe;QAChD,gCAAgC,IAAI,IAAI;KACzC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,4BAA4B,CAAC,IAAc;IACzD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,GAAG,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,sBAAsB;IACpC,OAAO,yEAAyE,CAAC;AACnF,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,sBAAsB,CAAC,IAA6B;IAClE,MAAM,QAAQ,GAAG,sBAAsB,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9D,OAAO,qFAAqF,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC;AACpI,CAAC;AAED,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAU,CAAC;AAE3F;;;;;GAKG;AACH,SAAgB,qBAAqB,CAAC,MAAW;IAK/C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;IAClE,CAAC;IAED,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC;QACvC,IAAI,MAAM,EAAE,CAAC;YACX,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,EAAE,IAAI,EAAE;QACvD,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,EAAE,IAAI,EAAE;KACxD,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Copyright © 2023 650 Industries.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// See: https://github.com/urql-graphql/urql/blob/ad0276ae616b2b2f2cd01a527b4217ae35c3fa2d/packages/next-urql/src/htmlescape.ts#L10\n// License: https://github.com/urql-graphql/urql/blob/ad0276ae616b2b2f2cd01a527b4217ae35c3fa2d/LICENSE\n\n// This utility is based on https://github.com/zertosh/htmlescape\n// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE\n\nconst UNSAFE_CHARACTERS_REGEX = /[&><\\u2028\\u2029]/g;\nconst ESCAPED_CHARACTERS: { [match: string]: string } = {\n '&': '\\\\u0026',\n '>': '\\\\u003e',\n '<': '\\\\u003c',\n '\\u2028': '\\\\u2028',\n '\\u2029': '\\\\u2029',\n};\n\n/**\n * Replaces unsafe characters in a string with their escaped equivalents. This is to safely\n * embed data in an HTML context to prevent XSS.\n */\nexport function escapeUnsafeCharacters(str: string): string {\n return str.replace(UNSAFE_CHARACTERS_REGEX, (match) => ESCAPED_CHARACTERS[match] ?? match);\n}\n\n/**\n * Returns a newline-separated `` and `` pair for each\n * CSS href.\n *\n * Used by both `renderStaticContent()` and `serializeHtml()` to inject CSS bundles into the HTML\n * document's `` element.\n */\nexport function createInjectedCssElements(hrefs: string[]): string {\n return hrefs\n .flatMap((href) => [\n ``,\n ``,\n ])\n .join('\\n');\n}\n\n/**\n * Returns newline-separated ``).join('\\n');\n}\n\n/**\n * Returns a module script that sets the `__EXPO_ROUTER_HYDRATE__` global flag, which tells the\n * client-side Expo Router entrypoint to hydrate the server-rendered markup instead of performing\n * a full client render.\n *\n * @see packages/expo/src/launch/registerRootComponent.tsx\n */\nexport function getHydrationFlagScript(): string {\n return ``;\n}\n\n/**\n * Returns a synchronous inline ``;\n}\n\nconst HELMET_HEAD_KEYS = ['title', 'priority', 'meta', 'link', 'script', 'style'] as const;\n\n/**\n * Extracts head tags and document attributes from a `react-helmet-async` helmet instance.\n *\n * `` keys are serialized in document order: title, priority, meta, link, script, style.\n * Returns empty strings when `helmet` is `null`/`undefined`.\n */\nexport function serializeHelmetToHtml(helmet: any): {\n headTags: string;\n htmlAttributes: string;\n bodyAttributes: string;\n} {\n if (!helmet) {\n return { headTags: '', htmlAttributes: '', bodyAttributes: '' };\n }\n\n const headParts: string[] = [];\n for (const key of HELMET_HEAD_KEYS) {\n const result = helmet[key]?.toString();\n if (result) {\n headParts.push(result);\n }\n }\n\n return {\n headTags: headParts.join(''),\n htmlAttributes: helmet.htmlAttributes?.toString() ?? '',\n bodyAttributes: helmet.bodyAttributes?.toString() ?? '',\n };\n}\n"]} \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/render.d.ts b/packages/@expo/router-server/build/utils/metadata/render.d.ts new file mode 100644 index 00000000000000..a61d4d5003ed85 --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/render.d.ts @@ -0,0 +1,4 @@ +import type { MetadataTag, ResolvedMetadata } from './types'; +export declare function renderMetadataTags(resolved: ResolvedMetadata): MetadataTag[]; +export declare function renderMetadataHtml(tags: MetadataTag[]): string; +//# sourceMappingURL=render.d.ts.map \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/render.d.ts.map b/packages/@expo/router-server/build/utils/metadata/render.d.ts.map new file mode 100644 index 00000000000000..d08fe9c7299b79 --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/render.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/utils/metadata/render.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EAMX,gBAAgB,EAIjB,MAAM,SAAS,CAAC;AAEjB,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,WAAW,EAAE,CAuB5E;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,MAAM,CAE9D"} \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/render.js b/packages/@expo/router-server/build/utils/metadata/render.js new file mode 100644 index 00000000000000..3eaaa26d286780 --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/render.js @@ -0,0 +1,245 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.renderMetadataTags = renderMetadataTags; +exports.renderMetadataHtml = renderMetadataHtml; +const tag_1 = require("./tag"); +function renderMetadataTags(resolved) { + const tags = []; + renderTitle(tags, resolved.title); + renderBasicMetadata(tags, resolved); + renderAuthors(tags, resolved.authors); + renderCreatorMetadata(tags, resolved); + renderRobots(tags, resolved); + renderAlternates(tags, resolved.alternates); + renderOpenGraph(tags, resolved.openGraph); + renderTwitter(tags, resolved.twitter); + renderIcons(tags, resolved.icons); + renderFormatDetection(tags, resolved.formatDetection); + renderVerification(tags, resolved.verification); + renderAppleWebApp(tags, resolved.appleWebApp); + renderItunes(tags, resolved.itunes); + renderFacebook(tags, resolved.facebook); + renderPinterest(tags, resolved.pinterest); + renderAppLinks(tags, resolved.appLinks); + renderLinkRelations(tags, resolved); + renderOther(tags, resolved.other); + return tags; +} +function renderMetadataHtml(tags) { + return tags.map(tag_1.renderMetadataTag).join(''); +} +function renderTitle(tags, title) { + if (!title) + return; + tags.push({ + tagName: 'title', + content: title, + }); +} +function renderBasicMetadata(tags, resolved) { + (0, tag_1.pushName)(tags, 'description', resolved.description); + (0, tag_1.pushName)(tags, 'application-name', resolved.applicationName); + (0, tag_1.pushName)(tags, 'keywords', resolved.keywords); + (0, tag_1.pushName)(tags, 'generator', resolved.generator); + (0, tag_1.pushName)(tags, 'referrer', resolved.referrer); +} +function renderAuthors(tags, authors) { + for (const author of authors) { + (0, tag_1.pushName)(tags, 'author', author.name); + if (author.url) { + (0, tag_1.pushLink)(tags, { rel: 'author', href: author.url }); + } + } +} +function renderCreatorMetadata(tags, resolved) { + (0, tag_1.pushName)(tags, 'creator', resolved.creator); + (0, tag_1.pushName)(tags, 'publisher', resolved.publisher); + (0, tag_1.pushName)(tags, 'category', resolved.category); +} +function renderRobots(tags, resolved) { + (0, tag_1.pushName)(tags, 'robots', resolved.robots); + (0, tag_1.pushName)(tags, 'googlebot', resolved.googleBot); +} +function renderAlternates(tags, alternates) { + if (!alternates) + return; + if (alternates.canonical) { + (0, tag_1.pushLink)(tags, { rel: 'canonical', href: alternates.canonical }); + } + for (const language of alternates.languages) { + (0, tag_1.pushLink)(tags, { + rel: 'alternate', + href: language.href, + hreflang: language.hrefLang, + }); + } + for (const media of alternates.media) { + (0, tag_1.pushLink)(tags, { + rel: 'alternate', + href: media.href, + media: media.media, + }); + } + for (const type of alternates.types) { + (0, tag_1.pushLink)(tags, { + rel: 'alternate', + href: type.href, + type: type.type, + }); + } +} +function renderOpenGraph(tags, openGraph) { + if (!openGraph) + return; + for (const field of openGraph.basic) { + (0, tag_1.pushProperty)(tags, field.property, field.content); + } + for (const image of openGraph.images) { + (0, tag_1.pushProperty)(tags, 'og:image', image.url); + (0, tag_1.pushProperty)(tags, 'og:image:alt', image.alt); + (0, tag_1.pushProperty)(tags, 'og:image:width', image.width); + (0, tag_1.pushProperty)(tags, 'og:image:height', image.height); + (0, tag_1.pushProperty)(tags, 'og:image:type', image.type); + (0, tag_1.pushProperty)(tags, 'og:image:secure_url', image.secureUrl); + } + for (const video of openGraph.videos) { + (0, tag_1.pushProperty)(tags, 'og:video', video.url); + (0, tag_1.pushProperty)(tags, 'og:video:secure_url', video.secureUrl); + (0, tag_1.pushProperty)(tags, 'og:video:type', video.type); + (0, tag_1.pushProperty)(tags, 'og:video:width', video.width); + (0, tag_1.pushProperty)(tags, 'og:video:height', video.height); + } + for (const audio of openGraph.audio) { + (0, tag_1.pushProperty)(tags, 'og:audio', audio.url); + (0, tag_1.pushProperty)(tags, 'og:audio:secure_url', audio.secureUrl); + (0, tag_1.pushProperty)(tags, 'og:audio:type', audio.type); + } + for (const field of openGraph.article) { + (0, tag_1.pushProperty)(tags, field.property, field.content); + } +} +function renderTwitter(tags, twitter) { + if (!twitter) + return; + for (const field of twitter.basic) { + (0, tag_1.pushName)(tags, field.name, field.content); + } + for (const image of twitter.images) { + (0, tag_1.pushName)(tags, 'twitter:image', image.url); + (0, tag_1.pushName)(tags, 'twitter:image:alt', image.alt); + } + for (const player of twitter.players) { + (0, tag_1.pushName)(tags, 'twitter:player', player.url); + (0, tag_1.pushName)(tags, 'twitter:player:width', player.width); + (0, tag_1.pushName)(tags, 'twitter:player:height', player.height); + (0, tag_1.pushName)(tags, 'twitter:player:stream', player.stream); + } + for (const app of twitter.app) { + (0, tag_1.pushName)(tags, `twitter:app:name:${app.platform}`, app.name); + (0, tag_1.pushName)(tags, `twitter:app:id:${app.platform}`, app.id); + (0, tag_1.pushName)(tags, `twitter:app:url:${app.platform}`, app.url); + } +} +function renderIcons(tags, icons) { + if (!icons) + return; + for (const icon of icons.icon) { + (0, tag_1.pushLink)(tags, icon); + } + for (const shortcut of icons.shortcut) { + (0, tag_1.pushLink)(tags, shortcut); + } + for (const apple of icons.apple) { + (0, tag_1.pushLink)(tags, apple); + } + for (const other of icons.other) { + (0, tag_1.pushLink)(tags, other); + } +} +function renderFormatDetection(tags, formatDetection) { + (0, tag_1.pushName)(tags, 'format-detection', formatDetection); +} +function renderVerification(tags, verification) { + if (!verification) + return; + for (const content of verification.google) { + (0, tag_1.pushName)(tags, 'google-site-verification', content); + } + for (const content of verification.yahoo) { + (0, tag_1.pushName)(tags, 'y_key', content); + } + for (const content of verification.yandex) { + (0, tag_1.pushName)(tags, 'yandex-verification', content); + } + for (const entry of verification.other) { + (0, tag_1.pushName)(tags, entry.name, entry.content); + } +} +function renderAppleWebApp(tags, appleWebApp) { + if (!appleWebApp) + return; + (0, tag_1.pushName)(tags, 'mobile-web-app-capable', appleWebApp.capable); + (0, tag_1.pushName)(tags, 'apple-mobile-web-app-title', appleWebApp.title); + (0, tag_1.pushName)(tags, 'apple-mobile-web-app-status-bar-style', appleWebApp.statusBarStyle); + for (const image of appleWebApp.startupImages) { + (0, tag_1.pushLink)(tags, { + rel: 'apple-touch-startup-image', + href: image.href, + media: image.media, + }); + } +} +function renderItunes(tags, itunes) { + (0, tag_1.pushName)(tags, 'apple-itunes-app', itunes); +} +function renderFacebook(tags, facebook) { + if (!facebook) + return; + (0, tag_1.pushProperty)(tags, 'fb:app_id', facebook.appId); + for (const admin of facebook.admins) { + (0, tag_1.pushProperty)(tags, 'fb:admins', admin); + } +} +function renderPinterest(tags, pinterest) { + (0, tag_1.pushName)(tags, 'pinterest-rich-pin', pinterest); +} +function renderAppLinks(tags, appLinks) { + if (!appLinks) + return; + if (appLinks.ios) { + (0, tag_1.pushProperty)(tags, 'al:ios:url', appLinks.ios.url); + (0, tag_1.pushProperty)(tags, 'al:ios:app_store_id', appLinks.ios.appStoreId); + (0, tag_1.pushProperty)(tags, 'al:ios:app_name', appLinks.ios.appName); + } + if (appLinks.android) { + (0, tag_1.pushProperty)(tags, 'al:android:url', appLinks.android.url); + (0, tag_1.pushProperty)(tags, 'al:android:package', appLinks.android.package); + (0, tag_1.pushProperty)(tags, 'al:android:app_name', appLinks.android.appName); + } + if (appLinks.web) { + (0, tag_1.pushProperty)(tags, 'al:web:url', appLinks.web.url); + (0, tag_1.pushProperty)(tags, 'al:web:should_fallback', appLinks.web.shouldFallback); + } +} +function renderLinkRelations(tags, resolved) { + renderLinkArray(tags, 'archives', resolved.archives); + renderLinkArray(tags, 'assets', resolved.assets); + renderLinkArray(tags, 'bookmarks', resolved.bookmarks); + renderManifest(tags, resolved.manifest); +} +function renderLinkArray(tags, rel, urls) { + for (const url of urls) { + (0, tag_1.pushLink)(tags, { rel, href: url }); + } +} +function renderManifest(tags, manifest) { + if (!manifest) + return; + (0, tag_1.pushLink)(tags, { rel: 'manifest', href: manifest }); +} +function renderOther(tags, other) { + for (const entry of other) { + (0, tag_1.pushName)(tags, entry.name, entry.content); + } +} +//# sourceMappingURL=render.js.map \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/render.js.map b/packages/@expo/router-server/build/utils/metadata/render.js.map new file mode 100644 index 00000000000000..1defd641d976e6 --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/render.js.map @@ -0,0 +1 @@ +{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/utils/metadata/render.ts"],"names":[],"mappings":"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","sourcesContent":["import { renderMetadataTag, pushLink, pushName, pushProperty } from './tag';\nimport type {\n MetadataTag,\n ResolvedAlternates,\n ResolvedAppLinks,\n ResolvedAppleWebApp,\n ResolvedFacebook,\n ResolvedIcons,\n ResolvedMetadata,\n ResolvedOpenGraph,\n ResolvedTwitter,\n ResolvedVerification,\n} from './types';\n\nexport function renderMetadataTags(resolved: ResolvedMetadata): MetadataTag[] {\n const tags: MetadataTag[] = [];\n\n renderTitle(tags, resolved.title);\n renderBasicMetadata(tags, resolved);\n renderAuthors(tags, resolved.authors);\n renderCreatorMetadata(tags, resolved);\n renderRobots(tags, resolved);\n renderAlternates(tags, resolved.alternates);\n renderOpenGraph(tags, resolved.openGraph);\n renderTwitter(tags, resolved.twitter);\n renderIcons(tags, resolved.icons);\n renderFormatDetection(tags, resolved.formatDetection);\n renderVerification(tags, resolved.verification);\n renderAppleWebApp(tags, resolved.appleWebApp);\n renderItunes(tags, resolved.itunes);\n renderFacebook(tags, resolved.facebook);\n renderPinterest(tags, resolved.pinterest);\n renderAppLinks(tags, resolved.appLinks);\n renderLinkRelations(tags, resolved);\n renderOther(tags, resolved.other);\n\n return tags;\n}\n\nexport function renderMetadataHtml(tags: MetadataTag[]): string {\n return tags.map(renderMetadataTag).join('');\n}\n\nfunction renderTitle(tags: MetadataTag[], title: string | undefined) {\n if (!title) return;\n\n tags.push({\n tagName: 'title',\n content: title,\n });\n}\n\nfunction renderBasicMetadata(tags: MetadataTag[], resolved: ResolvedMetadata) {\n pushName(tags, 'description', resolved.description);\n pushName(tags, 'application-name', resolved.applicationName);\n pushName(tags, 'keywords', resolved.keywords);\n pushName(tags, 'generator', resolved.generator);\n pushName(tags, 'referrer', resolved.referrer);\n}\n\nfunction renderAuthors(tags: MetadataTag[], authors: ResolvedMetadata['authors']) {\n for (const author of authors) {\n pushName(tags, 'author', author.name);\n if (author.url) {\n pushLink(tags, { rel: 'author', href: author.url });\n }\n }\n}\n\nfunction renderCreatorMetadata(tags: MetadataTag[], resolved: ResolvedMetadata) {\n pushName(tags, 'creator', resolved.creator);\n pushName(tags, 'publisher', resolved.publisher);\n pushName(tags, 'category', resolved.category);\n}\n\nfunction renderRobots(tags: MetadataTag[], resolved: ResolvedMetadata) {\n pushName(tags, 'robots', resolved.robots);\n pushName(tags, 'googlebot', resolved.googleBot);\n}\n\nfunction renderAlternates(tags: MetadataTag[], alternates: ResolvedAlternates | undefined) {\n if (!alternates) return;\n\n if (alternates.canonical) {\n pushLink(tags, { rel: 'canonical', href: alternates.canonical });\n }\n\n for (const language of alternates.languages) {\n pushLink(tags, {\n rel: 'alternate',\n href: language.href,\n hreflang: language.hrefLang,\n });\n }\n\n for (const media of alternates.media) {\n pushLink(tags, {\n rel: 'alternate',\n href: media.href,\n media: media.media,\n });\n }\n\n for (const type of alternates.types) {\n pushLink(tags, {\n rel: 'alternate',\n href: type.href,\n type: type.type,\n });\n }\n}\n\nfunction renderOpenGraph(tags: MetadataTag[], openGraph: ResolvedOpenGraph | undefined) {\n if (!openGraph) return;\n\n for (const field of openGraph.basic) {\n pushProperty(tags, field.property, field.content);\n }\n\n for (const image of openGraph.images) {\n pushProperty(tags, 'og:image', image.url);\n pushProperty(tags, 'og:image:alt', image.alt);\n pushProperty(tags, 'og:image:width', image.width);\n pushProperty(tags, 'og:image:height', image.height);\n pushProperty(tags, 'og:image:type', image.type);\n pushProperty(tags, 'og:image:secure_url', image.secureUrl);\n }\n\n for (const video of openGraph.videos) {\n pushProperty(tags, 'og:video', video.url);\n pushProperty(tags, 'og:video:secure_url', video.secureUrl);\n pushProperty(tags, 'og:video:type', video.type);\n pushProperty(tags, 'og:video:width', video.width);\n pushProperty(tags, 'og:video:height', video.height);\n }\n\n for (const audio of openGraph.audio) {\n pushProperty(tags, 'og:audio', audio.url);\n pushProperty(tags, 'og:audio:secure_url', audio.secureUrl);\n pushProperty(tags, 'og:audio:type', audio.type);\n }\n\n for (const field of openGraph.article) {\n pushProperty(tags, field.property, field.content);\n }\n}\n\nfunction renderTwitter(tags: MetadataTag[], twitter: ResolvedTwitter | undefined) {\n if (!twitter) return;\n\n for (const field of twitter.basic) {\n pushName(tags, field.name, field.content);\n }\n\n for (const image of twitter.images) {\n pushName(tags, 'twitter:image', image.url);\n pushName(tags, 'twitter:image:alt', image.alt);\n }\n\n for (const player of twitter.players) {\n pushName(tags, 'twitter:player', player.url);\n pushName(tags, 'twitter:player:width', player.width);\n pushName(tags, 'twitter:player:height', player.height);\n pushName(tags, 'twitter:player:stream', player.stream);\n }\n\n for (const app of twitter.app) {\n pushName(tags, `twitter:app:name:${app.platform}`, app.name);\n pushName(tags, `twitter:app:id:${app.platform}`, app.id);\n pushName(tags, `twitter:app:url:${app.platform}`, app.url);\n }\n}\n\nfunction renderIcons(tags: MetadataTag[], icons: ResolvedIcons | undefined) {\n if (!icons) return;\n\n for (const icon of icons.icon) {\n pushLink(tags, icon);\n }\n for (const shortcut of icons.shortcut) {\n pushLink(tags, shortcut);\n }\n for (const apple of icons.apple) {\n pushLink(tags, apple);\n }\n for (const other of icons.other) {\n pushLink(tags, other);\n }\n}\n\nfunction renderFormatDetection(tags: MetadataTag[], formatDetection: string | undefined) {\n pushName(tags, 'format-detection', formatDetection);\n}\n\nfunction renderVerification(tags: MetadataTag[], verification: ResolvedVerification | undefined) {\n if (!verification) return;\n\n for (const content of verification.google) {\n pushName(tags, 'google-site-verification', content);\n }\n for (const content of verification.yahoo) {\n pushName(tags, 'y_key', content);\n }\n for (const content of verification.yandex) {\n pushName(tags, 'yandex-verification', content);\n }\n for (const entry of verification.other) {\n pushName(tags, entry.name, entry.content);\n }\n}\n\nfunction renderAppleWebApp(tags: MetadataTag[], appleWebApp: ResolvedAppleWebApp | undefined) {\n if (!appleWebApp) return;\n\n pushName(tags, 'mobile-web-app-capable', appleWebApp.capable);\n pushName(tags, 'apple-mobile-web-app-title', appleWebApp.title);\n pushName(tags, 'apple-mobile-web-app-status-bar-style', appleWebApp.statusBarStyle);\n\n for (const image of appleWebApp.startupImages) {\n pushLink(tags, {\n rel: 'apple-touch-startup-image',\n href: image.href,\n media: image.media,\n });\n }\n}\n\nfunction renderItunes(tags: MetadataTag[], itunes: string | undefined) {\n pushName(tags, 'apple-itunes-app', itunes);\n}\n\nfunction renderFacebook(tags: MetadataTag[], facebook: ResolvedFacebook | undefined) {\n if (!facebook) return;\n\n pushProperty(tags, 'fb:app_id', facebook.appId);\n for (const admin of facebook.admins) {\n pushProperty(tags, 'fb:admins', admin);\n }\n}\n\nfunction renderPinterest(tags: MetadataTag[], pinterest: 'true' | 'false' | undefined) {\n pushName(tags, 'pinterest-rich-pin', pinterest);\n}\n\nfunction renderAppLinks(tags: MetadataTag[], appLinks: ResolvedAppLinks | undefined) {\n if (!appLinks) return;\n\n if (appLinks.ios) {\n pushProperty(tags, 'al:ios:url', appLinks.ios.url);\n pushProperty(tags, 'al:ios:app_store_id', appLinks.ios.appStoreId);\n pushProperty(tags, 'al:ios:app_name', appLinks.ios.appName);\n }\n\n if (appLinks.android) {\n pushProperty(tags, 'al:android:url', appLinks.android.url);\n pushProperty(tags, 'al:android:package', appLinks.android.package);\n pushProperty(tags, 'al:android:app_name', appLinks.android.appName);\n }\n\n if (appLinks.web) {\n pushProperty(tags, 'al:web:url', appLinks.web.url);\n pushProperty(tags, 'al:web:should_fallback', appLinks.web.shouldFallback);\n }\n}\n\nfunction renderLinkRelations(tags: MetadataTag[], resolved: ResolvedMetadata) {\n renderLinkArray(tags, 'archives', resolved.archives);\n renderLinkArray(tags, 'assets', resolved.assets);\n renderLinkArray(tags, 'bookmarks', resolved.bookmarks);\n renderManifest(tags, resolved.manifest);\n}\n\nfunction renderLinkArray(tags: MetadataTag[], rel: string, urls: string[]) {\n for (const url of urls) {\n pushLink(tags, { rel, href: url });\n }\n}\n\nfunction renderManifest(tags: MetadataTag[], manifest: string | undefined) {\n if (!manifest) return;\n pushLink(tags, { rel: 'manifest', href: manifest });\n}\n\nfunction renderOther(tags: MetadataTag[], other: { name: string; content: string }[]) {\n for (const entry of other) {\n pushName(tags, entry.name, entry.content);\n }\n}\n"]} \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/resolve.d.ts b/packages/@expo/router-server/build/utils/metadata/resolve.d.ts new file mode 100644 index 00000000000000..e783a3858a0155 --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/resolve.d.ts @@ -0,0 +1,15 @@ +import type { Metadata, ResolvedAppleWebApp, ResolvedMetadata, ResolvedOpenGraph, ResolvedTwitter, ResolvedVerification } from './types'; +export declare function resolveMetadata(metadata: Metadata): ResolvedMetadata; +export declare function resolveRobots(robots: Metadata['robots']): { + robots?: string; + googleBot?: string; +}; +export declare function resolveOpenGraph(openGraph: Metadata['openGraph']): ResolvedOpenGraph | undefined; +export declare function resolveTwitter(twitter: Metadata['twitter']): ResolvedTwitter | undefined; +export declare function resolveVerification(verification: Metadata['verification']): ResolvedVerification | undefined; +export declare function resolveAppleWebApp(appleWebApp: Metadata['appleWebApp']): ResolvedAppleWebApp | undefined; +export declare function resolveOther(other: Metadata['other']): { + name: string; + content: string; +}[]; +//# sourceMappingURL=resolve.d.ts.map \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/resolve.d.ts.map b/packages/@expo/router-server/build/utils/metadata/resolve.d.ts.map new file mode 100644 index 00000000000000..c27588918d3cce --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/resolve.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../src/utils/metadata/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EAaR,mBAAmB,EAInB,gBAAgB,EAChB,iBAAiB,EAIjB,eAAe,EAIf,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAUjB,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,gBAAgB,CAyBpE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,GAAG;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAYA;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,iBAAiB,GAAG,SAAS,CAqGhG;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG,eAAe,GAAG,SAAS,CA6CxF;AAED,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,QAAQ,CAAC,cAAc,CAAC,GACrC,oBAAoB,GAAG,SAAS,CAmBlC;AAED,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,QAAQ,CAAC,aAAa,CAAC,GACnC,mBAAmB,GAAG,SAAS,CAsBjC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,CAW1F"} \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/resolve.js b/packages/@expo/router-server/build/utils/metadata/resolve.js new file mode 100644 index 00000000000000..b1a6ae184254f4 --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/resolve.js @@ -0,0 +1,465 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.resolveMetadata = resolveMetadata; +exports.resolveRobots = resolveRobots; +exports.resolveOpenGraph = resolveOpenGraph; +exports.resolveTwitter = resolveTwitter; +exports.resolveVerification = resolveVerification; +exports.resolveAppleWebApp = resolveAppleWebApp; +exports.resolveOther = resolveOther; +const NEGATIVE_ROBOT_DIRECTIVES = new Set([ + 'noarchive', + 'nosnippet', + 'noimageindex', + 'nocache', + 'notranslate', +]); +function resolveMetadata(metadata) { + const resolvedRobots = resolveRobots(metadata.robots); + return { + ...resolveBasicMetadata(metadata), + authors: resolveAuthors(metadata.authors), + robots: resolvedRobots.robots, + googleBot: resolvedRobots.googleBot, + alternates: resolveAlternates(metadata.alternates), + openGraph: resolveOpenGraph(metadata.openGraph), + twitter: resolveTwitter(metadata.twitter), + icons: resolveIcons(metadata.icons), + formatDetection: resolveFormatDetection(metadata.formatDetection), + verification: resolveVerification(metadata.verification), + appleWebApp: resolveAppleWebApp(metadata.appleWebApp), + itunes: resolveItunes(metadata.itunes), + facebook: resolveFacebook(metadata.facebook), + pinterest: resolvePinterest(metadata.pinterest), + appLinks: resolveAppLinks(metadata.appLinks), + archives: metadata.archives ?? [], + assets: metadata.assets ?? [], + bookmarks: metadata.bookmarks ?? [], + manifest: metadata.manifest, + other: resolveOther(metadata.other), + }; +} +function resolveRobots(robots) { + if (!robots) { + return {}; + } + return { + robots: serializeRobotsValue(robots), + googleBot: typeof robots === 'object' + ? serializeRobotsValue(robots.googleBot) + : undefined, + }; +} +function resolveOpenGraph(openGraph) { + if (!openGraph) + return undefined; + const basic = []; + const images = []; + const videos = []; + const audio = []; + const article = []; + pushResolvedProperty(basic, 'og:title', openGraph.title); + pushResolvedProperty(basic, 'og:description', openGraph.description); + pushResolvedProperty(basic, 'og:url', openGraph.url); + pushResolvedProperty(basic, 'og:site_name', openGraph.siteName); + pushResolvedProperty(basic, 'og:locale', openGraph.locale); + pushResolvedProperty(basic, 'og:type', openGraph.type); + if (openGraph.determiner) { + pushResolvedProperty(basic, 'og:determiner', openGraph.determiner); + } + pushResolvedProperty(basic, 'og:country_name', openGraph.countryName); + if (openGraph.ttl != null) { + pushResolvedProperty(basic, 'og:ttl', String(openGraph.ttl)); + } + for (const locale of openGraph.alternateLocale ?? []) { + pushResolvedProperty(basic, 'og:locale:alternate', locale); + } + for (const email of openGraph.emails ?? []) { + pushResolvedProperty(basic, 'og:email', email); + } + for (const phoneNumber of openGraph.phoneNumbers ?? []) { + pushResolvedProperty(basic, 'og:phone_number', phoneNumber); + } + for (const faxNumber of openGraph.faxNumbers ?? []) { + pushResolvedProperty(basic, 'og:fax_number', faxNumber); + } + for (const image of normalizeMetadataImages(openGraph.images)) { + if (typeof image === 'string') { + images.push({ url: image }); + continue; + } + images.push({ + url: image.url, + alt: image.alt, + width: image.width != null ? String(image.width) : undefined, + height: image.height != null ? String(image.height) : undefined, + type: image.type, + secureUrl: image.secureUrl, + }); + } + for (const video of normalizeArray(openGraph.videos)) { + if (typeof video === 'string') { + videos.push({ url: video }); + continue; + } + videos.push({ + url: video.url, + secureUrl: video.secureUrl, + type: video.type, + width: video.width != null ? String(video.width) : undefined, + height: video.height != null ? String(video.height) : undefined, + }); + } + for (const audioItem of normalizeArray(openGraph.audio)) { + if (typeof audioItem === 'string') { + audio.push({ url: audioItem }); + continue; + } + audio.push({ + url: audioItem.url, + secureUrl: audioItem.secureUrl, + type: audioItem.type, + }); + } + if (openGraph.type === 'article') { + pushResolvedProperty(article, 'article:published_time', openGraph.publishedTime); + pushResolvedProperty(article, 'article:modified_time', openGraph.modifiedTime); + pushResolvedProperty(article, 'article:expiration_time', openGraph.expirationTime); + pushResolvedProperty(article, 'article:section', openGraph.section); + for (const tag of openGraph.tags ?? []) { + pushResolvedProperty(article, 'article:tag', tag); + } + for (const author of openGraph.authors ?? []) { + pushResolvedProperty(article, 'article:author', author); + } + } + return { + basic, + images, + videos, + audio, + article, + }; +} +function resolveTwitter(twitter) { + if (!twitter) + return undefined; + const basic = []; + const images = []; + const players = []; + const app = []; + pushResolvedName(basic, 'twitter:card', twitter.card); + pushResolvedName(basic, 'twitter:title', twitter.title); + pushResolvedName(basic, 'twitter:description', twitter.description); + pushResolvedName(basic, 'twitter:site', twitter.site); + pushResolvedName(basic, 'twitter:site:id', twitter.siteId); + pushResolvedName(basic, 'twitter:creator', twitter.creator); + pushResolvedName(basic, 'twitter:creator:id', twitter.creatorId); + for (const image of normalizeArray(twitter.images)) { + if (typeof image === 'string') { + images.push({ url: image }); + continue; + } + images.push({ + url: image.url, + alt: image.alt, + }); + } + for (const player of normalizeArray(twitter.players)) { + players.push({ + url: player.url, + width: player.width != null ? String(player.width) : undefined, + height: player.height != null ? String(player.height) : undefined, + stream: player.stream, + }); + } + app.push(...resolveTwitterApp(twitter.app)); + return { + basic, + images, + players, + app, + }; +} +function resolveVerification(verification) { + if (!verification) + return undefined; + const resolved = { + google: normalizeArray(verification.google), + yahoo: normalizeArray(verification.yahoo), + yandex: normalizeArray(verification.yandex), + other: [], + }; + if (verification.other) { + for (const [name, value] of Object.entries(verification.other)) { + for (const content of normalizeArray(value)) { + resolved.other.push({ name, content: String(content) }); + } + } + } + return resolved; +} +function resolveAppleWebApp(appleWebApp) { + if (!appleWebApp) + return undefined; + const startupImages = []; + for (const image of normalizeArray(appleWebApp.startupImage)) { + if (typeof image === 'string') { + startupImages.push({ href: image }); + continue; + } + if (image.url) { + startupImages.push({ href: image.url, media: image.media }); + } + } + return { + capable: 'yes', + title: appleWebApp.title, + statusBarStyle: appleWebApp.statusBarStyle, + startupImages, + }; +} +function resolveOther(other) { + const resolved = []; + if (!other) + return resolved; + for (const [name, value] of Object.entries(other)) { + for (const content of normalizeOtherValue(value)) { + resolved.push({ name, content }); + } + } + return resolved; +} +function resolveBasicMetadata(metadata) { + return { + title: metadata.title, + description: metadata.description, + applicationName: metadata.applicationName, + keywords: Array.isArray(metadata.keywords) ? metadata.keywords.join(', ') : metadata.keywords, + generator: metadata.generator, + referrer: metadata.referrer, + creator: metadata.creator, + publisher: metadata.publisher, + category: metadata.category, + }; +} +function resolveAuthors(authors) { + return normalizeArray(authors).map((author) => ({ + name: author.name, + url: author.url, + })); +} +function resolveAlternates(alternates) { + if (!alternates) + return undefined; + const resolved = { + canonical: alternates.canonical, + languages: [], + media: [], + types: [], + }; + if (alternates.languages) { + for (const [hrefLang, href] of Object.entries(alternates.languages)) { + if (!href) + continue; + resolved.languages.push({ href, hrefLang }); + } + } + if (alternates.media) { + for (const [media, href] of Object.entries(alternates.media)) { + if (!href) + continue; + resolved.media.push({ href, media }); + } + } + if (alternates.types) { + for (const [type, href] of Object.entries(alternates.types)) { + if (!href) + continue; + resolved.types.push({ href, type }); + } + } + return resolved; +} +function resolveTwitterApp(app) { + if (!app) + return []; + const resolved = []; + for (const platform of ['iphone', 'ipad', 'googleplay']) { + resolved.push({ + platform, + name: app.name, + id: app.id?.[platform], + url: app.url?.[platform], + }); + } + return resolved; +} +function resolveIcons(icons) { + if (!icons) + return undefined; + return { + icon: resolveIconDescriptors('icon', icons.icon), + shortcut: resolveIconDescriptors('shortcut icon', icons.shortcut), + apple: resolveIconDescriptors('apple-touch-icon', icons.apple), + other: resolveIconDescriptors('icon', icons.other), + }; +} +function resolveIconDescriptors(rel, descriptors) { + const resolved = []; + for (const descriptor of normalizeArray(descriptors)) { + const normalized = normalizeIconDescriptor(rel, descriptor); + if (normalized) { + resolved.push(normalized); + } + } + return resolved; +} +function normalizeIconDescriptor(rel, descriptor) { + if (typeof descriptor === 'string') { + return { rel, href: descriptor }; + } + if (!descriptor.url) { + return null; + } + return { + rel: descriptor.rel ?? rel, + href: descriptor.url, + type: descriptor.type, + sizes: descriptor.sizes, + media: descriptor.media, + }; +} +function resolveFormatDetection(formatDetection) { + if (!formatDetection) + return undefined; + const parts = []; + if (formatDetection.telephone != null) { + parts.push(`telephone=${formatDetection.telephone ? 'yes' : 'no'}`); + } + if (formatDetection.date != null) { + parts.push(`date=${formatDetection.date ? 'yes' : 'no'}`); + } + if (formatDetection.address != null) { + parts.push(`address=${formatDetection.address ? 'yes' : 'no'}`); + } + if (formatDetection.email != null) { + parts.push(`email=${formatDetection.email ? 'yes' : 'no'}`); + } + if (formatDetection.url != null) { + parts.push(`url=${formatDetection.url ? 'yes' : 'no'}`); + } + if (parts.length === 0) { + return undefined; + } + return parts.join(', '); +} +function resolveItunes(itunes) { + if (!itunes) + return undefined; + const parts = [`app-id=${itunes.appId}`]; + if (itunes.affiliateData) { + parts.push(`affiliate-data=${itunes.affiliateData}`); + } + if (itunes.appArgument) { + parts.push(`app-argument=${itunes.appArgument}`); + } + return parts.join(', '); +} +function resolveFacebook(facebook) { + if (!facebook) + return undefined; + return { + appId: facebook.appId, + admins: normalizeArray(facebook.admins), + }; +} +function resolvePinterest(pinterest) { + if (!pinterest || pinterest.richPin == null) + return undefined; + return pinterest.richPin ? 'true' : 'false'; +} +function resolveAppLinks(appLinks) { + if (!appLinks) + return undefined; + const ios = appLinks.ios && (appLinks.ios.url || appLinks.ios.appStoreId || appLinks.ios.appName) + ? { + url: appLinks.ios.url, + appStoreId: appLinks.ios.appStoreId, + appName: appLinks.ios.appName, + } + : undefined; + const android = appLinks.android && + (appLinks.android.url || appLinks.android.package || appLinks.android.appName) + ? { + url: appLinks.android.url, + package: appLinks.android.package, + appName: appLinks.android.appName, + } + : undefined; + const web = appLinks.web && (appLinks.web.url || appLinks.web.shouldFallback != null) + ? { + url: appLinks.web.url, + shouldFallback: appLinks.web.shouldFallback != null + ? appLinks.web.shouldFallback + ? 'true' + : 'false' + : undefined, + } + : undefined; + return { + ios, + android, + web, + }; +} +function serializeRobotsValue(robots) { + if (!robots) + return undefined; + if (typeof robots === 'string') + return robots; + const values = []; + for (const [key, value] of Object.entries(robots)) { + if (key === 'googleBot' || value == null || typeof value === 'object') + continue; + if (typeof value === 'boolean') { + if (NEGATIVE_ROBOT_DIRECTIVES.has(key)) { + if (value) { + values.push(key); + } + continue; + } + values.push(value ? key : `no${key}`); + continue; + } + values.push(`${key}:${String(value)}`); + } + if (values.length === 0) { + return undefined; + } + return values.join(', '); +} +function normalizeArray(items) { + if (!items) + return []; + return Array.isArray(items) ? items : [items]; +} +function normalizeMetadataImages(images) { + return normalizeArray(images); +} +function normalizeOtherValue(value) { + if (value == null) + return []; + return normalizeArray(value).map((entry) => String(entry)); +} +function pushResolvedName(fields, name, content) { + if (!content) + return; + fields.push({ name, content }); +} +function pushResolvedProperty(fields, property, content) { + if (!content) + return; + fields.push({ property, content }); +} +//# sourceMappingURL=resolve.js.map \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/resolve.js.map b/packages/@expo/router-server/build/utils/metadata/resolve.js.map new file mode 100644 index 00000000000000..8407de886cbfdd --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/resolve.js.map @@ -0,0 +1 @@ +{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../../src/utils/metadata/resolve.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n Metadata,\n MetadataAuthor,\n MetadataAudio,\n MetadataIconDescriptor,\n MetadataImage,\n MetadataTwitterApp,\n MetadataTwitterImage,\n MetadataTwitterPlayer,\n MetadataValue,\n MetadataValueArray,\n MetadataVideo,\n ResolvedAlternates,\n ResolvedAppLinks,\n ResolvedAppleWebApp,\n ResolvedFacebook,\n ResolvedIcon,\n ResolvedIcons,\n ResolvedMetadata,\n ResolvedOpenGraph,\n ResolvedOpenGraphAudio,\n ResolvedOpenGraphImage,\n ResolvedOpenGraphVideo,\n ResolvedTwitter,\n ResolvedTwitterAppEntry,\n ResolvedTwitterImage,\n ResolvedTwitterPlayer,\n ResolvedVerification,\n} from './types';\n\nconst NEGATIVE_ROBOT_DIRECTIVES = new Set([\n 'noarchive',\n 'nosnippet',\n 'noimageindex',\n 'nocache',\n 'notranslate',\n]);\n\nexport function resolveMetadata(metadata: Metadata): ResolvedMetadata {\n const resolvedRobots = resolveRobots(metadata.robots);\n\n return {\n ...resolveBasicMetadata(metadata),\n authors: resolveAuthors(metadata.authors),\n robots: resolvedRobots.robots,\n googleBot: resolvedRobots.googleBot,\n alternates: resolveAlternates(metadata.alternates),\n openGraph: resolveOpenGraph(metadata.openGraph),\n twitter: resolveTwitter(metadata.twitter),\n icons: resolveIcons(metadata.icons),\n formatDetection: resolveFormatDetection(metadata.formatDetection),\n verification: resolveVerification(metadata.verification),\n appleWebApp: resolveAppleWebApp(metadata.appleWebApp),\n itunes: resolveItunes(metadata.itunes),\n facebook: resolveFacebook(metadata.facebook),\n pinterest: resolvePinterest(metadata.pinterest),\n appLinks: resolveAppLinks(metadata.appLinks),\n archives: metadata.archives ?? [],\n assets: metadata.assets ?? [],\n bookmarks: metadata.bookmarks ?? [],\n manifest: metadata.manifest,\n other: resolveOther(metadata.other),\n };\n}\n\nexport function resolveRobots(robots: Metadata['robots']): {\n robots?: string;\n googleBot?: string;\n} {\n if (!robots) {\n return {};\n }\n\n return {\n robots: serializeRobotsValue(robots),\n googleBot:\n typeof robots === 'object'\n ? serializeRobotsValue(robots.googleBot as string | Record | undefined)\n : undefined,\n };\n}\n\nexport function resolveOpenGraph(openGraph: Metadata['openGraph']): ResolvedOpenGraph | undefined {\n if (!openGraph) return undefined;\n\n const basic: { property: string; content: string }[] = [];\n const images: ResolvedOpenGraphImage[] = [];\n const videos: ResolvedOpenGraphVideo[] = [];\n const audio: ResolvedOpenGraphAudio[] = [];\n const article: { property: string; content: string }[] = [];\n\n pushResolvedProperty(basic, 'og:title', openGraph.title);\n pushResolvedProperty(basic, 'og:description', openGraph.description);\n pushResolvedProperty(basic, 'og:url', openGraph.url);\n pushResolvedProperty(basic, 'og:site_name', openGraph.siteName);\n pushResolvedProperty(basic, 'og:locale', openGraph.locale);\n pushResolvedProperty(basic, 'og:type', openGraph.type);\n if (openGraph.determiner) {\n pushResolvedProperty(basic, 'og:determiner', openGraph.determiner);\n }\n pushResolvedProperty(basic, 'og:country_name', openGraph.countryName);\n if (openGraph.ttl != null) {\n pushResolvedProperty(basic, 'og:ttl', String(openGraph.ttl));\n }\n\n for (const locale of openGraph.alternateLocale ?? []) {\n pushResolvedProperty(basic, 'og:locale:alternate', locale);\n }\n for (const email of openGraph.emails ?? []) {\n pushResolvedProperty(basic, 'og:email', email);\n }\n for (const phoneNumber of openGraph.phoneNumbers ?? []) {\n pushResolvedProperty(basic, 'og:phone_number', phoneNumber);\n }\n for (const faxNumber of openGraph.faxNumbers ?? []) {\n pushResolvedProperty(basic, 'og:fax_number', faxNumber);\n }\n\n for (const image of normalizeMetadataImages(openGraph.images)) {\n if (typeof image === 'string') {\n images.push({ url: image });\n continue;\n }\n\n images.push({\n url: image.url,\n alt: image.alt,\n width: image.width != null ? String(image.width) : undefined,\n height: image.height != null ? String(image.height) : undefined,\n type: image.type,\n secureUrl: image.secureUrl,\n });\n }\n\n for (const video of normalizeArray(openGraph.videos)) {\n if (typeof video === 'string') {\n videos.push({ url: video });\n continue;\n }\n\n videos.push({\n url: video.url,\n secureUrl: video.secureUrl,\n type: video.type,\n width: video.width != null ? String(video.width) : undefined,\n height: video.height != null ? String(video.height) : undefined,\n });\n }\n\n for (const audioItem of normalizeArray(openGraph.audio)) {\n if (typeof audioItem === 'string') {\n audio.push({ url: audioItem });\n continue;\n }\n\n audio.push({\n url: audioItem.url,\n secureUrl: audioItem.secureUrl,\n type: audioItem.type,\n });\n }\n\n if (openGraph.type === 'article') {\n pushResolvedProperty(article, 'article:published_time', openGraph.publishedTime);\n pushResolvedProperty(article, 'article:modified_time', openGraph.modifiedTime);\n pushResolvedProperty(article, 'article:expiration_time', openGraph.expirationTime);\n pushResolvedProperty(article, 'article:section', openGraph.section);\n\n for (const tag of openGraph.tags ?? []) {\n pushResolvedProperty(article, 'article:tag', tag);\n }\n for (const author of openGraph.authors ?? []) {\n pushResolvedProperty(article, 'article:author', author);\n }\n }\n\n return {\n basic,\n images,\n videos,\n audio,\n article,\n };\n}\n\nexport function resolveTwitter(twitter: Metadata['twitter']): ResolvedTwitter | undefined {\n if (!twitter) return undefined;\n\n const basic: { name: string; content: string }[] = [];\n const images: ResolvedTwitterImage[] = [];\n const players: ResolvedTwitterPlayer[] = [];\n const app: ResolvedTwitterAppEntry[] = [];\n\n pushResolvedName(basic, 'twitter:card', twitter.card);\n pushResolvedName(basic, 'twitter:title', twitter.title);\n pushResolvedName(basic, 'twitter:description', twitter.description);\n pushResolvedName(basic, 'twitter:site', twitter.site);\n pushResolvedName(basic, 'twitter:site:id', twitter.siteId);\n pushResolvedName(basic, 'twitter:creator', twitter.creator);\n pushResolvedName(basic, 'twitter:creator:id', twitter.creatorId);\n\n for (const image of normalizeArray(twitter.images)) {\n if (typeof image === 'string') {\n images.push({ url: image });\n continue;\n }\n\n images.push({\n url: image.url,\n alt: image.alt,\n });\n }\n\n for (const player of normalizeArray(twitter.players)) {\n players.push({\n url: player.url,\n width: player.width != null ? String(player.width) : undefined,\n height: player.height != null ? String(player.height) : undefined,\n stream: player.stream,\n });\n }\n\n app.push(...resolveTwitterApp(twitter.app));\n\n return {\n basic,\n images,\n players,\n app,\n };\n}\n\nexport function resolveVerification(\n verification: Metadata['verification']\n): ResolvedVerification | undefined {\n if (!verification) return undefined;\n\n const resolved: ResolvedVerification = {\n google: normalizeArray(verification.google),\n yahoo: normalizeArray(verification.yahoo),\n yandex: normalizeArray(verification.yandex),\n other: [],\n };\n\n if (verification.other) {\n for (const [name, value] of Object.entries(verification.other)) {\n for (const content of normalizeArray(value)) {\n resolved.other.push({ name, content: String(content) });\n }\n }\n }\n\n return resolved;\n}\n\nexport function resolveAppleWebApp(\n appleWebApp: Metadata['appleWebApp']\n): ResolvedAppleWebApp | undefined {\n if (!appleWebApp) return undefined;\n\n const startupImages: { href: string; media?: string }[] = [];\n\n for (const image of normalizeArray(appleWebApp.startupImage)) {\n if (typeof image === 'string') {\n startupImages.push({ href: image });\n continue;\n }\n\n if (image.url) {\n startupImages.push({ href: image.url, media: image.media });\n }\n }\n\n return {\n capable: 'yes',\n title: appleWebApp.title,\n statusBarStyle: appleWebApp.statusBarStyle,\n startupImages,\n };\n}\n\nexport function resolveOther(other: Metadata['other']): { name: string; content: string }[] {\n const resolved: { name: string; content: string }[] = [];\n if (!other) return resolved;\n\n for (const [name, value] of Object.entries(other)) {\n for (const content of normalizeOtherValue(value)) {\n resolved.push({ name, content });\n }\n }\n\n return resolved;\n}\n\nfunction resolveBasicMetadata(\n metadata: Metadata\n): Pick<\n ResolvedMetadata,\n | 'title'\n | 'description'\n | 'applicationName'\n | 'keywords'\n | 'generator'\n | 'referrer'\n | 'creator'\n | 'publisher'\n | 'category'\n> {\n return {\n title: metadata.title,\n description: metadata.description,\n applicationName: metadata.applicationName,\n keywords: Array.isArray(metadata.keywords) ? metadata.keywords.join(', ') : metadata.keywords,\n generator: metadata.generator,\n referrer: metadata.referrer,\n creator: metadata.creator,\n publisher: metadata.publisher,\n category: metadata.category,\n };\n}\n\nfunction resolveAuthors(authors: Metadata['authors']): { name?: string; url?: string }[] {\n return normalizeArray(authors).map((author) => ({\n name: author.name,\n url: author.url,\n }));\n}\n\nfunction resolveAlternates(alternates: Metadata['alternates']): ResolvedAlternates | undefined {\n if (!alternates) return undefined;\n\n const resolved: ResolvedAlternates = {\n canonical: alternates.canonical,\n languages: [],\n media: [],\n types: [],\n };\n\n if (alternates.languages) {\n for (const [hrefLang, href] of Object.entries(alternates.languages)) {\n if (!href) continue;\n resolved.languages.push({ href, hrefLang });\n }\n }\n\n if (alternates.media) {\n for (const [media, href] of Object.entries(alternates.media)) {\n if (!href) continue;\n resolved.media.push({ href, media });\n }\n }\n\n if (alternates.types) {\n for (const [type, href] of Object.entries(alternates.types)) {\n if (!href) continue;\n resolved.types.push({ href, type });\n }\n }\n\n return resolved;\n}\n\nfunction resolveTwitterApp(app: MetadataTwitterApp | undefined): ResolvedTwitterAppEntry[] {\n if (!app) return [];\n\n const resolved: ResolvedTwitterAppEntry[] = [];\n\n for (const platform of ['iphone', 'ipad', 'googleplay'] as const) {\n resolved.push({\n platform,\n name: app.name,\n id: app.id?.[platform],\n url: app.url?.[platform],\n });\n }\n\n return resolved;\n}\n\nfunction resolveIcons(icons: Metadata['icons']): ResolvedIcons | undefined {\n if (!icons) return undefined;\n\n return {\n icon: resolveIconDescriptors('icon', icons.icon),\n shortcut: resolveIconDescriptors('shortcut icon', icons.shortcut),\n apple: resolveIconDescriptors('apple-touch-icon', icons.apple),\n other: resolveIconDescriptors('icon', icons.other),\n };\n}\n\nfunction resolveIconDescriptors(\n rel: string,\n descriptors: Metadata['icons'] extends infer T\n ? T extends null | undefined\n ? never\n : T[keyof T]\n : never\n): ResolvedIcon[] {\n const resolved: ResolvedIcon[] = [];\n\n for (const descriptor of normalizeArray(descriptors)) {\n const normalized = normalizeIconDescriptor(rel, descriptor);\n if (normalized) {\n resolved.push(normalized);\n }\n }\n\n return resolved;\n}\n\nfunction normalizeIconDescriptor(\n rel: string,\n descriptor: MetadataIconDescriptor\n): ResolvedIcon | null {\n if (typeof descriptor === 'string') {\n return { rel, href: descriptor };\n }\n\n if (!descriptor.url) {\n return null;\n }\n\n return {\n rel: descriptor.rel ?? rel,\n href: descriptor.url,\n type: descriptor.type,\n sizes: descriptor.sizes,\n media: descriptor.media,\n };\n}\n\nfunction resolveFormatDetection(formatDetection: Metadata['formatDetection']): string | undefined {\n if (!formatDetection) return undefined;\n\n const parts: string[] = [];\n if (formatDetection.telephone != null) {\n parts.push(`telephone=${formatDetection.telephone ? 'yes' : 'no'}`);\n }\n if (formatDetection.date != null) {\n parts.push(`date=${formatDetection.date ? 'yes' : 'no'}`);\n }\n if (formatDetection.address != null) {\n parts.push(`address=${formatDetection.address ? 'yes' : 'no'}`);\n }\n if (formatDetection.email != null) {\n parts.push(`email=${formatDetection.email ? 'yes' : 'no'}`);\n }\n if (formatDetection.url != null) {\n parts.push(`url=${formatDetection.url ? 'yes' : 'no'}`);\n }\n\n if (parts.length === 0) {\n return undefined;\n }\n\n return parts.join(', ');\n}\n\nfunction resolveItunes(itunes: Metadata['itunes']): string | undefined {\n if (!itunes) return undefined;\n\n const parts = [`app-id=${itunes.appId}`];\n if (itunes.affiliateData) {\n parts.push(`affiliate-data=${itunes.affiliateData}`);\n }\n if (itunes.appArgument) {\n parts.push(`app-argument=${itunes.appArgument}`);\n }\n\n return parts.join(', ');\n}\n\nfunction resolveFacebook(facebook: Metadata['facebook']): ResolvedFacebook | undefined {\n if (!facebook) return undefined;\n\n return {\n appId: facebook.appId,\n admins: normalizeArray(facebook.admins),\n };\n}\n\nfunction resolvePinterest(pinterest: Metadata['pinterest']): 'true' | 'false' | undefined {\n if (!pinterest || pinterest.richPin == null) return undefined;\n return pinterest.richPin ? 'true' : 'false';\n}\n\nfunction resolveAppLinks(appLinks: Metadata['appLinks']): ResolvedAppLinks | undefined {\n if (!appLinks) return undefined;\n\n const ios =\n appLinks.ios && (appLinks.ios.url || appLinks.ios.appStoreId || appLinks.ios.appName)\n ? {\n url: appLinks.ios.url,\n appStoreId: appLinks.ios.appStoreId,\n appName: appLinks.ios.appName,\n }\n : undefined;\n\n const android =\n appLinks.android &&\n (appLinks.android.url || appLinks.android.package || appLinks.android.appName)\n ? {\n url: appLinks.android.url,\n package: appLinks.android.package,\n appName: appLinks.android.appName,\n }\n : undefined;\n\n const web =\n appLinks.web && (appLinks.web.url || appLinks.web.shouldFallback != null)\n ? {\n url: appLinks.web.url,\n shouldFallback:\n appLinks.web.shouldFallback != null\n ? appLinks.web.shouldFallback\n ? ('true' as const)\n : ('false' as const)\n : undefined,\n }\n : undefined;\n\n return {\n ios,\n android,\n web,\n };\n}\n\nfunction serializeRobotsValue(\n robots: string | Record | null | undefined\n): string | undefined {\n if (!robots) return undefined;\n if (typeof robots === 'string') return robots;\n\n const values: string[] = [];\n\n for (const [key, value] of Object.entries(robots)) {\n if (key === 'googleBot' || value == null || typeof value === 'object') continue;\n if (typeof value === 'boolean') {\n if (NEGATIVE_ROBOT_DIRECTIVES.has(key)) {\n if (value) {\n values.push(key);\n }\n continue;\n }\n\n values.push(value ? key : `no${key}`);\n continue;\n }\n\n values.push(`${key}:${String(value)}`);\n }\n\n if (values.length === 0) {\n return undefined;\n }\n\n return values.join(', ');\n}\n\nfunction normalizeArray(items: T | T[] | undefined): T[] {\n if (!items) return [];\n return Array.isArray(items) ? items : [items];\n}\n\nfunction normalizeMetadataImages(\n images: MetadataImage | MetadataImage[] | undefined\n): MetadataImage[] {\n return normalizeArray(images);\n}\n\nfunction normalizeOtherValue(\n value: MetadataValue | MetadataValueArray | null | undefined\n): string[] {\n if (value == null) return [];\n return normalizeArray(value).map((entry) => String(entry));\n}\n\nfunction pushResolvedName(\n fields: { name: string; content: string }[],\n name: string,\n content: string | undefined\n) {\n if (!content) return;\n fields.push({ name, content });\n}\n\nfunction pushResolvedProperty(\n fields: { property: string; content: string }[],\n property: string,\n content: string | undefined\n) {\n if (!content) return;\n fields.push({ property, content });\n}\n"]} \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/serialize.d.ts b/packages/@expo/router-server/build/utils/metadata/serialize.d.ts new file mode 100644 index 00000000000000..de1e66816e727b --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/serialize.d.ts @@ -0,0 +1,4 @@ +import type { Metadata } from 'expo-server'; +export declare function serializeMetadataToTags(metadata: Metadata): import("./types").MetadataTag[]; +export declare function serializeMetadataToHtml(metadata: Metadata): string; +//# sourceMappingURL=serialize.d.ts.map \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/serialize.d.ts.map b/packages/@expo/router-server/build/utils/metadata/serialize.d.ts.map new file mode 100644 index 00000000000000..2ccf87aa031ccd --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/serialize.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../../src/utils/metadata/serialize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAK5C,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,mCAEzD;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAElE"} \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/serialize.js b/packages/@expo/router-server/build/utils/metadata/serialize.js new file mode 100644 index 00000000000000..e703ece0927ecd --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/serialize.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.serializeMetadataToTags = serializeMetadataToTags; +exports.serializeMetadataToHtml = serializeMetadataToHtml; +const render_1 = require("./render"); +const resolve_1 = require("./resolve"); +function serializeMetadataToTags(metadata) { + return (0, render_1.renderMetadataTags)((0, resolve_1.resolveMetadata)(metadata)); +} +function serializeMetadataToHtml(metadata) { + return (0, render_1.renderMetadataHtml)((0, render_1.renderMetadataTags)((0, resolve_1.resolveMetadata)(metadata))); +} +//# sourceMappingURL=serialize.js.map \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/serialize.js.map b/packages/@expo/router-server/build/utils/metadata/serialize.js.map new file mode 100644 index 00000000000000..079880153115d0 --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/serialize.js.map @@ -0,0 +1 @@ +{"version":3,"file":"serialize.js","sourceRoot":"","sources":["../../../src/utils/metadata/serialize.ts"],"names":[],"mappings":";;AAKA,0DAEC;AAED,0DAEC;AATD,qCAAkE;AAClE,uCAA4C;AAE5C,SAAgB,uBAAuB,CAAC,QAAkB;IACxD,OAAO,IAAA,2BAAkB,EAAC,IAAA,yBAAe,EAAC,QAAQ,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,SAAgB,uBAAuB,CAAC,QAAkB;IACxD,OAAO,IAAA,2BAAkB,EAAC,IAAA,2BAAkB,EAAC,IAAA,yBAAe,EAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC3E,CAAC","sourcesContent":["import type { Metadata } from 'expo-server';\n\nimport { renderMetadataHtml, renderMetadataTags } from './render';\nimport { resolveMetadata } from './resolve';\n\nexport function serializeMetadataToTags(metadata: Metadata) {\n return renderMetadataTags(resolveMetadata(metadata));\n}\n\nexport function serializeMetadataToHtml(metadata: Metadata): string {\n return renderMetadataHtml(renderMetadataTags(resolveMetadata(metadata)));\n}\n"]} \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/tag.d.ts b/packages/@expo/router-server/build/utils/metadata/tag.d.ts new file mode 100644 index 00000000000000..e441244ebb4319 --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/tag.d.ts @@ -0,0 +1,6 @@ +import type { MetadataTag } from './types'; +export declare function renderMetadataTag(tag: MetadataTag): string; +export declare function pushName(tags: MetadataTag[], key: string, value: string | undefined): void; +export declare function pushProperty(tags: MetadataTag[], key: string, value: string | undefined): void; +export declare function pushLink(tags: MetadataTag[], attributes: Record): void; +//# sourceMappingURL=tag.d.ts.map \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/tag.d.ts.map b/packages/@expo/router-server/build/utils/metadata/tag.d.ts.map new file mode 100644 index 00000000000000..832196ac3dc621 --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/tag.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../../src/utils/metadata/tag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAc1D;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,QAEnF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,QAEvF;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,QAa3F"} \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/tag.js b/packages/@expo/router-server/build/utils/metadata/tag.js new file mode 100644 index 00000000000000..708d89506fce3f --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/tag.js @@ -0,0 +1,57 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.renderMetadataTag = renderMetadataTag; +exports.pushName = pushName; +exports.pushProperty = pushProperty; +exports.pushLink = pushLink; +function renderMetadataTag(tag) { + const attributes = tag.attributes + ? ' ' + + Object.entries(tag.attributes) + .filter(([, value]) => value != null && value !== '') + .map(([key, value]) => `${key}="${escapeHtmlAttributeValue(value)}"`) + .join(' ') + : ''; + if (tag.tagName === 'title') { + return `${escapeHtmlTextNode(tag.content ?? '')}`; + } + return `<${tag.tagName}${attributes}>`; +} +function pushName(tags, key, value) { + pushMetaContent(tags, 'name', key, value); +} +function pushProperty(tags, key, value) { + pushMetaContent(tags, 'property', key, value); +} +function pushLink(tags, attributes) { + const normalizedAttributes = Object.fromEntries(Object.entries(attributes).filter(([, value]) => value != null && value !== '')); + if (Object.keys(normalizedAttributes).length === 0) { + return; + } + tags.push({ + tagName: 'link', + attributes: normalizedAttributes, + }); +} +function escapeHtmlAttributeValue(value) { + return value + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); +} +function escapeHtmlTextNode(value) { + return value.replace(/&/g, '&').replace(//g, '>'); +} +function pushMetaContent(tags, name, key, value) { + if (!value) + return; + tags.push({ + tagName: 'meta', + attributes: { + [name]: key, + content: value, + }, + }); +} +//# sourceMappingURL=tag.js.map \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/tag.js.map b/packages/@expo/router-server/build/utils/metadata/tag.js.map new file mode 100644 index 00000000000000..2c4a9e98594cdb --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/tag.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tag.js","sourceRoot":"","sources":["../../../src/utils/metadata/tag.ts"],"names":[],"mappings":";;AAEA,8CAcC;AAED,4BAEC;AAED,oCAEC;AAED,4BAaC;AArCD,SAAgB,iBAAiB,CAAC,GAAgB;IAChD,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU;QAC/B,CAAC,CAAC,GAAG;YACH,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;iBAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;iBACpD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,wBAAwB,CAAC,KAAK,CAAC,GAAG,CAAC;iBACpE,IAAI,CAAC,GAAG,CAAC;QACd,CAAC,CAAC,EAAE,CAAC;IAEP,IAAI,GAAG,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;QAC5B,OAAO,UAAU,kBAAkB,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,UAAU,CAAC;IACnE,CAAC;IAED,OAAO,IAAI,GAAG,CAAC,OAAO,GAAG,UAAU,GAAG,CAAC;AACzC,CAAC;AAED,SAAgB,QAAQ,CAAC,IAAmB,EAAE,GAAW,EAAE,KAAyB;IAClF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAC5C,CAAC;AAED,SAAgB,YAAY,CAAC,IAAmB,EAAE,GAAW,EAAE,KAAyB;IACtF,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAChD,CAAC;AAED,SAAgB,QAAQ,CAAC,IAAmB,EAAE,UAA8C;IAC1F,MAAM,oBAAoB,GAAG,MAAM,CAAC,WAAW,CAC7C,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC,CACtD,CAAC;IAE5B,IAAI,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnD,OAAO;IACT,CAAC;IAED,IAAI,CAAC,IAAI,CAAC;QACR,OAAO,EAAE,MAAM;QACf,UAAU,EAAE,oBAAoB;KACjC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAa;IAC7C,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAa;IACvC,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAClF,CAAC;AAED,SAAS,eAAe,CACtB,IAAmB,EACnB,IAAyB,EACzB,GAAW,EACX,KAAyB;IAEzB,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,IAAI,CAAC,IAAI,CAAC;QACR,OAAO,EAAE,MAAM;QACf,UAAU,EAAE;YACV,CAAC,IAAI,CAAC,EAAE,GAAG;YACX,OAAO,EAAE,KAAK;SACf;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { MetadataTag } from './types';\n\nexport function renderMetadataTag(tag: MetadataTag): string {\n const attributes = tag.attributes\n ? ' ' +\n Object.entries(tag.attributes)\n .filter(([, value]) => value != null && value !== '')\n .map(([key, value]) => `${key}=\"${escapeHtmlAttributeValue(value)}\"`)\n .join(' ')\n : '';\n\n if (tag.tagName === 'title') {\n return `${escapeHtmlTextNode(tag.content ?? '')}`;\n }\n\n return `<${tag.tagName}${attributes}>`;\n}\n\nexport function pushName(tags: MetadataTag[], key: string, value: string | undefined) {\n pushMetaContent(tags, 'name', key, value);\n}\n\nexport function pushProperty(tags: MetadataTag[], key: string, value: string | undefined) {\n pushMetaContent(tags, 'property', key, value);\n}\n\nexport function pushLink(tags: MetadataTag[], attributes: Record) {\n const normalizedAttributes = Object.fromEntries(\n Object.entries(attributes).filter(([, value]) => value != null && value !== '')\n ) as Record;\n\n if (Object.keys(normalizedAttributes).length === 0) {\n return;\n }\n\n tags.push({\n tagName: 'link',\n attributes: normalizedAttributes,\n });\n}\n\nfunction escapeHtmlAttributeValue(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(//g, '>');\n}\n\nfunction escapeHtmlTextNode(value: string): string {\n return value.replace(/&/g, '&').replace(//g, '>');\n}\n\nfunction pushMetaContent(\n tags: MetadataTag[],\n name: 'name' | 'property',\n key: string,\n value: string | undefined\n) {\n if (!value) return;\n tags.push({\n tagName: 'meta',\n attributes: {\n [name]: key,\n content: value,\n },\n });\n}\n"]} \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/types.d.ts b/packages/@expo/router-server/build/utils/metadata/types.d.ts new file mode 100644 index 00000000000000..a3d53d0770c690 --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/types.d.ts @@ -0,0 +1,176 @@ +import type { Metadata, MetadataIconDescriptor, MetadataImage, MetadataValue, MetadataValueArray } from 'expo-server'; +export type MetadataTag = { + tagName: 'title' | 'meta' | 'link'; + attributes?: Record; + content?: string; +}; +type ArrayElement = T extends readonly (infer U)[] ? U : T; +export type MetadataAuthor = ArrayElement>; +export type MetadataAudio = ArrayElement['audio']>>; +export type MetadataVideo = ArrayElement['videos']>>; +export type MetadataTwitterImage = ArrayElement['images']>>; +export type MetadataTwitterPlayer = ArrayElement['players']>>; +export type MetadataTwitterApp = NonNullable['app']>; +export type ResolvedAuthor = { + name?: string; + url?: string; +}; +export type ResolvedAlternates = { + canonical?: string; + languages: { + href: string; + hrefLang: string; + }[]; + media: { + href: string; + media: string; + }[]; + types: { + href: string; + type: string; + }[]; +}; +export type ResolvedOpenGraphImage = { + url: string; + alt?: string; + width?: string; + height?: string; + type?: string; + secureUrl?: string; +}; +export type ResolvedOpenGraphVideo = { + url: string; + secureUrl?: string; + type?: string; + width?: string; + height?: string; +}; +export type ResolvedOpenGraphAudio = { + url: string; + secureUrl?: string; + type?: string; +}; +export type ResolvedOpenGraph = { + basic: { + property: string; + content: string; + }[]; + images: ResolvedOpenGraphImage[]; + videos: ResolvedOpenGraphVideo[]; + audio: ResolvedOpenGraphAudio[]; + article: { + property: string; + content: string; + }[]; +}; +export type ResolvedTwitterImage = { + url: string; + alt?: string; +}; +export type ResolvedTwitterPlayer = { + url: string; + width?: string; + height?: string; + stream?: string; +}; +export type ResolvedTwitterAppEntry = { + platform: 'iphone' | 'ipad' | 'googleplay'; + name?: string; + id?: string; + url?: string; +}; +export type ResolvedTwitter = { + basic: { + name: string; + content: string; + }[]; + images: ResolvedTwitterImage[]; + players: ResolvedTwitterPlayer[]; + app: ResolvedTwitterAppEntry[]; +}; +export type ResolvedIcon = { + rel: string; + href: string; + type?: string; + sizes?: string; + media?: string; +}; +export type ResolvedIcons = { + icon: ResolvedIcon[]; + shortcut: ResolvedIcon[]; + apple: ResolvedIcon[]; + other: ResolvedIcon[]; +}; +export type ResolvedVerification = { + google: string[]; + yahoo: string[]; + yandex: string[]; + other: { + name: string; + content: string; + }[]; +}; +export type ResolvedAppleWebApp = { + capable: 'yes'; + title?: string; + statusBarStyle?: string; + startupImages: { + href: string; + media?: string; + }[]; +}; +export type ResolvedFacebook = { + appId?: string; + admins: string[]; +}; +export type ResolvedAppLinks = { + ios?: { + url?: string; + appStoreId?: string; + appName?: string; + }; + android?: { + url?: string; + package?: string; + appName?: string; + }; + web?: { + url?: string; + shouldFallback?: 'true' | 'false'; + }; +}; +export type ResolvedMetadata = { + title?: string; + description?: string; + applicationName?: string; + keywords?: string; + generator?: string; + referrer?: string; + authors: ResolvedAuthor[]; + creator?: string; + publisher?: string; + category?: string; + robots?: string; + googleBot?: string; + alternates?: ResolvedAlternates; + openGraph?: ResolvedOpenGraph; + twitter?: ResolvedTwitter; + icons?: ResolvedIcons; + formatDetection?: string; + verification?: ResolvedVerification; + appleWebApp?: ResolvedAppleWebApp; + itunes?: string; + facebook?: ResolvedFacebook; + pinterest?: 'true' | 'false'; + appLinks?: ResolvedAppLinks; + archives: string[]; + assets: string[]; + bookmarks: string[]; + manifest?: string; + other: { + name: string; + content: string; + }[]; +}; +export type { Metadata, MetadataIconDescriptor, MetadataImage, MetadataValue, MetadataValueArray }; +//# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/types.d.ts.map b/packages/@expo/router-server/build/utils/metadata/types.d.ts.map new file mode 100644 index 00000000000000..aab217c4bcad4d --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/metadata/types.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/types.js b/packages/@expo/router-server/build/utils/metadata/types.js new file mode 100644 index 00000000000000..11e638d1ee44ae --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/types.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/metadata/types.js.map b/packages/@expo/router-server/build/utils/metadata/types.js.map new file mode 100644 index 00000000000000..aa5fd3503337b0 --- /dev/null +++ b/packages/@expo/router-server/build/utils/metadata/types.js.map @@ -0,0 +1 @@ +{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/utils/metadata/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n Metadata,\n MetadataIconDescriptor,\n MetadataImage,\n MetadataValue,\n MetadataValueArray,\n} from 'expo-server';\n\nexport type MetadataTag = {\n tagName: 'title' | 'meta' | 'link';\n attributes?: Record;\n content?: string;\n};\n\ntype ArrayElement = T extends readonly (infer U)[] ? U : T;\n\nexport type MetadataAuthor = ArrayElement>;\nexport type MetadataAudio = ArrayElement['audio']>>;\nexport type MetadataVideo = ArrayElement['videos']>>;\nexport type MetadataTwitterImage = ArrayElement<\n NonNullable['images']>\n>;\nexport type MetadataTwitterPlayer = ArrayElement<\n NonNullable['players']>\n>;\nexport type MetadataTwitterApp = NonNullable['app']>;\nexport type ResolvedAuthor = { name?: string; url?: string };\n\nexport type ResolvedAlternates = {\n canonical?: string;\n languages: { href: string; hrefLang: string }[];\n media: { href: string; media: string }[];\n types: { href: string; type: string }[];\n};\n\nexport type ResolvedOpenGraphImage = {\n url: string;\n alt?: string;\n width?: string;\n height?: string;\n type?: string;\n secureUrl?: string;\n};\n\nexport type ResolvedOpenGraphVideo = {\n url: string;\n secureUrl?: string;\n type?: string;\n width?: string;\n height?: string;\n};\n\nexport type ResolvedOpenGraphAudio = {\n url: string;\n secureUrl?: string;\n type?: string;\n};\n\nexport type ResolvedOpenGraph = {\n basic: { property: string; content: string }[];\n images: ResolvedOpenGraphImage[];\n videos: ResolvedOpenGraphVideo[];\n audio: ResolvedOpenGraphAudio[];\n article: { property: string; content: string }[];\n};\n\nexport type ResolvedTwitterImage = {\n url: string;\n alt?: string;\n};\n\nexport type ResolvedTwitterPlayer = {\n url: string;\n width?: string;\n height?: string;\n stream?: string;\n};\n\nexport type ResolvedTwitterAppEntry = {\n platform: 'iphone' | 'ipad' | 'googleplay';\n name?: string;\n id?: string;\n url?: string;\n};\n\nexport type ResolvedTwitter = {\n basic: { name: string; content: string }[];\n images: ResolvedTwitterImage[];\n players: ResolvedTwitterPlayer[];\n app: ResolvedTwitterAppEntry[];\n};\n\nexport type ResolvedIcon = {\n rel: string;\n href: string;\n type?: string;\n sizes?: string;\n media?: string;\n};\n\nexport type ResolvedIcons = {\n icon: ResolvedIcon[];\n shortcut: ResolvedIcon[];\n apple: ResolvedIcon[];\n other: ResolvedIcon[];\n};\n\nexport type ResolvedVerification = {\n google: string[];\n yahoo: string[];\n yandex: string[];\n other: { name: string; content: string }[];\n};\n\nexport type ResolvedAppleWebApp = {\n capable: 'yes';\n title?: string;\n statusBarStyle?: string;\n startupImages: { href: string; media?: string }[];\n};\n\nexport type ResolvedFacebook = {\n appId?: string;\n admins: string[];\n};\n\nexport type ResolvedAppLinks = {\n ios?: {\n url?: string;\n appStoreId?: string;\n appName?: string;\n };\n android?: {\n url?: string;\n package?: string;\n appName?: string;\n };\n web?: {\n url?: string;\n shouldFallback?: 'true' | 'false';\n };\n};\n\nexport type ResolvedMetadata = {\n title?: string;\n description?: string;\n applicationName?: string;\n keywords?: string;\n generator?: string;\n referrer?: string;\n authors: ResolvedAuthor[];\n creator?: string;\n publisher?: string;\n category?: string;\n robots?: string;\n googleBot?: string;\n alternates?: ResolvedAlternates;\n openGraph?: ResolvedOpenGraph;\n twitter?: ResolvedTwitter;\n icons?: ResolvedIcons;\n formatDetection?: string;\n verification?: ResolvedVerification;\n appleWebApp?: ResolvedAppleWebApp;\n itunes?: string;\n facebook?: ResolvedFacebook;\n pinterest?: 'true' | 'false';\n appLinks?: ResolvedAppLinks;\n archives: string[];\n assets: string[];\n bookmarks: string[];\n manifest?: string;\n other: { name: string; content: string }[];\n};\n\nexport type { Metadata, MetadataIconDescriptor, MetadataImage, MetadataValue, MetadataValueArray };\n"]} \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/streams.d.ts b/packages/@expo/router-server/build/utils/streams.d.ts new file mode 100644 index 00000000000000..f7238dc9ee81a1 --- /dev/null +++ b/packages/@expo/router-server/build/utils/streams.d.ts @@ -0,0 +1,12 @@ +type HeadInjectionOptions = { + injectionParts: string[]; + htmlAttributes?: string; + bodyAttributes?: string; +}; +/** + * Buffers the initial HTML document prefix, injects head content plus any serialized document + * attributes, then switches to passthrough mode for the rest of the stream. + */ +export declare function createDocumentMetadataInjectionTransform(options: HeadInjectionOptions): TransformStream; +export {}; +//# sourceMappingURL=streams.d.ts.map \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/streams.d.ts.map b/packages/@expo/router-server/build/utils/streams.d.ts.map new file mode 100644 index 00000000000000..ad32583d6b9c5d --- /dev/null +++ b/packages/@expo/router-server/build/utils/streams.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"streams.d.ts","sourceRoot":"","sources":["../../src/utils/streams.ts"],"names":[],"mappings":"AAGA,KAAK,oBAAoB,GAAG;IAC1B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAeF;;;GAGG;AACH,wBAAgB,wCAAwC,CACtD,OAAO,EAAE,oBAAoB,GAC5B,eAAe,CAAC,UAAU,EAAE,UAAU,CAAC,CA4DzC"} \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/streams.js b/packages/@expo/router-server/build/utils/streams.js new file mode 100644 index 00000000000000..704b93cabc0543 --- /dev/null +++ b/packages/@expo/router-server/build/utils/streams.js @@ -0,0 +1,69 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createDocumentMetadataInjectionTransform = createDocumentMetadataInjectionTransform; +const HEAD_CLOSE_TAG = ''; +const BODY_OPEN_TAG = '`; + if (html.includes(bareTag)) { + return html.replace(bareTag, `<${tagName} ${attributes}>`); + } + return html.replace(`<${tagName} `, `<${tagName} ${attributes} `); +} +/** + * Buffers the initial HTML document prefix, injects head content plus any serialized document + * attributes, then switches to passthrough mode for the rest of the stream. + */ +function createDocumentMetadataInjectionTransform(options) { + let buffer = ''; + let injected = false; + const decoder = new TextDecoder(); + const encoder = new TextEncoder(); + const injection = options.injectionParts.join(''); + return new TransformStream({ + transform(chunk, controller) { + const decodedChunk = decoder.decode(chunk, { stream: true }); + if (injected) { + controller.enqueue(encoder.encode(decodedChunk)); + return; + } + buffer += decodedChunk; + const headCloseIdx = buffer.indexOf(HEAD_CLOSE_TAG); + const bodyOpenIdx = options.bodyAttributes ? buffer.indexOf(BODY_OPEN_TAG) : 0; + const hasRequiredDocumentPrefix = headCloseIdx !== -1 && bodyOpenIdx !== -1; + if (hasRequiredDocumentPrefix) { + const bodyOpenTagEndIdx = options.bodyAttributes + ? buffer.indexOf('>', bodyOpenIdx) + : headCloseIdx + HEAD_CLOSE_TAG.length; + if (bodyOpenTagEndIdx === -1) { + return; + } + let before = buffer.slice(0, headCloseIdx); + const afterHead = buffer.slice(headCloseIdx, bodyOpenTagEndIdx + 1); + const remainder = buffer.slice(bodyOpenTagEndIdx + 1); + before = injectTagAttributes(before, 'html', options.htmlAttributes); + const documentPrefix = injectTagAttributes(afterHead, 'body', options.bodyAttributes); + injected = true; + buffer = ''; + controller.enqueue(encoder.encode(before + injection + documentPrefix + remainder)); + } + }, + flush(controller) { + const trailing = decoder.decode(); + if (trailing) { + if (injected) { + controller.enqueue(encoder.encode(trailing)); + return; + } + buffer += trailing; + } + if (!injected) { + controller.error(new Error(`Streaming SSR head injection failed: missing ${HEAD_CLOSE_TAG} in HTML output.`)); + } + }, + }); +} +//# sourceMappingURL=streams.js.map \ No newline at end of file diff --git a/packages/@expo/router-server/build/utils/streams.js.map b/packages/@expo/router-server/build/utils/streams.js.map new file mode 100644 index 00000000000000..fb7996b3891078 --- /dev/null +++ b/packages/@expo/router-server/build/utils/streams.js.map @@ -0,0 +1 @@ +{"version":3,"file":"streams.js","sourceRoot":"","sources":["../../src/utils/streams.ts"],"names":[],"mappings":"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","sourcesContent":["const HEAD_CLOSE_TAG = '';\nconst BODY_OPEN_TAG = '`;\n if (html.includes(bareTag)) {\n return html.replace(bareTag, `<${tagName} ${attributes}>`);\n }\n\n return html.replace(`<${tagName} `, `<${tagName} ${attributes} `);\n}\n\n/**\n * Buffers the initial HTML document prefix, injects head content plus any serialized document\n * attributes, then switches to passthrough mode for the rest of the stream.\n */\nexport function createDocumentMetadataInjectionTransform(\n options: HeadInjectionOptions\n): TransformStream {\n let buffer = '';\n let injected = false;\n const decoder = new TextDecoder();\n const encoder = new TextEncoder();\n const injection = options.injectionParts.join('');\n\n return new TransformStream({\n transform(chunk, controller): void {\n const decodedChunk = decoder.decode(chunk, { stream: true });\n\n if (injected) {\n controller.enqueue(encoder.encode(decodedChunk));\n return;\n }\n\n buffer += decodedChunk;\n\n const headCloseIdx = buffer.indexOf(HEAD_CLOSE_TAG);\n const bodyOpenIdx = options.bodyAttributes ? buffer.indexOf(BODY_OPEN_TAG) : 0;\n const hasRequiredDocumentPrefix = headCloseIdx !== -1 && bodyOpenIdx !== -1;\n if (hasRequiredDocumentPrefix) {\n const bodyOpenTagEndIdx = options.bodyAttributes\n ? buffer.indexOf('>', bodyOpenIdx)\n : headCloseIdx + HEAD_CLOSE_TAG.length;\n\n if (bodyOpenTagEndIdx === -1) {\n return;\n }\n\n let before = buffer.slice(0, headCloseIdx);\n const afterHead = buffer.slice(headCloseIdx, bodyOpenTagEndIdx + 1);\n const remainder = buffer.slice(bodyOpenTagEndIdx + 1);\n before = injectTagAttributes(before, 'html', options.htmlAttributes);\n const documentPrefix = injectTagAttributes(afterHead, 'body', options.bodyAttributes);\n injected = true;\n buffer = '';\n controller.enqueue(encoder.encode(before + injection + documentPrefix + remainder));\n }\n },\n\n flush(controller): void {\n const trailing = decoder.decode();\n if (trailing) {\n if (injected) {\n controller.enqueue(encoder.encode(trailing));\n return;\n }\n buffer += trailing;\n }\n\n if (!injected) {\n controller.error(\n new Error(\n `Streaming SSR head injection failed: missing ${HEAD_CLOSE_TAG} in HTML output.`\n )\n );\n }\n },\n });\n}\n"]} \ No newline at end of file diff --git a/packages/@expo/router-server/src/server/__tests__/metadata.test.ts b/packages/@expo/router-server/src/server/__tests__/metadata.test.ts new file mode 100644 index 00000000000000..8eb135cb9c24c2 --- /dev/null +++ b/packages/@expo/router-server/src/server/__tests__/metadata.test.ts @@ -0,0 +1,87 @@ +import { ctx } from 'expo-router/_ctx'; +import { ImmutableRequest } from 'expo-server/private'; + +import { resolveMetadata } from '../metadata'; + +jest.mock('expo-router/_ctx', () => ({ + ctx: jest.fn(), +})); + +const mockedCtx = jest.mocked(ctx); + +function createMockRequest( + url: string, + init: { + signal?: AbortSignal; + } = {} +) { + return new ImmutableRequest({ + url, + method: 'GET', + headers: new Headers(), + signal: init.signal ?? new AbortController().signal, + clone() { + return this as Request; + }, + } as Request); +} + +describe(resolveMetadata, () => { + beforeEach(() => { + mockedCtx.mockReset(); + }); + + it('passes the request and params to a route `generateMetadata()`', async () => { + const generateMetadata = jest.fn().mockResolvedValue({ title: 'Post 123' }); + mockedCtx.mockResolvedValue({ + generateMetadata, + } as never); + + const request = createMockRequest('http://localhost/posts/123'); + const result = await resolveMetadata({ + route: { + file: './posts/[id].tsx', + page: '/posts/[id]', + }, + request, + params: { id: '123' }, + }); + + expect(generateMetadata).toHaveBeenCalledWith(request, { id: '123' }); + expect(result).toEqual({ + metadata: { title: 'Post 123' }, + headTags: 'Post 123', + }); + }); + + it('normalizes nullish metadata results to null', async () => { + mockedCtx.mockResolvedValue({ + generateMetadata: jest.fn().mockResolvedValue(undefined), + } as never); + + await expect( + resolveMetadata({ + route: { + file: './index.tsx', + page: '/index', + }, + request: createMockRequest('http://localhost/'), + params: {}, + }) + ).resolves.toBeNull(); + }); + + it('returns null when the route module cannot be resolved', async () => { + mockedCtx.mockResolvedValue(undefined as never); + await expect( + resolveMetadata({ + route: { + file: './index.tsx', + page: '/index', + }, + request: createMockRequest('http://localhost/'), + params: {}, + }) + ).resolves.toBeNull(); + }); +}); diff --git a/packages/@expo/router-server/src/server/metadata.ts b/packages/@expo/router-server/src/server/metadata.ts new file mode 100644 index 00000000000000..ad25f541ff8998 --- /dev/null +++ b/packages/@expo/router-server/src/server/metadata.ts @@ -0,0 +1,50 @@ +import { ctx } from 'expo-router/_ctx'; +import type { LoadedRoute } from 'expo-router/build/Route'; +import type { GenerateMetadataFunction, ImmutableRequest, Metadata } from 'expo-server'; + +import { serializeMetadataToHtml } from '../utils/metadata/serialize'; + +type ResolveMetadataOptions = { + route: { + file: string; + page: string; + }; + request: ImmutableRequest; + params: Record; +}; + +type ResolvedMetadata = { + metadata: Metadata; + headTags: string; +}; + +function getGenerateMetadata(moduleExports: LoadedRoute): GenerateMetadataFunction | null { + return typeof moduleExports.generateMetadata === 'function' + ? moduleExports.generateMetadata + : null; +} + +export async function resolveMetadata( + options: ResolveMetadataOptions +): Promise { + const routeModule = (await ctx(options.route.file)) as LoadedRoute | undefined; + if (!routeModule) { + return null; + } + + const generateMetadata = getGenerateMetadata(routeModule); + if (!generateMetadata) { + return null; + } + + const metadata = await generateMetadata(options.request, options.params); + + if (metadata == null) { + return null; + } + + return { + metadata, + headTags: serializeMetadataToHtml(metadata), + }; +} diff --git a/packages/@expo/router-server/src/static/__tests__/html.test.web.tsx b/packages/@expo/router-server/src/static/__tests__/html.test.web.tsx deleted file mode 100644 index 7692a332feeec0..00000000000000 --- a/packages/@expo/router-server/src/static/__tests__/html.test.web.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/** @jest-environment jsdom */ -import { render } from '@testing-library/react'; -import React from 'react'; - -import { PreloadedDataScript } from '../html'; - -describe(PreloadedDataScript, () => { - it('renders script tag with loader data', () => { - const data = { '/': { foo: 'bar' } }; - const { container } = render(); - const scriptElement = container.querySelector('script[id="expo-router-data"]'); - - expect(scriptElement).not.toBeNull(); - expect(scriptElement?.getAttribute('type')).toBe('module'); - expect(scriptElement?.innerHTML).toContain('globalThis.__EXPO_ROUTER_LOADER_DATA__'); - expect(scriptElement?.innerHTML).toContain('JSON.parse("{\\"/\\":{\\"foo\\":\\"bar\\"}}")'); - }); - - it('handles special characters', () => { - const data = { - '/': { - dangerous: '', - nested: { value: '' }, - multiple: '<<>>', - mixed: 'Text with and ', - quotes: 'He said "Hello"', - backslash: 'C:\\Users\\test', - newline: 'Line 1\nLine 2', - unicode: '你好世界 🌍', - }, - }; - const { container } = render(); - const scriptElement = container.querySelector('script[id="expo-router-data"]'); - const content = scriptElement?.innerHTML || ''; - - expect(content).not.toContain(''); - expect(content).toContain('\\\\u003cscript'); - expect(content).toContain('\\\\u003c/script\\\\u003e\\'); - - const unescapedMatches = content.match(/(? }) { - const safeJson = escapeUnsafeCharacters(JSON.stringify(data)); - - return ( - ', + nested: { value: '' }, + multiple: '<<>>', + mixed: 'Text with and ', + quotes: 'He said "Hello"', + backslash: 'C:\\Users\\test', + newline: 'Line 1\nLine 2', + unicode: '你好世界 🌍', + ampersand: 'A & B', + separators: '\u2028\u2029', + }, + }); + + const result = escapeUnsafeCharacters(input); + + expect(result).not.toContain(''); + expect(result).not.toContain(''); + expect(result).toContain('\\u003cscript'); + expect(result).toContain('\\u003c/script\\u003e'); + expect(result).toContain('\\u003c\\u003c\\u003cmultiple'); + expect(result).toContain('\\u003ctag'); + expect(result).toContain('\\u003c/tag\\u003e'); + expect(result).toContain('He said \\"Hello\\"'); + expect(result).toContain('C:\\\\Users\\\\test'); + expect(result).toContain('\\n'); + expect(result).toContain('你好世界'); + expect(result).toContain('A \\u0026 B'); + expect(result).toContain('\\u2028\\u2029'); + + const unescapedAngleBrackets = result.match(/[<>]/g); + expect(unescapedAngleBrackets).toBeNull(); + }); +}); + describe(createInjectedCssElements, () => { it('returns empty string for empty array', () => { expect(createInjectedCssElements([])).toBe(''); @@ -53,6 +93,30 @@ describe(getHydrationFlagScript, () => { }); }); +describe(createLoaderDataScript, () => { + it('returns the expected inline script markup', () => { + const data = { '/': { foo: 'bar' } }; + + expect(createLoaderDataScript(data)).toBe( + '' + ); + }); + + it('uses an escaped payload for unsafe HTML input', () => { + const data = { '/route': '' }; + const result = createLoaderDataScript(data); + + expect(result).toContain('`; } +/** + * Returns a synchronous inline ``; +} + const HELMET_HEAD_KEYS = ['title', 'priority', 'meta', 'link', 'script', 'style'] as const; /** diff --git a/packages/@expo/router-server/src/utils/metadata/render.ts b/packages/@expo/router-server/src/utils/metadata/render.ts new file mode 100644 index 00000000000000..076800b7aa150d --- /dev/null +++ b/packages/@expo/router-server/src/utils/metadata/render.ts @@ -0,0 +1,288 @@ +import { renderMetadataTag, pushLink, pushName, pushProperty } from './tag'; +import type { + MetadataTag, + ResolvedAlternates, + ResolvedAppLinks, + ResolvedAppleWebApp, + ResolvedFacebook, + ResolvedIcons, + ResolvedMetadata, + ResolvedOpenGraph, + ResolvedTwitter, + ResolvedVerification, +} from './types'; + +export function renderMetadataTags(resolved: ResolvedMetadata): MetadataTag[] { + const tags: MetadataTag[] = []; + + renderTitle(tags, resolved.title); + renderBasicMetadata(tags, resolved); + renderAuthors(tags, resolved.authors); + renderCreatorMetadata(tags, resolved); + renderRobots(tags, resolved); + renderAlternates(tags, resolved.alternates); + renderOpenGraph(tags, resolved.openGraph); + renderTwitter(tags, resolved.twitter); + renderIcons(tags, resolved.icons); + renderFormatDetection(tags, resolved.formatDetection); + renderVerification(tags, resolved.verification); + renderAppleWebApp(tags, resolved.appleWebApp); + renderItunes(tags, resolved.itunes); + renderFacebook(tags, resolved.facebook); + renderPinterest(tags, resolved.pinterest); + renderAppLinks(tags, resolved.appLinks); + renderLinkRelations(tags, resolved); + renderOther(tags, resolved.other); + + return tags; +} + +export function renderMetadataHtml(tags: MetadataTag[]): string { + return tags.map(renderMetadataTag).join(''); +} + +function renderTitle(tags: MetadataTag[], title: string | undefined) { + if (!title) return; + + tags.push({ + tagName: 'title', + content: title, + }); +} + +function renderBasicMetadata(tags: MetadataTag[], resolved: ResolvedMetadata) { + pushName(tags, 'description', resolved.description); + pushName(tags, 'application-name', resolved.applicationName); + pushName(tags, 'keywords', resolved.keywords); + pushName(tags, 'generator', resolved.generator); + pushName(tags, 'referrer', resolved.referrer); +} + +function renderAuthors(tags: MetadataTag[], authors: ResolvedMetadata['authors']) { + for (const author of authors) { + pushName(tags, 'author', author.name); + if (author.url) { + pushLink(tags, { rel: 'author', href: author.url }); + } + } +} + +function renderCreatorMetadata(tags: MetadataTag[], resolved: ResolvedMetadata) { + pushName(tags, 'creator', resolved.creator); + pushName(tags, 'publisher', resolved.publisher); + pushName(tags, 'category', resolved.category); +} + +function renderRobots(tags: MetadataTag[], resolved: ResolvedMetadata) { + pushName(tags, 'robots', resolved.robots); + pushName(tags, 'googlebot', resolved.googleBot); +} + +function renderAlternates(tags: MetadataTag[], alternates: ResolvedAlternates | undefined) { + if (!alternates) return; + + if (alternates.canonical) { + pushLink(tags, { rel: 'canonical', href: alternates.canonical }); + } + + for (const language of alternates.languages) { + pushLink(tags, { + rel: 'alternate', + href: language.href, + hreflang: language.hrefLang, + }); + } + + for (const media of alternates.media) { + pushLink(tags, { + rel: 'alternate', + href: media.href, + media: media.media, + }); + } + + for (const type of alternates.types) { + pushLink(tags, { + rel: 'alternate', + href: type.href, + type: type.type, + }); + } +} + +function renderOpenGraph(tags: MetadataTag[], openGraph: ResolvedOpenGraph | undefined) { + if (!openGraph) return; + + for (const field of openGraph.basic) { + pushProperty(tags, field.property, field.content); + } + + for (const image of openGraph.images) { + pushProperty(tags, 'og:image', image.url); + pushProperty(tags, 'og:image:alt', image.alt); + pushProperty(tags, 'og:image:width', image.width); + pushProperty(tags, 'og:image:height', image.height); + pushProperty(tags, 'og:image:type', image.type); + pushProperty(tags, 'og:image:secure_url', image.secureUrl); + } + + for (const video of openGraph.videos) { + pushProperty(tags, 'og:video', video.url); + pushProperty(tags, 'og:video:secure_url', video.secureUrl); + pushProperty(tags, 'og:video:type', video.type); + pushProperty(tags, 'og:video:width', video.width); + pushProperty(tags, 'og:video:height', video.height); + } + + for (const audio of openGraph.audio) { + pushProperty(tags, 'og:audio', audio.url); + pushProperty(tags, 'og:audio:secure_url', audio.secureUrl); + pushProperty(tags, 'og:audio:type', audio.type); + } + + for (const field of openGraph.article) { + pushProperty(tags, field.property, field.content); + } +} + +function renderTwitter(tags: MetadataTag[], twitter: ResolvedTwitter | undefined) { + if (!twitter) return; + + for (const field of twitter.basic) { + pushName(tags, field.name, field.content); + } + + for (const image of twitter.images) { + pushName(tags, 'twitter:image', image.url); + pushName(tags, 'twitter:image:alt', image.alt); + } + + for (const player of twitter.players) { + pushName(tags, 'twitter:player', player.url); + pushName(tags, 'twitter:player:width', player.width); + pushName(tags, 'twitter:player:height', player.height); + pushName(tags, 'twitter:player:stream', player.stream); + } + + for (const app of twitter.app) { + pushName(tags, `twitter:app:name:${app.platform}`, app.name); + pushName(tags, `twitter:app:id:${app.platform}`, app.id); + pushName(tags, `twitter:app:url:${app.platform}`, app.url); + } +} + +function renderIcons(tags: MetadataTag[], icons: ResolvedIcons | undefined) { + if (!icons) return; + + for (const icon of icons.icon) { + pushLink(tags, icon); + } + for (const shortcut of icons.shortcut) { + pushLink(tags, shortcut); + } + for (const apple of icons.apple) { + pushLink(tags, apple); + } + for (const other of icons.other) { + pushLink(tags, other); + } +} + +function renderFormatDetection(tags: MetadataTag[], formatDetection: string | undefined) { + pushName(tags, 'format-detection', formatDetection); +} + +function renderVerification(tags: MetadataTag[], verification: ResolvedVerification | undefined) { + if (!verification) return; + + for (const content of verification.google) { + pushName(tags, 'google-site-verification', content); + } + for (const content of verification.yahoo) { + pushName(tags, 'y_key', content); + } + for (const content of verification.yandex) { + pushName(tags, 'yandex-verification', content); + } + for (const entry of verification.other) { + pushName(tags, entry.name, entry.content); + } +} + +function renderAppleWebApp(tags: MetadataTag[], appleWebApp: ResolvedAppleWebApp | undefined) { + if (!appleWebApp) return; + + pushName(tags, 'mobile-web-app-capable', appleWebApp.capable); + pushName(tags, 'apple-mobile-web-app-title', appleWebApp.title); + pushName(tags, 'apple-mobile-web-app-status-bar-style', appleWebApp.statusBarStyle); + + for (const image of appleWebApp.startupImages) { + pushLink(tags, { + rel: 'apple-touch-startup-image', + href: image.href, + media: image.media, + }); + } +} + +function renderItunes(tags: MetadataTag[], itunes: string | undefined) { + pushName(tags, 'apple-itunes-app', itunes); +} + +function renderFacebook(tags: MetadataTag[], facebook: ResolvedFacebook | undefined) { + if (!facebook) return; + + pushProperty(tags, 'fb:app_id', facebook.appId); + for (const admin of facebook.admins) { + pushProperty(tags, 'fb:admins', admin); + } +} + +function renderPinterest(tags: MetadataTag[], pinterest: 'true' | 'false' | undefined) { + pushName(tags, 'pinterest-rich-pin', pinterest); +} + +function renderAppLinks(tags: MetadataTag[], appLinks: ResolvedAppLinks | undefined) { + if (!appLinks) return; + + if (appLinks.ios) { + pushProperty(tags, 'al:ios:url', appLinks.ios.url); + pushProperty(tags, 'al:ios:app_store_id', appLinks.ios.appStoreId); + pushProperty(tags, 'al:ios:app_name', appLinks.ios.appName); + } + + if (appLinks.android) { + pushProperty(tags, 'al:android:url', appLinks.android.url); + pushProperty(tags, 'al:android:package', appLinks.android.package); + pushProperty(tags, 'al:android:app_name', appLinks.android.appName); + } + + if (appLinks.web) { + pushProperty(tags, 'al:web:url', appLinks.web.url); + pushProperty(tags, 'al:web:should_fallback', appLinks.web.shouldFallback); + } +} + +function renderLinkRelations(tags: MetadataTag[], resolved: ResolvedMetadata) { + renderLinkArray(tags, 'archives', resolved.archives); + renderLinkArray(tags, 'assets', resolved.assets); + renderLinkArray(tags, 'bookmarks', resolved.bookmarks); + renderManifest(tags, resolved.manifest); +} + +function renderLinkArray(tags: MetadataTag[], rel: string, urls: string[]) { + for (const url of urls) { + pushLink(tags, { rel, href: url }); + } +} + +function renderManifest(tags: MetadataTag[], manifest: string | undefined) { + if (!manifest) return; + pushLink(tags, { rel: 'manifest', href: manifest }); +} + +function renderOther(tags: MetadataTag[], other: { name: string; content: string }[]) { + for (const entry of other) { + pushName(tags, entry.name, entry.content); + } +} diff --git a/packages/@expo/router-server/src/utils/metadata/resolve.ts b/packages/@expo/router-server/src/utils/metadata/resolve.ts new file mode 100644 index 00000000000000..e327b186dc86d3 --- /dev/null +++ b/packages/@expo/router-server/src/utils/metadata/resolve.ts @@ -0,0 +1,595 @@ +import type { + Metadata, + MetadataAuthor, + MetadataAudio, + MetadataIconDescriptor, + MetadataImage, + MetadataTwitterApp, + MetadataTwitterImage, + MetadataTwitterPlayer, + MetadataValue, + MetadataValueArray, + MetadataVideo, + ResolvedAlternates, + ResolvedAppLinks, + ResolvedAppleWebApp, + ResolvedFacebook, + ResolvedIcon, + ResolvedIcons, + ResolvedMetadata, + ResolvedOpenGraph, + ResolvedOpenGraphAudio, + ResolvedOpenGraphImage, + ResolvedOpenGraphVideo, + ResolvedTwitter, + ResolvedTwitterAppEntry, + ResolvedTwitterImage, + ResolvedTwitterPlayer, + ResolvedVerification, +} from './types'; + +const NEGATIVE_ROBOT_DIRECTIVES = new Set([ + 'noarchive', + 'nosnippet', + 'noimageindex', + 'nocache', + 'notranslate', +]); + +export function resolveMetadata(metadata: Metadata): ResolvedMetadata { + const resolvedRobots = resolveRobots(metadata.robots); + + return { + ...resolveBasicMetadata(metadata), + authors: resolveAuthors(metadata.authors), + robots: resolvedRobots.robots, + googleBot: resolvedRobots.googleBot, + alternates: resolveAlternates(metadata.alternates), + openGraph: resolveOpenGraph(metadata.openGraph), + twitter: resolveTwitter(metadata.twitter), + icons: resolveIcons(metadata.icons), + formatDetection: resolveFormatDetection(metadata.formatDetection), + verification: resolveVerification(metadata.verification), + appleWebApp: resolveAppleWebApp(metadata.appleWebApp), + itunes: resolveItunes(metadata.itunes), + facebook: resolveFacebook(metadata.facebook), + pinterest: resolvePinterest(metadata.pinterest), + appLinks: resolveAppLinks(metadata.appLinks), + archives: metadata.archives ?? [], + assets: metadata.assets ?? [], + bookmarks: metadata.bookmarks ?? [], + manifest: metadata.manifest, + other: resolveOther(metadata.other), + }; +} + +export function resolveRobots(robots: Metadata['robots']): { + robots?: string; + googleBot?: string; +} { + if (!robots) { + return {}; + } + + return { + robots: serializeRobotsValue(robots), + googleBot: + typeof robots === 'object' + ? serializeRobotsValue(robots.googleBot as string | Record | undefined) + : undefined, + }; +} + +export function resolveOpenGraph(openGraph: Metadata['openGraph']): ResolvedOpenGraph | undefined { + if (!openGraph) return undefined; + + const basic: { property: string; content: string }[] = []; + const images: ResolvedOpenGraphImage[] = []; + const videos: ResolvedOpenGraphVideo[] = []; + const audio: ResolvedOpenGraphAudio[] = []; + const article: { property: string; content: string }[] = []; + + pushResolvedProperty(basic, 'og:title', openGraph.title); + pushResolvedProperty(basic, 'og:description', openGraph.description); + pushResolvedProperty(basic, 'og:url', openGraph.url); + pushResolvedProperty(basic, 'og:site_name', openGraph.siteName); + pushResolvedProperty(basic, 'og:locale', openGraph.locale); + pushResolvedProperty(basic, 'og:type', openGraph.type); + if (openGraph.determiner) { + pushResolvedProperty(basic, 'og:determiner', openGraph.determiner); + } + pushResolvedProperty(basic, 'og:country_name', openGraph.countryName); + if (openGraph.ttl != null) { + pushResolvedProperty(basic, 'og:ttl', String(openGraph.ttl)); + } + + for (const locale of openGraph.alternateLocale ?? []) { + pushResolvedProperty(basic, 'og:locale:alternate', locale); + } + for (const email of openGraph.emails ?? []) { + pushResolvedProperty(basic, 'og:email', email); + } + for (const phoneNumber of openGraph.phoneNumbers ?? []) { + pushResolvedProperty(basic, 'og:phone_number', phoneNumber); + } + for (const faxNumber of openGraph.faxNumbers ?? []) { + pushResolvedProperty(basic, 'og:fax_number', faxNumber); + } + + for (const image of normalizeMetadataImages(openGraph.images)) { + if (typeof image === 'string') { + images.push({ url: image }); + continue; + } + + images.push({ + url: image.url, + alt: image.alt, + width: image.width != null ? String(image.width) : undefined, + height: image.height != null ? String(image.height) : undefined, + type: image.type, + secureUrl: image.secureUrl, + }); + } + + for (const video of normalizeArray(openGraph.videos)) { + if (typeof video === 'string') { + videos.push({ url: video }); + continue; + } + + videos.push({ + url: video.url, + secureUrl: video.secureUrl, + type: video.type, + width: video.width != null ? String(video.width) : undefined, + height: video.height != null ? String(video.height) : undefined, + }); + } + + for (const audioItem of normalizeArray(openGraph.audio)) { + if (typeof audioItem === 'string') { + audio.push({ url: audioItem }); + continue; + } + + audio.push({ + url: audioItem.url, + secureUrl: audioItem.secureUrl, + type: audioItem.type, + }); + } + + if (openGraph.type === 'article') { + pushResolvedProperty(article, 'article:published_time', openGraph.publishedTime); + pushResolvedProperty(article, 'article:modified_time', openGraph.modifiedTime); + pushResolvedProperty(article, 'article:expiration_time', openGraph.expirationTime); + pushResolvedProperty(article, 'article:section', openGraph.section); + + for (const tag of openGraph.tags ?? []) { + pushResolvedProperty(article, 'article:tag', tag); + } + for (const author of openGraph.authors ?? []) { + pushResolvedProperty(article, 'article:author', author); + } + } + + return { + basic, + images, + videos, + audio, + article, + }; +} + +export function resolveTwitter(twitter: Metadata['twitter']): ResolvedTwitter | undefined { + if (!twitter) return undefined; + + const basic: { name: string; content: string }[] = []; + const images: ResolvedTwitterImage[] = []; + const players: ResolvedTwitterPlayer[] = []; + const app: ResolvedTwitterAppEntry[] = []; + + pushResolvedName(basic, 'twitter:card', twitter.card); + pushResolvedName(basic, 'twitter:title', twitter.title); + pushResolvedName(basic, 'twitter:description', twitter.description); + pushResolvedName(basic, 'twitter:site', twitter.site); + pushResolvedName(basic, 'twitter:site:id', twitter.siteId); + pushResolvedName(basic, 'twitter:creator', twitter.creator); + pushResolvedName(basic, 'twitter:creator:id', twitter.creatorId); + + for (const image of normalizeArray(twitter.images)) { + if (typeof image === 'string') { + images.push({ url: image }); + continue; + } + + images.push({ + url: image.url, + alt: image.alt, + }); + } + + for (const player of normalizeArray(twitter.players)) { + players.push({ + url: player.url, + width: player.width != null ? String(player.width) : undefined, + height: player.height != null ? String(player.height) : undefined, + stream: player.stream, + }); + } + + app.push(...resolveTwitterApp(twitter.app)); + + return { + basic, + images, + players, + app, + }; +} + +export function resolveVerification( + verification: Metadata['verification'] +): ResolvedVerification | undefined { + if (!verification) return undefined; + + const resolved: ResolvedVerification = { + google: normalizeArray(verification.google), + yahoo: normalizeArray(verification.yahoo), + yandex: normalizeArray(verification.yandex), + other: [], + }; + + if (verification.other) { + for (const [name, value] of Object.entries(verification.other)) { + for (const content of normalizeArray(value)) { + resolved.other.push({ name, content: String(content) }); + } + } + } + + return resolved; +} + +export function resolveAppleWebApp( + appleWebApp: Metadata['appleWebApp'] +): ResolvedAppleWebApp | undefined { + if (!appleWebApp) return undefined; + + const startupImages: { href: string; media?: string }[] = []; + + for (const image of normalizeArray(appleWebApp.startupImage)) { + if (typeof image === 'string') { + startupImages.push({ href: image }); + continue; + } + + if (image.url) { + startupImages.push({ href: image.url, media: image.media }); + } + } + + return { + capable: 'yes', + title: appleWebApp.title, + statusBarStyle: appleWebApp.statusBarStyle, + startupImages, + }; +} + +export function resolveOther(other: Metadata['other']): { name: string; content: string }[] { + const resolved: { name: string; content: string }[] = []; + if (!other) return resolved; + + for (const [name, value] of Object.entries(other)) { + for (const content of normalizeOtherValue(value)) { + resolved.push({ name, content }); + } + } + + return resolved; +} + +function resolveBasicMetadata( + metadata: Metadata +): Pick< + ResolvedMetadata, + | 'title' + | 'description' + | 'applicationName' + | 'keywords' + | 'generator' + | 'referrer' + | 'creator' + | 'publisher' + | 'category' +> { + return { + title: metadata.title, + description: metadata.description, + applicationName: metadata.applicationName, + keywords: Array.isArray(metadata.keywords) ? metadata.keywords.join(', ') : metadata.keywords, + generator: metadata.generator, + referrer: metadata.referrer, + creator: metadata.creator, + publisher: metadata.publisher, + category: metadata.category, + }; +} + +function resolveAuthors(authors: Metadata['authors']): { name?: string; url?: string }[] { + return normalizeArray(authors).map((author) => ({ + name: author.name, + url: author.url, + })); +} + +function resolveAlternates(alternates: Metadata['alternates']): ResolvedAlternates | undefined { + if (!alternates) return undefined; + + const resolved: ResolvedAlternates = { + canonical: alternates.canonical, + languages: [], + media: [], + types: [], + }; + + if (alternates.languages) { + for (const [hrefLang, href] of Object.entries(alternates.languages)) { + if (!href) continue; + resolved.languages.push({ href, hrefLang }); + } + } + + if (alternates.media) { + for (const [media, href] of Object.entries(alternates.media)) { + if (!href) continue; + resolved.media.push({ href, media }); + } + } + + if (alternates.types) { + for (const [type, href] of Object.entries(alternates.types)) { + if (!href) continue; + resolved.types.push({ href, type }); + } + } + + return resolved; +} + +function resolveTwitterApp(app: MetadataTwitterApp | undefined): ResolvedTwitterAppEntry[] { + if (!app) return []; + + const resolved: ResolvedTwitterAppEntry[] = []; + + for (const platform of ['iphone', 'ipad', 'googleplay'] as const) { + resolved.push({ + platform, + name: app.name, + id: app.id?.[platform], + url: app.url?.[platform], + }); + } + + return resolved; +} + +function resolveIcons(icons: Metadata['icons']): ResolvedIcons | undefined { + if (!icons) return undefined; + + return { + icon: resolveIconDescriptors('icon', icons.icon), + shortcut: resolveIconDescriptors('shortcut icon', icons.shortcut), + apple: resolveIconDescriptors('apple-touch-icon', icons.apple), + other: resolveIconDescriptors('icon', icons.other), + }; +} + +function resolveIconDescriptors( + rel: string, + descriptors: Metadata['icons'] extends infer T + ? T extends null | undefined + ? never + : T[keyof T] + : never +): ResolvedIcon[] { + const resolved: ResolvedIcon[] = []; + + for (const descriptor of normalizeArray(descriptors)) { + const normalized = normalizeIconDescriptor(rel, descriptor); + if (normalized) { + resolved.push(normalized); + } + } + + return resolved; +} + +function normalizeIconDescriptor( + rel: string, + descriptor: MetadataIconDescriptor +): ResolvedIcon | null { + if (typeof descriptor === 'string') { + return { rel, href: descriptor }; + } + + if (!descriptor.url) { + return null; + } + + return { + rel: descriptor.rel ?? rel, + href: descriptor.url, + type: descriptor.type, + sizes: descriptor.sizes, + media: descriptor.media, + }; +} + +function resolveFormatDetection(formatDetection: Metadata['formatDetection']): string | undefined { + if (!formatDetection) return undefined; + + const parts: string[] = []; + if (formatDetection.telephone != null) { + parts.push(`telephone=${formatDetection.telephone ? 'yes' : 'no'}`); + } + if (formatDetection.date != null) { + parts.push(`date=${formatDetection.date ? 'yes' : 'no'}`); + } + if (formatDetection.address != null) { + parts.push(`address=${formatDetection.address ? 'yes' : 'no'}`); + } + if (formatDetection.email != null) { + parts.push(`email=${formatDetection.email ? 'yes' : 'no'}`); + } + if (formatDetection.url != null) { + parts.push(`url=${formatDetection.url ? 'yes' : 'no'}`); + } + + if (parts.length === 0) { + return undefined; + } + + return parts.join(', '); +} + +function resolveItunes(itunes: Metadata['itunes']): string | undefined { + if (!itunes) return undefined; + + const parts = [`app-id=${itunes.appId}`]; + if (itunes.affiliateData) { + parts.push(`affiliate-data=${itunes.affiliateData}`); + } + if (itunes.appArgument) { + parts.push(`app-argument=${itunes.appArgument}`); + } + + return parts.join(', '); +} + +function resolveFacebook(facebook: Metadata['facebook']): ResolvedFacebook | undefined { + if (!facebook) return undefined; + + return { + appId: facebook.appId, + admins: normalizeArray(facebook.admins), + }; +} + +function resolvePinterest(pinterest: Metadata['pinterest']): 'true' | 'false' | undefined { + if (!pinterest || pinterest.richPin == null) return undefined; + return pinterest.richPin ? 'true' : 'false'; +} + +function resolveAppLinks(appLinks: Metadata['appLinks']): ResolvedAppLinks | undefined { + if (!appLinks) return undefined; + + const ios = + appLinks.ios && (appLinks.ios.url || appLinks.ios.appStoreId || appLinks.ios.appName) + ? { + url: appLinks.ios.url, + appStoreId: appLinks.ios.appStoreId, + appName: appLinks.ios.appName, + } + : undefined; + + const android = + appLinks.android && + (appLinks.android.url || appLinks.android.package || appLinks.android.appName) + ? { + url: appLinks.android.url, + package: appLinks.android.package, + appName: appLinks.android.appName, + } + : undefined; + + const web = + appLinks.web && (appLinks.web.url || appLinks.web.shouldFallback != null) + ? { + url: appLinks.web.url, + shouldFallback: + appLinks.web.shouldFallback != null + ? appLinks.web.shouldFallback + ? ('true' as const) + : ('false' as const) + : undefined, + } + : undefined; + + return { + ios, + android, + web, + }; +} + +function serializeRobotsValue( + robots: string | Record | null | undefined +): string | undefined { + if (!robots) return undefined; + if (typeof robots === 'string') return robots; + + const values: string[] = []; + + for (const [key, value] of Object.entries(robots)) { + if (key === 'googleBot' || value == null || typeof value === 'object') continue; + if (typeof value === 'boolean') { + if (NEGATIVE_ROBOT_DIRECTIVES.has(key)) { + if (value) { + values.push(key); + } + continue; + } + + values.push(value ? key : `no${key}`); + continue; + } + + values.push(`${key}:${String(value)}`); + } + + if (values.length === 0) { + return undefined; + } + + return values.join(', '); +} + +function normalizeArray(items: T | T[] | undefined): T[] { + if (!items) return []; + return Array.isArray(items) ? items : [items]; +} + +function normalizeMetadataImages( + images: MetadataImage | MetadataImage[] | undefined +): MetadataImage[] { + return normalizeArray(images); +} + +function normalizeOtherValue( + value: MetadataValue | MetadataValueArray | null | undefined +): string[] { + if (value == null) return []; + return normalizeArray(value).map((entry) => String(entry)); +} + +function pushResolvedName( + fields: { name: string; content: string }[], + name: string, + content: string | undefined +) { + if (!content) return; + fields.push({ name, content }); +} + +function pushResolvedProperty( + fields: { property: string; content: string }[], + property: string, + content: string | undefined +) { + if (!content) return; + fields.push({ property, content }); +} diff --git a/packages/@expo/router-server/src/utils/metadata/serialize.ts b/packages/@expo/router-server/src/utils/metadata/serialize.ts new file mode 100644 index 00000000000000..0536aedde5cf5b --- /dev/null +++ b/packages/@expo/router-server/src/utils/metadata/serialize.ts @@ -0,0 +1,12 @@ +import type { Metadata } from 'expo-server'; + +import { renderMetadataHtml, renderMetadataTags } from './render'; +import { resolveMetadata } from './resolve'; + +export function serializeMetadataToTags(metadata: Metadata) { + return renderMetadataTags(resolveMetadata(metadata)); +} + +export function serializeMetadataToHtml(metadata: Metadata): string { + return renderMetadataHtml(renderMetadataTags(resolveMetadata(metadata))); +} diff --git a/packages/@expo/router-server/src/utils/metadata/tag.ts b/packages/@expo/router-server/src/utils/metadata/tag.ts new file mode 100644 index 00000000000000..605c147e3646a5 --- /dev/null +++ b/packages/@expo/router-server/src/utils/metadata/tag.ts @@ -0,0 +1,68 @@ +import type { MetadataTag } from './types'; + +export function renderMetadataTag(tag: MetadataTag): string { + const attributes = tag.attributes + ? ' ' + + Object.entries(tag.attributes) + .filter(([, value]) => value != null && value !== '') + .map(([key, value]) => `${key}="${escapeHtmlAttributeValue(value)}"`) + .join(' ') + : ''; + + if (tag.tagName === 'title') { + return `${escapeHtmlTextNode(tag.content ?? '')}`; + } + + return `<${tag.tagName}${attributes}>`; +} + +export function pushName(tags: MetadataTag[], key: string, value: string | undefined) { + pushMetaContent(tags, 'name', key, value); +} + +export function pushProperty(tags: MetadataTag[], key: string, value: string | undefined) { + pushMetaContent(tags, 'property', key, value); +} + +export function pushLink(tags: MetadataTag[], attributes: Record) { + const normalizedAttributes = Object.fromEntries( + Object.entries(attributes).filter(([, value]) => value != null && value !== '') + ) as Record; + + if (Object.keys(normalizedAttributes).length === 0) { + return; + } + + tags.push({ + tagName: 'link', + attributes: normalizedAttributes, + }); +} + +function escapeHtmlAttributeValue(value: string): string { + return value + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); +} + +function escapeHtmlTextNode(value: string): string { + return value.replace(/&/g, '&').replace(//g, '>'); +} + +function pushMetaContent( + tags: MetadataTag[], + name: 'name' | 'property', + key: string, + value: string | undefined +) { + if (!value) return; + tags.push({ + tagName: 'meta', + attributes: { + [name]: key, + content: value, + }, + }); +} diff --git a/packages/@expo/router-server/src/utils/metadata/types.ts b/packages/@expo/router-server/src/utils/metadata/types.ts new file mode 100644 index 00000000000000..d7385d0d386fa3 --- /dev/null +++ b/packages/@expo/router-server/src/utils/metadata/types.ts @@ -0,0 +1,175 @@ +import type { + Metadata, + MetadataIconDescriptor, + MetadataImage, + MetadataValue, + MetadataValueArray, +} from 'expo-server'; + +export type MetadataTag = { + tagName: 'title' | 'meta' | 'link'; + attributes?: Record; + content?: string; +}; + +type ArrayElement = T extends readonly (infer U)[] ? U : T; + +export type MetadataAuthor = ArrayElement>; +export type MetadataAudio = ArrayElement['audio']>>; +export type MetadataVideo = ArrayElement['videos']>>; +export type MetadataTwitterImage = ArrayElement< + NonNullable['images']> +>; +export type MetadataTwitterPlayer = ArrayElement< + NonNullable['players']> +>; +export type MetadataTwitterApp = NonNullable['app']>; +export type ResolvedAuthor = { name?: string; url?: string }; + +export type ResolvedAlternates = { + canonical?: string; + languages: { href: string; hrefLang: string }[]; + media: { href: string; media: string }[]; + types: { href: string; type: string }[]; +}; + +export type ResolvedOpenGraphImage = { + url: string; + alt?: string; + width?: string; + height?: string; + type?: string; + secureUrl?: string; +}; + +export type ResolvedOpenGraphVideo = { + url: string; + secureUrl?: string; + type?: string; + width?: string; + height?: string; +}; + +export type ResolvedOpenGraphAudio = { + url: string; + secureUrl?: string; + type?: string; +}; + +export type ResolvedOpenGraph = { + basic: { property: string; content: string }[]; + images: ResolvedOpenGraphImage[]; + videos: ResolvedOpenGraphVideo[]; + audio: ResolvedOpenGraphAudio[]; + article: { property: string; content: string }[]; +}; + +export type ResolvedTwitterImage = { + url: string; + alt?: string; +}; + +export type ResolvedTwitterPlayer = { + url: string; + width?: string; + height?: string; + stream?: string; +}; + +export type ResolvedTwitterAppEntry = { + platform: 'iphone' | 'ipad' | 'googleplay'; + name?: string; + id?: string; + url?: string; +}; + +export type ResolvedTwitter = { + basic: { name: string; content: string }[]; + images: ResolvedTwitterImage[]; + players: ResolvedTwitterPlayer[]; + app: ResolvedTwitterAppEntry[]; +}; + +export type ResolvedIcon = { + rel: string; + href: string; + type?: string; + sizes?: string; + media?: string; +}; + +export type ResolvedIcons = { + icon: ResolvedIcon[]; + shortcut: ResolvedIcon[]; + apple: ResolvedIcon[]; + other: ResolvedIcon[]; +}; + +export type ResolvedVerification = { + google: string[]; + yahoo: string[]; + yandex: string[]; + other: { name: string; content: string }[]; +}; + +export type ResolvedAppleWebApp = { + capable: 'yes'; + title?: string; + statusBarStyle?: string; + startupImages: { href: string; media?: string }[]; +}; + +export type ResolvedFacebook = { + appId?: string; + admins: string[]; +}; + +export type ResolvedAppLinks = { + ios?: { + url?: string; + appStoreId?: string; + appName?: string; + }; + android?: { + url?: string; + package?: string; + appName?: string; + }; + web?: { + url?: string; + shouldFallback?: 'true' | 'false'; + }; +}; + +export type ResolvedMetadata = { + title?: string; + description?: string; + applicationName?: string; + keywords?: string; + generator?: string; + referrer?: string; + authors: ResolvedAuthor[]; + creator?: string; + publisher?: string; + category?: string; + robots?: string; + googleBot?: string; + alternates?: ResolvedAlternates; + openGraph?: ResolvedOpenGraph; + twitter?: ResolvedTwitter; + icons?: ResolvedIcons; + formatDetection?: string; + verification?: ResolvedVerification; + appleWebApp?: ResolvedAppleWebApp; + itunes?: string; + facebook?: ResolvedFacebook; + pinterest?: 'true' | 'false'; + appLinks?: ResolvedAppLinks; + archives: string[]; + assets: string[]; + bookmarks: string[]; + manifest?: string; + other: { name: string; content: string }[]; +}; + +export type { Metadata, MetadataIconDescriptor, MetadataImage, MetadataValue, MetadataValueArray }; diff --git a/packages/@expo/router-server/src/utils/streams.ts b/packages/@expo/router-server/src/utils/streams.ts new file mode 100644 index 00000000000000..76a5f036ee3e2b --- /dev/null +++ b/packages/@expo/router-server/src/utils/streams.ts @@ -0,0 +1,89 @@ +const HEAD_CLOSE_TAG = ''; +const BODY_OPEN_TAG = '`; + if (html.includes(bareTag)) { + return html.replace(bareTag, `<${tagName} ${attributes}>`); + } + + return html.replace(`<${tagName} `, `<${tagName} ${attributes} `); +} + +/** + * Buffers the initial HTML document prefix, injects head content plus any serialized document + * attributes, then switches to passthrough mode for the rest of the stream. + */ +export function createDocumentMetadataInjectionTransform( + options: HeadInjectionOptions +): TransformStream { + let buffer = ''; + let injected = false; + const decoder = new TextDecoder(); + const encoder = new TextEncoder(); + const injection = options.injectionParts.join(''); + + return new TransformStream({ + transform(chunk, controller): void { + const decodedChunk = decoder.decode(chunk, { stream: true }); + + if (injected) { + controller.enqueue(encoder.encode(decodedChunk)); + return; + } + + buffer += decodedChunk; + + const headCloseIdx = buffer.indexOf(HEAD_CLOSE_TAG); + const bodyOpenIdx = options.bodyAttributes ? buffer.indexOf(BODY_OPEN_TAG) : 0; + const hasRequiredDocumentPrefix = headCloseIdx !== -1 && bodyOpenIdx !== -1; + if (hasRequiredDocumentPrefix) { + const bodyOpenTagEndIdx = options.bodyAttributes + ? buffer.indexOf('>', bodyOpenIdx) + : headCloseIdx + HEAD_CLOSE_TAG.length; + + if (bodyOpenTagEndIdx === -1) { + return; + } + + let before = buffer.slice(0, headCloseIdx); + const afterHead = buffer.slice(headCloseIdx, bodyOpenTagEndIdx + 1); + const remainder = buffer.slice(bodyOpenTagEndIdx + 1); + before = injectTagAttributes(before, 'html', options.htmlAttributes); + const documentPrefix = injectTagAttributes(afterHead, 'body', options.bodyAttributes); + injected = true; + buffer = ''; + controller.enqueue(encoder.encode(before + injection + documentPrefix + remainder)); + } + }, + + flush(controller): void { + const trailing = decoder.decode(); + if (trailing) { + if (injected) { + controller.enqueue(encoder.encode(trailing)); + return; + } + buffer += trailing; + } + + if (!injected) { + controller.error( + new Error( + `Streaming SSR head injection failed: missing ${HEAD_CLOSE_TAG} in HTML output.` + ) + ); + } + }, + }); +} diff --git a/packages/babel-preset-expo/CHANGELOG.md b/packages/babel-preset-expo/CHANGELOG.md index 4c65b30678a077..9dac7a771e42bc 100644 --- a/packages/babel-preset-expo/CHANGELOG.md +++ b/packages/babel-preset-expo/CHANGELOG.md @@ -8,6 +8,8 @@ ### 🎉 New features +- Add support for metadata in streaming SSR ([#44731](https://github.com/expo/expo/pull/44731) by [@hassankhan](https://github.com/hassankhan)) + ### 🐛 Bug fixes - Opt `"widget"` functions for `expo-widgets` out of react-compiler ([#43451](https://github.com/expo/expo/pull/43451) by [@kitten](https://github.com/kitten)) diff --git a/packages/babel-preset-expo/build/index.js b/packages/babel-preset-expo/build/index.js index b5a536409b80ab..d394223a349683 100644 --- a/packages/babel-preset-expo/build/index.js +++ b/packages/babel-preset-expo/build/index.js @@ -11,6 +11,7 @@ const lazyImports_1 = require("./lazyImports"); const restricted_react_api_plugin_1 = require("./restricted-react-api-plugin"); const server_actions_plugin_1 = require("./server-actions-plugin"); const server_data_loaders_plugin_1 = require("./server-data-loaders-plugin"); +const server_metadata_plugin_1 = require("./server-metadata-plugin"); const use_dom_directive_plugin_1 = require("./use-dom-directive-plugin"); const resolveModule_1 = require("./utils/resolveModule"); const widgets_plugin_1 = require("./widgets-plugin"); @@ -187,6 +188,7 @@ function babelPresetExpo(api, options = {}) { } if ((0, resolveModule_1.hasModule)(api, 'expo-router/package.json')) { extraPlugins.push(expo_router_plugin_1.expoRouterBabelPlugin); + extraPlugins.push(server_metadata_plugin_1.serverMetadataPlugin); // Process `loader()` functions for client, loader and server bundles (excluding RSC) // - Client bundles: Remove loader exports, they run on server only // - Server bundles: Keep loader exports (needed for SSG) diff --git a/packages/babel-preset-expo/build/index.js.map b/packages/babel-preset-expo/build/index.js.map index d106315c482c50..2b58741b191f48 100644 --- a/packages/babel-preset-expo/build/index.js.map +++ b/packages/babel-preset-expo/build/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/babel-preset-expo/build/server-metadata-plugin.d.ts b/packages/babel-preset-expo/build/server-metadata-plugin.d.ts new file mode 100644 index 00000000000000..b8a6373d062c82 --- /dev/null +++ b/packages/babel-preset-expo/build/server-metadata-plugin.d.ts @@ -0,0 +1,8 @@ +/** + * Copyright © 2026 650 Industries. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import type { ConfigAPI, PluginObj } from '@babel/core'; +export declare function serverMetadataPlugin(api: ConfigAPI & typeof import('@babel/core')): PluginObj; diff --git a/packages/babel-preset-expo/build/server-metadata-plugin.js b/packages/babel-preset-expo/build/server-metadata-plugin.js new file mode 100644 index 00000000000000..4b58cfb7635755 --- /dev/null +++ b/packages/babel-preset-expo/build/server-metadata-plugin.js @@ -0,0 +1,71 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.serverMetadataPlugin = serverMetadataPlugin; +const common_1 = require("./common"); +const debug = require('debug')('expo:babel:server-metadata'); +const GENERATE_METADATA_EXPORT_NAME = 'generateMetadata'; +function serverMetadataPlugin(api) { + const { types: t } = api; + const routerAbsoluteRoot = api.caller(common_1.getExpoRouterAbsoluteAppRoot); + const isServer = api.caller(common_1.getIsServer); + return { + name: 'expo-server-metadata', + visitor: { + ExportNamedDeclaration(path, state) { + if (!isInAppDirectory(state.file.opts.filename ?? '', routerAbsoluteRoot)) { + return; + } + if (isServer) { + return; + } + const { declaration, specifiers } = path.node; + const isTypeExport = path.node.exportKind === 'type'; + const hasSpecifiers = specifiers.length > 0; + if (isTypeExport || hasSpecifiers) { + return; + } + if (t.isFunctionDeclaration(declaration)) { + const name = declaration.id?.name; + if (name && isGenerateMetadataIdentifier(name)) { + debug('Removing generateMetadata function declaration from client bundle'); + markForConstantFolding(state); + path.remove(); + } + } + if (t.isVariableDeclaration(declaration)) { + const nextDeclarations = declaration.declarations.filter((declarator) => { + const name = t.isIdentifier(declarator.id) ? declarator.id.name : null; + return !name || !isGenerateMetadataIdentifier(name); + }); + if (nextDeclarations.length !== declaration.declarations.length) { + debug('Removing generateMetadata variable declaration from client bundle'); + markForConstantFolding(state); + declaration.declarations = nextDeclarations; + if (declaration.declarations.length === 0) { + path.remove(); + } + } + } + }, + }, + }; +} +function isGenerateMetadataIdentifier(name) { + return name === GENERATE_METADATA_EXPORT_NAME; +} +function isInAppDirectory(filePath, routerRoot) { + const normalizedFilePath = (0, common_1.toPosixPath)(filePath); + const normalizedAppRoot = (0, common_1.toPosixPath)(routerRoot); + return normalizedFilePath.startsWith(normalizedAppRoot + '/'); +} +function assertExpoMetadata(metadata) { + if (metadata && typeof metadata === 'object') { + return; + } + throw new Error('Expected Babel state.file.metadata to be an object'); +} +function markForConstantFolding(state) { + assertExpoMetadata(state.file.metadata); + state.file.metadata.performConstantFolding = true; +} +//# sourceMappingURL=server-metadata-plugin.js.map \ No newline at end of file diff --git a/packages/babel-preset-expo/build/server-metadata-plugin.js.map b/packages/babel-preset-expo/build/server-metadata-plugin.js.map new file mode 100644 index 00000000000000..f1938b7ee45f66 --- /dev/null +++ b/packages/babel-preset-expo/build/server-metadata-plugin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"server-metadata-plugin.js","sourceRoot":"","sources":["../src/server-metadata-plugin.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/babel-preset-expo/src/__tests__/server-metadata-plugin.test.ts b/packages/babel-preset-expo/src/__tests__/server-metadata-plugin.test.ts new file mode 100644 index 00000000000000..e58a3de340f435 --- /dev/null +++ b/packages/babel-preset-expo/src/__tests__/server-metadata-plugin.test.ts @@ -0,0 +1,202 @@ +/** + * Copyright © 2026 650 Industries. + */ + +import * as babel from '@babel/core'; + +import preset from '..'; + +const CLIENT_CALLER = { + name: 'metro', + isDev: false, + isServer: false, + platform: 'web', + projectRoot: '/', + supportsStaticESM: true, +}; + +const SERVER_CALLER = { + name: 'metro', + isDev: false, + isServer: true, + platform: 'web', + projectRoot: '/', + supportsStaticESM: true, +}; + +function transformTest( + code: string, + caller: babel.TransformCaller, + options: { filename?: string } = {} +) { + const result = babel.transform(code, { + filename: options.filename ?? '/app/index', + babelrc: false, + configFile: false, + presets: [preset], + comments: true, + compact: false, + caller, + }); + + if (!result) { + throw new Error('Failed to transform code'); + } + + return { + code: result.code ?? '', + metadata: result.metadata as { + performConstantFolding?: boolean; + }, + }; +} + +describe('serverMetadataPlugin', () => { + it('preserves `generateMetadata()` exports in server bundles', () => { + const result = transformTest( + ` + export async function generateMetadata() { + return { title: 'Hello' }; + } + + export default function Index() { + return
Hello
; + } + `, + SERVER_CALLER as babel.TransformCaller + ); + + expect(result.metadata.performConstantFolding).toBeUndefined(); + expect(result.code).toMatchInlineSnapshot(` + "import { jsx as _jsx } from "react/jsx-runtime"; + export async function generateMetadata() { + return { + title: 'Hello' + }; + } + export default function Index() { + return /*#__PURE__*/_jsx("div", { + children: "Hello" + }); + }" + `); + }); + + it('removes `generateMetadata()` exports from client bundles', () => { + const result = transformTest( + ` + export async function generateMetadata() { + return { title: 'Hello' }; + } + + export default function Index() { + return
Hello
; + } + `, + CLIENT_CALLER as babel.TransformCaller + ); + + expect(result.metadata.performConstantFolding).toBe(true); + expect(result.code).not.toContain('generateMetadata'); + expect(result.code).toMatchInlineSnapshot(` + "import { jsx as _jsx } from "react/jsx-runtime"; + export default function Index() { + return /*#__PURE__*/_jsx("div", { + children: "Hello" + }); + }" + `); + }); + + it('removes `generateMetadata()` variable exports from client bundles', () => { + const result = transformTest( + ` + export const generateMetadata = async () => ({ title: 'Hello' }); + + export default function Index() { + return
Hello
; + } + `, + CLIENT_CALLER as babel.TransformCaller + ); + + expect(result.metadata.performConstantFolding).toBe(true); + expect(result.code).toMatchInlineSnapshot(` + "import { jsx as _jsx } from "react/jsx-runtime"; + export default function Index() { + return /*#__PURE__*/_jsx("div", { + children: "Hello" + }); + }" + `); + }); + + it('removes `generateMetadata()` exports from mixed variable exports in client bundles', () => { + const result = transformTest( + ` + export const generateMetadata = async () => ({ title: 'Hello' }), routeValue = 'client'; + + export default function Index() { + return
{routeValue}
; + } + `, + CLIENT_CALLER as babel.TransformCaller + ); + + expect(result.metadata.performConstantFolding).toBe(true); + expect(result.code).not.toContain('generateMetadata'); + expect(result.code).toMatchInlineSnapshot(` + "import { jsx as _jsx } from "react/jsx-runtime"; + export const routeValue = 'client'; + export default function Index() { + return /*#__PURE__*/_jsx("div", { + children: routeValue + }); + }" + `); + }); + + it('preserves non-metadata exports in client bundles', () => { + const result = transformTest( + ` + export const title = 'Hello'; + export default function Index() { + return
{title}
; + } + `, + CLIENT_CALLER as babel.TransformCaller + ); + + expect(result.metadata.performConstantFolding).toBeUndefined(); + expect(result.code).toMatchInlineSnapshot(` + "import { jsx as _jsx } from "react/jsx-runtime"; + export const title = 'Hello'; + export default function Index() { + return /*#__PURE__*/_jsx("div", { + children: title + }); + }" + `); + }); + + it('does not modify files outside the app directory', () => { + const result = transformTest( + ` + export async function generateMetadata() { + return { title: 'Hello' }; + } + `, + CLIENT_CALLER as babel.TransformCaller, + { filename: '/components/metadata' } + ); + + expect(result.metadata.performConstantFolding).toBeUndefined(); + expect(result.code).toMatchInlineSnapshot(` + "export async function generateMetadata() { + return { + title: 'Hello' + }; + }" + `); + }); +}); diff --git a/packages/babel-preset-expo/src/index.ts b/packages/babel-preset-expo/src/index.ts index cfdc3357e0b2d2..ffa24bb38163ad 100644 --- a/packages/babel-preset-expo/src/index.ts +++ b/packages/babel-preset-expo/src/index.ts @@ -27,6 +27,7 @@ import { lazyImports } from './lazyImports'; import { environmentRestrictedReactAPIsPlugin } from './restricted-react-api-plugin'; import { reactServerActionsPlugin } from './server-actions-plugin'; import { serverDataLoadersPlugin } from './server-data-loaders-plugin'; +import { serverMetadataPlugin } from './server-metadata-plugin'; import { expoUseDomDirectivePlugin } from './use-dom-directive-plugin'; import { hasModule, resolveModule } from './utils/resolveModule'; import { widgetsPlugin } from './widgets-plugin'; @@ -295,6 +296,7 @@ function babelPresetExpo(api: ConfigAPI, options: BabelPresetExpoOptions = {}): if (hasModule(api, 'expo-router/package.json')) { extraPlugins.push(expoRouterBabelPlugin); + extraPlugins.push(serverMetadataPlugin); // Process `loader()` functions for client, loader and server bundles (excluding RSC) // - Client bundles: Remove loader exports, they run on server only // - Server bundles: Keep loader exports (needed for SSG) diff --git a/packages/babel-preset-expo/src/server-metadata-plugin.ts b/packages/babel-preset-expo/src/server-metadata-plugin.ts new file mode 100644 index 00000000000000..36335a93835b10 --- /dev/null +++ b/packages/babel-preset-expo/src/server-metadata-plugin.ts @@ -0,0 +1,95 @@ +/** + * Copyright © 2026 650 Industries. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import type { ConfigAPI, types as t, PluginObj, PluginPass } from '@babel/core'; + +import { getExpoRouterAbsoluteAppRoot, getIsServer, toPosixPath } from './common'; + +const debug = require('debug')('expo:babel:server-metadata'); + +const GENERATE_METADATA_EXPORT_NAME = 'generateMetadata'; + +export function serverMetadataPlugin(api: ConfigAPI & typeof import('@babel/core')): PluginObj { + const { types: t } = api; + + const routerAbsoluteRoot = api.caller(getExpoRouterAbsoluteAppRoot); + const isServer = api.caller(getIsServer); + + return { + name: 'expo-server-metadata', + visitor: { + ExportNamedDeclaration(path, state) { + if (!isInAppDirectory(state.file.opts.filename ?? '', routerAbsoluteRoot)) { + return; + } + + if (isServer) { + return; + } + + const { declaration, specifiers } = path.node; + const isTypeExport = path.node.exportKind === 'type'; + const hasSpecifiers = specifiers.length > 0; + + if (isTypeExport || hasSpecifiers) { + return; + } + + if (t.isFunctionDeclaration(declaration)) { + const name = declaration.id?.name; + if (name && isGenerateMetadataIdentifier(name)) { + debug('Removing generateMetadata function declaration from client bundle'); + markForConstantFolding(state); + path.remove(); + } + } + + if (t.isVariableDeclaration(declaration)) { + const nextDeclarations = declaration.declarations.filter( + (declarator: t.VariableDeclarator) => { + const name = t.isIdentifier(declarator.id) ? declarator.id.name : null; + return !name || !isGenerateMetadataIdentifier(name); + } + ); + + if (nextDeclarations.length !== declaration.declarations.length) { + debug('Removing generateMetadata variable declaration from client bundle'); + markForConstantFolding(state); + declaration.declarations = nextDeclarations; + + if (declaration.declarations.length === 0) { + path.remove(); + } + } + } + }, + }, + }; +} + +function isGenerateMetadataIdentifier(name: string): boolean { + return name === GENERATE_METADATA_EXPORT_NAME; +} + +function isInAppDirectory(filePath: string, routerRoot: string) { + const normalizedFilePath = toPosixPath(filePath); + const normalizedAppRoot = toPosixPath(routerRoot); + return normalizedFilePath.startsWith(normalizedAppRoot + '/'); +} + +function assertExpoMetadata(metadata: any): asserts metadata is { + performConstantFolding?: boolean; +} { + if (metadata && typeof metadata === 'object') { + return; + } + throw new Error('Expected Babel state.file.metadata to be an object'); +} + +function markForConstantFolding(state: PluginPass) { + assertExpoMetadata(state.file.metadata); + state.file.metadata.performConstantFolding = true; +} diff --git a/packages/expo-router/CHANGELOG.md b/packages/expo-router/CHANGELOG.md index 14bdb6fcf83f03..c2ec70476884e3 100644 --- a/packages/expo-router/CHANGELOG.md +++ b/packages/expo-router/CHANGELOG.md @@ -20,6 +20,8 @@ - Support `ColorValue`. ([#44301](https://github.com/expo/expo/pull/44301) by [@jakex7](https://github.com/jakex7)) - Allow customizing route-level `` fallback ([#43885](https://github.com/expo/expo/pull/43885) by [@hassankhan](https://github.com/hassankhan)) - Upgrade react-native-screens version to 4.25.0-beta.1 ([#45172](https://github.com/expo/expo/pull/45172) by [@Ubax](https://github.com/Ubax)) +- [web] Use stream rendering in SSR ([#43963](https://github.com/expo/expo/pull/43963) by [@hassankhan](https://github.com/hassankhan)) +- [web] Add support for metadata in streaming SSR ([#44731](https://github.com/expo/expo/pull/44731) by [@hassankhan](https://github.com/hassankhan)) ### 🐛 Bug fixes diff --git a/packages/expo-router/build/Route.d.ts b/packages/expo-router/build/Route.d.ts index aa32d98e5dc527..70e1f7fc9375f6 100644 --- a/packages/expo-router/build/Route.d.ts +++ b/packages/expo-router/build/Route.d.ts @@ -1,4 +1,4 @@ -import type { LoaderFunction } from 'expo-server'; +import type { GenerateMetadataFunction, LoaderFunction } from 'expo-server'; import { type ComponentType, type PropsWithChildren } from 'react'; import { sortRoutesWithInitial, sortRoutes } from './sortRoutes'; import type { SuspenseFallbackProps } from './views/SuspenseFallback'; @@ -19,6 +19,7 @@ export type LoadedRoute = { params?: Params; }) => Params[]; loader?: LoaderFunction; + generateMetadata?: GenerateMetadataFunction; }; export type LoadedMiddleware = Pick; export type MiddlewareNode = { diff --git a/packages/expo-router/build/Route.d.ts.map b/packages/expo-router/build/Route.d.ts.map index 28c8bbdf43e50b..921fa40f988af5 100644 --- a/packages/expo-router/build/Route.d.ts.map +++ b/packages/expo-router/build/Route.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"Route.d.ts","sourceRoot":"","sources":["../src/Route.tsx"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Route.d.ts","sourceRoot":"","sources":["../src/Route.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/expo-router/build/Route.js.map b/packages/expo-router/build/Route.js.map index d225c430bbd68d..60c641c84e5a61 100644 --- a/packages/expo-router/build/Route.js.map +++ b/packages/expo-router/build/Route.js.map @@ -1 +1 @@ -{"version":3,"file":"Route.js","sourceRoot":"","sources":["../src/Route.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;AA8Eb,oCAEC;AAED,sCAMC;AAQD,sBAMC;;AAnGD,iCAAuF;AAEvF,yCAA2C;AAC3C,6CAAiE;AAkGxD,sGAlGA,kCAAqB,OAkGA;AAAE,2FAlGA,uBAAU,OAkGA;AAtC1C,MAAM,mBAAmB,GAAG,IAAA,qBAAa,EAAmB,IAAI,CAAC,CAAC;AAClE,+FAA+F;AAClF,QAAA,uBAAuB,GAAG,IAAA,qBAAa,EAElD,SAAS,CAAC,CAAC;AACA,QAAA,uBAAuB,GAAG,IAAA,qBAAa,EAAqB,EAAE,CAAC,CAAC;AAE7E,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;IAC1C,mBAAmB,CAAC,WAAW,GAAG,WAAW,CAAC;AAChD,CAAC;AAED,+DAA+D;AAC/D,SAAgB,YAAY;IAC1B,OAAO,IAAA,WAAG,EAAC,mBAAmB,CAAC,CAAC;AAClC,CAAC;AAED,SAAgB,aAAa;IAC3B,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;IAC5B,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,IAAA,wBAAa,EAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AACxC,CAAC;AAOD,iEAAiE;AACjE,SAAgB,KAAK,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAc;IAC1D,OAAO,CACL,uBAAC,+BAAuB,CAAC,QAAQ,IAAC,KAAK,EAAE,MAAM,YAC7C,uBAAC,mBAAmB,CAAC,QAAQ,IAAC,KAAK,EAAE,IAAI,YAAG,QAAQ,GAAgC,GACnD,CACpC,CAAC;AACJ,CAAC","sourcesContent":["'use client';\n\nimport type { LoaderFunction } from 'expo-server';\nimport { createContext, use, type ComponentType, type PropsWithChildren } from 'react';\n\nimport { getContextKey } from './matchers';\nimport { sortRoutesWithInitial, sortRoutes } from './sortRoutes';\nimport type { SuspenseFallbackProps } from './views/SuspenseFallback';\nimport type { ErrorBoundaryProps } from './views/Try';\n\nexport type DynamicConvention = { name: string; deep: boolean; notFound?: boolean };\n\ntype Params = Record;\n\nexport type LoadedRoute = {\n ErrorBoundary?: ComponentType;\n SuspenseFallback?: ComponentType;\n default?: ComponentType;\n unstable_settings?: Record;\n getNavOptions?: (args: any) => any;\n generateStaticParams?: (props: { params?: Params }) => Params[];\n loader?: LoaderFunction;\n};\n\nexport type LoadedMiddleware = Pick;\n\nexport type MiddlewareNode = {\n /** Context Module ID. Used to resolve the middleware module */\n contextKey: string;\n /** Loads middleware into memory. Returns the exports from +middleware.ts */\n loadRoute: () => Partial;\n};\n\nexport type RouteNode = {\n /** The type of RouteNode */\n type: 'route' | 'api' | 'layout' | 'redirect' | 'rewrite';\n /** Load a route into memory. Returns the exports from a route. */\n loadRoute: () => Partial;\n /** Loaded initial route name. */\n initialRouteName?: string;\n /** Nested routes */\n children: RouteNode[];\n /** Is the route a dynamic path */\n dynamic: null | DynamicConvention[];\n /** `index`, `error-boundary`, etc. Relative to the nearest `_layout.tsx` */\n route: string;\n /** Context Module ID, used for matching children. */\n contextKey: string;\n /** Redirect Context Module ID, used for matching children. */\n destinationContextKey?: string;\n /** Parent Context Module ID, used for matching static routes to their parent dynamic route. */\n parentContextKey?: string;\n /** Is the redirect permanent. */\n permanent?: boolean;\n /** Added in-memory */\n generated?: boolean;\n /** Internal screens like the directory or the auto 404 should be marked as internal. */\n internal?: boolean;\n /** File paths for async entry modules that should be included in the initial chunk request to ensure the runtime JavaScript matches the statically rendered HTML representation. */\n entryPoints?: string[];\n /** HTTP methods for this route. If undefined, assumed to be ['GET'] */\n methods?: string[];\n /** Middleware function for server-side request processing. Only present on the root route node. */\n middleware?: MiddlewareNode;\n};\n\nconst CurrentRouteContext = createContext(null);\n/** This context allows a `_layout.tsx` to provide a Suspense fallback for its child routes. */\nexport const SuspenseFallbackContext = createContext<\n ComponentType | undefined\n>(undefined);\nexport const LocalRouteParamsContext = createContext({});\n\nif (process.env.NODE_ENV !== 'production') {\n CurrentRouteContext.displayName = 'RouteNode';\n}\n\n/** Return the RouteNode at the current contextual boundary. */\nexport function useRouteNode(): RouteNode | null {\n return use(CurrentRouteContext);\n}\n\nexport function useContextKey(): string {\n const node = useRouteNode();\n if (node == null) {\n throw new Error('No filename found. This is likely a bug in expo-router.');\n }\n return getContextKey(node.contextKey);\n}\n\nexport type RouteProps = PropsWithChildren<{\n node: RouteNode;\n params: object | undefined;\n}>;\n\n/** Provides the matching routes and filename to the children. */\nexport function Route({ children, node, params }: RouteProps) {\n return (\n \n {children}\n \n );\n}\n\nexport { sortRoutesWithInitial, sortRoutes };\n"]} \ No newline at end of file +{"version":3,"file":"Route.js","sourceRoot":"","sources":["../src/Route.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;AA+Eb,oCAEC;AAED,sCAMC;AAQD,sBAMC;;AApGD,iCAAuF;AAEvF,yCAA2C;AAC3C,6CAAiE;AAmGxD,sGAnGA,kCAAqB,OAmGA;AAAE,2FAnGA,uBAAU,OAmGA;AAtC1C,MAAM,mBAAmB,GAAG,IAAA,qBAAa,EAAmB,IAAI,CAAC,CAAC;AAClE,+FAA+F;AAClF,QAAA,uBAAuB,GAAG,IAAA,qBAAa,EAElD,SAAS,CAAC,CAAC;AACA,QAAA,uBAAuB,GAAG,IAAA,qBAAa,EAAqB,EAAE,CAAC,CAAC;AAE7E,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;IAC1C,mBAAmB,CAAC,WAAW,GAAG,WAAW,CAAC;AAChD,CAAC;AAED,+DAA+D;AAC/D,SAAgB,YAAY;IAC1B,OAAO,IAAA,WAAG,EAAC,mBAAmB,CAAC,CAAC;AAClC,CAAC;AAED,SAAgB,aAAa;IAC3B,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;IAC5B,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,IAAA,wBAAa,EAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AACxC,CAAC;AAOD,iEAAiE;AACjE,SAAgB,KAAK,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAc;IAC1D,OAAO,CACL,uBAAC,+BAAuB,CAAC,QAAQ,IAAC,KAAK,EAAE,MAAM,YAC7C,uBAAC,mBAAmB,CAAC,QAAQ,IAAC,KAAK,EAAE,IAAI,YAAG,QAAQ,GAAgC,GACnD,CACpC,CAAC;AACJ,CAAC","sourcesContent":["'use client';\n\nimport type { GenerateMetadataFunction, LoaderFunction } from 'expo-server';\nimport { createContext, use, type ComponentType, type PropsWithChildren } from 'react';\n\nimport { getContextKey } from './matchers';\nimport { sortRoutesWithInitial, sortRoutes } from './sortRoutes';\nimport type { SuspenseFallbackProps } from './views/SuspenseFallback';\nimport type { ErrorBoundaryProps } from './views/Try';\n\nexport type DynamicConvention = { name: string; deep: boolean; notFound?: boolean };\n\ntype Params = Record;\n\nexport type LoadedRoute = {\n ErrorBoundary?: ComponentType;\n SuspenseFallback?: ComponentType;\n default?: ComponentType;\n unstable_settings?: Record;\n getNavOptions?: (args: any) => any;\n generateStaticParams?: (props: { params?: Params }) => Params[];\n loader?: LoaderFunction;\n generateMetadata?: GenerateMetadataFunction;\n};\n\nexport type LoadedMiddleware = Pick;\n\nexport type MiddlewareNode = {\n /** Context Module ID. Used to resolve the middleware module */\n contextKey: string;\n /** Loads middleware into memory. Returns the exports from +middleware.ts */\n loadRoute: () => Partial;\n};\n\nexport type RouteNode = {\n /** The type of RouteNode */\n type: 'route' | 'api' | 'layout' | 'redirect' | 'rewrite';\n /** Load a route into memory. Returns the exports from a route. */\n loadRoute: () => Partial;\n /** Loaded initial route name. */\n initialRouteName?: string;\n /** Nested routes */\n children: RouteNode[];\n /** Is the route a dynamic path */\n dynamic: null | DynamicConvention[];\n /** `index`, `error-boundary`, etc. Relative to the nearest `_layout.tsx` */\n route: string;\n /** Context Module ID, used for matching children. */\n contextKey: string;\n /** Redirect Context Module ID, used for matching children. */\n destinationContextKey?: string;\n /** Parent Context Module ID, used for matching static routes to their parent dynamic route. */\n parentContextKey?: string;\n /** Is the redirect permanent. */\n permanent?: boolean;\n /** Added in-memory */\n generated?: boolean;\n /** Internal screens like the directory or the auto 404 should be marked as internal. */\n internal?: boolean;\n /** File paths for async entry modules that should be included in the initial chunk request to ensure the runtime JavaScript matches the statically rendered HTML representation. */\n entryPoints?: string[];\n /** HTTP methods for this route. If undefined, assumed to be ['GET'] */\n methods?: string[];\n /** Middleware function for server-side request processing. Only present on the root route node. */\n middleware?: MiddlewareNode;\n};\n\nconst CurrentRouteContext = createContext(null);\n/** This context allows a `_layout.tsx` to provide a Suspense fallback for its child routes. */\nexport const SuspenseFallbackContext = createContext<\n ComponentType | undefined\n>(undefined);\nexport const LocalRouteParamsContext = createContext({});\n\nif (process.env.NODE_ENV !== 'production') {\n CurrentRouteContext.displayName = 'RouteNode';\n}\n\n/** Return the RouteNode at the current contextual boundary. */\nexport function useRouteNode(): RouteNode | null {\n return use(CurrentRouteContext);\n}\n\nexport function useContextKey(): string {\n const node = useRouteNode();\n if (node == null) {\n throw new Error('No filename found. This is likely a bug in expo-router.');\n }\n return getContextKey(node.contextKey);\n}\n\nexport type RouteProps = PropsWithChildren<{\n node: RouteNode;\n params: object | undefined;\n}>;\n\n/** Provides the matching routes and filename to the children. */\nexport function Route({ children, node, params }: RouteProps) {\n return (\n \n {children}\n \n );\n}\n\nexport { sortRoutesWithInitial, sortRoutes };\n"]} \ No newline at end of file diff --git a/packages/expo-router/build/getRoutesCore.d.ts.map b/packages/expo-router/build/getRoutesCore.d.ts.map index dbcfb60a94ea39..452c140d8d20f2 100644 --- a/packages/expo-router/build/getRoutesCore.d.ts.map +++ b/packages/expo-router/build/getRoutesCore.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"getRoutesCore.d.ts","sourceRoot":"","sources":["../src/getRoutesCore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAkB,SAAS,EAAE,MAAM,SAAS,CAAC;AAW5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,MAAM,MAAM,OAAO,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAE5C,2BAA2B,CAAC,EAAE,OAAO,CAAC;IAEtC,yGAAyG;IACzG,cAAc,EAAE,CACd,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG;QACzC,QAAQ,CAAC,EAAE,SAAS,CAAC;QACrB,cAAc,CAAC,EAAE,cAAc,CAAC;QAChC,aAAa,CAAC,EAAE,aAAa,CAAC;KAC/B,KACE,SAAS,CAAC;CAChB,CAAC;AAQF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAIF;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,aAAa,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAoB3F;AAwoBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,GAAG,GAAG,CAAC,MAAM,CAAC,CAwBzF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,GAAG,IAAI,CAgBxE"} \ No newline at end of file +{"version":3,"file":"getRoutesCore.d.ts","sourceRoot":"","sources":["../src/getRoutesCore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAkB,SAAS,EAAE,MAAM,SAAS,CAAC;AAW5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,MAAM,MAAM,OAAO,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAE5C,2BAA2B,CAAC,EAAE,OAAO,CAAC;IAEtC,yGAAyG;IACzG,cAAc,EAAE,CACd,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG;QACzC,QAAQ,CAAC,EAAE,SAAS,CAAC;QACrB,cAAc,CAAC,EAAE,cAAc,CAAC;QAChC,aAAa,CAAC,EAAE,aAAa,CAAC;KAC/B,KACE,SAAS,CAAC;CAChB,CAAC;AAQF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAIF;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,aAAa,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAoB3F;AA6oBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,GAAG,GAAG,CAAC,MAAM,CAAC,CAwBzF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,GAAG,IAAI,CAgBxE"} \ No newline at end of file diff --git a/packages/expo-router/build/getRoutesCore.js b/packages/expo-router/build/getRoutesCore.js index 3f53885052280c..aa64ebe12ac5d5 100644 --- a/packages/expo-router/build/getRoutesCore.js +++ b/packages/expo-router/build/getRoutesCore.js @@ -253,6 +253,10 @@ function getDirectoryTree(contextModule, options) { if (loaderExport && typeof loaderExport !== 'function') { throw new Error(`Route "${filePath}" exports a loader that is not a function.`); } + const metadataExport = routeModule?.generateMetadata; + if (metadataExport && typeof metadataExport !== 'function') { + throw new Error(`Route "${filePath}" exports generateMetadata that is not a function.`); + } } return routeModule; }, diff --git a/packages/expo-router/build/getRoutesCore.js.map b/packages/expo-router/build/getRoutesCore.js.map index c7f774c1ba7a2c..6d7e744c267cd0 100644 --- a/packages/expo-router/build/getRoutesCore.js.map +++ b/packages/expo-router/build/getRoutesCore.js.map @@ -1 +1 @@ -{"version":3,"file":"getRoutesCore.js","sourceRoot":"","sources":["../src/getRoutesCore.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DynamicConvention, MiddlewareNode, RouteNode } from './Route';\nimport {\n matchArrayGroupName,\n matchDynamicName,\n matchGroupName,\n matchLastGroupName,\n removeFileSystemDots,\n removeFileSystemExtensions,\n removeSupportedExtensions,\n stripInvisibleSegmentsFromPath,\n} from './matchers';\nimport type { RequireContext } from './types';\nimport { shouldLinkExternally } from './utils/url';\n\nexport type Options = {\n ignore?: RegExp[];\n preserveApiRoutes?: boolean;\n ignoreRequireErrors?: boolean;\n ignoreEntryPoints?: boolean;\n /* Used to simplify testing for toEqual() comparison */\n internal_stripLoadRoute?: boolean;\n /* Used to simplify by skipping the generated routes */\n skipGenerated?: boolean;\n /** Skip routes created by `generateStaticParams()` */\n skipStaticParams?: boolean;\n /* Skip the generated not found route */\n notFound?: boolean;\n /* Enable experimental server middleware support */\n unstable_useServerMiddleware?: boolean;\n importMode?: string;\n platformRoutes?: boolean;\n sitemap?: boolean;\n platform?: string;\n redirects?: RedirectConfig[];\n rewrites?: RewriteConfig[];\n headers?: Record;\n /* Keep redirects as valid routes within the RouteConfig tree */\n preserveRedirectAndRewrites?: boolean;\n\n /** Get the system route for a location. Useful for shimming React Native imports in SSR environments. */\n getSystemRoute: (\n route: Pick & {\n defaults?: RouteNode;\n redirectConfig?: RedirectConfig;\n rewriteConfig?: RewriteConfig;\n }\n ) => RouteNode;\n};\n\ntype DirectoryNode = {\n layout?: RouteNode[];\n files: Map;\n subdirectories: Map;\n};\n\nexport type RedirectConfig = {\n source: string;\n destination: string;\n destinationContextKey: string;\n permanent?: boolean;\n methods?: string[];\n external?: boolean;\n};\n\nexport type RewriteConfig = {\n source: string;\n destination: string;\n destinationContextKey: string;\n methods?: string[];\n};\n\nconst validPlatforms = new Set(['android', 'ios', 'native', 'web']);\n\n/**\n * Given a Metro context module, return an array of nested routes.\n *\n * This is a two step process:\n * 1. Convert the RequireContext keys (file paths) into a directory tree.\n * - This should extrapolate array syntax into multiple routes\n * - Routes are given a specificity score\n * 2. Flatten the directory tree into routes\n * - Routes in directories without _layout files are hoisted to the nearest _layout\n * - The name of the route is relative to the nearest _layout\n * - If multiple routes have the same name, the most specific route is used\n */\nexport function getRoutes(contextModule: RequireContext, options: Options): RouteNode | null {\n const middleware = getMiddleware(contextModule, options);\n const directoryTree = getDirectoryTree(contextModule, options);\n\n // If there are no routes\n if (!directoryTree) {\n return null;\n }\n\n const rootNode = flattenDirectoryTreeToRoutes(directoryTree, options);\n\n if (middleware) {\n rootNode.middleware = middleware;\n }\n\n if (!options.ignoreEntryPoints) {\n crawlAndAppendInitialRoutesAndEntryFiles(rootNode, options);\n }\n\n return rootNode;\n}\n\n/**\n * Given a RequireContext, return the middleware node if one is found. If more than one middleware file is found, an error is thrown.\n */\nfunction getMiddleware(contextModule: RequireContext, options: Options): MiddlewareNode | null {\n const allMiddlewareFiles = contextModule.keys().filter((key) => key.includes('+middleware'));\n\n // Check if middleware is enabled via plugin config\n if (!options.unstable_useServerMiddleware) {\n if (allMiddlewareFiles.length > 0) {\n console.warn(\n 'Server middleware is not enabled. Add unstable_useServerMiddleware: true to your `expo-router` plugin config.\\n\\n' +\n JSON.stringify(\n {\n expo: {\n plugins: [['expo-router', { unstable_useServerMiddleware: true }]],\n },\n },\n null,\n 2\n )\n );\n }\n return null;\n }\n\n const isValidMiddleware = (key: string) => /^\\.\\/\\+middleware\\.[tj]sx?$/.test(key);\n\n const rootMiddlewareFiles = allMiddlewareFiles.filter(isValidMiddleware);\n\n const nonRootMiddleware = allMiddlewareFiles.filter(\n (file) => !rootMiddlewareFiles.includes(file)\n );\n if (nonRootMiddleware.length > 0) {\n throw new Error(\n `The middleware file can only be placed at the root level. Remove the following files: ${nonRootMiddleware.join(', ')}`\n );\n }\n\n if (rootMiddlewareFiles.length === 0) {\n return null;\n }\n\n // In development, throw an error if there are multiple root-level middleware files\n if (rootMiddlewareFiles.length > 1) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `Only one middleware file is allowed. Keep one of the conflicting files: ${rootMiddlewareFiles.map((p) => `\"${p}\"`).join(' or ')}`\n );\n }\n }\n\n const middlewareFilePath = rootMiddlewareFiles[0]!;\n\n const middleware: MiddlewareNode = {\n loadRoute() {\n if (options.ignoreRequireErrors) {\n try {\n return contextModule(middlewareFilePath);\n } catch {\n return {};\n }\n } else {\n return contextModule(middlewareFilePath);\n }\n },\n contextKey: middlewareFilePath,\n };\n\n if (options.internal_stripLoadRoute) {\n delete (middleware as any).loadRoute;\n }\n\n return middleware;\n}\n\n/**\n * Converts the RequireContext keys (file paths) into a directory tree.\n */\nfunction getDirectoryTree(contextModule: RequireContext, options: Options) {\n const importMode = options.importMode || process.env.EXPO_ROUTER_IMPORT_MODE;\n\n const ignoreList: RegExp[] = [/^\\.\\/\\+(html|native-intent)\\.[tj]sx?$/]; // Ignore the top level ./+html file\n\n if (options.ignore) {\n ignoreList.push(...options.ignore);\n }\n if (!options.preserveApiRoutes) {\n ignoreList.push(/\\+api$/, /\\+api\\.[tj]sx?$/);\n }\n\n // Always ignore middleware files in regular route processing\n ignoreList.push(/\\+middleware$/, /\\+middleware\\.[tj]sx?$/);\n\n const rootDirectory: DirectoryNode = {\n files: new Map(),\n subdirectories: new Map(),\n };\n\n let hasRoutes = false;\n let isValid = false;\n\n const contextKeys = contextModule.keys();\n const redirects: Record = {};\n const rewrites: Record = {};\n\n let validRedirectDestinations: { contextKey: string; nameWithoutInvisible: string }[] | undefined;\n\n const getValidDestinations = () => {\n // Loop over contexts once and cache the valid destinations\n validRedirectDestinations ??= contextKeys.map((key) => {\n return {\n contextKey: key,\n nameWithoutInvisible: getNameWithoutInvisibleSegmentsFromRedirectPath(\n removeSupportedExtensions(key)\n ),\n };\n });\n return validRedirectDestinations;\n };\n\n // If we are keeping redirects as valid routes, then we need to add them to the contextKeys\n // This is useful for generating a sitemap with redirects, or static site generation that includes redirects\n if (options.preserveRedirectAndRewrites) {\n if (options.redirects) {\n for (const redirect of options.redirects) {\n const sourceContextKey = getSourceContextKeyFromRedirectSource(redirect.source);\n const sourceName = getNameFromRedirectPath(redirect.source);\n\n const isExternalRedirect = shouldLinkExternally(redirect.destination);\n\n const targetDestinationName = isExternalRedirect\n ? redirect.destination\n : getNameWithoutInvisibleSegmentsFromRedirectPath(redirect.destination);\n\n if (ignoreList.some((regex) => regex.test(sourceContextKey))) {\n continue;\n }\n\n const validDestination = isExternalRedirect\n ? undefined\n : getValidDestinations().find(\n (key) => key.nameWithoutInvisible === targetDestinationName\n );\n const destination = isExternalRedirect\n ? targetDestinationName\n : validDestination?.nameWithoutInvisible;\n const destinationContextKey = isExternalRedirect\n ? targetDestinationName\n : validDestination?.contextKey;\n\n if (!destinationContextKey || destination === undefined) {\n /*\n * Only throw the error when we are preserving the api routes\n * When doing a static export, API routes will not exist so the redirect destination may not exist.\n * The desired behavior for this error is to warn the user when running `expo start`, so its ok if\n * `expo export` swallows this error.\n */\n if (options.preserveApiRoutes) {\n throw new Error(`Redirect destination \"${redirect.destination}\" does not exist.`);\n }\n\n continue;\n }\n\n contextKeys.push(sourceContextKey);\n redirects[sourceName] = {\n source: sourceName,\n destination,\n destinationContextKey,\n permanent: Boolean(redirect.permanent),\n external: isExternalRedirect,\n methods: redirect.methods,\n };\n }\n }\n\n if (options.rewrites) {\n for (const rewrite of options.rewrites) {\n const sourceContextKey = getSourceContextKeyFromRedirectSource(rewrite.source);\n const sourceName = getNameFromRedirectPath(rewrite.source);\n\n // We check to see if the context key is already known so that we don't create a rewrite for\n // a route that already exists on disk\n const isSourceContextKeyAlreadyKnown = contextKeys.includes(sourceContextKey);\n const targetDestinationName = isSourceContextKeyAlreadyKnown\n ? getNameFromRedirectPath(rewrite.destination)\n : getNameWithoutInvisibleSegmentsFromRedirectPath(rewrite.destination);\n\n if (ignoreList.some((regex) => regex.test(sourceContextKey))) {\n continue;\n }\n\n const validDestination = getValidDestinations().find(\n (key) => key.nameWithoutInvisible === targetDestinationName\n );\n const destination = validDestination?.nameWithoutInvisible;\n const destinationContextKey = validDestination?.contextKey;\n\n if (!destinationContextKey || destination === undefined) {\n /*\n * Only throw the error when we are preserving the api routes\n * When doing a static export, API routes will not exist so the redirect destination may not exist.\n * The desired behavior for this error is to warn the user when running `expo start`, so its ok if\n * `expo export` swallows this error.\n */\n if (options.preserveApiRoutes) {\n throw new Error(`Rewrite destination \"${rewrite.destination}\" does not exist.`);\n }\n\n continue;\n }\n\n contextKeys.push(sourceContextKey);\n rewrites[sourceName] = {\n source: sourceName,\n destination,\n destinationContextKey,\n methods: rewrite.methods,\n };\n }\n }\n }\n\n const processedRedirectsRewrites = new Set();\n\n for (const filePath of contextKeys) {\n if (ignoreList.some((regex) => regex.test(filePath))) {\n continue;\n }\n\n isValid = true;\n\n const meta = getFileMeta(filePath, options, redirects, rewrites);\n\n // This is a file that should be ignored. e.g maybe it has an invalid platform?\n if (meta.specificity < 0) {\n continue;\n }\n\n let node: RouteNode = {\n type: meta.isApi ? 'api' : meta.isLayout ? 'layout' : 'route',\n loadRoute() {\n let routeModule: any;\n\n if (options.ignoreRequireErrors) {\n try {\n routeModule = contextModule(filePath);\n } catch {\n routeModule = {};\n }\n } else {\n routeModule = contextModule(filePath);\n }\n\n if (process.env.NODE_ENV === 'development' && importMode === 'sync') {\n // In development mode, when async routes are disabled, add some extra error handling to improve the developer experience.\n // This can be useful when you accidentally use an async function in a route file for the default export.\n if (routeModule instanceof Promise) {\n throw new Error(\n `Route \"${filePath}\" cannot be a promise when async routes is disabled.`\n );\n }\n\n const defaultExport = routeModule?.default;\n if (defaultExport instanceof Promise) {\n throw new Error(\n `The default export from route \"${filePath}\" is a promise. Ensure the React Component does not use async or promises.`\n );\n }\n\n // check if default is an async function without invoking it\n if (\n defaultExport instanceof Function &&\n // This only works on web because Hermes support async functions so we have to transform them out.\n defaultExport.constructor.name === 'AsyncFunction'\n ) {\n throw new Error(\n `The default export from route \"${filePath}\" is an async function. Ensure the React Component does not use async or promises.`\n );\n }\n\n // Validate loader export in development\n const loaderExport = routeModule?.loader;\n if (loaderExport && typeof loaderExport !== 'function') {\n throw new Error(`Route \"${filePath}\" exports a loader that is not a function.`);\n }\n }\n\n return routeModule;\n },\n contextKey: filePath,\n route: '', // This is overwritten during hoisting based upon the _layout\n dynamic: null,\n children: [], // While we are building the directory tree, we don't know the node's children just yet. This is added during hoisting\n };\n\n if (meta.isRedirect) {\n if (processedRedirectsRewrites.has(meta.route)) {\n continue;\n }\n\n const redirect = redirects[meta.route]!;\n node.destinationContextKey = redirect.destinationContextKey;\n node.permanent = redirect.permanent;\n node.generated = true;\n if (node.type === 'route') {\n node = options.getSystemRoute({\n type: 'redirect',\n route: redirect.destination,\n defaults: node,\n redirectConfig: redirect,\n });\n }\n if (redirect!.methods) {\n node.methods = redirect.methods;\n }\n node.type = 'redirect';\n processedRedirectsRewrites.add(meta.route);\n }\n\n if (meta.isRewrite) {\n if (processedRedirectsRewrites.has(meta.route)) {\n continue;\n }\n\n const rewrite = rewrites[meta.route]!;\n node.destinationContextKey = rewrite.destinationContextKey;\n node.generated = true;\n if (node.type === 'route') {\n node = options.getSystemRoute({\n type: 'rewrite',\n route: rewrite.destination,\n defaults: node,\n rewriteConfig: rewrite,\n });\n }\n if (rewrite.methods) {\n node.methods = rewrite.methods;\n }\n node.type = 'rewrite';\n processedRedirectsRewrites.add(meta.route);\n }\n\n if (process.env.NODE_ENV === 'development') {\n // If the user has set the `EXPO_ROUTER_IMPORT_MODE` to `sync` then we should\n // filter the missing routes.\n if (node.type !== 'api' && importMode === 'sync') {\n const routeItem = node.loadRoute();\n // Have a warning for nullish ex\n const route = routeItem?.default;\n if (route == null) {\n // Do not throw an error since a user may just be creating a new route.\n console.warn(\n `Route \"${filePath}\" is missing the required default export. Ensure a React component is exported as default.`\n );\n continue;\n }\n if (['boolean', 'number', 'string'].includes(typeof route)) {\n throw new Error(\n `The default export from route \"${filePath}\" is an unsupported type: \"${typeof route}\". Only React Components are supported as default exports from route files.`\n );\n }\n }\n }\n\n /**\n * A single filepath may be extrapolated into multiple routes if it contains array syntax.\n * Another way to thinking about is that a filepath node is present in multiple leaves of the directory tree.\n */\n for (const route of extrapolateGroups(meta.route)) {\n // Traverse the directory tree to its leaf node, creating any missing directories along the way\n const subdirectoryParts = route.split('/').slice(0, -1);\n\n // Start at the root directory and traverse the path to the leaf directory\n let directory = rootDirectory;\n\n for (const part of subdirectoryParts) {\n let subDirectory = directory.subdirectories.get(part);\n\n // Create any missing subdirectories\n if (!subDirectory) {\n subDirectory = {\n files: new Map(),\n subdirectories: new Map(),\n };\n directory.subdirectories.set(part, subDirectory);\n }\n\n directory = subDirectory;\n }\n\n // Clone the node for this route\n node = { ...node, route };\n\n if (meta.isLayout) {\n directory.layout ??= [];\n const existing = directory.layout[meta.specificity];\n if (existing) {\n // In production, use the first route found\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `The layouts \"${filePath}\" and \"${existing.contextKey}\" conflict on the route \"/${route}\". Remove or rename one of these files.`\n );\n }\n } else {\n node = getLayoutNode(node, options);\n directory.layout[meta.specificity] = node;\n }\n } else if (meta.isApi) {\n const fileKey = `${route}+api`;\n let nodes = directory.files.get(fileKey);\n\n if (!nodes) {\n nodes = [];\n directory.files.set(fileKey, nodes);\n }\n\n // API Routes have no specificity, they are always the first node\n const existing = nodes[0];\n\n if (existing) {\n // In production, use the first route found\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `The API route file \"${filePath}\" and \"${existing.contextKey}\" conflict on the route \"/${route}\". Remove or rename one of these files.`\n );\n }\n } else {\n nodes[0] = node;\n }\n } else {\n let nodes = directory.files.get(route);\n\n if (!nodes) {\n nodes = [];\n directory.files.set(route, nodes);\n }\n\n /**\n * If there is an existing node with the same specificity, then we have a conflict.\n * NOTE(Platform Routes):\n * We cannot check for specificity conflicts here, as we haven't processed all the context keys yet!\n * This will be checked during hoisting, as well as enforcing that all routes have a non-platform route.\n */\n const existing = nodes[meta.specificity];\n if (existing) {\n // In production, use the first route found\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `The route files \"${filePath}\" and \"${existing.contextKey}\" conflict on the route \"/${route}\". Remove or rename one of these files.`\n );\n }\n } else {\n hasRoutes ||= true;\n nodes[meta.specificity] = node;\n }\n }\n }\n }\n\n // If there are no routes/layouts then we should display the tutorial.\n if (!isValid) {\n return null;\n }\n\n /**\n * If there are no top-level _layout, add a default _layout\n * While this is a generated route, it will still be generated even if skipGenerated is true.\n */\n if (!rootDirectory.layout) {\n rootDirectory.layout = [\n options.getSystemRoute({\n type: 'layout',\n route: '',\n }),\n ];\n }\n\n // Only include the sitemap if there are routes.\n if (!options.skipGenerated) {\n if (hasRoutes && options.sitemap !== false) {\n appendSitemapRoute(rootDirectory, options);\n }\n if (options.notFound !== false) {\n appendNotFoundRoute(rootDirectory, options);\n }\n }\n return rootDirectory;\n}\n\nfunction getNameFromRedirectPath(path: string): string {\n // Removing only the filesystem extensions, to be able to handle +api, +html\n return (\n removeFileSystemExtensions(removeFileSystemDots(path))\n // Remove the leading `/`\n .replace(/^\\//, '')\n );\n}\n\nfunction getNameWithoutInvisibleSegmentsFromRedirectPath(path: string): string {\n return stripInvisibleSegmentsFromPath(getNameFromRedirectPath(path));\n}\n\n// Creates fake context key for redirects and rewrites\nfunction getSourceContextKeyFromRedirectSource(source: string): string {\n const name = getNameFromRedirectPath(source);\n const prefix = './';\n const suffix = /\\.[tj]sx?$/.test(name) ? '' : '.js'; // Ensure it has a file extension\n return `${prefix}${name}${suffix}`;\n}\n\n/**\n * Flatten the directory tree into routes, hoisting routes to the nearest _layout.\n */\nfunction flattenDirectoryTreeToRoutes(\n directory: DirectoryNode,\n options: Options,\n /* The nearest _layout file in the directory tree */\n layout?: RouteNode,\n /* Route names are relative to their layout */\n pathToRemove = ''\n) {\n /**\n * This directory has a _layout file so it becomes the new target for hoisting routes.\n */\n if (directory.layout) {\n const previousLayout = layout;\n layout = getMostSpecific(directory.layout);\n\n // Add the new layout as a child of its parent\n if (previousLayout) {\n previousLayout.children.push(layout);\n }\n\n if (options.internal_stripLoadRoute) {\n delete (layout as any).loadRoute;\n }\n\n // `route` is the absolute pathname. We need to make this relative to the last _layout\n const newRoute = layout.route.replace(pathToRemove, '');\n pathToRemove = layout.route ? `${layout.route}/` : '';\n\n // Now update this layout with the new relative route and dynamic conventions\n layout.route = newRoute;\n layout.dynamic = generateDynamic(layout.contextKey.slice(0));\n }\n\n // This should never occur as there will always be a root layout, but it makes the type system happy\n if (!layout) throw new Error('Expo Router Internal Error: No nearest layout');\n\n for (const routes of directory.files.values()) {\n const routeNode = getMostSpecific(routes);\n\n // `route` is the absolute pathname. We need to make this relative to the nearest layout\n routeNode.route = routeNode.route.replace(pathToRemove, '');\n routeNode.dynamic = generateDynamic(routeNode.route);\n\n if (options.internal_stripLoadRoute) {\n delete (routeNode as any).loadRoute;\n }\n\n layout.children.push(routeNode);\n }\n\n // Recursively flatten the subdirectories\n for (const child of directory.subdirectories.values()) {\n flattenDirectoryTreeToRoutes(child, options, layout, pathToRemove);\n }\n\n return layout;\n}\n\nfunction getFileMeta(\n originalKey: string,\n options: Options,\n redirects: Record,\n rewrites: Record\n) {\n // Remove the leading `./`\n const key = removeSupportedExtensions(removeFileSystemDots(originalKey));\n let route = key;\n\n const parts = removeFileSystemDots(originalKey).split('/');\n const filename = parts[parts.length - 1]!;\n const filenameParts = removeSupportedExtensions(filename).split('.');\n const filenameWithoutExtensions = filenameParts[0]!;\n const platformExtension = filenameParts[1];\n\n const isLayout = filenameWithoutExtensions === '_layout';\n const isApi = originalKey.match(/\\+api\\.(\\w+\\.)?[jt]sx?$/);\n\n if (filenameWithoutExtensions.startsWith('(') && filenameWithoutExtensions.endsWith(')')) {\n throw new Error(`Invalid route ${originalKey}. Routes cannot end with '(group)' syntax`);\n }\n\n // Nested routes cannot start with the '+' character, except for the '+not-found' route\n if (!isApi && filename.startsWith('+') && filenameWithoutExtensions !== '+not-found') {\n const renamedRoute = [...parts.slice(0, -1), filename.slice(1)].join('/');\n throw new Error(\n `Invalid route ${originalKey}. Route nodes cannot start with the '+' character. \"Rename it to ${renamedRoute}\"`\n );\n }\n let specificity = 0;\n\n const hasPlatformExtension = validPlatforms.has(platformExtension!);\n const usePlatformRoutes = options.platformRoutes ?? true;\n\n if (hasPlatformExtension) {\n if (!usePlatformRoutes) {\n // If the user has disabled platform routes, then we should ignore this file\n specificity = -1;\n } else if (!options.platform) {\n // If we don't have a platform, then we should ignore this file\n // This used by typed routes, sitemap, etc\n specificity = -1;\n } else if (platformExtension === options.platform) {\n // If the platform extension is the same as the options.platform, then it is the most specific\n specificity = 2;\n } else if (platformExtension === 'native' && options.platform !== 'web') {\n // `native` is allow but isn't as specific as the platform\n specificity = 1;\n } else if (platformExtension !== options.platform) {\n // Somehow we have a platform extension that doesn't match the options.platform and it isn't native\n // This is an invalid file and we will ignore it\n specificity = -1;\n }\n\n if (isApi && specificity !== 0) {\n throw new Error(\n `API routes cannot have platform extensions. Remove '.${platformExtension}' from '${originalKey}'`\n );\n }\n\n route = route.replace(new RegExp(`.${platformExtension}$`), '');\n }\n\n return {\n route,\n specificity,\n isLayout,\n isApi,\n isRedirect: key in redirects,\n isRewrite: key in rewrites,\n };\n}\n\n/**\n * Generates a set of strings which have the router array syntax extrapolated.\n *\n * /(a,b)/(c,d)/e.tsx => new Set(['a/c/e.tsx', 'a/d/e.tsx', 'b/c/e.tsx', 'b/d/e.tsx'])\n */\nexport function extrapolateGroups(key: string, keys: Set = new Set()): Set {\n const match = matchArrayGroupName(key);\n\n if (!match) {\n keys.add(key);\n return keys;\n }\n const groups = match.split(',');\n const groupsSet = new Set(groups);\n\n if (groupsSet.size !== groups.length) {\n throw new Error(`Array syntax cannot contain duplicate group name \"${groups}\" in \"${key}\".`);\n }\n\n if (groups.length === 1) {\n keys.add(key);\n return keys;\n }\n\n for (const group of groups) {\n extrapolateGroups(key.replace(match, group.trim()), keys);\n }\n\n return keys;\n}\n\nexport function generateDynamic(path: string): DynamicConvention[] | null {\n const dynamic = path\n .split('/')\n .map((part): DynamicConvention | null => {\n if (part === '+not-found') {\n return {\n name: '+not-found',\n deep: true,\n notFound: true,\n };\n }\n return matchDynamicName(part) ?? null;\n })\n .filter((part): part is DynamicConvention => !!part);\n\n return dynamic.length === 0 ? null : dynamic;\n}\n\nfunction appendSitemapRoute(directory: DirectoryNode, options: Options) {\n if (!directory.files.has('_sitemap') && options.getSystemRoute) {\n directory.files.set('_sitemap', [\n options.getSystemRoute({\n type: 'route',\n route: '_sitemap',\n }),\n ]);\n }\n}\n\nfunction appendNotFoundRoute(directory: DirectoryNode, options: Options) {\n if (!directory.files.has('+not-found') && options.getSystemRoute) {\n directory.files.set('+not-found', [\n options.getSystemRoute({\n type: 'route',\n route: '+not-found',\n }),\n ]);\n }\n}\n\nfunction getLayoutNode(node: RouteNode, options: Options) {\n /**\n * A file called `(a,b)/(c)/_layout.tsx` will generate two _layout routes: `(a)/(c)/_layout` and `(b)/(c)/_layout`.\n * Each of these layouts will have a different anchor based upon the first group name.\n */\n // We may strip loadRoute during testing\n const groupName = matchLastGroupName(node.route);\n const childMatchingGroup = node.children.find((child) => {\n return child.route.replace(/\\/index$/, '') === groupName;\n });\n let anchor = childMatchingGroup?.route;\n const loaded = node.loadRoute();\n if (loaded?.unstable_settings) {\n try {\n // Allow unstable_settings={ initialRouteName: '...' } to override the default initial route name.\n anchor =\n loaded.unstable_settings.anchor ?? loaded.unstable_settings.initialRouteName ?? anchor;\n } catch (error: any) {\n if (error instanceof Error) {\n if (!error.message.match(/You cannot dot into a client module/)) {\n throw error;\n }\n }\n }\n\n if (groupName) {\n // Allow unstable_settings={ 'custom': { initialRouteName: '...' } } to override the less specific initial route name.\n const groupSpecificInitialRouteName =\n loaded.unstable_settings?.[groupName]?.anchor ??\n loaded.unstable_settings?.[groupName]?.initialRouteName;\n\n anchor = groupSpecificInitialRouteName ?? anchor;\n }\n }\n\n return {\n ...node,\n route: node.route.replace(/\\/?_layout$/, ''),\n children: [], // Each layout should have its own children\n initialRouteName: anchor,\n };\n}\n\nfunction crawlAndAppendInitialRoutesAndEntryFiles(\n node: RouteNode,\n options: Options,\n entryPoints: string[] = []\n) {\n if (node.type === 'route') {\n node.entryPoints = [...new Set([...entryPoints, node.contextKey])];\n } else if (node.type === 'redirect') {\n node.entryPoints = [...new Set([...entryPoints, node.destinationContextKey!])];\n } else if (node.type === 'layout') {\n if (!node.children) {\n throw new Error(`Layout \"${node.contextKey}\" does not contain any child routes`);\n }\n\n // Every node below this layout will have it as an entryPoint\n entryPoints = [...entryPoints, node.contextKey];\n\n /**\n * Calculate the initialRouteNode\n *\n * A file called `(a,b)/(c)/_layout.tsx` will generate two _layout routes: `(a)/(c)/_layout` and `(b)/(c)/_layout`.\n * Each of these layouts will have a different anchor based upon the first group.\n */\n const groupName = matchGroupName(node.route);\n const childMatchingGroup = node.children.find((child) => {\n return child.route.replace(/\\/index$/, '') === groupName;\n });\n let anchor = childMatchingGroup?.route;\n // We may strip loadRoute during testing\n if (!options.internal_stripLoadRoute) {\n const loaded = node.loadRoute();\n if (loaded?.unstable_settings) {\n try {\n // Allow unstable_settings={ initialRouteName: '...' } to override the default initial route name.\n anchor =\n loaded.unstable_settings.anchor ?? loaded.unstable_settings.initialRouteName ?? anchor;\n } catch (error: any) {\n if (error instanceof Error) {\n if (!error.message.match(/You cannot dot into a client module/)) {\n throw error;\n }\n }\n }\n\n if (groupName) {\n // Allow unstable_settings={ 'custom': { initialRouteName: '...' } } to override the less specific initial route name.\n const groupSpecificInitialRouteName =\n loaded.unstable_settings?.[groupName]?.anchor ??\n loaded.unstable_settings?.[groupName]?.initialRouteName;\n\n anchor = groupSpecificInitialRouteName ?? anchor;\n }\n }\n }\n\n if (anchor) {\n const anchorRoute = node.children.find((child) => child.route === anchor);\n if (!anchorRoute) {\n const validAnchorRoutes = node.children\n .filter((child) => !child.generated)\n .map((child) => `'${child.route}'`)\n .join(', ');\n\n if (groupName) {\n throw new Error(\n `Layout ${node.contextKey} has invalid anchor '${anchor}' for group '(${groupName})'. Valid options are: ${validAnchorRoutes}`\n );\n } else {\n throw new Error(\n `Layout ${node.contextKey} has invalid anchor '${anchor}'. Valid options are: ${validAnchorRoutes}`\n );\n }\n }\n\n // Navigators can add initialsRoutes into the history, so they need to be to be included in the entryPoints\n node.initialRouteName = anchor;\n entryPoints.push(anchorRoute.contextKey);\n }\n\n for (const child of node.children) {\n crawlAndAppendInitialRoutesAndEntryFiles(child, options, entryPoints);\n }\n }\n}\n\nfunction getMostSpecific(routes: RouteNode[]) {\n const route = routes[routes.length - 1]!;\n\n if (!routes[0]) {\n throw new Error(\n `The file ${route.contextKey} does not have a fallback sibling file without a platform extension.`\n );\n }\n\n // This works even tho routes is holey array (e.g it might have index 0 and 2 but not 1)\n // `.length` includes the holes in its count\n return route;\n}\n"]} \ No newline at end of file +{"version":3,"file":"getRoutesCore.js","sourceRoot":"","sources":["../src/getRoutesCore.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DynamicConvention, MiddlewareNode, RouteNode } from './Route';\nimport {\n matchArrayGroupName,\n matchDynamicName,\n matchGroupName,\n matchLastGroupName,\n removeFileSystemDots,\n removeFileSystemExtensions,\n removeSupportedExtensions,\n stripInvisibleSegmentsFromPath,\n} from './matchers';\nimport type { RequireContext } from './types';\nimport { shouldLinkExternally } from './utils/url';\n\nexport type Options = {\n ignore?: RegExp[];\n preserveApiRoutes?: boolean;\n ignoreRequireErrors?: boolean;\n ignoreEntryPoints?: boolean;\n /* Used to simplify testing for toEqual() comparison */\n internal_stripLoadRoute?: boolean;\n /* Used to simplify by skipping the generated routes */\n skipGenerated?: boolean;\n /** Skip routes created by `generateStaticParams()` */\n skipStaticParams?: boolean;\n /* Skip the generated not found route */\n notFound?: boolean;\n /* Enable experimental server middleware support */\n unstable_useServerMiddleware?: boolean;\n importMode?: string;\n platformRoutes?: boolean;\n sitemap?: boolean;\n platform?: string;\n redirects?: RedirectConfig[];\n rewrites?: RewriteConfig[];\n headers?: Record;\n /* Keep redirects as valid routes within the RouteConfig tree */\n preserveRedirectAndRewrites?: boolean;\n\n /** Get the system route for a location. Useful for shimming React Native imports in SSR environments. */\n getSystemRoute: (\n route: Pick & {\n defaults?: RouteNode;\n redirectConfig?: RedirectConfig;\n rewriteConfig?: RewriteConfig;\n }\n ) => RouteNode;\n};\n\ntype DirectoryNode = {\n layout?: RouteNode[];\n files: Map;\n subdirectories: Map;\n};\n\nexport type RedirectConfig = {\n source: string;\n destination: string;\n destinationContextKey: string;\n permanent?: boolean;\n methods?: string[];\n external?: boolean;\n};\n\nexport type RewriteConfig = {\n source: string;\n destination: string;\n destinationContextKey: string;\n methods?: string[];\n};\n\nconst validPlatforms = new Set(['android', 'ios', 'native', 'web']);\n\n/**\n * Given a Metro context module, return an array of nested routes.\n *\n * This is a two step process:\n * 1. Convert the RequireContext keys (file paths) into a directory tree.\n * - This should extrapolate array syntax into multiple routes\n * - Routes are given a specificity score\n * 2. Flatten the directory tree into routes\n * - Routes in directories without _layout files are hoisted to the nearest _layout\n * - The name of the route is relative to the nearest _layout\n * - If multiple routes have the same name, the most specific route is used\n */\nexport function getRoutes(contextModule: RequireContext, options: Options): RouteNode | null {\n const middleware = getMiddleware(contextModule, options);\n const directoryTree = getDirectoryTree(contextModule, options);\n\n // If there are no routes\n if (!directoryTree) {\n return null;\n }\n\n const rootNode = flattenDirectoryTreeToRoutes(directoryTree, options);\n\n if (middleware) {\n rootNode.middleware = middleware;\n }\n\n if (!options.ignoreEntryPoints) {\n crawlAndAppendInitialRoutesAndEntryFiles(rootNode, options);\n }\n\n return rootNode;\n}\n\n/**\n * Given a RequireContext, return the middleware node if one is found. If more than one middleware file is found, an error is thrown.\n */\nfunction getMiddleware(contextModule: RequireContext, options: Options): MiddlewareNode | null {\n const allMiddlewareFiles = contextModule.keys().filter((key) => key.includes('+middleware'));\n\n // Check if middleware is enabled via plugin config\n if (!options.unstable_useServerMiddleware) {\n if (allMiddlewareFiles.length > 0) {\n console.warn(\n 'Server middleware is not enabled. Add unstable_useServerMiddleware: true to your `expo-router` plugin config.\\n\\n' +\n JSON.stringify(\n {\n expo: {\n plugins: [['expo-router', { unstable_useServerMiddleware: true }]],\n },\n },\n null,\n 2\n )\n );\n }\n return null;\n }\n\n const isValidMiddleware = (key: string) => /^\\.\\/\\+middleware\\.[tj]sx?$/.test(key);\n\n const rootMiddlewareFiles = allMiddlewareFiles.filter(isValidMiddleware);\n\n const nonRootMiddleware = allMiddlewareFiles.filter(\n (file) => !rootMiddlewareFiles.includes(file)\n );\n if (nonRootMiddleware.length > 0) {\n throw new Error(\n `The middleware file can only be placed at the root level. Remove the following files: ${nonRootMiddleware.join(', ')}`\n );\n }\n\n if (rootMiddlewareFiles.length === 0) {\n return null;\n }\n\n // In development, throw an error if there are multiple root-level middleware files\n if (rootMiddlewareFiles.length > 1) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `Only one middleware file is allowed. Keep one of the conflicting files: ${rootMiddlewareFiles.map((p) => `\"${p}\"`).join(' or ')}`\n );\n }\n }\n\n const middlewareFilePath = rootMiddlewareFiles[0]!;\n\n const middleware: MiddlewareNode = {\n loadRoute() {\n if (options.ignoreRequireErrors) {\n try {\n return contextModule(middlewareFilePath);\n } catch {\n return {};\n }\n } else {\n return contextModule(middlewareFilePath);\n }\n },\n contextKey: middlewareFilePath,\n };\n\n if (options.internal_stripLoadRoute) {\n delete (middleware as any).loadRoute;\n }\n\n return middleware;\n}\n\n/**\n * Converts the RequireContext keys (file paths) into a directory tree.\n */\nfunction getDirectoryTree(contextModule: RequireContext, options: Options) {\n const importMode = options.importMode || process.env.EXPO_ROUTER_IMPORT_MODE;\n\n const ignoreList: RegExp[] = [/^\\.\\/\\+(html|native-intent)\\.[tj]sx?$/]; // Ignore the top level ./+html file\n\n if (options.ignore) {\n ignoreList.push(...options.ignore);\n }\n if (!options.preserveApiRoutes) {\n ignoreList.push(/\\+api$/, /\\+api\\.[tj]sx?$/);\n }\n\n // Always ignore middleware files in regular route processing\n ignoreList.push(/\\+middleware$/, /\\+middleware\\.[tj]sx?$/);\n\n const rootDirectory: DirectoryNode = {\n files: new Map(),\n subdirectories: new Map(),\n };\n\n let hasRoutes = false;\n let isValid = false;\n\n const contextKeys = contextModule.keys();\n const redirects: Record = {};\n const rewrites: Record = {};\n\n let validRedirectDestinations: { contextKey: string; nameWithoutInvisible: string }[] | undefined;\n\n const getValidDestinations = () => {\n // Loop over contexts once and cache the valid destinations\n validRedirectDestinations ??= contextKeys.map((key) => {\n return {\n contextKey: key,\n nameWithoutInvisible: getNameWithoutInvisibleSegmentsFromRedirectPath(\n removeSupportedExtensions(key)\n ),\n };\n });\n return validRedirectDestinations;\n };\n\n // If we are keeping redirects as valid routes, then we need to add them to the contextKeys\n // This is useful for generating a sitemap with redirects, or static site generation that includes redirects\n if (options.preserveRedirectAndRewrites) {\n if (options.redirects) {\n for (const redirect of options.redirects) {\n const sourceContextKey = getSourceContextKeyFromRedirectSource(redirect.source);\n const sourceName = getNameFromRedirectPath(redirect.source);\n\n const isExternalRedirect = shouldLinkExternally(redirect.destination);\n\n const targetDestinationName = isExternalRedirect\n ? redirect.destination\n : getNameWithoutInvisibleSegmentsFromRedirectPath(redirect.destination);\n\n if (ignoreList.some((regex) => regex.test(sourceContextKey))) {\n continue;\n }\n\n const validDestination = isExternalRedirect\n ? undefined\n : getValidDestinations().find(\n (key) => key.nameWithoutInvisible === targetDestinationName\n );\n const destination = isExternalRedirect\n ? targetDestinationName\n : validDestination?.nameWithoutInvisible;\n const destinationContextKey = isExternalRedirect\n ? targetDestinationName\n : validDestination?.contextKey;\n\n if (!destinationContextKey || destination === undefined) {\n /*\n * Only throw the error when we are preserving the api routes\n * When doing a static export, API routes will not exist so the redirect destination may not exist.\n * The desired behavior for this error is to warn the user when running `expo start`, so its ok if\n * `expo export` swallows this error.\n */\n if (options.preserveApiRoutes) {\n throw new Error(`Redirect destination \"${redirect.destination}\" does not exist.`);\n }\n\n continue;\n }\n\n contextKeys.push(sourceContextKey);\n redirects[sourceName] = {\n source: sourceName,\n destination,\n destinationContextKey,\n permanent: Boolean(redirect.permanent),\n external: isExternalRedirect,\n methods: redirect.methods,\n };\n }\n }\n\n if (options.rewrites) {\n for (const rewrite of options.rewrites) {\n const sourceContextKey = getSourceContextKeyFromRedirectSource(rewrite.source);\n const sourceName = getNameFromRedirectPath(rewrite.source);\n\n // We check to see if the context key is already known so that we don't create a rewrite for\n // a route that already exists on disk\n const isSourceContextKeyAlreadyKnown = contextKeys.includes(sourceContextKey);\n const targetDestinationName = isSourceContextKeyAlreadyKnown\n ? getNameFromRedirectPath(rewrite.destination)\n : getNameWithoutInvisibleSegmentsFromRedirectPath(rewrite.destination);\n\n if (ignoreList.some((regex) => regex.test(sourceContextKey))) {\n continue;\n }\n\n const validDestination = getValidDestinations().find(\n (key) => key.nameWithoutInvisible === targetDestinationName\n );\n const destination = validDestination?.nameWithoutInvisible;\n const destinationContextKey = validDestination?.contextKey;\n\n if (!destinationContextKey || destination === undefined) {\n /*\n * Only throw the error when we are preserving the api routes\n * When doing a static export, API routes will not exist so the redirect destination may not exist.\n * The desired behavior for this error is to warn the user when running `expo start`, so its ok if\n * `expo export` swallows this error.\n */\n if (options.preserveApiRoutes) {\n throw new Error(`Rewrite destination \"${rewrite.destination}\" does not exist.`);\n }\n\n continue;\n }\n\n contextKeys.push(sourceContextKey);\n rewrites[sourceName] = {\n source: sourceName,\n destination,\n destinationContextKey,\n methods: rewrite.methods,\n };\n }\n }\n }\n\n const processedRedirectsRewrites = new Set();\n\n for (const filePath of contextKeys) {\n if (ignoreList.some((regex) => regex.test(filePath))) {\n continue;\n }\n\n isValid = true;\n\n const meta = getFileMeta(filePath, options, redirects, rewrites);\n\n // This is a file that should be ignored. e.g maybe it has an invalid platform?\n if (meta.specificity < 0) {\n continue;\n }\n\n let node: RouteNode = {\n type: meta.isApi ? 'api' : meta.isLayout ? 'layout' : 'route',\n loadRoute() {\n let routeModule: any;\n\n if (options.ignoreRequireErrors) {\n try {\n routeModule = contextModule(filePath);\n } catch {\n routeModule = {};\n }\n } else {\n routeModule = contextModule(filePath);\n }\n\n if (process.env.NODE_ENV === 'development' && importMode === 'sync') {\n // In development mode, when async routes are disabled, add some extra error handling to improve the developer experience.\n // This can be useful when you accidentally use an async function in a route file for the default export.\n if (routeModule instanceof Promise) {\n throw new Error(\n `Route \"${filePath}\" cannot be a promise when async routes is disabled.`\n );\n }\n\n const defaultExport = routeModule?.default;\n if (defaultExport instanceof Promise) {\n throw new Error(\n `The default export from route \"${filePath}\" is a promise. Ensure the React Component does not use async or promises.`\n );\n }\n\n // check if default is an async function without invoking it\n if (\n defaultExport instanceof Function &&\n // This only works on web because Hermes support async functions so we have to transform them out.\n defaultExport.constructor.name === 'AsyncFunction'\n ) {\n throw new Error(\n `The default export from route \"${filePath}\" is an async function. Ensure the React Component does not use async or promises.`\n );\n }\n\n // Validate loader export in development\n const loaderExport = routeModule?.loader;\n if (loaderExport && typeof loaderExport !== 'function') {\n throw new Error(`Route \"${filePath}\" exports a loader that is not a function.`);\n }\n\n const metadataExport = routeModule?.generateMetadata;\n if (metadataExport && typeof metadataExport !== 'function') {\n throw new Error(`Route \"${filePath}\" exports generateMetadata that is not a function.`);\n }\n }\n\n return routeModule;\n },\n contextKey: filePath,\n route: '', // This is overwritten during hoisting based upon the _layout\n dynamic: null,\n children: [], // While we are building the directory tree, we don't know the node's children just yet. This is added during hoisting\n };\n\n if (meta.isRedirect) {\n if (processedRedirectsRewrites.has(meta.route)) {\n continue;\n }\n\n const redirect = redirects[meta.route]!;\n node.destinationContextKey = redirect.destinationContextKey;\n node.permanent = redirect.permanent;\n node.generated = true;\n if (node.type === 'route') {\n node = options.getSystemRoute({\n type: 'redirect',\n route: redirect.destination,\n defaults: node,\n redirectConfig: redirect,\n });\n }\n if (redirect!.methods) {\n node.methods = redirect.methods;\n }\n node.type = 'redirect';\n processedRedirectsRewrites.add(meta.route);\n }\n\n if (meta.isRewrite) {\n if (processedRedirectsRewrites.has(meta.route)) {\n continue;\n }\n\n const rewrite = rewrites[meta.route]!;\n node.destinationContextKey = rewrite.destinationContextKey;\n node.generated = true;\n if (node.type === 'route') {\n node = options.getSystemRoute({\n type: 'rewrite',\n route: rewrite.destination,\n defaults: node,\n rewriteConfig: rewrite,\n });\n }\n if (rewrite.methods) {\n node.methods = rewrite.methods;\n }\n node.type = 'rewrite';\n processedRedirectsRewrites.add(meta.route);\n }\n\n if (process.env.NODE_ENV === 'development') {\n // If the user has set the `EXPO_ROUTER_IMPORT_MODE` to `sync` then we should\n // filter the missing routes.\n if (node.type !== 'api' && importMode === 'sync') {\n const routeItem = node.loadRoute();\n // Have a warning for nullish ex\n const route = routeItem?.default;\n if (route == null) {\n // Do not throw an error since a user may just be creating a new route.\n console.warn(\n `Route \"${filePath}\" is missing the required default export. Ensure a React component is exported as default.`\n );\n continue;\n }\n if (['boolean', 'number', 'string'].includes(typeof route)) {\n throw new Error(\n `The default export from route \"${filePath}\" is an unsupported type: \"${typeof route}\". Only React Components are supported as default exports from route files.`\n );\n }\n }\n }\n\n /**\n * A single filepath may be extrapolated into multiple routes if it contains array syntax.\n * Another way to thinking about is that a filepath node is present in multiple leaves of the directory tree.\n */\n for (const route of extrapolateGroups(meta.route)) {\n // Traverse the directory tree to its leaf node, creating any missing directories along the way\n const subdirectoryParts = route.split('/').slice(0, -1);\n\n // Start at the root directory and traverse the path to the leaf directory\n let directory = rootDirectory;\n\n for (const part of subdirectoryParts) {\n let subDirectory = directory.subdirectories.get(part);\n\n // Create any missing subdirectories\n if (!subDirectory) {\n subDirectory = {\n files: new Map(),\n subdirectories: new Map(),\n };\n directory.subdirectories.set(part, subDirectory);\n }\n\n directory = subDirectory;\n }\n\n // Clone the node for this route\n node = { ...node, route };\n\n if (meta.isLayout) {\n directory.layout ??= [];\n const existing = directory.layout[meta.specificity];\n if (existing) {\n // In production, use the first route found\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `The layouts \"${filePath}\" and \"${existing.contextKey}\" conflict on the route \"/${route}\". Remove or rename one of these files.`\n );\n }\n } else {\n node = getLayoutNode(node, options);\n directory.layout[meta.specificity] = node;\n }\n } else if (meta.isApi) {\n const fileKey = `${route}+api`;\n let nodes = directory.files.get(fileKey);\n\n if (!nodes) {\n nodes = [];\n directory.files.set(fileKey, nodes);\n }\n\n // API Routes have no specificity, they are always the first node\n const existing = nodes[0];\n\n if (existing) {\n // In production, use the first route found\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `The API route file \"${filePath}\" and \"${existing.contextKey}\" conflict on the route \"/${route}\". Remove or rename one of these files.`\n );\n }\n } else {\n nodes[0] = node;\n }\n } else {\n let nodes = directory.files.get(route);\n\n if (!nodes) {\n nodes = [];\n directory.files.set(route, nodes);\n }\n\n /**\n * If there is an existing node with the same specificity, then we have a conflict.\n * NOTE(Platform Routes):\n * We cannot check for specificity conflicts here, as we haven't processed all the context keys yet!\n * This will be checked during hoisting, as well as enforcing that all routes have a non-platform route.\n */\n const existing = nodes[meta.specificity];\n if (existing) {\n // In production, use the first route found\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `The route files \"${filePath}\" and \"${existing.contextKey}\" conflict on the route \"/${route}\". Remove or rename one of these files.`\n );\n }\n } else {\n hasRoutes ||= true;\n nodes[meta.specificity] = node;\n }\n }\n }\n }\n\n // If there are no routes/layouts then we should display the tutorial.\n if (!isValid) {\n return null;\n }\n\n /**\n * If there are no top-level _layout, add a default _layout\n * While this is a generated route, it will still be generated even if skipGenerated is true.\n */\n if (!rootDirectory.layout) {\n rootDirectory.layout = [\n options.getSystemRoute({\n type: 'layout',\n route: '',\n }),\n ];\n }\n\n // Only include the sitemap if there are routes.\n if (!options.skipGenerated) {\n if (hasRoutes && options.sitemap !== false) {\n appendSitemapRoute(rootDirectory, options);\n }\n if (options.notFound !== false) {\n appendNotFoundRoute(rootDirectory, options);\n }\n }\n return rootDirectory;\n}\n\nfunction getNameFromRedirectPath(path: string): string {\n // Removing only the filesystem extensions, to be able to handle +api, +html\n return (\n removeFileSystemExtensions(removeFileSystemDots(path))\n // Remove the leading `/`\n .replace(/^\\//, '')\n );\n}\n\nfunction getNameWithoutInvisibleSegmentsFromRedirectPath(path: string): string {\n return stripInvisibleSegmentsFromPath(getNameFromRedirectPath(path));\n}\n\n// Creates fake context key for redirects and rewrites\nfunction getSourceContextKeyFromRedirectSource(source: string): string {\n const name = getNameFromRedirectPath(source);\n const prefix = './';\n const suffix = /\\.[tj]sx?$/.test(name) ? '' : '.js'; // Ensure it has a file extension\n return `${prefix}${name}${suffix}`;\n}\n\n/**\n * Flatten the directory tree into routes, hoisting routes to the nearest _layout.\n */\nfunction flattenDirectoryTreeToRoutes(\n directory: DirectoryNode,\n options: Options,\n /* The nearest _layout file in the directory tree */\n layout?: RouteNode,\n /* Route names are relative to their layout */\n pathToRemove = ''\n) {\n /**\n * This directory has a _layout file so it becomes the new target for hoisting routes.\n */\n if (directory.layout) {\n const previousLayout = layout;\n layout = getMostSpecific(directory.layout);\n\n // Add the new layout as a child of its parent\n if (previousLayout) {\n previousLayout.children.push(layout);\n }\n\n if (options.internal_stripLoadRoute) {\n delete (layout as any).loadRoute;\n }\n\n // `route` is the absolute pathname. We need to make this relative to the last _layout\n const newRoute = layout.route.replace(pathToRemove, '');\n pathToRemove = layout.route ? `${layout.route}/` : '';\n\n // Now update this layout with the new relative route and dynamic conventions\n layout.route = newRoute;\n layout.dynamic = generateDynamic(layout.contextKey.slice(0));\n }\n\n // This should never occur as there will always be a root layout, but it makes the type system happy\n if (!layout) throw new Error('Expo Router Internal Error: No nearest layout');\n\n for (const routes of directory.files.values()) {\n const routeNode = getMostSpecific(routes);\n\n // `route` is the absolute pathname. We need to make this relative to the nearest layout\n routeNode.route = routeNode.route.replace(pathToRemove, '');\n routeNode.dynamic = generateDynamic(routeNode.route);\n\n if (options.internal_stripLoadRoute) {\n delete (routeNode as any).loadRoute;\n }\n\n layout.children.push(routeNode);\n }\n\n // Recursively flatten the subdirectories\n for (const child of directory.subdirectories.values()) {\n flattenDirectoryTreeToRoutes(child, options, layout, pathToRemove);\n }\n\n return layout;\n}\n\nfunction getFileMeta(\n originalKey: string,\n options: Options,\n redirects: Record,\n rewrites: Record\n) {\n // Remove the leading `./`\n const key = removeSupportedExtensions(removeFileSystemDots(originalKey));\n let route = key;\n\n const parts = removeFileSystemDots(originalKey).split('/');\n const filename = parts[parts.length - 1]!;\n const filenameParts = removeSupportedExtensions(filename).split('.');\n const filenameWithoutExtensions = filenameParts[0]!;\n const platformExtension = filenameParts[1];\n\n const isLayout = filenameWithoutExtensions === '_layout';\n const isApi = originalKey.match(/\\+api\\.(\\w+\\.)?[jt]sx?$/);\n\n if (filenameWithoutExtensions.startsWith('(') && filenameWithoutExtensions.endsWith(')')) {\n throw new Error(`Invalid route ${originalKey}. Routes cannot end with '(group)' syntax`);\n }\n\n // Nested routes cannot start with the '+' character, except for the '+not-found' route\n if (!isApi && filename.startsWith('+') && filenameWithoutExtensions !== '+not-found') {\n const renamedRoute = [...parts.slice(0, -1), filename.slice(1)].join('/');\n throw new Error(\n `Invalid route ${originalKey}. Route nodes cannot start with the '+' character. \"Rename it to ${renamedRoute}\"`\n );\n }\n let specificity = 0;\n\n const hasPlatformExtension = validPlatforms.has(platformExtension!);\n const usePlatformRoutes = options.platformRoutes ?? true;\n\n if (hasPlatformExtension) {\n if (!usePlatformRoutes) {\n // If the user has disabled platform routes, then we should ignore this file\n specificity = -1;\n } else if (!options.platform) {\n // If we don't have a platform, then we should ignore this file\n // This used by typed routes, sitemap, etc\n specificity = -1;\n } else if (platformExtension === options.platform) {\n // If the platform extension is the same as the options.platform, then it is the most specific\n specificity = 2;\n } else if (platformExtension === 'native' && options.platform !== 'web') {\n // `native` is allow but isn't as specific as the platform\n specificity = 1;\n } else if (platformExtension !== options.platform) {\n // Somehow we have a platform extension that doesn't match the options.platform and it isn't native\n // This is an invalid file and we will ignore it\n specificity = -1;\n }\n\n if (isApi && specificity !== 0) {\n throw new Error(\n `API routes cannot have platform extensions. Remove '.${platformExtension}' from '${originalKey}'`\n );\n }\n\n route = route.replace(new RegExp(`.${platformExtension}$`), '');\n }\n\n return {\n route,\n specificity,\n isLayout,\n isApi,\n isRedirect: key in redirects,\n isRewrite: key in rewrites,\n };\n}\n\n/**\n * Generates a set of strings which have the router array syntax extrapolated.\n *\n * /(a,b)/(c,d)/e.tsx => new Set(['a/c/e.tsx', 'a/d/e.tsx', 'b/c/e.tsx', 'b/d/e.tsx'])\n */\nexport function extrapolateGroups(key: string, keys: Set = new Set()): Set {\n const match = matchArrayGroupName(key);\n\n if (!match) {\n keys.add(key);\n return keys;\n }\n const groups = match.split(',');\n const groupsSet = new Set(groups);\n\n if (groupsSet.size !== groups.length) {\n throw new Error(`Array syntax cannot contain duplicate group name \"${groups}\" in \"${key}\".`);\n }\n\n if (groups.length === 1) {\n keys.add(key);\n return keys;\n }\n\n for (const group of groups) {\n extrapolateGroups(key.replace(match, group.trim()), keys);\n }\n\n return keys;\n}\n\nexport function generateDynamic(path: string): DynamicConvention[] | null {\n const dynamic = path\n .split('/')\n .map((part): DynamicConvention | null => {\n if (part === '+not-found') {\n return {\n name: '+not-found',\n deep: true,\n notFound: true,\n };\n }\n return matchDynamicName(part) ?? null;\n })\n .filter((part): part is DynamicConvention => !!part);\n\n return dynamic.length === 0 ? null : dynamic;\n}\n\nfunction appendSitemapRoute(directory: DirectoryNode, options: Options) {\n if (!directory.files.has('_sitemap') && options.getSystemRoute) {\n directory.files.set('_sitemap', [\n options.getSystemRoute({\n type: 'route',\n route: '_sitemap',\n }),\n ]);\n }\n}\n\nfunction appendNotFoundRoute(directory: DirectoryNode, options: Options) {\n if (!directory.files.has('+not-found') && options.getSystemRoute) {\n directory.files.set('+not-found', [\n options.getSystemRoute({\n type: 'route',\n route: '+not-found',\n }),\n ]);\n }\n}\n\nfunction getLayoutNode(node: RouteNode, options: Options) {\n /**\n * A file called `(a,b)/(c)/_layout.tsx` will generate two _layout routes: `(a)/(c)/_layout` and `(b)/(c)/_layout`.\n * Each of these layouts will have a different anchor based upon the first group name.\n */\n // We may strip loadRoute during testing\n const groupName = matchLastGroupName(node.route);\n const childMatchingGroup = node.children.find((child) => {\n return child.route.replace(/\\/index$/, '') === groupName;\n });\n let anchor = childMatchingGroup?.route;\n const loaded = node.loadRoute();\n if (loaded?.unstable_settings) {\n try {\n // Allow unstable_settings={ initialRouteName: '...' } to override the default initial route name.\n anchor =\n loaded.unstable_settings.anchor ?? loaded.unstable_settings.initialRouteName ?? anchor;\n } catch (error: any) {\n if (error instanceof Error) {\n if (!error.message.match(/You cannot dot into a client module/)) {\n throw error;\n }\n }\n }\n\n if (groupName) {\n // Allow unstable_settings={ 'custom': { initialRouteName: '...' } } to override the less specific initial route name.\n const groupSpecificInitialRouteName =\n loaded.unstable_settings?.[groupName]?.anchor ??\n loaded.unstable_settings?.[groupName]?.initialRouteName;\n\n anchor = groupSpecificInitialRouteName ?? anchor;\n }\n }\n\n return {\n ...node,\n route: node.route.replace(/\\/?_layout$/, ''),\n children: [], // Each layout should have its own children\n initialRouteName: anchor,\n };\n}\n\nfunction crawlAndAppendInitialRoutesAndEntryFiles(\n node: RouteNode,\n options: Options,\n entryPoints: string[] = []\n) {\n if (node.type === 'route') {\n node.entryPoints = [...new Set([...entryPoints, node.contextKey])];\n } else if (node.type === 'redirect') {\n node.entryPoints = [...new Set([...entryPoints, node.destinationContextKey!])];\n } else if (node.type === 'layout') {\n if (!node.children) {\n throw new Error(`Layout \"${node.contextKey}\" does not contain any child routes`);\n }\n\n // Every node below this layout will have it as an entryPoint\n entryPoints = [...entryPoints, node.contextKey];\n\n /**\n * Calculate the initialRouteNode\n *\n * A file called `(a,b)/(c)/_layout.tsx` will generate two _layout routes: `(a)/(c)/_layout` and `(b)/(c)/_layout`.\n * Each of these layouts will have a different anchor based upon the first group.\n */\n const groupName = matchGroupName(node.route);\n const childMatchingGroup = node.children.find((child) => {\n return child.route.replace(/\\/index$/, '') === groupName;\n });\n let anchor = childMatchingGroup?.route;\n // We may strip loadRoute during testing\n if (!options.internal_stripLoadRoute) {\n const loaded = node.loadRoute();\n if (loaded?.unstable_settings) {\n try {\n // Allow unstable_settings={ initialRouteName: '...' } to override the default initial route name.\n anchor =\n loaded.unstable_settings.anchor ?? loaded.unstable_settings.initialRouteName ?? anchor;\n } catch (error: any) {\n if (error instanceof Error) {\n if (!error.message.match(/You cannot dot into a client module/)) {\n throw error;\n }\n }\n }\n\n if (groupName) {\n // Allow unstable_settings={ 'custom': { initialRouteName: '...' } } to override the less specific initial route name.\n const groupSpecificInitialRouteName =\n loaded.unstable_settings?.[groupName]?.anchor ??\n loaded.unstable_settings?.[groupName]?.initialRouteName;\n\n anchor = groupSpecificInitialRouteName ?? anchor;\n }\n }\n }\n\n if (anchor) {\n const anchorRoute = node.children.find((child) => child.route === anchor);\n if (!anchorRoute) {\n const validAnchorRoutes = node.children\n .filter((child) => !child.generated)\n .map((child) => `'${child.route}'`)\n .join(', ');\n\n if (groupName) {\n throw new Error(\n `Layout ${node.contextKey} has invalid anchor '${anchor}' for group '(${groupName})'. Valid options are: ${validAnchorRoutes}`\n );\n } else {\n throw new Error(\n `Layout ${node.contextKey} has invalid anchor '${anchor}'. Valid options are: ${validAnchorRoutes}`\n );\n }\n }\n\n // Navigators can add initialsRoutes into the history, so they need to be to be included in the entryPoints\n node.initialRouteName = anchor;\n entryPoints.push(anchorRoute.contextKey);\n }\n\n for (const child of node.children) {\n crawlAndAppendInitialRoutesAndEntryFiles(child, options, entryPoints);\n }\n }\n}\n\nfunction getMostSpecific(routes: RouteNode[]) {\n const route = routes[routes.length - 1]!;\n\n if (!routes[0]) {\n throw new Error(\n `The file ${route.contextKey} does not have a fallback sibling file without a platform extension.`\n );\n }\n\n // This works even tho routes is holey array (e.g it might have index 0 and 2 but not 1)\n // `.length` includes the holes in its count\n return route;\n}\n"]} \ No newline at end of file diff --git a/packages/expo-router/server.d.ts b/packages/expo-router/server.d.ts index 02bfc8cd267864..57d211ccb76a9a 100644 --- a/packages/expo-router/server.d.ts +++ b/packages/expo-router/server.d.ts @@ -1,4 +1,10 @@ -export type { ImmutableRequest, LoaderFunction, MiddlewareFunction } from 'expo-server'; +export type { + ImmutableRequest, + GenerateMetadataFunction, + LoaderFunction, + Metadata, + MiddlewareFunction, +} from 'expo-server'; export type RequestHandler = ( request: Request, diff --git a/packages/expo-router/src/Route.tsx b/packages/expo-router/src/Route.tsx index 0ac4bc89d2cba9..590f4a820f0861 100644 --- a/packages/expo-router/src/Route.tsx +++ b/packages/expo-router/src/Route.tsx @@ -1,6 +1,6 @@ 'use client'; -import type { LoaderFunction } from 'expo-server'; +import type { GenerateMetadataFunction, LoaderFunction } from 'expo-server'; import { createContext, use, type ComponentType, type PropsWithChildren } from 'react'; import { getContextKey } from './matchers'; @@ -20,6 +20,7 @@ export type LoadedRoute = { getNavOptions?: (args: any) => any; generateStaticParams?: (props: { params?: Params }) => Params[]; loader?: LoaderFunction; + generateMetadata?: GenerateMetadataFunction; }; export type LoadedMiddleware = Pick; diff --git a/packages/expo-router/src/getRoutesCore.ts b/packages/expo-router/src/getRoutesCore.ts index 4cd51a254826b8..6c15fe9dca8650 100644 --- a/packages/expo-router/src/getRoutesCore.ts +++ b/packages/expo-router/src/getRoutesCore.ts @@ -391,6 +391,11 @@ function getDirectoryTree(contextModule: RequireContext, options: Options) { if (loaderExport && typeof loaderExport !== 'function') { throw new Error(`Route "${filePath}" exports a loader that is not a function.`); } + + const metadataExport = routeModule?.generateMetadata; + if (metadataExport && typeof metadataExport !== 'function') { + throw new Error(`Route "${filePath}" exports generateMetadata that is not a function.`); + } } return routeModule; diff --git a/packages/expo-server/CHANGELOG.md b/packages/expo-server/CHANGELOG.md index 23e41b16a9642c..f51f2f539aebe3 100644 --- a/packages/expo-server/CHANGELOG.md +++ b/packages/expo-server/CHANGELOG.md @@ -6,6 +6,9 @@ ### 🎉 New features +- Use stream rendering in SSR ([#43963](https://github.com/expo/expo/pull/43963) by [@hassankhan](https://github.com/hassankhan)) +- Add support for metadata in streaming SSR ([#44731](https://github.com/expo/expo/pull/44731) by [@hassankhan](https://github.com/hassankhan)) + ### 🐛 Bug fixes - Correctly handle JavaScript assets when `asyncRoutes: true` in SSR ([#43446](https://github.com/expo/expo/pull/43446) by [@hassankhan](https://github.com/hassankhan))` diff --git a/packages/expo-server/build/cjs/manifest.d.ts b/packages/expo-server/build/cjs/manifest.d.ts index 5da88ab3fe0cce..6834b30572e2e0 100644 --- a/packages/expo-server/build/cjs/manifest.d.ts +++ b/packages/expo-server/build/cjs/manifest.d.ts @@ -103,6 +103,9 @@ export interface GetStaticContentOptions { data?: unknown; key: string; }; + metadata?: { + headTags: string; + } | null; request?: Request; assets?: AssetInfo; } diff --git a/packages/expo-server/build/cjs/metadata.d.ts b/packages/expo-server/build/cjs/metadata.d.ts new file mode 100644 index 00000000000000..7dc02442965329 --- /dev/null +++ b/packages/expo-server/build/cjs/metadata.d.ts @@ -0,0 +1,207 @@ +import type { ImmutableRequest } from './types'; +export type MetadataValue = string | number | boolean; +export type MetadataValueArray = MetadataValue[]; +export type MetadataGoogleBot = string | { + index?: boolean; + follow?: boolean; + noimageindex?: boolean; + 'max-video-preview'?: number; + 'max-image-preview'?: 'none' | 'standard' | 'large'; + 'max-snippet'?: number; +}; +export type MetadataRobots = string | { + index?: boolean; + follow?: boolean; + noarchive?: boolean; + nosnippet?: boolean; + noimageindex?: boolean; + nocache?: boolean; + notranslate?: boolean; + unavailableAfter?: string; + googleBot?: MetadataGoogleBot; + [key: string]: MetadataValue | MetadataGoogleBot | undefined; +}; +export type MetadataAlternates = { + canonical?: string; + languages?: Record; + media?: Record; + types?: Record; +}; +export type MetadataImage = string | { + url: string; + alt?: string; + width?: number; + height?: number; + type?: string; + secureUrl?: string; +}; +export type MetadataVideo = string | { + url: string; + secureUrl?: string; + type?: string; + width?: number; + height?: number; +}; +export type MetadataAudio = string | { + url: string; + secureUrl?: string; + type?: string; +}; +export type MetadataOpenGraph = { + title?: string; + description?: string; + url?: string; + siteName?: string; + locale?: string; + type?: string; + images?: MetadataImage | MetadataImage[]; + videos?: MetadataVideo | MetadataVideo[]; + audio?: MetadataAudio | MetadataAudio[]; + emails?: string[]; + phoneNumbers?: string[]; + faxNumbers?: string[]; + alternateLocale?: string[]; + countryName?: string; + determiner?: 'a' | 'an' | 'the' | 'auto' | ''; + ttl?: number; + publishedTime?: string; + modifiedTime?: string; + expirationTime?: string; + section?: string; + tags?: string[]; + authors?: string[]; +}; +export type MetadataTwitterImage = string | { + url: string; + alt?: string; +}; +export type MetadataTwitterPlayer = { + url: string; + width?: number; + height?: number; + stream?: string; +}; +export type MetadataTwitterApp = { + name?: string; + id?: { + iphone?: string; + ipad?: string; + googleplay?: string; + }; + url?: { + iphone?: string; + ipad?: string; + googleplay?: string; + }; +}; +export type MetadataTwitter = { + card?: 'summary' | 'summary_large_image' | 'player' | 'app'; + title?: string; + description?: string; + site?: string; + siteId?: string; + creator?: string; + creatorId?: string; + images?: MetadataTwitterImage | MetadataTwitterImage[]; + players?: MetadataTwitterPlayer | MetadataTwitterPlayer[]; + app?: MetadataTwitterApp; +}; +export type MetadataIconDescriptor = string | { + url: string; + rel?: string; + type?: string; + sizes?: string; + media?: string; +}; +export type MetadataIcons = { + icon?: MetadataIconDescriptor | MetadataIconDescriptor[]; + shortcut?: MetadataIconDescriptor | MetadataIconDescriptor[]; + apple?: MetadataIconDescriptor | MetadataIconDescriptor[]; + other?: MetadataIconDescriptor | MetadataIconDescriptor[]; +}; +export type MetadataAuthor = { + name?: string; + url?: string; +}; +export type MetadataFormatDetection = { + telephone?: boolean; + date?: boolean; + address?: boolean; + email?: boolean; + url?: boolean; +}; +export type MetadataVerification = { + google?: string | string[]; + yahoo?: string | string[]; + yandex?: string | string[]; + other?: Record; +}; +export type MetadataAppleWebApp = { + title?: string; + statusBarStyle?: 'default' | 'black' | 'black-translucent'; + startupImage?: string | { + url: string; + media?: string; + } | (string | { + url: string; + media?: string; + })[]; +}; +export type MetadataItunes = { + appId: string; + affiliateData?: string; + appArgument?: string; +}; +export type MetadataFacebook = { + appId?: string; + admins?: string | string[]; +}; +export type MetadataPinterest = { + richPin?: boolean; +}; +export type MetadataAppLinks = { + ios?: { + url?: string; + appStoreId?: string; + appName?: string; + }; + android?: { + url?: string; + package?: string; + appName?: string; + }; + web?: { + url?: string; + shouldFallback?: boolean; + }; +}; +export type Metadata = { + title?: string; + description?: string; + applicationName?: string; + keywords?: string | string[]; + generator?: string; + referrer?: 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url'; + authors?: MetadataAuthor | MetadataAuthor[]; + creator?: string; + publisher?: string; + robots?: MetadataRobots; + alternates?: MetadataAlternates; + openGraph?: MetadataOpenGraph; + twitter?: MetadataTwitter; + icons?: MetadataIcons; + formatDetection?: MetadataFormatDetection; + verification?: MetadataVerification; + appleWebApp?: MetadataAppleWebApp; + itunes?: MetadataItunes; + appLinks?: MetadataAppLinks; + archives?: string[]; + assets?: string[]; + bookmarks?: string[]; + category?: string; + facebook?: MetadataFacebook; + pinterest?: MetadataPinterest; + manifest?: string; + other?: Record; +}; +export type GenerateMetadataFunction = (request: ImmutableRequest, params: Record) => Metadata | null | undefined | Promise; diff --git a/packages/expo-server/build/cjs/metadata.js b/packages/expo-server/build/cjs/metadata.js new file mode 100644 index 00000000000000..53ddc111b79ed6 --- /dev/null +++ b/packages/expo-server/build/cjs/metadata.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=metadata.js.map \ No newline at end of file diff --git a/packages/expo-server/build/cjs/metadata.js.map b/packages/expo-server/build/cjs/metadata.js.map new file mode 100644 index 00000000000000..c8391686517ce8 --- /dev/null +++ b/packages/expo-server/build/cjs/metadata.js.map @@ -0,0 +1 @@ +{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/metadata.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/packages/expo-server/build/cjs/rendering.d.ts b/packages/expo-server/build/cjs/rendering.d.ts index 5b8df2ecea0bfe..bfa5aa9f8c4e2d 100644 --- a/packages/expo-server/build/cjs/rendering.d.ts +++ b/packages/expo-server/build/cjs/rendering.d.ts @@ -1,22 +1,38 @@ import { type ImmutableRequest } from './ImmutableRequest'; import type { AssetInfo, GetStaticContentOptions } from './manifest'; +import type { Metadata } from './metadata'; +export interface MatchedRouteMetadata { + file: string; + page: string; +} +export interface ResolvedMetadata { + metadata: Metadata; + headTags: string; +} +export interface ResolveMetadataOptions { + route: MatchedRouteMetadata; + request: ImmutableRequest; + params: Record; +} /** * The SSR render module exported from `_expo/server/render.js`. * * {@link import('@expo/router-server/src/static/renderStaticContent')} */ export interface ServerRenderModule { - /** {@link import('@expo/router-server/src/static/renderStaticContent').getStaticContent} */ - getStaticContent(location: URL, options?: GetStaticContentOptions): Promise; + resolveMetadata?(options: ResolveMetadataOptions): Promise; + /** {@link import('@expo/router-server/src/static/renderStaticContent').getStreamingContent} */ + getStreamingContent(location: URL, options?: GetStaticContentOptions): Promise>; } export interface RenderOptions { loader?: { data: unknown; key: string; }; + metadata?: ResolvedMetadata | null; assets?: AssetInfo; } -export type SsrRenderFn = (request: Request, options?: RenderOptions) => Promise; +export type SsrRenderFn = (request: Request, options?: RenderOptions) => Promise>; /** Module exported from loader bundle, typically `_expo/loaders/[ROUTE].js` */ export interface LoaderModule { loader(request: ImmutableRequest | undefined, params: Record): Promise | unknown; diff --git a/packages/expo-server/build/cjs/types.d.ts b/packages/expo-server/build/cjs/types.d.ts index 65e023860f3210..05a8e100ee0dc6 100644 --- a/packages/expo-server/build/cjs/types.d.ts +++ b/packages/expo-server/build/cjs/types.d.ts @@ -1,4 +1,5 @@ import type { _ImmutableHeaders, _ImmutableRequest } from './ImmutableRequest'; +import type { GenerateMetadataFunction, Metadata, MetadataIconDescriptor, MetadataImage, MetadataValue, MetadataValueArray } from './metadata'; /** * An immutable version of the Fetch API's `Headers` object. It cannot be mutated or modified. */ @@ -82,3 +83,4 @@ export interface MiddlewareSettings { * @see [Data loaders](/router/web/data-loaders) for more information. */ export type LoaderFunction = (request: ImmutableRequest | undefined, params: Record) => Promise | T; +export type { GenerateMetadataFunction, Metadata, MetadataIconDescriptor, MetadataImage, MetadataValue, MetadataValueArray, }; diff --git a/packages/expo-server/build/cjs/vendor/abstract.d.ts b/packages/expo-server/build/cjs/vendor/abstract.d.ts index be2cdc116486c6..470a016e3c6cc3 100644 --- a/packages/expo-server/build/cjs/vendor/abstract.d.ts +++ b/packages/expo-server/build/cjs/vendor/abstract.d.ts @@ -32,7 +32,7 @@ export interface RequestHandlerParams { beforeAPIResponse?: BeforeResponseCallback; } export interface RequestHandlerInput { - getHtml(request: Request, route: Route): Promise; + getHtml(request: Request, route: Route): Promise; getRoutesManifest(): Promise; getApiRoute(route: Route): Promise; getMiddleware(route: MiddlewareInfo): Promise; diff --git a/packages/expo-server/build/cjs/vendor/abstract.js b/packages/expo-server/build/cjs/vendor/abstract.js index d9b2d9d7005eb4..4e03cfc820cd0a 100644 --- a/packages/expo-server/build/cjs/vendor/abstract.js +++ b/packages/expo-server/build/cjs/vendor/abstract.js @@ -202,6 +202,14 @@ function createRequestHandler({ getRoutesManifest, getHtml, getApiRoute, getMidd // Only used for development errors return html; } + if (html != null) { + return createResponse('notFoundHtml', route, html, { + status: 404, + headers: new Headers({ + 'Content-Type': 'text/html', + }), + }); + } throw new ExpoError(`HTML route file ${route.page}.html could not be loaded`); } async function respondAPI(mod, request, route) { @@ -241,6 +249,14 @@ function createRequestHandler({ getRoutesManifest, getHtml, getApiRoute, getMidd // Only used for development error responses return html; } + if (html != null) { + return createResponse('html', route, html, { + status: 200, + headers: new Headers({ + 'Content-Type': 'text/html', + }), + }); + } throw new ExpoError(`HTML route file ${route.page}.html could not be loaded`); } function respondRedirect(url, request, route) { diff --git a/packages/expo-server/build/cjs/vendor/abstract.js.map b/packages/expo-server/build/cjs/vendor/abstract.js.map index c9a6e4f920b64c..091c84cfc524cf 100644 --- a/packages/expo-server/build/cjs/vendor/abstract.js.map +++ b/packages/expo-server/build/cjs/vendor/abstract.js.map @@ -1 +1 @@ -{"version":3,"file":"abstract.js","sourceRoot":"","sources":["../../../src/vendor/abstract.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"abstract.js","sourceRoot":"","sources":["../../../src/vendor/abstract.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/expo-server/build/cjs/vendor/environment/common.d.ts b/packages/expo-server/build/cjs/vendor/environment/common.d.ts index 8a3573f757bdff..a0812a6f7355ca 100644 --- a/packages/expo-server/build/cjs/vendor/environment/common.d.ts +++ b/packages/expo-server/build/cjs/vendor/environment/common.d.ts @@ -7,7 +7,7 @@ interface EnvironmentInput { } export interface CommonEnvironment { getRoutesManifest(): Promise; - getHtml(request: Request, route: Route): Promise; + getHtml(request: Request, route: Route): Promise; getApiRoute(route: Route): Promise; getMiddleware(middleware: MiddlewareInfo): Promise; getLoaderData(request: Request, route: Route): Promise; diff --git a/packages/expo-server/build/cjs/vendor/environment/common.js b/packages/expo-server/build/cjs/vendor/environment/common.js index 145d1193e92267..50aecf010371f7 100644 --- a/packages/expo-server/build/cjs/vendor/environment/common.js +++ b/packages/expo-server/build/cjs/vendor/environment/common.js @@ -31,6 +31,7 @@ function initManifestRegExp(manifest) { function createEnvironment(input) { // Cached manifest and SSR renderer, initialized on first request let cachedManifest; + let cachedSsrModule = null; let ssrRenderer = null; async function getRoutesManifest() { if (cachedManifest === undefined || input.isDevelopment) { @@ -41,11 +42,17 @@ function createEnvironment(input) { } async function getServerRenderer() { if (ssrRenderer && !input.isDevelopment) { - return ssrRenderer; + return { + renderer: ssrRenderer, + module: cachedSsrModule, + }; } const manifest = await getRoutesManifest(); if (manifest?.rendering?.mode !== 'ssr') { - return null; + return { + renderer: null, + module: null, + }; } // If `manifest.rendering.mode === 'ssr'`, we always expect the SSR rendering module to be // available @@ -54,17 +61,22 @@ function createEnvironment(input) { throw new Error(`SSR module not found at: ${manifest.rendering.file}`); } const topLevelAssets = manifest.assets; + cachedSsrModule = ssrModule; ssrRenderer = async (request, options) => { const url = new URL(request.url); const location = new URL(url.pathname + url.search, url.origin); const assets = mergeAssets(topLevelAssets, options?.assets); - return ssrModule.getStaticContent(location, { + return ssrModule.getStreamingContent(location, { loader: options?.loader, + metadata: options?.metadata, request, assets, }); }; - return ssrRenderer; + return { + renderer: ssrRenderer, + module: ssrModule, + }; } async function executeLoader(request, route, params) { if (!route.loader) { @@ -80,16 +92,26 @@ function createEnvironment(input) { getRoutesManifest, async getHtml(request, route) { // SSR path: Render at runtime if SSR module is available - const renderer = await getServerRenderer(); + const { renderer, module: ssrModule } = await getServerRenderer(); if (renderer) { let renderOptions = { assets: route.assets }; + const params = (0, matchers_1.parseParams)(request, route); try { + if (ssrModule?.resolveMetadata) { + renderOptions.metadata = await ssrModule.resolveMetadata({ + route: { + file: route.file, + page: route.page, + }, + request: new ImmutableRequest_1.ImmutableRequest(request), + params, + }); + } if (route.loader) { - const params = (0, matchers_1.parseParams)(request, route); const result = await executeLoader(request, route, params); const data = (0, matchers_1.isResponse)(result) ? await result.json() : result; renderOptions = { - assets: route.assets, + ...renderOptions, loader: { data: data ?? null, key: (0, matchers_1.resolveLoaderContextKey)(route.page, params), diff --git a/packages/expo-server/build/cjs/vendor/environment/common.js.map b/packages/expo-server/build/cjs/vendor/environment/common.js.map index 58f5444038313d..16fa8f7de0269f 100644 --- a/packages/expo-server/build/cjs/vendor/environment/common.js.map +++ b/packages/expo-server/build/cjs/vendor/environment/common.js.map @@ -1 +1 @@ -{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../src/vendor/environment/common.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../src/vendor/environment/common.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/expo-server/build/mjs/manifest.d.ts b/packages/expo-server/build/mjs/manifest.d.ts index 5da88ab3fe0cce..6834b30572e2e0 100644 --- a/packages/expo-server/build/mjs/manifest.d.ts +++ b/packages/expo-server/build/mjs/manifest.d.ts @@ -103,6 +103,9 @@ export interface GetStaticContentOptions { data?: unknown; key: string; }; + metadata?: { + headTags: string; + } | null; request?: Request; assets?: AssetInfo; } diff --git a/packages/expo-server/build/mjs/metadata.d.ts b/packages/expo-server/build/mjs/metadata.d.ts new file mode 100644 index 00000000000000..7dc02442965329 --- /dev/null +++ b/packages/expo-server/build/mjs/metadata.d.ts @@ -0,0 +1,207 @@ +import type { ImmutableRequest } from './types'; +export type MetadataValue = string | number | boolean; +export type MetadataValueArray = MetadataValue[]; +export type MetadataGoogleBot = string | { + index?: boolean; + follow?: boolean; + noimageindex?: boolean; + 'max-video-preview'?: number; + 'max-image-preview'?: 'none' | 'standard' | 'large'; + 'max-snippet'?: number; +}; +export type MetadataRobots = string | { + index?: boolean; + follow?: boolean; + noarchive?: boolean; + nosnippet?: boolean; + noimageindex?: boolean; + nocache?: boolean; + notranslate?: boolean; + unavailableAfter?: string; + googleBot?: MetadataGoogleBot; + [key: string]: MetadataValue | MetadataGoogleBot | undefined; +}; +export type MetadataAlternates = { + canonical?: string; + languages?: Record; + media?: Record; + types?: Record; +}; +export type MetadataImage = string | { + url: string; + alt?: string; + width?: number; + height?: number; + type?: string; + secureUrl?: string; +}; +export type MetadataVideo = string | { + url: string; + secureUrl?: string; + type?: string; + width?: number; + height?: number; +}; +export type MetadataAudio = string | { + url: string; + secureUrl?: string; + type?: string; +}; +export type MetadataOpenGraph = { + title?: string; + description?: string; + url?: string; + siteName?: string; + locale?: string; + type?: string; + images?: MetadataImage | MetadataImage[]; + videos?: MetadataVideo | MetadataVideo[]; + audio?: MetadataAudio | MetadataAudio[]; + emails?: string[]; + phoneNumbers?: string[]; + faxNumbers?: string[]; + alternateLocale?: string[]; + countryName?: string; + determiner?: 'a' | 'an' | 'the' | 'auto' | ''; + ttl?: number; + publishedTime?: string; + modifiedTime?: string; + expirationTime?: string; + section?: string; + tags?: string[]; + authors?: string[]; +}; +export type MetadataTwitterImage = string | { + url: string; + alt?: string; +}; +export type MetadataTwitterPlayer = { + url: string; + width?: number; + height?: number; + stream?: string; +}; +export type MetadataTwitterApp = { + name?: string; + id?: { + iphone?: string; + ipad?: string; + googleplay?: string; + }; + url?: { + iphone?: string; + ipad?: string; + googleplay?: string; + }; +}; +export type MetadataTwitter = { + card?: 'summary' | 'summary_large_image' | 'player' | 'app'; + title?: string; + description?: string; + site?: string; + siteId?: string; + creator?: string; + creatorId?: string; + images?: MetadataTwitterImage | MetadataTwitterImage[]; + players?: MetadataTwitterPlayer | MetadataTwitterPlayer[]; + app?: MetadataTwitterApp; +}; +export type MetadataIconDescriptor = string | { + url: string; + rel?: string; + type?: string; + sizes?: string; + media?: string; +}; +export type MetadataIcons = { + icon?: MetadataIconDescriptor | MetadataIconDescriptor[]; + shortcut?: MetadataIconDescriptor | MetadataIconDescriptor[]; + apple?: MetadataIconDescriptor | MetadataIconDescriptor[]; + other?: MetadataIconDescriptor | MetadataIconDescriptor[]; +}; +export type MetadataAuthor = { + name?: string; + url?: string; +}; +export type MetadataFormatDetection = { + telephone?: boolean; + date?: boolean; + address?: boolean; + email?: boolean; + url?: boolean; +}; +export type MetadataVerification = { + google?: string | string[]; + yahoo?: string | string[]; + yandex?: string | string[]; + other?: Record; +}; +export type MetadataAppleWebApp = { + title?: string; + statusBarStyle?: 'default' | 'black' | 'black-translucent'; + startupImage?: string | { + url: string; + media?: string; + } | (string | { + url: string; + media?: string; + })[]; +}; +export type MetadataItunes = { + appId: string; + affiliateData?: string; + appArgument?: string; +}; +export type MetadataFacebook = { + appId?: string; + admins?: string | string[]; +}; +export type MetadataPinterest = { + richPin?: boolean; +}; +export type MetadataAppLinks = { + ios?: { + url?: string; + appStoreId?: string; + appName?: string; + }; + android?: { + url?: string; + package?: string; + appName?: string; + }; + web?: { + url?: string; + shouldFallback?: boolean; + }; +}; +export type Metadata = { + title?: string; + description?: string; + applicationName?: string; + keywords?: string | string[]; + generator?: string; + referrer?: 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url'; + authors?: MetadataAuthor | MetadataAuthor[]; + creator?: string; + publisher?: string; + robots?: MetadataRobots; + alternates?: MetadataAlternates; + openGraph?: MetadataOpenGraph; + twitter?: MetadataTwitter; + icons?: MetadataIcons; + formatDetection?: MetadataFormatDetection; + verification?: MetadataVerification; + appleWebApp?: MetadataAppleWebApp; + itunes?: MetadataItunes; + appLinks?: MetadataAppLinks; + archives?: string[]; + assets?: string[]; + bookmarks?: string[]; + category?: string; + facebook?: MetadataFacebook; + pinterest?: MetadataPinterest; + manifest?: string; + other?: Record; +}; +export type GenerateMetadataFunction = (request: ImmutableRequest, params: Record) => Metadata | null | undefined | Promise; diff --git a/packages/expo-server/build/mjs/metadata.js b/packages/expo-server/build/mjs/metadata.js new file mode 100644 index 00000000000000..417da8c9bd278b --- /dev/null +++ b/packages/expo-server/build/mjs/metadata.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=metadata.js.map \ No newline at end of file diff --git a/packages/expo-server/build/mjs/metadata.js.map b/packages/expo-server/build/mjs/metadata.js.map new file mode 100644 index 00000000000000..c8391686517ce8 --- /dev/null +++ b/packages/expo-server/build/mjs/metadata.js.map @@ -0,0 +1 @@ +{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/metadata.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/packages/expo-server/build/mjs/rendering.d.ts b/packages/expo-server/build/mjs/rendering.d.ts index 5b8df2ecea0bfe..bfa5aa9f8c4e2d 100644 --- a/packages/expo-server/build/mjs/rendering.d.ts +++ b/packages/expo-server/build/mjs/rendering.d.ts @@ -1,22 +1,38 @@ import { type ImmutableRequest } from './ImmutableRequest'; import type { AssetInfo, GetStaticContentOptions } from './manifest'; +import type { Metadata } from './metadata'; +export interface MatchedRouteMetadata { + file: string; + page: string; +} +export interface ResolvedMetadata { + metadata: Metadata; + headTags: string; +} +export interface ResolveMetadataOptions { + route: MatchedRouteMetadata; + request: ImmutableRequest; + params: Record; +} /** * The SSR render module exported from `_expo/server/render.js`. * * {@link import('@expo/router-server/src/static/renderStaticContent')} */ export interface ServerRenderModule { - /** {@link import('@expo/router-server/src/static/renderStaticContent').getStaticContent} */ - getStaticContent(location: URL, options?: GetStaticContentOptions): Promise; + resolveMetadata?(options: ResolveMetadataOptions): Promise; + /** {@link import('@expo/router-server/src/static/renderStaticContent').getStreamingContent} */ + getStreamingContent(location: URL, options?: GetStaticContentOptions): Promise>; } export interface RenderOptions { loader?: { data: unknown; key: string; }; + metadata?: ResolvedMetadata | null; assets?: AssetInfo; } -export type SsrRenderFn = (request: Request, options?: RenderOptions) => Promise; +export type SsrRenderFn = (request: Request, options?: RenderOptions) => Promise>; /** Module exported from loader bundle, typically `_expo/loaders/[ROUTE].js` */ export interface LoaderModule { loader(request: ImmutableRequest | undefined, params: Record): Promise | unknown; diff --git a/packages/expo-server/build/mjs/types.d.ts b/packages/expo-server/build/mjs/types.d.ts index 65e023860f3210..05a8e100ee0dc6 100644 --- a/packages/expo-server/build/mjs/types.d.ts +++ b/packages/expo-server/build/mjs/types.d.ts @@ -1,4 +1,5 @@ import type { _ImmutableHeaders, _ImmutableRequest } from './ImmutableRequest'; +import type { GenerateMetadataFunction, Metadata, MetadataIconDescriptor, MetadataImage, MetadataValue, MetadataValueArray } from './metadata'; /** * An immutable version of the Fetch API's `Headers` object. It cannot be mutated or modified. */ @@ -82,3 +83,4 @@ export interface MiddlewareSettings { * @see [Data loaders](/router/web/data-loaders) for more information. */ export type LoaderFunction = (request: ImmutableRequest | undefined, params: Record) => Promise | T; +export type { GenerateMetadataFunction, Metadata, MetadataIconDescriptor, MetadataImage, MetadataValue, MetadataValueArray, }; diff --git a/packages/expo-server/build/mjs/vendor/abstract.d.ts b/packages/expo-server/build/mjs/vendor/abstract.d.ts index be2cdc116486c6..470a016e3c6cc3 100644 --- a/packages/expo-server/build/mjs/vendor/abstract.d.ts +++ b/packages/expo-server/build/mjs/vendor/abstract.d.ts @@ -32,7 +32,7 @@ export interface RequestHandlerParams { beforeAPIResponse?: BeforeResponseCallback; } export interface RequestHandlerInput { - getHtml(request: Request, route: Route): Promise; + getHtml(request: Request, route: Route): Promise; getRoutesManifest(): Promise; getApiRoute(route: Route): Promise; getMiddleware(route: MiddlewareInfo): Promise; diff --git a/packages/expo-server/build/mjs/vendor/abstract.js b/packages/expo-server/build/mjs/vendor/abstract.js index 754a1a89a9816a..07a4ee8d143f10 100644 --- a/packages/expo-server/build/mjs/vendor/abstract.js +++ b/packages/expo-server/build/mjs/vendor/abstract.js @@ -197,6 +197,14 @@ export function createRequestHandler({ getRoutesManifest, getHtml, getApiRoute, // Only used for development errors return html; } + if (html != null) { + return createResponse('notFoundHtml', route, html, { + status: 404, + headers: new Headers({ + 'Content-Type': 'text/html', + }), + }); + } throw new ExpoError(`HTML route file ${route.page}.html could not be loaded`); } async function respondAPI(mod, request, route) { @@ -236,6 +244,14 @@ export function createRequestHandler({ getRoutesManifest, getHtml, getApiRoute, // Only used for development error responses return html; } + if (html != null) { + return createResponse('html', route, html, { + status: 200, + headers: new Headers({ + 'Content-Type': 'text/html', + }), + }); + } throw new ExpoError(`HTML route file ${route.page}.html could not be loaded`); } function respondRedirect(url, request, route) { diff --git a/packages/expo-server/build/mjs/vendor/abstract.js.map b/packages/expo-server/build/mjs/vendor/abstract.js.map index b7b992cfda9925..e2dfaf344f1206 100644 --- a/packages/expo-server/build/mjs/vendor/abstract.js.map +++ b/packages/expo-server/build/mjs/vendor/abstract.js.map @@ -1 +1 @@ -{"version":3,"file":"abstract.js","sourceRoot":"","sources":["../../../src/vendor/abstract.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"abstract.js","sourceRoot":"","sources":["../../../src/vendor/abstract.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/expo-server/build/mjs/vendor/environment/common.d.ts b/packages/expo-server/build/mjs/vendor/environment/common.d.ts index 8a3573f757bdff..a0812a6f7355ca 100644 --- a/packages/expo-server/build/mjs/vendor/environment/common.d.ts +++ b/packages/expo-server/build/mjs/vendor/environment/common.d.ts @@ -7,7 +7,7 @@ interface EnvironmentInput { } export interface CommonEnvironment { getRoutesManifest(): Promise; - getHtml(request: Request, route: Route): Promise; + getHtml(request: Request, route: Route): Promise; getApiRoute(route: Route): Promise; getMiddleware(middleware: MiddlewareInfo): Promise; getLoaderData(request: Request, route: Route): Promise; diff --git a/packages/expo-server/build/mjs/vendor/environment/common.js b/packages/expo-server/build/mjs/vendor/environment/common.js index d4ccb0c04b57ef..3bb5b4ac744475 100644 --- a/packages/expo-server/build/mjs/vendor/environment/common.js +++ b/packages/expo-server/build/mjs/vendor/environment/common.js @@ -28,6 +28,7 @@ function initManifestRegExp(manifest) { export function createEnvironment(input) { // Cached manifest and SSR renderer, initialized on first request let cachedManifest; + let cachedSsrModule = null; let ssrRenderer = null; async function getRoutesManifest() { if (cachedManifest === undefined || input.isDevelopment) { @@ -38,11 +39,17 @@ export function createEnvironment(input) { } async function getServerRenderer() { if (ssrRenderer && !input.isDevelopment) { - return ssrRenderer; + return { + renderer: ssrRenderer, + module: cachedSsrModule, + }; } const manifest = await getRoutesManifest(); if (manifest?.rendering?.mode !== 'ssr') { - return null; + return { + renderer: null, + module: null, + }; } // If `manifest.rendering.mode === 'ssr'`, we always expect the SSR rendering module to be // available @@ -51,17 +58,22 @@ export function createEnvironment(input) { throw new Error(`SSR module not found at: ${manifest.rendering.file}`); } const topLevelAssets = manifest.assets; + cachedSsrModule = ssrModule; ssrRenderer = async (request, options) => { const url = new URL(request.url); const location = new URL(url.pathname + url.search, url.origin); const assets = mergeAssets(topLevelAssets, options?.assets); - return ssrModule.getStaticContent(location, { + return ssrModule.getStreamingContent(location, { loader: options?.loader, + metadata: options?.metadata, request, assets, }); }; - return ssrRenderer; + return { + renderer: ssrRenderer, + module: ssrModule, + }; } async function executeLoader(request, route, params) { if (!route.loader) { @@ -77,16 +89,26 @@ export function createEnvironment(input) { getRoutesManifest, async getHtml(request, route) { // SSR path: Render at runtime if SSR module is available - const renderer = await getServerRenderer(); + const { renderer, module: ssrModule } = await getServerRenderer(); if (renderer) { let renderOptions = { assets: route.assets }; + const params = parseParams(request, route); try { + if (ssrModule?.resolveMetadata) { + renderOptions.metadata = await ssrModule.resolveMetadata({ + route: { + file: route.file, + page: route.page, + }, + request: new ImmutableRequest(request), + params, + }); + } if (route.loader) { - const params = parseParams(request, route); const result = await executeLoader(request, route, params); const data = isResponse(result) ? await result.json() : result; renderOptions = { - assets: route.assets, + ...renderOptions, loader: { data: data ?? null, key: resolveLoaderContextKey(route.page, params), diff --git a/packages/expo-server/build/mjs/vendor/environment/common.js.map b/packages/expo-server/build/mjs/vendor/environment/common.js.map index d56a891de2246f..17bec12626e1e9 100644 --- a/packages/expo-server/build/mjs/vendor/environment/common.js.map +++ b/packages/expo-server/build/mjs/vendor/environment/common.js.map @@ -1 +1 @@ -{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../src/vendor/environment/common.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../src/vendor/environment/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAExF,SAAS,kBAAkB,CAAC,QAAqB;IAC/C,OAAO;QACL,GAAG,QAAQ;QACX,UAAU,EACR,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACnC,GAAG,KAAK;YACR,UAAU,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;SACzC,CAAC,CAAC,IAAI,EAAE;QACX,SAAS,EACP,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAClC,GAAG,KAAK;YACR,UAAU,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;SACzC,CAAC,CAAC,IAAI,EAAE;QACX,cAAc,EACZ,QAAQ,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACvC,GAAG,KAAK;YACR,UAAU,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;SACzC,CAAC,CAAC,IAAI,EAAE;QACX,SAAS,EACP,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAClC,GAAG,KAAK;YACR,UAAU,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;SACzC,CAAC,CAAC,IAAI,EAAE;QACX,QAAQ,EACN,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACjC,GAAG,KAAK;YACR,UAAU,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;SACzC,CAAC,CAAC,IAAI,EAAE;KACZ,CAAC;AACJ,CAAC;AAkBD,MAAM,UAAU,iBAAiB,CAAC,KAAuB;IACvD,iEAAiE;IACjE,IAAI,cAA2C,CAAC;IAChD,IAAI,eAAe,GAA8B,IAAI,CAAC;IACtD,IAAI,WAAW,GAAuB,IAAI,CAAC;IAE3C,KAAK,UAAU,iBAAiB;QAC9B,IAAI,cAAc,KAAK,SAAS,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;YACxD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;YACvD,cAAc,GAAG,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAmB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACzE,CAAC;QACD,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,KAAK,UAAU,iBAAiB;QAI9B,IAAI,WAAW,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;YACxC,OAAO;gBACL,QAAQ,EAAE,WAAW;gBACrB,MAAM,EAAE,eAAe;aACxB,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAC3C,IAAI,QAAQ,EAAE,SAAS,EAAE,IAAI,KAAK,KAAK,EAAE,CAAC;YACxC,OAAO;gBACL,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,IAAI;aACb,CAAC;QACJ,CAAC;QAED,0FAA0F;QAC1F,YAAY;QACZ,MAAM,SAAS,GAAG,CAAC,MAAM,KAAK,CAAC,UAAU,CACvC,QAAQ,CAAC,SAAS,CAAC,IAAI,CACxB,CAA8B,CAAC;QAEhC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC;QACvC,eAAe,GAAG,SAAS,CAAC;QAC5B,WAAW,GAAG,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;YACvC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACjC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YAChE,MAAM,MAAM,GAAG,WAAW,CAAC,cAAc,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;YAE5D,OAAO,SAAS,CAAC,mBAAmB,CAAC,QAAQ,EAAE;gBAC7C,MAAM,EAAE,OAAO,EAAE,MAAM;gBACvB,QAAQ,EAAE,OAAO,EAAE,QAAQ;gBAC3B,OAAO;gBACP,MAAM;aACP,CAAC,CAAC;QACL,CAAC,CAAC;QACF,OAAO;YACL,QAAQ,EAAE,WAAW;YACrB,MAAM,EAAE,SAAS;SAClB,CAAC;IACJ,CAAC;IAED,KAAK,UAAU,aAAa,CAC1B,OAAgB,EAChB,KAAY,EACZ,MAA8B;QAE9B,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAClB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,YAAY,GAAG,CAAC,MAAM,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAwB,CAAC;QACnF,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,+BAA+B,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,OAAO,YAAY,CAAC,MAAM,CAAC,IAAI,gBAAgB,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IACpE,CAAC;IAED,OAAO;QACL,iBAAiB;QAEjB,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK;YAC1B,yDAAyD;YACzD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;YAClE,IAAI,QAAQ,EAAE,CAAC;gBACb,IAAI,aAAa,GAAkB,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC5D,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;gBAE3C,IAAI,CAAC;oBACH,IAAI,SAAS,EAAE,eAAe,EAAE,CAAC;wBAC/B,aAAa,CAAC,QAAQ,GAAG,MAAM,SAAS,CAAC,eAAe,CAAC;4BACvD,KAAK,EAAE;gCACL,IAAI,EAAE,KAAK,CAAC,IAAI;gCAChB,IAAI,EAAE,KAAK,CAAC,IAAI;6BACjB;4BACD,OAAO,EAAE,IAAI,gBAAgB,CAAC,OAAO,CAAC;4BACtC,MAAM;yBACP,CAAC,CAAC;oBACL,CAAC;oBAED,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;wBACjB,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;wBAC3D,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;wBAC/D,aAAa,GAAG;4BACd,GAAG,aAAa;4BAChB,MAAM,EAAE;gCACN,IAAI,EAAE,IAAI,IAAI,IAAI;gCAClB,GAAG,EAAE,uBAAuB,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC;6BACjD;yBACF,CAAC;oBACJ,CAAC;oBACD,OAAO,MAAM,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;gBAChD,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;oBAC1C,MAAM,KAAK,CAAC;gBACd,CAAC;YACH,CAAC;YAED,iDAAiD;YACjD,IAAI,IAAmB,CAAC;YACxB,IAAI,CAAC,IAAI,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;gBAChE,OAAO,IAAI,CAAC;YACd,CAAC;YACD,uDAAuD;YACvD,+CAA+C;YAC/C,MAAM,UAAU,GAAG,QAAQ,CAAC;YAC5B,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;gBAC7E,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;gBACrD,IAAI,CAAC,IAAI,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;oBAC1D,OAAO,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,KAAK;YACrB,OAAO,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,UAAU;YAC5B,MAAM,GAAG,GAAG,CAAC,MAAM,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAQ,CAAC;YAC7D,IAAI,OAAO,GAAG,EAAE,OAAO,KAAK,UAAU,EAAE,CAAC;gBACvC,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK;YAChC,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;YAE3D,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvB,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC;QACvC,CAAC;QAED,KAAK,CAAC,OAAO;YACX,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;gBACxB,OAAO;YACT,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,iBAAiB,EAAE,CAAC;YAC3C,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,QAAQ,GAAa,EAAE,CAAC;gBAC9B,IAAI,QAAQ,CAAC,UAAU;oBAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;gBACjE,IAAI,QAAQ,CAAC,SAAS;oBAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;gBAC/D,KAAK,MAAM,QAAQ,IAAI,QAAQ,CAAC,SAAS;oBAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACxE,KAAK,MAAM,SAAS,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;oBAC5C,IAAI,SAAS,CAAC,MAAM;wBAAE,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;gBACxD,CAAC;gBACD,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,QAAoB,EAAE,UAAsB;IAC/D,OAAO;QACL,GAAG,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;QAC3D,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;KACzD,CAAC;AACJ,CAAC"} \ No newline at end of file diff --git a/packages/expo-server/src/manifest.ts b/packages/expo-server/src/manifest.ts index 1e11633e4d6c21..daa3124df0cfcb 100644 --- a/packages/expo-server/src/manifest.ts +++ b/packages/expo-server/src/manifest.ts @@ -107,6 +107,7 @@ export type Route = RouteInfo; */ export interface GetStaticContentOptions { loader?: { data?: unknown; key: string }; + metadata?: { headTags: string } | null; request?: Request; assets?: AssetInfo; } diff --git a/packages/expo-server/src/metadata.ts b/packages/expo-server/src/metadata.ts new file mode 100644 index 00000000000000..506b98599519df --- /dev/null +++ b/packages/expo-server/src/metadata.ts @@ -0,0 +1,263 @@ +import type { ImmutableRequest } from './types'; + +export type MetadataValue = string | number | boolean; +export type MetadataValueArray = MetadataValue[]; + +export type MetadataGoogleBot = + | string + | { + index?: boolean; + follow?: boolean; + noimageindex?: boolean; + 'max-video-preview'?: number; + 'max-image-preview'?: 'none' | 'standard' | 'large'; + 'max-snippet'?: number; + }; + +export type MetadataRobots = + | string + | { + index?: boolean; + follow?: boolean; + noarchive?: boolean; + nosnippet?: boolean; + noimageindex?: boolean; + nocache?: boolean; + notranslate?: boolean; + unavailableAfter?: string; + googleBot?: MetadataGoogleBot; + [key: string]: MetadataValue | MetadataGoogleBot | undefined; + }; + +export type MetadataAlternates = { + canonical?: string; + languages?: Record; + media?: Record; + types?: Record; +}; + +export type MetadataImage = + | string + | { + url: string; + alt?: string; + width?: number; + height?: number; + type?: string; + secureUrl?: string; + }; + +export type MetadataVideo = + | string + | { + url: string; + secureUrl?: string; + type?: string; + width?: number; + height?: number; + }; + +export type MetadataAudio = + | string + | { + url: string; + secureUrl?: string; + type?: string; + }; + +export type MetadataOpenGraph = { + title?: string; + description?: string; + url?: string; + siteName?: string; + locale?: string; + type?: string; + images?: MetadataImage | MetadataImage[]; + videos?: MetadataVideo | MetadataVideo[]; + audio?: MetadataAudio | MetadataAudio[]; + emails?: string[]; + phoneNumbers?: string[]; + faxNumbers?: string[]; + alternateLocale?: string[]; + countryName?: string; + determiner?: 'a' | 'an' | 'the' | 'auto' | ''; + ttl?: number; + // Article-specific fields (used when type === 'article') + publishedTime?: string; + modifiedTime?: string; + expirationTime?: string; + section?: string; + tags?: string[]; + authors?: string[]; +}; + +export type MetadataTwitterImage = + | string + | { + url: string; + alt?: string; + }; + +export type MetadataTwitterPlayer = { + url: string; + width?: number; + height?: number; + stream?: string; +}; + +export type MetadataTwitterApp = { + name?: string; + id?: { + iphone?: string; + ipad?: string; + googleplay?: string; + }; + url?: { + iphone?: string; + ipad?: string; + googleplay?: string; + }; +}; + +export type MetadataTwitter = { + card?: 'summary' | 'summary_large_image' | 'player' | 'app'; + title?: string; + description?: string; + site?: string; + siteId?: string; + creator?: string; + creatorId?: string; + images?: MetadataTwitterImage | MetadataTwitterImage[]; + players?: MetadataTwitterPlayer | MetadataTwitterPlayer[]; + app?: MetadataTwitterApp; +}; + +export type MetadataIconDescriptor = + | string + | { + url: string; + rel?: string; + type?: string; + sizes?: string; + media?: string; + }; + +export type MetadataIcons = { + icon?: MetadataIconDescriptor | MetadataIconDescriptor[]; + shortcut?: MetadataIconDescriptor | MetadataIconDescriptor[]; + apple?: MetadataIconDescriptor | MetadataIconDescriptor[]; + other?: MetadataIconDescriptor | MetadataIconDescriptor[]; +}; + +export type MetadataAuthor = { + name?: string; + url?: string; +}; + +export type MetadataFormatDetection = { + telephone?: boolean; + date?: boolean; + address?: boolean; + email?: boolean; + url?: boolean; +}; + +export type MetadataVerification = { + google?: string | string[]; + yahoo?: string | string[]; + yandex?: string | string[]; + other?: Record; +}; + +export type MetadataAppleWebApp = { + title?: string; + statusBarStyle?: 'default' | 'black' | 'black-translucent'; + startupImage?: + | string + | { + url: string; + media?: string; + } + | ( + | string + | { + url: string; + media?: string; + } + )[]; +}; + +export type MetadataItunes = { + appId: string; + affiliateData?: string; + appArgument?: string; +}; + +export type MetadataFacebook = { + appId?: string; + admins?: string | string[]; +}; + +export type MetadataPinterest = { + richPin?: boolean; +}; + +export type MetadataAppLinks = { + ios?: { + url?: string; + appStoreId?: string; + appName?: string; + }; + android?: { + url?: string; + package?: string; + appName?: string; + }; + web?: { + url?: string; + shouldFallback?: boolean; + }; +}; + +export type Metadata = { + title?: string; + description?: string; + applicationName?: string; + keywords?: string | string[]; + generator?: string; + referrer?: + | 'no-referrer' + | 'no-referrer-when-downgrade' + | 'origin' + | 'origin-when-cross-origin' + | 'same-origin' + | 'strict-origin' + | 'strict-origin-when-cross-origin' + | 'unsafe-url'; + authors?: MetadataAuthor | MetadataAuthor[]; + creator?: string; + publisher?: string; + robots?: MetadataRobots; + alternates?: MetadataAlternates; + openGraph?: MetadataOpenGraph; + twitter?: MetadataTwitter; + icons?: MetadataIcons; + formatDetection?: MetadataFormatDetection; + verification?: MetadataVerification; + appleWebApp?: MetadataAppleWebApp; + itunes?: MetadataItunes; + appLinks?: MetadataAppLinks; + archives?: string[]; + assets?: string[]; + bookmarks?: string[]; + category?: string; + facebook?: MetadataFacebook; + pinterest?: MetadataPinterest; + manifest?: string; + other?: Record; +}; + +export type GenerateMetadataFunction = ( + request: ImmutableRequest, + params: Record +) => Metadata | null | undefined | Promise; diff --git a/packages/expo-server/src/rendering.ts b/packages/expo-server/src/rendering.ts index e06918d74d4cf0..38da977f8fb7bc 100644 --- a/packages/expo-server/src/rendering.ts +++ b/packages/expo-server/src/rendering.ts @@ -1,5 +1,22 @@ import { type ImmutableRequest } from './ImmutableRequest'; import type { AssetInfo, GetStaticContentOptions } from './manifest'; +import type { Metadata } from './metadata'; + +export interface MatchedRouteMetadata { + file: string; + page: string; +} + +export interface ResolvedMetadata { + metadata: Metadata; + headTags: string; +} + +export interface ResolveMetadataOptions { + route: MatchedRouteMetadata; + request: ImmutableRequest; + params: Record; +} /** * The SSR render module exported from `_expo/server/render.js`. @@ -7,16 +24,24 @@ import type { AssetInfo, GetStaticContentOptions } from './manifest'; * {@link import('@expo/router-server/src/static/renderStaticContent')} */ export interface ServerRenderModule { - /** {@link import('@expo/router-server/src/static/renderStaticContent').getStaticContent} */ - getStaticContent(location: URL, options?: GetStaticContentOptions): Promise; + resolveMetadata?(options: ResolveMetadataOptions): Promise; + /** {@link import('@expo/router-server/src/static/renderStaticContent').getStreamingContent} */ + getStreamingContent( + location: URL, + options?: GetStaticContentOptions + ): Promise>; } export interface RenderOptions { loader?: { data: unknown; key: string }; + metadata?: ResolvedMetadata | null; assets?: AssetInfo; } -export type SsrRenderFn = (request: Request, options?: RenderOptions) => Promise; +export type SsrRenderFn = ( + request: Request, + options?: RenderOptions +) => Promise>; /** Module exported from loader bundle, typically `_expo/loaders/[ROUTE].js` */ export interface LoaderModule { diff --git a/packages/expo-server/src/types.ts b/packages/expo-server/src/types.ts index d428e7979baa47..26ec2e4c211cd0 100644 --- a/packages/expo-server/src/types.ts +++ b/packages/expo-server/src/types.ts @@ -1,4 +1,12 @@ import type { _ImmutableHeaders, _ImmutableRequest } from './ImmutableRequest'; +import type { + GenerateMetadataFunction, + Metadata, + MetadataIconDescriptor, + MetadataImage, + MetadataValue, + MetadataValueArray, +} from './metadata'; /** * An immutable version of the Fetch API's `Headers` object. It cannot be mutated or modified. @@ -93,3 +101,12 @@ export type LoaderFunction = ( request: ImmutableRequest | undefined, params: Record ) => Promise | T; + +export type { + GenerateMetadataFunction, + Metadata, + MetadataIconDescriptor, + MetadataImage, + MetadataValue, + MetadataValueArray, +}; diff --git a/packages/expo-server/src/vendor/__tests__/abstract.test.ts b/packages/expo-server/src/vendor/__tests__/abstract.test.ts index 183b03d038c8fc..9a3e5333149e81 100644 --- a/packages/expo-server/src/vendor/__tests__/abstract.test.ts +++ b/packages/expo-server/src/vendor/__tests__/abstract.test.ts @@ -2,6 +2,68 @@ import type { Manifest } from '../../manifest'; import { createRequestHandler } from '../abstract'; describe(createRequestHandler, () => { + it('returns streamed HTML responses for matched routes', async () => { + const manifest: Manifest = { + htmlRoutes: [ + { + file: 'index', + page: '/', + namedRegex: /^\/$/, + routeKeys: {}, + }, + ], + apiRoutes: [], + notFoundRoutes: [], + redirects: [], + rewrites: [], + }; + + const handler = createRequestHandler({ + getRoutesManifest: jest.fn(async () => manifest), + getHtml: jest.fn(async () => createHtmlStream('streamed')), + getApiRoute: jest.fn(), + getMiddleware: jest.fn(), + getLoaderData: jest.fn(), + }); + + const response = await handler(new Request('http://localhost/')); + + expect(response.status).toBe(200); + expect(response.headers.get('Content-Type')).toBe('text/html'); + expect(await response.text()).toBe('streamed'); + }); + + it('returns streamed 404 HTML responses for matched not-found routes', async () => { + const manifest: Manifest = { + htmlRoutes: [], + apiRoutes: [], + notFoundRoutes: [ + { + file: 'not-found', + page: '/404', + namedRegex: /^\/.*$/, + routeKeys: {}, + }, + ], + redirects: [], + rewrites: [], + }; + + const handler = createRequestHandler({ + getRoutesManifest: jest.fn(async () => manifest), + getHtml: jest.fn(async () => createHtmlStream('missing')), + getApiRoute: jest.fn(), + getMiddleware: jest.fn(), + getLoaderData: jest.fn(), + }); + + const response = await handler(new Request('http://localhost/missing')); + + expect(response.status).toBe(404); + expect(response.headers.get('Content-Type')).toBe('text/html'); + expect(await response.text()).toBe('missing'); + }); + it('applies custom headers from manifest', async () => { const manifest: Manifest = { htmlRoutes: [ @@ -330,3 +392,12 @@ describe(createRequestHandler, () => { }); }); }); + +function createHtmlStream(html: string): ReadableStream { + return new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode(html)); + controller.close(); + }, + }); +} diff --git a/packages/expo-server/src/vendor/abstract.ts b/packages/expo-server/src/vendor/abstract.ts index 875284ace4f2b2..5cace9347c6ced 100644 --- a/packages/expo-server/src/vendor/abstract.ts +++ b/packages/expo-server/src/vendor/abstract.ts @@ -52,7 +52,7 @@ export interface RequestHandlerParams { } export interface RequestHandlerInput { - getHtml(request: Request, route: Route): Promise; + getHtml(request: Request, route: Route): Promise; getRoutesManifest(): Promise; getApiRoute(route: Route): Promise; getMiddleware(route: MiddlewareInfo): Promise; @@ -271,7 +271,7 @@ export function createRequestHandler({ } async function respondNotFoundHTML( - html: string | Response | null, + html: string | ReadableStream | Response | null, route: Route ): Promise { if (typeof html === 'string') { @@ -288,6 +288,15 @@ export function createRequestHandler({ return html; } + if (html != null) { + return createResponse('notFoundHtml', route, html, { + status: 404, + headers: new Headers({ + 'Content-Type': 'text/html', + }), + }); + } + throw new ExpoError(`HTML route file ${route.page}.html could not be loaded`); } @@ -322,7 +331,7 @@ export function createRequestHandler({ return createResponseFrom('api', route, response); } - function respondHTML(html: string | Response | null, route: Route): Response { + function respondHTML(html: string | ReadableStream | Response | null, route: Route): Response { if (typeof html === 'string') { return createResponse('html', route, html, { status: 200, @@ -337,6 +346,15 @@ export function createRequestHandler({ return html; } + if (html != null) { + return createResponse('html', route, html, { + status: 200, + headers: new Headers({ + 'Content-Type': 'text/html', + }), + }); + } + throw new ExpoError(`HTML route file ${route.page}.html could not be loaded`); } diff --git a/packages/expo-server/src/vendor/environment/__tests__/common.test.ts b/packages/expo-server/src/vendor/environment/__tests__/common.test.ts index fe353d176a836a..39bfaa2ed3c2d0 100644 --- a/packages/expo-server/src/vendor/environment/__tests__/common.test.ts +++ b/packages/expo-server/src/vendor/environment/__tests__/common.test.ts @@ -207,7 +207,7 @@ describe('getHtml', () => { }) ); - expect(html).toBe('SSR content'); + expect(await new Response(html as ReadableStream).text()).toBe('SSR content'); expect(input.loadModule).toHaveBeenCalledWith('_expo/server/render.js'); expect(input.readText).not.toHaveBeenCalled(); }); @@ -295,7 +295,7 @@ describe('getHtml', () => { expect(input.loadModule).toHaveBeenCalledTimes(2); }); - it('passes location, request, and assets to `getStaticContent()`', async () => { + it('passes location, request, and assets to `getStreamingContent()`', async () => { const mockSSRModule = createMockSSRModule(); const input = createMockInput({ manifest: { @@ -316,7 +316,7 @@ describe('getHtml', () => { }) ); - expect(mockSSRModule.getStaticContent).toHaveBeenCalledWith( + expect(mockSSRModule.getStreamingContent).toHaveBeenCalledWith( expect.objectContaining({ pathname: '/path', search: '?query=1', @@ -326,6 +326,14 @@ describe('getHtml', () => { assets: { css: ['/style.css'], js: ['/app.js'] }, }) ); + expect(mockSSRModule.getStreamingContent).toHaveBeenCalledWith( + expect.any(URL), + expect.objectContaining({ + request: expect.objectContaining({ + signal: request.signal, + }), + }) + ); }); it('merges top-level and per-route assets', async () => { @@ -349,7 +357,7 @@ describe('getHtml', () => { }) ); - expect(mockSSRModule.getStaticContent).toHaveBeenCalledWith( + expect(mockSSRModule.getStreamingContent).toHaveBeenCalledWith( expect.any(URL), expect.objectContaining({ assets: { @@ -364,7 +372,7 @@ describe('getHtml', () => { const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); const renderError = new Error('Render failed'); const mockSSRModule = { - getStaticContent: jest.fn().mockRejectedValue(renderError), + getStreamingContent: jest.fn().mockRejectedValue(renderError), }; const input = createMockInput({ manifest: { @@ -426,13 +434,117 @@ describe('getHtml', () => { expect(input.loadModule).toHaveBeenCalledWith('_expo/loaders/index.js'); expect(loaderModule.loader).toHaveBeenCalledWith(expect.any(ImmutableRequest), {}); - expect(mockSSRModule.getStaticContent).toHaveBeenCalledWith( + expect(mockSSRModule.getStreamingContent).toHaveBeenCalledWith( expect.any(URL), expect.objectContaining({ loader: { key: '/index', data: loaderData }, }) ); }); + + it('resolves metadata before render and passes the resolved head tags to the SSR renderer', async () => { + const mockSSRModule = createMockSSRModule({ + resolveMetadata: jest.fn().mockResolvedValue({ + metadata: { title: 'Route title' }, + headTags: 'Route title', + }), + }); + const input = createMockInput({ + manifest: { + rendering: { mode: 'ssr', file: '_expo/server/render.js' }, + }, + modules: { '_expo/server/render.js': mockSSRModule }, + }); + const env = createEnvironment(input); + const request = new Request('http://localhost/posts/123'); + + await env.getHtml( + request, + createMockRoute({ + file: './posts/[id].tsx', + page: '/posts/[id]', + namedRegex: new RegExp('^/posts/(?[^/]+?)(?:/)?$'), + routeKeys: { id: 'id' }, + }) + ); + + expect(mockSSRModule.resolveMetadata).toHaveBeenCalledWith({ + route: { + file: './posts/[id].tsx', + page: '/posts/[id]', + }, + request: new ImmutableRequest(request), + params: { id: '123' }, + }); + expect(mockSSRModule.getStreamingContent).toHaveBeenCalledWith( + expect.any(URL), + expect.objectContaining({ + metadata: { + metadata: { title: 'Route title' }, + headTags: 'Route title', + }, + }) + ); + }); + + it('passes through null metadata results without adding renderer metadata input', async () => { + const mockSSRModule = createMockSSRModule({ + resolveMetadata: jest.fn().mockResolvedValue(null), + }); + const input = createMockInput({ + manifest: { + rendering: { mode: 'ssr', file: '_expo/server/render.js' }, + }, + modules: { '_expo/server/render.js': mockSSRModule }, + }); + const env = createEnvironment(input); + + await env.getHtml( + new Request('http://localhost/'), + createMockRoute({ + file: './index.tsx', + page: '/index', + namedRegex: new RegExp('^/(?:/)?$'), + }) + ); + + expect(mockSSRModule.getStreamingContent).toHaveBeenCalledWith( + expect.any(URL), + expect.objectContaining({ + metadata: null, + }) + ); + }); + + it('re-throws metadata resolution errors before render', async () => { + const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + const metadataError = new Error('Metadata failed'); + const mockSSRModule = createMockSSRModule({ + resolveMetadata: jest.fn().mockRejectedValue(metadataError), + }); + const input = createMockInput({ + manifest: { + rendering: { mode: 'ssr', file: '_expo/server/render.js' }, + }, + modules: { '_expo/server/render.js': mockSSRModule }, + }); + const env = createEnvironment(input); + + await expect( + env.getHtml( + new Request('http://localhost/'), + createMockRoute({ + file: './index.tsx', + page: '/index', + namedRegex: new RegExp('^/(?:/)?$'), + }) + ) + ).rejects.toThrow('Metadata failed'); + + expect(mockSSRModule.getStreamingContent).not.toHaveBeenCalled(); + expect(consoleErrorSpy).toHaveBeenCalledWith('SSR render error:', metadataError); + consoleErrorSpy.mockRestore(); + }); }); describe('getApiRoute', () => { @@ -722,8 +834,21 @@ function createMockRoute( }; } -function createMockSSRModule(): ServerRenderModule { +function createMockSSRModule(overrides: Partial = {}): ServerRenderModule { return { - getStaticContent: jest.fn().mockResolvedValue('SSR content'), + resolveMetadata: jest.fn().mockResolvedValue(null), + getStreamingContent: jest + .fn() + .mockResolvedValue(createMockHtmlStream('SSR content')), + ...overrides, }; } + +function createMockHtmlStream(html: string): ReadableStream { + return new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode(html)); + controller.close(); + }, + }); +} diff --git a/packages/expo-server/src/vendor/environment/common.ts b/packages/expo-server/src/vendor/environment/common.ts index e347005201fe44..4ec8cd39634f02 100644 --- a/packages/expo-server/src/vendor/environment/common.ts +++ b/packages/expo-server/src/vendor/environment/common.ts @@ -43,7 +43,7 @@ interface EnvironmentInput { export interface CommonEnvironment { getRoutesManifest(): Promise; - getHtml(request: Request, route: Route): Promise; + getHtml(request: Request, route: Route): Promise; getApiRoute(route: Route): Promise; getMiddleware(middleware: MiddlewareInfo): Promise; getLoaderData(request: Request, route: Route): Promise; @@ -53,6 +53,7 @@ export interface CommonEnvironment { export function createEnvironment(input: EnvironmentInput): CommonEnvironment { // Cached manifest and SSR renderer, initialized on first request let cachedManifest: Manifest | null | undefined; + let cachedSsrModule: ServerRenderModule | null = null; let ssrRenderer: SsrRenderFn | null = null; async function getRoutesManifest(): Promise { @@ -63,14 +64,23 @@ export function createEnvironment(input: EnvironmentInput): CommonEnvironment { return cachedManifest; } - async function getServerRenderer(): Promise { + async function getServerRenderer(): Promise<{ + renderer: SsrRenderFn | null; + module: ServerRenderModule | null; + }> { if (ssrRenderer && !input.isDevelopment) { - return ssrRenderer; + return { + renderer: ssrRenderer, + module: cachedSsrModule, + }; } const manifest = await getRoutesManifest(); if (manifest?.rendering?.mode !== 'ssr') { - return null; + return { + renderer: null, + module: null, + }; } // If `manifest.rendering.mode === 'ssr'`, we always expect the SSR rendering module to be @@ -84,18 +94,23 @@ export function createEnvironment(input: EnvironmentInput): CommonEnvironment { } const topLevelAssets = manifest.assets; + cachedSsrModule = ssrModule; ssrRenderer = async (request, options) => { const url = new URL(request.url); const location = new URL(url.pathname + url.search, url.origin); const assets = mergeAssets(topLevelAssets, options?.assets); - return ssrModule.getStaticContent(location, { + return ssrModule.getStreamingContent(location, { loader: options?.loader, + metadata: options?.metadata, request, assets, }); }; - return ssrRenderer; + return { + renderer: ssrRenderer, + module: ssrModule, + }; } async function executeLoader( @@ -120,17 +135,28 @@ export function createEnvironment(input: EnvironmentInput): CommonEnvironment { async getHtml(request, route) { // SSR path: Render at runtime if SSR module is available - const renderer = await getServerRenderer(); + const { renderer, module: ssrModule } = await getServerRenderer(); if (renderer) { let renderOptions: RenderOptions = { assets: route.assets }; + const params = parseParams(request, route); try { + if (ssrModule?.resolveMetadata) { + renderOptions.metadata = await ssrModule.resolveMetadata({ + route: { + file: route.file, + page: route.page, + }, + request: new ImmutableRequest(request), + params, + }); + } + if (route.loader) { - const params = parseParams(request, route); const result = await executeLoader(request, route, params); const data = isResponse(result) ? await result.json() : result; renderOptions = { - assets: route.assets, + ...renderOptions, loader: { data: data ?? null, key: resolveLoaderContextKey(route.page, params),