Add trailingSlash & pageDirectoryUrl config options (#1197)
This commit is contained in:
parent
1185d8ffcb
commit
c06da5dd78
17 changed files with 284 additions and 92 deletions
5
.changeset/soft-goats-wash.md
Normal file
5
.changeset/soft-goats-wash.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add configuration options for url format behavior: buildOptions.pageDirectoryUrl & trailingSlash
|
5
.changeset/tough-dancers-hear.md
Normal file
5
.changeset/tough-dancers-hear.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Move 404.html output from /404/index.html to /404.html
|
|
@ -1 +0,0 @@
|
||||||
<h1>hello</h1>
|
|
|
@ -3,30 +3,18 @@ layout: ~/layouts/MainLayout.astro
|
||||||
title: Configuration Reference
|
title: Configuration Reference
|
||||||
---
|
---
|
||||||
|
|
||||||
To configure Astro, add an `astro.config.mjs` file in the root of your project. All settings are optional. Here are the defaults:
|
To configure Astro, add an `astro.config.mjs` file in the root of your project. All settings are optional.
|
||||||
|
|
||||||
|
You can view the full configuration API (including information about default configuration) on GitHub: https://github.com/snowpackjs/astro/blob/latest/packages/astro/src/@types/config.ts
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
// Example: astro.config.mjs
|
||||||
|
|
||||||
|
/** @type {import('astro').AstroUserConfig} */
|
||||||
export default {
|
export default {
|
||||||
projectRoot: '.', // Where to resolve all URLs relative to. Useful if you have a monorepo project.
|
|
||||||
src: './src', // Path to Astro components, pages, and data
|
|
||||||
pages: './src/pages', // Path to Astro/Markdown pages
|
|
||||||
dist: './dist', // When running `astro build`, path to final static output
|
|
||||||
public: './public', // A folder of static files Astro will copy to the root. Useful for favicons, images, and other files that don't need processing.
|
|
||||||
buildOptions: {
|
buildOptions: {
|
||||||
// site: '', // Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs.
|
site: 'https://example.com',
|
||||||
sitemap: true, // Generate sitemap (set to "false" to disable)
|
|
||||||
},
|
},
|
||||||
devOptions: {
|
|
||||||
port: 3000, // The port to run the dev server on.
|
|
||||||
// tailwindConfig: '', // Path to tailwind.config.js if used, e.g. './tailwind.config.js'
|
|
||||||
},
|
|
||||||
// component renderers which are enabled by default
|
|
||||||
renderers: [
|
|
||||||
'@astrojs/renderer-svelte',
|
|
||||||
'@astrojs/renderer-vue',
|
|
||||||
'@astrojs/renderer-react',
|
|
||||||
'@astrojs/renderer-preact',
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { ImportSpecifier, ImportDefaultSpecifier, ImportNamespaceSpecifier } from '@babel/types';
|
import type { ImportSpecifier, ImportDefaultSpecifier, ImportNamespaceSpecifier } from '@babel/types';
|
||||||
import type { AstroMarkdownOptions } from '@astrojs/markdown-support';
|
import type { AstroMarkdownOptions } from '@astrojs/markdown-support';
|
||||||
|
import type { AstroConfig } from './config';
|
||||||
|
|
||||||
export interface RouteData {
|
export interface RouteData {
|
||||||
type: 'page';
|
type: 'page';
|
||||||
|
@ -22,33 +23,7 @@ export interface AstroConfigRaw {
|
||||||
jsx?: string;
|
jsx?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { AstroMarkdownOptions };
|
export { AstroMarkdownOptions, AstroConfig };
|
||||||
export interface AstroConfig {
|
|
||||||
dist: string;
|
|
||||||
projectRoot: URL;
|
|
||||||
pages: URL;
|
|
||||||
public: URL;
|
|
||||||
src: URL;
|
|
||||||
renderers?: string[];
|
|
||||||
/** Options for rendering markdown content */
|
|
||||||
markdownOptions?: Partial<AstroMarkdownOptions>;
|
|
||||||
/** Options specific to `astro build` */
|
|
||||||
buildOptions: {
|
|
||||||
/** Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs. */
|
|
||||||
site?: string;
|
|
||||||
/** Generate sitemap (set to "false" to disable) */
|
|
||||||
sitemap: boolean;
|
|
||||||
};
|
|
||||||
/** Options for the development server run with `astro dev`. */
|
|
||||||
devOptions: {
|
|
||||||
hostname?: string;
|
|
||||||
/** The port to run the dev server on. */
|
|
||||||
port: number;
|
|
||||||
projectRoot?: string;
|
|
||||||
/** Path to tailwind.config.js, if used */
|
|
||||||
tailwindConfig?: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AstroUserConfig = Omit<AstroConfig, 'buildOptions' | 'devOptions'> & {
|
export type AstroUserConfig = Omit<AstroConfig, 'buildOptions' | 'devOptions'> & {
|
||||||
buildOptions: {
|
buildOptions: {
|
||||||
|
|
75
packages/astro/src/@types/config.ts
Normal file
75
packages/astro/src/@types/config.ts
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import type { AstroMarkdownOptions } from '@astrojs/markdown-support';
|
||||||
|
export interface AstroConfig {
|
||||||
|
/**
|
||||||
|
* Where to resolve all URLs relative to. Useful if you have a monorepo project.
|
||||||
|
* Default: '.' (current working directory)
|
||||||
|
*/
|
||||||
|
projectRoot: URL;
|
||||||
|
/**
|
||||||
|
* Path to the `astro build` output.
|
||||||
|
* Default: './dist'
|
||||||
|
*/
|
||||||
|
dist: string;
|
||||||
|
/**
|
||||||
|
* Path to all of your Astro components, pages, and data.
|
||||||
|
* Default: './src'
|
||||||
|
*/
|
||||||
|
src: URL;
|
||||||
|
/**
|
||||||
|
* Path to your Astro/Markdown pages. Each file in this directory
|
||||||
|
* becomes a page in your final build.
|
||||||
|
* Default: './src/pages'
|
||||||
|
*/
|
||||||
|
pages: URL;
|
||||||
|
/**
|
||||||
|
* Path to your public files. These are copied over into your build directory, untouched.
|
||||||
|
* Useful for favicons, images, and other files that don't need processing.
|
||||||
|
* Default: './public'
|
||||||
|
*/
|
||||||
|
public: URL;
|
||||||
|
/**
|
||||||
|
* Framework component renderers enable UI framework rendering (static and dynamic).
|
||||||
|
* When you define this in your configuration, all other defaults are disabled.
|
||||||
|
* Default: [
|
||||||
|
* '@astrojs/renderer-svelte',
|
||||||
|
* '@astrojs/renderer-vue',
|
||||||
|
* '@astrojs/renderer-react',
|
||||||
|
* '@astrojs/renderer-preact',
|
||||||
|
* ],
|
||||||
|
*/
|
||||||
|
renderers?: string[];
|
||||||
|
/** Options for rendering markdown content */
|
||||||
|
markdownOptions?: Partial<AstroMarkdownOptions>;
|
||||||
|
/** Options specific to `astro build` */
|
||||||
|
buildOptions: {
|
||||||
|
/** Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs. */
|
||||||
|
site?: string;
|
||||||
|
/** Generate an automatically-generated sitemap for your build.
|
||||||
|
* Default: true
|
||||||
|
*/
|
||||||
|
sitemap: boolean;
|
||||||
|
/**
|
||||||
|
* Control the output file/URL format of each page.
|
||||||
|
* If true, Astro will generate a directory with a nested index.html (ex: "/foo/index.html") for each page.
|
||||||
|
* If false, Astro will generate a matching HTML file (ex: "/foo.html") instead of a directory.
|
||||||
|
* Default: true
|
||||||
|
*/
|
||||||
|
pageDirectoryUrl: boolean;
|
||||||
|
};
|
||||||
|
/** Options for the development server run with `astro dev`. */
|
||||||
|
devOptions: {
|
||||||
|
hostname?: string;
|
||||||
|
/** The port to run the dev server on. */
|
||||||
|
port: number;
|
||||||
|
/** Path to tailwind.config.js, if used */
|
||||||
|
tailwindConfig?: string;
|
||||||
|
/**
|
||||||
|
* Configure The trailing slash behavior of URL route matching:
|
||||||
|
* 'always' - Only match URLs that include a trailing slash (ex: "/foo/")
|
||||||
|
* 'never' - Never match URLs that include a trailing slash (ex: "/foo")
|
||||||
|
* 'ignore' - Match URLs regardless of whether a trailing "/" exists
|
||||||
|
* Default: 'always'
|
||||||
|
*/
|
||||||
|
trailingSlash: 'always' | 'never' | 'ignore';
|
||||||
|
};
|
||||||
|
}
|
|
@ -45,18 +45,30 @@ export async function getStaticPathsForPage({
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatOutFile(path: string, pageDirectoryUrl: boolean) {
|
||||||
|
if (path === '/404') {
|
||||||
|
return '/404.html';
|
||||||
|
}
|
||||||
|
if (path === '/') {
|
||||||
|
return '/index.html';
|
||||||
|
}
|
||||||
|
if (pageDirectoryUrl) {
|
||||||
|
return _path.posix.join(path, '/index.html');
|
||||||
|
}
|
||||||
|
return `${path}.html`;
|
||||||
|
}
|
||||||
/** Build static page */
|
/** Build static page */
|
||||||
export async function buildStaticPage({ astroConfig, buildState, path, route, astroRuntime }: PageBuildOptions): Promise<void> {
|
export async function buildStaticPage({ astroConfig, buildState, path, route, astroRuntime }: PageBuildOptions): Promise<void> {
|
||||||
const location = convertMatchToLocation(route, astroConfig);
|
const location = convertMatchToLocation(route, astroConfig);
|
||||||
const result = await astroRuntime.load(path);
|
const normalizedPath = astroConfig.devOptions.trailingSlash === 'never' ? path : path.endsWith('/') ? path : `${path}/`;
|
||||||
|
const result = await astroRuntime.load(normalizedPath);
|
||||||
if (result.statusCode !== 200) {
|
if (result.statusCode !== 200) {
|
||||||
let err = (result as any).error;
|
let err = (result as any).error;
|
||||||
if (!(err instanceof Error)) err = new Error(err);
|
if (!(err instanceof Error)) err = new Error(err);
|
||||||
err.filename = fileURLToPath(location.fileURL);
|
err.filename = fileURLToPath(location.fileURL);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
const outFile = _path.posix.join(path, '/index.html');
|
buildState[formatOutFile(path, astroConfig.buildOptions.pageDirectoryUrl)] = {
|
||||||
buildState[outFile] = {
|
|
||||||
srcPath: location.fileURL,
|
srcPath: location.fileURL,
|
||||||
contents: result.contents,
|
contents: result.contents,
|
||||||
contentType: 'text/html',
|
contentType: 'text/html',
|
||||||
|
|
|
@ -72,20 +72,21 @@ export async function collectBundleStats(buildState: BuildOutput, depTree: Bundl
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logURLStats(logging: LogOptions, urlStats: URLStatsMap) {
|
export function logURLStats(logging: LogOptions, urlStats: URLStatsMap) {
|
||||||
const builtURLs = [...urlStats.keys()].map((url) => url.replace(/index\.html$/, ''));
|
const builtURLs = [...urlStats.keys()].sort((a, b) => a.localeCompare(b, 'en', { numeric: true }));
|
||||||
builtURLs.sort((a, b) => a.localeCompare(b, 'en', { numeric: true }));
|
|
||||||
info(logging, null, '');
|
info(logging, null, '');
|
||||||
const log = table(logging, [60, 20]);
|
const log = table(logging, [60, 20]);
|
||||||
log(info, ' ' + bold(underline('Pages')), bold(underline('Page Weight (GZip)')));
|
log(info, ' ' + bold(underline('Pages')), bold(underline('Page Weight (GZip)')));
|
||||||
|
|
||||||
const lastIndex = builtURLs.length - 1;
|
const lastIndex = builtURLs.length - 1;
|
||||||
builtURLs.forEach((url, index) => {
|
builtURLs.forEach((url, index) => {
|
||||||
const sep = index === 0 ? '┌' : index === lastIndex ? '└' : '├';
|
const sep = index === 0 ? '┌' : index === lastIndex ? '└' : '├';
|
||||||
const urlPart = ' ' + sep + ' ' + url;
|
const urlPart = ' ' + sep + ' ' + url;
|
||||||
|
const bytes =
|
||||||
const bytes = (urlStats.get(url) || urlStats.get(url + 'index.html'))?.stats.map((s) => s.gzipSize).reduce((a, b) => a + b, 0) || 0;
|
urlStats
|
||||||
|
.get(url)
|
||||||
|
?.stats.map((s) => s.gzipSize)
|
||||||
|
.reduce((a, b) => a + b, 0) || 0;
|
||||||
const kb = (bytes * 0.001).toFixed(2);
|
const kb = (bytes * 0.001).toFixed(2);
|
||||||
const sizePart = kb + ' kB';
|
const sizePart = kb + ' kB';
|
||||||
log(info, urlPart + 'index.html', sizePart);
|
log(info, urlPart, sizePart);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,17 +55,19 @@ function validateConfig(config: any): void {
|
||||||
async function configDefaults(userConfig?: any): Promise<any> {
|
async function configDefaults(userConfig?: any): Promise<any> {
|
||||||
const config: any = { ...(userConfig || {}) };
|
const config: any = { ...(userConfig || {}) };
|
||||||
|
|
||||||
if (!config.projectRoot) config.projectRoot = '.';
|
if (config.projectRoot === undefined) config.projectRoot = '.';
|
||||||
if (!config.src) config.src = './src';
|
if (config.src === undefined) config.src = './src';
|
||||||
if (!config.pages) config.pages = './src/pages';
|
if (config.pages === undefined) config.pages = './src/pages';
|
||||||
if (!config.dist) config.dist = './dist';
|
if (config.dist === undefined) config.dist = './dist';
|
||||||
if (!config.public) config.public = './public';
|
if (config.public === undefined) config.public = './public';
|
||||||
if (!config.devOptions) config.devOptions = {};
|
if (config.devOptions === undefined) config.devOptions = {};
|
||||||
if (!config.devOptions.port) config.devOptions.port = await getPort({ port: getPort.makeRange(3000, 3050) });
|
if (config.devOptions.port === undefined) config.devOptions.port = await getPort({ port: getPort.makeRange(3000, 3050) });
|
||||||
if (!config.devOptions.hostname) config.devOptions.hostname = 'localhost';
|
if (config.devOptions.hostname === undefined) config.devOptions.hostname = 'localhost';
|
||||||
if (!config.buildOptions) config.buildOptions = {};
|
if (config.devOptions.trailingSlash === undefined) config.devOptions.trailingSlash = 'ignore';
|
||||||
if (!config.markdownOptions) config.markdownOptions = {};
|
if (config.buildOptions === undefined) config.buildOptions = {};
|
||||||
if (typeof config.buildOptions.sitemap === 'undefined') config.buildOptions.sitemap = true;
|
if (config.buildOptions.pageDirectoryUrl === undefined) config.buildOptions.pageDirectoryUrl = true;
|
||||||
|
if (config.markdownOptions === undefined) config.markdownOptions = {};
|
||||||
|
if (config.buildOptions.sitemap === undefined) config.buildOptions.sitemap = true;
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,8 +119,8 @@ export function createManifest({ config, cwd }: { config: AstroConfig; cwd?: str
|
||||||
} else {
|
} else {
|
||||||
components.push(item.file);
|
components.push(item.file);
|
||||||
const component = item.file;
|
const component = item.file;
|
||||||
const pattern = getPattern(segments, true);
|
const pattern = getPattern(segments, config.devOptions.trailingSlash);
|
||||||
const generate = getGenerator(segments, false);
|
const generate = getGenerator(segments, config.devOptions.trailingSlash);
|
||||||
const pathname = segments.every((segment) => segment.length === 1 && !segment[0].dynamic) ? `/${segments.map((segment) => segment[0].content).join('/')}` : null;
|
const pathname = segments.every((segment) => segment.length === 1 && !segment[0].dynamic) ? `/${segments.map((segment) => segment[0].content).join('/')}` : null;
|
||||||
|
|
||||||
routes.push({
|
routes.push({
|
||||||
|
@ -218,7 +218,17 @@ function getParts(part: string, file: string) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPattern(segments: Part[][], addTrailingSlash: boolean) {
|
function getTrailingSlashPattern(addTrailingSlash: AstroConfig['devOptions']['trailingSlash']): string {
|
||||||
|
if (addTrailingSlash === 'always') {
|
||||||
|
return '\\/$';
|
||||||
|
}
|
||||||
|
if (addTrailingSlash === 'never') {
|
||||||
|
return '$';
|
||||||
|
}
|
||||||
|
return '\\/?$';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPattern(segments: Part[][], addTrailingSlash: AstroConfig['devOptions']['trailingSlash']) {
|
||||||
const pathname = segments
|
const pathname = segments
|
||||||
.map((segment) => {
|
.map((segment) => {
|
||||||
return segment[0].spread
|
return segment[0].spread
|
||||||
|
@ -241,11 +251,11 @@ function getPattern(segments: Part[][], addTrailingSlash: boolean) {
|
||||||
})
|
})
|
||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
const trailing = addTrailingSlash && segments.length ? '\\/?$' : '$';
|
const trailing = addTrailingSlash && segments.length ? getTrailingSlashPattern(addTrailingSlash) : '$';
|
||||||
return new RegExp(`^${pathname || '\\/'}${trailing}`);
|
return new RegExp(`^${pathname || '\\/'}${trailing}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getGenerator(segments: Part[][], addTrailingSlash: boolean) {
|
function getGenerator(segments: Part[][], addTrailingSlash: AstroConfig['devOptions']['trailingSlash']) {
|
||||||
const template = segments
|
const template = segments
|
||||||
.map((segment) => {
|
.map((segment) => {
|
||||||
return segment[0].spread
|
return segment[0].spread
|
||||||
|
@ -268,7 +278,7 @@ function getGenerator(segments: Part[][], addTrailingSlash: boolean) {
|
||||||
})
|
})
|
||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
const trailing = addTrailingSlash && segments.length ? '/' : '';
|
const trailing = addTrailingSlash !== 'never' && segments.length ? '/' : '';
|
||||||
const toPath = compile(template + trailing);
|
const toPath = compile(template + trailing);
|
||||||
return toPath;
|
return toPath;
|
||||||
}
|
}
|
||||||
|
|
16
packages/astro/test/astro-pageDirectoryUrl.test.js
Normal file
16
packages/astro/test/astro-pageDirectoryUrl.test.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { suite } from 'uvu';
|
||||||
|
import * as assert from 'uvu/assert';
|
||||||
|
import { setupBuild } from './helpers.js';
|
||||||
|
|
||||||
|
const PageDirectoryUrl = suite('pageDirectoryUrl');
|
||||||
|
|
||||||
|
setupBuild(PageDirectoryUrl, './fixtures/astro-page-directory-url');
|
||||||
|
|
||||||
|
PageDirectoryUrl('outputs', async ({ build, readFile }) => {
|
||||||
|
await build();
|
||||||
|
assert.ok(await readFile('/client.html'));
|
||||||
|
assert.ok(await readFile('/nested-md.html'));
|
||||||
|
assert.ok(await readFile('/nested-astro.html'));
|
||||||
|
});
|
||||||
|
|
||||||
|
PageDirectoryUrl.run();
|
5
packages/astro/test/fixtures/astro-page-directory-url/astro.config.mjs
vendored
Normal file
5
packages/astro/test/fixtures/astro-page-directory-url/astro.config.mjs
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export default {
|
||||||
|
buildOptions: {
|
||||||
|
pageDirectoryUrl: false
|
||||||
|
}
|
||||||
|
};
|
3
packages/astro/test/fixtures/astro-page-directory-url/snowpack.config.json
vendored
Normal file
3
packages/astro/test/fixtures/astro-page-directory-url/snowpack.config.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"workspaceRoot": "../../../../../"
|
||||||
|
}
|
7
packages/astro/test/fixtures/astro-page-directory-url/src/pages/client.astro
vendored
Normal file
7
packages/astro/test/fixtures/astro-page-directory-url/src/pages/client.astro
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Stuff</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
12
packages/astro/test/fixtures/astro-page-directory-url/src/pages/nested-astro/index.astro
vendored
Normal file
12
packages/astro/test/fixtures/astro-page-directory-url/src/pages/nested-astro/index.astro
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
let title = 'Nested page'
|
||||||
|
---
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Head Stuff -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>{title}</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
5
packages/astro/test/fixtures/astro-page-directory-url/src/pages/nested-md/index.md
vendored
Normal file
5
packages/astro/test/fixtures/astro-page-directory-url/src/pages/nested-md/index.md
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: My Page
|
||||||
|
---
|
||||||
|
|
||||||
|
Hello world
|
|
@ -5,16 +5,14 @@ import { createManifest } from '../dist/manifest/create.js';
|
||||||
|
|
||||||
const cwd = new URL('./fixtures/route-manifest/', import.meta.url);
|
const cwd = new URL('./fixtures/route-manifest/', import.meta.url);
|
||||||
|
|
||||||
/**
|
const create = (dir, trailingSlash) => {
|
||||||
* @param {string} dir
|
|
||||||
* @param {string[]} [extensions]
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const create = (dir) => {
|
|
||||||
return createManifest({
|
return createManifest({
|
||||||
config: {
|
config: {
|
||||||
projectRoot: cwd,
|
projectRoot: cwd,
|
||||||
pages: new URL(dir, cwd),
|
pages: new URL(dir, cwd),
|
||||||
|
devOptions: {
|
||||||
|
trailingSlash,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cwd: fileURLToPath(cwd),
|
cwd: fileURLToPath(cwd),
|
||||||
});
|
});
|
||||||
|
@ -26,8 +24,82 @@ function cleanRoutes(routes) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
test('creates routes', () => {
|
test('creates routes with trailingSlashes = always', () => {
|
||||||
const { routes } = create('basic');
|
const { routes } = create('basic', 'always');
|
||||||
|
assert.equal(cleanRoutes(routes), [
|
||||||
|
{
|
||||||
|
type: 'page',
|
||||||
|
pattern: /^\/$/,
|
||||||
|
params: [],
|
||||||
|
component: 'basic/index.astro',
|
||||||
|
path: '/',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'page',
|
||||||
|
pattern: /^\/about\/$/,
|
||||||
|
params: [],
|
||||||
|
component: 'basic/about.astro',
|
||||||
|
path: '/about',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'page',
|
||||||
|
pattern: /^\/blog\/$/,
|
||||||
|
params: [],
|
||||||
|
component: 'basic/blog/index.astro',
|
||||||
|
path: '/blog',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'page',
|
||||||
|
pattern: /^\/blog\/([^/]+?)\/$/,
|
||||||
|
params: ['slug'],
|
||||||
|
component: 'basic/blog/[slug].astro',
|
||||||
|
path: null,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('creates routes with trailingSlashes = never', () => {
|
||||||
|
const { routes } = create('basic', 'never');
|
||||||
|
assert.equal(cleanRoutes(routes), [
|
||||||
|
{
|
||||||
|
type: 'page',
|
||||||
|
pattern: /^\/$/,
|
||||||
|
params: [],
|
||||||
|
component: 'basic/index.astro',
|
||||||
|
path: '/',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'page',
|
||||||
|
pattern: /^\/about$/,
|
||||||
|
params: [],
|
||||||
|
component: 'basic/about.astro',
|
||||||
|
path: '/about',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'page',
|
||||||
|
pattern: /^\/blog$/,
|
||||||
|
params: [],
|
||||||
|
component: 'basic/blog/index.astro',
|
||||||
|
path: '/blog',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'page',
|
||||||
|
pattern: /^\/blog\/([^/]+?)$/,
|
||||||
|
params: ['slug'],
|
||||||
|
component: 'basic/blog/[slug].astro',
|
||||||
|
path: null,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('creates routes with trailingSlashes = ignore', () => {
|
||||||
|
const { routes } = create('basic', 'ignore');
|
||||||
assert.equal(cleanRoutes(routes), [
|
assert.equal(cleanRoutes(routes), [
|
||||||
{
|
{
|
||||||
type: 'page',
|
type: 'page',
|
||||||
|
@ -64,7 +136,7 @@ test('creates routes', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('encodes invalid characters', () => {
|
test('encodes invalid characters', () => {
|
||||||
const { routes } = create('encoding');
|
const { routes } = create('encoding', 'always');
|
||||||
|
|
||||||
// had to remove ? and " because windows
|
// had to remove ? and " because windows
|
||||||
|
|
||||||
|
@ -76,34 +148,34 @@ test('encodes invalid characters', () => {
|
||||||
routes.map((p) => p.pattern),
|
routes.map((p) => p.pattern),
|
||||||
[
|
[
|
||||||
// /^\/%22$/,
|
// /^\/%22$/,
|
||||||
/^\/%23\/?$/,
|
/^\/%23\/$/,
|
||||||
// /^\/%3F$/
|
// /^\/%3F$/
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ignores files and directories with leading underscores', () => {
|
test('ignores files and directories with leading underscores', () => {
|
||||||
const { routes } = create('hidden-underscore');
|
const { routes } = create('hidden-underscore', 'always');
|
||||||
|
|
||||||
assert.equal(routes.map((r) => r.component).filter(Boolean), ['hidden-underscore/index.astro', 'hidden-underscore/e/f/g/h.astro']);
|
assert.equal(routes.map((r) => r.component).filter(Boolean), ['hidden-underscore/index.astro', 'hidden-underscore/e/f/g/h.astro']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ignores files and directories with leading dots except .well-known', () => {
|
test('ignores files and directories with leading dots except .well-known', () => {
|
||||||
const { routes } = create('hidden-dot');
|
const { routes } = create('hidden-dot', 'always');
|
||||||
|
|
||||||
assert.equal(routes.map((r) => r.component).filter(Boolean), ['hidden-dot/.well-known/dnt-policy.astro']);
|
assert.equal(routes.map((r) => r.component).filter(Boolean), ['hidden-dot/.well-known/dnt-policy.astro']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fails if dynamic params are not separated', () => {
|
test('fails if dynamic params are not separated', () => {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
create('invalid-params');
|
create('invalid-params', 'always');
|
||||||
}, /Invalid route invalid-params\/\[foo\]\[bar\]\.astro — parameters must be separated/);
|
}, /Invalid route invalid-params\/\[foo\]\[bar\]\.astro — parameters must be separated/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('disallows rest parameters inside segments', () => {
|
test('disallows rest parameters inside segments', () => {
|
||||||
assert.throws(
|
assert.throws(
|
||||||
() => {
|
() => {
|
||||||
create('invalid-rest');
|
create('invalid-rest', 'always');
|
||||||
},
|
},
|
||||||
/** @param {Error} e */
|
/** @param {Error} e */
|
||||||
(e) => {
|
(e) => {
|
||||||
|
@ -113,11 +185,11 @@ test('disallows rest parameters inside segments', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ignores things that look like lockfiles', () => {
|
test('ignores things that look like lockfiles', () => {
|
||||||
const { routes } = create('lockfiles');
|
const { routes } = create('lockfiles', 'always');
|
||||||
assert.equal(cleanRoutes(routes), [
|
assert.equal(cleanRoutes(routes), [
|
||||||
{
|
{
|
||||||
type: 'page',
|
type: 'page',
|
||||||
pattern: /^\/foo\/?$/,
|
pattern: /^\/foo\/$/,
|
||||||
params: [],
|
params: [],
|
||||||
component: 'lockfiles/foo.astro',
|
component: 'lockfiles/foo.astro',
|
||||||
path: '/foo',
|
path: '/foo',
|
||||||
|
@ -126,12 +198,12 @@ test('ignores things that look like lockfiles', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('allows multiple slugs', () => {
|
test('allows multiple slugs', () => {
|
||||||
const { routes } = create('multiple-slugs');
|
const { routes } = create('multiple-slugs', 'always');
|
||||||
|
|
||||||
assert.equal(cleanRoutes(routes), [
|
assert.equal(cleanRoutes(routes), [
|
||||||
{
|
{
|
||||||
type: 'page',
|
type: 'page',
|
||||||
pattern: /^\/([^/]+?)\.([^/]+?)\/?$/,
|
pattern: /^\/([^/]+?)\.([^/]+?)\/$/,
|
||||||
component: 'multiple-slugs/[file].[ext].astro',
|
component: 'multiple-slugs/[file].[ext].astro',
|
||||||
params: ['file', 'ext'],
|
params: ['file', 'ext'],
|
||||||
path: null,
|
path: null,
|
||||||
|
@ -140,7 +212,7 @@ test('allows multiple slugs', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sorts routes correctly', () => {
|
test('sorts routes correctly', () => {
|
||||||
const { routes } = create('sorting');
|
const { routes } = create('sorting', 'always');
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
routes.map((p) => p.component),
|
routes.map((p) => p.component),
|
||||||
|
|
Loading…
Reference in a new issue