astro 7.0.4 → 7.0.5
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/cli/dev/background.js +4 -2
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/content/content-layer.js +3 -3
- package/dist/core/build/plugins/index.js +2 -0
- package/dist/core/build/plugins/plugin-css-target-lowering.d.ts +14 -0
- package/dist/core/build/plugins/plugin-css-target-lowering.js +82 -0
- package/dist/core/constants.js +1 -1
- package/dist/core/create-vite.js +2 -0
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/fetch/fetch-state.js +2 -0
- package/dist/core/head-propagation/buffer.d.ts +14 -3
- package/dist/core/head-propagation/buffer.js +17 -7
- package/dist/core/messages/runtime.js +1 -1
- package/dist/prefetch/index.js +10 -0
- package/dist/runtime/server/render/astro/instance.js +3 -0
- package/dist/runtime/server/render/page.js +7 -2
- package/dist/runtime/server/render/server-islands-shared.d.ts +1 -0
- package/dist/runtime/server/render/server-islands-shared.js +4 -0
- package/dist/runtime/server/render/server-islands.js +3 -2
- package/dist/template/4xx.js +1 -1
- package/dist/transitions/swap-functions.js +9 -2
- package/dist/types/public/internal.d.ts +14 -0
- package/dist/vite-plugin-astro/index.js +3 -1
- package/dist/vite-plugin-config-alias/index.d.ts +19 -1
- package/dist/vite-plugin-config-alias/index.js +6 -5
- package/dist/vite-plugin-html/transform/index.js +16 -7
- package/dist/vite-plugin-html/transform/slots.d.ts +10 -7
- package/dist/vite-plugin-html/transform/slots.js +19 -26
- package/dist/vite-plugin-html/transform/utils.d.ts +0 -3
- package/dist/vite-plugin-html/transform/utils.js +1 -20
- package/package.json +1 -6
- package/dist/vite-plugin-html/transform/escape.d.ts +0 -7
- package/dist/vite-plugin-html/transform/escape.js +0 -30
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { existsSync, mkdirSync, openSync } from "node:fs";
|
|
3
|
-
import {
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
4
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
6
|
import {
|
|
6
7
|
checkExistingServer,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
GRACEFUL_SHUTDOWN_TIMEOUT
|
|
12
13
|
} from "../../core/dev/lockfile.js";
|
|
13
14
|
import { resolveRoot } from "../../core/config/config.js";
|
|
15
|
+
const require2 = createRequire(import.meta.url);
|
|
14
16
|
function formatBackgroundOutput(result) {
|
|
15
17
|
return JSON.stringify(result);
|
|
16
18
|
}
|
|
@@ -76,7 +78,7 @@ async function background({
|
|
|
76
78
|
}
|
|
77
79
|
const logFd = openSync(logFilePath, "w");
|
|
78
80
|
const rootPath = fileURLToPath(root);
|
|
79
|
-
const astroBin = resolve(
|
|
81
|
+
const astroBin = resolve(dirname(require2.resolve("astro/package.json")), "bin", "astro.mjs");
|
|
80
82
|
const child = spawn(process.execPath, [astroBin, ...args], {
|
|
81
83
|
detached: true,
|
|
82
84
|
stdio: ["ignore", logFd, logFd],
|
|
@@ -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.5") {
|
|
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.5") {
|
|
209
|
+
this.#store.metaStore().set("astro-version", "7.0.5");
|
|
210
210
|
}
|
|
211
211
|
if (currentConfigDigest) {
|
|
212
212
|
this.#store.metaStore().set("content-config-digest", currentConfigDigest);
|
|
@@ -3,6 +3,7 @@ import { astroHeadBuildPlugin } from "../../../vite-plugin-head/index.js";
|
|
|
3
3
|
import { pluginAnalyzer } from "./plugin-analyzer.js";
|
|
4
4
|
import { pluginComponentEntry } from "./plugin-component-entry.js";
|
|
5
5
|
import { pluginCSS } from "./plugin-css.js";
|
|
6
|
+
import { pluginCssTargetLowering } from "./plugin-css-target-lowering.js";
|
|
6
7
|
import { pluginInternals } from "./plugin-internals.js";
|
|
7
8
|
import { pluginMiddleware } from "./plugin-middleware.js";
|
|
8
9
|
import { pluginPrerender } from "./plugin-prerender.js";
|
|
@@ -18,6 +19,7 @@ function getAllBuildPlugins(internals, options) {
|
|
|
18
19
|
pluginInternals(options, internals),
|
|
19
20
|
pluginMiddleware(options, internals),
|
|
20
21
|
vitePluginActionsBuild(options, internals),
|
|
22
|
+
pluginCssTargetLowering(),
|
|
21
23
|
...pluginCSS(options, internals),
|
|
22
24
|
astroHeadBuildPlugin(internals),
|
|
23
25
|
pluginPrerender(options, internals),
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Plugin as VitePlugin } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* Vite plugin that applies CSS target lowering without minification.
|
|
4
|
+
*
|
|
5
|
+
* Vite's `finalizeCss` only applies CSS target lowering (via lightningcss) when
|
|
6
|
+
* `config.build.cssMinify` is truthy, because target lowering and minification
|
|
7
|
+
* are coupled in `minifyCSS`. When users set `minify: false`, Astro forces
|
|
8
|
+
* `cssMinify: false`, which disables target lowering for all CSS.
|
|
9
|
+
*
|
|
10
|
+
* This plugin fills that gap: when `cssMinify` is falsy and a CSS target is
|
|
11
|
+
* configured, it runs lightningcss with `minify: false` on all CSS assets in
|
|
12
|
+
* `generateBundle`, applying target lowering without minification.
|
|
13
|
+
*/
|
|
14
|
+
export declare function pluginCssTargetLowering(): VitePlugin;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
const BROWSER_MAP = {
|
|
3
|
+
chrome: "chrome",
|
|
4
|
+
edge: "edge",
|
|
5
|
+
firefox: "firefox",
|
|
6
|
+
hermes: false,
|
|
7
|
+
ie: "ie",
|
|
8
|
+
ios: "ios_saf",
|
|
9
|
+
node: false,
|
|
10
|
+
opera: "opera",
|
|
11
|
+
rhino: false,
|
|
12
|
+
safari: "safari"
|
|
13
|
+
};
|
|
14
|
+
const VERSION_RE = /\d/;
|
|
15
|
+
function convertTargets(esbuildTarget) {
|
|
16
|
+
if (!esbuildTarget) return {};
|
|
17
|
+
const targets = {};
|
|
18
|
+
const entries = Array.isArray(esbuildTarget) ? esbuildTarget : [esbuildTarget];
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
if (entry === "esnext") continue;
|
|
21
|
+
const index = entry.search(VERSION_RE);
|
|
22
|
+
if (index >= 0) {
|
|
23
|
+
const browserName = entry.slice(0, index);
|
|
24
|
+
const browser = BROWSER_MAP[browserName];
|
|
25
|
+
if (browser === false) continue;
|
|
26
|
+
if (browser) {
|
|
27
|
+
const [major, minor = 0] = entry.slice(index).split(".").map((v) => Number.parseInt(v, 10));
|
|
28
|
+
if (!isNaN(major) && !isNaN(minor)) {
|
|
29
|
+
const version = major << 16 | minor << 8;
|
|
30
|
+
if (!targets[browser] || version < targets[browser]) {
|
|
31
|
+
targets[browser] = version;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return targets;
|
|
38
|
+
}
|
|
39
|
+
function pluginCssTargetLowering() {
|
|
40
|
+
let resolvedConfig;
|
|
41
|
+
return {
|
|
42
|
+
name: "astro:css-target-lowering",
|
|
43
|
+
enforce: "post",
|
|
44
|
+
configResolved(config) {
|
|
45
|
+
resolvedConfig = config;
|
|
46
|
+
},
|
|
47
|
+
async generateBundle(_outputOptions, bundle) {
|
|
48
|
+
if (resolvedConfig.build.cssMinify) return;
|
|
49
|
+
const cssTarget = resolvedConfig.build.cssTarget;
|
|
50
|
+
if (!cssTarget) return;
|
|
51
|
+
const targets = convertTargets(cssTarget);
|
|
52
|
+
if (Object.keys(targets).length === 0) return;
|
|
53
|
+
let lcssTransform;
|
|
54
|
+
try {
|
|
55
|
+
const requireFromVite = createRequire(import.meta.resolve("vite"));
|
|
56
|
+
lcssTransform = requireFromVite("lightningcss").transform;
|
|
57
|
+
} catch {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
for (const [, asset] of Object.entries(bundle)) {
|
|
61
|
+
if (asset.type !== "asset") continue;
|
|
62
|
+
if (!asset.fileName.endsWith(".css")) continue;
|
|
63
|
+
if (typeof asset.source !== "string") continue;
|
|
64
|
+
try {
|
|
65
|
+
const result = lcssTransform({
|
|
66
|
+
...resolvedConfig.css?.lightningcss,
|
|
67
|
+
targets,
|
|
68
|
+
cssModules: void 0,
|
|
69
|
+
filename: asset.fileName,
|
|
70
|
+
code: Buffer.from(asset.source),
|
|
71
|
+
minify: false
|
|
72
|
+
});
|
|
73
|
+
asset.source = new TextDecoder().decode(result.code);
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export {
|
|
81
|
+
pluginCssTargetLowering
|
|
82
|
+
};
|
package/dist/core/constants.js
CHANGED
package/dist/core/create-vite.js
CHANGED
|
@@ -201,6 +201,8 @@ async function createVite(commandConfig, { settings, logger, mode, command, fs =
|
|
|
201
201
|
}
|
|
202
202
|
},
|
|
203
203
|
resolve: {
|
|
204
|
+
// Vite's native tsconfig path resolution; see configAliasVitePlugin for the deprecated fallback.
|
|
205
|
+
tsconfigPaths: true,
|
|
204
206
|
alias: [
|
|
205
207
|
{
|
|
206
208
|
// This is needed for Deno compatibility, as the non-browser version
|
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.5";
|
|
30
30
|
const isPrerelease = currentVersion.includes("-");
|
|
31
31
|
if (!isPrerelease) {
|
|
32
32
|
try {
|
|
@@ -297,6 +297,8 @@ class FetchState {
|
|
|
297
297
|
extraStyleHashes,
|
|
298
298
|
extraScriptHashes,
|
|
299
299
|
propagators: /* @__PURE__ */ new Set(),
|
|
300
|
+
routeHasPropagation: false,
|
|
301
|
+
pendingSlotEvaluations: [],
|
|
300
302
|
templateDepth: 0
|
|
301
303
|
},
|
|
302
304
|
cspDestination: manifest.csp?.cspDestination ?? (routeData.prerender ? "meta" : "header"),
|
|
@@ -3,10 +3,21 @@ export interface HeadPropagator {
|
|
|
3
3
|
init(result: SSRResult): unknown | Promise<unknown>;
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
* Runs all registered propagators and collects
|
|
6
|
+
* Runs all registered propagators and collects the head HTML they emit.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Components with head content are discovered as we go. Initializing one
|
|
9
|
+
* propagator can register more of them: a component marked `in-tree` renders
|
|
10
|
+
* its children, and one of those children may be a `self` component that emits
|
|
11
|
+
* styles. Slots add a second way to find them — a slot whose markup contains an
|
|
12
|
+
* `await` only reaches the components after that `await` once it resolves, so
|
|
13
|
+
* we also wait for those pending slot pre-renders. We keep initializing
|
|
14
|
+
* propagators and waiting on slots until no new ones appear.
|
|
15
|
+
*
|
|
16
|
+
* Propagators are tracked in a `seen` set rather than read through a single
|
|
17
|
+
* live `Set` iterator. A propagator can be registered after we have already
|
|
18
|
+
* iterated to the end of the set (e.g. once a slot's `await` resolves), and a
|
|
19
|
+
* `Set` iterator that has reported `done` would never report those late
|
|
20
|
+
* additions.
|
|
10
21
|
*
|
|
11
22
|
* @example
|
|
12
23
|
* If a layout initializes and discovers a nested component that also emits
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
async function collectPropagatedHeadParts(input) {
|
|
2
2
|
const collectedHeadParts = [];
|
|
3
|
-
const
|
|
3
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4
|
+
const pendingSlotEvaluations = input.result._metadata?.pendingSlotEvaluations ?? [];
|
|
4
5
|
while (true) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
if (pendingSlotEvaluations.length > 0) {
|
|
7
|
+
const batch = pendingSlotEvaluations.splice(0, pendingSlotEvaluations.length);
|
|
8
|
+
await Promise.all(batch);
|
|
9
|
+
continue;
|
|
8
10
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
let progressed = false;
|
|
12
|
+
for (const propagator of input.propagators) {
|
|
13
|
+
if (seen.has(propagator)) continue;
|
|
14
|
+
seen.add(propagator);
|
|
15
|
+
progressed = true;
|
|
16
|
+
const returnValue = await propagator.init(input.result);
|
|
17
|
+
if (input.isHeadAndContent(returnValue) && returnValue.head) {
|
|
18
|
+
collectedHeadParts.push(returnValue.head);
|
|
19
|
+
}
|
|
20
|
+
break;
|
|
12
21
|
}
|
|
22
|
+
if (!progressed) break;
|
|
13
23
|
}
|
|
14
24
|
return collectedHeadParts;
|
|
15
25
|
}
|
package/dist/prefetch/index.js
CHANGED
|
@@ -191,6 +191,16 @@ function onPageLoad(cb) {
|
|
|
191
191
|
}
|
|
192
192
|
cb();
|
|
193
193
|
});
|
|
194
|
+
new MutationObserver((mutations) => {
|
|
195
|
+
for (const mutation of mutations) {
|
|
196
|
+
for (const node of mutation.addedNodes) {
|
|
197
|
+
if (node instanceof Element && (node.tagName === "A" || node.querySelector?.("a"))) {
|
|
198
|
+
cb();
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}).observe(document.body, { childList: true, subtree: true });
|
|
194
204
|
}
|
|
195
205
|
function appendSpeculationRules(url, eagerness) {
|
|
196
206
|
const script = document.createElement("script");
|
|
@@ -18,6 +18,9 @@ class AstroComponentInstance {
|
|
|
18
18
|
for (const name in slots) {
|
|
19
19
|
let didRender = false;
|
|
20
20
|
let value = slots[name](result);
|
|
21
|
+
if (result._metadata.routeHasPropagation && isPromise(value)) {
|
|
22
|
+
result._metadata.pendingSlotEvaluations.push(value);
|
|
23
|
+
}
|
|
21
24
|
this.slotValues[name] = () => {
|
|
22
25
|
if (!didRender) {
|
|
23
26
|
didRender = true;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isRoute404, isRoute500 } from "../../../core/routing/internal/route-errors.js";
|
|
2
|
+
import { isPropagatingHint } from "../../../core/head-propagation/resolver.js";
|
|
2
3
|
import { renderToAsyncIterable, renderToReadableStream, renderToString } from "./astro/render.js";
|
|
3
4
|
import { encoder } from "./common.js";
|
|
4
5
|
import { renderComponentToString } from "./component.js";
|
|
@@ -7,7 +8,9 @@ import { isDeno, isNode } from "./util.js";
|
|
|
7
8
|
import { isAstroComponentFactory } from "./astro/factory.js";
|
|
8
9
|
async function renderPage(result, componentFactory, props, children, streaming, route) {
|
|
9
10
|
if (!isAstroComponentFactory(componentFactory)) {
|
|
10
|
-
|
|
11
|
+
const nonAstroMeta = result.componentMetadata.get(componentFactory.moduleId);
|
|
12
|
+
result._metadata.headInTree = nonAstroMeta?.containsHead ?? false;
|
|
13
|
+
result._metadata.routeHasPropagation = isPropagatingHint(nonAstroMeta?.propagation ?? "none");
|
|
11
14
|
const pageProps = { ...props ?? {}, "server:root": true };
|
|
12
15
|
const str = await renderComponentToString(
|
|
13
16
|
result,
|
|
@@ -31,7 +34,9 @@ async function renderPage(result, componentFactory, props, children, streaming,
|
|
|
31
34
|
status: result.response.status
|
|
32
35
|
});
|
|
33
36
|
}
|
|
34
|
-
|
|
37
|
+
const pageMeta = result.componentMetadata.get(componentFactory.moduleId);
|
|
38
|
+
result._metadata.headInTree = pageMeta?.containsHead ?? false;
|
|
39
|
+
result._metadata.routeHasPropagation = isPropagatingHint(pageMeta?.propagation ?? "none");
|
|
35
40
|
let body;
|
|
36
41
|
if (streaming) {
|
|
37
42
|
if (isNode && !isDeno) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SERVER_ISLAND_START = "[if astro]>server-island-start<![endif]";
|
|
@@ -3,6 +3,7 @@ import { markHTMLString, stringifyForScript } from "../escape.js";
|
|
|
3
3
|
import { renderChild } from "./any.js";
|
|
4
4
|
import { createThinHead } from "./astro/head-and-content.js";
|
|
5
5
|
import { createRenderInstruction } from "./instruction.js";
|
|
6
|
+
import { SERVER_ISLAND_START } from "./server-islands-shared.js";
|
|
6
7
|
import { renderSlotToString } from "./slot.js";
|
|
7
8
|
const internalProps = /* @__PURE__ */ new Set([
|
|
8
9
|
"server:component-path",
|
|
@@ -56,7 +57,7 @@ class ServerIslandComponent {
|
|
|
56
57
|
const hostId = await this.getHostId();
|
|
57
58
|
const islandContent = await this.getIslandContent();
|
|
58
59
|
destination.write(createRenderInstruction({ type: "server-island-runtime" }));
|
|
59
|
-
destination.write(
|
|
60
|
+
destination.write(`<!--${SERVER_ISLAND_START}-->`);
|
|
60
61
|
for (const name in this.slots) {
|
|
61
62
|
if (name === "fallback") {
|
|
62
63
|
await renderChild(destination, this.slots.fallback(this.result));
|
|
@@ -186,7 +187,7 @@ const SERVER_ISLAND_REPLACER = markHTMLString(
|
|
|
186
187
|
// Load the HTML before modifying the DOM in case of errors
|
|
187
188
|
let html = await r.text();
|
|
188
189
|
// Remove any placeholder content before the island script
|
|
189
|
-
while (s.previousSibling && s.previousSibling.nodeType !== 8 && s.previousSibling.data !== '
|
|
190
|
+
while (s.previousSibling && s.previousSibling.nodeType !== 8 && s.previousSibling.data !== '${SERVER_ISLAND_START}')
|
|
190
191
|
s.previousSibling.remove();
|
|
191
192
|
s.previousSibling?.remove();
|
|
192
193
|
// Insert the new HTML
|
package/dist/template/4xx.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SERVER_ISLAND_START } from "../runtime/server/render/server-islands-shared.js";
|
|
1
2
|
const PERSIST_ATTR = "data-astro-transition-persist";
|
|
2
3
|
const NON_OVERRIDABLE_ASTRO_ATTRS = ["data-astro-transition", "data-astro-transition-fallback"];
|
|
3
4
|
const knownVueScopedStyles = /* @__PURE__ */ new Map();
|
|
@@ -29,6 +30,11 @@ function swapRootAttributes(newDoc) {
|
|
|
29
30
|
);
|
|
30
31
|
}
|
|
31
32
|
function swapHeadElements(doc) {
|
|
33
|
+
const relevantNodes = (parent, what = "both") => [
|
|
34
|
+
...[...parent.childNodes].filter(
|
|
35
|
+
(child) => child.nodeType === 8 && child.textContent === SERVER_ISLAND_START || what === "both" && child.nodeType === 1
|
|
36
|
+
)
|
|
37
|
+
];
|
|
32
38
|
for (const el of Array.from(document.head.children)) {
|
|
33
39
|
const newEl = persistedHeadElement(el, doc);
|
|
34
40
|
if (newEl) {
|
|
@@ -41,12 +47,13 @@ function swapHeadElements(doc) {
|
|
|
41
47
|
el.remove();
|
|
42
48
|
}
|
|
43
49
|
}
|
|
50
|
+
relevantNodes(document.head, "commentsOnly").forEach((node) => node.remove());
|
|
44
51
|
if (import.meta.env.DEV) {
|
|
45
|
-
|
|
52
|
+
relevantNodes(doc.head).forEach((child) => {
|
|
46
53
|
document.head.append(knownVueScopedStyles.get(child.dataset?.viteDevId) || child);
|
|
47
54
|
});
|
|
48
55
|
} else {
|
|
49
|
-
document.head.append(...doc.head
|
|
56
|
+
document.head.append(...relevantNodes(doc.head));
|
|
50
57
|
}
|
|
51
58
|
}
|
|
52
59
|
function swapBodyElement(newElement, oldElement) {
|
|
@@ -280,6 +280,20 @@ export interface SSRMetadata {
|
|
|
280
280
|
extraStyleHashes: string[];
|
|
281
281
|
extraScriptHashes: string[];
|
|
282
282
|
propagators: Set<AstroComponentInstance | ServerIslandComponent>;
|
|
283
|
+
/**
|
|
284
|
+
* `true` when the page being rendered is on a head-propagation path (its
|
|
285
|
+
* component metadata hint is `in-tree`/`self`). Only then do we await async
|
|
286
|
+
* slot pre-renders before collecting head content, so that propagating
|
|
287
|
+
* components hidden behind an `await` in slot markup are discovered in time.
|
|
288
|
+
* Pages with no propagation keep streaming without paying that cost.
|
|
289
|
+
*/
|
|
290
|
+
routeHasPropagation: boolean;
|
|
291
|
+
/**
|
|
292
|
+
* Promises from async slot pre-renders that may still need to register
|
|
293
|
+
* propagating components. Drained by `collectPropagatedHeadParts` before
|
|
294
|
+
* head content is flushed. Only populated when `routeHasPropagation` is true.
|
|
295
|
+
*/
|
|
296
|
+
pendingSlotEvaluations: Promise<unknown>[];
|
|
283
297
|
/**
|
|
284
298
|
* Tracks nesting depth of HTML `<template>` elements during rendering.
|
|
285
299
|
* Scripts rendered inside `<template>` tags should not be deduplicated,
|
|
@@ -55,7 +55,9 @@ function astro({ settings, logger }) {
|
|
|
55
55
|
viteConfig.resolve.conditions = [...defaultServerConditions];
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
viteConfig.resolve.conditions.
|
|
58
|
+
if (!viteConfig.resolve.conditions.includes("astro")) {
|
|
59
|
+
viteConfig.resolve.conditions.push("astro");
|
|
60
|
+
}
|
|
59
61
|
},
|
|
60
62
|
async configResolved(viteConfig) {
|
|
61
63
|
const toolbarEnabled = await settings.preferences.get("devToolbar.enabled");
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import { type Plugin as VitePlugin } from 'vite';
|
|
2
2
|
import type { AstroSettings } from '../types/astro.js';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Fallback for tsconfig path aliases that Vite's `resolve.tsconfigPaths` does
|
|
5
|
+
* not currently handle in Astro's pipeline.
|
|
6
|
+
*
|
|
7
|
+
* This plugin is intentionally limited to the syntax Astro already supported
|
|
8
|
+
* before enabling `resolve.tsconfigPaths`:
|
|
9
|
+
* - CSS: `@import "..."`
|
|
10
|
+
* - CSS: `@import url("...")`
|
|
11
|
+
* - CSS: quoted `url("...")` references
|
|
12
|
+
* - Modules: JS, TS, and Astro import specifiers handled by `resolveId`
|
|
13
|
+
*
|
|
14
|
+
* Do not expand this fallback to new CSS at-rules or preprocessor syntax. It
|
|
15
|
+
* does not support `@use`, `@forward`, `@reference`, `@config`, unquoted
|
|
16
|
+
* `url(...)`, or every place a CSS tool might accept a file path. Those should
|
|
17
|
+
* be handled by Vite's native resolver instead.
|
|
18
|
+
*
|
|
19
|
+
* @deprecated This fallback will be removed in a future Astro version once Vite
|
|
20
|
+
* handles these remaining alias paths.
|
|
21
|
+
*/
|
|
4
22
|
export default function configAliasVitePlugin({ settings, }: {
|
|
5
23
|
settings: AstroSettings;
|
|
6
24
|
}): VitePlugin[] | null;
|
|
@@ -51,10 +51,10 @@ function configAliasVitePlugin({
|
|
|
51
51
|
const configAlias = getConfigAlias(settings);
|
|
52
52
|
if (!configAlias) return null;
|
|
53
53
|
return [
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
54
|
+
// Deprecated CSS fallback for Vite's transform pipeline. Only supports
|
|
55
|
+
// `@import "..."`, `@import url("...")`, and quoted `url("...")`.
|
|
56
|
+
// Do not add support here for `@use`, `@forward`, `@reference`, `@config`,
|
|
57
|
+
// or other CSS/preprocessor file-reference syntax.
|
|
58
58
|
{
|
|
59
59
|
name: "astro:tsconfig-alias-css",
|
|
60
60
|
enforce: "pre",
|
|
@@ -88,7 +88,8 @@ function configAliasVitePlugin({
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
},
|
|
91
|
-
//
|
|
91
|
+
// Deprecated module fallback for JS, TS, and Astro import specifiers that
|
|
92
|
+
// Vite's native tsconfig path resolution does not currently resolve.
|
|
92
93
|
{
|
|
93
94
|
name: "astro:tsconfig-alias",
|
|
94
95
|
// use post to only resolve ids that all other plugins before it can't
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import MagicString from "magic-string";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import escape from "./escape.js";
|
|
5
|
-
import slots, { SLOT_PREFIX } from "./slots.js";
|
|
2
|
+
import { collectSlots, SLOT_PREFIX } from "./slots.js";
|
|
3
|
+
import { escapeTemplateLiteralCharacters, needsEscape } from "./utils.js";
|
|
6
4
|
async function transform(code, id) {
|
|
7
5
|
const s = new MagicString(code, { filename: id });
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
6
|
+
let cursor = 0;
|
|
7
|
+
for (const slot of collectSlots(code)) {
|
|
8
|
+
escapeRange(s, code, cursor, slot.start);
|
|
9
|
+
s.overwrite(slot.start, slot.end, slot.value);
|
|
10
|
+
cursor = slot.end;
|
|
11
|
+
}
|
|
12
|
+
escapeRange(s, code, cursor, code.length);
|
|
11
13
|
s.prepend(`function render({ slots: ${SLOT_PREFIX} }) {
|
|
12
14
|
return \``);
|
|
13
15
|
s.append('`\n }\nrender["astro:html"] = true;\nexport default render;');
|
|
@@ -16,6 +18,13 @@ async function transform(code, id) {
|
|
|
16
18
|
map: s.generateMap({ hires: "boundary" })
|
|
17
19
|
};
|
|
18
20
|
}
|
|
21
|
+
function escapeRange(s, code, start, end) {
|
|
22
|
+
if (start >= end) return;
|
|
23
|
+
const segment = code.slice(start, end);
|
|
24
|
+
if (needsEscape(segment)) {
|
|
25
|
+
s.overwrite(start, end, escapeTemplateLiteralCharacters(segment));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
19
28
|
export {
|
|
20
29
|
transform
|
|
21
30
|
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type { Root } from 'hast';
|
|
2
|
-
import type MagicString from 'magic-string';
|
|
3
|
-
import type { Plugin } from 'unified';
|
|
4
|
-
declare const rehypeSlots: Plugin<[{
|
|
5
|
-
s: MagicString;
|
|
6
|
-
}], Root>;
|
|
7
|
-
export default rehypeSlots;
|
|
8
1
|
export declare const SLOT_PREFIX = "___SLOTS___";
|
|
2
|
+
export interface SlotReplacement {
|
|
3
|
+
start: number;
|
|
4
|
+
end: number;
|
|
5
|
+
value: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Find every `<slot>` (excluding `is:inline` ones) and describe how to replace it
|
|
9
|
+
* with a `${___SLOTS___[name] ?? `fallback`}` template literal expression.
|
|
10
|
+
*/
|
|
11
|
+
export declare function collectSlots(code: string): SlotReplacement[];
|
|
@@ -1,31 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ELEMENT_NODE, parse, walkSync } from "ultrahtml";
|
|
2
2
|
import { escapeTemplateLiteralCharacters } from "./utils.js";
|
|
3
|
-
const rehypeSlots = ({ s }) => {
|
|
4
|
-
return (tree, file) => {
|
|
5
|
-
visit(tree, (node) => {
|
|
6
|
-
if (node.type === "element" && node.tagName === "slot") {
|
|
7
|
-
if (typeof node.properties?.["is:inline"] !== "undefined") return;
|
|
8
|
-
const name = node.properties?.["name"] ?? "default";
|
|
9
|
-
const start = node.position?.start.offset ?? 0;
|
|
10
|
-
const end = node.position?.end.offset ?? 0;
|
|
11
|
-
const first = node.children.at(0) ?? node;
|
|
12
|
-
const last = node.children.at(-1) ?? node;
|
|
13
|
-
const text = file.value.slice(
|
|
14
|
-
first.position?.start.offset ?? 0,
|
|
15
|
-
last.position?.end.offset ?? 0
|
|
16
|
-
).toString();
|
|
17
|
-
s.overwrite(
|
|
18
|
-
start,
|
|
19
|
-
end,
|
|
20
|
-
`\${${SLOT_PREFIX}["${name}"] ?? \`${escapeTemplateLiteralCharacters(text).trim()}\`}`
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
var slots_default = rehypeSlots;
|
|
27
3
|
const SLOT_PREFIX = `___SLOTS___`;
|
|
4
|
+
function collectSlots(code) {
|
|
5
|
+
const slots = [];
|
|
6
|
+
walkSync(parse(code), (node) => {
|
|
7
|
+
if (node.type !== ELEMENT_NODE || node.name !== "slot") return;
|
|
8
|
+
if ("is:inline" in node.attributes) return;
|
|
9
|
+
const [open, close] = node.loc;
|
|
10
|
+
if (!close) return;
|
|
11
|
+
const name = node.attributes.name ?? "default";
|
|
12
|
+
const fallback = open.end < close.start ? code.slice(open.end, close.start) : code.slice(open.start, close.end);
|
|
13
|
+
slots.push({
|
|
14
|
+
start: open.start,
|
|
15
|
+
end: close.end,
|
|
16
|
+
value: `\${${SLOT_PREFIX}["${name}"] ?? \`${escapeTemplateLiteralCharacters(fallback).trim()}\`}`
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
return slots;
|
|
20
|
+
}
|
|
28
21
|
export {
|
|
29
22
|
SLOT_PREFIX,
|
|
30
|
-
|
|
23
|
+
collectSlots
|
|
31
24
|
};
|
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
import type { Element } from 'hast';
|
|
2
|
-
import type MagicString from 'magic-string';
|
|
3
|
-
export declare function replaceAttribute(s: MagicString, node: Element, key: string, newValue: string): MagicString | undefined;
|
|
4
1
|
export declare function needsEscape(value: any): value is string;
|
|
5
2
|
export declare function escapeTemplateLiteralCharacters(value: string): string;
|
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
const splitAttrsTokenizer = /([${}@\w:\-]*)\s*=\s*?(['"]?)(.*?)\2\s+/g;
|
|
2
|
-
function replaceAttribute(s, node, key, newValue) {
|
|
3
|
-
splitAttrsTokenizer.lastIndex = 0;
|
|
4
|
-
const text = s.original.slice(node.position?.start.offset ?? 0, node.position?.end.offset ?? 0).toString();
|
|
5
|
-
const offset = text.indexOf(key);
|
|
6
|
-
if (offset === -1) return;
|
|
7
|
-
const start = node.position.start.offset + offset;
|
|
8
|
-
const tokens = text.slice(offset).split(splitAttrsTokenizer);
|
|
9
|
-
const token = tokens[0].replace(/([^>])>[\s\S]*$/gm, "$1");
|
|
10
|
-
if (token.trim() === key) {
|
|
11
|
-
const end = start + key.length;
|
|
12
|
-
return s.overwrite(start, end, newValue, { contentOnly: true });
|
|
13
|
-
} else {
|
|
14
|
-
const length = token.length;
|
|
15
|
-
const end = start + length;
|
|
16
|
-
return s.overwrite(start, end, newValue, { contentOnly: true });
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
1
|
const NEEDS_ESCAPE_RE = /[`\\]|\$\{/g;
|
|
20
2
|
function needsEscape(value) {
|
|
21
3
|
NEEDS_ESCAPE_RE.lastIndex = 0;
|
|
@@ -42,6 +24,5 @@ function escapeTemplateLiteralCharacters(value) {
|
|
|
42
24
|
}
|
|
43
25
|
export {
|
|
44
26
|
escapeTemplateLiteralCharacters,
|
|
45
|
-
needsEscape
|
|
46
|
-
replaceAttribute
|
|
27
|
+
needsEscape
|
|
47
28
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.5",
|
|
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",
|
|
@@ -131,7 +131,6 @@
|
|
|
131
131
|
"package-manager-detector": "^1.6.0",
|
|
132
132
|
"piccolore": "^0.1.3",
|
|
133
133
|
"picomatch": "^4.0.4",
|
|
134
|
-
"rehype": "^13.0.2",
|
|
135
134
|
"semver": "^7.7.4",
|
|
136
135
|
"shiki": "^4.0.2",
|
|
137
136
|
"smol-toml": "^1.6.0",
|
|
@@ -141,9 +140,7 @@
|
|
|
141
140
|
"tinyglobby": "^0.2.15",
|
|
142
141
|
"ultrahtml": "^1.6.0",
|
|
143
142
|
"unifont": "~0.7.4",
|
|
144
|
-
"unist-util-visit": "^5.1.0",
|
|
145
143
|
"unstorage": "^1.17.5",
|
|
146
|
-
"vfile": "^6.0.3",
|
|
147
144
|
"vite": "^8.0.13",
|
|
148
145
|
"vitefu": "^1.1.2",
|
|
149
146
|
"xxhash-wasm": "^1.1.0",
|
|
@@ -167,7 +164,6 @@
|
|
|
167
164
|
"devDependencies": {
|
|
168
165
|
"@playwright/test": "1.58.2",
|
|
169
166
|
"@types/aria-query": "^5.0.4",
|
|
170
|
-
"@types/hast": "^3.0.4",
|
|
171
167
|
"@types/html-escaper": "3.0.4",
|
|
172
168
|
"@types/http-cache-semantics": "^4.2.0",
|
|
173
169
|
"@types/js-yaml": "^4.0.9",
|
|
@@ -189,7 +185,6 @@
|
|
|
189
185
|
"sass": "^1.98.0",
|
|
190
186
|
"typescript": "^6.0.3",
|
|
191
187
|
"undici": "^7.22.0",
|
|
192
|
-
"unified": "^11.0.5",
|
|
193
188
|
"vitest": "^4.1.0",
|
|
194
189
|
"@astrojs/check": "0.9.9",
|
|
195
190
|
"@astrojs/markdown-remark": "7.2.0",
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { visit } from "unist-util-visit";
|
|
2
|
-
import { escapeTemplateLiteralCharacters, needsEscape, replaceAttribute } from "./utils.js";
|
|
3
|
-
const rehypeEscape = ({ s }) => {
|
|
4
|
-
return (tree) => {
|
|
5
|
-
visit(tree, (node) => {
|
|
6
|
-
if (node.type === "text" || node.type === "comment") {
|
|
7
|
-
if (needsEscape(node.value)) {
|
|
8
|
-
s.overwrite(
|
|
9
|
-
node.position.start.offset,
|
|
10
|
-
node.position.end.offset,
|
|
11
|
-
escapeTemplateLiteralCharacters(node.value)
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
} else if (node.type === "element") {
|
|
15
|
-
if (!node.properties) return;
|
|
16
|
-
for (let [key, value] of Object.entries(node.properties)) {
|
|
17
|
-
key = key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
18
|
-
const newKey = needsEscape(key) ? escapeTemplateLiteralCharacters(key) : key;
|
|
19
|
-
const newValue = needsEscape(value) ? escapeTemplateLiteralCharacters(value) : value;
|
|
20
|
-
if (newKey === key && newValue === value) continue;
|
|
21
|
-
replaceAttribute(s, node, key, value === "" ? newKey : `${newKey}="${newValue}"`);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
var escape_default = rehypeEscape;
|
|
28
|
-
export {
|
|
29
|
-
escape_default as default
|
|
30
|
-
};
|