Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/astro/dev-only.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,23 @@ declare module 'virtual:astro:actions/options' {
declare module 'virtual:astro:actions/runtime' {
export * from './src/actions/runtime/client.js';
}

declare module 'virtual:astro:actions/entrypoint' {
import type { SSRActions } from './src/index.js';
export const server: SSRActions;
}

declare module 'virtual:astro:serialized-manifest' {
import type { SSRManifest } from './src/index.js';
export const manifest: SSRManifest;
}

declare module 'virtual:astro:routes' {
import type { RoutesList } from './src/types/astro.js';
export const routes: RoutesList[];
}

declare module 'virtual:astro:renderers' {
import type { AstroRenderer } from './src/index.js';
export const renderers: AstroRenderer[];
}
2 changes: 2 additions & 0 deletions packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"./container": "./dist/container/index.js",
"./app": "./dist/core/app/index.js",
"./app/node": "./dist/core/app/node.js",
"./app/entrypoint": "./dist/core/app/entrypoint.js",
"./client/*": "./dist/runtime/client/*",
"./components": "./components/index.ts",
"./components/*": "./components/*",
Expand All @@ -55,6 +56,7 @@
"./assets": "./dist/assets/index.js",
"./assets/runtime": "./dist/assets/runtime.js",
"./assets/utils": "./dist/assets/utils/index.js",
"./assets/utils/node": "./dist/assets/utils/node.js",
"./assets/utils/inferRemoteSize.js": "./dist/assets/utils/remoteProbe.js",
"./assets/endpoint/*": "./dist/assets/endpoint/*.js",
"./assets/services/sharp": "./dist/assets/services/sharp.js",
Expand Down
2 changes: 0 additions & 2 deletions packages/astro/src/assets/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

export { isESMImportedImage, isRemoteImage, resolveSrc } from './imageKind.js';
export { imageMetadata } from './metadata.js';
export { emitImageMetadata } from './node/emitAsset.js';
export { getOrigQueryParams } from './queryParams.js';
export {
isRemoteAllowed,
Expand All @@ -19,4 +18,3 @@ export {
type RemotePattern,
} from './remotePattern.js';
export { inferRemoteSize } from './remoteProbe.js';
export { hashTransform, propsToFilename } from './transformToPath.js';
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import path, { basename, dirname, extname } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { deterministicString } from 'deterministic-object-hash';
import type * as vite from 'vite';
import { generateContentHash } from '../../../core/encryption.js';
import { prependForwardSlash, slash } from '../../../core/path.js';
import type { ImageMetadata } from '../../types.js';
import { imageMetadata } from '../metadata.js';
import { generateContentHash } from '../../core/encryption.js';
import { prependForwardSlash, removeQueryString, slash } from '../../core/path.js';
import { shorthash } from '../../runtime/server/shorthash.js';
import type { ImageMetadata, ImageTransform } from '../types.js';
import { isESMImportedImage } from './imageKind.js';
import { imageMetadata } from './metadata.js';

type FileEmitter = vite.Rollup.EmitFile;
type ImageMetadataWithContents = ImageMetadata & { contents?: Buffer };
Expand Down Expand Up @@ -139,3 +142,70 @@ function fileURLToNormalizedPath(filePath: URL): string {
// Uses `slash` instead of Vite's `normalizePath` to avoid CJS bundling issues.
return slash(fileURLToPath(filePath) + filePath.search).replace(/\\/g, '/');
}

// Taken from https://github.com/rollup/rollup/blob/a8647dac0fe46c86183be8596ef7de25bc5b4e4b/src/utils/sanitizeFileName.ts
// eslint-disable-next-line no-control-regex
const INVALID_CHAR_REGEX = /[\u0000-\u001F"#$%&*+,:;<=>?[\]^`{|}\u007F]/g;

/**
* Converts a file path and transformation properties of the transformation image service, into a formatted filename.
*
* The formatted filename follows this structure:
*
* `<prefixDirname>/<baseFilename>_<hash><outputExtension>`
*
* - `prefixDirname`: If the image is an ESM imported image, this is the directory name of the original file path; otherwise, it will be an empty string.
* - `baseFilename`: The base name of the file or a hashed short name if the file is a `data:` URI.
* - `hash`: A unique hash string generated to distinguish the transformed file.
* - `outputExtension`: The desired output file extension derived from the `transform.format` or the original file extension.
*
* ## Example
* - Input: `filePath = '/images/photo.jpg'`, `transform = { format: 'png', src: '/images/photo.jpg' }`, `hash = 'abcd1234'`.
* - Output: `/images/photo_abcd1234.png`
*
* @param {string} filePath - The original file path or data URI of the source image.
* @param {ImageTransform} transform - An object representing the transformation properties, including format and source.
* @param {string} hash - A unique hash used to differentiate the transformed file.
* @return {string} The generated filename based on the provided input, transformations, and hash.
*/

export function propsToFilename(filePath: string, transform: ImageTransform, hash: string): string {
let filename = decodeURIComponent(removeQueryString(filePath));
const ext = extname(filename);
if (filePath.startsWith('data:')) {
filename = shorthash(filePath);
} else {
filename = basename(filename, ext).replace(INVALID_CHAR_REGEX, '_');
}
const prefixDirname = isESMImportedImage(transform.src) ? dirname(filePath) : '';

let outputExt = transform.format ? `.${transform.format}` : ext;
return `${prefixDirname}/${filename}_${hash}${outputExt}`;
}

/**
* Transforms the provided `transform` object into a hash string based on selected properties
* and the specified `imageService`.
*
* @param {ImageTransform} transform - The transform object containing various image transformation properties.
* @param {string} imageService - The name of the image service related to the transform.
* @param {string[]} propertiesToHash - An array of property names from the `transform` object that should be used to generate the hash.
* @return {string} A hashed string created from the specified properties of the `transform` object and the image service.
*/
export function hashTransform(
transform: ImageTransform,
imageService: string,
propertiesToHash: string[],
): string {
// Extract the fields we want to hash
const hashFields = propertiesToHash.reduce(
(acc, prop) => {
// It's possible for `transform[prop]` here to be undefined, or null, but that's fine because it's still consistent
// between different transforms. (ex: every transform without a height will explicitly have a `height: undefined` property)
acc[prop] = transform[prop];
return acc;
},
{ imageService } as Record<string, unknown>,
);
return shorthash(deterministicString(hashFields));
}
73 changes: 0 additions & 73 deletions packages/astro/src/assets/utils/transformToPath.ts

This file was deleted.

5 changes: 2 additions & 3 deletions packages/astro/src/assets/vite-plugin-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ import { VALID_INPUT_FORMATS, VIRTUAL_MODULE_ID, VIRTUAL_SERVICE_ID } from './co
import { fontsPlugin } from './fonts/vite-plugin-fonts.js';
import type { ImageTransform } from './types.js';
import { getAssetsPrefix } from './utils/getAssetsPrefix.js';
import { isESMImportedImage } from './utils/imageKind.js';
import { emitImageMetadata } from './utils/node/emitAsset.js';
import { isESMImportedImage } from './utils/index.js';
import { emitImageMetadata, hashTransform, propsToFilename } from './utils/node.js';
import { getProxyCode } from './utils/proxy.js';
import { makeSvgComponent } from './utils/svg.js';
import { hashTransform, propsToFilename } from './utils/transformToPath.js';

const resolvedVirtualModuleId = '\0' + VIRTUAL_MODULE_ID;

Expand Down
5 changes: 1 addition & 4 deletions packages/astro/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,7 @@ export function getViteConfig(
const { astroConfig: config } = await resolveConfig(inlineAstroConfig, cmd);
let settings = await createSettings(config, userViteConfig.root);
settings = await runHookConfigSetup({ settings, command: cmd, logger });
const viteConfig = await createVite(
{},
{ settings, command: cmd, logger, mode, sync: false },
);
const viteConfig = await createVite({}, { settings, command: cmd, logger, mode, sync: false });
await runHookConfigDone({ settings, logger });
return mergeConfig(viteConfig, userViteConfig);
};
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/content/runtime-assets.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { PluginContext } from 'rollup';
import { z } from 'zod';
import type { ImageMetadata, OmitBrand } from '../assets/types.js';
import { emitImageMetadata } from '../assets/utils/node/emitAsset.js';
import { emitImageMetadata } from '../assets/utils/node.js';

export function createImage(
pluginContext: PluginContext,
Expand Down
21 changes: 21 additions & 0 deletions packages/astro/src/core/app/entrypoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { renderers } from 'virtual:astro:renderers';
import { routes } from 'virtual:astro:routes';
import { manifest as serializedManifest } from 'virtual:astro:serialized-manifest';
import { App } from './app.js';
import type { BaseApp } from './base.js';
import { DevApp } from './dev/app.js';
import { createConsoleLogger } from './logging.js';

const actions = async () => {
return await import('virtual:astro:actions/entrypoint');
};
const manifest = Object.assign(serializedManifest, { renderers, actions });

export function getApp(dev = import.meta.env.DEV): BaseApp {
if (dev) {
const logger = createConsoleLogger('debug');
return new DevApp(manifest, true, logger, { routes: routes.map((r) => r.routeData) });
} else {
return new App(manifest);
}
}
3 changes: 2 additions & 1 deletion packages/astro/src/core/build/plugins/plugin-pages.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Plugin as VitePlugin } from 'vite';
import { ASTRO_RENDERERS_MODULE_ID } from '../../../vite-plugin-renderers/index.js';
import { routeIsRedirect } from '../../redirects/index.js';
import { addRollupInput } from '../add-rollup-input.js';
import type { BuildInternals } from '../internal.js';
Expand Down Expand Up @@ -46,7 +47,7 @@ function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): V
imports.push(`import * as _page from ${JSON.stringify(pageData.moduleSpecifier)};`);
exports.push(`export const page = () => _page`);

imports.push(`import { renderers } from "astro:renderers";`);
imports.push(`import { renderers } from "${ASTRO_RENDERERS_MODULE_ID}";`);
exports.push(`export { renderers };`);

return { code: `${imports.join('\n')}${exports.join('\n')}` };
Expand Down
3 changes: 2 additions & 1 deletion packages/astro/src/core/build/plugins/plugin-ssr.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Plugin as VitePlugin } from 'vite';
import { ENTRYPOINT_VIRTUAL_MODULE_ID } from '../../../actions/consts.js';
import type { AstroAdapter } from '../../../types/public/integrations.js';
import { ASTRO_RENDERERS_MODULE_ID } from '../../../vite-plugin-renderers/index.js';
import { MIDDLEWARE_MODULE_ID } from '../../middleware/vite-plugin.js';
import { routeIsRedirect } from '../../redirects/index.js';
import { VIRTUAL_ISLAND_MAP_ID } from '../../server-islands/vite-plugin-server-islands.js';
Expand Down Expand Up @@ -167,7 +168,7 @@ function generateSSRCode(adapter: AstroAdapter, middlewareId: string) {
const edgeMiddleware = adapter?.adapterFeatures?.edgeMiddleware ?? false;

const imports = [
`import { renderers } from 'astro:renderers';`,
`import { renderers } from '${ASTRO_RENDERERS_MODULE_ID}';`,
`import * as serverEntrypointModule from '${ADAPTER_VIRTUAL_MODULE_ID}';`,
`import { manifest as defaultManifest } from '${SSR_MANIFEST_VIRTUAL_MODULE_ID}';`,
`import { serverIslandMap } from '${VIRTUAL_ISLAND_MAP_ID}';`,
Expand Down
84 changes: 1 addition & 83 deletions packages/astro/src/i18n/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { SSRManifest } from '../core/app/types.js';
import type { AstroConfig, Locales } from '../types/public/config.js';
import type { Locales } from '../types/public/config.js';
import { getAllCodes, normalizeTheLocale, normalizeThePath } from './index.js';

type BrowserLocale = {
Expand Down Expand Up @@ -188,84 +187,3 @@ export function computeCurrentLocale(
}
}
}

export type RoutingStrategies =
| 'manual'
| 'pathname-prefix-always'
| 'pathname-prefix-other-locales'
| 'pathname-prefix-always-no-redirect'
| 'domains-prefix-always'
| 'domains-prefix-other-locales'
| 'domains-prefix-always-no-redirect';
export function toRoutingStrategy(
routing: NonNullable<AstroConfig['i18n']>['routing'],
domains: NonNullable<AstroConfig['i18n']>['domains'],
) {
let strategy: RoutingStrategies;
const hasDomains = domains ? Object.keys(domains).length > 0 : false;
if (routing === 'manual') {
strategy = 'manual';
} else {
if (!hasDomains) {
if (routing?.prefixDefaultLocale === true) {
if (routing.redirectToDefaultLocale) {
strategy = 'pathname-prefix-always';
} else {
strategy = 'pathname-prefix-always-no-redirect';
}
} else {
strategy = 'pathname-prefix-other-locales';
}
} else {
if (routing?.prefixDefaultLocale === true) {
if (routing.redirectToDefaultLocale) {
strategy = 'domains-prefix-always';
} else {
strategy = 'domains-prefix-always-no-redirect';
}
} else {
strategy = 'domains-prefix-other-locales';
}
}
}

return strategy;
}

const PREFIX_DEFAULT_LOCALE = new Set([
'pathname-prefix-always',
'domains-prefix-always',
'pathname-prefix-always-no-redirect',
'domains-prefix-always-no-redirect',
]);

const REDIRECT_TO_DEFAULT_LOCALE = new Set([
'pathname-prefix-always-no-redirect',
'domains-prefix-always-no-redirect',
]);

export function fromRoutingStrategy(
strategy: RoutingStrategies,
fallbackType: NonNullable<SSRManifest['i18n']>['fallbackType'],
): NonNullable<AstroConfig['i18n']>['routing'] {
let routing: NonNullable<AstroConfig['i18n']>['routing'];
if (strategy === 'manual') {
routing = 'manual';
} else {
routing = {
prefixDefaultLocale: PREFIX_DEFAULT_LOCALE.has(strategy),
redirectToDefaultLocale: !REDIRECT_TO_DEFAULT_LOCALE.has(strategy),
fallbackType,
};
}
return routing;
}

export function toFallbackType(
routing: NonNullable<AstroConfig['i18n']>['routing'],
): 'redirect' | 'rewrite' {
if (routing === 'manual') {
return 'rewrite';
}
return routing.fallbackType;
}
Loading
Loading