astro 7.0.0-beta.3 → 7.0.0-beta.4
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/dist/assets/build/generate.js +4 -3
- package/dist/cli/add/index.js +1 -0
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/container/index.d.ts +3 -3
- package/dist/content/content-layer.js +3 -3
- package/dist/content/runtime.d.ts +1 -1
- package/dist/content/runtime.js +1 -0
- package/dist/content/vite-plugin-content-virtual-mod.js +27 -0
- package/dist/core/app/base.js +7 -15
- package/dist/core/build/plugins/plugin-css.js +1 -0
- package/dist/core/config/schemas/base.d.ts +3 -3
- package/dist/core/config/schemas/base.js +3 -3
- package/dist/core/config/validate.js +10 -2
- package/dist/core/constants.js +1 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/errors/default-handler.js +21 -8
- package/dist/core/fetch/fetch-state.js +3 -2
- package/dist/core/messages/runtime.js +1 -1
- package/dist/core/middleware/vite-plugin.d.ts +1 -0
- package/dist/core/middleware/vite-plugin.js +5 -1
- package/dist/core/util/normalized-url.js +2 -5
- package/dist/core/util/pathname.d.ts +13 -7
- package/dist/core/util/pathname.js +9 -6
- package/dist/i18n/index.js +6 -2
- package/dist/runtime/server/jsx.js +2 -1
- package/dist/runtime/server/render/head.js +2 -1
- package/dist/runtime/server/render/util.js +4 -0
- package/dist/types/public/config.d.ts +62 -7
- package/dist/vite-plugin-hmr-reload/index.js +19 -6
- package/dist/vite-plugin-html/transform/slots.js +4 -1
- package/dist/vite-plugin-pages/pages.d.ts +11 -0
- package/dist/vite-plugin-pages/pages.js +1 -3
- package/package.json +13 -7
- package/dist/jsx/rehype.d.ts +0 -5
- package/dist/jsx/rehype.js +0 -241
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import fs, { readFileSync } from "node:fs";
|
|
2
2
|
import { basename } from "node:path/posix";
|
|
3
3
|
import colors from "piccolore";
|
|
4
|
-
import { getOutDirWithinCwd } from "../../core/build/common.js";
|
|
5
4
|
import { getTimeStat } from "../../core/build/util.js";
|
|
6
5
|
import { AstroError } from "../../core/errors/errors.js";
|
|
7
6
|
import { AstroErrorData } from "../../core/errors/index.js";
|
|
8
7
|
import { isRemotePath, removeLeadingForwardSlash } from "../../core/path.js";
|
|
8
|
+
import { getClientOutputDirectory } from "../../prerender/utils.js";
|
|
9
9
|
import { getConfiguredImageService } from "../internal.js";
|
|
10
10
|
import { isESMImportedImage } from "../utils/imageKind.js";
|
|
11
11
|
import { loadRemoteImage, revalidateRemoteImage } from "./remote.js";
|
|
@@ -29,8 +29,9 @@ async function prepareAssetsGenerationEnv(options, totalCount) {
|
|
|
29
29
|
serverRoot = new URL(".prerender/", settings.config.build.server);
|
|
30
30
|
clientRoot = settings.config.build.client;
|
|
31
31
|
} else {
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const clientOutputDir = getClientOutputDirectory(settings);
|
|
33
|
+
serverRoot = clientOutputDir;
|
|
34
|
+
clientRoot = clientOutputDir;
|
|
34
35
|
}
|
|
35
36
|
return {
|
|
36
37
|
logger,
|
package/dist/cli/add/index.js
CHANGED
|
@@ -48,6 +48,7 @@ export default {
|
|
|
48
48
|
public-hoist-pattern[]=*lit*
|
|
49
49
|
`,
|
|
50
50
|
CLOUDFLARE_WRANGLER_CONFIG: (name, compatibilityDate) => `{
|
|
51
|
+
"$schema": "./node_modules/wrangler/config-schema.json",
|
|
51
52
|
"compatibility_date": ${JSON.stringify(compatibilityDate)},
|
|
52
53
|
"compatibility_flags": ["global_fetch_strictly_public"],
|
|
53
54
|
"name": ${JSON.stringify(name)},
|
|
@@ -67,7 +67,7 @@ export type ContainerRenderOptions = {
|
|
|
67
67
|
*/
|
|
68
68
|
routeType?: RouteType;
|
|
69
69
|
/**
|
|
70
|
-
* Allows
|
|
70
|
+
* Allows passing `Astro.props` to an Astro component:
|
|
71
71
|
*
|
|
72
72
|
* ```js
|
|
73
73
|
* container.renderToString(Endpoint, { props: { "lorem": "ipsum" } });
|
|
@@ -75,9 +75,9 @@ export type ContainerRenderOptions = {
|
|
|
75
75
|
*/
|
|
76
76
|
props?: Props;
|
|
77
77
|
/**
|
|
78
|
-
* When `false`, it forces to render
|
|
78
|
+
* When `false`, it forces the component to render as if it were a full-fledged page.
|
|
79
79
|
*
|
|
80
|
-
* By default, the container API
|
|
80
|
+
* By default, the container API renders components as [partials](https://docs.astro.build/en/basics/astro-pages/#page-partials).
|
|
81
81
|
*
|
|
82
82
|
*/
|
|
83
83
|
partial?: boolean;
|
|
@@ -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.0-beta.
|
|
200
|
+
if (previousAstroVersion && previousAstroVersion !== "7.0.0-beta.4") {
|
|
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.0-beta.
|
|
209
|
-
this.#store.metaStore().set("astro-version", "7.0.0-beta.
|
|
208
|
+
if ("7.0.0-beta.4") {
|
|
209
|
+
this.#store.metaStore().set("astro-version", "7.0.0-beta.4");
|
|
210
210
|
}
|
|
211
211
|
if (currentConfigDigest) {
|
|
212
212
|
this.#store.metaStore().set("content-config-digest", currentConfigDigest);
|
|
@@ -70,7 +70,7 @@ type RenderResult = {
|
|
|
70
70
|
};
|
|
71
71
|
export declare function updateImageReferencesInData<T extends Record<string, unknown>>(data: T, fileName?: string, imageAssetMap?: Map<string, ImageMetadata>): T;
|
|
72
72
|
export declare function renderEntry(entry: DataEntry): Promise<RenderResult>;
|
|
73
|
-
export declare function createReference(): (collection: string) => z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
73
|
+
export declare function createReference(): (collection: string) => z.ZodPipe<z.ZodUnion<readonly [z.ZodPipe<z.ZodNumber, z.ZodTransform<string, number>>, z.ZodString, z.ZodObject<{
|
|
74
74
|
id: z.ZodString;
|
|
75
75
|
collection: z.ZodString;
|
|
76
76
|
}, z.core.$strip>, z.ZodObject<{
|
package/dist/content/runtime.js
CHANGED
|
@@ -23,6 +23,25 @@ import {
|
|
|
23
23
|
} from "./consts.js";
|
|
24
24
|
import { getDataStoreFile } from "./content-layer.js";
|
|
25
25
|
import { getContentPaths, isDeferredModule } from "./utils.js";
|
|
26
|
+
function invalidateAssetImports(viteServer, filePath) {
|
|
27
|
+
const timestamp = Date.now();
|
|
28
|
+
for (const environment of Object.values(viteServer.environments)) {
|
|
29
|
+
const modules = environment.moduleGraph.getModulesByFile(filePath);
|
|
30
|
+
if (modules) {
|
|
31
|
+
for (const module of modules) {
|
|
32
|
+
environment.moduleGraph.invalidateModule(module, void 0, timestamp, true);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (isRunnableDevEnvironment(environment)) {
|
|
36
|
+
const runnerModules = environment.runner.evaluatedModules.getModulesByFile(filePath);
|
|
37
|
+
if (runnerModules) {
|
|
38
|
+
for (const runnerModule of runnerModules) {
|
|
39
|
+
environment.runner.evaluatedModules.invalidateModule(runnerModule);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
26
45
|
function invalidateDataStore(viteServer) {
|
|
27
46
|
const environment = viteServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr];
|
|
28
47
|
const module = environment.moduleGraph.getModuleById(RESOLVED_DATA_STORE_VIRTUAL_ID);
|
|
@@ -67,8 +86,11 @@ function astroContentVirtualModPlugin({
|
|
|
67
86
|
},
|
|
68
87
|
buildStart() {
|
|
69
88
|
if (devServer) {
|
|
89
|
+
const assetImportsPath = fileURLToPath(new URL(ASSET_IMPORTS_FILE, settings.dotAstroDir));
|
|
70
90
|
devServer.watcher.add(fileURLToPath(dataStoreFile));
|
|
91
|
+
devServer.watcher.add(assetImportsPath);
|
|
71
92
|
invalidateDataStore(devServer);
|
|
93
|
+
invalidateAssetImports(devServer, assetImportsPath);
|
|
72
94
|
}
|
|
73
95
|
},
|
|
74
96
|
resolveId: {
|
|
@@ -175,14 +197,19 @@ function astroContentVirtualModPlugin({
|
|
|
175
197
|
configureServer(server) {
|
|
176
198
|
devServer = server;
|
|
177
199
|
const dataStorePath = fileURLToPath(dataStoreFile);
|
|
200
|
+
const assetImportsPath = fileURLToPath(new URL(ASSET_IMPORTS_FILE, settings.dotAstroDir));
|
|
178
201
|
server.watcher.on("add", (addedPath) => {
|
|
179
202
|
if (addedPath === dataStorePath) {
|
|
180
203
|
invalidateDataStore(server);
|
|
204
|
+
invalidateAssetImports(server, assetImportsPath);
|
|
181
205
|
}
|
|
182
206
|
});
|
|
183
207
|
server.watcher.on("change", (changedPath) => {
|
|
184
208
|
if (changedPath === dataStorePath) {
|
|
185
209
|
invalidateDataStore(server);
|
|
210
|
+
invalidateAssetImports(server, assetImportsPath);
|
|
211
|
+
} else if (changedPath === assetImportsPath) {
|
|
212
|
+
invalidateAssetImports(server, assetImportsPath);
|
|
186
213
|
}
|
|
187
214
|
});
|
|
188
215
|
}
|
package/dist/core/app/base.js
CHANGED
|
@@ -14,7 +14,6 @@ import { DefaultFetchHandler } from "../fetch/default-handler.js";
|
|
|
14
14
|
import { appSymbol } from "../constants.js";
|
|
15
15
|
import { DefaultErrorHandler } from "../errors/default-handler.js";
|
|
16
16
|
import { setRenderOptions } from "./render-options.js";
|
|
17
|
-
import { MultiLevelEncodingError } from "../util/pathname.js";
|
|
18
17
|
class BaseApp {
|
|
19
18
|
manifest;
|
|
20
19
|
manifestData;
|
|
@@ -237,20 +236,13 @@ class BaseApp {
|
|
|
237
236
|
waitUntil
|
|
238
237
|
};
|
|
239
238
|
let response;
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
response = await this.#fetchHandler.fetch(request);
|
|
248
|
-
}
|
|
249
|
-
} catch (err) {
|
|
250
|
-
if (err instanceof MultiLevelEncodingError) {
|
|
251
|
-
return new Response("Bad Request", { status: 400 });
|
|
252
|
-
}
|
|
253
|
-
throw err;
|
|
239
|
+
if (this.#fetchHandler instanceof DefaultFetchHandler) {
|
|
240
|
+
Reflect.set(request, appSymbol, this);
|
|
241
|
+
response = await this.#fetchHandler.renderWithOptions(request, resolvedOptions);
|
|
242
|
+
} else {
|
|
243
|
+
setRenderOptions(request, resolvedOptions);
|
|
244
|
+
Reflect.set(request, appSymbol, this);
|
|
245
|
+
response = await this.#fetchHandler.fetch(request);
|
|
254
246
|
}
|
|
255
247
|
this.#warnMissingFeatures();
|
|
256
248
|
if (response.headers.get(ASTRO_ERROR_HEADER)) {
|
|
@@ -107,6 +107,7 @@ function rollupPluginAstroBuildCSS(options) {
|
|
|
107
107
|
if (meta.importedCss.size < 1) continue;
|
|
108
108
|
if (this.environment?.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) {
|
|
109
109
|
for (const id of Object.keys(chunk.modules)) {
|
|
110
|
+
if (!isCSSRequest(id)) continue;
|
|
110
111
|
for (const pageData of getParentClientOnlys(id, this, internals)) {
|
|
111
112
|
for (const importedCssImport of meta.importedCss) {
|
|
112
113
|
const cssToInfoRecord = pagesToCss[pageData.moduleSpecifier] ??= {};
|
|
@@ -57,7 +57,7 @@ export declare const ASTRO_CONFIG_DEFAULTS: {
|
|
|
57
57
|
allowedHosts: never[];
|
|
58
58
|
};
|
|
59
59
|
integrations: never[];
|
|
60
|
-
markdown: Required<Omit<import("@astrojs/markdown
|
|
60
|
+
markdown: Required<Omit<import("@astrojs/internal-helpers/markdown").AstroMarkdownOptions, "image">>;
|
|
61
61
|
vite: {};
|
|
62
62
|
legacy: {
|
|
63
63
|
collectionsBackwardsCompat: false;
|
|
@@ -329,8 +329,8 @@ export declare const AstroConfigSchema: z.ZodObject<{
|
|
|
329
329
|
processor: z.ZodDefault<z.ZodObject<{
|
|
330
330
|
name: z.ZodString;
|
|
331
331
|
options: z.ZodDefault<z.ZodCustom<object, object>>;
|
|
332
|
-
createRenderer: z.ZodCustom<(shared: import("@astrojs/markdown
|
|
333
|
-
createMdxRenderer: z.ZodOptional<z.ZodCustom<((shared: import("@astrojs/markdown
|
|
332
|
+
createRenderer: z.ZodCustom<(shared: import("@astrojs/internal-helpers/markdown").AstroMarkdownOptions) => Promise<import("@astrojs/internal-helpers/markdown").MarkdownRenderer>, (shared: import("@astrojs/internal-helpers/markdown").AstroMarkdownOptions) => Promise<import("@astrojs/internal-helpers/markdown").MarkdownRenderer>>;
|
|
333
|
+
createMdxRenderer: z.ZodOptional<z.ZodCustom<((shared: import("@astrojs/internal-helpers/markdown").AstroMarkdownOptions, mdx: import("@astrojs/internal-helpers/markdown").MdxRendererOptions) => Promise<import("@astrojs/internal-helpers/markdown").MdxRenderer>) | undefined, ((shared: import("@astrojs/internal-helpers/markdown").AstroMarkdownOptions, mdx: import("@astrojs/internal-helpers/markdown").MdxRendererOptions) => Promise<import("@astrojs/internal-helpers/markdown").MdxRenderer>) | undefined>>;
|
|
334
334
|
}, z.core.$strip>>;
|
|
335
335
|
}, z.core.$strip>>;
|
|
336
336
|
vite: z.ZodDefault<z.ZodCustom<ViteUserConfig, ViteUserConfig>>;
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
markdownConfigDefaults,
|
|
3
3
|
syntaxHighlightDefaults
|
|
4
4
|
} from "@astrojs/internal-helpers/markdown";
|
|
5
|
-
import {
|
|
5
|
+
import { satteri } from "@astrojs/markdown-satteri";
|
|
6
6
|
import { bundledThemes } from "shiki";
|
|
7
7
|
import * as z from "zod/v4";
|
|
8
8
|
import { FontFamilySchema } from "../../../assets/fonts/config.js";
|
|
@@ -238,7 +238,7 @@ const AstroConfigSchema = z.object({
|
|
|
238
238
|
remarkRehype: z.custom((data) => data instanceof Object && !Array.isArray(data)).default(ASTRO_CONFIG_DEFAULTS.markdown.remarkRehype),
|
|
239
239
|
// Deprecated: left undefined unless the user explicitly sets them, so the
|
|
240
240
|
// deprecation warning only fires when actually used. The active processor
|
|
241
|
-
// (`
|
|
241
|
+
// (`satteri()`) supplies the real default (`gfm`/smart punctuation on) when
|
|
242
242
|
// these are absent.
|
|
243
243
|
gfm: z.boolean().optional(),
|
|
244
244
|
smartypants: z.union([z.boolean(), smartypantsOptionsSchema]).transform((val) => {
|
|
@@ -256,7 +256,7 @@ const AstroConfigSchema = z.object({
|
|
|
256
256
|
createMdxRenderer: z.custom(
|
|
257
257
|
(v) => v === void 0 || typeof v === "function"
|
|
258
258
|
).optional()
|
|
259
|
-
}).default(() =>
|
|
259
|
+
}).default(() => satteri())
|
|
260
260
|
}).prefault({}),
|
|
261
261
|
vite: z.custom((data) => data instanceof Object && !Array.isArray(data)).default(ASTRO_CONFIG_DEFAULTS.vite),
|
|
262
262
|
i18n: z.optional(
|
|
@@ -30,7 +30,7 @@ function warnDeprecatedMarkdownOptions(config) {
|
|
|
30
30
|
const names = deprecated.map((key) => `\`markdown.${key}\``).join(" and ");
|
|
31
31
|
const isPlural = deprecated.length > 1;
|
|
32
32
|
console.warn(
|
|
33
|
-
`[astro] ${names} ${isPlural ? "are" : "is"} deprecated. Move ${isPlural ? "them" : "it"} onto your processor instead (e.g. \`unified({ gfm: false, smartypants: false })\`). Will be removed in a future major.`
|
|
33
|
+
`[astro] ${names} ${isPlural ? "are" : "is"} deprecated. Move ${isPlural ? "them" : "it"} onto your processor instead (e.g. \`satteri({ features: { gfm: false, smartPunctuation: false } })\`, or \`unified({ gfm: false, smartypants: false })\` from \`@astrojs/markdown-remark\`). Will be removed in a future major.`
|
|
34
34
|
);
|
|
35
35
|
}
|
|
36
36
|
let didWarnAboutLegacyMarkdownPlugins = false;
|
|
@@ -45,7 +45,15 @@ async function coerceLegacyMarkdownPlugins(config) {
|
|
|
45
45
|
if (remarkPlugins.length === 0 && rehypePlugins.length === 0 && Object.keys(remarkRehype).length === 0) {
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
|
-
|
|
48
|
+
let unified;
|
|
49
|
+
let isUnifiedProcessor;
|
|
50
|
+
try {
|
|
51
|
+
({ unified, isUnifiedProcessor } = await import("@astrojs/markdown-remark"));
|
|
52
|
+
} catch {
|
|
53
|
+
throw new Error(
|
|
54
|
+
"`markdown.remarkPlugins`, `markdown.rehypePlugins`, and `markdown.remarkRehype` run on the `unified` processor from `@astrojs/markdown-remark`, which is no longer installed by default now that S\xE4tteri is the default Markdown processor. Install it with:\n npm install @astrojs/markdown-remark"
|
|
55
|
+
);
|
|
56
|
+
}
|
|
49
57
|
const current = md.processor;
|
|
50
58
|
if (!current || isUnifiedProcessor(current)) {
|
|
51
59
|
const target = current ?? (md.processor = unified());
|
package/dist/core/constants.js
CHANGED
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.0-beta.
|
|
29
|
+
const currentVersion = "7.0.0-beta.4";
|
|
30
30
|
const isPrerelease = currentVersion.includes("-");
|
|
31
31
|
if (!isPrerelease) {
|
|
32
32
|
try {
|
|
@@ -6,6 +6,7 @@ import { AstroMiddleware } from "../middleware/astro-middleware.js";
|
|
|
6
6
|
import { PagesHandler } from "../pages/handler.js";
|
|
7
7
|
import { matchRoute } from "../routing/match.js";
|
|
8
8
|
import { provideSession } from "../session/handler.js";
|
|
9
|
+
import { validateHost } from "../app/validate-headers.js";
|
|
9
10
|
class DefaultErrorHandler {
|
|
10
11
|
#app;
|
|
11
12
|
#astroMiddleware;
|
|
@@ -31,15 +32,27 @@ class DefaultErrorHandler {
|
|
|
31
32
|
if (errorRouteData) {
|
|
32
33
|
if (errorRouteData.prerender) {
|
|
33
34
|
const maybeDotHtml = errorRouteData.route.endsWith(`/${status}`) ? ".html" : "";
|
|
34
|
-
const
|
|
35
|
+
const allowedDomains = app.manifest.allowedDomains;
|
|
36
|
+
const validatedHost = validateHost(url.host, url.protocol.replace(":", ""), allowedDomains);
|
|
37
|
+
const safeOrigin = validatedHost ? url.origin : `${url.protocol}//localhost`;
|
|
38
|
+
const statusURL = new URL(
|
|
39
|
+
`${app.baseWithoutTrailingSlash}/${status}${maybeDotHtml}`,
|
|
40
|
+
safeOrigin
|
|
41
|
+
);
|
|
35
42
|
if (statusURL.toString() !== request.url && resolvedRenderOptions.prerenderedErrorPageFetch) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
try {
|
|
44
|
+
const response2 = await resolvedRenderOptions.prerenderedErrorPageFetch(
|
|
45
|
+
statusURL.toString()
|
|
46
|
+
);
|
|
47
|
+
const override = { status, removeContentEncodingHeaders: true };
|
|
48
|
+
const newResponse = mergeResponses(response2, originalResponse, override);
|
|
49
|
+
prepareResponse(newResponse, resolvedRenderOptions);
|
|
50
|
+
return newResponse;
|
|
51
|
+
} catch {
|
|
52
|
+
const response2 = mergeResponses(new Response(null, { status }), originalResponse);
|
|
53
|
+
prepareResponse(response2, resolvedRenderOptions);
|
|
54
|
+
return response2;
|
|
55
|
+
}
|
|
43
56
|
}
|
|
44
57
|
}
|
|
45
58
|
const mod = await app.pipeline.getComponentByRoute(errorRouteData);
|
|
@@ -29,6 +29,7 @@ import { getParams, getProps } from "../render/index.js";
|
|
|
29
29
|
import { Rewrites } from "../rewrites/handler.js";
|
|
30
30
|
import { isRoute404or500, isRouteServerIsland } from "../routing/match.js";
|
|
31
31
|
import { normalizeUrl } from "../util/normalized-url.js";
|
|
32
|
+
import { validateAndDecodePathname } from "../util/pathname.js";
|
|
32
33
|
import { getOriginPathname, setOriginPathname } from "../routing/rewrite.js";
|
|
33
34
|
import { computePathnameFromDomain } from "../i18n/domain.js";
|
|
34
35
|
import { getCustom404Route, routeHasHtmlExtension } from "../routing/helpers.js";
|
|
@@ -190,7 +191,7 @@ class FetchState {
|
|
|
190
191
|
this.locals = options?.locals ?? {};
|
|
191
192
|
this.url = normalizeUrl(url);
|
|
192
193
|
this.cookies = new AstroCookies(request);
|
|
193
|
-
if (pipeline.manifest.allowedDomains && pipeline.manifest.allowedDomains.length > 0) {
|
|
194
|
+
if (pipeline.manifest.allowedDomains && pipeline.manifest.allowedDomains.length > 0 && !this.routeData?.prerender) {
|
|
194
195
|
this.#applyForwardedHeaders();
|
|
195
196
|
}
|
|
196
197
|
if (!Reflect.get(this.request, originPathnameSymbol)) {
|
|
@@ -694,7 +695,7 @@ class FetchState {
|
|
|
694
695
|
}
|
|
695
696
|
pathname = prependForwardSlash(pathname);
|
|
696
697
|
try {
|
|
697
|
-
return
|
|
698
|
+
return validateAndDecodePathname(pathname);
|
|
698
699
|
} catch (e) {
|
|
699
700
|
this.pipeline.logger.error(null, e.toString());
|
|
700
701
|
return pathname;
|
|
@@ -3,6 +3,7 @@ import type { AstroSettings } from '../../types/astro.js';
|
|
|
3
3
|
import type { BuildInternals } from '../build/internal.js';
|
|
4
4
|
import type { StaticBuildOptions } from '../build/types.js';
|
|
5
5
|
export declare const MIDDLEWARE_MODULE_ID = "virtual:astro:middleware";
|
|
6
|
+
export declare function isMiddlewarePath(relativePath: string): boolean;
|
|
6
7
|
export declare function vitePluginMiddleware({ settings }: {
|
|
7
8
|
settings: AstroSettings;
|
|
8
9
|
}): VitePlugin;
|
|
@@ -11,6 +11,9 @@ import { normalizePath } from "../viteUtils.js";
|
|
|
11
11
|
const MIDDLEWARE_MODULE_ID = "virtual:astro:middleware";
|
|
12
12
|
const MIDDLEWARE_RESOLVED_MODULE_ID = "\0" + MIDDLEWARE_MODULE_ID;
|
|
13
13
|
const NOOP_MIDDLEWARE = "\0noop-middleware";
|
|
14
|
+
function isMiddlewarePath(relativePath) {
|
|
15
|
+
return relativePath.startsWith(`${MIDDLEWARE_PATH_SEGMENT_NAME}.`) || relativePath.startsWith(`${MIDDLEWARE_PATH_SEGMENT_NAME}/`);
|
|
16
|
+
}
|
|
14
17
|
function vitePluginMiddleware({ settings }) {
|
|
15
18
|
let resolvedMiddlewareId = void 0;
|
|
16
19
|
const hasIntegrationMiddleware = settings.middlewares.pre.length > 0 || settings.middlewares.post.length > 0;
|
|
@@ -26,7 +29,7 @@ function vitePluginMiddleware({ settings }) {
|
|
|
26
29
|
const normalizedPath = viteNormalizePath(path);
|
|
27
30
|
if (!normalizedPath.startsWith(normalizedSrcDir)) return;
|
|
28
31
|
const relativePath = normalizedPath.slice(normalizedSrcDir.length);
|
|
29
|
-
if (!relativePath
|
|
32
|
+
if (!isMiddlewarePath(relativePath)) return;
|
|
30
33
|
for (const name of [
|
|
31
34
|
ASTRO_VITE_ENVIRONMENT_NAMES.ssr,
|
|
32
35
|
ASTRO_VITE_ENVIRONMENT_NAMES.astro
|
|
@@ -135,6 +138,7 @@ function vitePluginMiddlewareBuild(opts, internals) {
|
|
|
135
138
|
}
|
|
136
139
|
export {
|
|
137
140
|
MIDDLEWARE_MODULE_ID,
|
|
141
|
+
isMiddlewarePath,
|
|
138
142
|
vitePluginMiddleware,
|
|
139
143
|
vitePluginMiddlewareBuild
|
|
140
144
|
};
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { collapseDuplicateSlashes } from "@astrojs/internal-helpers/path";
|
|
2
|
-
import {
|
|
2
|
+
import { validateAndDecodePathname } from "./pathname.js";
|
|
3
3
|
function createNormalizedUrl(requestUrl) {
|
|
4
4
|
return normalizeUrl(new URL(requestUrl));
|
|
5
5
|
}
|
|
6
6
|
function normalizeUrl(url) {
|
|
7
7
|
try {
|
|
8
8
|
url.pathname = validateAndDecodePathname(url.pathname);
|
|
9
|
-
} catch
|
|
10
|
-
if (e instanceof MultiLevelEncodingError) {
|
|
11
|
-
throw e;
|
|
12
|
-
}
|
|
9
|
+
} catch {
|
|
13
10
|
try {
|
|
14
11
|
url.pathname = decodeURI(url.pathname);
|
|
15
12
|
} catch {
|
|
@@ -2,18 +2,24 @@
|
|
|
2
2
|
* Error thrown when multi-level URL encoding is detected in a pathname.
|
|
3
3
|
* This is a distinct error type so callers can handle it specifically
|
|
4
4
|
* (e.g., returning a 400 response) rather than falling back to partial decoding.
|
|
5
|
+
*
|
|
6
|
+
* @deprecated No longer thrown internally — multi-level encoding is now
|
|
7
|
+
* decoded iteratively instead of rejected. Kept for backwards compatibility
|
|
8
|
+
* in case third-party code references the class.
|
|
5
9
|
*/
|
|
6
10
|
export declare class MultiLevelEncodingError extends Error {
|
|
7
11
|
constructor();
|
|
8
12
|
}
|
|
9
13
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
14
|
+
* Decodes a pathname iteratively until stable, collapsing all levels of
|
|
15
|
+
* percent-encoding into a single canonical form. This prevents
|
|
16
|
+
* double/triple encoding from bypassing middleware authorization checks
|
|
17
|
+
* (CVE-2025-66202) — instead of rejecting multi-level encoding, we
|
|
18
|
+
* fully resolve it so middleware always sees the true decoded path.
|
|
13
19
|
*
|
|
14
|
-
* @param pathname - The pathname to
|
|
15
|
-
* @returns The decoded pathname
|
|
16
|
-
* @throws
|
|
17
|
-
*
|
|
20
|
+
* @param pathname - The pathname to decode
|
|
21
|
+
* @returns The fully decoded pathname
|
|
22
|
+
* @throws Error if the pathname contains invalid URL encoding that
|
|
23
|
+
* cannot be decoded at all (e.g., a bare `%` not followed by hex digits)
|
|
18
24
|
*/
|
|
19
25
|
export declare function validateAndDecodePathname(pathname: string): string;
|
|
@@ -4,19 +4,22 @@ class MultiLevelEncodingError extends Error {
|
|
|
4
4
|
this.name = "MultiLevelEncodingError";
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
|
-
const ENCODING_REGEX = /%25[0-9a-fA-F]{2}/;
|
|
8
7
|
function validateAndDecodePathname(pathname) {
|
|
9
|
-
if (ENCODING_REGEX.test(pathname)) {
|
|
10
|
-
throw new MultiLevelEncodingError();
|
|
11
|
-
}
|
|
12
8
|
let decoded;
|
|
13
9
|
try {
|
|
14
10
|
decoded = decodeURI(pathname);
|
|
15
11
|
} catch (_e) {
|
|
16
12
|
throw new Error("Invalid URL encoding");
|
|
17
13
|
}
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
let iterations = 0;
|
|
15
|
+
while (decoded !== pathname && iterations < 10) {
|
|
16
|
+
pathname = decoded;
|
|
17
|
+
try {
|
|
18
|
+
decoded = decodeURI(pathname);
|
|
19
|
+
} catch {
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
iterations++;
|
|
20
23
|
}
|
|
21
24
|
return decoded;
|
|
22
25
|
}
|
package/dist/i18n/index.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
appendForwardSlash,
|
|
3
|
+
joinPaths,
|
|
4
|
+
removeTrailingForwardSlash
|
|
5
|
+
} from "@astrojs/internal-helpers/path";
|
|
2
6
|
import { shouldAppendForwardSlash } from "../core/build/util.js";
|
|
3
7
|
import { REROUTE_DIRECTIVE_HEADER } from "../core/constants.js";
|
|
4
8
|
import { i18nNoLocaleFoundInPath, MissingLocale } from "../core/errors/errors-data.js";
|
|
@@ -55,7 +59,7 @@ function getLocaleRelativeUrl({
|
|
|
55
59
|
if (shouldAppendForwardSlash(trailingSlash, format)) {
|
|
56
60
|
relativePath = appendForwardSlash(joinPaths(...pathsToJoin));
|
|
57
61
|
} else {
|
|
58
|
-
relativePath = joinPaths(...pathsToJoin);
|
|
62
|
+
relativePath = removeTrailingForwardSlash(joinPaths(...pathsToJoin));
|
|
59
63
|
}
|
|
60
64
|
if (relativePath === "") {
|
|
61
65
|
return "/";
|
|
@@ -87,7 +87,7 @@ Did you forget to import the component or is it possible there is a typo?`);
|
|
|
87
87
|
_slots.default.push(child);
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
|
-
if ("slot" in child.props) {
|
|
90
|
+
if ("slot" in child.props && !isCustomElement) {
|
|
91
91
|
_slots[child.props.slot] = [..._slots[child.props.slot] ?? [], child];
|
|
92
92
|
delete child.props.slot;
|
|
93
93
|
return;
|
|
@@ -116,6 +116,7 @@ Did you forget to import the component or is it possible there is a typo?`);
|
|
|
116
116
|
const _slots = {
|
|
117
117
|
default: []
|
|
118
118
|
};
|
|
119
|
+
const isCustomElement = typeof vnode.type === "string" && vnode.type.includes("-");
|
|
119
120
|
extractSlots2(children);
|
|
120
121
|
for (const [key, value] of Object.entries(props)) {
|
|
121
122
|
if (value?.["$$slot"]) {
|
|
@@ -51,7 +51,8 @@ function renderAllHeadContent(result) {
|
|
|
51
51
|
const links = deduplicateElements(Array.from(result.links)).map(
|
|
52
52
|
(link) => renderElement("link", link, false)
|
|
53
53
|
);
|
|
54
|
-
|
|
54
|
+
const sep = result.compressHTML === true || result.compressHTML === "jsx" ? "" : "\n";
|
|
55
|
+
content += styles.join(sep) + links.join(sep) + scripts.join(sep);
|
|
55
56
|
content += result._metadata.extraHead.join("");
|
|
56
57
|
return markHTMLString(content);
|
|
57
58
|
}
|
|
@@ -6,6 +6,7 @@ const htmlBooleanAttributes = /^(?:allowfullscreen|async|autofocus|autoplay|chec
|
|
|
6
6
|
const AMPERSAND_REGEX = /&/g;
|
|
7
7
|
const DOUBLE_QUOTE_REGEX = /"/g;
|
|
8
8
|
const STATIC_DIRECTIVES = /* @__PURE__ */ new Set(["set:html", "set:text"]);
|
|
9
|
+
const INVALID_ATTR_NAME_CHAR = /[\s"'>/=]/;
|
|
9
10
|
const toIdent = (k) => k.trim().replace(/(?!^)\b\w|\s+|\W+/g, (match, index) => {
|
|
10
11
|
if (/\W/.test(match)) return "";
|
|
11
12
|
return index === 0 ? match : match.toUpperCase();
|
|
@@ -43,6 +44,9 @@ function addAttribute(value, key, shouldEscape = true, tagName = "") {
|
|
|
43
44
|
if (value == null) {
|
|
44
45
|
return "";
|
|
45
46
|
}
|
|
47
|
+
if (INVALID_ATTR_NAME_CHAR.test(key)) {
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
46
50
|
if (STATIC_DIRECTIVES.has(key)) {
|
|
47
51
|
console.warn(`[astro] The "${key}" directive cannot be applied dynamically at runtime. It will not be rendered as an attribute.
|
|
48
52
|
|
|
@@ -1348,7 +1348,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
|
|
|
1348
1348
|
* });
|
|
1349
1349
|
* ```
|
|
1350
1350
|
*
|
|
1351
|
-
* Learn more about customizing the request pipeline in the [advanced routing guide](https://docs.astro.build/en/guides/routing/#advanced-routing).
|
|
1351
|
+
* Learn more about customizing the request pipeline in the [advanced routing guide](https://v7.docs.astro.build/en/guides/routing/#advanced-routing).
|
|
1352
1352
|
*/
|
|
1353
1353
|
fetchFile?: string | null;
|
|
1354
1354
|
/**
|
|
@@ -1360,10 +1360,52 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
|
|
|
1360
1360
|
* @version 7.0.0
|
|
1361
1361
|
* @description
|
|
1362
1362
|
*
|
|
1363
|
-
* Configures
|
|
1363
|
+
* Configures how Astro logs messages during development and production.
|
|
1364
|
+
*
|
|
1365
|
+
* By default, Astro uses a built-in logger that outputs human-friendly logs to the console. You can customize this behavior by providing [your own logger handler](https://v7.docs.astro.build/en/reference/logger-reference/#custom-loggers) or by using one of the [built-in log handlers](https://v7.docs.astro.build/en/reference/logger-reference/#built-in-loggers):
|
|
1364
1366
|
*
|
|
1365
1367
|
* ```js
|
|
1366
1368
|
* // astro.config.mjs
|
|
1369
|
+
* import { defineConfig, logHandlers } from 'astro/config';
|
|
1370
|
+
*
|
|
1371
|
+
* export default defineConfig({
|
|
1372
|
+
* logger: logHandlers.json({ level: 'info' })
|
|
1373
|
+
* });
|
|
1374
|
+
* ```
|
|
1375
|
+
*
|
|
1376
|
+
* See [the logger API reference](https://v7.docs.astro.build/en/reference/logger-reference/) for more information.
|
|
1377
|
+
*/
|
|
1378
|
+
logger?: LoggerHandlerConfig;
|
|
1379
|
+
/**
|
|
1380
|
+
* @docs
|
|
1381
|
+
* @name logger.entrypoint
|
|
1382
|
+
* @type {string}
|
|
1383
|
+
* @version 7.0.0
|
|
1384
|
+
* @description
|
|
1385
|
+
*
|
|
1386
|
+
* The entrypoint of the log handler. This can be a path to a file in your project or an npm package:
|
|
1387
|
+
*
|
|
1388
|
+
* ```js title="astro.config.mjs"
|
|
1389
|
+
* import { defineConfig } from 'astro/config';
|
|
1390
|
+
*
|
|
1391
|
+
* export default defineConfig({
|
|
1392
|
+
* logger: {
|
|
1393
|
+
* entrypoint: "@org/astro-logger",
|
|
1394
|
+
* }
|
|
1395
|
+
* });
|
|
1396
|
+
* ```
|
|
1397
|
+
*/
|
|
1398
|
+
/**
|
|
1399
|
+
* @docs
|
|
1400
|
+
* @name logger.config
|
|
1401
|
+
* @type {Record<string, unknown> | undefined}
|
|
1402
|
+
* @version 7.0.0
|
|
1403
|
+
* @default `{}`
|
|
1404
|
+
* @description
|
|
1405
|
+
*
|
|
1406
|
+
* The configuration object for the log handler. The options depend on the configured logger.
|
|
1407
|
+
*
|
|
1408
|
+
* ```js title="astro.config.mjs"
|
|
1367
1409
|
* import { defineConfig } from 'astro/config';
|
|
1368
1410
|
*
|
|
1369
1411
|
* export default defineConfig({
|
|
@@ -1375,10 +1417,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
|
|
|
1375
1417
|
* }
|
|
1376
1418
|
* });
|
|
1377
1419
|
* ```
|
|
1378
|
-
*
|
|
1379
|
-
* See [the logger API reference](https://docs.astro.build/en/reference/logger-reference/) for more information.
|
|
1380
1420
|
*/
|
|
1381
|
-
logger?: LoggerHandlerConfig;
|
|
1382
1421
|
/**
|
|
1383
1422
|
* @docs
|
|
1384
1423
|
* @kind heading
|
|
@@ -2129,8 +2168,24 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
|
|
|
2129
2168
|
* @type {MarkdownProcessor}
|
|
2130
2169
|
* @version 6.4.0
|
|
2131
2170
|
* @description
|
|
2132
|
-
* Configures the Markdown processor used to render `.md` files. Defaults to `
|
|
2133
|
-
* `@astrojs/markdown-
|
|
2171
|
+
* Configures the Markdown processor used to render `.md` files. Defaults to `satteri()` from
|
|
2172
|
+
* `@astrojs/markdown-satteri`, Astro's native Markdown pipeline.
|
|
2173
|
+
*
|
|
2174
|
+
* ```js
|
|
2175
|
+
* // astro.config.mjs
|
|
2176
|
+
* import { defineConfig } from 'astro/config';
|
|
2177
|
+
* import { satteri } from '@astrojs/markdown-satteri';
|
|
2178
|
+
*
|
|
2179
|
+
* export default defineConfig({
|
|
2180
|
+
* markdown: {
|
|
2181
|
+
* processor: satteri({
|
|
2182
|
+
* features: { gfm: false },
|
|
2183
|
+
* }),
|
|
2184
|
+
* },
|
|
2185
|
+
* });
|
|
2186
|
+
* ```
|
|
2187
|
+
*
|
|
2188
|
+
* To keep the remark/rehype pipeline, install `@astrojs/markdown-remark` and pass `unified()`:
|
|
2134
2189
|
*
|
|
2135
2190
|
* ```js
|
|
2136
2191
|
* // astro.config.mjs
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { isRunnableDevEnvironment } from "vite";
|
|
2
2
|
import { VIRTUAL_PAGE_RESOLVED_MODULE_ID } from "../vite-plugin-pages/const.js";
|
|
3
|
+
import { RESOLVED_MODULE_DEV_CSS_PREFIX } from "../vite-plugin-css/const.js";
|
|
3
4
|
import { getDevCssModuleNameFromPageVirtualModuleName } from "../vite-plugin-css/util.js";
|
|
4
5
|
import { isAstroServerEnvironment } from "../environments.js";
|
|
5
6
|
const STYLE_EXT_REGEX = /\.(?:css|scss|sass|less|styl|pcss)$/i;
|
|
7
|
+
const RAW_QUERY_REGEX = /(?:\?|&)raw(?:&|$)/;
|
|
8
|
+
function hasStyleExtension(id) {
|
|
9
|
+
return STYLE_EXT_REGEX.test(id.split("?")[0]);
|
|
10
|
+
}
|
|
6
11
|
function isStyleModule(mod) {
|
|
7
|
-
if (mod.
|
|
8
|
-
if (mod.
|
|
9
|
-
|
|
10
|
-
if (STYLE_EXT_REGEX.test(idPath)) return true;
|
|
11
|
-
}
|
|
12
|
-
return false;
|
|
12
|
+
if (mod.id && RAW_QUERY_REGEX.test(mod.id) && hasStyleExtension(mod.id)) return false;
|
|
13
|
+
if (mod.file && hasStyleExtension(mod.file)) return true;
|
|
14
|
+
return mod.id ? hasStyleExtension(mod.id) : false;
|
|
13
15
|
}
|
|
14
16
|
function hmrReload() {
|
|
15
17
|
return {
|
|
@@ -65,6 +67,17 @@ function hmrReload() {
|
|
|
65
67
|
return [];
|
|
66
68
|
}
|
|
67
69
|
if (hasSkippedStyleModules) {
|
|
70
|
+
for (const [id, mod] of this.environment.moduleGraph.idToModuleMap) {
|
|
71
|
+
if (id.startsWith(RESOLVED_MODULE_DEV_CSS_PREFIX)) {
|
|
72
|
+
this.environment.moduleGraph.invalidateModule(mod, void 0, timestamp, true);
|
|
73
|
+
if (isRunnableDevEnvironment(this.environment)) {
|
|
74
|
+
const runnerMod = this.environment.runner.evaluatedModules.getModuleById(id);
|
|
75
|
+
if (runnerMod) {
|
|
76
|
+
this.environment.runner.evaluatedModules.invalidateModule(runnerMod);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
68
81
|
return [];
|
|
69
82
|
}
|
|
70
83
|
if (modules.length > 0) {
|
|
@@ -10,7 +10,10 @@ const rehypeSlots = ({ s }) => {
|
|
|
10
10
|
const end = node.position?.end.offset ?? 0;
|
|
11
11
|
const first = node.children.at(0) ?? node;
|
|
12
12
|
const last = node.children.at(-1) ?? node;
|
|
13
|
-
const text = file.value.slice(
|
|
13
|
+
const text = file.value.slice(
|
|
14
|
+
first.position?.start.offset ?? 0,
|
|
15
|
+
last.position?.end.offset ?? 0
|
|
16
|
+
).toString();
|
|
14
17
|
s.overwrite(
|
|
15
18
|
start,
|
|
16
19
|
end,
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import type { Plugin as VitePlugin } from 'vite';
|
|
2
2
|
import type { RoutesList } from '../types/astro.js';
|
|
3
|
+
import type { RouteData } from '../types/public/internal.js';
|
|
3
4
|
export declare const VIRTUAL_PAGES_MODULE_ID = "virtual:astro:pages";
|
|
4
5
|
interface PagesPluginOptions {
|
|
5
6
|
routesList: RoutesList;
|
|
6
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Filters routes for a specific build environment.
|
|
10
|
+
*
|
|
11
|
+
* Redirect target routes do not need special handling here: they already
|
|
12
|
+
* appear in the routes list with their own `prerender` flag and are
|
|
13
|
+
* included when it matches `isPrerender`. At SSR runtime, redirect
|
|
14
|
+
* responses are generated from route metadata alone (no component is
|
|
15
|
+
* loaded), so the target's component is never needed in the SSR page map.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getRoutesForEnvironment(routes: RouteData[], isPrerender: boolean): Set<RouteData>;
|
|
7
18
|
export declare function pluginPages({ routesList }: PagesPluginOptions): VitePlugin;
|
|
8
19
|
export {};
|
|
@@ -11,9 +11,6 @@ function getRoutesForEnvironment(routes, isPrerender) {
|
|
|
11
11
|
if (route.prerender === isPrerender) {
|
|
12
12
|
result.add(route);
|
|
13
13
|
}
|
|
14
|
-
if (route.redirectRoute) {
|
|
15
|
-
result.add(route.redirectRoute);
|
|
16
|
-
}
|
|
17
14
|
}
|
|
18
15
|
return result;
|
|
19
16
|
}
|
|
@@ -75,5 +72,6 @@ export { pageMap };`;
|
|
|
75
72
|
}
|
|
76
73
|
export {
|
|
77
74
|
VIRTUAL_PAGES_MODULE_ID,
|
|
75
|
+
getRoutesForEnvironment,
|
|
78
76
|
pluginPages
|
|
79
77
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro",
|
|
3
|
-
"version": "7.0.0-beta.
|
|
3
|
+
"version": "7.0.0-beta.4",
|
|
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",
|
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
"./astro-jsx": "./astro-jsx.d.ts",
|
|
40
40
|
"./tsconfigs/*.json": "./tsconfigs/*",
|
|
41
41
|
"./tsconfigs/*": "./tsconfigs/*.json",
|
|
42
|
-
"./jsx/rehype.js": "./dist/jsx/rehype.js",
|
|
43
42
|
"./jsx-runtime": {
|
|
44
43
|
"types": "./jsx-runtime.d.ts",
|
|
45
44
|
"default": "./dist/jsx-runtime/index.js"
|
|
@@ -167,12 +166,20 @@
|
|
|
167
166
|
"yargs-parser": "^22.0.0",
|
|
168
167
|
"zod": "^4.3.6",
|
|
169
168
|
"@astrojs/internal-helpers": "0.10.0",
|
|
170
|
-
"@astrojs/
|
|
171
|
-
"@astrojs/
|
|
169
|
+
"@astrojs/telemetry": "3.3.2",
|
|
170
|
+
"@astrojs/markdown-satteri": "0.3.1-beta.1"
|
|
172
171
|
},
|
|
173
172
|
"optionalDependencies": {
|
|
174
173
|
"sharp": "^0.34.0"
|
|
175
174
|
},
|
|
175
|
+
"peerDependencies": {
|
|
176
|
+
"@astrojs/markdown-remark": "7.2.0"
|
|
177
|
+
},
|
|
178
|
+
"peerDependenciesMeta": {
|
|
179
|
+
"@astrojs/markdown-remark": {
|
|
180
|
+
"optional": true
|
|
181
|
+
}
|
|
182
|
+
},
|
|
176
183
|
"devDependencies": {
|
|
177
184
|
"@playwright/test": "1.58.2",
|
|
178
185
|
"@types/aria-query": "^5.0.4",
|
|
@@ -189,8 +196,6 @@
|
|
|
189
196
|
"expect-type": "^1.3.0",
|
|
190
197
|
"fs-fixture": "^2.13.0",
|
|
191
198
|
"hono": "^4.12.14",
|
|
192
|
-
"mdast-util-mdx": "^3.0.0",
|
|
193
|
-
"mdast-util-mdx-jsx": "^3.2.0",
|
|
194
199
|
"node-mocks-http": "^1.17.2",
|
|
195
200
|
"parse-srcset": "^1.0.2",
|
|
196
201
|
"rehype-autolink-headings": "^7.1.0",
|
|
@@ -203,7 +208,8 @@
|
|
|
203
208
|
"unified": "^11.0.5",
|
|
204
209
|
"vitest": "^4.1.0",
|
|
205
210
|
"@astrojs/check": "0.9.9",
|
|
206
|
-
"astro-scripts": "0.0.14"
|
|
211
|
+
"astro-scripts": "0.0.14",
|
|
212
|
+
"@astrojs/markdown-remark": "7.2.0"
|
|
207
213
|
},
|
|
208
214
|
"engines": {
|
|
209
215
|
"node": ">=22.12.0",
|
package/dist/jsx/rehype.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { RehypePlugin } from '@astrojs/internal-helpers/markdown';
|
|
2
|
-
import type { VFile } from 'vfile';
|
|
3
|
-
import type { PluginMetadata } from '../vite-plugin-astro/types.js';
|
|
4
|
-
export declare const rehypeAnalyzeAstroMetadata: RehypePlugin;
|
|
5
|
-
export declare function getAstroMetadata(file: VFile): PluginMetadata["astro"] | undefined;
|
package/dist/jsx/rehype.js
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import { visit } from "unist-util-visit";
|
|
2
|
-
import { AstroError } from "../core/errors/errors.js";
|
|
3
|
-
import { AstroErrorData } from "../core/errors/index.js";
|
|
4
|
-
import { resolvePath } from "../core/viteUtils.js";
|
|
5
|
-
import { createDefaultAstroMetadata } from "../vite-plugin-astro/metadata.js";
|
|
6
|
-
const ClientOnlyPlaceholder = "astro-client-only";
|
|
7
|
-
const rehypeAnalyzeAstroMetadata = () => {
|
|
8
|
-
return (tree, file) => {
|
|
9
|
-
const metadata = createDefaultAstroMetadata();
|
|
10
|
-
const imports = parseImports(tree.children);
|
|
11
|
-
visit(tree, (node) => {
|
|
12
|
-
if (node.type !== "mdxJsxFlowElement" && node.type !== "mdxJsxTextElement") return;
|
|
13
|
-
const tagName = node.name;
|
|
14
|
-
if (!tagName || !isComponent(tagName) || !(hasClientDirective(node) || hasServerDeferDirective(node)))
|
|
15
|
-
return;
|
|
16
|
-
const matchedImport = findMatchingImport(tagName, imports);
|
|
17
|
-
if (!matchedImport) {
|
|
18
|
-
throw new AstroError({
|
|
19
|
-
...AstroErrorData.NoMatchingImport,
|
|
20
|
-
message: AstroErrorData.NoMatchingImport.message(node.name)
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
if (matchedImport.path.endsWith(".astro")) {
|
|
24
|
-
const clientAttribute = node.attributes.find(
|
|
25
|
-
(attr) => attr.type === "mdxJsxAttribute" && attr.name.startsWith("client:")
|
|
26
|
-
);
|
|
27
|
-
if (clientAttribute) {
|
|
28
|
-
console.warn(
|
|
29
|
-
`You are attempting to render <${node.name} ${clientAttribute.name} />, but ${node.name} is an Astro component. Astro components do not render in the client and should not have a hydration directive. Please use a framework component for client rendering.`
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
const resolvedPath = resolvePath(matchedImport.path, file.path);
|
|
34
|
-
if (hasClientOnlyDirective(node)) {
|
|
35
|
-
metadata.clientOnlyComponents.push({
|
|
36
|
-
exportName: matchedImport.name,
|
|
37
|
-
localName: "",
|
|
38
|
-
specifier: tagName,
|
|
39
|
-
resolvedPath
|
|
40
|
-
});
|
|
41
|
-
addClientOnlyMetadata(node, matchedImport, resolvedPath);
|
|
42
|
-
} else if (hasClientDirective(node)) {
|
|
43
|
-
metadata.hydratedComponents.push({
|
|
44
|
-
exportName: "*",
|
|
45
|
-
localName: "",
|
|
46
|
-
specifier: tagName,
|
|
47
|
-
resolvedPath
|
|
48
|
-
});
|
|
49
|
-
addClientMetadata(node, matchedImport, resolvedPath);
|
|
50
|
-
} else if (hasServerDeferDirective(node)) {
|
|
51
|
-
metadata.serverComponents.push({
|
|
52
|
-
exportName: matchedImport.name,
|
|
53
|
-
localName: tagName,
|
|
54
|
-
specifier: matchedImport.path,
|
|
55
|
-
resolvedPath
|
|
56
|
-
});
|
|
57
|
-
addServerDeferMetadata(node, matchedImport, resolvedPath);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
file.data.__astroMetadata = metadata;
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
function getAstroMetadata(file) {
|
|
64
|
-
return file.data.__astroMetadata;
|
|
65
|
-
}
|
|
66
|
-
function parseImports(children) {
|
|
67
|
-
const imports = /* @__PURE__ */ new Map();
|
|
68
|
-
for (const child of children) {
|
|
69
|
-
if (child.type !== "mdxjsEsm") continue;
|
|
70
|
-
const body = child.data?.estree?.body;
|
|
71
|
-
if (!body) continue;
|
|
72
|
-
for (const ast of body) {
|
|
73
|
-
if (ast.type !== "ImportDeclaration") continue;
|
|
74
|
-
const source = ast.source.value;
|
|
75
|
-
const specs = ast.specifiers.map((spec) => {
|
|
76
|
-
switch (spec.type) {
|
|
77
|
-
case "ImportDefaultSpecifier":
|
|
78
|
-
return { local: spec.local.name, imported: "default" };
|
|
79
|
-
case "ImportNamespaceSpecifier":
|
|
80
|
-
return { local: spec.local.name, imported: "*" };
|
|
81
|
-
case "ImportSpecifier": {
|
|
82
|
-
return {
|
|
83
|
-
local: spec.local.name,
|
|
84
|
-
imported: spec.imported.type === "Identifier" ? spec.imported.name : String(spec.imported.value)
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
default:
|
|
88
|
-
throw new Error("Unknown import declaration specifier: " + spec);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
let specSet = imports.get(source);
|
|
92
|
-
if (!specSet) {
|
|
93
|
-
specSet = /* @__PURE__ */ new Set();
|
|
94
|
-
imports.set(source, specSet);
|
|
95
|
-
}
|
|
96
|
-
for (const spec of specs) {
|
|
97
|
-
specSet.add(spec);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return imports;
|
|
102
|
-
}
|
|
103
|
-
function isComponent(tagName) {
|
|
104
|
-
return tagName[0] && tagName[0].toLowerCase() !== tagName[0] || tagName.includes(".") || /[^a-zA-Z]/.test(tagName[0]);
|
|
105
|
-
}
|
|
106
|
-
function hasClientDirective(node) {
|
|
107
|
-
return node.attributes.some(
|
|
108
|
-
(attr) => attr.type === "mdxJsxAttribute" && attr.name.startsWith("client:")
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
function hasServerDeferDirective(node) {
|
|
112
|
-
return node.attributes.some(
|
|
113
|
-
(attr) => attr.type === "mdxJsxAttribute" && attr.name === "server:defer"
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
function hasClientOnlyDirective(node) {
|
|
117
|
-
return node.attributes.some(
|
|
118
|
-
(attr) => attr.type === "mdxJsxAttribute" && attr.name === "client:only"
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
function findMatchingImport(tagName, imports) {
|
|
122
|
-
const tagSpecifier = tagName.split(".")[0];
|
|
123
|
-
for (const [source, specs] of imports) {
|
|
124
|
-
for (const { imported, local } of specs) {
|
|
125
|
-
if (local === tagSpecifier) {
|
|
126
|
-
if (tagSpecifier !== tagName) {
|
|
127
|
-
switch (imported) {
|
|
128
|
-
// Namespace import: "<buttons.Foo.Bar />" => name: "Foo.Bar"
|
|
129
|
-
case "*": {
|
|
130
|
-
const accessPath = tagName.slice(tagSpecifier.length + 1);
|
|
131
|
-
return { name: accessPath, path: source };
|
|
132
|
-
}
|
|
133
|
-
// Default import: "<buttons.Foo.Bar />" => name: "default.Foo.Bar"
|
|
134
|
-
case "default": {
|
|
135
|
-
const accessPath = tagName.slice(tagSpecifier.length + 1);
|
|
136
|
-
return { name: `default.${accessPath}`, path: source };
|
|
137
|
-
}
|
|
138
|
-
// Named import: "<buttons.Foo.Bar />" => name: "buttons.Foo.Bar"
|
|
139
|
-
default: {
|
|
140
|
-
return { name: tagName, path: source };
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return { name: imported, path: source };
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
function addClientMetadata(node, meta, resolvedPath) {
|
|
150
|
-
const attributeNames = node.attributes.map((attr) => attr.type === "mdxJsxAttribute" ? attr.name : null).filter(Boolean);
|
|
151
|
-
if (!attributeNames.includes("client:component-path")) {
|
|
152
|
-
node.attributes.push({
|
|
153
|
-
type: "mdxJsxAttribute",
|
|
154
|
-
name: "client:component-path",
|
|
155
|
-
value: resolvedPath
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
if (!attributeNames.includes("client:component-export")) {
|
|
159
|
-
if (meta.name === "*") {
|
|
160
|
-
meta.name = node.name.split(".").slice(1).join(".");
|
|
161
|
-
}
|
|
162
|
-
node.attributes.push({
|
|
163
|
-
type: "mdxJsxAttribute",
|
|
164
|
-
name: "client:component-export",
|
|
165
|
-
value: meta.name
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
if (!attributeNames.includes("client:component-hydration")) {
|
|
169
|
-
node.attributes.push({
|
|
170
|
-
type: "mdxJsxAttribute",
|
|
171
|
-
name: "client:component-hydration",
|
|
172
|
-
value: null
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
function addClientOnlyMetadata(node, meta, resolvedPath) {
|
|
177
|
-
const attributeNames = node.attributes.map((attr) => attr.type === "mdxJsxAttribute" ? attr.name : null).filter(Boolean);
|
|
178
|
-
if (!attributeNames.includes("client:display-name")) {
|
|
179
|
-
node.attributes.push({
|
|
180
|
-
type: "mdxJsxAttribute",
|
|
181
|
-
name: "client:display-name",
|
|
182
|
-
value: node.name
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
if (!attributeNames.includes("client:component-path")) {
|
|
186
|
-
node.attributes.push({
|
|
187
|
-
type: "mdxJsxAttribute",
|
|
188
|
-
name: "client:component-path",
|
|
189
|
-
value: resolvedPath
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
if (!attributeNames.includes("client:component-export")) {
|
|
193
|
-
if (meta.name === "*") {
|
|
194
|
-
meta.name = node.name.split(".").slice(1).join(".");
|
|
195
|
-
}
|
|
196
|
-
node.attributes.push({
|
|
197
|
-
type: "mdxJsxAttribute",
|
|
198
|
-
name: "client:component-export",
|
|
199
|
-
value: meta.name
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
if (!attributeNames.includes("client:component-hydration")) {
|
|
203
|
-
node.attributes.push({
|
|
204
|
-
type: "mdxJsxAttribute",
|
|
205
|
-
name: "client:component-hydration",
|
|
206
|
-
value: null
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
node.name = ClientOnlyPlaceholder;
|
|
210
|
-
}
|
|
211
|
-
function addServerDeferMetadata(node, meta, resolvedPath) {
|
|
212
|
-
const attributeNames = node.attributes.map((attr) => attr.type === "mdxJsxAttribute" ? attr.name : null).filter(Boolean);
|
|
213
|
-
if (!attributeNames.includes("server:component-directive")) {
|
|
214
|
-
node.attributes.push({
|
|
215
|
-
type: "mdxJsxAttribute",
|
|
216
|
-
name: "server:component-directive",
|
|
217
|
-
value: "server:defer"
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
if (!attributeNames.includes("server:component-path")) {
|
|
221
|
-
node.attributes.push({
|
|
222
|
-
type: "mdxJsxAttribute",
|
|
223
|
-
name: "server:component-path",
|
|
224
|
-
value: resolvedPath
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
if (!attributeNames.includes("server:component-export")) {
|
|
228
|
-
if (meta.name === "*") {
|
|
229
|
-
meta.name = node.name.split(".").slice(1).join(".");
|
|
230
|
-
}
|
|
231
|
-
node.attributes.push({
|
|
232
|
-
type: "mdxJsxAttribute",
|
|
233
|
-
name: "server:component-export",
|
|
234
|
-
value: meta.name
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
export {
|
|
239
|
-
getAstroMetadata,
|
|
240
|
-
rehypeAnalyzeAstroMetadata
|
|
241
|
-
};
|