astro 7.0.0-alpha.0 → 7.0.0-alpha.2
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/Code.astro +1 -1
- package/dist/actions/handler.d.ts +32 -0
- package/dist/actions/handler.js +45 -0
- package/dist/actions/runtime/server.js +1 -1
- package/dist/assets/build/generate.js +1 -1
- package/dist/assets/build/remote.d.ts +3 -2
- package/dist/assets/build/remote.js +16 -9
- package/dist/assets/endpoint/dev.js +1 -1
- package/dist/assets/endpoint/generic.js +8 -20
- package/dist/assets/endpoint/loadImage.d.ts +11 -0
- package/dist/assets/endpoint/loadImage.js +19 -0
- package/dist/assets/endpoint/shared.js +7 -2
- package/dist/assets/fonts/config.d.ts +4 -4
- package/dist/assets/fonts/core/optimize-fallbacks.js +38 -13
- package/dist/assets/fonts/definitions.d.ts +2 -2
- package/dist/assets/fonts/infra/system-fallbacks-provider.d.ts +2 -2
- package/dist/assets/fonts/infra/system-fallbacks-provider.js +46 -9
- package/dist/assets/fonts/types.d.ts +1 -0
- package/dist/assets/index.d.ts +1 -0
- package/dist/assets/index.js +2 -0
- package/dist/assets/internal.js +22 -3
- package/dist/assets/services/service.d.ts +1 -1
- package/dist/assets/services/service.js +9 -9
- package/dist/assets/services/sharp.js +53 -18
- package/dist/assets/utils/generateImageStylesCSS.js +26 -6
- package/dist/assets/utils/index.d.ts +1 -0
- package/dist/assets/utils/index.js +2 -0
- package/dist/assets/utils/inferSourceFormat.d.ts +8 -3
- package/dist/assets/utils/inferSourceFormat.js +15 -4
- package/dist/assets/utils/metadata.js +1 -1
- package/dist/assets/utils/redirectValidation.d.ts +48 -0
- package/dist/assets/utils/redirectValidation.js +48 -0
- package/dist/assets/utils/remoteProbe.js +25 -2
- package/dist/assets/utils/vendor/image-size/types/svg.js +1 -1
- package/dist/cli/add/index.js +7 -58
- package/dist/cli/dev/background.d.ts +16 -0
- package/dist/cli/dev/background.js +116 -0
- package/dist/cli/dev/index.js +82 -3
- package/dist/cli/dev/logs.d.ts +6 -0
- package/dist/cli/dev/logs.js +72 -0
- package/dist/cli/dev/status.d.ts +15 -0
- package/dist/cli/dev/status.js +27 -0
- package/dist/cli/dev/stop.d.ts +12 -0
- package/dist/cli/dev/stop.js +43 -0
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/container/index.js +18 -14
- package/dist/content/content-layer.js +16 -11
- package/dist/content/data-store.d.ts +1 -1
- package/dist/content/loaders/types.d.ts +1 -1
- package/dist/content/runtime-assets.d.ts +2 -2
- package/dist/content/runtime.d.ts +1 -1
- package/dist/content/runtime.js +9 -4
- package/dist/content/server-listeners.js +0 -4
- package/dist/content/types-generator.js +5 -1
- package/dist/content/utils.d.ts +1 -1
- package/dist/content/utils.js +1 -1
- package/dist/content/vite-plugin-content-assets.js +1 -0
- package/dist/content/vite-plugin-content-virtual-mod.js +9 -1
- package/dist/core/app/base.d.ts +42 -15
- package/dist/core/app/base.js +151 -375
- package/dist/core/app/dev/app.d.ts +3 -2
- package/dist/core/app/dev/app.js +4 -60
- package/dist/core/app/entrypoints/node.d.ts +1 -1
- package/dist/core/app/entrypoints/node.js +2 -0
- package/dist/core/app/entrypoints/virtual/dev.js +2 -0
- package/dist/core/app/entrypoints/virtual/prod.js +4 -1
- package/dist/core/app/node.d.ts +16 -0
- package/dist/core/app/node.js +59 -13
- package/dist/core/app/prepare-response.d.ts +11 -0
- package/dist/core/app/prepare-response.js +18 -0
- package/dist/core/app/render-options.d.ts +11 -0
- package/dist/core/app/render-options.js +11 -0
- package/dist/core/base-pipeline.d.ts +48 -1
- package/dist/core/base-pipeline.js +63 -8
- package/dist/core/build/app.d.ts +3 -4
- package/dist/core/build/app.js +3 -17
- package/dist/core/build/generate.js +8 -1
- package/dist/core/build/index.d.ts +0 -11
- package/dist/core/build/index.js +0 -3
- package/dist/core/build/internal.d.ts +7 -0
- package/dist/core/build/plugins/plugin-chunk-imports.d.ts +6 -0
- package/dist/core/build/plugins/plugin-chunk-imports.js +29 -17
- package/dist/core/build/plugins/plugin-css.js +40 -1
- package/dist/core/build/plugins/plugin-internals.js +1 -1
- package/dist/core/build/plugins/plugin-manifest.js +11 -1
- package/dist/core/build/static-build.js +22 -141
- package/dist/core/build/types.d.ts +0 -1
- package/dist/core/build/util.js +8 -1
- package/dist/core/build/vite-build-config.d.ts +28 -0
- package/dist/core/build/vite-build-config.js +165 -0
- package/dist/core/cache/handler.d.ts +29 -0
- package/dist/core/cache/handler.js +81 -0
- package/dist/core/compile/style.js +18 -1
- package/dist/core/config/index.d.ts +1 -1
- package/dist/core/config/index.js +4 -1
- package/dist/core/config/merge.js +4 -0
- package/dist/core/config/schemas/base.d.ts +25 -13
- package/dist/core/config/schemas/base.js +39 -10
- package/dist/core/config/schemas/relative.d.ts +76 -49
- package/dist/core/config/schemas/relative.js +2 -3
- package/dist/core/config/settings.js +2 -4
- package/dist/core/config/tsconfig.d.ts +24 -9
- package/dist/core/config/tsconfig.js +54 -45
- package/dist/core/config/validate.js +59 -0
- package/dist/core/constants.d.ts +27 -1
- package/dist/core/constants.js +14 -1
- package/dist/core/cookies/cookies.d.ts +7 -2
- package/dist/core/cookies/cookies.js +11 -4
- package/dist/core/cookies/response.d.ts +1 -1
- package/dist/core/cookies/response.js +1 -2
- package/dist/core/create-vite.js +18 -1
- package/dist/core/csp/config.js +17 -5
- package/dist/core/csp/runtime.js +6 -4
- package/dist/core/dev/dev.d.ts +1 -0
- package/dist/core/dev/dev.js +4 -13
- package/dist/core/dev/lockfile.d.ts +54 -0
- package/dist/core/dev/lockfile.js +93 -0
- package/dist/core/errors/build-handler.d.ts +17 -0
- package/dist/core/errors/build-handler.js +22 -0
- package/dist/core/errors/default-handler.d.ts +14 -0
- package/dist/core/errors/default-handler.js +144 -0
- package/dist/core/errors/dev-handler.d.ts +21 -0
- package/dist/core/errors/dev-handler.js +82 -0
- package/dist/core/errors/errors-data.d.ts +43 -38
- package/dist/core/errors/errors-data.js +79 -73
- package/dist/core/errors/handler.d.ts +9 -0
- package/dist/core/errors/handler.js +0 -0
- package/dist/core/errors/zod-error-map.js +30 -1
- package/dist/core/fetch/default-handler.d.ts +17 -0
- package/dist/core/fetch/default-handler.js +45 -0
- package/dist/core/fetch/fetch-state.d.ts +230 -0
- package/dist/core/fetch/fetch-state.js +896 -0
- package/dist/core/fetch/index.d.ts +61 -0
- package/dist/core/fetch/index.js +121 -0
- package/dist/core/fetch/types.d.ts +25 -0
- package/dist/core/fetch/types.js +0 -0
- package/dist/core/fetch/vite-plugin.d.ts +5 -0
- package/dist/core/fetch/vite-plugin.js +76 -0
- package/dist/core/hono/index.d.ts +21 -0
- package/dist/core/hono/index.js +101 -0
- package/dist/core/i18n/domain.d.ts +12 -0
- package/dist/core/i18n/domain.js +66 -0
- package/dist/core/i18n/handler.d.ts +18 -0
- package/dist/core/i18n/handler.js +122 -0
- package/dist/core/logger/core.d.ts +9 -1
- package/dist/core/logger/core.js +17 -1
- package/dist/core/messages/runtime.d.ts +0 -3
- package/dist/core/messages/runtime.js +1 -9
- package/dist/core/middleware/astro-middleware.d.ts +27 -0
- package/dist/core/middleware/astro-middleware.js +51 -0
- package/dist/core/module-loader/vite.js +1 -2
- package/dist/core/pages/handler.d.ts +20 -0
- package/dist/core/pages/handler.js +75 -0
- package/dist/core/preview/index.js +6 -5
- package/dist/core/preview/static-preview-server.js +5 -2
- package/dist/core/redirects/render.d.ts +2 -2
- package/dist/core/redirects/render.js +7 -8
- package/dist/core/render/params-and-props.js +2 -2
- package/dist/core/render/route-cache.d.ts +1 -0
- package/dist/core/render/route-cache.js +4 -4
- package/dist/core/render/slots.js +9 -2
- package/dist/core/rewrites/handler.d.ts +37 -0
- package/dist/core/rewrites/handler.js +67 -0
- package/dist/core/routing/3xx.js +8 -4
- package/dist/core/routing/create-manifest.js +11 -1
- package/dist/core/routing/handler.d.ts +17 -0
- package/dist/core/routing/handler.js +171 -0
- package/dist/core/routing/match.d.ts +0 -7
- package/dist/core/routing/match.js +0 -5
- package/dist/core/routing/parse-route.js +1 -1
- package/dist/core/routing/pattern.js +1 -1
- package/dist/core/routing/rewrite.js +2 -5
- package/dist/core/routing/router.d.ts +8 -0
- package/dist/core/routing/router.js +28 -0
- package/dist/core/routing/trailing-slash-handler.d.ts +18 -0
- package/dist/core/routing/trailing-slash-handler.js +67 -0
- package/dist/core/routing/validation.js +1 -1
- package/dist/core/server-islands/vite-plugin-server-islands.d.ts +6 -1
- package/dist/core/server-islands/vite-plugin-server-islands.js +13 -3
- package/dist/core/session/config.d.ts +1 -1
- package/dist/core/session/drivers.d.ts +1 -1
- package/dist/core/session/handler.d.ts +11 -0
- package/dist/core/session/handler.js +33 -0
- package/dist/core/session/runtime.js +7 -2
- package/dist/core/util/normalized-url.d.ts +10 -0
- package/dist/core/util/normalized-url.js +24 -0
- package/dist/core/util/pathname.d.ts +10 -1
- package/dist/core/util/pathname.js +13 -4
- package/dist/environments.js +1 -1
- package/dist/events/session.d.ts +8 -0
- package/dist/events/session.js +11 -0
- package/dist/i18n/middleware.d.ts +10 -0
- package/dist/i18n/middleware.js +4 -88
- package/dist/i18n/utils.js +2 -2
- package/dist/jsx/rehype.d.ts +1 -1
- package/dist/manifest/virtual-module.js +3 -1
- package/dist/markdown/index.d.ts +4 -0
- package/dist/markdown/index.js +14 -0
- package/dist/prefetch/index.js +12 -7
- package/dist/prerender/utils.js +5 -1
- package/dist/runtime/client/dev-toolbar/apps/audit/rules/a11y.js +9 -0
- package/dist/runtime/server/astro-island.js +57 -20
- package/dist/runtime/server/astro-island.prebuilt-dev.d.ts +1 -1
- package/dist/runtime/server/astro-island.prebuilt-dev.js +1 -1
- package/dist/runtime/server/astro-island.prebuilt.d.ts +1 -1
- package/dist/runtime/server/astro-island.prebuilt.js +1 -1
- package/dist/runtime/server/jsx.js +1 -1
- package/dist/runtime/server/render/common.js +10 -4
- package/dist/runtime/server/render/component.js +8 -6
- package/dist/runtime/server/render/head.js +1 -5
- package/dist/runtime/server/render/server-islands.js +2 -1
- package/dist/runtime/server/render/util.js +2 -2
- package/dist/runtime/server/scripts.js +6 -0
- package/dist/runtime/server/transition.d.ts +1 -6
- package/dist/runtime/server/transition.js +0 -8
- package/dist/transitions/events.d.ts +0 -14
- package/dist/transitions/events.js +0 -14
- package/dist/transitions/index.d.ts +0 -1
- package/dist/transitions/index.js +0 -2
- package/dist/transitions/vite-plugin-transitions.js +2 -4
- package/dist/types/public/config.d.ts +96 -14
- package/dist/types/public/content.d.ts +5 -5
- package/dist/types/public/index.d.ts +2 -1
- package/dist/types/public/integrations.d.ts +11 -3
- package/dist/types/public/internal.d.ts +1 -1
- package/dist/types/public/manifest.d.ts +1 -1
- package/dist/virtual-modules/i18n.d.ts +2 -2
- package/dist/virtual-modules/i18n.js +1 -1
- package/dist/vite-plugin-app/app.d.ts +13 -6
- package/dist/vite-plugin-app/app.js +51 -89
- package/dist/vite-plugin-app/createAstroServerApp.d.ts +3 -1
- package/dist/vite-plugin-app/createAstroServerApp.js +4 -3
- package/dist/vite-plugin-astro-server/plugin.js +11 -5
- package/dist/vite-plugin-astro-server/route-guard.d.ts +33 -0
- package/dist/vite-plugin-astro-server/route-guard.js +42 -23
- package/dist/vite-plugin-dev-status/index.d.ts +2 -0
- package/dist/vite-plugin-dev-status/index.js +15 -0
- package/dist/vite-plugin-head/index.js +36 -19
- package/dist/vite-plugin-hmr-reload/index.d.ts +1 -1
- package/dist/vite-plugin-hmr-reload/index.js +23 -1
- package/dist/vite-plugin-integrations-container/index.js +15 -6
- package/dist/vite-plugin-markdown/content-entry-type.js +7 -4
- package/dist/vite-plugin-markdown/images.js +9 -11
- package/dist/vite-plugin-markdown/index.js +12 -11
- package/dist/vite-plugin-utils/index.d.ts +1 -0
- package/dist/vite-plugin-utils/index.js +10 -1
- package/package.json +23 -16
- package/templates/content/types.d.ts +1 -0
- package/types/transitions.d.ts +0 -7
- package/dist/core/render-context.d.ts +0 -77
- package/dist/core/render-context.js +0 -826
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { AstroError, AstroErrorData } from "../../core/errors/index.js";
|
|
2
|
+
import { resolveDefaultOutputFormat } from "../utils/inferSourceFormat.js";
|
|
3
|
+
import { detector } from "../utils/vendor/image-size/detector.js";
|
|
2
4
|
import {
|
|
3
5
|
baseService,
|
|
4
6
|
parseQuality
|
|
@@ -20,6 +22,9 @@ function resolveSharpQuality(quality) {
|
|
|
20
22
|
}
|
|
21
23
|
function resolveSharpEncoderOptions(transform, inputFormat, serviceConfig = {}) {
|
|
22
24
|
const quality = resolveSharpQuality(transform.quality);
|
|
25
|
+
if (transform.format === void 0) {
|
|
26
|
+
return quality === void 0 ? void 0 : { quality };
|
|
27
|
+
}
|
|
23
28
|
switch (transform.format) {
|
|
24
29
|
case "jpg":
|
|
25
30
|
case "jpeg":
|
|
@@ -81,14 +86,35 @@ const sharpService = {
|
|
|
81
86
|
if (!sharp) sharp = await loadSharp();
|
|
82
87
|
const transform = transformOptions;
|
|
83
88
|
const kernel = config.service.config.kernel;
|
|
84
|
-
|
|
89
|
+
const bufferFormat = detector(inputBuffer);
|
|
90
|
+
const outputFormat = transform.format ?? resolveDefaultOutputFormat(bufferFormat);
|
|
91
|
+
if (outputFormat === "svg") {
|
|
92
|
+
if (bufferFormat && bufferFormat !== "svg") {
|
|
93
|
+
console.warn(
|
|
94
|
+
`\u26A0\uFE0F Astro expected an SVG for "${transform.src}" but the source is ${bufferFormat}. Passing it through as ${bufferFormat} instead.`
|
|
95
|
+
);
|
|
96
|
+
return { data: inputBuffer, format: bufferFormat };
|
|
97
|
+
}
|
|
98
|
+
return { data: inputBuffer, format: "svg" };
|
|
99
|
+
}
|
|
100
|
+
if (!bufferFormat) {
|
|
101
|
+
throw new AstroError({
|
|
102
|
+
...AstroErrorData.NoImageMetadata,
|
|
103
|
+
message: AstroErrorData.NoImageMetadata.message(transform.src)
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (bufferFormat === "svg" && !config.dangerouslyProcessSVG) {
|
|
107
|
+
throw new AstroError({
|
|
108
|
+
...AstroErrorData.UnsupportedImageFormat,
|
|
109
|
+
message: `SVG image processing is disabled, but the source for "${transform.src}" is an SVG. Pass it through unchanged by setting \`format="svg"\` on the component, or set \`image.dangerouslyProcessSVG: true\` to rasterize SVG sources.`
|
|
110
|
+
});
|
|
111
|
+
}
|
|
85
112
|
const result = sharp(inputBuffer, {
|
|
86
113
|
failOnError: false,
|
|
87
114
|
pages: -1,
|
|
88
115
|
limitInputPixels: config.service.config.limitInputPixels
|
|
89
116
|
});
|
|
90
117
|
result.rotate();
|
|
91
|
-
const { format } = await result.metadata();
|
|
92
118
|
if (transform.width && transform.height) {
|
|
93
119
|
const fit = transform.fit ? fitMap[transform.fit] ?? "inside" : void 0;
|
|
94
120
|
result.resize({
|
|
@@ -115,23 +141,32 @@ const sharpService = {
|
|
|
115
141
|
if (transform.background) {
|
|
116
142
|
result.flatten({ background: transform.background });
|
|
117
143
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
144
|
+
const encoderOptions = resolveSharpEncoderOptions(
|
|
145
|
+
{ format: outputFormat, quality: transform.quality },
|
|
146
|
+
bufferFormat,
|
|
147
|
+
config.service.config
|
|
148
|
+
);
|
|
149
|
+
if (outputFormat === "webp") {
|
|
150
|
+
result.webp(encoderOptions);
|
|
151
|
+
} else if (outputFormat === "png") {
|
|
152
|
+
result.png(encoderOptions);
|
|
153
|
+
} else if (outputFormat === "avif") {
|
|
154
|
+
result.avif(encoderOptions);
|
|
155
|
+
} else if (outputFormat === "jpeg" || outputFormat === "jpg") {
|
|
156
|
+
result.jpeg(encoderOptions);
|
|
157
|
+
} else {
|
|
158
|
+
result.toFormat(outputFormat, encoderOptions);
|
|
159
|
+
}
|
|
160
|
+
let data;
|
|
161
|
+
let info;
|
|
162
|
+
try {
|
|
163
|
+
({ data, info } = await result.toBuffer({ resolveWithObject: true }));
|
|
164
|
+
} catch {
|
|
165
|
+
console.warn(
|
|
166
|
+
`\u26A0\uFE0F Astro could not optimize image "${transform.src}". Sharp doesn't support this format. The image will be used unoptimized. Consider converting to WebP or placing in the public/ folder.`
|
|
167
|
+
);
|
|
168
|
+
return { data: inputBuffer, format: bufferFormat };
|
|
133
169
|
}
|
|
134
|
-
const { data, info } = await result.toBuffer({ resolveWithObject: true });
|
|
135
170
|
const needsCopy = "buffer" in data && data.buffer instanceof SharedArrayBuffer;
|
|
136
171
|
return {
|
|
137
172
|
data: needsCopy ? new Uint8Array(data) : data,
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import { cssFitValues } from "../internal.js";
|
|
2
|
+
const POSITION_KEYWORDS = ["top", "bottom", "left", "right", "center"];
|
|
3
|
+
function getPositionEntries() {
|
|
4
|
+
const entries = [];
|
|
5
|
+
for (const kw of POSITION_KEYWORDS) {
|
|
6
|
+
entries.push([kw, kw]);
|
|
7
|
+
}
|
|
8
|
+
for (const a of POSITION_KEYWORDS) {
|
|
9
|
+
for (const b of POSITION_KEYWORDS) {
|
|
10
|
+
if (a === b) continue;
|
|
11
|
+
const cssValue = `${a} ${b}`;
|
|
12
|
+
const dataAttr = `${a}-${b}`;
|
|
13
|
+
entries.push([dataAttr, cssValue]);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return entries;
|
|
17
|
+
}
|
|
2
18
|
function generateImageStylesCSS(defaultObjectFit, defaultObjectPosition) {
|
|
3
19
|
const fitStyles = cssFitValues.map(
|
|
4
20
|
(fit) => `
|
|
@@ -10,11 +26,14 @@ function generateImageStylesCSS(defaultObjectFit, defaultObjectPosition) {
|
|
|
10
26
|
:where([data-astro-image]:not([data-astro-image-fit])) {
|
|
11
27
|
object-fit: ${defaultObjectFit};
|
|
12
28
|
}` : "";
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
29
|
+
const positionEntries = getPositionEntries();
|
|
30
|
+
const positionStyles = positionEntries.map(
|
|
31
|
+
([dataAttr, cssValue]) => `
|
|
32
|
+
[data-astro-image-pos="${dataAttr}"] {
|
|
33
|
+
object-position: ${cssValue};
|
|
34
|
+
}`
|
|
35
|
+
).join("\n");
|
|
36
|
+
const defaultPositionStyle = defaultObjectPosition ? `
|
|
18
37
|
:where([data-astro-image]:not([data-astro-image-pos])) {
|
|
19
38
|
object-position: ${defaultObjectPosition};
|
|
20
39
|
}` : "";
|
|
@@ -30,7 +49,8 @@ function generateImageStylesCSS(defaultObjectFit, defaultObjectPosition) {
|
|
|
30
49
|
}
|
|
31
50
|
${fitStyles}
|
|
32
51
|
${defaultFitStyle}
|
|
33
|
-
${
|
|
52
|
+
${positionStyles}
|
|
53
|
+
${defaultPositionStyle}
|
|
34
54
|
`.trim();
|
|
35
55
|
}
|
|
36
56
|
export {
|
|
@@ -10,3 +10,4 @@ export { isESMImportedImage, isRemoteImage, resolveSrc } from './imageKind.js';
|
|
|
10
10
|
export { imageMetadata } from './metadata.js';
|
|
11
11
|
export { getOrigQueryParams } from './queryParams.js';
|
|
12
12
|
export { inferRemoteSize } from './remoteProbe.js';
|
|
13
|
+
export { fetchWithRedirects } from './redirectValidation.js';
|
|
@@ -7,8 +7,10 @@ import { isESMImportedImage, isRemoteImage, resolveSrc } from "./imageKind.js";
|
|
|
7
7
|
import { imageMetadata } from "./metadata.js";
|
|
8
8
|
import { getOrigQueryParams } from "./queryParams.js";
|
|
9
9
|
import { inferRemoteSize } from "./remoteProbe.js";
|
|
10
|
+
import { fetchWithRedirects } from "./redirectValidation.js";
|
|
10
11
|
export {
|
|
11
12
|
emitClientAsset,
|
|
13
|
+
fetchWithRedirects,
|
|
12
14
|
getOrigQueryParams,
|
|
13
15
|
imageMetadata,
|
|
14
16
|
inferRemoteSize,
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Infer the image format from a source path or URL
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Infer the image format from a source path or URL.
|
|
3
|
+
*
|
|
4
|
+
* For `data:` URIs the MIME is read up to the first `;` or `,` (whichever comes first),
|
|
5
|
+
* so both `data:image/svg+xml;base64,...` and `data:image/svg+xml,<svg>...` work.
|
|
6
|
+
* `image/svg+xml` normalizes to `svg`; otherwise the subtype after the slash is returned.
|
|
7
|
+
*
|
|
8
|
+
* Returns undefined when the format cannot be determined.
|
|
5
9
|
*/
|
|
6
10
|
export declare function inferSourceFormat(src: string): string | undefined;
|
|
11
|
+
export declare function resolveDefaultOutputFormat(sourceFormat: string | undefined): string;
|
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
import { removeQueryString } from "@astrojs/internal-helpers/path";
|
|
2
|
+
import { DEFAULT_OUTPUT_FORMAT } from "../consts.js";
|
|
2
3
|
const DATA_PREFIX = "data:";
|
|
3
4
|
function inferSourceFormat(src) {
|
|
4
5
|
if (src.startsWith(DATA_PREFIX)) {
|
|
5
|
-
const
|
|
6
|
+
const sepIndex = src.indexOf(";");
|
|
7
|
+
const commaIndex = src.indexOf(",");
|
|
8
|
+
const mimeEnd = sepIndex === -1 ? commaIndex : commaIndex === -1 ? sepIndex : Math.min(sepIndex, commaIndex);
|
|
9
|
+
if (mimeEnd === -1) return void 0;
|
|
10
|
+
const mime = src.slice(DATA_PREFIX.length, mimeEnd);
|
|
6
11
|
if (mime === "image/svg+xml") return "svg";
|
|
7
12
|
const sub = mime.split("/")[1];
|
|
8
13
|
return sub || void 0;
|
|
9
14
|
}
|
|
10
15
|
try {
|
|
11
16
|
const cleanSrc = removeQueryString(src).split("#")[0];
|
|
12
|
-
const
|
|
17
|
+
const lastSlash = cleanSrc.lastIndexOf("/");
|
|
18
|
+
const basename = lastSlash === -1 ? cleanSrc : cleanSrc.slice(lastSlash + 1);
|
|
19
|
+
const lastDot = basename.lastIndexOf(".");
|
|
13
20
|
if (lastDot === -1) return void 0;
|
|
14
|
-
return
|
|
21
|
+
return basename.slice(lastDot + 1).toLowerCase();
|
|
15
22
|
} catch {
|
|
16
23
|
return void 0;
|
|
17
24
|
}
|
|
18
25
|
}
|
|
26
|
+
function resolveDefaultOutputFormat(sourceFormat) {
|
|
27
|
+
return sourceFormat === "svg" ? "svg" : DEFAULT_OUTPUT_FORMAT;
|
|
28
|
+
}
|
|
19
29
|
export {
|
|
20
|
-
inferSourceFormat
|
|
30
|
+
inferSourceFormat,
|
|
31
|
+
resolveDefaultOutputFormat
|
|
21
32
|
};
|
|
@@ -10,7 +10,7 @@ async function imageMetadata(data, src) {
|
|
|
10
10
|
message: AstroErrorData.NoImageMetadata.message(src)
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
|
-
if (
|
|
13
|
+
if (result.height == null || result.width == null || !result.type) {
|
|
14
14
|
throw new AstroError({
|
|
15
15
|
...AstroErrorData.NoImageMetadata,
|
|
16
16
|
message: AstroErrorData.NoImageMetadata.message(src)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for handling HTTP redirects with validation
|
|
3
|
+
*/
|
|
4
|
+
import type { AstroConfig } from '../../types/public/config.js';
|
|
5
|
+
export type RemoteImageConfig = Pick<AstroConfig['image'], 'remotePatterns' | 'domains'>;
|
|
6
|
+
export type FetchRedirectOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* URL to fetch (either string or URL object)
|
|
9
|
+
*/
|
|
10
|
+
url: string | URL;
|
|
11
|
+
/**
|
|
12
|
+
* Headers to include in the request (optional)
|
|
13
|
+
*/
|
|
14
|
+
headers?: Headers;
|
|
15
|
+
/**
|
|
16
|
+
* Image config for validating redirect destinations (optional)
|
|
17
|
+
*/
|
|
18
|
+
imageConfig: RemoteImageConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Fetch function to use (default: globalThis.fetch)
|
|
21
|
+
*/
|
|
22
|
+
fetchFn?: typeof fetch;
|
|
23
|
+
/**
|
|
24
|
+
* Maximum number of redirects to follow (default: 10)
|
|
25
|
+
*/
|
|
26
|
+
redirectLimit?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Error handler for redirect depth exceeded (default: generic Error)
|
|
29
|
+
*/
|
|
30
|
+
onMaxRedirectsExceeded?: (url: string) => Error;
|
|
31
|
+
/**
|
|
32
|
+
* Error handler for missing Location header (default: generic Error)
|
|
33
|
+
*/
|
|
34
|
+
onMissingLocationHeader?: (status: number, url: string) => Error;
|
|
35
|
+
/**
|
|
36
|
+
* Error handler for disallowed redirect (default: generic Error)
|
|
37
|
+
*/
|
|
38
|
+
onDisallowedRedirect?: (currentUrl: string, targetUrl: string) => Error;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Recursively follows HTTP redirects with validation according to the image configuration.
|
|
42
|
+
*
|
|
43
|
+
* If any of the domains in the redirect chain are not allowed by either `image.remotePatterns`
|
|
44
|
+
* or `image.domains`, this function will throw an error for a disallowed redirect.
|
|
45
|
+
*
|
|
46
|
+
* @param options The options for this fetch call.
|
|
47
|
+
*/
|
|
48
|
+
export declare function fetchWithRedirects(options: FetchRedirectOptions): Promise<Response>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { isRemoteAllowed } from "@astrojs/internal-helpers/remote";
|
|
2
|
+
async function fetchWithRedirects(options) {
|
|
3
|
+
const {
|
|
4
|
+
url,
|
|
5
|
+
headers,
|
|
6
|
+
imageConfig,
|
|
7
|
+
fetchFn = globalThis.fetch,
|
|
8
|
+
redirectLimit = 10,
|
|
9
|
+
onMaxRedirectsExceeded = (_u) => new Error("Maximum redirect depth exceeded"),
|
|
10
|
+
onMissingLocationHeader = (_s, _u) => new Error(`Redirect response ${_s} missing Location header`),
|
|
11
|
+
onDisallowedRedirect = (_current, _target) => new Error(
|
|
12
|
+
`The image at ${_current} redirected to ${_target}, which is not an allowed remote location.`
|
|
13
|
+
)
|
|
14
|
+
} = options;
|
|
15
|
+
if (redirectLimit <= 0) {
|
|
16
|
+
throw onMaxRedirectsExceeded(typeof url === "string" ? url : url.toString());
|
|
17
|
+
}
|
|
18
|
+
const urlString = typeof url === "string" ? url : url.toString();
|
|
19
|
+
const req = new Request(url, { headers });
|
|
20
|
+
const res = await fetchFn(req, { redirect: "manual" });
|
|
21
|
+
if ([301, 302, 303, 307, 308].includes(res.status)) {
|
|
22
|
+
const location = res.headers.get("Location");
|
|
23
|
+
if (!location) {
|
|
24
|
+
throw onMissingLocationHeader(res.status, urlString);
|
|
25
|
+
}
|
|
26
|
+
const redirectUrl = new URL(location, urlString).toString();
|
|
27
|
+
if (!isRemoteAllowed(redirectUrl, {
|
|
28
|
+
domains: imageConfig.domains ?? [],
|
|
29
|
+
remotePatterns: imageConfig.remotePatterns ?? []
|
|
30
|
+
})) {
|
|
31
|
+
throw onDisallowedRedirect(urlString, redirectUrl);
|
|
32
|
+
}
|
|
33
|
+
return fetchWithRedirects({
|
|
34
|
+
url: redirectUrl,
|
|
35
|
+
headers,
|
|
36
|
+
imageConfig,
|
|
37
|
+
fetchFn,
|
|
38
|
+
redirectLimit: redirectLimit - 1,
|
|
39
|
+
onMaxRedirectsExceeded,
|
|
40
|
+
onMissingLocationHeader,
|
|
41
|
+
onDisallowedRedirect
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return res;
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
fetchWithRedirects
|
|
48
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isRemoteAllowed } from "@astrojs/internal-helpers/remote";
|
|
2
2
|
import { AstroError, AstroErrorData } from "../../core/errors/index.js";
|
|
3
3
|
import { imageMetadata } from "./metadata.js";
|
|
4
|
+
import { fetchWithRedirects } from "./redirectValidation.js";
|
|
4
5
|
async function inferRemoteSize(url, imageConfig) {
|
|
5
6
|
if (!URL.canParse(url)) {
|
|
6
7
|
throw new AstroError({
|
|
@@ -27,13 +28,35 @@ async function inferRemoteSize(url, imageConfig) {
|
|
|
27
28
|
message: AstroErrorData.RemoteImageNotAllowed.message(url)
|
|
28
29
|
});
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
let response;
|
|
32
|
+
try {
|
|
33
|
+
response = await fetchWithRedirects({
|
|
34
|
+
url,
|
|
35
|
+
onMaxRedirectsExceeded: (u) => new AstroError({
|
|
36
|
+
...AstroErrorData.FailedToFetchRemoteImageDimensions,
|
|
37
|
+
message: AstroErrorData.FailedToFetchRemoteImageDimensions.message(u)
|
|
38
|
+
}),
|
|
39
|
+
onMissingLocationHeader: (_status, u) => new AstroError({
|
|
40
|
+
...AstroErrorData.FailedToFetchRemoteImageDimensions,
|
|
41
|
+
message: AstroErrorData.FailedToFetchRemoteImageDimensions.message(u)
|
|
42
|
+
}),
|
|
43
|
+
imageConfig: imageConfig ?? {
|
|
44
|
+
remotePatterns: [],
|
|
45
|
+
domains: []
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
} catch (_err) {
|
|
32
49
|
throw new AstroError({
|
|
33
50
|
...AstroErrorData.FailedToFetchRemoteImageDimensions,
|
|
34
51
|
message: AstroErrorData.FailedToFetchRemoteImageDimensions.message(url)
|
|
35
52
|
});
|
|
36
53
|
}
|
|
54
|
+
if (allowlistConfig && !isRemoteAllowed(response.url, allowlistConfig)) {
|
|
55
|
+
throw new AstroError({
|
|
56
|
+
...AstroErrorData.RemoteImageNotAllowed,
|
|
57
|
+
message: AstroErrorData.RemoteImageNotAllowed.message(url)
|
|
58
|
+
});
|
|
59
|
+
}
|
|
37
60
|
if (!response.body || !response.ok) {
|
|
38
61
|
throw new AstroError({
|
|
39
62
|
...AstroErrorData.FailedToFetchRemoteImageDimensions,
|
|
@@ -77,7 +77,7 @@ const SVG = {
|
|
|
77
77
|
const root = extractorRegExps.root.exec(toUTF8String(input));
|
|
78
78
|
if (root) {
|
|
79
79
|
const attrs = parseAttributes(root[0]);
|
|
80
|
-
if (attrs.width && attrs.height) {
|
|
80
|
+
if (attrs.width != null && attrs.height != null) {
|
|
81
81
|
return calculateByDimensions(attrs);
|
|
82
82
|
}
|
|
83
83
|
if (attrs.viewbox) {
|
package/dist/cli/add/index.js
CHANGED
|
@@ -197,12 +197,6 @@ async function add(names, { flags }) {
|
|
|
197
197
|
logger,
|
|
198
198
|
scripts: { "generate-types": "wrangler types" }
|
|
199
199
|
});
|
|
200
|
-
await updatePackageJsonOverrides({
|
|
201
|
-
configURL,
|
|
202
|
-
flags,
|
|
203
|
-
logger,
|
|
204
|
-
overrides: { vite: "^7" }
|
|
205
|
-
});
|
|
206
200
|
}
|
|
207
201
|
if (integrations.find((integration) => integration.id === "tailwind")) {
|
|
208
202
|
const dir = new URL("./styles/", new URL(userConfig.srcDir ?? "./src/", root));
|
|
@@ -590,54 +584,6 @@ async function updateAstroConfig({
|
|
|
590
584
|
return "cancelled";
|
|
591
585
|
}
|
|
592
586
|
}
|
|
593
|
-
async function updatePackageJsonOverrides({
|
|
594
|
-
configURL,
|
|
595
|
-
flags,
|
|
596
|
-
logger,
|
|
597
|
-
overrides
|
|
598
|
-
}) {
|
|
599
|
-
const pkgURL = new URL("./package.json", configURL);
|
|
600
|
-
if (!existsSync(pkgURL)) {
|
|
601
|
-
logger.debug("add", "No package.json found, skipping overrides update");
|
|
602
|
-
return "none";
|
|
603
|
-
}
|
|
604
|
-
const pkgPath = fileURLToPath(pkgURL);
|
|
605
|
-
const input = await fs.readFile(pkgPath, { encoding: "utf-8" });
|
|
606
|
-
const pkgJson = JSON.parse(input);
|
|
607
|
-
pkgJson.overrides ??= {};
|
|
608
|
-
let hasChanges = false;
|
|
609
|
-
for (const [name, range] of Object.entries(overrides)) {
|
|
610
|
-
if (!(name in pkgJson.overrides)) {
|
|
611
|
-
pkgJson.overrides[name] = range;
|
|
612
|
-
hasChanges = true;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
if (!hasChanges) {
|
|
616
|
-
return "none";
|
|
617
|
-
}
|
|
618
|
-
const output = JSON.stringify(pkgJson, null, 2);
|
|
619
|
-
const diff = getDiffContent(input, output);
|
|
620
|
-
if (!diff) {
|
|
621
|
-
return "none";
|
|
622
|
-
}
|
|
623
|
-
logger.info(
|
|
624
|
-
"SKIP_FORMAT",
|
|
625
|
-
`
|
|
626
|
-
${magenta("Astro will add the following overrides to your package.json:")}`
|
|
627
|
-
);
|
|
628
|
-
clack.box(diff, "package.json", {
|
|
629
|
-
rounded: true,
|
|
630
|
-
withGuide: false,
|
|
631
|
-
width: "auto"
|
|
632
|
-
});
|
|
633
|
-
if (await askToContinue({ flags, logger })) {
|
|
634
|
-
await fs.writeFile(pkgPath, output, { encoding: "utf-8" });
|
|
635
|
-
logger.debug("add", "Updated package.json overrides");
|
|
636
|
-
return "updated";
|
|
637
|
-
} else {
|
|
638
|
-
return "cancelled";
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
587
|
async function updatePackageJsonScripts({
|
|
642
588
|
configURL,
|
|
643
589
|
flags,
|
|
@@ -898,14 +844,17 @@ async function updateTSConfig(cwd = process.cwd(), logger, integrationsInfo, fla
|
|
|
898
844
|
}
|
|
899
845
|
let inputConfig = await loadTSConfig(cwd);
|
|
900
846
|
let inputConfigText = "";
|
|
901
|
-
if (inputConfig === "invalid-config"
|
|
847
|
+
if (inputConfig.error === "invalid-config") {
|
|
848
|
+
logger.warn(`add`, `Couldn't parse tsconfig.json or jsconfig.json: ${inputConfig.message}`);
|
|
902
849
|
return "failure";
|
|
903
|
-
} else if (inputConfig === "missing-config") {
|
|
850
|
+
} else if (inputConfig.error === "missing-config") {
|
|
904
851
|
logger.debug("add", "Couldn't find tsconfig.json or jsconfig.json, generating one");
|
|
852
|
+
const tsconfigFile = path.join(cwd, "tsconfig.json");
|
|
905
853
|
inputConfig = {
|
|
906
854
|
tsconfig: defaultTSConfig,
|
|
907
|
-
tsconfigFile
|
|
908
|
-
rawConfig: defaultTSConfig
|
|
855
|
+
tsconfigFile,
|
|
856
|
+
rawConfig: defaultTSConfig,
|
|
857
|
+
sources: [tsconfigFile]
|
|
909
858
|
};
|
|
910
859
|
} else {
|
|
911
860
|
inputConfigText = JSON.stringify(inputConfig.rawConfig, null, 2);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AstroLogger } from '../../core/logger/core.js';
|
|
2
|
+
import type { Flags } from '../flags.js';
|
|
3
|
+
export interface BackgroundResult {
|
|
4
|
+
pid: number;
|
|
5
|
+
url: string;
|
|
6
|
+
existing?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface BackgroundErrorResult {
|
|
9
|
+
error: string;
|
|
10
|
+
message: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function formatBackgroundOutput(result: BackgroundResult | BackgroundErrorResult): string;
|
|
13
|
+
export declare function background({ flags, logger, }: {
|
|
14
|
+
flags: Flags;
|
|
15
|
+
logger: AstroLogger;
|
|
16
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, openSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import {
|
|
6
|
+
checkExistingServer,
|
|
7
|
+
getLogFileURL,
|
|
8
|
+
readLockFile,
|
|
9
|
+
removeLockFile,
|
|
10
|
+
isProcessAlive,
|
|
11
|
+
GRACEFUL_SHUTDOWN_TIMEOUT
|
|
12
|
+
} from "../../core/dev/lockfile.js";
|
|
13
|
+
import { resolveRoot } from "../../core/config/config.js";
|
|
14
|
+
function formatBackgroundOutput(result) {
|
|
15
|
+
return JSON.stringify(result);
|
|
16
|
+
}
|
|
17
|
+
async function background({
|
|
18
|
+
flags,
|
|
19
|
+
logger
|
|
20
|
+
}) {
|
|
21
|
+
const root = pathToFileURL(resolveRoot(flags.root) + "/");
|
|
22
|
+
const existing = checkExistingServer(root);
|
|
23
|
+
if (existing && !flags.force) {
|
|
24
|
+
logger.info(
|
|
25
|
+
"SKIP_FORMAT",
|
|
26
|
+
`Dev server already running at ${existing.url} (pid ${existing.pid})
|
|
27
|
+
Stop: astro dev stop
|
|
28
|
+
Status: astro dev status
|
|
29
|
+
Logs: astro dev logs`
|
|
30
|
+
);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (existing && flags.force) {
|
|
34
|
+
try {
|
|
35
|
+
process.kill(existing.pid, "SIGTERM");
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
const deadline2 = Date.now() + GRACEFUL_SHUTDOWN_TIMEOUT;
|
|
39
|
+
while (Date.now() < deadline2) {
|
|
40
|
+
if (!isProcessAlive(existing.pid)) break;
|
|
41
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
42
|
+
}
|
|
43
|
+
if (isProcessAlive(existing.pid)) {
|
|
44
|
+
try {
|
|
45
|
+
process.kill(existing.pid, "SIGKILL");
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
removeLockFile(root);
|
|
50
|
+
}
|
|
51
|
+
const args = ["dev"];
|
|
52
|
+
if (flags.port) args.push("--port", String(flags.port));
|
|
53
|
+
if (flags.host != null) {
|
|
54
|
+
if (typeof flags.host === "string") {
|
|
55
|
+
args.push("--host", flags.host);
|
|
56
|
+
} else {
|
|
57
|
+
args.push("--host");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (flags.config) args.push("--config", String(flags.config));
|
|
61
|
+
if (flags.root) args.push("--root", String(flags.root));
|
|
62
|
+
if (flags.allowedHosts) args.push("--allowed-hosts", String(flags.allowedHosts));
|
|
63
|
+
if (flags.experimentalJson) args.push("--experimental-json");
|
|
64
|
+
const logFileURL = getLogFileURL(root);
|
|
65
|
+
const logFilePath = fileURLToPath(logFileURL);
|
|
66
|
+
const dotAstroDir = fileURLToPath(new URL(".astro/", root));
|
|
67
|
+
if (!existsSync(dotAstroDir)) {
|
|
68
|
+
mkdirSync(dotAstroDir, { recursive: true });
|
|
69
|
+
}
|
|
70
|
+
const logFd = openSync(logFilePath, "w");
|
|
71
|
+
const rootPath = fileURLToPath(root);
|
|
72
|
+
const astroBin = resolve(rootPath, "node_modules", ".bin", "astro");
|
|
73
|
+
const child = spawn(astroBin, args, {
|
|
74
|
+
detached: true,
|
|
75
|
+
stdio: ["ignore", logFd, logFd],
|
|
76
|
+
cwd: rootPath,
|
|
77
|
+
env: { ...process.env, ASTRO_DEV_BACKGROUND: "1" }
|
|
78
|
+
});
|
|
79
|
+
child.unref();
|
|
80
|
+
const childPid = child.pid;
|
|
81
|
+
if (!childPid) {
|
|
82
|
+
logger.error("SKIP_FORMAT", "Failed to spawn background dev server process.");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
const timeout = 3e4;
|
|
86
|
+
const deadline = Date.now() + timeout;
|
|
87
|
+
while (Date.now() < deadline) {
|
|
88
|
+
if (!isProcessAlive(childPid)) {
|
|
89
|
+
logger.error("SKIP_FORMAT", "Dev server process exited before becoming ready.");
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
const lockData = readLockFile(root);
|
|
93
|
+
if (lockData && lockData.pid === childPid) {
|
|
94
|
+
logger.info(
|
|
95
|
+
"SKIP_FORMAT",
|
|
96
|
+
`Dev server running at ${lockData.url} (pid ${lockData.pid})
|
|
97
|
+
Stop: astro dev stop
|
|
98
|
+
Status: astro dev status
|
|
99
|
+
Logs: astro dev logs`
|
|
100
|
+
);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
process.kill(childPid, "SIGTERM");
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
removeLockFile(root);
|
|
110
|
+
logger.error("SKIP_FORMAT", `Dev server failed to start within ${timeout / 1e3}s.`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
export {
|
|
114
|
+
background,
|
|
115
|
+
formatBackgroundOutput
|
|
116
|
+
};
|