From 435b10549878281ad2bb60207cb86f312a4a809f Mon Sep 17 00:00:00 2001 From: Marc <8125196+rdwz@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:45:21 +0200 Subject: [PATCH 01/33] Fix: lang unspecified code blocks (#8461) --- .changeset/poor-pears-wash.md | 5 +++++ examples/README.md | 6 +++--- examples/basics/README.md | 4 ++-- examples/blog/README.md | 4 ++-- examples/component/README.md | 4 ++-- examples/deno/README.md | 4 ++-- examples/framework-alpine/README.md | 2 +- examples/framework-lit/README.md | 2 +- examples/framework-multiple/README.md | 2 +- examples/framework-preact/README.md | 2 +- examples/framework-react/README.md | 2 +- examples/framework-solid/README.md | 2 +- examples/framework-svelte/README.md | 2 +- examples/framework-vue/README.md | 2 +- examples/hackernews/README.md | 4 ++-- examples/integration/README.md | 4 ++-- examples/minimal/README.md | 4 ++-- examples/non-html-pages/README.md | 4 ++-- examples/portfolio/README.md | 2 +- examples/with-markdoc/README.md | 4 ++-- examples/with-markdown-plugins/README.md | 2 +- examples/with-markdown-shiki/README.md | 2 +- examples/with-mdx/README.md | 2 +- examples/with-nanostores/README.md | 2 +- examples/with-tailwindcss/README.md | 2 +- examples/with-vite-plugin-pwa/README.md | 4 ++-- examples/with-vitest/README.md | 2 +- 27 files changed, 43 insertions(+), 38 deletions(-) create mode 100644 .changeset/poor-pears-wash.md diff --git a/.changeset/poor-pears-wash.md b/.changeset/poor-pears-wash.md new file mode 100644 index 000000000000..5a7117e93fe3 --- /dev/null +++ b/.changeset/poor-pears-wash.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix lang unspecified code blocks (markdownlint MD040) diff --git a/examples/README.md b/examples/README.md index ab0e5a6c0791..1482084d42df 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,7 +2,7 @@ The easiest way to check out one of these examples on your machine is by running this command in an empty directory: -``` +```sh npm create astro@latest -- --template [EXAMPLE_NAME] ``` @@ -10,12 +10,12 @@ npm create astro@latest -- --template [EXAMPLE_NAME] Visit [awesome-astro](https://github.com/one-aalam/awesome-astro) for a full list of community examples. You can use `npm create astro@latest` to check out any community examples: -``` +```sh npm create astro@latest -- --template [GITHUB_USER]/[REPO_NAME] ``` Paths to examples nested inside of a repo are also supported: -``` +```sh npm create astro@latest -- --template [GITHUB_USER]/[REPO_NAME]/path/to/example ``` diff --git a/examples/basics/README.md b/examples/basics/README.md index 5369073b906d..1db3fb3991a8 100644 --- a/examples/basics/README.md +++ b/examples/basics/README.md @@ -1,6 +1,6 @@ # Astro Starter Kit: Basics -``` +```sh npm create astro@latest -- --template basics ``` @@ -16,7 +16,7 @@ npm create astro@latest -- --template basics Inside of your Astro project, you'll see the following folders and files: -``` +```text / ├── public/ │ └── favicon.svg diff --git a/examples/blog/README.md b/examples/blog/README.md index 67d0cfc2ee8b..758716e07c57 100644 --- a/examples/blog/README.md +++ b/examples/blog/README.md @@ -1,6 +1,6 @@ # Astro Starter Kit: Blog -``` +```sh npm create astro@latest -- --template blog ``` @@ -25,7 +25,7 @@ Features: Inside of your Astro project, you'll see the following folders and files: -``` +```text ├── public/ ├── src/ │   ├── components/ diff --git a/examples/component/README.md b/examples/component/README.md index c92a01a954f4..f51958529925 100644 --- a/examples/component/README.md +++ b/examples/component/README.md @@ -2,7 +2,7 @@ This is a template for an Astro component library. Use this template for writing components to use in multiple projects or publish to NPM. -``` +```sh npm create astro@latest -- --template component ``` @@ -14,7 +14,7 @@ npm create astro@latest -- --template component Inside of your Astro project, you'll see the following folders and files: -``` +```text / ├── index.ts ├── src diff --git a/examples/deno/README.md b/examples/deno/README.md index af49ebcb7e30..17deeb3b9d9e 100644 --- a/examples/deno/README.md +++ b/examples/deno/README.md @@ -4,7 +4,7 @@ [![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/deno) [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/deno/devcontainer.json) -``` +```sh npm create astro@latest -- --template deno ``` @@ -16,7 +16,7 @@ npm create astro@latest -- --template deno Inside of your Astro project, you'll see the following folders and files: -``` +```text / ├── public/ │ └── favicon.svg diff --git a/examples/framework-alpine/README.md b/examples/framework-alpine/README.md index 9c61ea1b0f81..22cc7537430b 100644 --- a/examples/framework-alpine/README.md +++ b/examples/framework-alpine/README.md @@ -1,6 +1,6 @@ # Astro + AlpineJS Example -``` +```sh npm create astro@latest -- --template framework-alpine ``` diff --git a/examples/framework-lit/README.md b/examples/framework-lit/README.md index 9afdb987cb03..714d179da2c3 100644 --- a/examples/framework-lit/README.md +++ b/examples/framework-lit/README.md @@ -1,6 +1,6 @@ # Astro + Lit Example -``` +```sh npm create astro@latest -- --template framework-lit ``` diff --git a/examples/framework-multiple/README.md b/examples/framework-multiple/README.md index 385b0ade7872..30b020effb0c 100644 --- a/examples/framework-multiple/README.md +++ b/examples/framework-multiple/README.md @@ -1,6 +1,6 @@ # Kitchen Sink: Microfrontends with Astro -``` +```sh npm create astro@latest -- --template framework-multiple ``` diff --git a/examples/framework-preact/README.md b/examples/framework-preact/README.md index 8bebec195aa3..04efb2d6ea98 100644 --- a/examples/framework-preact/README.md +++ b/examples/framework-preact/README.md @@ -1,6 +1,6 @@ # Astro + Preact Example -``` +```sh npm create astro@latest -- --template framework-preact ``` diff --git a/examples/framework-react/README.md b/examples/framework-react/README.md index 57afd632aaa1..f036afa8c6a8 100644 --- a/examples/framework-react/README.md +++ b/examples/framework-react/README.md @@ -1,6 +1,6 @@ # Astro + React Example -``` +```sh npm create astro@latest -- --template framework-react ``` diff --git a/examples/framework-solid/README.md b/examples/framework-solid/README.md index f95f8d8f5bc0..2943a7692b11 100644 --- a/examples/framework-solid/README.md +++ b/examples/framework-solid/README.md @@ -1,6 +1,6 @@ # Astro + Solid.js Example -``` +```sh npm create astro@latest -- --template framework-solid ``` diff --git a/examples/framework-svelte/README.md b/examples/framework-svelte/README.md index abf0679cc684..5c5a24cc05b6 100644 --- a/examples/framework-svelte/README.md +++ b/examples/framework-svelte/README.md @@ -1,6 +1,6 @@ # Astro + Svelte Example -``` +```sh npm create astro@latest -- --template framework-svelte ``` diff --git a/examples/framework-vue/README.md b/examples/framework-vue/README.md index 07f94e1c3f54..14e778765a5b 100644 --- a/examples/framework-vue/README.md +++ b/examples/framework-vue/README.md @@ -1,6 +1,6 @@ # Astro + Vue Example -``` +```sh npm create astro@latest -- --template framework-vue ``` diff --git a/examples/hackernews/README.md b/examples/hackernews/README.md index 042a7807b51f..55c657dc91ac 100644 --- a/examples/hackernews/README.md +++ b/examples/hackernews/README.md @@ -1,6 +1,6 @@ # Astro Starter Kit: Hackernews -``` +```sh npm create astro@latest -- --template hackernews ``` @@ -14,7 +14,7 @@ npm create astro@latest -- --template hackernews Inside of your Astro project, you'll see the following folders and files: -``` +```text / ├── public/ │ └── favicon.svg diff --git a/examples/integration/README.md b/examples/integration/README.md index a6709d4004de..4f0880031250 100644 --- a/examples/integration/README.md +++ b/examples/integration/README.md @@ -2,7 +2,7 @@ This is a template for an Astro integration. Use this template for writing integrations to use in multiple projects or publish to NPM. -``` +```sh npm create astro@latest -- --template integration ``` @@ -14,7 +14,7 @@ npm create astro@latest -- --template integration Inside of your Astro project, you'll see the following folders and files: -``` +```text / ├── index.ts ├── tsconfig.json diff --git a/examples/minimal/README.md b/examples/minimal/README.md index 20bad66154ec..e34a99b446b4 100644 --- a/examples/minimal/README.md +++ b/examples/minimal/README.md @@ -1,6 +1,6 @@ # Astro Starter Kit: Minimal -``` +```sh npm create astro@latest -- --template minimal ``` @@ -14,7 +14,7 @@ npm create astro@latest -- --template minimal Inside of your Astro project, you'll see the following folders and files: -``` +```text / ├── public/ ├── src/ diff --git a/examples/non-html-pages/README.md b/examples/non-html-pages/README.md index ecdee84b10dc..c26b016982dc 100644 --- a/examples/non-html-pages/README.md +++ b/examples/non-html-pages/README.md @@ -4,7 +4,7 @@ Documentation for "Non-HTML Pages": https://docs.astro.build/en/core-concepts/endpoints/#static-file-endpoints -``` +```sh npm create astro@latest -- --template non-html-pages ``` @@ -18,7 +18,7 @@ npm create astro@latest -- --template non-html-pages Inside of your Astro project, you'll see the following folders and files: -``` +```text / ├── public/ ├── src/ diff --git a/examples/portfolio/README.md b/examples/portfolio/README.md index 3c8ee3aa4725..312d18b1cf5d 100644 --- a/examples/portfolio/README.md +++ b/examples/portfolio/README.md @@ -1,6 +1,6 @@ # Astro Starter Kit: Portfolio -``` +```sh npm create astro@latest -- --template portfolio ``` diff --git a/examples/with-markdoc/README.md b/examples/with-markdoc/README.md index 1a1acc1a5f39..35afb0f3f196 100644 --- a/examples/with-markdoc/README.md +++ b/examples/with-markdoc/README.md @@ -2,7 +2,7 @@ This starter showcases the experimental Markdoc integration. -``` +```sh npm create astro@latest -- --template with-markdoc ``` @@ -15,7 +15,7 @@ npm create astro@latest -- --template with-markdoc Inside of your Astro project, you'll see the following folders and files: -``` +```text / ├── public/ ├── src/ diff --git a/examples/with-markdown-plugins/README.md b/examples/with-markdown-plugins/README.md index 7f955f6c4091..91811541a7e8 100644 --- a/examples/with-markdown-plugins/README.md +++ b/examples/with-markdown-plugins/README.md @@ -1,6 +1,6 @@ # Astro Example: Markdown with Plugins -``` +```sh npm create astro@latest -- --template with-markdown-plugins ``` diff --git a/examples/with-markdown-shiki/README.md b/examples/with-markdown-shiki/README.md index 7a26a9e9b5d7..72251f6ba4bc 100644 --- a/examples/with-markdown-shiki/README.md +++ b/examples/with-markdown-shiki/README.md @@ -1,6 +1,6 @@ # Astro Example: Markdown with Shiki -``` +```sh npm create astro@latest -- --template with-markdown-shiki ``` diff --git a/examples/with-mdx/README.md b/examples/with-mdx/README.md index 8f6780443db2..55fe589cf0b7 100644 --- a/examples/with-mdx/README.md +++ b/examples/with-mdx/README.md @@ -1,6 +1,6 @@ # Astro Example: MDX -``` +```sh npm create astro@latest -- --template with-mdx ``` diff --git a/examples/with-nanostores/README.md b/examples/with-nanostores/README.md index 7771a9a6aed7..163c9129a1c1 100644 --- a/examples/with-nanostores/README.md +++ b/examples/with-nanostores/README.md @@ -1,6 +1,6 @@ # Astro Example: Nanostores -``` +```sh npm create astro@latest -- --template with-nanostores ``` diff --git a/examples/with-tailwindcss/README.md b/examples/with-tailwindcss/README.md index 54dbc84be0a5..8b512bb7fdc6 100644 --- a/examples/with-tailwindcss/README.md +++ b/examples/with-tailwindcss/README.md @@ -1,6 +1,6 @@ # Astro with Tailwind -``` +```sh npm create astro@latest -- --template with-tailwindcss ``` diff --git a/examples/with-vite-plugin-pwa/README.md b/examples/with-vite-plugin-pwa/README.md index 555d3e7de755..3cc99dbc93f8 100644 --- a/examples/with-vite-plugin-pwa/README.md +++ b/examples/with-vite-plugin-pwa/README.md @@ -1,6 +1,6 @@ # Astro Starter Kit: Vite PWA -``` +```sh npm create astro@latest -- --template with-vite-plugin-pwa ``` @@ -14,7 +14,7 @@ npm create astro@latest -- --template with-vite-plugin-pwa Inside of your Astro project, you'll see the following folders and files: -``` +```text / ├── public/ ├── src/ diff --git a/examples/with-vitest/README.md b/examples/with-vitest/README.md index 5187424e00a9..8b831d88c011 100644 --- a/examples/with-vitest/README.md +++ b/examples/with-vitest/README.md @@ -1,6 +1,6 @@ # Astro + [Vitest](https://vitest.dev/) Example -``` +```sh npm create astro@latest -- --template with-vitest ``` From a6a516d9446a50cc32fbd7201b243c63b3a4db43 Mon Sep 17 00:00:00 2001 From: Oscar Lee-Vermeren Date: Mon, 11 Sep 2023 07:52:25 -0400 Subject: [PATCH 02/33] fix(types): make `image.service` optional (#8492) --- .changeset/spotty-tools-rhyme.md | 5 +++++ packages/astro/src/@types/astro.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/spotty-tools-rhyme.md diff --git a/.changeset/spotty-tools-rhyme.md b/.changeset/spotty-tools-rhyme.md new file mode 100644 index 000000000000..a54691a48db6 --- /dev/null +++ b/.changeset/spotty-tools-rhyme.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +fix(types): make `image.service` optional diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index e99077d350fe..9a0cfd7ad9b9 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -996,7 +996,7 @@ export interface AstroUserConfig { * } * ``` */ - service: ImageServiceConfig; + service?: ImageServiceConfig; /** * @docs From 78b82bb3929bee5d8d9bd32d65374956ddb05859 Mon Sep 17 00:00:00 2001 From: bb010g Date: Mon, 11 Sep 2023 04:54:56 -0700 Subject: [PATCH 03/33] fix(astro): support `content/config.mts` (#8484) Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> --- .changeset/calm-houses-carry.md | 5 +++++ packages/astro/src/content/utils.ts | 2 +- packages/astro/test/content-collections.test.js | 14 ++++++++++++++ .../package.json | 8 ++++++++ .../src/content/blog/introduction.md | 5 +++++ .../src/content/config.mts | 11 +++++++++++ .../src/pages/index.astro | 5 +++++ pnpm-lock.yaml | 6 ++++++ 8 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 .changeset/calm-houses-carry.md create mode 100644 packages/astro/test/fixtures/content-collections-with-config-mts/package.json create mode 100644 packages/astro/test/fixtures/content-collections-with-config-mts/src/content/blog/introduction.md create mode 100644 packages/astro/test/fixtures/content-collections-with-config-mts/src/content/config.mts create mode 100644 packages/astro/test/fixtures/content-collections-with-config-mts/src/pages/index.astro diff --git a/.changeset/calm-houses-carry.md b/.changeset/calm-houses-carry.md new file mode 100644 index 000000000000..4e614e623e83 --- /dev/null +++ b/.changeset/calm-houses-carry.md @@ -0,0 +1,5 @@ +--- +"astro": patch +--- + +fix(astro): add support for `src/content/config.mts` files diff --git a/packages/astro/src/content/utils.ts b/packages/astro/src/content/utils.ts index 202acd6b9602..5834c81d82e7 100644 --- a/packages/astro/src/content/utils.ts +++ b/packages/astro/src/content/utils.ts @@ -448,7 +448,7 @@ export function getContentPaths( }; } function search(fs: typeof fsMod, srcDir: URL) { - const paths = ['config.mjs', 'config.js', 'config.ts'].map( + const paths = ['config.mjs', 'config.js', 'config.mts', 'config.ts'].map( (p) => new URL(`./content/${p}`, srcDir) ); for (const file of paths) { diff --git a/packages/astro/test/content-collections.test.js b/packages/astro/test/content-collections.test.js index 10c65b0dd20f..e39d7da5e7fc 100644 --- a/packages/astro/test/content-collections.test.js +++ b/packages/astro/test/content-collections.test.js @@ -214,6 +214,20 @@ describe('Content Collections', () => { expect(error).to.include('**title**: Expected type `"string"`, received "number"'); }); }); + describe('With config.mts', () => { + it("Errors when frontmatter doesn't match schema", async () => { + const fixture = await loadFixture({ + root: './fixtures/content-collections-with-config-mts/', + }); + let error; + try { + await fixture.build(); + } catch (e) { + error = e.message; + } + expect(error).to.include('**title**: Expected type `"string"`, received "number"'); + }); + }); describe('With empty markdown file', () => { it('Throws the right error', async () => { diff --git a/packages/astro/test/fixtures/content-collections-with-config-mts/package.json b/packages/astro/test/fixtures/content-collections-with-config-mts/package.json new file mode 100644 index 000000000000..cba237161e1c --- /dev/null +++ b/packages/astro/test/fixtures/content-collections-with-config-mts/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/content-collections-with-config-mts", + "version": "0.0.0", + "private": true, + "dependencies": { + "astro": "workspace:*" + } +} diff --git a/packages/astro/test/fixtures/content-collections-with-config-mts/src/content/blog/introduction.md b/packages/astro/test/fixtures/content-collections-with-config-mts/src/content/blog/introduction.md new file mode 100644 index 000000000000..c85be69f6ce2 --- /dev/null +++ b/packages/astro/test/fixtures/content-collections-with-config-mts/src/content/blog/introduction.md @@ -0,0 +1,5 @@ +--- +title: 10000 +--- + +# Hi there! \ No newline at end of file diff --git a/packages/astro/test/fixtures/content-collections-with-config-mts/src/content/config.mts b/packages/astro/test/fixtures/content-collections-with-config-mts/src/content/config.mts new file mode 100644 index 000000000000..bb2c54aea440 --- /dev/null +++ b/packages/astro/test/fixtures/content-collections-with-config-mts/src/content/config.mts @@ -0,0 +1,11 @@ +import { z, defineCollection } from 'astro:content'; + +const blog = defineCollection({ + schema: z.object({ + title: z.string(), + }), +}); + +export const collections = { + blog +} diff --git a/packages/astro/test/fixtures/content-collections-with-config-mts/src/pages/index.astro b/packages/astro/test/fixtures/content-collections-with-config-mts/src/pages/index.astro new file mode 100644 index 000000000000..4152c0734663 --- /dev/null +++ b/packages/astro/test/fixtures/content-collections-with-config-mts/src/pages/index.astro @@ -0,0 +1,5 @@ +--- +import {getEntryBySlug} from "astro:content" +const blogEntry = await getEntryBySlug("blog", "introduction"); +--- +{blogEntry.data.title} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8ab3a4d0ae4f..fd844e217e63 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2373,6 +2373,12 @@ importers: specifier: workspace:* version: link:../../.. + packages/astro/test/fixtures/content-collections-with-config-mts: + dependencies: + astro: + specifier: workspace:* + version: link:../../.. + packages/astro/test/fixtures/content-mixed-errors: dependencies: astro: From 0ca332ba4ab82cc04872776398952867b0f43d33 Mon Sep 17 00:00:00 2001 From: Martin Trapp <94928215+martrapp@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:58:52 +0200 Subject: [PATCH 04/33] Bugfixes for back navigation in the view transition client-side router (#8491) * Bugfixes for back navigation in the view transition client-side router * re-introduced pushState on self links as required for update of browser's address bar * format --- .changeset/witty-readers-behave.md | 5 ++++ .../astro/components/ViewTransitions.astro | 23 ++++++++++++------- packages/astro/e2e/view-transitions.test.js | 22 ++++++++++++++++++ 3 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 .changeset/witty-readers-behave.md diff --git a/.changeset/witty-readers-behave.md b/.changeset/witty-readers-behave.md new file mode 100644 index 000000000000..ec999f763f98 --- /dev/null +++ b/.changeset/witty-readers-behave.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Bugfixes for back navigation in the view transition client-side router diff --git a/packages/astro/components/ViewTransitions.astro b/packages/astro/components/ViewTransitions.astro index 7b556c252770..8fb506037e6b 100644 --- a/packages/astro/components/ViewTransitions.astro +++ b/packages/astro/components/ViewTransitions.astro @@ -262,6 +262,9 @@ const { fallback = 'animate' } = Astro.props as Props; return; } + // Now we are sure that we will push state, and it is time to create a state if it is still missing. + !state && history.replaceState({ index: currentHistoryIndex, scrollY }, ''); + document.documentElement.dataset.astroTransition = dir; if (supportsViewTransitions) { finished = document.startViewTransition(() => updateDOM(doc, loc, state)).finished; @@ -335,28 +338,28 @@ const { fallback = 'animate' } = Astro.props as Props; // But we want to handle it like any other same page navigation // So we scroll to the top of the page but do not start page transitions ev.preventDefault(); - persistState({ ...history.state, scrollY }); - scrollTo({ left: 0, top: 0, behavior: 'instant' }); + // push state on the first navigation but not if we were here already if (location.hash) { - // last target was different + history.replaceState({ index: currentHistoryIndex, scrollY: -(scrollY + 1) }, ''); const newState: State = { index: ++currentHistoryIndex, scrollY: 0 }; history.pushState(newState, '', link.href); } + scrollTo({ left: 0, top: 0, behavior: 'instant' }); return; } } // these are the cases we will handle: same origin, different page ev.preventDefault(); - persistState({ index: currentHistoryIndex, scrollY }); navigate('forward', new URL(link.href)); }); addEventListener('popstate', (ev) => { if (!transitionEnabledOnThisPage() && ev.state) { - // The current page doesn't haven't View Transitions, - // respect that with a full page reload - // -- but only for transition managed by us (ev.state is set) + // The current page doesn't have View Transitions enabled + // but the page we navigate to does (because it set the state). + // Do a full page refresh to reload the client-side router from the new page. + // Scroll restauration will then happen during the reload when the router's code is re-executed history.scrollRestoration && (history.scrollRestoration = 'manual'); location.reload(); return; @@ -383,7 +386,11 @@ const { fallback = 'animate' } = Astro.props as Props; const nextIndex = state.index; const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back'; currentHistoryIndex = nextIndex; - navigate(direction, new URL(location.href), state); + if (state.scrollY < 0) { + scrollTo(0, -(state.scrollY + 1)); + } else { + navigate(direction, new URL(location.href), state); + } }); ['mouseenter', 'touchstart', 'focus'].forEach((evName) => { diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js index 34f1a4e027b5..80a180608152 100644 --- a/packages/astro/e2e/view-transitions.test.js +++ b/packages/astro/e2e/view-transitions.test.js @@ -282,6 +282,28 @@ test.describe('View Transitions', () => { await expect(locator).toBeInViewport(); }); + test('Scroll position restored when transitioning back to fragment', async ({ page, astro }) => { + // Go to the long page + await page.goto(astro.resolveUrl('/long-page')); + let locator = page.locator('#longpage'); + await expect(locator).toBeInViewport(); + + // Scroll down to middle fragment + await page.click('#click-scroll-down'); + locator = page.locator('#click-one-again'); + await expect(locator).toBeInViewport(); + + // Scroll up to top fragment + await page.click('#click-one-again'); + locator = page.locator('#one'); + await expect(locator).toHaveText('Page 1'); + + // Back to middle of the page + await page.goBack(); + locator = page.locator('#click-one-again'); + await expect(locator).toBeInViewport(); + }); + test('Scroll position restored on forward button', async ({ page, astro }) => { // Go to page 1 await page.goto(astro.resolveUrl('/one')); From 1ed21d10db3f57e19861a865722d039238e82c8b Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Mon, 11 Sep 2023 20:05:17 +0800 Subject: [PATCH 05/33] Clear caches older than 5 days (#8494) --- .github/workflows/cleanup-cache.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cleanup-cache.yml b/.github/workflows/cleanup-cache.yml index a3c4d25a489c..cf03bbfee584 100644 --- a/.github/workflows/cleanup-cache.yml +++ b/.github/workflows/cleanup-cache.yml @@ -1,19 +1,28 @@ name: Cleanup cache on: + schedule: + - cron: "0 11 * * *" pull_request: types: - closed -# Workflow copied from https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries jobs: cleanup: runs-on: ubuntu-latest steps: - name: Check out code uses: actions/checkout@v3 - - - name: Cleanup + + - name: Cleanup caches older than 5 days + if: github.event_name == 'schedule' + uses: MyAlbum/purge-cache@v2 + with: + max-age: 432000 + + # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries + - name: Cleanup on PR close + if: github.event_name == 'pull_request' run: | gh extension install actions/gh-actions-cache From fa77fa63d944f709a37f08be93f0d14fe1d91188 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Mon, 11 Sep 2023 20:39:22 +0800 Subject: [PATCH 06/33] Prevent style removal of client:only components (#8472) --- .changeset/grumpy-hotels-heal.md | 5 +++++ .../astro/components/ViewTransitions.astro | 15 ++++++++++++++ .../src/pages/client-only-one.astro | 10 ++++++++++ .../src/pages/client-only-two.astro | 10 ++++++++++ packages/astro/e2e/view-transitions.test.js | 20 +++++++++++++++++++ 5 files changed, 60 insertions(+) create mode 100644 .changeset/grumpy-hotels-heal.md create mode 100644 packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-one.astro create mode 100644 packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-two.astro diff --git a/.changeset/grumpy-hotels-heal.md b/.changeset/grumpy-hotels-heal.md new file mode 100644 index 000000000000..f171c91fb1d7 --- /dev/null +++ b/.changeset/grumpy-hotels-heal.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Prevent client:only styles from being removed in dev (View Transitions) diff --git a/packages/astro/components/ViewTransitions.astro b/packages/astro/components/ViewTransitions.astro index 8fb506037e6b..822be5ccbf23 100644 --- a/packages/astro/components/ViewTransitions.astro +++ b/packages/astro/components/ViewTransitions.astro @@ -113,6 +113,11 @@ const { fallback = 'animate' } = Astro.props as Props; const parser = new DOMParser(); + // A noop element used to prevent styles from being removed + if(import.meta.env.DEV) { + var noopEl: string | undefined = document.createElement('div'); + } + async function updateDOM(doc: Document, loc: URL, state?: State, fallback?: Fallback) { // Check for a head element that should persist, either because it has the data // attribute or is a link el. @@ -139,6 +144,16 @@ const { fallback = 'animate' } = Astro.props as Props; } } } + // Only run this in dev. This will get stripped from production builds and is not needed. + if(import.meta.env.DEV) { + if(el.tagName === 'STYLE' && el.dataset.viteDevId) { + const devId = el.dataset.viteDevId; + // If this same style tag exists, remove it from the new page + return doc.querySelector(`style[data-astro-dev-id="${devId}"]`) + // Otherwise, keep it anyways. This is client:only styles. + || noopEl; + } + } return null; }; diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-one.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-one.astro new file mode 100644 index 000000000000..a8d5e8995ae4 --- /dev/null +++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-one.astro @@ -0,0 +1,10 @@ +--- +import Layout from '../components/Layout.astro'; +import Island from '../components/Island'; +--- + + go to page 2 +
+ message here +
+
diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-two.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-two.astro new file mode 100644 index 000000000000..884ec46833d5 --- /dev/null +++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/client-only-two.astro @@ -0,0 +1,10 @@ +--- +import Layout from '../components/Layout.astro'; +import Island from '../components/Island'; +--- + +

Page 2

+
+ message here +
+
diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js index 80a180608152..bd8ad5c362bb 100644 --- a/packages/astro/e2e/view-transitions.test.js +++ b/packages/astro/e2e/view-transitions.test.js @@ -565,4 +565,24 @@ test.describe('View Transitions', () => { p = page.locator('#one'); await expect(p, 'should have content').toHaveText('Page 1'); }); + + test("client:only styles are retained on transition", async ({ page, astro }) => { + const totalExpectedStyles = 8; + + // Go to page 1 + await page.goto(astro.resolveUrl('/client-only-one')); + let msg = page.locator('.counter-message'); + await expect(msg).toHaveText('message here'); + + let styles = await page.locator('style').all(); + expect(styles.length).toEqual(totalExpectedStyles); + + await page.click('#click-two'); + + let pageTwo = page.locator('#page-two'); + await expect(pageTwo, 'should have content').toHaveText('Page 2'); + + styles = await page.locator('style').all(); + expect(styles.length).toEqual(totalExpectedStyles, 'style count has not changed'); + }); }); From c934b087d738401f2394c3738d156993e66b4b64 Mon Sep 17 00:00:00 2001 From: matthewp Date: Mon, 11 Sep 2023 12:41:49 +0000 Subject: [PATCH 07/33] [ci] format --- packages/astro/components/ViewTransitions.astro | 14 ++++++++------ packages/astro/e2e/view-transitions.test.js | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/astro/components/ViewTransitions.astro b/packages/astro/components/ViewTransitions.astro index 822be5ccbf23..4d615188dbaa 100644 --- a/packages/astro/components/ViewTransitions.astro +++ b/packages/astro/components/ViewTransitions.astro @@ -114,7 +114,7 @@ const { fallback = 'animate' } = Astro.props as Props; const parser = new DOMParser(); // A noop element used to prevent styles from being removed - if(import.meta.env.DEV) { + if (import.meta.env.DEV) { var noopEl: string | undefined = document.createElement('div'); } @@ -145,13 +145,15 @@ const { fallback = 'animate' } = Astro.props as Props; } } // Only run this in dev. This will get stripped from production builds and is not needed. - if(import.meta.env.DEV) { - if(el.tagName === 'STYLE' && el.dataset.viteDevId) { + if (import.meta.env.DEV) { + if (el.tagName === 'STYLE' && el.dataset.viteDevId) { const devId = el.dataset.viteDevId; // If this same style tag exists, remove it from the new page - return doc.querySelector(`style[data-astro-dev-id="${devId}"]`) - // Otherwise, keep it anyways. This is client:only styles. - || noopEl; + return ( + doc.querySelector(`style[data-astro-dev-id="${devId}"]`) || + // Otherwise, keep it anyways. This is client:only styles. + noopEl + ); } } return null; diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js index bd8ad5c362bb..64041ab05ecb 100644 --- a/packages/astro/e2e/view-transitions.test.js +++ b/packages/astro/e2e/view-transitions.test.js @@ -566,7 +566,7 @@ test.describe('View Transitions', () => { await expect(p, 'should have content').toHaveText('Page 1'); }); - test("client:only styles are retained on transition", async ({ page, astro }) => { + test('client:only styles are retained on transition', async ({ page, astro }) => { const totalExpectedStyles = 8; // Go to page 1 From 5e1099f686abcc7026bd4fa74727f3b311c6d6d6 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Mon, 11 Sep 2023 15:11:57 +0100 Subject: [PATCH 08/33] fix: minify HTML for redirects when emitted as HTML files (#8504) --- .changeset/gold-weeks-hammer.md | 5 +++++ packages/astro/src/core/build/generate.ts | 3 +++ packages/astro/test/redirects.test.js | 6 ++++++ 3 files changed, 14 insertions(+) create mode 100644 .changeset/gold-weeks-hammer.md diff --git a/.changeset/gold-weeks-hammer.md b/.changeset/gold-weeks-hammer.md new file mode 100644 index 000000000000..4f6946116649 --- /dev/null +++ b/.changeset/gold-weeks-hammer.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Minify the HTML of the redicts emitted during the build. diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 5fe670e99104..f1044434ba4a 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -584,6 +584,9 @@ async function generatePath(pathname: string, gopts: GeneratePathOptions, pipeli Redirecting from ${fromPath} to ${location} `; + if (pipeline.getConfig().compressHTML === true) { + body = body.replaceAll('\n', ''); + } // A dynamic redirect, set the location so that integrations know about it. if (pageData.route.type !== 'redirect') { pageData.route.redirect = location; diff --git a/packages/astro/test/redirects.test.js b/packages/astro/test/redirects.test.js index 17c1070dc2ad..999ab1f0f3ed 100644 --- a/packages/astro/test/redirects.test.js +++ b/packages/astro/test/redirects.test.js @@ -69,6 +69,7 @@ describe('Astro.redirect', () => { root: './fixtures/ssr-redirect/', output: 'static', redirects: { + '/old': '/test', '/': '/test', '/one': '/test', '/two': '/test', @@ -82,6 +83,11 @@ describe('Astro.redirect', () => { await fixture.build(); }); + it("Minifies the HTML emitted when a page that doesn't exist is emitted", async () => { + const html = await fixture.readFile('/old/index.html'); + expect(html).to.not.include('\n'); + }); + it('Includes the meta refresh tag in Astro.redirect pages', async () => { const html = await fixture.readFile('/secret/index.html'); expect(html).to.include('http-equiv="refresh'); From 23f9536de0456ed2ddc9a77f7aef773ab6a8e73c Mon Sep 17 00:00:00 2001 From: Masaki Koyanagi Date: Tue, 12 Sep 2023 01:26:45 +0900 Subject: [PATCH 09/33] chore: correct description of `attribute` option in `scopedStyleStrategy` (#8506) * chore: correct description of `attribute` option in `scopedStyleStrategy` * chore: fix typo (specifity -> specificity) --- .changeset/itchy-clouds-design.md | 5 +++++ packages/astro/src/@types/astro.ts | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 .changeset/itchy-clouds-design.md diff --git a/.changeset/itchy-clouds-design.md b/.changeset/itchy-clouds-design.md new file mode 100644 index 000000000000..b6d5046e3175 --- /dev/null +++ b/.changeset/itchy-clouds-design.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +chore: correct description of `attribute` option in `scopedStyleStrategy` diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 9a0cfd7ad9b9..4210dd36d913 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -618,12 +618,12 @@ export interface AstroUserConfig { * @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. - * - `'attribute'` - Use `data-` attributes, causing no specifity increase. + * - `'where'` - Use `:where` selectors, causing no specificity increase. + * - `'class'` - Use class-based selectors, causing a +1 specificity increase. + * - `'attribute'` - Use `data-` attributes, causing a +1 specificity 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 `'where'` gives you more control over specificity, but requires that you use higher-specificity 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' | 'attribute'; From 345cb4aeb99c08288baf2d6e17ce64c5e5ecac80 Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:28:35 -0400 Subject: [PATCH 10/33] Support AVIF input assets (#8380) Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> Co-authored-by: Princesseuh --- .changeset/five-doors-love.md | 5 + packages/astro/client.d.ts | 4 + packages/astro/package.json | 2 + packages/astro/src/assets/consts.ts | 10 +- packages/astro/src/assets/utils/metadata.ts | 9 +- packages/astro/src/assets/vendor/README.md | 3 - .../src/assets/vendor/image-size/LICENSE | 9 - .../src/assets/vendor/image-size/detector.ts | 30 --- .../src/assets/vendor/image-size/index.ts | 146 ------------ .../src/assets/vendor/image-size/readUInt.ts | 10 - .../src/assets/vendor/image-size/types.ts | 38 --- .../src/assets/vendor/image-size/types/bmp.ts | 14 -- .../src/assets/vendor/image-size/types/cur.ts | 16 -- .../src/assets/vendor/image-size/types/dds.ts | 14 -- .../src/assets/vendor/image-size/types/gif.ts | 16 -- .../assets/vendor/image-size/types/icns.ts | 113 --------- .../src/assets/vendor/image-size/types/ico.ts | 76 ------ .../vendor/image-size/types/interface.ts | 15 -- .../src/assets/vendor/image-size/types/j2c.ts | 15 -- .../src/assets/vendor/image-size/types/jp2.ts | 61 ----- .../src/assets/vendor/image-size/types/jpg.ts | 151 ------------ .../src/assets/vendor/image-size/types/ktx.ts | 16 -- .../src/assets/vendor/image-size/types/png.ts | 36 --- .../src/assets/vendor/image-size/types/pnm.ts | 80 ------- .../src/assets/vendor/image-size/types/psd.ts | 14 -- .../src/assets/vendor/image-size/types/svg.ts | 106 --------- .../assets/vendor/image-size/types/tiff.ts | 115 --------- .../assets/vendor/image-size/types/webp.ts | 65 ----- .../astro/src/assets/vendor/queue/LICENSE | 8 - .../astro/src/assets/vendor/queue/queue.js | 225 ------------------ .../astro/src/assets/vite-plugin-assets.ts | 2 +- packages/astro/test/core-image.test.js | 16 ++ .../core-image/src/assets/light_walrus.avif | Bin 0 -> 19439 bytes .../fixtures/core-image/src/pages/avif.astro | 5 + pnpm-lock.yaml | 63 ++++- 35 files changed, 102 insertions(+), 1406 deletions(-) create mode 100644 .changeset/five-doors-love.md delete mode 100644 packages/astro/src/assets/vendor/README.md delete mode 100644 packages/astro/src/assets/vendor/image-size/LICENSE delete mode 100644 packages/astro/src/assets/vendor/image-size/detector.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/index.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/readUInt.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/bmp.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/cur.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/dds.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/gif.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/icns.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/ico.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/interface.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/j2c.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/jp2.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/jpg.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/ktx.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/png.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/pnm.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/psd.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/svg.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/tiff.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/webp.ts delete mode 100644 packages/astro/src/assets/vendor/queue/LICENSE delete mode 100644 packages/astro/src/assets/vendor/queue/queue.js create mode 100644 packages/astro/test/fixtures/core-image/src/assets/light_walrus.avif create mode 100644 packages/astro/test/fixtures/core-image/src/pages/avif.astro diff --git a/.changeset/five-doors-love.md b/.changeset/five-doors-love.md new file mode 100644 index 000000000000..2569f54b3df2 --- /dev/null +++ b/.changeset/five-doors-love.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Support AVIF input assets diff --git a/packages/astro/client.d.ts b/packages/astro/client.d.ts index 90f06c72d067..1c86d26351e4 100644 --- a/packages/astro/client.d.ts +++ b/packages/astro/client.d.ts @@ -108,6 +108,10 @@ declare module '*.svg' { const metadata: ImageMetadata; export default metadata; } +declare module '*.avif' { + const metadata: ImageMetadata; + export default metadata; +} declare module 'astro:transitions' { type TransitionModule = typeof import('./dist/transitions/index.js'); diff --git a/packages/astro/package.json b/packages/astro/package.json index 34e3b7aa9caf..2052f8158dc2 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -157,6 +157,7 @@ "p-limit": "^4.0.0", "path-to-regexp": "^6.2.1", "preferred-pm": "^3.1.2", + "probe-image-size": "^7.2.3", "prompts": "^2.4.2", "rehype": "^12.0.1", "resolve": "^1.22.4", @@ -197,6 +198,7 @@ "@types/js-yaml": "^4.0.5", "@types/mime": "^3.0.1", "@types/mocha": "^10.0.1", + "@types/probe-image-size": "^7.2.0", "@types/prompts": "^2.4.4", "@types/resolve": "^1.20.2", "@types/send": "^0.17.1", diff --git a/packages/astro/src/assets/consts.ts b/packages/astro/src/assets/consts.ts index d184c9359c77..90dfa599cf21 100644 --- a/packages/astro/src/assets/consts.ts +++ b/packages/astro/src/assets/consts.ts @@ -1,14 +1,6 @@ export const VIRTUAL_MODULE_ID = 'astro:assets'; export const VIRTUAL_SERVICE_ID = 'virtual:image-service'; export const VALID_INPUT_FORMATS = [ - // TODO: `image-size` does not support the following formats, so users can't import them. - // However, it would be immensely useful to add, for three reasons: - // - `heic` and `heif` are common formats, especially among Apple users. - // - AVIF is a common format on the web that's bound to become more and more common. - // - It's totally reasonable for an user's provided image service to want to support more image types. - //'heic', - //'heif', - //'avif', 'jpeg', 'jpg', 'png', @@ -16,6 +8,7 @@ export const VALID_INPUT_FORMATS = [ 'webp', 'gif', 'svg', + 'avif', ] as const; /** * Valid formats that our base services support. @@ -29,5 +22,6 @@ export const VALID_SUPPORTED_FORMATS = [ 'webp', 'gif', 'svg', + 'avif', ] as const; export const VALID_OUTPUT_FORMATS = ['avif', 'png', 'webp', 'jpeg', 'jpg', 'svg'] as const; diff --git a/packages/astro/src/assets/utils/metadata.ts b/packages/astro/src/assets/utils/metadata.ts index de4136b36b26..c4e96f800984 100644 --- a/packages/astro/src/assets/utils/metadata.ts +++ b/packages/astro/src/assets/utils/metadata.ts @@ -1,8 +1,13 @@ import type { ImageInputFormat, ImageMetadata } from '../types.js'; -import imageSize from '../vendor/image-size/index.js'; +import probe from 'probe-image-size'; export async function imageMetadata(data: Buffer): Promise | undefined> { - const { width, height, type, orientation } = imageSize(data); + const result = probe.sync(data); + if (result === null) { + throw new Error('Failed to probe image size.'); + } + + const { width, height, type, orientation } = result; const isPortrait = (orientation || 0) >= 5; if (!width || !height || !type) { diff --git a/packages/astro/src/assets/vendor/README.md b/packages/astro/src/assets/vendor/README.md deleted file mode 100644 index 7b6927b674b3..000000000000 --- a/packages/astro/src/assets/vendor/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Vendored version of `image-size` and `queue` because we had issues with the CJS nature of those packages. - -Should hopefully be fixed by https://github.com/image-size/image-size/pull/370 diff --git a/packages/astro/src/assets/vendor/image-size/LICENSE b/packages/astro/src/assets/vendor/image-size/LICENSE deleted file mode 100644 index 1341a90d565f..000000000000 --- a/packages/astro/src/assets/vendor/image-size/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -The MIT License (MIT) - -Copyright © 2017 Aditya Yadav, http://netroy.in - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/astro/src/assets/vendor/image-size/detector.ts b/packages/astro/src/assets/vendor/image-size/detector.ts deleted file mode 100644 index 7a8873ab29f4..000000000000 --- a/packages/astro/src/assets/vendor/image-size/detector.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { typeHandlers, type imageType } from './types.js' - -const keys = Object.keys(typeHandlers) as imageType[] - -// This map helps avoid validating for every single image type -const firstBytes: { [byte: number]: imageType } = { - 0x38: 'psd', - 0x42: 'bmp', - 0x44: 'dds', - 0x47: 'gif', - 0x49: 'tiff', - 0x4d: 'tiff', - 0x52: 'webp', - 0x69: 'icns', - 0x89: 'png', - 0xff: 'jpg' -} - -export function detector(buffer: Buffer): imageType | undefined { - const byte = buffer[0] - if (byte in firstBytes) { - const type = firstBytes[byte] - if (type && typeHandlers[type].validate(buffer)) { - return type - } - } - - const finder = (key: imageType) => typeHandlers[key].validate(buffer) - return keys.find(finder) -} diff --git a/packages/astro/src/assets/vendor/image-size/index.ts b/packages/astro/src/assets/vendor/image-size/index.ts deleted file mode 100644 index 24f910b332c1..000000000000 --- a/packages/astro/src/assets/vendor/image-size/index.ts +++ /dev/null @@ -1,146 +0,0 @@ -import * as fs from "node:fs"; -import * as path from "node:path"; -import Queue from "../queue/queue.js"; -import { detector } from "./detector.js"; -import { typeHandlers, type imageType } from "./types.js"; -import type { ISizeCalculationResult } from "./types/interface.js"; - -type CallbackFn = (e: Error | null, r?: ISizeCalculationResult) => void; - -// Maximum buffer size, with a default of 512 kilobytes. -// TO-DO: make this adaptive based on the initial signature of the image -const MaxBufferSize = 512 * 1024; - -// This queue is for async `fs` operations, to avoid reaching file-descriptor limits -const queue = new Queue({ concurrency: 100, autostart: true }); - -interface Options { - disabledFS: boolean; - disabledTypes: imageType[]; -} - -const globalOptions: Options = { - disabledFS: false, - disabledTypes: [], -}; - -/** - * Return size information based on a buffer - * - * @param {Buffer} buffer - * @param {String} filepath - * @returns {Object} - */ -function lookup(buffer: Buffer, filepath?: string): ISizeCalculationResult { - // detect the file type.. don't rely on the extension - const type = detector(buffer); - - if (typeof type !== "undefined") { - if (globalOptions.disabledTypes.indexOf(type) > -1) { - throw new TypeError("disabled file type: " + type); - } - - // find an appropriate handler for this file type - if (type in typeHandlers) { - const size = typeHandlers[type].calculate(buffer, filepath); - if (size !== undefined) { - size.type = type; - return size; - } - } - } - - // throw up, if we don't understand the file - throw new TypeError( - "unsupported file type: " + type + " (file: " + filepath + ")" - ); -} - -/** - * Reads a file into a buffer. - * @param {String} filepath - * @returns {Promise} - */ -async function asyncFileToBuffer(filepath: string): Promise { - const handle = await fs.promises.open(filepath, "r"); - const { size } = await handle.stat(); - if (size <= 0) { - await handle.close(); - throw new Error("Empty file"); - } - const bufferSize = Math.min(size, MaxBufferSize); - const buffer = Buffer.alloc(bufferSize); - await handle.read(buffer, 0, bufferSize, 0); - await handle.close(); - return buffer; -} - -/** - * Synchronously reads a file into a buffer, blocking the nodejs process. - * - * @param {String} filepath - * @returns {Buffer} - */ -function syncFileToBuffer(filepath: string): Buffer { - // read from the file, synchronously - const descriptor = fs.openSync(filepath, "r"); - const { size } = fs.fstatSync(descriptor); - if (size <= 0) { - fs.closeSync(descriptor); - throw new Error("Empty file"); - } - const bufferSize = Math.min(size, MaxBufferSize); - const buffer = Buffer.alloc(bufferSize); - fs.readSync(descriptor, buffer, 0, bufferSize, 0); - fs.closeSync(descriptor); - return buffer; -} - -export default imageSize; -export function imageSize(input: Buffer | string): ISizeCalculationResult; -export function imageSize(input: string, callback: CallbackFn): void; - -/** - * @param {Buffer|string} input - buffer or relative/absolute path of the image file - * @param {Function=} [callback] - optional function for async detection - */ -export function imageSize( - input: Buffer | string, - callback?: CallbackFn -): ISizeCalculationResult | void { - // Handle buffer input - if (Buffer.isBuffer(input)) { - return lookup(input); - } - - // input should be a string at this point - if (typeof input !== "string" || globalOptions.disabledFS) { - throw new TypeError("invalid invocation. input should be a Buffer"); - } - - // resolve the file path - const filepath = path.resolve(input); - if (typeof callback === "function") { - queue.push(() => - asyncFileToBuffer(filepath) - .then((buffer) => - process.nextTick(callback, null, lookup(buffer, filepath)) - ) - .catch(callback) - ); - } else { - const buffer = syncFileToBuffer(filepath); - return lookup(buffer, filepath); - } -} - -export const disableFS = (v: boolean): void => { - globalOptions.disabledFS = v; -}; -export const disableTypes = (types: imageType[]): void => { - globalOptions.disabledTypes = types; -}; -export const setConcurrency = (c: number): void => { - queue.concurrency = c; -}; -export const types = Object.keys(typeHandlers); diff --git a/packages/astro/src/assets/vendor/image-size/readUInt.ts b/packages/astro/src/assets/vendor/image-size/readUInt.ts deleted file mode 100644 index ede811408a75..000000000000 --- a/packages/astro/src/assets/vendor/image-size/readUInt.ts +++ /dev/null @@ -1,10 +0,0 @@ -type Bits = 16 | 32 -type MethodName = 'readUInt16BE' | 'readUInt16LE' | 'readUInt32BE' | 'readUInt32LE' - -// Abstract reading multi-byte unsigned integers -export function readUInt(buffer: Buffer, bits: Bits, offset: number, isBigEndian: boolean): number { - offset = offset || 0 - const endian = isBigEndian ? 'BE' : 'LE' - const methodName: MethodName = ('readUInt' + bits + endian) as MethodName - return buffer[methodName].call(buffer, offset) -} diff --git a/packages/astro/src/assets/vendor/image-size/types.ts b/packages/astro/src/assets/vendor/image-size/types.ts deleted file mode 100644 index 05f4f82cc4e0..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types.ts +++ /dev/null @@ -1,38 +0,0 @@ -// load all available handlers explicitely for browserify support -import { BMP } from './types/bmp.js' -import { CUR } from './types/cur.js' -import { DDS } from './types/dds.js' -import { GIF } from './types/gif.js' -import { ICNS } from './types/icns.js' -import { ICO } from './types/ico.js' -import { J2C } from './types/j2c.js' -import { JP2 } from './types/jp2.js' -import { JPG } from './types/jpg.js' -import { KTX } from './types/ktx.js' -import { PNG } from './types/png.js' -import { PNM } from './types/pnm.js' -import { PSD } from './types/psd.js' -import { SVG } from './types/svg.js' -import { TIFF } from './types/tiff.js' -import { WEBP } from './types/webp.js' - -export const typeHandlers = { - bmp: BMP, - cur: CUR, - dds: DDS, - gif: GIF, - icns: ICNS, - ico: ICO, - j2c: J2C, - jp2: JP2, - jpg: JPG, - ktx: KTX, - png: PNG, - pnm: PNM, - psd: PSD, - svg: SVG, - tiff: TIFF, - webp: WEBP, -} - -export type imageType = keyof typeof typeHandlers diff --git a/packages/astro/src/assets/vendor/image-size/types/bmp.ts b/packages/astro/src/assets/vendor/image-size/types/bmp.ts deleted file mode 100644 index 2f55ccdd1057..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/bmp.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { IImage } from './interface' - -export const BMP: IImage = { - validate(buffer) { - return ('BM' === buffer.toString('ascii', 0, 2)) - }, - - calculate(buffer) { - return { - height: Math.abs(buffer.readInt32LE(22)), - width: buffer.readUInt32LE(18) - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/cur.ts b/packages/astro/src/assets/vendor/image-size/types/cur.ts deleted file mode 100644 index 42766baecd1f..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/cur.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ICO } from './ico.js' -import type { IImage } from './interface' - -const TYPE_CURSOR = 2 -export const CUR: IImage = { - validate(buffer) { - if (buffer.readUInt16LE(0) !== 0) { - return false - } - return buffer.readUInt16LE(2) === TYPE_CURSOR - }, - - calculate(buffer) { - return ICO.calculate(buffer) - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/dds.ts b/packages/astro/src/assets/vendor/image-size/types/dds.ts deleted file mode 100644 index e9ceb63ba6e3..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/dds.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { IImage } from './interface' - -export const DDS: IImage = { - validate(buffer) { - return buffer.readUInt32LE(0) === 0x20534444 - }, - - calculate(buffer) { - return { - height: buffer.readUInt32LE(12), - width: buffer.readUInt32LE(16) - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/gif.ts b/packages/astro/src/assets/vendor/image-size/types/gif.ts deleted file mode 100644 index b18b305f199e..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/gif.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { IImage } from './interface' - -const gifRegexp = /^GIF8[79]a/ -export const GIF: IImage = { - validate(buffer) { - const signature = buffer.toString('ascii', 0, 6) - return (gifRegexp.test(signature)) - }, - - calculate(buffer) { - return { - height: buffer.readUInt16LE(8), - width: buffer.readUInt16LE(6) - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/icns.ts b/packages/astro/src/assets/vendor/image-size/types/icns.ts deleted file mode 100644 index 5beccb02c47d..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/icns.ts +++ /dev/null @@ -1,113 +0,0 @@ -import type { IImage, ISize } from './interface' - -/** - * ICNS Header - * - * | Offset | Size | Purpose | - * | 0 | 4 | Magic literal, must be "icns" (0x69, 0x63, 0x6e, 0x73) | - * | 4 | 4 | Length of file, in bytes, msb first. | - * - */ -const SIZE_HEADER = 4 + 4 // 8 -const FILE_LENGTH_OFFSET = 4 // MSB => BIG ENDIAN - -/** - * Image Entry - * - * | Offset | Size | Purpose | - * | 0 | 4 | Icon type, see OSType below. | - * | 4 | 4 | Length of data, in bytes (including type and length), msb first. | - * | 8 | n | Icon data | - */ -const ENTRY_LENGTH_OFFSET = 4 // MSB => BIG ENDIAN - -const ICON_TYPE_SIZE: {[key: string]: number} = { - ICON: 32, - 'ICN#': 32, - // m => 16 x 16 - 'icm#': 16, - icm4: 16, - icm8: 16, - // s => 16 x 16 - 'ics#': 16, - ics4: 16, - ics8: 16, - is32: 16, - s8mk: 16, - icp4: 16, - // l => 32 x 32 - icl4: 32, - icl8: 32, - il32: 32, - l8mk: 32, - icp5: 32, - ic11: 32, - // h => 48 x 48 - ich4: 48, - ich8: 48, - ih32: 48, - h8mk: 48, - // . => 64 x 64 - icp6: 64, - ic12: 32, - // t => 128 x 128 - it32: 128, - t8mk: 128, - ic07: 128, - // . => 256 x 256 - ic08: 256, - ic13: 256, - // . => 512 x 512 - ic09: 512, - ic14: 512, - // . => 1024 x 1024 - ic10: 1024, -} - -function readImageHeader(buffer: Buffer, imageOffset: number): [string, number] { - const imageLengthOffset = imageOffset + ENTRY_LENGTH_OFFSET - return [ - buffer.toString('ascii', imageOffset, imageLengthOffset), - buffer.readUInt32BE(imageLengthOffset) - ] -} - -function getImageSize(type: string): ISize { - const size = ICON_TYPE_SIZE[type] - return { width: size, height: size, type } -} - -export const ICNS: IImage = { - validate(buffer) { - return ('icns' === buffer.toString('ascii', 0, 4)) - }, - - calculate(buffer) { - const bufferLength = buffer.length - const fileLength = buffer.readUInt32BE(FILE_LENGTH_OFFSET) - let imageOffset = SIZE_HEADER - - let imageHeader = readImageHeader(buffer, imageOffset) - let imageSize = getImageSize(imageHeader[0]) - imageOffset += imageHeader[1] - - if (imageOffset === fileLength) { - return imageSize - } - - const result = { - height: imageSize.height, - images: [imageSize], - width: imageSize.width - } - - while (imageOffset < fileLength && imageOffset < bufferLength) { - imageHeader = readImageHeader(buffer, imageOffset) - imageSize = getImageSize(imageHeader[0]) - imageOffset += imageHeader[1] - result.images.push(imageSize) - } - - return result - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/ico.ts b/packages/astro/src/assets/vendor/image-size/types/ico.ts deleted file mode 100644 index 09310176fcb9..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/ico.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type { IImage, ISize, ISizeCalculationResult } from './interface' - -const TYPE_ICON = 1 - -/** - * ICON Header - * - * | Offset | Size | Purpose | - * | 0 | 2 | Reserved. Must always be 0. | - * | 2 | 2 | Image type: 1 for icon (.ICO) image, 2 for cursor (.CUR) image. Other values are invalid. | - * | 4 | 2 | Number of images in the file. | - * - */ -const SIZE_HEADER = 2 + 2 + 2 // 6 - -/** - * Image Entry - * - * | Offset | Size | Purpose | - * | 0 | 1 | Image width in pixels. Can be any number between 0 and 255. Value 0 means width is 256 pixels. | - * | 1 | 1 | Image height in pixels. Can be any number between 0 and 255. Value 0 means height is 256 pixels. | - * | 2 | 1 | Number of colors in the color palette. Should be 0 if the image does not use a color palette. | - * | 3 | 1 | Reserved. Should be 0. | - * | 4 | 2 | ICO format: Color planes. Should be 0 or 1. | - * | | | CUR format: The horizontal coordinates of the hotspot in number of pixels from the left. | - * | 6 | 2 | ICO format: Bits per pixel. | - * | | | CUR format: The vertical coordinates of the hotspot in number of pixels from the top. | - * | 8 | 4 | The size of the image's data in bytes | - * | 12 | 4 | The offset of BMP or PNG data from the beginning of the ICO/CUR file | - * - */ -const SIZE_IMAGE_ENTRY = 1 + 1 + 1 + 1 + 2 + 2 + 4 + 4 // 16 - -function getSizeFromOffset(buffer: Buffer, offset: number): number { - const value = buffer.readUInt8(offset) - return value === 0 ? 256 : value -} - -function getImageSize(buffer: Buffer, imageIndex: number): ISize { - const offset = SIZE_HEADER + (imageIndex * SIZE_IMAGE_ENTRY) - return { - height: getSizeFromOffset(buffer, offset + 1), - width: getSizeFromOffset(buffer, offset) - } -} - -export const ICO: IImage = { - validate(buffer) { - if (buffer.readUInt16LE(0) !== 0) { - return false - } - return buffer.readUInt16LE(2) === TYPE_ICON - }, - - calculate(buffer) { - const nbImages = buffer.readUInt16LE(4) - const imageSize = getImageSize(buffer, 0) - - if (nbImages === 1) { - return imageSize - } - - const imgs: ISize[] = [imageSize] - for (let imageIndex = 1; imageIndex < nbImages; imageIndex += 1) { - imgs.push(getImageSize(buffer, imageIndex)) - } - - const result: ISizeCalculationResult = { - height: imageSize.height, - images: imgs, - width: imageSize.width - } - - return result - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/interface.ts b/packages/astro/src/assets/vendor/image-size/types/interface.ts deleted file mode 100644 index 9886b357361e..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/interface.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface ISize { - width: number | undefined - height: number | undefined - orientation?: number - type?: string -} - -export interface ISizeCalculationResult extends ISize { - images?: ISize[] -} - -export interface IImage { - validate: (buffer: Buffer) => boolean - calculate: (buffer: Buffer, filepath?: string) => ISizeCalculationResult -} diff --git a/packages/astro/src/assets/vendor/image-size/types/j2c.ts b/packages/astro/src/assets/vendor/image-size/types/j2c.ts deleted file mode 100644 index 301c38cb6f06..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/j2c.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { IImage } from './interface' - -export const J2C: IImage = { - validate(buffer) { - // TODO: this doesn't seem right. SIZ marker doesn't have to be right after the SOC - return buffer.toString('hex', 0, 4) === 'ff4fff51' - }, - - calculate(buffer) { - return { - height: buffer.readUInt32BE(12), - width: buffer.readUInt32BE(8), - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/jp2.ts b/packages/astro/src/assets/vendor/image-size/types/jp2.ts deleted file mode 100644 index 127a96d608e3..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/jp2.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { IImage, ISize } from './interface' - -const BoxTypes = { - ftyp: '66747970', - ihdr: '69686472', - jp2h: '6a703268', - jp__: '6a502020', - rreq: '72726571', - xml_: '786d6c20' -} - -const calculateRREQLength = (box: Buffer): number => { - const unit = box.readUInt8(0) - let offset = 1 + (2 * unit) - const numStdFlags = box.readUInt16BE(offset) - const flagsLength = numStdFlags * (2 + unit) - offset = offset + 2 + flagsLength - const numVendorFeatures = box.readUInt16BE(offset) - const featuresLength = numVendorFeatures * (16 + unit) - return offset + 2 + featuresLength -} - -const parseIHDR = (box: Buffer): ISize => { - return { - height: box.readUInt32BE(4), - width: box.readUInt32BE(8), - } -} - -export const JP2: IImage = { - validate(buffer) { - const signature = buffer.toString('hex', 4, 8) - const signatureLength = buffer.readUInt32BE(0) - if (signature !== BoxTypes.jp__ || signatureLength < 1) { - return false - } - - const ftypeBoxStart = signatureLength + 4 - const ftypBoxLength = buffer.readUInt32BE(signatureLength) - const ftypBox = buffer.slice(ftypeBoxStart, ftypeBoxStart + ftypBoxLength) - return ftypBox.toString('hex', 0, 4) === BoxTypes.ftyp - }, - - calculate(buffer) { - const signatureLength = buffer.readUInt32BE(0) - const ftypBoxLength = buffer.readUInt16BE(signatureLength + 2) - let offset = signatureLength + 4 + ftypBoxLength - const nextBoxType = buffer.toString('hex', offset, offset + 4) - switch (nextBoxType) { - case BoxTypes.rreq: - // WHAT ARE THESE 4 BYTES????? - const MAGIC = 4 - offset = offset + 4 + MAGIC + calculateRREQLength(buffer.slice(offset + 4)) - return parseIHDR(buffer.slice(offset + 8, offset + 24)) - case BoxTypes.jp2h : - return parseIHDR(buffer.slice(offset + 8, offset + 24)) - default: - throw new TypeError('Unsupported header found: ' + buffer.toString('ascii', offset, offset + 4)) - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/jpg.ts b/packages/astro/src/assets/vendor/image-size/types/jpg.ts deleted file mode 100644 index 68c32b7bec9c..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/jpg.ts +++ /dev/null @@ -1,151 +0,0 @@ -// NOTE: we only support baseline and progressive JPGs here -// due to the structure of the loader class, we only get a buffer -// with a maximum size of 4096 bytes. so if the SOF marker is outside -// if this range we can't detect the file size correctly. - -import { readUInt } from '../readUInt.js' -import type { IImage, ISize } from './interface' - -const EXIF_MARKER = '45786966' -const APP1_DATA_SIZE_BYTES = 2 -const EXIF_HEADER_BYTES = 6 -const TIFF_BYTE_ALIGN_BYTES = 2 -const BIG_ENDIAN_BYTE_ALIGN = '4d4d' -const LITTLE_ENDIAN_BYTE_ALIGN = '4949' - -// Each entry is exactly 12 bytes -const IDF_ENTRY_BYTES = 12 -const NUM_DIRECTORY_ENTRIES_BYTES = 2 - -function isEXIF(buffer: Buffer): boolean { - return (buffer.toString('hex', 2, 6) === EXIF_MARKER) -} - -function extractSize(buffer: Buffer, index: number): ISize { - return { - height : buffer.readUInt16BE(index), - width : buffer.readUInt16BE(index + 2) - } -} - -function extractOrientation(exifBlock: Buffer, isBigEndian: boolean) { - // TODO: assert that this contains 0x002A - // let STATIC_MOTOROLA_TIFF_HEADER_BYTES = 2 - // let TIFF_IMAGE_FILE_DIRECTORY_BYTES = 4 - - // TODO: derive from TIFF_IMAGE_FILE_DIRECTORY_BYTES - const idfOffset = 8 - - // IDF osset works from right after the header bytes - // (so the offset includes the tiff byte align) - const offset = EXIF_HEADER_BYTES + idfOffset - - const idfDirectoryEntries = readUInt(exifBlock, 16, offset, isBigEndian) - - for (let directoryEntryNumber = 0; directoryEntryNumber < idfDirectoryEntries; directoryEntryNumber++) { - const start = offset + NUM_DIRECTORY_ENTRIES_BYTES + (directoryEntryNumber * IDF_ENTRY_BYTES) - const end = start + IDF_ENTRY_BYTES - - // Skip on corrupt EXIF blocks - if (start > exifBlock.length) { - return - } - - const block = exifBlock.slice(start, end) - const tagNumber = readUInt(block, 16, 0, isBigEndian) - - // 0x0112 (decimal: 274) is the `orientation` tag ID - if (tagNumber === 274) { - const dataFormat = readUInt(block, 16, 2, isBigEndian) - if (dataFormat !== 3) { - return - } - - // unsinged int has 2 bytes per component - // if there would more than 4 bytes in total it's a pointer - const numberOfComponents = readUInt(block, 32, 4, isBigEndian) - if (numberOfComponents !== 1) { - return - } - - return readUInt(block, 16, 8, isBigEndian) - } - } -} - -function validateExifBlock(buffer: Buffer, index: number) { - // Skip APP1 Data Size - const exifBlock = buffer.slice(APP1_DATA_SIZE_BYTES, index) - - // Consider byte alignment - const byteAlign = exifBlock.toString('hex', EXIF_HEADER_BYTES, EXIF_HEADER_BYTES + TIFF_BYTE_ALIGN_BYTES) - - // Ignore Empty EXIF. Validate byte alignment - const isBigEndian = byteAlign === BIG_ENDIAN_BYTE_ALIGN - const isLittleEndian = byteAlign === LITTLE_ENDIAN_BYTE_ALIGN - - if (isBigEndian || isLittleEndian) { - return extractOrientation(exifBlock, isBigEndian) - } -} - -function validateBuffer(buffer: Buffer, index: number): void { - // index should be within buffer limits - if (index > buffer.length) { - throw new TypeError('Corrupt JPG, exceeded buffer limits') - } - // Every JPEG block must begin with a 0xFF - if (buffer[index] !== 0xFF) { - throw new TypeError('Invalid JPG, marker table corrupted') - } -} - -export const JPG: IImage = { - validate(buffer) { - const SOIMarker = buffer.toString('hex', 0, 2) - return ('ffd8' === SOIMarker) - }, - - calculate(buffer) { - // Skip 4 chars, they are for signature - buffer = buffer.slice(4) - - let orientation: number | undefined - let next: number - while (buffer.length) { - // read length of the next block - const i = buffer.readUInt16BE(0) - - if (isEXIF(buffer)) { - orientation = validateExifBlock(buffer, i) - } - - // ensure correct format - validateBuffer(buffer, i) - - // 0xFFC0 is baseline standard(SOF) - // 0xFFC1 is baseline optimized(SOF) - // 0xFFC2 is progressive(SOF2) - next = buffer[i + 1] - if (next === 0xC0 || next === 0xC1 || next === 0xC2) { - const size = extractSize(buffer, i + 5) - - // TODO: is orientation=0 a valid answer here? - if (!orientation) { - return size - } - - return { - height: size.height, - orientation, - width: size.width - } - } - - // move to the next block - buffer = buffer.slice(i + 2) - } - - throw new TypeError('Invalid JPG, no size found') - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/ktx.ts b/packages/astro/src/assets/vendor/image-size/types/ktx.ts deleted file mode 100644 index 9e43fdeaaf37..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/ktx.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { IImage } from './interface' - -const SIGNATURE = 'KTX 11' - -export const KTX: IImage = { - validate(buffer) { - return SIGNATURE === buffer.toString('ascii', 1, 7) - }, - - calculate(buffer) { - return { - height: buffer.readUInt32LE(40), - width: buffer.readUInt32LE(36), - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/png.ts b/packages/astro/src/assets/vendor/image-size/types/png.ts deleted file mode 100644 index a31411380c3e..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/png.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { IImage } from './interface' - -const pngSignature = 'PNG\r\n\x1a\n' -const pngImageHeaderChunkName = 'IHDR' - -// Used to detect "fried" png's: http://www.jongware.com/pngdefry.html -const pngFriedChunkName = 'CgBI' - -export const PNG: IImage = { - validate(buffer) { - if (pngSignature === buffer.toString('ascii', 1, 8)) { - let chunkName = buffer.toString('ascii', 12, 16) - if (chunkName === pngFriedChunkName) { - chunkName = buffer.toString('ascii', 28, 32) - } - if (chunkName !== pngImageHeaderChunkName) { - throw new TypeError('Invalid PNG') - } - return true - } - return false - }, - - calculate(buffer) { - if (buffer.toString('ascii', 12, 16) === pngFriedChunkName) { - return { - height: buffer.readUInt32BE(36), - width: buffer.readUInt32BE(32) - } - } - return { - height: buffer.readUInt32BE(20), - width: buffer.readUInt32BE(16) - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/pnm.ts b/packages/astro/src/assets/vendor/image-size/types/pnm.ts deleted file mode 100644 index fa30a53d50a0..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/pnm.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { IImage, ISize } from './interface' - -const PNMTypes: { [signature: string]: string } = { - P1: 'pbm/ascii', - P2: 'pgm/ascii', - P3: 'ppm/ascii', - P4: 'pbm', - P5: 'pgm', - P6: 'ppm', - P7: 'pam', - PF: 'pfm' -} - -const Signatures = Object.keys(PNMTypes) - -type Handler = (type: string[]) => ISize -const handlers: { [type: string]: Handler} = { - default: (lines) => { - let dimensions: string[] = [] - - while (lines.length > 0) { - const line = lines.shift()! - if (line[0] === '#') { - continue - } - dimensions = line.split(' ') - break - } - - if (dimensions.length === 2) { - return { - height: parseInt(dimensions[1], 10), - width: parseInt(dimensions[0], 10), - } - } else { - throw new TypeError('Invalid PNM') - } - }, - pam: (lines) => { - const size: { [key: string]: number } = {} - while (lines.length > 0) { - const line = lines.shift()! - if (line.length > 16 || line.charCodeAt(0) > 128) { - continue - } - const [key, value] = line.split(' ') - if (key && value) { - size[key.toLowerCase()] = parseInt(value, 10) - } - if (size.height && size.width) { - break - } - } - - if (size.height && size.width) { - return { - height: size.height, - width: size.width - } - } else { - throw new TypeError('Invalid PAM') - } - } -} - -export const PNM: IImage = { - validate(buffer) { - const signature = buffer.toString('ascii', 0, 2) - return Signatures.includes(signature) - }, - - calculate(buffer) { - const signature = buffer.toString('ascii', 0, 2) - const type = PNMTypes[signature] - // TODO: this probably generates garbage. move to a stream based parser - const lines = buffer.toString('ascii', 3).split(/[\r\n]+/) - const handler = handlers[type] || handlers.default - return handler(lines) - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/psd.ts b/packages/astro/src/assets/vendor/image-size/types/psd.ts deleted file mode 100644 index 7521f5e9f826..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/psd.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { IImage } from './interface' - -export const PSD: IImage = { - validate(buffer) { - return ('8BPS' === buffer.toString('ascii', 0, 4)) - }, - - calculate(buffer) { - return { - height: buffer.readUInt32BE(14), - width: buffer.readUInt32BE(18) - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/svg.ts b/packages/astro/src/assets/vendor/image-size/types/svg.ts deleted file mode 100644 index 945be962dd4a..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/svg.ts +++ /dev/null @@ -1,106 +0,0 @@ -import type { IImage, ISize } from './interface' - -interface IAttributes { - width: number | null - height: number | null - viewbox?: IAttributes | null -} - -const svgReg = /"']|"[^"]*"|'[^']*')*>/ - -const extractorRegExps = { - height: /\sheight=(['"])([^%]+?)\1/, - root: svgReg, - viewbox: /\sviewBox=(['"])(.+?)\1/i, - width: /\swidth=(['"])([^%]+?)\1/, -} - -const INCH_CM = 2.54 -const units: { [unit: string]: number } = { - in: 96, - cm: 96 / INCH_CM, - em: 16, - ex: 8, - m: 96 / INCH_CM * 100, - mm: 96 / INCH_CM / 10, - pc: 96 / 72 / 12, - pt: 96 / 72, - px: 1 -} - -const unitsReg = new RegExp(`^([0-9.]+(?:e\\d+)?)(${Object.keys(units).join('|')})?$`) - -function parseLength(len: string) { - const m = unitsReg.exec(len) - if (!m) { - return undefined - } - return Math.round(Number(m[1]) * (units[m[2]] || 1)) -} - -function parseViewbox(viewbox: string): IAttributes { - const bounds = viewbox.split(' ') - return { - height: parseLength(bounds[3])!, - width: parseLength(bounds[2])! - } -} - -function parseAttributes(root: string): IAttributes { - const width = root.match(extractorRegExps.width) - const height = root.match(extractorRegExps.height) - const viewbox = root.match(extractorRegExps.viewbox) - return { - height: height && parseLength(height[2])!, - viewbox: viewbox && parseViewbox(viewbox[2])!, - width: width && parseLength(width[2])!, - } -} - -function calculateByDimensions(attrs: IAttributes): ISize { - return { - height: attrs.height!, - width: attrs.width!, - } -} - -function calculateByViewbox(attrs: IAttributes, viewbox: IAttributes): ISize { - const ratio = (viewbox.width!) / (viewbox.height!) - if (attrs.width) { - return { - height: Math.floor(attrs.width / ratio), - width: attrs.width, - } - } - if (attrs.height) { - return { - height: attrs.height, - width: Math.floor(attrs.height * ratio), - } - } - return { - height: viewbox.height!, - width: viewbox.width!, - } -} - -export const SVG: IImage = { - validate(buffer) { - const str = String(buffer) - return svgReg.test(str) - }, - - calculate(buffer) { - const root = buffer.toString('utf8').match(extractorRegExps.root) - if (root) { - const attrs = parseAttributes(root[0]) - if (attrs.width && attrs.height) { - return calculateByDimensions(attrs) - } - if (attrs.viewbox) { - return calculateByViewbox(attrs, attrs.viewbox) - } - } - throw new TypeError('Invalid SVG') - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/tiff.ts b/packages/astro/src/assets/vendor/image-size/types/tiff.ts deleted file mode 100644 index ae228c1b8448..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/tiff.ts +++ /dev/null @@ -1,115 +0,0 @@ -// based on http://www.compix.com/fileformattif.htm -// TO-DO: support big-endian as well -import * as fs from 'node:fs' -import { readUInt } from '../readUInt.js' -import type { IImage } from './interface' - -// Read IFD (image-file-directory) into a buffer -function readIFD(buffer: Buffer, filepath: string, isBigEndian: boolean) { - - const ifdOffset = readUInt(buffer, 32, 4, isBigEndian) - - // read only till the end of the file - let bufferSize = 1024 - const fileSize = fs.statSync(filepath).size - if (ifdOffset + bufferSize > fileSize) { - bufferSize = fileSize - ifdOffset - 10 - } - - // populate the buffer - const endBuffer = Buffer.alloc(bufferSize) - const descriptor = fs.openSync(filepath, 'r') - fs.readSync(descriptor, endBuffer, 0, bufferSize, ifdOffset) - fs.closeSync(descriptor) - - return endBuffer.slice(2) -} - -// TIFF values seem to be messed up on Big-Endian, this helps -function readValue(buffer: Buffer, isBigEndian: boolean): number { - const low = readUInt(buffer, 16, 8, isBigEndian) - const high = readUInt(buffer, 16, 10, isBigEndian) - return (high << 16) + low -} - -// move to the next tag -function nextTag(buffer: Buffer) { - if (buffer.length > 24) { - return buffer.slice(12) - } -} - -// Extract IFD tags from TIFF metadata -function extractTags(buffer: Buffer, isBigEndian: boolean) { - const tags: {[key: number]: number} = {} - - let temp: Buffer | undefined = buffer - while (temp?.length) { - const code = readUInt(temp, 16, 0, isBigEndian) - const type = readUInt(temp, 16, 2, isBigEndian) - const length = readUInt(temp, 32, 4, isBigEndian) - - // 0 means end of IFD - if (code === 0) { - break - } else { - // 256 is width, 257 is height - // if (code === 256 || code === 257) { - if (length === 1 && (type === 3 || type === 4)) { - tags[code] = readValue(temp, isBigEndian) - } - - // move to the next tag - temp = nextTag(temp) - } - } - - return tags -} - -// Test if the TIFF is Big Endian or Little Endian -function determineEndianness(buffer: Buffer) { - const signature = buffer.toString('ascii', 0, 2) - if ('II' === signature) { - return 'LE' - } else if ('MM' === signature) { - return 'BE' - } -} - -const signatures = [ - // '492049', // currently not supported - '49492a00', // Little endian - '4d4d002a', // Big Endian - // '4d4d002a', // BigTIFF > 4GB. currently not supported -] - -export const TIFF: IImage = { - validate(buffer) { - return signatures.includes(buffer.toString('hex', 0, 4)) - }, - - calculate(buffer, filepath) { - if (!filepath) { - throw new TypeError('Tiff doesn\'t support buffer') - } - - // Determine BE/LE - const isBigEndian = determineEndianness(buffer) === 'BE' - - // read the IFD - const ifdBuffer = readIFD(buffer, filepath, isBigEndian) - - // extract the tags from the IFD - const tags = extractTags(ifdBuffer, isBigEndian) - - const width = tags[256] - const height = tags[257] - - if (!width || !height) { - throw new TypeError('Invalid Tiff. Missing tags') - } - - return { height, width } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/webp.ts b/packages/astro/src/assets/vendor/image-size/types/webp.ts deleted file mode 100644 index aaafbf12d82d..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/webp.ts +++ /dev/null @@ -1,65 +0,0 @@ -// based on https://developers.google.com/speed/webp/docs/riff_container -import type { IImage, ISize } from './interface' - -function calculateExtended(buffer: Buffer): ISize { - return { - height: 1 + buffer.readUIntLE(7, 3), - width: 1 + buffer.readUIntLE(4, 3) - } -} - -function calculateLossless(buffer: Buffer): ISize { - return { - height: 1 + (((buffer[4] & 0xF) << 10) | (buffer[3] << 2) | ((buffer[2] & 0xC0) >> 6)), - width: 1 + (((buffer[2] & 0x3F) << 8) | buffer[1]) - } -} - -function calculateLossy(buffer: Buffer): ISize { - // `& 0x3fff` returns the last 14 bits - // TO-DO: include webp scaling in the calculations - return { - height: buffer.readInt16LE(8) & 0x3fff, - width: buffer.readInt16LE(6) & 0x3fff - } -} - -export const WEBP: IImage = { - validate(buffer) { - const riffHeader = 'RIFF' === buffer.toString('ascii', 0, 4) - const webpHeader = 'WEBP' === buffer.toString('ascii', 8, 12) - const vp8Header = 'VP8' === buffer.toString('ascii', 12, 15) - return (riffHeader && webpHeader && vp8Header) - }, - - calculate(buffer) { - const chunkHeader = buffer.toString('ascii', 12, 16) - buffer = buffer.slice(20, 30) - - // Extended webp stream signature - if (chunkHeader === 'VP8X') { - const extendedHeader = buffer[0] - const validStart = (extendedHeader & 0xc0) === 0 - const validEnd = (extendedHeader & 0x01) === 0 - if (validStart && validEnd) { - return calculateExtended(buffer) - } else { - // TODO: breaking change - throw new TypeError('Invalid WebP') - } - } - - // Lossless webp stream signature - if (chunkHeader === 'VP8 ' && buffer[0] !== 0x2f) { - return calculateLossy(buffer) - } - - // Lossy webp stream signature - const signature = buffer.toString('hex', 3, 6) - if (chunkHeader === 'VP8L' && signature !== '9d012a') { - return calculateLossless(buffer) - } - - throw new TypeError('Invalid WebP') - } -} diff --git a/packages/astro/src/assets/vendor/queue/LICENSE b/packages/astro/src/assets/vendor/queue/LICENSE deleted file mode 100644 index 50e946098625..000000000000 --- a/packages/astro/src/assets/vendor/queue/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -The MIT License (MIT) -Copyright (c) 2014 Jesse Tane - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/astro/src/assets/vendor/queue/queue.js b/packages/astro/src/assets/vendor/queue/queue.js deleted file mode 100644 index 6c71704355a4..000000000000 --- a/packages/astro/src/assets/vendor/queue/queue.js +++ /dev/null @@ -1,225 +0,0 @@ -const has = Object.prototype.hasOwnProperty - -/** - * Since CustomEvent is only supported in nodejs since version 19, - * you have to create your own class instead of using CustomEvent - * @see https://github.com/nodejs/node/issues/40678 - * */ -export class QueueEvent extends Event { - constructor (name, detail) { - super(name) - this.detail = detail - } -} - - -export default class Queue extends EventTarget { - constructor (options = {}) { - super() - const { concurrency = Infinity, timeout = 0, autostart = false, results = null } = options - - this.concurrency = concurrency - this.timeout = timeout - this.autostart = autostart - this.results = results - this.pending = 0 - this.session = 0 - this.running = false - this.jobs = [] - this.timers = [] - - this.addEventListener('error', this._errorHandler) - } - - _errorHandler(evt) { - this.end(evt.detail.error); - } - - pop () { - return this.jobs.pop() - } - - shift () { - return this.jobs.shift() - } - - indexOf (searchElement, fromIndex) { - return this.jobs.indexOf(searchElement, fromIndex) - } - - lastIndexOf (searchElement, fromIndex) { - if (fromIndex !== undefined) { return this.jobs.lastIndexOf(searchElement, fromIndex) } - return this.jobs.lastIndexOf(searchElement) - } - - slice (start, end) { - this.jobs = this.jobs.slice(start, end) - return this - } - - reverse () { - this.jobs.reverse() - return this - } - - push (...workers) { - const methodResult = this.jobs.push(...workers) - if (this.autostart) { - this.start() - } - return methodResult - } - - unshift (...workers) { - const methodResult = this.jobs.unshift(...workers) - if (this.autostart) { - this.start() - } - return methodResult - } - - splice (start, deleteCount, ...workers) { - this.jobs.splice(start, deleteCount, ...workers) - if (this.autostart) { - this.start() - } - return this - } - - get length () { - return this.pending + this.jobs.length - } - - start (callback) { - let awaiter; - - if (callback) { - this._addCallbackToEndEvent(callback) - } else { - awaiter = this._createPromiseToEndEvent(); - } - - this.running = true - - if (this.pending >= this.concurrency) { - return - } - - if (this.jobs.length === 0) { - if (this.pending === 0) { - this.done() - } - return - } - - const job = this.jobs.shift() - const session = this.session - const timeout = (job !== undefined) && has.call(job, 'timeout') ? job.timeout : this.timeout - let once = true - let timeoutId = null - let didTimeout = false - let resultIndex = null - - const next = (error, ...result) => { - if (once && this.session === session) { - once = false - this.pending-- - if (timeoutId !== null) { - this.timers = this.timers.filter((tID) => tID !== timeoutId) - clearTimeout(timeoutId) - } - - if (error) { - this.dispatchEvent(new QueueEvent('error', { error, job })) - } else if (!didTimeout) { - if (resultIndex !== null && this.results !== null) { - this.results[resultIndex] = [...result] - } - this.dispatchEvent(new QueueEvent('success', { result: [...result], job })) - } - - if (this.session === session) { - if (this.pending === 0 && this.jobs.length === 0) { - this.done() - } else if (this.running) { - this.start() - } - } - } - } - - if (timeout) { - timeoutId = setTimeout(() => { - didTimeout = true - this.dispatchEvent(new QueueEvent('timeout', { next, job })) - next() - }, timeout) - this.timers.push(timeoutId) - } - - if (this.results != null) { - resultIndex = this.results.length - this.results[resultIndex] = null - } - - this.pending++ - this.dispatchEvent(new QueueEvent('start', { job })) - - const promise = job(next) - - if (promise !== undefined && typeof promise.then === 'function') { - promise.then(function (result) { - return next(undefined, result) - }).catch(function (err) { - return next(err || true) - }) - } - - if (this.running && this.jobs.length > 0) { - return this.start() - } - - return awaiter; - } - - stop () { - this.running = false - } - - end (error) { - this.clearTimers() - this.jobs.length = 0 - this.pending = 0 - this.done(error) - } - - clearTimers () { - this.timers.forEach((timer) => { - clearTimeout(timer) - }) - - this.timers = [] - } - - _addCallbackToEndEvent (cb) { - const onend = (evt) => { - this.removeEventListener('end', onend) - cb(evt.detail.error, this.results) - } - this.addEventListener('end', onend) - } - - _createPromiseToEndEvent() { - return new Promise((resolve) => { - this._addCallbackToEndEvent((error, results) => { - resolve({ error, results }); - }); - }); - } - - done (error) { - this.session++ - this.running = false - this.dispatchEvent(new QueueEvent('end', { error })) - } -} diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts index f114e7ef8371..87fd950ee9db 100644 --- a/packages/astro/src/assets/vite-plugin-assets.ts +++ b/packages/astro/src/assets/vite-plugin-assets.ts @@ -14,7 +14,7 @@ import { hashTransform, propsToFilename } from './utils/transformToPath.js'; const resolvedVirtualModuleId = '\0' + VIRTUAL_MODULE_ID; -const assetRegex = new RegExp(`\.(${VALID_INPUT_FORMATS.join('|')})$`, 'i'); +const assetRegex = new RegExp(`\\.(${VALID_INPUT_FORMATS.join('|')})$`, 'i'); export default function assets({ settings, diff --git a/packages/astro/test/core-image.test.js b/packages/astro/test/core-image.test.js index f5a1b28f4fd4..666739539a21 100644 --- a/packages/astro/test/core-image.test.js +++ b/packages/astro/test/core-image.test.js @@ -174,6 +174,22 @@ describe('astro:image', () => { expect(res.status).to.equal(200); expect(loading).to.not.be.undefined; }); + + it('supports avif', async () => { + let res = await fixture.fetch('/avif'); + let html = await res.text(); + $ = cheerio.load(html); + + console.log(html); + + let $img = $('img'); + expect($img).to.have.a.lengthOf(1); + + let src = $img.attr('src'); + res = await fixture.fetch(src); + expect(res.status).to.equal(200); + expect(res.headers.get('content-type')).to.equal('image/avif'); + }); }); describe('vite-isms', () => { diff --git a/packages/astro/test/fixtures/core-image/src/assets/light_walrus.avif b/packages/astro/test/fixtures/core-image/src/assets/light_walrus.avif new file mode 100644 index 0000000000000000000000000000000000000000..89e1c3a143111f0a609c83f05c5fecd0ddce77b2 GIT binary patch literal 19439 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 es8D~R1oB4={TAM+9brJNiAM diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd844e217e63..f70352874e9c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -596,6 +596,9 @@ importers: preferred-pm: specifier: ^3.1.2 version: 3.1.2 + probe-image-size: + specifier: ^7.2.3 + version: 7.2.3 prompts: specifier: ^2.4.2 version: 2.4.2 @@ -706,6 +709,9 @@ importers: '@types/mocha': specifier: ^10.0.1 version: 10.0.1 + '@types/probe-image-size': + specifier: ^7.2.0 + version: 7.2.0 '@types/prompts': specifier: ^2.4.4 version: 2.4.4 @@ -8805,6 +8811,12 @@ packages: /@types/ms@0.7.31: resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} + /@types/needle@3.2.0: + resolution: {integrity: sha512-6XzvzEyJ2ozFNfPajFmqH9JOt0Hp+9TawaYpJT59iIP/zR0U37cfWCRwosyIeEBBZBi021Osq4jGAD3AOju5fg==} + dependencies: + '@types/node': 18.17.8 + dev: true + /@types/nlcst@1.0.0: resolution: {integrity: sha512-3TGCfOcy8R8mMQ4CNSNOe3PG66HttvjcLzCoOpvXvDtfWOTi+uT/rxeOKm/qEwbM4SNe1O/PjdiBK2YcTjU4OQ==} dependencies: @@ -8846,6 +8858,13 @@ packages: resolution: {integrity: sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==} dev: true + /@types/probe-image-size@7.2.0: + resolution: {integrity: sha512-R5H3vw62gHNHrn+JGZbKejb+Z2D/6E5UNVlhCzIaBBLroMQMOFqy5Pap2gM+ZZHdqBtVU0/cx/M6to+mOJcoew==} + dependencies: + '@types/needle': 3.2.0 + '@types/node': 18.17.8 + dev: true + /@types/prompts@2.4.4: resolution: {integrity: sha512-p5N9uoTH76lLvSAaYSZtBCdEXzpOOufsRjnhjVSrZGXikVGHX9+cc9ERtHRV4hvBKHyZb1bg4K+56Bd2TqUn4A==} dependencies: @@ -10664,6 +10683,17 @@ packages: dependencies: ms: 2.0.0 + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: false + /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -12389,7 +12419,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: safer-buffer: 2.1.2 - dev: true /iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} @@ -13113,7 +13142,6 @@ packages: /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true /lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} @@ -14199,6 +14227,18 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true + /needle@2.9.1: + resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==} + engines: {node: '>= 4.4.x'} + hasBin: true + dependencies: + debug: 3.2.7 + iconv-lite: 0.4.24 + sax: 1.2.4 + transitivePeerDependencies: + - supports-color + dev: false + /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -15271,6 +15311,16 @@ packages: engines: {node: '>=6'} dev: false + /probe-image-size@7.2.3: + resolution: {integrity: sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==} + dependencies: + lodash.merge: 4.6.2 + needle: 2.9.1 + stream-parser: 0.3.1 + transitivePeerDependencies: + - supports-color + dev: false + /progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -15905,7 +15955,6 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: true /sass-formatter@0.7.7: resolution: {integrity: sha512-axtQ7c7Cf4UgHsD8e4okhIkkc90+tdgBIfUMx69+qJuMNq9EOo2k+RH/mDKj0XeA5z3nC1Ca5TCntuxRhI+1MA==} @@ -16325,6 +16374,14 @@ packages: engines: {node: '>=4', npm: '>=6'} dev: true + /stream-parser@0.3.1: + resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==} + dependencies: + debug: 2.6.9 + transitivePeerDependencies: + - supports-color + dev: false + /stream-transform@2.1.3: resolution: {integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==} dependencies: From b384cf4f7d3d257a560c1c8ffbf35d3b96a21d46 Mon Sep 17 00:00:00 2001 From: Princesseuh Date: Mon, 11 Sep 2023 17:30:40 +0000 Subject: [PATCH 11/33] [ci] format --- packages/astro/src/assets/utils/metadata.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/assets/utils/metadata.ts b/packages/astro/src/assets/utils/metadata.ts index c4e96f800984..7d7ee74573ba 100644 --- a/packages/astro/src/assets/utils/metadata.ts +++ b/packages/astro/src/assets/utils/metadata.ts @@ -1,5 +1,5 @@ -import type { ImageInputFormat, ImageMetadata } from '../types.js'; import probe from 'probe-image-size'; +import type { ImageInputFormat, ImageMetadata } from '../types.js'; export async function imageMetadata(data: Buffer): Promise | undefined> { const result = probe.sync(data); From 2c9614469674509b3e3bc21a4471a1aeb9b4141f Mon Sep 17 00:00:00 2001 From: Alexander Niebuhr Date: Mon, 11 Sep 2023 20:04:44 +0200 Subject: [PATCH 12/33] feat(@astrojs/cloudflare): add runtime support to `astro dev` (#8426) * add necessary libs * cleanup stale code * add base feature-set of runtime to `astro dev` * fix lockfile * remove future code Co-authored-by: Arsh <69170106+lilnasy@users.noreply.github.com> * remove future code Co-authored-by: Arsh <69170106+lilnasy@users.noreply.github.com> * remove future code Co-authored-by: Arsh <69170106+lilnasy@users.noreply.github.com> * remove future code Co-authored-by: Arsh <69170106+lilnasy@users.noreply.github.com> * remove future code Co-authored-by: Arsh <69170106+lilnasy@users.noreply.github.com> * address review comments * fix linting issue * add docs & tests * fix test paths * add changeset * update README.md Co-authored-by: Sarah Rainsberger * fix docs & make adapter options optional * fix package resolve mode * fix pnpm-lock --------- Co-authored-by: Arsh <69170106+lilnasy@users.noreply.github.com> Co-authored-by: Sarah Rainsberger --- .changeset/smart-dragons-taste.md | 5 + packages/integrations/cloudflare/README.md | 26 ++- packages/integrations/cloudflare/package.json | 8 +- packages/integrations/cloudflare/runtime.d.ts | 3 - packages/integrations/cloudflare/src/index.ts | 149 +++++++++++++- .../integrations/cloudflare/src/parser.ts | 134 +++++++++++++ .../cloudflare/src/server.advanced.ts | 13 -- .../cloudflare/src/server.directory.ts | 15 +- .../integrations/cloudflare/test/cf.test.js | 38 +++- .../cloudflare/test/fixtures/cf/.dev.vars | 1 + .../test/fixtures/cf/astro.config.mjs | 8 - .../cloudflare/test/fixtures/cf/wrangler.toml | 4 + pnpm-lock.yaml | 183 +++++++++++++++++- 13 files changed, 535 insertions(+), 52 deletions(-) create mode 100644 .changeset/smart-dragons-taste.md delete mode 100644 packages/integrations/cloudflare/runtime.d.ts create mode 100644 packages/integrations/cloudflare/src/parser.ts create mode 100644 packages/integrations/cloudflare/test/fixtures/cf/.dev.vars delete mode 100644 packages/integrations/cloudflare/test/fixtures/cf/astro.config.mjs create mode 100644 packages/integrations/cloudflare/test/fixtures/cf/wrangler.toml diff --git a/.changeset/smart-dragons-taste.md b/.changeset/smart-dragons-taste.md new file mode 100644 index 000000000000..3b79d9410723 --- /dev/null +++ b/.changeset/smart-dragons-taste.md @@ -0,0 +1,5 @@ +--- +'@astrojs/cloudflare': minor +--- + +Add support for Cloudflare Runtime (env vars, caches and req object), using `astro dev` diff --git a/packages/integrations/cloudflare/README.md b/packages/integrations/cloudflare/README.md index f49824cd9364..b627b44d9fc7 100644 --- a/packages/integrations/cloudflare/README.md +++ b/packages/integrations/cloudflare/README.md @@ -142,7 +142,7 @@ declare namespace App { } ``` -## Environment Variables +### Environment Variables See Cloudflare's documentation for [working with environment variables](https://developers.cloudflare.com/pages/platform/functions/bindings/#environment-variables). @@ -159,6 +159,30 @@ export function GET({ params }) { } ``` +### `cloudflare.runtime` + +`runtime: "off" | "local" | "remote"` +default `"off"` + +This optional flag enables the Astro dev server to populate environment variables and the Cloudflare Request Object, avoiding the need for Wrangler. + +- `local`: environment variables are available, but the request object is populated from a static placeholder value. +- `remote`: environment variables and the live, fetched request object are available. +- `off`: the Astro dev server will populate neither environment variables nor the request object. Use Wrangler to access Cloudflare bindings and environment variables. + +```js +// astro.config.mjs +import { defineConfig } from 'astro/config'; +import cloudflare from '@astrojs/cloudflare'; + +export default defineConfig({ + output: 'server', + adapter: cloudflare({ + runtime: 'off' | 'local' | 'remote', + }), +}); +``` + ## Headers, Redirects and function invocation routes Cloudflare has support for adding custom [headers](https://developers.cloudflare.com/pages/platform/headers/), configuring static [redirects](https://developers.cloudflare.com/pages/platform/redirects/) and defining which routes should [invoke functions](https://developers.cloudflare.com/pages/platform/functions/routing/#function-invocation-routes). Cloudflare looks for `_headers`, `_redirects`, and `_routes.json` files in your build output directory to configure these features. This means they should be placed in your Astro project’s `public/` directory. diff --git a/packages/integrations/cloudflare/package.json b/packages/integrations/cloudflare/package.json index e00078a65835..79fb83bb7d29 100644 --- a/packages/integrations/cloudflare/package.json +++ b/packages/integrations/cloudflare/package.json @@ -42,7 +42,13 @@ "@astrojs/underscore-redirects": "workspace:*", "@cloudflare/workers-types": "^4.20230821.0", "esbuild": "^0.19.2", - "tiny-glob": "^0.2.9" + "tiny-glob": "^0.2.9", + "find-up": "^6.3.0", + "@iarna/toml": "^2.2.5", + "dotenv": "^16.3.1", + "@miniflare/cache": "^2.14.1", + "@miniflare/shared": "^2.14.1", + "@miniflare/storage-memory": "^2.14.1" }, "peerDependencies": { "astro": "workspace:^3.0.12" diff --git a/packages/integrations/cloudflare/runtime.d.ts b/packages/integrations/cloudflare/runtime.d.ts deleted file mode 100644 index e2a72940a0c7..000000000000 --- a/packages/integrations/cloudflare/runtime.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type { WorkerRuntime, PagesRuntime } from './dist/runtime'; - -export { getRuntime } from './dist/runtime'; diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index 718b1efa8648..c70c9c5aab1f 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -1,18 +1,31 @@ -import { createRedirectsFromAstroRoutes } from '@astrojs/underscore-redirects'; +import type { IncomingRequestCfProperties } from '@cloudflare/workers-types/experimental'; import type { AstroAdapter, AstroConfig, AstroIntegration, RouteData } from 'astro'; + +import { createRedirectsFromAstroRoutes } from '@astrojs/underscore-redirects'; +import { CacheStorage } from '@miniflare/cache'; +import { NoOpLog } from '@miniflare/shared'; +import { MemoryStorage } from '@miniflare/storage-memory'; +import { AstroError } from 'astro/errors'; import esbuild from 'esbuild'; import * as fs from 'node:fs'; import * as os from 'node:os'; import { sep } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import glob from 'tiny-glob'; +import { getEnvVars } from './parser.js'; export type { AdvancedRuntime } from './server.advanced'; export type { DirectoryRuntime } from './server.directory'; type Options = { - mode: 'directory' | 'advanced'; + mode?: 'directory' | 'advanced'; functionPerRoute?: boolean; + /** + * 'off': current behaviour (wrangler is needed) + * 'local': use a static req.cf object, and env vars defined in wrangler.toml & .dev.vars (astro dev is enough) + * 'remote': use a dynamic real-live req.cf object, and env vars defined in wrangler.toml & .dev.vars (astro dev is enough) + */ + runtime?: 'off' | 'local' | 'remote'; }; interface BuildConfig { @@ -22,6 +35,17 @@ interface BuildConfig { split?: boolean; } +class StorageFactory { + storages = new Map(); + + storage(namespace: string) { + let storage = this.storages.get(namespace); + if (storage) return storage; + this.storages.set(namespace, (storage = new MemoryStorage())); + return storage; + } +} + export function getAdapter({ isModeDirectory, functionPerRoute, @@ -66,6 +90,73 @@ export function getAdapter({ }; } +async function getCFObject(runtimeMode: string): Promise { + const CF_ENDPOINT = 'https://workers.cloudflare.com/cf.json'; + const CF_FALLBACK: IncomingRequestCfProperties = { + asOrganization: '', + asn: 395747, + colo: 'DFW', + city: 'Austin', + region: 'Texas', + regionCode: 'TX', + metroCode: '635', + postalCode: '78701', + country: 'US', + continent: 'NA', + timezone: 'America/Chicago', + latitude: '30.27130', + longitude: '-97.74260', + clientTcpRtt: 0, + httpProtocol: 'HTTP/1.1', + requestPriority: 'weight=192;exclusive=0', + tlsCipher: 'AEAD-AES128-GCM-SHA256', + tlsVersion: 'TLSv1.3', + tlsClientAuth: { + certPresented: '0', + certVerified: 'NONE', + certRevoked: '0', + certIssuerDN: '', + certSubjectDN: '', + certIssuerDNRFC2253: '', + certSubjectDNRFC2253: '', + certIssuerDNLegacy: '', + certSubjectDNLegacy: '', + certSerial: '', + certIssuerSerial: '', + certSKI: '', + certIssuerSKI: '', + certFingerprintSHA1: '', + certFingerprintSHA256: '', + certNotBefore: '', + certNotAfter: '', + }, + edgeRequestKeepAliveStatus: 0, + hostMetadata: undefined, + clientTrustScore: 99, + botManagement: { + corporateProxy: false, + verifiedBot: false, + ja3Hash: '25b4882c2bcb50cd6b469ff28c596742', + staticResource: false, + detectionIds: [], + score: 99, + }, + }; + + if (runtimeMode === 'local') { + return CF_FALLBACK; + } else if (runtimeMode === 'remote') { + try { + const res = await fetch(CF_ENDPOINT); + const cfText = await res.text(); + const storedCf = JSON.parse(cfText); + return storedCf; + } catch (e: any) { + return CF_FALLBACK; + } + } +} + const SHIM = `globalThis.process = { argv: [], env: {}, @@ -85,6 +176,7 @@ export default function createIntegration(args?: Options): AstroIntegration { const isModeDirectory = args?.mode === 'directory'; const functionPerRoute = args?.functionPerRoute ?? false; + const runtimeMode = args?.runtime ?? 'off'; return { name: '@astrojs/cloudflare', @@ -105,15 +197,56 @@ export default function createIntegration(args?: Options): AstroIntegration { _buildConfig = config.build; if (config.output === 'static') { - throw new Error(` - [@astrojs/cloudflare] \`output: "server"\` or \`output: "hybrid"\` is required to use this adapter. Otherwise, this adapter is not necessary to deploy a static site to Cloudflare. - -`); + throw new AstroError( + '[@astrojs/cloudflare] `output: "server"` or `output: "hybrid"` is required to use this adapter. Otherwise, this adapter is not necessary to deploy a static site to Cloudflare.' + ); } if (config.base === SERVER_BUILD_FOLDER) { - throw new Error(` - [@astrojs/cloudflare] \`base: "${SERVER_BUILD_FOLDER}"\` is not allowed. Please change your \`base\` config to something else.`); + throw new AstroError( + '[@astrojs/cloudflare] `base: "${SERVER_BUILD_FOLDER}"` is not allowed. Please change your `base` config to something else.' + ); + } + }, + 'astro:server:setup': ({ server }) => { + if (runtimeMode !== 'off') { + server.middlewares.use(async function middleware(req, res, next) { + try { + const cf = await getCFObject(runtimeMode); + const vars = await getEnvVars(); + + const clientLocalsSymbol = Symbol.for('astro.locals'); + Reflect.set(req, clientLocalsSymbol, { + runtime: { + env: { + // default binding for static assets will be dynamic once we support mocking of bindings + ASSETS: {}, + // this is just a VAR for CF to change build behavior, on dev it should be 0 + CF_PAGES: '0', + // will be fetched from git dynamically once we support mocking of bindings + CF_PAGES_BRANCH: 'TBA', + // will be fetched from git dynamically once we support mocking of bindings + CF_PAGES_COMMIT_SHA: 'TBA', + CF_PAGES_URL: `http://${req.headers.host}`, + ...vars, + }, + cf: cf, + waitUntil: (_promise: Promise) => { + return; + }, + caches: new CacheStorage( + { cache: true, cachePersist: false }, + new NoOpLog(), + new StorageFactory(), + {} + ), + }, + }); + next(); + } catch { + next(); + } + }); } }, 'astro:build:setup': ({ vite, target }) => { diff --git a/packages/integrations/cloudflare/src/parser.ts b/packages/integrations/cloudflare/src/parser.ts new file mode 100644 index 000000000000..d7130ff9d99b --- /dev/null +++ b/packages/integrations/cloudflare/src/parser.ts @@ -0,0 +1,134 @@ +/** + * This file is a derivative work of wrangler by Cloudflare + * An upstream request for exposing this API was made here: + * https://github.com/cloudflare/workers-sdk/issues/3897 + * + * Until further notice, we will be using this file as a workaround + * TODO: Tackle this file, once their is an decision on the upstream request + */ + +import * as fs from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { findUpSync } from 'find-up'; +import TOML from '@iarna/toml'; +import dotenv from 'dotenv'; + +function findWranglerToml( + referencePath: string = process.cwd(), + preferJson = false +): string | undefined { + if (preferJson) { + return ( + findUpSync(`wrangler.json`, { cwd: referencePath }) ?? + findUpSync(`wrangler.toml`, { cwd: referencePath }) + ); + } + return findUpSync(`wrangler.toml`, { cwd: referencePath }); +} +type File = { + file?: string; + fileText?: string; +}; +type Location = File & { + line: number; + column: number; + length?: number; + lineText?: string; + suggestion?: string; +}; +type Message = { + text: string; + location?: Location; + notes?: Message[]; + kind?: 'warning' | 'error'; +}; +class ParseError extends Error implements Message { + readonly text: string; + readonly notes: Message[]; + readonly location?: Location; + readonly kind: 'warning' | 'error'; + + constructor({ text, notes, location, kind }: Message) { + super(text); + this.name = this.constructor.name; + this.text = text; + this.notes = notes ?? []; + this.location = location; + this.kind = kind ?? 'error'; + } +} +const TOML_ERROR_NAME = 'TomlError'; +const TOML_ERROR_SUFFIX = ' at row '; +type TomlError = Error & { + line: number; + col: number; +}; +function parseTOML(input: string, file?: string): TOML.JsonMap | never { + try { + // Normalize CRLF to LF to avoid hitting https://github.com/iarna/iarna-toml/issues/33. + const normalizedInput = input.replace(/\r\n/g, '\n'); + return TOML.parse(normalizedInput); + } catch (err) { + const { name, message, line, col } = err as TomlError; + if (name !== TOML_ERROR_NAME) { + throw err; + } + const text = message.substring(0, message.lastIndexOf(TOML_ERROR_SUFFIX)); + const lineText = input.split('\n')[line]; + const location = { + lineText, + line: line + 1, + column: col - 1, + file, + fileText: input, + }; + throw new ParseError({ text, location }); + } +} + +export interface DotEnv { + path: string; + parsed: dotenv.DotenvParseOutput; +} +function tryLoadDotEnv(path: string): DotEnv | undefined { + try { + const parsed = dotenv.parse(fs.readFileSync(path)); + return { path, parsed }; + } catch (e) { + // logger.debug(`Failed to load .env file "${path}":`, e); + } +} +/** + * Loads a dotenv file from , preferring to read . if + * is defined and that file exists. + */ + +export function loadDotEnv(path: string): DotEnv | undefined { + return tryLoadDotEnv(path); +} +function getVarsForDev(config: any, configPath: string | undefined): any { + const configDir = resolve(dirname(configPath ?? '.')); + const devVarsPath = resolve(configDir, '.dev.vars'); + const loaded = loadDotEnv(devVarsPath); + if (loaded !== undefined) { + return { + ...config.vars, + ...loaded.parsed, + }; + } else { + return config.vars; + } +} +export async function getEnvVars() { + let rawConfig; + const configPath = findWranglerToml(process.cwd(), false); // false = args.experimentalJsonConfig + if (!configPath) { + throw new Error('Could not find wrangler.toml'); + } + // Load the configuration from disk if available + if (configPath?.endsWith('toml')) { + rawConfig = parseTOML(fs.readFileSync(configPath).toString(), configPath); + } + const vars = getVarsForDev(rawConfig, configPath); + return vars; +} diff --git a/packages/integrations/cloudflare/src/server.advanced.ts b/packages/integrations/cloudflare/src/server.advanced.ts index 24358a5e07d6..6e305b1b96cc 100644 --- a/packages/integrations/cloudflare/src/server.advanced.ts +++ b/packages/integrations/cloudflare/src/server.advanced.ts @@ -44,19 +44,6 @@ export function createExports(manifest: SSRManifest) { request.headers.get('cf-connecting-ip') ); - // `getRuntime()` is deprecated, currently available additionally to new Astro.locals.runtime - // TODO: remove `getRuntime()` in Astro 3.0 - Reflect.set(request, Symbol.for('runtime'), { - env, - name: 'cloudflare', - caches, - cf: request.cf, - ...context, - waitUntil: (promise: Promise) => { - context.waitUntil(promise); - }, - }); - const locals: AdvancedRuntime = { runtime: { waitUntil: (promise: Promise) => { diff --git a/packages/integrations/cloudflare/src/server.directory.ts b/packages/integrations/cloudflare/src/server.directory.ts index 64d820d999a7..48c97392cc03 100644 --- a/packages/integrations/cloudflare/src/server.directory.ts +++ b/packages/integrations/cloudflare/src/server.directory.ts @@ -21,7 +21,7 @@ export function createExports(manifest: SSRManifest) { const onRequest = async (context: EventContext) => { const request = context.request as CFRequest & Request; - const { next, env } = context; + const { env } = context; // TODO: remove this any cast in the future // REF: the type cast to any is needed because the Cloudflare Env Type is not assignable to type 'ProcessEnv' @@ -41,19 +41,6 @@ export function createExports(manifest: SSRManifest) { request.headers.get('cf-connecting-ip') ); - // `getRuntime()` is deprecated, currently available additionally to new Astro.locals.runtime - // TODO: remove `getRuntime()` in Astro 3.0 - Reflect.set(request, Symbol.for('runtime'), { - ...context, - waitUntil: (promise: Promise) => { - context.waitUntil(promise); - }, - name: 'cloudflare', - next, - caches, - cf: request.cf, - }); - const locals: DirectoryRuntime = { runtime: { waitUntil: (promise: Promise) => { diff --git a/packages/integrations/cloudflare/test/cf.test.js b/packages/integrations/cloudflare/test/cf.test.js index 64c406d12f26..53b1bbf2c3d8 100644 --- a/packages/integrations/cloudflare/test/cf.test.js +++ b/packages/integrations/cloudflare/test/cf.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import * as cheerio from 'cheerio'; import cloudflare from '../dist/index.js'; -describe('Cf metadata and caches', () => { +describe('Wrangler Cloudflare Runtime', () => { /** @type {import('./test-utils').Fixture} */ let fixture; /** @type {import('./test-utils').WranglerCLI} */ @@ -39,3 +39,39 @@ describe('Cf metadata and caches', () => { expect($('#hasCache').text()).to.equal('true'); }); }); + +describe('Astro Cloudflare Runtime', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + let devServer; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/cf/', + output: 'server', + adapter: cloudflare({ + runtime: 'local', + }), + image: { + service: { + entrypoint: 'astro/assets/services/noop', + }, + }, + }); + process.chdir('./test/fixtures/cf'); + devServer = await fixture.startDevServer(); + }); + + after(async () => { + await devServer.stop(); + }); + + it('Populates CF, Vars & Bindings', async () => { + let res = await fixture.fetch('/'); + expect(res.status).to.equal(200); + let html = await res.text(); + let $ = cheerio.load(html); + expect($('#hasRuntime').text()).to.equal('true'); + expect($('#hasCache').text()).to.equal('true'); + }); +}); diff --git a/packages/integrations/cloudflare/test/fixtures/cf/.dev.vars b/packages/integrations/cloudflare/test/fixtures/cf/.dev.vars new file mode 100644 index 000000000000..9296c384b23f --- /dev/null +++ b/packages/integrations/cloudflare/test/fixtures/cf/.dev.vars @@ -0,0 +1 @@ +DATABASE_URL="postgresql://lorem" diff --git a/packages/integrations/cloudflare/test/fixtures/cf/astro.config.mjs b/packages/integrations/cloudflare/test/fixtures/cf/astro.config.mjs deleted file mode 100644 index f92829843d3c..000000000000 --- a/packages/integrations/cloudflare/test/fixtures/cf/astro.config.mjs +++ /dev/null @@ -1,8 +0,0 @@ -import { defineConfig } from 'astro/config'; -import cloudflare from '@astrojs/cloudflare'; - - -export default defineConfig({ - adapter: cloudflare(), - output: 'server', -}); diff --git a/packages/integrations/cloudflare/test/fixtures/cf/wrangler.toml b/packages/integrations/cloudflare/test/fixtures/cf/wrangler.toml new file mode 100644 index 000000000000..ba0fa64c4b09 --- /dev/null +++ b/packages/integrations/cloudflare/test/fixtures/cf/wrangler.toml @@ -0,0 +1,4 @@ +name = "test" + +[vars] +COOL = "ME" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f70352874e9c..f062f95f5788 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3628,9 +3628,27 @@ importers: '@cloudflare/workers-types': specifier: ^4.20230821.0 version: 4.20230821.0 + '@iarna/toml': + specifier: ^2.2.5 + version: 2.2.5 + '@miniflare/cache': + specifier: ^2.14.1 + version: 2.14.1 + '@miniflare/shared': + specifier: ^2.14.1 + version: 2.14.1 + '@miniflare/storage-memory': + specifier: ^2.14.1 + version: 2.14.1 + dotenv: + specifier: ^16.3.1 + version: 16.3.1 esbuild: specifier: ^0.19.2 version: 0.19.2 + find-up: + specifier: ^6.3.0 + version: 6.3.0 tiny-glob: specifier: ^0.2.9 version: 0.2.9 @@ -8013,6 +8031,10 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true + /@iarna/toml@2.2.5: + resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} + dev: false + /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -8179,6 +8201,63 @@ packages: - supports-color dev: false + /@miniflare/cache@2.14.1: + resolution: {integrity: sha512-f/o6UBV6UX+MlhjcEch73/wjQvvNo37dgYmP6Pn2ax1/mEHhJ7allNAqenmonT4djNeyB3eEYV3zUl54wCEwrg==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.14.1 + '@miniflare/shared': 2.14.1 + http-cache-semantics: 4.1.1 + undici: 5.20.0 + dev: false + + /@miniflare/core@2.14.1: + resolution: {integrity: sha512-d+SGAda/VoXq+SKz04oq8ATUwQw5755L87fgPR8pTdR2YbWkxdbmEm1z2olOpDiUjcR86aN6NtCjY6tUC7fqaw==} + engines: {node: '>=16.13'} + dependencies: + '@iarna/toml': 2.2.5 + '@miniflare/queues': 2.14.1 + '@miniflare/shared': 2.14.1 + '@miniflare/watcher': 2.14.1 + busboy: 1.6.0 + dotenv: 10.0.0 + kleur: 4.1.5 + set-cookie-parser: 2.6.0 + undici: 5.20.0 + urlpattern-polyfill: 4.0.3 + dev: false + + /@miniflare/queues@2.14.1: + resolution: {integrity: sha512-uBzrbBkIgtNoztDpmMMISg/brYtxLHRE7oTaN8OVnq3bG+3nF9kQC42HUz+Vg+sf65UlvhSaqkjllgx+fNtOxQ==} + engines: {node: '>=16.7'} + dependencies: + '@miniflare/shared': 2.14.1 + dev: false + + /@miniflare/shared@2.14.1: + resolution: {integrity: sha512-73GnLtWn5iP936ctE6ZJrMqGu134KOoIIveq5Yd/B+NnbFfzpuzjCpkLrnqjkDdsxDbruXSb5eTR/SmAdpJxZQ==} + engines: {node: '>=16.13'} + dependencies: + '@types/better-sqlite3': 7.6.4 + kleur: 4.1.5 + npx-import: 1.1.4 + picomatch: 2.3.1 + dev: false + + /@miniflare/storage-memory@2.14.1: + resolution: {integrity: sha512-lfQbQwopVWd4W5XzrYdp0rhk3dJpvSmv1Wwn9RhNO20WrcuoxpdSzbmpBahsgYVg+OheVaEbS6RpFqdmwwLTog==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.14.1 + dev: false + + /@miniflare/watcher@2.14.1: + resolution: {integrity: sha512-dkFvetm5wk6pwunlYb/UkI0yFNb3otLpRm5RDywMUzqObEf+rCiNNAbJe3HUspr2ncZVAaRWcEaDh82vYK5cmw==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.14.1 + dev: false + /@nanostores/preact@0.5.0(nanostores@0.9.3)(preact@10.17.1): resolution: {integrity: sha512-Zq5DEAY+kIfwJ1NPd43D1mpsbISuiD6N/SuTHrt/8jUoifLwXaReaZMAnvkvbIGOgcB1Hy++A9jZix2taNNYxQ==} engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} @@ -8645,6 +8724,12 @@ packages: dependencies: '@babel/types': 7.22.10 + /@types/better-sqlite3@7.6.4: + resolution: {integrity: sha512-dzrRZCYPXIXfSR1/surNbJ/grU3scTaygS0OMzjlGf71i9sc2fGyHPXXiXmEvNIoE0cGwsanEFMVJxPXmco9Eg==} + dependencies: + '@types/node': 18.17.8 + dev: false + /@types/canvas-confetti@1.6.0: resolution: {integrity: sha512-Yq6rIccwcco0TLD5SMUrIM7Fk7Fe/C0jmNRxJJCLtAF6gebDkPuUjK5EHedxecm69Pi/aA+It39Ux4OHmFhjRw==} dev: false @@ -10033,6 +10118,12 @@ packages: engines: {node: '>=6'} dev: false + /builtins@5.0.1: + resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} + dependencies: + semver: 7.5.4 + dev: false + /bundle-name@3.0.0: resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} engines: {node: '>=12'} @@ -10938,6 +11029,16 @@ packages: domelementtype: 2.3.0 domhandler: 5.0.3 + /dotenv@10.0.0: + resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==} + engines: {node: '>=10'} + dev: false + + /dotenv@16.3.1: + resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + engines: {node: '>=12'} + dev: false + /dotenv@8.6.0: resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} engines: {node: '>=10'} @@ -11479,6 +11580,21 @@ packages: strip-final-newline: 2.0.0 dev: true + /execa@6.1.0: + resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 3.0.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: false + /execa@7.2.0: resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} @@ -11686,6 +11802,14 @@ packages: locate-path: 6.0.0 path-exists: 4.0.0 + /find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + dev: false + /find-yarn-workspace-root2@1.2.16: resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} dependencies: @@ -11872,7 +11996,6 @@ packages: /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - dev: true /get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} @@ -12393,6 +12516,11 @@ packages: engines: {node: '>=10.17.0'} dev: true + /human-signals@3.0.1: + resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==} + engines: {node: '>=12.20.0'} + dev: false + /human-signals@4.3.1: resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} engines: {node: '>=14.18.0'} @@ -13124,6 +13252,13 @@ packages: dependencies: p-locate: 5.0.0 + /locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-locate: 6.0.0 + dev: false + /lodash.chunk@4.2.0: resolution: {integrity: sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w==} dev: false @@ -13207,7 +13342,7 @@ packages: /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: - tslib: 2.5.3 + tslib: 2.6.2 dev: false /lru-cache@4.1.5: @@ -14426,6 +14561,15 @@ packages: set-blocking: 2.0.0 dev: false + /npx-import@1.1.4: + resolution: {integrity: sha512-3ShymTWOgqGyNlh5lMJAejLuIv3W1K3fbI5Ewc6YErZU3Sp0PqsNs8UIU1O8z5+KVl/Du5ag56Gza9vdorGEoA==} + dependencies: + execa: 6.1.0 + parse-package-name: 1.0.0 + semver: 7.5.4 + validate-npm-package-name: 4.0.0 + dev: false + /nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} dependencies: @@ -14589,6 +14733,13 @@ packages: dependencies: p-limit: 3.1.0 + /p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-limit: 4.0.0 + dev: false + /p-map@2.1.0: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} @@ -14664,6 +14815,10 @@ packages: resolution: {integrity: sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==} dev: true + /parse-package-name@1.0.0: + resolution: {integrity: sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==} + dev: false + /parse5-htmlparser2-tree-adapter@7.0.0: resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} dependencies: @@ -14700,6 +14855,11 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + /path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: false + /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -16966,7 +17126,6 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - dev: true /tty-table@4.2.1: resolution: {integrity: sha512-xz0uKo+KakCQ+Dxj1D/tKn2FSyreSYWzdkL/BYhgN6oMW808g8QRMuh1atAV9fjTPbWBjfbkKQpI/5rEcnAc7g==} @@ -17188,6 +17347,13 @@ packages: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + /undici@5.20.0: + resolution: {integrity: sha512-J3j60dYzuo6Eevbawwp1sdg16k5Tf768bxYK4TUJRH7cBM4kFCbf3mOnM/0E3vQYXvpxITbbWmBafaDbxLDz3g==} + engines: {node: '>=12.18'} + dependencies: + busboy: 1.6.0 + dev: false + /undici@5.23.0: resolution: {integrity: sha512-1D7w+fvRsqlQ9GscLBwcAJinqcZGHUKjbOmXdlE/v8BvEGXjeWAax+341q44EuTcHXXnfyKNbKRq4Lg7OzhMmg==} engines: {node: '>=14.0'} @@ -17430,6 +17596,10 @@ packages: requires-port: 1.0.0 dev: true + /urlpattern-polyfill@4.0.3: + resolution: {integrity: sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ==} + dev: false + /urlpattern-polyfill@8.0.2: resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==} dev: false @@ -17472,6 +17642,13 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /validate-npm-package-name@4.0.0: + resolution: {integrity: sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + builtins: 5.0.1 + dev: false + /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} From fdf9b66f9bec03e9c93f0ead9befd4a2da2d946a Mon Sep 17 00:00:00 2001 From: lilnasy Date: Mon, 11 Sep 2023 18:07:37 +0000 Subject: [PATCH 13/33] [ci] format --- packages/integrations/cloudflare/README.md | 4 ++-- packages/integrations/cloudflare/src/parser.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/integrations/cloudflare/README.md b/packages/integrations/cloudflare/README.md index b627b44d9fc7..8d8b3c041576 100644 --- a/packages/integrations/cloudflare/README.md +++ b/packages/integrations/cloudflare/README.md @@ -159,14 +159,14 @@ export function GET({ params }) { } ``` -### `cloudflare.runtime` +### `cloudflare.runtime` `runtime: "off" | "local" | "remote"` default `"off"` This optional flag enables the Astro dev server to populate environment variables and the Cloudflare Request Object, avoiding the need for Wrangler. -- `local`: environment variables are available, but the request object is populated from a static placeholder value. +- `local`: environment variables are available, but the request object is populated from a static placeholder value. - `remote`: environment variables and the live, fetched request object are available. - `off`: the Astro dev server will populate neither environment variables nor the request object. Use Wrangler to access Cloudflare bindings and environment variables. diff --git a/packages/integrations/cloudflare/src/parser.ts b/packages/integrations/cloudflare/src/parser.ts index d7130ff9d99b..e9a9cdd00a1b 100644 --- a/packages/integrations/cloudflare/src/parser.ts +++ b/packages/integrations/cloudflare/src/parser.ts @@ -7,11 +7,11 @@ * TODO: Tackle this file, once their is an decision on the upstream request */ -import * as fs from 'node:fs'; -import { resolve, dirname } from 'node:path'; -import { findUpSync } from 'find-up'; import TOML from '@iarna/toml'; import dotenv from 'dotenv'; +import { findUpSync } from 'find-up'; +import * as fs from 'node:fs'; +import { dirname, resolve } from 'node:path'; function findWranglerToml( referencePath: string = process.cwd(), From f41157e955b44bd12e9c59eebfe0cd5ef10d435a Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:20:55 +0200 Subject: [PATCH 14/33] chore: do a minor for avif support (#8508) --- .changeset/five-doors-love.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/five-doors-love.md b/.changeset/five-doors-love.md index 2569f54b3df2..74877aa68429 100644 --- a/.changeset/five-doors-love.md +++ b/.changeset/five-doors-love.md @@ -1,5 +1,5 @@ --- -'astro': patch +'astro': minor --- Support AVIF input assets From cda7d80ac5c2b7fdc2763a89a1d3663df256c0cf Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:29:36 +0200 Subject: [PATCH 15/33] chore: revert avif change (#8509) --- .changeset/five-doors-love.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/five-doors-love.md b/.changeset/five-doors-love.md index 74877aa68429..2569f54b3df2 100644 --- a/.changeset/five-doors-love.md +++ b/.changeset/five-doors-love.md @@ -1,5 +1,5 @@ --- -'astro': minor +'astro': patch --- Support AVIF input assets From bf341d6762b1907d22b0d56b1df114585a180ad0 Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Mon, 11 Sep 2023 23:00:43 +0200 Subject: [PATCH 16/33] revert: avif support (#8511) --- .changeset/five-doors-love.md | 5 - packages/astro/client.d.ts | 4 - packages/astro/package.json | 2 - packages/astro/src/assets/consts.ts | 10 +- packages/astro/src/assets/utils/metadata.ts | 9 +- packages/astro/src/assets/vendor/README.md | 3 + .../src/assets/vendor/image-size/LICENSE | 9 + .../src/assets/vendor/image-size/detector.ts | 30 +++ .../src/assets/vendor/image-size/index.ts | 146 ++++++++++++ .../src/assets/vendor/image-size/readUInt.ts | 10 + .../src/assets/vendor/image-size/types.ts | 38 +++ .../src/assets/vendor/image-size/types/bmp.ts | 14 ++ .../src/assets/vendor/image-size/types/cur.ts | 16 ++ .../src/assets/vendor/image-size/types/dds.ts | 14 ++ .../src/assets/vendor/image-size/types/gif.ts | 16 ++ .../assets/vendor/image-size/types/icns.ts | 113 +++++++++ .../src/assets/vendor/image-size/types/ico.ts | 76 ++++++ .../vendor/image-size/types/interface.ts | 15 ++ .../src/assets/vendor/image-size/types/j2c.ts | 15 ++ .../src/assets/vendor/image-size/types/jp2.ts | 61 +++++ .../src/assets/vendor/image-size/types/jpg.ts | 151 ++++++++++++ .../src/assets/vendor/image-size/types/ktx.ts | 16 ++ .../src/assets/vendor/image-size/types/png.ts | 36 +++ .../src/assets/vendor/image-size/types/pnm.ts | 80 +++++++ .../src/assets/vendor/image-size/types/psd.ts | 14 ++ .../src/assets/vendor/image-size/types/svg.ts | 106 +++++++++ .../assets/vendor/image-size/types/tiff.ts | 115 +++++++++ .../assets/vendor/image-size/types/webp.ts | 65 +++++ .../astro/src/assets/vendor/queue/LICENSE | 8 + .../astro/src/assets/vendor/queue/queue.js | 225 ++++++++++++++++++ .../astro/src/assets/vite-plugin-assets.ts | 2 +- packages/astro/test/core-image.test.js | 16 -- .../core-image/src/assets/light_walrus.avif | Bin 19439 -> 0 bytes .../fixtures/core-image/src/pages/avif.astro | 5 - pnpm-lock.yaml | 63 +---- 35 files changed, 1406 insertions(+), 102 deletions(-) delete mode 100644 .changeset/five-doors-love.md create mode 100644 packages/astro/src/assets/vendor/README.md create mode 100644 packages/astro/src/assets/vendor/image-size/LICENSE create mode 100644 packages/astro/src/assets/vendor/image-size/detector.ts create mode 100644 packages/astro/src/assets/vendor/image-size/index.ts create mode 100644 packages/astro/src/assets/vendor/image-size/readUInt.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/bmp.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/cur.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/dds.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/gif.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/icns.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/ico.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/interface.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/j2c.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/jp2.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/jpg.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/ktx.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/png.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/pnm.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/psd.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/svg.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/tiff.ts create mode 100644 packages/astro/src/assets/vendor/image-size/types/webp.ts create mode 100644 packages/astro/src/assets/vendor/queue/LICENSE create mode 100644 packages/astro/src/assets/vendor/queue/queue.js delete mode 100644 packages/astro/test/fixtures/core-image/src/assets/light_walrus.avif delete mode 100644 packages/astro/test/fixtures/core-image/src/pages/avif.astro diff --git a/.changeset/five-doors-love.md b/.changeset/five-doors-love.md deleted file mode 100644 index 2569f54b3df2..000000000000 --- a/.changeset/five-doors-love.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'astro': patch ---- - -Support AVIF input assets diff --git a/packages/astro/client.d.ts b/packages/astro/client.d.ts index 1c86d26351e4..90f06c72d067 100644 --- a/packages/astro/client.d.ts +++ b/packages/astro/client.d.ts @@ -108,10 +108,6 @@ declare module '*.svg' { const metadata: ImageMetadata; export default metadata; } -declare module '*.avif' { - const metadata: ImageMetadata; - export default metadata; -} declare module 'astro:transitions' { type TransitionModule = typeof import('./dist/transitions/index.js'); diff --git a/packages/astro/package.json b/packages/astro/package.json index 2052f8158dc2..34e3b7aa9caf 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -157,7 +157,6 @@ "p-limit": "^4.0.0", "path-to-regexp": "^6.2.1", "preferred-pm": "^3.1.2", - "probe-image-size": "^7.2.3", "prompts": "^2.4.2", "rehype": "^12.0.1", "resolve": "^1.22.4", @@ -198,7 +197,6 @@ "@types/js-yaml": "^4.0.5", "@types/mime": "^3.0.1", "@types/mocha": "^10.0.1", - "@types/probe-image-size": "^7.2.0", "@types/prompts": "^2.4.4", "@types/resolve": "^1.20.2", "@types/send": "^0.17.1", diff --git a/packages/astro/src/assets/consts.ts b/packages/astro/src/assets/consts.ts index 90dfa599cf21..d184c9359c77 100644 --- a/packages/astro/src/assets/consts.ts +++ b/packages/astro/src/assets/consts.ts @@ -1,6 +1,14 @@ export const VIRTUAL_MODULE_ID = 'astro:assets'; export const VIRTUAL_SERVICE_ID = 'virtual:image-service'; export const VALID_INPUT_FORMATS = [ + // TODO: `image-size` does not support the following formats, so users can't import them. + // However, it would be immensely useful to add, for three reasons: + // - `heic` and `heif` are common formats, especially among Apple users. + // - AVIF is a common format on the web that's bound to become more and more common. + // - It's totally reasonable for an user's provided image service to want to support more image types. + //'heic', + //'heif', + //'avif', 'jpeg', 'jpg', 'png', @@ -8,7 +16,6 @@ export const VALID_INPUT_FORMATS = [ 'webp', 'gif', 'svg', - 'avif', ] as const; /** * Valid formats that our base services support. @@ -22,6 +29,5 @@ export const VALID_SUPPORTED_FORMATS = [ 'webp', 'gif', 'svg', - 'avif', ] as const; export const VALID_OUTPUT_FORMATS = ['avif', 'png', 'webp', 'jpeg', 'jpg', 'svg'] as const; diff --git a/packages/astro/src/assets/utils/metadata.ts b/packages/astro/src/assets/utils/metadata.ts index 7d7ee74573ba..de4136b36b26 100644 --- a/packages/astro/src/assets/utils/metadata.ts +++ b/packages/astro/src/assets/utils/metadata.ts @@ -1,13 +1,8 @@ -import probe from 'probe-image-size'; import type { ImageInputFormat, ImageMetadata } from '../types.js'; +import imageSize from '../vendor/image-size/index.js'; export async function imageMetadata(data: Buffer): Promise | undefined> { - const result = probe.sync(data); - if (result === null) { - throw new Error('Failed to probe image size.'); - } - - const { width, height, type, orientation } = result; + const { width, height, type, orientation } = imageSize(data); const isPortrait = (orientation || 0) >= 5; if (!width || !height || !type) { diff --git a/packages/astro/src/assets/vendor/README.md b/packages/astro/src/assets/vendor/README.md new file mode 100644 index 000000000000..7b6927b674b3 --- /dev/null +++ b/packages/astro/src/assets/vendor/README.md @@ -0,0 +1,3 @@ +Vendored version of `image-size` and `queue` because we had issues with the CJS nature of those packages. + +Should hopefully be fixed by https://github.com/image-size/image-size/pull/370 diff --git a/packages/astro/src/assets/vendor/image-size/LICENSE b/packages/astro/src/assets/vendor/image-size/LICENSE new file mode 100644 index 000000000000..1341a90d565f --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/LICENSE @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright © 2017 Aditya Yadav, http://netroy.in + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/astro/src/assets/vendor/image-size/detector.ts b/packages/astro/src/assets/vendor/image-size/detector.ts new file mode 100644 index 000000000000..7a8873ab29f4 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/detector.ts @@ -0,0 +1,30 @@ +import { typeHandlers, type imageType } from './types.js' + +const keys = Object.keys(typeHandlers) as imageType[] + +// This map helps avoid validating for every single image type +const firstBytes: { [byte: number]: imageType } = { + 0x38: 'psd', + 0x42: 'bmp', + 0x44: 'dds', + 0x47: 'gif', + 0x49: 'tiff', + 0x4d: 'tiff', + 0x52: 'webp', + 0x69: 'icns', + 0x89: 'png', + 0xff: 'jpg' +} + +export function detector(buffer: Buffer): imageType | undefined { + const byte = buffer[0] + if (byte in firstBytes) { + const type = firstBytes[byte] + if (type && typeHandlers[type].validate(buffer)) { + return type + } + } + + const finder = (key: imageType) => typeHandlers[key].validate(buffer) + return keys.find(finder) +} diff --git a/packages/astro/src/assets/vendor/image-size/index.ts b/packages/astro/src/assets/vendor/image-size/index.ts new file mode 100644 index 000000000000..24f910b332c1 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/index.ts @@ -0,0 +1,146 @@ +import * as fs from "node:fs"; +import * as path from "node:path"; +import Queue from "../queue/queue.js"; +import { detector } from "./detector.js"; +import { typeHandlers, type imageType } from "./types.js"; +import type { ISizeCalculationResult } from "./types/interface.js"; + +type CallbackFn = (e: Error | null, r?: ISizeCalculationResult) => void; + +// Maximum buffer size, with a default of 512 kilobytes. +// TO-DO: make this adaptive based on the initial signature of the image +const MaxBufferSize = 512 * 1024; + +// This queue is for async `fs` operations, to avoid reaching file-descriptor limits +const queue = new Queue({ concurrency: 100, autostart: true }); + +interface Options { + disabledFS: boolean; + disabledTypes: imageType[]; +} + +const globalOptions: Options = { + disabledFS: false, + disabledTypes: [], +}; + +/** + * Return size information based on a buffer + * + * @param {Buffer} buffer + * @param {String} filepath + * @returns {Object} + */ +function lookup(buffer: Buffer, filepath?: string): ISizeCalculationResult { + // detect the file type.. don't rely on the extension + const type = detector(buffer); + + if (typeof type !== "undefined") { + if (globalOptions.disabledTypes.indexOf(type) > -1) { + throw new TypeError("disabled file type: " + type); + } + + // find an appropriate handler for this file type + if (type in typeHandlers) { + const size = typeHandlers[type].calculate(buffer, filepath); + if (size !== undefined) { + size.type = type; + return size; + } + } + } + + // throw up, if we don't understand the file + throw new TypeError( + "unsupported file type: " + type + " (file: " + filepath + ")" + ); +} + +/** + * Reads a file into a buffer. + * @param {String} filepath + * @returns {Promise} + */ +async function asyncFileToBuffer(filepath: string): Promise { + const handle = await fs.promises.open(filepath, "r"); + const { size } = await handle.stat(); + if (size <= 0) { + await handle.close(); + throw new Error("Empty file"); + } + const bufferSize = Math.min(size, MaxBufferSize); + const buffer = Buffer.alloc(bufferSize); + await handle.read(buffer, 0, bufferSize, 0); + await handle.close(); + return buffer; +} + +/** + * Synchronously reads a file into a buffer, blocking the nodejs process. + * + * @param {String} filepath + * @returns {Buffer} + */ +function syncFileToBuffer(filepath: string): Buffer { + // read from the file, synchronously + const descriptor = fs.openSync(filepath, "r"); + const { size } = fs.fstatSync(descriptor); + if (size <= 0) { + fs.closeSync(descriptor); + throw new Error("Empty file"); + } + const bufferSize = Math.min(size, MaxBufferSize); + const buffer = Buffer.alloc(bufferSize); + fs.readSync(descriptor, buffer, 0, bufferSize, 0); + fs.closeSync(descriptor); + return buffer; +} + +export default imageSize; +export function imageSize(input: Buffer | string): ISizeCalculationResult; +export function imageSize(input: string, callback: CallbackFn): void; + +/** + * @param {Buffer|string} input - buffer or relative/absolute path of the image file + * @param {Function=} [callback] - optional function for async detection + */ +export function imageSize( + input: Buffer | string, + callback?: CallbackFn +): ISizeCalculationResult | void { + // Handle buffer input + if (Buffer.isBuffer(input)) { + return lookup(input); + } + + // input should be a string at this point + if (typeof input !== "string" || globalOptions.disabledFS) { + throw new TypeError("invalid invocation. input should be a Buffer"); + } + + // resolve the file path + const filepath = path.resolve(input); + if (typeof callback === "function") { + queue.push(() => + asyncFileToBuffer(filepath) + .then((buffer) => + process.nextTick(callback, null, lookup(buffer, filepath)) + ) + .catch(callback) + ); + } else { + const buffer = syncFileToBuffer(filepath); + return lookup(buffer, filepath); + } +} + +export const disableFS = (v: boolean): void => { + globalOptions.disabledFS = v; +}; +export const disableTypes = (types: imageType[]): void => { + globalOptions.disabledTypes = types; +}; +export const setConcurrency = (c: number): void => { + queue.concurrency = c; +}; +export const types = Object.keys(typeHandlers); diff --git a/packages/astro/src/assets/vendor/image-size/readUInt.ts b/packages/astro/src/assets/vendor/image-size/readUInt.ts new file mode 100644 index 000000000000..ede811408a75 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/readUInt.ts @@ -0,0 +1,10 @@ +type Bits = 16 | 32 +type MethodName = 'readUInt16BE' | 'readUInt16LE' | 'readUInt32BE' | 'readUInt32LE' + +// Abstract reading multi-byte unsigned integers +export function readUInt(buffer: Buffer, bits: Bits, offset: number, isBigEndian: boolean): number { + offset = offset || 0 + const endian = isBigEndian ? 'BE' : 'LE' + const methodName: MethodName = ('readUInt' + bits + endian) as MethodName + return buffer[methodName].call(buffer, offset) +} diff --git a/packages/astro/src/assets/vendor/image-size/types.ts b/packages/astro/src/assets/vendor/image-size/types.ts new file mode 100644 index 000000000000..05f4f82cc4e0 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types.ts @@ -0,0 +1,38 @@ +// load all available handlers explicitely for browserify support +import { BMP } from './types/bmp.js' +import { CUR } from './types/cur.js' +import { DDS } from './types/dds.js' +import { GIF } from './types/gif.js' +import { ICNS } from './types/icns.js' +import { ICO } from './types/ico.js' +import { J2C } from './types/j2c.js' +import { JP2 } from './types/jp2.js' +import { JPG } from './types/jpg.js' +import { KTX } from './types/ktx.js' +import { PNG } from './types/png.js' +import { PNM } from './types/pnm.js' +import { PSD } from './types/psd.js' +import { SVG } from './types/svg.js' +import { TIFF } from './types/tiff.js' +import { WEBP } from './types/webp.js' + +export const typeHandlers = { + bmp: BMP, + cur: CUR, + dds: DDS, + gif: GIF, + icns: ICNS, + ico: ICO, + j2c: J2C, + jp2: JP2, + jpg: JPG, + ktx: KTX, + png: PNG, + pnm: PNM, + psd: PSD, + svg: SVG, + tiff: TIFF, + webp: WEBP, +} + +export type imageType = keyof typeof typeHandlers diff --git a/packages/astro/src/assets/vendor/image-size/types/bmp.ts b/packages/astro/src/assets/vendor/image-size/types/bmp.ts new file mode 100644 index 000000000000..2f55ccdd1057 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/bmp.ts @@ -0,0 +1,14 @@ +import type { IImage } from './interface' + +export const BMP: IImage = { + validate(buffer) { + return ('BM' === buffer.toString('ascii', 0, 2)) + }, + + calculate(buffer) { + return { + height: Math.abs(buffer.readInt32LE(22)), + width: buffer.readUInt32LE(18) + } + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/cur.ts b/packages/astro/src/assets/vendor/image-size/types/cur.ts new file mode 100644 index 000000000000..42766baecd1f --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/cur.ts @@ -0,0 +1,16 @@ +import { ICO } from './ico.js' +import type { IImage } from './interface' + +const TYPE_CURSOR = 2 +export const CUR: IImage = { + validate(buffer) { + if (buffer.readUInt16LE(0) !== 0) { + return false + } + return buffer.readUInt16LE(2) === TYPE_CURSOR + }, + + calculate(buffer) { + return ICO.calculate(buffer) + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/dds.ts b/packages/astro/src/assets/vendor/image-size/types/dds.ts new file mode 100644 index 000000000000..e9ceb63ba6e3 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/dds.ts @@ -0,0 +1,14 @@ +import type { IImage } from './interface' + +export const DDS: IImage = { + validate(buffer) { + return buffer.readUInt32LE(0) === 0x20534444 + }, + + calculate(buffer) { + return { + height: buffer.readUInt32LE(12), + width: buffer.readUInt32LE(16) + } + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/gif.ts b/packages/astro/src/assets/vendor/image-size/types/gif.ts new file mode 100644 index 000000000000..b18b305f199e --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/gif.ts @@ -0,0 +1,16 @@ +import type { IImage } from './interface' + +const gifRegexp = /^GIF8[79]a/ +export const GIF: IImage = { + validate(buffer) { + const signature = buffer.toString('ascii', 0, 6) + return (gifRegexp.test(signature)) + }, + + calculate(buffer) { + return { + height: buffer.readUInt16LE(8), + width: buffer.readUInt16LE(6) + } + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/icns.ts b/packages/astro/src/assets/vendor/image-size/types/icns.ts new file mode 100644 index 000000000000..5beccb02c47d --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/icns.ts @@ -0,0 +1,113 @@ +import type { IImage, ISize } from './interface' + +/** + * ICNS Header + * + * | Offset | Size | Purpose | + * | 0 | 4 | Magic literal, must be "icns" (0x69, 0x63, 0x6e, 0x73) | + * | 4 | 4 | Length of file, in bytes, msb first. | + * + */ +const SIZE_HEADER = 4 + 4 // 8 +const FILE_LENGTH_OFFSET = 4 // MSB => BIG ENDIAN + +/** + * Image Entry + * + * | Offset | Size | Purpose | + * | 0 | 4 | Icon type, see OSType below. | + * | 4 | 4 | Length of data, in bytes (including type and length), msb first. | + * | 8 | n | Icon data | + */ +const ENTRY_LENGTH_OFFSET = 4 // MSB => BIG ENDIAN + +const ICON_TYPE_SIZE: {[key: string]: number} = { + ICON: 32, + 'ICN#': 32, + // m => 16 x 16 + 'icm#': 16, + icm4: 16, + icm8: 16, + // s => 16 x 16 + 'ics#': 16, + ics4: 16, + ics8: 16, + is32: 16, + s8mk: 16, + icp4: 16, + // l => 32 x 32 + icl4: 32, + icl8: 32, + il32: 32, + l8mk: 32, + icp5: 32, + ic11: 32, + // h => 48 x 48 + ich4: 48, + ich8: 48, + ih32: 48, + h8mk: 48, + // . => 64 x 64 + icp6: 64, + ic12: 32, + // t => 128 x 128 + it32: 128, + t8mk: 128, + ic07: 128, + // . => 256 x 256 + ic08: 256, + ic13: 256, + // . => 512 x 512 + ic09: 512, + ic14: 512, + // . => 1024 x 1024 + ic10: 1024, +} + +function readImageHeader(buffer: Buffer, imageOffset: number): [string, number] { + const imageLengthOffset = imageOffset + ENTRY_LENGTH_OFFSET + return [ + buffer.toString('ascii', imageOffset, imageLengthOffset), + buffer.readUInt32BE(imageLengthOffset) + ] +} + +function getImageSize(type: string): ISize { + const size = ICON_TYPE_SIZE[type] + return { width: size, height: size, type } +} + +export const ICNS: IImage = { + validate(buffer) { + return ('icns' === buffer.toString('ascii', 0, 4)) + }, + + calculate(buffer) { + const bufferLength = buffer.length + const fileLength = buffer.readUInt32BE(FILE_LENGTH_OFFSET) + let imageOffset = SIZE_HEADER + + let imageHeader = readImageHeader(buffer, imageOffset) + let imageSize = getImageSize(imageHeader[0]) + imageOffset += imageHeader[1] + + if (imageOffset === fileLength) { + return imageSize + } + + const result = { + height: imageSize.height, + images: [imageSize], + width: imageSize.width + } + + while (imageOffset < fileLength && imageOffset < bufferLength) { + imageHeader = readImageHeader(buffer, imageOffset) + imageSize = getImageSize(imageHeader[0]) + imageOffset += imageHeader[1] + result.images.push(imageSize) + } + + return result + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/ico.ts b/packages/astro/src/assets/vendor/image-size/types/ico.ts new file mode 100644 index 000000000000..09310176fcb9 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/ico.ts @@ -0,0 +1,76 @@ +import type { IImage, ISize, ISizeCalculationResult } from './interface' + +const TYPE_ICON = 1 + +/** + * ICON Header + * + * | Offset | Size | Purpose | + * | 0 | 2 | Reserved. Must always be 0. | + * | 2 | 2 | Image type: 1 for icon (.ICO) image, 2 for cursor (.CUR) image. Other values are invalid. | + * | 4 | 2 | Number of images in the file. | + * + */ +const SIZE_HEADER = 2 + 2 + 2 // 6 + +/** + * Image Entry + * + * | Offset | Size | Purpose | + * | 0 | 1 | Image width in pixels. Can be any number between 0 and 255. Value 0 means width is 256 pixels. | + * | 1 | 1 | Image height in pixels. Can be any number between 0 and 255. Value 0 means height is 256 pixels. | + * | 2 | 1 | Number of colors in the color palette. Should be 0 if the image does not use a color palette. | + * | 3 | 1 | Reserved. Should be 0. | + * | 4 | 2 | ICO format: Color planes. Should be 0 or 1. | + * | | | CUR format: The horizontal coordinates of the hotspot in number of pixels from the left. | + * | 6 | 2 | ICO format: Bits per pixel. | + * | | | CUR format: The vertical coordinates of the hotspot in number of pixels from the top. | + * | 8 | 4 | The size of the image's data in bytes | + * | 12 | 4 | The offset of BMP or PNG data from the beginning of the ICO/CUR file | + * + */ +const SIZE_IMAGE_ENTRY = 1 + 1 + 1 + 1 + 2 + 2 + 4 + 4 // 16 + +function getSizeFromOffset(buffer: Buffer, offset: number): number { + const value = buffer.readUInt8(offset) + return value === 0 ? 256 : value +} + +function getImageSize(buffer: Buffer, imageIndex: number): ISize { + const offset = SIZE_HEADER + (imageIndex * SIZE_IMAGE_ENTRY) + return { + height: getSizeFromOffset(buffer, offset + 1), + width: getSizeFromOffset(buffer, offset) + } +} + +export const ICO: IImage = { + validate(buffer) { + if (buffer.readUInt16LE(0) !== 0) { + return false + } + return buffer.readUInt16LE(2) === TYPE_ICON + }, + + calculate(buffer) { + const nbImages = buffer.readUInt16LE(4) + const imageSize = getImageSize(buffer, 0) + + if (nbImages === 1) { + return imageSize + } + + const imgs: ISize[] = [imageSize] + for (let imageIndex = 1; imageIndex < nbImages; imageIndex += 1) { + imgs.push(getImageSize(buffer, imageIndex)) + } + + const result: ISizeCalculationResult = { + height: imageSize.height, + images: imgs, + width: imageSize.width + } + + return result + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/interface.ts b/packages/astro/src/assets/vendor/image-size/types/interface.ts new file mode 100644 index 000000000000..9886b357361e --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/interface.ts @@ -0,0 +1,15 @@ +export interface ISize { + width: number | undefined + height: number | undefined + orientation?: number + type?: string +} + +export interface ISizeCalculationResult extends ISize { + images?: ISize[] +} + +export interface IImage { + validate: (buffer: Buffer) => boolean + calculate: (buffer: Buffer, filepath?: string) => ISizeCalculationResult +} diff --git a/packages/astro/src/assets/vendor/image-size/types/j2c.ts b/packages/astro/src/assets/vendor/image-size/types/j2c.ts new file mode 100644 index 000000000000..301c38cb6f06 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/j2c.ts @@ -0,0 +1,15 @@ +import type { IImage } from './interface' + +export const J2C: IImage = { + validate(buffer) { + // TODO: this doesn't seem right. SIZ marker doesn't have to be right after the SOC + return buffer.toString('hex', 0, 4) === 'ff4fff51' + }, + + calculate(buffer) { + return { + height: buffer.readUInt32BE(12), + width: buffer.readUInt32BE(8), + } + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/jp2.ts b/packages/astro/src/assets/vendor/image-size/types/jp2.ts new file mode 100644 index 000000000000..127a96d608e3 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/jp2.ts @@ -0,0 +1,61 @@ +import type { IImage, ISize } from './interface' + +const BoxTypes = { + ftyp: '66747970', + ihdr: '69686472', + jp2h: '6a703268', + jp__: '6a502020', + rreq: '72726571', + xml_: '786d6c20' +} + +const calculateRREQLength = (box: Buffer): number => { + const unit = box.readUInt8(0) + let offset = 1 + (2 * unit) + const numStdFlags = box.readUInt16BE(offset) + const flagsLength = numStdFlags * (2 + unit) + offset = offset + 2 + flagsLength + const numVendorFeatures = box.readUInt16BE(offset) + const featuresLength = numVendorFeatures * (16 + unit) + return offset + 2 + featuresLength +} + +const parseIHDR = (box: Buffer): ISize => { + return { + height: box.readUInt32BE(4), + width: box.readUInt32BE(8), + } +} + +export const JP2: IImage = { + validate(buffer) { + const signature = buffer.toString('hex', 4, 8) + const signatureLength = buffer.readUInt32BE(0) + if (signature !== BoxTypes.jp__ || signatureLength < 1) { + return false + } + + const ftypeBoxStart = signatureLength + 4 + const ftypBoxLength = buffer.readUInt32BE(signatureLength) + const ftypBox = buffer.slice(ftypeBoxStart, ftypeBoxStart + ftypBoxLength) + return ftypBox.toString('hex', 0, 4) === BoxTypes.ftyp + }, + + calculate(buffer) { + const signatureLength = buffer.readUInt32BE(0) + const ftypBoxLength = buffer.readUInt16BE(signatureLength + 2) + let offset = signatureLength + 4 + ftypBoxLength + const nextBoxType = buffer.toString('hex', offset, offset + 4) + switch (nextBoxType) { + case BoxTypes.rreq: + // WHAT ARE THESE 4 BYTES????? + const MAGIC = 4 + offset = offset + 4 + MAGIC + calculateRREQLength(buffer.slice(offset + 4)) + return parseIHDR(buffer.slice(offset + 8, offset + 24)) + case BoxTypes.jp2h : + return parseIHDR(buffer.slice(offset + 8, offset + 24)) + default: + throw new TypeError('Unsupported header found: ' + buffer.toString('ascii', offset, offset + 4)) + } + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/jpg.ts b/packages/astro/src/assets/vendor/image-size/types/jpg.ts new file mode 100644 index 000000000000..68c32b7bec9c --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/jpg.ts @@ -0,0 +1,151 @@ +// NOTE: we only support baseline and progressive JPGs here +// due to the structure of the loader class, we only get a buffer +// with a maximum size of 4096 bytes. so if the SOF marker is outside +// if this range we can't detect the file size correctly. + +import { readUInt } from '../readUInt.js' +import type { IImage, ISize } from './interface' + +const EXIF_MARKER = '45786966' +const APP1_DATA_SIZE_BYTES = 2 +const EXIF_HEADER_BYTES = 6 +const TIFF_BYTE_ALIGN_BYTES = 2 +const BIG_ENDIAN_BYTE_ALIGN = '4d4d' +const LITTLE_ENDIAN_BYTE_ALIGN = '4949' + +// Each entry is exactly 12 bytes +const IDF_ENTRY_BYTES = 12 +const NUM_DIRECTORY_ENTRIES_BYTES = 2 + +function isEXIF(buffer: Buffer): boolean { + return (buffer.toString('hex', 2, 6) === EXIF_MARKER) +} + +function extractSize(buffer: Buffer, index: number): ISize { + return { + height : buffer.readUInt16BE(index), + width : buffer.readUInt16BE(index + 2) + } +} + +function extractOrientation(exifBlock: Buffer, isBigEndian: boolean) { + // TODO: assert that this contains 0x002A + // let STATIC_MOTOROLA_TIFF_HEADER_BYTES = 2 + // let TIFF_IMAGE_FILE_DIRECTORY_BYTES = 4 + + // TODO: derive from TIFF_IMAGE_FILE_DIRECTORY_BYTES + const idfOffset = 8 + + // IDF osset works from right after the header bytes + // (so the offset includes the tiff byte align) + const offset = EXIF_HEADER_BYTES + idfOffset + + const idfDirectoryEntries = readUInt(exifBlock, 16, offset, isBigEndian) + + for (let directoryEntryNumber = 0; directoryEntryNumber < idfDirectoryEntries; directoryEntryNumber++) { + const start = offset + NUM_DIRECTORY_ENTRIES_BYTES + (directoryEntryNumber * IDF_ENTRY_BYTES) + const end = start + IDF_ENTRY_BYTES + + // Skip on corrupt EXIF blocks + if (start > exifBlock.length) { + return + } + + const block = exifBlock.slice(start, end) + const tagNumber = readUInt(block, 16, 0, isBigEndian) + + // 0x0112 (decimal: 274) is the `orientation` tag ID + if (tagNumber === 274) { + const dataFormat = readUInt(block, 16, 2, isBigEndian) + if (dataFormat !== 3) { + return + } + + // unsinged int has 2 bytes per component + // if there would more than 4 bytes in total it's a pointer + const numberOfComponents = readUInt(block, 32, 4, isBigEndian) + if (numberOfComponents !== 1) { + return + } + + return readUInt(block, 16, 8, isBigEndian) + } + } +} + +function validateExifBlock(buffer: Buffer, index: number) { + // Skip APP1 Data Size + const exifBlock = buffer.slice(APP1_DATA_SIZE_BYTES, index) + + // Consider byte alignment + const byteAlign = exifBlock.toString('hex', EXIF_HEADER_BYTES, EXIF_HEADER_BYTES + TIFF_BYTE_ALIGN_BYTES) + + // Ignore Empty EXIF. Validate byte alignment + const isBigEndian = byteAlign === BIG_ENDIAN_BYTE_ALIGN + const isLittleEndian = byteAlign === LITTLE_ENDIAN_BYTE_ALIGN + + if (isBigEndian || isLittleEndian) { + return extractOrientation(exifBlock, isBigEndian) + } +} + +function validateBuffer(buffer: Buffer, index: number): void { + // index should be within buffer limits + if (index > buffer.length) { + throw new TypeError('Corrupt JPG, exceeded buffer limits') + } + // Every JPEG block must begin with a 0xFF + if (buffer[index] !== 0xFF) { + throw new TypeError('Invalid JPG, marker table corrupted') + } +} + +export const JPG: IImage = { + validate(buffer) { + const SOIMarker = buffer.toString('hex', 0, 2) + return ('ffd8' === SOIMarker) + }, + + calculate(buffer) { + // Skip 4 chars, they are for signature + buffer = buffer.slice(4) + + let orientation: number | undefined + let next: number + while (buffer.length) { + // read length of the next block + const i = buffer.readUInt16BE(0) + + if (isEXIF(buffer)) { + orientation = validateExifBlock(buffer, i) + } + + // ensure correct format + validateBuffer(buffer, i) + + // 0xFFC0 is baseline standard(SOF) + // 0xFFC1 is baseline optimized(SOF) + // 0xFFC2 is progressive(SOF2) + next = buffer[i + 1] + if (next === 0xC0 || next === 0xC1 || next === 0xC2) { + const size = extractSize(buffer, i + 5) + + // TODO: is orientation=0 a valid answer here? + if (!orientation) { + return size + } + + return { + height: size.height, + orientation, + width: size.width + } + } + + // move to the next block + buffer = buffer.slice(i + 2) + } + + throw new TypeError('Invalid JPG, no size found') + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/ktx.ts b/packages/astro/src/assets/vendor/image-size/types/ktx.ts new file mode 100644 index 000000000000..9e43fdeaaf37 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/ktx.ts @@ -0,0 +1,16 @@ +import type { IImage } from './interface' + +const SIGNATURE = 'KTX 11' + +export const KTX: IImage = { + validate(buffer) { + return SIGNATURE === buffer.toString('ascii', 1, 7) + }, + + calculate(buffer) { + return { + height: buffer.readUInt32LE(40), + width: buffer.readUInt32LE(36), + } + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/png.ts b/packages/astro/src/assets/vendor/image-size/types/png.ts new file mode 100644 index 000000000000..a31411380c3e --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/png.ts @@ -0,0 +1,36 @@ +import type { IImage } from './interface' + +const pngSignature = 'PNG\r\n\x1a\n' +const pngImageHeaderChunkName = 'IHDR' + +// Used to detect "fried" png's: http://www.jongware.com/pngdefry.html +const pngFriedChunkName = 'CgBI' + +export const PNG: IImage = { + validate(buffer) { + if (pngSignature === buffer.toString('ascii', 1, 8)) { + let chunkName = buffer.toString('ascii', 12, 16) + if (chunkName === pngFriedChunkName) { + chunkName = buffer.toString('ascii', 28, 32) + } + if (chunkName !== pngImageHeaderChunkName) { + throw new TypeError('Invalid PNG') + } + return true + } + return false + }, + + calculate(buffer) { + if (buffer.toString('ascii', 12, 16) === pngFriedChunkName) { + return { + height: buffer.readUInt32BE(36), + width: buffer.readUInt32BE(32) + } + } + return { + height: buffer.readUInt32BE(20), + width: buffer.readUInt32BE(16) + } + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/pnm.ts b/packages/astro/src/assets/vendor/image-size/types/pnm.ts new file mode 100644 index 000000000000..fa30a53d50a0 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/pnm.ts @@ -0,0 +1,80 @@ +import type { IImage, ISize } from './interface' + +const PNMTypes: { [signature: string]: string } = { + P1: 'pbm/ascii', + P2: 'pgm/ascii', + P3: 'ppm/ascii', + P4: 'pbm', + P5: 'pgm', + P6: 'ppm', + P7: 'pam', + PF: 'pfm' +} + +const Signatures = Object.keys(PNMTypes) + +type Handler = (type: string[]) => ISize +const handlers: { [type: string]: Handler} = { + default: (lines) => { + let dimensions: string[] = [] + + while (lines.length > 0) { + const line = lines.shift()! + if (line[0] === '#') { + continue + } + dimensions = line.split(' ') + break + } + + if (dimensions.length === 2) { + return { + height: parseInt(dimensions[1], 10), + width: parseInt(dimensions[0], 10), + } + } else { + throw new TypeError('Invalid PNM') + } + }, + pam: (lines) => { + const size: { [key: string]: number } = {} + while (lines.length > 0) { + const line = lines.shift()! + if (line.length > 16 || line.charCodeAt(0) > 128) { + continue + } + const [key, value] = line.split(' ') + if (key && value) { + size[key.toLowerCase()] = parseInt(value, 10) + } + if (size.height && size.width) { + break + } + } + + if (size.height && size.width) { + return { + height: size.height, + width: size.width + } + } else { + throw new TypeError('Invalid PAM') + } + } +} + +export const PNM: IImage = { + validate(buffer) { + const signature = buffer.toString('ascii', 0, 2) + return Signatures.includes(signature) + }, + + calculate(buffer) { + const signature = buffer.toString('ascii', 0, 2) + const type = PNMTypes[signature] + // TODO: this probably generates garbage. move to a stream based parser + const lines = buffer.toString('ascii', 3).split(/[\r\n]+/) + const handler = handlers[type] || handlers.default + return handler(lines) + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/psd.ts b/packages/astro/src/assets/vendor/image-size/types/psd.ts new file mode 100644 index 000000000000..7521f5e9f826 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/psd.ts @@ -0,0 +1,14 @@ +import type { IImage } from './interface' + +export const PSD: IImage = { + validate(buffer) { + return ('8BPS' === buffer.toString('ascii', 0, 4)) + }, + + calculate(buffer) { + return { + height: buffer.readUInt32BE(14), + width: buffer.readUInt32BE(18) + } + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/svg.ts b/packages/astro/src/assets/vendor/image-size/types/svg.ts new file mode 100644 index 000000000000..945be962dd4a --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/svg.ts @@ -0,0 +1,106 @@ +import type { IImage, ISize } from './interface' + +interface IAttributes { + width: number | null + height: number | null + viewbox?: IAttributes | null +} + +const svgReg = /"']|"[^"]*"|'[^']*')*>/ + +const extractorRegExps = { + height: /\sheight=(['"])([^%]+?)\1/, + root: svgReg, + viewbox: /\sviewBox=(['"])(.+?)\1/i, + width: /\swidth=(['"])([^%]+?)\1/, +} + +const INCH_CM = 2.54 +const units: { [unit: string]: number } = { + in: 96, + cm: 96 / INCH_CM, + em: 16, + ex: 8, + m: 96 / INCH_CM * 100, + mm: 96 / INCH_CM / 10, + pc: 96 / 72 / 12, + pt: 96 / 72, + px: 1 +} + +const unitsReg = new RegExp(`^([0-9.]+(?:e\\d+)?)(${Object.keys(units).join('|')})?$`) + +function parseLength(len: string) { + const m = unitsReg.exec(len) + if (!m) { + return undefined + } + return Math.round(Number(m[1]) * (units[m[2]] || 1)) +} + +function parseViewbox(viewbox: string): IAttributes { + const bounds = viewbox.split(' ') + return { + height: parseLength(bounds[3])!, + width: parseLength(bounds[2])! + } +} + +function parseAttributes(root: string): IAttributes { + const width = root.match(extractorRegExps.width) + const height = root.match(extractorRegExps.height) + const viewbox = root.match(extractorRegExps.viewbox) + return { + height: height && parseLength(height[2])!, + viewbox: viewbox && parseViewbox(viewbox[2])!, + width: width && parseLength(width[2])!, + } +} + +function calculateByDimensions(attrs: IAttributes): ISize { + return { + height: attrs.height!, + width: attrs.width!, + } +} + +function calculateByViewbox(attrs: IAttributes, viewbox: IAttributes): ISize { + const ratio = (viewbox.width!) / (viewbox.height!) + if (attrs.width) { + return { + height: Math.floor(attrs.width / ratio), + width: attrs.width, + } + } + if (attrs.height) { + return { + height: attrs.height, + width: Math.floor(attrs.height * ratio), + } + } + return { + height: viewbox.height!, + width: viewbox.width!, + } +} + +export const SVG: IImage = { + validate(buffer) { + const str = String(buffer) + return svgReg.test(str) + }, + + calculate(buffer) { + const root = buffer.toString('utf8').match(extractorRegExps.root) + if (root) { + const attrs = parseAttributes(root[0]) + if (attrs.width && attrs.height) { + return calculateByDimensions(attrs) + } + if (attrs.viewbox) { + return calculateByViewbox(attrs, attrs.viewbox) + } + } + throw new TypeError('Invalid SVG') + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/tiff.ts b/packages/astro/src/assets/vendor/image-size/types/tiff.ts new file mode 100644 index 000000000000..ae228c1b8448 --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/tiff.ts @@ -0,0 +1,115 @@ +// based on http://www.compix.com/fileformattif.htm +// TO-DO: support big-endian as well +import * as fs from 'node:fs' +import { readUInt } from '../readUInt.js' +import type { IImage } from './interface' + +// Read IFD (image-file-directory) into a buffer +function readIFD(buffer: Buffer, filepath: string, isBigEndian: boolean) { + + const ifdOffset = readUInt(buffer, 32, 4, isBigEndian) + + // read only till the end of the file + let bufferSize = 1024 + const fileSize = fs.statSync(filepath).size + if (ifdOffset + bufferSize > fileSize) { + bufferSize = fileSize - ifdOffset - 10 + } + + // populate the buffer + const endBuffer = Buffer.alloc(bufferSize) + const descriptor = fs.openSync(filepath, 'r') + fs.readSync(descriptor, endBuffer, 0, bufferSize, ifdOffset) + fs.closeSync(descriptor) + + return endBuffer.slice(2) +} + +// TIFF values seem to be messed up on Big-Endian, this helps +function readValue(buffer: Buffer, isBigEndian: boolean): number { + const low = readUInt(buffer, 16, 8, isBigEndian) + const high = readUInt(buffer, 16, 10, isBigEndian) + return (high << 16) + low +} + +// move to the next tag +function nextTag(buffer: Buffer) { + if (buffer.length > 24) { + return buffer.slice(12) + } +} + +// Extract IFD tags from TIFF metadata +function extractTags(buffer: Buffer, isBigEndian: boolean) { + const tags: {[key: number]: number} = {} + + let temp: Buffer | undefined = buffer + while (temp?.length) { + const code = readUInt(temp, 16, 0, isBigEndian) + const type = readUInt(temp, 16, 2, isBigEndian) + const length = readUInt(temp, 32, 4, isBigEndian) + + // 0 means end of IFD + if (code === 0) { + break + } else { + // 256 is width, 257 is height + // if (code === 256 || code === 257) { + if (length === 1 && (type === 3 || type === 4)) { + tags[code] = readValue(temp, isBigEndian) + } + + // move to the next tag + temp = nextTag(temp) + } + } + + return tags +} + +// Test if the TIFF is Big Endian or Little Endian +function determineEndianness(buffer: Buffer) { + const signature = buffer.toString('ascii', 0, 2) + if ('II' === signature) { + return 'LE' + } else if ('MM' === signature) { + return 'BE' + } +} + +const signatures = [ + // '492049', // currently not supported + '49492a00', // Little endian + '4d4d002a', // Big Endian + // '4d4d002a', // BigTIFF > 4GB. currently not supported +] + +export const TIFF: IImage = { + validate(buffer) { + return signatures.includes(buffer.toString('hex', 0, 4)) + }, + + calculate(buffer, filepath) { + if (!filepath) { + throw new TypeError('Tiff doesn\'t support buffer') + } + + // Determine BE/LE + const isBigEndian = determineEndianness(buffer) === 'BE' + + // read the IFD + const ifdBuffer = readIFD(buffer, filepath, isBigEndian) + + // extract the tags from the IFD + const tags = extractTags(ifdBuffer, isBigEndian) + + const width = tags[256] + const height = tags[257] + + if (!width || !height) { + throw new TypeError('Invalid Tiff. Missing tags') + } + + return { height, width } + } +} diff --git a/packages/astro/src/assets/vendor/image-size/types/webp.ts b/packages/astro/src/assets/vendor/image-size/types/webp.ts new file mode 100644 index 000000000000..aaafbf12d82d --- /dev/null +++ b/packages/astro/src/assets/vendor/image-size/types/webp.ts @@ -0,0 +1,65 @@ +// based on https://developers.google.com/speed/webp/docs/riff_container +import type { IImage, ISize } from './interface' + +function calculateExtended(buffer: Buffer): ISize { + return { + height: 1 + buffer.readUIntLE(7, 3), + width: 1 + buffer.readUIntLE(4, 3) + } +} + +function calculateLossless(buffer: Buffer): ISize { + return { + height: 1 + (((buffer[4] & 0xF) << 10) | (buffer[3] << 2) | ((buffer[2] & 0xC0) >> 6)), + width: 1 + (((buffer[2] & 0x3F) << 8) | buffer[1]) + } +} + +function calculateLossy(buffer: Buffer): ISize { + // `& 0x3fff` returns the last 14 bits + // TO-DO: include webp scaling in the calculations + return { + height: buffer.readInt16LE(8) & 0x3fff, + width: buffer.readInt16LE(6) & 0x3fff + } +} + +export const WEBP: IImage = { + validate(buffer) { + const riffHeader = 'RIFF' === buffer.toString('ascii', 0, 4) + const webpHeader = 'WEBP' === buffer.toString('ascii', 8, 12) + const vp8Header = 'VP8' === buffer.toString('ascii', 12, 15) + return (riffHeader && webpHeader && vp8Header) + }, + + calculate(buffer) { + const chunkHeader = buffer.toString('ascii', 12, 16) + buffer = buffer.slice(20, 30) + + // Extended webp stream signature + if (chunkHeader === 'VP8X') { + const extendedHeader = buffer[0] + const validStart = (extendedHeader & 0xc0) === 0 + const validEnd = (extendedHeader & 0x01) === 0 + if (validStart && validEnd) { + return calculateExtended(buffer) + } else { + // TODO: breaking change + throw new TypeError('Invalid WebP') + } + } + + // Lossless webp stream signature + if (chunkHeader === 'VP8 ' && buffer[0] !== 0x2f) { + return calculateLossy(buffer) + } + + // Lossy webp stream signature + const signature = buffer.toString('hex', 3, 6) + if (chunkHeader === 'VP8L' && signature !== '9d012a') { + return calculateLossless(buffer) + } + + throw new TypeError('Invalid WebP') + } +} diff --git a/packages/astro/src/assets/vendor/queue/LICENSE b/packages/astro/src/assets/vendor/queue/LICENSE new file mode 100644 index 000000000000..50e946098625 --- /dev/null +++ b/packages/astro/src/assets/vendor/queue/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2014 Jesse Tane + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/astro/src/assets/vendor/queue/queue.js b/packages/astro/src/assets/vendor/queue/queue.js new file mode 100644 index 000000000000..6c71704355a4 --- /dev/null +++ b/packages/astro/src/assets/vendor/queue/queue.js @@ -0,0 +1,225 @@ +const has = Object.prototype.hasOwnProperty + +/** + * Since CustomEvent is only supported in nodejs since version 19, + * you have to create your own class instead of using CustomEvent + * @see https://github.com/nodejs/node/issues/40678 + * */ +export class QueueEvent extends Event { + constructor (name, detail) { + super(name) + this.detail = detail + } +} + + +export default class Queue extends EventTarget { + constructor (options = {}) { + super() + const { concurrency = Infinity, timeout = 0, autostart = false, results = null } = options + + this.concurrency = concurrency + this.timeout = timeout + this.autostart = autostart + this.results = results + this.pending = 0 + this.session = 0 + this.running = false + this.jobs = [] + this.timers = [] + + this.addEventListener('error', this._errorHandler) + } + + _errorHandler(evt) { + this.end(evt.detail.error); + } + + pop () { + return this.jobs.pop() + } + + shift () { + return this.jobs.shift() + } + + indexOf (searchElement, fromIndex) { + return this.jobs.indexOf(searchElement, fromIndex) + } + + lastIndexOf (searchElement, fromIndex) { + if (fromIndex !== undefined) { return this.jobs.lastIndexOf(searchElement, fromIndex) } + return this.jobs.lastIndexOf(searchElement) + } + + slice (start, end) { + this.jobs = this.jobs.slice(start, end) + return this + } + + reverse () { + this.jobs.reverse() + return this + } + + push (...workers) { + const methodResult = this.jobs.push(...workers) + if (this.autostart) { + this.start() + } + return methodResult + } + + unshift (...workers) { + const methodResult = this.jobs.unshift(...workers) + if (this.autostart) { + this.start() + } + return methodResult + } + + splice (start, deleteCount, ...workers) { + this.jobs.splice(start, deleteCount, ...workers) + if (this.autostart) { + this.start() + } + return this + } + + get length () { + return this.pending + this.jobs.length + } + + start (callback) { + let awaiter; + + if (callback) { + this._addCallbackToEndEvent(callback) + } else { + awaiter = this._createPromiseToEndEvent(); + } + + this.running = true + + if (this.pending >= this.concurrency) { + return + } + + if (this.jobs.length === 0) { + if (this.pending === 0) { + this.done() + } + return + } + + const job = this.jobs.shift() + const session = this.session + const timeout = (job !== undefined) && has.call(job, 'timeout') ? job.timeout : this.timeout + let once = true + let timeoutId = null + let didTimeout = false + let resultIndex = null + + const next = (error, ...result) => { + if (once && this.session === session) { + once = false + this.pending-- + if (timeoutId !== null) { + this.timers = this.timers.filter((tID) => tID !== timeoutId) + clearTimeout(timeoutId) + } + + if (error) { + this.dispatchEvent(new QueueEvent('error', { error, job })) + } else if (!didTimeout) { + if (resultIndex !== null && this.results !== null) { + this.results[resultIndex] = [...result] + } + this.dispatchEvent(new QueueEvent('success', { result: [...result], job })) + } + + if (this.session === session) { + if (this.pending === 0 && this.jobs.length === 0) { + this.done() + } else if (this.running) { + this.start() + } + } + } + } + + if (timeout) { + timeoutId = setTimeout(() => { + didTimeout = true + this.dispatchEvent(new QueueEvent('timeout', { next, job })) + next() + }, timeout) + this.timers.push(timeoutId) + } + + if (this.results != null) { + resultIndex = this.results.length + this.results[resultIndex] = null + } + + this.pending++ + this.dispatchEvent(new QueueEvent('start', { job })) + + const promise = job(next) + + if (promise !== undefined && typeof promise.then === 'function') { + promise.then(function (result) { + return next(undefined, result) + }).catch(function (err) { + return next(err || true) + }) + } + + if (this.running && this.jobs.length > 0) { + return this.start() + } + + return awaiter; + } + + stop () { + this.running = false + } + + end (error) { + this.clearTimers() + this.jobs.length = 0 + this.pending = 0 + this.done(error) + } + + clearTimers () { + this.timers.forEach((timer) => { + clearTimeout(timer) + }) + + this.timers = [] + } + + _addCallbackToEndEvent (cb) { + const onend = (evt) => { + this.removeEventListener('end', onend) + cb(evt.detail.error, this.results) + } + this.addEventListener('end', onend) + } + + _createPromiseToEndEvent() { + return new Promise((resolve) => { + this._addCallbackToEndEvent((error, results) => { + resolve({ error, results }); + }); + }); + } + + done (error) { + this.session++ + this.running = false + this.dispatchEvent(new QueueEvent('end', { error })) + } +} diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts index 87fd950ee9db..f114e7ef8371 100644 --- a/packages/astro/src/assets/vite-plugin-assets.ts +++ b/packages/astro/src/assets/vite-plugin-assets.ts @@ -14,7 +14,7 @@ import { hashTransform, propsToFilename } from './utils/transformToPath.js'; const resolvedVirtualModuleId = '\0' + VIRTUAL_MODULE_ID; -const assetRegex = new RegExp(`\\.(${VALID_INPUT_FORMATS.join('|')})$`, 'i'); +const assetRegex = new RegExp(`\.(${VALID_INPUT_FORMATS.join('|')})$`, 'i'); export default function assets({ settings, diff --git a/packages/astro/test/core-image.test.js b/packages/astro/test/core-image.test.js index 666739539a21..f5a1b28f4fd4 100644 --- a/packages/astro/test/core-image.test.js +++ b/packages/astro/test/core-image.test.js @@ -174,22 +174,6 @@ describe('astro:image', () => { expect(res.status).to.equal(200); expect(loading).to.not.be.undefined; }); - - it('supports avif', async () => { - let res = await fixture.fetch('/avif'); - let html = await res.text(); - $ = cheerio.load(html); - - console.log(html); - - let $img = $('img'); - expect($img).to.have.a.lengthOf(1); - - let src = $img.attr('src'); - res = await fixture.fetch(src); - expect(res.status).to.equal(200); - expect(res.headers.get('content-type')).to.equal('image/avif'); - }); }); describe('vite-isms', () => { diff --git a/packages/astro/test/fixtures/core-image/src/assets/light_walrus.avif b/packages/astro/test/fixtures/core-image/src/assets/light_walrus.avif deleted file mode 100644 index 89e1c3a143111f0a609c83f05c5fecd0ddce77b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19439 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 es8D~R1oB4={TAM+9brJNiAM diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f062f95f5788..ec37f2a0f9f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -596,9 +596,6 @@ importers: preferred-pm: specifier: ^3.1.2 version: 3.1.2 - probe-image-size: - specifier: ^7.2.3 - version: 7.2.3 prompts: specifier: ^2.4.2 version: 2.4.2 @@ -709,9 +706,6 @@ importers: '@types/mocha': specifier: ^10.0.1 version: 10.0.1 - '@types/probe-image-size': - specifier: ^7.2.0 - version: 7.2.0 '@types/prompts': specifier: ^2.4.4 version: 2.4.4 @@ -8896,12 +8890,6 @@ packages: /@types/ms@0.7.31: resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} - /@types/needle@3.2.0: - resolution: {integrity: sha512-6XzvzEyJ2ozFNfPajFmqH9JOt0Hp+9TawaYpJT59iIP/zR0U37cfWCRwosyIeEBBZBi021Osq4jGAD3AOju5fg==} - dependencies: - '@types/node': 18.17.8 - dev: true - /@types/nlcst@1.0.0: resolution: {integrity: sha512-3TGCfOcy8R8mMQ4CNSNOe3PG66HttvjcLzCoOpvXvDtfWOTi+uT/rxeOKm/qEwbM4SNe1O/PjdiBK2YcTjU4OQ==} dependencies: @@ -8943,13 +8931,6 @@ packages: resolution: {integrity: sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==} dev: true - /@types/probe-image-size@7.2.0: - resolution: {integrity: sha512-R5H3vw62gHNHrn+JGZbKejb+Z2D/6E5UNVlhCzIaBBLroMQMOFqy5Pap2gM+ZZHdqBtVU0/cx/M6to+mOJcoew==} - dependencies: - '@types/needle': 3.2.0 - '@types/node': 18.17.8 - dev: true - /@types/prompts@2.4.4: resolution: {integrity: sha512-p5N9uoTH76lLvSAaYSZtBCdEXzpOOufsRjnhjVSrZGXikVGHX9+cc9ERtHRV4hvBKHyZb1bg4K+56Bd2TqUn4A==} dependencies: @@ -10774,17 +10755,6 @@ packages: dependencies: ms: 2.0.0 - /debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.3 - dev: false - /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -12547,6 +12517,7 @@ packages: engines: {node: '>=0.10.0'} dependencies: safer-buffer: 2.1.2 + dev: true /iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} @@ -13277,6 +13248,7 @@ packages: /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true /lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} @@ -14362,18 +14334,6 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /needle@2.9.1: - resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==} - engines: {node: '>= 4.4.x'} - hasBin: true - dependencies: - debug: 3.2.7 - iconv-lite: 0.4.24 - sax: 1.2.4 - transitivePeerDependencies: - - supports-color - dev: false - /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -15471,16 +15431,6 @@ packages: engines: {node: '>=6'} dev: false - /probe-image-size@7.2.3: - resolution: {integrity: sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==} - dependencies: - lodash.merge: 4.6.2 - needle: 2.9.1 - stream-parser: 0.3.1 - transitivePeerDependencies: - - supports-color - dev: false - /progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -16115,6 +16065,7 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true /sass-formatter@0.7.7: resolution: {integrity: sha512-axtQ7c7Cf4UgHsD8e4okhIkkc90+tdgBIfUMx69+qJuMNq9EOo2k+RH/mDKj0XeA5z3nC1Ca5TCntuxRhI+1MA==} @@ -16534,14 +16485,6 @@ packages: engines: {node: '>=4', npm: '>=6'} dev: true - /stream-parser@0.3.1: - resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==} - dependencies: - debug: 2.6.9 - transitivePeerDependencies: - - supports-color - dev: false - /stream-transform@2.1.3: resolution: {integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==} dependencies: From 644825845c11c8d100a9b0d16b69a23c165c529e Mon Sep 17 00:00:00 2001 From: Okuto Oyama <0910yama@gmail.com> Date: Tue, 12 Sep 2023 18:56:52 +0900 Subject: [PATCH 17/33] Removed ``; diff --git a/packages/astro/src/runtime/server/serialize.ts b/packages/astro/src/runtime/server/serialize.ts index b52c9e2156d9..0d754ebc8f18 100644 --- a/packages/astro/src/runtime/server/serialize.ts +++ b/packages/astro/src/runtime/server/serialize.ts @@ -1,5 +1,5 @@ -import type { AstroComponentMetadata } from '../../@types/astro'; -import type { ValueOf } from '../../type-utils'; +import type { AstroComponentMetadata } from '../../@types/astro.js'; +import type { ValueOf } from '../../type-utils.js'; const PROP_TYPE = { Value: 0, diff --git a/packages/astro/src/runtime/server/transition.ts b/packages/astro/src/runtime/server/transition.ts index 4a13c8126f95..17eece1d9819 100644 --- a/packages/astro/src/runtime/server/transition.ts +++ b/packages/astro/src/runtime/server/transition.ts @@ -1,4 +1,8 @@ -import type { SSRResult, TransitionAnimation, TransitionAnimationValue } from '../../@types/astro'; +import type { + SSRResult, + TransitionAnimation, + TransitionAnimationValue, +} from '../../@types/astro.js'; import { fade, slide } from '../../transitions/index.js'; import { markHTMLString } from './escape.js'; diff --git a/packages/astro/src/transitions/index.ts b/packages/astro/src/transitions/index.ts index 7d4711a32aec..0a58d2d4b48a 100644 --- a/packages/astro/src/transitions/index.ts +++ b/packages/astro/src/transitions/index.ts @@ -1,4 +1,4 @@ -import type { TransitionAnimationPair, TransitionDirectionalAnimations } from '../@types/astro'; +import type { TransitionAnimationPair, TransitionDirectionalAnimations } from '../@types/astro.js'; const EASE_IN_OUT_QUART = 'cubic-bezier(0.76, 0, 0.24, 1)'; diff --git a/packages/astro/src/vite-plugin-astro-server/base.ts b/packages/astro/src/vite-plugin-astro-server/base.ts index bb6a8a009c99..7bf57d10a394 100644 --- a/packages/astro/src/vite-plugin-astro-server/base.ts +++ b/packages/astro/src/vite-plugin-astro-server/base.ts @@ -1,5 +1,5 @@ import type * as vite from 'vite'; -import type { AstroSettings } from '../@types/astro'; +import type { AstroSettings } from '../@types/astro.js'; import * as fs from 'node:fs'; import type { Logger } from '../core/logger/core.js'; diff --git a/packages/astro/src/vite-plugin-astro-server/controller.ts b/packages/astro/src/vite-plugin-astro-server/controller.ts index 3004e68f34ce..279863613471 100644 --- a/packages/astro/src/vite-plugin-astro-server/controller.ts +++ b/packages/astro/src/vite-plugin-astro-server/controller.ts @@ -1,5 +1,5 @@ -import type { LoaderEvents, ModuleLoader } from '../core/module-loader/index'; -import type { ServerState } from './server-state'; +import type { LoaderEvents, ModuleLoader } from '../core/module-loader/index.js'; +import type { ServerState } from './server-state.js'; import { clearRouteError, diff --git a/packages/astro/src/vite-plugin-astro-server/css.ts b/packages/astro/src/vite-plugin-astro-server/css.ts index 093bda461fe3..46f1235bbab0 100644 --- a/packages/astro/src/vite-plugin-astro-server/css.ts +++ b/packages/astro/src/vite-plugin-astro-server/css.ts @@ -1,5 +1,5 @@ import type { RuntimeMode } from '../@types/astro.js'; -import type { ModuleLoader } from '../core/module-loader'; +import type { ModuleLoader } from '../core/module-loader/index.js'; import { viteID } from '../core/util.js'; import { isBuildableCSSRequest } from './util.js'; import { crawlGraph } from './vite.js'; diff --git a/packages/astro/src/vite-plugin-astro-server/devPipeline.ts b/packages/astro/src/vite-plugin-astro-server/devPipeline.ts index fbe9c0429646..e16b3d7e2300 100644 --- a/packages/astro/src/vite-plugin-astro-server/devPipeline.ts +++ b/packages/astro/src/vite-plugin-astro-server/devPipeline.ts @@ -4,11 +4,11 @@ import type { RuntimeMode, SSRLoadedRenderer, SSRManifest, -} from '../@types/astro'; -import type { Logger } from '../core/logger/core'; -import type { ModuleLoader } from '../core/module-loader'; +} from '../@types/astro.js'; +import type { Logger } from '../core/logger/core.js'; +import type { ModuleLoader } from '../core/module-loader/index.js'; import { Pipeline } from '../core/pipeline.js'; -import type { Environment } from '../core/render'; +import type { Environment } from '../core/render/index.js'; import { createEnvironment, loadRenderer } from '../core/render/index.js'; import { RouteCache } from '../core/render/route-cache.js'; import { isServerLikeOutput } from '../prerender/utils.js'; diff --git a/packages/astro/src/vite-plugin-astro-server/index.ts b/packages/astro/src/vite-plugin-astro-server/index.ts index b02c57e1c16f..97592d47a4f9 100644 --- a/packages/astro/src/vite-plugin-astro-server/index.ts +++ b/packages/astro/src/vite-plugin-astro-server/index.ts @@ -2,7 +2,7 @@ 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 { viteID } from '../core/util.js'; -import type DevPipeline from './devPipeline'; +import type DevPipeline from './devPipeline.js'; export async function preload({ pipeline, diff --git a/packages/astro/src/vite-plugin-astro-server/metadata.ts b/packages/astro/src/vite-plugin-astro-server/metadata.ts index 81e972dbaaf8..09e2373cf694 100644 --- a/packages/astro/src/vite-plugin-astro-server/metadata.ts +++ b/packages/astro/src/vite-plugin-astro-server/metadata.ts @@ -1,5 +1,5 @@ -import type { SSRComponentMetadata, SSRResult } from '../@types/astro'; -import type { ModuleInfo, ModuleLoader } from '../core/module-loader'; +import type { SSRComponentMetadata, SSRResult } from '../@types/astro.js'; +import type { ModuleInfo, ModuleLoader } from '../core/module-loader/index.js'; import { viteID } from '../core/util.js'; import { getAstroMetadata } from '../vite-plugin-astro/index.js'; import { crawlGraph } from './vite.js'; diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts index 80da6d787832..daa1c01e682b 100644 --- a/packages/astro/src/vite-plugin-astro-server/plugin.ts +++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts @@ -1,6 +1,6 @@ import type fs from 'node:fs'; import type * as vite from 'vite'; -import type { AstroSettings, ManifestData, SSRManifest } from '../@types/astro'; +import type { AstroSettings, ManifestData, SSRManifest } from '../@types/astro.js'; import { patchOverlay } from '../core/errors/overlay.js'; import type { Logger } from '../core/logger/core.js'; import { createViteLoader } from '../core/module-loader/index.js'; diff --git a/packages/astro/src/vite-plugin-astro-server/request.ts b/packages/astro/src/vite-plugin-astro-server/request.ts index cea9349e0c70..65e97b96ff1a 100644 --- a/packages/astro/src/vite-plugin-astro-server/request.ts +++ b/packages/astro/src/vite-plugin-astro-server/request.ts @@ -1,14 +1,14 @@ import type http from 'node:http'; -import type { ManifestData, SSRManifest } from '../@types/astro'; +import type { ManifestData, SSRManifest } from '../@types/astro.js'; import { collectErrorMetadata } from '../core/errors/dev/index.js'; import { createSafeError } from '../core/errors/index.js'; import * as msg from '../core/messages.js'; import { collapseDuplicateSlashes, removeTrailingForwardSlash } from '../core/path.js'; import { eventError, telemetry } from '../events/index.js'; import { isServerLikeOutput } from '../prerender/utils.js'; -import type { DevServerController } from './controller'; +import type { DevServerController } from './controller.js'; import { runWithErrorHandling } from './controller.js'; -import type DevPipeline from './devPipeline'; +import type DevPipeline from './devPipeline.js'; import { handle500Response } from './response.js'; import { handleRoute, matchRoute } from './route.js'; diff --git a/packages/astro/src/vite-plugin-astro-server/resolve.ts b/packages/astro/src/vite-plugin-astro-server/resolve.ts index f67d83576d6f..cbeda56b0c87 100644 --- a/packages/astro/src/vite-plugin-astro-server/resolve.ts +++ b/packages/astro/src/vite-plugin-astro-server/resolve.ts @@ -1,4 +1,4 @@ -import type { ModuleLoader } from '../core/module-loader'; +import type { ModuleLoader } from '../core/module-loader/index.js'; import { resolveIdToUrl } from '../core/util.js'; export function createResolve(loader: ModuleLoader, root: URL) { diff --git a/packages/astro/src/vite-plugin-astro-server/response.ts b/packages/astro/src/vite-plugin-astro-server/response.ts index 9e74a754ffe5..b1c9480956b4 100644 --- a/packages/astro/src/vite-plugin-astro-server/response.ts +++ b/packages/astro/src/vite-plugin-astro-server/response.ts @@ -1,6 +1,6 @@ import type http from 'node:http'; import type { ErrorWithMetadata } from '../core/errors/index.js'; -import type { ModuleLoader } from '../core/module-loader/index'; +import type { ModuleLoader } from '../core/module-loader/index.js'; import { Readable } from 'stream'; import { getSetCookiesFromResponse } from '../core/cookies/index.js'; diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index 120fc7722b80..da2c4b948a96 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -6,7 +6,7 @@ import type { RouteData, SSRElement, SSRManifest, -} from '../@types/astro'; +} from '../@types/astro.js'; import { AstroErrorData, isAstroError } from '../core/errors/index.js'; import { loadMiddleware } from '../core/middleware/loadMiddleware.js'; import { createRenderContext, getParamsAndProps, type SSROptions } from '../core/render/index.js'; diff --git a/packages/astro/src/vite-plugin-astro-server/scripts.ts b/packages/astro/src/vite-plugin-astro-server/scripts.ts index 55fe20254047..00bc4054b029 100644 --- a/packages/astro/src/vite-plugin-astro-server/scripts.ts +++ b/packages/astro/src/vite-plugin-astro-server/scripts.ts @@ -1,8 +1,8 @@ -import type { SSRElement } from '../@types/astro'; -import type { ModuleInfo, ModuleLoader } from '../core/module-loader'; +import type { SSRElement } from '../@types/astro.js'; +import type { ModuleInfo, ModuleLoader } from '../core/module-loader/index.js'; import { createModuleScriptElementWithSrc } from '../core/render/ssr-element.js'; import { rootRelativePath, viteID } from '../core/util.js'; -import type { PluginMetadata as AstroPluginMetadata } from '../vite-plugin-astro/types'; +import type { PluginMetadata as AstroPluginMetadata } from '../vite-plugin-astro/types.js'; import { crawlGraph } from './vite.js'; export async function getScriptsForURL( diff --git a/packages/astro/src/vite-plugin-astro-server/vite.ts b/packages/astro/src/vite-plugin-astro-server/vite.ts index ef6ffd308671..6c2bc2497af1 100644 --- a/packages/astro/src/vite-plugin-astro-server/vite.ts +++ b/packages/astro/src/vite-plugin-astro-server/vite.ts @@ -1,6 +1,6 @@ import npath from 'node:path'; import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from '../core/constants.js'; -import type { ModuleLoader, ModuleNode } from '../core/module-loader/index'; +import type { ModuleLoader, ModuleNode } from '../core/module-loader/index.js'; import { unwrapId } from '../core/util.js'; import { isCSSRequest } from './util.js'; diff --git a/packages/astro/src/vite-plugin-astro/compile.ts b/packages/astro/src/vite-plugin-astro/compile.ts index 6c6b797ec4c6..768d18d86832 100644 --- a/packages/astro/src/vite-plugin-astro/compile.ts +++ b/packages/astro/src/vite-plugin-astro/compile.ts @@ -1,5 +1,5 @@ import { transformWithEsbuild, type ESBuildTransformResult } from 'vite'; -import type { AstroConfig } from '../@types/astro'; +import type { AstroConfig } from '../@types/astro.js'; import { cachedCompilation, type CompileProps, type CompileResult } from '../core/compile/index.js'; import type { Logger } from '../core/logger/core.js'; import { getFileInfo } from '../vite-plugin-utils/index.js'; diff --git a/packages/astro/src/vite-plugin-astro/hmr.ts b/packages/astro/src/vite-plugin-astro/hmr.ts index 6502ae513d2a..65186af5e3f8 100644 --- a/packages/astro/src/vite-plugin-astro/hmr.ts +++ b/packages/astro/src/vite-plugin-astro/hmr.ts @@ -1,6 +1,6 @@ import { fileURLToPath } from 'node:url'; import type { HmrContext, ModuleNode } from 'vite'; -import type { AstroConfig } from '../@types/astro'; +import type { AstroConfig } from '../@types/astro.js'; import { cachedCompilation, invalidateCompilation, diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts index abeade65e2dd..1649d8069151 100644 --- a/packages/astro/src/vite-plugin-astro/index.ts +++ b/packages/astro/src/vite-plugin-astro/index.ts @@ -1,8 +1,8 @@ import type { SourceDescription } from 'rollup'; import type * as vite from 'vite'; -import type { AstroSettings } from '../@types/astro'; +import type { AstroSettings } from '../@types/astro.js'; import type { Logger } from '../core/logger/core.js'; -import type { PluginMetadata as AstroPluginMetadata } from './types'; +import type { PluginMetadata as AstroPluginMetadata } from './types.js'; import { normalizePath } from 'vite'; import { diff --git a/packages/astro/src/vite-plugin-astro/metadata.ts b/packages/astro/src/vite-plugin-astro/metadata.ts index ee66c12d0377..d0a2b3644b22 100644 --- a/packages/astro/src/vite-plugin-astro/metadata.ts +++ b/packages/astro/src/vite-plugin-astro/metadata.ts @@ -1,5 +1,5 @@ -import type { ModuleInfo } from '../core/module-loader'; -import type { PluginMetadata } from './types'; +import type { ModuleInfo } from '../core/module-loader/index.js'; +import type { PluginMetadata } from './types.js'; export function getAstroMetadata(modInfo: ModuleInfo): PluginMetadata['astro'] | undefined { if (modInfo.meta?.astro) { diff --git a/packages/astro/src/vite-plugin-astro/types.ts b/packages/astro/src/vite-plugin-astro/types.ts index 8b74d7f33865..49bbd5feaf1c 100644 --- a/packages/astro/src/vite-plugin-astro/types.ts +++ b/packages/astro/src/vite-plugin-astro/types.ts @@ -1,5 +1,5 @@ import type { TransformResult } from '@astrojs/compiler'; -import type { PropagationHint } from '../@types/astro'; +import type { PropagationHint } from '../@types/astro.js'; export interface PageOptions { prerender?: boolean; diff --git a/packages/astro/src/vite-plugin-config-alias/index.ts b/packages/astro/src/vite-plugin-config-alias/index.ts index 3df286047faf..8b8535af988c 100644 --- a/packages/astro/src/vite-plugin-config-alias/index.ts +++ b/packages/astro/src/vite-plugin-config-alias/index.ts @@ -1,6 +1,6 @@ import path from 'node:path'; import { normalizePath, type ResolvedConfig, type Plugin as VitePlugin } from 'vite'; -import type { AstroSettings } from '../@types/astro'; +import type { AstroSettings } from '../@types/astro.js'; type Alias = { find: RegExp; diff --git a/packages/astro/src/vite-plugin-env/index.ts b/packages/astro/src/vite-plugin-env/index.ts index 2b9f04cd4f1d..1958344e595c 100644 --- a/packages/astro/src/vite-plugin-env/index.ts +++ b/packages/astro/src/vite-plugin-env/index.ts @@ -2,7 +2,7 @@ import MagicString from 'magic-string'; import { fileURLToPath } from 'node:url'; import type * as vite from 'vite'; import { loadEnv } from 'vite'; -import type { AstroConfig, AstroSettings } from '../@types/astro'; +import type { AstroConfig, AstroSettings } from '../@types/astro.js'; interface EnvPluginOptions { settings: AstroSettings; diff --git a/packages/astro/src/vite-plugin-head/index.ts b/packages/astro/src/vite-plugin-head/index.ts index 9b0a7fb55929..f8a13f925d33 100644 --- a/packages/astro/src/vite-plugin-head/index.ts +++ b/packages/astro/src/vite-plugin-head/index.ts @@ -1,8 +1,8 @@ import type { ModuleInfo } from 'rollup'; import type * as vite from 'vite'; -import type { SSRComponentMetadata, SSRResult } from '../@types/astro'; +import type { SSRComponentMetadata, SSRResult } from '../@types/astro.js'; import type { AstroBuildPlugin } from '../core/build/plugin.js'; -import type { PluginMetadata } from '../vite-plugin-astro/types'; +import type { PluginMetadata } from '../vite-plugin-astro/types.js'; import { getTopLevelPages, walkParentInfos } from '../core/build/graph.js'; import type { BuildInternals } from '../core/build/internal.js'; diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts index deaccebef559..41cf08e42e8c 100644 --- a/packages/astro/src/vite-plugin-markdown/index.ts +++ b/packages/astro/src/vite-plugin-markdown/index.ts @@ -9,7 +9,7 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import type { Plugin } from 'vite'; import { normalizePath } from 'vite'; -import type { AstroSettings } from '../@types/astro'; +import type { AstroSettings } from '../@types/astro.js'; import { AstroError, AstroErrorData, MarkdownError } from '../core/errors/index.js'; import type { Logger } from '../core/logger/core.js'; import { isMarkdownFile, rootRelativePath } from '../core/util.js'; diff --git a/packages/astro/src/vite-plugin-mdx/index.ts b/packages/astro/src/vite-plugin-mdx/index.ts index b640d6c80170..5e2ce2a98436 100644 --- a/packages/astro/src/vite-plugin-mdx/index.ts +++ b/packages/astro/src/vite-plugin-mdx/index.ts @@ -1,8 +1,8 @@ import type { TransformResult } from 'rollup'; import { transformWithEsbuild, type Plugin, type ResolvedConfig } from 'vite'; -import type { AstroRenderer, AstroSettings } from '../@types/astro'; +import type { AstroRenderer, AstroSettings } from '../@types/astro.js'; import type { Logger } from '../core/logger/core.js'; -import type { PluginMetadata } from '../vite-plugin-astro/types'; +import type { PluginMetadata } from '../vite-plugin-astro/types.js'; import babel from '@babel/core'; import { CONTENT_FLAG, PROPAGATED_ASSET_FLAG } from '../content/index.js'; diff --git a/packages/astro/src/vite-plugin-utils/index.ts b/packages/astro/src/vite-plugin-utils/index.ts index 468360372133..51f0e6cc4c1c 100644 --- a/packages/astro/src/vite-plugin-utils/index.ts +++ b/packages/astro/src/vite-plugin-utils/index.ts @@ -1,6 +1,6 @@ import ancestor from 'common-ancestor-path'; import { fileURLToPath } from 'node:url'; -import type { AstroConfig } from '../@types/astro'; +import type { AstroConfig } from '../@types/astro.js'; import { appendExtension, appendForwardSlash, diff --git a/packages/astro/tsconfig.json b/packages/astro/tsconfig.json index d8bd9b19752b..e36a58321e89 100644 --- a/packages/astro/tsconfig.json +++ b/packages/astro/tsconfig.json @@ -4,9 +4,7 @@ "compilerOptions": { "allowJs": true, "declarationDir": "./dist", - "module": "ES2022", "outDir": "./dist", - "target": "ES2022", "jsx": "preserve", "types": ["@types/dom-view-transitions", "network-information-types"] } diff --git a/packages/astro/types.d.ts b/packages/astro/types.d.ts index 0939c828aac5..da48b5e1b66d 100644 --- a/packages/astro/types.d.ts +++ b/packages/astro/types.d.ts @@ -1,5 +1,5 @@ import './astro-jsx'; -import { AstroBuiltinAttributes } from './dist/@types/astro'; +import { AstroBuiltinAttributes } from './dist/@types/astro.js'; /** Any supported HTML or SVG element name, as defined by the HTML specification */ export type HTMLTag = keyof astroHTML.JSX.DefinedIntrinsicElements; diff --git a/packages/create-astro/package.json b/packages/create-astro/package.json index b9641b1d5b20..7a341d125f4e 100644 --- a/packages/create-astro/package.json +++ b/packages/create-astro/package.json @@ -31,7 +31,7 @@ "//a": "MOST PACKAGES SHOULD GO IN DEV_DEPENDENCIES! THEY WILL BE BUNDLED.", "//b": "DEPENDENCIES IS FOR UNBUNDLED PACKAGES", "dependencies": { - "@astrojs/cli-kit": "^0.2.5", + "@astrojs/cli-kit": "^0.3.0", "giget": "1.1.2" }, "devDependencies": { diff --git a/packages/create-astro/src/actions/dependencies.ts b/packages/create-astro/src/actions/dependencies.ts index f05e9e93ae02..1e731099c010 100644 --- a/packages/create-astro/src/actions/dependencies.ts +++ b/packages/create-astro/src/actions/dependencies.ts @@ -3,7 +3,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { error, info, spinner, title } from '../messages.js'; import { shell } from '../shell.js'; -import type { Context } from './context'; +import type { Context } from './context.js'; export async function dependencies( ctx: Pick diff --git a/packages/create-astro/src/actions/git.ts b/packages/create-astro/src/actions/git.ts index c2a59b0b32f1..dd703b1f559c 100644 --- a/packages/create-astro/src/actions/git.ts +++ b/packages/create-astro/src/actions/git.ts @@ -1,6 +1,6 @@ import fs from 'node:fs'; import path from 'node:path'; -import type { Context } from './context'; +import type { Context } from './context.js'; import { color } from '@astrojs/cli-kit'; import { error, info, spinner, title } from '../messages.js'; diff --git a/packages/create-astro/src/actions/intro.ts b/packages/create-astro/src/actions/intro.ts index 2a6e866c8046..1781fb260109 100644 --- a/packages/create-astro/src/actions/intro.ts +++ b/packages/create-astro/src/actions/intro.ts @@ -1,4 +1,4 @@ -import type { Context } from './context'; +import type { Context } from './context.js'; import { color, label } from '@astrojs/cli-kit'; import { random } from '@astrojs/cli-kit/utils'; diff --git a/packages/create-astro/src/actions/next-steps.ts b/packages/create-astro/src/actions/next-steps.ts index ac69b7e9407a..86907abf54bc 100644 --- a/packages/create-astro/src/actions/next-steps.ts +++ b/packages/create-astro/src/actions/next-steps.ts @@ -1,5 +1,5 @@ import path from 'node:path'; -import type { Context } from './context'; +import type { Context } from './context.js'; import { nextSteps, say } from '../messages.js'; diff --git a/packages/create-astro/src/actions/project-name.ts b/packages/create-astro/src/actions/project-name.ts index b7099d9bbbed..533240efd42d 100644 --- a/packages/create-astro/src/actions/project-name.ts +++ b/packages/create-astro/src/actions/project-name.ts @@ -1,4 +1,4 @@ -import type { Context } from './context'; +import type { Context } from './context.js'; import { color, generateProjectName } from '@astrojs/cli-kit'; import path from 'node:path'; diff --git a/packages/create-astro/src/actions/template.ts b/packages/create-astro/src/actions/template.ts index 3d3d1075d79e..60a77104f028 100644 --- a/packages/create-astro/src/actions/template.ts +++ b/packages/create-astro/src/actions/template.ts @@ -1,4 +1,4 @@ -import type { Context } from './context'; +import type { Context } from './context.js'; import { color } from '@astrojs/cli-kit'; import { downloadTemplate } from 'giget'; diff --git a/packages/create-astro/src/actions/typescript.ts b/packages/create-astro/src/actions/typescript.ts index 8c0e2716ceae..97ae243032b7 100644 --- a/packages/create-astro/src/actions/typescript.ts +++ b/packages/create-astro/src/actions/typescript.ts @@ -1,4 +1,4 @@ -import type { Context } from './context'; +import type { Context } from './context.js'; import { color } from '@astrojs/cli-kit'; import fs from 'node:fs'; diff --git a/packages/create-astro/src/actions/verify.ts b/packages/create-astro/src/actions/verify.ts index 2df256ae9c0a..ac3eae484ee1 100644 --- a/packages/create-astro/src/actions/verify.ts +++ b/packages/create-astro/src/actions/verify.ts @@ -1,4 +1,4 @@ -import type { Context } from './context'; +import type { Context } from './context.js'; import { color } from '@astrojs/cli-kit'; import dns from 'node:dns/promises'; diff --git a/packages/create-astro/tsconfig.json b/packages/create-astro/tsconfig.json index 1ab34c5a2010..18443cddf207 100644 --- a/packages/create-astro/tsconfig.json +++ b/packages/create-astro/tsconfig.json @@ -1,13 +1,7 @@ { "extends": "../../tsconfig.base.json", - "include": ["src", "index.d.ts"], + "include": ["src"], "compilerOptions": { - "allowJs": true, - "emitDeclarationOnly": false, - "noEmit": true, - "target": "ES2022", - "module": "ES2022", - "outDir": "./dist", - "declarationDir": "./dist/types" + "outDir": "./dist" } } diff --git a/packages/integrations/alpinejs/tsconfig.json b/packages/integrations/alpinejs/tsconfig.json index af1b43564edc..1504b4b6dfa4 100644 --- a/packages/integrations/alpinejs/tsconfig.json +++ b/packages/integrations/alpinejs/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", - "outDir": "./dist", - "target": "ES2022" + "outDir": "./dist" } } diff --git a/packages/integrations/cloudflare/package.json b/packages/integrations/cloudflare/package.json index 1fc188e0202b..ef17b0ec74ea 100644 --- a/packages/integrations/cloudflare/package.json +++ b/packages/integrations/cloudflare/package.json @@ -41,19 +41,20 @@ "dependencies": { "@astrojs/underscore-redirects": "workspace:*", "@cloudflare/workers-types": "^4.20230821.0", - "esbuild": "^0.19.2", - "tiny-glob": "^0.2.9", - "find-up": "^6.3.0", "@iarna/toml": "^2.2.5", - "dotenv": "^16.3.1", "@miniflare/cache": "^2.14.1", "@miniflare/shared": "^2.14.1", - "@miniflare/storage-memory": "^2.14.1" + "@miniflare/storage-memory": "^2.14.1", + "dotenv": "^16.3.1", + "esbuild": "^0.19.2", + "find-up": "^6.3.0", + "tiny-glob": "^0.2.9" }, "peerDependencies": { "astro": "workspace:^3.0.13" }, "devDependencies": { + "@types/iarna__toml": "^2.0.2", "astro": "workspace:*", "astro-scripts": "workspace:*", "chai": "^4.3.7", diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index c70c9c5aab1f..b64d986affa2 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -14,8 +14,8 @@ import { fileURLToPath, pathToFileURL } from 'node:url'; import glob from 'tiny-glob'; import { getEnvVars } from './parser.js'; -export type { AdvancedRuntime } from './server.advanced'; -export type { DirectoryRuntime } from './server.directory'; +export type { AdvancedRuntime } from './server.advanced.js'; +export type { DirectoryRuntime } from './server.directory.js'; type Options = { mode?: 'directory' | 'advanced'; diff --git a/packages/integrations/cloudflare/tsconfig.json b/packages/integrations/cloudflare/tsconfig.json index af1b43564edc..1504b4b6dfa4 100644 --- a/packages/integrations/cloudflare/tsconfig.json +++ b/packages/integrations/cloudflare/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", - "outDir": "./dist", - "target": "ES2022" + "outDir": "./dist" } } diff --git a/packages/integrations/deno/tsconfig.json b/packages/integrations/deno/tsconfig.json index f3c96447a6a1..d999917aafdd 100644 --- a/packages/integrations/deno/tsconfig.json +++ b/packages/integrations/deno/tsconfig.json @@ -2,11 +2,9 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, "module": "ES2022", "outDir": "./dist", - "target": "ES2022", - // TODO: Due to the shim for Deno imports in `server.ts`, we can't use moduleResolution: 'bundler' or the types get very weird. + // TODO: Due to the shim for Deno imports in `server.ts`, we can't use moduleResolution: 'node16' or the types get very weird. "moduleResolution": "Node" } } diff --git a/packages/integrations/lit/tsconfig.json b/packages/integrations/lit/tsconfig.json index af1b43564edc..1504b4b6dfa4 100644 --- a/packages/integrations/lit/tsconfig.json +++ b/packages/integrations/lit/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", - "outDir": "./dist", - "target": "ES2022" + "outDir": "./dist" } } diff --git a/packages/integrations/markdoc/src/html/index.ts b/packages/integrations/markdoc/src/html/index.ts index 8798d3c9a224..3f947736cdf6 100644 --- a/packages/integrations/markdoc/src/html/index.ts +++ b/packages/integrations/markdoc/src/html/index.ts @@ -1,2 +1,2 @@ -export { htmlTag } from './tagdefs/html.tag'; -export { htmlTokenTransform } from './transform/html-token-transform'; +export { htmlTag } from './tagdefs/html.tag.js'; +export { htmlTokenTransform } from './transform/html-token-transform.js'; diff --git a/packages/integrations/markdoc/src/html/transform/html-token-transform.ts b/packages/integrations/markdoc/src/html/transform/html-token-transform.ts index cfa511a9f90f..10796cdc0c85 100644 --- a/packages/integrations/markdoc/src/html/transform/html-token-transform.ts +++ b/packages/integrations/markdoc/src/html/transform/html-token-transform.ts @@ -1,5 +1,6 @@ import type { Tokenizer } from '@markdoc/markdoc'; import { Parser } from 'htmlparser2'; +// @ts-expect-error This type isn't exported import type * as Token from 'markdown-it/lib/token'; export function htmlTokenTransform(tokenizer: Tokenizer, tokens: Token[]): Token[] { diff --git a/packages/integrations/markdoc/tsconfig.json b/packages/integrations/markdoc/tsconfig.json index af1b43564edc..1504b4b6dfa4 100644 --- a/packages/integrations/markdoc/tsconfig.json +++ b/packages/integrations/markdoc/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", - "outDir": "./dist", - "target": "ES2022" + "outDir": "./dist" } } diff --git a/packages/integrations/mdx/tsconfig.json b/packages/integrations/mdx/tsconfig.json index af1b43564edc..1504b4b6dfa4 100644 --- a/packages/integrations/mdx/tsconfig.json +++ b/packages/integrations/mdx/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", - "outDir": "./dist", - "target": "ES2022" + "outDir": "./dist" } } diff --git a/packages/integrations/netlify/tsconfig.json b/packages/integrations/netlify/tsconfig.json index 66b0102c718d..4095e9b83248 100644 --- a/packages/integrations/netlify/tsconfig.json +++ b/packages/integrations/netlify/tsconfig.json @@ -2,11 +2,7 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", "outDir": "./dist", - "target": "ES2022", - "typeRoots": ["node_modules/@types", "node_modules/@netlify"], - "allowImportingTsExtensions": true + "typeRoots": ["node_modules/@types", "node_modules/@netlify"] } } diff --git a/packages/integrations/node/src/index.ts b/packages/integrations/node/src/index.ts index 5afc49f6a679..5978371e466d 100644 --- a/packages/integrations/node/src/index.ts +++ b/packages/integrations/node/src/index.ts @@ -1,6 +1,6 @@ import type { AstroAdapter, AstroIntegration } from 'astro'; import { AstroError } from 'astro/errors'; -import type { Options, UserOptions } from './types'; +import type { Options, UserOptions } from './types.js'; export function getAdapter(options: Options): AstroAdapter { return { name: '@astrojs/node', diff --git a/packages/integrations/node/src/nodeMiddleware.ts b/packages/integrations/node/src/nodeMiddleware.ts index 1e0aaea0fc8a..32b8020dc199 100644 --- a/packages/integrations/node/src/nodeMiddleware.ts +++ b/packages/integrations/node/src/nodeMiddleware.ts @@ -1,9 +1,9 @@ import type { NodeApp } from 'astro/app/node'; import type { ServerResponse } from 'node:http'; import type { Readable } from 'stream'; -import { createOutgoingHttpHeaders } from './createOutgoingHttpHeaders'; -import { responseIterator } from './response-iterator'; -import type { ErrorHandlerParams, Options, RequestHandlerParams } from './types'; +import { createOutgoingHttpHeaders } from './createOutgoingHttpHeaders.js'; +import { responseIterator } from './response-iterator.js'; +import type { ErrorHandlerParams, Options, RequestHandlerParams } from './types.js'; // Disable no-unused-vars to avoid breaking signature change export default function (app: NodeApp, mode: Options['mode']) { diff --git a/packages/integrations/node/src/preview.ts b/packages/integrations/node/src/preview.ts index 77560d734687..70ed5469875b 100644 --- a/packages/integrations/node/src/preview.ts +++ b/packages/integrations/node/src/preview.ts @@ -4,7 +4,7 @@ import type http from 'node:http'; import { fileURLToPath } from 'node:url'; import { getNetworkAddress } from './get-network-address.js'; import { createServer } from './http-server.js'; -import type { createExports } from './server'; +import type { createExports } from './server.js'; const preview: CreatePreviewServer = async function ({ client, diff --git a/packages/integrations/node/src/server.ts b/packages/integrations/node/src/server.ts index 04c81c2d1f85..90bf8c44c46f 100644 --- a/packages/integrations/node/src/server.ts +++ b/packages/integrations/node/src/server.ts @@ -2,7 +2,7 @@ import type { SSRManifest } from 'astro'; import { NodeApp, applyPolyfills } from 'astro/app/node'; import middleware from './nodeMiddleware.js'; import startServer from './standalone.js'; -import type { Options } from './types'; +import type { Options } from './types.js'; applyPolyfills(); export function createExports(manifest: SSRManifest, options: Options) { diff --git a/packages/integrations/node/src/standalone.ts b/packages/integrations/node/src/standalone.ts index 66d1b9c6a244..abe40ff5cced 100644 --- a/packages/integrations/node/src/standalone.ts +++ b/packages/integrations/node/src/standalone.ts @@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url'; import { getNetworkAddress } from './get-network-address.js'; import { createServer } from './http-server.js'; import middleware from './nodeMiddleware.js'; -import type { Options } from './types'; +import type { Options } from './types.js'; function resolvePaths(options: Options) { const clientURLRaw = new URL(options.client); diff --git a/packages/integrations/node/tsconfig.json b/packages/integrations/node/tsconfig.json index af1b43564edc..1504b4b6dfa4 100644 --- a/packages/integrations/node/tsconfig.json +++ b/packages/integrations/node/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", - "outDir": "./dist", - "target": "ES2022" + "outDir": "./dist" } } diff --git a/packages/integrations/partytown/tsconfig.json b/packages/integrations/partytown/tsconfig.json index af1b43564edc..1504b4b6dfa4 100644 --- a/packages/integrations/partytown/tsconfig.json +++ b/packages/integrations/partytown/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", - "outDir": "./dist", - "target": "ES2022" + "outDir": "./dist" } } diff --git a/packages/integrations/preact/src/client.ts b/packages/integrations/preact/src/client.ts index b64431130b91..050a86f8a2f7 100644 --- a/packages/integrations/preact/src/client.ts +++ b/packages/integrations/preact/src/client.ts @@ -1,6 +1,6 @@ import { h, hydrate, render } from 'preact'; import StaticHtml from './static-html.js'; -import type { SignalLike } from './types'; +import type { SignalLike } from './types.js'; const sharedSignalMap = new Map(); diff --git a/packages/integrations/preact/src/context.ts b/packages/integrations/preact/src/context.ts index c711017c4d28..4d2398d288af 100644 --- a/packages/integrations/preact/src/context.ts +++ b/packages/integrations/preact/src/context.ts @@ -1,4 +1,4 @@ -import type { PropNameToSignalMap, RendererContext, SignalLike } from './types'; +import type { PropNameToSignalMap, RendererContext, SignalLike } from './types.js'; export type Context = { id: string; diff --git a/packages/integrations/preact/src/index.ts b/packages/integrations/preact/src/index.ts index 85c3c66ec659..85f9bed0f593 100644 --- a/packages/integrations/preact/src/index.ts +++ b/packages/integrations/preact/src/index.ts @@ -1,4 +1,4 @@ -import preact, { type PreactPluginOptions as VitePreactPluginOptions } from '@preact/preset-vite'; +import { preact, type PreactPluginOptions as VitePreactPluginOptions } from '@preact/preset-vite'; import type { AstroIntegration, AstroRenderer, ViteUserConfig } from 'astro'; import { fileURLToPath } from 'node:url'; diff --git a/packages/integrations/preact/src/server.ts b/packages/integrations/preact/src/server.ts index e55d29d1c956..a395433c9bad 100644 --- a/packages/integrations/preact/src/server.ts +++ b/packages/integrations/preact/src/server.ts @@ -1,10 +1,10 @@ import type { AstroComponentMetadata } from 'astro'; import { Component as BaseComponent, h, type VNode } from 'preact'; -import render from 'preact-render-to-string'; +import { render } from 'preact-render-to-string'; import { getContext } from './context.js'; import { restoreSignalsOnProps, serializeSignals } from './signals.js'; import StaticHtml from './static-html.js'; -import type { AstroPreactAttrs, RendererContext } from './types'; +import type { AstroPreactAttrs, RendererContext } from './types.js'; const slotName = (str: string) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase()); diff --git a/packages/integrations/preact/src/signals.ts b/packages/integrations/preact/src/signals.ts index 3fa1529f4004..ef3a4b70a4ee 100644 --- a/packages/integrations/preact/src/signals.ts +++ b/packages/integrations/preact/src/signals.ts @@ -1,6 +1,6 @@ -import type { Context } from './context'; +import type { Context } from './context.js'; import { incrementId } from './context.js'; -import type { AstroPreactAttrs, PropNameToSignalMap, SignalLike } from './types'; +import type { AstroPreactAttrs, PropNameToSignalMap, SignalLike } from './types.js'; function isSignal(x: any): x is SignalLike { return x != null && typeof x === 'object' && typeof x.peek === 'function' && 'value' in x; diff --git a/packages/integrations/preact/tsconfig.json b/packages/integrations/preact/tsconfig.json index af1b43564edc..1504b4b6dfa4 100644 --- a/packages/integrations/preact/tsconfig.json +++ b/packages/integrations/preact/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", - "outDir": "./dist", - "target": "ES2022" + "outDir": "./dist" } } diff --git a/packages/integrations/prefetch/tsconfig.json b/packages/integrations/prefetch/tsconfig.json index 6457dfe8c831..dadc37a826de 100644 --- a/packages/integrations/prefetch/tsconfig.json +++ b/packages/integrations/prefetch/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src", "@types"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", - "outDir": "./dist", - "target": "ES2022" + "outDir": "./dist" } } diff --git a/packages/integrations/react/src/index.ts b/packages/integrations/react/src/index.ts index d5f6965224e4..e4b9778808cf 100644 --- a/packages/integrations/react/src/index.ts +++ b/packages/integrations/react/src/index.ts @@ -7,6 +7,7 @@ export type ReactIntegrationOptions = Pick str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase()); diff --git a/packages/integrations/solid/tsconfig.json b/packages/integrations/solid/tsconfig.json index af1b43564edc..1504b4b6dfa4 100644 --- a/packages/integrations/solid/tsconfig.json +++ b/packages/integrations/solid/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", - "outDir": "./dist", - "target": "ES2022" + "outDir": "./dist" } } diff --git a/packages/integrations/svelte/tsconfig.json b/packages/integrations/svelte/tsconfig.json index af1b43564edc..5742d1f6efd2 100644 --- a/packages/integrations/svelte/tsconfig.json +++ b/packages/integrations/svelte/tsconfig.json @@ -2,9 +2,7 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", "outDir": "./dist", - "target": "ES2022" + "verbatimModuleSyntax": false } } diff --git a/packages/integrations/tailwind/src/index.ts b/packages/integrations/tailwind/src/index.ts index df0f01723abe..700f16937d2c 100644 --- a/packages/integrations/tailwind/src/index.ts +++ b/packages/integrations/tailwind/src/index.ts @@ -33,6 +33,7 @@ async function getViteConfiguration( const postcssOptions = postcssConfigResult?.options ?? {}; const postcssPlugins = postcssConfigResult?.plugins?.slice() ?? []; + // @ts-expect-error Tailwind plugin types are wrong postcssPlugins.push(tailwindPlugin(tailwindConfigPath) as ResultPlugin); postcssPlugins.push(autoprefixerPlugin()); diff --git a/packages/integrations/tailwind/tsconfig.json b/packages/integrations/tailwind/tsconfig.json index af1b43564edc..1504b4b6dfa4 100644 --- a/packages/integrations/tailwind/tsconfig.json +++ b/packages/integrations/tailwind/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", - "outDir": "./dist", - "target": "ES2022" + "outDir": "./dist" } } diff --git a/packages/integrations/vercel/src/image/build-service.ts b/packages/integrations/vercel/src/image/build-service.ts index 63a37a5fee89..0e45167d4780 100644 --- a/packages/integrations/vercel/src/image/build-service.ts +++ b/packages/integrations/vercel/src/image/build-service.ts @@ -1,5 +1,5 @@ import type { ExternalImageService } from 'astro'; -import { isESMImportedImage, sharedValidateOptions } from './shared'; +import { isESMImportedImage, sharedValidateOptions } from './shared.js'; const service: ExternalImageService = { validateOptions: (options, serviceOptions) => diff --git a/packages/integrations/vercel/src/image/dev-service.ts b/packages/integrations/vercel/src/image/dev-service.ts index 72eb7ca0bfad..a335c8d23295 100644 --- a/packages/integrations/vercel/src/image/dev-service.ts +++ b/packages/integrations/vercel/src/image/dev-service.ts @@ -1,6 +1,6 @@ import type { LocalImageService } from 'astro'; import squooshService from 'astro/assets/services/squoosh'; -import { sharedValidateOptions } from './shared'; +import { sharedValidateOptions } from './shared.js'; const service: LocalImageService = { validateOptions: (options, serviceOptions) => diff --git a/packages/integrations/vercel/src/serverless/entrypoint.ts b/packages/integrations/vercel/src/serverless/entrypoint.ts index f132d71f346c..7b548dc37bfa 100644 --- a/packages/integrations/vercel/src/serverless/entrypoint.ts +++ b/packages/integrations/vercel/src/serverless/entrypoint.ts @@ -3,8 +3,8 @@ import { App } from 'astro/app'; import { applyPolyfills } from 'astro/app/node'; import type { IncomingMessage, ServerResponse } from 'node:http'; -import { ASTRO_LOCALS_HEADER } from './adapter'; -import { getRequest, setResponse } from './request-transform'; +import { ASTRO_LOCALS_HEADER } from './adapter.js'; +import { getRequest, setResponse } from './request-transform.js'; applyPolyfills(); diff --git a/packages/integrations/vercel/tsconfig.json b/packages/integrations/vercel/tsconfig.json index af1b43564edc..1504b4b6dfa4 100644 --- a/packages/integrations/vercel/tsconfig.json +++ b/packages/integrations/vercel/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", - "outDir": "./dist", - "target": "ES2022" + "outDir": "./dist" } } diff --git a/packages/integrations/vue/tsconfig.json b/packages/integrations/vue/tsconfig.json index af1b43564edc..5742d1f6efd2 100644 --- a/packages/integrations/vue/tsconfig.json +++ b/packages/integrations/vue/tsconfig.json @@ -2,9 +2,7 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "module": "ES2022", "outDir": "./dist", - "target": "ES2022" + "verbatimModuleSyntax": false } } diff --git a/packages/internal-helpers/tsconfig.json b/packages/internal-helpers/tsconfig.json index fd652e629fc1..18443cddf207 100644 --- a/packages/internal-helpers/tsconfig.json +++ b/packages/internal-helpers/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "target": "ES2022", - "module": "ES2022", "outDir": "./dist" } } diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts index c54826bdc656..d81d1702ec00 100644 --- a/packages/markdown/remark/src/index.ts +++ b/packages/markdown/remark/src/index.ts @@ -3,7 +3,7 @@ import type { MarkdownRenderingOptions, MarkdownRenderingResult, MarkdownVFile, -} from './types'; +} from './types.js'; import { toRemarkInitializeAstroData } from './frontmatter-injection.js'; import { loadPlugins } from './load-plugins.js'; diff --git a/packages/markdown/remark/src/remark-collect-images.ts b/packages/markdown/remark/src/remark-collect-images.ts index a9f524f7ac69..ecaa82d1dca1 100644 --- a/packages/markdown/remark/src/remark-collect-images.ts +++ b/packages/markdown/remark/src/remark-collect-images.ts @@ -1,6 +1,6 @@ import type { Image } from 'mdast'; import { visit } from 'unist-util-visit'; -import type { MarkdownVFile } from './types'; +import type { MarkdownVFile } from './types.js'; export function remarkCollectImages() { return function (tree: any, vfile: MarkdownVFile) { diff --git a/packages/markdown/remark/tsconfig.json b/packages/markdown/remark/tsconfig.json index 9a8c6d8cb545..1504b4b6dfa4 100644 --- a/packages/markdown/remark/tsconfig.json +++ b/packages/markdown/remark/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "target": "ES2022", - "module": "ES2022", "outDir": "./dist" } } diff --git a/packages/telemetry/package.json b/packages/telemetry/package.json index 8f8a634bf3b4..d4082091d171 100644 --- a/packages/telemetry/package.json +++ b/packages/telemetry/package.json @@ -2,7 +2,7 @@ "name": "@astrojs/telemetry", "version": "3.0.1", "type": "module", - "types": "./dist/types/index.d.ts", + "types": "./dist/index.d.ts", "author": "withastro", "license": "MIT", "repository": { @@ -14,7 +14,7 @@ "homepage": "https://astro.build", "exports": { ".": { - "types": "./dist/types/index.d.ts", + "types": "./dist/index.d.ts", "default": "./dist/index.js" }, "./package.json": "./package.json" diff --git a/packages/telemetry/tsconfig.json b/packages/telemetry/tsconfig.json index 451badc02b5d..18443cddf207 100644 --- a/packages/telemetry/tsconfig.json +++ b/packages/telemetry/tsconfig.json @@ -2,10 +2,6 @@ "extends": "../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "target": "ES2022", - "module": "ES2022", - "outDir": "./dist", - "declarationDir": "./dist/types" + "outDir": "./dist" } } diff --git a/packages/underscore-redirects/src/print.ts b/packages/underscore-redirects/src/print.ts index 5d7bd238785a..2a9bec257829 100644 --- a/packages/underscore-redirects/src/print.ts +++ b/packages/underscore-redirects/src/print.ts @@ -1,4 +1,4 @@ -import type { RedirectDefinition } from './redirects'; +import type { RedirectDefinition } from './redirects.js'; /** * Pretty print a list of definitions into the output format. Keeps diff --git a/packages/underscore-redirects/tsconfig.json b/packages/underscore-redirects/tsconfig.json index fd652e629fc1..18443cddf207 100644 --- a/packages/underscore-redirects/tsconfig.json +++ b/packages/underscore-redirects/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "allowJs": true, - "target": "ES2022", - "module": "ES2022", "outDir": "./dist" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 470bd046b480..08cbf364366a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3566,8 +3566,8 @@ importers: packages/create-astro: dependencies: '@astrojs/cli-kit': - specifier: ^0.2.5 - version: 0.2.5 + specifier: ^0.3.0 + version: 0.3.0 giget: specifier: 1.1.2 version: 1.1.2 @@ -3635,6 +3635,9 @@ importers: specifier: ^0.2.9 version: 0.2.9 devDependencies: + '@types/iarna__toml': + specifier: ^2.0.2 + version: 2.0.2 astro: specifier: workspace:* version: link:../../astro @@ -5176,8 +5179,9 @@ packages: - prettier-plugin-astro dev: true - /@astrojs/cli-kit@0.2.5: - resolution: {integrity: sha512-j6zpNUjtHJGEIKkTrTPvQD3G/sJUKyseJty42iVR3HqytzqHwLK165vptdT4NZKfZ082yLnUtsOXxRyIdfm/AQ==} + /@astrojs/cli-kit@0.3.0: + resolution: {integrity: sha512-nil0Kz2xuzR3xQX+FVHg2W8g+FvbeUeoCeU53duQjAFuHRJrbqWRmgfjYeM6f2780dsSuGiYMXmY+IaJqaqiaw==} + engines: {node: '>=18.14.1'} dependencies: chalk: 5.3.0 log-update: 5.0.1 @@ -8797,6 +8801,12 @@ packages: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} dev: true + /@types/iarna__toml@2.0.2: + resolution: {integrity: sha512-Q3obxKhBLVVbEQ8zsAmsQVobAAZhi8dFFFjF0q5xKXiaHvH8IkSxcbM27e46M9feUMieR03SPpmp5CtaNzpdBg==} + dependencies: + '@types/node': 18.17.8 + dev: true + /@types/is-ci@3.0.0: resolution: {integrity: sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ==} dependencies: diff --git a/tsconfig.base.json b/tsconfig.base.json index 337005ad49fa..0349af8a150b 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,10 +1,12 @@ { + "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "declaration": true, "emitDeclarationOnly": true, "strict": true, - // All packages are built with ESBuild, so we can use `moduleResolution: 'bundler'` - "moduleResolution": "Bundler", + "moduleResolution": "Node16", + "target": "ES2022", + "module": "Node16", "esModuleInterop": true, "skipLibCheck": true, "verbatimModuleSyntax": true From 9552ef1481241b00c458f18ecc5ba0af9e866efd Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Wed, 13 Sep 2023 22:58:18 +0800 Subject: [PATCH 30/33] chore(create-astro): Added create project using `pnpm` (#8340) --- packages/create-astro/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/create-astro/README.md b/packages/create-astro/README.md index 60baa62a1aa9..ba406d942698 100644 --- a/packages/create-astro/README.md +++ b/packages/create-astro/README.md @@ -14,6 +14,12 @@ npm create astro@latest yarn create astro ``` +**With PNPM:** + +```bash +pnpm create astro +``` + `create-astro` automatically runs in _interactive_ mode, but you can also specify your project name and template with command line arguments. ```bash @@ -22,6 +28,9 @@ npm create astro@latest my-astro-project -- --template minimal # yarn yarn create astro my-astro-project --template minimal + +# pnpm +pnpm create astro my-astro-project --template minimal ``` [Check out the full list][examples] of example templates, available on GitHub. From 2c4fc878bece36b7fcf1470419c7ce6f1e1e95d0 Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Wed, 13 Sep 2023 17:27:16 +0200 Subject: [PATCH 31/33] Support AVIF input assets (#8518) --- .changeset/five-doors-love.md | 5 + packages/astro/client.d.ts | 4 + packages/astro/package.json | 2 + packages/astro/src/assets/consts.ts | 10 +- packages/astro/src/assets/utils/metadata.ts | 9 +- packages/astro/src/assets/vendor/README.md | 3 - .../src/assets/vendor/image-size/LICENSE | 9 - .../src/assets/vendor/image-size/detector.ts | 30 --- .../src/assets/vendor/image-size/index.ts | 146 ------------ .../src/assets/vendor/image-size/readUInt.ts | 10 - .../src/assets/vendor/image-size/types.ts | 38 --- .../src/assets/vendor/image-size/types/bmp.ts | 14 -- .../src/assets/vendor/image-size/types/cur.ts | 16 -- .../src/assets/vendor/image-size/types/dds.ts | 14 -- .../src/assets/vendor/image-size/types/gif.ts | 16 -- .../assets/vendor/image-size/types/icns.ts | 113 --------- .../src/assets/vendor/image-size/types/ico.ts | 76 ------ .../vendor/image-size/types/interface.ts | 15 -- .../src/assets/vendor/image-size/types/j2c.ts | 15 -- .../src/assets/vendor/image-size/types/jp2.ts | 61 ----- .../src/assets/vendor/image-size/types/jpg.ts | 151 ------------ .../src/assets/vendor/image-size/types/ktx.ts | 16 -- .../src/assets/vendor/image-size/types/png.ts | 36 --- .../src/assets/vendor/image-size/types/pnm.ts | 80 ------- .../src/assets/vendor/image-size/types/psd.ts | 14 -- .../src/assets/vendor/image-size/types/svg.ts | 106 --------- .../assets/vendor/image-size/types/tiff.ts | 115 --------- .../assets/vendor/image-size/types/webp.ts | 65 ----- .../astro/src/assets/vendor/queue/LICENSE | 8 - .../astro/src/assets/vendor/queue/queue.js | 225 ------------------ .../astro/src/assets/vite-plugin-assets.ts | 2 +- packages/astro/test/core-image.test.js | 16 ++ .../core-image/src/assets/light_walrus.avif | Bin 0 -> 19439 bytes .../fixtures/core-image/src/pages/avif.astro | 5 + pnpm-lock.yaml | 63 ++++- 35 files changed, 102 insertions(+), 1406 deletions(-) create mode 100644 .changeset/five-doors-love.md delete mode 100644 packages/astro/src/assets/vendor/README.md delete mode 100644 packages/astro/src/assets/vendor/image-size/LICENSE delete mode 100644 packages/astro/src/assets/vendor/image-size/detector.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/index.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/readUInt.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/bmp.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/cur.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/dds.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/gif.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/icns.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/ico.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/interface.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/j2c.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/jp2.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/jpg.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/ktx.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/png.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/pnm.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/psd.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/svg.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/tiff.ts delete mode 100644 packages/astro/src/assets/vendor/image-size/types/webp.ts delete mode 100644 packages/astro/src/assets/vendor/queue/LICENSE delete mode 100644 packages/astro/src/assets/vendor/queue/queue.js create mode 100644 packages/astro/test/fixtures/core-image/src/assets/light_walrus.avif create mode 100644 packages/astro/test/fixtures/core-image/src/pages/avif.astro diff --git a/.changeset/five-doors-love.md b/.changeset/five-doors-love.md new file mode 100644 index 000000000000..92b5d8afccef --- /dev/null +++ b/.changeset/five-doors-love.md @@ -0,0 +1,5 @@ +--- +'astro': minor +--- + +Adds support for using AVIF (`.avif`) files with the Image component. Importing an AVIF file will now correctly return the same object shape as other image file types. See the [Image docs](https://docs.astro.build/en/guides/images/#update-existing-img-tags) for more information on the different properties available on the returned object. diff --git a/packages/astro/client.d.ts b/packages/astro/client.d.ts index 183527d74372..146ab8355ecf 100644 --- a/packages/astro/client.d.ts +++ b/packages/astro/client.d.ts @@ -108,6 +108,10 @@ declare module '*.svg' { const metadata: ImageMetadata; export default metadata; } +declare module '*.avif' { + const metadata: ImageMetadata; + export default metadata; +} declare module 'astro:transitions' { type TransitionModule = typeof import('./dist/transitions/index.js'); diff --git a/packages/astro/package.json b/packages/astro/package.json index 8490dd3185a6..9bc54395a066 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -157,6 +157,7 @@ "p-limit": "^4.0.0", "path-to-regexp": "^6.2.1", "preferred-pm": "^3.1.2", + "probe-image-size": "^7.2.3", "prompts": "^2.4.2", "rehype": "^12.0.1", "resolve": "^1.22.4", @@ -197,6 +198,7 @@ "@types/js-yaml": "^4.0.5", "@types/mime": "^3.0.1", "@types/mocha": "^10.0.1", + "@types/probe-image-size": "^7.2.0", "@types/prompts": "^2.4.4", "@types/resolve": "^1.20.2", "@types/send": "^0.17.1", diff --git a/packages/astro/src/assets/consts.ts b/packages/astro/src/assets/consts.ts index d184c9359c77..90dfa599cf21 100644 --- a/packages/astro/src/assets/consts.ts +++ b/packages/astro/src/assets/consts.ts @@ -1,14 +1,6 @@ export const VIRTUAL_MODULE_ID = 'astro:assets'; export const VIRTUAL_SERVICE_ID = 'virtual:image-service'; export const VALID_INPUT_FORMATS = [ - // TODO: `image-size` does not support the following formats, so users can't import them. - // However, it would be immensely useful to add, for three reasons: - // - `heic` and `heif` are common formats, especially among Apple users. - // - AVIF is a common format on the web that's bound to become more and more common. - // - It's totally reasonable for an user's provided image service to want to support more image types. - //'heic', - //'heif', - //'avif', 'jpeg', 'jpg', 'png', @@ -16,6 +8,7 @@ export const VALID_INPUT_FORMATS = [ 'webp', 'gif', 'svg', + 'avif', ] as const; /** * Valid formats that our base services support. @@ -29,5 +22,6 @@ export const VALID_SUPPORTED_FORMATS = [ 'webp', 'gif', 'svg', + 'avif', ] as const; export const VALID_OUTPUT_FORMATS = ['avif', 'png', 'webp', 'jpeg', 'jpg', 'svg'] as const; diff --git a/packages/astro/src/assets/utils/metadata.ts b/packages/astro/src/assets/utils/metadata.ts index de4136b36b26..7d7ee74573ba 100644 --- a/packages/astro/src/assets/utils/metadata.ts +++ b/packages/astro/src/assets/utils/metadata.ts @@ -1,8 +1,13 @@ +import probe from 'probe-image-size'; import type { ImageInputFormat, ImageMetadata } from '../types.js'; -import imageSize from '../vendor/image-size/index.js'; export async function imageMetadata(data: Buffer): Promise | undefined> { - const { width, height, type, orientation } = imageSize(data); + const result = probe.sync(data); + if (result === null) { + throw new Error('Failed to probe image size.'); + } + + const { width, height, type, orientation } = result; const isPortrait = (orientation || 0) >= 5; if (!width || !height || !type) { diff --git a/packages/astro/src/assets/vendor/README.md b/packages/astro/src/assets/vendor/README.md deleted file mode 100644 index 7b6927b674b3..000000000000 --- a/packages/astro/src/assets/vendor/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Vendored version of `image-size` and `queue` because we had issues with the CJS nature of those packages. - -Should hopefully be fixed by https://github.com/image-size/image-size/pull/370 diff --git a/packages/astro/src/assets/vendor/image-size/LICENSE b/packages/astro/src/assets/vendor/image-size/LICENSE deleted file mode 100644 index 1341a90d565f..000000000000 --- a/packages/astro/src/assets/vendor/image-size/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -The MIT License (MIT) - -Copyright © 2017 Aditya Yadav, http://netroy.in - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/astro/src/assets/vendor/image-size/detector.ts b/packages/astro/src/assets/vendor/image-size/detector.ts deleted file mode 100644 index 7a8873ab29f4..000000000000 --- a/packages/astro/src/assets/vendor/image-size/detector.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { typeHandlers, type imageType } from './types.js' - -const keys = Object.keys(typeHandlers) as imageType[] - -// This map helps avoid validating for every single image type -const firstBytes: { [byte: number]: imageType } = { - 0x38: 'psd', - 0x42: 'bmp', - 0x44: 'dds', - 0x47: 'gif', - 0x49: 'tiff', - 0x4d: 'tiff', - 0x52: 'webp', - 0x69: 'icns', - 0x89: 'png', - 0xff: 'jpg' -} - -export function detector(buffer: Buffer): imageType | undefined { - const byte = buffer[0] - if (byte in firstBytes) { - const type = firstBytes[byte] - if (type && typeHandlers[type].validate(buffer)) { - return type - } - } - - const finder = (key: imageType) => typeHandlers[key].validate(buffer) - return keys.find(finder) -} diff --git a/packages/astro/src/assets/vendor/image-size/index.ts b/packages/astro/src/assets/vendor/image-size/index.ts deleted file mode 100644 index 24f910b332c1..000000000000 --- a/packages/astro/src/assets/vendor/image-size/index.ts +++ /dev/null @@ -1,146 +0,0 @@ -import * as fs from "node:fs"; -import * as path from "node:path"; -import Queue from "../queue/queue.js"; -import { detector } from "./detector.js"; -import { typeHandlers, type imageType } from "./types.js"; -import type { ISizeCalculationResult } from "./types/interface.js"; - -type CallbackFn = (e: Error | null, r?: ISizeCalculationResult) => void; - -// Maximum buffer size, with a default of 512 kilobytes. -// TO-DO: make this adaptive based on the initial signature of the image -const MaxBufferSize = 512 * 1024; - -// This queue is for async `fs` operations, to avoid reaching file-descriptor limits -const queue = new Queue({ concurrency: 100, autostart: true }); - -interface Options { - disabledFS: boolean; - disabledTypes: imageType[]; -} - -const globalOptions: Options = { - disabledFS: false, - disabledTypes: [], -}; - -/** - * Return size information based on a buffer - * - * @param {Buffer} buffer - * @param {String} filepath - * @returns {Object} - */ -function lookup(buffer: Buffer, filepath?: string): ISizeCalculationResult { - // detect the file type.. don't rely on the extension - const type = detector(buffer); - - if (typeof type !== "undefined") { - if (globalOptions.disabledTypes.indexOf(type) > -1) { - throw new TypeError("disabled file type: " + type); - } - - // find an appropriate handler for this file type - if (type in typeHandlers) { - const size = typeHandlers[type].calculate(buffer, filepath); - if (size !== undefined) { - size.type = type; - return size; - } - } - } - - // throw up, if we don't understand the file - throw new TypeError( - "unsupported file type: " + type + " (file: " + filepath + ")" - ); -} - -/** - * Reads a file into a buffer. - * @param {String} filepath - * @returns {Promise} - */ -async function asyncFileToBuffer(filepath: string): Promise { - const handle = await fs.promises.open(filepath, "r"); - const { size } = await handle.stat(); - if (size <= 0) { - await handle.close(); - throw new Error("Empty file"); - } - const bufferSize = Math.min(size, MaxBufferSize); - const buffer = Buffer.alloc(bufferSize); - await handle.read(buffer, 0, bufferSize, 0); - await handle.close(); - return buffer; -} - -/** - * Synchronously reads a file into a buffer, blocking the nodejs process. - * - * @param {String} filepath - * @returns {Buffer} - */ -function syncFileToBuffer(filepath: string): Buffer { - // read from the file, synchronously - const descriptor = fs.openSync(filepath, "r"); - const { size } = fs.fstatSync(descriptor); - if (size <= 0) { - fs.closeSync(descriptor); - throw new Error("Empty file"); - } - const bufferSize = Math.min(size, MaxBufferSize); - const buffer = Buffer.alloc(bufferSize); - fs.readSync(descriptor, buffer, 0, bufferSize, 0); - fs.closeSync(descriptor); - return buffer; -} - -export default imageSize; -export function imageSize(input: Buffer | string): ISizeCalculationResult; -export function imageSize(input: string, callback: CallbackFn): void; - -/** - * @param {Buffer|string} input - buffer or relative/absolute path of the image file - * @param {Function=} [callback] - optional function for async detection - */ -export function imageSize( - input: Buffer | string, - callback?: CallbackFn -): ISizeCalculationResult | void { - // Handle buffer input - if (Buffer.isBuffer(input)) { - return lookup(input); - } - - // input should be a string at this point - if (typeof input !== "string" || globalOptions.disabledFS) { - throw new TypeError("invalid invocation. input should be a Buffer"); - } - - // resolve the file path - const filepath = path.resolve(input); - if (typeof callback === "function") { - queue.push(() => - asyncFileToBuffer(filepath) - .then((buffer) => - process.nextTick(callback, null, lookup(buffer, filepath)) - ) - .catch(callback) - ); - } else { - const buffer = syncFileToBuffer(filepath); - return lookup(buffer, filepath); - } -} - -export const disableFS = (v: boolean): void => { - globalOptions.disabledFS = v; -}; -export const disableTypes = (types: imageType[]): void => { - globalOptions.disabledTypes = types; -}; -export const setConcurrency = (c: number): void => { - queue.concurrency = c; -}; -export const types = Object.keys(typeHandlers); diff --git a/packages/astro/src/assets/vendor/image-size/readUInt.ts b/packages/astro/src/assets/vendor/image-size/readUInt.ts deleted file mode 100644 index ede811408a75..000000000000 --- a/packages/astro/src/assets/vendor/image-size/readUInt.ts +++ /dev/null @@ -1,10 +0,0 @@ -type Bits = 16 | 32 -type MethodName = 'readUInt16BE' | 'readUInt16LE' | 'readUInt32BE' | 'readUInt32LE' - -// Abstract reading multi-byte unsigned integers -export function readUInt(buffer: Buffer, bits: Bits, offset: number, isBigEndian: boolean): number { - offset = offset || 0 - const endian = isBigEndian ? 'BE' : 'LE' - const methodName: MethodName = ('readUInt' + bits + endian) as MethodName - return buffer[methodName].call(buffer, offset) -} diff --git a/packages/astro/src/assets/vendor/image-size/types.ts b/packages/astro/src/assets/vendor/image-size/types.ts deleted file mode 100644 index 05f4f82cc4e0..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types.ts +++ /dev/null @@ -1,38 +0,0 @@ -// load all available handlers explicitely for browserify support -import { BMP } from './types/bmp.js' -import { CUR } from './types/cur.js' -import { DDS } from './types/dds.js' -import { GIF } from './types/gif.js' -import { ICNS } from './types/icns.js' -import { ICO } from './types/ico.js' -import { J2C } from './types/j2c.js' -import { JP2 } from './types/jp2.js' -import { JPG } from './types/jpg.js' -import { KTX } from './types/ktx.js' -import { PNG } from './types/png.js' -import { PNM } from './types/pnm.js' -import { PSD } from './types/psd.js' -import { SVG } from './types/svg.js' -import { TIFF } from './types/tiff.js' -import { WEBP } from './types/webp.js' - -export const typeHandlers = { - bmp: BMP, - cur: CUR, - dds: DDS, - gif: GIF, - icns: ICNS, - ico: ICO, - j2c: J2C, - jp2: JP2, - jpg: JPG, - ktx: KTX, - png: PNG, - pnm: PNM, - psd: PSD, - svg: SVG, - tiff: TIFF, - webp: WEBP, -} - -export type imageType = keyof typeof typeHandlers diff --git a/packages/astro/src/assets/vendor/image-size/types/bmp.ts b/packages/astro/src/assets/vendor/image-size/types/bmp.ts deleted file mode 100644 index e411bb56121a..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/bmp.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { IImage } from './interface.js' - -export const BMP: IImage = { - validate(buffer) { - return ('BM' === buffer.toString('ascii', 0, 2)) - }, - - calculate(buffer) { - return { - height: Math.abs(buffer.readInt32LE(22)), - width: buffer.readUInt32LE(18) - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/cur.ts b/packages/astro/src/assets/vendor/image-size/types/cur.ts deleted file mode 100644 index 4f612dcbb42d..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/cur.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ICO } from './ico.js' -import type { IImage } from './interface.js' - -const TYPE_CURSOR = 2 -export const CUR: IImage = { - validate(buffer) { - if (buffer.readUInt16LE(0) !== 0) { - return false - } - return buffer.readUInt16LE(2) === TYPE_CURSOR - }, - - calculate(buffer) { - return ICO.calculate(buffer) - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/dds.ts b/packages/astro/src/assets/vendor/image-size/types/dds.ts deleted file mode 100644 index a9f33e046099..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/dds.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { IImage } from './interface.js' - -export const DDS: IImage = { - validate(buffer) { - return buffer.readUInt32LE(0) === 0x20534444 - }, - - calculate(buffer) { - return { - height: buffer.readUInt32LE(12), - width: buffer.readUInt32LE(16) - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/gif.ts b/packages/astro/src/assets/vendor/image-size/types/gif.ts deleted file mode 100644 index b49fc27c6f76..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/gif.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { IImage } from './interface.js' - -const gifRegexp = /^GIF8[79]a/ -export const GIF: IImage = { - validate(buffer) { - const signature = buffer.toString('ascii', 0, 6) - return (gifRegexp.test(signature)) - }, - - calculate(buffer) { - return { - height: buffer.readUInt16LE(8), - width: buffer.readUInt16LE(6) - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/icns.ts b/packages/astro/src/assets/vendor/image-size/types/icns.ts deleted file mode 100644 index 59e5a8425d5d..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/icns.ts +++ /dev/null @@ -1,113 +0,0 @@ -import type { IImage, ISize } from './interface.js' - -/** - * ICNS Header - * - * | Offset | Size | Purpose | - * | 0 | 4 | Magic literal, must be "icns" (0x69, 0x63, 0x6e, 0x73) | - * | 4 | 4 | Length of file, in bytes, msb first. | - * - */ -const SIZE_HEADER = 4 + 4 // 8 -const FILE_LENGTH_OFFSET = 4 // MSB => BIG ENDIAN - -/** - * Image Entry - * - * | Offset | Size | Purpose | - * | 0 | 4 | Icon type, see OSType below. | - * | 4 | 4 | Length of data, in bytes (including type and length), msb first. | - * | 8 | n | Icon data | - */ -const ENTRY_LENGTH_OFFSET = 4 // MSB => BIG ENDIAN - -const ICON_TYPE_SIZE: {[key: string]: number} = { - ICON: 32, - 'ICN#': 32, - // m => 16 x 16 - 'icm#': 16, - icm4: 16, - icm8: 16, - // s => 16 x 16 - 'ics#': 16, - ics4: 16, - ics8: 16, - is32: 16, - s8mk: 16, - icp4: 16, - // l => 32 x 32 - icl4: 32, - icl8: 32, - il32: 32, - l8mk: 32, - icp5: 32, - ic11: 32, - // h => 48 x 48 - ich4: 48, - ich8: 48, - ih32: 48, - h8mk: 48, - // . => 64 x 64 - icp6: 64, - ic12: 32, - // t => 128 x 128 - it32: 128, - t8mk: 128, - ic07: 128, - // . => 256 x 256 - ic08: 256, - ic13: 256, - // . => 512 x 512 - ic09: 512, - ic14: 512, - // . => 1024 x 1024 - ic10: 1024, -} - -function readImageHeader(buffer: Buffer, imageOffset: number): [string, number] { - const imageLengthOffset = imageOffset + ENTRY_LENGTH_OFFSET - return [ - buffer.toString('ascii', imageOffset, imageLengthOffset), - buffer.readUInt32BE(imageLengthOffset) - ] -} - -function getImageSize(type: string): ISize { - const size = ICON_TYPE_SIZE[type] - return { width: size, height: size, type } -} - -export const ICNS: IImage = { - validate(buffer) { - return ('icns' === buffer.toString('ascii', 0, 4)) - }, - - calculate(buffer) { - const bufferLength = buffer.length - const fileLength = buffer.readUInt32BE(FILE_LENGTH_OFFSET) - let imageOffset = SIZE_HEADER - - let imageHeader = readImageHeader(buffer, imageOffset) - let imageSize = getImageSize(imageHeader[0]) - imageOffset += imageHeader[1] - - if (imageOffset === fileLength) { - return imageSize - } - - const result = { - height: imageSize.height, - images: [imageSize], - width: imageSize.width - } - - while (imageOffset < fileLength && imageOffset < bufferLength) { - imageHeader = readImageHeader(buffer, imageOffset) - imageSize = getImageSize(imageHeader[0]) - imageOffset += imageHeader[1] - result.images.push(imageSize) - } - - return result - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/ico.ts b/packages/astro/src/assets/vendor/image-size/types/ico.ts deleted file mode 100644 index 17cc778bb811..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/ico.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type { IImage, ISize, ISizeCalculationResult } from './interface.js' - -const TYPE_ICON = 1 - -/** - * ICON Header - * - * | Offset | Size | Purpose | - * | 0 | 2 | Reserved. Must always be 0. | - * | 2 | 2 | Image type: 1 for icon (.ICO) image, 2 for cursor (.CUR) image. Other values are invalid. | - * | 4 | 2 | Number of images in the file. | - * - */ -const SIZE_HEADER = 2 + 2 + 2 // 6 - -/** - * Image Entry - * - * | Offset | Size | Purpose | - * | 0 | 1 | Image width in pixels. Can be any number between 0 and 255. Value 0 means width is 256 pixels. | - * | 1 | 1 | Image height in pixels. Can be any number between 0 and 255. Value 0 means height is 256 pixels. | - * | 2 | 1 | Number of colors in the color palette. Should be 0 if the image does not use a color palette. | - * | 3 | 1 | Reserved. Should be 0. | - * | 4 | 2 | ICO format: Color planes. Should be 0 or 1. | - * | | | CUR format: The horizontal coordinates of the hotspot in number of pixels from the left. | - * | 6 | 2 | ICO format: Bits per pixel. | - * | | | CUR format: The vertical coordinates of the hotspot in number of pixels from the top. | - * | 8 | 4 | The size of the image's data in bytes | - * | 12 | 4 | The offset of BMP or PNG data from the beginning of the ICO/CUR file | - * - */ -const SIZE_IMAGE_ENTRY = 1 + 1 + 1 + 1 + 2 + 2 + 4 + 4 // 16 - -function getSizeFromOffset(buffer: Buffer, offset: number): number { - const value = buffer.readUInt8(offset) - return value === 0 ? 256 : value -} - -function getImageSize(buffer: Buffer, imageIndex: number): ISize { - const offset = SIZE_HEADER + (imageIndex * SIZE_IMAGE_ENTRY) - return { - height: getSizeFromOffset(buffer, offset + 1), - width: getSizeFromOffset(buffer, offset) - } -} - -export const ICO: IImage = { - validate(buffer) { - if (buffer.readUInt16LE(0) !== 0) { - return false - } - return buffer.readUInt16LE(2) === TYPE_ICON - }, - - calculate(buffer) { - const nbImages = buffer.readUInt16LE(4) - const imageSize = getImageSize(buffer, 0) - - if (nbImages === 1) { - return imageSize - } - - const imgs: ISize[] = [imageSize] - for (let imageIndex = 1; imageIndex < nbImages; imageIndex += 1) { - imgs.push(getImageSize(buffer, imageIndex)) - } - - const result: ISizeCalculationResult = { - height: imageSize.height, - images: imgs, - width: imageSize.width - } - - return result - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/interface.ts b/packages/astro/src/assets/vendor/image-size/types/interface.ts deleted file mode 100644 index 9886b357361e..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/interface.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface ISize { - width: number | undefined - height: number | undefined - orientation?: number - type?: string -} - -export interface ISizeCalculationResult extends ISize { - images?: ISize[] -} - -export interface IImage { - validate: (buffer: Buffer) => boolean - calculate: (buffer: Buffer, filepath?: string) => ISizeCalculationResult -} diff --git a/packages/astro/src/assets/vendor/image-size/types/j2c.ts b/packages/astro/src/assets/vendor/image-size/types/j2c.ts deleted file mode 100644 index 26c582714f74..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/j2c.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { IImage } from './interface.js' - -export const J2C: IImage = { - validate(buffer) { - // TODO: this doesn't seem right. SIZ marker doesn't have to be right after the SOC - return buffer.toString('hex', 0, 4) === 'ff4fff51' - }, - - calculate(buffer) { - return { - height: buffer.readUInt32BE(12), - width: buffer.readUInt32BE(8), - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/jp2.ts b/packages/astro/src/assets/vendor/image-size/types/jp2.ts deleted file mode 100644 index 0b8d01625c56..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/jp2.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { IImage, ISize } from './interface.js' - -const BoxTypes = { - ftyp: '66747970', - ihdr: '69686472', - jp2h: '6a703268', - jp__: '6a502020', - rreq: '72726571', - xml_: '786d6c20' -} - -const calculateRREQLength = (box: Buffer): number => { - const unit = box.readUInt8(0) - let offset = 1 + (2 * unit) - const numStdFlags = box.readUInt16BE(offset) - const flagsLength = numStdFlags * (2 + unit) - offset = offset + 2 + flagsLength - const numVendorFeatures = box.readUInt16BE(offset) - const featuresLength = numVendorFeatures * (16 + unit) - return offset + 2 + featuresLength -} - -const parseIHDR = (box: Buffer): ISize => { - return { - height: box.readUInt32BE(4), - width: box.readUInt32BE(8), - } -} - -export const JP2: IImage = { - validate(buffer) { - const signature = buffer.toString('hex', 4, 8) - const signatureLength = buffer.readUInt32BE(0) - if (signature !== BoxTypes.jp__ || signatureLength < 1) { - return false - } - - const ftypeBoxStart = signatureLength + 4 - const ftypBoxLength = buffer.readUInt32BE(signatureLength) - const ftypBox = buffer.slice(ftypeBoxStart, ftypeBoxStart + ftypBoxLength) - return ftypBox.toString('hex', 0, 4) === BoxTypes.ftyp - }, - - calculate(buffer) { - const signatureLength = buffer.readUInt32BE(0) - const ftypBoxLength = buffer.readUInt16BE(signatureLength + 2) - let offset = signatureLength + 4 + ftypBoxLength - const nextBoxType = buffer.toString('hex', offset, offset + 4) - switch (nextBoxType) { - case BoxTypes.rreq: - // WHAT ARE THESE 4 BYTES????? - const MAGIC = 4 - offset = offset + 4 + MAGIC + calculateRREQLength(buffer.slice(offset + 4)) - return parseIHDR(buffer.slice(offset + 8, offset + 24)) - case BoxTypes.jp2h : - return parseIHDR(buffer.slice(offset + 8, offset + 24)) - default: - throw new TypeError('Unsupported header found: ' + buffer.toString('ascii', offset, offset + 4)) - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/jpg.ts b/packages/astro/src/assets/vendor/image-size/types/jpg.ts deleted file mode 100644 index c06302e48ed6..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/jpg.ts +++ /dev/null @@ -1,151 +0,0 @@ -// NOTE: we only support baseline and progressive JPGs here -// due to the structure of the loader class, we only get a buffer -// with a maximum size of 4096 bytes. so if the SOF marker is outside -// if this range we can't detect the file size correctly. - -import { readUInt } from '../readUInt.js' -import type { IImage, ISize } from './interface.js' - -const EXIF_MARKER = '45786966' -const APP1_DATA_SIZE_BYTES = 2 -const EXIF_HEADER_BYTES = 6 -const TIFF_BYTE_ALIGN_BYTES = 2 -const BIG_ENDIAN_BYTE_ALIGN = '4d4d' -const LITTLE_ENDIAN_BYTE_ALIGN = '4949' - -// Each entry is exactly 12 bytes -const IDF_ENTRY_BYTES = 12 -const NUM_DIRECTORY_ENTRIES_BYTES = 2 - -function isEXIF(buffer: Buffer): boolean { - return (buffer.toString('hex', 2, 6) === EXIF_MARKER) -} - -function extractSize(buffer: Buffer, index: number): ISize { - return { - height : buffer.readUInt16BE(index), - width : buffer.readUInt16BE(index + 2) - } -} - -function extractOrientation(exifBlock: Buffer, isBigEndian: boolean) { - // TODO: assert that this contains 0x002A - // let STATIC_MOTOROLA_TIFF_HEADER_BYTES = 2 - // let TIFF_IMAGE_FILE_DIRECTORY_BYTES = 4 - - // TODO: derive from TIFF_IMAGE_FILE_DIRECTORY_BYTES - const idfOffset = 8 - - // IDF osset works from right after the header bytes - // (so the offset includes the tiff byte align) - const offset = EXIF_HEADER_BYTES + idfOffset - - const idfDirectoryEntries = readUInt(exifBlock, 16, offset, isBigEndian) - - for (let directoryEntryNumber = 0; directoryEntryNumber < idfDirectoryEntries; directoryEntryNumber++) { - const start = offset + NUM_DIRECTORY_ENTRIES_BYTES + (directoryEntryNumber * IDF_ENTRY_BYTES) - const end = start + IDF_ENTRY_BYTES - - // Skip on corrupt EXIF blocks - if (start > exifBlock.length) { - return - } - - const block = exifBlock.slice(start, end) - const tagNumber = readUInt(block, 16, 0, isBigEndian) - - // 0x0112 (decimal: 274) is the `orientation` tag ID - if (tagNumber === 274) { - const dataFormat = readUInt(block, 16, 2, isBigEndian) - if (dataFormat !== 3) { - return - } - - // unsinged int has 2 bytes per component - // if there would more than 4 bytes in total it's a pointer - const numberOfComponents = readUInt(block, 32, 4, isBigEndian) - if (numberOfComponents !== 1) { - return - } - - return readUInt(block, 16, 8, isBigEndian) - } - } -} - -function validateExifBlock(buffer: Buffer, index: number) { - // Skip APP1 Data Size - const exifBlock = buffer.slice(APP1_DATA_SIZE_BYTES, index) - - // Consider byte alignment - const byteAlign = exifBlock.toString('hex', EXIF_HEADER_BYTES, EXIF_HEADER_BYTES + TIFF_BYTE_ALIGN_BYTES) - - // Ignore Empty EXIF. Validate byte alignment - const isBigEndian = byteAlign === BIG_ENDIAN_BYTE_ALIGN - const isLittleEndian = byteAlign === LITTLE_ENDIAN_BYTE_ALIGN - - if (isBigEndian || isLittleEndian) { - return extractOrientation(exifBlock, isBigEndian) - } -} - -function validateBuffer(buffer: Buffer, index: number): void { - // index should be within buffer limits - if (index > buffer.length) { - throw new TypeError('Corrupt JPG, exceeded buffer limits') - } - // Every JPEG block must begin with a 0xFF - if (buffer[index] !== 0xFF) { - throw new TypeError('Invalid JPG, marker table corrupted') - } -} - -export const JPG: IImage = { - validate(buffer) { - const SOIMarker = buffer.toString('hex', 0, 2) - return ('ffd8' === SOIMarker) - }, - - calculate(buffer) { - // Skip 4 chars, they are for signature - buffer = buffer.slice(4) - - let orientation: number | undefined - let next: number - while (buffer.length) { - // read length of the next block - const i = buffer.readUInt16BE(0) - - if (isEXIF(buffer)) { - orientation = validateExifBlock(buffer, i) - } - - // ensure correct format - validateBuffer(buffer, i) - - // 0xFFC0 is baseline standard(SOF) - // 0xFFC1 is baseline optimized(SOF) - // 0xFFC2 is progressive(SOF2) - next = buffer[i + 1] - if (next === 0xC0 || next === 0xC1 || next === 0xC2) { - const size = extractSize(buffer, i + 5) - - // TODO: is orientation=0 a valid answer here? - if (!orientation) { - return size - } - - return { - height: size.height, - orientation, - width: size.width - } - } - - // move to the next block - buffer = buffer.slice(i + 2) - } - - throw new TypeError('Invalid JPG, no size found') - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/ktx.ts b/packages/astro/src/assets/vendor/image-size/types/ktx.ts deleted file mode 100644 index 080bc261f3a8..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/ktx.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { IImage } from './interface.js' - -const SIGNATURE = 'KTX 11' - -export const KTX: IImage = { - validate(buffer) { - return SIGNATURE === buffer.toString('ascii', 1, 7) - }, - - calculate(buffer) { - return { - height: buffer.readUInt32LE(40), - width: buffer.readUInt32LE(36), - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/png.ts b/packages/astro/src/assets/vendor/image-size/types/png.ts deleted file mode 100644 index 7c70025daacd..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/png.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { IImage } from './interface.js' - -const pngSignature = 'PNG\r\n\x1a\n' -const pngImageHeaderChunkName = 'IHDR' - -// Used to detect "fried" png's: http://www.jongware.com/pngdefry.html -const pngFriedChunkName = 'CgBI' - -export const PNG: IImage = { - validate(buffer) { - if (pngSignature === buffer.toString('ascii', 1, 8)) { - let chunkName = buffer.toString('ascii', 12, 16) - if (chunkName === pngFriedChunkName) { - chunkName = buffer.toString('ascii', 28, 32) - } - if (chunkName !== pngImageHeaderChunkName) { - throw new TypeError('Invalid PNG') - } - return true - } - return false - }, - - calculate(buffer) { - if (buffer.toString('ascii', 12, 16) === pngFriedChunkName) { - return { - height: buffer.readUInt32BE(36), - width: buffer.readUInt32BE(32) - } - } - return { - height: buffer.readUInt32BE(20), - width: buffer.readUInt32BE(16) - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/pnm.ts b/packages/astro/src/assets/vendor/image-size/types/pnm.ts deleted file mode 100644 index 8f1765aa754c..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/pnm.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { IImage, ISize } from './interface.js' - -const PNMTypes: { [signature: string]: string } = { - P1: 'pbm/ascii', - P2: 'pgm/ascii', - P3: 'ppm/ascii', - P4: 'pbm', - P5: 'pgm', - P6: 'ppm', - P7: 'pam', - PF: 'pfm' -} - -const Signatures = Object.keys(PNMTypes) - -type Handler = (type: string[]) => ISize -const handlers: { [type: string]: Handler} = { - default: (lines) => { - let dimensions: string[] = [] - - while (lines.length > 0) { - const line = lines.shift()! - if (line[0] === '#') { - continue - } - dimensions = line.split(' ') - break - } - - if (dimensions.length === 2) { - return { - height: parseInt(dimensions[1], 10), - width: parseInt(dimensions[0], 10), - } - } else { - throw new TypeError('Invalid PNM') - } - }, - pam: (lines) => { - const size: { [key: string]: number } = {} - while (lines.length > 0) { - const line = lines.shift()! - if (line.length > 16 || line.charCodeAt(0) > 128) { - continue - } - const [key, value] = line.split(' ') - if (key && value) { - size[key.toLowerCase()] = parseInt(value, 10) - } - if (size.height && size.width) { - break - } - } - - if (size.height && size.width) { - return { - height: size.height, - width: size.width - } - } else { - throw new TypeError('Invalid PAM') - } - } -} - -export const PNM: IImage = { - validate(buffer) { - const signature = buffer.toString('ascii', 0, 2) - return Signatures.includes(signature) - }, - - calculate(buffer) { - const signature = buffer.toString('ascii', 0, 2) - const type = PNMTypes[signature] - // TODO: this probably generates garbage. move to a stream based parser - const lines = buffer.toString('ascii', 3).split(/[\r\n]+/) - const handler = handlers[type] || handlers.default - return handler(lines) - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/psd.ts b/packages/astro/src/assets/vendor/image-size/types/psd.ts deleted file mode 100644 index c88855fe7056..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/psd.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { IImage } from './interface.js' - -export const PSD: IImage = { - validate(buffer) { - return ('8BPS' === buffer.toString('ascii', 0, 4)) - }, - - calculate(buffer) { - return { - height: buffer.readUInt32BE(14), - width: buffer.readUInt32BE(18) - } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/svg.ts b/packages/astro/src/assets/vendor/image-size/types/svg.ts deleted file mode 100644 index 50c4856836db..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/svg.ts +++ /dev/null @@ -1,106 +0,0 @@ -import type { IImage, ISize } from './interface.js' - -interface IAttributes { - width: number | null - height: number | null - viewbox?: IAttributes | null -} - -const svgReg = /"']|"[^"]*"|'[^']*')*>/ - -const extractorRegExps = { - height: /\sheight=(['"])([^%]+?)\1/, - root: svgReg, - viewbox: /\sviewBox=(['"])(.+?)\1/i, - width: /\swidth=(['"])([^%]+?)\1/, -} - -const INCH_CM = 2.54 -const units: { [unit: string]: number } = { - in: 96, - cm: 96 / INCH_CM, - em: 16, - ex: 8, - m: 96 / INCH_CM * 100, - mm: 96 / INCH_CM / 10, - pc: 96 / 72 / 12, - pt: 96 / 72, - px: 1 -} - -const unitsReg = new RegExp(`^([0-9.]+(?:e\\d+)?)(${Object.keys(units).join('|')})?$`) - -function parseLength(len: string) { - const m = unitsReg.exec(len) - if (!m) { - return undefined - } - return Math.round(Number(m[1]) * (units[m[2]] || 1)) -} - -function parseViewbox(viewbox: string): IAttributes { - const bounds = viewbox.split(' ') - return { - height: parseLength(bounds[3])!, - width: parseLength(bounds[2])! - } -} - -function parseAttributes(root: string): IAttributes { - const width = root.match(extractorRegExps.width) - const height = root.match(extractorRegExps.height) - const viewbox = root.match(extractorRegExps.viewbox) - return { - height: height && parseLength(height[2])!, - viewbox: viewbox && parseViewbox(viewbox[2])!, - width: width && parseLength(width[2])!, - } -} - -function calculateByDimensions(attrs: IAttributes): ISize { - return { - height: attrs.height!, - width: attrs.width!, - } -} - -function calculateByViewbox(attrs: IAttributes, viewbox: IAttributes): ISize { - const ratio = (viewbox.width!) / (viewbox.height!) - if (attrs.width) { - return { - height: Math.floor(attrs.width / ratio), - width: attrs.width, - } - } - if (attrs.height) { - return { - height: attrs.height, - width: Math.floor(attrs.height * ratio), - } - } - return { - height: viewbox.height!, - width: viewbox.width!, - } -} - -export const SVG: IImage = { - validate(buffer) { - const str = String(buffer) - return svgReg.test(str) - }, - - calculate(buffer) { - const root = buffer.toString('utf8').match(extractorRegExps.root) - if (root) { - const attrs = parseAttributes(root[0]) - if (attrs.width && attrs.height) { - return calculateByDimensions(attrs) - } - if (attrs.viewbox) { - return calculateByViewbox(attrs, attrs.viewbox) - } - } - throw new TypeError('Invalid SVG') - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/tiff.ts b/packages/astro/src/assets/vendor/image-size/types/tiff.ts deleted file mode 100644 index c5f76f80e77c..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/tiff.ts +++ /dev/null @@ -1,115 +0,0 @@ -// based on http://www.compix.com/fileformattif.htm -// TO-DO: support big-endian as well -import * as fs from 'node:fs' -import { readUInt } from '../readUInt.js' -import type { IImage } from './interface.js' - -// Read IFD (image-file-directory) into a buffer -function readIFD(buffer: Buffer, filepath: string, isBigEndian: boolean) { - - const ifdOffset = readUInt(buffer, 32, 4, isBigEndian) - - // read only till the end of the file - let bufferSize = 1024 - const fileSize = fs.statSync(filepath).size - if (ifdOffset + bufferSize > fileSize) { - bufferSize = fileSize - ifdOffset - 10 - } - - // populate the buffer - const endBuffer = Buffer.alloc(bufferSize) - const descriptor = fs.openSync(filepath, 'r') - fs.readSync(descriptor, endBuffer, 0, bufferSize, ifdOffset) - fs.closeSync(descriptor) - - return endBuffer.slice(2) -} - -// TIFF values seem to be messed up on Big-Endian, this helps -function readValue(buffer: Buffer, isBigEndian: boolean): number { - const low = readUInt(buffer, 16, 8, isBigEndian) - const high = readUInt(buffer, 16, 10, isBigEndian) - return (high << 16) + low -} - -// move to the next tag -function nextTag(buffer: Buffer) { - if (buffer.length > 24) { - return buffer.slice(12) - } -} - -// Extract IFD tags from TIFF metadata -function extractTags(buffer: Buffer, isBigEndian: boolean) { - const tags: {[key: number]: number} = {} - - let temp: Buffer | undefined = buffer - while (temp?.length) { - const code = readUInt(temp, 16, 0, isBigEndian) - const type = readUInt(temp, 16, 2, isBigEndian) - const length = readUInt(temp, 32, 4, isBigEndian) - - // 0 means end of IFD - if (code === 0) { - break - } else { - // 256 is width, 257 is height - // if (code === 256 || code === 257) { - if (length === 1 && (type === 3 || type === 4)) { - tags[code] = readValue(temp, isBigEndian) - } - - // move to the next tag - temp = nextTag(temp) - } - } - - return tags -} - -// Test if the TIFF is Big Endian or Little Endian -function determineEndianness(buffer: Buffer) { - const signature = buffer.toString('ascii', 0, 2) - if ('II' === signature) { - return 'LE' - } else if ('MM' === signature) { - return 'BE' - } -} - -const signatures = [ - // '492049', // currently not supported - '49492a00', // Little endian - '4d4d002a', // Big Endian - // '4d4d002a', // BigTIFF > 4GB. currently not supported -] - -export const TIFF: IImage = { - validate(buffer) { - return signatures.includes(buffer.toString('hex', 0, 4)) - }, - - calculate(buffer, filepath) { - if (!filepath) { - throw new TypeError('Tiff doesn\'t support buffer') - } - - // Determine BE/LE - const isBigEndian = determineEndianness(buffer) === 'BE' - - // read the IFD - const ifdBuffer = readIFD(buffer, filepath, isBigEndian) - - // extract the tags from the IFD - const tags = extractTags(ifdBuffer, isBigEndian) - - const width = tags[256] - const height = tags[257] - - if (!width || !height) { - throw new TypeError('Invalid Tiff. Missing tags') - } - - return { height, width } - } -} diff --git a/packages/astro/src/assets/vendor/image-size/types/webp.ts b/packages/astro/src/assets/vendor/image-size/types/webp.ts deleted file mode 100644 index 6592d87fb9be..000000000000 --- a/packages/astro/src/assets/vendor/image-size/types/webp.ts +++ /dev/null @@ -1,65 +0,0 @@ -// based on https://developers.google.com/speed/webp/docs/riff_container -import type { IImage, ISize } from './interface.js' - -function calculateExtended(buffer: Buffer): ISize { - return { - height: 1 + buffer.readUIntLE(7, 3), - width: 1 + buffer.readUIntLE(4, 3) - } -} - -function calculateLossless(buffer: Buffer): ISize { - return { - height: 1 + (((buffer[4] & 0xF) << 10) | (buffer[3] << 2) | ((buffer[2] & 0xC0) >> 6)), - width: 1 + (((buffer[2] & 0x3F) << 8) | buffer[1]) - } -} - -function calculateLossy(buffer: Buffer): ISize { - // `& 0x3fff` returns the last 14 bits - // TO-DO: include webp scaling in the calculations - return { - height: buffer.readInt16LE(8) & 0x3fff, - width: buffer.readInt16LE(6) & 0x3fff - } -} - -export const WEBP: IImage = { - validate(buffer) { - const riffHeader = 'RIFF' === buffer.toString('ascii', 0, 4) - const webpHeader = 'WEBP' === buffer.toString('ascii', 8, 12) - const vp8Header = 'VP8' === buffer.toString('ascii', 12, 15) - return (riffHeader && webpHeader && vp8Header) - }, - - calculate(buffer) { - const chunkHeader = buffer.toString('ascii', 12, 16) - buffer = buffer.slice(20, 30) - - // Extended webp stream signature - if (chunkHeader === 'VP8X') { - const extendedHeader = buffer[0] - const validStart = (extendedHeader & 0xc0) === 0 - const validEnd = (extendedHeader & 0x01) === 0 - if (validStart && validEnd) { - return calculateExtended(buffer) - } else { - // TODO: breaking change - throw new TypeError('Invalid WebP') - } - } - - // Lossless webp stream signature - if (chunkHeader === 'VP8 ' && buffer[0] !== 0x2f) { - return calculateLossy(buffer) - } - - // Lossy webp stream signature - const signature = buffer.toString('hex', 3, 6) - if (chunkHeader === 'VP8L' && signature !== '9d012a') { - return calculateLossless(buffer) - } - - throw new TypeError('Invalid WebP') - } -} diff --git a/packages/astro/src/assets/vendor/queue/LICENSE b/packages/astro/src/assets/vendor/queue/LICENSE deleted file mode 100644 index 50e946098625..000000000000 --- a/packages/astro/src/assets/vendor/queue/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -The MIT License (MIT) -Copyright (c) 2014 Jesse Tane - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/astro/src/assets/vendor/queue/queue.js b/packages/astro/src/assets/vendor/queue/queue.js deleted file mode 100644 index 6c71704355a4..000000000000 --- a/packages/astro/src/assets/vendor/queue/queue.js +++ /dev/null @@ -1,225 +0,0 @@ -const has = Object.prototype.hasOwnProperty - -/** - * Since CustomEvent is only supported in nodejs since version 19, - * you have to create your own class instead of using CustomEvent - * @see https://github.com/nodejs/node/issues/40678 - * */ -export class QueueEvent extends Event { - constructor (name, detail) { - super(name) - this.detail = detail - } -} - - -export default class Queue extends EventTarget { - constructor (options = {}) { - super() - const { concurrency = Infinity, timeout = 0, autostart = false, results = null } = options - - this.concurrency = concurrency - this.timeout = timeout - this.autostart = autostart - this.results = results - this.pending = 0 - this.session = 0 - this.running = false - this.jobs = [] - this.timers = [] - - this.addEventListener('error', this._errorHandler) - } - - _errorHandler(evt) { - this.end(evt.detail.error); - } - - pop () { - return this.jobs.pop() - } - - shift () { - return this.jobs.shift() - } - - indexOf (searchElement, fromIndex) { - return this.jobs.indexOf(searchElement, fromIndex) - } - - lastIndexOf (searchElement, fromIndex) { - if (fromIndex !== undefined) { return this.jobs.lastIndexOf(searchElement, fromIndex) } - return this.jobs.lastIndexOf(searchElement) - } - - slice (start, end) { - this.jobs = this.jobs.slice(start, end) - return this - } - - reverse () { - this.jobs.reverse() - return this - } - - push (...workers) { - const methodResult = this.jobs.push(...workers) - if (this.autostart) { - this.start() - } - return methodResult - } - - unshift (...workers) { - const methodResult = this.jobs.unshift(...workers) - if (this.autostart) { - this.start() - } - return methodResult - } - - splice (start, deleteCount, ...workers) { - this.jobs.splice(start, deleteCount, ...workers) - if (this.autostart) { - this.start() - } - return this - } - - get length () { - return this.pending + this.jobs.length - } - - start (callback) { - let awaiter; - - if (callback) { - this._addCallbackToEndEvent(callback) - } else { - awaiter = this._createPromiseToEndEvent(); - } - - this.running = true - - if (this.pending >= this.concurrency) { - return - } - - if (this.jobs.length === 0) { - if (this.pending === 0) { - this.done() - } - return - } - - const job = this.jobs.shift() - const session = this.session - const timeout = (job !== undefined) && has.call(job, 'timeout') ? job.timeout : this.timeout - let once = true - let timeoutId = null - let didTimeout = false - let resultIndex = null - - const next = (error, ...result) => { - if (once && this.session === session) { - once = false - this.pending-- - if (timeoutId !== null) { - this.timers = this.timers.filter((tID) => tID !== timeoutId) - clearTimeout(timeoutId) - } - - if (error) { - this.dispatchEvent(new QueueEvent('error', { error, job })) - } else if (!didTimeout) { - if (resultIndex !== null && this.results !== null) { - this.results[resultIndex] = [...result] - } - this.dispatchEvent(new QueueEvent('success', { result: [...result], job })) - } - - if (this.session === session) { - if (this.pending === 0 && this.jobs.length === 0) { - this.done() - } else if (this.running) { - this.start() - } - } - } - } - - if (timeout) { - timeoutId = setTimeout(() => { - didTimeout = true - this.dispatchEvent(new QueueEvent('timeout', { next, job })) - next() - }, timeout) - this.timers.push(timeoutId) - } - - if (this.results != null) { - resultIndex = this.results.length - this.results[resultIndex] = null - } - - this.pending++ - this.dispatchEvent(new QueueEvent('start', { job })) - - const promise = job(next) - - if (promise !== undefined && typeof promise.then === 'function') { - promise.then(function (result) { - return next(undefined, result) - }).catch(function (err) { - return next(err || true) - }) - } - - if (this.running && this.jobs.length > 0) { - return this.start() - } - - return awaiter; - } - - stop () { - this.running = false - } - - end (error) { - this.clearTimers() - this.jobs.length = 0 - this.pending = 0 - this.done(error) - } - - clearTimers () { - this.timers.forEach((timer) => { - clearTimeout(timer) - }) - - this.timers = [] - } - - _addCallbackToEndEvent (cb) { - const onend = (evt) => { - this.removeEventListener('end', onend) - cb(evt.detail.error, this.results) - } - this.addEventListener('end', onend) - } - - _createPromiseToEndEvent() { - return new Promise((resolve) => { - this._addCallbackToEndEvent((error, results) => { - resolve({ error, results }); - }); - }); - } - - done (error) { - this.session++ - this.running = false - this.dispatchEvent(new QueueEvent('end', { error })) - } -} diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts index 300263adaffc..0fe45d1ab022 100644 --- a/packages/astro/src/assets/vite-plugin-assets.ts +++ b/packages/astro/src/assets/vite-plugin-assets.ts @@ -14,7 +14,7 @@ import { hashTransform, propsToFilename } from './utils/transformToPath.js'; const resolvedVirtualModuleId = '\0' + VIRTUAL_MODULE_ID; -const assetRegex = new RegExp(`\.(${VALID_INPUT_FORMATS.join('|')})$`, 'i'); +const assetRegex = new RegExp(`\\.(${VALID_INPUT_FORMATS.join('|')})$`, 'i'); export default function assets({ settings, diff --git a/packages/astro/test/core-image.test.js b/packages/astro/test/core-image.test.js index f5a1b28f4fd4..666739539a21 100644 --- a/packages/astro/test/core-image.test.js +++ b/packages/astro/test/core-image.test.js @@ -174,6 +174,22 @@ describe('astro:image', () => { expect(res.status).to.equal(200); expect(loading).to.not.be.undefined; }); + + it('supports avif', async () => { + let res = await fixture.fetch('/avif'); + let html = await res.text(); + $ = cheerio.load(html); + + console.log(html); + + let $img = $('img'); + expect($img).to.have.a.lengthOf(1); + + let src = $img.attr('src'); + res = await fixture.fetch(src); + expect(res.status).to.equal(200); + expect(res.headers.get('content-type')).to.equal('image/avif'); + }); }); describe('vite-isms', () => { diff --git a/packages/astro/test/fixtures/core-image/src/assets/light_walrus.avif b/packages/astro/test/fixtures/core-image/src/assets/light_walrus.avif new file mode 100644 index 0000000000000000000000000000000000000000..89e1c3a143111f0a609c83f05c5fecd0ddce77b2 GIT binary patch literal 19439 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 es8D~R1oB4={TAM+9brJNiAM diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 08cbf364366a..bb4bf96d7313 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -596,6 +596,9 @@ importers: preferred-pm: specifier: ^3.1.2 version: 3.1.2 + probe-image-size: + specifier: ^7.2.3 + version: 7.2.3 prompts: specifier: ^2.4.2 version: 2.4.2 @@ -706,6 +709,9 @@ importers: '@types/mocha': specifier: ^10.0.1 version: 10.0.1 + '@types/probe-image-size': + specifier: ^7.2.0 + version: 7.2.0 '@types/prompts': specifier: ^2.4.4 version: 2.4.4 @@ -8888,6 +8894,12 @@ packages: /@types/ms@0.7.31: resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} + /@types/needle@3.2.0: + resolution: {integrity: sha512-6XzvzEyJ2ozFNfPajFmqH9JOt0Hp+9TawaYpJT59iIP/zR0U37cfWCRwosyIeEBBZBi021Osq4jGAD3AOju5fg==} + dependencies: + '@types/node': 18.17.8 + dev: true + /@types/nlcst@1.0.0: resolution: {integrity: sha512-3TGCfOcy8R8mMQ4CNSNOe3PG66HttvjcLzCoOpvXvDtfWOTi+uT/rxeOKm/qEwbM4SNe1O/PjdiBK2YcTjU4OQ==} dependencies: @@ -8929,6 +8941,13 @@ packages: resolution: {integrity: sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==} dev: true + /@types/probe-image-size@7.2.0: + resolution: {integrity: sha512-R5H3vw62gHNHrn+JGZbKejb+Z2D/6E5UNVlhCzIaBBLroMQMOFqy5Pap2gM+ZZHdqBtVU0/cx/M6to+mOJcoew==} + dependencies: + '@types/needle': 3.2.0 + '@types/node': 18.17.8 + dev: true + /@types/prompts@2.4.4: resolution: {integrity: sha512-p5N9uoTH76lLvSAaYSZtBCdEXzpOOufsRjnhjVSrZGXikVGHX9+cc9ERtHRV4hvBKHyZb1bg4K+56Bd2TqUn4A==} dependencies: @@ -10748,6 +10767,17 @@ packages: dependencies: ms: 2.0.0 + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: false + /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -12510,7 +12540,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: safer-buffer: 2.1.2 - dev: true /iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} @@ -13241,7 +13270,6 @@ packages: /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true /lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} @@ -14327,6 +14355,18 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true + /needle@2.9.1: + resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==} + engines: {node: '>= 4.4.x'} + hasBin: true + dependencies: + debug: 3.2.7 + iconv-lite: 0.4.24 + sax: 1.2.4 + transitivePeerDependencies: + - supports-color + dev: false + /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -15424,6 +15464,16 @@ packages: engines: {node: '>=6'} dev: false + /probe-image-size@7.2.3: + resolution: {integrity: sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==} + dependencies: + lodash.merge: 4.6.2 + needle: 2.9.1 + stream-parser: 0.3.1 + transitivePeerDependencies: + - supports-color + dev: false + /progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -16058,7 +16108,6 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: true /sass-formatter@0.7.7: resolution: {integrity: sha512-axtQ7c7Cf4UgHsD8e4okhIkkc90+tdgBIfUMx69+qJuMNq9EOo2k+RH/mDKj0XeA5z3nC1Ca5TCntuxRhI+1MA==} @@ -16478,6 +16527,14 @@ packages: engines: {node: '>=4', npm: '>=6'} dev: true + /stream-parser@0.3.1: + resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==} + dependencies: + debug: 2.6.9 + transitivePeerDependencies: + - supports-color + dev: false + /stream-transform@2.1.3: resolution: {integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==} dependencies: From d93987824d3d6b4f58267be21ab8466ee8d5d5f8 Mon Sep 17 00:00:00 2001 From: Lars Kappert Date: Wed, 13 Sep 2023 17:29:39 +0200 Subject: [PATCH 32/33] feat(markdown): Add support for `imageReference` paths when collecting images (#8475) --- .changeset/grumpy-seas-learn.md | 5 ++++ packages/markdown/remark/package.json | 1 + .../remark/src/remark-collect-images.ts | 16 +++++++++-- .../remark/test/remark-collect-images.test.js | 28 +++++++++++++++++++ packages/markdown/remark/test/test-utils.js | 1 + pnpm-lock.yaml | 11 ++++++++ 6 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 .changeset/grumpy-seas-learn.md create mode 100644 packages/markdown/remark/test/remark-collect-images.test.js diff --git a/.changeset/grumpy-seas-learn.md b/.changeset/grumpy-seas-learn.md new file mode 100644 index 000000000000..84daf0f441a6 --- /dev/null +++ b/.changeset/grumpy-seas-learn.md @@ -0,0 +1,5 @@ +--- +'@astrojs/markdown-remark': minor +--- + +feat(markdown): Add support for `imageReference` paths when collecting images diff --git a/packages/markdown/remark/package.json b/packages/markdown/remark/package.json index 8710cce0854f..b25950c00f2f 100644 --- a/packages/markdown/remark/package.json +++ b/packages/markdown/remark/package.json @@ -34,6 +34,7 @@ "@astrojs/prism": "^3.0.0", "github-slugger": "^2.0.0", "import-meta-resolve": "^3.0.0", + "mdast-util-definitions": "^6.0.0", "rehype-raw": "^6.1.1", "rehype-stringify": "^9.0.4", "remark-gfm": "^3.0.1", diff --git a/packages/markdown/remark/src/remark-collect-images.ts b/packages/markdown/remark/src/remark-collect-images.ts index ecaa82d1dca1..cfce51376592 100644 --- a/packages/markdown/remark/src/remark-collect-images.ts +++ b/packages/markdown/remark/src/remark-collect-images.ts @@ -1,4 +1,5 @@ -import type { Image } from 'mdast'; +import type { Image, ImageReference } from 'mdast'; +import { definitions } from 'mdast-util-definitions'; import { visit } from 'unist-util-visit'; import type { MarkdownVFile } from './types.js'; @@ -6,9 +7,18 @@ export function remarkCollectImages() { return function (tree: any, vfile: MarkdownVFile) { if (typeof vfile?.path !== 'string') return; + const definition = definitions(tree); const imagePaths = new Set(); - visit(tree, 'image', (node: Image) => { - if (shouldOptimizeImage(node.url)) imagePaths.add(node.url); + visit(tree, ['image', 'imageReference'], (node: Image | ImageReference) => { + if (node.type === 'image') { + if (shouldOptimizeImage(node.url)) imagePaths.add(node.url); + } + if (node.type === 'imageReference') { + const imageDefinition = definition(node.identifier); + if (imageDefinition) { + if (shouldOptimizeImage(imageDefinition.url)) imagePaths.add(imageDefinition.url); + } + } }); vfile.data.imagePaths = imagePaths; diff --git a/packages/markdown/remark/test/remark-collect-images.test.js b/packages/markdown/remark/test/remark-collect-images.test.js new file mode 100644 index 000000000000..d5c743e20d6b --- /dev/null +++ b/packages/markdown/remark/test/remark-collect-images.test.js @@ -0,0 +1,28 @@ +import { renderMarkdown } from '../dist/index.js'; +import { mockRenderMarkdownParams } from './test-utils.js'; +import chai from 'chai'; + +describe('collect images', () => { + it('should collect inline image paths', async () => { + const { code, vfile } = await renderMarkdown( + `Hello ![inline image url](./img.png)`, + mockRenderMarkdownParams + ); + + chai + .expect(code) + .to.equal('

Hello inline image url

'); + + chai.expect(Array.from(vfile.data.imagePaths)).to.deep.equal(['./img.png']); + }); + + it('should add image paths from definition', async () => { + const { code, vfile } = await renderMarkdown( + `Hello ![image ref][img-ref]\n\n[img-ref]: ./img.webp`, + mockRenderMarkdownParams + ); + + chai.expect(code).to.equal('

Hello image ref

'); + chai.expect(Array.from(vfile.data.imagePaths)).to.deep.equal(['./img.webp']); + }); +}); diff --git a/packages/markdown/remark/test/test-utils.js b/packages/markdown/remark/test/test-utils.js index 10b779a7de79..76b593deb71c 100644 --- a/packages/markdown/remark/test/test-utils.js +++ b/packages/markdown/remark/test/test-utils.js @@ -1,3 +1,4 @@ export const mockRenderMarkdownParams = { + fileURL: 'file.md', contentDir: new URL('file:///src/content/'), }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bb4bf96d7313..e178a0ee91bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4918,6 +4918,9 @@ importers: import-meta-resolve: specifier: ^3.0.0 version: 3.0.0 + mdast-util-definitions: + specifier: ^6.0.0 + version: 6.0.0 rehype-raw: specifier: ^6.1.1 version: 6.1.1 @@ -13425,6 +13428,14 @@ packages: '@types/unist': 2.0.7 unist-util-visit: 4.1.2 + /mdast-util-definitions@6.0.0: + resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} + dependencies: + '@types/mdast': 4.0.0 + '@types/unist': 3.0.0 + unist-util-visit: 5.0.0 + dev: false + /mdast-util-find-and-replace@2.2.2: resolution: {integrity: sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==} dependencies: From c92e0acd715171b3f4c3294099780e21576648c8 Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Wed, 13 Sep 2023 17:57:59 +0200 Subject: [PATCH 33/33] feat(types): Add types for the object form of style (#8464) --- .changeset/long-trees-listen.md | 5 +++++ packages/astro/astro-jsx.d.ts | 29 ++++++++++++++++++++++++++++- packages/astro/src/type-utils.ts | 8 ++++++++ packages/astro/types.d.ts | 5 +++++ 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 .changeset/long-trees-listen.md diff --git a/.changeset/long-trees-listen.md b/.changeset/long-trees-listen.md new file mode 100644 index 000000000000..3778873ae521 --- /dev/null +++ b/.changeset/long-trees-listen.md @@ -0,0 +1,5 @@ +--- +'astro': minor +--- + +Add types for the object syntax for `style` (ex: `style={{color: 'red'}}`) diff --git a/packages/astro/astro-jsx.d.ts b/packages/astro/astro-jsx.d.ts index b82de9388591..3e8a86889282 100644 --- a/packages/astro/astro-jsx.d.ts +++ b/packages/astro/astro-jsx.d.ts @@ -471,6 +471,33 @@ declare namespace astroHTML.JSX { | 'treegrid' | 'treeitem'; + type CssProperty = keyof Omit< + CSSStyleDeclaration, + | 'item' + | 'setProperty' + | 'removeProperty' + | 'getPropertyValue' + | 'getPropertyPriority' + | 'parentRule' + | 'length' + | 'cssFloat' + | 'cssText' + | typeof Symbol.iterator + | number + >; + + type KebabCSSDOMProperties = import('./dist/type-utils.js').KebabKeys; + + type DOMCSSProperties = { + [key in CssProperty]?: string | number | null | undefined; + }; + type AllCSSProperties = { + [key: string]: string | number | null | undefined; + }; + type StyleObject = import('./dist/type-utils.js').Simplify< + KebabCSSDOMProperties & DOMCSSProperties & AllCSSProperties + >; + interface HTMLAttributes extends AriaAttributes, DOMAttributes, AstroBuiltinAttributes { // Standard HTML Attributes accesskey?: string | undefined | null; @@ -513,7 +540,7 @@ declare namespace astroHTML.JSX { lang?: string | undefined | null; slot?: string | undefined | null; spellcheck?: 'true' | 'false' | boolean | undefined | null; - style?: string | Record | undefined | null; + style?: string | StyleObject | undefined | null; tabindex?: number | string | undefined | null; title?: string | undefined | null; translate?: 'yes' | 'no' | undefined | null; diff --git a/packages/astro/src/type-utils.ts b/packages/astro/src/type-utils.ts index 926b0349d5c9..96970f7c4fb1 100644 --- a/packages/astro/src/type-utils.ts +++ b/packages/astro/src/type-utils.ts @@ -17,5 +17,13 @@ export type OmitIndexSignature = { : KeyType]: ObjectType[KeyType]; }; +// Transform a string into its kebab case equivalent (camelCase -> kebab-case). Useful for CSS-in-JS to CSS. +export type Kebab = T extends `${infer F}${infer R}` + ? Kebab ? '' : '-'}${Lowercase}`> + : A; + +// Transform every key of an object to its kebab case equivalent using the above utility +export type KebabKeys = { [K in keyof T as K extends string ? Kebab : K]: T[K] }; + // Similar to `keyof`, gets the type of all the values of an object export type ValueOf = T[keyof T]; diff --git a/packages/astro/types.d.ts b/packages/astro/types.d.ts index da48b5e1b66d..638ad762e81b 100644 --- a/packages/astro/types.d.ts +++ b/packages/astro/types.d.ts @@ -9,6 +9,11 @@ export type HTMLAttributes = Omit< keyof Omit >; +/** + * All the CSS properties available, as defined by the CSS specification + */ +export type CSSProperty = keyof astroHTML.JSX.KebabCSSDOMProperties; + type PolymorphicAttributes

= Omit

, 'as'> & { as?: P['as']; };