summaryrefslogtreecommitdiff
path: root/packages/astro
diff options
context:
space:
mode:
Diffstat (limited to 'packages/astro')
-rw-r--r--packages/astro/CHANGELOG.md477
-rw-r--r--packages/astro/astro-jsx.d.ts2
-rwxr-xr-xpackages/astro/astro.js10
-rw-r--r--packages/astro/client-base.d.ts428
-rw-r--r--packages/astro/client-image.d.ts41
-rw-r--r--packages/astro/client.d.ts458
-rw-r--r--packages/astro/components/shiki-themes.js6
-rw-r--r--packages/astro/config.d.ts7
-rw-r--r--packages/astro/config.mjs7
-rw-r--r--packages/astro/content-types.template.d.ts31
-rw-r--r--packages/astro/e2e/astro-envs.test.js4
-rw-r--r--packages/astro/e2e/css-sourcemaps.test.js4
-rw-r--r--packages/astro/e2e/css.test.js4
-rw-r--r--packages/astro/e2e/errors.test.js7
-rw-r--r--packages/astro/e2e/fixtures/client-only/astro.config.mjs8
-rw-r--r--packages/astro/e2e/fixtures/client-only/src/components/preact/PreactCounter.tsx (renamed from packages/astro/e2e/fixtures/client-only/src/components/PreactCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/client-only/src/components/react/ReactCounter.jsx (renamed from packages/astro/e2e/fixtures/client-only/src/components/ReactCounter.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/client-only/src/components/solid/SolidCounter.tsx (renamed from packages/astro/e2e/fixtures/client-only/src/components/SolidCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/client-only/src/components/svelte/SvelteCounter.svelte (renamed from packages/astro/e2e/fixtures/client-only/src/components/SvelteCounter.svelte)0
-rw-r--r--packages/astro/e2e/fixtures/client-only/src/components/vue/VueCounter.vue (renamed from packages/astro/e2e/fixtures/client-only/src/components/VueCounter.vue)0
-rw-r--r--packages/astro/e2e/fixtures/client-only/src/pages/index.astro10
-rw-r--r--packages/astro/e2e/fixtures/errors/astro.config.mjs10
-rw-r--r--packages/astro/e2e/fixtures/errors/src/components/preact/PreactRuntimeError.jsx (renamed from packages/astro/e2e/fixtures/errors/src/components/PreactRuntimeError.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/errors/src/components/preact/PreactSyntaxError.jsx (renamed from packages/astro/e2e/fixtures/errors/src/components/PreactSyntaxError.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/errors/src/components/react/ReactRuntimeError.jsx (renamed from packages/astro/e2e/fixtures/errors/src/components/ReactRuntimeError.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/errors/src/components/react/ReactSyntaxError.jsx (renamed from packages/astro/e2e/fixtures/errors/src/components/ReactSyntaxError.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/errors/src/components/solid/SolidRuntimeError.jsx (renamed from packages/astro/e2e/fixtures/errors/src/components/SolidRuntimeError.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/errors/src/components/solid/SolidSyntaxError.jsx (renamed from packages/astro/e2e/fixtures/errors/src/components/SolidSyntaxError.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteDirectiveError.svelte (renamed from packages/astro/e2e/fixtures/errors/src/components/SvelteDirectiveError.svelte)0
-rw-r--r--packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteRuntimeError.svelte (renamed from packages/astro/e2e/fixtures/errors/src/components/SvelteRuntimeError.svelte)0
-rw-r--r--packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteSyntaxError.svelte (renamed from packages/astro/e2e/fixtures/errors/src/components/SvelteSyntaxError.svelte)0
-rw-r--r--packages/astro/e2e/fixtures/errors/src/components/vue/VueRuntimeError.vue (renamed from packages/astro/e2e/fixtures/errors/src/components/VueRuntimeError.vue)0
-rw-r--r--packages/astro/e2e/fixtures/errors/src/components/vue/VueSyntaxError.vue (renamed from packages/astro/e2e/fixtures/errors/src/components/VueSyntaxError.vue)0
-rw-r--r--packages/astro/e2e/fixtures/errors/src/pages/astro-client-media-error.astro4
-rw-r--r--packages/astro/e2e/fixtures/errors/src/pages/astro-hydration-error.astro4
-rw-r--r--packages/astro/e2e/fixtures/errors/src/pages/preact-runtime-error.astro2
-rw-r--r--packages/astro/e2e/fixtures/errors/src/pages/preact-syntax-error.astro2
-rw-r--r--packages/astro/e2e/fixtures/errors/src/pages/react-runtime-error.astro2
-rw-r--r--packages/astro/e2e/fixtures/errors/src/pages/react-syntax-error.astro2
-rw-r--r--packages/astro/e2e/fixtures/errors/src/pages/solid-runtime-error.astro2
-rw-r--r--packages/astro/e2e/fixtures/errors/src/pages/solid-syntax-error.astro2
-rw-r--r--packages/astro/e2e/fixtures/errors/src/pages/svelte-runtime-error.astro2
-rw-r--r--packages/astro/e2e/fixtures/errors/src/pages/svelte-syntax-error.astro2
-rw-r--r--packages/astro/e2e/fixtures/errors/src/pages/vue-runtime-error.astro2
-rw-r--r--packages/astro/e2e/fixtures/errors/src/pages/vue-syntax-error.astro2
-rw-r--r--packages/astro/e2e/fixtures/multiple-frameworks/astro.config.mjs8
-rw-r--r--packages/astro/e2e/fixtures/multiple-frameworks/src/components/preact/PreactCounter.tsx (renamed from packages/astro/e2e/fixtures/multiple-frameworks/src/components/PreactCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/multiple-frameworks/src/components/react/ReactCounter.jsx (renamed from packages/astro/e2e/fixtures/multiple-frameworks/src/components/ReactCounter.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/multiple-frameworks/src/components/solid/SolidCounter.tsx (renamed from packages/astro/e2e/fixtures/multiple-frameworks/src/components/SolidCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/multiple-frameworks/src/components/svelte/SvelteCounter.svelte (renamed from packages/astro/e2e/fixtures/multiple-frameworks/src/components/SvelteCounter.svelte)0
-rw-r--r--packages/astro/e2e/fixtures/multiple-frameworks/src/components/vue/VueCounter.vue (renamed from packages/astro/e2e/fixtures/multiple-frameworks/src/components/VueCounter.vue)0
-rw-r--r--packages/astro/e2e/fixtures/multiple-frameworks/src/pages/index.astro10
-rw-r--r--packages/astro/e2e/fixtures/nested-in-preact/astro.config.mjs8
-rw-r--r--packages/astro/e2e/fixtures/nested-in-preact/src/components/preact/PreactCounter.tsx (renamed from packages/astro/e2e/fixtures/nested-in-preact/src/components/PreactCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-preact/src/components/react/ReactCounter.jsx (renamed from packages/astro/e2e/fixtures/nested-in-preact/src/components/ReactCounter.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-preact/src/components/solid/SolidCounter.tsx (renamed from packages/astro/e2e/fixtures/nested-in-preact/src/components/SolidCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-preact/src/components/svelte/SvelteCounter.svelte (renamed from packages/astro/e2e/fixtures/nested-in-preact/src/components/SvelteCounter.svelte)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-preact/src/components/vue/VueCounter.vue (renamed from packages/astro/e2e/fixtures/nested-in-preact/src/components/VueCounter.vue)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-preact/src/pages/index.astro10
-rw-r--r--packages/astro/e2e/fixtures/nested-in-react/astro.config.mjs8
-rw-r--r--packages/astro/e2e/fixtures/nested-in-react/src/components/preact/PreactCounter.tsx (renamed from packages/astro/e2e/fixtures/nested-in-react/src/components/PreactCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-react/src/components/react/ReactCounter.jsx (renamed from packages/astro/e2e/fixtures/nested-in-react/src/components/ReactCounter.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-react/src/components/solid/SolidCounter.tsx (renamed from packages/astro/e2e/fixtures/nested-in-react/src/components/SolidCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-react/src/components/svelte/SvelteCounter.svelte (renamed from packages/astro/e2e/fixtures/nested-in-react/src/components/SvelteCounter.svelte)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-react/src/components/vue/VueCounter.vue (renamed from packages/astro/e2e/fixtures/nested-in-react/src/components/VueCounter.vue)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-react/src/pages/index.astro10
-rw-r--r--packages/astro/e2e/fixtures/nested-in-solid/astro.config.mjs8
-rw-r--r--packages/astro/e2e/fixtures/nested-in-solid/src/components/preact/PreactCounter.tsx (renamed from packages/astro/e2e/fixtures/nested-in-solid/src/components/PreactCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-solid/src/components/react/ReactCounter.jsx (renamed from packages/astro/e2e/fixtures/nested-in-solid/src/components/ReactCounter.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-solid/src/components/solid/SolidCounter.tsx (renamed from packages/astro/e2e/fixtures/nested-in-solid/src/components/SolidCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-solid/src/components/svelte/SvelteCounter.svelte (renamed from packages/astro/e2e/fixtures/nested-in-solid/src/components/SvelteCounter.svelte)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-solid/src/components/vue/VueCounter.vue (renamed from packages/astro/e2e/fixtures/nested-in-solid/src/components/VueCounter.vue)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-solid/src/pages/index.astro10
-rw-r--r--packages/astro/e2e/fixtures/nested-in-svelte/astro.config.mjs8
-rw-r--r--packages/astro/e2e/fixtures/nested-in-svelte/src/components/preact/PreactCounter.tsx (renamed from packages/astro/e2e/fixtures/nested-in-svelte/src/components/PreactCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-svelte/src/components/react/ReactCounter.jsx (renamed from packages/astro/e2e/fixtures/nested-in-svelte/src/components/ReactCounter.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-svelte/src/components/solid/SolidCounter.tsx (renamed from packages/astro/e2e/fixtures/nested-in-svelte/src/components/SolidCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-svelte/src/components/svelte/SvelteCounter.svelte (renamed from packages/astro/e2e/fixtures/nested-in-svelte/src/components/SvelteCounter.svelte)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-svelte/src/components/vue/VueCounter.vue (renamed from packages/astro/e2e/fixtures/nested-in-svelte/src/components/VueCounter.vue)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-svelte/src/pages/index.astro10
-rw-r--r--packages/astro/e2e/fixtures/nested-in-vue/astro.config.mjs8
-rw-r--r--packages/astro/e2e/fixtures/nested-in-vue/src/components/preact/PreactCounter.tsx (renamed from packages/astro/e2e/fixtures/nested-in-vue/src/components/PreactCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-vue/src/components/react/ReactCounter.jsx (renamed from packages/astro/e2e/fixtures/nested-in-vue/src/components/ReactCounter.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-vue/src/components/solid/SolidCounter.tsx (renamed from packages/astro/e2e/fixtures/nested-in-vue/src/components/SolidCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-vue/src/components/svelte/SvelteCounter.svelte (renamed from packages/astro/e2e/fixtures/nested-in-vue/src/components/SvelteCounter.svelte)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-vue/src/components/vue/VueCounter.vue (renamed from packages/astro/e2e/fixtures/nested-in-vue/src/components/VueCounter.vue)0
-rw-r--r--packages/astro/e2e/fixtures/nested-in-vue/src/pages/index.astro10
-rw-r--r--packages/astro/e2e/fixtures/nested-recursive/astro.config.mjs8
-rw-r--r--packages/astro/e2e/fixtures/nested-recursive/src/components/preact/PreactCounter.tsx (renamed from packages/astro/e2e/fixtures/nested-recursive/src/components/PreactCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-recursive/src/components/react/ReactCounter.jsx (renamed from packages/astro/e2e/fixtures/nested-recursive/src/components/ReactCounter.jsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-recursive/src/components/solid/SolidCounter.tsx (renamed from packages/astro/e2e/fixtures/nested-recursive/src/components/SolidCounter.tsx)0
-rw-r--r--packages/astro/e2e/fixtures/nested-recursive/src/components/svelte/SvelteCounter.svelte (renamed from packages/astro/e2e/fixtures/nested-recursive/src/components/SvelteCounter.svelte)0
-rw-r--r--packages/astro/e2e/fixtures/nested-recursive/src/components/vue/VueCounter.vue (renamed from packages/astro/e2e/fixtures/nested-recursive/src/components/VueCounter.vue)0
-rw-r--r--packages/astro/e2e/fixtures/nested-recursive/src/pages/index.astro10
-rw-r--r--packages/astro/e2e/fixtures/view-transitions/astro.config.mjs1
-rw-r--r--packages/astro/e2e/multiple-frameworks.test.js8
-rw-r--r--packages/astro/env.d.ts4
-rw-r--r--packages/astro/index.d.ts2
-rw-r--r--packages/astro/package.json68
-rw-r--r--packages/astro/scripts/shiki-gen-themes.mjs13
-rw-r--r--packages/astro/src/@types/astro.ts245
-rw-r--r--packages/astro/src/assets/image-endpoint.ts9
-rw-r--r--packages/astro/src/assets/internal.ts1
-rw-r--r--packages/astro/src/assets/services/noop.ts17
-rw-r--r--packages/astro/src/assets/services/sharp.ts3
-rw-r--r--packages/astro/src/assets/services/vendor/squoosh/impl.ts4
-rw-r--r--packages/astro/src/assets/types.ts2
-rw-r--r--packages/astro/src/assets/vite-plugin-assets.ts40
-rw-r--r--packages/astro/src/cli/add/index.ts8
-rw-r--r--packages/astro/src/cli/build/index.ts4
-rw-r--r--packages/astro/src/cli/check/index.ts419
-rw-r--r--packages/astro/src/cli/check/print.ts119
-rw-r--r--packages/astro/src/cli/dev/index.ts2
-rw-r--r--packages/astro/src/cli/flags.ts3
-rw-r--r--packages/astro/src/cli/index.ts16
-rw-r--r--packages/astro/src/cli/install-package.ts124
-rw-r--r--packages/astro/src/cli/sync/index.ts2
-rw-r--r--packages/astro/src/content/types-generator.ts3
-rw-r--r--packages/astro/src/content/utils.ts15
-rw-r--r--packages/astro/src/content/vite-plugin-content-imports.ts18
-rw-r--r--packages/astro/src/core/README.md17
-rw-r--r--packages/astro/src/core/app/index.ts82
-rw-r--r--packages/astro/src/core/app/node.ts1
-rw-r--r--packages/astro/src/core/app/ssrPipeline.ts28
-rw-r--r--packages/astro/src/core/app/types.ts2
-rw-r--r--packages/astro/src/core/build/buildPipeline.ts163
-rw-r--r--packages/astro/src/core/build/generate.ts260
-rw-r--r--packages/astro/src/core/build/index.ts18
-rw-r--r--packages/astro/src/core/build/internal.ts11
-rw-r--r--packages/astro/src/core/build/plugins/README.md32
-rw-r--r--packages/astro/src/core/build/plugins/index.ts2
-rw-r--r--packages/astro/src/core/build/plugins/plugin-manifest.ts251
-rw-r--r--packages/astro/src/core/build/plugins/plugin-pages.ts14
-rw-r--r--packages/astro/src/core/build/plugins/plugin-prerender.ts2
-rw-r--r--packages/astro/src/core/build/plugins/plugin-renderers.ts2
-rw-r--r--packages/astro/src/core/build/plugins/plugin-ssr.ts237
-rw-r--r--packages/astro/src/core/build/static-build.ts11
-rw-r--r--packages/astro/src/core/compile/compile.ts2
-rw-r--r--packages/astro/src/core/config/config.ts63
-rw-r--r--packages/astro/src/core/config/schema.ts79
-rw-r--r--packages/astro/src/core/config/settings.ts4
-rw-r--r--packages/astro/src/core/cookies/cookies.ts16
-rw-r--r--packages/astro/src/core/cookies/index.ts2
-rw-r--r--packages/astro/src/core/cookies/response.ts2
-rw-r--r--packages/astro/src/core/create-vite.ts39
-rw-r--r--packages/astro/src/core/dev/container.ts6
-rw-r--r--packages/astro/src/core/dev/dev.ts7
-rw-r--r--packages/astro/src/core/endpoint/index.ts135
-rw-r--r--packages/astro/src/core/errors/errors-data.ts56
-rw-r--r--packages/astro/src/core/errors/errors.ts23
-rw-r--r--packages/astro/src/core/errors/index.ts1
-rw-r--r--packages/astro/src/core/errors/userError.ts1
-rw-r--r--packages/astro/src/core/index.ts26
-rw-r--r--packages/astro/src/core/logger/console.ts2
-rw-r--r--packages/astro/src/core/logger/core.ts69
-rw-r--r--packages/astro/src/core/logger/node.ts14
-rw-r--r--packages/astro/src/core/messages.ts9
-rw-r--r--packages/astro/src/core/middleware/namespace.ts1
-rw-r--r--packages/astro/src/core/pipeline.ts153
-rw-r--r--packages/astro/src/core/polyfill.ts76
-rw-r--r--packages/astro/src/core/preview/index.ts11
-rw-r--r--packages/astro/src/core/render/context.ts3
-rw-r--r--packages/astro/src/core/render/core.ts39
-rw-r--r--packages/astro/src/core/render/environment.ts13
-rw-r--r--packages/astro/src/core/render/index.ts13
-rw-r--r--packages/astro/src/core/render/params-and-props.ts1
-rw-r--r--packages/astro/src/core/render/renderer.ts10
-rw-r--r--packages/astro/src/core/render/result.ts34
-rw-r--r--packages/astro/src/core/render/route-cache.ts11
-rw-r--r--packages/astro/src/core/routing/manifest/create.ts7
-rw-r--r--packages/astro/src/core/routing/validation.ts19
-rw-r--r--packages/astro/src/core/sync/index.ts13
-rw-r--r--packages/astro/src/core/util.ts1
-rw-r--r--packages/astro/src/integrations/astroFeaturesValidation.ts157
-rw-r--r--packages/astro/src/integrations/index.ts102
-rw-r--r--packages/astro/src/jsx/server.ts20
-rw-r--r--packages/astro/src/prerender/routing.ts16
-rw-r--r--packages/astro/src/prerender/utils.ts13
-rw-r--r--packages/astro/src/runtime/README.md1
-rw-r--r--packages/astro/src/runtime/compiler/index.ts20
-rw-r--r--packages/astro/src/runtime/server/endpoint.ts65
-rw-r--r--packages/astro/src/runtime/server/hydration.ts6
-rw-r--r--packages/astro/src/runtime/server/index.ts2
-rw-r--r--packages/astro/src/runtime/server/render/component.ts16
-rw-r--r--packages/astro/src/runtime/server/render/page.ts3
-rw-r--r--packages/astro/src/runtime/server/render/util.ts8
-rw-r--r--packages/astro/src/runtime/server/response.ts80
-rw-r--r--packages/astro/src/runtime/server/util.ts30
-rw-r--r--packages/astro/src/vite-plugin-astro-postprocess/index.ts3
-rw-r--r--packages/astro/src/vite-plugin-astro-server/devPipeline.ts91
-rw-r--r--packages/astro/src/vite-plugin-astro-server/environment.ts13
-rw-r--r--packages/astro/src/vite-plugin-astro-server/index.ts16
-rw-r--r--packages/astro/src/vite-plugin-astro-server/plugin.ts9
-rw-r--r--packages/astro/src/vite-plugin-astro-server/request.ts22
-rw-r--r--packages/astro/src/vite-plugin-astro-server/route.ts174
-rw-r--r--packages/astro/src/vite-plugin-head/index.ts2
-rw-r--r--packages/astro/src/vite-plugin-inject-env-ts/index.ts28
-rw-r--r--packages/astro/src/vite-plugin-jsx/import-source.ts59
-rw-r--r--packages/astro/src/vite-plugin-jsx/index.ts251
-rw-r--r--packages/astro/src/vite-plugin-markdown/index.ts49
-rw-r--r--packages/astro/src/vite-plugin-mdx/README.md (renamed from packages/astro/src/vite-plugin-jsx/README.md)0
-rw-r--r--packages/astro/src/vite-plugin-mdx/index.ts130
-rw-r--r--packages/astro/src/vite-plugin-mdx/tag.ts (renamed from packages/astro/src/vite-plugin-jsx/tag.ts)4
-rw-r--r--packages/astro/test/0-css.test.js40
-rw-r--r--packages/astro/test/alias-tsconfig-baseurl-only.test.js2
-rw-r--r--packages/astro/test/alias-tsconfig.test.js2
-rw-r--r--packages/astro/test/asset-url-base.test.js4
-rw-r--r--packages/astro/test/astro-assets-prefix.test.js6
-rw-r--r--packages/astro/test/astro-class-list.test.js41
-rw-r--r--packages/astro/test/astro-client-only.test.js4
-rw-r--r--packages/astro/test/astro-css-bundling.test.js5
-rw-r--r--packages/astro/test/astro-directives.test.js6
-rw-r--r--packages/astro/test/astro-envs.test.js4
-rw-r--r--packages/astro/test/astro-external-files.test.js24
-rw-r--r--packages/astro/test/astro-get-static-paths.test.js7
-rw-r--r--packages/astro/test/astro-global.test.js6
-rw-r--r--packages/astro/test/astro-head.test.js2
-rw-r--r--packages/astro/test/astro-partial-html.test.js2
-rw-r--r--packages/astro/test/build-assets.test.js6
-rw-r--r--packages/astro/test/cli.test.js4
-rw-r--r--packages/astro/test/component-library.test.js2
-rw-r--r--packages/astro/test/config-vite-css-target.test.js8
-rw-r--r--packages/astro/test/config-vite.test.js6
-rw-r--r--packages/astro/test/content-collections-render.test.js4
-rw-r--r--packages/astro/test/core-image.test.js21
-rw-r--r--packages/astro/test/css-import-as-inline.test.js2
-rw-r--r--packages/astro/test/css-no-code-split.test.js6
-rw-r--r--packages/astro/test/css-order-import.test.js4
-rw-r--r--packages/astro/test/css-order-layout.test.js2
-rw-r--r--packages/astro/test/css-order.test.js2
-rw-r--r--packages/astro/test/custom-elements.test.js70
-rw-r--r--packages/astro/test/dev-routing.test.js8
-rw-r--r--packages/astro/test/featuresSupport.test.js55
-rw-r--r--packages/astro/test/fixtures/api-routes/src/pages/binary.dat.ts2
-rw-r--r--packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js2
-rw-r--r--packages/astro/test/fixtures/astro-assets-prefix/astro.config.mjs5
-rw-r--r--packages/astro/test/fixtures/astro-assets/src/pages/index.astro4
-rw-r--r--packages/astro/test/fixtures/astro-basic/astro.config.mjs2
-rw-r--r--packages/astro/test/fixtures/astro-class-list/src/components/Span.astro2
-rw-r--r--packages/astro/test/fixtures/astro-class-list/src/pages/component.astro8
-rw-r--r--packages/astro/test/fixtures/astro-class-list/src/pages/index.astro4
-rw-r--r--packages/astro/test/fixtures/astro-component-code/src/pages/basic.astro2
-rw-r--r--packages/astro/test/fixtures/astro-component-code/src/pages/css-theme.astro2
-rw-r--r--packages/astro/test/fixtures/astro-component-code/src/pages/custom-theme.astro2
-rw-r--r--packages/astro/test/fixtures/astro-component-code/src/pages/imported.astro2
-rw-r--r--packages/astro/test/fixtures/astro-component-code/src/pages/inline.astro2
-rw-r--r--packages/astro/test/fixtures/astro-component-code/src/pages/no-lang.astro2
-rw-r--r--packages/astro/test/fixtures/astro-component-code/src/pages/wrap-false.astro2
-rw-r--r--packages/astro/test/fixtures/astro-component-code/src/pages/wrap-null.astro2
-rw-r--r--packages/astro/test/fixtures/astro-component-code/src/pages/wrap-true.astro2
-rw-r--r--packages/astro/test/fixtures/astro-cookies/src/pages/early-return.astro2
-rw-r--r--packages/astro/test/fixtures/astro-cookies/src/pages/get-json.astro2
-rw-r--r--packages/astro/test/fixtures/astro-cookies/src/pages/set-prefs.js2
-rw-r--r--packages/astro/test/fixtures/astro-get-static-paths/src/pages/data/[slug].json.ts2
-rw-r--r--packages/astro/test/fixtures/astro-get-static-paths/src/pages/nested-arrays/[slug].astro8
-rw-r--r--packages/astro/test/fixtures/astro-markdown-frontmatter-injection/src/pages/glob.json.js2
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/headings-glob.json.js2
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js2
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/vite-env-vars-glob.json.js2
-rw-r--r--packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro2
-rw-r--r--packages/astro/test/fixtures/astro-slots-nested/astro.config.mjs6
-rw-r--r--packages/astro/test/fixtures/astro-slots-nested/src/components/preact/PassesChildrenP.tsx (renamed from packages/astro/test/fixtures/astro-slots-nested/src/components/PassesChildrenP.tsx)0
-rw-r--r--packages/astro/test/fixtures/astro-slots-nested/src/components/react/Inner.tsx (renamed from packages/astro/test/fixtures/astro-slots-nested/src/components/Inner.tsx)0
-rw-r--r--packages/astro/test/fixtures/astro-slots-nested/src/components/react/Parent.jsx (renamed from packages/astro/test/fixtures/astro-slots-nested/src/components/Parent.jsx)0
-rw-r--r--packages/astro/test/fixtures/astro-slots-nested/src/components/react/PassesChildren.tsx (renamed from packages/astro/test/fixtures/astro-slots-nested/src/components/PassesChildren.tsx)0
-rw-r--r--packages/astro/test/fixtures/astro-slots-nested/src/components/solid/PassesChildrenS.tsx (renamed from packages/astro/test/fixtures/astro-slots-nested/src/components/PassesChildrenS.tsx)0
-rw-r--r--packages/astro/test/fixtures/astro-slots-nested/src/pages/component-slot.astro2
-rw-r--r--packages/astro/test/fixtures/astro-slots-nested/src/pages/hidden-nested.astro4
-rw-r--r--packages/astro/test/fixtures/astro-slots-nested/src/pages/server-component-nested.astro6
-rw-r--r--packages/astro/test/fixtures/code-component/src/pages/index.astro2
-rw-r--r--packages/astro/test/fixtures/content-collection-references/astro.config.mjs3
-rw-r--r--packages/astro/test/fixtures/content-collection-references/src/content/banners/welcome.json2
-rw-r--r--packages/astro/test/fixtures/content-collection-references/src/pages/welcome-data.json.js2
-rw-r--r--packages/astro/test/fixtures/content-collections/src/pages/collections.json.js2
-rw-r--r--packages/astro/test/fixtures/content-collections/src/pages/entries.json.js2
-rw-r--r--packages/astro/test/fixtures/core-image-base/src/content/blog/one.md2
-rw-r--r--packages/astro/test/fixtures/core-image-base/tsconfig.json9
-rw-r--r--packages/astro/test/fixtures/core-image-ssg/tsconfig.json9
-rw-r--r--packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts2
-rw-r--r--packages/astro/test/fixtures/core-image/src/content/blog/one.md6
-rw-r--r--packages/astro/test/fixtures/core-image/tsconfig.json9
-rw-r--r--packages/astro/test/fixtures/custom-elements/astro.config.mjs6
-rw-r--r--packages/astro/test/fixtures/custom-elements/my-component-lib/hydration-polyfill.js1
-rw-r--r--packages/astro/test/fixtures/custom-elements/my-component-lib/index.js31
-rw-r--r--packages/astro/test/fixtures/custom-elements/my-component-lib/package.json13
-rw-r--r--packages/astro/test/fixtures/custom-elements/my-component-lib/polyfill.js2
-rw-r--r--packages/astro/test/fixtures/custom-elements/my-component-lib/server.js30
-rw-r--r--packages/astro/test/fixtures/custom-elements/my-component-lib/shim.js28
-rw-r--r--packages/astro/test/fixtures/custom-elements/package.json9
-rw-r--r--packages/astro/test/fixtures/custom-elements/src/components/my-element.js13
-rw-r--r--packages/astro/test/fixtures/custom-elements/src/pages/ctr.astro16
-rw-r--r--packages/astro/test/fixtures/custom-elements/src/pages/index.astro15
-rw-r--r--packages/astro/test/fixtures/custom-elements/src/pages/nested.astro11
-rw-r--r--packages/astro/test/fixtures/custom-elements/src/pages/nossr.astro14
-rw-r--r--packages/astro/test/fixtures/data-collections/src/pages/authors/[id].json.js2
-rw-r--r--packages/astro/test/fixtures/data-collections/src/pages/authors/all.json.js2
-rw-r--r--packages/astro/test/fixtures/data-collections/src/pages/translations/[lang].json.js2
-rw-r--r--packages/astro/test/fixtures/data-collections/src/pages/translations/all.json.js2
-rw-r--r--packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts2
-rw-r--r--packages/astro/test/fixtures/jsx/astro.config.mjs19
-rw-r--r--packages/astro/test/fixtures/jsx/package.json1
-rw-r--r--packages/astro/test/fixtures/jsx/src/components/Content.mdx5
-rw-r--r--packages/astro/test/fixtures/jsx/src/components/Frameworks.jsx28
-rw-r--r--packages/astro/test/fixtures/jsx/src/components/preact/PreactCounter.tsx (renamed from packages/astro/test/fixtures/jsx/src/components/PreactCounter.tsx)0
-rw-r--r--packages/astro/test/fixtures/jsx/src/components/react/ReactCounter.jsx (renamed from packages/astro/test/fixtures/jsx/src/components/ReactCounter.jsx)2
-rw-r--r--packages/astro/test/fixtures/jsx/src/components/solid/SolidCounter.jsx (renamed from packages/astro/test/fixtures/jsx/src/components/SolidCounter.jsx)0
-rw-r--r--packages/astro/test/fixtures/jsx/src/pages/frameworks.astro34
-rw-r--r--packages/astro/test/fixtures/middleware-dev/src/middleware.js2
-rw-r--r--packages/astro/test/fixtures/middleware-dev/src/pages/api/endpoint.js2
-rw-r--r--packages/astro/test/fixtures/middleware-ssg/src/middleware.js2
-rw-r--r--packages/astro/test/fixtures/non-html-pages/src/pages/about-object.json.ts12
-rw-r--r--packages/astro/test/fixtures/non-html-pages/src/pages/about.json.ts19
-rw-r--r--packages/astro/test/fixtures/non-html-pages/src/pages/placeholder-object.png.ts18
-rw-r--r--packages/astro/test/fixtures/non-html-pages/src/pages/placeholder.png.ts7
-rw-r--r--packages/astro/test/fixtures/preact-compat-component/packages/react-lib/index.js2
-rw-r--r--packages/astro/test/fixtures/preact-compat-component/src/components/Counter.jsx1
-rw-r--r--packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[...slug].json.ts2
-rw-r--r--packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[foo]-[bar].json.ts2
-rw-r--r--packages/astro/test/fixtures/ssr-api-route-custom-404/src/pages/api/route.js2
-rw-r--r--packages/astro/test/fixtures/ssr-api-route/src/pages/binary.js6
-rw-r--r--packages/astro/test/fixtures/ssr-api-route/src/pages/context/[param].js2
-rw-r--r--packages/astro/test/fixtures/ssr-api-route/src/pages/food-object.json.js10
-rw-r--r--packages/astro/test/fixtures/ssr-api-route/src/pages/food.json.js10
-rw-r--r--packages/astro/test/fixtures/ssr-api-route/src/pages/login.js2
-rw-r--r--packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js2
-rw-r--r--packages/astro/test/fixtures/ssr-locals/src/pages/api.js2
-rw-r--r--packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/data/[slug].json.ts2
-rw-r--r--packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/nested-arrays/[slug].astro10
-rw-r--r--packages/astro/test/fixtures/ssr-preview/preview.mjs2
-rw-r--r--packages/astro/test/fixtures/ssr-redirect/src/middleware.ts2
-rw-r--r--packages/astro/test/fixtures/static-build-code-component/src/pages/index.astro2
-rw-r--r--packages/astro/test/fixtures/static-build-dir/src/pages/index.astro2
-rw-r--r--packages/astro/test/fixtures/static-build/src/pages/company.json.ts4
-rw-r--r--packages/astro/test/fixtures/static-build/src/pages/data/[slug].json.ts4
-rw-r--r--packages/astro/test/fixtures/static-build/src/pages/posts.json.js2
-rw-r--r--packages/astro/test/fixtures/tailwindcss/src/pages/markdown-page.md11
-rw-r--r--packages/astro/test/fixtures/with-endpoint-routes/src/pages/[slug].json.ts2
-rw-r--r--packages/astro/test/fixtures/with-endpoint-routes/src/pages/data/[slug].json.ts2
-rw-r--r--packages/astro/test/fixtures/with-endpoint-routes/src/pages/home.json.ts2
-rw-r--r--packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/[image].svg.ts2
-rw-r--r--packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/hex.ts2
-rw-r--r--packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/static.svg.ts2
-rw-r--r--packages/astro/test/glob-pages-css.test.js2
-rw-r--r--packages/astro/test/head-injection.test.js2
-rw-r--r--packages/astro/test/jsx.test.js28
-rw-r--r--packages/astro/test/lazy-layout.test.js6
-rw-r--r--packages/astro/test/minification-html.test.js8
-rw-r--r--packages/astro/test/non-html-pages.test.js23
-rw-r--r--packages/astro/test/page-level-styles.test.js2
-rw-r--r--packages/astro/test/postcss.test.js2
-rw-r--r--packages/astro/test/preact-compat-component.test.js29
-rw-r--r--packages/astro/test/preview-routing.test.js8
-rw-r--r--packages/astro/test/public-base-404.test.js2
-rw-r--r--packages/astro/test/remote-css.test.js2
-rw-r--r--packages/astro/test/root-srcdir-css.test.js2
-rw-r--r--packages/astro/test/scoped-style-strategy.test.js40
-rw-r--r--packages/astro/test/ssr-404-500-pages.test.js2
-rw-r--r--packages/astro/test/ssr-api-route.test.js14
-rw-r--r--packages/astro/test/ssr-assets.test.js2
-rw-r--r--packages/astro/test/ssr-dynamic.test.js2
-rw-r--r--packages/astro/test/ssr-hoisted-script.test.js64
-rw-r--r--packages/astro/test/ssr-manifest.test.js3
-rw-r--r--packages/astro/test/ssr-prerender-get-static-paths.test.js12
-rw-r--r--packages/astro/test/ssr-split-manifest.test.js4
-rw-r--r--packages/astro/test/static-build.test.js4
-rw-r--r--packages/astro/test/tailwindcss.test.js9
-rw-r--r--packages/astro/test/test-adapter.js9
-rw-r--r--packages/astro/test/test-utils.js16
-rw-r--r--packages/astro/test/units/content-collections/get-entry-type.test.js21
-rw-r--r--packages/astro/test/units/cookies/delete.test.js2
-rw-r--r--packages/astro/test/units/cookies/get.test.js14
-rw-r--r--packages/astro/test/units/dev/collections-mixed-content-errors.test.js2
-rw-r--r--packages/astro/test/units/integrations/api.test.js186
-rw-r--r--packages/astro/test/units/render/components.test.js66
-rw-r--r--packages/astro/test/units/render/head.test.js9
-rw-r--r--packages/astro/test/units/render/jsx.test.js9
-rw-r--r--packages/astro/test/units/routing/route-matching.test.js10
-rw-r--r--packages/astro/test/units/vite-plugin-astro-server/request.test.js30
-rw-r--r--packages/astro/test/virtual-astro-file.test.js6
-rw-r--r--packages/astro/tsconfig.json4
-rw-r--r--packages/astro/tsconfigs/base.json19
-rw-r--r--packages/astro/tsconfigs/strict.json5
382 files changed, 4399 insertions, 3525 deletions
diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md
index f26dae2f7..25f57c8a3 100644
--- a/packages/astro/CHANGELOG.md
+++ b/packages/astro/CHANGELOG.md
@@ -1,5 +1,482 @@
# astro
+## 3.0.0-beta.4
+
+### Patch Changes
+
+- [#8139](https://github.com/withastro/astro/pull/8139) [`db39206cb`](https://github.com/withastro/astro/commit/db39206cbb85b034859ac416179f141184bb2bff) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Use `undici` for File changeset for Node 16 compatibility
+
+## 3.0.0-beta.3
+
+### Major Changes
+
+- [#8113](https://github.com/withastro/astro/pull/8113) [`2484dc408`](https://github.com/withastro/astro/commit/2484dc4080e5cd84b9a53648a1de426d7c907be2) Thanks [@Princesseuh](https://github.com/Princesseuh)! - This import alias is no longer included by default with astro:assets. If you were using this alias with experimental assets, you must convert them to relative file paths, or create your own [import aliases](https://docs.astro.build/en/guides/aliases/).
+
+ ```diff
+ ---
+ // src/pages/posts/post-1.astro
+ - import rocket from '~/assets/rocket.png'
+ + import rocket from '../../assets/rocket.png';
+ ---
+ ```
+
+- [#7979](https://github.com/withastro/astro/pull/7979) [`dbc97b121`](https://github.com/withastro/astro/commit/dbc97b121f42583728f1cdfdbf14575fda943f5b) Thanks [@bluwy](https://github.com/bluwy)! - Export experimental `dev`, `build`, `preview`, and `sync` APIs from `astro`. These APIs allow you to run Astro's commands programmatically, and replaces the previous entry point that runs the Astro CLI.
+
+ While these APIs are experimental, the inline config parameter is relatively stable without foreseeable changes. However, the returned results of these APIs are more likely to change in the future.
+
+ ```ts
+ import { dev, build, preview, sync, type AstroInlineConfig } from 'astro';
+
+ // Inline Astro config object.
+ // Provide a path to a configuration file to load or set options directly inline.
+ const inlineConfig: AstroInlineConfig = {
+ // Inline-specific options...
+ configFile: './astro.config.mjs',
+ logLevel: 'info',
+ // Standard Astro config options...
+ site: 'https://example.com',
+ };
+
+ // Start the Astro dev server
+ const devServer = await dev(inlineConfig);
+ await devServer.stop();
+
+ // Build your Astro project
+ await build(inlineConfig);
+
+ // Preview your built project
+ const previewServer = await preview(inlineConfig);
+ await previewServer.stop();
+
+ // Generate types for your Astro project
+ await sync(inlineConfig);
+ ```
+
+- [#8085](https://github.com/withastro/astro/pull/8085) [`68efd4a8b`](https://github.com/withastro/astro/commit/68efd4a8b29f248397667801465b3152dc98e9a7) Thanks [@bluwy](https://github.com/bluwy)! - Remove exports for `astro/internal/*` and `astro/runtime/server/*` in favour of `astro/runtime/*`. Add new `astro/compiler-runtime` export for compiler-specific runtime code.
+
+ These are exports for Astro's internal API and should not affect your project, but if you do use these entrypoints, you can migrate like below:
+
+ ```diff
+ - import 'astro/internal/index.js';
+ + import 'astro/runtime/server/index.js';
+
+ - import 'astro/server/index.js';
+ + import 'astro/runtime/server/index.js';
+ ```
+
+ ```diff
+ import { transform } from '@astrojs/compiler';
+
+ const result = await transform(source, {
+ - internalURL: 'astro/runtime/server/index.js',
+ + internalURL: 'astro/compiler-runtime',
+ // ...
+ });
+ ```
+
+- [#8030](https://github.com/withastro/astro/pull/8030) [`5208a3c8f`](https://github.com/withastro/astro/commit/5208a3c8fefcec7694857fb344af351f4631fc34) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Removed duplicate `astro/dist/jsx` export. Please use the `astro/jsx` export instead
+
+- [#8118](https://github.com/withastro/astro/pull/8118) [`8a5b0c1f3`](https://github.com/withastro/astro/commit/8a5b0c1f3a4be6bb62db66ec70144109ff5b4c59) Thanks [@lilnasy](https://github.com/lilnasy)! - Astro is smarter about CSS! Small stylesheets are now inlined by default, and no longer incur the cost of additional requests to your server. Your visitors will have to wait less before they see your pages, especially those in remote locations or in a subway.
+
+ This may not be news to you if you had opted-in via the `build.inlineStylesheets` configuration. Stabilized in Astro 2.6 and set to "auto" by default for Starlight, this configuration allows you to reduce the number of requests for stylesheets by inlining them into <style> tags. The new default is "auto", which selects assets smaller than 4kB and includes them in the initial response.
+
+ To go back to the previous default behavior, change `build.inlineStylesheets` to "never".
+
+ ```ts
+ import { defineConfig } from 'astro/config';
+
+ export default defineConfig({
+ build: {
+ inlineStylesheets: 'never',
+ },
+ });
+ ```
+
+- [#7921](https://github.com/withastro/astro/pull/7921) [`b76c166bd`](https://github.com/withastro/astro/commit/b76c166bdd8e28683f62806aef968d1e0c3b06d9) Thanks [@Princesseuh](https://github.com/Princesseuh)! - `astro:assets` is now enabled by default. If you were previously using the `experimental.assets` flag, please remove it from your config. Also note that the previous `@astrojs/image` integration is incompatible, and must be removed.
+
+ This also brings two important changes to using images in Astro:
+
+ - New ESM shape: importing an image will now return an object with different properties describing the image such as its path, format and dimensions. This is a breaking change and may require you to update your existing images.
+ - In Markdown, MDX, and Markdoc, the `![]()` syntax will now resolve relative images located anywhere in your project in addition to remote images and images stored in the `public/` folder. This notably unlocks storing images next to your content.
+
+ Please see our existing [Assets page in Docs](https://docs.astro.build/en/guides/assets/) for more information about using `astro:assets`.
+
+### Minor Changes
+
+- [#8101](https://github.com/withastro/astro/pull/8101) [`ea7ff5177`](https://github.com/withastro/astro/commit/ea7ff5177dbcd7b2508cb1eef1b22b8ee1f47079) Thanks [@matthewp](https://github.com/matthewp)! - `astro:`namespace aliases for middleware and components
+
+ This adds aliases of `astro:middleware` and `astro:components` for the middleware and components modules. This is to make our documentation consistent between are various modules, where some are virtual modules and others are not. Going forward new built-in modules will use this namespace.
+
+### Patch Changes
+
+- [#8128](https://github.com/withastro/astro/pull/8128) [`c2c71d90c`](https://github.com/withastro/astro/commit/c2c71d90c264a2524f99e0373ab59015f23ad4b1) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Update error message when Sharp couldn't be found (tends to happen on pnpm notably)
+
+- [#8092](https://github.com/withastro/astro/pull/8092) [`7177f7579`](https://github.com/withastro/astro/commit/7177f7579b6e866f0fd895b3fd079d8ba330b1a9) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Ensure dotfiles are cleaned during static builds
+
+- [#8070](https://github.com/withastro/astro/pull/8070) [`097a8e4e9`](https://github.com/withastro/astro/commit/097a8e4e916c7df18eafdaa6c8d6ce2991c17ab6) Thanks [@lilnasy](https://github.com/lilnasy)! - Fix a handful of edge cases with prerendered 404/500 pages
+
+- [#8078](https://github.com/withastro/astro/pull/8078) [`2540feedb`](https://github.com/withastro/astro/commit/2540feedb06785d5a20eecc3668849f147d778d4) Thanks [@alexanderniebuhr](https://github.com/alexanderniebuhr)! - Reimplement https://github.com/withastro/astro/pull/7509 to correctly emit pre-rendered pages now that `build.split` is deprecated and this configuration has been moved to `functionPerRoute` inside the adapter.
+
+- [#8105](https://github.com/withastro/astro/pull/8105) [`0e0fa605d`](https://github.com/withastro/astro/commit/0e0fa605d109cc91e08a1ae1cc560ea240fe631b) Thanks [@martrapp](https://github.com/martrapp)! - ViewTransition: bug fix for lost scroll position in browser history
+
+- [#7778](https://github.com/withastro/astro/pull/7778) [`d6b494376`](https://github.com/withastro/astro/commit/d6b4943764989c0e89df2d6875cd19691566dfb3) Thanks [@y-nk](https://github.com/y-nk)! - Added support for optimizing remote images from authorized sources when using `astro:assets`. This comes with two new parameters to specify which domains (`image.domains`) and host patterns (`image.remotePatterns`) are authorized for remote images.
+
+ For example, the following configuration will only allow remote images from `astro.build` to be optimized:
+
+ ```ts
+ // astro.config.mjs
+ export default defineConfig({
+ image: {
+ domains: ['astro.build'],
+ },
+ });
+ ```
+
+ The following configuration will only allow remote images from HTTPS hosts:
+
+ ```ts
+ // astro.config.mjs
+ export default defineConfig({
+ image: {
+ remotePatterns: [{ protocol: 'https' }],
+ },
+ });
+ ```
+
+- [#8072](https://github.com/withastro/astro/pull/8072) [`4477bb41c`](https://github.com/withastro/astro/commit/4477bb41c8ed688785c545731ef5b184b629f4e5) Thanks [@matthewp](https://github.com/matthewp)! - Update Astro types to reflect that compress defaults to true
+
+- [#8130](https://github.com/withastro/astro/pull/8130) [`3e834293d`](https://github.com/withastro/astro/commit/3e834293d47ab2761a7aa013916e8371871efb7f) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Add some polyfills for Stackblitz until they support Node 18. Running Astro on Node 16 is still not officially supported, however.
+
+- Updated dependencies [[`3e834293d`](https://github.com/withastro/astro/commit/3e834293d47ab2761a7aa013916e8371871efb7f)]:
+ - @astrojs/telemetry@3.0.0-beta.2
+
+## 3.0.0-beta.2
+
+### Patch Changes
+
+- Updated dependencies [[`2aa6d8ace`](https://github.com/withastro/astro/commit/2aa6d8ace398a41c2dec5473521d758816b08191)]:
+ - @astrojs/internal-helpers@0.2.0-beta.1
+
+## 3.0.0-beta.1
+
+### Major Changes
+
+- [#7952](https://github.com/withastro/astro/pull/7952) [`3c3100851`](https://github.com/withastro/astro/commit/3c31008519ce68b5b1b1cb23b71fbe0a2d506882) Thanks [@astrobot-houston](https://github.com/astrobot-houston)! - Remove support for `Astro.__renderMarkdown` which is used by `@astrojs/markdown-component`.
+
+ The `<Markdown />` component was deprecated in Astro v1 and is completely removed in v3. This integration must now be removed from your project.
+
+ As an alternative, you can use community packages that provide a similar component like https://github.com/natemoo-re/astro-remote instead.
+
+- [#8019](https://github.com/withastro/astro/pull/8019) [`34cb20021`](https://github.com/withastro/astro/commit/34cb2002161ba88df6bcb72fecfd12ed867c134b) Thanks [@bluwy](https://github.com/bluwy)! - Remove backwards-compatible kebab-case transform for camelCase CSS variable names passed to the `style` attribute. If you were relying on the kebab-case transform in your styles, make sure to use the camelCase version to prevent missing styles. For example:
+
+ ```astro
+ ---
+ const myValue = 'red';
+ ---
+
+ <!-- input -->
+ <div style={{ '--myValue': myValue }}></div>
+
+ <!-- output (before) -->
+ <div style="--my-value:var(--myValue);--myValue:red"></div>
+
+ <!-- output (after) -->
+ <div style="--myValue:red"></div>
+ ```
+
+ ```diff
+ <style>
+ div {
+ - color: var(--my-value);
+ + color: var(--myValue);
+ }
+ </style>
+ ```
+
+- [#7893](https://github.com/withastro/astro/pull/7893) [`7bd1b86f8`](https://github.com/withastro/astro/commit/7bd1b86f85c06fdde0a1ed9146d01bac69990671) Thanks [@ematipico](https://github.com/ematipico)! - Implements a new scope style strategy called `"attribute"`. When enabled, styles are applied using `data-*` attributes.
+
+ The **default** value of `scopedStyleStrategy` is `"attribute"`.
+
+ If you want to use the previous behaviour, you have to use the `"where"` option:
+
+ ```diff
+ import { defineConfig } from 'astro/config';
+
+ export default defineConfig({
+ + scopedStyleStrategy: 'where',
+ });
+ ```
+
+- [#7924](https://github.com/withastro/astro/pull/7924) [`519a1c4e8`](https://github.com/withastro/astro/commit/519a1c4e8407c7abcb8d879b67a9f4b960652cae) Thanks [@matthewp](https://github.com/matthewp)! - Astro's JSX handling has been refactored with better support for each framework.
+
+ Previously, Astro automatically scanned your components to determine which framework-specific transformations should be used. In practice, supporting advanced features like Fast Refresh with this approach proved difficult.
+
+ Now, Astro determines which framework to use with `include` and `exclude` config options where you can specify files and folders on a per-framework basis. When using multiple JSX frameworks in the same project, users should manually control which files belong to each framework using the `include` and `exclude` options.
+
+ ```js
+ export default defineConfig({
+ // The `include` config is only needed in projects that use multiple JSX frameworks;
+ // if only using one no extra config is needed.
+ integrations: [
+ preact({
+ include: ['**/preact/*'],
+ }),
+ react({
+ include: ['**/react/*'],
+ }),
+ solid({
+ include: ['**/solid/*'],
+ }),
+ ],
+ });
+ ```
+
+- [#7878](https://github.com/withastro/astro/pull/7878) [`0f637c71e`](https://github.com/withastro/astro/commit/0f637c71e511cb4c51712128d217a26c8eee4d40) Thanks [@bluwy](https://github.com/bluwy)! - The value of `import.meta.env.BASE_URL`, which is derived from the `base` option, will no longer have a trailing slash added by default or when `trailingSlash: "ignore"` is set. The existing behavior of `base` in combination with `trailingSlash: "always"` or `trailingSlash: "never"` is unchanged.
+
+ If your `base` already has a trailing slash, no change is needed.
+
+ If your `base` does not have a trailing slash, add one to preserve the previous behaviour:
+
+ ```diff
+ // astro.config.mjs
+ - base: 'my-base',
+ + base: 'my-base/',
+ ```
+
+### Minor Changes
+
+- [#8012](https://github.com/withastro/astro/pull/8012) [`866ed4098`](https://github.com/withastro/astro/commit/866ed4098edffb052239cdb26e076cf8db61b1d9) Thanks [@ematipico](https://github.com/ematipico)! - Add a new `astro/errors` module. Developers can import `AstroUserError`, and provide a `message` and an optional `hint`
+
+### Patch Changes
+
+- [#7998](https://github.com/withastro/astro/pull/7998) [`65c354969`](https://github.com/withastro/astro/commit/65c354969e6fe0ef6d622e8f4c545e2f717ce8c6) Thanks [@bluwy](https://github.com/bluwy)! - Call `astro sync` once before calling `astro check`
+
+- [#7952](https://github.com/withastro/astro/pull/7952) [`70f34f5a3`](https://github.com/withastro/astro/commit/70f34f5a355f42526ee9e5355f3de8e510002ea2) Thanks [@astrobot-houston](https://github.com/astrobot-houston)! - Remove StreamingCompatibleResponse polyfill
+
+- [#8011](https://github.com/withastro/astro/pull/8011) [`5b1e39ef6`](https://github.com/withastro/astro/commit/5b1e39ef6ec6dcebea96584f95d9530bd9aa715d) Thanks [@bluwy](https://github.com/bluwy)! - Move hoisted script analysis optimization behind the `experimental.optimizeHoistedScript` option
+
+- Updated dependencies [[`b675acb2a`](https://github.com/withastro/astro/commit/b675acb2aa820448e9c0d363339a37fbac873215)]:
+ - @astrojs/telemetry@3.0.0-beta.1
+
+## 3.0.0-beta.0
+
+### Major Changes
+
+- [`1eae2e3f7`](https://github.com/withastro/astro/commit/1eae2e3f7d693c9dfe91c8ccfbe606d32bf2fb81) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Remove support for Node 16. The lowest supported version by Astro and all integrations is now v18.14.1. As a reminder, Node 16 will be deprecated on the 11th September 2023.
+
+- [`76ddef19c`](https://github.com/withastro/astro/commit/76ddef19ccab6e5f7d3a5740cd41acf10e334b38) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Removed automatic flattening of `getStaticPaths` result. `.flatMap` and `.flat` should now be used to ensure that you're returning a flat array.
+
+- [`3fdf509b2`](https://github.com/withastro/astro/commit/3fdf509b2731a9b2f972d89291e57cf78d62c769) Thanks [@ematipico](https://github.com/ematipico)! - The `build.split` and `build.excludeMiddleware` configuration options are deprecated and have been replaced by options in the adapter config.
+
+ If your config includes the `build.excludeMiddleware` option, replace it with `edgeMiddleware` in your adapter options:
+
+ ```diff
+ import { defineConfig } from "astro/config";
+ import netlify from "@astrojs/netlify/functions";
+
+ export default defineConfig({
+ build: {
+ - excludeMiddleware: true
+ },
+ adapter: netlify({
+ + edgeMiddleware: true
+ }),
+ });
+ ```
+
+ If your config includes the `build.split` option, replace it with `functionPerRoute` in your adapter options:
+
+ ```diff
+ import { defineConfig } from "astro/config";
+ import netlify from "@astrojs/netlify/functions";
+
+ export default defineConfig({
+ build: {
+ - split: true
+ },
+ adapter: netlify({
+ + functionPerRoute: true
+ }),
+ });
+ ```
+
+- [`2f951cd40`](https://github.com/withastro/astro/commit/2f951cd403dfcc2c3ca6aae618ae3e1409516e32) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Sharp is now the default image service used for `astro:assets`. If you would prefer to still use Squoosh, you can update your config with the following:
+
+ ```ts
+ import { defineConfig, squooshImageService } from 'astro/config';
+
+ // https://astro.build/config
+ export default defineConfig({
+ image: {
+ service: squooshImageService(),
+ },
+ });
+ ```
+
+ However, not only do we recommend using Sharp as it is faster and more reliable, it is also highly likely that the Squoosh service will be removed in a future release.
+
+- [`c022a4217`](https://github.com/withastro/astro/commit/c022a4217a805d223c1494e9eda4e48bbf810388) Thanks [@Princesseuh](https://github.com/Princesseuh)! - When using an adapter that supports neither Squoosh or Sharp, Astro will now automatically use an image service that does not support processing, but still provides the other benefits of `astro:assets` such as enforcing `alt`, no CLS etc to users
+
+- [`67becaa58`](https://github.com/withastro/astro/commit/67becaa580b8f787df58de66b7008b7098f1209c) Thanks [@ematipico](https://github.com/ematipico)! - Removed support for old syntax of the API routes.
+
+- [`dfc2d93e3`](https://github.com/withastro/astro/commit/dfc2d93e3c645995379358fabbdfa9aab99f43d8) Thanks [@bluwy](https://github.com/bluwy)! - Remove MDX plugin re-ordering hack
+
+- [`3dc1ca2fa`](https://github.com/withastro/astro/commit/3dc1ca2fac8d9965cc5085a5d09e72ed87b4281a) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Reduced the amount of polyfills provided by Astro. Astro will no longer provide (no-op) polyfills for several web apis such as HTMLElement, Image or Document. If you need access to those APIs on the server, we recommend using more proper polyfills available on npm.
+
+- [`1be84dfee`](https://github.com/withastro/astro/commit/1be84dfee3ce8e6f5cc624f99aec4e980f6fde37) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Update `tsconfig.json` presets with `moduleResolution: 'bundler'` and other new options from TypeScript 5.0. Astro now assumes that you use TypeScript 5.0 (March 2023), or that your editor includes it, ex: VS Code 1.77
+
+- [`35f01df79`](https://github.com/withastro/astro/commit/35f01df797d23315f2bee2fc3fd795adb0559c58) Thanks [@Princesseuh](https://github.com/Princesseuh)! - The `astro check` command now requires an external package `@astrojs/check` and an install of `typescript` in your project. This was done in order to make the main `astro` package smaller and give more flexibility to users in regard to the version of TypeScript they use.
+
+- [`3fdf509b2`](https://github.com/withastro/astro/commit/3fdf509b2731a9b2f972d89291e57cf78d62c769) Thanks [@ematipico](https://github.com/ematipico)! - The `build.split` and `build.excludeMiddleware` configuration options are deprecated and have been replaced by options in the adapter config.
+
+ If your config includes the `build.excludeMiddleware` option, replace it with `edgeMiddleware` in your adapter options:
+
+ ```diff
+ import { defineConfig } from "astro/config";
+ import vercel from "@astrojs/vercel/serverless";
+
+ export default defineConfig({
+ build: {
+ - excludeMiddleware: true
+ },
+ adapter: vercel({
+ + edgeMiddleware: true
+ }),
+ });
+ ```
+
+ If your config includes the `build.split` option, replace it with `functionPerRoute` in your adapter options:
+
+ ```diff
+ import { defineConfig } from "astro/config";
+ import vercel from "@astrojs/vercel/serverless";
+
+ export default defineConfig({
+ build: {
+ - split: true
+ },
+ adapter: vercel({
+ + functionPerRoute: true
+ }),
+ });
+ ```
+
+- [`78de801f2`](https://github.com/withastro/astro/commit/78de801f21fd4ca1653950027d953bf08614566b) Thanks [@ematipico](https://github.com/ematipico)! - Lowercase names for endpoint functions are now deprecated.
+
+ Rename functions to their uppercase equivalent:
+
+ ```diff
+ - export function get() {
+ + export function GET() {
+ return new Response(JSON.stringify({ "title": "Bob's blog" }));
+ }
+
+ - export function post() {
+ + export function POST() {
+ return new Response(JSON.stringify({ "title": "Bob's blog" }));
+ }
+
+ - export function put() {
+ + export function PUT() {
+ return new Response(JSON.stringify({ "title": "Bob's blog" }));
+ }
+
+ - export function all() {
+ + export function ALL() {
+ return new Response(JSON.stringify({ "title": "Bob's blog" }));
+ }
+
+ // you can use the whole word "DELETE"
+ - export function del() {
+ + export function DELETE() {
+ return new Response(JSON.stringify({ "title": "Bob's blog" }));
+ }
+ ```
+
+- [`59d6e569f`](https://github.com/withastro/astro/commit/59d6e569f63e175c97e82e94aa7974febfb76f7c) Thanks [@matthewp](https://github.com/matthewp)! - Astro.cookies.get(key) returns undefined if cookie doesn't exist
+
+ With this change, Astro.cookies.get(key) no longer always returns a `AstroCookie` object. Instead it now returns `undefined` if the cookie does not exist.
+
+ You should update your code if you assume that all calls to `get()` return a value. When using with `has()` you still need to assert the value, like so:
+
+ ```astro
+ ---
+ if (Astro.cookies.has(id)) {
+ const id = Astro.cookies.get(id)!;
+ }
+ ---
+ ```
+
+- [`7723c4cc9`](https://github.com/withastro/astro/commit/7723c4cc93298c2e6530e55da7afda048f22cf81) Thanks [@ematipico](https://github.com/ematipico)! - The property `compressHTML` is now `true` by default. Setting this value to `true` is no longer required.
+
+ If you do not want to minify your HTML output, you must set this value to `false` in `astro.config.mjs`.
+
+ ```diff
+ import {defineConfig} from "astro/config";
+ export default defineConfig({
+ + compressHTML: false
+ })
+ ```
+
+- [`fb5cd6b56`](https://github.com/withastro/astro/commit/fb5cd6b56dc27a71366ed5e1ab8bfe9b8f96bac5) Thanks [@ematipico](https://github.com/ematipico)! - Astro's default port when running the dev or preview server is now `4321`.
+
+ This will reduce conflicts with ports used by other tools.
+
+- [`631b9c410`](https://github.com/withastro/astro/commit/631b9c410d5d66fa384674027ba95d69ebb5063f) Thanks [@bluwy](https://github.com/bluwy)! - Remove MDX special `components` export handling
+
+### Minor Changes
+
+- [`9b4f70a62`](https://github.com/withastro/astro/commit/9b4f70a629f55e461759ba46f68af7097a2e9215) Thanks [@ematipico](https://github.com/ematipico)! - Introduced the concept of feature map. A feature map is a list of features that are built-in in Astro, and an Adapter
+ can tell Astro if it can support it.
+
+ ```ts
+ import { AstroIntegration } from './astro';
+
+ function myIntegration(): AstroIntegration {
+ return {
+ name: 'astro-awesome-list',
+ // new feature map
+ supportedAstroFeatures: {
+ hybridOutput: 'experimental',
+ staticOutput: 'stable',
+ serverOutput: 'stable',
+ assets: {
+ supportKind: 'stable',
+ isSharpCompatible: false,
+ isSquooshCompatible: false,
+ },
+ },
+ };
+ }
+ ```
+
+- [`bc37331d8`](https://github.com/withastro/astro/commit/bc37331d8154e3e95a8df9131e4e014e78a7a9e7) Thanks [@ematipico](https://github.com/ematipico)! - Integrations can now log messages using Astro’s built-in logger.
+
+ The logger is available to all hooks as an additional parameter:
+
+ ```ts
+ import { AstroIntegration } from './astro';
+
+ // integration.js
+ export function myIntegration(): AstroIntegration {
+ return {
+ name: 'my-integration',
+ hooks: {
+ 'astro:config:done': ({ logger }) => {
+ logger.info('Configure integration...');
+ },
+ },
+ };
+ }
+ ```
+
+### Patch Changes
+
+- Updated dependencies [[`1eae2e3f7`](https://github.com/withastro/astro/commit/1eae2e3f7d693c9dfe91c8ccfbe606d32bf2fb81)]:
+ - @astrojs/telemetry@3.0.0-beta.0
+ - @astrojs/internal-helpers@0.2.0-beta.0
+ - @astrojs/markdown-remark@3.0.0-beta.0
+
## 2.10.12
### Patch Changes
diff --git a/packages/astro/astro-jsx.d.ts b/packages/astro/astro-jsx.d.ts
index a04d755ec..b82de9388 100644
--- a/packages/astro/astro-jsx.d.ts
+++ b/packages/astro/astro-jsx.d.ts
@@ -9,6 +9,8 @@
* Adapted from React’s TypeScript definition from DefinitelyTyped.
* @see https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts
*/
+// BUG! Prettier 3.0 removes `declare`: https://github.com/prettier/prettier/issues/15207
+// prettier-ignore
declare namespace astroHTML.JSX {
export type Child = Node | Node[] | string | number | boolean | null | undefined | unknown;
export type Children = Child | Child[];
diff --git a/packages/astro/astro.js b/packages/astro/astro.js
index 37ea869bc..ef5349854 100755
--- a/packages/astro/astro.js
+++ b/packages/astro/astro.js
@@ -13,14 +13,18 @@ const CI_INSTRUCTIONS = {
};
// Hardcode supported Node.js version so we don't have to read differently in CJS & ESM.
-const engines = '>=16.12.0';
-const skipSemverCheckIfAbove = 16;
+const engines = '>=18.14.1';
+const skipSemverCheckIfAbove = 19;
+
+// HACK (2023-08-18) Stackblitz does not support Node 18 yet, so we'll fake Node 16 support for some time until it's supported
+// TODO: Remove when Node 18 is supported on Stackblitz
+const isStackblitz = process.env.SHELL === '/bin/jsh' && process.versions.webcontainer != null;
/** `astro *` */
async function main() {
const version = process.versions.node;
// Fast-path for higher Node.js versions
- if ((parseInt(version) || 0) <= skipSemverCheckIfAbove) {
+ if (!isStackblitz && (parseInt(version) || 0) <= skipSemverCheckIfAbove) {
try {
const semver = await import('semver');
if (!semver.satisfies(version, engines)) {
diff --git a/packages/astro/client-base.d.ts b/packages/astro/client-base.d.ts
deleted file mode 100644
index 6bd60da8c..000000000
--- a/packages/astro/client-base.d.ts
+++ /dev/null
@@ -1,428 +0,0 @@
-/// <reference path="./import-meta.d.ts" />
-
-// eslint-disable-next-line @typescript-eslint/no-namespace
-declare namespace App {
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
- export interface Locals {}
-}
-
-interface ImportMetaEnv {
- /**
- * The prefix for Astro-generated asset links if the build.assetsPrefix config option is set. This can be used to create asset links not handled by Astro.
- */
- readonly ASSETS_PREFIX: string;
- /**
- * This is set to the site option specified in your project’s Astro config file.
- */
- readonly SITE: string;
-}
-
-interface ImportMeta {
- /**
- * Astro and Vite expose environment variables through `import.meta.env`. For a complete list of the environment variables available, see the two references below.
- *
- * - [Astro reference](https://docs.astro.build/en/guides/environment-variables/#default-environment-variables)
- * - [Vite reference](https://vitejs.dev/guide/env-and-mode.html#env-variables)
- */
- readonly env: ImportMetaEnv;
-}
-
-declare module 'astro:assets' {
- // Exporting things one by one is a bit cumbersome, not sure if there's a better way - erika, 2023-02-03
- type AstroAssets = {
- // getImage's type here is different from the internal function since the Vite module implicitly pass the service config
- /**
- * Get an optimized image and the necessary attributes to render it.
- *
- * **Example**
- * ```astro
- * ---
- * import { getImage } from 'astro:assets';
- * import originalImage from '../assets/image.png';
- *
- * const optimizedImage = await getImage({src: originalImage, width: 1280 });
- * ---
- * <img src={optimizedImage.src} {...optimizedImage.attributes} />
- * ```
- *
- * This is functionally equivalent to using the `<Image />` component, as the component calls this function internally.
- */
- getImage: (
- options:
- | import('./dist/assets/types.js').ImageTransform
- | import('./dist/assets/types.js').UnresolvedImageTransform
- ) => Promise<import('./dist/assets/types.js').GetImageResult>;
- getConfiguredImageService: typeof import('./dist/assets/index.js').getConfiguredImageService;
- imageConfig: import('./dist/@types/astro').AstroConfig['image'];
- Image: typeof import('./components/Image.astro').default;
- };
-
- type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
- type Simplify<T> = { [KeyType in keyof T]: T[KeyType] };
- type ImgAttributes = WithRequired<
- Omit<import('./types').HTMLAttributes<'img'>, 'src' | 'width' | 'height'>,
- 'alt'
- >;
-
- export type LocalImageProps = Simplify<
- import('./dist/assets/types.js').LocalImageProps<ImgAttributes>
- >;
- export type RemoteImageProps = Simplify<
- import('./dist/assets/types.js').RemoteImageProps<ImgAttributes>
- >;
- export const { getImage, getConfiguredImageService, imageConfig, Image }: AstroAssets;
-}
-
-declare module 'astro:transitions' {
- type TransitionModule = typeof import('./dist/transitions/index.js');
- export const slide: TransitionModule['slide'];
- export const fade: TransitionModule['fade'];
-
- type ViewTransitionsModule = typeof import('./components/ViewTransitions.astro');
- export const ViewTransitions: ViewTransitionsModule['default'];
-}
-
-type MD = import('./dist/@types/astro').MarkdownInstance<Record<string, any>>;
-interface ExportedMarkdownModuleEntities {
- frontmatter: MD['frontmatter'];
- file: MD['file'];
- url: MD['url'];
- getHeadings: MD['getHeadings'];
- /** @deprecated Renamed to `getHeadings()` */
- getHeaders: () => void;
- Content: MD['Content'];
- rawContent: MD['rawContent'];
- compiledContent: MD['compiledContent'];
- load: MD['default'];
-}
-
-declare module '*.md' {
- const { load }: ExportedMarkdownModuleEntities;
- export const {
- frontmatter,
- file,
- url,
- getHeadings,
- getHeaders,
- Content,
- rawContent,
- compiledContent,
- }: ExportedMarkdownModuleEntities;
- export default load;
-}
-
-declare module '*.markdown' {
- const { load }: ExportedMarkdownModuleEntities;
- export const {
- frontmatter,
- file,
- url,
- getHeadings,
- getHeaders,
- Content,
- rawContent,
- compiledContent,
- }: ExportedMarkdownModuleEntities;
- export default load;
-}
-
-declare module '*.mkdn' {
- const { load }: ExportedMarkdownModuleEntities;
- export const {
- frontmatter,
- file,
- url,
- getHeadings,
- getHeaders,
- Content,
- rawContent,
- compiledContent,
- }: ExportedMarkdownModuleEntities;
- export default load;
-}
-
-declare module '*.mkd' {
- const { load }: ExportedMarkdownModuleEntities;
- export const {
- frontmatter,
- file,
- url,
- getHeadings,
- getHeaders,
- Content,
- rawContent,
- compiledContent,
- }: ExportedMarkdownModuleEntities;
- export default load;
-}
-
-declare module '*.mdwn' {
- const { load }: ExportedMarkdownModuleEntities;
- export const {
- frontmatter,
- file,
- url,
- getHeadings,
- getHeaders,
- Content,
- rawContent,
- compiledContent,
- }: ExportedMarkdownModuleEntities;
- export default load;
-}
-
-declare module '*.mdown' {
- const { load }: ExportedMarkdownModuleEntities;
- export const {
- frontmatter,
- file,
- url,
- getHeadings,
- getHeaders,
- Content,
- rawContent,
- compiledContent,
- }: ExportedMarkdownModuleEntities;
- export default load;
-}
-
-declare module '*.mdx' {
- type MDX = import('./dist/@types/astro').MDXInstance<Record<string, any>>;
-
- export const frontmatter: MDX['frontmatter'];
- export const file: MDX['file'];
- export const url: MDX['url'];
- export const getHeadings: MDX['getHeadings'];
- export const Content: MDX['Content'];
-
- const load: MDX['default'];
- export default load;
-}
-
-declare module 'astro:ssr-manifest' {
- export const manifest: import('./dist/@types/astro').SSRManifest;
-}
-
-// Everything below are Vite's types (apart from image types, which are in `client.d.ts`)
-
-// CSS modules
-type CSSModuleClasses = { readonly [key: string]: string };
-
-declare module '*.module.css' {
- const classes: CSSModuleClasses;
- export default classes;
-}
-declare module '*.module.scss' {
- const classes: CSSModuleClasses;
- export default classes;
-}
-declare module '*.module.sass' {
- const classes: CSSModuleClasses;
- export default classes;
-}
-declare module '*.module.less' {
- const classes: CSSModuleClasses;
- export default classes;
-}
-declare module '*.module.styl' {
- const classes: CSSModuleClasses;
- export default classes;
-}
-declare module '*.module.stylus' {
- const classes: CSSModuleClasses;
- export default classes;
-}
-declare module '*.module.pcss' {
- const classes: CSSModuleClasses;
- export default classes;
-}
-declare module '*.module.sss' {
- const classes: CSSModuleClasses;
- export default classes;
-}
-
-// CSS
-declare module '*.css' {
- const css: string;
- export default css;
-}
-declare module '*.scss' {
- const css: string;
- export default css;
-}
-declare module '*.sass' {
- const css: string;
- export default css;
-}
-declare module '*.less' {
- const css: string;
- export default css;
-}
-declare module '*.styl' {
- const css: string;
- export default css;
-}
-declare module '*.stylus' {
- const css: string;
- export default css;
-}
-declare module '*.pcss' {
- const css: string;
- export default css;
-}
-declare module '*.sss' {
- const css: string;
- export default css;
-}
-
-// Built-in asset types
-// see `src/node/constants.ts`
-
-// images
-declare module '*.jfif' {
- const src: string;
- export default src;
-}
-declare module '*.pjpeg' {
- const src: string;
- export default src;
-}
-declare module '*.pjp' {
- const src: string;
- export default src;
-}
-declare module '*.ico' {
- const src: string;
- export default src;
-}
-
-// media
-declare module '*.mp4' {
- const src: string;
- export default src;
-}
-declare module '*.webm' {
- const src: string;
- export default src;
-}
-declare module '*.ogg' {
- const src: string;
- export default src;
-}
-declare module '*.mp3' {
- const src: string;
- export default src;
-}
-declare module '*.wav' {
- const src: string;
- export default src;
-}
-declare module '*.flac' {
- const src: string;
- export default src;
-}
-declare module '*.aac' {
- const src: string;
- export default src;
-}
-
-declare module '*.opus' {
- const src: string;
- export default src;
-}
-
-// fonts
-declare module '*.woff' {
- const src: string;
- export default src;
-}
-declare module '*.woff2' {
- const src: string;
- export default src;
-}
-declare module '*.eot' {
- const src: string;
- export default src;
-}
-declare module '*.ttf' {
- const src: string;
- export default src;
-}
-declare module '*.otf' {
- const src: string;
- export default src;
-}
-
-// other
-declare module '*.webmanifest' {
- const src: string;
- export default src;
-}
-declare module '*.pdf' {
- const src: string;
- export default src;
-}
-declare module '*.txt' {
- const src: string;
- export default src;
-}
-
-// wasm?init
-declare module '*.wasm?init' {
- const initWasm: (options: WebAssembly.Imports) => Promise<WebAssembly.Instance>;
- export default initWasm;
-}
-
-// web worker
-declare module '*?worker' {
- const workerConstructor: {
- new (): Worker;
- };
- export default workerConstructor;
-}
-
-declare module '*?worker&inline' {
- const workerConstructor: {
- new (): Worker;
- };
- export default workerConstructor;
-}
-
-declare module '*?worker&url' {
- const src: string;
- export default src;
-}
-
-declare module '*?sharedworker' {
- const sharedWorkerConstructor: {
- new (): SharedWorker;
- };
- export default sharedWorkerConstructor;
-}
-
-declare module '*?sharedworker&inline' {
- const sharedWorkerConstructor: {
- new (): SharedWorker;
- };
- export default sharedWorkerConstructor;
-}
-
-declare module '*?sharedworker&url' {
- const src: string;
- export default src;
-}
-
-declare module '*?raw' {
- const src: string;
- export default src;
-}
-
-declare module '*?url' {
- const src: string;
- export default src;
-}
-
-declare module '*?inline' {
- const src: string;
- export default src;
-}
diff --git a/packages/astro/client-image.d.ts b/packages/astro/client-image.d.ts
deleted file mode 100644
index ffcc1c63c..000000000
--- a/packages/astro/client-image.d.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/// <reference path="./client-base.d.ts" />
-
-// TODO: Merge this file with `client-base.d.ts` in 3.0, when the `astro:assets` feature isn't under a flag anymore.
-
-type InputFormat = import('./dist/assets/types.js').ImageInputFormat;
-
-interface ImageMetadata {
- src: string;
- width: number;
- height: number;
- format: InputFormat;
-}
-
-declare module '*.gif' {
- const metadata: ImageMetadata;
- export default metadata;
-}
-declare module '*.jpeg' {
- const metadata: ImageMetadata;
- export default metadata;
-}
-declare module '*.jpg' {
- const metadata: ImageMetadata;
- export default metadata;
-}
-declare module '*.png' {
- const metadata: ImageMetadata;
- export default metadata;
-}
-declare module '*.tiff' {
- const metadata: ImageMetadata;
- export default metadata;
-}
-declare module '*.webp' {
- const metadata: ImageMetadata;
- export default metadata;
-}
-declare module '*.svg' {
- const metadata: ImageMetadata;
- export default metadata;
-}
diff --git a/packages/astro/client.d.ts b/packages/astro/client.d.ts
index 96f59d586..e6389d415 100644
--- a/packages/astro/client.d.ts
+++ b/packages/astro/client.d.ts
@@ -1,31 +1,473 @@
-/// <reference path="./client-base.d.ts" />
+/// <reference path="./import-meta.d.ts" />
-// images
+// eslint-disable-next-line @typescript-eslint/no-namespace
+declare namespace App {
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
+ export interface Locals {}
+}
+
+interface ImportMetaEnv {
+ /**
+ * The prefix for Astro-generated asset links if the build.assetsPrefix config option is set. This can be used to create asset links not handled by Astro.
+ */
+ readonly ASSETS_PREFIX: string;
+ /**
+ * This is set to the site option specified in your project’s Astro config file.
+ */
+ readonly SITE: string;
+}
+
+interface ImportMeta {
+ /**
+ * Astro and Vite expose environment variables through `import.meta.env`. For a complete list of the environment variables available, see the two references below.
+ *
+ * - [Astro reference](https://docs.astro.build/en/guides/environment-variables/#default-environment-variables)
+ * - [Vite reference](https://vitejs.dev/guide/env-and-mode.html#env-variables)
+ */
+ readonly env: ImportMetaEnv;
+}
+
+declare module 'astro:assets' {
+ // Exporting things one by one is a bit cumbersome, not sure if there's a better way - erika, 2023-02-03
+ type AstroAssets = {
+ // getImage's type here is different from the internal function since the Vite module implicitly pass the service config
+ /**
+ * Get an optimized image and the necessary attributes to render it.
+ *
+ * **Example**
+ * ```astro
+ * ---
+ * import { getImage } from 'astro:assets';
+ * import originalImage from '../assets/image.png';
+ *
+ * const optimizedImage = await getImage({src: originalImage, width: 1280 });
+ * ---
+ * <img src={optimizedImage.src} {...optimizedImage.attributes} />
+ * ```
+ *
+ * This is functionally equivalent to using the `<Image />` component, as the component calls this function internally.
+ */
+ getImage: (
+ options:
+ | import('./dist/assets/types.js').ImageTransform
+ | import('./dist/assets/types.js').UnresolvedImageTransform
+ ) => Promise<import('./dist/assets/types.js').GetImageResult>;
+ getConfiguredImageService: typeof import('./dist/assets/index.js').getConfiguredImageService;
+ Image: typeof import('./components/Image.astro').default;
+ };
+
+ type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
+ type Simplify<T> = { [KeyType in keyof T]: T[KeyType] };
+ type ImgAttributes = WithRequired<
+ Omit<import('./types').HTMLAttributes<'img'>, 'src' | 'width' | 'height'>,
+ 'alt'
+ >;
+
+ export type LocalImageProps = Simplify<
+ import('./dist/assets/types.js').LocalImageProps<ImgAttributes>
+ >;
+ export type RemoteImageProps = Simplify<
+ import('./dist/assets/types.js').RemoteImageProps<ImgAttributes>
+ >;
+ export const { getImage, getConfiguredImageService, Image }: AstroAssets;
+}
+
+type InputFormat = import('./dist/assets/types.js').ImageInputFormat;
+
+interface ImageMetadata {
+ src: string;
+ width: number;
+ height: number;
+ format: InputFormat;
+}
+
+declare module '*.gif' {
+ const metadata: ImageMetadata;
+ export default metadata;
+}
+declare module '*.jpeg' {
+ const metadata: ImageMetadata;
+ export default metadata;
+}
declare module '*.jpg' {
+ const metadata: ImageMetadata;
+ export default metadata;
+}
+declare module '*.png' {
+ const metadata: ImageMetadata;
+ export default metadata;
+}
+declare module '*.tiff' {
+ const metadata: ImageMetadata;
+ export default metadata;
+}
+declare module '*.webp' {
+ const metadata: ImageMetadata;
+ export default metadata;
+}
+declare module '*.svg' {
+ const metadata: ImageMetadata;
+ export default metadata;
+}
+
+declare module 'astro:transitions' {
+ type TransitionModule = typeof import('./dist/transitions/index.js');
+ export const slide: TransitionModule['slide'];
+ export const fade: TransitionModule['fade'];
+
+ type ViewTransitionsModule = typeof import('./components/ViewTransitions.astro');
+ export const ViewTransitions: ViewTransitionsModule['default'];
+}
+
+declare module 'astro:middleware' {
+ export * from 'astro/middleware/namespace';
+}
+
+declare module 'astro:components' {
+ export * from 'astro/components';
+}
+
+type MD = import('./dist/@types/astro').MarkdownInstance<Record<string, any>>;
+interface ExportedMarkdownModuleEntities {
+ frontmatter: MD['frontmatter'];
+ file: MD['file'];
+ url: MD['url'];
+ getHeadings: MD['getHeadings'];
+ /** @deprecated Renamed to `getHeadings()` */
+ getHeaders: () => void;
+ Content: MD['Content'];
+ rawContent: MD['rawContent'];
+ compiledContent: MD['compiledContent'];
+ load: MD['default'];
+}
+
+declare module '*.md' {
+ const { load }: ExportedMarkdownModuleEntities;
+ export const {
+ frontmatter,
+ file,
+ url,
+ getHeadings,
+ getHeaders,
+ Content,
+ rawContent,
+ compiledContent,
+ }: ExportedMarkdownModuleEntities;
+ export default load;
+}
+
+declare module '*.markdown' {
+ const { load }: ExportedMarkdownModuleEntities;
+ export const {
+ frontmatter,
+ file,
+ url,
+ getHeadings,
+ getHeaders,
+ Content,
+ rawContent,
+ compiledContent,
+ }: ExportedMarkdownModuleEntities;
+ export default load;
+}
+
+declare module '*.mkdn' {
+ const { load }: ExportedMarkdownModuleEntities;
+ export const {
+ frontmatter,
+ file,
+ url,
+ getHeadings,
+ getHeaders,
+ Content,
+ rawContent,
+ compiledContent,
+ }: ExportedMarkdownModuleEntities;
+ export default load;
+}
+
+declare module '*.mkd' {
+ const { load }: ExportedMarkdownModuleEntities;
+ export const {
+ frontmatter,
+ file,
+ url,
+ getHeadings,
+ getHeaders,
+ Content,
+ rawContent,
+ compiledContent,
+ }: ExportedMarkdownModuleEntities;
+ export default load;
+}
+
+declare module '*.mdwn' {
+ const { load }: ExportedMarkdownModuleEntities;
+ export const {
+ frontmatter,
+ file,
+ url,
+ getHeadings,
+ getHeaders,
+ Content,
+ rawContent,
+ compiledContent,
+ }: ExportedMarkdownModuleEntities;
+ export default load;
+}
+
+declare module '*.mdown' {
+ const { load }: ExportedMarkdownModuleEntities;
+ export const {
+ frontmatter,
+ file,
+ url,
+ getHeadings,
+ getHeaders,
+ Content,
+ rawContent,
+ compiledContent,
+ }: ExportedMarkdownModuleEntities;
+ export default load;
+}
+
+declare module '*.mdx' {
+ type MDX = import('./dist/@types/astro').MDXInstance<Record<string, any>>;
+
+ export const frontmatter: MDX['frontmatter'];
+ export const file: MDX['file'];
+ export const url: MDX['url'];
+ export const getHeadings: MDX['getHeadings'];
+ export const Content: MDX['Content'];
+
+ const load: MDX['default'];
+ export default load;
+}
+
+declare module 'astro:ssr-manifest' {
+ export const manifest: import('./dist/@types/astro').SSRManifest;
+}
+
+// Everything below are Vite's types (apart from image types, which are in `client.d.ts`)
+
+// CSS modules
+type CSSModuleClasses = { readonly [key: string]: string };
+
+declare module '*.module.css' {
+ const classes: CSSModuleClasses;
+ export default classes;
+}
+declare module '*.module.scss' {
+ const classes: CSSModuleClasses;
+ export default classes;
+}
+declare module '*.module.sass' {
+ const classes: CSSModuleClasses;
+ export default classes;
+}
+declare module '*.module.less' {
+ const classes: CSSModuleClasses;
+ export default classes;
+}
+declare module '*.module.styl' {
+ const classes: CSSModuleClasses;
+ export default classes;
+}
+declare module '*.module.stylus' {
+ const classes: CSSModuleClasses;
+ export default classes;
+}
+declare module '*.module.pcss' {
+ const classes: CSSModuleClasses;
+ export default classes;
+}
+declare module '*.module.sss' {
+ const classes: CSSModuleClasses;
+ export default classes;
+}
+
+// CSS
+declare module '*.css' {
+ const css: string;
+ export default css;
+}
+declare module '*.scss' {
+ const css: string;
+ export default css;
+}
+declare module '*.sass' {
+ const css: string;
+ export default css;
+}
+declare module '*.less' {
+ const css: string;
+ export default css;
+}
+declare module '*.styl' {
+ const css: string;
+ export default css;
+}
+declare module '*.stylus' {
+ const css: string;
+ export default css;
+}
+declare module '*.pcss' {
+ const css: string;
+ export default css;
+}
+declare module '*.sss' {
+ const css: string;
+ export default css;
+}
+
+// Built-in asset types
+// see `src/node/constants.ts`
+
+// images
+declare module '*.jfif' {
const src: string;
export default src;
}
-declare module '*.jpeg' {
+declare module '*.pjpeg' {
const src: string;
export default src;
}
-declare module '*.png' {
+declare module '*.pjp' {
const src: string;
export default src;
}
-declare module '*.gif' {
+declare module '*.ico' {
const src: string;
export default src;
}
-declare module '*.svg' {
+
+// media
+declare module '*.mp4' {
const src: string;
export default src;
}
-declare module '*.webp' {
+declare module '*.webm' {
+ const src: string;
+ export default src;
+}
+declare module '*.ogg' {
+ const src: string;
+ export default src;
+}
+declare module '*.mp3' {
+ const src: string;
+ export default src;
+}
+declare module '*.wav' {
+ const src: string;
+ export default src;
+}
+declare module '*.flac' {
+ const src: string;
+ export default src;
+}
+declare module '*.aac' {
const src: string;
export default src;
}
-declare module '*.avif' {
+
+declare module '*.opus' {
+ const src: string;
+ export default src;
+}
+
+// fonts
+declare module '*.woff' {
+ const src: string;
+ export default src;
+}
+declare module '*.woff2' {
+ const src: string;
+ export default src;
+}
+declare module '*.eot' {
+ const src: string;
+ export default src;
+}
+declare module '*.ttf' {
+ const src: string;
+ export default src;
+}
+declare module '*.otf' {
+ const src: string;
+ export default src;
+}
+
+// other
+declare module '*.webmanifest' {
+ const src: string;
+ export default src;
+}
+declare module '*.pdf' {
+ const src: string;
+ export default src;
+}
+declare module '*.txt' {
+ const src: string;
+ export default src;
+}
+
+// wasm?init
+declare module '*.wasm?init' {
+ const initWasm: (options: WebAssembly.Imports) => Promise<WebAssembly.Instance>;
+ export default initWasm;
+}
+
+// web worker
+declare module '*?worker' {
+ const workerConstructor: {
+ new (): Worker;
+ };
+ export default workerConstructor;
+}
+
+declare module '*?worker&inline' {
+ const workerConstructor: {
+ new (): Worker;
+ };
+ export default workerConstructor;
+}
+
+declare module '*?worker&url' {
+ const src: string;
+ export default src;
+}
+
+declare module '*?sharedworker' {
+ const sharedWorkerConstructor: {
+ new (): SharedWorker;
+ };
+ export default sharedWorkerConstructor;
+}
+
+declare module '*?sharedworker&inline' {
+ const sharedWorkerConstructor: {
+ new (): SharedWorker;
+ };
+ export default sharedWorkerConstructor;
+}
+
+declare module '*?sharedworker&url' {
+ const src: string;
+ export default src;
+}
+
+declare module '*?raw' {
+ const src: string;
+ export default src;
+}
+
+declare module '*?url' {
+ const src: string;
+ export default src;
+}
+
+declare module '*?inline' {
const src: string;
export default src;
}
diff --git a/packages/astro/components/shiki-themes.js b/packages/astro/components/shiki-themes.js
index 7f07d2417..8f6c4fccd 100644
--- a/packages/astro/components/shiki-themes.js
+++ b/packages/astro/components/shiki-themes.js
@@ -34,10 +34,4 @@ export const themes = {
'solarized-light': () => import('shiki/themes/solarized-light.json').then(mod => mod.default),
'vitesse-dark': () => import('shiki/themes/vitesse-dark.json').then(mod => mod.default),
'vitesse-light': () => import('shiki/themes/vitesse-light.json').then(mod => mod.default),
- // old theme names for compat
- 'material-darker': () => import('shiki/themes/material-theme-darker').then(mod => mod.default),
- 'material-default': () => import('shiki/themes/material-theme').then(mod => mod.default),
- 'material-lighter': () => import('shiki/themes/material-theme-lighter').then(mod => mod.default),
- 'material-ocean': () => import('shiki/themes/material-theme-ocean').then(mod => mod.default),
- 'material-palenight': () => import('shiki/themes/material-theme-palenight').then(mod => mod.default),
};
diff --git a/packages/astro/config.d.ts b/packages/astro/config.d.ts
index 33aebd4f7..cdd38c4ea 100644
--- a/packages/astro/config.d.ts
+++ b/packages/astro/config.d.ts
@@ -24,3 +24,10 @@ export function sharpImageService(): ImageServiceConfig;
* Return the configuration needed to use the Squoosh-based image service
*/
export function squooshImageService(): ImageServiceConfig;
+
+/**
+ * Return the configuration needed to use the passthrough image service. This image services does not perform
+ * any image transformations, and is mainly useful when your platform does not support other image services, or you are
+ * not using Astro's built-in image processing.
+ */
+export function passthroughImageService(): ImageServiceConfig;
diff --git a/packages/astro/config.mjs b/packages/astro/config.mjs
index 9f5883015..208313287 100644
--- a/packages/astro/config.mjs
+++ b/packages/astro/config.mjs
@@ -13,3 +13,10 @@ export function squooshImageService() {
config: {},
};
}
+
+export function passthroughImageService() {
+ return {
+ entrypoint: 'astro/assets/services/noop',
+ config: {},
+ };
+}
diff --git a/packages/astro/content-types.template.d.ts b/packages/astro/content-types.template.d.ts
index 3f9529012..596764fe6 100644
--- a/packages/astro/content-types.template.d.ts
+++ b/packages/astro/content-types.template.d.ts
@@ -14,25 +14,6 @@ declare module 'astro:content' {
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
export type CollectionEntry<C extends keyof AnyEntryMap> = Flatten<AnyEntryMap[C]>;
- // TODO: Remove this when having this fallback is no longer relevant. 2.3? 3.0? - erika, 2023-04-04
- /**
- * @deprecated
- * `astro:content` no longer provide `image()`.
- *
- * Please use it through `schema`, like such:
- * ```ts
- * import { defineCollection, z } from "astro:content";
- *
- * defineCollection({
- * schema: ({ image }) =>
- * z.object({
- * image: image(),
- * }),
- * });
- * ```
- */
- export const image: never;
-
// This needs to be in sync with ImageMetadata
export type ImageFunction = () => import('astro/zod').ZodObject<{
src: import('astro/zod').ZodString;
@@ -46,7 +27,7 @@ declare module 'astro:content' {
import('astro/zod').ZodLiteral<'tiff'>,
import('astro/zod').ZodLiteral<'webp'>,
import('astro/zod').ZodLiteral<'gif'>,
- import('astro/zod').ZodLiteral<'svg'>
+ import('astro/zod').ZodLiteral<'svg'>,
]
>;
}>;
@@ -86,7 +67,7 @@ declare module 'astro:content' {
export function getEntryBySlug<
C extends keyof ContentEntryMap,
- E extends ValidContentEntrySlug<C> | (string & {})
+ E extends ValidContentEntrySlug<C> | (string & {}),
>(
collection: C,
// Note that this has to accept a regular string too, for SSR
@@ -111,7 +92,7 @@ declare module 'astro:content' {
export function getEntry<
C extends keyof ContentEntryMap,
- E extends ValidContentEntrySlug<C> | (string & {})
+ E extends ValidContentEntrySlug<C> | (string & {}),
>(entry: {
collection: C;
slug: E;
@@ -120,7 +101,7 @@ declare module 'astro:content' {
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
- E extends keyof DataEntryMap[C] | (string & {})
+ E extends keyof DataEntryMap[C] | (string & {}),
>(entry: {
collection: C;
id: E;
@@ -129,7 +110,7 @@ declare module 'astro:content' {
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof ContentEntryMap,
- E extends ValidContentEntrySlug<C> | (string & {})
+ E extends ValidContentEntrySlug<C> | (string & {}),
>(
collection: C,
slug: E
@@ -138,7 +119,7 @@ declare module 'astro:content' {
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
- E extends keyof DataEntryMap[C] | (string & {})
+ E extends keyof DataEntryMap[C] | (string & {}),
>(
collection: C,
id: E
diff --git a/packages/astro/e2e/astro-envs.test.js b/packages/astro/e2e/astro-envs.test.js
index 1a4f4a1ca..50cff6e89 100644
--- a/packages/astro/e2e/astro-envs.test.js
+++ b/packages/astro/e2e/astro-envs.test.js
@@ -18,11 +18,11 @@ test.describe('Astro Environment BASE_URL', () => {
await page.goto(astro.resolveUrl('/blog/'));
const astroBaseUrl = page.locator('id=astro-base-url');
- await expect(astroBaseUrl, 'astroBaseUrl equals to /blog/').toHaveText('/blog/');
+ await expect(astroBaseUrl, 'astroBaseUrl equals to /blog').toHaveText('/blog');
const clientComponentBaseUrl = page.locator('id=client-component-base-url');
await expect(clientComponentBaseUrl, 'clientComponentBaseUrl equals to /blog').toHaveText(
- '/blog/'
+ '/blog'
);
});
});
diff --git a/packages/astro/e2e/css-sourcemaps.test.js b/packages/astro/e2e/css-sourcemaps.test.js
index 50b18834f..4ea3fc0e2 100644
--- a/packages/astro/e2e/css-sourcemaps.test.js
+++ b/packages/astro/e2e/css-sourcemaps.test.js
@@ -1,5 +1,5 @@
import { expect } from '@playwright/test';
-import { isWindows, testFactory } from './test-utils.js';
+import { testFactory } from './test-utils.js';
const test = testFactory({
root: './fixtures/css/',
@@ -16,8 +16,6 @@ test.afterAll(async () => {
});
test.describe('CSS Sourcemap HMR', () => {
- test.skip(isWindows, 'TODO: fix css hmr in windows');
-
test('removes Astro-injected CSS once Vite-injected CSS loads', async ({ page, astro }) => {
const html = await astro.fetch('/').then((res) => res.text());
diff --git a/packages/astro/e2e/css.test.js b/packages/astro/e2e/css.test.js
index 7c2527499..184e5dba3 100644
--- a/packages/astro/e2e/css.test.js
+++ b/packages/astro/e2e/css.test.js
@@ -1,5 +1,5 @@
import { expect } from '@playwright/test';
-import { getColor, isWindows, testFactory } from './test-utils.js';
+import { getColor, testFactory } from './test-utils.js';
const test = testFactory({
root: './fixtures/css/',
@@ -16,8 +16,6 @@ test.afterAll(async () => {
});
test.describe('CSS HMR', () => {
- test.skip(isWindows, 'TODO: fix css hmr in windows');
-
test('edit CSS from @import', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/'));
diff --git a/packages/astro/e2e/errors.test.js b/packages/astro/e2e/errors.test.js
index e23b22c65..4cb1b02fb 100644
--- a/packages/astro/e2e/errors.test.js
+++ b/packages/astro/e2e/errors.test.js
@@ -75,7 +75,7 @@ test.describe('Error display', () => {
const fileExists = astro.pathExists(absoluteFileUrl);
expect(fileExists).toBeTruthy();
- expect(fileLocation).toMatch(/^components\/PreactRuntimeError.jsx/);
+ expect(fileLocation).toMatch(/^preact\/PreactRuntimeError.jsx/);
});
test('shows correct line when a style preprocess has an error', async ({ page, astro }) => {
@@ -105,7 +105,10 @@ test.describe('Error display', () => {
// Wait for page reload
page.waitForNavigation(),
// Edit the component file
- astro.editFile('./src/components/SvelteSyntaxError.svelte', () => `<h1>No mismatch</h1>`),
+ astro.editFile(
+ './src/components/svelte/SvelteSyntaxError.svelte',
+ () => `<h1>No mismatch</h1>`
+ ),
]);
expect(await page.locator('vite-error-overlay').count()).toEqual(0);
diff --git a/packages/astro/e2e/fixtures/client-only/astro.config.mjs b/packages/astro/e2e/fixtures/client-only/astro.config.mjs
index 4b50887cd..0ef8f6e2b 100644
--- a/packages/astro/e2e/fixtures/client-only/astro.config.mjs
+++ b/packages/astro/e2e/fixtures/client-only/astro.config.mjs
@@ -8,5 +8,11 @@ import solid from '@astrojs/solid-js';
// https://astro.build/config
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
- integrations: [preact(), react(), svelte(), vue(), solid()],
+ integrations: [
+ react({ include: ['**/react/*'] }),
+ preact({ include: ['**/preact/*'] }),
+ solid({ include: ['**/solid/*'] }),
+ svelte(),
+ vue(),
+ ],
});
diff --git a/packages/astro/e2e/fixtures/client-only/src/components/PreactCounter.tsx b/packages/astro/e2e/fixtures/client-only/src/components/preact/PreactCounter.tsx
index b0570046c..b0570046c 100644
--- a/packages/astro/e2e/fixtures/client-only/src/components/PreactCounter.tsx
+++ b/packages/astro/e2e/fixtures/client-only/src/components/preact/PreactCounter.tsx
diff --git a/packages/astro/e2e/fixtures/client-only/src/components/ReactCounter.jsx b/packages/astro/e2e/fixtures/client-only/src/components/react/ReactCounter.jsx
index 770f93890..770f93890 100644
--- a/packages/astro/e2e/fixtures/client-only/src/components/ReactCounter.jsx
+++ b/packages/astro/e2e/fixtures/client-only/src/components/react/ReactCounter.jsx
diff --git a/packages/astro/e2e/fixtures/client-only/src/components/SolidCounter.tsx b/packages/astro/e2e/fixtures/client-only/src/components/solid/SolidCounter.tsx
index fbbb9850b..fbbb9850b 100644
--- a/packages/astro/e2e/fixtures/client-only/src/components/SolidCounter.tsx
+++ b/packages/astro/e2e/fixtures/client-only/src/components/solid/SolidCounter.tsx
diff --git a/packages/astro/e2e/fixtures/client-only/src/components/SvelteCounter.svelte b/packages/astro/e2e/fixtures/client-only/src/components/svelte/SvelteCounter.svelte
index cc9fe8c93..cc9fe8c93 100644
--- a/packages/astro/e2e/fixtures/client-only/src/components/SvelteCounter.svelte
+++ b/packages/astro/e2e/fixtures/client-only/src/components/svelte/SvelteCounter.svelte
diff --git a/packages/astro/e2e/fixtures/client-only/src/components/VueCounter.vue b/packages/astro/e2e/fixtures/client-only/src/components/vue/VueCounter.vue
index 906da1944..906da1944 100644
--- a/packages/astro/e2e/fixtures/client-only/src/components/VueCounter.vue
+++ b/packages/astro/e2e/fixtures/client-only/src/components/vue/VueCounter.vue
diff --git a/packages/astro/e2e/fixtures/client-only/src/pages/index.astro b/packages/astro/e2e/fixtures/client-only/src/pages/index.astro
index 708ba1582..0b4299600 100644
--- a/packages/astro/e2e/fixtures/client-only/src/pages/index.astro
+++ b/packages/astro/e2e/fixtures/client-only/src/pages/index.astro
@@ -1,9 +1,9 @@
---
-import * as react from '../components/ReactCounter.jsx';
-import { PreactCounter } from '../components/PreactCounter.tsx';
-import SolidCounter from '../components/SolidCounter.tsx';
-import VueCounter from '../components/VueCounter.vue';
-import SvelteCounter from '../components/SvelteCounter.svelte';
+import * as react from '../components/react/ReactCounter.jsx';
+import { PreactCounter } from '../components/preact/PreactCounter.jsx';
+import SolidCounter from '../components/solid/SolidCounter.jsx';
+import VueCounter from '../components/vue/VueCounter.vue';
+import SvelteCounter from '../components/svelte/SvelteCounter.svelte';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
diff --git a/packages/astro/e2e/fixtures/errors/astro.config.mjs b/packages/astro/e2e/fixtures/errors/astro.config.mjs
index 8f27d8fab..48e8736bd 100644
--- a/packages/astro/e2e/fixtures/errors/astro.config.mjs
+++ b/packages/astro/e2e/fixtures/errors/astro.config.mjs
@@ -7,5 +7,11 @@ import vue from '@astrojs/vue';
// https://astro.build/config
export default defineConfig({
- integrations: [react(), preact(), solid(), svelte(), vue()],
-}); \ No newline at end of file
+ integrations: [
+ react({ include: ['**/react/*'] }),
+ preact({ include: ['**/preact/*'] }),
+ solid({ include: ['**/solid/*'] }),
+ svelte(),
+ vue(),
+ ],
+});
diff --git a/packages/astro/e2e/fixtures/errors/src/components/PreactRuntimeError.jsx b/packages/astro/e2e/fixtures/errors/src/components/preact/PreactRuntimeError.jsx
index 8ee30ccf4..8ee30ccf4 100644
--- a/packages/astro/e2e/fixtures/errors/src/components/PreactRuntimeError.jsx
+++ b/packages/astro/e2e/fixtures/errors/src/components/preact/PreactRuntimeError.jsx
diff --git a/packages/astro/e2e/fixtures/errors/src/components/PreactSyntaxError.jsx b/packages/astro/e2e/fixtures/errors/src/components/preact/PreactSyntaxError.jsx
index f6826c588..f6826c588 100644
--- a/packages/astro/e2e/fixtures/errors/src/components/PreactSyntaxError.jsx
+++ b/packages/astro/e2e/fixtures/errors/src/components/preact/PreactSyntaxError.jsx
diff --git a/packages/astro/e2e/fixtures/errors/src/components/ReactRuntimeError.jsx b/packages/astro/e2e/fixtures/errors/src/components/react/ReactRuntimeError.jsx
index 2ea3d9981..2ea3d9981 100644
--- a/packages/astro/e2e/fixtures/errors/src/components/ReactRuntimeError.jsx
+++ b/packages/astro/e2e/fixtures/errors/src/components/react/ReactRuntimeError.jsx
diff --git a/packages/astro/e2e/fixtures/errors/src/components/ReactSyntaxError.jsx b/packages/astro/e2e/fixtures/errors/src/components/react/ReactSyntaxError.jsx
index ff2798aa4..ff2798aa4 100644
--- a/packages/astro/e2e/fixtures/errors/src/components/ReactSyntaxError.jsx
+++ b/packages/astro/e2e/fixtures/errors/src/components/react/ReactSyntaxError.jsx
diff --git a/packages/astro/e2e/fixtures/errors/src/components/SolidRuntimeError.jsx b/packages/astro/e2e/fixtures/errors/src/components/solid/SolidRuntimeError.jsx
index 70398ddcb..70398ddcb 100644
--- a/packages/astro/e2e/fixtures/errors/src/components/SolidRuntimeError.jsx
+++ b/packages/astro/e2e/fixtures/errors/src/components/solid/SolidRuntimeError.jsx
diff --git a/packages/astro/e2e/fixtures/errors/src/components/SolidSyntaxError.jsx b/packages/astro/e2e/fixtures/errors/src/components/solid/SolidSyntaxError.jsx
index 0e396be43..0e396be43 100644
--- a/packages/astro/e2e/fixtures/errors/src/components/SolidSyntaxError.jsx
+++ b/packages/astro/e2e/fixtures/errors/src/components/solid/SolidSyntaxError.jsx
diff --git a/packages/astro/e2e/fixtures/errors/src/components/SvelteDirectiveError.svelte b/packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteDirectiveError.svelte
index b07b120a2..b07b120a2 100644
--- a/packages/astro/e2e/fixtures/errors/src/components/SvelteDirectiveError.svelte
+++ b/packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteDirectiveError.svelte
diff --git a/packages/astro/e2e/fixtures/errors/src/components/SvelteRuntimeError.svelte b/packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteRuntimeError.svelte
index 54dbab6a7..54dbab6a7 100644
--- a/packages/astro/e2e/fixtures/errors/src/components/SvelteRuntimeError.svelte
+++ b/packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteRuntimeError.svelte
diff --git a/packages/astro/e2e/fixtures/errors/src/components/SvelteSyntaxError.svelte b/packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteSyntaxError.svelte
index 457878396..457878396 100644
--- a/packages/astro/e2e/fixtures/errors/src/components/SvelteSyntaxError.svelte
+++ b/packages/astro/e2e/fixtures/errors/src/components/svelte/SvelteSyntaxError.svelte
diff --git a/packages/astro/e2e/fixtures/errors/src/components/VueRuntimeError.vue b/packages/astro/e2e/fixtures/errors/src/components/vue/VueRuntimeError.vue
index e2e38338b..e2e38338b 100644
--- a/packages/astro/e2e/fixtures/errors/src/components/VueRuntimeError.vue
+++ b/packages/astro/e2e/fixtures/errors/src/components/vue/VueRuntimeError.vue
diff --git a/packages/astro/e2e/fixtures/errors/src/components/VueSyntaxError.vue b/packages/astro/e2e/fixtures/errors/src/components/vue/VueSyntaxError.vue
index bd509b3bc..bd509b3bc 100644
--- a/packages/astro/e2e/fixtures/errors/src/components/VueSyntaxError.vue
+++ b/packages/astro/e2e/fixtures/errors/src/components/vue/VueSyntaxError.vue
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/astro-client-media-error.astro b/packages/astro/e2e/fixtures/errors/src/pages/astro-client-media-error.astro
index 46bb9a2c4..94e124c9a 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/astro-client-media-error.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/astro-client-media-error.astro
@@ -1,7 +1,7 @@
---
-import SvelteDirectiveError from '../components/SvelteDirectiveError.svelte';
+import SvelteDirectiveError from '../components/svelte/SvelteDirectiveError.svelte';
---
<div>
<SvelteDirectiveError client:media />
-</div> \ No newline at end of file
+</div>
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/astro-hydration-error.astro b/packages/astro/e2e/fixtures/errors/src/pages/astro-hydration-error.astro
index 1852539eb..8fcd56bd4 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/astro-hydration-error.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/astro-hydration-error.astro
@@ -1,7 +1,7 @@
---
-import SvelteDirectiveError from '../components/SvelteDirectiveError.svelte';
+import SvelteDirectiveError from '../components/svelte/SvelteDirectiveError.svelte';
---
<div>
<SvelteDirectiveError client:loadm />
-</div> \ No newline at end of file
+</div>
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/preact-runtime-error.astro b/packages/astro/e2e/fixtures/errors/src/pages/preact-runtime-error.astro
index 5c0aa2526..17ad92808 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/preact-runtime-error.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/preact-runtime-error.astro
@@ -1,5 +1,5 @@
---
-import PreactRuntimeError from '../components/PreactRuntimeError.jsx';
+import PreactRuntimeError from '../components/preact/PreactRuntimeError.jsx';
---
<div>
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/preact-syntax-error.astro b/packages/astro/e2e/fixtures/errors/src/pages/preact-syntax-error.astro
index 6748b0366..b0f709b9d 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/preact-syntax-error.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/preact-syntax-error.astro
@@ -1,5 +1,5 @@
---
-import PreactSyntaxError from '../components/PreactSyntaxError.jsx';
+import PreactSyntaxError from '../components/preact/PreactSyntaxError.jsx';
---
<div>
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/react-runtime-error.astro b/packages/astro/e2e/fixtures/errors/src/pages/react-runtime-error.astro
index f1df8ec99..18490518e 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/react-runtime-error.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/react-runtime-error.astro
@@ -1,5 +1,5 @@
---
-import ReactRuntimeError from '../components/ReactRuntimeError.jsx';
+import ReactRuntimeError from '../components/react/ReactRuntimeError.jsx';
---
<div>
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/react-syntax-error.astro b/packages/astro/e2e/fixtures/errors/src/pages/react-syntax-error.astro
index 6e61ae31d..c11b49ae0 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/react-syntax-error.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/react-syntax-error.astro
@@ -1,5 +1,5 @@
---
-import ReactSyntaxError from '../components/ReactSyntaxError.jsx';
+import ReactSyntaxError from '../components/react/ReactSyntaxError.jsx';
---
<div>
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/solid-runtime-error.astro b/packages/astro/e2e/fixtures/errors/src/pages/solid-runtime-error.astro
index 51a2608bd..7d4160126 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/solid-runtime-error.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/solid-runtime-error.astro
@@ -1,5 +1,5 @@
---
-import SolidRuntimeError from '../components/SolidRuntimeError.jsx';
+import SolidRuntimeError from '../components/solid/SolidRuntimeError.jsx';
---
<div>
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/solid-syntax-error.astro b/packages/astro/e2e/fixtures/errors/src/pages/solid-syntax-error.astro
index 0fdbad25b..028ab9ede 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/solid-syntax-error.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/solid-syntax-error.astro
@@ -1,5 +1,5 @@
---
-import SolidSyntaxError from '../components/SolidSyntaxError.jsx';
+import SolidSyntaxError from '../components/solid/SolidSyntaxError.jsx';
---
<div>
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/svelte-runtime-error.astro b/packages/astro/e2e/fixtures/errors/src/pages/svelte-runtime-error.astro
index ec499f6db..662a48f0e 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/svelte-runtime-error.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/svelte-runtime-error.astro
@@ -1,5 +1,5 @@
---
-import SvelteRuntimeError from '../components/SvelteRuntimeError.svelte';
+import SvelteRuntimeError from '../components/svelte/SvelteRuntimeError.svelte';
---
<div>
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/svelte-syntax-error.astro b/packages/astro/e2e/fixtures/errors/src/pages/svelte-syntax-error.astro
index 26c14c15e..e275251a4 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/svelte-syntax-error.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/svelte-syntax-error.astro
@@ -1,5 +1,5 @@
---
-import SvelteSyntaxError from '../components/SvelteSyntaxError.svelte';
+import SvelteSyntaxError from '../components/svelte/SvelteSyntaxError.svelte';
---
<div>
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/vue-runtime-error.astro b/packages/astro/e2e/fixtures/errors/src/pages/vue-runtime-error.astro
index b63f85e63..698da561f 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/vue-runtime-error.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/vue-runtime-error.astro
@@ -1,5 +1,5 @@
---
-import VueRuntimeError from '../components/VueRuntimeError.vue';
+import VueRuntimeError from '../components/vue/VueRuntimeError.vue';
---
<div>
diff --git a/packages/astro/e2e/fixtures/errors/src/pages/vue-syntax-error.astro b/packages/astro/e2e/fixtures/errors/src/pages/vue-syntax-error.astro
index 8db897fe1..c76ed16fe 100644
--- a/packages/astro/e2e/fixtures/errors/src/pages/vue-syntax-error.astro
+++ b/packages/astro/e2e/fixtures/errors/src/pages/vue-syntax-error.astro
@@ -1,5 +1,5 @@
---
-import VueSyntaxError from '../components/VueSyntaxError.vue';
+import VueSyntaxError from '../components/vue/VueSyntaxError.vue';
---
<div>
diff --git a/packages/astro/e2e/fixtures/multiple-frameworks/astro.config.mjs b/packages/astro/e2e/fixtures/multiple-frameworks/astro.config.mjs
index 4b50887cd..0ef8f6e2b 100644
--- a/packages/astro/e2e/fixtures/multiple-frameworks/astro.config.mjs
+++ b/packages/astro/e2e/fixtures/multiple-frameworks/astro.config.mjs
@@ -8,5 +8,11 @@ import solid from '@astrojs/solid-js';
// https://astro.build/config
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
- integrations: [preact(), react(), svelte(), vue(), solid()],
+ integrations: [
+ react({ include: ['**/react/*'] }),
+ preact({ include: ['**/preact/*'] }),
+ solid({ include: ['**/solid/*'] }),
+ svelte(),
+ vue(),
+ ],
});
diff --git a/packages/astro/e2e/fixtures/multiple-frameworks/src/components/PreactCounter.tsx b/packages/astro/e2e/fixtures/multiple-frameworks/src/components/preact/PreactCounter.tsx
index af2258fdf..af2258fdf 100644
--- a/packages/astro/e2e/fixtures/multiple-frameworks/src/components/PreactCounter.tsx
+++ b/packages/astro/e2e/fixtures/multiple-frameworks/src/components/preact/PreactCounter.tsx
diff --git a/packages/astro/e2e/fixtures/multiple-frameworks/src/components/ReactCounter.jsx b/packages/astro/e2e/fixtures/multiple-frameworks/src/components/react/ReactCounter.jsx
index 02eb19539..02eb19539 100644
--- a/packages/astro/e2e/fixtures/multiple-frameworks/src/components/ReactCounter.jsx
+++ b/packages/astro/e2e/fixtures/multiple-frameworks/src/components/react/ReactCounter.jsx
diff --git a/packages/astro/e2e/fixtures/multiple-frameworks/src/components/SolidCounter.tsx b/packages/astro/e2e/fixtures/multiple-frameworks/src/components/solid/SolidCounter.tsx
index 689c5222c..689c5222c 100644
--- a/packages/astro/e2e/fixtures/multiple-frameworks/src/components/SolidCounter.tsx
+++ b/packages/astro/e2e/fixtures/multiple-frameworks/src/components/solid/SolidCounter.tsx
diff --git a/packages/astro/e2e/fixtures/multiple-frameworks/src/components/SvelteCounter.svelte b/packages/astro/e2e/fixtures/multiple-frameworks/src/components/svelte/SvelteCounter.svelte
index d6870af3c..d6870af3c 100644
--- a/packages/astro/e2e/fixtures/multiple-frameworks/src/components/SvelteCounter.svelte
+++ b/packages/astro/e2e/fixtures/multiple-frameworks/src/components/svelte/SvelteCounter.svelte
diff --git a/packages/astro/e2e/fixtures/multiple-frameworks/src/components/VueCounter.vue b/packages/astro/e2e/fixtures/multiple-frameworks/src/components/vue/VueCounter.vue
index 4861511c8..4861511c8 100644
--- a/packages/astro/e2e/fixtures/multiple-frameworks/src/components/VueCounter.vue
+++ b/packages/astro/e2e/fixtures/multiple-frameworks/src/components/vue/VueCounter.vue
diff --git a/packages/astro/e2e/fixtures/multiple-frameworks/src/pages/index.astro b/packages/astro/e2e/fixtures/multiple-frameworks/src/pages/index.astro
index a30688bca..747744852 100644
--- a/packages/astro/e2e/fixtures/multiple-frameworks/src/pages/index.astro
+++ b/packages/astro/e2e/fixtures/multiple-frameworks/src/pages/index.astro
@@ -3,11 +3,11 @@
import '../styles/global.css';
// Component Imports
import { A, B as Renamed } from '../components';
-import * as react from '../components/ReactCounter.jsx';
-import { PreactCounter } from '../components/PreactCounter.tsx';
-import SolidCounter from '../components/SolidCounter.tsx';
-import VueCounter from '../components/VueCounter.vue';
-import SvelteCounter from '../components/SvelteCounter.svelte';
+import * as react from '../components/react/ReactCounter.jsx';
+import { PreactCounter } from '../components/preact/PreactCounter.tsx';
+import SolidCounter from '../components/solid/SolidCounter.tsx';
+import VueCounter from '../components/vue/VueCounter.vue';
+import SvelteCounter from '../components/svelte/SvelteCounter.svelte';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
diff --git a/packages/astro/e2e/fixtures/nested-in-preact/astro.config.mjs b/packages/astro/e2e/fixtures/nested-in-preact/astro.config.mjs
index 4b50887cd..0ef8f6e2b 100644
--- a/packages/astro/e2e/fixtures/nested-in-preact/astro.config.mjs
+++ b/packages/astro/e2e/fixtures/nested-in-preact/astro.config.mjs
@@ -8,5 +8,11 @@ import solid from '@astrojs/solid-js';
// https://astro.build/config
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
- integrations: [preact(), react(), svelte(), vue(), solid()],
+ integrations: [
+ react({ include: ['**/react/*'] }),
+ preact({ include: ['**/preact/*'] }),
+ solid({ include: ['**/solid/*'] }),
+ svelte(),
+ vue(),
+ ],
});
diff --git a/packages/astro/e2e/fixtures/nested-in-preact/src/components/PreactCounter.tsx b/packages/astro/e2e/fixtures/nested-in-preact/src/components/preact/PreactCounter.tsx
index 5f20f560d..5f20f560d 100644
--- a/packages/astro/e2e/fixtures/nested-in-preact/src/components/PreactCounter.tsx
+++ b/packages/astro/e2e/fixtures/nested-in-preact/src/components/preact/PreactCounter.tsx
diff --git a/packages/astro/e2e/fixtures/nested-in-preact/src/components/ReactCounter.jsx b/packages/astro/e2e/fixtures/nested-in-preact/src/components/react/ReactCounter.jsx
index 0dc0deb47..0dc0deb47 100644
--- a/packages/astro/e2e/fixtures/nested-in-preact/src/components/ReactCounter.jsx
+++ b/packages/astro/e2e/fixtures/nested-in-preact/src/components/react/ReactCounter.jsx
diff --git a/packages/astro/e2e/fixtures/nested-in-preact/src/components/SolidCounter.tsx b/packages/astro/e2e/fixtures/nested-in-preact/src/components/solid/SolidCounter.tsx
index afabe43b9..afabe43b9 100644
--- a/packages/astro/e2e/fixtures/nested-in-preact/src/components/SolidCounter.tsx
+++ b/packages/astro/e2e/fixtures/nested-in-preact/src/components/solid/SolidCounter.tsx
diff --git a/packages/astro/e2e/fixtures/nested-in-preact/src/components/SvelteCounter.svelte b/packages/astro/e2e/fixtures/nested-in-preact/src/components/svelte/SvelteCounter.svelte
index 733f58076..733f58076 100644
--- a/packages/astro/e2e/fixtures/nested-in-preact/src/components/SvelteCounter.svelte
+++ b/packages/astro/e2e/fixtures/nested-in-preact/src/components/svelte/SvelteCounter.svelte
diff --git a/packages/astro/e2e/fixtures/nested-in-preact/src/components/VueCounter.vue b/packages/astro/e2e/fixtures/nested-in-preact/src/components/vue/VueCounter.vue
index d404cc965..d404cc965 100644
--- a/packages/astro/e2e/fixtures/nested-in-preact/src/components/VueCounter.vue
+++ b/packages/astro/e2e/fixtures/nested-in-preact/src/components/vue/VueCounter.vue
diff --git a/packages/astro/e2e/fixtures/nested-in-preact/src/pages/index.astro b/packages/astro/e2e/fixtures/nested-in-preact/src/pages/index.astro
index 619e8cccd..7512b86f4 100644
--- a/packages/astro/e2e/fixtures/nested-in-preact/src/pages/index.astro
+++ b/packages/astro/e2e/fixtures/nested-in-preact/src/pages/index.astro
@@ -1,9 +1,9 @@
---
-import ReactCounter from '../components/ReactCounter.jsx';
-import { PreactCounter } from '../components/PreactCounter.tsx';
-import SolidCounter from '../components/SolidCounter.tsx';
-import VueCounter from '../components/VueCounter.vue';
-import SvelteCounter from '../components/SvelteCounter.svelte';
+import ReactCounter from '../components/react/ReactCounter.jsx';
+import { PreactCounter } from '../components/preact/PreactCounter.tsx';
+import SolidCounter from '../components/solid/SolidCounter.tsx';
+import VueCounter from '../components/vue/VueCounter.vue';
+import SvelteCounter from '../components/svelte/SvelteCounter.svelte';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
diff --git a/packages/astro/e2e/fixtures/nested-in-react/astro.config.mjs b/packages/astro/e2e/fixtures/nested-in-react/astro.config.mjs
index 4b50887cd..0ef8f6e2b 100644
--- a/packages/astro/e2e/fixtures/nested-in-react/astro.config.mjs
+++ b/packages/astro/e2e/fixtures/nested-in-react/astro.config.mjs
@@ -8,5 +8,11 @@ import solid from '@astrojs/solid-js';
// https://astro.build/config
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
- integrations: [preact(), react(), svelte(), vue(), solid()],
+ integrations: [
+ react({ include: ['**/react/*'] }),
+ preact({ include: ['**/preact/*'] }),
+ solid({ include: ['**/solid/*'] }),
+ svelte(),
+ vue(),
+ ],
});
diff --git a/packages/astro/e2e/fixtures/nested-in-react/src/components/PreactCounter.tsx b/packages/astro/e2e/fixtures/nested-in-react/src/components/preact/PreactCounter.tsx
index 5f20f560d..5f20f560d 100644
--- a/packages/astro/e2e/fixtures/nested-in-react/src/components/PreactCounter.tsx
+++ b/packages/astro/e2e/fixtures/nested-in-react/src/components/preact/PreactCounter.tsx
diff --git a/packages/astro/e2e/fixtures/nested-in-react/src/components/ReactCounter.jsx b/packages/astro/e2e/fixtures/nested-in-react/src/components/react/ReactCounter.jsx
index 0dc0deb47..0dc0deb47 100644
--- a/packages/astro/e2e/fixtures/nested-in-react/src/components/ReactCounter.jsx
+++ b/packages/astro/e2e/fixtures/nested-in-react/src/components/react/ReactCounter.jsx
diff --git a/packages/astro/e2e/fixtures/nested-in-react/src/components/SolidCounter.tsx b/packages/astro/e2e/fixtures/nested-in-react/src/components/solid/SolidCounter.tsx
index afabe43b9..afabe43b9 100644
--- a/packages/astro/e2e/fixtures/nested-in-react/src/components/SolidCounter.tsx
+++ b/packages/astro/e2e/fixtures/nested-in-react/src/components/solid/SolidCounter.tsx
diff --git a/packages/astro/e2e/fixtures/nested-in-react/src/components/SvelteCounter.svelte b/packages/astro/e2e/fixtures/nested-in-react/src/components/svelte/SvelteCounter.svelte
index 733f58076..733f58076 100644
--- a/packages/astro/e2e/fixtures/nested-in-react/src/components/SvelteCounter.svelte
+++ b/packages/astro/e2e/fixtures/nested-in-react/src/components/svelte/SvelteCounter.svelte
diff --git a/packages/astro/e2e/fixtures/nested-in-react/src/components/VueCounter.vue b/packages/astro/e2e/fixtures/nested-in-react/src/components/vue/VueCounter.vue
index d404cc965..d404cc965 100644
--- a/packages/astro/e2e/fixtures/nested-in-react/src/components/VueCounter.vue
+++ b/packages/astro/e2e/fixtures/nested-in-react/src/components/vue/VueCounter.vue
diff --git a/packages/astro/e2e/fixtures/nested-in-react/src/pages/index.astro b/packages/astro/e2e/fixtures/nested-in-react/src/pages/index.astro
index 0b3b23d9d..fe18a3151 100644
--- a/packages/astro/e2e/fixtures/nested-in-react/src/pages/index.astro
+++ b/packages/astro/e2e/fixtures/nested-in-react/src/pages/index.astro
@@ -1,9 +1,9 @@
---
-import ReactCounter from '../components/ReactCounter.jsx';
-import { PreactCounter } from '../components/PreactCounter.tsx';
-import SolidCounter from '../components/SolidCounter.tsx';
-import VueCounter from '../components/VueCounter.vue';
-import SvelteCounter from '../components/SvelteCounter.svelte';
+import ReactCounter from '../components/react/ReactCounter.jsx';
+import { PreactCounter } from '../components/preact/PreactCounter.tsx';
+import SolidCounter from '../components/solid/SolidCounter.tsx';
+import VueCounter from '../components/vue/VueCounter.vue';
+import SvelteCounter from '../components/svelte/SvelteCounter.svelte';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
diff --git a/packages/astro/e2e/fixtures/nested-in-solid/astro.config.mjs b/packages/astro/e2e/fixtures/nested-in-solid/astro.config.mjs
index 4b50887cd..0ef8f6e2b 100644
--- a/packages/astro/e2e/fixtures/nested-in-solid/astro.config.mjs
+++ b/packages/astro/e2e/fixtures/nested-in-solid/astro.config.mjs
@@ -8,5 +8,11 @@ import solid from '@astrojs/solid-js';
// https://astro.build/config
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
- integrations: [preact(), react(), svelte(), vue(), solid()],
+ integrations: [
+ react({ include: ['**/react/*'] }),
+ preact({ include: ['**/preact/*'] }),
+ solid({ include: ['**/solid/*'] }),
+ svelte(),
+ vue(),
+ ],
});
diff --git a/packages/astro/e2e/fixtures/nested-in-solid/src/components/PreactCounter.tsx b/packages/astro/e2e/fixtures/nested-in-solid/src/components/preact/PreactCounter.tsx
index 5f20f560d..5f20f560d 100644
--- a/packages/astro/e2e/fixtures/nested-in-solid/src/components/PreactCounter.tsx
+++ b/packages/astro/e2e/fixtures/nested-in-solid/src/components/preact/PreactCounter.tsx
diff --git a/packages/astro/e2e/fixtures/nested-in-solid/src/components/ReactCounter.jsx b/packages/astro/e2e/fixtures/nested-in-solid/src/components/react/ReactCounter.jsx
index c7197a072..c7197a072 100644
--- a/packages/astro/e2e/fixtures/nested-in-solid/src/components/ReactCounter.jsx
+++ b/packages/astro/e2e/fixtures/nested-in-solid/src/components/react/ReactCounter.jsx
diff --git a/packages/astro/e2e/fixtures/nested-in-solid/src/components/SolidCounter.tsx b/packages/astro/e2e/fixtures/nested-in-solid/src/components/solid/SolidCounter.tsx
index afabe43b9..afabe43b9 100644
--- a/packages/astro/e2e/fixtures/nested-in-solid/src/components/SolidCounter.tsx
+++ b/packages/astro/e2e/fixtures/nested-in-solid/src/components/solid/SolidCounter.tsx
diff --git a/packages/astro/e2e/fixtures/nested-in-solid/src/components/SvelteCounter.svelte b/packages/astro/e2e/fixtures/nested-in-solid/src/components/svelte/SvelteCounter.svelte
index 733f58076..733f58076 100644
--- a/packages/astro/e2e/fixtures/nested-in-solid/src/components/SvelteCounter.svelte
+++ b/packages/astro/e2e/fixtures/nested-in-solid/src/components/svelte/SvelteCounter.svelte
diff --git a/packages/astro/e2e/fixtures/nested-in-solid/src/components/VueCounter.vue b/packages/astro/e2e/fixtures/nested-in-solid/src/components/vue/VueCounter.vue
index d404cc965..d404cc965 100644
--- a/packages/astro/e2e/fixtures/nested-in-solid/src/components/VueCounter.vue
+++ b/packages/astro/e2e/fixtures/nested-in-solid/src/components/vue/VueCounter.vue
diff --git a/packages/astro/e2e/fixtures/nested-in-solid/src/pages/index.astro b/packages/astro/e2e/fixtures/nested-in-solid/src/pages/index.astro
index 0feb5ba60..7b2c8da8d 100644
--- a/packages/astro/e2e/fixtures/nested-in-solid/src/pages/index.astro
+++ b/packages/astro/e2e/fixtures/nested-in-solid/src/pages/index.astro
@@ -1,9 +1,9 @@
---
-import { Counter as ReactCounter } from '../components/ReactCounter.jsx';
-import { PreactCounter } from '../components/PreactCounter.tsx';
-import SolidCounter from '../components/SolidCounter.tsx';
-import VueCounter from '../components/VueCounter.vue';
-import SvelteCounter from '../components/SvelteCounter.svelte';
+import { Counter as ReactCounter } from '../components/react/ReactCounter.jsx';
+import { PreactCounter } from '../components/preact/PreactCounter.tsx';
+import SolidCounter from '../components/solid/SolidCounter.tsx';
+import VueCounter from '../components/vue/VueCounter.vue';
+import SvelteCounter from '../components/svelte/SvelteCounter.svelte';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
diff --git a/packages/astro/e2e/fixtures/nested-in-svelte/astro.config.mjs b/packages/astro/e2e/fixtures/nested-in-svelte/astro.config.mjs
index 4b50887cd..0ef8f6e2b 100644
--- a/packages/astro/e2e/fixtures/nested-in-svelte/astro.config.mjs
+++ b/packages/astro/e2e/fixtures/nested-in-svelte/astro.config.mjs
@@ -8,5 +8,11 @@ import solid from '@astrojs/solid-js';
// https://astro.build/config
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
- integrations: [preact(), react(), svelte(), vue(), solid()],
+ integrations: [
+ react({ include: ['**/react/*'] }),
+ preact({ include: ['**/preact/*'] }),
+ solid({ include: ['**/solid/*'] }),
+ svelte(),
+ vue(),
+ ],
});
diff --git a/packages/astro/e2e/fixtures/nested-in-svelte/src/components/PreactCounter.tsx b/packages/astro/e2e/fixtures/nested-in-svelte/src/components/preact/PreactCounter.tsx
index 5f20f560d..5f20f560d 100644
--- a/packages/astro/e2e/fixtures/nested-in-svelte/src/components/PreactCounter.tsx
+++ b/packages/astro/e2e/fixtures/nested-in-svelte/src/components/preact/PreactCounter.tsx
diff --git a/packages/astro/e2e/fixtures/nested-in-svelte/src/components/ReactCounter.jsx b/packages/astro/e2e/fixtures/nested-in-svelte/src/components/react/ReactCounter.jsx
index c7197a072..c7197a072 100644
--- a/packages/astro/e2e/fixtures/nested-in-svelte/src/components/ReactCounter.jsx
+++ b/packages/astro/e2e/fixtures/nested-in-svelte/src/components/react/ReactCounter.jsx
diff --git a/packages/astro/e2e/fixtures/nested-in-svelte/src/components/SolidCounter.tsx b/packages/astro/e2e/fixtures/nested-in-svelte/src/components/solid/SolidCounter.tsx
index afabe43b9..afabe43b9 100644
--- a/packages/astro/e2e/fixtures/nested-in-svelte/src/components/SolidCounter.tsx
+++ b/packages/astro/e2e/fixtures/nested-in-svelte/src/components/solid/SolidCounter.tsx
diff --git a/packages/astro/e2e/fixtures/nested-in-svelte/src/components/SvelteCounter.svelte b/packages/astro/e2e/fixtures/nested-in-svelte/src/components/svelte/SvelteCounter.svelte
index 733f58076..733f58076 100644
--- a/packages/astro/e2e/fixtures/nested-in-svelte/src/components/SvelteCounter.svelte
+++ b/packages/astro/e2e/fixtures/nested-in-svelte/src/components/svelte/SvelteCounter.svelte
diff --git a/packages/astro/e2e/fixtures/nested-in-svelte/src/components/VueCounter.vue b/packages/astro/e2e/fixtures/nested-in-svelte/src/components/vue/VueCounter.vue
index d404cc965..d404cc965 100644
--- a/packages/astro/e2e/fixtures/nested-in-svelte/src/components/VueCounter.vue
+++ b/packages/astro/e2e/fixtures/nested-in-svelte/src/components/vue/VueCounter.vue
diff --git a/packages/astro/e2e/fixtures/nested-in-svelte/src/pages/index.astro b/packages/astro/e2e/fixtures/nested-in-svelte/src/pages/index.astro
index 764ebfaf7..aa11de7fd 100644
--- a/packages/astro/e2e/fixtures/nested-in-svelte/src/pages/index.astro
+++ b/packages/astro/e2e/fixtures/nested-in-svelte/src/pages/index.astro
@@ -1,9 +1,9 @@
---
-import { Counter as ReactCounter } from '../components/ReactCounter.jsx';
-import { PreactCounter } from '../components/PreactCounter.tsx';
-import SolidCounter from '../components/SolidCounter.tsx';
-import VueCounter from '../components/VueCounter.vue';
-import SvelteCounter from '../components/SvelteCounter.svelte';
+import { Counter as ReactCounter } from '../components/react/ReactCounter.jsx';
+import { PreactCounter } from '../components/preact/PreactCounter.tsx';
+import SolidCounter from '../components/solid/SolidCounter.tsx';
+import VueCounter from '../components/vue/VueCounter.vue';
+import SvelteCounter from '../components/svelte/SvelteCounter.svelte';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
diff --git a/packages/astro/e2e/fixtures/nested-in-vue/astro.config.mjs b/packages/astro/e2e/fixtures/nested-in-vue/astro.config.mjs
index 4b50887cd..0ef8f6e2b 100644
--- a/packages/astro/e2e/fixtures/nested-in-vue/astro.config.mjs
+++ b/packages/astro/e2e/fixtures/nested-in-vue/astro.config.mjs
@@ -8,5 +8,11 @@ import solid from '@astrojs/solid-js';
// https://astro.build/config
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
- integrations: [preact(), react(), svelte(), vue(), solid()],
+ integrations: [
+ react({ include: ['**/react/*'] }),
+ preact({ include: ['**/preact/*'] }),
+ solid({ include: ['**/solid/*'] }),
+ svelte(),
+ vue(),
+ ],
});
diff --git a/packages/astro/e2e/fixtures/nested-in-vue/src/components/PreactCounter.tsx b/packages/astro/e2e/fixtures/nested-in-vue/src/components/preact/PreactCounter.tsx
index 5f20f560d..5f20f560d 100644
--- a/packages/astro/e2e/fixtures/nested-in-vue/src/components/PreactCounter.tsx
+++ b/packages/astro/e2e/fixtures/nested-in-vue/src/components/preact/PreactCounter.tsx
diff --git a/packages/astro/e2e/fixtures/nested-in-vue/src/components/ReactCounter.jsx b/packages/astro/e2e/fixtures/nested-in-vue/src/components/react/ReactCounter.jsx
index c7197a072..c7197a072 100644
--- a/packages/astro/e2e/fixtures/nested-in-vue/src/components/ReactCounter.jsx
+++ b/packages/astro/e2e/fixtures/nested-in-vue/src/components/react/ReactCounter.jsx
diff --git a/packages/astro/e2e/fixtures/nested-in-vue/src/components/SolidCounter.tsx b/packages/astro/e2e/fixtures/nested-in-vue/src/components/solid/SolidCounter.tsx
index afabe43b9..afabe43b9 100644
--- a/packages/astro/e2e/fixtures/nested-in-vue/src/components/SolidCounter.tsx
+++ b/packages/astro/e2e/fixtures/nested-in-vue/src/components/solid/SolidCounter.tsx
diff --git a/packages/astro/e2e/fixtures/nested-in-vue/src/components/SvelteCounter.svelte b/packages/astro/e2e/fixtures/nested-in-vue/src/components/svelte/SvelteCounter.svelte
index 733f58076..733f58076 100644
--- a/packages/astro/e2e/fixtures/nested-in-vue/src/components/SvelteCounter.svelte
+++ b/packages/astro/e2e/fixtures/nested-in-vue/src/components/svelte/SvelteCounter.svelte
diff --git a/packages/astro/e2e/fixtures/nested-in-vue/src/components/VueCounter.vue b/packages/astro/e2e/fixtures/nested-in-vue/src/components/vue/VueCounter.vue
index d404cc965..d404cc965 100644
--- a/packages/astro/e2e/fixtures/nested-in-vue/src/components/VueCounter.vue
+++ b/packages/astro/e2e/fixtures/nested-in-vue/src/components/vue/VueCounter.vue
diff --git a/packages/astro/e2e/fixtures/nested-in-vue/src/pages/index.astro b/packages/astro/e2e/fixtures/nested-in-vue/src/pages/index.astro
index 5e4d47d76..e89554755 100644
--- a/packages/astro/e2e/fixtures/nested-in-vue/src/pages/index.astro
+++ b/packages/astro/e2e/fixtures/nested-in-vue/src/pages/index.astro
@@ -1,9 +1,9 @@
---
-import { Counter as ReactCounter } from '../components/ReactCounter.jsx';
-import { PreactCounter } from '../components/PreactCounter.tsx';
-import SolidCounter from '../components/SolidCounter.tsx';
-import VueCounter from '../components/VueCounter.vue';
-import SvelteCounter from '../components/SvelteCounter.svelte';
+import { Counter as ReactCounter } from '../components/react/ReactCounter.jsx';
+import { PreactCounter } from '../components/preact/PreactCounter.tsx';
+import SolidCounter from '../components/solid/SolidCounter.tsx';
+import VueCounter from '../components/vue/VueCounter.vue';
+import SvelteCounter from '../components/svelte/SvelteCounter.svelte';
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
diff --git a/packages/astro/e2e/fixtures/nested-recursive/astro.config.mjs b/packages/astro/e2e/fixtures/nested-recursive/astro.config.mjs
index 4b50887cd..0ef8f6e2b 100644
--- a/packages/astro/e2e/fixtures/nested-recursive/astro.config.mjs
+++ b/packages/astro/e2e/fixtures/nested-recursive/astro.config.mjs
@@ -8,5 +8,11 @@ import solid from '@astrojs/solid-js';
// https://astro.build/config
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
- integrations: [preact(), react(), svelte(), vue(), solid()],
+ integrations: [
+ react({ include: ['**/react/*'] }),
+ preact({ include: ['**/preact/*'] }),
+ solid({ include: ['**/solid/*'] }),
+ svelte(),
+ vue(),
+ ],
});
diff --git a/packages/astro/e2e/fixtures/nested-recursive/src/components/PreactCounter.tsx b/packages/astro/e2e/fixtures/nested-recursive/src/components/preact/PreactCounter.tsx
index 32200f41f..32200f41f 100644
--- a/packages/astro/e2e/fixtures/nested-recursive/src/components/PreactCounter.tsx
+++ b/packages/astro/e2e/fixtures/nested-recursive/src/components/preact/PreactCounter.tsx
diff --git a/packages/astro/e2e/fixtures/nested-recursive/src/components/ReactCounter.jsx b/packages/astro/e2e/fixtures/nested-recursive/src/components/react/ReactCounter.jsx
index 6b3a1de5f..6b3a1de5f 100644
--- a/packages/astro/e2e/fixtures/nested-recursive/src/components/ReactCounter.jsx
+++ b/packages/astro/e2e/fixtures/nested-recursive/src/components/react/ReactCounter.jsx
diff --git a/packages/astro/e2e/fixtures/nested-recursive/src/components/SolidCounter.tsx b/packages/astro/e2e/fixtures/nested-recursive/src/components/solid/SolidCounter.tsx
index afabe43b9..afabe43b9 100644
--- a/packages/astro/e2e/fixtures/nested-recursive/src/components/SolidCounter.tsx
+++ b/packages/astro/e2e/fixtures/nested-recursive/src/components/solid/SolidCounter.tsx
diff --git a/packages/astro/e2e/fixtures/nested-recursive/src/components/SvelteCounter.svelte b/packages/astro/e2e/fixtures/nested-recursive/src/components/svelte/SvelteCounter.svelte
index 733f58076..733f58076 100644
--- a/packages/astro/e2e/fixtures/nested-recursive/src/components/SvelteCounter.svelte
+++ b/packages/astro/e2e/fixtures/nested-recursive/src/components/svelte/SvelteCounter.svelte
diff --git a/packages/astro/e2e/fixtures/nested-recursive/src/components/VueCounter.vue b/packages/astro/e2e/fixtures/nested-recursive/src/components/vue/VueCounter.vue
index d404cc965..d404cc965 100644
--- a/packages/astro/e2e/fixtures/nested-recursive/src/components/VueCounter.vue
+++ b/packages/astro/e2e/fixtures/nested-recursive/src/components/vue/VueCounter.vue
diff --git a/packages/astro/e2e/fixtures/nested-recursive/src/pages/index.astro b/packages/astro/e2e/fixtures/nested-recursive/src/pages/index.astro
index 685c7fb5e..a17337c89 100644
--- a/packages/astro/e2e/fixtures/nested-recursive/src/pages/index.astro
+++ b/packages/astro/e2e/fixtures/nested-recursive/src/pages/index.astro
@@ -1,9 +1,9 @@
---
-import ReactCounter from '../components/ReactCounter.jsx';
-import PreactCounter from '../components/PreactCounter.tsx';
-import SolidCounter from '../components/SolidCounter.tsx';
-import VueCounter from '../components/VueCounter.vue';
-import SvelteCounter from '../components/SvelteCounter.svelte';
+import ReactCounter from '../components/react/ReactCounter.jsx';
+import PreactCounter from '../components/preact/PreactCounter.tsx';
+import SolidCounter from '../components/solid/SolidCounter.tsx';
+import VueCounter from '../components/vue/VueCounter.vue';
+import SvelteCounter from '../components/svelte/SvelteCounter.svelte';
---
<html lang="en">
diff --git a/packages/astro/e2e/fixtures/view-transitions/astro.config.mjs b/packages/astro/e2e/fixtures/view-transitions/astro.config.mjs
index 8a9f43bcc..78c248963 100644
--- a/packages/astro/e2e/fixtures/view-transitions/astro.config.mjs
+++ b/packages/astro/e2e/fixtures/view-transitions/astro.config.mjs
@@ -9,7 +9,6 @@ export default defineConfig({
integrations: [react()],
experimental: {
viewTransitions: true,
- assets: true,
},
vite: {
build: {
diff --git a/packages/astro/e2e/multiple-frameworks.test.js b/packages/astro/e2e/multiple-frameworks.test.js
index b9bc85576..e067b0357 100644
--- a/packages/astro/e2e/multiple-frameworks.test.js
+++ b/packages/astro/e2e/multiple-frameworks.test.js
@@ -154,9 +154,7 @@ test.skip('Multiple frameworks', () => {
await expect(count, 'initial count updated to 5').toHaveText('5');
});
- // TODO: re-enable this test when #3559 is fixed
- // https://github.com/withastro/astro/issues/3559
- test.skip('Vue component', async ({ astro, page }) => {
+ test('Vue component', async ({ astro, page }) => {
await page.goto(astro.resolveUrl('/'));
const count = page.locator('#vue-counter pre');
@@ -169,9 +167,7 @@ test.skip('Multiple frameworks', () => {
await expect(count, 'initial count updated to 5').toHaveText('5');
});
- // TODO: track down a reliability issue in this test
- // It seems to lost connection to the vite server in CI
- test.skip('Svelte component', async ({ astro, page }) => {
+ test('Svelte component', async ({ astro, page }) => {
await page.goto(astro.resolveUrl('/'));
const count = page.locator('#svelte-counter pre');
diff --git a/packages/astro/env.d.ts b/packages/astro/env.d.ts
index 079370bec..876a29c60 100644
--- a/packages/astro/env.d.ts
+++ b/packages/astro/env.d.ts
@@ -1,8 +1,8 @@
-/// <reference path="./client-base.d.ts" />
+/// <reference path="./client.d.ts" />
// Caution! The types here are only available inside Astro files (injected automatically by our language server)
// As such, if the typings you're trying to add should be available inside ex: React components, they should instead
-// be inside `client-base.d.ts`
+// be inside `client.d.ts`
type Astro = import('./dist/@types/astro.js').AstroGlobal;
diff --git a/packages/astro/index.d.ts b/packages/astro/index.d.ts
new file mode 100644
index 000000000..a9e679be1
--- /dev/null
+++ b/packages/astro/index.d.ts
@@ -0,0 +1,2 @@
+export type * from './dist/@types/astro.js';
+export * from './dist/core/index.js';
diff --git a/packages/astro/package.json b/packages/astro/package.json
index 2a97d12d1..462a10307 100644
--- a/packages/astro/package.json
+++ b/packages/astro/package.json
@@ -1,6 +1,6 @@
{
"name": "astro",
- "version": "2.10.12",
+ "version": "3.0.0-beta.4",
"description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
"type": "module",
"author": "withastro",
@@ -12,7 +12,7 @@
},
"bugs": "https://github.com/withastro/astro/issues",
"homepage": "https://astro.build",
- "types": "./dist/@types/astro.d.ts",
+ "types": "./index.d.ts",
"typesVersions": {
"*": {
"app": [
@@ -23,25 +23,28 @@
],
"middleware": [
"./dist/core/middleware/index.d.ts"
+ ],
+ "middleware/namespace": [
+ "./dist/core/middleware/namespace.d.ts"
]
}
},
"exports": {
".": {
- "types": "./dist/@types/astro.d.ts",
- "default": "./astro.js"
+ "types": "./index.d.ts",
+ "default": "./dist/core/index.js"
},
"./env": "./env.d.ts",
"./types": "./types.d.ts",
"./client": "./client.d.ts",
- "./client-base": "./client-base.d.ts",
- "./client-image": "./client-image.d.ts",
"./import-meta": "./import-meta.d.ts",
"./astro-jsx": "./astro-jsx.d.ts",
"./tsconfigs/*.json": "./tsconfigs/*",
"./tsconfigs/*": "./tsconfigs/*.json",
"./jsx/*": "./dist/jsx/*",
"./jsx-runtime": "./dist/jsx-runtime/index.js",
+ "./compiler-runtime": "./dist/runtime/compiler/index.js",
+ "./runtime/*": "./dist/runtime/*",
"./config": {
"types": "./config.d.ts",
"default": "./config.mjs"
@@ -56,22 +59,24 @@
"./assets/image-endpoint": "./dist/assets/image-endpoint.js",
"./assets/services/sharp": "./dist/assets/services/sharp.js",
"./assets/services/squoosh": "./dist/assets/services/squoosh.js",
+ "./assets/services/noop": "./dist/assets/services/noop.js",
"./content/runtime": "./dist/content/runtime.js",
"./content/runtime-assets": "./dist/content/runtime-assets.js",
"./debug": "./components/Debug.astro",
- "./internal/*": "./dist/runtime/server/*",
"./package.json": "./package.json",
- "./runtime/*": "./dist/runtime/*",
- "./server/*": "./dist/runtime/server/*",
- "./dist/jsx/*": "./dist/jsx/*",
"./zod": {
"types": "./zod.d.ts",
"default": "./zod.mjs"
},
+ "./errors": "./dist/core/errors/userError.js",
"./middleware": {
"types": "./dist/core/middleware/index.d.ts",
"default": "./dist/core/middleware/index.js"
},
+ "./middleware/namespace": {
+ "types": "./dist/core/middleware/namespace.d.ts",
+ "default": "./dist/core/middleware/namespace.js"
+ },
"./transitions": "./dist/transitions/index.js"
},
"imports": {
@@ -85,14 +90,13 @@
"tsconfigs",
"dist",
"astro.js",
+ "index.d.ts",
"config.d.ts",
"config.mjs",
"zod.d.ts",
"zod.mjs",
"env.d.ts",
"client.d.ts",
- "client-base.d.ts",
- "client-image.d.ts",
"content-types.template.d.ts",
"content-module.template.mjs",
"import-meta.d.ts",
@@ -115,12 +119,10 @@
"test:e2e:match": "playwright test -g"
},
"dependencies": {
- "@astrojs/compiler": "^1.8.0",
- "@astrojs/internal-helpers": "^0.1.2",
- "@astrojs/language-server": "^1.0.0",
- "@astrojs/markdown-remark": "^2.2.1",
- "@astrojs/telemetry": "^2.1.1",
- "@astrojs/webapi": "^2.2.0",
+ "@astrojs/compiler": "^2.0.0",
+ "@astrojs/internal-helpers": "workspace:*",
+ "@astrojs/markdown-remark": "workspace:*",
+ "@astrojs/telemetry": "workspace:*",
"@babel/core": "^7.22.5",
"@babel/generator": "^7.22.5",
"@babel/parser": "^7.22.5",
@@ -128,20 +130,20 @@
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5",
"@types/babel__core": "^7.20.1",
- "@types/dom-view-transitions": "^1.0.1",
- "@types/yargs-parser": "^21.0.0",
"acorn": "^8.9.0",
"boxen": "^6.2.1",
"chokidar": "^3.5.3",
"ci-info": "^3.8.0",
+ "clsx": "^2.0.0",
"common-ancestor-path": "^1.0.1",
"cookie": "^0.5.0",
"debug": "^4.3.4",
+ "deepmerge-ts": "^4.3.0",
"devalue": "^4.3.2",
"diff": "^5.1.0",
"es-module-lexer": "^1.3.0",
- "esbuild": "^0.17.19",
- "estree-walker": "3.0.0",
+ "esbuild": "^0.18.16",
+ "estree-walker": "^3.0.3",
"execa": "^6.1.0",
"fast-glob": "^3.2.12",
"github-slugger": "^2.0.0",
@@ -152,7 +154,6 @@
"kleur": "^4.1.4",
"magic-string": "^0.30.2",
"mime": "^3.0.0",
- "network-information-types": "^0.1.1",
"ora": "^6.3.1",
"p-limit": "^4.0.0",
"path-to-regexp": "^6.2.1",
@@ -161,11 +162,12 @@
"rehype": "^12.0.1",
"semver": "^7.5.3",
"server-destroy": "^1.0.1",
+ "sharp": "^0.32.1",
"shiki": "^0.14.1",
"string-width": "^5.1.2",
"strip-ansi": "^7.1.0",
"tsconfig-resolver": "^3.0.1",
- "typescript": "*",
+ "undici": "^5.23.0",
"unist-util-visit": "^4.1.2",
"vfile": "^5.3.7",
"vite": "^4.4.6",
@@ -175,6 +177,7 @@
"zod": "^3.20.6"
},
"devDependencies": {
+ "@astrojs/check": "^0.1.0",
"@playwright/test": "^1.29.2",
"@types/babel__generator": "^7.6.4",
"@types/babel__traverse": "^7.20.1",
@@ -184,6 +187,7 @@
"@types/cookie": "^0.5.1",
"@types/debug": "^4.1.8",
"@types/diff": "^5.0.3",
+ "@types/dom-view-transitions": "^1.0.1",
"@types/estree": "^0.0.51",
"@types/hast": "^2.3.4",
"@types/html-escaper": "^3.0.0",
@@ -193,17 +197,17 @@
"@types/mocha": "^9.1.1",
"@types/prettier": "^2.7.3",
"@types/prompts": "^2.4.4",
- "@types/resolve": "^1.20.2",
- "@types/rimraf": "^3.0.2",
"@types/send": "^0.17.1",
"@types/server-destroy": "^1.0.1",
"@types/unist": "^2.0.6",
+ "@types/yargs-parser": "^21.0.0",
"astro-scripts": "workspace:*",
"chai": "^4.3.7",
"cheerio": "1.0.0-rc.12",
"eol": "^0.9.1",
"memfs": "^3.5.3",
"mocha": "^9.2.2",
+ "network-information-types": "^0.1.1",
"node-mocks-http": "^1.12.2",
"rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.0.1",
@@ -211,21 +215,11 @@
"remark-code-titles": "^0.1.2",
"rollup": "^3.25.1",
"sass": "^1.63.4",
- "sharp": "^0.32.1",
"srcset-parse": "^1.1.0",
- "undici": "^5.22.1",
"unified": "^10.1.2"
},
- "peerDependencies": {
- "sharp": ">=0.31.0"
- },
- "peerDependenciesMeta": {
- "sharp": {
- "optional": true
- }
- },
"engines": {
- "node": ">=16.12.0",
+ "node": ">=18.14.1",
"npm": ">=6.14.0"
}
}
diff --git a/packages/astro/scripts/shiki-gen-themes.mjs b/packages/astro/scripts/shiki-gen-themes.mjs
index f6d30865d..30ff2f461 100644
--- a/packages/astro/scripts/shiki-gen-themes.mjs
+++ b/packages/astro/scripts/shiki-gen-themes.mjs
@@ -8,15 +8,6 @@ const themeImports = dir.map((f) => {
return [f.slice(0, f.indexOf('.json')), toThemeImport(f)];
});
-// Map of old theme names to new names to preserve compatibility when we upgrade shiki
-const compatThemes = {
- 'material-darker': 'material-theme-darker',
- 'material-default': 'material-theme',
- 'material-lighter': 'material-theme-lighter',
- 'material-ocean': 'material-theme-ocean',
- 'material-palenight': 'material-theme-palenight',
-};
-
let code = `\
/**
* This file is prebuilt from packages/astro/scripts/shiki-gen-themes.mjs
@@ -29,10 +20,6 @@ export const themes = {`;
for (const [key, imp] of themeImports) {
code += `\n\t'${key}': () => ${imp},`;
}
-code += `\n\t// old theme names for compat`;
-for (const oldName in compatThemes) {
- code += `\n\t'${oldName}': () => ${toThemeImport(compatThemes[oldName])},`;
-}
code += '\n};';
// eslint-disable-next-line no-console
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index cc5ddea7f..557ad1ed1 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -20,8 +20,11 @@ import type { AstroConfigType } from '../core/config';
import type { AstroTimer } from '../core/config/timer';
import type { AstroCookies } from '../core/cookies';
import type { LogOptions, LoggerLevel } from '../core/logger/core';
+import type { AstroIntegrationLogger } from '../core/logger/core';
import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server';
import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js';
+import type { ResponseWithEncoding } from '../core/endpoint/index.js';
+
export type {
MarkdownHeading,
MarkdownMetadata,
@@ -101,15 +104,11 @@ export interface AstroDefineVarsAttribute {
}
export interface AstroStyleAttributes {
- /** @deprecated Use `is:global` instead */
- global?: boolean;
'is:global'?: boolean;
'is:inline'?: boolean;
}
export interface AstroScriptAttributes {
- /** @deprecated Hoist is now the default behavior */
- hoist?: boolean;
'is:inline'?: boolean;
}
@@ -132,7 +131,6 @@ export interface CLIFlags {
config?: string;
drafts?: boolean;
open?: boolean;
- experimentalAssets?: boolean;
}
/**
@@ -142,7 +140,7 @@ export interface CLIFlags {
*/
export interface AstroGlobal<
Props extends Record<string, any> = Record<string, any>,
- Self = AstroComponentFactory
+ Self = AstroComponentFactory,
> extends AstroGlobalPartial,
AstroSharedContext<Props> {
/**
@@ -329,7 +327,7 @@ type ServerConfig = {
/**
* @name server.port
* @type {number}
- * @default `3000`
+ * @default `4321`
* @description
* Set which port the dev server should listen on.
*
@@ -543,14 +541,14 @@ export interface AstroUserConfig {
* @docs
* @name compressHTML
* @type {boolean}
- * @default `false`
+ * @default `true`
* @description
- * This is an option to minify your HTML output and reduce the size of your HTML files. When enabled, Astro removes all whitespace from your HTML, including line breaks, from `.astro` components. This occurs both in development mode and in the final build.
- * To enable this, set the `compressHTML` flag to `true`.
+ * This is an option to minify your HTML output and reduce the size of your HTML files. By default, Astro removes all whitespace from your HTML, including line breaks, from `.astro` components. This occurs both in development mode and in the final build.
+ * To disable HTML compression, set the `compressHTML` flag to `false`.
*
* ```js
* {
- * compressHTML: true
+ * compressHTML: false
* }
* ```
*/
@@ -573,12 +571,7 @@ export interface AstroUserConfig {
*
* When using this option, all of your static asset imports and URLs should add the base as a prefix. You can access this value via `import.meta.env.BASE_URL`.
*
- * By default, the value of `import.meta.env.BASE_URL` includes a trailing slash. If you have the [`trailingSlash`](https://docs.astro.build/en/reference/configuration-reference/#trailingslash) option set to `'never'`, you will need to add it manually in your static asset imports and URLs.
- *
- * ```astro
- * <a href="/docs/about/">About</a>
- * <img src=`${import.meta.env.BASE_URL}image.png`>
- * ```
+ * The value of `import.meta.env.BASE_URL` respects your `trailingSlash` config and will include a trailing slash if you explicitly include one or if `trailingSlash: "always"` is set. If `trailingSlash: "never"` is set, `BASE_URL` will not include a trailing slash, even if `base` includes one.
*/
base?: string;
@@ -611,19 +604,21 @@ export interface AstroUserConfig {
/**
* @docs
* @name scopedStyleStrategy
- * @type {('where' | 'class')}
- * @default `'where'`
+ * @type {('where' | 'class' | 'attribute')}
+ * @default `'attribute'`
* @version 2.4
* @description
*
* Specify the strategy used for scoping styles within Astro components. Choose from:
- * - `'where'` - Use `:where` selectors, causing no specifity increase.
- * - `'class'` - Use class-based selectors, causing a +1 specifity increase.
+ * - `'where'` - Use `:where` selectors, causing no specifity increase.
+ * - `'class'` - Use class-based selectors, causing a +1 specifity increase.
+ * - `'attribute'` - Use `data-` attributes, causing no specifity increase.
*
* Using `'class'` is helpful when you want to ensure that element selectors within an Astro component override global style defaults (e.g. from a global stylesheet).
* Using `'where'` gives you more control over specifity, but requires that you use higher-specifity selectors, layers, and other tools to control which selectors are applied.
+ * Using 'attribute' is useful when you are manipulating the `class` attribute of elements and need to avoid conflicts between your own styling logic and Astro's application of styles.
*/
- scopedStyleStrategy?: 'where' | 'class';
+ scopedStyleStrategy?: 'where' | 'class' | 'attribute';
/**
* @docs
@@ -838,7 +833,7 @@ export interface AstroUserConfig {
* @docs
* @name build.inlineStylesheets
* @type {('always' | 'auto' | 'never')}
- * @default `never`
+ * @default `auto`
* @version 2.6.0
* @description
* Control whether project styles are sent to the browser in a separate css file or inlined into `<style>` tags. Choose from the following options:
@@ -849,7 +844,7 @@ export interface AstroUserConfig {
* ```js
* {
* build: {
- * inlineStylesheets: `auto`,
+ * inlineStylesheets: `never`,
* },
* }
* ```
@@ -861,22 +856,12 @@ export interface AstroUserConfig {
* @name build.split
* @type {boolean}
* @default `false`
- * @version 2.7.0
+ * @deprecated Deprecated since version 3.0.
* @description
- * Defines how the SSR code should be bundled when built.
+ * The build config option `build.split` has been replaced by the adapter configuration option [`functionPerRoute`](/en/reference/adapter-reference/#functionperroute).
*
- * When `split` is `true`, Astro will emit a file for each page.
- * Each file emitted will render only one page. The pages will be emitted
- * inside a `dist/pages/` directory, and the emitted files will keep the same file paths
- * of the `src/pages` directory.
- *
- * ```js
- * {
- * build: {
- * split: true
- * }
- * }
- * ```
+ * Please see your [SSR adapter's documentation](/en/guides/integrations-guide/#official-integrations) for using `functionPerRoute` to define how your SSR code is bundled.
+ *
*/
split?: boolean;
@@ -885,19 +870,11 @@ export interface AstroUserConfig {
* @name build.excludeMiddleware
* @type {boolean}
* @default `false`
- * @version 2.8.0
+ * @deprecated Deprecated since version 3.0.
* @description
- * Defines whether or not any SSR middleware code will be bundled when built.
- *
- * When enabled, middleware code is not bundled and imported by all pages during the build. To instead execute and import middleware code manually, set `build.excludeMiddleware: true`:
+ * The build config option `build.excludeMiddleware` has been replaced by the adapter configuration option [`edgeMiddleware`](/en/reference/adapter-reference/#edgemiddleware).
*
- * ```js
- * {
- * build: {
- * excludeMiddleware: true
- * }
- * }
- * ```
+ * Please see your [SSR adapter's documentation](/en/guides/integrations-guide/#official-integrations) for using `edgeMiddleware` to define whether or not any SSR middleware code will be bundled when built.
*/
excludeMiddleware?: boolean;
};
@@ -921,7 +898,7 @@ export interface AstroUserConfig {
* ```js
* {
* // Example: Use the function syntax to customize based on command
- * server: ({ command }) => ({ port: command === 'dev' ? 3000 : 4000 })
+ * server: ({ command }) => ({ port: command === 'dev' ? 4321 : 4000 })
* }
* ```
*/
@@ -943,7 +920,7 @@ export interface AstroUserConfig {
* @docs
* @name server.port
* @type {number}
- * @default `3000`
+ * @default `4321`
* @description
* Set which port the server should listen on.
*
@@ -993,7 +970,7 @@ export interface AstroUserConfig {
* @docs
* @name image.service (Experimental)
* @type {{entrypoint: 'astro/assets/services/sharp' | 'astro/assets/services/squoosh' | string, config: Record<string, any>}}
- * @default `{entrypoint: 'astro/assets/services/squoosh', config?: {}}`
+ * @default `{entrypoint: 'astro/assets/services/sharp', config?: {}}`
* @version 2.1.0
* @description
* Set which image service is used for Astro’s experimental assets support.
@@ -1087,6 +1064,7 @@ export interface AstroUserConfig {
* @name markdown.drafts
* @type {boolean}
* @default `false`
+ * @deprecated Deprecated since version 3.0. Use content collections instead.
* @description
* Control whether Markdown draft pages should be included in the build.
*
@@ -1302,27 +1280,6 @@ export interface AstroUserConfig {
experimental?: {
/**
* @docs
- * @name experimental.assets
- * @type {boolean}
- * @default `false`
- * @version 2.1.0
- * @description
- * Enable experimental support for optimizing and resizing images. With this enabled, a new `astro:assets` module will be exposed.
- *
- * To enable this feature, set `experimental.assets` to `true` in your Astro config:
- *
- * ```js
- * {
- * experimental: {
- * assets: true,
- * },
- * }
- * ```
- */
- assets?: boolean;
-
- /**
- * @docs
* @name experimental.viewTransitions
* @type {boolean}
* @default `false`
@@ -1364,29 +1321,6 @@ export interface AstroUserConfig {
*/
optimizeHoistedScript?: boolean;
};
-
- // Legacy options to be removed
-
- /** @deprecated - Use "integrations" instead. Run Astro to learn more about migrating. */
- renderers?: never;
- /** @deprecated `projectRoot` has been renamed to `root` */
- projectRoot?: never;
- /** @deprecated `src` has been renamed to `srcDir` */
- src?: never;
- /** @deprecated `pages` has been removed. It is no longer configurable. */
- pages?: never;
- /** @deprecated `public` has been renamed to `publicDir` */
- public?: never;
- /** @deprecated `dist` has been renamed to `outDir` */
- dist?: never;
- /** @deprecated `styleOptions` has been renamed to `style` */
- styleOptions?: never;
- /** @deprecated `markdownOptions` has been renamed to `markdown` */
- markdownOptions?: never;
- /** @deprecated `buildOptions` has been renamed to `build` */
- buildOptions?: never;
- /** @deprecated `devOptions` has been renamed to `server` */
- devOptions?: never;
}
// NOTE(fks): We choose to keep our hand-generated AstroUserConfig interface so that
@@ -1427,13 +1361,39 @@ export interface AstroConfig extends AstroConfigType {
// TypeScript still confirms zod validation matches this type.
integrations: AstroIntegration[];
}
+/**
+ * An inline Astro config that takes highest priority when merging with the user config,
+ * and includes inline-specific options to configure how Astro runs.
+ */
export interface AstroInlineConfig extends AstroUserConfig, AstroInlineOnlyConfig {}
export interface AstroInlineOnlyConfig {
+ /**
+ * A custom path to the Astro config file. If relative, it'll resolve based on the current working directory.
+ * Set to false to disable loading any config files.
+ *
+ * If this value is undefined or unset, Astro will search for an `astro.config.(js,mjs,ts)` file relative to
+ * the `root` and load the config file if found.
+ *
+ * The inline config passed in this object will take highest priority when merging with the loaded user config.
+ */
configFile?: string | false;
+ /**
+ * The mode used when building your site to generate either "development" or "production" code.
+ */
mode?: RuntimeMode;
+ /**
+ * The logging level to filter messages logged by Astro.
+ * - "debug": Log everything, including noisy debugging diagnostics.
+ * - "info": Log informational messages, warnings, and errors.
+ * - "warn": Log warnings and errors.
+ * - "error": Log errors only.
+ * - "silent": No logging.
+ *
+ * @default "info"
+ */
logLevel?: LoggerLevel;
/**
- * @internal for testing only
+ * @internal for testing only, use `logLevel` instead.
*/
logging?: LogOptions;
}
@@ -1505,6 +1465,17 @@ export interface DataEntryType {
export type GetDataEntryInfoReturnType = { data: Record<string, unknown>; rawData?: string };
+export interface AstroAdapterFeatures {
+ /**
+ * Creates and edge function that will communiate with the Astro middleware
+ */
+ edgeMiddleware: boolean;
+ /**
+ * SSR only. Each route becomes its own function/file.
+ */
+ functionPerRoute: boolean;
+}
+
export interface AstroSettings {
config: AstroConfig;
adapter: AstroAdapter | undefined;
@@ -1540,6 +1511,10 @@ export interface ComponentInstance {
default: AstroComponentFactory;
css?: string[];
prerender?: boolean;
+ /**
+ * Only used for logging if deprecated drafts feature is used
+ */
+ frontmatter?: Record<string, any>;
getStaticPaths?: (options: GetStaticPathsOptions) => GetStaticPathsResult;
}
@@ -1621,10 +1596,7 @@ export type GetStaticPathsResultKeyed = GetStaticPathsResult & {
*/
export type GetStaticPaths = (
options: GetStaticPathsOptions
-) =>
- | Promise<GetStaticPathsResult | GetStaticPathsResult[]>
- | GetStaticPathsResult
- | GetStaticPathsResult[];
+) => Promise<GetStaticPathsResult> | GetStaticPathsResult;
/**
* Infers the shape of the `params` property returned by `getStaticPaths()`.
@@ -1767,12 +1739,52 @@ export type PaginateFunction = (data: any[], args?: PaginateOptions) => GetStati
export type Params = Record<string, string | undefined>;
+export type SupportsKind = 'unsupported' | 'stable' | 'experimental' | 'deprecated';
+
+export type AstroFeatureMap = {
+ /**
+ * The adapter is able serve static pages
+ */
+ staticOutput?: SupportsKind;
+ /**
+ * The adapter is able to serve pages that are static or rendered via server
+ */
+ hybridOutput?: SupportsKind;
+ /**
+ * The adapter is able to serve SSR pages
+ */
+ serverOutput?: SupportsKind;
+ /**
+ * The adapter can emit static assets
+ */
+ assets?: AstroAssetsFeature;
+};
+
+export interface AstroAssetsFeature {
+ supportKind?: SupportsKind;
+ /**
+ * Whether if this adapter deploys files in an enviroment that is compatible with the library `sharp`
+ */
+ isSharpCompatible?: boolean;
+ /**
+ * Whether if this adapter deploys files in an enviroment that is compatible with the library `squoosh`
+ */
+ isSquooshCompatible?: boolean;
+}
+
export interface AstroAdapter {
name: string;
serverEntrypoint?: string;
previewEntrypoint?: string;
exports?: string[];
args?: any;
+ adapterFeatures?: AstroAdapterFeatures;
+ /**
+ * List of features supported by an adapter.
+ *
+ * If the adapter is not able to handle certain configurations, Astro will throw an error.
+ */
+ supportedAstroFeatures?: AstroFeatureMap;
}
type Body = string;
@@ -1895,7 +1907,7 @@ export interface APIContext<Props extends Record<string, any> = Record<string, a
*
* ```ts
* // src/middleware.ts
- * import {defineMiddleware} from "astro/middleware";
+ * import {defineMiddleware} from "astro:middleware";
*
* export const onRequest = defineMiddleware((context, next) => {
* context.locals.greeting = "Hello!";
@@ -1912,12 +1924,13 @@ export interface APIContext<Props extends Record<string, any> = Record<string, a
* ```
*/
locals: App.Locals;
+ ResponseWithEncoding: typeof ResponseWithEncoding;
}
export type EndpointOutput =
| {
body: Body;
- encoding?: Exclude<BufferEncoding, 'binary'>;
+ encoding?: BufferEncoding;
}
| {
body: Uint8Array;
@@ -1960,7 +1973,7 @@ export interface SSRLoadedRenderer extends AstroRenderer {
export type HookParameters<
Hook extends keyof AstroIntegration['hooks'],
- Fn = AstroIntegration['hooks'][Hook]
+ Fn = AstroIntegration['hooks'][Hook],
> = Fn extends (...args: any) => any ? Parameters<Fn>[0] : never;
export interface AstroIntegration {
@@ -1978,6 +1991,7 @@ export interface AstroIntegration {
injectScript: (stage: InjectedScriptStage, content: string) => void;
injectRoute: (injectRoute: InjectedRoute) => void;
addClientDirective: (directive: ClientDirectiveConfig) => void;
+ logger: AstroIntegrationLogger;
// TODO: Add support for `injectElement()` for full HTML element injection, not just scripts.
// This may require some refactoring of `scripts`, `styles`, and `links` into something
// more generalized. Consider the SSR use-case as well.
@@ -1986,10 +2000,17 @@ export interface AstroIntegration {
'astro:config:done'?: (options: {
config: AstroConfig;
setAdapter: (adapter: AstroAdapter) => void;
+ logger: AstroIntegrationLogger;
}) => void | Promise<void>;
- 'astro:server:setup'?: (options: { server: vite.ViteDevServer }) => void | Promise<void>;
- 'astro:server:start'?: (options: { address: AddressInfo }) => void | Promise<void>;
- 'astro:server:done'?: () => void | Promise<void>;
+ 'astro:server:setup'?: (options: {
+ server: vite.ViteDevServer;
+ logger: AstroIntegrationLogger;
+ }) => void | Promise<void>;
+ 'astro:server:start'?: (options: {
+ address: AddressInfo;
+ logger: AstroIntegrationLogger;
+ }) => void | Promise<void>;
+ 'astro:server:done'?: (options: { logger: AstroIntegrationLogger }) => void | Promise<void>;
'astro:build:ssr'?: (options: {
manifest: SerializedSSRManifest;
/**
@@ -2001,19 +2022,25 @@ export interface AstroIntegration {
* File path of the emitted middleware
*/
middlewareEntryPoint: URL | undefined;
+ logger: AstroIntegrationLogger;
}) => void | Promise<void>;
- 'astro:build:start'?: () => void | Promise<void>;
+ 'astro:build:start'?: (options: { logger: AstroIntegrationLogger }) => void | Promise<void>;
'astro:build:setup'?: (options: {
vite: vite.InlineConfig;
pages: Map<string, PageBuildData>;
target: 'client' | 'server';
updateConfig: (newConfig: vite.InlineConfig) => void;
+ logger: AstroIntegrationLogger;
+ }) => void | Promise<void>;
+ 'astro:build:generated'?: (options: {
+ dir: URL;
+ logger: AstroIntegrationLogger;
}) => void | Promise<void>;
- 'astro:build:generated'?: (options: { dir: URL }) => void | Promise<void>;
'astro:build:done'?: (options: {
pages: { pathname: string }[];
dir: URL;
routes: RouteData[];
+ logger: AstroIntegrationLogger;
}) => void | Promise<void>;
};
}
diff --git a/packages/astro/src/assets/image-endpoint.ts b/packages/astro/src/assets/image-endpoint.ts
index fa62cbdd1..aa2baf0a5 100644
--- a/packages/astro/src/assets/image-endpoint.ts
+++ b/packages/astro/src/assets/image-endpoint.ts
@@ -1,9 +1,8 @@
import mime from 'mime/lite.js';
import type { APIRoute } from '../@types/astro.js';
-import { isRemotePath } from '../core/path.js';
-import { getConfiguredImageService, isRemoteAllowed } from './internal.js';
-import { isLocalService } from './services/service.js';
import { etag } from './utils/etag.js';
+import { isRemotePath } from '@astrojs/internal-helpers/path';
+import { getConfiguredImageService, isRemoteAllowed } from './internal.js';
// @ts-expect-error
import { imageConfig } from 'astro:assets';
@@ -24,11 +23,11 @@ async function loadRemoteImage(src: URL) {
/**
* Endpoint used in dev and SSR to serve optimized images by the base image services
*/
-export const get: APIRoute = async ({ request }) => {
+export const GET: APIRoute = async ({ request }) => {
try {
const imageService = await getConfiguredImageService();
- if (!isLocalService(imageService)) {
+ if (!('transform' in imageService)) {
throw new Error('Configured image service is not a local service');
}
diff --git a/packages/astro/src/assets/internal.ts b/packages/astro/src/assets/internal.ts
index ffc27333f..dd5e427f6 100644
--- a/packages/astro/src/assets/internal.ts
+++ b/packages/astro/src/assets/internal.ts
@@ -11,6 +11,7 @@ import type {
import { matchHostname, matchPattern } from './utils/remotePattern.js';
export function injectImageEndpoint(settings: AstroSettings) {
+ // TODO: Add a setting to disable the image endpoint
settings.injectedRoutes.push({
pattern: '/_image',
entryPoint: 'astro/assets/image-endpoint',
diff --git a/packages/astro/src/assets/services/noop.ts b/packages/astro/src/assets/services/noop.ts
new file mode 100644
index 000000000..d57ffeb27
--- /dev/null
+++ b/packages/astro/src/assets/services/noop.ts
@@ -0,0 +1,17 @@
+import { baseService, type LocalImageService } from './service.js';
+
+// Empty service used for platforms that neither support Squoosh or Sharp.
+const noopService: LocalImageService = {
+ validateOptions: baseService.validateOptions,
+ getURL: baseService.getURL,
+ parseURL: baseService.parseURL,
+ getHTMLAttributes: baseService.getHTMLAttributes,
+ async transform(inputBuffer, transformOptions) {
+ return {
+ data: inputBuffer,
+ format: transformOptions.format,
+ };
+ },
+};
+
+export default noopService;
diff --git a/packages/astro/src/assets/services/sharp.ts b/packages/astro/src/assets/services/sharp.ts
index 7f70b9268..0819ffcd1 100644
--- a/packages/astro/src/assets/services/sharp.ts
+++ b/packages/astro/src/assets/services/sharp.ts
@@ -1,4 +1,5 @@
import type { FormatEnum } from 'sharp';
+import { AstroError, AstroErrorData } from '../../core/errors/index.js';
import type { ImageOutputFormat, ImageQualityPreset } from '../types.js';
import {
baseService,
@@ -21,7 +22,7 @@ async function loadSharp() {
try {
sharpImport = (await import('sharp')).default;
} catch (e) {
- throw new Error('Could not find Sharp. Please install Sharp manually into your project.');
+ throw new AstroError(AstroErrorData.MissingSharp);
}
return sharpImport;
diff --git a/packages/astro/src/assets/services/vendor/squoosh/impl.ts b/packages/astro/src/assets/services/vendor/squoosh/impl.ts
index 7bb9aeeea..273957e4b 100644
--- a/packages/astro/src/assets/services/vendor/squoosh/impl.ts
+++ b/packages/astro/src/assets/services/vendor/squoosh/impl.ts
@@ -33,7 +33,7 @@ export async function decodeBuffer(
.join('')
// TODO (future PR): support more formats
if (firstChunkString.includes('GIF')) {
- throw Error(`GIF images are not supported, please install the @astrojs/image/sharp plugin`)
+ throw Error(`GIF images are not supported, please use the Sharp image service`)
}
const key = Object.entries(supportedFormats).find(([, { detectors }]) =>
detectors.some((detector) => detector.exec(firstChunkString))
@@ -78,7 +78,7 @@ export async function encodeJpeg(
opts: { quality?: number }
): Promise<Uint8Array> {
image = ImageData.from(image)
-
+
const e = supportedFormats['mozjpeg']
const m = await e.enc()
await maybeDelay()
diff --git a/packages/astro/src/assets/types.ts b/packages/astro/src/assets/types.ts
index 9c5990cb7..ae74fc692 100644
--- a/packages/astro/src/assets/types.ts
+++ b/packages/astro/src/assets/types.ts
@@ -90,7 +90,7 @@ export type LocalImageProps<T> = ImageSharedProps<T> & {
*
* **Example**:
* ```js
- * import myImage from "~/assets/my_image.png";
+ * import myImage from "../assets/my_image.png";
* ```
* And then refer to the image, like so:
* ```astro
diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts
index 0f00e0ecb..f194e5288 100644
--- a/packages/astro/src/assets/vite-plugin-assets.ts
+++ b/packages/astro/src/assets/vite-plugin-assets.ts
@@ -1,10 +1,7 @@
-import { bold } from 'kleur/colors';
import MagicString from 'magic-string';
-import { fileURLToPath } from 'node:url';
import type * as vite from 'vite';
import { normalizePath } from 'vite';
import type { AstroPluginOptions, ImageTransform } from '../@types/astro';
-import { error } from '../core/logger/core.js';
import {
appendForwardSlash,
joinPaths,
@@ -22,53 +19,16 @@ const urlRE = /(\?|&)url(?:&|$)/;
export default function assets({
settings,
- logging,
mode,
}: AstroPluginOptions & { mode: string }): vite.Plugin[] {
let resolvedConfig: vite.ResolvedConfig;
globalThis.astroAsset = {};
- const UNSUPPORTED_ADAPTERS = new Set([
- '@astrojs/cloudflare',
- '@astrojs/deno',
- '@astrojs/netlify/edge-functions',
- '@astrojs/vercel/edge',
- ]);
-
- const adapterName = settings.config.adapter?.name;
- if (
- ['astro/assets/services/sharp', 'astro/assets/services/squoosh'].includes(
- settings.config.image.service.entrypoint
- ) &&
- adapterName &&
- UNSUPPORTED_ADAPTERS.has(adapterName)
- ) {
- error(
- logging,
- 'assets',
- `The currently selected adapter \`${adapterName}\` does not run on Node, however the currently used image service depends on Node built-ins. ${bold(
- 'Your project will NOT be able to build.'
- )}`
- );
- }
-
return [
// Expose the components and different utilities from `astro:assets` and handle serving images from `/_image` in dev
{
name: 'astro:assets',
- config() {
- return {
- resolve: {
- alias: [
- {
- find: /^~\/assets\/(.+)$/,
- replacement: fileURLToPath(new URL('./assets/$1', settings.config.srcDir)),
- },
- ],
- },
- };
- },
async resolveId(id) {
if (id === VIRTUAL_SERVICE_ID) {
return await this.resolve(settings.config.image.service.entrypoint);
diff --git a/packages/astro/src/cli/add/index.ts b/packages/astro/src/cli/add/index.ts
index cae1f9e6b..fcaeb07c7 100644
--- a/packages/astro/src/cli/add/index.ts
+++ b/packages/astro/src/cli/add/index.ts
@@ -216,12 +216,6 @@ export async function add(names: string[], { flags }: AddOptions) {
await fs.writeFile(fileURLToPath(configURL), ASTRO_CONFIG_STUB, { encoding: 'utf-8' });
}
- // TODO: improve error handling for invalid configs
- if (configURL?.pathname.endsWith('package.json')) {
- throw new Error(
- `Unable to use "astro add" with package.json configuration. Try migrating to \`astro.config.mjs\` and try again.`
- );
- }
let ast: t.File | null = null;
try {
ast = await parseAstroConfig(configURL);
@@ -709,7 +703,7 @@ async function tryToInstallIntegrations({
} catch (err) {
spinner.fail();
debug('add', 'Error installing dependencies', err);
- // eslint-disable no-console
+ // eslint-disable-next-line no-console
console.error('\n', (err as any).stdout, '\n');
return UpdateResult.failure;
}
diff --git a/packages/astro/src/cli/build/index.ts b/packages/astro/src/cli/build/index.ts
index 9e26108a2..dd44823d1 100644
--- a/packages/astro/src/cli/build/index.ts
+++ b/packages/astro/src/cli/build/index.ts
@@ -25,7 +25,5 @@ export async function build({ flags }: BuildOptions) {
const inlineConfig = flagsToAstroInlineConfig(flags);
- await _build(inlineConfig, {
- teardownCompiler: true,
- });
+ await _build(inlineConfig);
}
diff --git a/packages/astro/src/cli/check/index.ts b/packages/astro/src/cli/check/index.ts
index 96bee308d..6ae11d576 100644
--- a/packages/astro/src/cli/check/index.ts
+++ b/packages/astro/src/cli/check/index.ts
@@ -1,396 +1,43 @@
-import {
- AstroCheck,
- DiagnosticSeverity,
- type GetDiagnosticsResult,
-} from '@astrojs/language-server';
-import type { FSWatcher } from 'chokidar';
-import glob from 'fast-glob';
-import { bold, dim, red, yellow } from 'kleur/colors';
-import { createRequire } from 'module';
-import fs from 'node:fs';
-import { join } from 'node:path';
-import { fileURLToPath, pathToFileURL } from 'node:url';
-import ora from 'ora';
-import type { Arguments as Flags } from 'yargs-parser';
-import type { AstroSettings } from '../../@types/astro';
-import { resolveConfig } from '../../core/config/config.js';
-import { createNodeLogging } from '../../core/config/logging.js';
-import { createSettings } from '../../core/config/settings.js';
-import type { LogOptions } from '../../core/logger/core.js';
-import { debug, info } from '../../core/logger/core.js';
-import { printHelp } from '../../core/messages.js';
-import type { syncInternal } from '../../core/sync';
-import { eventCliSession, telemetry } from '../../events/index.js';
-import { runHookConfigSetup } from '../../integrations/index.js';
-import { flagsToAstroInlineConfig } from '../flags.js';
-import { printDiagnostic } from './print.js';
-
-type DiagnosticResult = {
- errors: number;
- warnings: number;
- hints: number;
-};
-
-export type CheckPayload = {
- /**
- * Flags passed via CLI
- */
- flags: Flags;
-};
-
-type CheckFlags = {
- /**
- * Whether the `check` command should watch for `.astro` and report errors
- * @default {false}
- */
- watch: boolean;
-};
-
-/**
- *
- * Types of response emitted by the checker
- */
-export enum CheckResult {
- /**
- * Operation finished without errors
- */
- ExitWithSuccess,
- /**
- * Operation finished with errors
- */
- ExitWithError,
- /**
- * The consumer should not terminate the operation
- */
- Listen,
-}
-
-const ASTRO_GLOB_PATTERN = '**/*.astro';
-
-/**
- * Checks `.astro` files for possible errors.
- *
- * If the `--watch` flag is provided, the command runs indefinitely and provides diagnostics
- * when `.astro` files are modified.
- *
- * Every time an astro files is modified, content collections are also generated.
- *
- * @param {CheckPayload} options Options passed {@link AstroChecker}
- * @param {Flags} options.flags Flags coming from the CLI
- */
-export async function check({ flags }: CheckPayload): Promise<AstroChecker | undefined> {
- if (flags.help || flags.h) {
- printHelp({
- commandName: 'astro check',
- usage: '[...flags]',
- tables: {
- Flags: [
- ['--watch', 'Watch Astro files for changes and re-run checks.'],
- ['--help (-h)', 'See all available flags.'],
- ],
- },
- description: `Runs diagnostics against your project and reports errors to the console.`,
- });
- return;
- }
-
- // Load settings
- const inlineConfig = flagsToAstroInlineConfig(flags);
- const logging = createNodeLogging(inlineConfig);
- const { userConfig, astroConfig } = await resolveConfig(inlineConfig, 'check');
- telemetry.record(eventCliSession('check', userConfig, flags));
- const settings = createSettings(astroConfig, fileURLToPath(astroConfig.root));
-
- const checkFlags = parseFlags(flags);
- if (checkFlags.watch) {
- info(logging, 'check', 'Checking files in watch mode');
- } else {
- info(logging, 'check', 'Checking files');
- }
-
- const { syncInternal } = await import('../../core/sync/index.js');
- const root = settings.config.root;
- const require = createRequire(import.meta.url);
- const diagnosticChecker = new AstroCheck(
- root.toString(),
- require.resolve('typescript/lib/tsserverlibrary.js', {
- paths: [root.toString()],
- })
- );
-
- return new AstroChecker({
- syncInternal,
- settings,
- fileSystem: fs,
+import path from 'node:path';
+import type { Arguments } from 'yargs-parser';
+import { error, info } from '../../core/logger/core.js';
+import { createLoggingFromFlags, flagsToAstroInlineConfig } from '../flags.js';
+import { getPackage } from '../install-package.js';
+
+export async function check(flags: Arguments) {
+ const logging = createLoggingFromFlags(flags);
+ const getPackageOpts = { skipAsk: flags.yes || flags.y, cwd: flags.root };
+ const checkPackage = await getPackage<typeof import('@astrojs/check')>(
+ '@astrojs/check',
logging,
- diagnosticChecker,
- isWatchMode: checkFlags.watch,
- });
-}
-
-type CheckerConstructor = {
- diagnosticChecker: AstroCheck;
-
- isWatchMode: boolean;
-
- syncInternal: typeof syncInternal;
-
- settings: Readonly<AstroSettings>;
-
- logging: Readonly<LogOptions>;
-
- fileSystem: typeof fs;
-};
-
-/**
- * Responsible to check files - classic or watch mode - and report diagnostics.
- *
- * When in watch mode, the class does a whole check pass, and then starts watching files.
- * When a change occurs to an `.astro` file, the checker builds content collections again and lint all the `.astro` files.
- */
-export class AstroChecker {
- readonly #diagnosticsChecker: AstroCheck;
- readonly #shouldWatch: boolean;
- readonly #syncInternal: CheckerConstructor['syncInternal'];
-
- readonly #settings: AstroSettings;
-
- readonly #logging: LogOptions;
- readonly #fs: typeof fs;
- #watcher?: FSWatcher;
-
- #filesCount: number;
- #updateDiagnostics: NodeJS.Timeout | undefined;
-
- constructor({
- diagnosticChecker,
- isWatchMode,
- syncInternal,
- settings,
- fileSystem,
- logging,
- }: CheckerConstructor) {
- this.#diagnosticsChecker = diagnosticChecker;
- this.#shouldWatch = isWatchMode;
- this.#syncInternal = syncInternal;
- this.#logging = logging;
- this.#settings = settings;
- this.#fs = fileSystem;
- this.#filesCount = 0;
- }
-
- /**
- * Check all `.astro` files once and then finishes the operation.
- */
- public async check(): Promise<CheckResult> {
- return await this.#checkAllFiles(true);
- }
-
- /**
- * Check all `.astro` files and then start watching for changes.
- */
- public async watch(): Promise<CheckResult> {
- await this.#checkAllFiles(true);
- await this.#watch();
- return CheckResult.Listen;
- }
-
- /**
- * Stops the watch. It terminates the inner server.
- */
- public async stop() {
- await this.#watcher?.close();
- }
-
- /**
- * Whether the checker should run in watch mode
- */
- public get isWatchMode(): boolean {
- return this.#shouldWatch;
- }
-
- async #openDocuments() {
- this.#filesCount = await openAllDocuments(
- this.#settings.config.root,
- [],
- this.#diagnosticsChecker
- );
- }
-
- /**
- * Lint all `.astro` files, and report the result in console. Operations executed, in order:
- * 1. Compile content collections.
- * 2. Optionally, traverse the file system for `.astro` files and saves their paths.
- * 3. Get diagnostics for said files and print the result in console.
- *
- * @param openDocuments Whether the operation should open all `.astro` files
- */
- async #checkAllFiles(openDocuments: boolean): Promise<CheckResult> {
- // Run `astro:config:setup` before syncing to initialize integrations.
- // We do this manually as we're calling `syncInternal` directly.
- const syncSettings = await runHookConfigSetup({
- settings: this.#settings,
- logging: this.#logging,
- command: 'build',
- });
- const processExit = await this.#syncInternal(syncSettings, {
- logging: this.#logging,
- fs: this.#fs,
- });
- // early exit on sync failure
- if (processExit === 1) return processExit;
-
- let spinner = ora(
- ` Getting diagnostics for Astro files in ${fileURLToPath(this.#settings.config.root)}…`
- ).start();
-
- if (openDocuments) {
- await this.#openDocuments();
- }
-
- let diagnostics = await this.#diagnosticsChecker.getDiagnostics();
-
- spinner.succeed();
-
- let brokenDownDiagnostics = this.#breakDownDiagnostics(diagnostics);
- this.#logDiagnosticsSeverity(brokenDownDiagnostics);
- return brokenDownDiagnostics.errors > 0
- ? CheckResult.ExitWithError
- : CheckResult.ExitWithSuccess;
- }
-
- #checkForDiagnostics() {
- clearTimeout(this.#updateDiagnostics);
- // @ematipico: I am not sure of `setTimeout`. I would rather use a debounce but let's see if this works.
- // Inspiration from `svelte-check`.
- this.#updateDiagnostics = setTimeout(async () => await this.#checkAllFiles(false), 500);
- }
-
- /**
- * This function is responsible to attach events to the server watcher
- */
- async #watch() {
- const { default: chokidar } = await import('chokidar');
- this.#watcher = chokidar.watch(
- join(fileURLToPath(this.#settings.config.root), ASTRO_GLOB_PATTERN),
- {
- ignored: ['**/node_modules/**'],
- ignoreInitial: true,
- }
- );
-
- this.#watcher.on('add', (file) => {
- this.#addDocument(file);
- this.#filesCount += 1;
- this.#checkForDiagnostics();
- });
- this.#watcher.on('change', (file) => {
- this.#addDocument(file);
- this.#checkForDiagnostics();
- });
- this.#watcher.on('unlink', (file) => {
- this.#diagnosticsChecker.removeDocument(file);
- this.#filesCount -= 1;
- this.#checkForDiagnostics();
- });
- }
-
- /**
- * Add a document to the diagnostics checker
- * @param filePath Path to the file
- */
- #addDocument(filePath: string) {
- const text = fs.readFileSync(filePath, 'utf-8');
- this.#diagnosticsChecker.upsertDocument({
- uri: pathToFileURL(filePath).toString(),
- text,
- });
- }
+ getPackageOpts,
+ ['typescript']
+ );
+ const typescript = await getPackage('typescript', logging, getPackageOpts);
- /**
- * Logs the result of the various diagnostics
- *
- * @param result Result emitted by AstroChecker.#breakDownDiagnostics
- */
- #logDiagnosticsSeverity(result: Readonly<DiagnosticResult>) {
- info(
- this.#logging,
- 'diagnostics',
- [
- bold(`Result (${this.#filesCount} file${this.#filesCount === 1 ? '' : 's'}): `),
- bold(red(`${result.errors} ${result.errors === 1 ? 'error' : 'errors'}`)),
- bold(yellow(`${result.warnings} ${result.warnings === 1 ? 'warning' : 'warnings'}`)),
- dim(`${result.hints} ${result.hints === 1 ? 'hint' : 'hints'}\n`),
- ].join(`\n${dim('-')} `)
+ if (!checkPackage || !typescript) {
+ error(
+ logging,
+ 'check',
+ 'The `@astrojs/check` and `typescript` packages are required for this command to work. Please manually install them into your project and try again.'
);
+ return;
}
- /**
- * It loops through all diagnostics and break down diagnostics that are errors, warnings or hints.
- */
- #breakDownDiagnostics(diagnostics: Readonly<GetDiagnosticsResult[]>): DiagnosticResult {
- let result: DiagnosticResult = {
- errors: 0,
- warnings: 0,
- hints: 0,
- };
-
- diagnostics.forEach((diag) => {
- diag.diagnostics.forEach((d) => {
- info(this.#logging, 'diagnostics', `\n ${printDiagnostic(diag.fileUri, diag.text, d)}`);
-
- switch (d.severity) {
- case DiagnosticSeverity.Error: {
- result.errors++;
- break;
- }
- case DiagnosticSeverity.Warning: {
- result.warnings++;
- break;
- }
- case DiagnosticSeverity.Hint: {
- result.hints++;
- break;
- }
- }
- });
- });
-
- return result;
+ // Run sync before check to make sure types are generated.
+ // NOTE: In the future, `@astrojs/check` can expose a `before lint` hook so that this works during `astro check --watch` too.
+ // For now, we run this once as usually `astro check --watch` is ran alongside `astro dev` which also calls `astro sync`.
+ const { default: sync } = await import('../../core/sync/index.js');
+ const inlineConfig = flagsToAstroInlineConfig(flags);
+ const exitCode = await sync(inlineConfig);
+ if (exitCode !== 0) {
+ process.exit(exitCode);
}
-}
-
-/**
- * Open all Astro files in the given directory and return the number of files found.
- */
-async function openAllDocuments(
- workspaceUri: URL,
- filePathsToIgnore: string[],
- checker: AstroCheck
-): Promise<number> {
- const files = await glob(ASTRO_GLOB_PATTERN, {
- cwd: fileURLToPath(workspaceUri),
- ignore: ['node_modules/**'].concat(filePathsToIgnore.map((ignore) => `${ignore}/**`)),
- absolute: true,
- });
- for (const file of files) {
- debug('check', `Adding file ${file} to the list of files to check.`);
- const text = fs.readFileSync(file, 'utf-8');
- checker.upsertDocument({
- uri: pathToFileURL(file).toString(),
- text,
- });
- }
+ const { check: checker, parseArgsAsCheckConfig } = checkPackage;
- return files.length;
-}
+ const config = parseArgsAsCheckConfig(process.argv);
-/**
- * Parse flags and sets defaults
- */
-function parseFlags(flags: Flags): CheckFlags {
- return {
- watch: flags.watch ?? false,
- };
+ info(logging, 'check', `Getting diagnostics for Astro files in ${path.resolve(config.root)}...`);
+ return await checker(config);
}
diff --git a/packages/astro/src/cli/check/print.ts b/packages/astro/src/cli/check/print.ts
deleted file mode 100644
index bd8de2ddb..000000000
--- a/packages/astro/src/cli/check/print.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-import { DiagnosticSeverity, offsetAt, type Diagnostic } from '@astrojs/language-server';
-import {
- bgRed,
- bgWhite,
- bgYellow,
- black,
- bold,
- cyan,
- gray,
- red,
- white,
- yellow,
-} from 'kleur/colors';
-import { fileURLToPath } from 'node:url';
-import stringWidth from 'string-width';
-
-export function printDiagnostic(filePath: string, text: string, diag: Diagnostic): string {
- let result = [];
-
- // Lines and characters are 0-indexed, so we need to add 1 to the offset to get the actual line and character
- const realStartLine = diag.range.start.line + 1;
- const realStartCharacter = diag.range.start.character + 1;
-
- // IDE friendly path that user can CTRL+Click to open the file at a specific line / character
- const IDEFilePath = `${bold(cyan(fileURLToPath(filePath)))}:${bold(yellow(realStartLine))}:${bold(
- yellow(realStartCharacter)
- )}`;
- result.push(
- `${IDEFilePath} ${bold(getColorForSeverity(diag, getStringForSeverity(diag)))}: ${diag.message}`
- );
-
- // Optionally add the line before the error to add context if not empty
- const previousLine = getLine(diag.range.start.line - 1, text);
- if (previousLine) {
- result.push(`${getPrintableLineNumber(realStartLine - 1)} ${gray(previousLine)}`);
- }
-
- // Add the line with the error
- const str = getLine(diag.range.start.line, text);
- const lineNumStr = realStartLine.toString().padStart(2, '0');
- const lineNumLen = lineNumStr.length;
- result.push(`${getBackgroundForSeverity(diag, lineNumStr)} ${str}`);
-
- // Adds tildes under the specific range where the diagnostic is
- const tildes = generateString('~', diag.range.end.character - diag.range.start.character);
-
- // NOTE: This is not perfect, if the line include any characters that is made of multiple characters, for example
- // regionals flags, but the terminal can't display it, then the number of spaces will be wrong. Not sure how to fix.
- const beforeChars = stringWidth(str.substring(0, diag.range.start.character));
- const spaces = generateString(' ', beforeChars + lineNumLen - 1);
- result.push(` ${spaces}${bold(getColorForSeverity(diag, tildes))}`);
-
- const nextLine = getLine(diag.range.start.line + 1, text);
- if (nextLine) {
- result.push(`${getPrintableLineNumber(realStartLine + 1)} ${gray(nextLine)}`);
- }
-
- // Force a new line at the end
- result.push('');
-
- return result.join('\n');
-}
-
-function generateString(str: string, len: number): string {
- return Array.from({ length: len }, () => str).join('');
-}
-
-function getStringForSeverity(diag: Diagnostic): string {
- switch (diag.severity) {
- case DiagnosticSeverity.Error:
- return 'Error';
- case DiagnosticSeverity.Warning:
- return 'Warning';
- case DiagnosticSeverity.Hint:
- return 'Hint';
- default:
- return 'Unknown';
- }
-}
-
-function getColorForSeverity(diag: Diagnostic, text: string): string {
- switch (diag.severity) {
- case DiagnosticSeverity.Error:
- return red(text);
- case DiagnosticSeverity.Warning:
- return yellow(text);
- case DiagnosticSeverity.Hint:
- return gray(text);
- default:
- return text;
- }
-}
-
-function getBackgroundForSeverity(diag: Diagnostic, text: string): string {
- switch (diag.severity) {
- case DiagnosticSeverity.Error:
- return bgRed(white(text));
- case DiagnosticSeverity.Warning:
- return bgYellow(white(text));
- case DiagnosticSeverity.Hint:
- return bgWhite(black(text));
- default:
- return text;
- }
-}
-
-function getPrintableLineNumber(line: number): string {
- return bgWhite(black(line.toString().padStart(2, '0')));
-}
-
-function getLine(line: number, text: string): string {
- return text
- .substring(
- offsetAt({ line, character: 0 }, text),
- offsetAt({ line, character: Number.MAX_SAFE_INTEGER }, text)
- )
- .replace(/\t/g, ' ')
- .trimEnd();
-}
diff --git a/packages/astro/src/cli/dev/index.ts b/packages/astro/src/cli/dev/index.ts
index e55496c4a..5db47fb97 100644
--- a/packages/astro/src/cli/dev/index.ts
+++ b/packages/astro/src/cli/dev/index.ts
@@ -15,7 +15,7 @@ export async function dev({ flags }: DevOptions) {
usage: '[...flags]',
tables: {
Flags: [
- ['--port', `Specify which port to run on. Defaults to 3000.`],
+ ['--port', `Specify which port to run on. Defaults to 4321.`],
['--host', `Listen on all addresses, including LAN and public addresses.`],
['--host <custom-address>', `Expose on a network IP address at <custom-address>`],
['--open', 'Automatically open the app in the browser on server start'],
diff --git a/packages/astro/src/cli/flags.ts b/packages/astro/src/cli/flags.ts
index 703422d50..3d7360a29 100644
--- a/packages/astro/src/cli/flags.ts
+++ b/packages/astro/src/cli/flags.ts
@@ -23,9 +23,6 @@ export function flagsToAstroInlineConfig(flags: Flags): AstroInlineConfig {
typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined,
open: typeof flags.open === 'boolean' ? flags.open : undefined,
},
- experimental: {
- assets: typeof flags.experimentalAssets === 'boolean' ? flags.experimentalAssets : undefined,
- },
};
}
diff --git a/packages/astro/src/cli/index.ts b/packages/astro/src/cli/index.ts
index d16ea91e2..fdf43201f 100644
--- a/packages/astro/src/cli/index.ts
+++ b/packages/astro/src/cli/index.ts
@@ -154,18 +154,12 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
}
case 'check': {
const { check } = await import('./check/index.js');
- // We create a server to start doing our operations
- const checkServer = await check({ flags });
- if (checkServer) {
- if (checkServer.isWatchMode) {
- await checkServer.watch();
- return await new Promise(() => {}); // lives forever
- } else {
- const checkResult = await checkServer.check();
- return process.exit(checkResult);
- }
+ const checkServer = await check(flags);
+ if (flags.watch) {
+ return await new Promise(() => {}); // lives forever
+ } else {
+ return process.exit(checkServer ? 1 : 0);
}
- return;
}
case 'sync': {
const { sync } = await import('./sync/index.js');
diff --git a/packages/astro/src/cli/install-package.ts b/packages/astro/src/cli/install-package.ts
new file mode 100644
index 000000000..8793d9985
--- /dev/null
+++ b/packages/astro/src/cli/install-package.ts
@@ -0,0 +1,124 @@
+import boxen from 'boxen';
+import { execa } from 'execa';
+import { bold, cyan, dim, magenta } from 'kleur/colors';
+import { createRequire } from 'node:module';
+import ora from 'ora';
+import prompts from 'prompts';
+import whichPm from 'which-pm';
+import { debug, info, type LogOptions } from '../core/logger/core.js';
+
+type GetPackageOptions = {
+ skipAsk?: boolean;
+ cwd?: string;
+};
+
+export async function getPackage<T>(
+ packageName: string,
+ logging: LogOptions,
+ options: GetPackageOptions,
+ otherDeps: string[] = []
+): Promise<T | undefined> {
+ const require = createRequire(options.cwd ?? process.cwd());
+
+ let packageImport;
+ try {
+ require.resolve(packageName);
+
+ // The `require.resolve` is required as to avoid Node caching the failed `import`
+ packageImport = await import(packageName);
+ } catch (e) {
+ info(
+ logging,
+ '',
+ `To continue, Astro requires the following dependency to be installed: ${bold(packageName)}.`
+ );
+ const result = await installPackage([packageName, ...otherDeps], options, logging);
+
+ if (result) {
+ packageImport = await import(packageName);
+ } else {
+ return undefined;
+ }
+ }
+
+ return packageImport as T;
+}
+
+function getInstallCommand(packages: string[], packageManager: string) {
+ switch (packageManager) {
+ case 'npm':
+ return { pm: 'npm', command: 'install', flags: [], dependencies: packages };
+ case 'yarn':
+ return { pm: 'yarn', command: 'add', flags: [], dependencies: packages };
+ case 'pnpm':
+ return { pm: 'pnpm', command: 'add', flags: [], dependencies: packages };
+ default:
+ return null;
+ }
+}
+
+async function installPackage(
+ packageNames: string[],
+ options: GetPackageOptions,
+ logging: LogOptions
+): Promise<boolean> {
+ const cwd = options.cwd ?? process.cwd();
+ const packageManager = (await whichPm(cwd)).name ?? 'npm';
+ const installCommand = getInstallCommand(packageNames, packageManager);
+
+ if (!installCommand) {
+ return false;
+ }
+
+ const coloredOutput = `${bold(installCommand.pm)} ${installCommand.command}${[
+ '',
+ ...installCommand.flags,
+ ].join(' ')} ${cyan(installCommand.dependencies.join(' '))}`;
+ const message = `\n${boxen(coloredOutput, {
+ margin: 0.5,
+ padding: 0.5,
+ borderStyle: 'round',
+ })}\n`;
+ info(
+ logging,
+ null,
+ `\n ${magenta('Astro will run the following command:')}\n ${dim(
+ 'If you skip this step, you can always run it yourself later'
+ )}\n${message}`
+ );
+
+ let response;
+ if (options.skipAsk) {
+ response = true;
+ } else {
+ response = (
+ await prompts({
+ type: 'confirm',
+ name: 'askToContinue',
+ message: 'Continue?',
+ initial: true,
+ })
+ ).askToContinue;
+ }
+
+ if (Boolean(response)) {
+ const spinner = ora('Installing dependencies...').start();
+ try {
+ await execa(
+ installCommand.pm,
+ [installCommand.command, ...installCommand.flags, ...installCommand.dependencies],
+ { cwd: cwd }
+ );
+ spinner.succeed();
+
+ return true;
+ } catch (err) {
+ debug('add', 'Error installing dependencies', err);
+ spinner.fail();
+
+ return false;
+ }
+ } else {
+ return false;
+ }
+}
diff --git a/packages/astro/src/cli/sync/index.ts b/packages/astro/src/cli/sync/index.ts
index 66a277e46..8650bf904 100644
--- a/packages/astro/src/cli/sync/index.ts
+++ b/packages/astro/src/cli/sync/index.ts
@@ -1,6 +1,6 @@
import type yargs from 'yargs-parser';
import { printHelp } from '../../core/messages.js';
-import { sync as _sync } from '../../core/sync/index.js';
+import _sync from '../../core/sync/index.js';
import { flagsToAstroInlineConfig } from '../flags.js';
interface SyncOptions {
diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts
index 238e32c5f..078197cd0 100644
--- a/packages/astro/src/content/types-generator.ts
+++ b/packages/astro/src/content/types-generator.ts
@@ -155,8 +155,7 @@ export async function createContentTypesGenerator({
fileURLToPath(event.entry),
contentPaths,
contentEntryExts,
- dataEntryExts,
- settings.config.experimental.assets
+ dataEntryExts
);
if (fileType === 'ignored') {
return { shouldGenerateTypes: false };
diff --git a/packages/astro/src/content/utils.ts b/packages/astro/src/content/utils.ts
index d02bd8669..202acd6b9 100644
--- a/packages/astro/src/content/utils.ts
+++ b/packages/astro/src/content/utils.ts
@@ -93,8 +93,7 @@ export async function getEntryData(
_internal: EntryInternal;
},
collectionConfig: CollectionConfig,
- pluginContext: PluginContext,
- config: AstroConfig
+ pluginContext: PluginContext
) {
let data;
if (collectionConfig.type === 'data') {
@@ -106,12 +105,6 @@ export async function getEntryData(
let schema = collectionConfig.schema;
if (typeof schema === 'function') {
- if (!config.experimental.assets) {
- throw new Error(
- 'The function shape for schema can only be used when `experimental.assets` is enabled.'
- );
- }
-
schema = schema({
image: createImage(pluginContext, entry._internal.filePath),
});
@@ -253,9 +246,7 @@ export function getEntryType(
entryPath: string,
paths: Pick<ContentPaths, 'config' | 'contentDir'>,
contentFileExts: string[],
- dataFileExts: string[],
- // TODO: Unflag this when we're ready to release assets - erika, 2023-04-12
- experimentalAssets = false
+ dataFileExts: string[]
): 'content' | 'data' | 'config' | 'ignored' | 'unsupported' {
const { ext, base } = path.parse(entryPath);
const fileUrl = pathToFileURL(entryPath);
@@ -263,7 +254,7 @@ export function getEntryType(
if (
hasUnderscoreBelowContentDirectoryPath(fileUrl, paths.contentDir) ||
isOnIgnoreList(base) ||
- (experimentalAssets && isImageAsset(ext))
+ isImageAsset(ext)
) {
return 'ignored';
} else if (contentFileExts.includes(ext)) {
diff --git a/packages/astro/src/content/vite-plugin-content-imports.ts b/packages/astro/src/content/vite-plugin-content-imports.ts
index a659dd4a0..4643e0922 100644
--- a/packages/astro/src/content/vite-plugin-content-imports.ts
+++ b/packages/astro/src/content/vite-plugin-content-imports.ts
@@ -131,13 +131,7 @@ export const _internal = {
configureServer(viteServer) {
viteServer.watcher.on('all', async (event, entry) => {
if (CHOKIDAR_MODIFIED_EVENTS.includes(event)) {
- const entryType = getEntryType(
- entry,
- contentPaths,
- contentEntryExts,
- dataEntryExts,
- settings.config.experimental.assets
- );
+ const entryType = getEntryType(entry, contentPaths, contentEntryExts, dataEntryExts);
if (!COLLECTION_TYPES_TO_INVALIDATE_ON.includes(entryType)) return;
// The content config could depend on collection entries via `reference()`.
@@ -194,7 +188,7 @@ type GetEntryModuleParams<TEntryType extends ContentEntryType | DataEntryType> =
async function getContentEntryModule(
params: GetEntryModuleParams<ContentEntryType>
): Promise<ContentEntryModule> {
- const { fileId, contentDir, pluginContext, config } = params;
+ const { fileId, contentDir, pluginContext } = params;
const { collectionConfig, entryConfig, entry, rawContents, collection } =
await getEntryModuleBaseInfo(params);
@@ -221,8 +215,7 @@ async function getContentEntryModule(
? await getEntryData(
{ id, collection, _internal, unvalidatedData },
collectionConfig,
- pluginContext,
- config
+ pluginContext
)
: unvalidatedData;
@@ -241,7 +234,7 @@ async function getContentEntryModule(
async function getDataEntryModule(
params: GetEntryModuleParams<DataEntryType>
): Promise<DataEntryModule> {
- const { fileId, contentDir, pluginContext, config } = params;
+ const { fileId, contentDir, pluginContext } = params;
const { collectionConfig, entryConfig, entry, rawContents, collection } =
await getEntryModuleBaseInfo(params);
@@ -256,8 +249,7 @@ async function getDataEntryModule(
? await getEntryData(
{ id, collection, _internal, unvalidatedData },
collectionConfig,
- pluginContext,
- config
+ pluginContext
)
: unvalidatedData;
diff --git a/packages/astro/src/core/README.md b/packages/astro/src/core/README.md
index 7f5e4f89c..ef4dd8750 100644
--- a/packages/astro/src/core/README.md
+++ b/packages/astro/src/core/README.md
@@ -1,5 +1,20 @@
# `core/`
-Code that executes within the top-level Node context. Contains the main Astro logic for the `build` and `dev` commands, and also manages the Vite server and SSR.
+Code that executes within the top-level Node context. Contains the main Astro logic for the `build`, `dev`, `preview`, and `sync` commands, and also manages the Vite server and SSR.
+
+The `core/index.ts` file is the main entry point for the `astro` package.
[See CONTRIBUTING.md](../../../../CONTRIBUTING.md) for a code overview.
+
+## Pipeline
+
+The pipeline is an internal concept that describes how Astro pages are eventually created and rendered to the user.
+
+Each pipeline has different requirements, criteria and quirks. Although, each pipeline must use the same underline functions, because
+the core of the pipeline is the same.
+
+The core of the pipeline is rendering a generic route (page, endpoint or redirect) and returning a `Response`.
+When rendering a route, a pipeline must pass a `RenderContext` and `ComponentInstance`. The way these two information are
+computed doesn't concern the core of a pipeline. In fact, these types will be computed in different manner based on the type of pipeline.
+
+Each consumer will decide how to handle a `Response`.
diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts
index 76958a549..95abac957 100644
--- a/packages/astro/src/core/app/index.ts
+++ b/packages/astro/src/core/app/index.ts
@@ -1,13 +1,13 @@
-import mime from 'mime';
import type {
EndpointHandler,
ManifestData,
+ MiddlewareEndpointHandler,
RouteData,
SSRElement,
SSRManifest,
} from '../../@types/astro';
import type { SinglePageBuiltModule } from '../build/types';
-import { attachToResponse, getSetCookiesFromResponse } from '../cookies/index.js';
+import { getSetCookiesFromResponse } from '../cookies/index.js';
import { consoleLogDestination } from '../logger/console.js';
import { error, type LogOptions } from '../logger/core.js';
import {
@@ -16,14 +16,7 @@ import {
removeTrailingForwardSlash,
} from '../path.js';
import { RedirectSinglePageBuiltModule } from '../redirects/index.js';
-import { isResponse } from '../render/core.js';
-import {
- createEnvironment,
- createRenderContext,
- tryRenderRoute,
- type Environment,
- type RenderContext,
-} from '../render/index.js';
+import { createEnvironment, createRenderContext, type RenderContext } from '../render/index.js';
import { RouteCache } from '../render/route-cache.js';
import {
createAssetLink,
@@ -32,6 +25,7 @@ import {
} from '../render/ssr-element.js';
import { matchRoute } from '../routing/match.js';
import type { RouteInfo } from './types';
+import { EndpointNotFoundError, SSRRoutePipeline } from './ssrPipeline.js';
export { deserializeManifest } from './common.js';
const clientLocalsSymbol = Symbol.for('astro.locals');
@@ -53,16 +47,15 @@ export class App {
/**
* The current environment of the application
*/
- #env: Environment;
#manifest: SSRManifest;
#manifestData: ManifestData;
#routeDataToRouteInfo: Map<RouteData, RouteInfo>;
- #encoder = new TextEncoder();
#logging: LogOptions = {
dest: consoleLogDestination,
level: 'info',
};
#baseWithoutTrailingSlash: string;
+ #pipeline: SSRRoutePipeline;
constructor(manifest: SSRManifest, streaming = true) {
this.#manifest = manifest;
@@ -71,7 +64,7 @@ export class App {
};
this.#routeDataToRouteInfo = new Map(manifest.routes.map((route) => [route.routeData, route]));
this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
- this.#env = this.#createEnvironment(streaming);
+ this.#pipeline = new SSRRoutePipeline(this.#createEnvironment(streaming));
}
set setManifest(newManifest: SSRManifest) {
@@ -88,7 +81,6 @@ export class App {
return createEnvironment({
adapterName: this.#manifest.adapterName,
logging: this.#logging,
- markdown: this.#manifest.markdown,
mode: 'production',
compressHTML: this.#manifest.compressHTML,
renderers: this.#manifest.renderers,
@@ -164,19 +156,21 @@ export class App {
);
let response;
try {
- response = await tryRenderRoute(
- routeData.type,
- renderContext,
- this.#env,
- pageModule,
- mod.onRequest
- );
+ // NOTE: ideally we could set the middleware function just once, but we don't have the infrastructure to that yet
+ if (mod.onRequest) {
+ this.#pipeline.setMiddlewareFunction(mod.onRequest as MiddlewareEndpointHandler);
+ }
+ response = await this.#pipeline.renderRoute(renderContext, pageModule);
} catch (err: any) {
- error(this.#logging, 'ssr', err.stack || err.message || String(err));
- return this.#renderError(request, { status: 500 });
+ if (err instanceof EndpointNotFoundError) {
+ return this.#renderError(request, { status: 404, response: err.originalResponse });
+ } else {
+ error(this.#logging, 'ssr', err.stack || err.message || String(err));
+ return this.#renderError(request, { status: 500 });
+ }
}
- if (isResponse(response, routeData.type)) {
+ if (routeData.type === 'page' || routeData.type === 'redirect') {
if (STATUS_CODES.has(response.status)) {
return this.#renderError(request, {
response,
@@ -185,35 +179,8 @@ export class App {
}
Reflect.set(response, responseSentSymbol, true);
return response;
- } else {
- if (response.type === 'response') {
- if (response.response.headers.get('X-Astro-Response') === 'Not-Found') {
- return this.#renderError(request, {
- response: response.response,
- status: 404,
- });
- }
- return response.response;
- } else {
- const headers = new Headers();
- const mimeType = mime.getType(url.pathname);
- if (mimeType) {
- headers.set('Content-Type', `${mimeType};charset=utf-8`);
- } else {
- headers.set('Content-Type', 'text/plain;charset=utf-8');
- }
- const bytes =
- response.encoding !== 'binary' ? this.#encoder.encode(response.body) : response.body;
- headers.set('Content-Length', bytes.byteLength.toString());
-
- const newResponse = new Response(bytes, {
- status: 200,
- headers,
- });
- attachToResponse(newResponse, response.cookies);
- return newResponse;
- }
}
+ return response;
}
setCookieHeaders(response: Response) {
@@ -239,7 +206,7 @@ export class App {
pathname,
route: routeData,
status,
- env: this.#env,
+ env: this.#pipeline.env,
mod: handler as any,
});
} else {
@@ -273,7 +240,7 @@ export class App {
route: routeData,
status,
mod,
- env: this.#env,
+ env: this.#pipeline.env,
});
}
}
@@ -310,12 +277,7 @@ export class App {
status
);
const page = (await mod.page()) as any;
- const response = (await tryRenderRoute(
- 'page', // this is hardcoded to ensure proper behavior for missing endpoints
- newRenderContext,
- this.#env,
- page
- )) as Response;
+ const response = await this.#pipeline.renderRoute(newRenderContext, page);
return this.#mergeResponses(response, originalResponse);
} catch {}
}
diff --git a/packages/astro/src/core/app/node.ts b/packages/astro/src/core/app/node.ts
index 4ae6e98a9..054064a08 100644
--- a/packages/astro/src/core/app/node.ts
+++ b/packages/astro/src/core/app/node.ts
@@ -6,6 +6,7 @@ import { IncomingMessage } from 'node:http';
import { TLSSocket } from 'node:tls';
import { deserializeManifest } from './common.js';
import { App, type MatchOptions } from './index.js';
+export { apply as applyPolyfills } from '../polyfill.js';
const clientAddressSymbol = Symbol.for('astro.clientAddress');
diff --git a/packages/astro/src/core/app/ssrPipeline.ts b/packages/astro/src/core/app/ssrPipeline.ts
new file mode 100644
index 000000000..6ee135197
--- /dev/null
+++ b/packages/astro/src/core/app/ssrPipeline.ts
@@ -0,0 +1,28 @@
+import type { Environment } from '../render';
+import { Pipeline } from '../pipeline.js';
+
+/**
+ * Thrown when an endpoint contains a response with the header "X-Astro-Response" === 'Not-Found'
+ */
+export class EndpointNotFoundError extends Error {
+ originalResponse: Response;
+ constructor(originalResponse: Response) {
+ super();
+ this.originalResponse = originalResponse;
+ }
+}
+
+export class SSRRoutePipeline extends Pipeline {
+ constructor(env: Environment) {
+ super(env);
+ this.setEndpointHandler(this.#ssrEndpointHandler);
+ }
+
+ // This function is responsible for handling the result coming from an endpoint.
+ async #ssrEndpointHandler(request: Request, response: Response): Promise<Response> {
+ if (response.headers.get('X-Astro-Response') === 'Not-Found') {
+ throw new EndpointNotFoundError(response);
+ }
+ return response
+ }
+}
diff --git a/packages/astro/src/core/app/types.ts b/packages/astro/src/core/app/types.ts
index 67d16d457..8812d2c44 100644
--- a/packages/astro/src/core/app/types.ts
+++ b/packages/astro/src/core/app/types.ts
@@ -1,4 +1,3 @@
-import type { MarkdownRenderingOptions } from '@astrojs/markdown-remark';
import type {
RouteData,
SerializedRouteData,
@@ -40,7 +39,6 @@ export type SSRManifest = {
base: string;
compressHTML: boolean;
assetsPrefix?: string;
- markdown: MarkdownRenderingOptions;
renderers: SSRLoadedRenderer[];
/**
* Map of directive name (e.g. `load`) to the directive script code
diff --git a/packages/astro/src/core/build/buildPipeline.ts b/packages/astro/src/core/build/buildPipeline.ts
new file mode 100644
index 000000000..52d694077
--- /dev/null
+++ b/packages/astro/src/core/build/buildPipeline.ts
@@ -0,0 +1,163 @@
+import { Pipeline } from '../pipeline.js';
+import type { BuildInternals } from './internal';
+import type { PageBuildData, StaticBuildOptions } from './types';
+import { ASTRO_PAGE_RESOLVED_MODULE_ID } from './plugins/plugin-pages.js';
+import { RESOLVED_SPLIT_MODULE_ID } from './plugins/plugin-ssr.js';
+import { ASTRO_PAGE_EXTENSION_POST_PATTERN } from './plugins/util.js';
+import type { SSRManifest } from '../app/types';
+import type { AstroConfig, AstroSettings, SSRLoadedRenderer } from '../../@types/astro';
+import { getOutputDirectory, isServerLikeOutput } from '../../prerender/utils.js';
+import { createEnvironment } from '../render/index.js';
+import { BEFORE_HYDRATION_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
+import { createAssetLink } from '../render/ssr-element.js';
+
+/**
+ * This pipeline is responsible to gather the files emitted by the SSR build and generate the pages by executing these files.
+ */
+export class BuildPipeline extends Pipeline {
+ #internals: BuildInternals;
+ #staticBuildOptions: StaticBuildOptions;
+ #manifest: SSRManifest;
+
+ constructor(
+ staticBuildOptions: StaticBuildOptions,
+ internals: BuildInternals,
+ manifest: SSRManifest
+ ) {
+ const ssr = isServerLikeOutput(staticBuildOptions.settings.config);
+ super(
+ createEnvironment({
+ adapterName: manifest.adapterName,
+ logging: staticBuildOptions.logging,
+ mode: staticBuildOptions.mode,
+ renderers: manifest.renderers,
+ clientDirectives: manifest.clientDirectives,
+ compressHTML: manifest.compressHTML,
+ async resolve(specifier: string) {
+ const hashedFilePath = manifest.entryModules[specifier];
+ if (typeof hashedFilePath !== 'string') {
+ // If no "astro:scripts/before-hydration.js" script exists in the build,
+ // then we can assume that no before-hydration scripts are needed.
+ if (specifier === BEFORE_HYDRATION_SCRIPT_ID) {
+ return '';
+ }
+ throw new Error(`Cannot find the built path for ${specifier}`);
+ }
+ return createAssetLink(hashedFilePath, manifest.base, manifest.assetsPrefix);
+ },
+ routeCache: staticBuildOptions.routeCache,
+ site: manifest.site,
+ ssr,
+ streaming: true,
+ })
+ );
+ this.#internals = internals;
+ this.#staticBuildOptions = staticBuildOptions;
+ this.#manifest = manifest;
+ this.setEndpointHandler(this.#handleEndpointResult);
+ }
+
+ getInternals(): Readonly<BuildInternals> {
+ return this.#internals;
+ }
+
+ getSettings(): Readonly<AstroSettings> {
+ return this.#staticBuildOptions.settings;
+ }
+
+ getStaticBuildOptions(): Readonly<StaticBuildOptions> {
+ return this.#staticBuildOptions;
+ }
+
+ getConfig(): AstroConfig {
+ return this.#staticBuildOptions.settings.config;
+ }
+
+ getManifest(): SSRManifest {
+ return this.#manifest;
+ }
+
+ /**
+ * The SSR build emits two important files:
+ * - dist/server/manifest.mjs
+ * - dist/renderers.mjs
+ *
+ * These two files, put together, will be used to generate the pages.
+ *
+ * ## Errors
+ *
+ * It will throw errors if the previous files can't be found in the file system.
+ *
+ * @param staticBuildOptions
+ */
+ static async retrieveManifest(
+ staticBuildOptions: StaticBuildOptions,
+ internals: BuildInternals
+ ): Promise<SSRManifest> {
+ const config = staticBuildOptions.settings.config;
+ const baseDirectory = getOutputDirectory(config);
+ const manifestEntryUrl = new URL(
+ `${internals.manifestFileName}?time=${Date.now()}`,
+ baseDirectory
+ );
+ const { manifest } = await import(manifestEntryUrl.toString());
+ if (!manifest) {
+ throw new Error(
+ "Astro couldn't find the emitted manifest. This is an internal error, please file an issue."
+ );
+ }
+
+ const renderersEntryUrl = new URL(`renderers.mjs?time=${Date.now()}`, baseDirectory);
+ const renderers = await import(renderersEntryUrl.toString());
+ if (!renderers) {
+ throw new Error(
+ "Astro couldn't find the emitted renderers. This is an internal error, please file an issue."
+ );
+ }
+ return {
+ ...manifest,
+ renderers: renderers.renderers as SSRLoadedRenderer[],
+ };
+ }
+
+ /**
+ * It collects the routes to generate during the build.
+ *
+ * It returns a map of page information and their relative entry point as a string.
+ */
+ retrieveRoutesToGenerate(): Map<PageBuildData, string> {
+ const pages = new Map<PageBuildData, string>();
+
+ for (const [entryPoint, filePath] of this.#internals.entrySpecifierToBundleMap) {
+ // virtual pages can be emitted with different prefixes:
+ // - the classic way are pages emitted with prefix ASTRO_PAGE_RESOLVED_MODULE_ID -> plugin-pages
+ // - pages emitted using `build.split`, in this case pages are emitted with prefix RESOLVED_SPLIT_MODULE_ID
+ if (
+ entryPoint.includes(ASTRO_PAGE_RESOLVED_MODULE_ID) ||
+ entryPoint.includes(RESOLVED_SPLIT_MODULE_ID)
+ ) {
+ const [, pageName] = entryPoint.split(':');
+ const pageData = this.#internals.pagesByComponent.get(
+ `${pageName.replace(ASTRO_PAGE_EXTENSION_POST_PATTERN, '.')}`
+ );
+ if (!pageData) {
+ throw new Error(
+ "Build failed. Astro couldn't find the emitted page from " + pageName + ' pattern'
+ );
+ }
+
+ pages.set(pageData, filePath);
+ }
+ }
+ for (const [path, pageData] of this.#internals.pagesByComponent.entries()) {
+ if (pageData.route.type === 'redirect') {
+ pages.set(pageData, path);
+ }
+ }
+ return pages;
+ }
+
+ async #handleEndpointResult(_: Request, response: Response): Promise<Response> {
+ return response;
+ }
+}
diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts
index 32b5ebb09..5ec60b86d 100644
--- a/packages/astro/src/core/build/generate.ts
+++ b/packages/astro/src/core/build/generate.ts
@@ -9,7 +9,7 @@ import type {
ComponentInstance,
GetStaticPathsItem,
ImageTransform,
- MiddlewareHandler,
+ MiddlewareEndpointHandler,
RouteData,
RouteType,
SSRError,
@@ -20,26 +20,21 @@ import {
generateImage as generateImageInternal,
getStaticImageList,
} from '../../assets/build/generate.js';
-import {
- eachPageDataFromEntryPoint,
- eachRedirectPageData,
- hasPrerenderedPages,
- type BuildInternals,
-} from '../../core/build/internal.js';
+import { hasPrerenderedPages, type BuildInternals } from '../../core/build/internal.js';
import {
isRelativePath,
+ joinPaths,
prependForwardSlash,
removeLeadingForwardSlash,
removeTrailingForwardSlash,
} from '../../core/path.js';
import { runHookBuildGenerated } from '../../integrations/index.js';
-import { isServerLikeOutput } from '../../prerender/utils.js';
-import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
+import { getOutputDirectory, isServerLikeOutput } from '../../prerender/utils.js';
+import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
-import { debug, info } from '../logger/core.js';
+import { debug, info, Logger } from '../logger/core.js';
import { RedirectSinglePageBuiltModule, getRedirectLocationOrThrow } from '../redirects/index.js';
-import { isEndpointResult } from '../render/core.js';
-import { createEnvironment, createRenderContext, tryRenderRoute } from '../render/index.js';
+import { createRenderContext } from '../render/index.js';
import { callGetStaticPaths } from '../render/route-cache.js';
import {
createAssetLink,
@@ -63,6 +58,8 @@ import type {
StylesheetAsset,
} from './types';
import { getTimeStat } from './util.js';
+import { BuildPipeline } from './buildPipeline.js';
+import type { BufferEncoding } from 'vfile';
function createEntryURL(filePath: string, outFolder: URL) {
return new URL('./' + filePath + `?time=${Date.now()}`, outFolder);
@@ -124,8 +121,23 @@ export function chunkIsPage(
}
export async function generatePages(opts: StaticBuildOptions, internals: BuildInternals) {
+ const logger = new Logger(opts.logging);
const timer = performance.now();
const ssr = isServerLikeOutput(opts.settings.config);
+ let manifest: SSRManifest;
+ if (ssr) {
+ manifest = await BuildPipeline.retrieveManifest(opts, internals);
+ } else {
+ const baseDirectory = getOutputDirectory(opts.settings.config);
+ const renderersEntryUrl = new URL('renderers.mjs', baseDirectory);
+ const renderers = await import(renderersEntryUrl.toString());
+ manifest = createBuildManifest(
+ opts.settings,
+ internals,
+ renderers.renderers as SSRLoadedRenderer[]
+ );
+ }
+ const buildPipeline = new BuildPipeline(opts, internals, manifest);
const outFolder = ssr
? opts.settings.config.build.server
: getOutDirWithinCwd(opts.settings.config.outDir);
@@ -139,20 +151,22 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn
const verb = ssr ? 'prerendering' : 'generating';
info(opts.logging, null, `\n${bgGreen(black(` ${verb} static routes `))}`);
-
const builtPaths = new Set<string>();
-
+ const pagesToGenerate = buildPipeline.retrieveRoutesToGenerate();
if (ssr) {
- for (const [pageData, filePath] of eachPageDataFromEntryPoint(internals)) {
+ for (const [pageData, filePath] of pagesToGenerate) {
if (pageData.route.prerender) {
const ssrEntryURLPage = createEntryURL(filePath, outFolder);
const ssrEntryPage = await import(ssrEntryURLPage.toString());
- if (opts.settings.config.build.split) {
+ if (
+ // TODO: remove in Astro 4.0
+ opts.settings.config.build.split ||
+ opts.settings.adapter?.adapterFeatures?.functionPerRoute
+ ) {
// forcing to use undefined, so we fail in an expected way if the module is not even there.
- const manifest: SSRManifest | undefined = ssrEntryPage.manifest;
- const ssrEntry = manifest?.pageModule;
+ const ssrEntry = ssrEntryPage?.manifest?.pageModule;
if (ssrEntry) {
- await generatePage(opts, internals, pageData, ssrEntry, builtPaths, manifest);
+ await generatePage(pageData, ssrEntry, builtPaths, buildPipeline, logger);
} else {
throw new Error(
`Unable to find the manifest for the module ${ssrEntryURLPage.toString()}. This is unexpected and likely a bug in Astro, please report.`
@@ -160,40 +174,35 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn
}
} else {
const ssrEntry = ssrEntryPage as SinglePageBuiltModule;
- const manifest = createBuildManifest(opts.settings, internals, ssrEntry.renderers);
- await generatePage(opts, internals, pageData, ssrEntry, builtPaths, manifest);
+ await generatePage(pageData, ssrEntry, builtPaths, buildPipeline, logger);
}
}
- }
- for (const pageData of eachRedirectPageData(internals)) {
- const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder);
- const manifest = createBuildManifest(opts.settings, internals, entry.renderers);
- await generatePage(opts, internals, pageData, entry, builtPaths, manifest);
+ if (pageData.route.type === 'redirect') {
+ const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder);
+ await generatePage(pageData, entry, builtPaths, buildPipeline, logger);
+ }
}
} else {
- for (const [pageData, filePath] of eachPageDataFromEntryPoint(internals)) {
- const ssrEntryURLPage = createEntryURL(filePath, outFolder);
- const entry: SinglePageBuiltModule = await import(ssrEntryURLPage.toString());
- const manifest = createBuildManifest(opts.settings, internals, entry.renderers);
+ for (const [pageData, filePath] of pagesToGenerate) {
+ if (pageData.route.type === 'redirect') {
+ const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder);
+ await generatePage(pageData, entry, builtPaths, buildPipeline, logger);
+ } else {
+ const ssrEntryURLPage = createEntryURL(filePath, outFolder);
+ const entry: SinglePageBuiltModule = await import(ssrEntryURLPage.toString());
- await generatePage(opts, internals, pageData, entry, builtPaths, manifest);
- }
- for (const pageData of eachRedirectPageData(internals)) {
- const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder);
- const manifest = createBuildManifest(opts.settings, internals, entry.renderers);
- await generatePage(opts, internals, pageData, entry, builtPaths, manifest);
+ await generatePage(pageData, entry, builtPaths, buildPipeline, logger);
+ }
}
}
- if (opts.settings.config.experimental.assets) {
- info(opts.logging, null, `\n${bgGreen(black(` generating optimized images `))}`);
- for (const imageData of getStaticImageList()) {
- await generateImage(opts, imageData[1].options, imageData[1].path);
- }
-
- delete globalThis?.astroAsset?.addStaticImage;
+ info(opts.logging, null, `\n${bgGreen(black(` generating optimized images `))}`);
+ for (const imageData of getStaticImageList()) {
+ await generateImage(opts, imageData[1].options, imageData[1].path);
}
+ delete globalThis?.astroAsset?.addStaticImage;
+
await runHookBuildGenerated({
config: opts.settings.config,
logging: opts.logging,
@@ -220,16 +229,15 @@ async function generateImage(opts: StaticBuildOptions, transform: ImageTransform
}
async function generatePage(
- opts: StaticBuildOptions,
- internals: BuildInternals,
pageData: PageBuildData,
ssrEntry: SinglePageBuiltModule,
builtPaths: Set<string>,
- manifest: SSRManifest
+ pipeline: BuildPipeline,
+ logger: Logger
) {
let timeStart = performance.now();
- const pageInfo = getPageDataByComponent(internals, pageData.route.component);
+ const pageInfo = getPageDataByComponent(pipeline.getInternals(), pageData.route.component);
// may be used in the future for handling rel=modulepreload, rel=icon, rel=manifest etc.
const linkIds: [] = [];
@@ -241,6 +249,9 @@ async function generatePage(
const pageModulePromise = ssrEntry.page;
const onRequest = ssrEntry.onRequest;
+ if (onRequest) {
+ pipeline.setMiddlewareFunction(onRequest as MiddlewareEndpointHandler);
+ }
if (!pageModulePromise) {
throw new Error(
@@ -248,14 +259,18 @@ async function generatePage(
);
}
const pageModule = await pageModulePromise();
- if (shouldSkipDraft(pageModule, opts.settings)) {
- info(opts.logging, null, `${magenta('⚠️')} Skipping draft ${pageData.route.component}`);
+ if (shouldSkipDraft(pageModule, pipeline.getSettings())) {
+ logger.info(null, `${magenta('⚠️')} Skipping draft ${pageData.route.component}`);
+ // TODO: Remove in Astro 4.0
+ logger.warn(
+ 'astro',
+ `The drafts feature is deprecated. You should migrate to content collections instead. See https://docs.astro.build/en/guides/content-collections/#filtering-collection-queries for more information.`
+ );
return;
}
const generationOptions: Readonly<GeneratePathOptions> = {
pageData,
- internals,
linkIds,
scripts,
styles,
@@ -264,24 +279,29 @@ async function generatePage(
const icon = pageData.route.type === 'page' ? green('▶') : magenta('λ');
if (isRelativePath(pageData.route.component)) {
- info(opts.logging, null, `${icon} ${pageData.route.route}`);
+ logger.info(null, `${icon} ${pageData.route.route}`);
} else {
- info(opts.logging, null, `${icon} ${pageData.route.component}`);
+ logger.info(null, `${icon} ${pageData.route.component}`);
}
// Get paths for the route, calling getStaticPaths if needed.
- const paths = await getPathsForRoute(pageData, pageModule, opts, builtPaths);
+ const paths = await getPathsForRoute(
+ pageData,
+ pageModule,
+ pipeline.getStaticBuildOptions(),
+ builtPaths
+ );
let prevTimeEnd = timeStart;
for (let i = 0; i < paths.length; i++) {
const path = paths[i];
- await generatePath(path, opts, generationOptions, manifest, onRequest);
+ await generatePath(path, generationOptions, pipeline);
const timeEnd = performance.now();
const timeChange = getTimeStat(prevTimeEnd, timeEnd);
const timeIncrease = `(+${timeChange})`;
- const filePath = getOutputFilename(opts.settings.config, path, pageData.route.type);
+ const filePath = getOutputFilename(pipeline.getConfig(), path, pageData.route.type);
const lineIcon = i === paths.length - 1 ? '└─' : '├─';
- info(opts.logging, null, ` ${cyan(lineIcon)} ${dim(filePath)} ${dim(timeIncrease)}`);
+ logger.info(null, ` ${cyan(lineIcon)} ${dim(filePath)} ${dim(timeIncrease)}`);
prevTimeEnd = timeEnd;
}
}
@@ -302,7 +322,6 @@ async function getPathsForRoute(
mod,
route,
routeCache: opts.routeCache,
- isValidate: false,
logging: opts.logging,
ssr: isServerLikeOutput(opts.settings.config),
}).catch((err) => {
@@ -386,7 +405,6 @@ function getInvalidRouteSegmentError(
interface GeneratePathOptions {
pageData: PageBuildData;
- internals: BuildInternals;
linkIds: string[];
scripts: { type: 'inline' | 'external'; value: string } | null;
styles: StylesheetAsset[];
@@ -440,29 +458,23 @@ function getUrlForPath(
buildPathname = base;
} else if (routeType === 'endpoint') {
const buildPathRelative = removeLeadingForwardSlash(pathname);
- buildPathname = base + buildPathRelative;
+ buildPathname = joinPaths(base, buildPathRelative);
} else {
const buildPathRelative =
removeTrailingForwardSlash(removeLeadingForwardSlash(pathname)) + ending;
- buildPathname = base + buildPathRelative;
+ buildPathname = joinPaths(base, buildPathRelative);
}
const url = new URL(buildPathname, origin);
return url;
}
-async function generatePath(
- pathname: string,
- opts: StaticBuildOptions,
- gopts: GeneratePathOptions,
- manifest: SSRManifest,
- onRequest?: MiddlewareHandler<unknown>
-) {
- const { settings, logging, origin, routeCache } = opts;
- const { mod, internals, scripts: hoistedScripts, styles: _styles, pageData } = gopts;
+async function generatePath(pathname: string, gopts: GeneratePathOptions, pipeline: BuildPipeline) {
+ const manifest = pipeline.getManifest();
+ const { mod, scripts: hoistedScripts, styles: _styles, pageData } = gopts;
// This adds the page name to the array so it can be shown as part of stats.
if (pageData.route.type === 'page') {
- addPageName(pathname, opts);
+ addPageName(pathname, pipeline.getStaticBuildOptions());
}
debug('build', `Generating: ${pathname}`);
@@ -476,8 +488,8 @@ async function generatePath(
);
const styles = createStylesheetElementSet(_styles, manifest.base, manifest.assetsPrefix);
- if (settings.scripts.some((script) => script.stage === 'page')) {
- const hashedFilePath = internals.entrySpecifierToBundleMap.get(PAGE_SCRIPT_ID);
+ if (pipeline.getSettings().scripts.some((script) => script.stage === 'page')) {
+ const hashedFilePath = pipeline.getInternals().entrySpecifierToBundleMap.get(PAGE_SCRIPT_ID);
if (typeof hashedFilePath !== 'string') {
throw new Error(`Cannot find the built path for ${PAGE_SCRIPT_ID}`);
}
@@ -489,7 +501,7 @@ async function generatePath(
}
// Add all injected scripts to the page.
- for (const script of settings.scripts) {
+ for (const script of pipeline.getSettings().scripts) {
if (script.stage === 'head-inline') {
scripts.add({
props: {},
@@ -498,59 +510,38 @@ async function generatePath(
}
}
- const ssr = isServerLikeOutput(settings.config);
+ const ssr = isServerLikeOutput(pipeline.getConfig());
const url = getUrlForPath(
pathname,
- opts.settings.config.base,
- origin,
- opts.settings.config.build.format,
+ pipeline.getConfig().base,
+ pipeline.getStaticBuildOptions().origin,
+ pipeline.getConfig().build.format,
pageData.route.type
);
- const env = createEnvironment({
- adapterName: manifest.adapterName,
- logging,
- markdown: manifest.markdown,
- mode: opts.mode,
- renderers: manifest.renderers,
- clientDirectives: manifest.clientDirectives,
- compressHTML: manifest.compressHTML,
- async resolve(specifier: string) {
- const hashedFilePath = manifest.entryModules[specifier];
- if (typeof hashedFilePath !== 'string') {
- // If no "astro:scripts/before-hydration.js" script exists in the build,
- // then we can assume that no before-hydration scripts are needed.
- if (specifier === BEFORE_HYDRATION_SCRIPT_ID) {
- return '';
- }
- throw new Error(`Cannot find the built path for ${specifier}`);
- }
- return createAssetLink(hashedFilePath, manifest.base, manifest.assetsPrefix);
- },
- routeCache,
- site: manifest.site,
- ssr,
- streaming: true,
- });
-
const renderContext = await createRenderContext({
pathname,
- request: createRequest({ url, headers: new Headers(), logging, ssr }),
+ request: createRequest({
+ url,
+ headers: new Headers(),
+ logging: pipeline.getStaticBuildOptions().logging,
+ ssr,
+ }),
componentMetadata: manifest.componentMetadata,
scripts,
styles,
links,
route: pageData.route,
- env,
+ env: pipeline.getEnvironment(),
mod,
});
let body: string | Uint8Array;
let encoding: BufferEncoding | undefined;
- let response;
+ let response: Response;
try {
- response = await tryRenderRoute(pageData.route.type, renderContext, env, mod, onRequest);
+ response = await pipeline.renderRoute(renderContext, mod);
} catch (err) {
if (!AstroError.is(err) && !(err as SSRError).id && typeof err === 'object') {
(err as SSRError).id = pageData.component;
@@ -558,28 +549,17 @@ async function generatePath(
throw err;
}
- if (isEndpointResult(response, pageData.route.type)) {
- if (response.type === 'response') {
- // If there's no body, do nothing
- if (!response.response.body) return;
- const ab = await response.response.arrayBuffer();
- body = new Uint8Array(ab);
- } else {
- body = response.body;
- encoding = response.encoding;
+ if (response.status >= 300 && response.status < 400) {
+ // If redirects is set to false, don't output the HTML
+ if (!pipeline.getConfig().build.redirects) {
+ return;
}
- } else {
- if (response.status >= 300 && response.status < 400) {
- // If redirects is set to false, don't output the HTML
- if (!opts.settings.config.build.redirects) {
- return;
- }
- const location = getRedirectLocationOrThrow(response.headers);
- const fromPath = new URL(renderContext.request.url).pathname;
- // A short delay causes Google to interpret the redirect as temporary.
- // https://developers.google.com/search/docs/crawling-indexing/301-redirects#metarefresh
- const delay = response.status === 302 ? 2 : 0;
- body = `<!doctype html>
+ const location = getRedirectLocationOrThrow(response.headers);
+ const fromPath = new URL(renderContext.request.url).pathname;
+ // A short delay causes Google to interpret the redirect as temporary.
+ // https://developers.google.com/search/docs/crawling-indexing/301-redirects#metarefresh
+ const delay = response.status === 302 ? 2 : 0;
+ body = `<!doctype html>
<title>Redirecting to: ${location}</title>
<meta http-equiv="refresh" content="${delay};url=${location}">
<meta name="robots" content="noindex">
@@ -587,22 +567,23 @@ async function generatePath(
<body>
<a href="${location}">Redirecting from <code>${fromPath}</code> to <code>${location}</code></a>
</body>`;
- // A dynamic redirect, set the location so that integrations know about it.
- if (pageData.route.type !== 'redirect') {
- pageData.route.redirect = location;
- }
- } else {
- // If there's no body, do nothing
- if (!response.body) return;
- body = await response.text();
+ // A dynamic redirect, set the location so that integrations know about it.
+ if (pageData.route.type !== 'redirect') {
+ pageData.route.redirect = location;
}
+ } else {
+ // If there's no body, do nothing
+ if (!response.body) return;
+ body = Buffer.from(await response.arrayBuffer());
+ encoding = (response.headers.get('X-Astro-Encoding') as BufferEncoding | null) ?? 'utf-8';
}
- const outFolder = getOutFolder(settings.config, pathname, pageData.route.type);
- const outFile = getOutFile(settings.config, outFolder, pathname, pageData.route.type);
+ const outFolder = getOutFolder(pipeline.getConfig(), pathname, pageData.route.type);
+ const outFile = getOutFile(pipeline.getConfig(), outFolder, pathname, pageData.route.type);
pageData.route.distURL = outFile;
+
await fs.promises.mkdir(outFolder, { recursive: true });
- await fs.promises.writeFile(outFile, body, encoding ?? 'utf-8');
+ await fs.promises.writeFile(outFile, body, encoding);
}
/**
@@ -622,7 +603,6 @@ export function createBuildManifest(
entryModules: Object.fromEntries(internals.entrySpecifierToBundleMap.entries()),
routes: [],
adapterName: '',
- markdown: settings.config.markdown,
clientDirectives: settings.clientDirectives,
compressHTML: settings.config.compressHTML,
renderers,
diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts
index 5b1ecf404..894a5e6e2 100644
--- a/packages/astro/src/core/build/index.ts
+++ b/packages/astro/src/core/build/index.ts
@@ -37,14 +37,22 @@ export interface BuildOptions {
/**
* Teardown the compiler WASM instance after build. This can improve performance when
* building once, but may cause a performance hit if building multiple times in a row.
+ *
+ * @internal only used for testing
+ * @default true
*/
teardownCompiler?: boolean;
}
-/** `astro build` */
+/**
+ * Builds your site for deployment. By default, this will generate static files and place them in a dist/ directory.
+ * If SSR is enabled, this will generate the necessary server files to serve your site.
+ *
+ * @experimental The JavaScript API is experimental
+ */
export default async function build(
inlineConfig: AstroInlineConfig,
- options: BuildOptions
+ options?: BuildOptions
): Promise<void> {
applyPolyfill();
const logging = createNodeLogging(inlineConfig);
@@ -82,7 +90,7 @@ class AstroBuilder {
}
this.settings = settings;
this.logging = options.logging;
- this.teardownCompiler = options.teardownCompiler ?? false;
+ this.teardownCompiler = options.teardownCompiler ?? true;
this.routeCache = new RouteCache(this.logging);
this.origin = settings.config.site
? new URL(settings.config.site).origin
@@ -102,9 +110,7 @@ class AstroBuilder {
logging,
});
- // HACK: Since we only inject the endpoint if `experimental.assets` is on and it's possible for an integration to
- // add that flag, we need to only check and inject the endpoint after running the config setup hook.
- if (this.settings.config.experimental.assets && isServerLikeOutput(this.settings.config)) {
+ if (isServerLikeOutput(this.settings.config)) {
this.settings = injectImageEndpoint(this.settings);
}
diff --git a/packages/astro/src/core/build/internal.ts b/packages/astro/src/core/build/internal.ts
index 5dff6f3dd..c1123e36b 100644
--- a/packages/astro/src/core/build/internal.ts
+++ b/packages/astro/src/core/build/internal.ts
@@ -85,6 +85,9 @@ export interface BuildInternals {
staticFiles: Set<string>;
// The SSR entry chunk. Kept in internals to share between ssr/client build steps
ssrEntryChunk?: Rollup.OutputChunk;
+ // The SSR manifest entry chunk.
+ manifestEntryChunk?: Rollup.OutputChunk;
+ manifestFileName?: string;
entryPoints: Map<RouteData, URL>;
ssrSplitEntryChunks: Map<string, Rollup.OutputChunk>;
componentMetadata: SSRResult['componentMetadata'];
@@ -227,14 +230,6 @@ export function* eachPageData(internals: BuildInternals) {
yield* internals.pagesByComponent.values();
}
-export function* eachRedirectPageData(internals: BuildInternals) {
- for (const pageData of eachPageData(internals)) {
- if (pageData.route.type === 'redirect') {
- yield pageData;
- }
- }
-}
-
export function* eachPageDataFromEntryPoint(
internals: BuildInternals
): Generator<[PageBuildData, string]> {
diff --git a/packages/astro/src/core/build/plugins/README.md b/packages/astro/src/core/build/plugins/README.md
index 145158163..410b81da6 100644
--- a/packages/astro/src/core/build/plugins/README.md
+++ b/packages/astro/src/core/build/plugins/README.md
@@ -125,10 +125,13 @@ will look like this:
Of course, all these files will be deleted by Astro at the end build.
-## `plugin-ssr` (WIP)
+## `plugin-ssr`
-This plugin is responsible to create a single `entry.mjs` file that will be used
-in SSR.
+This plugin is responsible to create the JS files that will be executed in SSR.
+
+### Classic mode
+
+The plugin will emit a single entry point called `entry.mjs`.
This plugin **will emit code** only when building an **SSR** site.
@@ -146,4 +149,25 @@ const pageMap = new Map([
```
It will also import the [`renderers`](#plugin-renderers) virtual module
-and the [`middleware`](#plugin-middleware) virtual module.
+and the [`manifest`](#plugin-manifest) virtual module.
+
+### Split mode
+
+The plugin will emit various entry points. Each route will be an entry point.
+
+Each entry point will contain the necessary code to **render one single route**.
+
+Each entry point will also import the [`renderers`](#plugin-renderers) virtual module
+and the [`manifest`](#plugin-manifest) virtual module.
+
+## `plugin-manifest`
+
+This plugin is responsible to create a file called `manifest.mjs`. In SSG, the file is saved
+in `config.outDir`, in SSR the file is saved in `config.build.server`.
+
+This file is important to do two things:
+
+- generate the pages during the SSG;
+- render the pages in SSR;
+
+The file contains all the information needed to Astro to accomplish the operations mentioned above.
diff --git a/packages/astro/src/core/build/plugins/index.ts b/packages/astro/src/core/build/plugins/index.ts
index decfefd04..19c952660 100644
--- a/packages/astro/src/core/build/plugins/index.ts
+++ b/packages/astro/src/core/build/plugins/index.ts
@@ -12,12 +12,14 @@ import { pluginPages } from './plugin-pages.js';
import { pluginPrerender } from './plugin-prerender.js';
import { pluginRenderers } from './plugin-renderers.js';
import { pluginSSR, pluginSSRSplit } from './plugin-ssr.js';
+import { pluginManifest } from './plugin-manifest.js';
export function registerAllPlugins({ internals, options, register }: AstroBuildPluginContainer) {
register(pluginComponentEntry(internals));
register(pluginAliasResolve(internals));
register(pluginAnalyzer(options, internals));
register(pluginInternals(internals));
+ register(pluginManifest(options, internals));
register(pluginRenderers(options));
register(pluginMiddleware(options, internals));
register(pluginPages(options, internals));
diff --git a/packages/astro/src/core/build/plugins/plugin-manifest.ts b/packages/astro/src/core/build/plugins/plugin-manifest.ts
new file mode 100644
index 000000000..2c2ceb7e1
--- /dev/null
+++ b/packages/astro/src/core/build/plugins/plugin-manifest.ts
@@ -0,0 +1,251 @@
+import { type BuildInternals, cssOrder, mergeInlineCss } from '../internal.js';
+import type { AstroBuildPlugin } from '../plugin';
+import { type Plugin as VitePlugin } from 'vite';
+import { runHookBuildSsr } from '../../../integrations/index.js';
+import { addRollupInput } from '../add-rollup-input.js';
+import glob from 'fast-glob';
+import { fileURLToPath } from 'node:url';
+import type { OutputChunk } from 'rollup';
+import { getOutFile, getOutFolder } from '../common.js';
+import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from '../../../vite-plugin-scripts/index.js';
+import { joinPaths, prependForwardSlash } from '../../path.js';
+import { serializeRouteData } from '../../routing/index.js';
+import type { SerializedRouteInfo, SerializedSSRManifest } from '../../app/types';
+import type { StaticBuildOptions } from '../types';
+
+const manifestReplace = '@@ASTRO_MANIFEST_REPLACE@@';
+const replaceExp = new RegExp(`['"](${manifestReplace})['"]`, 'g');
+
+export const SSR_MANIFEST_VIRTUAL_MODULE_ID = '@astrojs-manifest';
+export const RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID = '\0' + SSR_MANIFEST_VIRTUAL_MODULE_ID;
+
+function vitePluginManifest(options: StaticBuildOptions, internals: BuildInternals): VitePlugin {
+ return {
+ name: '@astro/plugin-build-manifest',
+ enforce: 'post',
+ options(opts) {
+ return addRollupInput(opts, [SSR_MANIFEST_VIRTUAL_MODULE_ID]);
+ },
+ resolveId(id) {
+ if (id === SSR_MANIFEST_VIRTUAL_MODULE_ID) {
+ return RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID;
+ }
+ },
+ augmentChunkHash(chunkInfo) {
+ if (chunkInfo.facadeModuleId === RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID) {
+ return Date.now().toString();
+ }
+ },
+ async load(id) {
+ if (id === RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID) {
+ const imports = [];
+ const contents = [];
+ const exports = [];
+ imports.push(
+ `import { deserializeManifest as _deserializeManifest } from 'astro/app'`,
+ `import { _privateSetManifestDontUseThis } from 'astro:ssr-manifest'`
+ );
+
+ contents.push(`
+const manifest = _deserializeManifest('${manifestReplace}');
+_privateSetManifestDontUseThis(manifest);
+`);
+
+ exports.push('export { manifest }');
+
+ return `${imports.join('\n')}${contents.join('\n')}${exports.join('\n')}`;
+ }
+ },
+
+ async generateBundle(_opts, bundle) {
+ for (const [chunkName, chunk] of Object.entries(bundle)) {
+ if (chunk.type === 'asset') {
+ continue;
+ }
+ if (chunk.modules[RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID]) {
+ internals.manifestEntryChunk = chunk;
+ delete bundle[chunkName];
+ }
+ if (chunkName.startsWith('manifest')) {
+ internals.manifestFileName = chunkName;
+ }
+ }
+ },
+ };
+}
+
+export function pluginManifest(
+ options: StaticBuildOptions,
+ internals: BuildInternals
+): AstroBuildPlugin {
+ return {
+ build: 'ssr',
+ hooks: {
+ 'build:before': () => {
+ return {
+ vitePlugin: vitePluginManifest(options, internals),
+ };
+ },
+
+ 'build:post': async ({ mutate }) => {
+ if (!internals.manifestEntryChunk) {
+ throw new Error(`Did not generate an entry chunk for SSR`);
+ }
+
+ const manifest = await createManifest(options, internals);
+ await runHookBuildSsr({
+ config: options.settings.config,
+ manifest,
+ logging: options.logging,
+ entryPoints: internals.entryPoints,
+ middlewareEntryPoint: internals.middlewareEntryPoint,
+ });
+ // TODO: use the manifest entry chunk instead
+ const code = injectManifest(manifest, internals.manifestEntryChunk);
+ mutate(internals.manifestEntryChunk, 'server', code);
+ },
+ },
+ };
+}
+
+export async function createManifest(
+ buildOpts: StaticBuildOptions,
+ internals: BuildInternals
+): Promise<SerializedSSRManifest> {
+ if (!internals.manifestEntryChunk) {
+ throw new Error(`Did not generate an entry chunk for SSR`);
+ }
+
+ // Add assets from the client build.
+ const clientStatics = new Set(
+ await glob('**/*', {
+ cwd: fileURLToPath(buildOpts.settings.config.build.client),
+ })
+ );
+ for (const file of clientStatics) {
+ internals.staticFiles.add(file);
+ }
+
+ const staticFiles = internals.staticFiles;
+ return buildManifest(buildOpts, internals, Array.from(staticFiles));
+}
+
+/**
+ * It injects the manifest in the given output rollup chunk. It returns the new emitted code
+ * @param buildOpts
+ * @param internals
+ * @param chunk
+ */
+export function injectManifest(manifest: SerializedSSRManifest, chunk: Readonly<OutputChunk>) {
+ const code = chunk.code;
+
+ return code.replace(replaceExp, () => {
+ return JSON.stringify(manifest);
+ });
+}
+
+function buildManifest(
+ opts: StaticBuildOptions,
+ internals: BuildInternals,
+ staticFiles: string[]
+): SerializedSSRManifest {
+ const { settings } = opts;
+
+ const routes: SerializedRouteInfo[] = [];
+ const entryModules = Object.fromEntries(internals.entrySpecifierToBundleMap.entries());
+ if (settings.scripts.some((script) => script.stage === 'page')) {
+ staticFiles.push(entryModules[PAGE_SCRIPT_ID]);
+ }
+
+ const prefixAssetPath = (pth: string) => {
+ if (settings.config.build.assetsPrefix) {
+ return joinPaths(settings.config.build.assetsPrefix, pth);
+ } else {
+ return prependForwardSlash(joinPaths(settings.config.base, pth));
+ }
+ };
+
+ for (const route of opts.manifest.routes) {
+ if (!route.prerender) continue;
+ if (!route.pathname) continue;
+
+ const outFolder = getOutFolder(opts.settings.config, route.pathname, route.type);
+ const outFile = getOutFile(opts.settings.config, outFolder, route.pathname, route.type);
+ const file = outFile.toString().replace(opts.settings.config.build.client.toString(), '');
+ routes.push({
+ file,
+ links: [],
+ scripts: [],
+ styles: [],
+ routeData: serializeRouteData(route, settings.config.trailingSlash),
+ });
+ staticFiles.push(file);
+ }
+
+ for (const route of opts.manifest.routes) {
+ const pageData = internals.pagesByComponent.get(route.component);
+ if (route.prerender || !pageData) continue;
+ const scripts: SerializedRouteInfo['scripts'] = [];
+ if (pageData.hoistedScript) {
+ const hoistedValue = pageData.hoistedScript.value;
+ const value = hoistedValue.endsWith('.js') ? prefixAssetPath(hoistedValue) : hoistedValue;
+ scripts.unshift(
+ Object.assign({}, pageData.hoistedScript, {
+ value,
+ })
+ );
+ }
+ if (settings.scripts.some((script) => script.stage === 'page')) {
+ const src = entryModules[PAGE_SCRIPT_ID];
+
+ scripts.push({
+ type: 'external',
+ value: prefixAssetPath(src),
+ });
+ }
+
+ // may be used in the future for handling rel=modulepreload, rel=icon, rel=manifest etc.
+ const links: [] = [];
+
+ const styles = pageData.styles
+ .sort(cssOrder)
+ .map(({ sheet }) => sheet)
+ .map((s) => (s.type === 'external' ? { ...s, src: prefixAssetPath(s.src) } : s))
+ .reduce(mergeInlineCss, []);
+
+ routes.push({
+ file: '',
+ links,
+ scripts: [
+ ...scripts,
+ ...settings.scripts
+ .filter((script) => script.stage === 'head-inline')
+ .map(({ stage, content }) => ({ stage, children: content })),
+ ],
+ styles,
+ routeData: serializeRouteData(route, settings.config.trailingSlash),
+ });
+ }
+
+ // HACK! Patch this special one.
+ if (!(BEFORE_HYDRATION_SCRIPT_ID in entryModules)) {
+ // Set this to an empty string so that the runtime knows not to try and load this.
+ entryModules[BEFORE_HYDRATION_SCRIPT_ID] = '';
+ }
+
+ const ssrManifest: SerializedSSRManifest = {
+ adapterName: opts.settings.adapter?.name ?? '',
+ routes,
+ site: settings.config.site,
+ base: settings.config.base,
+ compressHTML: settings.config.compressHTML,
+ assetsPrefix: settings.config.build.assetsPrefix,
+ componentMetadata: Array.from(internals.componentMetadata),
+ renderers: [],
+ clientDirectives: Array.from(settings.clientDirectives),
+ entryModules,
+ assets: staticFiles.map(prefixAssetPath),
+ };
+
+ return ssrManifest;
+}
diff --git a/packages/astro/src/core/build/plugins/plugin-pages.ts b/packages/astro/src/core/build/plugins/plugin-pages.ts
index 2ee438a6a..ff63acd74 100644
--- a/packages/astro/src/core/build/plugins/plugin-pages.ts
+++ b/packages/astro/src/core/build/plugins/plugin-pages.ts
@@ -8,6 +8,7 @@ import type { StaticBuildOptions } from '../types';
import { MIDDLEWARE_MODULE_ID } from './plugin-middleware.js';
import { RENDERERS_MODULE_ID } from './plugin-renderers.js';
import { ASTRO_PAGE_EXTENSION_POST_PATTERN, getPathFromVirtualModulePageName } from './util.js';
+import type { AstroSettings } from '../../../@types/astro';
export const ASTRO_PAGE_MODULE_ID = '@astro-page:';
export const ASTRO_PAGE_RESOLVED_MODULE_ID = '\0' + ASTRO_PAGE_MODULE_ID;
@@ -74,7 +75,7 @@ function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): V
exports.push(`export { renderers };`);
// The middleware should not be imported by the pages
- if (!opts.settings.config.build.excludeMiddleware) {
+ if (shouldBundleMiddleware(opts.settings)) {
const middlewareModule = await this.resolve(MIDDLEWARE_MODULE_ID);
if (middlewareModule) {
imports.push(`import { onRequest } from "${middlewareModule.id}";`);
@@ -90,6 +91,17 @@ function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): V
};
}
+export function shouldBundleMiddleware(settings: AstroSettings) {
+ // TODO: Remove in Astro 4.0
+ if (settings.config.build.excludeMiddleware === true) {
+ return false;
+ }
+ if (settings.adapter?.adapterFeatures?.edgeMiddleware === true) {
+ return false;
+ }
+ return true;
+}
+
export function pluginPages(opts: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin {
return {
build: 'ssr',
diff --git a/packages/astro/src/core/build/plugins/plugin-prerender.ts b/packages/astro/src/core/build/plugins/plugin-prerender.ts
index a0d6a9c7b..402264c6e 100644
--- a/packages/astro/src/core/build/plugins/plugin-prerender.ts
+++ b/packages/astro/src/core/build/plugins/plugin-prerender.ts
@@ -14,7 +14,7 @@ function vitePluginPrerender(opts: StaticBuildOptions, internals: BuildInternals
extendManualChunks(outputOptions, {
after(id, meta) {
// Split the Astro runtime into a separate chunk for readability
- if (id.includes('astro/dist')) {
+ if (id.includes('astro/dist/runtime')) {
return 'astro';
}
const pageInfo = internals.pagesByViteID.get(id);
diff --git a/packages/astro/src/core/build/plugins/plugin-renderers.ts b/packages/astro/src/core/build/plugins/plugin-renderers.ts
index 912df4241..f0cdf8983 100644
--- a/packages/astro/src/core/build/plugins/plugin-renderers.ts
+++ b/packages/astro/src/core/build/plugins/plugin-renderers.ts
@@ -38,6 +38,8 @@ export function vitePluginRenderers(opts: StaticBuildOptions): VitePlugin {
exports.push(`export const renderers = [${rendererItems}];`);
return `${imports.join('\n')}\n${exports.join('\n')}`;
+ } else {
+ return `export const renderers = [];`;
}
}
},
diff --git a/packages/astro/src/core/build/plugins/plugin-ssr.ts b/packages/astro/src/core/build/plugins/plugin-ssr.ts
index 514fe2409..098b9dee8 100644
--- a/packages/astro/src/core/build/plugins/plugin-ssr.ts
+++ b/packages/astro/src/core/build/plugins/plugin-ssr.ts
@@ -1,28 +1,21 @@
-import glob from 'fast-glob';
import { join } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import type { Plugin as VitePlugin } from 'vite';
import type { AstroAdapter, AstroConfig } from '../../../@types/astro';
-import { runHookBuildSsr } from '../../../integrations/index.js';
+import { isFunctionPerRouteEnabled } from '../../../integrations/index.js';
import { isServerLikeOutput } from '../../../prerender/utils.js';
-import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from '../../../vite-plugin-scripts/index.js';
-import type { SerializedRouteInfo, SerializedSSRManifest } from '../../app/types';
-import { joinPaths, prependForwardSlash } from '../../path.js';
import { routeIsRedirect } from '../../redirects/index.js';
-import { serializeRouteData } from '../../routing/index.js';
import { addRollupInput } from '../add-rollup-input.js';
-import { getOutFile, getOutFolder } from '../common.js';
-import { cssOrder, mergeInlineCss, type BuildInternals } from '../internal.js';
+import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin';
-import type { OutputChunk, StaticBuildOptions } from '../types';
+import type { StaticBuildOptions } from '../types';
import { ASTRO_PAGE_MODULE_ID } from './plugin-pages.js';
import { RENDERERS_MODULE_ID } from './plugin-renderers.js';
import { getPathFromVirtualModulePageName, getVirtualModulePageNameFromPath } from './util.js';
+import { SSR_MANIFEST_VIRTUAL_MODULE_ID } from './plugin-manifest.js';
export const SSR_VIRTUAL_MODULE_ID = '@astrojs-ssr-virtual-entry';
-const RESOLVED_SSR_VIRTUAL_MODULE_ID = '\0' + SSR_VIRTUAL_MODULE_ID;
-const manifestReplace = '@@ASTRO_MANIFEST_REPLACE@@';
-const replaceExp = new RegExp(`['"](${manifestReplace})['"]`, 'g');
+export const RESOLVED_SSR_VIRTUAL_MODULE_ID = '\0' + SSR_VIRTUAL_MODULE_ID;
function vitePluginSSR(
internals: BuildInternals,
@@ -85,13 +78,12 @@ function vitePluginSSR(
}
}
- for (const [chunkName, chunk] of Object.entries(bundle)) {
+ for (const [, chunk] of Object.entries(bundle)) {
if (chunk.type === 'asset') {
continue;
}
if (chunk.modules[RESOLVED_SSR_VIRTUAL_MODULE_ID]) {
internals.ssrEntryChunk = chunk;
- delete bundle[chunkName];
}
}
},
@@ -103,12 +95,16 @@ export function pluginSSR(
internals: BuildInternals
): AstroBuildPlugin {
const ssr = isServerLikeOutput(options.settings.config);
+ const functionPerRouteEnabled = isFunctionPerRouteEnabled(options.settings.adapter);
return {
build: 'ssr',
hooks: {
'build:before': () => {
let vitePlugin =
- ssr && !options.settings.config.build.split
+ ssr &&
+ // TODO: Remove in Astro 4.0
+ options.settings.config.build.split === false &&
+ functionPerRouteEnabled === false
? vitePluginSSR(internals, options.settings.adapter!, options)
: undefined;
@@ -117,12 +113,12 @@ export function pluginSSR(
vitePlugin,
};
},
- 'build:post': async ({ mutate }) => {
+ 'build:post': async () => {
if (!ssr) {
return;
}
- if (options.settings.config.build.split) {
+ if (options.settings.config.build.split || functionPerRouteEnabled) {
return;
}
@@ -131,17 +127,6 @@ export function pluginSSR(
}
// Mutate the filename
internals.ssrEntryChunk.fileName = options.settings.config.build.serverEntry;
-
- const manifest = await createManifest(options, internals);
- await runHookBuildSsr({
- config: options.settings.config,
- manifest,
- logging: options.logging,
- entryPoints: internals.entryPoints,
- middlewareEntryPoint: internals.middlewareEntryPoint,
- });
- const code = injectManifest(manifest, internals.ssrEntryChunk);
- mutate(internals.ssrEntryChunk, 'server', code);
},
},
};
@@ -155,11 +140,12 @@ function vitePluginSSRSplit(
adapter: AstroAdapter,
options: StaticBuildOptions
): VitePlugin {
+ const functionPerRouteEnabled = isFunctionPerRouteEnabled(options.settings.adapter);
return {
name: '@astrojs/vite-plugin-astro-ssr-split',
enforce: 'post',
options(opts) {
- if (options.settings.config.build.split) {
+ if (options.settings.config.build.split || functionPerRouteEnabled) {
const inputs = new Set<string>();
for (const path of Object.keys(options.allPages)) {
@@ -204,21 +190,16 @@ function vitePluginSSRSplit(
}
}
- for (const [chunkName, chunk] of Object.entries(bundle)) {
+ for (const [, chunk] of Object.entries(bundle)) {
if (chunk.type === 'asset') {
continue;
}
- let shouldDeleteBundle = false;
for (const moduleKey of Object.keys(chunk.modules)) {
if (moduleKey.startsWith(RESOLVED_SPLIT_MODULE_ID)) {
internals.ssrSplitEntryChunks.set(moduleKey, chunk);
storeEntryPoint(moduleKey, options, internals, chunk.fileName);
- shouldDeleteBundle = true;
}
}
- if (shouldDeleteBundle) {
- delete bundle[chunkName];
- }
}
},
};
@@ -229,12 +210,14 @@ export function pluginSSRSplit(
internals: BuildInternals
): AstroBuildPlugin {
const ssr = isServerLikeOutput(options.settings.config);
+ const functionPerRouteEnabled = isFunctionPerRouteEnabled(options.settings.adapter);
+
return {
build: 'ssr',
hooks: {
'build:before': () => {
let vitePlugin =
- ssr && options.settings.config.build.split
+ ssr && (options.settings.config.build.split || functionPerRouteEnabled)
? vitePluginSSRSplit(internals, options.settings.adapter!, options)
: undefined;
@@ -243,31 +226,6 @@ export function pluginSSRSplit(
vitePlugin,
};
},
- 'build:post': async ({ mutate }) => {
- if (!ssr) {
- return;
- }
- if (!options.settings.config.build.split) {
- return;
- }
-
- if (internals.ssrSplitEntryChunks.size === 0) {
- throw new Error(`Did not generate an entry chunk for SSR serverless`);
- }
-
- const manifest = await createManifest(options, internals);
- await runHookBuildSsr({
- config: options.settings.config,
- manifest,
- logging: options.logging,
- entryPoints: internals.entryPoints,
- middlewareEntryPoint: internals.middlewareEntryPoint,
- });
- for (const [, chunk] of internals.ssrSplitEntryChunks) {
- const code = injectManifest(manifest, chunk);
- mutate(chunk, 'server', code);
- }
- },
},
};
}
@@ -276,7 +234,7 @@ function generateSSRCode(config: AstroConfig, adapter: AstroAdapter) {
const imports: string[] = [];
const contents: string[] = [];
let pageMap;
- if (config.build.split) {
+ if (config.build.split || isFunctionPerRouteEnabled(adapter)) {
pageMap = 'pageModule';
} else {
pageMap = 'pageMap';
@@ -284,13 +242,11 @@ function generateSSRCode(config: AstroConfig, adapter: AstroAdapter) {
contents.push(`import * as adapter from '${adapter.serverEntrypoint}';
import { renderers } from '${RENDERERS_MODULE_ID}';
-import { deserializeManifest as _deserializeManifest } from 'astro/app';
-import { _privateSetManifestDontUseThis } from 'astro:ssr-manifest';
-const _manifest = Object.assign(_deserializeManifest('${manifestReplace}'), {
+import { manifest as defaultManifest} from '${SSR_MANIFEST_VIRTUAL_MODULE_ID}';
+const _manifest = Object.assign(defaultManifest, {
${pageMap},
renderers,
});
-_privateSetManifestDontUseThis(_manifest);
const _args = ${adapter.args ? JSON.stringify(adapter.args) : 'undefined'};
${
@@ -320,48 +276,6 @@ if(_start in adapter) {
}
/**
- * It injects the manifest in the given output rollup chunk. It returns the new emitted code
- * @param buildOpts
- * @param internals
- * @param chunk
- */
-export function injectManifest(manifest: SerializedSSRManifest, chunk: Readonly<OutputChunk>) {
- const code = chunk.code;
-
- return code.replace(replaceExp, () => {
- return JSON.stringify(manifest);
- });
-}
-
-export async function createManifest(
- buildOpts: StaticBuildOptions,
- internals: BuildInternals
-): Promise<SerializedSSRManifest> {
- if (buildOpts.settings.config.build.split) {
- if (internals.ssrSplitEntryChunks.size === 0) {
- throw new Error(`Did not generate an entry chunk for SSR in serverless mode`);
- }
- } else {
- if (!internals.ssrEntryChunk) {
- throw new Error(`Did not generate an entry chunk for SSR`);
- }
- }
-
- // Add assets from the client build.
- const clientStatics = new Set(
- await glob('**/*', {
- cwd: fileURLToPath(buildOpts.settings.config.build.client),
- })
- );
- for (const file of clientStatics) {
- internals.staticFiles.add(file);
- }
-
- const staticFiles = internals.staticFiles;
- return buildManifest(buildOpts, internals, Array.from(staticFiles));
-}
-
-/**
* Because we delete the bundle from rollup at the end of this function,
* we can't use `writeBundle` hook to get the final file name of the entry point written on disk.
* We use this hook instead.
@@ -382,110 +296,3 @@ function storeEntryPoint(
}
}
}
-
-function buildManifest(
- opts: StaticBuildOptions,
- internals: BuildInternals,
- staticFiles: string[]
-): SerializedSSRManifest {
- const { settings } = opts;
-
- const routes: SerializedRouteInfo[] = [];
- const entryModules = Object.fromEntries(internals.entrySpecifierToBundleMap.entries());
- if (settings.scripts.some((script) => script.stage === 'page')) {
- staticFiles.push(entryModules[PAGE_SCRIPT_ID]);
- }
-
- const prefixAssetPath = (pth: string) => {
- if (settings.config.build.assetsPrefix) {
- return joinPaths(settings.config.build.assetsPrefix, pth);
- } else {
- return prependForwardSlash(joinPaths(settings.config.base, pth));
- }
- };
-
- for (const route of opts.manifest.routes) {
- if (!route.prerender) continue;
- if (!route.pathname) continue;
-
- const outFolder = getOutFolder(opts.settings.config, route.pathname, route.type);
- const outFile = getOutFile(opts.settings.config, outFolder, route.pathname, route.type);
- const file = outFile.toString().replace(opts.settings.config.build.client.toString(), '');
- routes.push({
- file,
- links: [],
- scripts: [],
- styles: [],
- routeData: serializeRouteData(route, settings.config.trailingSlash),
- });
- staticFiles.push(file);
- }
-
- for (const route of opts.manifest.routes) {
- const pageData = internals.pagesByComponent.get(route.component);
- if (route.prerender || !pageData) continue;
- const scripts: SerializedRouteInfo['scripts'] = [];
- if (pageData.hoistedScript) {
- const hoistedValue = pageData.hoistedScript.value;
- const value = hoistedValue.endsWith('.js') ? prefixAssetPath(hoistedValue) : hoistedValue;
- scripts.unshift(
- Object.assign({}, pageData.hoistedScript, {
- value,
- })
- );
- }
- if (settings.scripts.some((script) => script.stage === 'page')) {
- const src = entryModules[PAGE_SCRIPT_ID];
-
- scripts.push({
- type: 'external',
- value: prefixAssetPath(src),
- });
- }
-
- // may be used in the future for handling rel=modulepreload, rel=icon, rel=manifest etc.
- const links: [] = [];
-
- const styles = pageData.styles
- .sort(cssOrder)
- .map(({ sheet }) => sheet)
- .map((s) => (s.type === 'external' ? { ...s, src: prefixAssetPath(s.src) } : s))
- .reduce(mergeInlineCss, []);
-
- routes.push({
- file: '',
- links,
- scripts: [
- ...scripts,
- ...settings.scripts
- .filter((script) => script.stage === 'head-inline')
- .map(({ stage, content }) => ({ stage, children: content })),
- ],
- styles,
- routeData: serializeRouteData(route, settings.config.trailingSlash),
- });
- }
-
- // HACK! Patch this special one.
- if (!(BEFORE_HYDRATION_SCRIPT_ID in entryModules)) {
- // Set this to an empty string so that the runtime knows not to try and load this.
- entryModules[BEFORE_HYDRATION_SCRIPT_ID] = '';
- }
-
- const ssrManifest: SerializedSSRManifest = {
- adapterName: opts.settings.adapter!.name,
- routes,
- site: settings.config.site,
- base: settings.config.base,
- compressHTML: settings.config.compressHTML,
- assetsPrefix: settings.config.build.assetsPrefix,
- markdown: settings.config.markdown,
- componentMetadata: Array.from(internals.componentMetadata),
- renderers: [],
- clientDirectives: Array.from(settings.clientDirectives),
- entryModules,
- assets: staticFiles.map(prefixAssetPath),
- };
-
- return ssrManifest;
-}
diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts
index fb16b433d..a1c7c3e56 100644
--- a/packages/astro/src/core/build/static-build.ts
+++ b/packages/astro/src/core/build/static-build.ts
@@ -16,7 +16,7 @@ import { emptyDir, removeEmptyDirs } from '../../core/fs/index.js';
import { appendForwardSlash, prependForwardSlash } from '../../core/path.js';
import { isModeServerWithNoAdapter } from '../../core/util.js';
import { runHookBuildSetup } from '../../integrations/index.js';
-import { isServerLikeOutput } from '../../prerender/utils.js';
+import { getOutputDirectory, isServerLikeOutput } from '../../prerender/utils.js';
import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
import { info } from '../logger/core.js';
@@ -28,10 +28,11 @@ import { createPluginContainer, type AstroBuildPluginContainer } from './plugin.
import { registerAllPlugins } from './plugins/index.js';
import { ASTRO_PAGE_RESOLVED_MODULE_ID } from './plugins/plugin-pages.js';
import { RESOLVED_RENDERERS_MODULE_ID } from './plugins/plugin-renderers.js';
-import { RESOLVED_SPLIT_MODULE_ID, SSR_VIRTUAL_MODULE_ID } from './plugins/plugin-ssr.js';
+import { RESOLVED_SPLIT_MODULE_ID, RESOLVED_SSR_VIRTUAL_MODULE_ID } from './plugins/plugin-ssr.js';
import { ASTRO_PAGE_EXTENSION_POST_PATTERN } from './plugins/util.js';
import type { PageBuildData, StaticBuildOptions } from './types';
import { getTimeStat } from './util.js';
+import { RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID } from './plugins/plugin-manifest.js';
export async function viteBuild(opts: StaticBuildOptions) {
const { allPages, settings } = opts;
@@ -147,7 +148,7 @@ async function ssrBuild(
) {
const { allPages, settings, viteConfig } = opts;
const ssr = isServerLikeOutput(settings.config);
- const out = ssr ? settings.config.build.server : getOutDirWithinCwd(settings.config.outDir);
+ const out = getOutputDirectory(settings.config);
const routes = Object.values(allPages).map((pd) => pd.route);
const { lastVitePlugins, vitePlugins } = container.runBeforeHook('ssr', input);
@@ -184,10 +185,12 @@ async function ssrBuild(
);
} else if (chunkInfo.facadeModuleId?.startsWith(RESOLVED_SPLIT_MODULE_ID)) {
return makeSplitEntryPointFileName(chunkInfo.facadeModuleId, routes);
- } else if (chunkInfo.facadeModuleId === SSR_VIRTUAL_MODULE_ID) {
+ } else if (chunkInfo.facadeModuleId === RESOLVED_SSR_VIRTUAL_MODULE_ID) {
return opts.settings.config.build.serverEntry;
} else if (chunkInfo.facadeModuleId === RESOLVED_RENDERERS_MODULE_ID) {
return 'renderers.mjs';
+ } else if (chunkInfo.facadeModuleId === RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID) {
+ return 'manifest.[hash].mjs';
} else {
return '[name].mjs';
}
diff --git a/packages/astro/src/core/compile/compile.ts b/packages/astro/src/core/compile/compile.ts
index f266c0b16..bd069611d 100644
--- a/packages/astro/src/core/compile/compile.ts
+++ b/packages/astro/src/core/compile/compile.ts
@@ -41,7 +41,7 @@ export async function compile({
filename,
normalizedFilename: normalizeFilename(filename, astroConfig.root),
sourcemap: 'both',
- internalURL: 'astro/server/index.js',
+ internalURL: 'astro/compiler-runtime',
astroGlobalArgs: JSON.stringify(astroConfig.site),
scopedStyleStrategy: astroConfig.scopedStyleStrategy,
resultScopedSlot: true,
diff --git a/packages/astro/src/core/config/config.ts b/packages/astro/src/core/config/config.ts
index 29b0bb23a..16c7d28bc 100644
--- a/packages/astro/src/core/config/config.ts
+++ b/packages/astro/src/core/config/config.ts
@@ -20,73 +20,12 @@ import { mergeConfig } from './merge.js';
import { createRelativeSchema } from './schema.js';
import { loadConfigWithVite } from './vite-load.js';
-const LEGACY_ASTRO_CONFIG_KEYS = new Set([
- 'projectRoot',
- 'src',
- 'pages',
- 'public',
- 'dist',
- 'styleOptions',
- 'markdownOptions',
- 'buildOptions',
- 'devOptions',
-]);
-
/** Turn raw config values into normalized values */
export async function validateConfig(
userConfig: any,
root: string,
cmd: string
): Promise<AstroConfig> {
- // Manual deprecation checks
- /* eslint-disable no-console */
- if (userConfig.hasOwnProperty('renderers')) {
- console.error('Astro "renderers" are now "integrations"!');
- console.error('Update your configuration and install new dependencies:');
- try {
- const rendererKeywords = userConfig.renderers.map((r: string) =>
- r.replace('@astrojs/renderer-', '')
- );
- const rendererImports = rendererKeywords
- .map((r: string) => ` import ${r} from '@astrojs/${r === 'solid' ? 'solid-js' : r}';`)
- .join('\n');
- const rendererIntegrations = rendererKeywords.map((r: string) => ` ${r}(),`).join('\n');
- console.error('');
- console.error(colors.dim(' // astro.config.js'));
- if (rendererImports.length > 0) {
- console.error(colors.green(rendererImports));
- }
- console.error('');
- console.error(colors.dim(' // ...'));
- if (rendererIntegrations.length > 0) {
- console.error(colors.green(' integrations: ['));
- console.error(colors.green(rendererIntegrations));
- console.error(colors.green(' ],'));
- } else {
- console.error(colors.green(' integrations: [],'));
- }
- console.error('');
- } catch (err) {
- // We tried, better to just exit.
- }
- process.exit(1);
- }
-
- let legacyConfigKey: string | undefined;
- for (const key of Object.keys(userConfig)) {
- if (LEGACY_ASTRO_CONFIG_KEYS.has(key)) {
- legacyConfigKey = key;
- break;
- }
- }
- if (legacyConfigKey) {
- throw new AstroError({
- ...AstroErrorData.ConfigLegacyKey,
- message: AstroErrorData.ConfigLegacyKey.message(legacyConfigKey),
- });
- }
- /* eslint-enable no-console */
-
const AstroConfigRelativeSchema = createRelativeSchema(cmd, root);
// First-Pass Validation
@@ -124,8 +63,6 @@ export function resolveFlags(flags: Partial<Flags>): CLIFlags {
host:
typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined,
drafts: typeof flags.drafts === 'boolean' ? flags.drafts : undefined,
- experimentalAssets:
- typeof flags.experimentalAssets === 'boolean' ? flags.experimentalAssets : undefined,
};
}
diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts
index c051903d3..0717e1a81 100644
--- a/packages/astro/src/core/config/schema.ts
+++ b/packages/astro/src/core/config/schema.ts
@@ -8,7 +8,7 @@ import path from 'node:path';
import { pathToFileURL } from 'node:url';
import { BUNDLED_THEMES } from 'shiki';
import { z } from 'zod';
-import { appendForwardSlash, prependForwardSlash, trimSlashes } from '../path.js';
+import { appendForwardSlash, prependForwardSlash, removeTrailingForwardSlash } from '../path.js';
const ASTRO_CONFIG_DEFAULTS = {
root: '.',
@@ -25,14 +25,14 @@ const ASTRO_CONFIG_DEFAULTS = {
assets: '_astro',
serverEntry: 'entry.mjs',
redirects: true,
- inlineStylesheets: 'never',
+ inlineStylesheets: 'auto',
split: false,
excludeMiddleware: false,
},
- compressHTML: false,
+ compressHTML: true,
server: {
host: false,
- port: 3000,
+ port: 4321,
open: false,
},
integrations: [],
@@ -44,7 +44,6 @@ const ASTRO_CONFIG_DEFAULTS = {
legacy: {},
redirects: {},
experimental: {
- assets: false,
viewTransitions: false,
optimizeHoistedScript: false,
},
@@ -88,9 +87,9 @@ export const AstroConfigSchema = z.object({
.optional()
.default('static'),
scopedStyleStrategy: z
- .union([z.literal('where'), z.literal('class')])
+ .union([z.literal('where'), z.literal('class'), z.literal('attribute')])
.optional()
- .default('where'),
+ .default('attribute'),
adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(),
integrations: z.preprocess(
// preprocess
@@ -125,7 +124,15 @@ export const AstroConfigSchema = z.object({
.optional()
.default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets),
+ /**
+ * @deprecated
+ * Use the adapter feature instead
+ */
split: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.build.split),
+ /**
+ * @deprecated
+ * Use the adapter feature instead
+ */
excludeMiddleware: z
.boolean()
.optional()
@@ -208,9 +215,7 @@ export const AstroConfigSchema = z.object({
.default([]),
})
.default({
- service: { entrypoint: 'astro/assets/services/squoosh', config: {} },
- domains: [],
- remotePatterns: [],
+ service: { entrypoint: 'astro/assets/services/sharp', config: {} },
}),
markdown: z
.object({
@@ -259,7 +264,6 @@ export const AstroConfigSchema = z.object({
.default(ASTRO_CONFIG_DEFAULTS.vite),
experimental: z
.object({
- assets: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.assets),
viewTransitions: z
.boolean()
.optional()
@@ -376,38 +380,29 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: string) {
.optional()
.default({})
),
- })
- .transform((config) => {
- // If the user changed outDir but not build.server, build.config, adjust so those
- // are relative to the outDir, as is the expected default.
- if (
- !config.build.server.toString().startsWith(config.outDir.toString()) &&
- config.build.server.toString().endsWith('dist/server/')
- ) {
- config.build.server = new URL('./dist/server/', config.outDir);
- }
- if (
- !config.build.client.toString().startsWith(config.outDir.toString()) &&
- config.build.client.toString().endsWith('dist/client/')
- ) {
- config.build.client = new URL('./dist/client/', config.outDir);
- }
- const trimmedBase = trimSlashes(config.base);
-
- // If there is no base but there is a base for site config, warn.
- const sitePathname = config.site && new URL(config.site).pathname;
- if (!trimmedBase.length && sitePathname && sitePathname !== '/') {
- config.base = sitePathname;
- /* eslint-disable no-console */
- console.warn(`The site configuration value includes a pathname of ${sitePathname} but there is no base configuration.
-
-A future version of Astro will stop using the site pathname when producing <link> and <script> tags. Set your site's base with the base configuration.`);
- }
+ }).transform((config) => {
+ // If the user changed outDir but not build.server, build.config, adjust so those
+ // are relative to the outDir, as is the expected default.
+ if (
+ !config.build.server.toString().startsWith(config.outDir.toString()) &&
+ config.build.server.toString().endsWith('dist/server/')
+ ) {
+ config.build.server = new URL('./dist/server/', config.outDir);
+ }
+ if (
+ !config.build.client.toString().startsWith(config.outDir.toString()) &&
+ config.build.client.toString().endsWith('dist/client/')
+ ) {
+ config.build.client = new URL('./dist/client/', config.outDir);
+ }
- if (trimmedBase.length && config.trailingSlash === 'never') {
- config.base = prependForwardSlash(trimmedBase);
- } else {
- config.base = prependForwardSlash(appendForwardSlash(trimmedBase));
+ // Handle `base` trailing slash based on `trailingSlash` config
+ if (config.trailingSlash === 'never') {
+ config.base = prependForwardSlash(removeTrailingForwardSlash(config.base));
+ } else if (config.trailingSlash === 'always') {
+ config.base = prependForwardSlash(appendForwardSlash(config.base));
+ } else {
+ config.base = prependForwardSlash(config.base);
}
return config;
diff --git a/packages/astro/src/core/config/settings.ts b/packages/astro/src/core/config/settings.ts
index c0274f602..30ca7c4c2 100644
--- a/packages/astro/src/core/config/settings.ts
+++ b/packages/astro/src/core/config/settings.ts
@@ -3,7 +3,6 @@ import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import type { AstroConfig, AstroSettings } from '../../@types/astro';
import { getContentPaths } from '../../content/index.js';
-import jsxRenderer from '../../jsx/renderer.js';
import { markdownContentEntryType } from '../../vite-plugin-markdown/content-entry-type.js';
import { getDefaultClientDirectives } from '../client-directive/index.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
@@ -18,7 +17,6 @@ export function createBaseSettings(config: AstroConfig): AstroSettings {
config,
tsConfig: undefined,
tsConfigPath: undefined,
-
adapter: undefined,
injectedRoutes: [],
resolvedInjectedRoutes: [],
@@ -96,7 +94,7 @@ export function createBaseSettings(config: AstroConfig): AstroSettings {
},
},
],
- renderers: [jsxRenderer],
+ renderers: [],
scripts: [],
clientDirectives: getDefaultClientDirectives(),
watchFiles: [],
diff --git a/packages/astro/src/core/cookies/cookies.ts b/packages/astro/src/core/cookies/cookies.ts
index 013357f32..604f30e63 100644
--- a/packages/astro/src/core/cookies/cookies.ts
+++ b/packages/astro/src/core/cookies/cookies.ts
@@ -15,14 +15,14 @@ interface AstroCookieSetOptions {
type AstroCookieDeleteOptions = Pick<AstroCookieSetOptions, 'domain' | 'path'>;
interface AstroCookieInterface {
- value: string | undefined;
+ value: string;
json(): Record<string, any>;
number(): number;
boolean(): boolean;
}
interface AstroCookiesInterface {
- get(key: string): AstroCookieInterface;
+ get(key: string): AstroCookieInterface | undefined;
has(key: string): boolean;
set(
key: string,
@@ -37,7 +37,7 @@ const DELETED_VALUE = 'deleted';
const responseSentSymbol = Symbol.for('astro.responseSent');
class AstroCookie implements AstroCookieInterface {
- constructor(public value: string | undefined) {}
+ constructor(public value: string) {}
json() {
if (this.value === undefined) {
throw new Error(`Cannot convert undefined to an object.`);
@@ -97,20 +97,22 @@ class AstroCookies implements AstroCookiesInterface {
* @param key The cookie to get.
* @returns An object containing the cookie value as well as convenience methods for converting its value.
*/
- get(key: string): AstroCookie {
+ get(key: string): AstroCookie | undefined {
// Check for outgoing Set-Cookie values first
if (this.#outgoing?.has(key)) {
let [serializedValue, , isSetValue] = this.#outgoing.get(key)!;
if (isSetValue) {
return new AstroCookie(serializedValue);
} else {
- return new AstroCookie(undefined);
+ return undefined;
}
}
const values = this.#ensureParsed();
- const value = values[key];
- return new AstroCookie(value);
+ if (key in values) {
+ const value = values[key];
+ return new AstroCookie(value);
+ }
}
/**
diff --git a/packages/astro/src/core/cookies/index.ts b/packages/astro/src/core/cookies/index.ts
index 1b0c6b7a0..f3c7b6d61 100644
--- a/packages/astro/src/core/cookies/index.ts
+++ b/packages/astro/src/core/cookies/index.ts
@@ -1,2 +1,2 @@
export { AstroCookies } from './cookies.js';
-export { attachToResponse, getSetCookiesFromResponse } from './response.js';
+export { attachCookiesToResponse, getSetCookiesFromResponse } from './response.js';
diff --git a/packages/astro/src/core/cookies/response.ts b/packages/astro/src/core/cookies/response.ts
index 18d72ab1c..668bd265f 100644
--- a/packages/astro/src/core/cookies/response.ts
+++ b/packages/astro/src/core/cookies/response.ts
@@ -2,7 +2,7 @@ import type { AstroCookies } from './cookies';
const astroCookiesSymbol = Symbol.for('astro.cookies');
-export function attachToResponse(response: Response, cookies: AstroCookies) {
+export function attachCookiesToResponse(response: Response, cookies: AstroCookies) {
Reflect.set(response, astroCookiesSymbol, cookies);
}
diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts
index 0fb64ef69..f4938d2eb 100644
--- a/packages/astro/src/core/create-vite.ts
+++ b/packages/astro/src/core/create-vite.ts
@@ -19,9 +19,9 @@ import configAliasVitePlugin from '../vite-plugin-config-alias/index.js';
import envVitePlugin from '../vite-plugin-env/index.js';
import astroHeadPlugin from '../vite-plugin-head/index.js';
import htmlVitePlugin from '../vite-plugin-html/index.js';
+import mdxVitePlugin from '../vite-plugin-mdx/index.js';
import { astroInjectEnvTsPlugin } from '../vite-plugin-inject-env-ts/index.js';
import astroIntegrationsContainerPlugin from '../vite-plugin-integrations-container/index.js';
-import jsxVitePlugin from '../vite-plugin-jsx/index.js';
import astroLoadFallbackPlugin from '../vite-plugin-load-fallback/index.js';
import markdownVitePlugin from '../vite-plugin-markdown/index.js';
import astroScannerPlugin from '../vite-plugin-scanner/index.js';
@@ -121,7 +121,7 @@ export async function createVite(
envVitePlugin({ settings }),
markdownVitePlugin({ settings, logging }),
htmlVitePlugin(),
- jsxVitePlugin({ settings, logging }),
+ mdxVitePlugin({ settings, logging }),
astroPostprocessVitePlugin(),
astroIntegrationsContainerPlugin({ settings, logging }),
astroScriptsPageSSRPlugin({ settings }),
@@ -132,7 +132,7 @@ export async function createVite(
astroContentImportPlugin({ fs, settings }),
astroContentAssetPropagationPlugin({ mode, settings }),
vitePluginSSRManifest(),
- settings.config.experimental.assets ? [astroAssetsPlugin({ settings, logging, mode })] : [],
+ astroAssetsPlugin({ settings, logging, mode }),
astroTransitions({ config: settings.config }),
],
publicDir: fileURLToPath(settings.config.publicDir),
@@ -170,6 +170,14 @@ export async function createVite(
find: /^astro$/,
replacement: fileURLToPath(new URL('../@types/astro', import.meta.url)),
},
+ {
+ find: 'astro:middleware',
+ replacement: 'astro/middleware/namespace',
+ },
+ {
+ find: 'astro:components',
+ replacement: 'astro/components',
+ },
],
conditions: ['astro'],
// Astro imports in third-party packages should use the same version as root
@@ -234,37 +242,12 @@ export async function createVite(
result = vite.mergeConfig(result, settings.config.vite || {});
}
result = vite.mergeConfig(result, commandConfig);
- if (result.plugins) {
- sortPlugins(result.plugins);
- }
result.customLogger = vite.createLogger(result.logLevel ?? 'warn');
return result;
}
-function isVitePlugin(plugin: vite.PluginOption): plugin is vite.Plugin {
- return Boolean(plugin?.hasOwnProperty('name'));
-}
-
-function findPluginIndexByName(pluginOptions: vite.PluginOption[], name: string): number {
- return pluginOptions.findIndex(function (pluginOption) {
- // Use isVitePlugin to ignore nulls, booleans, promises, and arrays
- // CAUTION: could be a problem if a plugin we're searching for becomes async!
- return isVitePlugin(pluginOption) && pluginOption.name === name;
- });
-}
-
-function sortPlugins(pluginOptions: vite.PluginOption[]) {
- // HACK: move mdxPlugin to top because it needs to run before internal JSX plugin
- const mdxPluginIndex = findPluginIndexByName(pluginOptions, '@mdx-js/rollup');
- if (mdxPluginIndex === -1) return;
- const jsxPluginIndex = findPluginIndexByName(pluginOptions, 'astro:jsx');
- const mdxPlugin = pluginOptions[mdxPluginIndex];
- pluginOptions.splice(mdxPluginIndex, 1);
- pluginOptions.splice(jsxPluginIndex, 0, mdxPlugin);
-}
-
const COMMON_DEPENDENCIES_NOT_ASTRO = [
'autoprefixer',
'react',
diff --git a/packages/astro/src/core/dev/container.ts b/packages/astro/src/core/dev/container.ts
index a2c6d5dc8..cd7ca3b0e 100644
--- a/packages/astro/src/core/dev/container.ts
+++ b/packages/astro/src/core/dev/container.ts
@@ -50,11 +50,7 @@ export async function createContainer({
isRestart,
});
- // HACK: Since we only inject the endpoint if `experimental.assets` is on and it's possible for an integration to
- // add that flag, we need to only check and inject the endpoint after running the config setup hook.
- if (settings.config.experimental.assets) {
- settings = injectImageEndpoint(settings);
- }
+ settings = injectImageEndpoint(settings);
const {
base,
diff --git a/packages/astro/src/core/dev/dev.ts b/packages/astro/src/core/dev/dev.ts
index b14656d26..115cbe825 100644
--- a/packages/astro/src/core/dev/dev.ts
+++ b/packages/astro/src/core/dev/dev.ts
@@ -18,7 +18,12 @@ export interface DevServer {
stop(): Promise<void>;
}
-/** `astro dev` */
+/**
+ * Runs Astro’s development server. This is a local HTTP server that doesn’t bundle assets.
+ * It uses Hot Module Replacement (HMR) to update your browser as you save changes in your editor.
+ *
+ * @experimental The JavaScript API is experimental
+ */
export default async function dev(inlineConfig: AstroInlineConfig): Promise<DevServer> {
const devStart = performance.now();
await telemetry.record([]);
diff --git a/packages/astro/src/core/endpoint/index.ts b/packages/astro/src/core/endpoint/index.ts
index 485190e47..9c3b57133 100644
--- a/packages/astro/src/core/endpoint/index.ts
+++ b/packages/astro/src/core/endpoint/index.ts
@@ -6,27 +6,20 @@ import type {
MiddlewareHandler,
Params,
} from '../../@types/astro';
+import mime from 'mime';
import type { Environment, RenderContext } from '../render/index';
-
import { renderEndpoint } from '../../runtime/server/index.js';
import { ASTRO_VERSION } from '../constants.js';
-import { AstroCookies, attachToResponse } from '../cookies/index.js';
+import { AstroCookies, attachCookiesToResponse } from '../cookies/index.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
import { warn } from '../logger/core.js';
import { callMiddleware } from '../middleware/callMiddleware.js';
+
+const encoder = new TextEncoder();
+
const clientAddressSymbol = Symbol.for('astro.clientAddress');
const clientLocalsSymbol = Symbol.for('astro.locals');
-export type EndpointCallResult =
- | (EndpointOutput & {
- type: 'simple';
- cookies: AstroCookies;
- })
- | {
- type: 'response';
- response: Response;
- };
-
type CreateAPIContext = {
request: Request;
params: Params;
@@ -62,6 +55,7 @@ export function createAPIContext({
},
});
},
+ ResponseWithEncoding,
url: new URL(request.url),
get clientAddress() {
if (!(clientAddressSymbol in request)) {
@@ -96,12 +90,37 @@ export function createAPIContext({
return context;
}
+type ResponseParameters = ConstructorParameters<typeof Response>;
+
+export class ResponseWithEncoding extends Response {
+ constructor(body: ResponseParameters[0], init: ResponseParameters[1], encoding?: BufferEncoding) {
+ // If a body string is given, try to encode it to preserve the behaviour as simple objects.
+ // We don't do the full handling as simple objects so users can control how headers are set instead.
+ if (typeof body === 'string') {
+ // In NodeJS, we can use Buffer.from which supports all BufferEncoding
+ if (typeof Buffer !== 'undefined' && Buffer.from) {
+ body = Buffer.from(body, encoding);
+ }
+ // In non-NodeJS, use the web-standard TextEncoder for utf-8 strings
+ else if (encoding == null || encoding === 'utf8' || encoding === 'utf-8') {
+ body = encoder.encode(body);
+ }
+ }
+
+ super(body, init);
+
+ if (encoding) {
+ this.headers.set('X-Astro-Encoding', encoding);
+ }
+ }
+}
+
export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>(
mod: EndpointHandler,
env: Environment,
ctx: RenderContext,
onRequest?: MiddlewareHandler<MiddlewareResult> | undefined
-): Promise<EndpointCallResult> {
+): Promise<Response> {
const context = createAPIContext({
request: ctx.request,
params: ctx.params,
@@ -117,22 +136,37 @@ export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>
onRequest as MiddlewareEndpointHandler,
context,
async () => {
- return await renderEndpoint(mod, context, env.ssr);
+ return await renderEndpoint(mod, context, env.ssr, env.logging);
}
);
} else {
- response = await renderEndpoint(mod, context, env.ssr);
+ response = await renderEndpoint(mod, context, env.ssr, env.logging);
}
+ const isEndpointSSR = env.ssr && !ctx.route?.prerender;
+
if (response instanceof Response) {
- attachToResponse(response, context.cookies);
- return {
- type: 'response',
- response,
- };
+ if (isEndpointSSR && response.headers.get('X-Astro-Encoding')) {
+ warn(
+ env.logging,
+ 'ssr',
+ '`ResponseWithEncoding` is ignored in SSR. Please return an instance of Response. See https://docs.astro.build/en/core-concepts/endpoints/#server-endpoints-api-routes for more information.'
+ );
+ }
+ attachCookiesToResponse(response, context.cookies);
+ return response;
}
- if (env.ssr && !ctx.route?.prerender) {
+ // The endpoint returned a simple object, convert it to a Response
+
+ // TODO: Remove in Astro 4.0
+ warn(
+ env.logging,
+ 'astro',
+ `${ctx.route.component} returns a simple object which is deprecated. Please return an instance of Response. See https://docs.astro.build/en/core-concepts/endpoints/#server-endpoints-api-routes for more information.`
+ );
+
+ if (isEndpointSSR) {
if (response.hasOwnProperty('headers')) {
warn(
env.logging,
@@ -150,9 +184,58 @@ export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>
}
}
- return {
- ...response,
- type: 'simple',
- cookies: context.cookies,
- };
+ let body: BodyInit;
+ const headers = new Headers();
+
+ // Try to get the MIME type for this route
+ const pathname = ctx.route
+ ? // Try the static route `pathname`
+ ctx.route.pathname ??
+ // Dynamic routes don't include `pathname`, so synthesize a path for these (e.g. 'src/pages/[slug].svg')
+ ctx.route.segments.map((s) => s.map((p) => p.content).join('')).join('/')
+ : // Fallback to pathname of the request
+ ctx.pathname;
+ const mimeType = mime.getType(pathname) || 'text/plain';
+ headers.set('Content-Type', `${mimeType};charset=utf-8`);
+
+ // Save encoding to X-Astro-Encoding to be used later during SSG with `fs.writeFile`.
+ // It won't work in SSR and is already warned above.
+ if (response.encoding) {
+ headers.set('X-Astro-Encoding', response.encoding);
+ }
+
+ // For Uint8Array (binary), it can passed to Response directly
+ if (response.body instanceof Uint8Array) {
+ body = response.body;
+ headers.set('Content-Length', body.byteLength.toString());
+ }
+ // In NodeJS, we can use Buffer.from which supports all BufferEncoding
+ else if (typeof Buffer !== 'undefined' && Buffer.from) {
+ body = Buffer.from(response.body, response.encoding);
+ headers.set('Content-Length', body.byteLength.toString());
+ }
+ // In non-NodeJS, use the web-standard TextEncoder for utf-8 strings only
+ // to calculate the content length
+ else if (
+ response.encoding == null ||
+ response.encoding === 'utf8' ||
+ response.encoding === 'utf-8'
+ ) {
+ body = encoder.encode(response.body);
+ headers.set('Content-Length', body.byteLength.toString());
+ }
+ // Fallback pass it to Response directly. It will mainly rely on X-Astro-Encoding
+ // to be further processed in SSG.
+ else {
+ body = response.body;
+ // NOTE: Can't calculate the content length as we can't encode to figure out the real length.
+ // But also because we don't need the length for SSG as it's only being written to disk.
+ }
+
+ response = new Response(body, {
+ status: 200,
+ headers,
+ });
+ attachCookiesToResponse(response, context.cookies);
+ return response;
}
diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts
index c0aadaae7..60e78adf6 100644
--- a/packages/astro/src/core/errors/errors-data.ts
+++ b/packages/astro/src/core/errors/errors-data.ts
@@ -42,7 +42,7 @@ export const UnknownCompilerError = {
* The `Astro.redirect` function is only available when [Server-side rendering](/en/guides/server-side-rendering/) is enabled.
*
* To redirect on a static website, the [meta refresh attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta) can be used. Certain hosts also provide config-based redirects (ex: [Netlify redirects](https://docs.netlify.com/routing/redirects/)).
- * @deprecated since version 2.6
+ * @deprecated Deprecated since version 2.6.
*/
export const StaticRedirectNotAvailable = {
name: 'StaticRedirectNotAvailable',
@@ -481,9 +481,9 @@ export const PageNumberParamNotFound = {
/**
* @docs
* @see
- * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
- * - [Image component](https://docs.astro.build/en/guides/assets/#image--astroassets)
- * - [Image component#alt](https://docs.astro.build/en/guides/assets/#alt-required)
+ * - [Images](https://docs.astro.build/en/guides/images/)
+ * - [Image component](https://docs.astro.build/en/guides/images/#image--astroassets)
+ * - [Image component#alt](https://docs.astro.build/en/guides/images/#alt-required)
* @description
* The `alt` property allows you to provide descriptive alt text to users of screen readers and other assistive technologies. In order to ensure your images are accessible, the `Image` component requires that an `alt` be specified.
*
@@ -515,8 +515,8 @@ export const InvalidImageService = {
* @message
* Missing width and height attributes for `IMAGE_URL`. When using remote images, both dimensions are always required in order to avoid cumulative layout shift (CLS).
* @see
- * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
- * - [Image component#width-and-height](https://docs.astro.build/en/guides/assets/#width-and-height)
+ * - [Images](https://docs.astro.build/en/guides/images/)
+ * - [Image component#width-and-height-required](https://docs.astro.build/en/guides/images/#width-and-height-required-for-public-and-remote-images)
* @description
* For remote images, `width` and `height` cannot be inferred from the original file. As such, in order to avoid CLS, those two properties are always required.
*
@@ -536,10 +536,10 @@ export const MissingImageDimension = {
* @description
* The built-in image services do not currently support optimizing all image formats.
*
- * For unsupported formats such as SVGs and GIFs, you may be able to use an `img` tag directly:
+ * For unsupported formats such as GIFs, you may be able to use an `img` tag directly:
* ```astro
* ---
- * import rocket from '../assets/images/rocket.svg';
+ * import rocket from '../assets/images/rocket.gif';
* ---
*
* <img src={rocket.src} width={rocket.width} height={rocket.height} alt="A rocketship in space." />
@@ -575,7 +575,7 @@ export const PrerenderDynamicEndpointPathCollide = {
/**
* @docs
* @see
- * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
+ * - [Images](https://docs.astro.build/en/guides/images/)
* @description
* An image's `src` property is not valid. The Image component requires the `src` attribute to be either an image that has been ESM imported or a string. This is also true for the first parameter of `getImage()`.
*
@@ -601,7 +601,7 @@ export const ExpectedImage = {
/**
* @docs
* @see
- * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
+ * - [Images](https://docs.astro.build/en/guides/images/)
* @description
* `getImage()`'s first parameter should be an object with the different properties to apply to your image.
*
@@ -625,7 +625,7 @@ export const ExpectedImageOptions = {
* @message
* Could not find requested image `IMAGE_PATH` at `FULL_IMAGE_PATH`.
* @see
- * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
+ * - [Images](https://docs.astro.build/en/guides/images/)
* @description
* Astro could not find an image you included in your Markdown content. Usually, this is simply caused by a typo in the path.
*
@@ -658,7 +658,7 @@ export const ResponseSentError = {
*
* For example:
* ```ts
- * import {defineMiddleware} from "astro/middleware";
+ * import {defineMiddleware} from "astro:middleware";
* export const onRequest = defineMiddleware((context, _) => {
* // doesn't return anything or call `next`
* context.locals.someData = false;
@@ -678,7 +678,7 @@ export const MiddlewareNoDataOrNextCalled = {
*
* For example:
* ```ts
- * import {defineMiddleware} from "astro/middleware";
+ * import {defineMiddleware} from "astro:middleware";
* export const onRequest = defineMiddleware(() => {
* return "string"
* });
@@ -698,7 +698,7 @@ export const MiddlewareNotAResponse = {
*
* For example:
* ```ts
- * import {defineMiddleware} from "astro/middleware";
+ * import {defineMiddleware} from "astro:middleware";
* export const onRequest = defineMiddleware((context, next) => {
* context.locals = 1541;
* return next();
@@ -715,7 +715,7 @@ export const LocalsNotAnObject = {
/**
* @docs
* @see
- * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
+ * - [Images](https://docs.astro.build/en/guides/images/)
* @description
* When using the default image services, `Image`'s and `getImage`'s `src` parameter must be either an imported image or an URL, it cannot be a filepath.
*
@@ -791,6 +791,32 @@ export const InvalidDynamicRoute = {
message: (route: string, invalidParam: string, received: string) =>
`The ${invalidParam} param for route ${route} is invalid. Received **${received}**.`,
} satisfies ErrorData;
+/**
+ * @docs
+ * @see
+ * - [Default Image Service](https://docs.astro.build/en/guides/images/#default-image-service)
+ * - [Image Component](https://docs.astro.build/en/guides/images/#image--astroassets)
+ * - [Image Services API](https://docs.astro.build/en/reference/image-service-reference/)
+ * @description
+ * Sharp is the default image service used for `astro:assets`. When using a [strict package manager](https://pnpm.io/pnpm-vs-npm#npms-flat-tree) like pnpm, Sharp must be installed manually into your project in order to use image processing.
+ *
+ * If you are not using `astro:assets` for image processing, and do not wish to install Sharp, you can configure the following passthrough image service that does no processing:
+ *
+ * ```js
+ * import { defineConfig, passthroughImageService } from "astro/config";
+ * export default defineConfig({
+ * image: {
+ * service: passthroughImageService(),
+ * },
+ * });
+ * ```
+ */
+export const MissingSharp = {
+ name: 'MissingSharp',
+ title: 'Could not find Sharp.',
+ message: 'Could not find Sharp. Please install Sharp (`sharp`) manually into your project.',
+ hint: "See Sharp's installation instructions for more information: https://sharp.pixelplumbing.com/install. If you are not relying on `astro:assets` to optimize, transform, or process any images, you can configure a passthrough image service instead of installing Sharp. See https://docs.astro.build/en/reference/errors/missing-sharp for more information.",
+};
// No headings here, that way Vite errors are merged with Astro ones in the docs, which makes more sense to users.
// Vite Errors - 4xxx
/**
diff --git a/packages/astro/src/core/errors/errors.ts b/packages/astro/src/core/errors/errors.ts
index 1960bac4a..faf365686 100644
--- a/packages/astro/src/core/errors/errors.ts
+++ b/packages/astro/src/core/errors/errors.ts
@@ -19,6 +19,7 @@ export interface ErrorLocation {
type ErrorTypes =
| 'AstroError'
+ | 'AstroUserError'
| 'CompilerError'
| 'CSSError'
| 'MarkdownError'
@@ -171,3 +172,25 @@ export interface ErrorWithMetadata {
};
cause?: any;
}
+
+/**
+ * Special error that is exposed to users.
+ * Compared to AstroError, it contains a subset of information.
+ */
+export class AstroUserError extends Error {
+ type: ErrorTypes = 'AstroUserError';
+ /**
+ * A message that explains to the user how they can fix the error.
+ */
+ hint: string | undefined;
+ name = 'AstroUserError';
+ constructor(message: string, hint?: string) {
+ super();
+ this.message = message;
+ this.hint = hint;
+ }
+
+ static is(err: unknown): err is AstroUserError {
+ return (err as AstroUserError).type === 'AstroUserError';
+ }
+}
diff --git a/packages/astro/src/core/errors/index.ts b/packages/astro/src/core/errors/index.ts
index e09225af4..5a796a0b3 100644
--- a/packages/astro/src/core/errors/index.ts
+++ b/packages/astro/src/core/errors/index.ts
@@ -7,6 +7,7 @@ export {
CompilerError,
MarkdownError,
isAstroError,
+ AstroUserError,
} from './errors.js';
export { codeFrame } from './printer.js';
export { createSafeError, positionAt } from './utils.js';
diff --git a/packages/astro/src/core/errors/userError.ts b/packages/astro/src/core/errors/userError.ts
new file mode 100644
index 000000000..663549314
--- /dev/null
+++ b/packages/astro/src/core/errors/userError.ts
@@ -0,0 +1 @@
+export { AstroUserError as AstroError } from './errors.js';
diff --git a/packages/astro/src/core/index.ts b/packages/astro/src/core/index.ts
new file mode 100644
index 000000000..31d868311
--- /dev/null
+++ b/packages/astro/src/core/index.ts
@@ -0,0 +1,26 @@
+// This is the main entrypoint when importing the `astro` package.
+
+import type { AstroInlineConfig } from '../@types/astro.js';
+import { default as _build } from './build/index.js';
+import { default as _sync } from './sync/index.js';
+
+export { default as dev } from './dev/index.js';
+export { default as preview } from './preview/index.js';
+
+/**
+ * Builds your site for deployment. By default, this will generate static files and place them in a dist/ directory.
+ * If SSR is enabled, this will generate the necessary server files to serve your site.
+ *
+ * @experimental The JavaScript API is experimental
+ */
+// Wrap `_build` to prevent exposing the second internal options parameter
+export const build = (inlineConfig: AstroInlineConfig) => _build(inlineConfig);
+
+/**
+ * Generates TypeScript types for all Astro modules. This sets up a `src/env.d.ts` file for type inferencing,
+ * and defines the `astro:content` module for the Content Collections API.
+ *
+ * @experimental The JavaScript API is experimental
+ */
+// Wrap `_sync` to prevent exposing the second internal options parameter
+export const sync = (inlineConfig: AstroInlineConfig) => _sync(inlineConfig);
diff --git a/packages/astro/src/core/logger/console.ts b/packages/astro/src/core/logger/console.ts
index dfe732bd7..f39f6b74d 100644
--- a/packages/astro/src/core/logger/console.ts
+++ b/packages/astro/src/core/logger/console.ts
@@ -15,7 +15,7 @@ export const consoleLogDestination = {
function getPrefix() {
let prefix = '';
- let type = event.type;
+ let type = event.label;
if (type) {
// hide timestamp when type is undefined
prefix += dim(dateTimeFormat.format(new Date()) + ' ');
diff --git a/packages/astro/src/core/logger/core.ts b/packages/astro/src/core/logger/core.ts
index 4f0c281e0..c92cdbb24 100644
--- a/packages/astro/src/core/logger/core.ts
+++ b/packages/astro/src/core/logger/core.ts
@@ -6,7 +6,6 @@ interface LogWritable<T> {
}
export type LoggerLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'; // same as Pino
-export type LoggerEvent = 'info' | 'warn' | 'error';
export interface LogOptions {
dest: LogWritable<LogMessage>;
@@ -29,7 +28,7 @@ export const dateTimeFormat = new Intl.DateTimeFormat([], {
});
export interface LogMessage {
- type: string | null;
+ label: string | null;
level: LoggerLevel;
message: string;
}
@@ -43,11 +42,11 @@ export const levels: Record<LoggerLevel, number> = {
};
/** Full logging API */
-export function log(opts: LogOptions, level: LoggerLevel, type: string | null, message: string) {
+export function log(opts: LogOptions, level: LoggerLevel, label: string | null, message: string) {
const logLevel = opts.level;
const dest = opts.dest;
const event: LogMessage = {
- type,
+ label,
level,
message,
};
@@ -61,18 +60,18 @@ export function log(opts: LogOptions, level: LoggerLevel, type: string | null, m
}
/** Emit a user-facing message. Useful for UI and other console messages. */
-export function info(opts: LogOptions, type: string | null, message: string) {
- return log(opts, 'info', type, message);
+export function info(opts: LogOptions, label: string | null, message: string) {
+ return log(opts, 'info', label, message);
}
/** Emit a warning message. Useful for high-priority messages that aren't necessarily errors. */
-export function warn(opts: LogOptions, type: string | null, message: string) {
- return log(opts, 'warn', type, message);
+export function warn(opts: LogOptions, label: string | null, message: string) {
+ return log(opts, 'warn', label, message);
}
/** Emit a error message, Useful when Astro can't recover from some error. */
-export function error(opts: LogOptions, type: string | null, message: string) {
- return log(opts, 'error', type, message);
+export function error(opts: LogOptions, label: string | null, message: string) {
+ return log(opts, 'error', label, message);
}
type LogFn = typeof info | typeof warn | typeof error;
@@ -127,3 +126,53 @@ export function timerMessage(message: string, startTime: number = Date.now()) {
timeDiff < 750 ? `${Math.round(timeDiff)}ms` : `${(timeDiff / 1000).toFixed(1)}s`;
return `${message} ${dim(timeDisplay)}`;
}
+
+export class Logger {
+ options: LogOptions;
+ constructor(options: LogOptions) {
+ this.options = options;
+ }
+
+ info(label: string | null, message: string) {
+ info(this.options, label, message);
+ }
+ warn(label: string | null, message: string) {
+ warn(this.options, label, message);
+ }
+ error(label: string | null, message: string) {
+ error(this.options, label, message);
+ }
+ debug(label: string | null, message: string) {
+ debug(this.options, label, message);
+ }
+}
+
+export class AstroIntegrationLogger {
+ options: LogOptions;
+ label: string;
+
+ constructor(logging: LogOptions, label: string) {
+ this.options = logging;
+ this.label = label;
+ }
+
+ /**
+ * Creates a new logger instance with a new label, but the same log options.
+ */
+ fork(label: string): AstroIntegrationLogger {
+ return new AstroIntegrationLogger(this.options, label);
+ }
+
+ info(message: string) {
+ info(this.options, this.label, message);
+ }
+ warn(message: string) {
+ warn(this.options, this.label, message);
+ }
+ error(message: string) {
+ error(this.options, this.label, message);
+ }
+ debug(message: string) {
+ debug(this.options, this.label, message);
+ }
+}
diff --git a/packages/astro/src/core/logger/node.ts b/packages/astro/src/core/logger/node.ts
index 513ba257e..aeef4bd84 100644
--- a/packages/astro/src/core/logger/node.ts
+++ b/packages/astro/src/core/logger/node.ts
@@ -21,19 +21,19 @@ export const nodeLogDestination = new Writable({
function getPrefix() {
let prefix = '';
- let type = event.type;
- if (type) {
+ let label = event.label;
+ if (label) {
// hide timestamp when type is undefined
prefix += dim(dateTimeFormat.format(new Date()) + ' ');
if (event.level === 'info') {
- type = bold(cyan(`[${type}]`));
+ label = bold(cyan(`[${label}]`));
} else if (event.level === 'warn') {
- type = bold(yellow(`[${type}]`));
+ label = bold(yellow(`[${label}]`));
} else if (event.level === 'error') {
- type = bold(red(`[${type}]`));
+ label = bold(red(`[${label}]`));
}
- prefix += `${type} `;
+ prefix += `${label} `;
}
return reset(prefix);
}
@@ -87,7 +87,7 @@ export const nodeLogOptions: Required<LogOptions> = {
};
export interface LogMessage {
- type: string | null;
+ label: string | null;
level: LoggerLevel;
message: string;
}
diff --git a/packages/astro/src/core/messages.ts b/packages/astro/src/core/messages.ts
index 51ec39ad9..4fc3ca02a 100644
--- a/packages/astro/src/core/messages.ts
+++ b/packages/astro/src/core/messages.ts
@@ -17,7 +17,12 @@ import {
import type { ResolvedServerUrls } from 'vite';
import type { ZodError } from 'zod';
import { renderErrorMarkdown } from './errors/dev/utils.js';
-import { AstroError, CompilerError, type ErrorWithMetadata } from './errors/index.js';
+import {
+ AstroError,
+ CompilerError,
+ type ErrorWithMetadata,
+ AstroUserError,
+} from './errors/index.js';
import { emoji, padMultilineString } from './util.js';
const PREFIX_PADDING = 6;
@@ -198,7 +203,7 @@ export function formatConfigErrorMessage(err: ZodError) {
}
export function formatErrorMessage(err: ErrorWithMetadata, args: string[] = []): string {
- const isOurError = AstroError.is(err) || CompilerError.is(err);
+ const isOurError = AstroError.is(err) || CompilerError.is(err) || AstroUserError.is(err);
args.push(
`${bgRed(black(` error `))}${red(
diff --git a/packages/astro/src/core/middleware/namespace.ts b/packages/astro/src/core/middleware/namespace.ts
new file mode 100644
index 000000000..55a84f666
--- /dev/null
+++ b/packages/astro/src/core/middleware/namespace.ts
@@ -0,0 +1 @@
+export { defineMiddleware, sequence } from './index.js';
diff --git a/packages/astro/src/core/pipeline.ts b/packages/astro/src/core/pipeline.ts
new file mode 100644
index 000000000..19e1ef82b
--- /dev/null
+++ b/packages/astro/src/core/pipeline.ts
@@ -0,0 +1,153 @@
+import { type RenderContext, type Environment } from './render/index.js';
+import { callEndpoint, createAPIContext } from './endpoint/index.js';
+import type {
+ MiddlewareHandler,
+ MiddlewareResponseHandler,
+ ComponentInstance,
+ MiddlewareEndpointHandler,
+ EndpointHandler,
+} from '../@types/astro';
+import { callMiddleware } from './middleware/callMiddleware.js';
+import { renderPage } from './render/core.js';
+
+type EndpointResultHandler = (
+ originalRequest: Request,
+ result: Response
+) => Promise<Response> | Response;
+
+/**
+ * This is the basic class of a pipeline.
+ *
+ * Check the {@link ./README.md|README} for more information about the pipeline.
+ */
+export class Pipeline {
+ env: Environment;
+ #onRequest?: MiddlewareEndpointHandler;
+ /**
+ * The handler accepts the *original* `Request` and result returned by the endpoint.
+ * It must return a `Response`.
+ */
+ #endpointHandler?: EndpointResultHandler;
+
+ /**
+ * When creating a pipeline, an environment is mandatory.
+ * The environment won't change for the whole lifetime of the pipeline.
+ */
+ constructor(env: Environment) {
+ this.env = env;
+ }
+
+ setEnvironment() {}
+
+ /**
+ * When rendering a route, an "endpoint" will a type that needs to be handled and transformed into a `Response`.
+ *
+ * Each consumer might have different needs; use this function to set up the handler.
+ */
+ setEndpointHandler(handler: EndpointResultHandler) {
+ this.#endpointHandler = handler;
+ }
+
+ /**
+ * A middleware function that will be called before each request.
+ */
+ setMiddlewareFunction(onRequest: MiddlewareEndpointHandler) {
+ this.#onRequest = onRequest;
+ }
+
+ /**
+ * Returns the current environment
+ */
+ getEnvironment(): Readonly<Environment> {
+ return this.env;
+ }
+
+ /**
+ * The main function of the pipeline. Use this function to render any route known to Astro;
+ */
+ async renderRoute(
+ renderContext: RenderContext,
+ componentInstance: ComponentInstance
+ ): Promise<Response> {
+ const result = await this.#tryRenderRoute(
+ renderContext,
+ this.env,
+ componentInstance,
+ this.#onRequest
+ );
+ if (renderContext.route.type === 'endpoint') {
+ if (!this.#endpointHandler) {
+ throw new Error(
+ 'You created a pipeline that does not know how to handle the result coming from an endpoint.'
+ );
+ }
+ return this.#endpointHandler(renderContext.request, result);
+ } else {
+ return result;
+ }
+ }
+
+ /**
+ * It attempts to render a route. A route can be a:
+ * - page
+ * - redirect
+ * - endpoint
+ *
+ * ## Errors
+ *
+ * It throws an error if the page can't be rendered.
+ */
+ async #tryRenderRoute<MiddlewareReturnType = Response>(
+ renderContext: Readonly<RenderContext>,
+ env: Readonly<Environment>,
+ mod: Readonly<ComponentInstance>,
+ onRequest?: MiddlewareHandler<MiddlewareReturnType>
+ ): Promise<Response> {
+ const apiContext = createAPIContext({
+ request: renderContext.request,
+ params: renderContext.params,
+ props: renderContext.props,
+ site: env.site,
+ adapterName: env.adapterName,
+ });
+
+ switch (renderContext.route.type) {
+ case 'page':
+ case 'redirect': {
+ if (onRequest) {
+ return await callMiddleware<Response>(
+ env.logging,
+ onRequest as MiddlewareResponseHandler,
+ apiContext,
+ () => {
+ return renderPage({
+ mod,
+ renderContext,
+ env,
+ cookies: apiContext.cookies,
+ });
+ }
+ );
+ } else {
+ return await renderPage({
+ mod,
+ renderContext,
+ env,
+ cookies: apiContext.cookies,
+ });
+ }
+ }
+ case 'endpoint': {
+ const result = await callEndpoint(
+ mod as any as EndpointHandler,
+ env,
+ renderContext,
+ onRequest
+ );
+ return result;
+ }
+ default:
+ throw new Error(`Couldn't find route of type [${renderContext.route.type}]`);
+ }
+ }
+}
diff --git a/packages/astro/src/core/polyfill.ts b/packages/astro/src/core/polyfill.ts
index 99e0d5cc5..ea7916eb3 100644
--- a/packages/astro/src/core/polyfill.ts
+++ b/packages/astro/src/core/polyfill.ts
@@ -1,8 +1,74 @@
-import { polyfill } from '@astrojs/webapi';
+import crypto from 'node:crypto';
+import {
+ ByteLengthQueuingStrategy,
+ CountQueuingStrategy,
+ ReadableByteStreamController,
+ ReadableStream,
+ ReadableStreamBYOBReader,
+ ReadableStreamBYOBRequest,
+ ReadableStreamDefaultController,
+ ReadableStreamDefaultReader,
+ TransformStream,
+ WritableStream,
+ WritableStreamDefaultController,
+ WritableStreamDefaultWriter,
+} from 'node:stream/web';
+import { File, FormData, Headers, Request, Response, fetch } from 'undici';
+
+// NOTE: This file does not intend to polyfill everything that exists, its main goal is to make life easier
+// for users deploying to runtime that do support these features. In the future, we hope for this file to disappear.
+
+// HACK (2023-08-18) Stackblitz does not support Node 18 yet, so we'll fake Node 16 support for some time until it's supported
+// TODO: Remove when Node 18 is supported on Stackblitz. File should get imported from `node:buffer` instead of `undici` once this is removed
+const isStackblitz = process.env.SHELL === '/bin/jsh' && process.versions.webcontainer != null;
export function apply() {
- // polyfill WebAPIs for Node.js runtime
- polyfill(globalThis, {
- exclude: 'window document',
- });
+ if (isStackblitz) {
+ const neededPolyfills = {
+ ByteLengthQueuingStrategy,
+ CountQueuingStrategy,
+ ReadableByteStreamController,
+ ReadableStream,
+ ReadableStreamBYOBReader,
+ ReadableStreamBYOBRequest,
+ ReadableStreamDefaultController,
+ ReadableStreamDefaultReader,
+ TransformStream,
+ WritableStream,
+ WritableStreamDefaultController,
+ WritableStreamDefaultWriter,
+ File,
+ FormData,
+ Headers,
+ Request,
+ Response,
+ fetch,
+ };
+
+ for (let polyfillName of Object.keys(neededPolyfills)) {
+ if (Object.hasOwnProperty.call(globalThis, polyfillName)) continue;
+
+ // Add polyfill to globalThis
+ Object.defineProperty(globalThis, polyfillName, {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: neededPolyfills[polyfillName as keyof typeof neededPolyfills],
+ });
+ }
+ }
+
+ // Remove when Node 18 is dropped for Node 20
+ if (!globalThis.crypto) {
+ Object.defineProperty(globalThis, 'crypto', {
+ value: crypto.webcrypto,
+ });
+ }
+
+ // Remove when Node 18 is dropped for Node 20
+ if (!globalThis.File) {
+ Object.defineProperty(globalThis, 'File', {
+ value: File,
+ });
+ }
}
diff --git a/packages/astro/src/core/preview/index.ts b/packages/astro/src/core/preview/index.ts
index fdd5d6fe7..d5b2b2db7 100644
--- a/packages/astro/src/core/preview/index.ts
+++ b/packages/astro/src/core/preview/index.ts
@@ -10,10 +10,13 @@ import { createSettings } from '../config/settings.js';
import createStaticPreviewServer from './static-preview-server.js';
import { getResolvedHostForHttpServer } from './util.js';
-/** The primary dev action */
-export default async function preview(
- inlineConfig: AstroInlineConfig
-): Promise<PreviewServer | undefined> {
+/**
+ * Starts a local server to serve your static dist/ directory. This command is useful for previewing
+ * your build locally, before deploying it. It is not designed to be run in production.
+ *
+ * @experimental The JavaScript API is experimental
+ */
+export default async function preview(inlineConfig: AstroInlineConfig): Promise<PreviewServer> {
const logging = createNodeLogging(inlineConfig);
const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, 'preview');
telemetry.record(eventCliSession('preview', userConfig));
diff --git a/packages/astro/src/core/render/context.ts b/packages/astro/src/core/render/context.ts
index 5b26eda18..d767d7910 100644
--- a/packages/astro/src/core/render/context.ts
+++ b/packages/astro/src/core/render/context.ts
@@ -22,7 +22,7 @@ export interface RenderContext {
links?: Set<SSRElement>;
styles?: Set<SSRElement>;
componentMetadata?: SSRResult['componentMetadata'];
- route?: RouteData;
+ route: RouteData;
status?: number;
params: Params;
props: Props;
@@ -32,6 +32,7 @@ export interface RenderContext {
export type CreateRenderContextArgs = Partial<
Omit<RenderContext, 'params' | 'props' | 'locals'>
> & {
+ route: RouteData;
request: RenderContext['request'];
mod: ComponentInstance;
env: Environment;
diff --git a/packages/astro/src/core/render/core.ts b/packages/astro/src/core/render/core.ts
index d6228fbbe..fb10ccc80 100644
--- a/packages/astro/src/core/render/core.ts
+++ b/packages/astro/src/core/render/core.ts
@@ -4,11 +4,11 @@ import type {
EndpointHandler,
MiddlewareHandler,
MiddlewareResponseHandler,
- RouteType,
} from '../../@types/astro';
import { renderPage as runtimeRenderPage } from '../../runtime/server/index.js';
-import { attachToResponse } from '../cookies/index.js';
-import { callEndpoint, createAPIContext, type EndpointCallResult } from '../endpoint/index.js';
+import { attachCookiesToResponse } from '../cookies/index.js';
+import { callEndpoint, createAPIContext } from '../endpoint/index.js';
+import { warn } from '../logger/core.js';
import { callMiddleware } from '../middleware/callMiddleware.js';
import { redirectRouteGenerate, redirectRouteStatus, routeIsRedirect } from '../redirects/index.js';
import type { RenderContext } from './context.js';
@@ -22,7 +22,7 @@ export type RenderPage = {
cookies: AstroCookies;
};
-async function renderPage({ mod, renderContext, env, cookies }: RenderPage) {
+export async function renderPage({ mod, renderContext, env, cookies }: RenderPage) {
if (routeIsRedirect(renderContext.route)) {
return new Response(null, {
status: redirectRouteStatus(renderContext.route, renderContext.request.method),
@@ -42,7 +42,6 @@ async function renderPage({ mod, renderContext, env, cookies }: RenderPage) {
links: renderContext.links,
styles: renderContext.styles,
logging: env.logging,
- markdown: env.markdown,
params: renderContext.params,
pathname: renderContext.pathname,
componentMetadata: renderContext.componentMetadata,
@@ -59,12 +58,16 @@ async function renderPage({ mod, renderContext, env, cookies }: RenderPage) {
locals: renderContext.locals ?? {},
});
- // Support `export const components` for `MDX` pages
- if (typeof (mod as any).components === 'object') {
- Object.assign(renderContext.props, { components: (mod as any).components });
+ // TODO: Remove in Astro 4.0
+ if (mod.frontmatter && typeof mod.frontmatter === 'object' && 'draft' in mod.frontmatter) {
+ warn(
+ env.logging,
+ 'astro',
+ `The drafts feature is deprecated and used in ${renderContext.route.component}. You should migrate to content collections instead. See https://docs.astro.build/en/guides/content-collections/#filtering-collection-queries for more information.`
+ );
}
- let response = await runtimeRenderPage(
+ const response = await runtimeRenderPage(
result,
Component,
renderContext.props,
@@ -76,7 +79,7 @@ async function renderPage({ mod, renderContext, env, cookies }: RenderPage) {
// If there is an Astro.cookies instance, attach it to the response so that
// adapters can grab the Set-Cookie headers.
if (result.cookies) {
- attachToResponse(response, result.cookies);
+ attachCookiesToResponse(response, result.cookies);
}
return response;
@@ -91,14 +94,14 @@ async function renderPage({ mod, renderContext, env, cookies }: RenderPage) {
* ## Errors
*
* It throws an error if the page can't be rendered.
+ * @deprecated Use the pipeline instead
*/
export async function tryRenderRoute<MiddlewareReturnType = Response>(
- routeType: RouteType,
renderContext: Readonly<RenderContext>,
env: Readonly<Environment>,
mod: Readonly<ComponentInstance>,
onRequest?: MiddlewareHandler<MiddlewareReturnType>
-): Promise<Response | EndpointCallResult> {
+): Promise<Response> {
const apiContext = createAPIContext({
request: renderContext.request,
params: renderContext.params,
@@ -107,7 +110,7 @@ export async function tryRenderRoute<MiddlewareReturnType = Response>(
adapterName: env.adapterName,
});
- switch (routeType) {
+ switch (renderContext.route.type) {
case 'page':
case 'redirect': {
if (onRequest) {
@@ -143,14 +146,6 @@ export async function tryRenderRoute<MiddlewareReturnType = Response>(
return result;
}
default:
- throw new Error(`Couldn't find route of type [${routeType}]`);
+ throw new Error(`Couldn't find route of type [${renderContext.route.type}]`);
}
}
-
-export function isEndpointResult(result: any, routeType: RouteType): result is EndpointCallResult {
- return !(result instanceof Response) && routeType === 'endpoint';
-}
-
-export function isResponse(result: any, routeType: RouteType): result is Response {
- return result instanceof Response && (routeType === 'page' || routeType === 'redirect');
-}
diff --git a/packages/astro/src/core/render/environment.ts b/packages/astro/src/core/render/environment.ts
index de7cbe6a8..f38d98551 100644
--- a/packages/astro/src/core/render/environment.ts
+++ b/packages/astro/src/core/render/environment.ts
@@ -1,7 +1,5 @@
-import type { MarkdownRenderingOptions } from '@astrojs/markdown-remark';
-import type { AstroSettings, RuntimeMode, SSRLoadedRenderer } from '../../@types/astro';
+import type { RuntimeMode, SSRLoadedRenderer } from '../../@types/astro';
import type { LogOptions } from '../logger/core.js';
-import type { ModuleLoader } from '../module-loader';
import type { RouteCache } from './route-cache.js';
/**
@@ -16,10 +14,6 @@ export interface Environment {
adapterName?: string;
/** logging options */
logging: LogOptions;
- /**
- * Used to support `Astro.__renderMarkdown` for legacy `<Markdown />` component
- */
- markdown: MarkdownRenderingOptions;
/** "development" or "production" */
mode: RuntimeMode;
compressHTML: boolean;
@@ -43,8 +37,3 @@ export type CreateEnvironmentArgs = Environment;
export function createEnvironment(options: CreateEnvironmentArgs): Environment {
return options;
}
-
-export type DevelopmentEnvironment = Environment & {
- loader: ModuleLoader;
- settings: AstroSettings;
-};
diff --git a/packages/astro/src/core/render/index.ts b/packages/astro/src/core/render/index.ts
index a82c5699e..f39c02ae2 100644
--- a/packages/astro/src/core/render/index.ts
+++ b/packages/astro/src/core/render/index.ts
@@ -1,18 +1,17 @@
import type { AstroMiddlewareInstance, ComponentInstance, RouteData } from '../../@types/astro';
-import type { DevelopmentEnvironment } from './environment';
-
export { createRenderContext } from './context.js';
export type { RenderContext } from './context.js';
export { tryRenderRoute } from './core.js';
-export type { Environment } from './environment';
+import type { Environment } from './environment';
export { createEnvironment } from './environment.js';
export { getParamsAndProps } from './params-and-props.js';
-export { loadRenderer, loadRenderers } from './renderer.js';
-export type { DevelopmentEnvironment };
+export { loadRenderer } from './renderer.js';
+
+export type { Environment };
export interface SSROptions {
/** The environment instance */
- env: DevelopmentEnvironment;
+ env: Environment;
/** location of file on disk */
filePath: URL;
/** the web request (needed for dynamic routes) */
@@ -22,7 +21,7 @@ export interface SSROptions {
/** Request */
request: Request;
/** optional, in case we need to render something outside of a dev server */
- route?: RouteData;
+ route: RouteData;
/**
* Optional middlewares
*/
diff --git a/packages/astro/src/core/render/params-and-props.ts b/packages/astro/src/core/render/params-and-props.ts
index a5e4fa222..fc08c495e 100644
--- a/packages/astro/src/core/render/params-and-props.ts
+++ b/packages/astro/src/core/render/params-and-props.ts
@@ -33,7 +33,6 @@ export async function getParamsAndProps(opts: GetParamsAndPropsOptions): Promise
mod,
route,
routeCache,
- isValidate: true,
logging,
ssr,
});
diff --git a/packages/astro/src/core/render/renderer.ts b/packages/astro/src/core/render/renderer.ts
index 8e5e97202..e64a27ba5 100644
--- a/packages/astro/src/core/render/renderer.ts
+++ b/packages/astro/src/core/render/renderer.ts
@@ -1,14 +1,6 @@
-import type { AstroRenderer, AstroSettings, SSRLoadedRenderer } from '../../@types/astro';
+import type { AstroRenderer, SSRLoadedRenderer } from '../../@types/astro';
import type { ModuleLoader } from '../module-loader/index.js';
-export async function loadRenderers(
- settings: AstroSettings,
- moduleLoader: ModuleLoader
-): Promise<SSRLoadedRenderer[]> {
- const renderers = await Promise.all(settings.renderers.map((r) => loadRenderer(r, moduleLoader)));
- return renderers.filter(Boolean) as SSRLoadedRenderer[];
-}
-
export async function loadRenderer(
renderer: AstroRenderer,
moduleLoader: ModuleLoader
diff --git a/packages/astro/src/core/render/result.ts b/packages/astro/src/core/render/result.ts
index 968b232d4..72fa4ddcf 100644
--- a/packages/astro/src/core/render/result.ts
+++ b/packages/astro/src/core/render/result.ts
@@ -1,4 +1,3 @@
-import type { MarkdownRenderingOptions } from '@astrojs/markdown-remark';
import type {
AstroGlobal,
AstroGlobalPartial,
@@ -27,10 +26,6 @@ export interface CreateResultArgs {
*/
ssr: boolean;
logging: LogOptions;
- /**
- * Used to support `Astro.__renderMarkdown` for legacy `<Markdown />` component
- */
- markdown: MarkdownRenderingOptions;
params: Params;
pathname: string;
renderers: SSRLoadedRenderer[];
@@ -128,10 +123,8 @@ class Slots {
}
}
-let renderMarkdown: any = null;
-
export function createResult(args: CreateResultArgs): SSRResult {
- const { markdown, params, request, resolve, locals } = args;
+ const { params, request, resolve, locals } = args;
const url = new URL(request.url);
const headers = new Headers();
@@ -222,31 +215,6 @@ export function createResult(args: CreateResultArgs): SSRResult {
slots: astroSlots as unknown as AstroGlobal['slots'],
};
- Object.defineProperty(Astro, '__renderMarkdown', {
- // Ensure this API is not exposed to users
- enumerable: false,
- writable: false,
- // TODO: Remove this hole "Deno" logic once our plugin gets Deno support
- value: async function (content: string, opts: MarkdownRenderingOptions) {
- // @ts-expect-error
- if (typeof Deno !== 'undefined') {
- throw new Error('Markdown is not supported in Deno SSR');
- }
-
- if (!renderMarkdown) {
- // The package is saved in this variable because Vite is too smart
- // and will try to inline it in buildtime
- let astroRemark = '@astrojs/';
- astroRemark += 'markdown-remark';
-
- renderMarkdown = (await import(astroRemark)).renderMarkdown;
- }
-
- const { code } = await renderMarkdown(content, { ...markdown, ...(opts ?? {}) });
- return code;
- },
- });
-
return Astro;
},
resolve,
diff --git a/packages/astro/src/core/render/route-cache.ts b/packages/astro/src/core/render/route-cache.ts
index 7ad247ef8..804f09183 100644
--- a/packages/astro/src/core/render/route-cache.ts
+++ b/packages/astro/src/core/render/route-cache.ts
@@ -18,7 +18,6 @@ interface CallGetStaticPathsOptions {
mod: ComponentInstance;
route: RouteData;
routeCache: RouteCache;
- isValidate: boolean;
logging: LogOptions;
ssr: boolean;
}
@@ -27,7 +26,6 @@ export async function callGetStaticPaths({
mod,
route,
routeCache,
- isValidate,
logging,
ssr,
}: CallGetStaticPathsOptions): Promise<GetStaticPathsResultKeyed> {
@@ -58,14 +56,7 @@ export async function callGetStaticPaths({
},
});
- // Flatten the array before validating the content, otherwise users using `.map` will run into errors
- if (Array.isArray(staticPaths)) {
- staticPaths = staticPaths.flat();
- }
-
- if (isValidate) {
- validateGetStaticPathsResult(staticPaths, logging, route);
- }
+ validateGetStaticPathsResult(staticPaths, logging, route);
const keyedStaticPaths = staticPaths as GetStaticPathsResultKeyed;
keyedStaticPaths.keyed = new Map<string, GetStaticPathsItem>();
diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts
index e669f293b..124d870d9 100644
--- a/packages/astro/src/core/routing/manifest/create.ts
+++ b/packages/astro/src/core/routing/manifest/create.ts
@@ -185,7 +185,12 @@ function injectedRouteToItem(
{ config, cwd }: { config: AstroConfig; cwd?: string },
{ pattern, entryPoint }: InjectedRoute
): Item {
- const resolved = require.resolve(entryPoint, { paths: [cwd || fileURLToPath(config.root)] });
+ let resolved: string;
+ try {
+ resolved = require.resolve(entryPoint, { paths: [cwd || fileURLToPath(config.root)] });
+ } catch (e) {
+ resolved = fileURLToPath(new URL(entryPoint, config.root));
+ }
const ext = path.extname(pattern);
diff --git a/packages/astro/src/core/routing/validation.ts b/packages/astro/src/core/routing/validation.ts
index 9a562c044..b5c29b16e 100644
--- a/packages/astro/src/core/routing/validation.ts
+++ b/packages/astro/src/core/routing/validation.ts
@@ -54,6 +54,15 @@ export function validateGetStaticPathsResult(
}
result.forEach((pathObject) => {
+ if ((typeof pathObject === 'object' && Array.isArray(pathObject)) || pathObject === null) {
+ throw new AstroError({
+ ...AstroErrorData.InvalidGetStaticPathsEntry,
+ message: AstroErrorData.InvalidGetStaticPathsEntry.message(
+ Array.isArray(pathObject) ? 'array' : typeof pathObject
+ ),
+ });
+ }
+
if (
pathObject.params === undefined ||
pathObject.params === null ||
@@ -67,16 +76,6 @@ export function validateGetStaticPathsResult(
});
}
- if (typeof pathObject.params !== 'object') {
- throw new AstroError({
- ...AstroErrorData.InvalidGetStaticPathParam,
- message: AstroErrorData.InvalidGetStaticPathParam.message(typeof pathObject.params),
- location: {
- file: route.component,
- },
- });
- }
-
// TODO: Replace those with errors? They technically don't crash the build, but users might miss the warning. - erika, 2022-11-07
for (const [key, val] of Object.entries(pathObject.params)) {
if (!(typeof val === 'undefined' || typeof val === 'string' || typeof val === 'number')) {
diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts
index be51f0039..e1d432465 100644
--- a/packages/astro/src/core/sync/index.ts
+++ b/packages/astro/src/core/sync/index.ts
@@ -22,8 +22,7 @@ export type ProcessExit = 0 | 1;
export type SyncOptions = {
/**
- * Only used for testing
- * @internal
+ * @internal only used for testing
*/
fs?: typeof fsMod;
};
@@ -32,7 +31,13 @@ export type SyncInternalOptions = SyncOptions & {
logging: LogOptions;
};
-export async function sync(
+/**
+ * Generates TypeScript types for all Astro modules. This sets up a `src/env.d.ts` file for type inferencing,
+ * and defines the `astro:content` module for the Content Collections API.
+ *
+ * @experimental The JavaScript API is experimental
+ */
+export default async function sync(
inlineConfig: AstroInlineConfig,
options?: SyncOptions
): Promise<ProcessExit> {
@@ -48,7 +53,7 @@ export async function sync(
command: 'build',
});
- return await syncInternal(settings, { logging, fs: options?.fs });
+ return await syncInternal(settings, { ...options, logging });
}
/**
diff --git a/packages/astro/src/core/util.ts b/packages/astro/src/core/util.ts
index ff41a5ed6..d442e5811 100644
--- a/packages/astro/src/core/util.ts
+++ b/packages/astro/src/core/util.ts
@@ -189,7 +189,6 @@ export function emoji(char: string, fallback: string) {
* through a script tag or a dynamic import as-is.
*/
// NOTE: `/@id/` should only be used when the id is fully resolved
-// TODO: Export a helper util from Vite
export async function resolveIdToUrl(loader: ModuleLoader, id: string, root?: URL) {
let resultId = await loader.resolveId(id, undefined);
// Try resolve jsx to tsx
diff --git a/packages/astro/src/integrations/astroFeaturesValidation.ts b/packages/astro/src/integrations/astroFeaturesValidation.ts
new file mode 100644
index 000000000..c494b35f4
--- /dev/null
+++ b/packages/astro/src/integrations/astroFeaturesValidation.ts
@@ -0,0 +1,157 @@
+import type {
+ AstroAssetsFeature,
+ AstroConfig,
+ AstroFeatureMap,
+ SupportsKind,
+} from '../@types/astro';
+import { error, warn, type LogOptions } from '../core/logger/core.js';
+
+const STABLE = 'stable';
+const DEPRECATED = 'deprecated';
+const UNSUPPORTED = 'unsupported';
+const EXPERIMENTAL = 'experimental';
+
+const UNSUPPORTED_ASSETS_FEATURE: AstroAssetsFeature = {
+ supportKind: UNSUPPORTED,
+ isSquooshCompatible: false,
+ isSharpCompatible: false,
+};
+
+// NOTE: remove for Astro 4.0
+const ALL_UNSUPPORTED: Required<AstroFeatureMap> = {
+ serverOutput: UNSUPPORTED,
+ staticOutput: UNSUPPORTED,
+ hybridOutput: UNSUPPORTED,
+ assets: UNSUPPORTED_ASSETS_FEATURE,
+};
+
+type ValidationResult = {
+ [Property in keyof AstroFeatureMap]: boolean;
+};
+
+/**
+ * Checks whether an adapter supports certain features that are enabled via Astro configuration.
+ *
+ * If a configuration is enabled and "unlocks" a feature, but the adapter doesn't support, the function
+ * will throw a runtime error.
+ *
+ */
+export function validateSupportedFeatures(
+ adapterName: string,
+ featureMap: AstroFeatureMap = ALL_UNSUPPORTED,
+ config: AstroConfig,
+ logging: LogOptions
+): ValidationResult {
+ const {
+ assets = UNSUPPORTED_ASSETS_FEATURE,
+ serverOutput = UNSUPPORTED,
+ staticOutput = UNSUPPORTED,
+ hybridOutput = UNSUPPORTED,
+ } = featureMap;
+ const validationResult: ValidationResult = {};
+
+ validationResult.staticOutput = validateSupportKind(
+ staticOutput,
+ adapterName,
+ logging,
+ 'staticOutput',
+ () => config?.output === 'static'
+ );
+
+ validationResult.hybridOutput = validateSupportKind(
+ hybridOutput,
+ adapterName,
+ logging,
+ 'hybridOutput',
+ () => config?.output === 'hybrid'
+ );
+
+ validationResult.serverOutput = validateSupportKind(
+ serverOutput,
+ adapterName,
+ logging,
+ 'serverOutput',
+ () => config?.output === 'server'
+ );
+ validationResult.assets = validateAssetsFeature(assets, adapterName, config, logging);
+
+ return validationResult;
+}
+
+function validateSupportKind(
+ supportKind: SupportsKind,
+ adapterName: string,
+ logging: LogOptions,
+ featureName: string,
+ hasCorrectConfig: () => boolean
+): boolean {
+ if (supportKind === STABLE) {
+ return true;
+ } else if (supportKind === DEPRECATED) {
+ featureIsDeprecated(adapterName, logging);
+ } else if (supportKind === EXPERIMENTAL) {
+ featureIsExperimental(adapterName, logging);
+ }
+
+ if (hasCorrectConfig() && supportKind === UNSUPPORTED) {
+ featureIsUnsupported(adapterName, logging, featureName);
+ return false;
+ } else {
+ return true;
+ }
+}
+
+function featureIsUnsupported(adapterName: string, logging: LogOptions, featureName: string) {
+ error(
+ logging,
+ `${adapterName}`,
+ `The feature ${featureName} is not supported by the adapter ${adapterName}.`
+ );
+}
+
+function featureIsExperimental(adapterName: string, logging: LogOptions) {
+ warn(logging, `${adapterName}`, 'The feature is experimental and subject to issues or changes.');
+}
+
+function featureIsDeprecated(adapterName: string, logging: LogOptions) {
+ warn(
+ logging,
+ `${adapterName}`,
+ 'The feature is deprecated and will be moved in the next release.'
+ );
+}
+
+const SHARP_SERVICE = 'astro/assets/services/sharp';
+const SQUOOSH_SERVICE = 'astro/assets/services/squoosh';
+
+function validateAssetsFeature(
+ assets: AstroAssetsFeature,
+ adapterName: string,
+ config: AstroConfig,
+ logging: LogOptions
+): boolean {
+ const {
+ supportKind = UNSUPPORTED,
+ isSharpCompatible = false,
+ isSquooshCompatible = false,
+ } = assets;
+ if (config?.image?.service?.entrypoint === SHARP_SERVICE && !isSharpCompatible) {
+ error(
+ logging,
+ 'astro',
+ `The currently selected adapter \`${adapterName}\` is not compatible with the image service "Sharp".`
+ );
+ return false;
+ }
+
+ if (config?.image?.service?.entrypoint === SQUOOSH_SERVICE && !isSquooshCompatible) {
+ error(
+ logging,
+ 'astro',
+ `The currently selected adapter \`${adapterName}\` is not compatible with the image service "Squoosh".`
+ );
+ return false;
+ }
+
+ return validateSupportKind(supportKind, adapterName, logging, 'assets', () => true);
+}
diff --git a/packages/astro/src/integrations/index.ts b/packages/astro/src/integrations/index.ts
index cf50df0e1..71c5a5e63 100644
--- a/packages/astro/src/integrations/index.ts
+++ b/packages/astro/src/integrations/index.ts
@@ -4,7 +4,9 @@ import type { AddressInfo } from 'node:net';
import { fileURLToPath } from 'node:url';
import type { InlineConfig, ViteDevServer } from 'vite';
import type {
+ AstroAdapter,
AstroConfig,
+ AstroIntegration,
AstroRenderer,
AstroSettings,
ContentEntryType,
@@ -16,8 +18,9 @@ import type { SerializedSSRManifest } from '../core/app/types';
import type { PageBuildData } from '../core/build/types';
import { buildClientDirectiveEntrypoint } from '../core/client-directive/index.js';
import { mergeConfig } from '../core/config/index.js';
-import { info, type LogOptions } from '../core/logger/core.js';
+import { AstroIntegrationLogger, error, info, warn, type LogOptions } from '../core/logger/core.js';
import { isServerLikeOutput } from '../prerender/utils.js';
+import { validateSupportedFeatures } from './astroFeaturesValidation.js';
async function withTakingALongTimeMsg<T>({
name,
@@ -38,6 +41,19 @@ async function withTakingALongTimeMsg<T>({
return result;
}
+// Used internally to store instances of loggers.
+const Loggers = new WeakMap<AstroIntegration, AstroIntegrationLogger>();
+
+function getLogger(integration: AstroIntegration, logging: LogOptions) {
+ if (Loggers.has(integration)) {
+ // SAFETY: we check the existence in the if block
+ return Loggers.get(integration)!;
+ }
+ const logger = new AstroIntegrationLogger(logging, integration.name);
+ Loggers.set(integration, logger);
+ return logger;
+}
+
export async function runHookConfigSetup({
settings,
command,
@@ -72,6 +88,8 @@ export async function runHookConfigSetup({
* ```
*/
if (integration.hooks?.['astro:config:setup']) {
+ const logger = getLogger(integration, logging);
+
const hooks: HookParameters<'astro:config:setup'> = {
config: updatedConfig,
command,
@@ -107,6 +125,7 @@ export async function runHookConfigSetup({
}
addedClientDirectives.set(name, buildClientDirectiveEntrypoint(name, entrypoint));
},
+ logger,
};
// ---
@@ -167,6 +186,7 @@ export async function runHookConfigDone({
logging: LogOptions;
}) {
for (const integration of settings.config.integrations) {
+ const logger = getLogger(integration, logging);
if (integration?.hooks?.['astro:config:done']) {
await withTakingALongTimeMsg({
name: integration.name,
@@ -178,8 +198,44 @@ export async function runHookConfigDone({
`Integration "${integration.name}" conflicts with "${settings.adapter.name}". You can only configure one deployment integration.`
);
}
+ if (!adapter.supportedAstroFeatures) {
+ // NOTE: throw an error in Astro 4.0
+ warn(
+ logging,
+ 'astro',
+ `The adapter ${adapter.name} doesn't provide a feature map. From Astro 3.0, an adapter can provide a feature map. Not providing a feature map will cause an error in Astro 4.0.`
+ );
+ } else {
+ const validationResult = validateSupportedFeatures(
+ adapter.name,
+ adapter.supportedAstroFeatures,
+ settings.config,
+ logging
+ );
+ for (const [featureName, supported] of Object.entries(validationResult)) {
+ if (!supported) {
+ error(
+ logging,
+ 'astro',
+ `The adapter ${adapter.name} doesn't support the feature ${featureName}. Your project won't be built. You should not use it.`
+ );
+ }
+ }
+ if (!validationResult.assets) {
+ info(
+ logging,
+ 'astro',
+ `The selected adapter ${adapter.name} does not support Sharp or Squoosh for image processing. To ensure your project is still able to build, image processing has been disabled.`
+ );
+ settings.config.image.service = {
+ entrypoint: 'astro/assets/services/noop',
+ config: {},
+ };
+ }
+ }
settings.adapter = adapter;
},
+ logger,
}),
logging,
});
@@ -198,9 +254,10 @@ export async function runHookServerSetup({
}) {
for (const integration of config.integrations) {
if (integration?.hooks?.['astro:server:setup']) {
+ const logger = getLogger(integration, logging);
await withTakingALongTimeMsg({
name: integration.name,
- hookResult: integration.hooks['astro:server:setup']({ server }),
+ hookResult: integration.hooks['astro:server:setup']({ server, logger }),
logging,
});
}
@@ -217,10 +274,12 @@ export async function runHookServerStart({
logging: LogOptions;
}) {
for (const integration of config.integrations) {
+ const logger = getLogger(integration, logging);
+
if (integration?.hooks?.['astro:server:start']) {
await withTakingALongTimeMsg({
name: integration.name,
- hookResult: integration.hooks['astro:server:start']({ address }),
+ hookResult: integration.hooks['astro:server:start']({ address, logger }),
logging,
});
}
@@ -235,10 +294,12 @@ export async function runHookServerDone({
logging: LogOptions;
}) {
for (const integration of config.integrations) {
+ const logger = getLogger(integration, logging);
+
if (integration?.hooks?.['astro:server:done']) {
await withTakingALongTimeMsg({
name: integration.name,
- hookResult: integration.hooks['astro:server:done'](),
+ hookResult: integration.hooks['astro:server:done']({ logger }),
logging,
});
}
@@ -254,9 +315,11 @@ export async function runHookBuildStart({
}) {
for (const integration of config.integrations) {
if (integration?.hooks?.['astro:build:start']) {
+ const logger = getLogger(integration, logging);
+
await withTakingALongTimeMsg({
name: integration.name,
- hookResult: integration.hooks['astro:build:start'](),
+ hookResult: integration.hooks['astro:build:start']({ logger }),
logging,
});
}
@@ -280,6 +343,8 @@ export async function runHookBuildSetup({
for (const integration of config.integrations) {
if (integration?.hooks?.['astro:build:setup']) {
+ const logger = getLogger(integration, logging);
+
await withTakingALongTimeMsg({
name: integration.name,
hookResult: integration.hooks['astro:build:setup']({
@@ -289,6 +354,7 @@ export async function runHookBuildSetup({
updateConfig: (newConfig) => {
updatedConfig = mergeConfig(updatedConfig, newConfig);
},
+ logger,
}),
logging,
});
@@ -315,12 +381,15 @@ export async function runHookBuildSsr({
}: RunHookBuildSsr) {
for (const integration of config.integrations) {
if (integration?.hooks?.['astro:build:ssr']) {
+ const logger = getLogger(integration, logging);
+
await withTakingALongTimeMsg({
name: integration.name,
hookResult: integration.hooks['astro:build:ssr']({
manifest,
entryPoints,
middlewareEntryPoint,
+ logger,
}),
logging,
});
@@ -338,10 +407,12 @@ export async function runHookBuildGenerated({
const dir = isServerLikeOutput(config) ? config.build.client : config.outDir;
for (const integration of config.integrations) {
+ const logger = getLogger(integration, logging);
+
if (integration?.hooks?.['astro:build:generated']) {
await withTakingALongTimeMsg({
name: integration.name,
- hookResult: integration.hooks['astro:build:generated']({ dir }),
+ hookResult: integration.hooks['astro:build:generated']({ dir, logger }),
logging,
});
}
@@ -361,15 +432,34 @@ export async function runHookBuildDone({ config, pages, routes, logging }: RunHo
for (const integration of config.integrations) {
if (integration?.hooks?.['astro:build:done']) {
+ const logger = getLogger(integration, logging);
+
await withTakingALongTimeMsg({
name: integration.name,
hookResult: integration.hooks['astro:build:done']({
pages: pages.map((p) => ({ pathname: p })),
dir,
routes,
+ logger,
}),
logging,
});
}
}
}
+
+export function isFunctionPerRouteEnabled(adapter: AstroAdapter | undefined): boolean {
+ if (adapter?.adapterFeatures?.functionPerRoute === true) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+export function isEdgeMiddlewareEnabled(adapter: AstroAdapter | undefined): boolean {
+ if (adapter?.adapterFeatures?.edgeMiddleware === true) {
+ return true;
+ } else {
+ return false;
+ }
+}
diff --git a/packages/astro/src/jsx/server.ts b/packages/astro/src/jsx/server.ts
index 6374b1ebd..d445ee3a5 100644
--- a/packages/astro/src/jsx/server.ts
+++ b/packages/astro/src/jsx/server.ts
@@ -1,3 +1,4 @@
+import { AstroError } from '../core/errors/errors.js';
import { AstroJSX, jsx } from '../jsx-runtime/index.js';
import { renderJSX } from '../runtime/server/jsx.js';
@@ -22,7 +23,7 @@ export async function check(
// if the exception is from an mdx component
// throw an error
if (Component[Symbol.for('mdx-component')]) {
- throw createFormattedError({
+ throw new AstroError({
message: error.message,
title: error.name,
hint: `This issue often occurs when your MDX component encounters runtime errors.`,
@@ -51,23 +52,6 @@ export async function renderToStaticMarkup(
return { html };
}
-type FormatErrorOptions = {
- message: string;
- name: string;
- stack?: string;
- hint: string;
- title: string;
-};
-// TODO: Remove this function and use `AstroError` when we refactor it to be usable without error codes
-function createFormattedError({ message, name, stack, hint }: FormatErrorOptions) {
- const error = new Error(message);
- error.name = name;
- error.stack = stack;
- // @ts-expect-error - hint is not part of the Error interface but it will be picked up by the error overlay
- error.hint = hint;
- return error;
-}
-
export default {
check,
renderToStaticMarkup,
diff --git a/packages/astro/src/prerender/routing.ts b/packages/astro/src/prerender/routing.ts
index a3b3c0d54..2fcfe207b 100644
--- a/packages/astro/src/prerender/routing.ts
+++ b/packages/astro/src/prerender/routing.ts
@@ -1,22 +1,22 @@
import type { AstroSettings, ComponentInstance, RouteData } from '../@types/astro';
import { RedirectComponentInstance, routeIsRedirect } from '../core/redirects/index.js';
-import type { DevelopmentEnvironment } from '../core/render';
import { preload } from '../vite-plugin-astro-server/index.js';
import { getPrerenderStatus } from './metadata.js';
+import type DevPipeline from '../vite-plugin-astro-server/devPipeline';
type GetSortedPreloadedMatchesParams = {
- env: DevelopmentEnvironment;
+ pipeline: DevPipeline;
matches: RouteData[];
settings: AstroSettings;
};
export async function getSortedPreloadedMatches({
- env,
+ pipeline,
matches,
settings,
}: GetSortedPreloadedMatchesParams) {
return (
await preloadAndSetPrerenderStatus({
- env,
+ pipeline,
matches,
settings,
})
@@ -24,7 +24,7 @@ export async function getSortedPreloadedMatches({
}
type PreloadAndSetPrerenderStatusParams = {
- env: DevelopmentEnvironment;
+ pipeline: DevPipeline;
matches: RouteData[];
settings: AstroSettings;
};
@@ -36,7 +36,7 @@ type PreloadAndSetPrerenderStatusResult = {
};
async function preloadAndSetPrerenderStatus({
- env,
+ pipeline,
matches,
settings,
}: PreloadAndSetPrerenderStatusParams): Promise<PreloadAndSetPrerenderStatusResult[]> {
@@ -52,12 +52,12 @@ async function preloadAndSetPrerenderStatus({
};
}
- const preloadedComponent = await preload({ env, filePath });
+ const preloadedComponent = await preload({ pipeline, filePath });
// gets the prerender metadata set by the `astro:scanner` vite plugin
const prerenderStatus = getPrerenderStatus({
filePath,
- loader: env.loader,
+ loader: pipeline.getModuleLoader(),
});
if (prerenderStatus !== undefined) {
diff --git a/packages/astro/src/prerender/utils.ts b/packages/astro/src/prerender/utils.ts
index bd6e367ad..a3655eead 100644
--- a/packages/astro/src/prerender/utils.ts
+++ b/packages/astro/src/prerender/utils.ts
@@ -1,4 +1,5 @@
import type { AstroConfig } from '../@types/astro';
+import { getOutDirWithinCwd } from '../core/build/common.js';
export function isServerLikeOutput(config: AstroConfig) {
return config.output === 'server' || config.output === 'hybrid';
@@ -7,3 +8,15 @@ export function isServerLikeOutput(config: AstroConfig) {
export function getPrerenderDefault(config: AstroConfig) {
return config.output === 'hybrid';
}
+
+/**
+ * Returns the correct output directory of hte SSR build based on the configuration
+ */
+export function getOutputDirectory(config: AstroConfig): URL {
+ const ssr = isServerLikeOutput(config);
+ if (ssr) {
+ return config.build.server;
+ } else {
+ return getOutDirWithinCwd(config.outDir);
+ }
+}
diff --git a/packages/astro/src/runtime/README.md b/packages/astro/src/runtime/README.md
index a11a98d8c..68225fed1 100644
--- a/packages/astro/src/runtime/README.md
+++ b/packages/astro/src/runtime/README.md
@@ -4,5 +4,6 @@ Code that executes within isolated contexts:
- `client/`: executes within the browser. Astro’s client-side partial hydration code lives here, and only browser-compatible code can be used.
- `server/`: executes inside Vite SSR. Though also a Node context, this is isolated from code in `core/`.
+- `compiler/`: same as `server/`, but only used by the Astro compiler `internalURL` option.
[See CONTRIBUTING.md](../../../../CONTRIBUTING.md) for a code overview.
diff --git a/packages/astro/src/runtime/compiler/index.ts b/packages/astro/src/runtime/compiler/index.ts
new file mode 100644
index 000000000..a5c238b68
--- /dev/null
+++ b/packages/astro/src/runtime/compiler/index.ts
@@ -0,0 +1,20 @@
+// NOTE: Although this entrypoint is exported, it is internal API and may change at any time.
+
+export {
+ Fragment,
+ render,
+ createAstro,
+ createComponent,
+ renderComponent,
+ renderHead,
+ maybeRenderHead,
+ unescapeHTML,
+ renderSlot,
+ mergeSlots,
+ addAttribute,
+ renderTransition,
+ createTransitionScope,
+ spreadAttributes,
+ defineStyleVars,
+ defineScriptVars,
+} from '../server/index.js';
diff --git a/packages/astro/src/runtime/server/endpoint.ts b/packages/astro/src/runtime/server/endpoint.ts
index c56ab7646..89c35957c 100644
--- a/packages/astro/src/runtime/server/endpoint.ts
+++ b/packages/astro/src/runtime/server/endpoint.ts
@@ -1,28 +1,56 @@
import type { APIContext, EndpointHandler, Params } from '../../@types/astro';
+import { type LogOptions, warn } from '../../core/logger/core.js';
-function getHandlerFromModule(mod: EndpointHandler, method: string) {
+function getHandlerFromModule(mod: EndpointHandler, method: string, logging: LogOptions) {
+ const lowerCaseMethod = method.toLowerCase();
+
+ // TODO: remove in Astro 4.0
+ if (mod[lowerCaseMethod]) {
+ warn(
+ logging,
+ 'astro',
+ `Lower case endpoint names are deprecated and will not be supported in Astro 4.0. Rename the endpoint ${lowerCaseMethod} to ${method}.`
+ );
+ }
// If there was an exact match on `method`, return that function.
if (mod[method]) {
return mod[method];
}
+
+ // TODO: remove in Astro 4.0
+ if (mod[lowerCaseMethod]) {
+ return mod[lowerCaseMethod];
+ }
+ // TODO: remove in Astro 4.0
// Handle `del` instead of `delete`, since `delete` is a reserved word in JS.
if (method === 'delete' && mod['del']) {
return mod['del'];
}
+ // TODO: remove in Astro 4.0
// If a single `all` handler was used, return that function.
if (mod['all']) {
return mod['all'];
}
+ if (mod['ALL']) {
+ return mod['ALL'];
+ }
// Otherwise, no handler found.
return undefined;
}
/** Renders an endpoint request to completion, returning the body. */
-export async function renderEndpoint(mod: EndpointHandler, context: APIContext, ssr: boolean) {
- const { request, params } = context;
- const chosenMethod = request.method?.toLowerCase();
- const handler = getHandlerFromModule(mod, chosenMethod);
- if (!ssr && ssr === false && chosenMethod && chosenMethod !== 'get') {
+export async function renderEndpoint(
+ mod: EndpointHandler,
+ context: APIContext,
+ ssr: boolean,
+ logging: LogOptions
+) {
+ const { request } = context;
+
+ const chosenMethod = request.method?.toUpperCase();
+ const handler = getHandlerFromModule(mod, chosenMethod, logging);
+ // TODO: remove the 'get' check in Astro 4.0
+ if (!ssr && ssr === false && chosenMethod && chosenMethod !== 'GET' && chosenMethod !== 'get') {
// eslint-disable-next-line no-console
console.warn(`
${chosenMethod} requests are not available when building a static site. Update your config to \`output: 'server'\` or \`output: 'hybrid'\` with an \`export const prerender = false\` to handle ${chosenMethod} requests.`);
@@ -40,35 +68,10 @@ ${chosenMethod} requests are not available when building a static site. Update y
return response;
}
- // TODO: Remove support for old API in Astro 3.0
- if (handler.length > 1) {
- // eslint-disable-next-line no-console
- console.warn(`
-API routes with 2 arguments have been deprecated. Instead they take a single argument in the form of:
-
-export function get({ params, request }) {
- //...
-}
-
-Update your code to remove this warning.`);
- }
-
const proxy = new Proxy(context, {
get(target, prop) {
if (prop in target) {
return Reflect.get(target, prop);
- } else if (prop in params) {
- // TODO: Remove support for old API in Astro 3.0
- // eslint-disable-next-line no-console
- console.warn(`
-API routes no longer pass params as the first argument. Instead an object containing a params property is provided in the form of:
-
-export function get({ params }) {
- // ...
-}
-
-Update your code to remove this warning.`);
- return Reflect.get(params, prop);
} else {
return undefined;
}
diff --git a/packages/astro/src/runtime/server/hydration.ts b/packages/astro/src/runtime/server/hydration.ts
index 11cce522a..81a3cf9e2 100644
--- a/packages/astro/src/runtime/server/hydration.ts
+++ b/packages/astro/src/runtime/server/hydration.ts
@@ -7,7 +7,6 @@ import type {
import { AstroError, AstroErrorData } from '../../core/errors/index.js';
import { escapeHTML } from './escape.js';
import { serializeProps } from './serialize.js';
-import { serializeListValue } from './util.js';
export interface HydrationMetadata {
directive: string;
@@ -95,11 +94,6 @@ export function extractDirectives(
break;
}
}
- } else if (key === 'class:list') {
- if (value) {
- // support "class" from an expression passed into a component (#782)
- extracted.props[key.slice(0, -5)] = serializeListValue(value);
- }
} else {
extracted.props[key] = value;
}
diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts
index 5d4697bc7..81d05987a 100644
--- a/packages/astro/src/runtime/server/index.ts
+++ b/packages/astro/src/runtime/server/index.ts
@@ -1,3 +1,5 @@
+// NOTE: Although this entrypoint is exported, it is internal API and may change at any time.
+
export { createComponent } from './astro-component.js';
export { createAstro } from './astro-global.js';
export { renderEndpoint } from './endpoint.js';
diff --git a/packages/astro/src/runtime/server/render/component.ts b/packages/astro/src/runtime/server/render/component.ts
index 5f61e68b5..36528daf1 100644
--- a/packages/astro/src/runtime/server/render/component.ts
+++ b/packages/astro/src/runtime/server/render/component.ts
@@ -6,6 +6,7 @@ import type {
} from '../../../@types/astro';
import { createRenderInstruction, type RenderInstruction } from './instruction.js';
+import { clsx } from 'clsx';
import { AstroError, AstroErrorData } from '../../../core/errors/index.js';
import { HTMLBytes, markHTMLString } from '../escape.js';
import { extractDirectives, generateHydrateScript } from '../hydration.js';
@@ -461,6 +462,9 @@ export async function renderComponent(
return await renderFragmentComponent(result, slots);
}
+ // Ensure directives (`class:list`) are processed
+ props = normalizeProps(props);
+
// .html components
if (isHTMLComponent(Component)) {
return await renderHTMLComponent(result, Component, props, slots);
@@ -473,6 +477,18 @@ export async function renderComponent(
return await renderFrameworkComponent(result, displayName, Component, props, slots);
}
+function normalizeProps(props: Record<string, any>): Record<string, any> {
+ if (props['class:list'] !== undefined) {
+ const value = props['class:list'];
+ delete props['class:list'];
+ props['class'] = clsx(props['class'], value)
+ if (props['class'] === '') {
+ delete props['class']
+ }
+ }
+ return props;
+}
+
export async function renderComponentToString(
result: SSRResult,
displayName: string,
diff --git a/packages/astro/src/runtime/server/render/page.ts b/packages/astro/src/runtime/server/render/page.ts
index cabbe8dae..74e8a45b7 100644
--- a/packages/astro/src/runtime/server/render/page.ts
+++ b/packages/astro/src/runtime/server/render/page.ts
@@ -2,7 +2,6 @@ import type { RouteData, SSRResult } from '../../../@types/astro';
import { renderComponentToString, type NonAstroPageComponent } from './component.js';
import type { AstroComponentFactory } from './index';
-import { createResponse } from '../response.js';
import { isAstroComponentFactory } from './astro/index.js';
import { renderToReadableStream, renderToString } from './astro/render.js';
import { encoder } from './common.js';
@@ -64,6 +63,6 @@ export async function renderPage(
body = encoder.encode(body);
headers.set('Content-Length', body.byteLength.toString());
}
- const response = createResponse(body, { ...init, headers });
+ const response = new Response(body, { ...init, headers });
return response;
}
diff --git a/packages/astro/src/runtime/server/render/util.ts b/packages/astro/src/runtime/server/render/util.ts
index e007fe6f1..e77f8ed8b 100644
--- a/packages/astro/src/runtime/server/render/util.ts
+++ b/packages/astro/src/runtime/server/render/util.ts
@@ -1,7 +1,7 @@
import type { SSRElement } from '../../../@types/astro';
import { HTMLString, markHTMLString } from '../escape.js';
-import { serializeListValue } from '../util.js';
+import { clsx } from 'clsx';
export const voidElementNames =
/^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i;
@@ -29,10 +29,6 @@ const toStyleString = (obj: Record<string, any>) =>
Object.entries(obj)
.map(([k, v]) => {
if (k[0] !== '-' && k[1] !== '-') return `${kebab(k)}:${v}`;
- // TODO: Remove in v3! See #6264
- // We need to emit --kebab-case AND --camelCase for backwards-compat in v2,
- // but we should be able to remove this workaround in v3.
- if (kebab(k) !== k) return `${kebab(k)}:var(${k});${k}:${v}`;
return `${k}:${v}`;
})
.join(';');
@@ -82,7 +78,7 @@ Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the
// support "class" from an expression passed into an element (#782)
if (key === 'class:list') {
- const listValue = toAttributeString(serializeListValue(value), shouldEscape);
+ const listValue = toAttributeString(clsx(value), shouldEscape);
if (listValue === '') {
return '';
}
diff --git a/packages/astro/src/runtime/server/response.ts b/packages/astro/src/runtime/server/response.ts
deleted file mode 100644
index bcfda19aa..000000000
--- a/packages/astro/src/runtime/server/response.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import { streamAsyncIterator } from './util.js';
-
-const isNodeJS =
- typeof process === 'object' && Object.prototype.toString.call(process) === '[object process]';
-
-let StreamingCompatibleResponse: typeof Response | undefined;
-
-function createResponseClass() {
- StreamingCompatibleResponse = class extends Response {
- #isStream: boolean;
- #body: any;
- constructor(body?: BodyInit | null, init?: ResponseInit) {
- let isStream = body instanceof ReadableStream;
- super(isStream ? null : body, init);
- this.#isStream = isStream;
- this.#body = body;
- }
-
- get body() {
- return this.#body;
- }
-
- async text(): Promise<string> {
- if (this.#isStream && isNodeJS) {
- let decoder = new TextDecoder();
- let body = this.#body;
- let out = '';
- for await (let chunk of streamAsyncIterator(body)) {
- out += decoder.decode(chunk);
- }
- return out;
- }
- return super.text();
- }
-
- async arrayBuffer(): Promise<ArrayBuffer> {
- if (this.#isStream && isNodeJS) {
- let body = this.#body;
- let chunks: Uint8Array[] = [];
- let len = 0;
- for await (let chunk of streamAsyncIterator(body)) {
- chunks.push(chunk);
- len += chunk.length;
- }
- let ab = new Uint8Array(len);
- let offset = 0;
- for (const chunk of chunks) {
- ab.set(chunk, offset);
- offset += chunk.length;
- }
- return ab;
- }
- return super.arrayBuffer();
- }
-
- clone() {
- return new StreamingCompatibleResponse!(this.#body, {
- status: this.status,
- statusText: this.statusText,
- headers: this.headers,
- });
- }
- };
-
- return StreamingCompatibleResponse;
-}
-
-type CreateResponseFn = (body?: BodyInit | null, init?: ResponseInit) => Response;
-
-export const createResponse: CreateResponseFn = isNodeJS
- ? (body, init) => {
- if (typeof body === 'string' || ArrayBuffer.isView(body)) {
- return new Response(body, init);
- }
- if (typeof StreamingCompatibleResponse === 'undefined') {
- return new (createResponseClass())(body, init);
- }
- return new StreamingCompatibleResponse(body, init);
- }
- : (body, init) => new Response(body, init);
diff --git a/packages/astro/src/runtime/server/util.ts b/packages/astro/src/runtime/server/util.ts
index b38fe5ef1..97b953c72 100644
--- a/packages/astro/src/runtime/server/util.ts
+++ b/packages/astro/src/runtime/server/util.ts
@@ -1,33 +1,3 @@
-export function serializeListValue(value: any) {
- const hash: Record<string, any> = {};
-
- push(value);
-
- return Object.keys(hash).join(' ');
-
- function push(item: any) {
- // push individual iteratables
- if (item && typeof item.forEach === 'function') item.forEach(push);
- // otherwise, push object value keys by truthiness
- else if (item === Object(item))
- Object.keys(item).forEach((name) => {
- if (item[name]) push(name);
- });
- // otherwise, push any other values as a string
- else {
- // get the item as a string
- item = item === false || item == null ? '' : String(item).trim();
-
- // add the item if it is filled
- if (item) {
- item.split(/\s+/).forEach((name: string) => {
- hash[name] = true;
- });
- }
- }
- }
-}
-
export function isPromise<T = any>(value: any): value is Promise<T> {
return !!value && typeof value === 'object' && typeof value.then === 'function';
}
diff --git a/packages/astro/src/vite-plugin-astro-postprocess/index.ts b/packages/astro/src/vite-plugin-astro-postprocess/index.ts
index 9a2e185af..39acd000c 100644
--- a/packages/astro/src/vite-plugin-astro-postprocess/index.ts
+++ b/packages/astro/src/vite-plugin-astro-postprocess/index.ts
@@ -1,4 +1,5 @@
import { parse } from 'acorn';
+import type { Node as ESTreeNode } from 'estree-walker';
import { walk } from 'estree-walker';
import MagicString from 'magic-string';
import type { Plugin } from 'vite';
@@ -28,7 +29,7 @@ export default function astro(): Plugin {
sourceType: 'module',
});
- walk(ast, {
+ walk(ast as ESTreeNode, {
enter(node: any) {
// Transform `Astro.glob("./pages/*.astro")` to `Astro.glob(import.meta.glob("./pages/*.astro"), () => "./pages/*.astro")`
// Also handle for `Astro2.glob()`
diff --git a/packages/astro/src/vite-plugin-astro-server/devPipeline.ts b/packages/astro/src/vite-plugin-astro-server/devPipeline.ts
new file mode 100644
index 000000000..a17472895
--- /dev/null
+++ b/packages/astro/src/vite-plugin-astro-server/devPipeline.ts
@@ -0,0 +1,91 @@
+import { Pipeline } from '../core/pipeline.js';
+import type { AstroConfig, AstroSettings } from '../@types/astro';
+import type { ModuleLoader } from '../core/module-loader';
+import type { Environment } from '../core/render';
+import { createEnvironment, loadRenderer } from '../core/render/index.js';
+import { createResolve } from './resolve.js';
+import { RouteCache } from '../core/render/route-cache.js';
+import { isServerLikeOutput } from '../prerender/utils.js';
+import type { RuntimeMode, SSRManifest, SSRLoadedRenderer } from '../@types/astro';
+import type { LogOptions } from '../core/logger/core';
+import { Logger } from '../core/logger/core.js';
+
+export default class DevPipeline extends Pipeline {
+ #settings: AstroSettings;
+ #loader: ModuleLoader;
+ #devLogger: Logger;
+
+ constructor({
+ manifest,
+ logging,
+ settings,
+ loader,
+ }: {
+ manifest: SSRManifest;
+ logging: LogOptions;
+ settings: AstroSettings;
+ loader: ModuleLoader;
+ }) {
+ const env = DevPipeline.createDevelopmentEnvironment(manifest, settings, logging, loader);
+ super(env);
+ this.#devLogger = new Logger(logging);
+ this.#settings = settings;
+ this.#loader = loader;
+ this.setEndpointHandler(this.#handleEndpointResult);
+ }
+
+ clearRouteCache() {
+ this.env.routeCache.clearAll();
+ }
+
+ getSettings(): Readonly<AstroSettings> {
+ return this.#settings;
+ }
+
+ getConfig(): Readonly<AstroConfig> {
+ return this.#settings.config;
+ }
+
+ getModuleLoader(): Readonly<ModuleLoader> {
+ return this.#loader;
+ }
+
+ get logger(): Readonly<Logger> {
+ return this.#devLogger;
+ }
+
+ async loadRenderers() {
+ const renderers = await Promise.all(
+ this.#settings.renderers.map((r) => loadRenderer(r, this.#loader))
+ );
+ this.env.renderers = renderers.filter(Boolean) as SSRLoadedRenderer[];
+ }
+
+ static createDevelopmentEnvironment(
+ manifest: SSRManifest,
+ settings: AstroSettings,
+ logging: LogOptions,
+ loader: ModuleLoader
+ ): Environment {
+ const mode: RuntimeMode = 'development';
+
+ return createEnvironment({
+ adapterName: manifest.adapterName,
+ logging,
+ mode,
+ // This will be overridden in the dev server
+ renderers: [],
+ clientDirectives: manifest.clientDirectives,
+ compressHTML: manifest.compressHTML,
+ resolve: createResolve(loader, settings.config.root),
+ routeCache: new RouteCache(logging, mode),
+ site: manifest.site,
+ ssr: isServerLikeOutput(settings.config),
+ streaming: true,
+ });
+ }
+
+ async #handleEndpointResult(_: Request, response: Response): Promise<Response> {
+ return response;
+ }
+}
diff --git a/packages/astro/src/vite-plugin-astro-server/environment.ts b/packages/astro/src/vite-plugin-astro-server/environment.ts
index bcf783bf2..010c1b96a 100644
--- a/packages/astro/src/vite-plugin-astro-server/environment.ts
+++ b/packages/astro/src/vite-plugin-astro-server/environment.ts
@@ -1,7 +1,7 @@
import type { AstroSettings, RuntimeMode, SSRManifest } from '../@types/astro.js';
import type { LogOptions } from '../core/logger/core.js';
import type { ModuleLoader } from '../core/module-loader';
-import type { DevelopmentEnvironment } from '../core/render';
+import type { Environment } from '../core/render';
import { createEnvironment } from '../core/render/index.js';
import { RouteCache } from '../core/render/route-cache.js';
import { isServerLikeOutput } from '../prerender/utils.js';
@@ -12,12 +12,11 @@ export function createDevelopmentEnvironment(
settings: AstroSettings,
logging: LogOptions,
loader: ModuleLoader
-): DevelopmentEnvironment {
+): Environment {
const mode: RuntimeMode = 'development';
- let env = createEnvironment({
+ return createEnvironment({
adapterName: manifest.adapterName,
logging,
- markdown: manifest.markdown,
mode,
// This will be overridden in the dev server
renderers: [],
@@ -29,10 +28,4 @@ export function createDevelopmentEnvironment(
ssr: isServerLikeOutput(settings.config),
streaming: true,
});
-
- return {
- ...env,
- loader,
- settings,
- };
}
diff --git a/packages/astro/src/vite-plugin-astro-server/index.ts b/packages/astro/src/vite-plugin-astro-server/index.ts
index 17302ba41..b02c57e1c 100644
--- a/packages/astro/src/vite-plugin-astro-server/index.ts
+++ b/packages/astro/src/vite-plugin-astro-server/index.ts
@@ -1,26 +1,22 @@
import type { ComponentInstance } from '../@types/astro.js';
import { enhanceViteSSRError } from '../core/errors/dev/index.js';
import { AggregateError, CSSError, MarkdownError } from '../core/errors/index.js';
-import type { DevelopmentEnvironment } from '../core/render/environment';
-import { loadRenderers } from '../core/render/index.js';
import { viteID } from '../core/util.js';
+import type DevPipeline from './devPipeline';
export async function preload({
- env,
+ pipeline,
filePath,
}: {
- env: DevelopmentEnvironment;
+ pipeline: DevPipeline;
filePath: URL;
}): Promise<ComponentInstance> {
// Important: This needs to happen first, in case a renderer provides polyfills.
- const renderers = await loadRenderers(env.settings, env.loader);
- // Override the environment's renderers. This ensures that if renderers change (HMR)
- // The new instances are passed through.
- env.renderers = renderers;
+ await pipeline.loadRenderers();
try {
// Load the module from the Vite SSR Runtime.
- const mod = (await env.loader.import(viteID(filePath))) as ComponentInstance;
+ const mod = (await pipeline.getModuleLoader().import(viteID(filePath))) as ComponentInstance;
return mod;
} catch (error) {
@@ -29,7 +25,7 @@ export async function preload({
throw error;
}
- throw enhanceViteSSRError({ error, filePath, loader: env.loader });
+ throw enhanceViteSSRError({ error, filePath, loader: pipeline.getModuleLoader() });
}
}
diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts
index 8f74bd47a..262d50148 100644
--- a/packages/astro/src/vite-plugin-astro-server/plugin.ts
+++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts
@@ -7,8 +7,8 @@ import { createViteLoader } from '../core/module-loader/index.js';
import { createRouteManifest } from '../core/routing/index.js';
import { baseMiddleware } from './base.js';
import { createController } from './controller.js';
-import { createDevelopmentEnvironment } from './environment.js';
import { handleRequest } from './request.js';
+import DevPipeline from './devPipeline.js';
export interface AstroPluginOptions {
settings: AstroSettings;
@@ -26,13 +26,13 @@ export default function createVitePluginAstroServer({
configureServer(viteServer) {
const loader = createViteLoader(viteServer);
const manifest = createDevelopmentManifest(settings);
- const env = createDevelopmentEnvironment(manifest, settings, logging, loader);
+ const pipeline = new DevPipeline({ logging, manifest, settings, loader });
let manifestData: ManifestData = createRouteManifest({ settings, fsMod }, logging);
const controller = createController({ loader });
/** rebuild the route cache + manifest, as needed. */
function rebuildManifest(needsManifestRebuild: boolean) {
- env.routeCache.clearAll();
+ pipeline.clearRouteCache();
if (needsManifestRebuild) {
manifestData = createRouteManifest({ settings }, logging);
}
@@ -57,7 +57,7 @@ export default function createVitePluginAstroServer({
return;
}
handleRequest({
- env,
+ pipeline,
manifestData,
controller,
incomingRequest: request,
@@ -91,7 +91,6 @@ export function createDevelopmentManifest(settings: AstroSettings): SSRManifest
entryModules: {},
routes: [],
adapterName: '',
- markdown: settings.config.markdown,
clientDirectives: settings.clientDirectives,
renderers: [],
base: settings.config.base,
diff --git a/packages/astro/src/vite-plugin-astro-server/request.ts b/packages/astro/src/vite-plugin-astro-server/request.ts
index b641503a6..72ce29b1b 100644
--- a/packages/astro/src/vite-plugin-astro-server/request.ts
+++ b/packages/astro/src/vite-plugin-astro-server/request.ts
@@ -1,11 +1,8 @@
import type http from 'node:http';
import type { ManifestData, SSRManifest } from '../@types/astro';
-import type { DevelopmentEnvironment } from '../core/render/index';
import type { DevServerController } from './controller';
-
import { collectErrorMetadata } from '../core/errors/dev/index.js';
import { createSafeError } from '../core/errors/index.js';
-import { error } from '../core/logger/core.js';
import * as msg from '../core/messages.js';
import { collapseDuplicateSlashes, removeTrailingForwardSlash } from '../core/path.js';
import { eventError, telemetry } from '../events/index.js';
@@ -13,9 +10,10 @@ import { isServerLikeOutput } from '../prerender/utils.js';
import { runWithErrorHandling } from './controller.js';
import { handle500Response } from './response.js';
import { handleRoute, matchRoute } from './route.js';
+import type DevPipeline from './devPipeline';
type HandleRequest = {
- env: DevelopmentEnvironment;
+ pipeline: DevPipeline;
manifestData: ManifestData;
controller: DevServerController;
incomingRequest: http.IncomingMessage;
@@ -25,15 +23,15 @@ type HandleRequest = {
/** The main logic to route dev server requests to pages in Astro. */
export async function handleRequest({
- env,
+ pipeline,
manifestData,
controller,
incomingRequest,
incomingResponse,
manifest,
}: HandleRequest) {
- const { settings, loader: moduleLoader } = env;
- const { config } = settings;
+ const config = pipeline.getConfig();
+ const moduleLoader = pipeline.getModuleLoader();
const origin = `${moduleLoader.isHttps() ? 'https' : 'http'}://${incomingRequest.headers.host}`;
const buildingToSSR = isServerLikeOutput(config);
@@ -48,7 +46,7 @@ export async function handleRequest({
// Add config.base back to url before passing it to SSR
url.pathname = removeTrailingForwardSlash(config.base) + url.pathname;
- // HACK! @astrojs/image uses query params for the injected route in `dev`
+ // HACK! astro:assets uses query params for the injected route in `dev`
if (!buildingToSSR && pathname !== '/_image') {
// Prevent user from depending on search params when not doing SSR.
// NOTE: Create an array copy here because deleting-while-iterating
@@ -75,7 +73,7 @@ export async function handleRequest({
controller,
pathname,
async run() {
- const matchedRoute = await matchRoute(pathname, env, manifestData);
+ const matchedRoute = await matchRoute(pathname, manifestData, pipeline);
const resolvedPathname = matchedRoute?.resolvedPathname ?? pathname;
return await handleRoute({
matchedRoute,
@@ -83,7 +81,7 @@ export async function handleRequest({
pathname: resolvedPathname,
body,
origin,
- env,
+ pipeline,
manifestData,
incomingRequest: incomingRequest,
incomingResponse: incomingResponse,
@@ -95,7 +93,7 @@ export async function handleRequest({
// This could be a runtime error from Vite's SSR module, so try to fix it here
try {
- env.loader.fixStacktrace(err);
+ moduleLoader.fixStacktrace(err);
} catch {}
// This is our last line of defense regarding errors where we still might have some information about the request
@@ -104,7 +102,7 @@ export async function handleRequest({
telemetry.record(eventError({ cmd: 'dev', err: errorWithMetadata, isFatal: false }));
- error(env.logging, null, msg.formatErrorMessage(errorWithMetadata));
+ pipeline.logger.error(null, msg.formatErrorMessage(errorWithMetadata));
handle500Response(moduleLoader, incomingResponse, errorWithMetadata);
return err;
diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts
index fa9941168..5604759e6 100644
--- a/packages/astro/src/vite-plugin-astro-server/route.ts
+++ b/packages/astro/src/vite-plugin-astro-server/route.ts
@@ -1,25 +1,15 @@
-import mime from 'mime';
import type http from 'node:http';
import type {
ComponentInstance,
ManifestData,
- MiddlewareResponseHandler,
+ MiddlewareEndpointHandler,
RouteData,
SSRElement,
SSRManifest,
} from '../@types/astro';
-import { attachToResponse } from '../core/cookies/index.js';
import { AstroErrorData, isAstroError } from '../core/errors/index.js';
-import { warn } from '../core/logger/core.js';
import { loadMiddleware } from '../core/middleware/loadMiddleware.js';
-import { isEndpointResult } from '../core/render/core.js';
-import {
- createRenderContext,
- getParamsAndProps,
- tryRenderRoute,
- type DevelopmentEnvironment,
- type SSROptions,
-} from '../core/render/index.js';
+import { createRenderContext, getParamsAndProps, type SSROptions } from '../core/render/index.js';
import { createRequest } from '../core/request.js';
import { matchAllRoutes } from '../core/routing/index.js';
import { isPage, resolveIdToUrl, viteID } from '../core/util.js';
@@ -32,6 +22,7 @@ import { preload } from './index.js';
import { getComponentMetadata } from './metadata.js';
import { handle404Response, writeSSRResult, writeWebResponse } from './response.js';
import { getScriptsForURL } from './scripts.js';
+import type DevPipeline from './devPipeline.js';
const clientLocalsSymbol = Symbol.for('astro.locals');
@@ -49,19 +40,24 @@ export interface MatchedRoute {
mod: ComponentInstance;
}
-function getCustom404Route(manifest: ManifestData): RouteData | undefined {
+function getCustom404Route(manifestData: ManifestData): RouteData | undefined {
const route404 = /^\/404\/?$/;
- return manifest.routes.find((r) => route404.test(r.route));
+ return manifestData.routes.find((r) => route404.test(r.route));
}
export async function matchRoute(
pathname: string,
- env: DevelopmentEnvironment,
- manifest: ManifestData
+ manifestData: ManifestData,
+ pipeline: DevPipeline
): Promise<MatchedRoute | undefined> {
- const { logging, settings, routeCache } = env;
- const matches = matchAllRoutes(pathname, manifest);
- const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings });
+ const env = pipeline.getEnvironment();
+ const { routeCache, logging } = env;
+ const matches = matchAllRoutes(pathname, manifestData);
+ const preloadedMatches = await getSortedPreloadedMatches({
+ pipeline,
+ matches,
+ settings: pipeline.getSettings(),
+ });
for await (const { preloadedComponent, route: maybeRoute, filePath } of preloadedMatches) {
// attempt to get static paths
@@ -73,7 +69,7 @@ export async function matchRoute(
routeCache,
pathname: pathname,
logging,
- ssr: isServerLikeOutput(settings.config),
+ ssr: isServerLikeOutput(pipeline.getConfig()),
});
return {
route: maybeRoute,
@@ -96,14 +92,13 @@ export async function matchRoute(
// build formats, and is necessary based on how the manifest tracks build targets.
const altPathname = pathname.replace(/(index)?\.html$/, '');
if (altPathname !== pathname) {
- return await matchRoute(altPathname, env, manifest);
+ return await matchRoute(altPathname, manifestData, pipeline);
}
if (matches.length) {
const possibleRoutes = matches.flatMap((route) => route.component);
- warn(
- logging,
+ pipeline.logger.warn(
'getStaticPaths',
`${AstroErrorData.NoMatchingStaticPathFound.message(
pathname
@@ -112,11 +107,11 @@ export async function matchRoute(
}
log404(logging, pathname);
- const custom404 = getCustom404Route(manifest);
+ const custom404 = getCustom404Route(manifestData);
if (custom404) {
- const filePath = new URL(`./${custom404.component}`, settings.config.root);
- const preloadedComponent = await preload({ env, filePath });
+ const filePath = new URL(`./${custom404.component}`, pipeline.getConfig().root);
+ const preloadedComponent = await preload({ pipeline, filePath });
return {
route: custom404,
@@ -136,12 +131,12 @@ type HandleRoute = {
pathname: string;
body: ArrayBuffer | undefined;
origin: string;
- env: DevelopmentEnvironment;
manifestData: ManifestData;
incomingRequest: http.IncomingMessage;
incomingResponse: http.ServerResponse;
manifest: SSRManifest;
status?: 404 | 500;
+ pipeline: DevPipeline;
};
export async function handleRoute({
@@ -151,18 +146,21 @@ export async function handleRoute({
status = getStatus(matchedRoute),
body,
origin,
- env,
+ pipeline,
manifestData,
incomingRequest,
incomingResponse,
manifest,
}: HandleRoute): Promise<void> {
- const { logging, settings } = env;
+ const env = pipeline.getEnvironment();
+ const settings = pipeline.getSettings();
+ const config = pipeline.getConfig();
+ const moduleLoader = pipeline.getModuleLoader();
+ const { logging } = env;
if (!matchedRoute) {
return handle404Response(origin, incomingRequest, incomingResponse);
}
- const { config } = settings;
const filePath: URL | undefined = matchedRoute.filePath;
const { route, preloadedComponent } = matchedRoute;
const buildingToSSR = isServerLikeOutput(config);
@@ -192,14 +190,14 @@ export async function handleRoute({
request,
route,
};
- const middleware = await loadMiddleware(env.loader, env.settings.config.srcDir);
+ const middleware = await loadMiddleware(moduleLoader, settings.config.srcDir);
if (middleware) {
options.middleware = middleware;
}
const mod = options.preload;
const { scripts, links, styles, metadata } = await getScriptsAndStyles({
- env: options.env,
+ pipeline,
filePath: options.filePath,
});
@@ -214,70 +212,31 @@ export async function handleRoute({
mod,
env,
});
- const onRequest = options.middleware?.onRequest as MiddlewareResponseHandler | undefined;
-
- const result = await tryRenderRoute(route.type, renderContext, env, mod, onRequest);
- if (isEndpointResult(result, route.type)) {
- if (result.type === 'response') {
- if (result.response.headers.get('X-Astro-Response') === 'Not-Found') {
- const fourOhFourRoute = await matchRoute('/404', env, manifestData);
- return handleRoute({
- matchedRoute: fourOhFourRoute,
- url: new URL('/404', url),
- pathname: '/404',
- status: 404,
- body,
- origin,
- env,
- manifestData,
- incomingRequest,
- incomingResponse,
- manifest,
- });
- }
- await writeWebResponse(incomingResponse, result.response);
- } else {
- let contentType = 'text/plain';
- // Dynamic routes don't include `route.pathname`, so synthesize a path for these (e.g. 'src/pages/[slug].svg')
- const filepath =
- route.pathname ||
- route.segments.map((segment) => segment.map((p) => p.content).join('')).join('/');
- const computedMimeType = mime.getType(filepath);
- if (computedMimeType) {
- contentType = computedMimeType;
- }
- const response = new Response(
- result.encoding !== 'binary' ? Buffer.from(result.body, result.encoding) : result.body,
- {
- status: 200,
- headers: {
- 'Content-Type': `${contentType};charset=utf-8`,
- },
- }
- );
- attachToResponse(response, result.cookies);
- await writeWebResponse(incomingResponse, response);
- }
- } else {
- if (result.status === 404 && has404Route(manifestData)) {
- const fourOhFourRoute = await matchRoute('/404', env, manifestData);
- return handleRoute({
- ...options,
- matchedRoute: fourOhFourRoute,
- url: new URL(pathname, url),
- status: 404,
- body,
- origin,
- env,
- manifestData,
- incomingRequest,
- incomingResponse,
- manifest,
- });
- }
-
- let response = result;
+ const onRequest = options.middleware?.onRequest as MiddlewareEndpointHandler | undefined;
+ if (onRequest) {
+ pipeline.setMiddlewareFunction(onRequest);
+ }
+ let response = await pipeline.renderRoute(renderContext, mod);
+ if (response.status === 404 && has404Route(manifestData)) {
+ const fourOhFourRoute = await matchRoute('/404', manifestData, pipeline);
+ return handleRoute({
+ ...options,
+ matchedRoute: fourOhFourRoute,
+ url: new URL(pathname, url),
+ status: 404,
+ body,
+ origin,
+ pipeline,
+ manifestData,
+ incomingRequest,
+ incomingResponse,
+ manifest,
+ });
+ }
+ if (route.type === 'endpoint') {
+ await writeWebResponse(incomingResponse, response);
+ } else {
if (
// We are in a recursion, and it's possible that this function is called itself with a status code
// By default, the status code passed via parameters is computed by the matched route.
@@ -291,23 +250,26 @@ export async function handleRoute({
return;
} else if (status && response.status !== status && (status === 404 || status === 500)) {
// Response.status is read-only, so a clone is required to override
- response = new Response(result.body, { ...result, status });
+ response = new Response(response.body, { ...response, status });
}
await writeSSRResult(request, response, incomingResponse);
}
}
interface GetScriptsAndStylesParams {
- env: DevelopmentEnvironment;
+ pipeline: DevPipeline;
filePath: URL;
}
-async function getScriptsAndStyles({ env, filePath }: GetScriptsAndStylesParams) {
+async function getScriptsAndStyles({ pipeline, filePath }: GetScriptsAndStylesParams) {
+ const moduleLoader = pipeline.getModuleLoader();
+ const settings = pipeline.getSettings();
+ const mode = pipeline.getEnvironment().mode;
// Add hoisted script tags
- const scripts = await getScriptsForURL(filePath, env.settings.config.root, env.loader);
+ const scripts = await getScriptsForURL(filePath, settings.config.root, moduleLoader);
// Inject HMR scripts
- if (isPage(filePath, env.settings) && env.mode === 'development') {
+ if (isPage(filePath, settings) && mode === 'development') {
scripts.add({
props: { type: 'module', src: '/@vite/client' },
children: '',
@@ -315,20 +277,20 @@ async function getScriptsAndStyles({ env, filePath }: GetScriptsAndStylesParams)
scripts.add({
props: {
type: 'module',
- src: await resolveIdToUrl(env.loader, 'astro/runtime/client/hmr.js'),
+ src: await resolveIdToUrl(moduleLoader, 'astro/runtime/client/hmr.js'),
},
children: '',
});
}
// TODO: We should allow adding generic HTML elements to the head, not just scripts
- for (const script of env.settings.scripts) {
+ for (const script of settings.scripts) {
if (script.stage === 'head-inline') {
scripts.add({
props: {},
children: script.content,
});
- } else if (script.stage === 'page' && isPage(filePath, env.settings)) {
+ } else if (script.stage === 'page' && isPage(filePath, settings)) {
scripts.add({
props: { type: 'module', src: `/@id/${PAGE_SCRIPT_ID}` },
children: '',
@@ -337,7 +299,7 @@ async function getScriptsAndStyles({ env, filePath }: GetScriptsAndStylesParams)
}
// Pass framework CSS in as style tags to be appended to the page.
- const { urls: styleUrls, stylesMap } = await getStylesForURL(filePath, env.loader, env.mode);
+ const { urls: styleUrls, stylesMap } = await getStylesForURL(filePath, moduleLoader, mode);
let links = new Set<SSRElement>();
[...styleUrls].forEach((href) => {
links.add({
@@ -364,13 +326,13 @@ async function getScriptsAndStyles({ env, filePath }: GetScriptsAndStylesParams)
props: {
type: 'text/css',
// Track the ID so we can match it to Vite's injected style later
- 'data-astro-dev-id': viteID(new URL(`.${url}`, env.settings.config.root)),
+ 'data-astro-dev-id': viteID(new URL(`.${url}`, settings.config.root)),
},
children: content,
});
});
- const metadata = await getComponentMetadata(filePath, env.loader);
+ const metadata = await getComponentMetadata(filePath, moduleLoader);
return { scripts, styles, links, metadata };
}
diff --git a/packages/astro/src/vite-plugin-head/index.ts b/packages/astro/src/vite-plugin-head/index.ts
index 9cfdc739f..ca95a334e 100644
--- a/packages/astro/src/vite-plugin-head/index.ts
+++ b/packages/astro/src/vite-plugin-head/index.ts
@@ -16,7 +16,7 @@ export default function configHeadVitePlugin(): vite.Plugin {
function propagateMetadata<
P extends keyof PluginMetadata['astro'],
- V extends PluginMetadata['astro'][P]
+ V extends PluginMetadata['astro'][P],
>(
this: { getModuleInfo(id: string): ModuleInfo | null },
id: string,
diff --git a/packages/astro/src/vite-plugin-inject-env-ts/index.ts b/packages/astro/src/vite-plugin-inject-env-ts/index.ts
index 9c2874fb9..0f0fbb86d 100644
--- a/packages/astro/src/vite-plugin-inject-env-ts/index.ts
+++ b/packages/astro/src/vite-plugin-inject-env-ts/index.ts
@@ -50,26 +50,6 @@ export async function setUpEnvTs({
if (fs.existsSync(envTsPath)) {
let typesEnvContents = await fs.promises.readFile(envTsPath, 'utf-8');
- // TODO: Remove this logic in 3.0, as `astro/client-image` will be merged into `astro/client`
- if (settings.config.experimental.assets && typesEnvContents.includes('types="astro/client"')) {
- typesEnvContents = typesEnvContents.replace(
- 'types="astro/client"',
- 'types="astro/client-image"'
- );
- await fs.promises.writeFile(envTsPath, typesEnvContents, 'utf-8');
- info(logging, 'assets', `Added ${bold(envTsPathRelativetoRoot)} types`);
- } else if (
- !settings.config.experimental.assets &&
- typesEnvContents.includes('types="astro/client-image"')
- ) {
- typesEnvContents = typesEnvContents.replace(
- 'types="astro/client-image"',
- 'types="astro/client"'
- );
- await fs.promises.writeFile(envTsPath, typesEnvContents, 'utf-8');
- info(logging, 'assets', `Removed ${bold(envTsPathRelativetoRoot)} types`);
- }
-
if (!fs.existsSync(dotAstroDir))
// Add `.astro` types reference if none exists
return;
@@ -83,13 +63,7 @@ export async function setUpEnvTs({
} else {
// Otherwise, inject the `env.d.ts` file
let referenceDefs: string[] = [];
- if (settings.config.experimental.assets) {
- referenceDefs.push('/// <reference types="astro/client-image" />');
- } else if (settings.config.integrations.find((i) => i.name === '@astrojs/image')) {
- referenceDefs.push('/// <reference types="@astrojs/image/client" />');
- } else {
- referenceDefs.push('/// <reference types="astro/client" />');
- }
+ referenceDefs.push('/// <reference types="astro/client" />');
if (fs.existsSync(dotAstroDir)) {
referenceDefs.push(dotAstroTypeReference);
diff --git a/packages/astro/src/vite-plugin-jsx/import-source.ts b/packages/astro/src/vite-plugin-jsx/import-source.ts
deleted file mode 100644
index c1f9ea6dc..000000000
--- a/packages/astro/src/vite-plugin-jsx/import-source.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import type { TsConfigJson } from 'tsconfig-resolver';
-import type { AstroRenderer } from '../@types/astro';
-import { parseNpmName } from '../core/util.js';
-
-export async function detectImportSource(
- code: string,
- jsxRenderers: Map<string, AstroRenderer>,
- tsConfig?: TsConfigJson
-): Promise<string | undefined> {
- let importSource = detectImportSourceFromComments(code);
- if (!importSource && /import/.test(code)) {
- importSource = await detectImportSourceFromImports(code, jsxRenderers);
- }
- if (!importSource && tsConfig) {
- importSource = tsConfig.compilerOptions?.jsxImportSource;
- }
- return importSource;
-}
-
-// Matches import statements and dynamic imports. Captures import specifiers only.
-// Adapted from: https://github.com/vitejs/vite/blob/97f8b4df3c9eb817ab2669e5c10b700802eec900/packages/vite/src/node/optimizer/scan.ts#L47-L48
-const importsRE =
- /(?<!\/\/.*)(?<=^|;|\*\/)\s*(?:import(?!\s+type)(?:[\w*{}\n\r\t, ]+from)?\s*("[^"]+"|'[^']+')\s*(?=$|;|\/\/|\/\*)|import\s*\(\s*("[^"]+"|'[^']+')\s*\))/gm;
-
-/**
- * Scan a file's imports to detect which renderer it may need.
- * ex: if the file imports "preact", it's safe to assume the
- * component should be built as a Preact component.
- * If no relevant imports found, return undefined.
- */
-async function detectImportSourceFromImports(
- code: string,
- jsxRenderers: Map<string, AstroRenderer>
-): Promise<string | undefined> {
- let m;
- importsRE.lastIndex = 0;
- while ((m = importsRE.exec(code)) != null) {
- const spec = (m[1] || m[2]).slice(1, -1);
- const pkg = parseNpmName(spec);
- if (pkg && jsxRenderers.has(pkg.name)) {
- return pkg.name;
- }
- }
-}
-
-/**
- * Scan a file for an explicit @jsxImportSource comment.
- * If one is found, return it's value. Otherwise, return undefined.
- */
-function detectImportSourceFromComments(code: string): string | undefined {
- // if no imports were found, look for @jsxImportSource comment
- const multiline = code.match(/\/\*\*?[\S\s]*\*\//gm) || [];
- for (const comment of multiline) {
- const [, lib] = comment.slice(0, -2).match(/@jsxImportSource\s*(\S+)/) || [];
- if (lib) {
- return lib.trim();
- }
- }
-}
diff --git a/packages/astro/src/vite-plugin-jsx/index.ts b/packages/astro/src/vite-plugin-jsx/index.ts
deleted file mode 100644
index 7aa7e7b16..000000000
--- a/packages/astro/src/vite-plugin-jsx/index.ts
+++ /dev/null
@@ -1,251 +0,0 @@
-import type { TransformResult } from 'rollup';
-import {
- transformWithEsbuild,
- type EsbuildTransformOptions,
- type Plugin,
- type ResolvedConfig,
-} from 'vite';
-import type { AstroRenderer, AstroSettings } from '../@types/astro';
-import type { LogOptions } from '../core/logger/core.js';
-import type { PluginMetadata } from '../vite-plugin-astro/types';
-
-import babel from '@babel/core';
-import * as colors from 'kleur/colors';
-import path from 'node:path';
-import { CONTENT_FLAG, PROPAGATED_ASSET_FLAG } from '../content/index.js';
-import { astroEntryPrefix } from '../core/build/plugins/plugin-component-entry.js';
-import { error } from '../core/logger/core.js';
-import { removeQueryString } from '../core/path.js';
-import { detectImportSource } from './import-source.js';
-import tagExportsPlugin from './tag.js';
-
-const JSX_EXTENSIONS = new Set(['.jsx', '.tsx', '.mdx']);
-const IMPORT_STATEMENTS: Record<string, string> = {
- react: "import React from 'react'",
- preact: "import { h } from 'preact'",
- 'solid-js': "import 'solid-js'",
- astro: "import 'astro/jsx-runtime'",
-};
-
-function getEsbuildLoader(filePath: string): EsbuildTransformOptions['loader'] {
- const fileExt = path.extname(filePath);
- if (fileExt === '.mdx') return 'jsx';
- return fileExt.slice(1) as EsbuildTransformOptions['loader'];
-}
-
-function collectJSXRenderers(renderers: AstroRenderer[]): Map<string, AstroRenderer> {
- const renderersWithJSXSupport = renderers.filter((r) => r.jsxImportSource);
- return new Map(
- renderersWithJSXSupport.map((r) => [r.jsxImportSource, r] as [string, AstroRenderer])
- );
-}
-
-interface TransformJSXOptions {
- code: string;
- id: string;
- mode: string;
- renderer: AstroRenderer;
- ssr: boolean;
- root: URL;
-}
-
-async function transformJSX({
- code,
- mode,
- id,
- ssr,
- renderer,
- root,
-}: TransformJSXOptions): Promise<TransformResult> {
- const { jsxTransformOptions } = renderer;
- const options = await jsxTransformOptions!({ mode, ssr });
- const plugins = [...(options.plugins || [])];
- if (ssr) {
- plugins.push(await tagExportsPlugin({ rendererName: renderer.name, root }));
- }
- const result = await babel.transformAsync(code, {
- presets: options.presets,
- plugins,
- cwd: process.cwd(),
- filename: id,
- ast: false,
- compact: false,
- sourceMaps: true,
- configFile: false,
- babelrc: false,
- inputSourceMap: options.inputSourceMap,
- });
- // TODO: Be more strict about bad return values here.
- // Should we throw an error instead? Should we never return `{code: ""}`?
- if (!result) return null;
-
- if (renderer.name === 'astro:jsx') {
- const { astro } = result.metadata as unknown as PluginMetadata;
- return {
- code: result.code || '',
- map: result.map,
- meta: {
- astro,
- vite: {
- // Setting this vite metadata to `ts` causes Vite to resolve .js
- // extensions to .ts files.
- lang: 'ts',
- },
- },
- };
- }
-
- return {
- code: result.code || '',
- map: result.map,
- };
-}
-
-interface AstroPluginJSXOptions {
- settings: AstroSettings;
- logging: LogOptions;
-}
-
-// Format inspired by https://github.com/vitejs/vite/blob/main/packages/vite/src/node/constants.ts#L54
-const SPECIAL_QUERY_REGEX = new RegExp(
- `[?&](?:worker|sharedworker|raw|url|${CONTENT_FLAG}|${PROPAGATED_ASSET_FLAG})\\b`
-);
-
-/** Use Astro config to allow for alternate or multiple JSX renderers (by default Vite will assume React) */
-export default function jsx({ settings, logging }: AstroPluginJSXOptions): Plugin {
- let viteConfig: ResolvedConfig;
- const jsxRenderers = new Map<string, AstroRenderer>();
- const jsxRenderersIntegrationOnly = new Map<string, AstroRenderer>();
- // A reference to Astro's internal JSX renderer.
- let astroJSXRenderer: AstroRenderer;
- // The first JSX renderer provided is considered the default renderer.
- // This is a useful reference for when the user only gives a single render.
- let defaultJSXRendererEntry: [string, AstroRenderer] | undefined;
-
- return {
- name: 'astro:jsx',
- enforce: 'pre', // run transforms before other plugins
- async configResolved(resolvedConfig) {
- viteConfig = resolvedConfig;
- const possibleRenderers = collectJSXRenderers(settings.renderers);
- for (const [importSource, renderer] of possibleRenderers) {
- jsxRenderers.set(importSource, renderer);
- if (importSource === 'astro') {
- astroJSXRenderer = renderer;
- } else {
- jsxRenderersIntegrationOnly.set(importSource, renderer);
- }
- }
- defaultJSXRendererEntry = [...jsxRenderersIntegrationOnly.entries()][0];
- },
- async transform(code, id, opts) {
- const ssr = Boolean(opts?.ssr);
- // Skip special queries and astro entries. We skip astro entries here as we know it doesn't contain
- // JSX code, and also because we can't detect the import source to apply JSX transforms.
- if (SPECIAL_QUERY_REGEX.test(id) || id.startsWith(astroEntryPrefix)) {
- return null;
- }
- id = removeQueryString(id);
- if (!JSX_EXTENSIONS.has(path.extname(id))) {
- return null;
- }
-
- const { mode } = viteConfig;
- // Shortcut: only use Astro renderer for MD and MDX files
- if (id.endsWith('.mdx')) {
- const { code: jsxCode } = await transformWithEsbuild(code, id, {
- loader: getEsbuildLoader(id),
- jsx: 'preserve',
- sourcemap: 'inline',
- tsconfigRaw: {
- compilerOptions: {
- // Ensure client:only imports are treeshaken
- verbatimModuleSyntax: false,
- importsNotUsedAsValues: 'remove',
- },
- },
- });
- return transformJSX({
- code: jsxCode,
- id,
- renderer: astroJSXRenderer,
- mode,
- ssr,
- root: settings.config.root,
- });
- }
- if (defaultJSXRendererEntry && jsxRenderersIntegrationOnly.size === 1) {
- // downlevel any non-standard syntax, but preserve JSX
- const { code: jsxCode } = await transformWithEsbuild(code, id, {
- loader: getEsbuildLoader(id),
- jsx: 'preserve',
- sourcemap: 'inline',
- });
- return transformJSX({
- code: jsxCode,
- id,
- renderer: defaultJSXRendererEntry[1],
- mode,
- ssr,
- root: settings.config.root,
- });
- }
-
- const importSource = await detectImportSource(code, jsxRenderers, settings.tsConfig);
-
- // if we still can’t tell the import source, now is the time to throw an error.
- if (!importSource && defaultJSXRendererEntry) {
- const [defaultRendererName] = defaultJSXRendererEntry;
- error(
- logging,
- 'renderer',
- `${colors.yellow(id)}
-Unable to resolve a renderer that handles this file! With more than one renderer enabled, you should include an import or use a pragma comment.
-Add ${colors.cyan(
- IMPORT_STATEMENTS[defaultRendererName] || `import '${defaultRendererName}';`
- )} or ${colors.cyan(`/** @jsxImportSource: ${defaultRendererName} */`)} to this file.
-`
- );
- return null;
- } else if (!importSource) {
- error(
- logging,
- 'renderer',
- `${colors.yellow(id)}
-Unable to find a renderer for JSX. Do you have one configured in your Astro config? See this page to learn how:
-https://docs.astro.build/en/core-concepts/framework-components/#installing-integrations
-`
- );
- return null;
- }
-
- const selectedJsxRenderer = jsxRenderers.get(importSource);
- // if the renderer is not installed for this JSX source, throw error
- if (!selectedJsxRenderer) {
- error(
- logging,
- 'renderer',
- `${colors.yellow(
- id
- )} No renderer installed for ${importSource}. Try adding \`@astrojs/${importSource}\` to your project.`
- );
- return null;
- }
-
- // downlevel any non-standard syntax, but preserve JSX
- const { code: jsxCode } = await transformWithEsbuild(code, id, {
- loader: getEsbuildLoader(id),
- jsx: 'preserve',
- sourcemap: 'inline',
- });
- return await transformJSX({
- code: jsxCode,
- id,
- renderer: selectedJsxRenderer,
- mode,
- ssr,
- root: settings.config.root,
- });
- },
- };
-}
diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts
index dd2cbcd85..ae26bfb42 100644
--- a/packages/astro/src/vite-plugin-markdown/index.ts
+++ b/packages/astro/src/vite-plugin-markdown/index.ts
@@ -49,11 +49,6 @@ function safeMatter(source: string, id: string) {
}
}
-// absolute path of "astro/jsx-runtime"
-const astroJsxRuntimeModulePath = normalizePath(
- fileURLToPath(new URL('../jsx-runtime/index.js', import.meta.url))
-);
-
const astroServerRuntimeModulePath = normalizePath(
fileURLToPath(new URL('../runtime/server/index.js', import.meta.url))
);
@@ -80,7 +75,6 @@ export default function markdown({ settings, logging }: AstroPluginOptions): Plu
...settings.config.markdown,
fileURL: new URL(`file://${fileId}`),
frontmatter: raw.data,
- experimentalAssets: settings.config.experimental.assets,
});
let html = renderResult.code;
@@ -88,7 +82,7 @@ export default function markdown({ settings, logging }: AstroPluginOptions): Plu
// Resolve all the extracted images from the content
let imagePaths: { raw: string; resolved: string }[] = [];
- if (settings.config.experimental.assets && renderResult.vfile.data.imagePaths) {
+ if (renderResult.vfile.data.imagePaths) {
for (let imagePath of renderResult.vfile.data.imagePaths.values()) {
imagePaths.push({
raw: imagePath,
@@ -115,12 +109,13 @@ export default function markdown({ settings, logging }: AstroPluginOptions): Plu
}
const code = escapeViteEnvReferences(`
- import { Fragment, jsx as h } from ${JSON.stringify(astroJsxRuntimeModulePath)};
- import { spreadAttributes } from ${JSON.stringify(astroServerRuntimeModulePath)};
+ import { unescapeHTML, spreadAttributes, createComponent, render, renderComponent } from ${JSON.stringify(
+ astroServerRuntimeModulePath
+ )};
import { AstroError, AstroErrorData } from ${JSON.stringify(astroErrorModulePath)};
${layout ? `import Layout from ${JSON.stringify(layout)};` : ''}
- ${settings.config.experimental.assets ? 'import { getImage } from "astro:assets";' : ''}
+ import { getImage } from "astro:assets";
export const images = {
${imagePaths.map(
@@ -167,27 +162,29 @@ export default function markdown({ settings, logging }: AstroPluginOptions): Plu
export function getHeadings() {
return ${JSON.stringify(headings)};
}
- export async function Content() {
+
+ export const Content = createComponent((result, _props, slots) => {
const { layout, ...content } = frontmatter;
content.file = file;
content.url = url;
- const contentFragment = h(Fragment, { 'set:html': html });
+
return ${
layout
- ? `h(Layout, {
- file,
- url,
- content,
- frontmatter: content,
- headings: getHeadings(),
- rawContent,
- compiledContent,
- 'server:root': true,
- children: contentFragment
- })`
- : `contentFragment`
- };
- }
+ ? `render\`\${renderComponent(result, 'Layout', Layout, {
+ file,
+ url,
+ content,
+ frontmatter: content,
+ headings: getHeadings(),
+ rawContent,
+ compiledContent,
+ 'server:root': true,
+ }, {
+ 'default': () => render\`\${unescapeHTML(html)}\`
+ })}\`;`
+ : `render\`\${unescapeHTML(html)}\`;`
+ }
+ });
Content[Symbol.for('astro.needsHeadRendering')] = ${layout ? 'false' : 'true'};
export default Content;
`);
diff --git a/packages/astro/src/vite-plugin-jsx/README.md b/packages/astro/src/vite-plugin-mdx/README.md
index 554651869..554651869 100644
--- a/packages/astro/src/vite-plugin-jsx/README.md
+++ b/packages/astro/src/vite-plugin-mdx/README.md
diff --git a/packages/astro/src/vite-plugin-mdx/index.ts b/packages/astro/src/vite-plugin-mdx/index.ts
new file mode 100644
index 000000000..f2b068068
--- /dev/null
+++ b/packages/astro/src/vite-plugin-mdx/index.ts
@@ -0,0 +1,130 @@
+import type { TransformResult } from 'rollup';
+import { transformWithEsbuild, type Plugin, type ResolvedConfig } from 'vite';
+import type { AstroRenderer, AstroSettings } from '../@types/astro';
+import type { LogOptions } from '../core/logger/core.js';
+import type { PluginMetadata } from '../vite-plugin-astro/types';
+
+import babel from '@babel/core';
+import { CONTENT_FLAG, PROPAGATED_ASSET_FLAG } from '../content/index.js';
+import { astroEntryPrefix } from '../core/build/plugins/plugin-component-entry.js';
+import { removeQueryString } from '../core/path.js';
+import tagExportsPlugin from './tag.js';
+
+interface TransformJSXOptions {
+ code: string;
+ id: string;
+ mode: string;
+ renderer: AstroRenderer;
+ ssr: boolean;
+ root: URL;
+}
+
+async function transformJSX({
+ code,
+ mode,
+ id,
+ ssr,
+ renderer,
+ root,
+}: TransformJSXOptions): Promise<TransformResult> {
+ const { jsxTransformOptions } = renderer;
+ const options = await jsxTransformOptions!({ mode, ssr });
+ const plugins = [...(options.plugins || [])];
+ if (ssr) {
+ plugins.push(await tagExportsPlugin({ rendererName: renderer.name, root }));
+ }
+ const result = await babel.transformAsync(code, {
+ presets: options.presets,
+ plugins,
+ cwd: process.cwd(),
+ filename: id,
+ ast: false,
+ compact: false,
+ sourceMaps: true,
+ configFile: false,
+ babelrc: false,
+ inputSourceMap: options.inputSourceMap,
+ });
+ // TODO: Be more strict about bad return values here.
+ // Should we throw an error instead? Should we never return `{code: ""}`?
+ if (!result) return null;
+
+ if (renderer.name === 'astro:jsx') {
+ const { astro } = result.metadata as unknown as PluginMetadata;
+ return {
+ code: result.code || '',
+ map: result.map,
+ meta: {
+ astro,
+ vite: {
+ // Setting this vite metadata to `ts` causes Vite to resolve .js
+ // extensions to .ts files.
+ lang: 'ts',
+ },
+ },
+ };
+ }
+
+ return {
+ code: result.code || '',
+ map: result.map,
+ };
+}
+
+interface AstroPluginJSXOptions {
+ settings: AstroSettings;
+ logging: LogOptions;
+}
+
+// Format inspired by https://github.com/vitejs/vite/blob/main/packages/vite/src/node/constants.ts#L54
+const SPECIAL_QUERY_REGEX = new RegExp(
+ `[?&](?:worker|sharedworker|raw|url|${CONTENT_FLAG}|${PROPAGATED_ASSET_FLAG})\\b`
+);
+
+/** Use Astro config to allow for alternate or multiple JSX renderers (by default Vite will assume React) */
+export default function mdxVitePlugin({ settings }: AstroPluginJSXOptions): Plugin {
+ let viteConfig: ResolvedConfig;
+ // A reference to Astro's internal JSX renderer.
+ let astroJSXRenderer: AstroRenderer;
+
+ return {
+ name: 'astro:jsx',
+ enforce: 'pre', // run transforms before other plugins
+ async configResolved(resolvedConfig) {
+ viteConfig = resolvedConfig;
+ astroJSXRenderer = settings.renderers.find((r) => r.jsxImportSource === 'astro')!;
+ },
+ async transform(code, id, opts) {
+ // Skip special queries and astro entries. We skip astro entries here as we know it doesn't contain
+ // JSX code, and also because we can't detect the import source to apply JSX transforms.
+ if (SPECIAL_QUERY_REGEX.test(id) || id.startsWith(astroEntryPrefix)) {
+ return null;
+ }
+ id = removeQueryString(id);
+ // Shortcut: only use Astro renderer for MD and MDX files
+ if (!id.endsWith('.mdx')) {
+ return null;
+ }
+ const { code: jsxCode } = await transformWithEsbuild(code, id, {
+ loader: 'jsx',
+ jsx: 'preserve',
+ sourcemap: 'inline',
+ tsconfigRaw: {
+ compilerOptions: {
+ // Ensure client:only imports are treeshaken
+ verbatimModuleSyntax: false,
+ importsNotUsedAsValues: 'remove',
+ },
+ },
+ });
+ return transformJSX({
+ code: jsxCode,
+ id,
+ renderer: astroJSXRenderer,
+ mode: viteConfig.mode,
+ ssr: Boolean(opts?.ssr),
+ root: settings.config.root,
+ });
+ },
+ };
+}
diff --git a/packages/astro/src/vite-plugin-jsx/tag.ts b/packages/astro/src/vite-plugin-mdx/tag.ts
index 5efc4c41f..b7ae1f2c4 100644
--- a/packages/astro/src/vite-plugin-jsx/tag.ts
+++ b/packages/astro/src/vite-plugin-mdx/tag.ts
@@ -18,7 +18,7 @@ export default async function tagExportsWithRenderer({
return {
visitor: {
Program: {
- // Inject `import { __astro_tag_component__ } from 'astro/server/index.js'`
+ // Inject `import { __astro_tag_component__ } from 'astro/runtime/server/index.js'`
enter(path) {
path.node.body.splice(
0,
@@ -30,7 +30,7 @@ export default async function tagExportsWithRenderer({
t.identifier('__astro_tag_component__')
),
],
- t.stringLiteral('astro/server/index.js')
+ t.stringLiteral('astro/runtime/server/index.js')
)
);
},
diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js
index 76bfba296..9371673d4 100644
--- a/packages/astro/test/0-css.test.js
+++ b/packages/astro/test/0-css.test.js
@@ -39,15 +39,25 @@ describe('CSS', function () {
it('HTML and CSS scoped correctly', async () => {
const el1 = $('#dynamic-class');
const el2 = $('#dynamic-vis');
- const classes = $('#class').attr('class').split(' ');
- const scopedClass = classes.find((name) => /^astro-[A-Za-z0-9-]+/.test(name));
+ const classes = $('#class');
+ let scopedAttribute;
+ for (const [key] of Object.entries(classes[0].attribs)) {
+ if (/^data-astro-cid-[A-Za-z0-9-]+/.test(key)) {
+ // Ema: this is ugly, but for reasons that I don't want to explore, cheerio
+ // lower case the hash of the attribute
+ scopedAttribute = key;
+ }
+ }
+ if (!scopedAttribute) {
+ throw new Error("Couldn't find scoped attribute");
+ }
// 1. check HTML
- expect(el1.attr('class')).to.equal(`blue ${scopedClass}`);
- expect(el2.attr('class')).to.equal(`visible ${scopedClass}`);
+ expect(el1.attr('class')).to.equal(`blue`);
+ expect(el2.attr('class')).to.equal(`visible`);
// 2. check CSS
- const expected = `.blue:where(.${scopedClass}){color:#b0e0e6}.color\\:blue:where(.${scopedClass}){color:#b0e0e6}.visible:where(.${scopedClass}){display:block}`;
+ const expected = `.blue[${scopedAttribute}],.color\\:blue[${scopedAttribute}]{color:#b0e0e6}.visible[${scopedAttribute}]{display:block}`;
expect(bundledCSS).to.include(expected);
});
@@ -60,8 +70,12 @@ describe('CSS', function () {
expect($('#no-scope').attr('class')).to.equal(undefined);
});
- it('Child inheritance', async () => {
- expect($('#passed-in').attr('class')).to.match(/outer astro-[A-Z0-9]+ astro-[A-Z0-9]+/);
+ it('Child inheritance', (done) => {
+ for (const [key] of Object.entries($('#passed-in')[0].attribs)) {
+ if (/^data-astro-cid-[A-Za-z0-9-]+/.test(key)) {
+ done();
+ }
+ }
});
it('Using hydrated components adds astro-island styles', async () => {
@@ -70,11 +84,11 @@ describe('CSS', function () {
});
it('<style lang="sass">', async () => {
- expect(bundledCSS).to.match(new RegExp('h1\\:where\\(.astro-[^{]*{color:#90ee90}'));
+ expect(bundledCSS).to.match(new RegExp('h1\\[data-astro-cid-[^{]*{color:#90ee90}'));
});
it('<style lang="scss">', async () => {
- expect(bundledCSS).to.match(new RegExp('h1\\:where\\(.astro-[^{]*{color:#ff69b4}'));
+ expect(bundledCSS).to.match(new RegExp('h1\\[data-astro-cid-[^{]*{color:#ff69b4}'));
});
});
@@ -331,10 +345,10 @@ describe('CSS', function () {
it('resolves Astro styles', async () => {
const allInjectedStyles = $('style').text();
- expect(allInjectedStyles).to.contain('.linked-css:where(.astro-');
- expect(allInjectedStyles).to.contain('.linked-sass:where(.astro-');
- expect(allInjectedStyles).to.contain('.linked-scss:where(.astro-');
- expect(allInjectedStyles).to.contain('.wrapper:where(.astro-');
+ expect(allInjectedStyles).to.contain('.linked-css[data-astro-cid-');
+ expect(allInjectedStyles).to.contain('.linked-sass[data-astro-cid-');
+ expect(allInjectedStyles).to.contain('.linked-scss[data-astro-cid-');
+ expect(allInjectedStyles).to.contain('.wrapper[data-astro-cid-');
});
it('resolves Styles from React', async () => {
diff --git a/packages/astro/test/alias-tsconfig-baseurl-only.test.js b/packages/astro/test/alias-tsconfig-baseurl-only.test.js
index 55b7ecc37..64c196808 100644
--- a/packages/astro/test/alias-tsconfig-baseurl-only.test.js
+++ b/packages/astro/test/alias-tsconfig-baseurl-only.test.js
@@ -29,6 +29,8 @@ describe('Aliases with tsconfig.json - baseUrl only', () => {
before(async () => {
fixture = await loadFixture({
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
root: './fixtures/alias-tsconfig-baseurl-only/',
});
});
diff --git a/packages/astro/test/alias-tsconfig.test.js b/packages/astro/test/alias-tsconfig.test.js
index 4668ea66e..283a66ea1 100644
--- a/packages/astro/test/alias-tsconfig.test.js
+++ b/packages/astro/test/alias-tsconfig.test.js
@@ -29,6 +29,8 @@ describe('Aliases with tsconfig.json', () => {
before(async () => {
fixture = await loadFixture({
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
root: './fixtures/alias-tsconfig/',
});
});
diff --git a/packages/astro/test/asset-url-base.test.js b/packages/astro/test/asset-url-base.test.js
index 0dc565b7f..de63ef4b7 100644
--- a/packages/astro/test/asset-url-base.test.js
+++ b/packages/astro/test/asset-url-base.test.js
@@ -12,6 +12,8 @@ describe('Asset URL resolution in build', () => {
fixture = await loadFixture({
root: './fixtures/asset-url-base/',
site: 'http://example.com/sub/path/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
@@ -30,6 +32,8 @@ describe('Asset URL resolution in build', () => {
root: './fixtures/asset-url-base/',
site: 'http://example.com/sub/path/',
base: '/another/base/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/astro-assets-prefix.test.js b/packages/astro/test/astro-assets-prefix.test.js
index 40562afd4..ab42439ae 100644
--- a/packages/astro/test/astro-assets-prefix.test.js
+++ b/packages/astro/test/astro-assets-prefix.test.js
@@ -63,7 +63,7 @@ describe('Assets Prefix - Static', () => {
});
});
-describe('Assets Prefix - Static with path prefix', () => {
+describe('Assets Prefix - with path prefix', () => {
let fixture;
before(async () => {
@@ -86,7 +86,7 @@ describe('Assets Prefix - Static with path prefix', () => {
});
});
-describe('Assets Prefix - Server', () => {
+describe('Assets Prefix, server', () => {
let app;
before(async () => {
@@ -143,7 +143,7 @@ describe('Assets Prefix - Server', () => {
});
});
-describe('Assets Prefix - Server with path prefix', () => {
+describe('Assets Prefix, with path prefix', () => {
let app;
before(async () => {
diff --git a/packages/astro/test/astro-class-list.test.js b/packages/astro/test/astro-class-list.test.js
index 9787fb458..b9d6aeba4 100644
--- a/packages/astro/test/astro-class-list.test.js
+++ b/packages/astro/test/astro-class-list.test.js
@@ -14,14 +14,14 @@ describe('Class List', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
- expect($('[class="test control"]')).to.have.lengthOf(1);
- expect($('[class="test expression"]')).to.have.lengthOf(1);
- expect($('[class="test true"]')).to.have.lengthOf(1);
- expect($('[class="test truthy"]')).to.have.lengthOf(1);
- expect($('[class="test set"]')).to.have.lengthOf(1);
- expect($('[class="hello goodbye world friend"]')).to.have.lengthOf(1);
- expect($('[class="foo baz"]')).to.have.lengthOf(1);
- expect($('span:not([class])')).to.have.lengthOf(1);
+ expect($('[class="test control"]')).to.have.lengthOf(1, '[class="test control"]');
+ expect($('[class="test expression"]')).to.have.lengthOf(1, '[class="test expression"]');
+ expect($('[class="test true"]')).to.have.lengthOf(1, '[class="test true"]');
+ expect($('[class="test truthy"]')).to.have.lengthOf(1, '[class="test truthy"]');
+ expect($('[class="test set"]')).to.have.lengthOf(1, '[class="test set"]');
+ expect($('[class="hello goodbye hello world hello friend"]')).to.have.lengthOf(1, '[class="hello goodbye hello world hello friend"]');
+ expect($('[class="foo baz"]')).to.have.lengthOf(1, '[class="foo baz"]');
+ expect($('span:not([class])')).to.have.lengthOf(1, 'span:not([class])');
expect($('.false, .noshow1, .noshow2, .noshow3, .noshow4')).to.have.lengthOf(0);
});
@@ -30,13 +30,22 @@ describe('Class List', async () => {
const html = await fixture.readFile('/component/index.html');
const $ = cheerio.load(html);
- expect($('[class="test control"]')).to.have.lengthOf(1);
- expect($('[class="test expression"]')).to.have.lengthOf(1);
- expect($('[class="test true"]')).to.have.lengthOf(1);
- expect($('[class="test truthy"]')).to.have.lengthOf(1);
- expect($('[class="test set"]')).to.have.lengthOf(1);
- expect($('[class="hello goodbye world friend"]')).to.have.lengthOf(1);
- expect($('[class="foo baz"]')).to.have.lengthOf(1);
- expect($('span:not([class])')).to.have.lengthOf(1);
+ expect($('[class="test control"]')).to.have.lengthOf(1, '[class="test control"]');
+ expect($('[class="test expression"]')).to.have.lengthOf(1, '[class="test expression"]');
+ expect($('[class="test true"]')).to.have.lengthOf(1, '[class="test true"]');
+ expect($('[class="test truthy"]')).to.have.lengthOf(1, '[class="test truthy"]');
+ expect($('[class="test set"]')).to.have.lengthOf(1, '[class="test set"]');
+ expect($('[class="hello goodbye hello world hello friend"]')).to.have.lengthOf(1, '[class="hello goodbye hello world hello friend"]');
+ expect($('[class="foo baz"]')).to.have.lengthOf(1, '[class="foo baz"]');
+ expect($('span:not([class])')).to.have.lengthOf(1, 'span:not([class])');
+
+ expect($('[class="test control"]').text()).to.equal('test control');
+ expect($('[class="test expression"]').text()).to.equal('test expression');
+ expect($('[class="test true"]').text()).to.equal('test true');
+ expect($('[class="test truthy"]').text()).to.equal('test truthy');
+ expect($('[class="test set"]').text()).to.equal('test set');
+ expect($('[class="hello goodbye hello world hello friend"]').text()).to.equal('hello goodbye hello world hello friend');
+ expect($('[class="foo baz"]').text()).to.equal('foo baz');
+ expect($('span:not([class])').text()).to.equal('');
});
});
diff --git a/packages/astro/test/astro-client-only.test.js b/packages/astro/test/astro-client-only.test.js
index c003e0a28..9f6de9116 100644
--- a/packages/astro/test/astro-client-only.test.js
+++ b/packages/astro/test/astro-client-only.test.js
@@ -9,6 +9,8 @@ describe('Client only components', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-client-only/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
@@ -72,6 +74,8 @@ describe('Client only components subpath', () => {
site: 'https://site.com',
base: '/blog',
root: './fixtures/astro-client-only/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/astro-css-bundling.test.js b/packages/astro/test/astro-css-bundling.test.js
index 415c8f398..2da01108f 100644
--- a/packages/astro/test/astro-css-bundling.test.js
+++ b/packages/astro/test/astro-css-bundling.test.js
@@ -26,6 +26,8 @@ describe('CSS Bundling', function () {
before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-css-bundling/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build({ mode: 'production' });
});
@@ -76,6 +78,9 @@ describe('CSS Bundling', function () {
fixture = await loadFixture({
root: './fixtures/astro-css-bundling/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
+
vite: {
build: {
rollupOptions: {
diff --git a/packages/astro/test/astro-directives.test.js b/packages/astro/test/astro-directives.test.js
index 2a86e4ba2..4ce5ee916 100644
--- a/packages/astro/test/astro-directives.test.js
+++ b/packages/astro/test/astro-directives.test.js
@@ -6,7 +6,11 @@ describe('Directives', async () => {
let fixture;
before(async () => {
- fixture = await loadFixture({ root: './fixtures/astro-directives/' });
+ fixture = await loadFixture({
+ root: './fixtures/astro-directives/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
+ });
await fixture.build();
});
diff --git a/packages/astro/test/astro-envs.test.js b/packages/astro/test/astro-envs.test.js
index 402878da5..c923ae065 100644
--- a/packages/astro/test/astro-envs.test.js
+++ b/packages/astro/test/astro-envs.test.js
@@ -109,7 +109,7 @@ describe('Environment Variables', () => {
expect(res.status).to.equal(200);
let indexHtml = await res.text();
let $ = cheerio.load(indexHtml);
- expect($('#base-url').text()).to.equal('/blog/');
+ expect($('#base-url').text()).to.equal('/blog');
});
it('does render destructured builtin SITE env', async () => {
@@ -117,7 +117,7 @@ describe('Environment Variables', () => {
expect(res.status).to.equal(200);
let indexHtml = await res.text();
let $ = cheerio.load(indexHtml);
- expect($('#base-url').text()).to.equal('/blog/');
+ expect($('#base-url').text()).to.equal('/blog');
});
});
});
diff --git a/packages/astro/test/astro-external-files.test.js b/packages/astro/test/astro-external-files.test.js
index 397f31785..7aaf04c80 100644
--- a/packages/astro/test/astro-external-files.test.js
+++ b/packages/astro/test/astro-external-files.test.js
@@ -1,22 +1,18 @@
-/**
- * UNCOMMENT: add support for smarter "external" scripts in Rollup
import { expect } from 'chai';
import { loadFixture } from './test-utils.js';
-let fixture;
+describe('External file references', () => {
+ let fixture;
-before(async () => {
- fixture = await loadFixture({ root: './fixtures/astro-external-files/' });
- await fixture.build();
-});
+ before(async () => {
+ fixture = await loadFixture({ root: './fixtures/astro-external-files/' });
+ await fixture.build();
+ });
-// TODO: Vite error: fix external files
-describe('Externeal file references', () => {
- it('Build with externeal reference', async () => {
- let rss = await fixture.readFile('/index.html');
- expect(rss).to.be(''); // TODO: inline snapshot
- });
+ it('Build with externeal reference', async () => {
+ const html = await fixture.readFile('/index.html');
+ expect(html).to.include('<script src="/external-file.js"');
+ });
});
-*/
it.skip('is skipped', () => {});
diff --git a/packages/astro/test/astro-get-static-paths.test.js b/packages/astro/test/astro-get-static-paths.test.js
index 784ff1718..66aa5b94d 100644
--- a/packages/astro/test/astro-get-static-paths.test.js
+++ b/packages/astro/test/astro-get-static-paths.test.js
@@ -1,6 +1,6 @@
import { expect } from 'chai';
-import { loadFixture } from './test-utils.js';
import * as cheerio from 'cheerio';
+import { loadFixture } from './test-utils.js';
describe('getStaticPaths - build calls', () => {
/** @type {import('./test-utils').Fixture} */
@@ -92,11 +92,6 @@ describe('getStaticPaths - dev calls', () => {
});
describe('route params type validation', () => {
- it('resolves 200 on nested array parameters', async () => {
- const res = await fixture.fetch('/nested-arrays/slug1');
- expect(res.status).to.equal(200);
- });
-
it('resolves 200 on matching static path - string params', async () => {
// route provided with { params: { year: "2022", slug: "post-2" }}
const res = await fixture.fetch('/blog/2022/post-1');
diff --git a/packages/astro/test/astro-global.test.js b/packages/astro/test/astro-global.test.js
index d49868584..f003bc035 100644
--- a/packages/astro/test/astro-global.test.js
+++ b/packages/astro/test/astro-global.test.js
@@ -54,10 +54,10 @@ describe('Astro Global', () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
- expect($('#pathname').text()).to.equal('/blog/');
+ expect($('#pathname').text()).to.equal('/blog');
expect($('#searchparams').text()).to.equal('{}');
- expect($('#child-pathname').text()).to.equal('/blog/');
- expect($('#nested-child-pathname').text()).to.equal('/blog/');
+ expect($('#child-pathname').text()).to.equal('/blog');
+ expect($('#nested-child-pathname').text()).to.equal('/blog');
});
it('Astro.site', async () => {
diff --git a/packages/astro/test/astro-head.test.js b/packages/astro/test/astro-head.test.js
index 862894d73..ed28500dc 100644
--- a/packages/astro/test/astro-head.test.js
+++ b/packages/astro/test/astro-head.test.js
@@ -10,6 +10,8 @@ describe('Head in its own component', () => {
root: './fixtures/astro-head/',
site: 'https://mysite.dev/',
base: '/blog',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/astro-partial-html.test.js b/packages/astro/test/astro-partial-html.test.js
index 6073f1bd1..162c6985d 100644
--- a/packages/astro/test/astro-partial-html.test.js
+++ b/packages/astro/test/astro-partial-html.test.js
@@ -26,7 +26,7 @@ describe('Partial HTML', async () => {
// test 2: correct CSS present
const allInjectedStyles = $('style').text();
- expect(allInjectedStyles).to.match(/\:where\(\.astro-[^{]+{color:red}/);
+ expect(allInjectedStyles).to.match(/\[data-astro-cid-[^{]+{color:red}/);
});
it('injects framework styles', async () => {
diff --git a/packages/astro/test/build-assets.test.js b/packages/astro/test/build-assets.test.js
index 1fa17e8d2..3d4dacd7b 100644
--- a/packages/astro/test/build-assets.test.js
+++ b/packages/astro/test/build-assets.test.js
@@ -13,6 +13,8 @@ describe('build assets (static)', () => {
fixture = await loadFixture({
root: './fixtures/build-assets/',
integrations: [preact()],
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
@@ -57,6 +59,7 @@ describe('build assets (static)', () => {
integrations: [preact()],
build: {
assets: 'custom-assets',
+ inlineStylesheets: 'never',
},
});
await fixture.build();
@@ -96,6 +99,8 @@ describe('build assets (server)', () => {
root: './fixtures/build-assets/',
integrations: [preact()],
adapter: testAdapter(),
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
@@ -140,6 +145,7 @@ describe('build assets (server)', () => {
integrations: [preact()],
build: {
assets: 'custom-assets',
+ inlineStylesheets: 'never',
},
adapter: testAdapter(),
});
diff --git a/packages/astro/test/cli.test.js b/packages/astro/test/cli.test.js
index 7626cc495..82cf7a12d 100644
--- a/packages/astro/test/cli.test.js
+++ b/packages/astro/test/cli.test.js
@@ -131,8 +131,8 @@ describe('astro cli', () => {
// Note: our tests run in parallel so this could be 3000+!
expect(Number.parseInt(localURL.port)).to.be.greaterThanOrEqual(
- 3000,
- `Expected Port to be >= 3000`
+ 4321,
+ `Expected Port to be >= 4321`
);
expect(networkURL.port).to.be.equal(
localURL.port,
diff --git a/packages/astro/test/component-library.test.js b/packages/astro/test/component-library.test.js
index 58c7fb6b8..c11f9eed6 100644
--- a/packages/astro/test/component-library.test.js
+++ b/packages/astro/test/component-library.test.js
@@ -13,6 +13,8 @@ describe('Component Libraries', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/component-library/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
});
diff --git a/packages/astro/test/config-vite-css-target.test.js b/packages/astro/test/config-vite-css-target.test.js
index 1dc2cce32..94fa74e74 100644
--- a/packages/astro/test/config-vite-css-target.test.js
+++ b/packages/astro/test/config-vite-css-target.test.js
@@ -10,7 +10,11 @@ let fixture;
describe('CSS', function () {
before(async () => {
- fixture = await loadFixture({ root: './fixtures/config-vite-css-target/' });
+ fixture = await loadFixture({
+ root: './fixtures/config-vite-css-target/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
+ });
});
describe('build', () => {
@@ -32,7 +36,7 @@ describe('CSS', function () {
it('vite.build.cssTarget is respected', async () => {
expect(bundledCSS).to.match(
- new RegExp('.class\\:where\\(.astro-[^{]*{top:0;right:0;bottom:0;left:0}')
+ new RegExp('.class\\[data-astro-[^{]*{top:0;right:0;bottom:0;left:0}')
);
});
});
diff --git a/packages/astro/test/config-vite.test.js b/packages/astro/test/config-vite.test.js
index f910bc30b..b15729dd7 100644
--- a/packages/astro/test/config-vite.test.js
+++ b/packages/astro/test/config-vite.test.js
@@ -6,7 +6,11 @@ describe('Vite Config', async () => {
let fixture;
before(async () => {
- fixture = await loadFixture({ root: './fixtures/config-vite/' });
+ fixture = await loadFixture({
+ root: './fixtures/config-vite/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
+ });
await fixture.build();
});
diff --git a/packages/astro/test/content-collections-render.test.js b/packages/astro/test/content-collections-render.test.js
index f6a9c3c72..27eb33b5a 100644
--- a/packages/astro/test/content-collections-render.test.js
+++ b/packages/astro/test/content-collections-render.test.js
@@ -13,6 +13,8 @@ describe('Content Collections - render()', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/content/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
@@ -106,6 +108,8 @@ describe('Content Collections - render()', () => {
output: 'server',
root: './fixtures/content/',
adapter: testAdapter(),
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/core-image.test.js b/packages/astro/test/core-image.test.js
index 5d656a6f6..26fafe41a 100644
--- a/packages/astro/test/core-image.test.js
+++ b/packages/astro/test/core-image.test.js
@@ -20,9 +20,6 @@ describe('astro:image', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/core-image/',
- experimental: {
- assets: true,
- },
image: {
service: testImageService({ foo: 'bar' }),
domains: ['avatars.githubusercontent.com'],
@@ -444,9 +441,6 @@ describe('astro:image', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/core-image-errors/',
- experimental: {
- assets: true,
- },
image: {
service: testImageService(),
},
@@ -512,9 +506,6 @@ describe('astro:image', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/core-image-base/',
- experimental: {
- assets: true,
- },
image: {
service: testImageService(),
},
@@ -568,9 +559,6 @@ describe('astro:image', () => {
root: './fixtures/core-image-ssr/',
output: 'server',
adapter: testAdapter(),
- experimental: {
- assets: true,
- },
image: {
service: testImageService(),
},
@@ -592,9 +580,6 @@ describe('astro:image', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/core-image-ssg/',
- experimental: {
- assets: true,
- },
image: {
service: testImageService(),
domains: ['astro.build'],
@@ -771,9 +756,6 @@ describe('astro:image', () => {
root: './fixtures/core-image-ssr/',
output: 'server',
adapter: testAdapter(),
- experimental: {
- assets: true,
- },
image: {
service: testImageService(),
},
@@ -798,9 +780,6 @@ describe('astro:image', () => {
root: './fixtures/core-image-ssr/',
output: 'server',
adapter: testAdapter(),
- experimental: {
- assets: true,
- },
image: {
service: testImageService(),
},
diff --git a/packages/astro/test/css-import-as-inline.test.js b/packages/astro/test/css-import-as-inline.test.js
index 7dacab377..dcff4f32b 100644
--- a/packages/astro/test/css-import-as-inline.test.js
+++ b/packages/astro/test/css-import-as-inline.test.js
@@ -7,6 +7,8 @@ describe('Importing raw/inlined CSS', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/css-import-as-inline/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
});
describe('Build', () => {
diff --git a/packages/astro/test/css-no-code-split.test.js b/packages/astro/test/css-no-code-split.test.js
index 7fcf7efc2..fc91c60f7 100644
--- a/packages/astro/test/css-no-code-split.test.js
+++ b/packages/astro/test/css-no-code-split.test.js
@@ -7,7 +7,11 @@ describe('vite.build.cssCodeSplit: false', () => {
let fixture;
before(async () => {
- fixture = await loadFixture({ root: './fixtures/css-no-code-split/' });
+ fixture = await loadFixture({
+ root: './fixtures/css-no-code-split/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
+ });
await fixture.build();
});
diff --git a/packages/astro/test/css-order-import.test.js b/packages/astro/test/css-order-import.test.js
index 745187c53..9a5cf6933 100644
--- a/packages/astro/test/css-order-import.test.js
+++ b/packages/astro/test/css-order-import.test.js
@@ -8,6 +8,8 @@ describe('CSS ordering - import order', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/css-order-import/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
});
@@ -133,6 +135,8 @@ describe('CSS ordering - import order', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/css-order-dynamic-import/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/css-order-layout.test.js b/packages/astro/test/css-order-layout.test.js
index 948f7975b..991246faf 100644
--- a/packages/astro/test/css-order-layout.test.js
+++ b/packages/astro/test/css-order-layout.test.js
@@ -8,6 +8,8 @@ describe('CSS ordering - import order with layouts', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/css-order-layout/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
});
diff --git a/packages/astro/test/css-order.test.js b/packages/astro/test/css-order.test.js
index e3333b875..73b147212 100644
--- a/packages/astro/test/css-order.test.js
+++ b/packages/astro/test/css-order.test.js
@@ -75,6 +75,8 @@ describe('CSS production ordering', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/css-order/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/custom-elements.test.js b/packages/astro/test/custom-elements.test.js
deleted file mode 100644
index 53a13b478..000000000
--- a/packages/astro/test/custom-elements.test.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import { expect } from 'chai';
-import { load as cheerioLoad } from 'cheerio';
-import { loadFixture } from './test-utils.js';
-
-describe('Custom Elements', () => {
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({
- root: './fixtures/custom-elements/',
- });
- await fixture.build();
- });
-
- it('Work as constructors', async () => {
- const html = await fixture.readFile('/ctr/index.html');
- const $ = cheerioLoad(html);
-
- // test 1: Element rendered
- expect($('my-element')).to.have.lengthOf(1);
-
- // test 2: shadow rendered
- expect($('my-element template[shadowroot=open][shadowrootmode=open]')).to.have.lengthOf(1);
- });
-
- it('Works with exported tagName', async () => {
- const html = await fixture.readFile('/index.html');
- const $ = cheerioLoad(html);
-
- // test 1: Element rendered
- expect($('my-element')).to.have.lengthOf(1);
-
- // test 2: shadow rendered
- expect($('my-element template[shadowroot=open][shadowrootmode=open]')).to.have.lengthOf(1);
- });
-
- it.skip('Hydration works with exported tagName', async () => {
- const html = await fixture.readFile('/load/index.html');
- const $ = cheerioLoad(html);
-
- // SSR
- // test 1: Element rendered
- expect($('my-element')).to.have.lengthOf(1);
-
- // test 2: shadow rendered
- expect($('my-element template[shadowroot=open][shadowrootmode=open]')).to.have.lengthOf(1);
-
- // Hydration
- // test 3: Component and polyfill scripts bundled separately
- expect($('script')).to.have.lengthOf(2);
- });
-
- it('Custom elements not claimed by renderer are rendered as regular HTML', async () => {
- const html = await fixture.readFile('/nossr/index.html');
- const $ = cheerioLoad(html);
-
- // test 1: Rendered the client-only element
- expect($('client-element')).to.have.lengthOf(1);
- // No children
- expect($('client-element').text()).to.equal('');
- });
-
- it('Can import a client-only element that is nested in JSX', async () => {
- const html = await fixture.readFile('/nested/index.html');
- const $ = cheerioLoad(html);
-
- // test 1: Element rendered
- expect($('client-only-element')).to.have.lengthOf(1);
- });
-});
diff --git a/packages/astro/test/dev-routing.test.js b/packages/astro/test/dev-routing.test.js
index 186355b43..ff5f3a75d 100644
--- a/packages/astro/test/dev-routing.test.js
+++ b/packages/astro/test/dev-routing.test.js
@@ -113,9 +113,9 @@ describe('Development Routing', () => {
expect(response.status).to.equal(200);
});
- it('404 when loading subpath root without trailing slash', async () => {
+ it('200 when loading subpath root without trailing slash', async () => {
const response = await fixture.fetch('/blog');
- expect(response.status).to.equal(404);
+ expect(response.status).to.equal(200);
});
it('200 when loading another page with subpath used', async () => {
@@ -163,9 +163,9 @@ describe('Development Routing', () => {
expect(response.status).to.equal(200);
});
- it('404 when loading subpath root without trailing slash', async () => {
+ it('200 when loading subpath root without trailing slash', async () => {
const response = await fixture.fetch('/blog');
- expect(response.status).to.equal(404);
+ expect(response.status).to.equal(200);
});
it('200 when loading another page with subpath used', async () => {
diff --git a/packages/astro/test/featuresSupport.test.js b/packages/astro/test/featuresSupport.test.js
new file mode 100644
index 000000000..fba8da475
--- /dev/null
+++ b/packages/astro/test/featuresSupport.test.js
@@ -0,0 +1,55 @@
+import { loadFixture } from './test-utils.js';
+import { expect } from 'chai';
+import testAdapter from './test-adapter.js';
+
+describe('Adapter', () => {
+ let fixture;
+
+ it("should error if the adapter doesn't support edge middleware", async () => {
+ try {
+ fixture = await loadFixture({
+ root: './fixtures/middleware-dev/',
+ output: 'server',
+ build: {
+ excludeMiddleware: true,
+ },
+ adapter: testAdapter({
+ extendAdapter: {
+ supportsFeatures: {
+ edgeMiddleware: 'Unsupported',
+ },
+ },
+ }),
+ });
+ await fixture.build();
+ } catch (e) {
+ expect(e.toString()).to.contain(
+ "The adapter my-ssr-adapter doesn't support the feature build.excludeMiddleware."
+ );
+ }
+ });
+
+ it("should error if the adapter doesn't support split build", async () => {
+ try {
+ fixture = await loadFixture({
+ root: './fixtures/middleware-dev/',
+ output: 'server',
+ build: {
+ split: true,
+ },
+ adapter: testAdapter({
+ extendAdapter: {
+ supportsFeatures: {
+ functionPerPage: 'Unsupported',
+ },
+ },
+ }),
+ });
+ await fixture.build();
+ } catch (e) {
+ expect(e.toString()).to.contain(
+ "The adapter my-ssr-adapter doesn't support the feature build.split."
+ );
+ }
+ });
+});
diff --git a/packages/astro/test/fixtures/api-routes/src/pages/binary.dat.ts b/packages/astro/test/fixtures/api-routes/src/pages/binary.dat.ts
index c73589633..cbd382fc3 100644
--- a/packages/astro/test/fixtures/api-routes/src/pages/binary.dat.ts
+++ b/packages/astro/test/fixtures/api-routes/src/pages/binary.dat.ts
@@ -1,5 +1,5 @@
import type { APIRoute } from 'astro';
-export const get: APIRoute = async function () {
+export const GET: APIRoute = async function () {
return new Response(new Uint8Array([0xff]));
};
diff --git a/packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js b/packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js
index 2ed42a5ec..d18eb086f 100644
--- a/packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js
+++ b/packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js
@@ -14,7 +14,7 @@ export function getStaticPaths() {
]
}
-export function get({ params, request }) {
+export function GET({ params, request }) {
return {
body: JSON.stringify({
param: params.param,
diff --git a/packages/astro/test/fixtures/astro-assets-prefix/astro.config.mjs b/packages/astro/test/fixtures/astro-assets-prefix/astro.config.mjs
index 7393b72b8..a5c3c5532 100644
--- a/packages/astro/test/fixtures/astro-assets-prefix/astro.config.mjs
+++ b/packages/astro/test/fixtures/astro-assets-prefix/astro.config.mjs
@@ -1,5 +1,5 @@
-import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
+import { defineConfig } from 'astro/config';
import { testImageService } from '../../test-image-service.js';
// https://astro.build/config
@@ -10,9 +10,6 @@ export default defineConfig({
build: {
assetsPrefix: 'http://localhost:4321',
},
- experimental: {
- assets: true,
- },
image: {
service: testImageService(),
},
diff --git a/packages/astro/test/fixtures/astro-assets/src/pages/index.astro b/packages/astro/test/fixtures/astro-assets/src/pages/index.astro
index 341f2744c..8da7feb0c 100644
--- a/packages/astro/test/fixtures/astro-assets/src/pages/index.astro
+++ b/packages/astro/test/fixtures/astro-assets/src/pages/index.astro
@@ -9,7 +9,7 @@ import p2Url from '../images/penguin2.jpg?url';
</style>
<body>
<h1>Icons</h1>
- <img src={(await import('../images/twitter.png')).default} srcset={`${(await import('../images/twitter.png')).default} 1x, ${(await import('../images/twitter@2x.png')).default} 2x, ${(await import('../images/twitter@3x.png')).default} 3x`} />
+ <img src={(await import('../images/twitter.png')).default.src} srcset={`${(await import('../images/twitter.png')).default.src} 1x, ${(await import('../images/twitter@2x.png')).default.src} 2x, ${(await import('../images/twitter@3x.png')).default.src} 3x`} />
<img srcset="https://ik.imagekit.io/demo/tr:w-300,h-300/medium_cafe_B1iTdD0C.jpg, https://ik.imagekit.io/demo/tr:w-450,h-450/medium_cafe_B1iTdD0C.jpg 600w, https://ik.imagekit.io/demo/tr:w-600,h-600/medium_cafe_B1iTdD0C.jpg 800w">
<img srcset="https://ik.imagekit.io/demo/tr:w-300,h-300/medium_cafe_B1iTdD0C.jpg, https://ik.imagekit.io/demo/tr:w-450,h-450/medium_cafe_B1iTdD0C.jpg 1.5x, https://ik.imagekit.io/demo/tr:w-600,h-600/medium_cafe_B1iTdD0C.jpg 2x">
<!--
@@ -20,7 +20,7 @@ import p2Url from '../images/penguin2.jpg?url';
<source srcset="https://ik.imagekit.io/demo/tr:w-300,h-300/medium_cafe_B1iTdD0C.jpg, https://ik.imagekit.io/demo/tr:w-450,h-450/medium_cafe_B1iTdD0C.jpg 600w, https://ik.imagekit.io/demo/tr:w-600,h-600/medium_cafe_B1iTdD0C.jpg 800w">
</picture>
- <img src={p1Url} id="import-no-url" />
+ <img src={p1Url.src} id="import-no-url" />
<img src={p2Url} id="import-url" />
</body>
</html>
diff --git a/packages/astro/test/fixtures/astro-basic/astro.config.mjs b/packages/astro/test/fixtures/astro-basic/astro.config.mjs
index b7b7dafe6..1b2eb163d 100644
--- a/packages/astro/test/fixtures/astro-basic/astro.config.mjs
+++ b/packages/astro/test/fixtures/astro-basic/astro.config.mjs
@@ -5,5 +5,5 @@ import preact from '@astrojs/preact';
export default defineConfig({
integrations: [preact()],
// make sure CLI flags have precedence
- server: () => ({ port: 3000 })
+ server: () => ({ port: 4321 })
});
diff --git a/packages/astro/test/fixtures/astro-class-list/src/components/Span.astro b/packages/astro/test/fixtures/astro-class-list/src/components/Span.astro
index bfadf035c..a00df0b31 100644
--- a/packages/astro/test/fixtures/astro-class-list/src/components/Span.astro
+++ b/packages/astro/test/fixtures/astro-class-list/src/components/Span.astro
@@ -1 +1 @@
-<span {...Astro.props} />
+<span {...Astro.props}>{Astro.props.class}</span>
diff --git a/packages/astro/test/fixtures/astro-class-list/src/pages/component.astro b/packages/astro/test/fixtures/astro-class-list/src/pages/component.astro
index 5eb64bb12..8dccbbb11 100644
--- a/packages/astro/test/fixtures/astro-class-list/src/pages/component.astro
+++ b/packages/astro/test/fixtures/astro-class-list/src/pages/component.astro
@@ -1,10 +1,8 @@
---
import Component from '../components/Span.astro'
---
-<Component class="test control" />
-<!-- @note: `class:list` will not be parsed if its value is not an expression -->
-<!-- <Component class:list="test" /> -->
+<Component class="test control" />
<Component class:list={'test expression'} />
@@ -13,9 +11,9 @@ import Component from '../components/Span.astro'
<Component class:list={{ test: true, true: true, false: false }} />
<Component class:list={{ test: 1, truthy: '0', noshow1: 0, noshow2: '', noshow3: null, noshow4: undefined }} />
-<Component class:list={new Set(['test', 'set'])} />
+<Component class:list={['test', 'set']} />
-<Component class:list={[ 'hello goodbye', { hello: true, world: true }, new Set([ 'hello', 'friend' ]) ]} />
+<Component class:list={[ 'hello goodbye', { hello: true, world: true }, [ 'hello', 'friend' ] ]} />
<Component class:list={['foo', false && 'bar', true && 'baz']} />
diff --git a/packages/astro/test/fixtures/astro-class-list/src/pages/index.astro b/packages/astro/test/fixtures/astro-class-list/src/pages/index.astro
index d8ad8aec2..13a35610d 100644
--- a/packages/astro/test/fixtures/astro-class-list/src/pages/index.astro
+++ b/packages/astro/test/fixtures/astro-class-list/src/pages/index.astro
@@ -10,9 +10,9 @@
<span class:list={{ test: true, true: true, false: false }} />
<span class:list={{ test: 1, truthy: '0', noshow1: 0, noshow2: '', noshow3: null, noshow4: undefined }} />
-<span class:list={new Set(['test', 'set'])} />
+<span class:list={['test', 'set']} />
-<span class:list={[ 'hello goodbye', { hello: true, world: true }, new Set([ 'hello', 'friend' ]) ]} />
+<span class:list={[ 'hello goodbye', { hello: true, world: true }, [ 'hello', 'friend' ] ]} />
<span class:list={['foo', false && 'bar', true && 'baz']} />
diff --git a/packages/astro/test/fixtures/astro-component-code/src/pages/basic.astro b/packages/astro/test/fixtures/astro-component-code/src/pages/basic.astro
index 9fba8620a..ddbd92fed 100644
--- a/packages/astro/test/fixtures/astro-component-code/src/pages/basic.astro
+++ b/packages/astro/test/fixtures/astro-component-code/src/pages/basic.astro
@@ -1,5 +1,5 @@
---
-import {Code} from 'astro/components';
+import {Code} from 'astro:components';
---
<html>
<head><title>Code component</title></head>
diff --git a/packages/astro/test/fixtures/astro-component-code/src/pages/css-theme.astro b/packages/astro/test/fixtures/astro-component-code/src/pages/css-theme.astro
index 8a4a8debf..743423849 100644
--- a/packages/astro/test/fixtures/astro-component-code/src/pages/css-theme.astro
+++ b/packages/astro/test/fixtures/astro-component-code/src/pages/css-theme.astro
@@ -1,5 +1,5 @@
---
-import {Code} from 'astro/components';
+import {Code} from 'astro:components';
---
<html>
<head><title>Code component</title></head>
diff --git a/packages/astro/test/fixtures/astro-component-code/src/pages/custom-theme.astro b/packages/astro/test/fixtures/astro-component-code/src/pages/custom-theme.astro
index 80cafe5cc..45e92dccf 100644
--- a/packages/astro/test/fixtures/astro-component-code/src/pages/custom-theme.astro
+++ b/packages/astro/test/fixtures/astro-component-code/src/pages/custom-theme.astro
@@ -1,5 +1,5 @@
---
-import {Code} from 'astro/components';
+import {Code} from 'astro:components';
---
<html>
<head><title>Code component</title></head>
diff --git a/packages/astro/test/fixtures/astro-component-code/src/pages/imported.astro b/packages/astro/test/fixtures/astro-component-code/src/pages/imported.astro
index 02e6b4b03..6ea6782d6 100644
--- a/packages/astro/test/fixtures/astro-component-code/src/pages/imported.astro
+++ b/packages/astro/test/fixtures/astro-component-code/src/pages/imported.astro
@@ -1,5 +1,5 @@
---
-import {Code} from 'astro/components';
+import {Code} from 'astro:components';
import riGrammar from '../assets/ri.tmLanguage.json'
import serendipity from '../assets/serendipity-morning.json'
diff --git a/packages/astro/test/fixtures/astro-component-code/src/pages/inline.astro b/packages/astro/test/fixtures/astro-component-code/src/pages/inline.astro
index 05422c8bf..10c17f4e2 100644
--- a/packages/astro/test/fixtures/astro-component-code/src/pages/inline.astro
+++ b/packages/astro/test/fixtures/astro-component-code/src/pages/inline.astro
@@ -1,5 +1,5 @@
---
-import {Code} from 'astro/components';
+import {Code} from 'astro:components';
---
<html>
<head><title>Code component</title></head>
diff --git a/packages/astro/test/fixtures/astro-component-code/src/pages/no-lang.astro b/packages/astro/test/fixtures/astro-component-code/src/pages/no-lang.astro
index ebc1b0810..d0ae5f95f 100644
--- a/packages/astro/test/fixtures/astro-component-code/src/pages/no-lang.astro
+++ b/packages/astro/test/fixtures/astro-component-code/src/pages/no-lang.astro
@@ -1,5 +1,5 @@
---
-import {Code} from 'astro/components';
+import {Code} from 'astro:components';
---
<html>
<head><title>Code component</title></head>
diff --git a/packages/astro/test/fixtures/astro-component-code/src/pages/wrap-false.astro b/packages/astro/test/fixtures/astro-component-code/src/pages/wrap-false.astro
index ca1e238c7..aa4a58707 100644
--- a/packages/astro/test/fixtures/astro-component-code/src/pages/wrap-false.astro
+++ b/packages/astro/test/fixtures/astro-component-code/src/pages/wrap-false.astro
@@ -1,5 +1,5 @@
---
-import {Code} from 'astro/components';
+import {Code} from 'astro:components';
---
<html>
<head><title>Code component</title></head>
diff --git a/packages/astro/test/fixtures/astro-component-code/src/pages/wrap-null.astro b/packages/astro/test/fixtures/astro-component-code/src/pages/wrap-null.astro
index cff0190e8..f72e093dd 100644
--- a/packages/astro/test/fixtures/astro-component-code/src/pages/wrap-null.astro
+++ b/packages/astro/test/fixtures/astro-component-code/src/pages/wrap-null.astro
@@ -1,5 +1,5 @@
---
-import {Code} from 'astro/components';
+import {Code} from 'astro:components';
---
<html>
<head><title>Code component</title></head>
diff --git a/packages/astro/test/fixtures/astro-component-code/src/pages/wrap-true.astro b/packages/astro/test/fixtures/astro-component-code/src/pages/wrap-true.astro
index 82e8f65c2..43ccb5452 100644
--- a/packages/astro/test/fixtures/astro-component-code/src/pages/wrap-true.astro
+++ b/packages/astro/test/fixtures/astro-component-code/src/pages/wrap-true.astro
@@ -1,5 +1,5 @@
---
-import {Code} from 'astro/components';
+import {Code} from 'astro:components';
---
<html>
<head><title>Code component</title></head>
diff --git a/packages/astro/test/fixtures/astro-cookies/src/pages/early-return.astro b/packages/astro/test/fixtures/astro-cookies/src/pages/early-return.astro
index 2796b3989..1457cb882 100644
--- a/packages/astro/test/fixtures/astro-cookies/src/pages/early-return.astro
+++ b/packages/astro/test/fixtures/astro-cookies/src/pages/early-return.astro
@@ -1,5 +1,5 @@
---
-const mode = Astro.cookies.get('prefs').json().mode;
+const mode = Astro.cookies.get('prefs')!.json().mode;
Astro.cookies.set('prefs', {
mode: mode === 'light' ? 'dark' : 'light'
diff --git a/packages/astro/test/fixtures/astro-cookies/src/pages/get-json.astro b/packages/astro/test/fixtures/astro-cookies/src/pages/get-json.astro
index 034881d22..44ee024ae 100644
--- a/packages/astro/test/fixtures/astro-cookies/src/pages/get-json.astro
+++ b/packages/astro/test/fixtures/astro-cookies/src/pages/get-json.astro
@@ -1,5 +1,5 @@
---
-const cookie = Astro.cookies.get('prefs');
+const cookie = Astro.cookies.get('prefs')!;
const prefs = cookie.json();
---
<html>
diff --git a/packages/astro/test/fixtures/astro-cookies/src/pages/set-prefs.js b/packages/astro/test/fixtures/astro-cookies/src/pages/set-prefs.js
index ccbdceff6..93a6a96de 100644
--- a/packages/astro/test/fixtures/astro-cookies/src/pages/set-prefs.js
+++ b/packages/astro/test/fixtures/astro-cookies/src/pages/set-prefs.js
@@ -1,5 +1,5 @@
-export function post({ cookies }) {
+export function POST({ cookies }) {
const mode = cookies.get('prefs').json().mode;
cookies.set('prefs', {
diff --git a/packages/astro/test/fixtures/astro-get-static-paths/src/pages/data/[slug].json.ts b/packages/astro/test/fixtures/astro-get-static-paths/src/pages/data/[slug].json.ts
index 3c7cc63ba..32a0fd140 100644
--- a/packages/astro/test/fixtures/astro-get-static-paths/src/pages/data/[slug].json.ts
+++ b/packages/astro/test/fixtures/astro-get-static-paths/src/pages/data/[slug].json.ts
@@ -5,7 +5,7 @@ export async function getStaticPaths() {
];
}
-export async function get() {
+export async function GET() {
return {
body: JSON.stringify({
title: '[slug]'
diff --git a/packages/astro/test/fixtures/astro-get-static-paths/src/pages/nested-arrays/[slug].astro b/packages/astro/test/fixtures/astro-get-static-paths/src/pages/nested-arrays/[slug].astro
deleted file mode 100644
index 9bd7b4f41..000000000
--- a/packages/astro/test/fixtures/astro-get-static-paths/src/pages/nested-arrays/[slug].astro
+++ /dev/null
@@ -1,8 +0,0 @@
----
- export function getStaticPaths() {
- return [
- [ { params: {slug: "slug1"} } ],
- [ { params: {slug: "slug2"} } ],
- ]
- }
----
diff --git a/packages/astro/test/fixtures/astro-markdown-frontmatter-injection/src/pages/glob.json.js b/packages/astro/test/fixtures/astro-markdown-frontmatter-injection/src/pages/glob.json.js
index a56f5306f..3aae6d89a 100644
--- a/packages/astro/test/fixtures/astro-markdown-frontmatter-injection/src/pages/glob.json.js
+++ b/packages/astro/test/fixtures/astro-markdown-frontmatter-injection/src/pages/glob.json.js
@@ -1,4 +1,4 @@
-export async function get() {
+export async function GET() {
const docs = await import.meta.glob('./*.md', { eager: true });
return {
body: JSON.stringify(Object.values(docs).map(doc => doc.frontmatter)),
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/headings-glob.json.js b/packages/astro/test/fixtures/astro-markdown/src/pages/headings-glob.json.js
index 631250c33..b2c9ea6ea 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/headings-glob.json.js
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/headings-glob.json.js
@@ -1,6 +1,6 @@
import { getHeadings } from './with-layout.md';
-export async function get() {
+export async function GET() {
return {
body: JSON.stringify({
headings: getHeadings(),
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js b/packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js
index ef933a373..82977443d 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js
@@ -1,6 +1,6 @@
import { rawContent, compiledContent } from './basic.md';
-export async function get() {
+export async function GET() {
return {
body: JSON.stringify({
raw: rawContent(),
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/vite-env-vars-glob.json.js b/packages/astro/test/fixtures/astro-markdown/src/pages/vite-env-vars-glob.json.js
index 4a7e4dd78..7a5d00f47 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/vite-env-vars-glob.json.js
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/vite-env-vars-glob.json.js
@@ -1,6 +1,6 @@
import { frontmatter } from './vite-env-vars.md';
-export async function get() {
+export async function GET() {
return {
body: JSON.stringify(frontmatter),
}
diff --git a/packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro b/packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro
index c4cc39739..33df98cf5 100644
--- a/packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro
+++ b/packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro
@@ -1,7 +1,7 @@
---
export async function getStaticPaths({paginate}) {
const allPosts = await Astro.glob('../../post/*.md');
- return ['red', 'blue'].map((filter) => {
+ return ['red', 'blue'].flatMap((filter) => {
const filteredPosts = allPosts.filter((post) => post.frontmatter.tag === filter);
return paginate(filteredPosts, {
params: { slug: filter },
diff --git a/packages/astro/test/fixtures/astro-slots-nested/astro.config.mjs b/packages/astro/test/fixtures/astro-slots-nested/astro.config.mjs
index 4a8807ed0..6f37285c6 100644
--- a/packages/astro/test/fixtures/astro-slots-nested/astro.config.mjs
+++ b/packages/astro/test/fixtures/astro-slots-nested/astro.config.mjs
@@ -7,9 +7,9 @@ import vue from '@astrojs/vue';
export default defineConfig({
integrations: [
- react(),
- preact(),
- solid(),
+ preact({ include: ['**/preact/*'] }),
+ solid({ include: ['**/solid/*'] }),
+ react({ include: ['**/react/*'] }),
svelte(),
vue()
]
diff --git a/packages/astro/test/fixtures/astro-slots-nested/src/components/PassesChildrenP.tsx b/packages/astro/test/fixtures/astro-slots-nested/src/components/preact/PassesChildrenP.tsx
index ec89ed15c..ec89ed15c 100644
--- a/packages/astro/test/fixtures/astro-slots-nested/src/components/PassesChildrenP.tsx
+++ b/packages/astro/test/fixtures/astro-slots-nested/src/components/preact/PassesChildrenP.tsx
diff --git a/packages/astro/test/fixtures/astro-slots-nested/src/components/Inner.tsx b/packages/astro/test/fixtures/astro-slots-nested/src/components/react/Inner.tsx
index b7cfe16a2..b7cfe16a2 100644
--- a/packages/astro/test/fixtures/astro-slots-nested/src/components/Inner.tsx
+++ b/packages/astro/test/fixtures/astro-slots-nested/src/components/react/Inner.tsx
diff --git a/packages/astro/test/fixtures/astro-slots-nested/src/components/Parent.jsx b/packages/astro/test/fixtures/astro-slots-nested/src/components/react/Parent.jsx
index 340dacab3..340dacab3 100644
--- a/packages/astro/test/fixtures/astro-slots-nested/src/components/Parent.jsx
+++ b/packages/astro/test/fixtures/astro-slots-nested/src/components/react/Parent.jsx
diff --git a/packages/astro/test/fixtures/astro-slots-nested/src/components/PassesChildren.tsx b/packages/astro/test/fixtures/astro-slots-nested/src/components/react/PassesChildren.tsx
index e764d5867..e764d5867 100644
--- a/packages/astro/test/fixtures/astro-slots-nested/src/components/PassesChildren.tsx
+++ b/packages/astro/test/fixtures/astro-slots-nested/src/components/react/PassesChildren.tsx
diff --git a/packages/astro/test/fixtures/astro-slots-nested/src/components/PassesChildrenS.tsx b/packages/astro/test/fixtures/astro-slots-nested/src/components/solid/PassesChildrenS.tsx
index d539c55dc..d539c55dc 100644
--- a/packages/astro/test/fixtures/astro-slots-nested/src/components/PassesChildrenS.tsx
+++ b/packages/astro/test/fixtures/astro-slots-nested/src/components/solid/PassesChildrenS.tsx
diff --git a/packages/astro/test/fixtures/astro-slots-nested/src/pages/component-slot.astro b/packages/astro/test/fixtures/astro-slots-nested/src/pages/component-slot.astro
index b9a03f887..d803f3ff7 100644
--- a/packages/astro/test/fixtures/astro-slots-nested/src/pages/component-slot.astro
+++ b/packages/astro/test/fixtures/astro-slots-nested/src/pages/component-slot.astro
@@ -1,6 +1,6 @@
---
import SlotRender from '../components/SlotRender.astro'
-import Inner from '../components/Inner'
+import Inner from '../components/react/Inner'
---
<html lang="en">
diff --git a/packages/astro/test/fixtures/astro-slots-nested/src/pages/hidden-nested.astro b/packages/astro/test/fixtures/astro-slots-nested/src/pages/hidden-nested.astro
index e51ce00a0..dcb3ebcba 100644
--- a/packages/astro/test/fixtures/astro-slots-nested/src/pages/hidden-nested.astro
+++ b/packages/astro/test/fixtures/astro-slots-nested/src/pages/hidden-nested.astro
@@ -1,6 +1,6 @@
---
-import Parent from '../components/Parent'
-import Inner from '../components/Inner'
+import Parent from '../components/react/Parent'
+import Inner from '../components/react/Inner'
---
<html lang="en">
diff --git a/packages/astro/test/fixtures/astro-slots-nested/src/pages/server-component-nested.astro b/packages/astro/test/fixtures/astro-slots-nested/src/pages/server-component-nested.astro
index b5a3d72a0..ec4ae3158 100644
--- a/packages/astro/test/fixtures/astro-slots-nested/src/pages/server-component-nested.astro
+++ b/packages/astro/test/fixtures/astro-slots-nested/src/pages/server-component-nested.astro
@@ -1,7 +1,7 @@
---
-import PassesChildren from '../components/PassesChildren.jsx';
-import PassesChildrenP from '../components/PassesChildrenP.jsx';
-import PassesChildrenS from '../components/PassesChildrenS.jsx';
+import PassesChildren from '../components/react/PassesChildren.jsx';
+import PassesChildrenP from '../components/preact/PassesChildrenP.jsx';
+import PassesChildrenS from '../components/solid/PassesChildrenS.jsx';
import PassesChildrenSv from '../components/PassesChildrenSv.svelte';
import PassesChildrenV from '../components/PassesChildrenV.vue';
---
diff --git a/packages/astro/test/fixtures/code-component/src/pages/index.astro b/packages/astro/test/fixtures/code-component/src/pages/index.astro
index 763f32cae..d165a4308 100644
--- a/packages/astro/test/fixtures/code-component/src/pages/index.astro
+++ b/packages/astro/test/fixtures/code-component/src/pages/index.astro
@@ -1,5 +1,5 @@
---
-import { Code } from 'astro/components';
+import { Code } from 'astro:components';
---
<html>
<head>
diff --git a/packages/astro/test/fixtures/content-collection-references/astro.config.mjs b/packages/astro/test/fixtures/content-collection-references/astro.config.mjs
index 7287a1259..409407db3 100644
--- a/packages/astro/test/fixtures/content-collection-references/astro.config.mjs
+++ b/packages/astro/test/fixtures/content-collection-references/astro.config.mjs
@@ -3,9 +3,6 @@ import { testImageService } from '../../test-image-service.js';
// https://astro.build/config
export default defineConfig({
- experimental: {
- assets: true,
- },
image: {
service: testImageService(),
},
diff --git a/packages/astro/test/fixtures/content-collection-references/src/content/banners/welcome.json b/packages/astro/test/fixtures/content-collection-references/src/content/banners/welcome.json
index c62d06aab..8ab06ff1f 100644
--- a/packages/astro/test/fixtures/content-collection-references/src/content/banners/welcome.json
+++ b/packages/astro/test/fixtures/content-collection-references/src/content/banners/welcome.json
@@ -1,4 +1,4 @@
{
"alt": "Futuristic landscape with chrome buildings and blue skies",
- "src": "~/assets/the-future.jpg"
+ "src": "../../assets/the-future.jpg"
}
diff --git a/packages/astro/test/fixtures/content-collection-references/src/pages/welcome-data.json.js b/packages/astro/test/fixtures/content-collection-references/src/pages/welcome-data.json.js
index 4f529dac6..a461a1a65 100644
--- a/packages/astro/test/fixtures/content-collection-references/src/pages/welcome-data.json.js
+++ b/packages/astro/test/fixtures/content-collection-references/src/pages/welcome-data.json.js
@@ -1,6 +1,6 @@
import { getEntry, getEntries } from 'astro:content';
-export async function get() {
+export async function GET() {
const welcomePost = await getEntry('blog', 'welcome');
if (!welcomePost?.data) {
diff --git a/packages/astro/test/fixtures/content-collections/src/pages/collections.json.js b/packages/astro/test/fixtures/content-collections/src/pages/collections.json.js
index e74d03ad9..e335d2b05 100644
--- a/packages/astro/test/fixtures/content-collections/src/pages/collections.json.js
+++ b/packages/astro/test/fixtures/content-collections/src/pages/collections.json.js
@@ -2,7 +2,7 @@ import { getCollection } from 'astro:content';
import * as devalue from 'devalue';
import { stripAllRenderFn } from '../utils.js';
-export async function get() {
+export async function GET() {
const withoutConfig = stripAllRenderFn(await getCollection('without-config'));
const withSchemaConfig = stripAllRenderFn(await getCollection('with-schema-config'));
const withSlugConfig = stripAllRenderFn(await getCollection('with-custom-slugs'));
diff --git a/packages/astro/test/fixtures/content-collections/src/pages/entries.json.js b/packages/astro/test/fixtures/content-collections/src/pages/entries.json.js
index 0d7d22d08..311b76cc8 100644
--- a/packages/astro/test/fixtures/content-collections/src/pages/entries.json.js
+++ b/packages/astro/test/fixtures/content-collections/src/pages/entries.json.js
@@ -2,7 +2,7 @@ import { getEntryBySlug } from 'astro:content';
import * as devalue from 'devalue';
import { stripRenderFn } from '../utils.js';
-export async function get() {
+export async function GET() {
const columbiaWithoutConfig = stripRenderFn(await getEntryBySlug('without-config', 'columbia'));
const oneWithSchemaConfig = stripRenderFn(await getEntryBySlug('with-schema-config', 'one'));
const twoWithSlugConfig = stripRenderFn(await getEntryBySlug('with-custom-slugs', 'interesting-two'));
diff --git a/packages/astro/test/fixtures/core-image-base/src/content/blog/one.md b/packages/astro/test/fixtures/core-image-base/src/content/blog/one.md
index 59a5b77ba..d449290ef 100644
--- a/packages/astro/test/fixtures/core-image-base/src/content/blog/one.md
+++ b/packages/astro/test/fixtures/core-image-base/src/content/blog/one.md
@@ -1,6 +1,6 @@
---
title: One
-image: ~/assets/penguin2.jpg
+image: ../../assets/penguin2.jpg
cover:
image: ../../assets/penguin1.jpg
---
diff --git a/packages/astro/test/fixtures/core-image-base/tsconfig.json b/packages/astro/test/fixtures/core-image-base/tsconfig.json
new file mode 100644
index 000000000..b5bf6a715
--- /dev/null
+++ b/packages/astro/test/fixtures/core-image-base/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "astro/tsconfigs/base",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "~/assets/*": ["src/assets/*"]
+ },
+ }
+}
diff --git a/packages/astro/test/fixtures/core-image-ssg/tsconfig.json b/packages/astro/test/fixtures/core-image-ssg/tsconfig.json
new file mode 100644
index 000000000..b5bf6a715
--- /dev/null
+++ b/packages/astro/test/fixtures/core-image-ssg/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "astro/tsconfigs/base",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "~/assets/*": ["src/assets/*"]
+ },
+ }
+}
diff --git a/packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts b/packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts
index c10946318..7847baf62 100644
--- a/packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts
+++ b/packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts
@@ -1,6 +1,6 @@
import type { APIRoute } from "../../../../../src/@types/astro";
-export const get = (async ({ params, request }) => {
+export const GET = (async ({ params, request }) => {
const url = new URL(request.url);
const src = url.searchParams.get("src");
diff --git a/packages/astro/test/fixtures/core-image/src/content/blog/one.md b/packages/astro/test/fixtures/core-image/src/content/blog/one.md
index ef0993f63..8c6522534 100644
--- a/packages/astro/test/fixtures/core-image/src/content/blog/one.md
+++ b/packages/astro/test/fixtures/core-image/src/content/blog/one.md
@@ -1,11 +1,11 @@
---
title: One
-image: ~/assets/penguin2.jpg
+image: ../../assets/penguin2.jpg
cover:
image: ../../assets/penguin1.jpg
arrayOfImages:
- - ~/assets/penguin2.jpg
- - ~/assets/penguin1.jpg
+ - ../../assets/penguin2.jpg
+ - ../../assets/penguin1.jpg
refinedImage: ../../assets/penguin1.jpg
---
diff --git a/packages/astro/test/fixtures/core-image/tsconfig.json b/packages/astro/test/fixtures/core-image/tsconfig.json
new file mode 100644
index 000000000..b5bf6a715
--- /dev/null
+++ b/packages/astro/test/fixtures/core-image/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "astro/tsconfigs/base",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "~/assets/*": ["src/assets/*"]
+ },
+ }
+}
diff --git a/packages/astro/test/fixtures/custom-elements/astro.config.mjs b/packages/astro/test/fixtures/custom-elements/astro.config.mjs
deleted file mode 100644
index 439334f8f..000000000
--- a/packages/astro/test/fixtures/custom-elements/astro.config.mjs
+++ /dev/null
@@ -1,6 +0,0 @@
-import { defineConfig } from 'astro/config';
-import ceIntegration from '@test/custom-element-renderer';
-
-export default defineConfig({
- integrations: [ceIntegration()],
-})
diff --git a/packages/astro/test/fixtures/custom-elements/my-component-lib/hydration-polyfill.js b/packages/astro/test/fixtures/custom-elements/my-component-lib/hydration-polyfill.js
deleted file mode 100644
index 665844481..000000000
--- a/packages/astro/test/fixtures/custom-elements/my-component-lib/hydration-polyfill.js
+++ /dev/null
@@ -1 +0,0 @@
-globalThis.somePolyfillHere = '';
diff --git a/packages/astro/test/fixtures/custom-elements/my-component-lib/index.js b/packages/astro/test/fixtures/custom-elements/my-component-lib/index.js
deleted file mode 100644
index 5b9bba7e6..000000000
--- a/packages/astro/test/fixtures/custom-elements/my-component-lib/index.js
+++ /dev/null
@@ -1,31 +0,0 @@
-function getViteConfiguration() {
- return {
- optimizeDeps: {
- include: ['@test/custom-element-renderer/polyfill.js', '@test/custom-element-renderer/hydration-polyfill.js'],
- exclude: ['@test/custom-element-renderer/server.js']
- },
- };
-}
-
-export default function () {
- return {
- name: '@test/custom-element-renderer',
- hooks: {
- 'astro:config:setup': ({ updateConfig, addRenderer, injectScript }) => {
- // Inject the necessary polyfills on every page
- injectScript('head-inline', `import('@test/custom-element-renderer/polyfill.js');`);
- // Inject the hydration code, before a component is hydrated.
- injectScript('before-hydration', `import('@test/custom-element-renderer/hydration-polyfill.js');`);
- // Add the lit renderer so that Astro can understand lit components.
- addRenderer({
- name: '@test/custom-element-renderer',
- serverEntrypoint: '@test/custom-element-renderer/server.js',
- });
- // Update the vite configuration.
- updateConfig({
- vite: getViteConfiguration(),
- });
- },
- },
- };
-}
diff --git a/packages/astro/test/fixtures/custom-elements/my-component-lib/package.json b/packages/astro/test/fixtures/custom-elements/my-component-lib/package.json
deleted file mode 100644
index f1d53b985..000000000
--- a/packages/astro/test/fixtures/custom-elements/my-component-lib/package.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "name": "@test/custom-element-renderer",
- "version": "0.1.0",
- "private": true,
- "main": "index.js",
- "type": "module",
- "exports": {
- ".": "./index.js",
- "./server.js": "./server.js",
- "./polyfill.js": "./polyfill.js",
- "./hydration-polyfill.js": "./hydration-polyfill.js"
- }
-}
diff --git a/packages/astro/test/fixtures/custom-elements/my-component-lib/polyfill.js b/packages/astro/test/fixtures/custom-elements/my-component-lib/polyfill.js
deleted file mode 100644
index 92788352b..000000000
--- a/packages/astro/test/fixtures/custom-elements/my-component-lib/polyfill.js
+++ /dev/null
@@ -1,2 +0,0 @@
-console.log('this is a polyfill');
-export default {};
diff --git a/packages/astro/test/fixtures/custom-elements/my-component-lib/server.js b/packages/astro/test/fixtures/custom-elements/my-component-lib/server.js
deleted file mode 100644
index 688923159..000000000
--- a/packages/astro/test/fixtures/custom-elements/my-component-lib/server.js
+++ /dev/null
@@ -1,30 +0,0 @@
-function getConstructor(Component) {
- if (typeof Component === 'string') {
- const tagName = Component;
- Component = customElements.get(tagName);
- }
- return Component;
-}
-
-function check(component) {
- const Component = getConstructor(component);
- if (typeof Component === 'function' && globalThis.HTMLElement.isPrototypeOf(Component)) {
- return true;
- }
- return false;
-}
-
-function renderToStaticMarkup(component, props, innerHTML) {
- const Component = getConstructor(component);
- const el = new Component();
- el.connectedCallback();
- const html = `<${el.localName}><template shadowroot="open" shadowrootmode="open">${el.shadowRoot.innerHTML}</template>${el.innerHTML}</${el.localName}>`
- return {
- html
- };
-}
-
-export default {
- check,
- renderToStaticMarkup
-};
diff --git a/packages/astro/test/fixtures/custom-elements/my-component-lib/shim.js b/packages/astro/test/fixtures/custom-elements/my-component-lib/shim.js
deleted file mode 100644
index eb969e528..000000000
--- a/packages/astro/test/fixtures/custom-elements/my-component-lib/shim.js
+++ /dev/null
@@ -1,28 +0,0 @@
-globalThis.customElements = {
- _elements: new Map(),
- define(name, ctr) {
- ctr.tagName = name;
- this._elements.set(name, ctr);
- },
- get(name) {
- return this._elements.get(name);
- }
-};
-
-globalThis.HTMLElement = class {
- attachShadow() {
- this.shadowRoot = new HTMLElement();
- }
-
- get localName() {
- return this.constructor.tagName;
- }
-
- get innerHTML() {
- return this._innerHTML;
- }
-
- set innerHTML(val) {
- this._innerHTML = val;
- }
-};
diff --git a/packages/astro/test/fixtures/custom-elements/package.json b/packages/astro/test/fixtures/custom-elements/package.json
deleted file mode 100644
index 80246a3b0..000000000
--- a/packages/astro/test/fixtures/custom-elements/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "@test/custom-elements",
- "version": "0.0.0",
- "private": true,
- "dependencies": {
- "astro": "workspace:*",
- "@test/custom-element-renderer": "workspace:*"
- }
-}
diff --git a/packages/astro/test/fixtures/custom-elements/src/components/my-element.js b/packages/astro/test/fixtures/custom-elements/src/components/my-element.js
deleted file mode 100644
index ffc5e6ffb..000000000
--- a/packages/astro/test/fixtures/custom-elements/src/components/my-element.js
+++ /dev/null
@@ -1,13 +0,0 @@
-export const tagName = 'my-element';
-
-class MyElement extends HTMLElement {
- connectedCallback() {
- this.attachShadow({ mode: 'open' });
- this.shadowRoot.innerHTML = `<span id="custom">Hello from a custom element!</span>`;
- this.innerHTML = `<div id="custom-light">Light dom!</div>`
- }
-}
-
-customElements.define(tagName, MyElement);
-
-export default MyElement;
diff --git a/packages/astro/test/fixtures/custom-elements/src/pages/ctr.astro b/packages/astro/test/fixtures/custom-elements/src/pages/ctr.astro
deleted file mode 100644
index 8b1c83512..000000000
--- a/packages/astro/test/fixtures/custom-elements/src/pages/ctr.astro
+++ /dev/null
@@ -1,16 +0,0 @@
----
-import MyElement from '../components/my-element.js';
-
-const title = 'My App';
----
-
-<html>
-<head>
- <title>{title}</title>
-</head>
-<body>
- <h1>{title}</h1>
-
- <MyElement />
-</body>
-</html> \ No newline at end of file
diff --git a/packages/astro/test/fixtures/custom-elements/src/pages/index.astro b/packages/astro/test/fixtures/custom-elements/src/pages/index.astro
deleted file mode 100644
index b7380624a..000000000
--- a/packages/astro/test/fixtures/custom-elements/src/pages/index.astro
+++ /dev/null
@@ -1,15 +0,0 @@
----
-import '../components/my-element.js';
-const title = 'My App';
----
-
-<html>
-<head>
- <title>{title}</title>
-</head>
-<body>
- <h1>{title}</h1>
-
- <my-element></my-element>
-</body>
-</html> \ No newline at end of file
diff --git a/packages/astro/test/fixtures/custom-elements/src/pages/nested.astro b/packages/astro/test/fixtures/custom-elements/src/pages/nested.astro
deleted file mode 100644
index f23e3b6bc..000000000
--- a/packages/astro/test/fixtures/custom-elements/src/pages/nested.astro
+++ /dev/null
@@ -1,11 +0,0 @@
----
-let show = true
----
-<html>
-<head>
- <title>Custom element not imported but nested</title>
-</head>
-<body>
- {show && <client-only-element></client-only-element>}
-</body>
-</html> \ No newline at end of file
diff --git a/packages/astro/test/fixtures/custom-elements/src/pages/nossr.astro b/packages/astro/test/fixtures/custom-elements/src/pages/nossr.astro
deleted file mode 100644
index 53e580afb..000000000
--- a/packages/astro/test/fixtures/custom-elements/src/pages/nossr.astro
+++ /dev/null
@@ -1,14 +0,0 @@
----
-const title = 'My App';
----
-
-<html>
-<head>
- <title>{title}</title>
-</head>
-<body>
- <h1>{title}</h1>
-
- <client-element></client-element>
-</body>
-</html> \ No newline at end of file
diff --git a/packages/astro/test/fixtures/data-collections/src/pages/authors/[id].json.js b/packages/astro/test/fixtures/data-collections/src/pages/authors/[id].json.js
index 1cc26fb73..76f4d5760 100644
--- a/packages/astro/test/fixtures/data-collections/src/pages/authors/[id].json.js
+++ b/packages/astro/test/fixtures/data-collections/src/pages/authors/[id].json.js
@@ -7,7 +7,7 @@ export function getStaticPaths() {
}
/** @param {import('astro').APIContext} params */
-export async function get({ params }) {
+export async function GET({ params }) {
const { id } = params;
const author = await getEntry('authors-without-config', id);
if (!author) {
diff --git a/packages/astro/test/fixtures/data-collections/src/pages/authors/all.json.js b/packages/astro/test/fixtures/data-collections/src/pages/authors/all.json.js
index e4c804064..5b5007fed 100644
--- a/packages/astro/test/fixtures/data-collections/src/pages/authors/all.json.js
+++ b/packages/astro/test/fixtures/data-collections/src/pages/authors/all.json.js
@@ -1,6 +1,6 @@
import { getCollection } from 'astro:content';
-export async function get() {
+export async function GET() {
const authors = await getCollection('authors-without-config');
return {
diff --git a/packages/astro/test/fixtures/data-collections/src/pages/translations/[lang].json.js b/packages/astro/test/fixtures/data-collections/src/pages/translations/[lang].json.js
index 73c90354d..ab8cc764e 100644
--- a/packages/astro/test/fixtures/data-collections/src/pages/translations/[lang].json.js
+++ b/packages/astro/test/fixtures/data-collections/src/pages/translations/[lang].json.js
@@ -7,7 +7,7 @@ export function getStaticPaths() {
}
/** @param {import('astro').APIContext} params */
-export async function get({ params }) {
+export async function GET({ params }) {
const { lang } = params;
const translations = await getEntry('i18n', lang);
if (!translations) {
diff --git a/packages/astro/test/fixtures/data-collections/src/pages/translations/all.json.js b/packages/astro/test/fixtures/data-collections/src/pages/translations/all.json.js
index 7d953838f..e8a1fee92 100644
--- a/packages/astro/test/fixtures/data-collections/src/pages/translations/all.json.js
+++ b/packages/astro/test/fixtures/data-collections/src/pages/translations/all.json.js
@@ -1,6 +1,6 @@
import { getCollection } from 'astro:content';
-export async function get() {
+export async function GET() {
const translations = await getCollection('i18n');
return {
diff --git a/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts b/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts
index 8f64c2401..8fccd6695 100644
--- a/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts
+++ b/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts
@@ -2,7 +2,7 @@ import type { APIRoute } from "astro";
const slugs = ["one", undefined];
-export const get: APIRoute = ({ params }) => {
+export const GET: APIRoute = ({ params }) => {
return {
body: JSON.stringify({
slug: params.slug || "index",
diff --git a/packages/astro/test/fixtures/jsx/astro.config.mjs b/packages/astro/test/fixtures/jsx/astro.config.mjs
index 5b84d23a8..61d0d075e 100644
--- a/packages/astro/test/fixtures/jsx/astro.config.mjs
+++ b/packages/astro/test/fixtures/jsx/astro.config.mjs
@@ -1,13 +1,27 @@
import { defineConfig } from 'astro/config';
import renderer from 'astro/jsx/renderer.js';
+import mdx from '@astrojs/mdx';
import preact from '@astrojs/preact';
import react from '@astrojs/react';
import svelte from '@astrojs/svelte';
import vue from '@astrojs/vue';
import solid from '@astrojs/solid-js';
+
export default defineConfig({
integrations: [
+ preact({
+ include: ['**/preact/*']
+ }),
+ react({
+ include: ['**/react/*']
+ }),
+ solid({
+ include: ['**/solid/*'],
+ }),
+ mdx(),
+ svelte(),
+ vue(),
{
name: '@astrojs/test-jsx',
hooks: {
@@ -16,10 +30,5 @@ export default defineConfig({
}
}
},
- preact(),
- react(),
- svelte(),
- vue(),
- solid(),
]
})
diff --git a/packages/astro/test/fixtures/jsx/package.json b/packages/astro/test/fixtures/jsx/package.json
index 1e7001b47..e5b12ece4 100644
--- a/packages/astro/test/fixtures/jsx/package.json
+++ b/packages/astro/test/fixtures/jsx/package.json
@@ -3,6 +3,7 @@
"version": "0.0.0",
"private": true,
"devDependencies": {
+ "@astrojs/mdx": "workspace:*",
"@astrojs/preact": "workspace:*",
"@astrojs/react": "workspace:*",
"@astrojs/solid-js": "workspace:*",
diff --git a/packages/astro/test/fixtures/jsx/src/components/Content.mdx b/packages/astro/test/fixtures/jsx/src/components/Content.mdx
new file mode 100644
index 000000000..7a8bbc0f4
--- /dev/null
+++ b/packages/astro/test/fixtures/jsx/src/components/Content.mdx
@@ -0,0 +1,5 @@
+import ReactCounter from './react/ReactCounter.jsx'
+
+# Hello world
+
+<ReactCounter />
diff --git a/packages/astro/test/fixtures/jsx/src/components/Frameworks.jsx b/packages/astro/test/fixtures/jsx/src/components/Frameworks.jsx
deleted file mode 100644
index 2cc175964..000000000
--- a/packages/astro/test/fixtures/jsx/src/components/Frameworks.jsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import 'astro/jsx-runtime';
-import { Test } from "./Test";
-
-import PreactCounter from "./PreactCounter";
-import ReactCounter from "./ReactCounter";
-import SolidCounter from "./SolidCounter";
-import SvelteCounter from "./SvelteCounter.svelte";
-import VueCounter from "./VueCounter.vue";
-
-export function Preact() {
- return <Test case="has-preact"><PreactCounter /></Test>
-}
-
-export function React() {
- return <Test case="has-react"><ReactCounter /></Test>
-}
-
-export function Solid() {
- return <Test case="has-solid"><SolidCounter /></Test>
-}
-
-export function Svelte() {
- return <Test case="has-svelte"><SvelteCounter /></Test>
-}
-
-export function Vue() {
- return <Test case="has-vue"><VueCounter /></Test>
-}
diff --git a/packages/astro/test/fixtures/jsx/src/components/PreactCounter.tsx b/packages/astro/test/fixtures/jsx/src/components/preact/PreactCounter.tsx
index cdb368377..cdb368377 100644
--- a/packages/astro/test/fixtures/jsx/src/components/PreactCounter.tsx
+++ b/packages/astro/test/fixtures/jsx/src/components/preact/PreactCounter.tsx
diff --git a/packages/astro/test/fixtures/jsx/src/components/ReactCounter.jsx b/packages/astro/test/fixtures/jsx/src/components/react/ReactCounter.jsx
index 5c5a001e8..7404d45bc 100644
--- a/packages/astro/test/fixtures/jsx/src/components/ReactCounter.jsx
+++ b/packages/astro/test/fixtures/jsx/src/components/react/ReactCounter.jsx
@@ -6,6 +6,8 @@ export default function ReactCounter() {
const add = () => setCount((i) => i + 1);
const subtract = () => setCount((i) => i - 1);
+ debugger;
+
return (
<div id="react">
<div className="counter">
diff --git a/packages/astro/test/fixtures/jsx/src/components/SolidCounter.jsx b/packages/astro/test/fixtures/jsx/src/components/solid/SolidCounter.jsx
index 9cfd85d02..9cfd85d02 100644
--- a/packages/astro/test/fixtures/jsx/src/components/SolidCounter.jsx
+++ b/packages/astro/test/fixtures/jsx/src/components/solid/SolidCounter.jsx
diff --git a/packages/astro/test/fixtures/jsx/src/pages/frameworks.astro b/packages/astro/test/fixtures/jsx/src/pages/frameworks.astro
index ede0f542c..cfd28a3d4 100644
--- a/packages/astro/test/fixtures/jsx/src/pages/frameworks.astro
+++ b/packages/astro/test/fixtures/jsx/src/pages/frameworks.astro
@@ -1,13 +1,27 @@
---
-import * as Framework from '../components/Frameworks'
+import FrameworkSolid from '../components/solid/SolidCounter.jsx'
+import FrameworkPreact from '../components/preact/PreactCounter.jsx'
+import FrameworkReact from '../components/react/ReactCounter.jsx'
+import FrameworkSvelte from '../components/SvelteCounter.svelte'
+import FrameworkVue from '../components/VueCounter.vue'
+import FrameworkMDX from '../components/Content.mdx'
---
-<Framework.Preact />
-
-<Framework.React />
-
-<Framework.Solid />
-
-<Framework.Svelte />
-
-<Framework.Vue />
+<div id="preact-wrapper">
+ <FrameworkPreact />
+</div>
+<div id="rreact-wrapper">
+ <FrameworkReact />
+</div>
+<div id="solid-wrapper">
+ <FrameworkSolid />
+</div>
+<div id="svelte-wrapper">
+ <FrameworkSvelte />
+</div>
+<div id="vue-wrapper">
+ <FrameworkVue />
+</div>
+<div id="mdx-wrapper">
+ <FrameworkMDX />
+</div>
diff --git a/packages/astro/test/fixtures/middleware-dev/src/middleware.js b/packages/astro/test/fixtures/middleware-dev/src/middleware.js
index 2f9fc08b9..854c997c1 100644
--- a/packages/astro/test/fixtures/middleware-dev/src/middleware.js
+++ b/packages/astro/test/fixtures/middleware-dev/src/middleware.js
@@ -1,4 +1,4 @@
-import { sequence, defineMiddleware } from 'astro/middleware';
+import { sequence, defineMiddleware } from 'astro:middleware';
const first = defineMiddleware(async (context, next) => {
if (context.request.url.includes('/lorem')) {
diff --git a/packages/astro/test/fixtures/middleware-dev/src/pages/api/endpoint.js b/packages/astro/test/fixtures/middleware-dev/src/pages/api/endpoint.js
index dadff6edb..69d989bc0 100644
--- a/packages/astro/test/fixtures/middleware-dev/src/pages/api/endpoint.js
+++ b/packages/astro/test/fixtures/middleware-dev/src/pages/api/endpoint.js
@@ -1,4 +1,4 @@
-export function get() {
+export function GET() {
const object = {
name: 'Endpoint!!',
};
diff --git a/packages/astro/test/fixtures/middleware-ssg/src/middleware.js b/packages/astro/test/fixtures/middleware-ssg/src/middleware.js
index f28d89f67..04c0bfb3a 100644
--- a/packages/astro/test/fixtures/middleware-ssg/src/middleware.js
+++ b/packages/astro/test/fixtures/middleware-ssg/src/middleware.js
@@ -1,4 +1,4 @@
-import { sequence, defineMiddleware } from 'astro/middleware';
+import { sequence, defineMiddleware } from 'astro:middleware';
const first = defineMiddleware(async (context, next) => {
if (context.request.url.includes('/second')) {
diff --git a/packages/astro/test/fixtures/non-html-pages/src/pages/about-object.json.ts b/packages/astro/test/fixtures/non-html-pages/src/pages/about-object.json.ts
new file mode 100644
index 000000000..3936cbe85
--- /dev/null
+++ b/packages/astro/test/fixtures/non-html-pages/src/pages/about-object.json.ts
@@ -0,0 +1,12 @@
+// NOTE: test deprecated object form
+// Returns the file body for this non-HTML file.
+// The content type is based off of the extension in the filename,
+// in this case: about.json.
+export async function GET() {
+ return {
+ body: JSON.stringify({
+ name: 'Astro',
+ url: 'https://astro.build/',
+ }),
+ };
+}
diff --git a/packages/astro/test/fixtures/non-html-pages/src/pages/about.json.ts b/packages/astro/test/fixtures/non-html-pages/src/pages/about.json.ts
index af61847f3..817446fb5 100644
--- a/packages/astro/test/fixtures/non-html-pages/src/pages/about.json.ts
+++ b/packages/astro/test/fixtures/non-html-pages/src/pages/about.json.ts
@@ -1,11 +1,12 @@
// Returns the file body for this non-HTML file.
-// The content type is based off of the extension in the filename,
-// in this case: about.json.
-export async function get() {
- return {
- body: JSON.stringify({
- name: 'Astro',
- url: 'https://astro.build/',
- }),
- };
+export async function GET() {
+ const data = JSON.stringify({
+ name: 'Astro',
+ url: 'https://astro.build/',
+ })
+ return new Response(data, {
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ })
}
diff --git a/packages/astro/test/fixtures/non-html-pages/src/pages/placeholder-object.png.ts b/packages/astro/test/fixtures/non-html-pages/src/pages/placeholder-object.png.ts
new file mode 100644
index 000000000..114159004
--- /dev/null
+++ b/packages/astro/test/fixtures/non-html-pages/src/pages/placeholder-object.png.ts
@@ -0,0 +1,18 @@
+import { promises as fs } from 'node:fs';
+
+import type { APIRoute } from 'astro';
+
+// NOTE: test deprecated object form
+export const GET: APIRoute = async function get() {
+ try {
+ // Image is in the public domain. Sourced from
+ // https://en.wikipedia.org/wiki/File:Portrait_placeholder.png
+ const buffer = await fs.readFile('./test/fixtures/non-html-pages/src/images/placeholder.png');
+ return {
+ body: buffer.toString('binary'),
+ encoding: 'binary',
+ } as const;
+ } catch (error: unknown) {
+ throw new Error(`Something went wrong in placeholder.png route!: ${error as string}`);
+ }
+};
diff --git a/packages/astro/test/fixtures/non-html-pages/src/pages/placeholder.png.ts b/packages/astro/test/fixtures/non-html-pages/src/pages/placeholder.png.ts
index 0c2d3806b..50c6b877b 100644
--- a/packages/astro/test/fixtures/non-html-pages/src/pages/placeholder.png.ts
+++ b/packages/astro/test/fixtures/non-html-pages/src/pages/placeholder.png.ts
@@ -2,15 +2,12 @@ import { promises as fs } from 'node:fs';
import type { APIRoute } from 'astro';
-export const get: APIRoute = async function get() {
+export const GET: APIRoute = async function get({ ResponseWithEncoding }) {
try {
// Image is in the public domain. Sourced from
// https://en.wikipedia.org/wiki/File:Portrait_placeholder.png
const buffer = await fs.readFile('./test/fixtures/non-html-pages/src/images/placeholder.png');
- return {
- body: buffer.toString('binary'),
- encoding: 'binary',
- } as const;
+ return new ResponseWithEncoding(buffer.toString('binary'), undefined, 'binary')
} catch (error: unknown) {
throw new Error(`Something went wrong in placeholder.png route!: ${error as string}`);
}
diff --git a/packages/astro/test/fixtures/preact-compat-component/packages/react-lib/index.js b/packages/astro/test/fixtures/preact-compat-component/packages/react-lib/index.js
index 22fb55f10..4f15bdac1 100644
--- a/packages/astro/test/fixtures/preact-compat-component/packages/react-lib/index.js
+++ b/packages/astro/test/fixtures/preact-compat-component/packages/react-lib/index.js
@@ -2,4 +2,4 @@ import { useState } from "react";
export function useSpecialState(initialState) {
return useState(initialState);
-} \ No newline at end of file
+}
diff --git a/packages/astro/test/fixtures/preact-compat-component/src/components/Counter.jsx b/packages/astro/test/fixtures/preact-compat-component/src/components/Counter.jsx
index bfdf5bed1..7ee876f2c 100644
--- a/packages/astro/test/fixtures/preact-compat-component/src/components/Counter.jsx
+++ b/packages/astro/test/fixtures/preact-compat-component/src/components/Counter.jsx
@@ -1,4 +1,3 @@
-/** @jsxImportSource preact */
import { useSpecialState } from '@test/react-lib'
export default function Counter({ children }) {
diff --git a/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[...slug].json.ts b/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[...slug].json.ts
index 142b11711..bc7e1b774 100644
--- a/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[...slug].json.ts
+++ b/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[...slug].json.ts
@@ -1,6 +1,6 @@
import type { APIRoute } from 'astro';
-export const get: APIRoute = async ({ params }) => {
+export const GET: APIRoute = async ({ params }) => {
return {
body: JSON.stringify({
path: params.slug,
diff --git a/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[foo]-[bar].json.ts b/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[foo]-[bar].json.ts
index 2e66a22ae..b06efff6f 100644
--- a/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[foo]-[bar].json.ts
+++ b/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[foo]-[bar].json.ts
@@ -1,6 +1,6 @@
import type { APIRoute } from 'astro';
-export const get: APIRoute = async ({ params }) => {
+export const GET: APIRoute = async ({ params }) => {
return {
body: JSON.stringify({
foo: params.foo,
diff --git a/packages/astro/test/fixtures/ssr-api-route-custom-404/src/pages/api/route.js b/packages/astro/test/fixtures/ssr-api-route-custom-404/src/pages/api/route.js
index c44461be9..5a1cacc11 100644
--- a/packages/astro/test/fixtures/ssr-api-route-custom-404/src/pages/api/route.js
+++ b/packages/astro/test/fixtures/ssr-api-route-custom-404/src/pages/api/route.js
@@ -1,5 +1,5 @@
-export function post() {
+export function POST() {
return {
body: JSON.stringify({ ok: true })
};
diff --git a/packages/astro/test/fixtures/ssr-api-route/src/pages/binary.js b/packages/astro/test/fixtures/ssr-api-route/src/pages/binary.js
index 3e1c70c81..796bf4616 100644
--- a/packages/astro/test/fixtures/ssr-api-route/src/pages/binary.js
+++ b/packages/astro/test/fixtures/ssr-api-route/src/pages/binary.js
@@ -1,9 +1,7 @@
import fs from 'node:fs';
-export function get() {
- return {
- body: 'ok'
- };
+export function GET() {
+ return new Response('ok')
}
export async function post({ request }) {
diff --git a/packages/astro/test/fixtures/ssr-api-route/src/pages/context/[param].js b/packages/astro/test/fixtures/ssr-api-route/src/pages/context/[param].js
index 0ff1f625a..ba110ee13 100644
--- a/packages/astro/test/fixtures/ssr-api-route/src/pages/context/[param].js
+++ b/packages/astro/test/fixtures/ssr-api-route/src/pages/context/[param].js
@@ -1,7 +1,7 @@
/**
* @param {import('astro').APIContext} api
*/
-export function get(ctx) {
+export function GET(ctx) {
return {
body: JSON.stringify({
cookiesExist: !!ctx.cookies,
diff --git a/packages/astro/test/fixtures/ssr-api-route/src/pages/food-object.json.js b/packages/astro/test/fixtures/ssr-api-route/src/pages/food-object.json.js
new file mode 100644
index 000000000..7992e697a
--- /dev/null
+++ b/packages/astro/test/fixtures/ssr-api-route/src/pages/food-object.json.js
@@ -0,0 +1,10 @@
+// NOTE: test deprecated object form
+export function GET() {
+ return {
+ body: JSON.stringify([
+ { name: 'lettuce' },
+ { name: 'broccoli' },
+ { name: 'pizza' }
+ ])
+ };
+}
diff --git a/packages/astro/test/fixtures/ssr-api-route/src/pages/food.json.js b/packages/astro/test/fixtures/ssr-api-route/src/pages/food.json.js
index 1f9ea5f29..2d6fb6d1b 100644
--- a/packages/astro/test/fixtures/ssr-api-route/src/pages/food.json.js
+++ b/packages/astro/test/fixtures/ssr-api-route/src/pages/food.json.js
@@ -1,15 +1,15 @@
-export function get() {
- return {
- body: JSON.stringify([
+export function GET() {
+ return new Response(
+ JSON.stringify([
{ name: 'lettuce' },
{ name: 'broccoli' },
{ name: 'pizza' }
])
- };
+ )
}
-export async function post({ params, request }) {
+export async function POST({ params, request }) {
const body = await request.text();
return new Response(body === `some data` ? `ok` : `not ok`, {
status: 200,
diff --git a/packages/astro/test/fixtures/ssr-api-route/src/pages/login.js b/packages/astro/test/fixtures/ssr-api-route/src/pages/login.js
index dfce0b5d6..0e851df74 100644
--- a/packages/astro/test/fixtures/ssr-api-route/src/pages/login.js
+++ b/packages/astro/test/fixtures/ssr-api-route/src/pages/login.js
@@ -1,5 +1,5 @@
/** @type {import('astro').APIRoute} */
-export function post({ cookies }) {
+export function POST({ cookies }) {
cookies.set('foo', 'foo', {
httpOnly: true
});
diff --git a/packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js b/packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js
index 4d96b62a5..8c7c39302 100644
--- a/packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js
+++ b/packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js
@@ -1,5 +1,5 @@
-export function get({ params }) {
+export function GET({ params }) {
return {
body: JSON.stringify(params)
};
diff --git a/packages/astro/test/fixtures/ssr-locals/src/pages/api.js b/packages/astro/test/fixtures/ssr-locals/src/pages/api.js
index d4f7386fb..366f26aae 100644
--- a/packages/astro/test/fixtures/ssr-locals/src/pages/api.js
+++ b/packages/astro/test/fixtures/ssr-locals/src/pages/api.js
@@ -1,5 +1,5 @@
-export async function get({ locals }) {
+export async function GET({ locals }) {
let out = { ...locals };
return new Response(JSON.stringify(out), {
diff --git a/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/data/[slug].json.ts b/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/data/[slug].json.ts
index 16e2a90ca..d969873c5 100644
--- a/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/data/[slug].json.ts
+++ b/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/data/[slug].json.ts
@@ -7,7 +7,7 @@ export async function getStaticPaths() {
];
}
-export async function get() {
+export async function GET() {
return {
body: JSON.stringify({
title: '[slug]'
diff --git a/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/nested-arrays/[slug].astro b/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/nested-arrays/[slug].astro
deleted file mode 100644
index 25d1bfff4..000000000
--- a/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/nested-arrays/[slug].astro
+++ /dev/null
@@ -1,10 +0,0 @@
----
- export function getStaticPaths() {
- return [
- [ { params: {slug: "slug1"} } ],
- [ { params: {slug: "slug2"} } ],
- ]
- }
-
- export const prerender = true;
----
diff --git a/packages/astro/test/fixtures/ssr-preview/preview.mjs b/packages/astro/test/fixtures/ssr-preview/preview.mjs
index 745f22624..d8d57afa8 100644
--- a/packages/astro/test/fixtures/ssr-preview/preview.mjs
+++ b/packages/astro/test/fixtures/ssr-preview/preview.mjs
@@ -1,7 +1,7 @@
export default () => {
// noop
return {
- port: 3000,
+ port: 4321,
closed() {},
stop() {}
}
diff --git a/packages/astro/test/fixtures/ssr-redirect/src/middleware.ts b/packages/astro/test/fixtures/ssr-redirect/src/middleware.ts
index a8de8d130..57ce55678 100644
--- a/packages/astro/test/fixtures/ssr-redirect/src/middleware.ts
+++ b/packages/astro/test/fixtures/ssr-redirect/src/middleware.ts
@@ -1,4 +1,4 @@
-import { defineMiddleware } from 'astro/middleware';
+import { defineMiddleware } from 'astro:middleware';
export const onRequest = defineMiddleware(({ request }, next) => {
if(new URL(request.url).pathname === '/middleware-redirect/') {
diff --git a/packages/astro/test/fixtures/static-build-code-component/src/pages/index.astro b/packages/astro/test/fixtures/static-build-code-component/src/pages/index.astro
index e2f564a3b..c87ed5b08 100644
--- a/packages/astro/test/fixtures/static-build-code-component/src/pages/index.astro
+++ b/packages/astro/test/fixtures/static-build-code-component/src/pages/index.astro
@@ -1,5 +1,5 @@
---
-import {Code} from 'astro/components';
+import {Code} from 'astro:components';
---
<html>
<head><title>Testing</title></head>
diff --git a/packages/astro/test/fixtures/static-build-dir/src/pages/index.astro b/packages/astro/test/fixtures/static-build-dir/src/pages/index.astro
index e2f564a3b..c87ed5b08 100644
--- a/packages/astro/test/fixtures/static-build-dir/src/pages/index.astro
+++ b/packages/astro/test/fixtures/static-build-dir/src/pages/index.astro
@@ -1,5 +1,5 @@
---
-import {Code} from 'astro/components';
+import {Code} from 'astro:components';
---
<html>
<head><title>Testing</title></head>
diff --git a/packages/astro/test/fixtures/static-build/src/pages/company.json.ts b/packages/astro/test/fixtures/static-build/src/pages/company.json.ts
index ee3f2f1ad..08f45a7b8 100644
--- a/packages/astro/test/fixtures/static-build/src/pages/company.json.ts
+++ b/packages/astro/test/fixtures/static-build/src/pages/company.json.ts
@@ -1,8 +1,8 @@
-export async function get() {
+export async function GET() {
return {
body: JSON.stringify({
name: 'Astro Technology Company',
url: 'https://astro.build/'
})
}
-} \ No newline at end of file
+}
diff --git a/packages/astro/test/fixtures/static-build/src/pages/data/[slug].json.ts b/packages/astro/test/fixtures/static-build/src/pages/data/[slug].json.ts
index 2bcfe50a1..2fa13ac18 100644
--- a/packages/astro/test/fixtures/static-build/src/pages/data/[slug].json.ts
+++ b/packages/astro/test/fixtures/static-build/src/pages/data/[slug].json.ts
@@ -5,7 +5,7 @@ export async function getStaticPaths() {
]
}
-export async function get({ params }) {
+export async function GET({ params }) {
return {
body: JSON.stringify({
slug: params.slug,
@@ -13,4 +13,4 @@ export async function get({ params }) {
url: 'https://astro.build/'
})
}
-} \ No newline at end of file
+}
diff --git a/packages/astro/test/fixtures/static-build/src/pages/posts.json.js b/packages/astro/test/fixtures/static-build/src/pages/posts.json.js
index 6463fdbad..aefbbffff 100644
--- a/packages/astro/test/fixtures/static-build/src/pages/posts.json.js
+++ b/packages/astro/test/fixtures/static-build/src/pages/posts.json.js
@@ -13,7 +13,7 @@ async function fetchPosts() {
return posts.sort((a, b) => a.title.localeCompare(b.title));
}
-export async function get() {
+export async function GET() {
const posts = await fetchPosts();
return {
diff --git a/packages/astro/test/fixtures/tailwindcss/src/pages/markdown-page.md b/packages/astro/test/fixtures/tailwindcss/src/pages/markdown-page.md
deleted file mode 100644
index e4c6b6bc9..000000000
--- a/packages/astro/test/fixtures/tailwindcss/src/pages/markdown-page.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: "Markdown + Tailwind"
-setup: |
- import Button from '../components/Button.astro';
- import Complex from '../components/Complex.astro';
----
-
-<div class="grid place-items-center h-screen content-center">
- <Button>Tailwind Button in Markdown!</Button>
- <Complex />
-</div> \ No newline at end of file
diff --git a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/[slug].json.ts b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/[slug].json.ts
index 364c886e3..783031605 100644
--- a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/[slug].json.ts
+++ b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/[slug].json.ts
@@ -5,7 +5,7 @@ export async function getStaticPaths() {
];
}
-export async function get({ params }) {
+export async function GET({ params }) {
return {
body: JSON.stringify({
slug: params.slug,
diff --git a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/data/[slug].json.ts b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/data/[slug].json.ts
index 4392c7ee7..707c39fa9 100644
--- a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/data/[slug].json.ts
+++ b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/data/[slug].json.ts
@@ -5,7 +5,7 @@ export async function getStaticPaths() {
];
}
-export async function get({ params }) {
+export async function GET({ params }) {
return {
body: JSON.stringify({
slug: params.slug,
diff --git a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/home.json.ts b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/home.json.ts
index 8046af6df..5eaac42f1 100644
--- a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/home.json.ts
+++ b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/home.json.ts
@@ -1,4 +1,4 @@
-export async function get() {
+export async function GET() {
return {
body: JSON.stringify({
title: 'home'
diff --git a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/[image].svg.ts b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/[image].svg.ts
index e728394f4..c6fa88711 100644
--- a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/[image].svg.ts
+++ b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/[image].svg.ts
@@ -5,7 +5,7 @@ export async function getStaticPaths() {
];
}
-export async function get({ params }) {
+export async function GET({ params }) {
return {
body: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200">
<title>${params.image}</title>
diff --git a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/hex.ts b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/hex.ts
index 546796fed..84dd6dcaa 100644
--- a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/hex.ts
+++ b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/hex.ts
@@ -1,6 +1,6 @@
import { readFileSync } from "node:fs";
-export async function get({ params, request }) {
+export async function GET({ params, request }) {
const buffer = readFileSync(new URL('../../astro.png', import.meta.url));
return {
body: buffer.toString('hex'),
diff --git a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/static.svg.ts b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/static.svg.ts
index 727a0eae8..93fcf4022 100644
--- a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/static.svg.ts
+++ b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/static.svg.ts
@@ -1,4 +1,4 @@
-export async function get() {
+export async function GET() {
return {
body: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200">
<title>Static SVG</title>
diff --git a/packages/astro/test/glob-pages-css.test.js b/packages/astro/test/glob-pages-css.test.js
index 72558490f..3c2bb5454 100644
--- a/packages/astro/test/glob-pages-css.test.js
+++ b/packages/astro/test/glob-pages-css.test.js
@@ -8,6 +8,8 @@ describe('Astro.glob on pages/ directory', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/glob-pages-css/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/head-injection.test.js b/packages/astro/test/head-injection.test.js
index a2c0389df..139c97005 100644
--- a/packages/astro/test/head-injection.test.js
+++ b/packages/astro/test/head-injection.test.js
@@ -9,6 +9,8 @@ describe('Head injection', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/head-injection/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
});
diff --git a/packages/astro/test/jsx.test.js b/packages/astro/test/jsx.test.js
index 41671699c..b19074817 100644
--- a/packages/astro/test/jsx.test.js
+++ b/packages/astro/test/jsx.test.js
@@ -20,43 +20,51 @@ describe('jsx-runtime', () => {
expect($('#named').text()).to.equal('Named');
});
- it('Can load Preact component inside Astro JSX', async () => {
+ it('Can load Preact component inside Astro', async () => {
const html = await fixture.readFile('/frameworks/index.html');
const $ = cheerio.load(html);
- expect($('#has-preact #preact').length).to.equal(1);
+ expect($('#has-preact #preact').length).to.equal(0);
expect($('#preact').text()).to.include('Preact');
});
- it('Can load React component inside Astro JSX', async () => {
+ it('Can load React component inside Astro', async () => {
const html = await fixture.readFile('/frameworks/index.html');
const $ = cheerio.load(html);
- expect($('#has-react #react').length).to.equal(1);
+ expect($('#has-react #react').length).to.equal(0);
expect($('#react').text()).to.include('React');
});
- it('Can load Solid component inside Astro JSX', async () => {
+ it('Can load Solid component inside Astro', async () => {
const html = await fixture.readFile('/frameworks/index.html');
const $ = cheerio.load(html);
- expect($('#has-solid #solid').length).to.equal(1);
+ expect($('#has-solid #solid').length).to.equal(0);
expect($('#solid').text()).to.include('Solid');
});
- it('Can load Svelte component inside Astro JSX', async () => {
+ it('Can load Svelte component inside Astro', async () => {
const html = await fixture.readFile('/frameworks/index.html');
const $ = cheerio.load(html);
- expect($('#has-svelte #svelte').length).to.equal(1);
+ expect($('#has-svelte #svelte').length).to.equal(0);
expect($('#svelte').text()).to.include('Svelte');
});
- it('Can load Vue component inside Astro JSX', async () => {
+ it('Can load Vue component inside Astro', async () => {
const html = await fixture.readFile('/frameworks/index.html');
const $ = cheerio.load(html);
- expect($('#has-vue #vue').length).to.equal(1);
+ expect($('#has-vue #vue').length).to.equal(0);
expect($('#vue').text()).to.include('Vue');
});
+
+ it('Can load MDX component inside Astro', async () => {
+ const html = await fixture.readFile('/frameworks/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#mdx-wrapper #hello-world')).to.have.a.lengthOf(1, 'md content rendered');
+ expect($('#mdx-wrapper #react')).to.have.a.lengthOf(1, 'React component rendered');
+ });
});
diff --git a/packages/astro/test/lazy-layout.test.js b/packages/astro/test/lazy-layout.test.js
index 4ed22f5a0..8f50a91dc 100644
--- a/packages/astro/test/lazy-layout.test.js
+++ b/packages/astro/test/lazy-layout.test.js
@@ -7,7 +7,11 @@ describe('Lazily imported layouts', () => {
let fixture;
before(async () => {
- fixture = await loadFixture({ root: './fixtures/lazy-layout/' });
+ fixture = await loadFixture({
+ root: './fixtures/lazy-layout/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
+ });
await fixture.build();
});
diff --git a/packages/astro/test/minification-html.test.js b/packages/astro/test/minification-html.test.js
index bd86e233b..cd3e70a1d 100644
--- a/packages/astro/test/minification-html.test.js
+++ b/packages/astro/test/minification-html.test.js
@@ -47,7 +47,11 @@ describe('HTML minification', () => {
describe('Build SSG', () => {
let fixture;
before(async () => {
- fixture = await loadFixture({ root: './fixtures/minification-html/' });
+ fixture = await loadFixture({
+ root: './fixtures/minification-html/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
+ });
await fixture.build();
});
@@ -64,6 +68,8 @@ describe('HTML minification', () => {
root: './fixtures/minification-html/',
output: 'server',
adapter: testAdapter(),
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/non-html-pages.test.js b/packages/astro/test/non-html-pages.test.js
index e1b89ee6a..3e873032e 100644
--- a/packages/astro/test/non-html-pages.test.js
+++ b/packages/astro/test/non-html-pages.test.js
@@ -15,6 +15,12 @@ describe('Non-HTML Pages', () => {
expect(json).to.have.property('name', 'Astro');
expect(json).to.have.property('url', 'https://astro.build/');
});
+
+ it('should match contents (deprecated object form)', async () => {
+ const json = JSON.parse(await fixture.readFile('/about-object.json'));
+ expect(json).to.have.property('name', 'Astro');
+ expect(json).to.have.property('url', 'https://astro.build/');
+ });
});
describe('png', () => {
@@ -34,5 +40,22 @@ describe('Non-HTML Pages', () => {
'iVBORw0KGgoAAAANSUhEUgAAAGQAAACWCAYAAAAouC1GAAAD10lEQVR4Xu3ZbW4iMRCE4c1RuP+ZEEfZFZHIAgHGH9Xtsv3m94yx6qHaM+HrfD7//cOfTQJfgNhYfG8EEC8PQMw8AAHELQGz/XCGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbWe6hpxOp6oIL5dL1fWjL54CpBbhXagz4FiDqCCegZxhLEGiIGaAsQPJwrjhuLXFBiQbwrUtFiCjMZzaMhzEBcMFZSiIG4YDyjAQV4zRKENA3DFGoqSDzIIxCgWQgn9eZb6rpILM1o57qyyUNJCZMTLHFyAFI2s5kBXakYWS0hBAymsYDrISRkZLACn/8j5cGfXUFQqyYjuiWwJIY0Out0W0JAxk5XZEtgQQGtKRgOGt6rEV0pAdxlXU2AKks3U0pDPAiNuVKDREIGQNstP5EXGOyBsCSF/lAOnL7/tuRpYgRPUSKhQaIpIBRBSkahlAVEmK1gFEFKRqGUuQHR951e8i0kMdkP6+SUGu29kVxXJkAUJD+hMQrUBDREGqlgFElaRgHRXGdSsc6oAIEjBbgoYAUpfAbu8i1g3Z7V1EiRFyqANSN02er5Y/Zd0+YJexNUVDdmmJGiNsZAHSPrbCRtYOKFM1ZHWQCIzQkbX64Q5I+1iW3xmFkdKQFUcXIPLvePuCkRhpDVmpJcuArIASjZHakNmfujIwAKk4SpYFmXF0ZWEMachsoysTYyjIDE3JxhgO4owyAsMCxBFlFIYNiBPKSAxAnh57R2PYgLj9/j4SJvQXw5L3LjeM+z2PgBkG4gzx/EXKhEkHmQliRFvSQGaFyEZJAVkB4wYTPb7CQVbCyEAJA1kRImN8hYCsjhHZFDnILhhRKICUvL0eXKM86KUgu7Uj4kyRgeyMoRxfEhAw/neld3x1g4Dx+4DpQQFEcKi/WqIVpQuEdrzXTAcB47haLSjNDQHkGOR6RS1KEwgYZRgtj8PVIGDUYdS2BJD6fJvuKB1dVSC0o8ni56YSFED6Mq66WwpCO6qyf3vxEUpxQwAxAgFDg1HyGFzUEECMQMDQYhy15LAhgBiBgBGD8ent/WNDAIkDeYcCSGzmH1d/9U7yFoR25Eg9owCSk3vxmzsgM4AwrnKV7sfWy4YAAkhuAmaf9rEhtCNfC5D8zA8/8Yby6wyhIYfZhVwASEis7Yu+BKEd7YH23glIb4IB919RHs4QGhKQcsWSgFSElXEpIBkpV3zGAwjjqiK5oEsBCQq2Z9l/4WuAC09sfQEAAAAASUVORK5CYII='
);
});
+
+ it('should not have had its encoding mangled (deprecated object form)', async () => {
+ const buffer = await fixture.readFile('/placeholder-object.png', 'base64');
+
+ // Sanity check the first byte
+ const hex = Buffer.from(buffer, 'base64').toString('hex');
+ const firstHexByte = hex.slice(0, 2);
+ // If we accidentally utf8 encode the png, the first byte (in hex) will be 'c2'
+ expect(firstHexByte).to.not.equal('c2');
+ // and if correctly encoded in binary, it should be '89'
+ expect(firstHexByte).to.equal('89');
+
+ // Make sure the whole buffer (in base64) matches this snapshot
+ expect(buffer).to.equal(
+ 'iVBORw0KGgoAAAANSUhEUgAAAGQAAACWCAYAAAAouC1GAAAD10lEQVR4Xu3ZbW4iMRCE4c1RuP+ZEEfZFZHIAgHGH9Xtsv3m94yx6qHaM+HrfD7//cOfTQJfgNhYfG8EEC8PQMw8AAHELQGz/XCGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbWe6hpxOp6oIL5dL1fWjL54CpBbhXagz4FiDqCCegZxhLEGiIGaAsQPJwrjhuLXFBiQbwrUtFiCjMZzaMhzEBcMFZSiIG4YDyjAQV4zRKENA3DFGoqSDzIIxCgWQgn9eZb6rpILM1o57qyyUNJCZMTLHFyAFI2s5kBXakYWS0hBAymsYDrISRkZLACn/8j5cGfXUFQqyYjuiWwJIY0Out0W0JAxk5XZEtgQQGtKRgOGt6rEV0pAdxlXU2AKks3U0pDPAiNuVKDREIGQNstP5EXGOyBsCSF/lAOnL7/tuRpYgRPUSKhQaIpIBRBSkahlAVEmK1gFEFKRqGUuQHR951e8i0kMdkP6+SUGu29kVxXJkAUJD+hMQrUBDREGqlgFElaRgHRXGdSsc6oAIEjBbgoYAUpfAbu8i1g3Z7V1EiRFyqANSN02er5Y/Zd0+YJexNUVDdmmJGiNsZAHSPrbCRtYOKFM1ZHWQCIzQkbX64Q5I+1iW3xmFkdKQFUcXIPLvePuCkRhpDVmpJcuArIASjZHakNmfujIwAKk4SpYFmXF0ZWEMachsoysTYyjIDE3JxhgO4owyAsMCxBFlFIYNiBPKSAxAnh57R2PYgLj9/j4SJvQXw5L3LjeM+z2PgBkG4gzx/EXKhEkHmQliRFvSQGaFyEZJAVkB4wYTPb7CQVbCyEAJA1kRImN8hYCsjhHZFDnILhhRKICUvL0eXKM86KUgu7Uj4kyRgeyMoRxfEhAw/neld3x1g4Dx+4DpQQFEcKi/WqIVpQuEdrzXTAcB47haLSjNDQHkGOR6RS1KEwgYZRgtj8PVIGDUYdS2BJD6fJvuKB1dVSC0o8ni56YSFED6Mq66WwpCO6qyf3vxEUpxQwAxAgFDg1HyGFzUEECMQMDQYhy15LAhgBiBgBGD8ent/WNDAIkDeYcCSGzmH1d/9U7yFoR25Eg9owCSk3vxmzsgM4AwrnKV7sfWy4YAAkhuAmaf9rEhtCNfC5D8zA8/8Yby6wyhIYfZhVwASEis7Yu+BKEd7YH23glIb4IB919RHs4QGhKQcsWSgFSElXEpIBkpV3zGAwjjqiK5oEsBCQq2Z9l/4WuAC09sfQEAAAAASUVORK5CYII='
+ );
+ });
});
});
diff --git a/packages/astro/test/page-level-styles.test.js b/packages/astro/test/page-level-styles.test.js
index 3fdce62b9..bed538a54 100644
--- a/packages/astro/test/page-level-styles.test.js
+++ b/packages/astro/test/page-level-styles.test.js
@@ -9,6 +9,8 @@ describe('Page-level styles', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/page-level-styles/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/postcss.test.js b/packages/astro/test/postcss.test.js
index 126fca95c..0fcc60409 100644
--- a/packages/astro/test/postcss.test.js
+++ b/packages/astro/test/postcss.test.js
@@ -10,6 +10,8 @@ describe('PostCSS', function () {
this.timeout(45000); // test needs a little more time in CI
fixture = await loadFixture({
root: './fixtures/postcss',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
diff --git a/packages/astro/test/preact-compat-component.test.js b/packages/astro/test/preact-compat-component.test.js
index f0cd9e45f..0c1991f12 100644
--- a/packages/astro/test/preact-compat-component.test.js
+++ b/packages/astro/test/preact-compat-component.test.js
@@ -3,18 +3,30 @@ import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Preact compat component', () => {
+ /** @type {import('./test-utils.js').Fixture} */
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/preact-compat-component/',
+ });
+ });
+
describe('Development', () => {
- let fixture;
+ /** @type {import('./test-utils.js').DevServer} */
+ let devServer;
before(async () => {
- fixture = await loadFixture({
- root: './fixtures/preact-compat-component/',
- });
- await fixture.startDevServer();
+ devServer = await fixture.startDevServer();
+ });
+
+ after(async () => {
+ await devServer.stop();
});
it('Can load Counter', async () => {
- const html = await fixture.fetch('/').then((res) => res.text());
+ const res = await fixture.fetch('/');
+ const html = await res.text();
const $ = cheerio.load(html);
expect($('#counter-text').text()).to.be.eq('0');
@@ -22,12 +34,7 @@ describe('Preact compat component', () => {
});
describe('Build', () => {
- let fixture;
-
before(async () => {
- fixture = await loadFixture({
- root: './fixtures/preact-compat-component/',
- });
await fixture.build();
});
diff --git a/packages/astro/test/preview-routing.test.js b/packages/astro/test/preview-routing.test.js
index 5c365b07c..4c99881b6 100644
--- a/packages/astro/test/preview-routing.test.js
+++ b/packages/astro/test/preview-routing.test.js
@@ -157,9 +157,9 @@ describe('Preview Routing', () => {
expect(response.status).to.equal(200);
});
- it('404 when loading subpath root without trailing slash', async () => {
+ it('200 when loading subpath root without trailing slash', async () => {
const response = await fixture.fetch('/blog');
- expect(response.status).to.equal(404);
+ expect(response.status).to.equal(200);
});
it('200 when loading another page with subpath used', async () => {
@@ -345,9 +345,9 @@ describe('Preview Routing', () => {
expect(response.status).to.equal(200);
});
- it('404 when loading subpath root without trailing slash', async () => {
+ it('200 when loading subpath root without trailing slash', async () => {
const response = await fixture.fetch('/blog');
- expect(response.status).to.equal(404);
+ expect(response.status).to.equal(200);
});
it('200 when loading another page with subpath used', async () => {
diff --git a/packages/astro/test/public-base-404.test.js b/packages/astro/test/public-base-404.test.js
index c8d58471d..969e2a952 100644
--- a/packages/astro/test/public-base-404.test.js
+++ b/packages/astro/test/public-base-404.test.js
@@ -44,7 +44,7 @@ describe('Public dev with base', () => {
expect(response.status).to.equal(404);
const html = await response.text();
$ = cheerio.load(html);
- expect($('a').first().text()).to.equal('/blog/');
+ expect($('a').first().text()).to.equal('/blog');
});
it('default 404 page when loading /none/', async () => {
diff --git a/packages/astro/test/remote-css.test.js b/packages/astro/test/remote-css.test.js
index 4ca6eb422..4f9a25e35 100644
--- a/packages/astro/test/remote-css.test.js
+++ b/packages/astro/test/remote-css.test.js
@@ -8,6 +8,8 @@ describe('Remote CSS', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/remote-css/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/root-srcdir-css.test.js b/packages/astro/test/root-srcdir-css.test.js
index 3902b4ab2..e033ff35b 100644
--- a/packages/astro/test/root-srcdir-css.test.js
+++ b/packages/astro/test/root-srcdir-css.test.js
@@ -8,6 +8,8 @@ describe('srcDir', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/root-srcdir-css/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/scoped-style-strategy.test.js b/packages/astro/test/scoped-style-strategy.test.js
index 022ef3d6f..7781badf7 100644
--- a/packages/astro/test/scoped-style-strategy.test.js
+++ b/packages/astro/test/scoped-style-strategy.test.js
@@ -3,7 +3,7 @@ import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('scopedStyleStrategy', () => {
- describe('default', () => {
+ describe('scopedStyleStrategy: "where"', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
let stylesheet;
@@ -11,6 +11,9 @@ describe('scopedStyleStrategy', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/scoped-style-strategy/',
+ scopedStyleStrategy: 'where',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
@@ -39,6 +42,8 @@ describe('scopedStyleStrategy', () => {
fixture = await loadFixture({
root: './fixtures/scoped-style-strategy/',
scopedStyleStrategy: 'class',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
@@ -57,4 +62,37 @@ describe('scopedStyleStrategy', () => {
expect(stylesheet).to.match(/h1\.astro/);
});
});
+
+ describe('default', () => {
+ /** @type {import('./test-utils').Fixture} */
+ let fixture;
+ let stylesheet;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/scoped-style-strategy/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
+ });
+ await fixture.build();
+
+ const html = await fixture.readFile('/index.html');
+ const $ = cheerio.load(html);
+ const $link = $('link[rel=stylesheet]');
+ const href = $link.attr('href');
+ stylesheet = await fixture.readFile(href);
+ });
+
+ it('does not include :where pseudo-selector', () => {
+ expect(stylesheet).to.not.match(/:where/);
+ });
+
+ it('does not include the class name directly in the selector', () => {
+ expect(stylesheet).to.not.match(/h1\.astro/);
+ });
+
+ it('includes the data attribute hash', () => {
+ expect(stylesheet).to.include('h1[data-astro-cid-');
+ });
+ });
});
diff --git a/packages/astro/test/ssr-404-500-pages.test.js b/packages/astro/test/ssr-404-500-pages.test.js
index ac747a16c..253f9bc1c 100644
--- a/packages/astro/test/ssr-404-500-pages.test.js
+++ b/packages/astro/test/ssr-404-500-pages.test.js
@@ -12,6 +12,8 @@ describe('404 and 500 pages', () => {
root: './fixtures/ssr-api-route-custom-404/',
output: 'server',
adapter: testAdapter(),
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
});
diff --git a/packages/astro/test/ssr-api-route.test.js b/packages/astro/test/ssr-api-route.test.js
index 3e2d5c327..fbaafc822 100644
--- a/packages/astro/test/ssr-api-route.test.js
+++ b/packages/astro/test/ssr-api-route.test.js
@@ -1,6 +1,5 @@
import { expect } from 'chai';
import net from 'node:net';
-import { File, FormData } from 'undici';
import testAdapter from './test-adapter.js';
import { loadFixture } from './test-utils.js';
@@ -30,6 +29,15 @@ describe('API routes in SSR', () => {
const request = new Request('http://example.com/food.json');
const response = await app.render(request);
expect(response.status).to.equal(200);
+ const body = await response.json();
+ expect(body.length).to.equal(3);
+ });
+
+ it('Can load the API route too (deprecated object form)', async () => {
+ const app = await fixture.loadTestAdapterApp();
+ const request = new Request('http://example.com/food-object.json');
+ const response = await app.render(request);
+ expect(response.status).to.equal(200);
expect(response.headers.get('Content-Type')).to.equal('application/json;charset=utf-8');
expect(response.headers.get('Content-Length')).to.not.be.empty;
const body = await response.json();
@@ -88,8 +96,8 @@ describe('API routes in SSR', () => {
expect(res.status).to.equal(200);
});
- it('Infer content type with charset for { body } shorthand', async () => {
- const response = await fixture.fetch('/food.json', {
+ it('Infer content type with charset for { body } shorthand (deprecated object form)', async () => {
+ const response = await fixture.fetch('/food-object.json', {
method: 'GET',
});
expect(response.headers.get('Content-Type')).to.equal('application/json;charset=utf-8');
diff --git a/packages/astro/test/ssr-assets.test.js b/packages/astro/test/ssr-assets.test.js
index e02045bc0..01015f087 100644
--- a/packages/astro/test/ssr-assets.test.js
+++ b/packages/astro/test/ssr-assets.test.js
@@ -11,6 +11,8 @@ describe('SSR Assets', () => {
root: './fixtures/ssr-assets/',
output: 'server',
adapter: testAdapter(),
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/ssr-dynamic.test.js b/packages/astro/test/ssr-dynamic.test.js
index e4bb61b4a..62e67e410 100644
--- a/packages/astro/test/ssr-dynamic.test.js
+++ b/packages/astro/test/ssr-dynamic.test.js
@@ -25,6 +25,8 @@ describe('Dynamic pages in SSR', () => {
},
],
adapter: testAdapter(),
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/ssr-hoisted-script.test.js b/packages/astro/test/ssr-hoisted-script.test.js
index 49e1e7b2f..e9549151e 100644
--- a/packages/astro/test/ssr-hoisted-script.test.js
+++ b/packages/astro/test/ssr-hoisted-script.test.js
@@ -3,50 +3,54 @@ import { load as cheerioLoad } from 'cheerio';
import { loadFixture } from './test-utils.js';
import testAdapter from './test-adapter.js';
+async function fetchHTML(fixture, path) {
+ const app = await fixture.loadTestAdapterApp();
+ const request = new Request('http://example.com' + path);
+ const response = await app.render(request);
+ const html = await response.text();
+ return html;
+}
+
describe('Hoisted scripts in SSR', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
- before(async () => {
- fixture = await loadFixture({
- root: './fixtures/ssr-hoisted-script/',
- output: 'server',
- adapter: testAdapter(),
- });
- await fixture.build();
- });
-
- async function fetchHTML(path) {
- const app = await fixture.loadTestAdapterApp();
- const request = new Request('http://example.com' + path);
- const response = await app.render(request);
- const html = await response.text();
- return html;
- }
-
- it('Inlined scripts get included', async () => {
- const html = await fetchHTML('/');
- const $ = cheerioLoad(html);
- expect($('script').length).to.equal(1);
- });
-
- describe('base path', () => {
- const base = '/hello';
-
+ describe('without base path', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/ssr-hoisted-script/',
output: 'server',
adapter: testAdapter(),
- base,
});
await fixture.build();
});
- it('Inlined scripts get included without base path in the script', async () => {
- const html = await fetchHTML('/hello/');
+ it('Inlined scripts get included', async () => {
+ const html = await fetchHTML(fixture, '/');
const $ = cheerioLoad(html);
- expect($('script').html()).to.equal('console.log("hello world");\n');
+ expect($('script').length).to.equal(1);
});
});
});
+
+describe('Hoisted scripts in SSR with base path', () => {
+ /** @type {import('./test-utils').Fixture} */
+ let fixture;
+ const base = '/hello';
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/ssr-hoisted-script/',
+ output: 'server',
+ adapter: testAdapter(),
+ base,
+ });
+ await fixture.build();
+ });
+
+ it('Inlined scripts get included without base path in the script', async () => {
+ const html = await fetchHTML(fixture, '/hello/');
+ const $ = cheerioLoad(html);
+ expect($('script').html()).to.equal('console.log("hello world");\n');
+ });
+});
diff --git a/packages/astro/test/ssr-manifest.test.js b/packages/astro/test/ssr-manifest.test.js
index 4e5521220..b45d6b581 100644
--- a/packages/astro/test/ssr-manifest.test.js
+++ b/packages/astro/test/ssr-manifest.test.js
@@ -11,8 +11,9 @@ describe('astro:ssr-manifest', () => {
fixture = await loadFixture({
root: './fixtures/ssr-manifest/',
output: 'server',
- compressHTML: true,
adapter: testAdapter(),
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
diff --git a/packages/astro/test/ssr-prerender-get-static-paths.test.js b/packages/astro/test/ssr-prerender-get-static-paths.test.js
index 391e7485d..3fe2950cb 100644
--- a/packages/astro/test/ssr-prerender-get-static-paths.test.js
+++ b/packages/astro/test/ssr-prerender-get-static-paths.test.js
@@ -1,7 +1,7 @@
import { expect } from 'chai';
-import { loadFixture } from './test-utils.js';
import * as cheerio from 'cheerio';
import testAdapter from './test-adapter.js';
+import { loadFixture } from './test-utils.js';
describe('Prerender', () => {
/** @type {import('./test-utils').Fixture} */
@@ -102,11 +102,6 @@ describe('Prerender', () => {
});
describe('route params type validation', () => {
- it('resolves 200 on nested array parameters', async () => {
- const res = await fixture.fetch('/blog/nested-arrays/slug1');
- expect(res.status).to.equal(200);
- });
-
it('resolves 200 on matching static path - string params', async () => {
// route provided with { params: { year: "2022", slug: "post-2" }}
const res = await fixture.fetch('/blog/blog/2022/post-1');
@@ -234,11 +229,6 @@ describe('Prerender', () => {
});
describe('route params type validation', () => {
- it('resolves 200 on nested array parameters', async () => {
- const res = await fixture.fetch('/blog/nested-arrays/slug1');
- expect(res.status).to.equal(200);
- });
-
it('resolves 200 on matching static path - string params', async () => {
// route provided with { params: { year: "2022", slug: "post-2" }}
const res = await fixture.fetch('/blog/blog/2022/post-1');
diff --git a/packages/astro/test/ssr-split-manifest.test.js b/packages/astro/test/ssr-split-manifest.test.js
index 394740395..38a3233ee 100644
--- a/packages/astro/test/ssr-split-manifest.test.js
+++ b/packages/astro/test/ssr-split-manifest.test.js
@@ -26,6 +26,8 @@ describe('astro:ssr-manifest, split', () => {
currentRoutes = routes;
},
}),
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build();
});
@@ -43,7 +45,7 @@ describe('astro:ssr-manifest, split', () => {
it('should give access to entry points that exists on file system', async () => {
// number of the pages inside src/
- expect(entryPoints.size).to.equal(5);
+ expect(entryPoints.size).to.equal(6);
for (const fileUrl of entryPoints.values()) {
let filePath = fileURLToPath(fileUrl);
expect(existsSync(filePath)).to.be.true;
diff --git a/packages/astro/test/static-build.test.js b/packages/astro/test/static-build.test.js
index d4a687a5d..77c24f398 100644
--- a/packages/astro/test/static-build.test.js
+++ b/packages/astro/test/static-build.test.js
@@ -34,6 +34,8 @@ describe('Static build', () => {
fixture = await loadFixture({
root: './fixtures/static-build/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
});
await fixture.build({ logging });
});
@@ -175,7 +177,7 @@ describe('Static build', () => {
let found = false;
for (const log of logs) {
if (
- log.type === 'ssg' &&
+ log.label === 'ssg' &&
/[hH]eaders are not exposed in static \(SSG\) output mode/.test(log.message)
) {
found = true;
diff --git a/packages/astro/test/tailwindcss.test.js b/packages/astro/test/tailwindcss.test.js
index 2a37c6572..3c249cc2c 100644
--- a/packages/astro/test/tailwindcss.test.js
+++ b/packages/astro/test/tailwindcss.test.js
@@ -3,6 +3,7 @@ import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Tailwind', () => {
+ /** @type {import('./test-utils.js').Fixture} */
let fixture;
before(async () => {
@@ -58,14 +59,6 @@ describe('Tailwind', () => {
expect(button.hasClass('2xl:w-[80%]'), 'complex class').to.be.true;
});
- it('handles Markdown pages', async () => {
- const html = await fixture.readFile('/markdown-page/index.html');
- const $md = cheerio.load(html);
- const bundledCSSHREF = $md('link[rel=stylesheet][href^=/_astro/]').attr('href');
- const mdBundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/'));
- expect(mdBundledCSS, 'includes used component classes').to.match(/\.bg-purple-600{/);
- });
-
it('handles MDX pages (with integration)', async () => {
const html = await fixture.readFile('/mdx-page/index.html');
const $md = cheerio.load(html);
diff --git a/packages/astro/test/test-adapter.js b/packages/astro/test/test-adapter.js
index 85b4d69c0..67058023d 100644
--- a/packages/astro/test/test-adapter.js
+++ b/packages/astro/test/test-adapter.js
@@ -71,6 +71,15 @@ export default function (
name: 'my-ssr-adapter',
serverEntrypoint: '@my-ssr',
exports: ['manifest', 'createApp'],
+ supportedFeatures: {
+ assets: {
+ supportKind: 'Stable',
+ isNodeCompatible: true,
+ },
+ serverOutput: 'Stable',
+ staticOutput: 'Stable',
+ hybridOutput: 'Stable',
+ },
...extendAdapter,
});
},
diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js
index 27f5d83f7..fafd3046c 100644
--- a/packages/astro/test/test-utils.js
+++ b/packages/astro/test/test-utils.js
@@ -1,4 +1,3 @@
-import { polyfill } from '@astrojs/webapi';
import { execa } from 'execa';
import fastGlob from 'fast-glob';
import fs from 'node:fs';
@@ -7,27 +6,20 @@ import path from 'node:path';
import { fileURLToPath } from 'node:url';
import stripAnsi from 'strip-ansi';
import { check } from '../dist/cli/check/index.js';
+import { dev, preview } from '../dist/core/index.js';
import build from '../dist/core/build/index.js';
+import sync from '../dist/core/sync/index.js';
import { RESOLVED_SPLIT_MODULE_ID } from '../dist/core/build/plugins/plugin-ssr.js';
import { getVirtualModulePageNameFromPath } from '../dist/core/build/plugins/util.js';
import { makeSplitEntryPointFileName } from '../dist/core/build/static-build.js';
import { mergeConfig, resolveConfig } from '../dist/core/config/index.js';
-import dev from '../dist/core/dev/index.js';
import { nodeLogDestination } from '../dist/core/logger/node.js';
-import preview from '../dist/core/preview/index.js';
-import { sync } from '../dist/core/sync/index.js';
-
-// polyfill WebAPIs to globalThis for Node v12, Node v14, and Node v16
-polyfill(globalThis, {
- exclude: 'window document',
-});
// Disable telemetry when running tests
process.env.ASTRO_TELEMETRY_DISABLED = true;
/**
- * @typedef {import('undici').Response} Response
- * @typedef {import('../src/core/dev/dev').DedvServer} DevServer
+ * @typedef {import('../src/core/dev/dev').DevServer} DevServer
* @typedef {import('../src/@types/astro').AstroInlineConfig & { root?: string | URL }} AstroInlineConfig
* @typedef {import('../src/core/preview/index').PreviewServer} PreviewServer
* @typedef {import('../src/core/app/index').App} App
@@ -155,7 +147,7 @@ export async function loadFixture(inlineConfig) {
return {
build: async (extraInlineConfig = {}) => {
process.env.NODE_ENV = 'production';
- return build(mergeConfig(inlineConfig, extraInlineConfig));
+ return build(mergeConfig(inlineConfig, extraInlineConfig), { teardownCompiler: false });
},
sync: async (extraInlineConfig = {}, opts) => {
return sync(mergeConfig(inlineConfig, extraInlineConfig), opts);
diff --git a/packages/astro/test/units/content-collections/get-entry-type.test.js b/packages/astro/test/units/content-collections/get-entry-type.test.js
index e7effa52f..a953d0c8f 100644
--- a/packages/astro/test/units/content-collections/get-entry-type.test.js
+++ b/packages/astro/test/units/content-collections/get-entry-type.test.js
@@ -28,14 +28,13 @@ const fixtures = [
const contentFileExts = ['.md', '.mdx'];
const dataFileExts = ['.yaml', '.yml', '.json'];
-// TODO: Remove `getEntryType` last parameter once `experimental.assets` is no longer experimental
describe('Content Collections - getEntryType', () => {
fixtures.forEach(({ title, contentPaths }) => {
describe(title, () => {
it('Returns "content" for Markdown files', () => {
for (const entryPath of ['blog/first-post.md', 'blog/first-post.mdx']) {
const entry = fileURLToPath(new URL(entryPath, contentPaths.contentDir));
- const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts, false);
+ const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts);
expect(type).to.equal('content');
}
});
@@ -47,7 +46,7 @@ describe('Content Collections - getEntryType', () => {
'banners/welcome.yml',
]) {
const entry = fileURLToPath(new URL(entryPath, contentPaths.contentDir));
- const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts, false);
+ const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts);
expect(type).to.equal('data');
}
});
@@ -55,50 +54,50 @@ describe('Content Collections - getEntryType', () => {
it('Returns "content" for Markdown files in nested directories', () => {
for (const entryPath of ['blog/2021/01/01/index.md', 'blog/2021/01/01/index.mdx']) {
const entry = fileURLToPath(new URL(entryPath, contentPaths.contentDir));
- const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts, false);
+ const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts);
expect(type).to.equal('content');
}
});
it('Returns "config" for config files', () => {
const entry = fileURLToPath(contentPaths.config.url);
- const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts, false);
+ const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts);
expect(type).to.equal('config');
});
it('Returns "unsupported" for non-Markdown files', () => {
const entry = fileURLToPath(new URL('blog/robots.txt', contentPaths.contentDir));
- const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts, false);
+ const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts);
expect(type).to.equal('unsupported');
});
it('Returns "ignored" for .DS_Store', () => {
const entry = fileURLToPath(new URL('blog/.DS_Store', contentPaths.contentDir));
- const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts, false);
+ const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts);
expect(type).to.equal('ignored');
});
it('Returns "ignored" for unsupported files using an underscore', () => {
const entry = fileURLToPath(new URL('blog/_draft-robots.txt', contentPaths.contentDir));
- const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts, false);
+ const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts);
expect(type).to.equal('ignored');
});
it('Returns "ignored" when using underscore on file name', () => {
const entry = fileURLToPath(new URL('blog/_first-post.md', contentPaths.contentDir));
- const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts, false);
+ const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts);
expect(type).to.equal('ignored');
});
it('Returns "ignored" when using underscore on directory name', () => {
const entry = fileURLToPath(new URL('blog/_draft/first-post.md', contentPaths.contentDir));
- const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts, false);
+ const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts);
expect(type).to.equal('ignored');
});
it('Returns "ignored" for images', () => {
const entry = fileURLToPath(new URL('blog/first-post.png', contentPaths.contentDir));
- const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts, true);
+ const type = getEntryType(entry, contentPaths, contentFileExts, dataFileExts);
expect(type).to.equal('ignored');
});
});
diff --git a/packages/astro/test/units/cookies/delete.test.js b/packages/astro/test/units/cookies/delete.test.js
index 67fa1306b..f4c9fab53 100644
--- a/packages/astro/test/units/cookies/delete.test.js
+++ b/packages/astro/test/units/cookies/delete.test.js
@@ -30,7 +30,7 @@ describe('astro/src/core/cookies', () => {
expect(cookies.get('foo').value).to.equal('bar');
cookies.delete('foo');
- expect(cookies.get('foo').value).to.equal(undefined);
+ expect(cookies.get('foo')).to.equal(undefined);
});
it('calling cookies.has() after returns false', () => {
diff --git a/packages/astro/test/units/cookies/get.test.js b/packages/astro/test/units/cookies/get.test.js
index f044d715a..216128907 100644
--- a/packages/astro/test/units/cookies/get.test.js
+++ b/packages/astro/test/units/cookies/get.test.js
@@ -16,6 +16,13 @@ describe('astro/src/core/cookies', () => {
expect(cookies.get('foo').value).to.equal('bar');
});
+ it("Returns undefined is the value doesn't exist", () => {
+ const req = new Request('http://example.com/');
+ let cookies = new AstroCookies(req);
+ let cookie = cookies.get('foo');
+ expect(cookie).to.equal(undefined);
+ });
+
describe('.json()', () => {
it('returns a JavaScript object', () => {
const req = new Request('http://example.com/', {
@@ -29,13 +36,6 @@ describe('astro/src/core/cookies', () => {
expect(json).to.be.an('object');
expect(json.key).to.equal('value');
});
-
- it('throws if the value is undefined', () => {
- const req = new Request('http://example.com/');
- let cookies = new AstroCookies(req);
- let cookie = cookies.get('foo');
- expect(() => cookie.json()).to.throw('Cannot convert undefined to an object.');
- });
});
describe('.number()', () => {
diff --git a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js
index 344615346..a8e282a88 100644
--- a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js
+++ b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js
@@ -1,6 +1,6 @@
import { expect } from 'chai';
import { fileURLToPath } from 'node:url';
-import { sync as _sync } from '../../../dist/core/sync/index.js';
+import _sync from '../../../dist/core/sync/index.js';
import { createFsWithFallback } from '../test-utils.js';
const root = new URL('../../fixtures/content-mixed-errors/', import.meta.url);
diff --git a/packages/astro/test/units/integrations/api.test.js b/packages/astro/test/units/integrations/api.test.js
index 919628da2..a420dd6c9 100644
--- a/packages/astro/test/units/integrations/api.test.js
+++ b/packages/astro/test/units/integrations/api.test.js
@@ -1,5 +1,7 @@
import { expect } from 'chai';
import { runHookBuildSetup } from '../../../dist/integrations/index.js';
+import { validateSupportedFeatures } from '../../../dist/integrations/astroFeaturesValidation.js';
+import { defaultLogging } from '../test-utils.js';
describe('Integration API', () => {
it('runHookBuildSetup should work', async () => {
@@ -28,3 +30,187 @@ describe('Integration API', () => {
expect(updatedViteConfig).to.haveOwnProperty('define');
});
});
+
+describe('Astro feature map', function () {
+ it('should support the feature when stable', () => {
+ let result = validateSupportedFeatures(
+ 'test',
+ {
+ hybridOutput: 'stable',
+ },
+ {
+ output: 'hybrid',
+ },
+ defaultLogging
+ );
+ expect(result['hybridOutput']).to.be.true;
+ });
+
+ it('should not support the feature when not provided', () => {
+ let result = validateSupportedFeatures(
+ 'test',
+ undefined,
+ {
+ output: 'hybrid',
+ },
+ defaultLogging
+ );
+ expect(result['hybridOutput']).to.be.false;
+ });
+
+ it('should not support the feature when an empty object is provided', () => {
+ let result = validateSupportedFeatures(
+ 'test',
+ {},
+ {
+ output: 'hybrid',
+ },
+ defaultLogging
+ );
+ expect(result['hybridOutput']).to.be.false;
+ });
+
+ describe('static output', function () {
+ it('should be supported with the correct config', () => {
+ let result = validateSupportedFeatures(
+ 'test',
+ { staticOutput: 'stable' },
+ {
+ output: 'static',
+ },
+ defaultLogging
+ );
+ expect(result['staticOutput']).to.be.true;
+ });
+
+ it("should not be valid if the config is correct, but the it's unsupported", () => {
+ let result = validateSupportedFeatures(
+ 'test',
+ { staticOutput: 'unsupported' },
+ {
+ output: 'static',
+ },
+ defaultLogging
+ );
+ expect(result['staticOutput']).to.be.false;
+ });
+ });
+ describe('hybrid output', function () {
+ it('should be supported with the correct config', () => {
+ let result = validateSupportedFeatures(
+ 'test',
+ { hybridOutput: 'stable' },
+ {
+ output: 'hybrid',
+ },
+ defaultLogging
+ );
+ expect(result['hybridOutput']).to.be.true;
+ });
+
+ it("should not be valid if the config is correct, but the it's unsupported", () => {
+ let result = validateSupportedFeatures(
+ 'test',
+ {
+ hybridOutput: 'unsupported',
+ },
+ {
+ output: 'hybrid',
+ },
+ defaultLogging
+ );
+ expect(result['hybridOutput']).to.be.false;
+ });
+ });
+ describe('server output', function () {
+ it('should be supported with the correct config', () => {
+ let result = validateSupportedFeatures(
+ 'test',
+ { serverOutput: 'stable' },
+ {
+ output: 'server',
+ },
+ defaultLogging
+ );
+ expect(result['serverOutput']).to.be.true;
+ });
+
+ it("should not be valid if the config is correct, but the it's unsupported", () => {
+ let result = validateSupportedFeatures(
+ 'test',
+ {
+ serverOutput: 'unsupported',
+ },
+ {
+ output: 'server',
+ },
+ defaultLogging
+ );
+ expect(result['serverOutput']).to.be.false;
+ });
+ });
+
+ describe('assets', function () {
+ it('should be supported when it is sharp compatible', () => {
+ let result = validateSupportedFeatures(
+ 'test',
+ {
+ assets: {
+ supportKind: 'stable',
+ isSharpCompatible: true,
+ },
+ },
+ {
+ image: {
+ service: {
+ entrypoint: 'astro/assets/services/sharp',
+ },
+ },
+ },
+ defaultLogging
+ );
+ expect(result['assets']).to.be.true;
+ });
+ it('should be supported when it is squoosh compatible', () => {
+ let result = validateSupportedFeatures(
+ 'test',
+ {
+ assets: {
+ supportKind: 'stable',
+ isSquooshCompatible: true,
+ },
+ },
+ {
+ image: {
+ service: {
+ entrypoint: 'astro/assets/services/squoosh',
+ },
+ },
+ },
+ defaultLogging
+ );
+ expect(result['assets']).to.be.true;
+ });
+
+ it("should not be valid if the config is correct, but the it's unsupported", () => {
+ let result = validateSupportedFeatures(
+ 'test',
+ {
+ assets: {
+ supportKind: 'unsupported',
+ isNodeCompatible: false,
+ },
+ },
+ {
+ image: {
+ service: {
+ entrypoint: 'astro/assets/services/sharp',
+ },
+ },
+ },
+ defaultLogging
+ );
+ expect(result['assets']).to.be.false;
+ });
+ });
+});
diff --git a/packages/astro/test/units/render/components.test.js b/packages/astro/test/units/render/components.test.js
index 0b7352453..f9dd71621 100644
--- a/packages/astro/test/units/render/components.test.js
+++ b/packages/astro/test/units/render/components.test.js
@@ -1,7 +1,6 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { fileURLToPath } from 'node:url';
-import svelte from '../../../../integrations/svelte/dist/index.js';
import { createFs, createRequestAndResponse, runInContainer } from '../test-utils.js';
const root = new URL('../../fixtures/alias/', import.meta.url);
@@ -33,7 +32,7 @@ describe('core/render components', () => {
inlineConfig: {
root: fileURLToPath(root),
logLevel: 'silent',
- integrations: [svelte()],
+ integrations: [],
},
},
async (container) => {
@@ -56,4 +55,67 @@ describe('core/render components', () => {
}
);
});
+
+ it('should merge `class` and `class:list`', async () => {
+ const fs = createFs(
+ {
+ '/src/pages/index.astro': `
+ ---
+ import Class from '../components/Class.astro';
+ import ClassList from '../components/ClassList.astro';
+ import BothLiteral from '../components/BothLiteral.astro';
+ import BothFlipped from '../components/BothFlipped.astro';
+ import BothSpread from '../components/BothSpread.astro';
+ ---
+ <Class class="red blue" />
+ <ClassList class:list={{ red: true, blue: true }} />
+ <BothLiteral class="red" class:list={{ blue: true }} />
+ <BothFlipped class:list={{ blue: true }} class="red" />
+ <BothSpread class:list={{ blue: true }} { ...{ class: "red" }} />
+ `,
+ '/src/components/Class.astro': `<pre id="class" set:html={JSON.stringify(Astro.props)} />`,
+ '/src/components/ClassList.astro': `<pre id="class-list" set:html={JSON.stringify(Astro.props)} />`,
+ '/src/components/BothLiteral.astro': `<pre id="both-literal" set:html={JSON.stringify(Astro.props)} />`,
+ '/src/components/BothFlipped.astro': `<pre id="both-flipped" set:html={JSON.stringify(Astro.props)} />`,
+ '/src/components/BothSpread.astro': `<pre id="both-spread" set:html={JSON.stringify(Astro.props)} />`,
+ },
+ root
+ );
+
+ await runInContainer(
+ {
+ fs,
+ inlineConfig: {
+ root: fileURLToPath(root),
+ logLevel: 'silent',
+ integrations: [],
+ },
+ },
+ async (container) => {
+ const { req, res, done, text } = createRequestAndResponse({
+ method: 'GET',
+ url: '/',
+ });
+ container.handle(req, res);
+
+ await done;
+ const html = await text();
+ const $ = cheerio.load(html);
+
+ const check = (name) => JSON.parse($(name).text() || '{}')
+
+ const Class = check('#class');
+ const ClassList = check('#class-list');
+ const BothLiteral = check('#both-literal');
+ const BothFlipped = check('#both-flipped');
+ const BothSpread = check('#both-spread');
+
+ expect(Class).to.deep.equal({ class: 'red blue' }, '#class');
+ expect(ClassList).to.deep.equal({ class: 'red blue' }, '#class-list');
+ expect(BothLiteral).to.deep.equal({ class: 'red blue' }, '#both-literal');
+ expect(BothFlipped).to.deep.equal({ class: 'red blue' }, '#both-flipped');
+ expect(BothSpread).to.deep.equal({ class: 'red blue' }, '#both-spread');
+ }
+ );
+ });
});
diff --git a/packages/astro/test/units/render/head.test.js b/packages/astro/test/units/render/head.test.js
index fbd16be31..d2580e30d 100644
--- a/packages/astro/test/units/render/head.test.js
+++ b/packages/astro/test/units/render/head.test.js
@@ -90,13 +90,14 @@ describe('core/render', () => {
const PageModule = createAstroModule(Page);
const ctx = await createRenderContext({
+ route: { type: 'page', pathname: '/index' },
request: new Request('http://example.com/'),
links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }],
mod: PageModule,
env,
});
- const response = await tryRenderRoute('page', ctx, env, PageModule);
+ const response = await tryRenderRoute(ctx, env, PageModule);
const html = await response.text();
const $ = cheerio.load(html);
@@ -170,13 +171,14 @@ describe('core/render', () => {
const PageModule = createAstroModule(Page);
const ctx = await createRenderContext({
+ route: { type: 'page', pathname: '/index' },
request: new Request('http://example.com/'),
links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }],
env,
mod: PageModule,
});
- const response = await tryRenderRoute('page', ctx, env, PageModule);
+ const response = await tryRenderRoute(ctx, env, PageModule);
const html = await response.text();
const $ = cheerio.load(html);
@@ -216,13 +218,14 @@ describe('core/render', () => {
const PageModule = createAstroModule(Page);
const ctx = await createRenderContext({
+ route: { type: 'page', pathname: '/index' },
request: new Request('http://example.com/'),
links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }],
env,
mod: PageModule,
});
- const response = await tryRenderRoute('page', ctx, env, PageModule);
+ const response = await tryRenderRoute(ctx, env, PageModule);
const html = await response.text();
const $ = cheerio.load(html);
diff --git a/packages/astro/test/units/render/jsx.test.js b/packages/astro/test/units/render/jsx.test.js
index 9be135fc0..1464b5b0c 100644
--- a/packages/astro/test/units/render/jsx.test.js
+++ b/packages/astro/test/units/render/jsx.test.js
@@ -45,12 +45,13 @@ describe('core/render', () => {
const mod = createAstroModule(Page);
const ctx = await createRenderContext({
+ route: { type: 'page', pathname: '/index' },
request: new Request('http://example.com/'),
env,
mod,
});
- const response = await tryRenderRoute('page', ctx, env, mod);
+ const response = await tryRenderRoute(ctx, env, mod);
expect(response.status).to.equal(200);
@@ -90,11 +91,12 @@ describe('core/render', () => {
const mod = createAstroModule(Page);
const ctx = await createRenderContext({
+ route: { type: 'page', pathname: '/index' },
request: new Request('http://example.com/'),
env,
mod,
});
- const response = await tryRenderRoute('page', ctx, env, mod);
+ const response = await tryRenderRoute(ctx, env, mod);
expect(response.status).to.equal(200);
@@ -115,12 +117,13 @@ describe('core/render', () => {
const mod = createAstroModule(Page);
const ctx = await createRenderContext({
+ route: { type: 'page', pathname: '/index' },
request: new Request('http://example.com/'),
env,
mod,
});
- const response = await tryRenderRoute('page', ctx, env, mod);
+ const response = await tryRenderRoute(ctx, env, mod);
try {
await response.text();
diff --git a/packages/astro/test/units/routing/route-matching.test.js b/packages/astro/test/units/routing/route-matching.test.js
index e1c4df5c5..c0bf7d6e6 100644
--- a/packages/astro/test/units/routing/route-matching.test.js
+++ b/packages/astro/test/units/routing/route-matching.test.js
@@ -12,8 +12,8 @@ import { createContainer } from '../../../dist/core/dev/container.js';
import * as cheerio from 'cheerio';
import testAdapter from '../../test-adapter.js';
import { getSortedPreloadedMatches } from '../../../dist/prerender/routing.js';
-import { createDevelopmentEnvironment } from '../../../dist/vite-plugin-astro-server/environment.js';
import { createDevelopmentManifest } from '../../../dist/vite-plugin-astro-server/plugin.js';
+import DevPipeline from '../../../dist/vite-plugin-astro-server/devPipeline.js';
const root = new URL('../../fixtures/alias/', import.meta.url);
const fileSystem = {
@@ -124,7 +124,7 @@ const fileSystem = {
};
describe('Route matching', () => {
- let env;
+ let pipeline;
let manifestData;
let container;
let settings;
@@ -145,7 +145,7 @@ describe('Route matching', () => {
const loader = createViteLoader(container.viteServer);
const manifest = createDevelopmentManifest(container.settings);
- env = createDevelopmentEnvironment(manifest, container.settings, defaultLogging, loader);
+ pipeline = new DevPipeline({ manifest, logging: defaultLogging, settings, loader });
manifestData = createRouteManifest(
{
cwd: fileURLToPath(root),
@@ -163,7 +163,7 @@ describe('Route matching', () => {
describe('Matched routes', () => {
it('should be sorted correctly', async () => {
const matches = matchAllRoutes('/try-matching-a-route', manifestData);
- const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings });
+ const preloadedMatches = await getSortedPreloadedMatches({ pipeline, matches, settings });
const sortedRouteNames = preloadedMatches.map((match) => match.route.route);
expect(sortedRouteNames).to.deep.equal([
@@ -177,7 +177,7 @@ describe('Route matching', () => {
});
it('nested should be sorted correctly', async () => {
const matches = matchAllRoutes('/nested/try-matching-a-route', manifestData);
- const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings });
+ const preloadedMatches = await getSortedPreloadedMatches({ pipeline, matches, settings });
const sortedRouteNames = preloadedMatches.map((match) => match.route.route);
expect(sortedRouteNames).to.deep.equal([
diff --git a/packages/astro/test/units/vite-plugin-astro-server/request.test.js b/packages/astro/test/units/vite-plugin-astro-server/request.test.js
index 48d449ccd..58ad404fd 100644
--- a/packages/astro/test/units/vite-plugin-astro-server/request.test.js
+++ b/packages/astro/test/units/vite-plugin-astro-server/request.test.js
@@ -1,31 +1,35 @@
import { expect } from 'chai';
-
import { createLoader } from '../../../dist/core/module-loader/index.js';
import { createRouteManifest } from '../../../dist/core/routing/index.js';
import { createComponent, render } from '../../../dist/runtime/server/index.js';
import { createController, handleRequest } from '../../../dist/vite-plugin-astro-server/index.js';
import {
createAstroModule,
- createBasicEnvironment,
createBasicSettings,
createFs,
createRequestAndResponse,
defaultLogging,
} from '../test-utils.js';
+import { createDevelopmentManifest } from '../../../dist/vite-plugin-astro-server/plugin.js';
+import DevPipeline from '../../../dist/vite-plugin-astro-server/devPipeline.js';
+
+async function createDevPipeline(overrides = {}) {
+ const settings = overrides.settings ?? (await createBasicSettings({ root: '/' }));
+ const loader = overrides.loader ?? createLoader();
+ const manifest = createDevelopmentManifest(settings);
-async function createDevEnvironment(overrides = {}) {
- const env = createBasicEnvironment();
- env.settings = await createBasicSettings({ root: '/' });
- env.settings.renderers = [];
- env.loader = createLoader();
- Object.assign(env, overrides);
- return env;
+ return new DevPipeline({
+ manifest,
+ settings,
+ logging: defaultLogging,
+ loader,
+ });
}
describe('vite-plugin-astro-server', () => {
describe('request', () => {
it('renders a request', async () => {
- const env = await createDevEnvironment({
+ const pipeline = await createDevPipeline({
loader: createLoader({
import() {
const Page = createComponent(() => {
@@ -35,7 +39,7 @@ describe('vite-plugin-astro-server', () => {
},
}),
});
- const controller = createController({ loader: env.loader });
+ const controller = createController({ loader: pipeline.getModuleLoader() });
const { req, res, text } = createRequestAndResponse();
const fs = createFs(
{
@@ -47,14 +51,14 @@ describe('vite-plugin-astro-server', () => {
const manifestData = createRouteManifest(
{
fsMod: fs,
- settings: env.settings,
+ settings: pipeline.getSettings(),
},
defaultLogging
);
try {
await handleRequest({
- env,
+ pipeline,
manifestData,
controller,
incomingRequest: req,
diff --git a/packages/astro/test/virtual-astro-file.test.js b/packages/astro/test/virtual-astro-file.test.js
index e5247a684..6da9fd1d0 100644
--- a/packages/astro/test/virtual-astro-file.test.js
+++ b/packages/astro/test/virtual-astro-file.test.js
@@ -6,7 +6,11 @@ describe('Loading virtual Astro files', () => {
let fixture;
before(async () => {
- fixture = await loadFixture({ root: './fixtures/virtual-astro-file/' });
+ fixture = await loadFixture({
+ root: './fixtures/virtual-astro-file/',
+ // test suite was authored when inlineStylesheets defaulted to never
+ build: { inlineStylesheets: 'never' },
+ });
await fixture.build();
});
diff --git a/packages/astro/tsconfig.json b/packages/astro/tsconfig.json
index 839239eaf..d8bd9b197 100644
--- a/packages/astro/tsconfig.json
+++ b/packages/astro/tsconfig.json
@@ -1,12 +1,12 @@
{
"extends": "../../tsconfig.base.json",
- "include": ["src", "index.d.ts"],
+ "include": ["src"],
"compilerOptions": {
"allowJs": true,
"declarationDir": "./dist",
"module": "ES2022",
"outDir": "./dist",
- "target": "ES2021",
+ "target": "ES2022",
"jsx": "preserve",
"types": ["@types/dom-view-transitions", "network-information-types"]
}
diff --git a/packages/astro/tsconfigs/base.json b/packages/astro/tsconfigs/base.json
index aa1f17543..578693195 100644
--- a/packages/astro/tsconfigs/base.json
+++ b/packages/astro/tsconfigs/base.json
@@ -5,27 +5,24 @@
"target": "ESNext",
"module": "ESNext",
// Enable node-style module resolution, for things like npm package imports.
- "moduleResolution": "node",
+ "moduleResolution": "Bundler",
+ // Allow importing TypeScript files using their native extension (.ts(x)).
+ "allowImportingTsExtensions": true,
// Enable JSON imports.
"resolveJsonModule": true,
- // Enable stricter transpilation for better output.
+ // Enforce the usage of type-only imports when needed, which helps avoiding bundling issues.
+ "verbatimModuleSyntax": true,
+ // Ensure that each file can be transpiled without relying on other imports.
+ // This is redundant with the previous option, however it ensures that it's on even if someone disable `verbatimModuleSyntax`
"isolatedModules": true,
// Astro directly run TypeScript code, no transpilation needed.
"noEmit": true,
- // Report an error when importing a file using a casing different from the casing on disk.
+ // Report an error when importing a file using a casing different from another import of the same file.
"forceConsistentCasingInFileNames": true,
// Properly support importing CJS modules in ESM
"esModuleInterop": true,
// Skip typechecking libraries and .d.ts files
"skipLibCheck": true,
- // Add alias for assets folder for easy reference to assets
- "baseUrl": ".",
- "paths": {
- "~/assets/*": ["src/assets/*"]
- },
- // TypeScript 5.0 changed how `isolatedModules` and `importsNotUsedAsValues` works, deprecating the later
- // Until the majority of users are on TypeScript 5.0, we'll have to supress those deprecation errors
- "ignoreDeprecations": "5.0",
// Allow JavaScript files to be imported
"allowJs": true
}
diff --git a/packages/astro/tsconfigs/strict.json b/packages/astro/tsconfigs/strict.json
index bc87a68e0..3064440a5 100644
--- a/packages/astro/tsconfigs/strict.json
+++ b/packages/astro/tsconfigs/strict.json
@@ -2,8 +2,7 @@
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
- "strict": true,
- // Error when a value import is only used as a type.
- "importsNotUsedAsValues": "error"
+ // Enable strict mode. This enables a few options at a time, see https://www.typescriptlang.org/tsconfig#strict for a list.
+ "strict": true
}
}