-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'markdalgleish/routes-ts' into markdalgleish/routes-ts-fs
Showing
20 changed files
with
532 additions
and
329 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
141 changes: 141 additions & 0 deletions
141
packages/remix-dev/__tests__/validateRouteConfig-test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import { validateRouteConfig } from "../config/routes"; | ||
|
||
describe("validateRouteConfig", () => { | ||
it("validates a route config", () => { | ||
expect( | ||
validateRouteConfig({ | ||
routeConfigFile: "routes.ts", | ||
routeConfig: [ | ||
{ | ||
path: "parent", | ||
file: "parent.tsx", | ||
children: [ | ||
{ | ||
path: "child", | ||
file: "child.tsx", | ||
}, | ||
], | ||
}, | ||
], | ||
}).valid | ||
).toBe(true); | ||
}); | ||
|
||
it("is invalid when not an array", () => { | ||
let result = validateRouteConfig({ | ||
routeConfigFile: "routes.ts", | ||
routeConfig: { path: "path", file: "file.tsx" }, | ||
}); | ||
|
||
expect(result.valid).toBe(false); | ||
expect(!result.valid && result.message).toMatchInlineSnapshot( | ||
`"Route config in "routes.ts" must be an array."` | ||
); | ||
}); | ||
|
||
it("is invalid when route is a promise", () => { | ||
let result = validateRouteConfig({ | ||
routeConfigFile: "routes.ts", | ||
routeConfig: [ | ||
{ | ||
path: "parent", | ||
file: "parent.tsx", | ||
children: [Promise.resolve({})], | ||
}, | ||
], | ||
}); | ||
|
||
expect(result.valid).toBe(false); | ||
expect(!result.valid && result.message).toMatchInlineSnapshot(` | ||
"Route config in "routes.ts" is invalid. | ||
Path: routes.0.children.0 | ||
Invalid type: Expected object but received a promise. Did you forget to await?" | ||
`); | ||
}); | ||
|
||
it("is invalid when file is missing", () => { | ||
let result = validateRouteConfig({ | ||
routeConfigFile: "routes.ts", | ||
routeConfig: [ | ||
{ | ||
path: "parent", | ||
file: "parent.tsx", | ||
children: [ | ||
{ | ||
id: "child", | ||
}, | ||
], | ||
}, | ||
], | ||
}); | ||
|
||
expect(result.valid).toBe(false); | ||
expect(!result.valid && result.message).toMatchInlineSnapshot(` | ||
"Route config in "routes.ts" is invalid. | ||
Path: routes.0.children.0.file | ||
Invalid type: Expected string but received undefined" | ||
`); | ||
}); | ||
|
||
it("is invalid when property is wrong type", () => { | ||
let result = validateRouteConfig({ | ||
routeConfigFile: "routes.ts", | ||
routeConfig: [ | ||
{ | ||
path: "parent", | ||
file: "parent.tsx", | ||
children: [ | ||
{ | ||
file: 123, | ||
}, | ||
], | ||
}, | ||
], | ||
}); | ||
|
||
expect(result.valid).toBe(false); | ||
expect(!result.valid && result.message).toMatchInlineSnapshot(` | ||
"Route config in "routes.ts" is invalid. | ||
Path: routes.0.children.0.file | ||
Invalid type: Expected string but received 123" | ||
`); | ||
}); | ||
|
||
it("shows multiple error messages", () => { | ||
let result = validateRouteConfig({ | ||
routeConfigFile: "routes.ts", | ||
routeConfig: [ | ||
{ | ||
path: "parent", | ||
file: "parent.tsx", | ||
children: [ | ||
{ | ||
id: "child", | ||
}, | ||
{ | ||
file: 123, | ||
}, | ||
Promise.resolve(), | ||
], | ||
}, | ||
], | ||
}); | ||
|
||
expect(result.valid).toBe(false); | ||
expect(!result.valid && result.message).toMatchInlineSnapshot(` | ||
"Route config in "routes.ts" is invalid. | ||
Path: routes.0.children.0.file | ||
Invalid type: Expected string but received undefined | ||
Path: routes.0.children.1.file | ||
Invalid type: Expected string but received 123 | ||
Path: routes.0.children.2 | ||
Invalid type: Expected object but received a promise. Did you forget to await?" | ||
`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Welcome to Remix! | ||
|
||
[Remix](https://remix.run) is a web framework that helps you build better websites with React. | ||
|
||
To get started, open a new shell and run: | ||
|
||
```sh | ||
npx create-remix@latest | ||
``` | ||
|
||
Then follow the prompts you see in your terminal. | ||
|
||
For more information about Remix, [visit remix.run](https://remix.run)! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
export type { | ||
UNSAFE_RouteConfig as RouteConfig, | ||
UNSAFE_RouteConfigEntry as RouteConfigEntry, | ||
} from "@remix-run/dev"; | ||
|
||
export { | ||
route, | ||
index, | ||
layout, | ||
prefix, | ||
relative, | ||
getAppDirectory, | ||
} from "./routes"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/** @type {import('jest').Config} */ | ||
module.exports = { | ||
...require("../../jest/jest.config.shared"), | ||
displayName: "route-config", | ||
setupFiles: [], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
{ | ||
"name": "@remix-run/route-config", | ||
"version": "2.13.1", | ||
"description": "Config-based routing for Remix", | ||
"bugs": { | ||
"url": "https://github.com/remix-run/remix/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/remix-run/remix", | ||
"directory": "packages/remix-fs-routes" | ||
}, | ||
"license": "MIT", | ||
"main": "dist/index.js", | ||
"typings": "dist/index.d.ts", | ||
"exports": { | ||
".": { | ||
"types": "./dist/index.d.ts", | ||
"default": "./dist/index.js" | ||
}, | ||
"./package.json": "./package.json" | ||
}, | ||
"scripts": { | ||
"tsc": "tsc" | ||
}, | ||
"dependencies": { | ||
"lodash": "^4.17.21" | ||
}, | ||
"devDependencies": { | ||
"@remix-run/dev": "workspace:*", | ||
"@types/lodash": "^4.14.182", | ||
"typescript": "^5.1.6", | ||
"vite": "5.1.8" | ||
}, | ||
"peerDependencies": { | ||
"@remix-run/dev": "workspace:^", | ||
"typescript": "^5.1.0" | ||
}, | ||
"peerDependenciesMeta": { | ||
"typescript": { | ||
"optional": true | ||
} | ||
}, | ||
"engines": { | ||
"node": ">=18.0.0" | ||
}, | ||
"files": [ | ||
"dist/", | ||
"CHANGELOG.md", | ||
"LICENSE.md", | ||
"README.md" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
const path = require("node:path"); | ||
const babel = require("@rollup/plugin-babel").default; | ||
const nodeResolve = require("@rollup/plugin-node-resolve").default; | ||
const copy = require("rollup-plugin-copy"); | ||
|
||
const { | ||
copyToPlaygrounds, | ||
createBanner, | ||
getOutputDir, | ||
isBareModuleId, | ||
} = require("../../rollup.utils"); | ||
const { name: packageName, version } = require("./package.json"); | ||
|
||
/** @returns {import("rollup").RollupOptions[]} */ | ||
module.exports = function rollup() { | ||
let sourceDir = "packages/remix-route-config"; | ||
let outputDir = getOutputDir(packageName); | ||
let outputDist = path.join(outputDir, "dist"); | ||
|
||
return [ | ||
{ | ||
external: (id) => isBareModuleId(id), | ||
input: `${sourceDir}/index.ts`, | ||
output: { | ||
banner: createBanner(packageName, version), | ||
dir: outputDist, | ||
format: "cjs", | ||
preserveModules: true, | ||
exports: "auto", | ||
}, | ||
plugins: [ | ||
babel({ | ||
babelHelpers: "bundled", | ||
exclude: /node_modules/, | ||
extensions: [".ts"], | ||
}), | ||
nodeResolve({ extensions: [".ts"] }), | ||
copy({ | ||
targets: [{ src: "LICENSE.md", dest: sourceDir }], | ||
}), | ||
copyToPlaygrounds(), | ||
], | ||
}, | ||
]; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
import { resolve } from "node:path"; | ||
import pick from "lodash/pick"; | ||
import { | ||
type UNSAFE_RouteConfigEntry as RouteConfigEntry, | ||
UNSAFE_getRouteConfigAppDirectory as getRouteConfigAppDirectory, | ||
} from "@remix-run/dev"; | ||
|
||
/** | ||
* Provides the absolute path to the app directory, for use within `routes.ts`. | ||
* This is designed to support resolving file system routes. | ||
*/ | ||
export function getAppDirectory() { | ||
return getRouteConfigAppDirectory(); | ||
} | ||
|
||
const createConfigRouteOptionKeys = [ | ||
"id", | ||
"index", | ||
"caseSensitive", | ||
] as const satisfies ReadonlyArray<keyof RouteConfigEntry>; | ||
type CreateRouteOptions = Pick< | ||
RouteConfigEntry, | ||
typeof createConfigRouteOptionKeys[number] | ||
>; | ||
/** | ||
* Helper function for creating a route config entry, for use within | ||
* `routes.ts`. | ||
*/ | ||
function route( | ||
path: string | null | undefined, | ||
file: string, | ||
children?: RouteConfigEntry[] | ||
): RouteConfigEntry; | ||
function route( | ||
path: string | null | undefined, | ||
file: string, | ||
options: CreateRouteOptions, | ||
children?: RouteConfigEntry[] | ||
): RouteConfigEntry; | ||
function route( | ||
path: string | null | undefined, | ||
file: string, | ||
optionsOrChildren: CreateRouteOptions | RouteConfigEntry[] | undefined, | ||
children?: RouteConfigEntry[] | ||
): RouteConfigEntry { | ||
let options: CreateRouteOptions = {}; | ||
|
||
if (Array.isArray(optionsOrChildren) || !optionsOrChildren) { | ||
children = optionsOrChildren; | ||
} else { | ||
options = optionsOrChildren; | ||
} | ||
|
||
return { | ||
file, | ||
children, | ||
path: path ?? undefined, | ||
...pick(options, createConfigRouteOptionKeys), | ||
}; | ||
} | ||
|
||
const createIndexOptionKeys = ["id"] as const satisfies ReadonlyArray< | ||
keyof RouteConfigEntry | ||
>; | ||
type CreateIndexOptions = Pick< | ||
RouteConfigEntry, | ||
typeof createIndexOptionKeys[number] | ||
>; | ||
/** | ||
* Helper function for creating a route config entry for an index route, for use | ||
* within `routes.ts`. | ||
*/ | ||
function index(file: string, options?: CreateIndexOptions): RouteConfigEntry { | ||
return { | ||
file, | ||
index: true, | ||
...pick(options, createIndexOptionKeys), | ||
}; | ||
} | ||
|
||
const createLayoutOptionKeys = ["id"] as const satisfies ReadonlyArray< | ||
keyof RouteConfigEntry | ||
>; | ||
type CreateLayoutOptions = Pick< | ||
RouteConfigEntry, | ||
typeof createLayoutOptionKeys[number] | ||
>; | ||
/** | ||
* Helper function for creating a route config entry for a layout route, for use | ||
* within `routes.ts`. | ||
*/ | ||
function layout(file: string, children?: RouteConfigEntry[]): RouteConfigEntry; | ||
function layout( | ||
file: string, | ||
options: CreateLayoutOptions, | ||
children?: RouteConfigEntry[] | ||
): RouteConfigEntry; | ||
function layout( | ||
file: string, | ||
optionsOrChildren: CreateLayoutOptions | RouteConfigEntry[] | undefined, | ||
children?: RouteConfigEntry[] | ||
): RouteConfigEntry { | ||
let options: CreateLayoutOptions = {}; | ||
|
||
if (Array.isArray(optionsOrChildren) || !optionsOrChildren) { | ||
children = optionsOrChildren; | ||
} else { | ||
options = optionsOrChildren; | ||
} | ||
|
||
return { | ||
file, | ||
children, | ||
...pick(options, createLayoutOptionKeys), | ||
}; | ||
} | ||
|
||
/** | ||
* Helper function for adding a path prefix to a set of routes without needing | ||
* to introduce a parent route file, for use within `routes.ts`. | ||
*/ | ||
function prefix( | ||
prefixPath: string, | ||
routes: RouteConfigEntry[] | ||
): RouteConfigEntry[] { | ||
return routes.map((route) => { | ||
if (route.index || typeof route.path === "string") { | ||
return { | ||
...route, | ||
path: route.path ? joinRoutePaths(prefixPath, route.path) : prefixPath, | ||
children: route.children, | ||
}; | ||
} else if (route.children) { | ||
return { | ||
...route, | ||
children: prefix(prefixPath, route.children), | ||
}; | ||
} | ||
return route; | ||
}); | ||
} | ||
|
||
const helpers = { route, index, layout, prefix }; | ||
export { route, index, layout, prefix }; | ||
/** | ||
* Creates a set of route config helpers that resolve file paths relative to the | ||
* given directory, for use within `routes.ts`. This is designed to support | ||
* splitting route config into multiple files within different directories. | ||
*/ | ||
export function relative(directory: string): typeof helpers { | ||
return { | ||
/** | ||
* Helper function for creating a route config entry, for use within | ||
* `routes.ts`. Note that this helper has been scoped, meaning that file | ||
* path will be resolved relative to the directory provided to the | ||
* `relative` call that created this helper. | ||
*/ | ||
route: (path, file, ...rest) => { | ||
return route(path, resolve(directory, file), ...(rest as any)); | ||
}, | ||
/** | ||
* Helper function for creating a route config entry for an index route, for | ||
* use within `routes.ts`. Note that this helper has been scoped, meaning | ||
* that file path will be resolved relative to the directory provided to the | ||
* `relative` call that created this helper. | ||
*/ | ||
index: (file, ...rest) => { | ||
return index(resolve(directory, file), ...(rest as any)); | ||
}, | ||
/** | ||
* Helper function for creating a route config entry for a layout route, for | ||
* use within `routes.ts`. Note that this helper has been scoped, meaning | ||
* that file path will be resolved relative to the directory provided to the | ||
* `relative` call that created this helper. | ||
*/ | ||
layout: (file, ...rest) => { | ||
return layout(resolve(directory, file), ...(rest as any)); | ||
}, | ||
|
||
// Passthrough of helper functions that don't need relative scoping so that | ||
// a complete API is still provided. | ||
prefix, | ||
}; | ||
} | ||
|
||
function joinRoutePaths(path1: string, path2: string): string { | ||
return [ | ||
path1.replace(/\/+$/, ""), // Remove trailing slashes | ||
path2.replace(/^\/+/, ""), // Remove leading slashes | ||
].join("/"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"include": ["**/*.ts"], | ||
"exclude": ["dist", "__tests__", "node_modules"], | ||
"compilerOptions": { | ||
"lib": ["DOM", "DOM.Iterable", "ES2022"], | ||
"target": "ES2022", | ||
"module": "ES2022", | ||
"skipLibCheck": true, | ||
|
||
"moduleResolution": "Bundler", | ||
"allowSyntheticDefaultImports": true, | ||
"strict": true, | ||
"jsx": "react", | ||
"declaration": true, | ||
"emitDeclarationOnly": true, | ||
"rootDir": ".", | ||
"outDir": "./dist" | ||
} | ||
} |
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters