first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
import { type GetRoutesOptions } from 'expo-router/internal/routing';
import { createPages } from './create-pages';
import { EntriesDev } from '../server';
type CreatePagesFn = Parameters<typeof createPages>[0];
type CreatePagesFns = Parameters<CreatePagesFn>[0];
type CreatePagesOptions = Parameters<CreatePagesFn>[1] & {
getRouteOptions?: GetRoutesOptions;
};
/**
* Wrapper around `createPages` to pass data from the server to the fn
*
* This is separated from the `createPages` function allowing us to keep the createPages
* in sync with the original Waku implementation.
*
* @param fn
* @returns
*/
export declare function createExpoPages(fn: (fn: CreatePagesFns, options: CreatePagesOptions) => ReturnType<CreatePagesFn>): (getRouteOptions?: GetRoutesOptions) => EntriesDev;
export {};
//# sourceMappingURL=create-expo-pages.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"create-expo-pages.d.ts","sourceRoot":"","sources":["../../../src/rsc/router/create-expo-pages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,KAAK,cAAc,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;AACnD,KAAK,kBAAkB,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG;IACvD,eAAe,CAAC,EAAE,gBAAgB,CAAC;CACpC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,kBAAkB,KAAK,UAAU,CAAC,aAAa,CAAC,IAE1E,kBAAkB,gBAAgB,KAAG,UAAU,CAKxD"}

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createExpoPages = createExpoPages;
const create_pages_1 = require("./create-pages");
/**
* Wrapper around `createPages` to pass data from the server to the fn
*
* This is separated from the `createPages` function allowing us to keep the createPages
* in sync with the original Waku implementation.
*
* @param fn
* @returns
*/
function createExpoPages(fn) {
return (getRouteOptions) => {
return {
default: (0, create_pages_1.createPages)((a, b) => fn(a, { ...b, getRouteOptions })),
};
};
}
//# sourceMappingURL=create-expo-pages.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"create-expo-pages.js","sourceRoot":"","sources":["../../../src/rsc/router/create-expo-pages.ts"],"names":[],"mappings":";;AAoBA,0CAQC;AA1BD,iDAA6C;AAS7C;;;;;;;;GAQG;AACH,SAAgB,eAAe,CAC7B,EAAkF;IAElF,OAAO,CAAC,eAAkC,EAAc,EAAE;QACxD,OAAO;YACL,OAAO,EAAE,IAAA,0BAAW,EAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC;SACjE,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { type GetRoutesOptions } from 'expo-router/internal/routing';\n\nimport { createPages } from './create-pages';\nimport { EntriesDev } from '../server';\n\ntype CreatePagesFn = Parameters<typeof createPages>[0];\ntype CreatePagesFns = Parameters<CreatePagesFn>[0];\ntype CreatePagesOptions = Parameters<CreatePagesFn>[1] & {\n getRouteOptions?: GetRoutesOptions;\n};\n\n/**\n * Wrapper around `createPages` to pass data from the server to the fn\n *\n * This is separated from the `createPages` function allowing us to keep the createPages\n * in sync with the original Waku implementation.\n *\n * @param fn\n * @returns\n */\nexport function createExpoPages(\n fn: (fn: CreatePagesFns, options: CreatePagesOptions) => ReturnType<CreatePagesFn>\n) {\n return (getRouteOptions?: GetRoutesOptions): EntriesDev => {\n return {\n default: createPages((a, b) => fn(a, { ...b, getRouteOptions })),\n };\n };\n}\n"]}

View File

@@ -0,0 +1,80 @@
/**
* Copyright © 2024 650 Industries.
* Copyright © 2024 2023 Daishi Kato
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* https://github.com/dai-shi/waku/blob/3d1cc7d714b67b142c847e879c30f0724fc457a7/packages/waku/src/router/create-pages.ts#L1
*/
import type { RouteProps } from 'expo-router/internal/rsc';
import type { FunctionComponent, ReactNode } from 'react';
import type { BuildConfig } from '../server';
/**
* Type version of `String.prototype.split()`. Splits the first string argument by the second string argument
* @example
* ```ts
* // ['a', 'b', 'c']
* type Case1 = Split<'abc', ''>
* // ['a', 'b', 'c']
* type Case2 = Split<'a,b,c', ','>
* ```
*/
type Split<Str extends string, Del extends string | number> = string extends Str ? string[] : '' extends Str ? [] : Str extends `${infer T}${Del}${infer U}` ? [T, ...Split<U, Del>] : [Str];
/** Assumes that the path is a part of a slug path. */
type IsValidPathItem<T> = T extends `/${infer _}` ? false : T extends '[]' | '' ? false : true;
/**
* This is a helper type to check if a path is valid in a slug path.
*/
export type IsValidPathInSlugPath<T> = T extends `/${infer L}/${infer R}` ? IsValidPathItem<L> extends true ? IsValidPathInSlugPath<`/${R}`> : false : T extends `/${infer U}` ? IsValidPathItem<U> : false;
/** Checks if a particular slug name exists in a path. */
export type HasSlugInPath<T, K extends string> = T extends `/[${K}]/${infer _}` ? true : T extends `/${infer _}/${infer U}` ? HasSlugInPath<`/${U}`, K> : T extends `/[${K}]` ? true : false;
export type HasWildcardInPath<T> = T extends `/[...${string}]/${string}` ? true : T extends `/${infer _}/${infer U}` ? HasWildcardInPath<`/${U}`> : T extends `/[...${string}]` ? true : false;
export type PathWithSlug<T, K extends string> = IsValidPathInSlugPath<T> extends true ? (HasSlugInPath<T, K> extends true ? T : never) : never;
type _GetSlugs<Route extends string, SplitRoute extends string[] = Split<Route, '/'>, Result extends string[] = []> = SplitRoute extends [] ? Result : SplitRoute extends [`${infer MaybeSlug}`, ...infer Rest] ? Rest extends string[] ? MaybeSlug extends `[${infer Slug}]` ? _GetSlugs<Route, Rest, [...Result, Slug]> : _GetSlugs<Route, Rest, Result> : never : Result;
export type GetSlugs<Route extends string> = _GetSlugs<Route>;
export type StaticSlugRoutePathsTuple<T extends string, Slugs extends unknown[] = GetSlugs<T>, Result extends string[] = []> = Slugs extends [] ? Result : Slugs extends [infer _, ...infer Rest] ? StaticSlugRoutePathsTuple<T, Rest, [...Result, string]> : never;
type StaticSlugRoutePaths<T extends string> = HasWildcardInPath<T> extends true ? string[] | string[][] : StaticSlugRoutePathsTuple<T> extends [string] ? string[] : StaticSlugRoutePathsTuple<T>[];
export type PathWithoutSlug<T> = T extends '/' ? T : IsValidPathInSlugPath<T> extends true ? HasSlugInPath<T, string> extends true ? never : T : never;
type PathWithStaticSlugs<T extends string> = T extends `/` ? T : IsValidPathInSlugPath<T> extends true ? T : never;
export type PathWithWildcard<Path, SlugKey extends string, WildSlugKey extends string> = PathWithSlug<Path, SlugKey | `...${WildSlugKey}`>;
export type CreatePage = <Path extends string, SlugKey extends string, WildSlugKey extends string>(page: ({
render: 'static';
path: PathWithoutSlug<Path>;
component: FunctionComponent<RouteProps>;
} | {
render: 'static';
path: PathWithStaticSlugs<Path>;
staticPaths: StaticSlugRoutePaths<Path>;
component: FunctionComponent<RouteProps & Record<SlugKey, string>>;
} | {
render: 'dynamic';
path: PathWithoutSlug<Path>;
component: FunctionComponent<RouteProps>;
} | {
render: 'dynamic';
path: PathWithWildcard<Path, SlugKey, WildSlugKey>;
component: FunctionComponent<RouteProps & Record<SlugKey, string> & Record<WildSlugKey, string[]>>;
}) & {
unstable_disableSSR?: boolean;
}) => void;
export type CreateLayout = <T extends string>(layout: {
render: 'static' | 'dynamic';
path: PathWithoutSlug<T>;
component: FunctionComponent<Omit<RouteProps, 'searchParams'> & {
children: ReactNode;
}>;
}) => void;
export declare function createPages(fn: (fns: {
createPage: CreatePage;
createLayout: CreateLayout;
unstable_setBuildData: (path: string, data: unknown) => void;
}, opts: {
unstable_buildConfig: BuildConfig | undefined;
}) => Promise<void>): {
renderEntries: import("../server").RenderEntries;
getBuildConfig: import("../server").GetBuildConfig | undefined;
getSsrConfig: import("../server").GetSsrConfig | undefined;
};
export {};
//# sourceMappingURL=create-pages.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"create-pages.d.ts","sourceRoot":"","sources":["../../../src/rsc/router/create-pages.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,232 @@
"use strict";
/* eslint-disable @typescript-eslint/no-unused-vars */
/**
* Copyright © 2024 650 Industries.
* Copyright © 2024 2023 Daishi Kato
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* https://github.com/dai-shi/waku/blob/3d1cc7d714b67b142c847e879c30f0724fc457a7/packages/waku/src/router/create-pages.ts#L1
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPages = createPages;
const react_1 = require("react");
const defineRouter_1 = require("./defineRouter");
const path_1 = require("../path");
const hasPathSpecPrefix = (prefix, path) => {
for (let i = 0; i < prefix.length; i++) {
if (i >= path.length ||
prefix[i].type !== path[i].type ||
prefix[i].name !== path[i].name) {
return false;
}
}
return true;
};
const sanitizeSlug = (slug) => slug.replace(/\./g, '').replace(/ /g, '-');
function createPages(fn) {
let configured = false;
// TODO I think there's room for improvement to refactor these structures
const staticPathSet = new Set();
const dynamicPagePathMap = new Map();
const wildcardPagePathMap = new Map();
const dynamicLayoutPathMap = new Map();
const staticComponentMap = new Map();
const noSsrSet = new WeakSet();
const buildDataMap = new Map();
const registerStaticComponent = (id, component) => {
if (staticComponentMap.has(id) && staticComponentMap.get(id) !== component) {
throw new Error(`Duplicated component for: ${id}`);
}
staticComponentMap.set(id, component);
};
const createPage = (page) => {
if (configured) {
throw new Error('no longer available');
}
const pathSpec = (0, path_1.parsePathWithSlug)(page.path);
if (page.unstable_disableSSR) {
noSsrSet.add(pathSpec);
}
const { numSlugs, numWildcards } = (() => {
let numSlugs = 0;
let numWildcards = 0;
for (const slug of pathSpec) {
if (slug.type !== 'literal') {
numSlugs++;
}
if (slug.type === 'wildcard') {
numWildcards++;
}
}
return { numSlugs, numWildcards };
})();
if (page.render === 'static' && numSlugs === 0) {
staticPathSet.add([page.path, pathSpec]);
const id = (0, path_1.joinPath)(page.path, 'page').replace(/^\//, '');
registerStaticComponent(id, page.component);
}
else if (page.render === 'static' && numSlugs > 0 && 'staticPaths' in page) {
const staticPaths = page.staticPaths.map((item) => (Array.isArray(item) ? item : [item]).map(sanitizeSlug));
for (const staticPath of staticPaths) {
if (staticPath.length !== numSlugs && numWildcards === 0) {
throw new Error('staticPaths does not match with slug pattern');
}
const mapping = {};
let slugIndex = 0;
const pathItems = [];
pathSpec.forEach(({ type, name }) => {
switch (type) {
case 'literal':
pathItems.push(name);
break;
case 'wildcard':
mapping[name] = staticPath.slice(slugIndex);
staticPath.slice(slugIndex++).forEach((slug) => {
pathItems.push(slug);
});
break;
case 'group':
pathItems.push(staticPath[slugIndex++]);
mapping[name] = pathItems[pathItems.length - 1];
break;
}
});
staticPathSet.add([page.path, pathItems.map((name) => ({ type: 'literal', name }))]);
const id = (0, path_1.joinPath)(...pathItems, 'page');
const WrappedComponent = (props) => (0, react_1.createElement)(page.component, { ...props, ...mapping });
registerStaticComponent(id, WrappedComponent);
}
}
else if (page.render === 'dynamic' && numWildcards === 0) {
if (dynamicPagePathMap.has(page.path)) {
throw new Error(`Duplicated dynamic path: ${page.path}`);
}
dynamicPagePathMap.set(page.path, [pathSpec, page.component]);
}
else if (page.render === 'dynamic' && numWildcards === 1) {
if (wildcardPagePathMap.has(page.path)) {
throw new Error(`Duplicated dynamic path: ${page.path}`);
}
wildcardPagePathMap.set(page.path, [pathSpec, page.component]);
}
else {
throw new Error('Invalid page configuration: ' + page.path);
}
};
const createLayout = (layout) => {
if (configured) {
throw new Error('no longer available');
}
if (layout.render === 'static') {
const id = (0, path_1.joinPath)(layout.path, 'layout').replace(/^\//, '');
registerStaticComponent(id, layout.component);
}
else if (layout.render === 'dynamic') {
if (dynamicLayoutPathMap.has(layout.path)) {
throw new Error(`Duplicated dynamic path: ${layout.path}`);
}
const pathSpec = (0, path_1.parsePathWithSlug)(layout.path);
dynamicLayoutPathMap.set(layout.path, [pathSpec, layout.component]);
}
else {
throw new Error('Invalid layout configuration');
}
};
const unstable_setBuildData = (path, data) => {
buildDataMap.set(path, data);
};
let ready;
const configure = async (buildConfig) => {
if (!configured && !ready) {
ready = fn({ createPage, createLayout, unstable_setBuildData }, { unstable_buildConfig: buildConfig });
await ready;
configured = true;
}
await ready;
};
return (0, defineRouter_1.unstable_defineRouter)(async () => {
await configure();
const paths = [];
for (const [path, pathSpec] of staticPathSet) {
const noSsr = noSsrSet.has(pathSpec);
const isStatic = (() => {
for (const [_, [layoutPathSpec]] of dynamicLayoutPathMap) {
if (hasPathSpecPrefix(layoutPathSpec, pathSpec)) {
return false;
}
}
return true;
})();
paths.push({
pattern: (0, path_1.path2regexp)((0, path_1.parsePathWithSlug)(path)),
path: pathSpec,
isStatic,
noSsr,
data: buildDataMap.get(path),
});
}
for (const [path, [pathSpec]] of dynamicPagePathMap) {
const noSsr = noSsrSet.has(pathSpec);
paths.push({
pattern: (0, path_1.path2regexp)((0, path_1.parsePathWithSlug)(path)),
path: pathSpec,
isStatic: false,
noSsr,
data: buildDataMap.get(path),
});
}
for (const [path, [pathSpec]] of wildcardPagePathMap) {
const noSsr = noSsrSet.has(pathSpec);
paths.push({
pattern: (0, path_1.path2regexp)((0, path_1.parsePathWithSlug)(path)),
path: pathSpec,
isStatic: false,
noSsr,
data: buildDataMap.get(path),
});
}
return paths;
}, async (id, { unstable_setShouldSkip, unstable_buildConfig }) => {
await configure(unstable_buildConfig);
const staticComponent = staticComponentMap.get(id);
if (staticComponent) {
unstable_setShouldSkip([]);
return staticComponent;
}
for (const [_, [pathSpec, Component]] of dynamicPagePathMap) {
const mapping = (0, path_1.getPathMapping)([...pathSpec, { type: 'literal', name: 'page' }], id);
if (mapping) {
if (Object.keys(mapping).length === 0) {
unstable_setShouldSkip();
return Component;
}
const WrappedComponent = (props) => (0, react_1.createElement)(Component, { ...props, ...mapping });
unstable_setShouldSkip();
return WrappedComponent;
}
}
for (const [_, [pathSpec, Component]] of wildcardPagePathMap) {
const mapping = (0, path_1.getPathMapping)([...pathSpec, { type: 'literal', name: 'page' }], id);
if (mapping) {
const WrappedComponent = (props) => (0, react_1.createElement)(Component, { ...props, ...mapping });
unstable_setShouldSkip();
return WrappedComponent;
}
}
for (const [_, [pathSpec, Component]] of dynamicLayoutPathMap) {
const mapping = (0, path_1.getPathMapping)([...pathSpec, { type: 'literal', name: 'layout' }], id);
if (mapping) {
if (Object.keys(mapping).length) {
throw new Error('[Bug] layout should not have slugs');
}
unstable_setShouldSkip();
return Component;
}
}
unstable_setShouldSkip([]); // negative cache
return null; // not found
});
}
//# sourceMappingURL=create-pages.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,30 @@
/**
* Copyright © 2024 650 Industries.
* Copyright © 2024 2023 Daishi Kato
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { type RouteProps, type ShouldSkip } from 'expo-router/internal/rsc';
import type { FunctionComponent, ReactNode } from 'react';
import type { PathSpec } from '../path';
import { defineEntries } from '../server';
import type { BuildConfig } from '../server';
type RoutePropsForLayout = Omit<RouteProps, 'searchParams'> & {
children: ReactNode;
};
type ShouldSkipValue = ShouldSkip[number][1];
export declare function unstable_defineRouter(getPathConfig: () => Promise<Iterable<{
pattern: string;
path: PathSpec;
isStatic?: boolean;
noSsr?: boolean;
data?: unknown;
}>>, getComponent: (componentId: string, // "**/layout" or "**/page"
options: {
unstable_setShouldSkip: (val?: ShouldSkipValue) => void;
unstable_buildConfig: BuildConfig | undefined;
}) => Promise<FunctionComponent<RouteProps> | FunctionComponent<RoutePropsForLayout> | null>): ReturnType<typeof defineEntries>;
export declare function unstable_redirect(pathname: string, searchParams?: URLSearchParams, skip?: string[]): void;
export {};
//# sourceMappingURL=defineRouter.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"defineRouter.d.ts","sourceRoot":"","sources":["../../../src/rsc/router/defineRouter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAUL,KAAK,UAAU,EACf,KAAK,UAAU,EAChB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAkB,iBAAiB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAI1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,aAAa,EAAY,MAAM,WAAW,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAA+C,MAAM,WAAW,CAAC;AAE1F,KAAK,mBAAmB,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,GAAG;IAC5D,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF,KAAK,eAAe,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAgB7C,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,MAAM,OAAO,CAC1B,QAAQ,CAAC;IACP,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC,CACH,EACD,YAAY,EAAE,CACZ,WAAW,EAAE,MAAM,EAAE,2BAA2B;AAChD,OAAO,EAAE;IAEP,sBAAsB,EAAE,CAAC,GAAG,CAAC,EAAE,eAAe,KAAK,IAAI,CAAC;IACxD,oBAAoB,EAAE,WAAW,GAAG,SAAS,CAAC;CAC/C,KACE,OAAO,CAAC,iBAAiB,CAAC,UAAU,CAAC,GAAG,iBAAiB,CAAC,mBAAmB,CAAC,GAAG,IAAI,CAAC,GAC1F,UAAU,CAAC,OAAO,aAAa,CAAC,CAmKlC;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,eAAe,EAC9B,IAAI,CAAC,EAAE,MAAM,EAAE,QAUhB"}

View File

@@ -0,0 +1,169 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.unstable_defineRouter = unstable_defineRouter;
exports.unstable_redirect = unstable_redirect;
/**
* Copyright © 2024 650 Industries.
* Copyright © 2024 2023 Daishi Kato
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const rsc_1 = require("expo-router/internal/rsc");
const react_1 = require("react");
const path_1 = require("../path");
const server_1 = require("../server");
const safeJsonParse = (str) => {
if (typeof str === 'string') {
try {
const obj = JSON.parse(str);
if (typeof obj === 'object') {
return obj;
}
}
catch {
// ignore
}
}
return undefined;
};
function unstable_defineRouter(getPathConfig, getComponent) {
let cachedPathConfig;
const getMyPathConfig = async (buildConfig) => {
if (buildConfig) {
return buildConfig;
}
if (!cachedPathConfig) {
cachedPathConfig = Array.from(await getPathConfig()).map((item) => {
const is404 = item.path.length === 1 &&
item.path[0].type === 'literal' &&
item.path[0].name === '404';
return {
pattern: item.pattern,
pathname: item.path,
isStatic: item.isStatic,
customData: { is404, noSsr: !!item.noSsr, data: item.data },
};
});
}
return cachedPathConfig;
};
const existsPath = async (pathname, buildConfig) => {
const pathConfig = await getMyPathConfig(buildConfig);
const found = pathConfig.find(({ pathname: pathSpec }) => (0, path_1.getPathMapping)(pathSpec, pathname));
return found
? found.customData.noSsr
? ['FOUND', 'NO_SSR']
: ['FOUND']
: pathConfig.some(({ customData: { is404 } }) => is404) // FIXMEs should avoid re-computation
? ['NOT_FOUND', 'HAS_404']
: ['NOT_FOUND'];
};
const renderEntries = async (input, { params, buildConfig }) => {
const pathname = (0, rsc_1.parseInputString)(input);
if ((await existsPath(pathname, buildConfig))[0] === 'NOT_FOUND') {
return null;
}
const shouldSkipObj = {};
const parsedParams = safeJsonParse(params);
const query = typeof parsedParams?.query === 'string' ? parsedParams.query : '';
const skip = Array.isArray(parsedParams?.skip) ? parsedParams?.skip : [];
const componentIds = (0, rsc_1.getComponentIds)(pathname);
const entries = (await Promise.all(componentIds.map(async (id) => {
if (skip?.includes(id)) {
return [];
}
const setShouldSkip = (val) => {
if (val) {
shouldSkipObj[id] = val;
}
else {
delete shouldSkipObj[id];
}
};
const component = await getComponent(id, {
unstable_setShouldSkip: setShouldSkip,
unstable_buildConfig: buildConfig,
});
if (!component) {
return [];
}
const element = (0, react_1.createElement)(component, id.endsWith('/layout') ? { path: pathname } : { path: pathname, query }, (0, react_1.createElement)(rsc_1.Children));
return [[id, element]];
}))).flat();
entries.push([rsc_1.SHOULD_SKIP_ID, Object.entries(shouldSkipObj)]);
entries.push([rsc_1.LOCATION_ID, [pathname, query]]);
return Object.fromEntries(entries);
};
const getBuildConfig = async (unstable_collectClientModules) => {
const pathConfig = await getMyPathConfig();
const path2moduleIds = {};
for (const { pathname: pathSpec } of pathConfig) {
if (pathSpec.some(({ type }) => type !== 'literal')) {
continue;
}
const pathname = '/' + pathSpec.map(({ name }) => name).join('/');
const input = (0, rsc_1.getInputString)(pathname);
const moduleIds = await unstable_collectClientModules(input);
path2moduleIds[pathname] = moduleIds;
}
const customCode = `
globalThis.__EXPO_ROUTER_PREFETCH__ = (path) => {
const path2ids = ${JSON.stringify(path2moduleIds)};
for (const id of path2ids[path] || []) {
import(id);
}
};`;
const buildConfig = [];
for (const { pathname: pathSpec, isStatic, customData } of pathConfig) {
const entries = [];
if (pathSpec.every(({ type }) => type === 'literal')) {
const pathname = '/' + pathSpec.map(({ name }) => name).join('/');
const input = (0, rsc_1.getInputString)(pathname);
entries.push({ input, isStatic });
}
buildConfig.push({
pathname: pathSpec,
isStatic,
entries,
customCode,
customData,
});
}
return buildConfig;
};
const getSsrConfig = async (pathname, { searchParams, buildConfig }) => {
const pathStatus = await existsPath(pathname, buildConfig);
if (pathStatus[1] === 'NO_SSR') {
return null;
}
if (pathStatus[0] === 'NOT_FOUND') {
if (pathStatus[1] === 'HAS_404') {
pathname = '/404';
}
else {
return null;
}
}
const componentIds = (0, rsc_1.getComponentIds)(pathname);
const input = (0, rsc_1.getInputString)(pathname);
const html = (0, react_1.createElement)(rsc_1.ServerRouter, { route: { path: pathname, query: searchParams.toString(), hash: '' } }, componentIds.reduceRight((acc, id) => (0, react_1.createElement)(rsc_1.Slot, { id, fallback: acc }, acc), null));
return {
input,
params: JSON.stringify({ query: searchParams.toString() }),
html,
};
};
return { renderEntries, getBuildConfig, getSsrConfig };
}
function unstable_redirect(pathname, searchParams, skip) {
if (skip) {
searchParams = new URLSearchParams(searchParams);
for (const id of skip) {
searchParams.append(rsc_1.PARAM_KEY_SKIP, id);
}
}
const input = (0, rsc_1.getInputString)(pathname);
(0, server_1.rerender)(input, searchParams);
}
//# sourceMappingURL=defineRouter.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
declare const _default: (getRouteOptions?: import("expo-router/build/getRoutes").Options) => import("../server").EntriesDev;
export default _default;
//# sourceMappingURL=expo-definedRouter.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"expo-definedRouter.d.ts","sourceRoot":"","sources":["../../../src/rsc/router/expo-definedRouter.ts"],"names":[],"mappings":";AAiBA,wBAyHG"}

View File

@@ -0,0 +1,111 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const _ctx_1 = require("expo-router/_ctx");
const routing_1 = require("expo-router/internal/routing");
const create_expo_pages_1 = require("./create-expo-pages");
const getRoutesSSR_1 = require("../../getRoutesSSR");
const loadStaticParamsAsync_1 = require("../../loadStaticParamsAsync");
const UNIMPLEMENTED_PARAMS = new Proxy({}, {
// Assert that params is unimplemented when accessed.
get() {
throw new Error('generateStaticParams(): params is not implemented yet');
},
});
exports.default = (0, create_expo_pages_1.createExpoPages)(async ({ createPage, createLayout }, { getRouteOptions }) => {
const routes = (0, getRoutesSSR_1.getRoutes)(_ctx_1.ctx, {
...getRouteOptions,
platform: process.env.EXPO_OS,
skipGenerated: true,
importMode: 'lazy',
});
if (!routes)
return;
async function loadAndConvertStaticParamsAsync(route) {
const loaded = route.loadRoute();
let staticPaths = undefined;
if (route.dynamic) {
const params = await (0, loadStaticParamsAsync_1.evalStaticParamsAsync)(route, { parentParams: UNIMPLEMENTED_PARAMS }, loaded.generateStaticParams);
// Sort `params` like `[{a: 'x', b: 'y'}, { a: 'z', b: 'w' }]` for a route.dynamic like `[{name: 'a', deep: false}, {name: 'b', deep: false}]` to `[['a', 'y'], ['z', 'w]]`
staticPaths = params?.map((p) => {
const grouped = [];
for (const dynamic of route.dynamic) {
const defined = p[dynamic.name];
if (!defined) {
throw new Error('generateStaticParams is missing param: ' +
dynamic.name +
'. In route: ' +
route.contextKey);
}
if (Array.isArray(defined)) {
if (defined.length > 1) {
throw new Error('generateStaticParams does not support returning multiple static paths for deep dynamic routes in React Server Components yet. Update route: ' +
route.contextKey);
}
}
const first = Array.isArray(defined) ? defined[0] : defined;
grouped.push(first);
}
return grouped;
});
}
else if (loaded.generateStaticParams) {
throw new Error('Cannot use generateStaticParams without a dynamic route: ' + route.contextKey);
}
return staticPaths;
}
async function addLayout(route) {
const normal = (0, routing_1.getContextKey)(route.contextKey).replace(/\/index$/, '');
const loaded = route.loadRoute();
if (loaded.generateStaticParams) {
throw new Error('generateStaticParams is not supported in _layout routes with React Server Components enabled yet.');
}
createLayout({
// NOTE(EvanBacon): Support routes with top-level "use client"
component: loaded.default,
path: normal,
render: 'static',
...loaded.unstable_settings,
});
await Promise.all(route.children.sort(routing_1.sortRoutes).map(async (child) => {
if (child.type === 'layout') {
await addLayout(child);
}
else {
const normal = (0, routing_1.getContextKey)(child.contextKey).replace(/\/index$/, '');
const loaded = child.loadRoute();
const settings = loaded.unstable_settings;
// Support generateStaticParams for dynamic routes by defining the route twice.
if (loaded.generateStaticParams) {
createPage({
// NOTE(EvanBacon): Support routes with top-level "use client"
component: loaded.default,
path: normal,
render: 'static',
...loaded.unstable_settings,
staticPaths: (await loadAndConvertStaticParamsAsync(child)),
});
if (settings?.render !== 'static') {
createPage({
// NOTE(EvanBacon): Support routes with top-level "use client"
component: loaded.default,
path: normal,
render: 'dynamic',
...settings,
});
}
}
else {
createPage({
// NOTE(EvanBacon): Support routes with top-level "use client"
component: loaded.default,
path: normal,
render: 'dynamic',
...settings,
});
}
}
}));
}
await addLayout(routes);
});
//# sourceMappingURL=expo-definedRouter.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
declare const _default: (getRouteOptions?: import("expo-router/build/getRoutes").Options) => import("../server").EntriesDev;
export default _default;
//# sourceMappingURL=noopRouter.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"noopRouter.d.ts","sourceRoot":"","sources":["../../../src/rsc/router/noopRouter.ts"],"names":[],"mappings":";AAEA,wBAEG"}

View File

@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const create_expo_pages_1 = require("./create-expo-pages");
exports.default = (0, create_expo_pages_1.createExpoPages)(async () => {
// noop the router for client-only mode. This ensures we skip loading the routes in react-server mode.
});
//# sourceMappingURL=noopRouter.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"noopRouter.js","sourceRoot":"","sources":["../../../src/rsc/router/noopRouter.ts"],"names":[],"mappings":";;AAAA,2DAAsD;AAEtD,kBAAe,IAAA,mCAAe,EAAC,KAAK,IAAI,EAAE;IACxC,sGAAsG;AACxG,CAAC,CAAC,CAAC","sourcesContent":["import { createExpoPages } from './create-expo-pages';\n\nexport default createExpoPages(async () => {\n // noop the router for client-only mode. This ensures we skip loading the routes in react-server mode.\n});\n"]}