astro 7.0.5 → 7.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Picture.astro +19 -2
- package/dist/actions/handler.js +7 -0
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/cli/install-package.js +5 -3
- package/dist/container/index.js +4 -2
- package/dist/content/content-layer.js +3 -3
- package/dist/content/vite-plugin-content-assets.d.ts +5 -1
- package/dist/content/vite-plugin-content-assets.js +6 -3
- package/dist/core/app/origin-check.d.ts +32 -0
- package/dist/core/app/origin-check.js +52 -0
- package/dist/core/base-pipeline.js +1 -1
- package/dist/core/build/plugins/plugin-scripts.d.ts +11 -1
- package/dist/core/build/plugins/plugin-scripts.js +23 -2
- package/dist/core/build/runtime.d.ts +5 -0
- package/dist/core/build/runtime.js +7 -3
- package/dist/core/constants.js +1 -1
- package/dist/core/create-vite.js +3 -2
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/messages/runtime.js +1 -1
- package/dist/core/pages/handler.js +9 -1
- package/dist/core/render/params-and-props.js +7 -2
- package/dist/core/routing/generator.js +2 -2
- package/dist/core/util.d.ts +8 -0
- package/dist/core/util.js +23 -0
- package/dist/core/viteUtils.d.ts +3 -0
- package/dist/core/viteUtils.js +8 -2
- package/dist/runtime/server/render/common.js +7 -1
- package/dist/runtime/server/render/dom.js +4 -1
- package/dist/runtime/server/render/instruction.d.ts +1 -0
- package/dist/runtime/server/render/instruction.js +5 -1
- package/dist/runtime/server/render/server-islands.js +6 -6
- package/dist/runtime/server/render/slot.d.ts +15 -2
- package/dist/runtime/server/render/slot.js +37 -6
- package/dist/runtime/server/render/util.d.ts +1 -0
- package/dist/runtime/server/render/util.js +1 -0
- package/dist/vite-plugin-app/app.js +24 -20
- package/dist/vite-plugin-astro-server/plugin.js +3 -0
- package/dist/vite-plugin-css/index.d.ts +6 -1
- package/dist/vite-plugin-css/index.js +6 -3
- package/package.json +6 -6
- package/templates/content/types.d.ts +4 -0
- package/dist/core/app/middlewares.d.ts +0 -8
- package/dist/core/app/middlewares.js +0 -49
package/components/Picture.astro
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getImage,
|
|
4
|
+
imageConfig,
|
|
5
|
+
inferRemoteSize,
|
|
6
|
+
type LocalImageProps,
|
|
7
|
+
type RemoteImageProps,
|
|
8
|
+
} from 'astro:assets';
|
|
3
9
|
import * as mime from 'mrmime';
|
|
4
|
-
import { isESMImportedImage, resolveSrc } from '../dist/assets/utils/imageKind.js';
|
|
10
|
+
import { isESMImportedImage, isRemoteImage, resolveSrc } from '../dist/assets/utils/imageKind.js';
|
|
5
11
|
import { AstroError, AstroErrorData } from '../dist/core/errors/index.js';
|
|
6
12
|
import type {
|
|
7
13
|
GetImageResult,
|
|
@@ -62,6 +68,17 @@ for (const key in props) {
|
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
const originalSrc = await resolveSrc(props.src);
|
|
71
|
+
|
|
72
|
+
// When inferSize is set for a remote image, resolve dimensions once and thread them through
|
|
73
|
+
// to all getImage() calls. This avoids multiple redundant HTTP requests to the same URL,
|
|
74
|
+
// which can trigger rate-limiting (HTTP 429) on servers like Wikimedia.
|
|
75
|
+
if (props.inferSize && isRemoteImage(originalSrc)) {
|
|
76
|
+
const remoteSize = await inferRemoteSize(originalSrc);
|
|
77
|
+
delete props.inferSize;
|
|
78
|
+
props.width ??= remoteSize.width;
|
|
79
|
+
props.height ??= remoteSize.height;
|
|
80
|
+
}
|
|
81
|
+
|
|
65
82
|
const optimizedImages: GetImageResult[] = await Promise.all(
|
|
66
83
|
formats.map(
|
|
67
84
|
async (format) =>
|
package/dist/actions/handler.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createCrossOriginForbiddenResponse,
|
|
3
|
+
isForbiddenCrossOriginRequest
|
|
4
|
+
} from "../core/app/origin-check.js";
|
|
1
5
|
import { PipelineFeatures } from "../core/base-pipeline.js";
|
|
2
6
|
import { getActionContext, serializeActionResult } from "./runtime/server.js";
|
|
3
7
|
class ActionHandler {
|
|
@@ -18,6 +22,9 @@ class ActionHandler {
|
|
|
18
22
|
if (!action) {
|
|
19
23
|
return void 0;
|
|
20
24
|
}
|
|
25
|
+
if (state.pipeline.manifest.checkOrigin && isForbiddenCrossOriginRequest(apiContext.request, apiContext.url, apiContext.isPrerendered)) {
|
|
26
|
+
return Promise.resolve(createCrossOriginForbiddenResponse(apiContext.request));
|
|
27
|
+
}
|
|
21
28
|
return this.#executeAction(action, setActionResult);
|
|
22
29
|
}
|
|
23
30
|
async #executeAction(action, setActionResult) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
2
3
|
import * as clack from "@clack/prompts";
|
|
3
4
|
import ci from "ci-info";
|
|
4
5
|
import { detect, resolveCommand } from "package-manager-detector";
|
|
@@ -8,8 +9,8 @@ const require2 = createRequire(import.meta.url);
|
|
|
8
9
|
const { bold, cyan, dim, magenta } = colors;
|
|
9
10
|
async function getPackage(packageName, logger, options, otherDeps = []) {
|
|
10
11
|
try {
|
|
11
|
-
require2.resolve(packageName, { paths: [options.cwd ?? process.cwd()] });
|
|
12
|
-
const packageImport = await import(
|
|
12
|
+
const resolved = require2.resolve(packageName, { paths: [options.cwd ?? process.cwd()] });
|
|
13
|
+
const packageImport = await import(pathToFileURL(resolved).href);
|
|
13
14
|
return packageImport;
|
|
14
15
|
} catch {
|
|
15
16
|
if (options.optional) return void 0;
|
|
@@ -25,7 +26,8 @@ async function getPackage(packageName, logger, options, otherDeps = []) {
|
|
|
25
26
|
}
|
|
26
27
|
const result = await installPackage([packageName, ...otherDeps], options, logger);
|
|
27
28
|
if (result) {
|
|
28
|
-
const
|
|
29
|
+
const resolved = require2.resolve(packageName, { paths: [options.cwd ?? process.cwd()] });
|
|
30
|
+
const packageImport = await import(pathToFileURL(resolved).href);
|
|
29
31
|
return packageImport;
|
|
30
32
|
} else {
|
|
31
33
|
return void 0;
|
package/dist/container/index.js
CHANGED
|
@@ -14,6 +14,8 @@ import { validateSegment } from "../core/routing/segment.js";
|
|
|
14
14
|
import { SlotString } from "../runtime/server/render/slot.js";
|
|
15
15
|
import { ContainerPipeline } from "./pipeline.js";
|
|
16
16
|
import { createConsoleLogger } from "../core/logger/impls/console.js";
|
|
17
|
+
const { gfm: _, smartypants: __, ...containerMarkdownDefaults } = ASTRO_CONFIG_DEFAULTS.markdown;
|
|
18
|
+
const CONTAINER_CONFIG_DEFAULTS = { ...ASTRO_CONFIG_DEFAULTS, markdown: containerMarkdownDefaults };
|
|
17
19
|
function createManifest(manifest, renderers, middleware) {
|
|
18
20
|
function middlewareInstance() {
|
|
19
21
|
return {
|
|
@@ -113,7 +115,7 @@ class experimental_AstroContainer {
|
|
|
113
115
|
*/
|
|
114
116
|
static async create(containerOptions = {}) {
|
|
115
117
|
const { streaming = false, manifest, renderers = [], resolve } = containerOptions;
|
|
116
|
-
const astroConfig = await validateConfig(
|
|
118
|
+
const astroConfig = await validateConfig(CONTAINER_CONFIG_DEFAULTS, process.cwd(), "container");
|
|
117
119
|
return new experimental_AstroContainer({
|
|
118
120
|
streaming,
|
|
119
121
|
manifest,
|
|
@@ -206,7 +208,7 @@ class experimental_AstroContainer {
|
|
|
206
208
|
// NOTE: we keep this private via TS instead via `#` so it's still available on the surface, so we can play with it.
|
|
207
209
|
// @ts-expect-error @ematipico: I plan to use it for a possible integration that could help people
|
|
208
210
|
static async createFromManifest(manifest) {
|
|
209
|
-
const astroConfig = await validateConfig(
|
|
211
|
+
const astroConfig = await validateConfig(CONTAINER_CONFIG_DEFAULTS, process.cwd(), "container");
|
|
210
212
|
const container = new experimental_AstroContainer({
|
|
211
213
|
manifest,
|
|
212
214
|
astroConfig
|
|
@@ -197,7 +197,7 @@ ${contentConfig.error.message}`
|
|
|
197
197
|
logger.info("Content config changed");
|
|
198
198
|
shouldClear = true;
|
|
199
199
|
}
|
|
200
|
-
if (previousAstroVersion && previousAstroVersion !== "7.0.
|
|
200
|
+
if (previousAstroVersion && previousAstroVersion !== "7.0.7") {
|
|
201
201
|
logger.info("Astro version changed");
|
|
202
202
|
shouldClear = true;
|
|
203
203
|
}
|
|
@@ -205,8 +205,8 @@ ${contentConfig.error.message}`
|
|
|
205
205
|
logger.info("Clearing content store");
|
|
206
206
|
this.#store.clearAll();
|
|
207
207
|
}
|
|
208
|
-
if ("7.0.
|
|
209
|
-
this.#store.metaStore().set("astro-version", "7.0.
|
|
208
|
+
if ("7.0.7") {
|
|
209
|
+
this.#store.metaStore().set("astro-version", "7.0.7");
|
|
210
210
|
}
|
|
211
211
|
if (currentConfigDigest) {
|
|
212
212
|
this.#store.metaStore().set("content-config-digest", currentConfigDigest);
|
|
@@ -3,8 +3,12 @@ import { type Plugin } from 'vite';
|
|
|
3
3
|
import type { BuildInternals } from '../core/build/internal.js';
|
|
4
4
|
import type { ExtractedChunk } from '../core/build/static-build.js';
|
|
5
5
|
import type { AstroSettings } from '../types/astro.js';
|
|
6
|
-
export declare function astroContentAssetPropagationPlugin({ settings, }: {
|
|
6
|
+
export declare function astroContentAssetPropagationPlugin({ settings, cssContentCache, }: {
|
|
7
7
|
settings: AstroSettings;
|
|
8
|
+
/** Shared cache of CSS content populated by the dev-css plugin's transform hook.
|
|
9
|
+
* Used to retrieve already-processed CSS for CSS modules without re-importing
|
|
10
|
+
* via `?inline`, which would produce different scoped-name hashes with Lightning CSS. */
|
|
11
|
+
cssContentCache?: Map<string, string>;
|
|
8
12
|
}): Plugin;
|
|
9
13
|
/**
|
|
10
14
|
* Post-build hook that injects propagated styles into content collection chunks.
|
|
@@ -19,7 +19,8 @@ import { joinPaths, prependForwardSlash, slash } from "@astrojs/internal-helpers
|
|
|
19
19
|
import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../core/constants.js";
|
|
20
20
|
import { isAstroServerEnvironment } from "../environments.js";
|
|
21
21
|
function astroContentAssetPropagationPlugin({
|
|
22
|
-
settings
|
|
22
|
+
settings,
|
|
23
|
+
cssContentCache
|
|
23
24
|
}) {
|
|
24
25
|
let environment = void 0;
|
|
25
26
|
return {
|
|
@@ -84,7 +85,7 @@ function astroContentAssetPropagationPlugin({
|
|
|
84
85
|
styles,
|
|
85
86
|
urls,
|
|
86
87
|
crawledFiles: styleCrawledFiles
|
|
87
|
-
} = await getStylesForURL(basePath, environment);
|
|
88
|
+
} = await getStylesForURL(basePath, environment, cssContentCache);
|
|
88
89
|
for (const file of styleCrawledFiles) {
|
|
89
90
|
if (!file.includes("node_modules")) {
|
|
90
91
|
this.addWatchFile(file);
|
|
@@ -113,7 +114,7 @@ function astroContentAssetPropagationPlugin({
|
|
|
113
114
|
};
|
|
114
115
|
}
|
|
115
116
|
const INLINE_QUERY_REGEX = /(?:\?|&)inline(?:$|&)/;
|
|
116
|
-
async function getStylesForURL(filePath, environment) {
|
|
117
|
+
async function getStylesForURL(filePath, environment, cssContentCache) {
|
|
117
118
|
const importedCssUrls = /* @__PURE__ */ new Set();
|
|
118
119
|
const importedStylesMap = /* @__PURE__ */ new Map();
|
|
119
120
|
const crawledFiles = /* @__PURE__ */ new Set();
|
|
@@ -125,6 +126,8 @@ async function getStylesForURL(filePath, environment) {
|
|
|
125
126
|
let css = "";
|
|
126
127
|
if (typeof importedModule.ssrModule?.default === "string") {
|
|
127
128
|
css = importedModule.ssrModule.default;
|
|
129
|
+
} else if (importedModule.id && cssContentCache?.has(importedModule.id)) {
|
|
130
|
+
css = cssContentCache.get(importedModule.id);
|
|
128
131
|
} else {
|
|
129
132
|
let modId = importedModule.url;
|
|
130
133
|
if (!INLINE_QUERY_REGEX.test(importedModule.url)) {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Home of the `security.checkOrigin` logic. Exposes the shared predicate and
|
|
3
|
+
* response used to reject cross-site submissions, plus the middleware factory
|
|
4
|
+
* that installs the check in the request pipeline. Consumers (the pipeline
|
|
5
|
+
* middleware and the Astro Actions dispatch) import from here so the check
|
|
6
|
+
* stays consistent regardless of where it runs.
|
|
7
|
+
*/
|
|
8
|
+
import type { MiddlewareHandler } from '../../types/public/common.js';
|
|
9
|
+
/**
|
|
10
|
+
* Determines whether a request should be rejected because it is a cross-site
|
|
11
|
+
* submission for a route rendered on demand.
|
|
12
|
+
*
|
|
13
|
+
* This encapsulates the shared logic used by both the origin-check middleware
|
|
14
|
+
* and the Astro Actions dispatch, so the check is applied consistently
|
|
15
|
+
* regardless of where the request is handled.
|
|
16
|
+
*
|
|
17
|
+
* @private
|
|
18
|
+
*/
|
|
19
|
+
export declare function isForbiddenCrossOriginRequest(request: Request, url: URL, isPrerendered: boolean): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Builds the 403 response returned when a cross-site submission is rejected.
|
|
22
|
+
*
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
export declare function createCrossOriginForbiddenResponse(request: Request): Response;
|
|
26
|
+
/**
|
|
27
|
+
* Returns a middleware function in charge to check the `origin` header.
|
|
28
|
+
*
|
|
29
|
+
* @private
|
|
30
|
+
*/
|
|
31
|
+
export declare function createOriginCheckMiddleware(): MiddlewareHandler;
|
|
32
|
+
export declare function hasFormLikeHeader(contentType: string | null): boolean;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { defineMiddleware } from "../middleware/defineMiddleware.js";
|
|
2
|
+
const FORM_CONTENT_TYPES = [
|
|
3
|
+
"application/x-www-form-urlencoded",
|
|
4
|
+
"multipart/form-data",
|
|
5
|
+
"text/plain"
|
|
6
|
+
];
|
|
7
|
+
const SAFE_METHODS = ["GET", "HEAD", "OPTIONS"];
|
|
8
|
+
function isForbiddenCrossOriginRequest(request, url, isPrerendered) {
|
|
9
|
+
if (isPrerendered) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (SAFE_METHODS.includes(request.method)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const isSameOrigin = request.headers.get("origin") === url.origin;
|
|
16
|
+
const hasContentType = request.headers.has("content-type");
|
|
17
|
+
if (hasContentType) {
|
|
18
|
+
const formLikeHeader = hasFormLikeHeader(request.headers.get("content-type"));
|
|
19
|
+
return formLikeHeader && !isSameOrigin;
|
|
20
|
+
}
|
|
21
|
+
return !isSameOrigin;
|
|
22
|
+
}
|
|
23
|
+
function createCrossOriginForbiddenResponse(request) {
|
|
24
|
+
return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
|
|
25
|
+
status: 403
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function createOriginCheckMiddleware() {
|
|
29
|
+
return defineMiddleware((context, next) => {
|
|
30
|
+
const { request, url, isPrerendered } = context;
|
|
31
|
+
if (isForbiddenCrossOriginRequest(request, url, isPrerendered)) {
|
|
32
|
+
return createCrossOriginForbiddenResponse(request);
|
|
33
|
+
}
|
|
34
|
+
return next();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function hasFormLikeHeader(contentType) {
|
|
38
|
+
if (contentType) {
|
|
39
|
+
for (const FORM_CONTENT_TYPE of FORM_CONTENT_TYPES) {
|
|
40
|
+
if (contentType.toLowerCase().includes(FORM_CONTENT_TYPE)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
createCrossOriginForbiddenResponse,
|
|
49
|
+
createOriginCheckMiddleware,
|
|
50
|
+
hasFormLikeHeader,
|
|
51
|
+
isForbiddenCrossOriginRequest
|
|
52
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NOOP_ACTIONS_MOD } from "../actions/noop-actions.js";
|
|
2
|
-
import { createOriginCheckMiddleware } from "./app/
|
|
2
|
+
import { createOriginCheckMiddleware } from "./app/origin-check.js";
|
|
3
3
|
import { ActionNotFoundError } from "./errors/errors-data.js";
|
|
4
4
|
import { AstroError } from "./errors/index.js";
|
|
5
5
|
import { NOOP_MIDDLEWARE_FN } from "./middleware/noop-middleware.js";
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
import type { Plugin as VitePlugin } from 'vite';
|
|
1
|
+
import type { BuildOptions, Plugin as VitePlugin, Rollup } from 'vite';
|
|
2
2
|
import type { BuildInternals } from '../internal.js';
|
|
3
|
+
type GetModuleInfo = (moduleId: string) => Rollup.ModuleInfo | null;
|
|
4
|
+
export type ScriptChunkInfo = Pick<Rollup.OutputChunk, 'code' | 'facadeModuleId' | 'fileName' | 'imports' | 'dynamicImports' | 'moduleIds'>;
|
|
5
|
+
export declare function chunkHasDynamicImports(output: Pick<ScriptChunkInfo, 'dynamicImports' | 'moduleIds'>, getModuleInfo: GetModuleInfo): boolean;
|
|
6
|
+
export declare function shouldInlineScriptChunk(output: ScriptChunkInfo, { discoveredScripts, importedIds, assetInlineLimit, getModuleInfo, }: {
|
|
7
|
+
discoveredScripts: Set<string>;
|
|
8
|
+
importedIds: Set<string>;
|
|
9
|
+
assetInlineLimit: NonNullable<BuildOptions['assetsInlineLimit']>;
|
|
10
|
+
getModuleInfo: GetModuleInfo;
|
|
11
|
+
}): boolean;
|
|
3
12
|
/**
|
|
4
13
|
* Inline scripts from Astro files directly into the HTML.
|
|
5
14
|
*/
|
|
6
15
|
export declare function pluginScripts(internals: BuildInternals): VitePlugin;
|
|
16
|
+
export {};
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { shouldInlineAsset } from "./util.js";
|
|
2
2
|
import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../../constants.js";
|
|
3
|
+
function chunkHasDynamicImports(output, getModuleInfo) {
|
|
4
|
+
return output.dynamicImports.length > 0 || output.moduleIds.some((id) => (getModuleInfo(id)?.dynamicallyImportedIds.length ?? 0) > 0);
|
|
5
|
+
}
|
|
6
|
+
function shouldInlineScriptChunk(output, {
|
|
7
|
+
discoveredScripts,
|
|
8
|
+
importedIds,
|
|
9
|
+
assetInlineLimit,
|
|
10
|
+
getModuleInfo
|
|
11
|
+
}) {
|
|
12
|
+
const facadeModuleId = output.facadeModuleId;
|
|
13
|
+
if (facadeModuleId === null) return false;
|
|
14
|
+
return discoveredScripts.has(facadeModuleId) && !importedIds.has(output.fileName) && output.imports.length === 0 && !chunkHasDynamicImports(output, getModuleInfo) && shouldInlineAsset(output.code, output.fileName, assetInlineLimit);
|
|
15
|
+
}
|
|
3
16
|
function pluginScripts(internals) {
|
|
4
17
|
let assetInlineLimit;
|
|
5
18
|
return {
|
|
@@ -20,8 +33,14 @@ function pluginScripts(internals) {
|
|
|
20
33
|
}
|
|
21
34
|
}
|
|
22
35
|
}
|
|
36
|
+
const getModuleInfo = this.getModuleInfo.bind(this);
|
|
23
37
|
for (const output of outputs) {
|
|
24
|
-
if (output.type === "chunk" &&
|
|
38
|
+
if (output.type === "chunk" && shouldInlineScriptChunk(output, {
|
|
39
|
+
discoveredScripts: internals.discoveredScripts,
|
|
40
|
+
importedIds,
|
|
41
|
+
assetInlineLimit,
|
|
42
|
+
getModuleInfo
|
|
43
|
+
})) {
|
|
25
44
|
internals.inlinedScripts.set(output.facadeModuleId, output.code.trim());
|
|
26
45
|
delete bundle[output.fileName];
|
|
27
46
|
}
|
|
@@ -30,5 +49,7 @@ function pluginScripts(internals) {
|
|
|
30
49
|
};
|
|
31
50
|
}
|
|
32
51
|
export {
|
|
33
|
-
|
|
52
|
+
chunkHasDynamicImports,
|
|
53
|
+
pluginScripts,
|
|
54
|
+
shouldInlineScriptChunk
|
|
34
55
|
};
|
|
@@ -21,6 +21,11 @@ export declare function cssOrder(a: OrderInfo, b: OrderInfo): 1 | -1;
|
|
|
21
21
|
/**
|
|
22
22
|
* Merges inline CSS into as few stylesheets as possible,
|
|
23
23
|
* preserving ordering when there are non-inlined in between.
|
|
24
|
+
*
|
|
25
|
+
* CSS chunks containing `@import` are never merged with other chunks.
|
|
26
|
+
* Per CSS spec, `@import` rules must appear at the top of a stylesheet
|
|
27
|
+
* or browsers silently ignore them. Keeping these chunks as separate
|
|
28
|
+
* `<style>` tags ensures the `@import` stays at the top of its own stylesheet.
|
|
24
29
|
*/
|
|
25
30
|
export declare function mergeInlineCss(acc: Array<StylesheetAsset>, current: StylesheetAsset): Array<StylesheetAsset>;
|
|
26
31
|
export {};
|
|
@@ -31,9 +31,13 @@ function mergeInlineCss(acc, current) {
|
|
|
31
31
|
const lastWasInline = lastAdded?.type === "inline";
|
|
32
32
|
const currentIsInline = current?.type === "inline";
|
|
33
33
|
if (lastWasInline && currentIsInline) {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
const currentHasImport = current.content.includes("@import");
|
|
35
|
+
const lastHasImport = lastAdded.content.includes("@import");
|
|
36
|
+
if (!currentHasImport && !lastHasImport) {
|
|
37
|
+
const merged = { type: "inline", content: lastAdded.content + current.content };
|
|
38
|
+
acc[acc.length - 1] = merged;
|
|
39
|
+
return acc;
|
|
40
|
+
}
|
|
37
41
|
}
|
|
38
42
|
acc.push(current);
|
|
39
43
|
return acc;
|
package/dist/core/constants.js
CHANGED
package/dist/core/create-vite.js
CHANGED
|
@@ -108,6 +108,7 @@ async function createVite(commandConfig, { settings, logger, mode, command, fs =
|
|
|
108
108
|
config: settings.config
|
|
109
109
|
});
|
|
110
110
|
const serverIslandsState = new ServerIslandsState();
|
|
111
|
+
const cssContentCache = /* @__PURE__ */ new Map();
|
|
111
112
|
validateEnvPrefixAgainstSchema(settings.config);
|
|
112
113
|
const commonConfig = {
|
|
113
114
|
// Tell Vite not to combine config from vite.config.js with our provided inline config
|
|
@@ -156,7 +157,7 @@ async function createVite(commandConfig, { settings, logger, mode, command, fs =
|
|
|
156
157
|
vitePluginFetchable({ settings }),
|
|
157
158
|
command === "dev" && vitePluginAstroServer({ settings, logger }),
|
|
158
159
|
command === "dev" && vitePluginAstroServerClient(),
|
|
159
|
-
astroDevCssPlugin({ routesList, command }),
|
|
160
|
+
astroDevCssPlugin({ routesList, command, cssContentCache }),
|
|
160
161
|
importMetaEnv({ envLoader }),
|
|
161
162
|
astroEnv({ settings, sync, envLoader }),
|
|
162
163
|
vitePluginAdapterConfig(settings),
|
|
@@ -167,7 +168,7 @@ async function createVite(commandConfig, { settings, logger, mode, command, fs =
|
|
|
167
168
|
astroHeadPlugin(),
|
|
168
169
|
astroContentVirtualModPlugin({ fs, settings }),
|
|
169
170
|
astroContentImportPlugin({ fs, settings, logger }),
|
|
170
|
-
astroContentAssetPropagationPlugin({ settings }),
|
|
171
|
+
astroContentAssetPropagationPlugin({ settings, cssContentCache }),
|
|
171
172
|
vitePluginMiddleware({ settings }),
|
|
172
173
|
astroAssetsPlugin({ fs, settings, sync, logger }),
|
|
173
174
|
astroPrefetch({ settings }),
|
package/dist/core/dev/dev.js
CHANGED
|
@@ -26,7 +26,7 @@ async function dev(inlineConfig) {
|
|
|
26
26
|
await telemetry.record([]);
|
|
27
27
|
const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
|
|
28
28
|
const logger = restart.container.logger;
|
|
29
|
-
const currentVersion = "7.0.
|
|
29
|
+
const currentVersion = "7.0.7";
|
|
30
30
|
const isPrerelease = currentVersion.includes("-");
|
|
31
31
|
if (!isPrerelease) {
|
|
32
32
|
try {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { renderEndpoint } from "../../runtime/server/endpoint.js";
|
|
2
2
|
import { renderPage } from "../../runtime/server/index.js";
|
|
3
3
|
import { ASTRO_ERROR_HEADER } from "../constants.js";
|
|
4
|
+
import {
|
|
5
|
+
createCrossOriginForbiddenResponse,
|
|
6
|
+
isForbiddenCrossOriginRequest
|
|
7
|
+
} from "../app/origin-check.js";
|
|
4
8
|
import { getCookiesFromResponse } from "../cookies/response.js";
|
|
5
9
|
const EMPTY_SLOTS = Object.freeze({});
|
|
6
10
|
class PagesHandler {
|
|
@@ -78,8 +82,12 @@ class PagesHandler {
|
|
|
78
82
|
if (!state.routeData) {
|
|
79
83
|
return new Response(null, { status: 404, headers: { [ASTRO_ERROR_HEADER]: "true" } });
|
|
80
84
|
}
|
|
85
|
+
const ctx = state.getAPIContext();
|
|
86
|
+
if (this.#pipeline.manifest.checkOrigin && isForbiddenCrossOriginRequest(ctx.request, ctx.url, ctx.isPrerendered)) {
|
|
87
|
+
return createCrossOriginForbiddenResponse(ctx.request);
|
|
88
|
+
}
|
|
81
89
|
try {
|
|
82
|
-
return await this.handle(state,
|
|
90
|
+
return await this.handle(state, ctx);
|
|
83
91
|
} catch (err) {
|
|
84
92
|
app.logger.error(null, err.stack || err.message || String(err));
|
|
85
93
|
return app.renderError(state.request, {
|
|
@@ -44,9 +44,14 @@ async function getProps(opts) {
|
|
|
44
44
|
}
|
|
45
45
|
function getParams(route, pathname) {
|
|
46
46
|
if (!route.params.length) return {};
|
|
47
|
-
const
|
|
47
|
+
const hasHtmlSuffix = pathname.endsWith(".html") && !routeHasHtmlExtension(route);
|
|
48
|
+
const path = hasHtmlSuffix && route.type === "page" ? pathname.slice(0, -".html".length) : pathname;
|
|
48
49
|
const allPatterns = [route, ...route.fallbackRoutes].map((r) => r.pattern);
|
|
49
|
-
|
|
50
|
+
let paramsMatch = allPatterns.map((pattern) => pattern.exec(path)).find((x) => x);
|
|
51
|
+
if (!paramsMatch && hasHtmlSuffix && route.type !== "page") {
|
|
52
|
+
const strippedPath = pathname.endsWith("/index.html") ? pathname.slice(0, -"/index.html".length) || "/" : pathname.slice(0, -".html".length);
|
|
53
|
+
paramsMatch = allPatterns.map((pattern) => pattern.exec(strippedPath)).find((x) => x);
|
|
54
|
+
}
|
|
50
55
|
if (!paramsMatch) return {};
|
|
51
56
|
const params = {};
|
|
52
57
|
route.params.forEach((key, i) => {
|
|
@@ -11,10 +11,10 @@ function sanitizeParams(params) {
|
|
|
11
11
|
}
|
|
12
12
|
function getParameter(part, params) {
|
|
13
13
|
if (part.spread) {
|
|
14
|
-
return params[part.content.slice(3)]
|
|
14
|
+
return params[part.content.slice(3)] ?? "";
|
|
15
15
|
}
|
|
16
16
|
if (part.dynamic) {
|
|
17
|
-
if (
|
|
17
|
+
if (params[part.content] === void 0) {
|
|
18
18
|
throw new TypeError(`Missing parameter: ${part.content}`);
|
|
19
19
|
}
|
|
20
20
|
return params[part.content];
|
package/dist/core/util.d.ts
CHANGED
|
@@ -24,6 +24,14 @@ export declare function resolvePages(config: AstroConfig): URL;
|
|
|
24
24
|
export declare function isPage(file: URL, settings: AstroSettings): boolean;
|
|
25
25
|
export declare function isEndpoint(file: URL, settings: AstroSettings): boolean;
|
|
26
26
|
export declare function resolveJsToTs(filePath: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Resolve a path that doesn't name a file on disk (e.g. produced by an
|
|
29
|
+
* extensionless import like `import { Counter } from './Counter'`) to the file
|
|
30
|
+
* Vite would load, by probing Vite's default extension order and directory
|
|
31
|
+
* `index` files. Returns the path unchanged when it already exists as a file
|
|
32
|
+
* or when no candidate is found.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveExtensionlessPath(filePath: string): string;
|
|
27
35
|
/**
|
|
28
36
|
* Set a default NODE_ENV so Vite doesn't set an incorrect default when loading the Astro config
|
|
29
37
|
*/
|
package/dist/core/util.js
CHANGED
|
@@ -94,6 +94,28 @@ function resolveJsToTs(filePath) {
|
|
|
94
94
|
}
|
|
95
95
|
return filePath;
|
|
96
96
|
}
|
|
97
|
+
const VITE_DEFAULT_RESOLVE_EXTENSIONS = [".mjs", ".js", ".mts", ".ts", ".jsx", ".tsx", ".json"];
|
|
98
|
+
function resolveExtensionlessPath(filePath) {
|
|
99
|
+
const stat = fs.statSync(filePath, { throwIfNoEntry: false });
|
|
100
|
+
if (stat?.isFile()) {
|
|
101
|
+
return filePath;
|
|
102
|
+
}
|
|
103
|
+
for (const ext of VITE_DEFAULT_RESOLVE_EXTENSIONS) {
|
|
104
|
+
const tryPath = filePath + ext;
|
|
105
|
+
if (fs.existsSync(tryPath)) {
|
|
106
|
+
return tryPath;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (stat?.isDirectory()) {
|
|
110
|
+
for (const ext of VITE_DEFAULT_RESOLVE_EXTENSIONS) {
|
|
111
|
+
const tryPath = `${filePath}/index${ext}`;
|
|
112
|
+
if (fs.existsSync(tryPath)) {
|
|
113
|
+
return tryPath;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return filePath;
|
|
118
|
+
}
|
|
97
119
|
function ensureProcessNodeEnv(defaultNodeEnv) {
|
|
98
120
|
if (!process.env.NODE_ENV) {
|
|
99
121
|
process.env.NODE_ENV = defaultNodeEnv;
|
|
@@ -106,6 +128,7 @@ export {
|
|
|
106
128
|
isMarkdownFile,
|
|
107
129
|
isPage,
|
|
108
130
|
parseNpmName,
|
|
131
|
+
resolveExtensionlessPath,
|
|
109
132
|
resolveJsToTs,
|
|
110
133
|
resolvePages,
|
|
111
134
|
unwrapId,
|
package/dist/core/viteUtils.d.ts
CHANGED
|
@@ -9,6 +9,9 @@ export declare function normalizePath(id: string): string;
|
|
|
9
9
|
* Examples:
|
|
10
10
|
* - `./components/Button.jsx` from `/app/src/pages/index.astro`
|
|
11
11
|
* -> `/app/src/pages/components/Button.tsx` (when `.tsx` exists)
|
|
12
|
+
* - `../components/Counter` from `/app/src/pages/index.astro`
|
|
13
|
+
* -> `/app/src/components/Counter.tsx` (extensionless imports probe Vite's
|
|
14
|
+
* default extension order, then directory `index` files)
|
|
12
15
|
* - `#components/react/Counter.tsx`
|
|
13
16
|
* -> `/app/src/components/react/Counter.tsx` via package `imports`
|
|
14
17
|
*/
|
package/dist/core/viteUtils.js
CHANGED
|
@@ -2,7 +2,13 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
4
|
import { prependForwardSlash, slash } from "../core/path.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
resolveExtensionlessPath,
|
|
7
|
+
resolveJsToTs,
|
|
8
|
+
unwrapId,
|
|
9
|
+
VALID_ID_PREFIX,
|
|
10
|
+
viteID
|
|
11
|
+
} from "./util.js";
|
|
6
12
|
const isWindows = typeof process !== "undefined" && process.platform === "win32";
|
|
7
13
|
function normalizePath(id) {
|
|
8
14
|
return path.posix.normalize(isWindows ? slash(id) : id);
|
|
@@ -10,7 +16,7 @@ function normalizePath(id) {
|
|
|
10
16
|
function resolvePath(specifier, importer) {
|
|
11
17
|
if (specifier.startsWith(".")) {
|
|
12
18
|
const absoluteSpecifier = path.resolve(path.dirname(importer), specifier);
|
|
13
|
-
return resolveJsToTs(normalizePath(absoluteSpecifier));
|
|
19
|
+
return resolveExtensionlessPath(resolveJsToTs(normalizePath(absoluteSpecifier)));
|
|
14
20
|
} else if (specifier.startsWith("#")) {
|
|
15
21
|
try {
|
|
16
22
|
const resolved = createRequire(pathToFileURL(importer)).resolve(specifier);
|
|
@@ -103,7 +103,13 @@ function stringifyChunk(result, chunk) {
|
|
|
103
103
|
out += stringifyChunk(result, instr);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
|
|
106
|
+
if (c.chunks.length) {
|
|
107
|
+
for (const part of c.chunks) {
|
|
108
|
+
out += typeof part === "string" ? part : stringifyChunk(result, part);
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
out += chunk.toString();
|
|
112
|
+
}
|
|
107
113
|
return out;
|
|
108
114
|
}
|
|
109
115
|
return chunk.toString();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { markHTMLString } from "../escape.js";
|
|
2
2
|
import { renderSlotToString } from "./slot.js";
|
|
3
|
-
import { toAttributeString } from "./util.js";
|
|
3
|
+
import { INVALID_ATTR_NAME_CHAR, toAttributeString } from "./util.js";
|
|
4
4
|
function componentIsHTMLElement(Component) {
|
|
5
5
|
return typeof HTMLElement !== "undefined" && HTMLElement.isPrototypeOf(Component);
|
|
6
6
|
}
|
|
@@ -8,6 +8,9 @@ async function renderHTMLElement(result, constructor, props, slots) {
|
|
|
8
8
|
const name = getHTMLElementName(constructor);
|
|
9
9
|
let attrHTML = "";
|
|
10
10
|
for (const attr in props) {
|
|
11
|
+
if (INVALID_ATTR_NAME_CHAR.test(attr)) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
11
14
|
attrHTML += ` ${attr}="${toAttributeString(await props[attr])}"`;
|
|
12
15
|
}
|
|
13
16
|
return markHTMLString(
|
|
@@ -35,3 +35,4 @@ export type TemplateExitInstruction = {
|
|
|
35
35
|
export type RenderInstruction = RenderDirectiveInstruction | RenderHeadInstruction | MaybeRenderHeadInstruction | RendererHydrationScriptInstruction | ServerIslandRuntimeInstruction | RenderScriptInstruction | TemplateEnterInstruction | TemplateExitInstruction;
|
|
36
36
|
export declare function createRenderInstruction<T extends RenderInstruction>(instruction: T): T;
|
|
37
37
|
export declare function isRenderInstruction(chunk: any): chunk is RenderInstruction;
|
|
38
|
+
export declare function isScriptInstruction(chunk: any): chunk is RenderScriptInstruction;
|
|
@@ -7,7 +7,11 @@ function createRenderInstruction(instruction) {
|
|
|
7
7
|
function isRenderInstruction(chunk) {
|
|
8
8
|
return chunk && typeof chunk === "object" && chunk[RenderInstructionSymbol];
|
|
9
9
|
}
|
|
10
|
+
function isScriptInstruction(chunk) {
|
|
11
|
+
return chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "script";
|
|
12
|
+
}
|
|
10
13
|
export {
|
|
11
14
|
createRenderInstruction,
|
|
12
|
-
isRenderInstruction
|
|
15
|
+
isRenderInstruction,
|
|
16
|
+
isScriptInstruction
|
|
13
17
|
};
|
|
@@ -115,14 +115,14 @@ class ServerIslandComponent {
|
|
|
115
115
|
for (const name in this.slots) {
|
|
116
116
|
if (name !== "fallback") {
|
|
117
117
|
const content = await renderSlotToString(this.result, this.slots[name]);
|
|
118
|
-
let slotHtml = content.toString();
|
|
119
118
|
const slotContent = content;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
119
|
+
let slotHtml = "";
|
|
120
|
+
if (slotContent.chunks?.length) {
|
|
121
|
+
for (const part of slotContent.chunks) {
|
|
122
|
+
slotHtml += typeof part === "string" ? part : part.content;
|
|
125
123
|
}
|
|
124
|
+
} else {
|
|
125
|
+
slotHtml = content.toString();
|
|
126
126
|
}
|
|
127
127
|
renderedSlots[name] = slotHtml;
|
|
128
128
|
}
|
|
@@ -2,15 +2,28 @@ import type { SSRResult } from '../../../types/public/internal.js';
|
|
|
2
2
|
import { HTMLString } from '../escape.js';
|
|
3
3
|
import { renderTemplate } from './astro/render-template.js';
|
|
4
4
|
import { type RenderInstance } from './common.js';
|
|
5
|
-
import type
|
|
5
|
+
import { type RenderInstruction, type RenderScriptInstruction } from './instruction.js';
|
|
6
6
|
type RenderTemplateResult = ReturnType<typeof renderTemplate>;
|
|
7
7
|
export type ComponentSlots = Record<string, ComponentSlotValue>;
|
|
8
8
|
export type ComponentSlotValue = (result: SSRResult) => RenderTemplateResult | Promise<RenderTemplateResult>;
|
|
9
9
|
declare const slotString: unique symbol;
|
|
10
|
+
/**
|
|
11
|
+
* A part of a slot's content stream: either already-stringified HTML or a
|
|
12
|
+
* position-sensitive script instruction that is resolved (and deduplicated)
|
|
13
|
+
* lazily when the slot is finally stringified.
|
|
14
|
+
*/
|
|
15
|
+
export type SlotStringChunk = string | RenderScriptInstruction;
|
|
10
16
|
export declare class SlotString extends HTMLString {
|
|
11
17
|
instructions: null | RenderInstruction[];
|
|
18
|
+
/**
|
|
19
|
+
* The slot's content as an ordered stream. Unlike `instructions` (which holds
|
|
20
|
+
* position-independent instructions like head/hydration content), scripts are
|
|
21
|
+
* kept inline here so they render at their original position instead of being
|
|
22
|
+
* hoisted to the start of the slot output.
|
|
23
|
+
*/
|
|
24
|
+
chunks: SlotStringChunk[];
|
|
12
25
|
[slotString]: boolean;
|
|
13
|
-
constructor(content: string, instructions: null | RenderInstruction[]);
|
|
26
|
+
constructor(content: string, instructions: null | RenderInstruction[], chunks?: SlotStringChunk[]);
|
|
14
27
|
}
|
|
15
28
|
export declare function isSlotString(str: string): str is any;
|
|
16
29
|
/**
|
|
@@ -2,13 +2,24 @@ import { HTMLString, markHTMLString, unescapeHTML } from "../escape.js";
|
|
|
2
2
|
import { renderChild } from "./any.js";
|
|
3
3
|
import { renderTemplate } from "./astro/render-template.js";
|
|
4
4
|
import { chunkToString } from "./common.js";
|
|
5
|
+
import {
|
|
6
|
+
isScriptInstruction
|
|
7
|
+
} from "./instruction.js";
|
|
5
8
|
const slotString = /* @__PURE__ */ Symbol.for("astro:slot-string");
|
|
6
9
|
class SlotString extends HTMLString {
|
|
7
10
|
instructions;
|
|
11
|
+
/**
|
|
12
|
+
* The slot's content as an ordered stream. Unlike `instructions` (which holds
|
|
13
|
+
* position-independent instructions like head/hydration content), scripts are
|
|
14
|
+
* kept inline here so they render at their original position instead of being
|
|
15
|
+
* hoisted to the start of the slot output.
|
|
16
|
+
*/
|
|
17
|
+
chunks;
|
|
8
18
|
[slotString];
|
|
9
|
-
constructor(content, instructions) {
|
|
19
|
+
constructor(content, instructions, chunks = []) {
|
|
10
20
|
super(content);
|
|
11
21
|
this.instructions = instructions;
|
|
22
|
+
this.chunks = chunks;
|
|
12
23
|
this[slotString] = true;
|
|
13
24
|
}
|
|
14
25
|
}
|
|
@@ -35,25 +46,35 @@ function renderSlot(result, slotted, fallback) {
|
|
|
35
46
|
async function renderSlotToString(result, slotted, fallback) {
|
|
36
47
|
let content = "";
|
|
37
48
|
let instructions = null;
|
|
49
|
+
const chunks = [];
|
|
38
50
|
const temporaryDestination = {
|
|
39
51
|
write(chunk) {
|
|
40
52
|
if (chunk instanceof SlotString) {
|
|
41
53
|
content += chunk;
|
|
54
|
+
if (chunk.chunks.length) {
|
|
55
|
+
chunks.push(...chunk.chunks);
|
|
56
|
+
}
|
|
42
57
|
instructions = mergeSlotInstructions(instructions, chunk);
|
|
43
58
|
} else if (chunk instanceof Response) return;
|
|
44
59
|
else if (typeof chunk === "object" && "type" in chunk && typeof chunk.type === "string") {
|
|
45
|
-
if (
|
|
46
|
-
|
|
60
|
+
if (isScriptInstruction(chunk)) {
|
|
61
|
+
chunks.push(chunk);
|
|
62
|
+
} else {
|
|
63
|
+
if (instructions === null) {
|
|
64
|
+
instructions = [];
|
|
65
|
+
}
|
|
66
|
+
instructions.push(chunk);
|
|
47
67
|
}
|
|
48
|
-
instructions.push(chunk);
|
|
49
68
|
} else {
|
|
50
|
-
|
|
69
|
+
const str = chunkToString(result, chunk);
|
|
70
|
+
content += str;
|
|
71
|
+
chunks.push(str);
|
|
51
72
|
}
|
|
52
73
|
}
|
|
53
74
|
};
|
|
54
75
|
const renderInstance = renderSlot(result, slotted, fallback);
|
|
55
76
|
await renderInstance.render(temporaryDestination);
|
|
56
|
-
return markHTMLString(new SlotString(content, instructions));
|
|
77
|
+
return markHTMLString(new SlotString(content, instructions, chunks));
|
|
57
78
|
}
|
|
58
79
|
async function renderSlots(result, slots = {}) {
|
|
59
80
|
let slotInstructions = null;
|
|
@@ -68,6 +89,16 @@ async function renderSlots(result, slots = {}) {
|
|
|
68
89
|
}
|
|
69
90
|
slotInstructions.push(...output.instructions);
|
|
70
91
|
}
|
|
92
|
+
if (output.chunks) {
|
|
93
|
+
for (const part of output.chunks) {
|
|
94
|
+
if (typeof part !== "string") {
|
|
95
|
+
if (slotInstructions === null) {
|
|
96
|
+
slotInstructions = [];
|
|
97
|
+
}
|
|
98
|
+
slotInstructions.push(part);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
71
102
|
children[key] = output;
|
|
72
103
|
})
|
|
73
104
|
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SSRElement } from '../../../types/public/internal.js';
|
|
2
2
|
import type { RenderDestination, RenderFunction } from './common.js';
|
|
3
3
|
export declare const voidElementNames: RegExp;
|
|
4
|
+
export declare const INVALID_ATTR_NAME_CHAR: RegExp;
|
|
4
5
|
export declare const toAttributeString: (value: any, shouldEscape?: boolean) => any;
|
|
5
6
|
export declare const toStyleString: (obj: Record<string, any>) => string;
|
|
6
7
|
export declare function defineScriptVars(vars: Record<any, any>): any;
|
|
@@ -167,27 +167,31 @@ class AstroServerApp extends BaseApp {
|
|
|
167
167
|
} else {
|
|
168
168
|
socket.on("close", onSocketClose);
|
|
169
169
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
170
|
+
try {
|
|
171
|
+
const request = createRequest({
|
|
172
|
+
url,
|
|
173
|
+
headers: incomingRequest.headers,
|
|
174
|
+
method: incomingRequest.method,
|
|
175
|
+
body,
|
|
176
|
+
logger: self.logger,
|
|
177
|
+
isPrerendered: matchedRoute.routeData.prerender,
|
|
178
|
+
routePattern: matchedRoute.routeData.component,
|
|
179
|
+
init: { signal: abortController.signal }
|
|
180
|
+
});
|
|
181
|
+
const locals = Reflect.get(incomingRequest, clientLocalsSymbol);
|
|
182
|
+
for (const [name, value] of Object.entries(self.settings.config.server.headers ?? {})) {
|
|
183
|
+
if (value) incomingResponse.setHeader(name, value);
|
|
184
|
+
}
|
|
185
|
+
const clientAddress = incomingRequest.socket.remoteAddress;
|
|
186
|
+
const response = await self.render(request, {
|
|
187
|
+
locals,
|
|
188
|
+
routeData: matchedRoute.routeData,
|
|
189
|
+
clientAddress
|
|
190
|
+
});
|
|
191
|
+
await writeSSRResult(request, response, incomingResponse);
|
|
192
|
+
} finally {
|
|
193
|
+
socket.off("close", onSocketClose);
|
|
183
194
|
}
|
|
184
|
-
const clientAddress = incomingRequest.socket.remoteAddress;
|
|
185
|
-
const response = await self.render(request, {
|
|
186
|
-
locals,
|
|
187
|
-
routeData: matchedRoute.routeData,
|
|
188
|
-
clientAddress
|
|
189
|
-
});
|
|
190
|
-
await writeSSRResult(request, response, incomingResponse);
|
|
191
195
|
},
|
|
192
196
|
onError(_err) {
|
|
193
197
|
const error = createSafeError(_err);
|
|
@@ -25,6 +25,9 @@ function createVitePluginAstroServer({
|
|
|
25
25
|
return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr;
|
|
26
26
|
},
|
|
27
27
|
async configureServer(viteServer) {
|
|
28
|
+
if (process.env.VITEST) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
28
31
|
const ssrEnvironment = viteServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr];
|
|
29
32
|
const prerenderEnvironment = viteServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.prerender];
|
|
30
33
|
const runnableSsrEnvironment = isRunnableDevEnvironment(ssrEnvironment) ? ssrEnvironment : void 0;
|
|
@@ -3,6 +3,11 @@ import type { RoutesList } from '../types/astro.js';
|
|
|
3
3
|
interface AstroVitePluginOptions {
|
|
4
4
|
routesList: RoutesList;
|
|
5
5
|
command: 'dev' | 'build';
|
|
6
|
+
/** Shared cache of CSS content by module ID. Populated by the transform hook and
|
|
7
|
+
* consumed by the content asset propagation plugin to avoid re-processing CSS
|
|
8
|
+
* modules with `?inline` (which can produce different scoped-name hashes with
|
|
9
|
+
* Lightning CSS). */
|
|
10
|
+
cssContentCache: Map<string, string>;
|
|
6
11
|
}
|
|
7
12
|
/**
|
|
8
13
|
* This plugin tracks the CSS that should be applied by route.
|
|
@@ -14,5 +19,5 @@ interface AstroVitePluginOptions {
|
|
|
14
19
|
*
|
|
15
20
|
* @param routesList
|
|
16
21
|
*/
|
|
17
|
-
export declare function astroDevCssPlugin({ routesList, command }: AstroVitePluginOptions): Plugin[];
|
|
22
|
+
export declare function astroDevCssPlugin({ routesList, command, cssContentCache, }: AstroVitePluginOptions): Plugin[];
|
|
18
23
|
export {};
|
|
@@ -60,9 +60,12 @@ function* collectCSSWithOrder(id, mod, seen = /* @__PURE__ */ new Set()) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
function astroDevCssPlugin({
|
|
63
|
+
function astroDevCssPlugin({
|
|
64
|
+
routesList,
|
|
65
|
+
command,
|
|
66
|
+
cssContentCache
|
|
67
|
+
}) {
|
|
64
68
|
let server;
|
|
65
|
-
const cssContentCache = /* @__PURE__ */ new Map();
|
|
66
69
|
function getCurrentEnvironment(pluginEnv) {
|
|
67
70
|
return pluginEnv ?? server?.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr];
|
|
68
71
|
}
|
|
@@ -122,7 +125,7 @@ function astroDevCssPlugin({ routesList, command }) {
|
|
|
122
125
|
}
|
|
123
126
|
for (const collected of collectCSSWithOrder(componentPageId, mod)) {
|
|
124
127
|
if (!cssWithOrder.has(collected.idKey)) {
|
|
125
|
-
const content = cssContentCache.get(collected.
|
|
128
|
+
const content = cssContentCache.get(collected.idKey) || collected.content;
|
|
126
129
|
cssWithOrder.set(collected.idKey, { ...collected, content });
|
|
127
130
|
}
|
|
128
131
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.7",
|
|
4
4
|
"description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "withastro",
|
|
@@ -146,15 +146,15 @@
|
|
|
146
146
|
"xxhash-wasm": "^1.1.0",
|
|
147
147
|
"yargs-parser": "^22.0.0",
|
|
148
148
|
"zod": "^4.3.6",
|
|
149
|
-
"@astrojs/
|
|
150
|
-
"@astrojs/
|
|
151
|
-
"@astrojs/telemetry": "3.3.
|
|
149
|
+
"@astrojs/markdown-satteri": "0.3.3",
|
|
150
|
+
"@astrojs/internal-helpers": "0.10.1",
|
|
151
|
+
"@astrojs/telemetry": "3.3.3"
|
|
152
152
|
},
|
|
153
153
|
"optionalDependencies": {
|
|
154
154
|
"sharp": "^0.34.0 || ^0.35.0"
|
|
155
155
|
},
|
|
156
156
|
"peerDependencies": {
|
|
157
|
-
"@astrojs/markdown-remark": "7.2.
|
|
157
|
+
"@astrojs/markdown-remark": "7.2.1"
|
|
158
158
|
},
|
|
159
159
|
"peerDependenciesMeta": {
|
|
160
160
|
"@astrojs/markdown-remark": {
|
|
@@ -186,8 +186,8 @@
|
|
|
186
186
|
"typescript": "^6.0.3",
|
|
187
187
|
"undici": "^7.22.0",
|
|
188
188
|
"vitest": "^4.1.0",
|
|
189
|
+
"@astrojs/markdown-remark": "7.2.1",
|
|
189
190
|
"@astrojs/check": "0.9.9",
|
|
190
|
-
"@astrojs/markdown-remark": "7.2.0",
|
|
191
191
|
"astro-scripts": "0.0.14"
|
|
192
192
|
},
|
|
193
193
|
"engines": {
|
|
@@ -85,6 +85,10 @@ declare module 'astro:content' {
|
|
|
85
85
|
entry: DataEntryMap[C][string],
|
|
86
86
|
): Promise<RenderResult>;
|
|
87
87
|
|
|
88
|
+
export function render<C extends keyof LiveContentConfig['collections']>(
|
|
89
|
+
entry: import('astro').LiveDataEntry<LiveLoaderDataType<C>>,
|
|
90
|
+
): Promise<RenderResult>;
|
|
91
|
+
|
|
88
92
|
export function reference<
|
|
89
93
|
C extends
|
|
90
94
|
| keyof DataEntryMap
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { MiddlewareHandler } from '../../types/public/common.js';
|
|
2
|
-
/**
|
|
3
|
-
* Returns a middleware function in charge to check the `origin` header.
|
|
4
|
-
*
|
|
5
|
-
* @private
|
|
6
|
-
*/
|
|
7
|
-
export declare function createOriginCheckMiddleware(): MiddlewareHandler;
|
|
8
|
-
export declare function hasFormLikeHeader(contentType: string | null): boolean;
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { defineMiddleware } from "../middleware/defineMiddleware.js";
|
|
2
|
-
const FORM_CONTENT_TYPES = [
|
|
3
|
-
"application/x-www-form-urlencoded",
|
|
4
|
-
"multipart/form-data",
|
|
5
|
-
"text/plain"
|
|
6
|
-
];
|
|
7
|
-
const SAFE_METHODS = ["GET", "HEAD", "OPTIONS"];
|
|
8
|
-
function createOriginCheckMiddleware() {
|
|
9
|
-
return defineMiddleware((context, next) => {
|
|
10
|
-
const { request, url, isPrerendered } = context;
|
|
11
|
-
if (isPrerendered) {
|
|
12
|
-
return next();
|
|
13
|
-
}
|
|
14
|
-
if (SAFE_METHODS.includes(request.method)) {
|
|
15
|
-
return next();
|
|
16
|
-
}
|
|
17
|
-
const isSameOrigin = request.headers.get("origin") === url.origin;
|
|
18
|
-
const hasContentType = request.headers.has("content-type");
|
|
19
|
-
if (hasContentType) {
|
|
20
|
-
const formLikeHeader = hasFormLikeHeader(request.headers.get("content-type"));
|
|
21
|
-
if (formLikeHeader && !isSameOrigin) {
|
|
22
|
-
return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
|
|
23
|
-
status: 403
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
} else {
|
|
27
|
-
if (!isSameOrigin) {
|
|
28
|
-
return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
|
|
29
|
-
status: 403
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return next();
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
function hasFormLikeHeader(contentType) {
|
|
37
|
-
if (contentType) {
|
|
38
|
-
for (const FORM_CONTENT_TYPE of FORM_CONTENT_TYPES) {
|
|
39
|
-
if (contentType.toLowerCase().includes(FORM_CONTENT_TYPE)) {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
export {
|
|
47
|
-
createOriginCheckMiddleware,
|
|
48
|
-
hasFormLikeHeader
|
|
49
|
-
};
|