mirror of
https://github.com/withastro/astro.git
synced 2025-01-06 22:10:10 -05:00
Add multiple cdn v2 (#10189)
* feat: add multiple cdn * add multiple cdn test * assetsPrefix not empty string * add changeset * simplify code * change docs * replace getFileExtension with node path.extname * Adapt node extname * multiple image test * wip space * update docs * update docs, assetsPrefix type * update docs * update docs * chore: update types and rename to `fallback` * enhance changelog * change docs * update change defaultAeestsPrefix to fallback key test * move utility to a new to avoid importing `node:path` inside vite plugins * Update packages/astro/src/@types/astro.ts Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * Apply suggestions from code review Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * chore: address Bjorn's comments * kill the variable * kill the variable /2 * Fix CI fail * Apply suggestions from code review Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * uniform code sample * add `.` string for fit getAssetsPrefix * Fix extension function --------- Co-authored-by: Emanuele Stoppa <my.burning@gmail.com> Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> Co-authored-by: bluwy <bjornlu.dev@gmail.com> Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com>
This commit is contained in:
parent
d9336668e5
commit
1ea0a25b94
13 changed files with 289 additions and 37 deletions
26
.changeset/fluffy-readers-add.md
Normal file
26
.changeset/fluffy-readers-add.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
"@astrojs/internal-helpers": minor
|
||||
"astro": minor
|
||||
---
|
||||
|
||||
Adds the option to pass an object to `build.assetsPrefix`. This allows for the use of multiple CDN prefixes based on the target file type.
|
||||
|
||||
When passing an object to `build.assetsPrefix`, you must also specify a `fallback` domain to be used for all other file types not specified.
|
||||
|
||||
Specify a file extension as the key (e.g. 'js', 'png') and the URL serving your assets of that file type as the value:
|
||||
|
||||
```js
|
||||
// astro.config.mjs
|
||||
import { defineConfig } from "astro/config"
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
assetsPrefix: {
|
||||
'js': "https://js.cdn.example.com",
|
||||
'mjs': "https://js.cdn.example.com", // if you have .mjs files, you must add a new entry like this
|
||||
'png': "https://images.cdn.example.com",
|
||||
'fallback': "https://generic.cdn.example.com"
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
|
@ -13,7 +13,7 @@ import type * as babel from '@babel/core';
|
|||
import type * as rollup from 'rollup';
|
||||
import type * as vite from 'vite';
|
||||
import type { RemotePattern } from '../assets/utils/remotePattern.js';
|
||||
import type { SerializedSSRManifest } from '../core/app/types.js';
|
||||
import type { SerializedSSRManifest, AssetsPrefix } from '../core/app/types.js';
|
||||
import type { PageBuildData } from '../core/build/types.js';
|
||||
import type { AstroConfigType } from '../core/config/index.js';
|
||||
import type { AstroTimer } from '../core/config/timer.js';
|
||||
|
@ -67,7 +67,7 @@ export type {
|
|||
UnresolvedImageTransform,
|
||||
} from '../assets/types.js';
|
||||
export type { RemotePattern } from '../assets/utils/remotePattern.js';
|
||||
export type { SSRManifest } from '../core/app/types.js';
|
||||
export type { SSRManifest, AssetsPrefix } from '../core/app/types.js';
|
||||
export type {
|
||||
AstroCookieGetOptions,
|
||||
AstroCookieSetOptions,
|
||||
|
@ -881,15 +881,15 @@ export interface AstroUserConfig {
|
|||
/**
|
||||
* @docs
|
||||
* @name build.assetsPrefix
|
||||
* @type {string}
|
||||
* @type {string | Record<string, string>}
|
||||
* @default `undefined`
|
||||
* @version 2.2.0
|
||||
* @description
|
||||
* Specifies the prefix for Astro-generated asset links. This can be used if assets are served from a different domain than the current site.
|
||||
*
|
||||
* For example, if this is set to `https://cdn.example.com`, assets will be fetched from `https://cdn.example.com/_astro/...` (regardless of the `base` option).
|
||||
* You would need to upload the files in `./dist/_astro/` to `https://cdn.example.com/_astro/` to serve the assets.
|
||||
* The process varies depending on how the third-party domain is hosted.
|
||||
* This requires uploading the assets in your local `./dist/_astro` folder to a corresponding `/_astro/` folder on the remote domain.
|
||||
*
|
||||
* To fetch all assets uploaded to the same domain (e.g. `https://cdn.example.com/_astro/...`), set `assetsPrefix` to the root domain as a string (regardless of your `base` configuration):
|
||||
* To rename the `_astro` path, specify a new directory in `build.assets`.
|
||||
*
|
||||
* ```js
|
||||
|
@ -899,8 +899,27 @@ export interface AstroUserConfig {
|
|||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* **Added in 4.5.0**
|
||||
*
|
||||
* You can also pass an object to `assetsPrefix` to specify a different domain for each file type.
|
||||
* In this case, a `fallback` property is required and will be used by default for any other files.
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* build: {
|
||||
* assetsPrefix: {
|
||||
* 'js': 'https://js.cdn.example.com',
|
||||
* 'mjs': 'https://js.cdn.example.com',
|
||||
* 'css': 'https://css.cdn.example.com',
|
||||
* 'fallback': 'https://cdn.example.com'
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
assetsPrefix?: string;
|
||||
assetsPrefix?: AssetsPrefix;
|
||||
/**
|
||||
* @docs
|
||||
* @name build.serverEntry
|
||||
|
|
12
packages/astro/src/assets/utils/getAssetsPrefix.ts
Normal file
12
packages/astro/src/assets/utils/getAssetsPrefix.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import type { AssetsPrefix } from '../../core/app/types.js';
|
||||
|
||||
export function getAssetsPrefix(fileExtension: string, assetsPrefix?: AssetsPrefix): string {
|
||||
if (!assetsPrefix) return '';
|
||||
if (typeof assetsPrefix === 'string') return assetsPrefix;
|
||||
// we assume the file extension has a leading '.' and we remove it
|
||||
const dotLessFileExtension = fileExtension.slice(1);
|
||||
if (assetsPrefix[dotLessFileExtension]) {
|
||||
return assetsPrefix[dotLessFileExtension];
|
||||
}
|
||||
return assetsPrefix.fallback;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import MagicString from 'magic-string';
|
||||
import type * as vite from 'vite';
|
||||
import { normalizePath } from 'vite';
|
||||
import { extname } from 'node:path';
|
||||
import type { AstroPluginOptions, ImageTransform } from '../@types/astro.js';
|
||||
import { extendManualChunks } from '../core/build/plugins/util.js';
|
||||
import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
||||
|
@ -16,6 +17,7 @@ import { emitESMImage } from './utils/emitAsset.js';
|
|||
import { isESMImportedImage } from './utils/imageKind.js';
|
||||
import { getProxyCode } from './utils/proxy.js';
|
||||
import { hashTransform, propsToFilename } from './utils/transformToPath.js';
|
||||
import { getAssetsPrefix } from './utils/getAssetsPrefix.js';
|
||||
|
||||
const resolvedVirtualModuleId = '\0' + VIRTUAL_MODULE_ID;
|
||||
|
||||
|
@ -95,9 +97,12 @@ export default function assets({
|
|||
}
|
||||
|
||||
// Rollup will copy the file to the output directory, this refer to this final path, not to the original path
|
||||
const finalOriginalImagePath = (
|
||||
isESMImportedImage(options.src) ? options.src.src : options.src
|
||||
).replace(settings.config.build.assetsPrefix || '', '');
|
||||
const ESMImportedImageSrc = isESMImportedImage(options.src)
|
||||
? options.src.src
|
||||
: options.src;
|
||||
const fileExtension = extname(ESMImportedImageSrc);
|
||||
const pf = getAssetsPrefix(fileExtension, settings.config.build.assetsPrefix);
|
||||
const finalOriginalImagePath = ESMImportedImageSrc.replace(pf, '');
|
||||
|
||||
const hash = hashTransform(
|
||||
options,
|
||||
|
@ -132,7 +137,7 @@ export default function assets({
|
|||
// The paths here are used for URLs, so we need to make sure they have the proper format for an URL
|
||||
// (leading slash, prefixed with the base / assets prefix, encoded, etc)
|
||||
if (settings.config.build.assetsPrefix) {
|
||||
return encodeURI(joinPaths(settings.config.build.assetsPrefix, finalFilePath));
|
||||
return encodeURI(joinPaths(pf, finalFilePath));
|
||||
} else {
|
||||
return encodeURI(prependForwardSlash(joinPaths(settings.config.base, finalFilePath)));
|
||||
}
|
||||
|
@ -149,9 +154,9 @@ export default function assets({
|
|||
const [full, hash, postfix = ''] = match;
|
||||
|
||||
const file = this.getFileName(hash);
|
||||
const prefix = settings.config.build.assetsPrefix
|
||||
? appendForwardSlash(settings.config.build.assetsPrefix)
|
||||
: resolvedConfig.base;
|
||||
const fileExtension = extname(file);
|
||||
const pf = getAssetsPrefix(fileExtension, settings.config.build.assetsPrefix);
|
||||
const prefix = pf ? appendForwardSlash(pf) : resolvedConfig.base;
|
||||
const outputFilepath = prefix + normalizePath(file + postfix);
|
||||
|
||||
s.overwrite(match.index, match.index + full.length, outputFilepath);
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
STYLES_PLACEHOLDER,
|
||||
} from './consts.js';
|
||||
import { hasContentFlag } from './utils.js';
|
||||
import { getAssetsPrefix } from '../assets/utils/getAssetsPrefix.js';
|
||||
|
||||
export function astroContentAssetPropagationPlugin({
|
||||
mode,
|
||||
|
@ -148,8 +149,11 @@ export function astroConfigBuildPlugin(
|
|||
'build:post': ({ ssrOutputs, clientOutputs, mutate }) => {
|
||||
const outputs = ssrOutputs.flatMap((o) => o.output);
|
||||
const prependBase = (src: string) => {
|
||||
if (options.settings.config.build.assetsPrefix) {
|
||||
return joinPaths(options.settings.config.build.assetsPrefix, src);
|
||||
const { assetsPrefix } = options.settings.config.build;
|
||||
if (assetsPrefix) {
|
||||
const fileExtension = extname(src);
|
||||
const pf = getAssetsPrefix(fileExtension, assetsPrefix);
|
||||
return joinPaths(pf, src);
|
||||
} else {
|
||||
return prependForwardSlash(joinPaths(options.settings.config.base, src));
|
||||
}
|
||||
|
|
|
@ -35,6 +35,13 @@ export type SerializedRouteInfo = Omit<RouteInfo, 'routeData'> & {
|
|||
|
||||
export type ImportComponentInstance = () => Promise<SinglePageBuiltModule>;
|
||||
|
||||
export type AssetsPrefix =
|
||||
| string
|
||||
| ({
|
||||
fallback: string;
|
||||
} & Record<string, string>)
|
||||
| undefined;
|
||||
|
||||
export type SSRManifest = {
|
||||
adapterName: string;
|
||||
routes: RouteInfo[];
|
||||
|
@ -43,7 +50,7 @@ export type SSRManifest = {
|
|||
trailingSlash: 'always' | 'never' | 'ignore';
|
||||
buildFormat: 'file' | 'directory' | 'preserve';
|
||||
compressHTML: boolean;
|
||||
assetsPrefix?: string;
|
||||
assetsPrefix?: AssetsPrefix;
|
||||
renderers: SSRLoadedRenderer[];
|
||||
/**
|
||||
* Map of directive name (e.g. `load`) to the directive script code
|
||||
|
|
|
@ -11,13 +11,14 @@ import type {
|
|||
SerializedRouteInfo,
|
||||
SerializedSSRManifest,
|
||||
} from '../../app/types.js';
|
||||
import { joinPaths, prependForwardSlash } from '../../path.js';
|
||||
import { joinPaths, prependForwardSlash, fileExtension } from '../../path.js';
|
||||
import { serializeRouteData } from '../../routing/index.js';
|
||||
import { addRollupInput } from '../add-rollup-input.js';
|
||||
import { getOutFile, getOutFolder } from '../common.js';
|
||||
import { type BuildInternals, cssOrder, mergeInlineCss } from '../internal.js';
|
||||
import type { AstroBuildPlugin } from '../plugin.js';
|
||||
import type { StaticBuildOptions } from '../types.js';
|
||||
import { getAssetsPrefix } from '../../../assets/utils/getAssetsPrefix.js';
|
||||
|
||||
const manifestReplace = '@@ASTRO_MANIFEST_REPLACE@@';
|
||||
const replaceExp = new RegExp(`['"]${manifestReplace}['"]`, 'g');
|
||||
|
@ -163,7 +164,8 @@ function buildManifest(
|
|||
|
||||
const prefixAssetPath = (pth: string) => {
|
||||
if (settings.config.build.assetsPrefix) {
|
||||
return joinPaths(settings.config.build.assetsPrefix, pth);
|
||||
const pf = getAssetsPrefix(fileExtension(pth), settings.config.build.assetsPrefix);
|
||||
return joinPaths(pf, pth);
|
||||
} else {
|
||||
return prependForwardSlash(joinPaths(settings.config.base, pth));
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import type { AstroUserConfig, ViteUserConfig } from '../../@types/astro.js';
|
|||
import type { OutgoingHttpHeaders } from 'node:http';
|
||||
import path from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { z } from 'zod';
|
||||
import { type TypeOf, z } from 'zod';
|
||||
import { appendForwardSlash, prependForwardSlash, removeTrailingForwardSlash } from '../path.js';
|
||||
|
||||
// These imports are required to appease TypeScript!
|
||||
|
@ -134,7 +134,23 @@ export const AstroConfigSchema = z.object({
|
|||
.default(ASTRO_CONFIG_DEFAULTS.build.server)
|
||||
.transform((val) => new URL(val)),
|
||||
assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets),
|
||||
assetsPrefix: z.string().optional(),
|
||||
assetsPrefix: z
|
||||
.string()
|
||||
.optional()
|
||||
.or(z.object({ fallback: z.string() }).and(z.record(z.string())).optional())
|
||||
.refine(
|
||||
(value) => {
|
||||
if (value && typeof value !== 'string') {
|
||||
if (!value.fallback) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: 'The `fallback` is mandatory when defining the option as an object.',
|
||||
}
|
||||
),
|
||||
serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry),
|
||||
redirects: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.build.redirects),
|
||||
inlineStylesheets: z
|
||||
|
@ -524,7 +540,23 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: string) {
|
|||
.default(ASTRO_CONFIG_DEFAULTS.build.server)
|
||||
.transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
|
||||
assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets),
|
||||
assetsPrefix: z.string().optional(),
|
||||
assetsPrefix: z
|
||||
.string()
|
||||
.optional()
|
||||
.or(z.object({ fallback: z.string() }).and(z.record(z.string())).optional())
|
||||
.refine(
|
||||
(value) => {
|
||||
if (value && typeof value !== 'string') {
|
||||
if (!value.fallback) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: 'The `fallback` is mandatory when defining the option as an object.',
|
||||
}
|
||||
),
|
||||
serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry),
|
||||
redirects: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.build.redirects),
|
||||
inlineStylesheets: z
|
||||
|
|
|
@ -35,6 +35,8 @@ import type { Logger } from './logger/core.js';
|
|||
import { createViteLogger } from './logger/vite.js';
|
||||
import { vitePluginMiddleware } from './middleware/vite-plugin.js';
|
||||
import { joinPaths } from './path.js';
|
||||
import { isObject } from './util.js';
|
||||
import { getAssetsPrefix } from '../assets/utils/getAssetsPrefix.js';
|
||||
|
||||
interface CreateViteOptions {
|
||||
settings: AstroSettings;
|
||||
|
@ -214,9 +216,9 @@ export async function createVite(
|
|||
const assetsPrefix = settings.config.build.assetsPrefix;
|
||||
if (assetsPrefix) {
|
||||
commonConfig.experimental = {
|
||||
renderBuiltUrl(filename, { type }) {
|
||||
renderBuiltUrl(filename, { type, hostType }) {
|
||||
if (type === 'asset') {
|
||||
return joinPaths(assetsPrefix, filename);
|
||||
return joinPaths(getAssetsPrefix(`.${hostType}`, assetsPrefix), filename);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -318,6 +320,6 @@ function isCommonNotAstro(dep: string): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
function stringifyForDefine(value: string | undefined): string {
|
||||
return typeof value === 'string' ? JSON.stringify(value) : 'undefined';
|
||||
function stringifyForDefine(value: string | undefined | object): string {
|
||||
return typeof value === 'string' || isObject(value) ? JSON.stringify(value) : 'undefined';
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import type { SSRElement } from '../../@types/astro.js';
|
||||
import { joinPaths, prependForwardSlash, slash } from '../../core/path.js';
|
||||
import type { SSRElement, AssetsPrefix } from '../../@types/astro.js';
|
||||
import { fileExtension, joinPaths, prependForwardSlash, slash } from '../../core/path.js';
|
||||
import type { StylesheetAsset } from '../app/types.js';
|
||||
import { getAssetsPrefix } from '../../assets/utils/getAssetsPrefix.js';
|
||||
|
||||
export function createAssetLink(href: string, base?: string, assetsPrefix?: string): string {
|
||||
export function createAssetLink(href: string, base?: string, assetsPrefix?: AssetsPrefix): string {
|
||||
if (assetsPrefix) {
|
||||
return joinPaths(assetsPrefix, slash(href));
|
||||
const pf = getAssetsPrefix(fileExtension(href), assetsPrefix);
|
||||
return joinPaths(pf, slash(href));
|
||||
} else if (base) {
|
||||
return prependForwardSlash(joinPaths(base, slash(href)));
|
||||
} else {
|
||||
|
@ -15,7 +17,7 @@ export function createAssetLink(href: string, base?: string, assetsPrefix?: stri
|
|||
export function createStylesheetElement(
|
||||
stylesheet: StylesheetAsset,
|
||||
base?: string,
|
||||
assetsPrefix?: string
|
||||
assetsPrefix?: AssetsPrefix
|
||||
): SSRElement {
|
||||
if (stylesheet.type === 'inline') {
|
||||
return {
|
||||
|
@ -36,7 +38,7 @@ export function createStylesheetElement(
|
|||
export function createStylesheetElementSet(
|
||||
stylesheets: StylesheetAsset[],
|
||||
base?: string,
|
||||
assetsPrefix?: string
|
||||
assetsPrefix?: AssetsPrefix
|
||||
): Set<SSRElement> {
|
||||
return new Set(stylesheets.map((s) => createStylesheetElement(s, base, assetsPrefix)));
|
||||
}
|
||||
|
@ -44,7 +46,7 @@ export function createStylesheetElementSet(
|
|||
export function createModuleScriptElement(
|
||||
script: { type: 'inline' | 'external'; value: string },
|
||||
base?: string,
|
||||
assetsPrefix?: string
|
||||
assetsPrefix?: AssetsPrefix
|
||||
): SSRElement {
|
||||
if (script.type === 'external') {
|
||||
return createModuleScriptElementWithSrc(script.value, base, assetsPrefix);
|
||||
|
@ -61,7 +63,7 @@ export function createModuleScriptElement(
|
|||
export function createModuleScriptElementWithSrc(
|
||||
src: string,
|
||||
base?: string,
|
||||
assetsPrefix?: string
|
||||
assetsPrefix?: AssetsPrefix
|
||||
): SSRElement {
|
||||
return {
|
||||
props: {
|
||||
|
@ -75,7 +77,7 @@ export function createModuleScriptElementWithSrc(
|
|||
export function createModuleScriptElementWithSrcSet(
|
||||
srces: string[],
|
||||
site?: string,
|
||||
assetsPrefix?: string
|
||||
assetsPrefix?: AssetsPrefix
|
||||
): Set<SSRElement> {
|
||||
return new Set<SSRElement>(
|
||||
srces.map((src) => createModuleScriptElementWithSrc(src, site, assetsPrefix))
|
||||
|
@ -85,7 +87,7 @@ export function createModuleScriptElementWithSrcSet(
|
|||
export function createModuleScriptsSet(
|
||||
scripts: { type: 'inline' | 'external'; value: string }[],
|
||||
base?: string,
|
||||
assetsPrefix?: string
|
||||
assetsPrefix?: AssetsPrefix
|
||||
): Set<SSRElement> {
|
||||
return new Set<SSRElement>(
|
||||
scripts.map((script) => createModuleScriptElement(script, base, assetsPrefix))
|
||||
|
|
136
packages/astro/test/astro-assets-prefix-multi-cdn.test.js
Normal file
136
packages/astro/test/astro-assets-prefix-multi-cdn.test.js
Normal file
|
@ -0,0 +1,136 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import { before, describe, it } from 'node:test';
|
||||
import * as cheerio from 'cheerio';
|
||||
import testAdapter from './test-adapter.js';
|
||||
import { loadFixture } from './test-utils.js';
|
||||
|
||||
const defaultAssetsPrefixRegex = /^https:\/\/example.com\/_astro\/.*/;
|
||||
const jsAssetsPrefixRegex = /^https:\/\/js\.example\.com\/_astro\/.*/;
|
||||
const cssAssetsPrefixRegex = /^https:\/\/css\.example\.com\/_astro\/.*/;
|
||||
const assetsPrefix = {
|
||||
js: 'https://js.example.com',
|
||||
css: 'https://css.example.com',
|
||||
fallback: 'https://example.com',
|
||||
};
|
||||
|
||||
// Asset prefix for CDN support
|
||||
describe('Assets Prefix Multiple CDN - Static', () => {
|
||||
let fixture;
|
||||
|
||||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/astro-assets-prefix',
|
||||
build: {
|
||||
assetsPrefix,
|
||||
},
|
||||
});
|
||||
await fixture.build();
|
||||
});
|
||||
|
||||
it('all stylesheets should start with cssAssetPrefix', async () => {
|
||||
const html = await fixture.readFile('/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
const stylesheets = $('link[rel="stylesheet"]');
|
||||
stylesheets.each((i, el) => {
|
||||
assert.match(el.attribs.href, cssAssetsPrefixRegex);
|
||||
});
|
||||
});
|
||||
|
||||
it('image src start with fallback', async () => {
|
||||
const html = await fixture.readFile('/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
const imgAsset = $('#image-asset');
|
||||
assert.match(imgAsset.attr('src'), defaultAssetsPrefixRegex);
|
||||
});
|
||||
|
||||
it('react component astro-island should import from jsAssetsPrefix', async () => {
|
||||
const html = await fixture.readFile('/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
const island = $('astro-island');
|
||||
assert.match(island.attr('component-url'), jsAssetsPrefixRegex);
|
||||
assert.match(island.attr('renderer-url'), jsAssetsPrefixRegex);
|
||||
});
|
||||
|
||||
it('import.meta.env.ASSETS_PREFIX works', async () => {
|
||||
const html = await fixture.readFile('/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
const env = $('#assets-prefix-env');
|
||||
assert.deepEqual(JSON.parse(env.text()), assetsPrefix);
|
||||
});
|
||||
|
||||
it('markdown image src start with assetsPrefix', async () => {
|
||||
const html = await fixture.readFile('/markdown/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
const imgAssets = $('img');
|
||||
imgAssets.each((i, el) => {
|
||||
assert.match(el.attribs.src, defaultAssetsPrefixRegex);
|
||||
});
|
||||
});
|
||||
|
||||
it('content collections image src start with assetsPrefix', async () => {
|
||||
const html = await fixture.readFile('/blog/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
const imgAsset = $('img');
|
||||
assert.match(imgAsset.attr('src'), defaultAssetsPrefixRegex);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Assets Prefix Multiple CDN, server', () => {
|
||||
let app;
|
||||
|
||||
before(async () => {
|
||||
const fixture = await loadFixture({
|
||||
root: './fixtures/astro-assets-prefix',
|
||||
output: 'server',
|
||||
adapter: testAdapter(),
|
||||
build: {
|
||||
assetsPrefix,
|
||||
},
|
||||
});
|
||||
await fixture.build();
|
||||
app = await fixture.loadTestAdapterApp();
|
||||
});
|
||||
|
||||
it('all stylesheets should start with assetPrefix', async () => {
|
||||
const request = new Request('http://example.com/custom-base/');
|
||||
const response = await app.render(request);
|
||||
assert.equal(response.status, 200);
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
const stylesheets = $('link[rel="stylesheet"]');
|
||||
stylesheets.each((i, el) => {
|
||||
assert.match(el.attribs.href, cssAssetsPrefixRegex);
|
||||
});
|
||||
});
|
||||
|
||||
it('image src start with assetsPrefix', async () => {
|
||||
const request = new Request('http://example.com/custom-base/');
|
||||
const response = await app.render(request);
|
||||
assert.equal(response.status, 200);
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
const imgAsset = $('#image-asset');
|
||||
assert.match(imgAsset.attr('src'), defaultAssetsPrefixRegex);
|
||||
});
|
||||
|
||||
it('react component astro-island should import from assetsPrefix', async () => {
|
||||
const request = new Request('http://example.com/custom-base/');
|
||||
const response = await app.render(request);
|
||||
assert.equal(response.status, 200);
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
const island = $('astro-island');
|
||||
assert.match(island.attr('component-url'), jsAssetsPrefixRegex);
|
||||
assert.match(island.attr('renderer-url'), jsAssetsPrefixRegex);
|
||||
});
|
||||
|
||||
it('markdown optimized image src does not start with assetsPrefix in SSR', async () => {
|
||||
const request = new Request('http://example.com/custom-base/markdown/');
|
||||
const response = await app.render(request);
|
||||
assert.equal(response.status, 200);
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
const imgAsset = $('img');
|
||||
assert.doesNotMatch(imgAsset.attr('src'), defaultAssetsPrefixRegex);
|
||||
});
|
||||
});
|
|
@ -13,7 +13,7 @@ import Counter from '../components/Counter.jsx';
|
|||
<img id="image-asset" src={p1Image.src} width={p1Image.width} height={p1Image.height} alt="penguin" />
|
||||
<Image src={p1Image} alt="penguin" />
|
||||
<Counter client:load />
|
||||
<p id="assets-prefix-env">{import.meta.env.ASSETS_PREFIX}</p>
|
||||
<p id="assets-prefix-env">{typeof import.meta.env.ASSETS_PREFIX === 'string' ? import.meta.env.ASSETS_PREFIX : JSON.stringify(import.meta.env.ASSETS_PREFIX)}</p>
|
||||
<style>
|
||||
h1 {
|
||||
color: red;
|
||||
|
|
|
@ -92,3 +92,8 @@ export function isRemotePath(src: string) {
|
|||
export function slash(path: string) {
|
||||
return path.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
export function fileExtension(path: string) {
|
||||
const ext = path.split('.').pop();
|
||||
return ext !== path ? `.${ext}` : '';
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue