Preserve base slash when trailingSlash ignore (#7878)
This commit is contained in:
parent
0f1e9d8361
commit
0f637c71e5
10 changed files with 42 additions and 39 deletions
15
.changeset/six-grapes-look.md
Normal file
15
.changeset/six-grapes-look.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
'astro': major
|
||||||
|
---
|
||||||
|
|
||||||
|
The value of `import.meta.env.BASE_URL`, which is derived from the `base` option, will no longer have a trailing slash added by default or when `trailingSlash: "ignore"` is set. The existing behavior of `base` in combination with `trailingSlash: "always"` or `trailingSlash: "never"` is unchanged.
|
||||||
|
|
||||||
|
If your `base` already has a trailing slash, no change is needed.
|
||||||
|
|
||||||
|
If your `base` does not have a trailing slash, add one to preserve the previous behaviour:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
// astro.config.mjs
|
||||||
|
- base: 'my-base',
|
||||||
|
+ base: 'my-base/',
|
||||||
|
```
|
|
@ -18,11 +18,11 @@ test.describe('Astro Environment BASE_URL', () => {
|
||||||
await page.goto(astro.resolveUrl('/blog/'));
|
await page.goto(astro.resolveUrl('/blog/'));
|
||||||
|
|
||||||
const astroBaseUrl = page.locator('id=astro-base-url');
|
const astroBaseUrl = page.locator('id=astro-base-url');
|
||||||
await expect(astroBaseUrl, 'astroBaseUrl equals to /blog/').toHaveText('/blog/');
|
await expect(astroBaseUrl, 'astroBaseUrl equals to /blog').toHaveText('/blog');
|
||||||
|
|
||||||
const clientComponentBaseUrl = page.locator('id=client-component-base-url');
|
const clientComponentBaseUrl = page.locator('id=client-component-base-url');
|
||||||
await expect(clientComponentBaseUrl, 'clientComponentBaseUrl equals to /blog').toHaveText(
|
await expect(clientComponentBaseUrl, 'clientComponentBaseUrl equals to /blog').toHaveText(
|
||||||
'/blog/'
|
'/blog'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -572,12 +572,7 @@ export interface AstroUserConfig {
|
||||||
*
|
*
|
||||||
* When using this option, all of your static asset imports and URLs should add the base as a prefix. You can access this value via `import.meta.env.BASE_URL`.
|
* When using this option, all of your static asset imports and URLs should add the base as a prefix. You can access this value via `import.meta.env.BASE_URL`.
|
||||||
*
|
*
|
||||||
* By default, the value of `import.meta.env.BASE_URL` includes a trailing slash. If you have the [`trailingSlash`](https://docs.astro.build/en/reference/configuration-reference/#trailingslash) option set to `'never'`, you will need to add it manually in your static asset imports and URLs.
|
* The value of `import.meta.env.BASE_URL` respects your `trailingSlash` config and will include a trailing slash if you explicitly include one or if `trailingSlash: "always"` is set. If `trailingSlash: "never"` is set, `BASE_URL` will not include a trailing slash, even if `base` includes one.
|
||||||
*
|
|
||||||
* ```astro
|
|
||||||
* <a href="/docs/about/">About</a>
|
|
||||||
* <img src=`${import.meta.env.BASE_URL}image.png`>
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
base?: string;
|
base?: string;
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import {
|
||||||
} from '../../core/build/internal.js';
|
} from '../../core/build/internal.js';
|
||||||
import {
|
import {
|
||||||
isRelativePath,
|
isRelativePath,
|
||||||
|
joinPaths,
|
||||||
prependForwardSlash,
|
prependForwardSlash,
|
||||||
removeLeadingForwardSlash,
|
removeLeadingForwardSlash,
|
||||||
removeTrailingForwardSlash,
|
removeTrailingForwardSlash,
|
||||||
|
@ -437,11 +438,11 @@ function getUrlForPath(
|
||||||
buildPathname = base;
|
buildPathname = base;
|
||||||
} else if (routeType === 'endpoint') {
|
} else if (routeType === 'endpoint') {
|
||||||
const buildPathRelative = removeLeadingForwardSlash(pathname);
|
const buildPathRelative = removeLeadingForwardSlash(pathname);
|
||||||
buildPathname = base + buildPathRelative;
|
buildPathname = joinPaths(base, buildPathRelative);
|
||||||
} else {
|
} else {
|
||||||
const buildPathRelative =
|
const buildPathRelative =
|
||||||
removeTrailingForwardSlash(removeLeadingForwardSlash(pathname)) + ending;
|
removeTrailingForwardSlash(removeLeadingForwardSlash(pathname)) + ending;
|
||||||
buildPathname = base + buildPathRelative;
|
buildPathname = joinPaths(base, buildPathRelative);
|
||||||
}
|
}
|
||||||
const url = new URL(buildPathname, origin);
|
const url = new URL(buildPathname, origin);
|
||||||
return url;
|
return url;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import path from 'node:path';
|
||||||
import { pathToFileURL } from 'node:url';
|
import { pathToFileURL } from 'node:url';
|
||||||
import { BUNDLED_THEMES } from 'shiki';
|
import { BUNDLED_THEMES } from 'shiki';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { appendForwardSlash, prependForwardSlash, trimSlashes } from '../path.js';
|
import { appendForwardSlash, prependForwardSlash, removeTrailingForwardSlash } from '../path.js';
|
||||||
|
|
||||||
const ASTRO_CONFIG_DEFAULTS = {
|
const ASTRO_CONFIG_DEFAULTS = {
|
||||||
root: '.',
|
root: '.',
|
||||||
|
@ -366,22 +366,14 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: string) {
|
||||||
) {
|
) {
|
||||||
config.build.client = new URL('./dist/client/', config.outDir);
|
config.build.client = new URL('./dist/client/', config.outDir);
|
||||||
}
|
}
|
||||||
const trimmedBase = trimSlashes(config.base);
|
|
||||||
|
|
||||||
// If there is no base but there is a base for site config, warn.
|
// Handle `base` trailing slash based on `trailingSlash` config
|
||||||
const sitePathname = config.site && new URL(config.site).pathname;
|
if (config.trailingSlash === 'never') {
|
||||||
if (!trimmedBase.length && sitePathname && sitePathname !== '/') {
|
config.base = prependForwardSlash(removeTrailingForwardSlash(config.base));
|
||||||
config.base = sitePathname;
|
} else if (config.trailingSlash === 'always') {
|
||||||
/* eslint-disable no-console */
|
config.base = prependForwardSlash(appendForwardSlash(config.base));
|
||||||
console.warn(`The site configuration value includes a pathname of ${sitePathname} but there is no base configuration.
|
|
||||||
|
|
||||||
A future version of Astro will stop using the site pathname when producing <link> and <script> tags. Set your site's base with the base configuration.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trimmedBase.length && config.trailingSlash === 'never') {
|
|
||||||
config.base = prependForwardSlash(trimmedBase);
|
|
||||||
} else {
|
} else {
|
||||||
config.base = prependForwardSlash(appendForwardSlash(trimmedBase));
|
config.base = prependForwardSlash(config.base);
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|
|
@ -109,7 +109,7 @@ describe('Environment Variables', () => {
|
||||||
expect(res.status).to.equal(200);
|
expect(res.status).to.equal(200);
|
||||||
let indexHtml = await res.text();
|
let indexHtml = await res.text();
|
||||||
let $ = cheerio.load(indexHtml);
|
let $ = cheerio.load(indexHtml);
|
||||||
expect($('#base-url').text()).to.equal('/blog/');
|
expect($('#base-url').text()).to.equal('/blog');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does render destructured builtin SITE env', async () => {
|
it('does render destructured builtin SITE env', async () => {
|
||||||
|
@ -117,7 +117,7 @@ describe('Environment Variables', () => {
|
||||||
expect(res.status).to.equal(200);
|
expect(res.status).to.equal(200);
|
||||||
let indexHtml = await res.text();
|
let indexHtml = await res.text();
|
||||||
let $ = cheerio.load(indexHtml);
|
let $ = cheerio.load(indexHtml);
|
||||||
expect($('#base-url').text()).to.equal('/blog/');
|
expect($('#base-url').text()).to.equal('/blog');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -54,10 +54,10 @@ describe('Astro Global', () => {
|
||||||
const html = await fixture.readFile('/index.html');
|
const html = await fixture.readFile('/index.html');
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
expect($('#pathname').text()).to.equal('/blog/');
|
expect($('#pathname').text()).to.equal('/blog');
|
||||||
expect($('#searchparams').text()).to.equal('{}');
|
expect($('#searchparams').text()).to.equal('{}');
|
||||||
expect($('#child-pathname').text()).to.equal('/blog/');
|
expect($('#child-pathname').text()).to.equal('/blog');
|
||||||
expect($('#nested-child-pathname').text()).to.equal('/blog/');
|
expect($('#nested-child-pathname').text()).to.equal('/blog');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Astro.site', async () => {
|
it('Astro.site', async () => {
|
||||||
|
|
|
@ -113,9 +113,9 @@ describe('Development Routing', () => {
|
||||||
expect(response.status).to.equal(200);
|
expect(response.status).to.equal(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('404 when loading subpath root without trailing slash', async () => {
|
it('200 when loading subpath root without trailing slash', async () => {
|
||||||
const response = await fixture.fetch('/blog');
|
const response = await fixture.fetch('/blog');
|
||||||
expect(response.status).to.equal(404);
|
expect(response.status).to.equal(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('200 when loading another page with subpath used', async () => {
|
it('200 when loading another page with subpath used', async () => {
|
||||||
|
@ -163,9 +163,9 @@ describe('Development Routing', () => {
|
||||||
expect(response.status).to.equal(200);
|
expect(response.status).to.equal(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('404 when loading subpath root without trailing slash', async () => {
|
it('200 when loading subpath root without trailing slash', async () => {
|
||||||
const response = await fixture.fetch('/blog');
|
const response = await fixture.fetch('/blog');
|
||||||
expect(response.status).to.equal(404);
|
expect(response.status).to.equal(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('200 when loading another page with subpath used', async () => {
|
it('200 when loading another page with subpath used', async () => {
|
||||||
|
|
|
@ -157,9 +157,9 @@ describe('Preview Routing', () => {
|
||||||
expect(response.status).to.equal(200);
|
expect(response.status).to.equal(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('404 when loading subpath root without trailing slash', async () => {
|
it('200 when loading subpath root without trailing slash', async () => {
|
||||||
const response = await fixture.fetch('/blog');
|
const response = await fixture.fetch('/blog');
|
||||||
expect(response.status).to.equal(404);
|
expect(response.status).to.equal(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('200 when loading another page with subpath used', async () => {
|
it('200 when loading another page with subpath used', async () => {
|
||||||
|
@ -345,9 +345,9 @@ describe('Preview Routing', () => {
|
||||||
expect(response.status).to.equal(200);
|
expect(response.status).to.equal(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('404 when loading subpath root without trailing slash', async () => {
|
it('200 when loading subpath root without trailing slash', async () => {
|
||||||
const response = await fixture.fetch('/blog');
|
const response = await fixture.fetch('/blog');
|
||||||
expect(response.status).to.equal(404);
|
expect(response.status).to.equal(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('200 when loading another page with subpath used', async () => {
|
it('200 when loading another page with subpath used', async () => {
|
||||||
|
|
|
@ -44,7 +44,7 @@ describe('Public dev with base', () => {
|
||||||
expect(response.status).to.equal(404);
|
expect(response.status).to.equal(404);
|
||||||
const html = await response.text();
|
const html = await response.text();
|
||||||
$ = cheerio.load(html);
|
$ = cheerio.load(html);
|
||||||
expect($('a').first().text()).to.equal('/blog/');
|
expect($('a').first().text()).to.equal('/blog');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('default 404 page when loading /none/', async () => {
|
it('default 404 page when loading /none/', async () => {
|
||||||
|
|
Loading…
Reference in a new issue