diff --git a/.changeset/angry-games-train.md b/.changeset/angry-games-train.md deleted file mode 100644 index aa109bba45..0000000000 --- a/.changeset/angry-games-train.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'skeleton': patch ---- - -Updated internal libraries for parsing `.env` file. - -Please update the `@shopify/cli` dependency in your app to avoid duplicated subdependencies: - - ```diff - "dependencies": { - - "@shopify/cli": "3.56.3", - + "@shopify/cli": "3.58.0", - } - ``` diff --git a/.changeset/little-paws-fry.md b/.changeset/little-paws-fry.md new file mode 100644 index 0000000000..6ffa5deff8 --- /dev/null +++ b/.changeset/little-paws-fry.md @@ -0,0 +1,20 @@ +--- +'skeleton': patch +--- + +In Vite projects, the way that plugins are imported and the options passed to Remix have changed: + +```diff +-import {hydrogen, oxygen} from '@shopify/cli-hydrogen/experimental-vite'; ++import {hydrogen} from '@shopify/hydrogen/vite'; ++import {oxygen} from '@shopify/mini-oxygen/vite'; +import {vitePlugin as remix} from '@remix-run/dev'; + +export default defineConfig({ + hydrogen(), + oxygen(), + remix({ +- buildDirectory: 'dist', ++ presets: [hydrogen.preset()], + future: { +``` diff --git a/.changeset/polite-seahorses-fail.md b/.changeset/polite-seahorses-fail.md new file mode 100644 index 0000000000..dd32236ecf --- /dev/null +++ b/.changeset/polite-seahorses-fail.md @@ -0,0 +1,5 @@ +--- +'@shopify/hydrogen': patch +--- + +A new Vite plugin is exported from `@shopify/hydrogen/vite`. It provides DX improvements for Vite users, such as adding `/subrequest-profiler` and `/graphiql`. diff --git a/.changeset/shy-bulldogs-smile.md b/.changeset/shy-bulldogs-smile.md new file mode 100644 index 0000000000..8842759950 --- /dev/null +++ b/.changeset/shy-bulldogs-smile.md @@ -0,0 +1,5 @@ +--- +'@shopify/remix-oxygen': patch +--- + +Fix compatibility of `/subrequest-profiler` with Vite. diff --git a/.changeset/shy-seas-own.md b/.changeset/shy-seas-own.md new file mode 100644 index 0000000000..dfe3069af3 --- /dev/null +++ b/.changeset/shy-seas-own.md @@ -0,0 +1,5 @@ +--- +'@shopify/cli-hydrogen': minor +--- + +The `@shopify/cli-hydrogen/experimental-vite` import path has been removed. Instead, use `@shopify/hydrogen/vite` and `@shopify/mini-oxygen/vite` to import the Vite plugins. diff --git a/.changeset/strong-beds-call.md b/.changeset/strong-beds-call.md new file mode 100644 index 0000000000..6b9fc8a157 --- /dev/null +++ b/.changeset/strong-beds-call.md @@ -0,0 +1,5 @@ +--- +'@shopify/mini-oxygen': minor +--- + +A new Vite plugin is exported from `@shopify/mini-oxygen/vite`. It integrates Vite with MiniOxygen by running the application code within a worker. diff --git a/.gitignore b/.gitignore index 8f23240b50..081a212642 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ node_modules .vscode .turbo .cache +.shopify /test-results/ /playwright-report/ /playwright/.cache/ diff --git a/package-lock.json b/package-lock.json index b7645eb2bb..581b401110 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4291,9 +4291,9 @@ } }, "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.8.tgz", - "integrity": "sha512-rB+2P3oi9fD4TcsijkflJAQqOh4yZrPgOV4fGaDgCdOqqwTicJvL2nnVbr3comW8bxEuypOcyE1AtBtkpip0Gw==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", + "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", "dependencies": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/events": "^0.1.0", @@ -4517,9 +4517,9 @@ } }, "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/node-fetch": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.8.tgz", - "integrity": "sha512-rB+2P3oi9fD4TcsijkflJAQqOh4yZrPgOV4fGaDgCdOqqwTicJvL2nnVbr3comW8bxEuypOcyE1AtBtkpip0Gw==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", + "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", "dependencies": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/events": "^0.1.0", @@ -4665,9 +4665,9 @@ } }, "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.8.tgz", - "integrity": "sha512-rB+2P3oi9fD4TcsijkflJAQqOh4yZrPgOV4fGaDgCdOqqwTicJvL2nnVbr3comW8bxEuypOcyE1AtBtkpip0Gw==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", + "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", "dependencies": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/events": "^0.1.0", @@ -4901,9 +4901,9 @@ } }, "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.8.tgz", - "integrity": "sha512-rB+2P3oi9fD4TcsijkflJAQqOh4yZrPgOV4fGaDgCdOqqwTicJvL2nnVbr3comW8bxEuypOcyE1AtBtkpip0Gw==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", + "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", "dependencies": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/events": "^0.1.0", @@ -5027,9 +5027,9 @@ } }, "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.8.tgz", - "integrity": "sha512-rB+2P3oi9fD4TcsijkflJAQqOh4yZrPgOV4fGaDgCdOqqwTicJvL2nnVbr3comW8bxEuypOcyE1AtBtkpip0Gw==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", + "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", "dependencies": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/events": "^0.1.0", @@ -5521,9 +5521,9 @@ } }, "node_modules/@ladle/react/node_modules/vite": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", - "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", + "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", "dev": true, "dependencies": { "esbuild": "^0.18.10", @@ -6140,9 +6140,9 @@ } }, "node_modules/@npmcli/package-json/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -7296,9 +7296,9 @@ } }, "node_modules/@remix-run/dev/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -7591,9 +7591,9 @@ "integrity": "sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.1.tgz", + "integrity": "sha512-fH8/o8nSUek8ceQnT7K4EQbSiV7jgkHq81m9lWZFIXjJ7lJzpWXbQFpT/Zh6OZYnpFykvzC3fbEvEAFZu03dPA==", "cpu": [ "arm" ], @@ -7604,9 +7604,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.1.tgz", + "integrity": "sha512-Y/9OHLjzkunF+KGEoJr3heiD5X9OLa8sbT1lm0NYeKyaM3oMhhQFvPB0bNZYJwlq93j8Z6wSxh9+cyKQaxS7PQ==", "cpu": [ "arm64" ], @@ -7617,9 +7617,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.1.tgz", + "integrity": "sha512-+kecg3FY84WadgcuSVm6llrABOdQAEbNdnpi5X3UwWiFVhZIZvKgGrF7kmLguvxHNQy+UuRV66cLVl3S+Rkt+Q==", "cpu": [ "arm64" ], @@ -7630,9 +7630,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.1.tgz", + "integrity": "sha512-2pYRzEjVqq2TB/UNv47BV/8vQiXkFGVmPFwJb+1E0IFFZbIX8/jo1olxqqMbo6xCXf8kabANhp5bzCij2tFLUA==", "cpu": [ "x64" ], @@ -7643,9 +7643,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.1.tgz", + "integrity": "sha512-mS6wQ6Do6/wmrF9aTFVpIJ3/IDXhg1EZcQFYHZLHqw6AzMBjTHWnCG35HxSqUNphh0EHqSM6wRTT8HsL1C0x5g==", "cpu": [ "arm" ], @@ -7656,9 +7656,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.1.tgz", + "integrity": "sha512-p9rGKYkHdFMzhckOTFubfxgyIO1vw//7IIjBBRVzyZebWlzRLeNhqxuSaZ7kCEKVkm/kuC9fVRW9HkC/zNRG2w==", "cpu": [ "arm64" ], @@ -7669,9 +7669,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.1.tgz", + "integrity": "sha512-nDY6Yz5xS/Y4M2i9JLQd3Rofh5OR8Bn8qe3Mv/qCVpHFlwtZSBYSPaU4mrGazWkXrdQ98GB//H0BirGR/SKFSw==", "cpu": [ "arm64" ], @@ -7681,10 +7681,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.1.tgz", + "integrity": "sha512-im7HE4VBL+aDswvcmfx88Mp1soqL9OBsdDBU8NqDEYtkri0qV0THhQsvZtZeNNlLeCUQ16PZyv7cqutjDF35qw==", + "cpu": [ + "ppc64le" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.1.tgz", + "integrity": "sha512-RWdiHuAxWmzPJgaHJdpvUUlDz8sdQz4P2uv367T2JocdDa98iRw2UjIJ4QxSyt077mXZT2X6pKfT2iYtVEvOFw==", "cpu": [ "riscv64" ], @@ -7694,10 +7707,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.1.tgz", + "integrity": "sha512-VMgaGQ5zRX6ZqV/fas65/sUGc9cPmsntq2FiGmayW9KMNfWVG/j0BAqImvU4KTeOOgYSf1F+k6at1UfNONuNjA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.1.tgz", + "integrity": "sha512-9Q7DGjZN+hTdJomaQ3Iub4m6VPu1r94bmK2z3UeWP3dGUecRC54tmVu9vKHTm1bOt3ASoYtEz6JSRLFzrysKlA==", "cpu": [ "x64" ], @@ -7708,9 +7734,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.1.tgz", + "integrity": "sha512-JNEG/Ti55413SsreTguSx0LOVKX902OfXIKVg+TCXO6Gjans/k9O6ww9q3oLGjNDaTLxM+IHFMeXy/0RXL5R/g==", "cpu": [ "x64" ], @@ -7721,9 +7747,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.1.tgz", + "integrity": "sha512-ryS22I9y0mumlLNwDFYZRDFLwWh3aKaC72CWjFcFvxK0U6v/mOkM5Up1bTbCRAhv3kEIwW2ajROegCIQViUCeA==", "cpu": [ "arm64" ], @@ -7734,9 +7760,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.1.tgz", + "integrity": "sha512-TdloItiGk+T0mTxKx7Hp279xy30LspMso+GzQvV2maYePMAWdmrzqSNZhUpPj3CGw12aGj57I026PgLCTu8CGg==", "cpu": [ "ia32" ], @@ -7747,9 +7773,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.1.tgz", + "integrity": "sha512-wQGI+LY/Py20zdUPq+XCem7JcPOyzIJBm3dli+56DJsQOHbnXZFEwgmnC6el1TPAfC8lBT3m+z69RmLykNUbew==", "cpu": [ "x64" ], @@ -10987,9 +11013,9 @@ } }, "node_modules/@shopify/graphql-codegen/node_modules/type-fest": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz", - "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", + "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==", "engines": { "node": ">=16" }, @@ -11701,7 +11727,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "devOptional": true }, "node_modules/@types/estree-jsx": { "version": "1.0.2", @@ -11917,9 +11943,9 @@ "dev": true }, "node_modules/@types/mime": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.4.tgz", - "integrity": "sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, "node_modules/@types/minimist": { @@ -12040,12 +12066,6 @@ "@types/node": "*" } }, - "node_modules/@types/send/node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, "node_modules/@types/serve-static": { "version": "1.15.1", "dev": true, @@ -12854,9 +12874,9 @@ } }, "node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dependencies": { "debug": "^4.3.4" }, @@ -13722,8 +13742,9 @@ "license": "MIT" }, "node_modules/builtins": { - "version": "5.0.1", - "license": "MIT", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", "dependencies": { "semver": "^7.0.0" } @@ -13821,16 +13842,16 @@ } }, "node_modules/cacache/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", + "jackspeak": "^2.3.6", "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" }, "bin": { "glob": "dist/esm/bin.mjs" @@ -13852,9 +13873,9 @@ } }, "node_modules/cacache/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -19146,23 +19167,9 @@ } }, "node_modules/ink/node_modules/ansi-escapes": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", - "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", - "dependencies": { - "type-fest": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ink/node_modules/ansi-escapes/node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", "engines": { "node": ">=14.16" }, @@ -21252,9 +21259,9 @@ } }, "node_modules/loglevel": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", - "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", + "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", "engines": { "node": ">= 0.6.0" }, @@ -21376,9 +21383,9 @@ "integrity": "sha512-vGBKTA+jwM4KgjGZ+S/8/Mkj9rWzePyGY6jManXPGhiWu63RYwW8dKPyk5koP+8qNVhPhHgFa1y/MJ4wrjsNrg==" }, "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "version": "0.30.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz", + "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -22917,7 +22924,7 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, + "devOptional": true, "funding": [ { "type": "github", @@ -24050,12 +24057,12 @@ } }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { @@ -24398,7 +24405,7 @@ "version": "8.4.35", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", - "dev": true, + "devOptional": true, "funding": [ { "type": "opencollective", @@ -25311,9 +25318,9 @@ "license": "MIT" }, "node_modules/property-information": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz", - "integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "dev": true, "funding": { "type": "github", @@ -27148,7 +27155,7 @@ }, "node_modules/source-map-js": { "version": "1.0.2", - "dev": true, + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -27898,9 +27905,9 @@ } }, "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, "dependencies": { "chownr": "^2.0.0", @@ -28776,9 +28783,9 @@ } }, "node_modules/typescript": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", - "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", + "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", "devOptional": true, "bin": { "tsc": "bin/tsc", @@ -29299,10 +29306,10 @@ } }, "node_modules/vite": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.7.tgz", - "integrity": "sha512-sgnEEFTZYMui/sTlH1/XEnVNHMujOahPLGMxn1+5sIT45Xjng1Ec1K78jRP15dSmVgg5WBin9yO81j3o9OxofA==", - "dev": true, + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz", + "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==", + "devOptional": true, "dependencies": { "esbuild": "^0.19.3", "postcss": "^8.4.35", @@ -29750,7 +29757,7 @@ "version": "0.19.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -29785,10 +29792,10 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", - "dev": true, + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.1.tgz", + "integrity": "sha512-4LnHSdd3QK2pa1J6dFbfm1HN0D7vSK/ZuZTsdyUAlA6Rr1yTouUTL13HaDOGJVgby461AhrNGBS7sCGXXtT+SA==", + "devOptional": true, "dependencies": { "@types/estree": "1.0.5" }, @@ -29800,19 +29807,21 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@rollup/rollup-android-arm-eabi": "4.14.1", + "@rollup/rollup-android-arm64": "4.14.1", + "@rollup/rollup-darwin-arm64": "4.14.1", + "@rollup/rollup-darwin-x64": "4.14.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.1", + "@rollup/rollup-linux-arm64-gnu": "4.14.1", + "@rollup/rollup-linux-arm64-musl": "4.14.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.1", + "@rollup/rollup-linux-riscv64-gnu": "4.14.1", + "@rollup/rollup-linux-s390x-gnu": "4.14.1", + "@rollup/rollup-linux-x64-gnu": "4.14.1", + "@rollup/rollup-linux-x64-musl": "4.14.1", + "@rollup/rollup-win32-arm64-msvc": "4.14.1", + "@rollup/rollup-win32-ia32-msvc": "4.14.1", + "@rollup/rollup-win32-x64-msvc": "4.14.1", "fsevents": "~2.3.2" } }, @@ -30602,7 +30611,7 @@ "flame-chart-js": "2.3.2", "get-port": "^7.0.0", "type-fest": "^4.5.0", - "vite": "~5.1.7", + "vite": "~5.1.0", "vitest": "^1.0.4" }, "engines": { @@ -30614,7 +30623,7 @@ "peerDependencies": { "@remix-run/dev": "^2.1.0", "@shopify/mini-oxygen": "^2.2.5", - "vite": "~5.1.7" + "vite": "~5.1.0" }, "peerDependenciesMeta": { "@remix-run/dev": { @@ -30665,22 +30674,9 @@ } }, "packages/cli/node_modules/ansi-escapes": { - "version": "6.2.0", - "license": "MIT", - "dependencies": { - "type-fest": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/cli/node_modules/ansi-escapes/node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", "engines": { "node": ">=14.16" }, @@ -30744,9 +30740,10 @@ } }, "packages/cli/node_modules/get-port": { - "version": "7.0.0", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", "dev": true, - "license": "MIT", "engines": { "node": ">=16" }, @@ -30811,9 +30808,9 @@ } }, "packages/cli/node_modules/type-fest": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz", - "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", + "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==", "dev": true, "engines": { "node": ">=16" @@ -30840,6 +30837,7 @@ "dependencies": { "@shopify/hydrogen-react": "2024.1.1", "content-security-policy-builder": "^2.1.1", + "source-map-support": "^0.5.21", "type-fest": "^4.5.0" }, "devDependencies": { @@ -30848,6 +30846,7 @@ "@shopify/generate-docs": "0.11.1", "@shopify/hydrogen-codegen": "*", "@testing-library/react": "^14.0.0", + "@types/source-map-support": "^0.5.10", "happy-dom": "^8.9.0", "react": "^18.2.0", "schema-dts": "^1.1.0", @@ -30856,7 +30855,13 @@ "peerDependencies": { "@remix-run/react": "^2.1.0", "@remix-run/server-runtime": "^2.1.0", - "react": "^18.2.0" + "react": "^18.2.0", + "vite": "~5.1.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } } }, "packages/hydrogen-codegen": { @@ -30875,9 +30880,9 @@ } }, "packages/hydrogen-codegen/node_modules/type-fest": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz", - "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", + "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==", "dev": true, "engines": { "node": ">=16" @@ -30930,7 +30935,7 @@ "rimraf": "^4.1.2", "ts-expect": "^1.3.0", "typescript": "^5.2.2", - "vite": "~5.1.7", + "vite": "~5.1.0", "vitest": "^1.0.4" }, "engines": { @@ -31094,9 +31099,9 @@ } }, "packages/hydrogen-react/node_modules/type-fest": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz", - "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", + "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==", "engines": { "node": ">=16" }, @@ -31113,9 +31118,9 @@ } }, "packages/hydrogen/node_modules/type-fest": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz", - "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", + "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==", "engines": { "node": ">=16" }, @@ -31163,6 +31168,14 @@ }, "engines": { "node": ">=18.0.0" + }, + "peerDependencies": { + "vite": "~5.1.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } } }, "packages/mini-oxygen/node_modules/@types/stack-trace": { @@ -31186,9 +31199,9 @@ } }, "packages/mini-oxygen/node_modules/get-port": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.0.0.tgz", - "integrity": "sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", "engines": { "node": ">=16" }, @@ -31291,7 +31304,7 @@ "eslint-plugin-hydrogen": "0.12.2", "prettier": "^2.8.4", "typescript": "^5.2.2", - "vite": "~5.1.7", + "vite": "~5.1.0", "vite-tsconfig-paths": "^4.3.1" }, "engines": { @@ -33940,9 +33953,9 @@ } }, "@whatwg-node/node-fetch": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.8.tgz", - "integrity": "sha512-rB+2P3oi9fD4TcsijkflJAQqOh4yZrPgOV4fGaDgCdOqqwTicJvL2nnVbr3comW8bxEuypOcyE1AtBtkpip0Gw==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", + "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", "requires": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/events": "^0.1.0", @@ -34120,9 +34133,9 @@ } }, "@whatwg-node/node-fetch": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.8.tgz", - "integrity": "sha512-rB+2P3oi9fD4TcsijkflJAQqOh4yZrPgOV4fGaDgCdOqqwTicJvL2nnVbr3comW8bxEuypOcyE1AtBtkpip0Gw==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", + "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", "requires": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/events": "^0.1.0", @@ -34222,9 +34235,9 @@ } }, "@whatwg-node/node-fetch": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.8.tgz", - "integrity": "sha512-rB+2P3oi9fD4TcsijkflJAQqOh4yZrPgOV4fGaDgCdOqqwTicJvL2nnVbr3comW8bxEuypOcyE1AtBtkpip0Gw==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", + "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", "requires": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/events": "^0.1.0", @@ -34414,9 +34427,9 @@ } }, "@whatwg-node/node-fetch": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.8.tgz", - "integrity": "sha512-rB+2P3oi9fD4TcsijkflJAQqOh4yZrPgOV4fGaDgCdOqqwTicJvL2nnVbr3comW8bxEuypOcyE1AtBtkpip0Gw==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", + "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", "requires": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/events": "^0.1.0", @@ -34516,9 +34529,9 @@ } }, "@whatwg-node/node-fetch": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.8.tgz", - "integrity": "sha512-rB+2P3oi9fD4TcsijkflJAQqOh4yZrPgOV4fGaDgCdOqqwTicJvL2nnVbr3comW8bxEuypOcyE1AtBtkpip0Gw==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", + "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", "requires": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/events": "^0.1.0", @@ -34858,9 +34871,9 @@ "dev": true }, "vite": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", - "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", + "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", "dev": true, "requires": { "esbuild": "^0.18.10", @@ -35289,9 +35302,9 @@ "dev": true }, "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -36020,9 +36033,9 @@ "dev": true }, "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -36219,93 +36232,107 @@ "integrity": "sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==" }, "@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.1.tgz", + "integrity": "sha512-fH8/o8nSUek8ceQnT7K4EQbSiV7jgkHq81m9lWZFIXjJ7lJzpWXbQFpT/Zh6OZYnpFykvzC3fbEvEAFZu03dPA==", "dev": true, "optional": true }, "@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.1.tgz", + "integrity": "sha512-Y/9OHLjzkunF+KGEoJr3heiD5X9OLa8sbT1lm0NYeKyaM3oMhhQFvPB0bNZYJwlq93j8Z6wSxh9+cyKQaxS7PQ==", "dev": true, "optional": true }, "@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.1.tgz", + "integrity": "sha512-+kecg3FY84WadgcuSVm6llrABOdQAEbNdnpi5X3UwWiFVhZIZvKgGrF7kmLguvxHNQy+UuRV66cLVl3S+Rkt+Q==", "dev": true, "optional": true }, "@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.1.tgz", + "integrity": "sha512-2pYRzEjVqq2TB/UNv47BV/8vQiXkFGVmPFwJb+1E0IFFZbIX8/jo1olxqqMbo6xCXf8kabANhp5bzCij2tFLUA==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.1.tgz", + "integrity": "sha512-mS6wQ6Do6/wmrF9aTFVpIJ3/IDXhg1EZcQFYHZLHqw6AzMBjTHWnCG35HxSqUNphh0EHqSM6wRTT8HsL1C0x5g==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.1.tgz", + "integrity": "sha512-p9rGKYkHdFMzhckOTFubfxgyIO1vw//7IIjBBRVzyZebWlzRLeNhqxuSaZ7kCEKVkm/kuC9fVRW9HkC/zNRG2w==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.1.tgz", + "integrity": "sha512-nDY6Yz5xS/Y4M2i9JLQd3Rofh5OR8Bn8qe3Mv/qCVpHFlwtZSBYSPaU4mrGazWkXrdQ98GB//H0BirGR/SKFSw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.1.tgz", + "integrity": "sha512-im7HE4VBL+aDswvcmfx88Mp1soqL9OBsdDBU8NqDEYtkri0qV0THhQsvZtZeNNlLeCUQ16PZyv7cqutjDF35qw==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.1.tgz", + "integrity": "sha512-RWdiHuAxWmzPJgaHJdpvUUlDz8sdQz4P2uv367T2JocdDa98iRw2UjIJ4QxSyt077mXZT2X6pKfT2iYtVEvOFw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.1.tgz", + "integrity": "sha512-VMgaGQ5zRX6ZqV/fas65/sUGc9cPmsntq2FiGmayW9KMNfWVG/j0BAqImvU4KTeOOgYSf1F+k6at1UfNONuNjA==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.1.tgz", + "integrity": "sha512-9Q7DGjZN+hTdJomaQ3Iub4m6VPu1r94bmK2z3UeWP3dGUecRC54tmVu9vKHTm1bOt3ASoYtEz6JSRLFzrysKlA==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.1.tgz", + "integrity": "sha512-JNEG/Ti55413SsreTguSx0LOVKX902OfXIKVg+TCXO6Gjans/k9O6ww9q3oLGjNDaTLxM+IHFMeXy/0RXL5R/g==", "dev": true, "optional": true }, "@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.1.tgz", + "integrity": "sha512-ryS22I9y0mumlLNwDFYZRDFLwWh3aKaC72CWjFcFvxK0U6v/mOkM5Up1bTbCRAhv3kEIwW2ajROegCIQViUCeA==", "dev": true, "optional": true }, "@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.1.tgz", + "integrity": "sha512-TdloItiGk+T0mTxKx7Hp279xy30LspMso+GzQvV2maYePMAWdmrzqSNZhUpPj3CGw12aGj57I026PgLCTu8CGg==", "dev": true, "optional": true }, "@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.1.tgz", + "integrity": "sha512-wQGI+LY/Py20zdUPq+XCem7JcPOyzIJBm3dli+56DJsQOHbnXZFEwgmnC6el1TPAfC8lBT3m+z69RmLykNUbew==", "dev": true, "optional": true }, @@ -38168,7 +38195,7 @@ "ts-morph": "20.0.0", "type-fest": "^4.5.0", "use-resize-observer": "^9.1.0", - "vite": "~5.1.7", + "vite": "~5.1.0", "vitest": "^1.0.4" }, "dependencies": { @@ -38193,17 +38220,9 @@ } }, "ansi-escapes": { - "version": "6.2.0", - "requires": { - "type-fest": "^3.0.0" - }, - "dependencies": { - "type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==" - } - } + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==" }, "ansi-regex": { "version": "6.0.1", @@ -38240,7 +38259,9 @@ } }, "get-port": { - "version": "7.0.0", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", "dev": true }, "is-fullwidth-code-point": { @@ -38276,9 +38297,9 @@ } }, "type-fest": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz", - "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", + "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==", "dev": true } } @@ -38525,9 +38546,9 @@ }, "dependencies": { "type-fest": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz", - "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==" + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", + "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==" } } }, @@ -38540,18 +38561,20 @@ "@shopify/hydrogen-codegen": "*", "@shopify/hydrogen-react": "2024.1.1", "@testing-library/react": "^14.0.0", + "@types/source-map-support": "^0.5.10", "content-security-policy-builder": "^2.1.1", "happy-dom": "^8.9.0", "react": "^18.2.0", "schema-dts": "^1.1.0", + "source-map-support": "^0.5.21", "type-fest": "^4.5.0", "vitest": "^1.0.4" }, "dependencies": { "type-fest": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz", - "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==" + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", + "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==" } } }, @@ -38567,9 +38590,9 @@ }, "dependencies": { "type-fest": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz", - "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", + "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==", "dev": true } } @@ -38613,7 +38636,7 @@ "ts-expect": "^1.3.0", "type-fest": "^4.5.0", "typescript": "^5.2.2", - "vite": "~5.1.7", + "vite": "~5.1.0", "vitest": "^1.0.4", "worktop": "^0.7.3" }, @@ -38712,9 +38735,9 @@ } }, "type-fest": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz", - "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==" + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", + "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==" }, "webidl-conversions": { "version": "7.0.0", @@ -38775,9 +38798,9 @@ } }, "get-port": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.0.0.tgz", - "integrity": "sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw==" + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==" }, "ws": { "version": "8.16.0", @@ -39267,7 +39290,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "devOptional": true }, "@types/estree-jsx": { "version": "1.0.2", @@ -39453,9 +39476,9 @@ "dev": true }, "@types/mime": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.4.tgz", - "integrity": "sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, "@types/minimist": { @@ -39562,14 +39585,6 @@ "requires": { "@types/mime": "^1", "@types/node": "*" - }, - "dependencies": { - "@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - } } }, "@types/serve-static": { @@ -40126,9 +40141,9 @@ "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==" }, "agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "requires": { "debug": "^4.3.4" } @@ -40689,7 +40704,9 @@ "version": "1.1.2" }, "builtins": { - "version": "5.0.1", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", "requires": { "semver": "^7.0.0" } @@ -40756,16 +40773,16 @@ } }, "glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", "dev": true, "requires": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", + "jackspeak": "^2.3.6", "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" } }, "lru-cache": { @@ -40775,9 +40792,9 @@ "dev": true }, "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -44372,19 +44389,9 @@ }, "dependencies": { "ansi-escapes": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", - "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", - "requires": { - "type-fest": "^3.0.0" - }, - "dependencies": { - "type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==" - } - } + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==" }, "ansi-regex": { "version": "6.0.1", @@ -45717,9 +45724,9 @@ } }, "loglevel": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", - "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==" + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", + "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==" }, "longest-streak": { "version": "3.0.1", @@ -45808,9 +45815,9 @@ "integrity": "sha512-vGBKTA+jwM4KgjGZ+S/8/Mkj9rWzePyGY6jManXPGhiWu63RYwW8dKPyk5koP+8qNVhPhHgFa1y/MJ4wrjsNrg==" }, "magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "version": "0.30.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz", + "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==", "dev": true, "requires": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -46744,7 +46751,7 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true + "devOptional": true }, "natural-compare": { "version": "1.4.0", @@ -47491,12 +47498,12 @@ "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==" }, "path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", "dev": true, "requires": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "dependencies": { @@ -47730,7 +47737,7 @@ "version": "8.4.35", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", - "dev": true, + "devOptional": true, "requires": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", @@ -48211,9 +48218,9 @@ } }, "property-information": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz", - "integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "dev": true }, "proto-list": { @@ -49302,7 +49309,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "typescript": "^5.2.2", - "vite": "~5.1.7", + "vite": "~5.1.0", "vite-tsconfig-paths": "^4.3.1" } }, @@ -49477,7 +49484,7 @@ }, "source-map-js": { "version": "1.0.2", - "dev": true + "devOptional": true }, "source-map-support": { "version": "0.5.21", @@ -50001,9 +50008,9 @@ "dev": true }, "tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -50575,9 +50582,9 @@ } }, "typescript": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", - "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", + "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", "devOptional": true }, "ua-parser-js": { @@ -50910,10 +50917,10 @@ } }, "vite": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.7.tgz", - "integrity": "sha512-sgnEEFTZYMui/sTlH1/XEnVNHMujOahPLGMxn1+5sIT45Xjng1Ec1K78jRP15dSmVgg5WBin9yO81j3o9OxofA==", - "dev": true, + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz", + "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==", + "devOptional": true, "requires": { "esbuild": "^0.19.3", "fsevents": "~2.3.3", @@ -51079,7 +51086,7 @@ "version": "0.19.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", - "dev": true, + "devOptional": true, "requires": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", @@ -51107,24 +51114,26 @@ } }, "rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", - "dev": true, - "requires": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.1.tgz", + "integrity": "sha512-4LnHSdd3QK2pa1J6dFbfm1HN0D7vSK/ZuZTsdyUAlA6Rr1yTouUTL13HaDOGJVgby461AhrNGBS7sCGXXtT+SA==", + "devOptional": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.14.1", + "@rollup/rollup-android-arm64": "4.14.1", + "@rollup/rollup-darwin-arm64": "4.14.1", + "@rollup/rollup-darwin-x64": "4.14.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.1", + "@rollup/rollup-linux-arm64-gnu": "4.14.1", + "@rollup/rollup-linux-arm64-musl": "4.14.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.1", + "@rollup/rollup-linux-riscv64-gnu": "4.14.1", + "@rollup/rollup-linux-s390x-gnu": "4.14.1", + "@rollup/rollup-linux-x64-gnu": "4.14.1", + "@rollup/rollup-linux-x64-musl": "4.14.1", + "@rollup/rollup-win32-arm64-msvc": "4.14.1", + "@rollup/rollup-win32-ia32-msvc": "4.14.1", + "@rollup/rollup-win32-x64-msvc": "4.14.1", "@types/estree": "1.0.5", "fsevents": "~2.3.2" } diff --git a/packages/cli/package.json b/packages/cli/package.json index 67844e1cee..94ba65402e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -29,7 +29,7 @@ "flame-chart-js": "2.3.2", "get-port": "^7.0.0", "type-fest": "^4.5.0", - "vite": "~5.1.7", + "vite": "~5.1.0", "vitest": "^1.0.4" }, "dependencies": { @@ -62,7 +62,7 @@ "peerDependencies": { "@remix-run/dev": "^2.1.0", "@shopify/mini-oxygen": "^2.2.5", - "vite": "~5.1.7" + "vite": "~5.1.0" }, "peerDependenciesMeta": { "@remix-run/dev": { @@ -79,12 +79,8 @@ "exports": { "./package.json": "./package.json", "./commands/hydrogen/init": { - "types": "./dist/commands/hydrogen/init.d.ts", + "types": "./dist/init.d.ts", "default": "./dist/commands/hydrogen/init.js" - }, - "./experimental-vite": { - "types": "./dist/lib/vite/plugins.d.ts", - "default": "./dist/lib/vite/plugins.js" } }, "files": [ diff --git a/packages/cli/src/commands/hydrogen/dev-vite.ts b/packages/cli/src/commands/hydrogen/dev-vite.ts index 956c0d1ef8..f088a37d58 100644 --- a/packages/cli/src/commands/hydrogen/dev-vite.ts +++ b/packages/cli/src/commands/hydrogen/dev-vite.ts @@ -23,7 +23,6 @@ import {spawnCodegenProcess} from '../../lib/codegen.js'; import {getAllEnvironmentVariables} from '../../lib/environment-variables.js'; import {displayDevUpgradeNotice} from './upgrade.js'; import {prepareDiffDirectory} from '../../lib/template-diff.js'; -import {setH2OPluginContext} from '../../lib/vite/shared.js'; import { getDebugBannerLine, startTunnelAndPushConfig, @@ -35,6 +34,8 @@ import { } from '../../lib/dev-shared.js'; import {getCliCommand} from '../../lib/shell.js'; import {findPort} from '../../lib/find-port.js'; +import {logRequestLine} from '../../lib/mini-oxygen/common.js'; +import {findHydrogenPlugin, findOxygenPlugin} from '../../lib/vite-config.js'; export default class DevVite extends Command { static description = @@ -50,7 +51,6 @@ export default class DevVite extends Command { description: "Disable rendering fallback routes when a route file doesn't exist.", env: 'SHOPIFY_HYDROGEN_FLAG_DISABLE_VIRTUAL_ROUTES', - default: false, }), ...commonFlags.debug, ...commonFlags.inspectorPort, @@ -178,36 +178,40 @@ export async function runViteDev({ customLogger, clearScreen: false, server: {fs, host: host ? true : undefined}, - plugins: customerAccountPushFlag - ? [ - { - name: 'hydrogen:tunnel', - configureServer: (viteDevServer) => { - viteDevServer.middlewares.use((req, res, next) => { - const host = req.headers.host; - - if (host?.includes(TUNNEL_DOMAIN.ORIGINAL)) { - req.headers.host = host.replace( - TUNNEL_DOMAIN.ORIGINAL, - TUNNEL_DOMAIN.REBRANDED, - ); - } - - next(); - }); - }, - }, - ] - : [], - ...setH2OPluginContext({ - cliOptions: { - debug, - ssrEntry, - envPromise: envPromise.then(({allVariables}) => allVariables), - inspectorPort, - disableVirtualRoutes, + plugins: [ + { + name: 'hydrogen:cli', + configResolved(config) { + findHydrogenPlugin(config)?.api?.registerPluginOptions({ + disableVirtualRoutes, + }); + + findOxygenPlugin(config)?.api?.registerPluginOptions({ + debug, + entry: ssrEntry, + envPromise: envPromise.then(({allVariables}) => allVariables), + inspectorPort, + logRequestLine, + }); + }, + configureServer: (viteDevServer) => { + if (customerAccountPushFlag) { + viteDevServer.middlewares.use((req, res, next) => { + const host = req.headers.host; + + if (host?.includes(TUNNEL_DOMAIN.ORIGINAL)) { + req.headers.host = host.replace( + TUNNEL_DOMAIN.ORIGINAL, + TUNNEL_DOMAIN.REBRANDED, + ); + } + + next(); + }); + } + }, }, - }), + ], }); process.once('SIGTERM', async () => { @@ -218,9 +222,8 @@ export async function runViteDev({ } }); - if ( - !viteServer.config.plugins.find((plugin) => plugin.name === 'hydrogen:main') - ) { + const h2Plugin = findHydrogenPlugin(viteServer.config); + if (!h2Plugin) { await viteServer.close(); throw new AbortError( 'Hydrogen plugin not found.', @@ -228,10 +231,13 @@ export async function runViteDev({ ); } + const h2PluginOptions = h2Plugin.api?.getPluginOptions?.(); + const codegenProcess = useCodegen ? spawnCodegenProcess({ rootDirectory: root, configFilePath: codegenConfigPath, + appDirectory: h2PluginOptions?.remixConfig?.appDirectory, }) : undefined; @@ -284,7 +290,7 @@ export async function runViteDev({ const customSections: AlertCustomSection[] = []; - if (!disableVirtualRoutes) { + if (!h2PluginOptions?.disableVirtualRoutes) { customSections.push({body: getUtilityBannerlines(finalHost)}); } diff --git a/packages/cli/src/lib/log.ts b/packages/cli/src/lib/log.ts index fb4f6d9cc0..394288f1e7 100644 --- a/packages/cli/src/lib/log.ts +++ b/packages/cli/src/lib/log.ts @@ -195,6 +195,20 @@ export function muteDevLogs({workerReload}: {workerReload?: boolean} = {}) { /^Re-optimizing dependencies because/i.test(first), () => {}, ], + [ + // This error is fixed in new Remix versions: + // https://github.com/remix-run/remix/pull/9194 + ([first]) => { + const message: string = first?.message ?? first; + return ( + /virtual-routes/.test(message) && + /(Failed to load url|Could not resolve module for file)/i.test( + message, + ) + ); + }, + () => {}, + ], [ // Log new lines between Request logs and other logs ([first], existingMatches) => { diff --git a/packages/cli/src/lib/mini-oxygen/common.ts b/packages/cli/src/lib/mini-oxygen/common.ts index 05781b8766..3736cf2a7a 100644 --- a/packages/cli/src/lib/mini-oxygen/common.ts +++ b/packages/cli/src/lib/mini-oxygen/common.ts @@ -6,6 +6,7 @@ import { import colors from '@shopify/cli-kit/node/colors'; import {DEV_ROUTES} from '../request-events.js'; import {AbortError} from '@shopify/cli-kit/node/error'; +import type {RequestHookInfo} from '@shopify/mini-oxygen'; // Default port used for debugging in VSCode and Chrome DevTools. export const DEFAULT_INSPECTOR_PORT = 9229; @@ -19,16 +20,11 @@ export function handleMiniOxygenImportFail(): never { ); } -export function logRequestLine( - // Minimal overlap between Fetch, Miniflare@2 and Miniflare@3 request types. - request: Pick & { - headers: {get: (key: string) => string | null}; - }, - { - responseStatus = 200, - durationMs = 0, - }: {responseStatus?: number; durationMs?: number} = {}, -): void { +export function logRequestLine({ + request, + response, + meta, +}: RequestHookInfo): void { try { const url = new URL(request.url); if (DEV_ROUTES.has(url.pathname) || url.pathname === '/favicon.ico') return; @@ -46,26 +42,26 @@ export function logRequestLine( } const colorizeStatus = - responseStatus < 300 + response.status < 300 ? outputToken.green - : responseStatus < 400 + : response.status < 400 ? outputToken.cyan : outputToken.errorText; outputInfo( outputContent`${request.method.padStart(6)} ${colorizeStatus( - String(responseStatus), + String(response.status), )} ${outputToken.italic(type.padEnd(7, ' '))} ${route} ${ - durationMs > 0 ? colors.dim(` ${durationMs}ms`) : '' + meta.durationMs > 0 ? colors.dim(` ${meta.durationMs}ms`) : '' }${info ? ' ' + colors.dim(info) : ''}${ - request.headers.get('purpose') === 'prefetch' + request.headers['purpose'] === 'prefetch' ? outputToken.italic(colors.dim(' prefetch')) : '' }`, ); } catch { - if (request && responseStatus) { - outputInfo(`${request.method} ${responseStatus} ${request.url}`); + if (request && response?.status) { + outputInfo(`${request.method} ${response.status} ${request.url}`); } } } diff --git a/packages/cli/src/lib/mini-oxygen/node.ts b/packages/cli/src/lib/mini-oxygen/node.ts index b751aea1cc..5753537a99 100644 --- a/packages/cli/src/lib/mini-oxygen/node.ts +++ b/packages/cli/src/lib/mini-oxygen/node.ts @@ -87,9 +87,24 @@ export async function startNodeServer({ () => defaultDispatcher(request), ); - logRequestLine(request, { - responseStatus: response.status, - durationMs: startTimeMs > 0 ? Date.now() - startTimeMs : 0, + const endTimeMs = Date.now(); + + logRequestLine({ + request: { + url: request.url, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + }, + response: { + status: response.status, + statusText: response.statusText, + headers: Object.fromEntries(response.headers.entries()), + }, + meta: { + startTimeMs, + endTimeMs, + durationMs: startTimeMs > 0 ? endTimeMs - startTimeMs : 0, + }, }); return response; diff --git a/packages/cli/src/lib/mini-oxygen/workerd.ts b/packages/cli/src/lib/mini-oxygen/workerd.ts index 69409e164d..ca62fc3969 100644 --- a/packages/cli/src/lib/mini-oxygen/workerd.ts +++ b/packages/cli/src/lib/mini-oxygen/workerd.ts @@ -57,10 +57,10 @@ export async function startWorkerdServer({ const miniOxygen = createMiniOxygen({ debug, - logRequestLine, port: appPort, host: 'localhost', liveReload: watch, + requestHook: logRequestLine, inspectorPort: publicInspectorPort, inspectWorkerName: mainWorkerName, assets: {port: assetsPort, directory: buildPathClient}, diff --git a/packages/cli/src/lib/request-events.ts b/packages/cli/src/lib/request-events.ts index 50b97b6331..8e43089ddf 100644 --- a/packages/cli/src/lib/request-events.ts +++ b/packages/cli/src/lib/request-events.ts @@ -30,8 +30,9 @@ export function setConstructors(constructors: { export const DEV_ROUTES = new Set([ '/graphiql', + '/graphiql/customer-account.schema.json', '/subrequest-profiler', - '/__vite_warmup', + '/debug-network-server', ]); type RequestEvent = { diff --git a/packages/cli/src/lib/virtual-routes.test.ts b/packages/cli/src/lib/virtual-routes.test.ts index 0c7a818201..f1f3409fd3 100644 --- a/packages/cli/src/lib/virtual-routes.test.ts +++ b/packages/cli/src/lib/virtual-routes.test.ts @@ -1,16 +1,22 @@ import {describe, it, expect} from 'vitest'; -import {fileURLToPath} from 'url'; -import type {RemixConfig} from './remix-config.js'; +import {fileURLToPath} from 'node:url'; +import path from 'node:path'; +import type {RemixConfig} from '@remix-run/dev/dist/config.js'; import { addVirtualRoutes, VIRTUAL_ROOT, VIRTUAL_ROUTES_DIR, } from './virtual-routes.js'; +const appDirectory = path.join( + path.dirname(fileURLToPath(import.meta.url)), + 'virtual-test', +); + describe('virtual routes', () => { it('adds virtual routes', async () => { const config = { - appDirectory: fileURLToPath(new URL('../virtual-test', import.meta.url)), + appDirectory, routes: {}, } as RemixConfig; @@ -19,19 +25,19 @@ describe('virtual routes', () => { expect(config.routes[VIRTUAL_ROOT]).toMatchObject({ path: '', id: VIRTUAL_ROOT, - file: '../virtual-routes/virtual-root.jsx', + file: expect.stringContaining('virtual-routes/virtual-root.jsx'), }); expect(config.routes[VIRTUAL_ROUTES_DIR + '/index']).toMatchObject({ parentId: VIRTUAL_ROOT, path: undefined, - file: '../' + VIRTUAL_ROUTES_DIR + '/index.tsx', + file: expect.stringContaining(VIRTUAL_ROUTES_DIR + '/index.tsx'), }); expect(config.routes[VIRTUAL_ROUTES_DIR + '/graphiql']).toMatchObject({ parentId: VIRTUAL_ROOT, path: 'graphiql', - file: '../' + VIRTUAL_ROUTES_DIR + '/graphiql.tsx', + file: expect.stringContaining(VIRTUAL_ROUTES_DIR + '/graphiql.tsx'), }); }); @@ -45,7 +51,7 @@ describe('virtual routes', () => { }; const config = { - appDirectory: fileURLToPath(new URL('../virtual-test', import.meta.url)), + appDirectory, routes: { [existingIndexRoute.id]: existingIndexRoute, }, diff --git a/packages/cli/src/lib/virtual-routes.ts b/packages/cli/src/lib/virtual-routes.ts index 22d19de812..7d6b353706 100644 --- a/packages/cli/src/lib/virtual-routes.ts +++ b/packages/cli/src/lib/virtual-routes.ts @@ -1,3 +1,9 @@ +/** + * This file is only used for the classic compiler. + * Refer to `hydrogen/src/vite/add-virtual-routes.ts` for Vite. + * @deprecated + */ + import {fileURLToPath} from 'node:url'; import {glob} from '@shopify/cli-kit/node/fs'; import {joinPath, relativePath} from '@shopify/cli-kit/node/path'; @@ -14,8 +20,11 @@ type MinimalRemixConfig = { export async function addVirtualRoutes( config: T, ): Promise { + const distPath = process.env.SHOPIFY_UNIT_TEST + ? fileURLToPath(new URL('../../../hydrogen/src/vite', import.meta.url)) + : fileURLToPath(new URL('..', import.meta.url)); + const userRouteList = Object.values(config.routes); - const distPath = fileURLToPath(new URL('..', import.meta.url)); const virtualRoutesPath = joinPath(distPath, VIRTUAL_ROUTES_DIR); for (const absoluteFilePath of await glob( diff --git a/packages/cli/src/lib/vite-config.ts b/packages/cli/src/lib/vite-config.ts index 79095465e5..801f53cc85 100644 --- a/packages/cli/src/lib/vite-config.ts +++ b/packages/cli/src/lib/vite-config.ts @@ -4,9 +4,12 @@ import { joinPath, resolvePath, } from '@shopify/cli-kit/node/path'; -import type {RemixPluginContext} from '@remix-run/dev/dist/vite/plugin.js'; import {findFileWithExtension} from './file.js'; +// Do not import JS from here, only types +import type {HydrogenPlugin} from '~/hydrogen/vite/plugin.js'; +import type {OxygenPlugin} from '~/mini-oxygen/vite/plugin.js'; + export async function hasViteConfig(root: string) { const result = await findFileWithExtension(root, 'vite.config'); return !!result.filepath; @@ -35,20 +38,24 @@ export async function getViteConfig(root: string, ssrEntryFlag?: string) { mode, ); + const {appDirectory, serverBuildFile, routes} = + getRemixConfigFromVite(resolvedViteConfig); + const serverOutDir = resolvedViteConfig.build.outDir; const clientOutDir = serverOutDir.replace(/server$/, 'client'); const rollupOutput = resolvedViteConfig.build.rollupOptions.output; const {entryFileNames} = (Array.isArray(rollupOutput) ? rollupOutput[0] : rollupOutput) ?? {}; + const serverOutFile = joinPath( serverOutDir, - typeof entryFileNames === 'string' ? entryFileNames : 'index.js', + typeof entryFileNames === 'string' + ? entryFileNames + : serverBuildFile ?? 'index.js', ); const ssrEntry = ssrEntryFlag ?? resolvedViteConfig.build.ssr; - const {...remixPluginConfig} = getRemixConfigFromVite(resolvedViteConfig); - const resolvedSsrEntry = resolvePath( resolvedViteConfig.root, typeof ssrEntry === 'string' ? ssrEntry : 'server', @@ -61,7 +68,8 @@ export async function getViteConfig(root: string, ssrEntryFlag?: string) { resolvedViteConfig, userViteConfig: maybeConfig.config, remixConfig: { - ...remixPluginConfig, + routes: routes ?? {}, + appDirectory: appDirectory ?? joinPath(resolvedViteConfig.root, 'app'), rootDirectory: resolvedViteConfig.root, serverEntryPoint: ( @@ -76,12 +84,35 @@ export async function getViteConfig(root: string, ssrEntryFlag?: string) { function getRemixConfigFromVite(viteConfig: any) { const {remixConfig} = - (viteConfig.__remixPluginContext as RemixPluginContext) || { - remixConfig: {appDirectory: joinPath(viteConfig.root, 'app')}, - }; + findHydrogenPlugin(viteConfig)?.api?.getPluginOptions() ?? {}; - type RemixPluginConfig = typeof remixConfig; + return remixConfig + ? { + appDirectory: remixConfig.appDirectory, + serverBuildFile: remixConfig.serverBuildFile, + routes: remixConfig.routes, + } + : {}; +} - // Remove these types because they create TS problems. - return remixConfig as Omit; +type MinimalViteConfig = {plugins: Readonly>}; + +function findPlugin< + PluginType extends Config['plugins'][number], + Config extends MinimalViteConfig = MinimalViteConfig, +>(config: Config, name: string) { + return config.plugins.find((plugin) => plugin.name === name) as + | PluginType + | undefined; +} + +export function findHydrogenPlugin( + config: Config, +) { + return findPlugin(config, 'hydrogen:main'); +} +export function findOxygenPlugin( + config: Config, +) { + return findPlugin(config, 'oxygen:main'); } diff --git a/packages/cli/src/lib/vite/hydrogen-middleware.ts b/packages/cli/src/lib/vite/hydrogen-middleware.ts deleted file mode 100644 index cb9fb21004..0000000000 --- a/packages/cli/src/lib/vite/hydrogen-middleware.ts +++ /dev/null @@ -1,137 +0,0 @@ -import {normalizePath, type ViteDevServer, type ResolvedConfig} from 'vite'; -import path from 'node:path'; -import {createRequire} from 'node:module'; -import {createFileReadStream} from '@shopify/cli-kit/node/fs'; -import {handleDebugNetworkRequest, setConstructors} from '../request-events.js'; -import {SUBREQUEST_PROFILER_ENDPOINT} from '../mini-oxygen/common.js'; -import {pipeFromWeb, toWeb} from './utils.js'; -import type {RemixPluginContext} from '@remix-run/dev/dist/vite/plugin.js'; -import {addVirtualRoutes} from '../virtual-routes.js'; -import type {HydrogenPluginOptions} from './shared.js'; - -// Function to be passed as a setup function to the Oxygen worker. -// It runs within workerd and sets up Remix dev server hooks. -// It is eventually stringified to be initialized in the worker, -// so do not use any external variables or imports. -export function setupRemixDevServerHooks(viteUrl: string) { - // @ts-expect-error Remix global magic - globalThis['__remix_devServerHooks'] = { - getCriticalCss: (...args: any) => - fetch(new URL('/__vite_critical_css', viteUrl), { - method: 'POST', - body: JSON.stringify(args), - }).then((res) => res.json()), - }; -} - -export function setupHydrogenMiddleware( - viteDevServer: ViteDevServer, - options: HydrogenPluginOptions, -) { - viteDevServer.middlewares.use( - '/__vite_critical_css', - function h2HandleCriticalCss(req, res) { - // This request comes from Remix's `getCriticalCss` function - // to gather the required CSS and avoid flashes of unstyled content in dev. - - toWeb(req) - .then((webRequest) => webRequest.json()) - .then(async (args: any) => { - // @ts-expect-error Remix global magic - const result = await globalThis[ - '__remix_devServerHooks' - ]?.getCriticalCss?.(...args); - res.writeHead(200, {'Content-Type': 'application/json'}); - res.end(JSON.stringify(result ?? '')); - }); - }, - ); - - if (options.disableVirtualRoutes) return; - - addVirtualRoutesToRemix(viteDevServer); - setConstructors({Response: globalThis.Response}); - - viteDevServer.middlewares.use( - SUBREQUEST_PROFILER_ENDPOINT, - function h2HandleSubrequestProfilerEvent(req, res) { - // This request comes from Hydrogen's Subrequest Profiler UI. - - toWeb(req) - .then(handleDebugNetworkRequest) - .then((webResponse) => pipeFromWeb(webResponse, res)); - }, - ); - - viteDevServer.middlewares.use( - '/graphiql/customer-account.schema.json', - function h2HandleGraphiQLCustomerSchema(req, res) { - // This request comes from Hydrogen's GraphiQL. - // Currently, the CAAPI schema is not available in the public API, - // so we serve it from here. - - const require = createRequire(import.meta.url); - const filePath = require.resolve( - '@shopify/hydrogen/customer-account.schema.json', - ); - - res.writeHead(200, {'Content-Type': 'application/json'}); - createFileReadStream(filePath).pipe(res); - }, - ); -} - -// TODO: Sync with Remix team and find an official way to add virtual routes. -let virtualRoutesAdded = false; -async function addVirtualRoutesToRemix(viteDevServer: ViteDevServer) { - if (virtualRoutesAdded) return; - - const appDirectory = await reloadRemixVirtualRoutes(viteDevServer.config); - - viteDevServer.watcher.on('all', (eventName, filepath) => { - const appFileAddedOrRemoved = - (eventName === 'add' || eventName === 'unlink') && - normalizePath(filepath).startsWith(normalizePath(appDirectory)); - - const viteConfigChanged = - eventName === 'change' && - normalizePath(filepath) === - normalizePath(viteDevServer.config.configFile ?? ''); - - if (appFileAddedOrRemoved || viteConfigChanged) { - setTimeout(() => reloadRemixVirtualRoutes(viteDevServer.config), 100); - } - }); - - virtualRoutesAdded = true; -} - -async function reloadRemixVirtualRoutes(config: ResolvedConfig) { - const remixPluginContext = (config as any) - .__remixPluginContext as RemixPluginContext; - - // Unfreeze remixConfig to extend it with virtual routes. - remixPluginContext.remixConfig = {...remixPluginContext.remixConfig}; - // @ts-expect-error - remixPluginContext.remixConfig.routes = { - ...remixPluginContext.remixConfig.routes, - }; - - await addVirtualRoutes(remixPluginContext.remixConfig).catch((error) => { - // Seen this fail when somehow NPM doesn't publish - // the full 'virtual-routes' directory. - // E.g. https://unpkg.com/browse/@shopify/cli-hydrogen@0.0.0-next-aa15969-20230703072007/dist/virtual-routes/ - console.debug( - 'Could not add virtual routes: ' + - (error?.stack ?? error?.message ?? error), - ); - }); - - Object.freeze(remixPluginContext.remixConfig.routes); - Object.freeze(remixPluginContext.remixConfig); - - return ( - remixPluginContext?.remixConfig?.appDirectory ?? - path.join(config.root, 'app') - ); -} diff --git a/packages/cli/src/lib/vite/mini-oxygen.ts b/packages/cli/src/lib/vite/mini-oxygen.ts deleted file mode 100644 index e61e0bf4bf..0000000000 --- a/packages/cli/src/lib/vite/mini-oxygen.ts +++ /dev/null @@ -1,154 +0,0 @@ -import {fetchModule, type ViteDevServer} from 'vite'; -import {fileURLToPath} from 'node:url'; -import type {Request, Response} from '@shopify/mini-oxygen'; -import {logRequestLine} from '../mini-oxygen/common.js'; -import {MiniOxygenOptions} from '../mini-oxygen/types.js'; -import {getHmrUrl, pipeFromWeb, toURL, toWeb} from './utils.js'; - -import type {ViteEnv} from './worker-entry.js'; -const scriptPath = fileURLToPath(new URL('./worker-entry.js', import.meta.url)); - -const FETCH_MODULE_PATHNAME = '/__vite_fetch_module'; -const WARMUP_PATHNAME = '/__vite_warmup'; - -export type InternalMiniOxygenOptions = { - setupScripts?: Array<(viteUrl: string) => void>; - services?: Record Response | Promise>; -}; - -type MiniOxygenViteOptions = InternalMiniOxygenOptions & - Pick & { - viteDevServer: ViteDevServer; - workerEntryFile: string; - }; - -export type MiniOxygen = Awaited>; - -export async function startMiniOxygenRuntime({ - viteDevServer, - env, - services, - debug = false, - inspectorPort, - workerEntryFile, - setupScripts, -}: MiniOxygenViteOptions) { - const {createMiniOxygen} = await import('@shopify/mini-oxygen'); - - const miniOxygen = createMiniOxygen({ - debug, - inspectorPort, - logRequestLine, - workers: [ - { - name: 'vite-env', - modulesRoot: '/', - modules: [{type: 'ESModule', path: scriptPath}], - serviceBindings: {...services}, - bindings: { - ...env, - __VITE_ROOT: viteDevServer.config.root, - __VITE_RUNTIME_EXECUTE_URL: workerEntryFile, - __VITE_FETCH_MODULE_PATHNAME: FETCH_MODULE_PATHNAME, - __VITE_HMR_URL: getHmrUrl(viteDevServer), - __VITE_WARMUP_PATHNAME: WARMUP_PATHNAME, - } satisfies Omit, - unsafeEvalBinding: '__VITE_UNSAFE_EVAL', - wrappedBindings: { - __VITE_SETUP_ENV: 'setup-environment', - }, - }, - { - name: 'setup-environment', - modules: true, - scriptPath, - script: ` - const setupScripts = [${setupScripts ?? ''}]; - export default (env) => (request) => { - const viteUrl = new URL(request.url).origin; - setupScripts.forEach((setup) => setup?.(viteUrl)); - setupScripts.length = 0; - }`, - }, - ], - }); - - const warmupWorkerdCache = () => { - let viteUrl = - viteDevServer.resolvedUrls?.local[0] ?? - viteDevServer.resolvedUrls?.network[0]; - - if (!viteUrl) { - const address = viteDevServer.httpServer?.address?.(); - viteUrl = - address && typeof address !== 'string' - ? `http://localhost:${address.port}` - : address ?? undefined; - } - - if (viteUrl) { - miniOxygen - .dispatchFetch(new URL(WARMUP_PATHNAME, viteUrl)) - .catch(() => {}); - } - }; - - viteDevServer.httpServer?.listening - ? warmupWorkerdCache() - : viteDevServer.httpServer?.once('listening', warmupWorkerdCache); - - return miniOxygen; -} - -export function setupOxygenMiddleware( - viteDevServer: ViteDevServer, - dispatchFetch: (webRequest: Request) => Promise, -) { - viteDevServer.middlewares.use( - FETCH_MODULE_PATHNAME, - function o2HandleModuleFetch(req, res) { - // This request comes from workerd. It is asking for the contents - // of backend files. We need to fetch the file through Vite, - // which transpiles/prepares the source code into valid JS, and - // send it back so that workerd can evaluate/run it. - - const url = toURL(req); - const id = url.searchParams.get('id'); - const importer = url.searchParams.get('importer') ?? undefined; - - if (id) { - res.setHeader('Cache-Control', 'no-store'); - res.setHeader('Content-Type', 'application/json'); - - // `fetchModule` is similar to `viteDevServer.ssrFetchModule`, - // but it treats source maps differently (avoids adding empty lines). - fetchModule(viteDevServer, id, importer) - .then((ssrModule) => res.end(JSON.stringify(ssrModule))) - .catch((error) => { - console.error('Error during module fetch:', error); - res.writeHead(500, {'Content-Type': 'text/plain'}); - res.end('Internal server error'); - }); - } else { - res.writeHead(400, {'Content-Type': 'text/plain'}); - res.end('Invalid request'); - } - }, - ); - - viteDevServer.middlewares.use(function o2HandleWorkerRequest(req, res) { - // This request comes from the browser. At this point, Vite - // tried to serve the request as a static file, but it didn't - // find it in the project. Therefore, we assume this is a - // request for a backend route, and we forward it to workerd. - - toWeb(req) - .then(dispatchFetch) - .then((webResponse) => pipeFromWeb(webResponse, res)) - .catch((error) => { - console.error('Error during evaluation:', error); - res.writeHead(500); - res.end(); - }); - }); -} diff --git a/packages/cli/src/lib/vite/plugins.ts b/packages/cli/src/lib/vite/plugins.ts deleted file mode 100644 index 96017d0dcc..0000000000 --- a/packages/cli/src/lib/vite/plugins.ts +++ /dev/null @@ -1,189 +0,0 @@ -import path from 'node:path'; -import type {Plugin, ResolvedConfig} from 'vite'; -import { - setupHydrogenMiddleware, - setupRemixDevServerHooks, -} from './hydrogen-middleware.js'; -import { - setupOxygenMiddleware, - startMiniOxygenRuntime, - type MiniOxygen, -} from './mini-oxygen.js'; -import { - getH2OPluginContext, - setH2OPluginContext, - DEFAULT_SSR_ENTRY, - type OxygenPluginOptions, - type HydrogenPluginOptions, -} from './shared.js'; -import {H2O_BINDING_NAME, createLogRequestEvent} from '../request-events.js'; - -let miniOxygen: MiniOxygen; - -/** - * Enables Hydrogen utilities for local development - * such as GraphiQL, Subrequest Profiler, etc. - * It must be used in combination with the `oxygen` plugin and Hydrogen CLI. - * @experimental - */ -export function hydrogen(pluginOptions: HydrogenPluginOptions = {}): Plugin[] { - const isRemixChildCompiler = (config: ResolvedConfig) => - !config.plugins?.some((plugin) => plugin.name === 'remix'); - - return [ - { - name: 'hydrogen:main', - config(config) { - return { - ssr: { - optimizeDeps: { - // Add CJS dependencies that break code in workerd - // with errors like "require/module/exports is not defined": - include: [ - // React deps: - 'react', - 'react/jsx-runtime', - 'react/jsx-dev-runtime', - 'react-dom', - 'react-dom/server', - // Remix deps: - 'set-cookie-parser', - 'cookie', - // Hydrogen deps: - 'content-security-policy-builder', - ], - }, - }, - // Pass the setup functions to the Oxygen runtime. - ...setH2OPluginContext({ - setupScripts: [setupRemixDevServerHooks], - shouldStartRuntime: (config) => !isRemixChildCompiler(config), - services: { - [H2O_BINDING_NAME]: createLogRequestEvent({ - transformLocation: (partialLocation) => - path.join(config.root ?? process.cwd(), partialLocation), - }), - }, - }), - }; - }, - configureServer(viteDevServer) { - if (isRemixChildCompiler(viteDevServer.config)) return; - - // Get options from Hydrogen CLI. - const {cliOptions} = getH2OPluginContext(viteDevServer.config) || {}; - - return () => { - setupHydrogenMiddleware(viteDevServer, { - ...pluginOptions, - ...cliOptions, - }); - }; - }, - }, - ]; -} - -/** - * Runs backend code in an Oxygen worker instead of Node.js during development. - * It must be placed after `hydrogen` but before `remix` in the Vite plugins list. - * @experimental - */ -export function oxygen(pluginOptions: OxygenPluginOptions = {}): Plugin[] { - let resolvedConfig: ResolvedConfig; - let absoluteWorkerEntryFile: string; - - return [ - { - name: 'oxygen:runtime', - config(config, env) { - return { - appType: 'custom', - resolve: { - conditions: ['worker', 'workerd'], - }, - ssr: { - noExternal: true, - target: 'webworker', - }, - // When building, the CLI will set the `ssr` option to `true` - // if no --entry flag is passed for the default SSR entry file. - // Replace it here with a default value. - ...(env.isSsrBuild && - config.build?.ssr && { - build: { - ssr: - config.build?.ssr === true - ? // No --entry flag passed by the user, use the - // option passed to the plugin or the default value - pluginOptions.ssrEntry ?? DEFAULT_SSR_ENTRY - : // --entry flag passed by the user, keep it - config.build?.ssr, - }, - }), - }; - }, - configureServer(viteDevServer) { - resolvedConfig = viteDevServer.config; - - // Get options from Hydrogen plugin and CLI. - const {shouldStartRuntime, cliOptions, setupScripts, services} = - getH2OPluginContext(resolvedConfig) || {}; - - if (shouldStartRuntime && !shouldStartRuntime(resolvedConfig)) return; - if (miniOxygen) return; - - const workerEntryFile = - cliOptions?.ssrEntry ?? pluginOptions.ssrEntry ?? DEFAULT_SSR_ENTRY; - absoluteWorkerEntryFile = path.isAbsolute(workerEntryFile) - ? workerEntryFile - : path.resolve(viteDevServer.config.root, workerEntryFile); - - const envPromise = cliOptions?.envPromise ?? Promise.resolve(); - - const miniOxygenPromise = envPromise.then((remoteEnv) => { - return startMiniOxygenRuntime({ - viteDevServer, - workerEntryFile, - setupScripts, - services, - env: {...remoteEnv, ...pluginOptions.env}, - debug: cliOptions?.debug ?? pluginOptions.debug ?? false, - inspectorPort: - cliOptions?.inspectorPort ?? pluginOptions.inspectorPort, - }); - }); - - process.once('SIGTERM', async () => { - try { - await miniOxygen?.dispose(); - } finally { - process.exit(); - } - }); - - return () => { - setupOxygenMiddleware(viteDevServer, async (request) => { - miniOxygen ??= await miniOxygenPromise; - return miniOxygen.dispatchFetch(request); - }); - }; - }, - transform(code, id, options) { - if ( - resolvedConfig?.command === 'serve' && - resolvedConfig?.server?.hmr !== false && - options?.ssr && - (id === absoluteWorkerEntryFile || - id === absoluteWorkerEntryFile + path.extname(id)) - ) { - return { - // Accept HMR in server entry module to avoid full-page refresh in the browser. - // Note: appending code at the end should not break the source map. - code: code + '\nif (import.meta.hot) import.meta.hot.accept();', - }; - } - }, - }, - ]; -} diff --git a/packages/cli/src/lib/vite/shared.ts b/packages/cli/src/lib/vite/shared.ts deleted file mode 100644 index 4a1e3c3587..0000000000 --- a/packages/cli/src/lib/vite/shared.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Do not import Vite in this file since it is used from -// the rest of the CLI when Vite might not be installed. -import type {ResolvedConfig, UserConfig} from 'vite'; -import type {InternalMiniOxygenOptions} from './mini-oxygen.js'; - -export type H2OPluginContext = InternalMiniOxygenOptions & { - shouldStartRuntime?: (config: ResolvedConfig) => boolean; - cliOptions?: Partial< - HydrogenPluginOptions & - OxygenPluginOptions & { - envPromise: Promise>; - } - >; -}; - -export type HydrogenPluginOptions = { - disableVirtualRoutes?: boolean; -}; - -export type OxygenPluginOptions = { - ssrEntry?: string; - debug?: boolean; - inspectorPort?: number; - env?: Record; -}; - -// Note: Vite resolves extensions like .js or .ts automatically. -export const DEFAULT_SSR_ENTRY = './server'; - -const H2O_CONTEXT_KEY = '__h2oPluginContext'; - -export function getH2OPluginContext(config: UserConfig | ResolvedConfig) { - return (config as any)?.[H2O_CONTEXT_KEY] as H2OPluginContext; -} - -export function setH2OPluginContext(options: Partial) { - return {[H2O_CONTEXT_KEY]: options} as Record; -} diff --git a/packages/cli/src/setup-assets/vite/vite.config.js b/packages/cli/src/setup-assets/vite/vite.config.js index 654277cbd9..b73891298a 100644 --- a/packages/cli/src/setup-assets/vite/vite.config.js +++ b/packages/cli/src/setup-assets/vite/vite.config.js @@ -1,5 +1,6 @@ import {defineConfig} from 'vite'; -import {hydrogen, oxygen} from '@shopify/cli-hydrogen/experimental-vite'; +import {hydrogen} from '@shopify/hydrogen/vite'; +import {oxygen} from '@shopify/mini-oxygen/vite'; import {vitePlugin as remix} from '@remix-run/dev'; import tsconfigPaths from 'vite-tsconfig-paths'; @@ -8,7 +9,7 @@ export default defineConfig({ hydrogen(), oxygen(), remix({ - buildDirectory: 'dist', + presets: [hydrogen.preset()], future: { v3_fetcherPersist: true, v3_relativeSplatPath: true, diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index b2a0812875..e6f87be962 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -6,9 +6,12 @@ "module": "NodeNext", "moduleResolution": "NodeNext", "jsx": "react-jsx", - "rootDir": "src", "noUncheckedIndexedAccess": true, "types": ["@shopify/oxygen-workers-types", "node", "@remix-run/dev"], - "outDir": "dist" + "outDir": "dist", + "paths": { + "~/mini-oxygen/*": ["../mini-oxygen/src/*"], + "~/hydrogen/*": ["../hydrogen/src/*"] + } } } diff --git a/packages/cli/tsup.config.ts b/packages/cli/tsup.config.ts index 7106e8d7a9..34c118e42b 100644 --- a/packages/cli/tsup.config.ts +++ b/packages/cli/tsup.config.ts @@ -28,10 +28,10 @@ const outDir = 'dist'; export default defineConfig([ { ...commonConfig, - entry: ['src/**/*.ts', '!src/lib/vite/worker-entry.ts'], + entry: ['src/**/*.ts', '!src/**/*.test.ts'], outDir, // Generate types only for the exposed entry points - dts: {entry: ['src/lib/vite/plugins.ts', 'src/commands/hydrogen/init.ts']}, + dts: {entry: ['src/commands/hydrogen/init.ts']}, async onSuccess() { // Copy TS templates const i18nTemplatesPath = 'lib/setups/i18n/templates'; @@ -62,16 +62,10 @@ export default defineConfig([ console.log('', 'Oclif manifest generated.\n'); }, }, - { - entry: ['src/lib/vite/worker-entry.ts'], - outDir: 'dist/lib/vite', - format: 'esm', - noExternal: [/./], - dts: false, - }, { ...commonConfig, - entry: ['src/virtual-routes/**/*.tsx'], + // TODO remove virtual routes copy when deprecating classic compiler + entry: ['../hydrogen/src/vite/virtual-routes/**/*.tsx'], outDir: `${outDir}/virtual-routes`, outExtension: () => ({js: '.jsx'}), dts: false, @@ -96,7 +90,7 @@ export default defineConfig([ // For some reason, it seems that publicDir => outDir might be skipped on CI, // so ensure here that asset files are copied: await fs.copy( - 'src/virtual-routes/assets', + '../hydrogen/src/vite/virtual-routes/assets', `${outDir}/virtual-routes/assets`, ); diff --git a/packages/hydrogen-react/package.json b/packages/hydrogen-react/package.json index c67d71c789..a300495a64 100644 --- a/packages/hydrogen-react/package.json +++ b/packages/hydrogen-react/package.json @@ -163,7 +163,7 @@ "rimraf": "^4.1.2", "ts-expect": "^1.3.0", "typescript": "^5.2.2", - "vite": "~5.1.7", + "vite": "~5.1.0", "vitest": "^1.0.4" }, "peerDependencies": { diff --git a/packages/hydrogen/package.json b/packages/hydrogen/package.json index a3ae29fd26..9dffd419e9 100644 --- a/packages/hydrogen/package.json +++ b/packages/hydrogen/package.json @@ -39,6 +39,10 @@ "default": "./dist/production/index.js" } }, + "./vite": { + "types": "./dist/vite/plugin.d.ts", + "default": "./dist/vite/plugin.js" + }, "./storefront-api-types": "./dist/storefront-api-types.d.ts", "./storefront.schema.json": "./dist/storefront.schema.json", "./customer-account-api-types": "./dist/customer-account-api-types.d.ts", @@ -61,12 +65,8 @@ "dependencies": { "@shopify/hydrogen-react": "2024.1.1", "content-security-policy-builder": "^2.1.1", - "type-fest": "^4.5.0" - }, - "peerDependencies": { - "@remix-run/react": "^2.1.0", - "@remix-run/server-runtime": "^2.1.0", - "react": "^18.2.0" + "type-fest": "^4.5.0", + "source-map-support": "^0.5.21" }, "devDependencies": { "@remix-run/react": "^2.8.0", @@ -74,9 +74,21 @@ "@shopify/generate-docs": "0.11.1", "@shopify/hydrogen-codegen": "*", "@testing-library/react": "^14.0.0", + "@types/source-map-support": "^0.5.10", "happy-dom": "^8.9.0", "react": "^18.2.0", "schema-dts": "^1.1.0", "vitest": "^1.0.4" + }, + "peerDependencies": { + "@remix-run/react": "^2.1.0", + "@remix-run/server-runtime": "^2.1.0", + "react": "^18.2.0", + "vite": "~5.1.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } } } diff --git a/packages/hydrogen/src/cache/fetch.ts b/packages/hydrogen/src/cache/fetch.ts index 26911bbbbe..d9f7880ed4 100644 --- a/packages/hydrogen/src/cache/fetch.ts +++ b/packages/hydrogen/src/cache/fetch.ts @@ -1,5 +1,3 @@ -/// - import {hashKey} from '../utils/hash.js'; import { CacheShort, diff --git a/packages/hydrogen/src/hydrogen.d.ts b/packages/hydrogen/src/hydrogen.d.ts index 686ba927e7..964b0028d5 100644 --- a/packages/hydrogen/src/hydrogen.d.ts +++ b/packages/hydrogen/src/hydrogen.d.ts @@ -4,6 +4,7 @@ import type { SessionData, FlashSessionData, } from '@remix-run/server-runtime'; +import type {RequestEventPayload} from './vite/request-events'; export interface HydrogenSessionData { customerAccount: { @@ -29,3 +30,10 @@ export interface HydrogenSession< SessionStorage['commitSession'] >; } + +declare global { + var __H2O_LOG_EVENT: undefined | ((event: RequestEventPayload) => void); + var __remix_devServerHooks: + | undefined + | {getCriticalCss: (...args: unknown[]) => any}; +} diff --git a/packages/hydrogen/src/vite/get-virtual-routes.test.ts b/packages/hydrogen/src/vite/get-virtual-routes.test.ts new file mode 100644 index 0000000000..7546e5dd22 --- /dev/null +++ b/packages/hydrogen/src/vite/get-virtual-routes.test.ts @@ -0,0 +1,31 @@ +import {describe, it, expect} from 'vitest'; +import {getVirtualRoutes, VIRTUAL_ROOT} from './get-virtual-routes.js'; + +describe('virtual routes', () => { + it('gets virtual routes', async () => { + await getVirtualRoutes(); + await expect(getVirtualRoutes()).resolves.toMatchObject({ + root: {file: expect.any(String), id: VIRTUAL_ROOT, path: ''}, + routes: expect.arrayContaining([ + { + id: expect.any(String), + file: expect.stringContaining('graphiql.tsx'), + index: false, + path: 'graphiql', + }, + { + id: expect.any(String), + file: expect.stringContaining('subrequest-profiler.tsx'), + index: false, + path: 'subrequest-profiler', + }, + { + id: expect.any(String), + file: expect.stringContaining('index.tsx'), + index: true, + path: '', + }, + ]), + }); + }); +}); diff --git a/packages/hydrogen/src/vite/get-virtual-routes.ts b/packages/hydrogen/src/vite/get-virtual-routes.ts new file mode 100644 index 0000000000..358f08e4a5 --- /dev/null +++ b/packages/hydrogen/src/vite/get-virtual-routes.ts @@ -0,0 +1,39 @@ +import {fileURLToPath} from 'node:url'; +import path from 'node:path'; +import {readdir} from 'node:fs/promises'; + +export const VIRTUAL_ROUTES_DIR = 'virtual-routes/routes'; +export const VIRTUAL_ROOT = 'virtual-routes/virtual-root'; + +export async function getVirtualRoutes() { + const distPath = path.dirname(fileURLToPath(import.meta.url)); + const virtualRoutesPath = path.join(distPath, VIRTUAL_ROUTES_DIR); + + const routes = await readdir(virtualRoutesPath, {recursive: true}).then( + (files) => + files.map((relativeFilePath) => { + const absoluteFilePath = path.join(virtualRoutesPath, relativeFilePath); + const id = relativeFilePath + .replace(/\.[jt]sx?$/, '') + .replaceAll('\\', '/'); + const isIndex = /(^|\/)index$/.test(id); + const routePath = id.replace(/(^|\/)index$/, ''); + + return { + id: `${VIRTUAL_ROUTES_DIR}/${id}`, + path: routePath, + file: absoluteFilePath, + index: isIndex, + }; + }), + ); + + return { + routes, + root: { + id: VIRTUAL_ROOT, + path: '', + file: path.join(distPath, VIRTUAL_ROOT + '.jsx'), + }, + }; +} diff --git a/packages/hydrogen/src/vite/hydrogen-middleware.ts b/packages/hydrogen/src/vite/hydrogen-middleware.ts new file mode 100644 index 0000000000..9e9540653a --- /dev/null +++ b/packages/hydrogen/src/vite/hydrogen-middleware.ts @@ -0,0 +1,97 @@ +import type {ViteDevServer} from 'vite'; +import crypto from 'node:crypto'; +import {createRequire} from 'node:module'; +import {createReadStream} from 'node:fs'; +import { + clearHistory, + emitRequestEvent, + streamRequestEvents, +} from './request-events.js'; +import type {HydrogenPluginOptions} from './types.js'; + +export type HydrogenMiddlewareOptions = HydrogenPluginOptions & { + isOxygen?: boolean; +}; + +export function setupHydrogenMiddleware( + viteDevServer: ViteDevServer, + options: HydrogenMiddlewareOptions, +) { + if (!options.isOxygen) { + // If Oxygen is not present, we are probably running + // on Node.js, so we can setup the global functions directly. + globalThis.__H2O_LOG_EVENT = (data) => { + emitRequestEvent(data, viteDevServer.config.root); + }; + + viteDevServer.middlewares.use(function (req, res, next) { + // Filter out dev requests + if (!/^\/__vite_/.test(req.url || '')) { + // Hydrogen requires a unique request ID for each request + // to track the request lifecycle. This is added by Oxygen + // normally but we can add it here for development in Node. + req.headers['request-id'] ??= crypto.randomUUID(); + + const startTimeMs = Date.now(); + let endTimeMs = 0; + + res.once('pipe', () => { + endTimeMs = Date.now(); + }); + + res.once('close', () => { + emitRequestEvent( + { + __fromVite: true, + eventType: 'request', + url: req.url!, + requestId: req.headers['request-id'] as string, + purpose: req.headers['purpose'] as string, + startTime: startTimeMs, + endTime: endTimeMs || Date.now(), + responseInit: { + status: res.statusCode, + statusText: res.statusMessage, + headers: Object.entries( + res.getHeaders() as Record, + ), + }, + }, + viteDevServer.config.root, + ); + }); + } + + next(); + }); + } + + if (options.disableVirtualRoutes) return; + + viteDevServer.middlewares.use( + '/debug-network-server', + function h2HandleSubrequestProfilerEvent(req, res) { + // This request comes from Hydrogen's Subrequest Profiler UI. + req.method === 'DELETE' + ? clearHistory(req, res) + : streamRequestEvents(req, res); + }, + ); + + viteDevServer.middlewares.use( + '/graphiql/customer-account.schema.json', + function h2HandleGraphiQLCustomerSchema(req, res) { + // This request comes from Hydrogen's GraphiQL. + // Currently, the CAAPI schema is not available in the public API, + // so we serve it from here. + + const require = createRequire(import.meta.url); + const filePath = require.resolve( + '@shopify/hydrogen/customer-account.schema.json', + ); + + res.writeHead(200, {'Content-Type': 'application/json'}); + createReadStream(filePath).pipe(res); + }, + ); +} diff --git a/packages/hydrogen/src/vite/plugin.ts b/packages/hydrogen/src/vite/plugin.ts new file mode 100644 index 0000000000..c4164f4bad --- /dev/null +++ b/packages/hydrogen/src/vite/plugin.ts @@ -0,0 +1,232 @@ +import type {Plugin, ResolvedConfig, ConfigEnv} from 'vite'; +import type {Preset as RemixPreset} from '@remix-run/dev'; +import { + setupHydrogenMiddleware, + type HydrogenMiddlewareOptions, +} from './hydrogen-middleware.js'; +import type {HydrogenPluginOptions} from './types.js'; +import {type RequestEventPayload, emitRequestEvent} from './request-events.js'; +import {getVirtualRoutes} from './get-virtual-routes.js'; + +// Do not import JS from here, only types +import type {OxygenPlugin} from '~/mini-oxygen/vite/plugin.js'; + +export type {HydrogenPluginOptions}; + +type HydrogenSharedOptions = Partial< + Pick & + Pick & { + remixConfig?: Parameters< + NonNullable + >[0]['remixConfig']; + } +>; + +const sharedOptions: HydrogenSharedOptions = {}; + +/** + * For internal use only. + * @private + */ +export type HydrogenPlugin = Plugin<{ + registerPluginOptions(newOptions: HydrogenPluginOptions): void; + getPluginOptions(): HydrogenSharedOptions; +}>; + +/** + * Enables Hydrogen utilities for local development + * such as GraphiQL, Subrequest Profiler, etc. + */ +export function hydrogen(pluginOptions: HydrogenPluginOptions = {}): Plugin[] { + let middlewareOptions: HydrogenMiddlewareOptions = {}; + const isRemixChildCompiler = (config: ResolvedConfig) => + !config.plugins?.some((plugin) => plugin.name === 'remix'); + + return [ + { + name: 'hydrogen:main', + config(_, env) { + sharedOptions.command = env.command; + + return { + ssr: { + optimizeDeps: { + // Add CJS dependencies that break code in workerd + // with errors like "require/module/exports is not defined": + include: [ + // React deps: + 'react', + 'react/jsx-runtime', + 'react/jsx-dev-runtime', + 'react-dom', + 'react-dom/server', + // Remix deps: + 'set-cookie-parser', + 'cookie', + // Hydrogen deps: + 'content-security-policy-builder', + ], + }, + }, + }; + }, + api: { + registerPluginOptions(newOptions) { + middlewareOptions = mergeOptions(middlewareOptions, newOptions); + if ('disableVirtualRoutes' in middlewareOptions) { + sharedOptions.disableVirtualRoutes = + middlewareOptions.disableVirtualRoutes; + } + }, + getPluginOptions() { + return sharedOptions; + }, + }, + configResolved(resolvedConfig) { + // Pass the setup functions to the Oxygen runtime. + const oxygenPlugin = resolvedConfig.plugins.find( + (plugin) => plugin.name === 'oxygen:main', + ) as undefined | OxygenPlugin; + + middlewareOptions.isOxygen = !!oxygenPlugin; + + oxygenPlugin?.api?.registerPluginOptions?.({ + shouldStartRuntime: () => !isRemixChildCompiler(resolvedConfig), + requestHook: ({request, response, meta}) => { + // Emit events for requests + emitRequestEvent( + { + __fromVite: true, + eventType: 'request', + url: request.url, + requestId: request.headers['request-id'], + purpose: request.headers['purpose'], + startTime: meta.startTimeMs, + endTime: meta.endTimeMs, + responseInit: { + status: response.status, + statusText: response.statusText, + headers: Object.entries(response.headers), + }, + }, + resolvedConfig.root, + ); + }, + crossBoundarySetup: [ + { + // Setup the global function in the Oxygen worker + script: (binding) => { + globalThis.__H2O_LOG_EVENT = binding; + }, + binding: (data) => { + // Emit events for subrequests from the parent process + emitRequestEvent( + data as RequestEventPayload, + resolvedConfig.root, + ); + }, + }, + /** + * To avoid initial CSS flash during development, + * most frameworks implement a way to gather critical CSS. + * Remix does this by calling a global function that their + * Vite plugin creates in the Node.js process: + * @see https://github.com/remix-run/remix/blob/b07921efd5e8eed98e2996749852777c71bc3e50/packages/remix-server-runtime/dev.ts#L37-L47 + * + * Here we are setting up a stub function in the Oxygen worker + * that will be called by Remix during development. Then, we forward + * this request to the Node.js process (Vite server) where the actual + * Remix function is called and the critical CSS is returned to the worker. + */ + { + script: (binding) => { + // Setup global dev hooks in Remix in the worker environment + // using the binding function passed from Node environment: + globalThis.__remix_devServerHooks = {getCriticalCss: binding}; + }, + binding: (...args) => { + // Call the global Remix dev hook for critical CSS in Node environment: + return globalThis.__remix_devServerHooks?.getCriticalCss?.( + ...args, + ); + }, + }, + ], + }); + }, + configureServer(viteDevServer) { + if (isRemixChildCompiler(viteDevServer.config)) return; + + return () => { + setupHydrogenMiddleware( + viteDevServer, + mergeOptions(pluginOptions, middlewareOptions), + ); + }; + }, + } satisfies HydrogenPlugin, + ]; +} + +function mergeOptions( + acc: HydrogenPluginOptions, + newOptions: HydrogenPluginOptions, +) { + const newOptionsWithoutUndefined = Object.fromEntries( + Object.entries(newOptions).filter(([_, value]) => value !== undefined), + ); + + return {...acc, ...newOptionsWithoutUndefined}; +} + +hydrogen.preset = () => + ({ + name: 'hydrogen', + remixConfigResolved({remixConfig}) { + sharedOptions.remixConfig = remixConfig; + }, + remixConfig() { + if (sharedOptions.disableVirtualRoutes) return {}; + + return { + buildDirectory: 'dist', + async routes(defineRoutes) { + if ( + sharedOptions.disableVirtualRoutes || + sharedOptions.command !== 'serve' + ) { + return {}; + } + + const {root, routes: virtualRoutes} = await getVirtualRoutes(); + + const result = defineRoutes((route) => { + route(root.path, root.file, {id: root.id}, () => { + virtualRoutes.map(({path, file, index, id}) => { + route(path, file, {id, index}); + }); + }); + }); + + // - Goal: stop matching the user's root with our virtual routes + // to avoid adding layouts and calling user loaders. + // + // - Problem: Even though root-less routes work in Remix, it always + // adds the user root as the parentId of every route when we leave it + // as undefined. Even if we delete it manually here, it adds it back: + // https://github.com/remix-run/remix/blob/b07921efd5e8eed98e2996749852777c71bc3e50/packages/remix-dev/config.ts#L565 + // + // - Solution: + // The String object tricks Remix into thinking that the + // parentId is defined (!!new String('') === true) so it doesn't + // overwrite it with `root`. Later, this value acts as an + // undefined / empty string when matching routes so it + // doesn't match the user root. + // @ts-expect-error + result[root.id].parentId = new String(''); + + return result; + }, + }; + }, + } satisfies RemixPreset); diff --git a/packages/hydrogen/src/vite/request-events.ts b/packages/hydrogen/src/vite/request-events.ts new file mode 100644 index 0000000000..80ebe9e5fe --- /dev/null +++ b/packages/hydrogen/src/vite/request-events.ts @@ -0,0 +1,208 @@ +import path from 'node:path'; +import {EventEmitter} from 'node:events'; +import type {IncomingMessage, ServerResponse} from 'node:http'; +import {mapSourcePosition} from 'source-map-support'; + +const DEV_ROUTES = new Set([ + '/graphiql', + '/subrequest-profiler', + '/debug-network-server', +]); + +type RequestEvent = { + event: string; + data: string; +}; + +const EVENT_MAP: Record = { + request: 'Request', + subrequest: 'Sub request', +}; + +export type RequestEventPayload = { + __fromVite?: boolean; + url: string; + eventType: 'request' | 'subrequest'; + requestId?: string | null; + purpose?: string | null; + startTime: number; + endTime?: number; + cacheStatus?: 'MISS' | 'HIT' | 'STALE' | 'PUT'; + waitUntil?: ExecutionContext['waitUntil']; + graphql?: string | null; + stackInfo?: { + file?: string; + func?: string; + line?: number; + column?: number; + }; + responsePayload?: any; + responseInit?: Omit & {headers?: [string, string][]}; + cache?: { + status?: string; + strategy?: string; + key?: string | readonly unknown[]; + }; + displayName?: string; +}; + +function getEventInfo(data: RequestEventPayload) { + return { + ...data, + requestId: data.requestId ?? '', + eventType: data.eventType || 'unknown', + endTime: data.endTime || Date.now(), + purpose: data.purpose === 'prefetch' ? '(prefetch)' : '', + cacheStatus: data.cacheStatus ?? '', + graphql: data.graphql + ? (JSON.parse(data.graphql) as { + query: string; + variables: object; + schema?: string; + }) + : null, + }; +} + +const eventEmitter = new EventEmitter(); +const eventHistory: RequestEvent[] = []; + +export function emitRequestEvent(payload: RequestEventPayload, root: string) { + if (!payload || !payload.url || !payload.requestId) { + // Ignore incorrect events, although this should not happen. + return; + } + + if (payload.eventType === 'request' && !payload.__fromVite) { + // Filter out events that come from @shopify/remix-oxygen, + // which is a deprecated way to send events. + return; + } + + delete payload.__fromVite; + + const {pathname} = new URL(payload.url, 'http://localhost'); + if (DEV_ROUTES.has(pathname)) return; + + const { + url: descriptionUrl, + displayName: displayNameData, + eventType, + purpose, + graphql, + stackInfo, + ...data + } = getEventInfo(payload); + + let graphiqlLink = ''; + let displayName = displayNameData; + + if (eventType === 'subrequest') { + displayName = + displayName || + graphql?.query + .match(/(query|mutation)\s+(\w+)/)?.[0] + ?.replace(/\s+/, ' '); + + if (graphql) { + graphiqlLink = getGraphiQLUrl(graphql); + } + } + + let stackLine: string | null = null; + let stackLink: string | null = null; + + if (stackInfo?.file) { + stackInfo.file = path.join(root, stackInfo.file); + + const {source, line, column} = mapSourcePosition({ + source: stackInfo.file, + line: stackInfo.line ?? 0, + column: stackInfo.column ?? 0, + }); + + stackLine = `${source}:${line}:${column + 1}`; + stackLink = `vscode://${path.join('file', stackLine)}`; + + stackLine = stackLine.split(path.sep + 'app' + path.sep)[1] ?? stackLine; + if (stackInfo.func) { + stackLine = `${stackInfo.func.replace(/\d+$/, '')} (${stackLine})`; + } + } + + const event = { + event: EVENT_MAP[eventType] || eventType, + data: JSON.stringify({ + ...data, + displayName, + url: `${purpose} ${descriptionUrl}`.trim(), + graphiqlLink, + stackLine, + stackLink, + }), + }; + + eventHistory.push(event); + if (eventHistory.length > 100) eventHistory.shift(); + + eventEmitter.emit('request', event); +} + +export function clearHistory( + req: IncomingMessage, + res: ServerResponse, +) { + eventHistory.length = 0; + res.writeHead(200); + res.end(); +} + +export function streamRequestEvents( + req: IncomingMessage, + res: ServerResponse, +) { + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-store', + Connection: 'keep-alive', + }); + + const enqueueEvent = ({event = 'message', data}: RequestEvent) => { + res.write(`event: ${event}\n`); + res.write(`data: ${data}\n\n`); + }; + + eventHistory.forEach(enqueueEvent); + eventEmitter.addListener('request', enqueueEvent); + + let closed = false; + function close() { + if (closed) return; + closed = true; + eventEmitter.removeListener('request', enqueueEvent); + } + + req.on('close', close); + res.on('close', close); +} + +type GraphiQLOptions = { + query: string; + variables: string | Record; + schema?: string; +}; + +function getGraphiQLUrl({query, variables, schema}: GraphiQLOptions) { + let url = `/graphiql?query=${encodeURIComponent(query)}`; + + if (variables) { + if (typeof variables !== 'string') variables = JSON.stringify(variables); + url += `&variables=${encodeURIComponent(variables)}`; + } + + if (schema) { + url += `&schema=${schema}`; + } + + return url; +} diff --git a/packages/hydrogen/src/vite/types.ts b/packages/hydrogen/src/vite/types.ts new file mode 100644 index 0000000000..d15981d245 --- /dev/null +++ b/packages/hydrogen/src/vite/types.ts @@ -0,0 +1,3 @@ +export type HydrogenPluginOptions = { + disableVirtualRoutes?: boolean; +}; diff --git a/packages/cli/src/virtual-routes/assets/debug-network.css b/packages/hydrogen/src/vite/virtual-routes/assets/debug-network.css similarity index 100% rename from packages/cli/src/virtual-routes/assets/debug-network.css rename to packages/hydrogen/src/vite/virtual-routes/assets/debug-network.css diff --git a/packages/cli/src/virtual-routes/assets/favicon-dark.svg b/packages/hydrogen/src/vite/virtual-routes/assets/favicon-dark.svg similarity index 100% rename from packages/cli/src/virtual-routes/assets/favicon-dark.svg rename to packages/hydrogen/src/vite/virtual-routes/assets/favicon-dark.svg diff --git a/packages/cli/src/virtual-routes/assets/favicon.svg b/packages/hydrogen/src/vite/virtual-routes/assets/favicon.svg similarity index 100% rename from packages/cli/src/virtual-routes/assets/favicon.svg rename to packages/hydrogen/src/vite/virtual-routes/assets/favicon.svg diff --git a/packages/cli/src/virtual-routes/assets/inter-variable-font.woff2 b/packages/hydrogen/src/vite/virtual-routes/assets/inter-variable-font.woff2 similarity index 100% rename from packages/cli/src/virtual-routes/assets/inter-variable-font.woff2 rename to packages/hydrogen/src/vite/virtual-routes/assets/inter-variable-font.woff2 diff --git a/packages/cli/src/virtual-routes/assets/jetbrainsmono-variable-font.woff2 b/packages/hydrogen/src/vite/virtual-routes/assets/jetbrainsmono-variable-font.woff2 similarity index 100% rename from packages/cli/src/virtual-routes/assets/jetbrainsmono-variable-font.woff2 rename to packages/hydrogen/src/vite/virtual-routes/assets/jetbrainsmono-variable-font.woff2 diff --git a/packages/cli/src/virtual-routes/assets/styles.css b/packages/hydrogen/src/vite/virtual-routes/assets/styles.css similarity index 100% rename from packages/cli/src/virtual-routes/assets/styles.css rename to packages/hydrogen/src/vite/virtual-routes/assets/styles.css diff --git a/packages/cli/src/virtual-routes/components/FlameChartWrapper.tsx b/packages/hydrogen/src/vite/virtual-routes/components/FlameChartWrapper.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/FlameChartWrapper.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/FlameChartWrapper.tsx diff --git a/packages/cli/src/virtual-routes/components/HydrogenLogoBaseBW.tsx b/packages/hydrogen/src/vite/virtual-routes/components/HydrogenLogoBaseBW.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/HydrogenLogoBaseBW.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/HydrogenLogoBaseBW.tsx diff --git a/packages/cli/src/virtual-routes/components/HydrogenLogoBaseColor.tsx b/packages/hydrogen/src/vite/virtual-routes/components/HydrogenLogoBaseColor.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/HydrogenLogoBaseColor.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/HydrogenLogoBaseColor.tsx diff --git a/packages/cli/src/virtual-routes/components/IconBanner.tsx b/packages/hydrogen/src/vite/virtual-routes/components/IconBanner.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/IconBanner.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/IconBanner.tsx diff --git a/packages/cli/src/virtual-routes/components/IconClose.tsx b/packages/hydrogen/src/vite/virtual-routes/components/IconClose.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/IconClose.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/IconClose.tsx diff --git a/packages/cli/src/virtual-routes/components/IconDiscard.tsx b/packages/hydrogen/src/vite/virtual-routes/components/IconDiscard.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/IconDiscard.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/IconDiscard.tsx diff --git a/packages/cli/src/virtual-routes/components/IconDiscord.tsx b/packages/hydrogen/src/vite/virtual-routes/components/IconDiscord.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/IconDiscord.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/IconDiscord.tsx diff --git a/packages/cli/src/virtual-routes/components/IconError.tsx b/packages/hydrogen/src/vite/virtual-routes/components/IconError.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/IconError.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/IconError.tsx diff --git a/packages/cli/src/virtual-routes/components/IconGithub.tsx b/packages/hydrogen/src/vite/virtual-routes/components/IconGithub.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/IconGithub.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/IconGithub.tsx diff --git a/packages/cli/src/virtual-routes/components/IconTwitter.tsx b/packages/hydrogen/src/vite/virtual-routes/components/IconTwitter.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/IconTwitter.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/IconTwitter.tsx diff --git a/packages/cli/src/virtual-routes/components/Layout.tsx b/packages/hydrogen/src/vite/virtual-routes/components/Layout.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/Layout.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/Layout.tsx diff --git a/packages/cli/src/virtual-routes/components/RequestDetails.tsx b/packages/hydrogen/src/vite/virtual-routes/components/RequestDetails.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/RequestDetails.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/RequestDetails.tsx diff --git a/packages/cli/src/virtual-routes/components/RequestTable.tsx b/packages/hydrogen/src/vite/virtual-routes/components/RequestTable.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/RequestTable.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/RequestTable.tsx diff --git a/packages/cli/src/virtual-routes/components/RequestWaterfall.tsx b/packages/hydrogen/src/vite/virtual-routes/components/RequestWaterfall.tsx similarity index 100% rename from packages/cli/src/virtual-routes/components/RequestWaterfall.tsx rename to packages/hydrogen/src/vite/virtual-routes/components/RequestWaterfall.tsx diff --git a/packages/cli/src/virtual-routes/lib/useDebugNetworkServer.tsx b/packages/hydrogen/src/vite/virtual-routes/lib/useDebugNetworkServer.tsx similarity index 100% rename from packages/cli/src/virtual-routes/lib/useDebugNetworkServer.tsx rename to packages/hydrogen/src/vite/virtual-routes/lib/useDebugNetworkServer.tsx diff --git a/packages/cli/src/virtual-routes/routes/graphiql.tsx b/packages/hydrogen/src/vite/virtual-routes/routes/graphiql.tsx similarity index 100% rename from packages/cli/src/virtual-routes/routes/graphiql.tsx rename to packages/hydrogen/src/vite/virtual-routes/routes/graphiql.tsx diff --git a/packages/cli/src/virtual-routes/routes/index.tsx b/packages/hydrogen/src/vite/virtual-routes/routes/index.tsx similarity index 100% rename from packages/cli/src/virtual-routes/routes/index.tsx rename to packages/hydrogen/src/vite/virtual-routes/routes/index.tsx diff --git a/packages/cli/src/virtual-routes/routes/subrequest-profiler.tsx b/packages/hydrogen/src/vite/virtual-routes/routes/subrequest-profiler.tsx similarity index 99% rename from packages/cli/src/virtual-routes/routes/subrequest-profiler.tsx rename to packages/hydrogen/src/vite/virtual-routes/routes/subrequest-profiler.tsx index 279c3bee80..479bf227e5 100644 --- a/packages/cli/src/virtual-routes/routes/subrequest-profiler.tsx +++ b/packages/hydrogen/src/vite/virtual-routes/routes/subrequest-profiler.tsx @@ -14,7 +14,6 @@ import {RequestDetails} from '../components/RequestDetails.jsx'; import {IconClose} from '../components/IconClose.jsx'; import {IconDiscard} from '../components/IconDiscard.jsx'; -// @ts-expect-error import styles from '../assets/debug-network.css?url'; export const links: LinksFunction = () => { diff --git a/packages/cli/src/virtual-routes/virtual-root.tsx b/packages/hydrogen/src/vite/virtual-routes/virtual-root.tsx similarity index 99% rename from packages/cli/src/virtual-routes/virtual-root.tsx rename to packages/hydrogen/src/vite/virtual-routes/virtual-root.tsx index c128bff3d5..dc5ed7f34d 100644 --- a/packages/cli/src/virtual-routes/virtual-root.tsx +++ b/packages/hydrogen/src/vite/virtual-routes/virtual-root.tsx @@ -15,7 +15,6 @@ import favicon from './assets/favicon.svg'; import {Layout} from './components/Layout.jsx'; import {useNonce} from '@shopify/hydrogen'; -// @ts-expect-error import styles from './assets/styles.css?url'; export const links: LinksFunction = () => { diff --git a/packages/hydrogen/tsconfig.json b/packages/hydrogen/tsconfig.json index 604136522e..9d7b4d468c 100644 --- a/packages/hydrogen/tsconfig.json +++ b/packages/hydrogen/tsconfig.json @@ -3,8 +3,10 @@ "include": ["src", "./hydrogen.config.d.ts"], "compilerOptions": { "jsx": "react-jsx", + "types": ["@shopify/oxygen-workers-types", "node", "vite/client"], "paths": { - "@shopify/hydrogen": ["./src"] + "@shopify/hydrogen": ["./src"], + "~/mini-oxygen/*": ["../mini-oxygen/src/*"] } }, "exclude": ["src/cart/createCartHandler.example.ts"] diff --git a/packages/hydrogen/tsup.config.ts b/packages/hydrogen/tsup.config.ts index 43a1c1a1f8..5f9b25c923 100644 --- a/packages/hydrogen/tsup.config.ts +++ b/packages/hydrogen/tsup.config.ts @@ -13,13 +13,13 @@ const commonConfig = defineConfig({ sourcemap: true, }); -export default [ - defineConfig({ +export default defineConfig([ + { ...commonConfig, env: {NODE_ENV: 'development'}, outDir: path.join(outDir, 'development'), - }), - defineConfig({ + }, + { ...commonConfig, env: {NODE_ENV: 'production'}, // Bundle types from hydrogen-codgen so that we @@ -63,5 +63,41 @@ export default [ console.log('\n', 'Customer API types copied from hydrogen-react', '\n'); }, - }), -]; + }, + { + entry: [ + 'src/vite/**/*.ts', + '!src/vite/**/*.test.ts', + '!src/vite/virtual-routes/**/*', + ], + outDir: 'dist/vite', + format: 'esm', + minify: false, + bundle: false, + sourcemap: false, + dts: true, + }, + { + entry: ['src/vite/virtual-routes/**/*.tsx'], + outDir: `${outDir}/vite/virtual-routes`, + outExtension: () => ({js: '.jsx'}), + format: 'esm', + minify: false, + bundle: false, + splitting: false, + treeshake: false, + sourcemap: false, + publicDir: false, + dts: false, + clean: false, // Avoid deleting the assets folder + async onSuccess() { + await fs.cp( + 'src/vite/virtual-routes/assets', + `${outDir}/vite/virtual-routes/assets`, + {recursive: true}, + ); + + console.log('\n', 'Copied virtual route assets to build directory', '\n'); + }, + }, +]); diff --git a/packages/hydrogen/vitest.config.ts b/packages/hydrogen/vitest.config.ts index ee1edf9a36..750bcbbd98 100644 --- a/packages/hydrogen/vitest.config.ts +++ b/packages/hydrogen/vitest.config.ts @@ -3,5 +3,10 @@ import {defineConfig} from 'vitest/config'; export default defineConfig({ test: { environment: 'happy-dom', + coverage: { + all: true, + include: ['src/**'], + exclude: ['src/vite/virtual-routes/**'], + }, }, }); diff --git a/packages/mini-oxygen/package.json b/packages/mini-oxygen/package.json index 0cb7006e56..357e58ca14 100644 --- a/packages/mini-oxygen/package.json +++ b/packages/mini-oxygen/package.json @@ -35,6 +35,10 @@ "types": "./dist/node/index.d.ts", "default": "./dist/node/index.js" }, + "./vite": { + "types": "./dist/vite/plugin.d.ts", + "default": "./dist/vite/plugin.js" + }, "./package.json": "./package.json" }, "dependencies": { @@ -70,5 +74,13 @@ "fs-extra": "^11.1.0", "tempy": "3.0.0", "vitest": "^1.0.4" + }, + "peerDependencies": { + "vite": "~5.1.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } } } diff --git a/packages/mini-oxygen/src/vite/plugin.ts b/packages/mini-oxygen/src/vite/plugin.ts new file mode 100644 index 0000000000..05c60d08a7 --- /dev/null +++ b/packages/mini-oxygen/src/vite/plugin.ts @@ -0,0 +1,153 @@ +import path from 'node:path'; +import type {Plugin, ResolvedConfig} from 'vite'; +import { + setupOxygenMiddleware, + startMiniOxygenRuntime, + type InternalMiniOxygenOptions, + type MiniOxygenViteOptions, + type MiniOxygen, +} from './server-middleware.js'; + +// Note: Vite resolves extensions like .js or .ts automatically. +const DEFAULT_SSR_ENTRY = './server'; + +let miniOxygen: MiniOxygen; + +export type OxygenPluginOptions = Partial< + Pick< + MiniOxygenViteOptions, + 'entry' | 'env' | 'inspectorPort' | 'logRequestLine' | 'debug' + > +>; + +type OxygenApiOptions = OxygenPluginOptions & + InternalMiniOxygenOptions & { + envPromise?: Promise>; + shouldStartRuntime?: () => boolean; + }; + +/** + * For internal use only. + * @private + */ +export type OxygenPlugin = Plugin<{ + registerPluginOptions(newOptions: OxygenApiOptions): void; +}>; + +/** + * Runs backend code in an Oxygen worker instead of Node.js during development. + * If used with `remix`, place it before it in the Vite plugin list. + */ +export function oxygen(pluginOptions: OxygenPluginOptions = {}): Plugin[] { + let resolvedConfig: ResolvedConfig; + let absoluteWorkerEntryFile: string; + let apiOptions: OxygenApiOptions = {}; + + return [ + { + name: 'oxygen:main', + config(config, env) { + return { + appType: 'custom', + resolve: { + conditions: ['worker', 'workerd'], + }, + ssr: { + noExternal: true, + target: 'webworker', + }, + // When building, the CLI will set the `ssr` option to `true` + // if no --entry flag is passed for the default SSR entry file. + // Replace it here with a default value. + ...(env.isSsrBuild && + config.build?.ssr && { + build: { + ssr: + config.build?.ssr === true + ? // No --entry flag passed by the user, use the + // option passed to the plugin or the default value + pluginOptions.entry ?? DEFAULT_SSR_ENTRY + : // --entry flag passed by the user, keep it + config.build?.ssr, + }, + }), + }; + }, + api: { + registerPluginOptions(newOptions) { + apiOptions = { + ...apiOptions, + ...newOptions, + env: {...apiOptions.env, ...newOptions.env}, + crossBoundarySetup: [ + ...(apiOptions.crossBoundarySetup || []), + ...(newOptions.crossBoundarySetup || []), + ], + }; + }, + }, + configureServer(viteDevServer) { + // For transform hook: + resolvedConfig = viteDevServer.config; + + if (miniOxygen) return; + if (apiOptions.shouldStartRuntime?.() !== true) return; + + const entry = + apiOptions.entry ?? pluginOptions.entry ?? DEFAULT_SSR_ENTRY; + + absoluteWorkerEntryFile = path.isAbsolute(entry) + ? entry + : path.resolve(resolvedConfig.root, entry); + + const miniOxygenPromise = Promise.resolve(apiOptions.envPromise).then( + (remoteEnv) => + startMiniOxygenRuntime({ + entry, + viteDevServer, + crossBoundarySetup: apiOptions.crossBoundarySetup, + env: {...remoteEnv, ...apiOptions.env, ...pluginOptions.env}, + debug: apiOptions.debug ?? pluginOptions.debug ?? false, + inspectorPort: + apiOptions.inspectorPort ?? pluginOptions.inspectorPort, + requestHook: apiOptions.requestHook, + logRequestLine: + // Give priority to the plugin option over the CLI option here, + // since the CLI one is just a default, not a user-provided flag. + pluginOptions?.logRequestLine ?? apiOptions.logRequestLine, + }), + ); + + process.once('SIGTERM', async () => { + try { + await miniOxygen?.dispose(); + } finally { + process.exit(); + } + }); + + return () => { + setupOxygenMiddleware(viteDevServer, async (request) => { + miniOxygen ??= await miniOxygenPromise; + return miniOxygen.dispatchFetch(request); + }); + }; + }, + transform(code, id, options) { + if ( + resolvedConfig?.command === 'serve' && + resolvedConfig?.server?.hmr !== false && + options?.ssr && + (id === absoluteWorkerEntryFile || + id === absoluteWorkerEntryFile + path.extname(id)) + ) { + return { + // Accept HMR in server entry module to avoid full-page refresh in the browser. + // Note: appending code at the end should not break the source map. + code: code + '\nif (import.meta.hot) import.meta.hot.accept();', + }; + } + }, + } satisfies OxygenPlugin, + ]; +} diff --git a/packages/mini-oxygen/src/vite/server-middleware.ts b/packages/mini-oxygen/src/vite/server-middleware.ts new file mode 100644 index 0000000000..31f4f19c43 --- /dev/null +++ b/packages/mini-oxygen/src/vite/server-middleware.ts @@ -0,0 +1,234 @@ +import {fetchModule, type ViteDevServer} from 'vite'; +import {fileURLToPath} from 'node:url'; +import { + fetch, + createMiniOxygen, + Request, + Response, + type RequestHook, + defaultLogRequestLine, +} from '../worker/index.js'; +import type {OnlyBindings, OnlyServices} from '../worker/utils.js'; +import {getHmrUrl, pipeFromWeb, toURL, toWeb} from './utils.js'; + +import type {ViteEnv} from './worker-entry.js'; +import {type RequestHookInfo} from '../worker/handler.js'; +const scriptPath = fileURLToPath(new URL('./worker-entry.js', import.meta.url)); + +const FETCH_MODULE_PATHNAME = '/__vite_fetch_module'; +const WARMUP_PATHNAME = '/__vite_warmup'; + +export type InternalMiniOxygenOptions = { + /** + * A function called asynchronously when the worker gets a response. + */ + requestHook?: RequestHook; + /** + * Allows setting up global state in the worker process + * that can optionally run code from the parent process. + */ + crossBoundarySetup?: Array< + | { + /** + * Function that is stringified and runs in the worker. + */ + script: () => void; + binding?: never; + } + | { + /** + * Function that is stringified and runs in the worker. + * It gets the binding function as its first argument. + */ + script: ( + binding: (...args: unknown[]) => Promise, + ) => void; + /** + * The binding function that runs in the parent process. + */ + binding: (...args: unknown[]) => unknown | Promise | void; + } + >; +}; + +export type MiniOxygenViteOptions = InternalMiniOxygenOptions & { + viteDevServer: ViteDevServer; + entry: string; + env?: {[key: string]: string}; + debug?: boolean; + inspectorPort?: number; + logRequestLine?: null | RequestHook; +}; + +export type MiniOxygen = Awaited>; + +export async function startMiniOxygenRuntime({ + viteDevServer, + env, + debug = false, + inspectorPort, + crossBoundarySetup, + entry: workerEntryFile, + requestHook, + logRequestLine = defaultLogRequestLine, +}: MiniOxygenViteOptions) { + const wrappedHook = + requestHook || logRequestLine + ? async (request: Request) => { + const info = (await request.json()) as RequestHookInfo; + + await Promise.all([requestHook?.(info), logRequestLine?.(info)]); + + return new Response('ok'); + } + : null; + + const miniOxygen = createMiniOxygen({ + debug, + inspectorPort, + requestHook: null, + workers: [ + { + name: 'vite-env', + modulesRoot: '/', + modules: [{type: 'ESModule', path: scriptPath}], + serviceBindings: { + ...(wrappedHook && {__VITE_REQUEST_HOOK: wrappedHook}), + } satisfies OnlyServices, + bindings: { + ...env, + __VITE_ROOT: viteDevServer.config.root, + __VITE_RUNTIME_EXECUTE_URL: workerEntryFile, + __VITE_FETCH_MODULE_PATHNAME: FETCH_MODULE_PATHNAME, + __VITE_HMR_URL: getHmrUrl(viteDevServer), + __VITE_WARMUP_PATHNAME: WARMUP_PATHNAME, + } satisfies OnlyBindings, + unsafeEvalBinding: '__VITE_UNSAFE_EVAL', + wrappedBindings: {__VITE_SETUP_ENV: 'setup-environment'}, + }, + { + name: 'setup-environment', + modules: true, + serviceBindings: crossBoundarySetup?.reduce((acc, {binding}, index) => { + if (binding) { + acc[`wrapped_service_${index}`] = async (request: Request) => { + const payload = (await request.json()) as unknown[]; + const result = await binding(...payload); + return new Response(JSON.stringify(result ?? '')); + }; + } + return acc; + }, {} as Record Promise>), + script: ` + const setupScripts = [${ + crossBoundarySetup?.map((boundary) => boundary.script) ?? '' + }]; + export default (env) => () => { + setupScripts.forEach((setup, index) => { + if (!setup) return; + + const service = env['wrapped_service_' + index]; + const wrappedBinding = service + ? (...args) => { + return service.fetch( + new Request( + 'http://localhost', + {method: 'POST', body: JSON.stringify(args)} + ) + ).then(response => response.json()); + } + : undefined; + + setup(wrappedBinding); + }); + + setupScripts.length = 0; + }`, + }, + ], + }); + + const warmupWorkerdCache = () => { + const viteUrl = getViteUrl(viteDevServer); + + if (viteUrl) { + miniOxygen + .dispatchFetch(new URL(WARMUP_PATHNAME, viteUrl)) + .catch(() => {}); + } + }; + + viteDevServer.httpServer?.listening + ? warmupWorkerdCache() + : viteDevServer.httpServer?.once('listening', warmupWorkerdCache); + + return miniOxygen; +} + +export function setupOxygenMiddleware( + viteDevServer: ViteDevServer, + dispatchFetch: (webRequest: Request) => Promise, +) { + viteDevServer.middlewares.use( + FETCH_MODULE_PATHNAME, + function o2HandleModuleFetch(req, res) { + // This request comes from workerd. It is asking for the contents + // of backend files. We need to fetch the file through Vite, + // which transpiles/prepares the source code into valid JS, and + // send it back so that workerd can evaluate/run it. + + const url = toURL(req); + const id = url.searchParams.get('id'); + const importer = url.searchParams.get('importer') ?? undefined; + + if (id) { + res.setHeader('Cache-Control', 'no-store'); + res.setHeader('Content-Type', 'application/json'); + + // `fetchModule` is similar to `viteDevServer.ssrFetchModule`, + // but it treats source maps differently (avoids adding empty lines). + fetchModule(viteDevServer, id, importer) + .then((ssrModule) => res.end(JSON.stringify(ssrModule))) + .catch((error) => { + console.error('Error during module fetch:', error); + res.writeHead(500, {'Content-Type': 'text/plain'}); + res.end('Internal server error'); + }); + } else { + res.writeHead(400, {'Content-Type': 'text/plain'}); + res.end('Invalid request'); + } + }, + ); + + viteDevServer.middlewares.use(function o2HandleWorkerRequest(req, res) { + // This request comes from the browser. At this point, Vite + // tried to serve the request as a static file, but it didn't + // find it in the project. Therefore, we assume this is a + // request for a backend route, and we forward it to workerd. + + dispatchFetch(toWeb(req)) + .then((webResponse) => pipeFromWeb(webResponse, res)) + .catch((error) => { + console.error('Error during evaluation:', error); + res.writeHead(500); + res.end(); + }); + }); +} + +function getViteUrl(viteDevServer: ViteDevServer) { + let viteUrl = + viteDevServer.resolvedUrls?.local[0] ?? + viteDevServer.resolvedUrls?.network[0]; + + if (!viteUrl) { + const address = viteDevServer.httpServer?.address?.(); + viteUrl = + address && typeof address !== 'string' + ? `http://localhost:${address.port}` + : address ?? undefined; + } + + return viteUrl; +} diff --git a/packages/cli/src/lib/vite/utils.ts b/packages/mini-oxygen/src/vite/utils.ts similarity index 88% rename from packages/cli/src/lib/vite/utils.ts rename to packages/mini-oxygen/src/vite/utils.ts index 6729069081..3763fddfa4 100644 --- a/packages/cli/src/lib/vite/utils.ts +++ b/packages/mini-oxygen/src/vite/utils.ts @@ -1,9 +1,8 @@ import type {ServerResponse, IncomingMessage} from 'node:http'; import path from 'node:path'; import {Readable} from 'node:stream'; -import type {Response} from '@shopify/mini-oxygen'; +import {Request, type Response} from '../worker/index.js'; import type {ViteDevServer} from 'vite'; -import {handleMiniOxygenImportFail} from '../mini-oxygen/common.js'; /** * Creates a fully qualified URL from a Node request or a string. @@ -24,14 +23,7 @@ export function toURL(req: string | IncomingMessage = '/', origin?: string) { /** * Turns a Node request into a Web request by using native Node APIs. */ -export async function toWeb( - req: IncomingMessage, - headers?: Record, -) { - const {Request} = await import('@shopify/mini-oxygen').catch( - handleMiniOxygenImportFail, - ); - +export function toWeb(req: IncomingMessage, headers?: Record) { if (!req.headers.host) { throw new Error('Request must contain a host header.'); } diff --git a/packages/cli/src/lib/vite/worker-entry.ts b/packages/mini-oxygen/src/vite/worker-entry.ts similarity index 93% rename from packages/cli/src/lib/vite/worker-entry.ts rename to packages/mini-oxygen/src/vite/worker-entry.ts index b4281480dc..a85965b50a 100644 --- a/packages/cli/src/lib/vite/worker-entry.ts +++ b/packages/mini-oxygen/src/vite/worker-entry.ts @@ -13,6 +13,7 @@ import { } from 'vite/runtime'; import type {HMRPayload} from 'vite'; import type {Response} from 'miniflare'; +import {withRequestHook} from '../worker/handler.js'; export interface ViteEnv { __VITE_ROOT: string; @@ -20,6 +21,7 @@ export interface ViteEnv { __VITE_FETCH_MODULE_PATHNAME: string; __VITE_RUNTIME_EXECUTE_URL: string; __VITE_WARMUP_PATHNAME: string; + __VITE_REQUEST_HOOK?: {fetch: typeof fetch}; __VITE_SETUP_ENV: (request: Request) => void; // Ref: https://github.com/cloudflare/workerd/blob/main/src/workerd/api/unsafe.h __VITE_UNSAFE_EVAL: { @@ -35,7 +37,7 @@ export default { /** * Worker entry module that wraps the user app's entry module. */ - async fetch(request: Request, env: ViteEnv, ctx: any) { + async fetch(request: Request, env: ViteEnv, context: ExecutionContext) { env.__VITE_SETUP_ENV(request); const url = new URL(request.url); @@ -47,8 +49,18 @@ export default { return new globalThis.Response(null); } + const handleRequest = () => + module.default.fetch(request, createUserEnv(env), context); + // Execute the user app's entry module. - return module.default.fetch(request, createUserEnv(env), ctx); + return env.__VITE_REQUEST_HOOK + ? withRequestHook({ + request, + context, + hook: env.__VITE_REQUEST_HOOK, + handleRequest, + }) + : handleRequest(); }, }; diff --git a/packages/mini-oxygen/src/worker/e2e.test.ts b/packages/mini-oxygen/src/worker/e2e.test.ts index b662ce32e6..41053cede2 100644 --- a/packages/mini-oxygen/src/worker/e2e.test.ts +++ b/packages/mini-oxygen/src/worker/e2e.test.ts @@ -300,7 +300,7 @@ function withFixtures( }, ], sourceMapPath: path.join(tmpDir, relativeWorkerEntry + '.map'), - logRequestLine: null, + requestHook: null, } satisfies MiniOxygenOptions; const miniOxygen = createMiniOxygen(miniOxygenOptions); diff --git a/packages/mini-oxygen/src/worker/handler.ts b/packages/mini-oxygen/src/worker/handler.ts index 9d6e5b49f4..9d6db740d2 100644 --- a/packages/mini-oxygen/src/worker/handler.ts +++ b/packages/mini-oxygen/src/worker/handler.ts @@ -1,14 +1,21 @@ type Service = {fetch: typeof fetch}; -export async function miniOxygenHandler( +export type MiniOxygenHandlerEnv = { + entry: Service; + assets?: Service; + hook?: Service; + staticAssetExtensions?: string[]; + oxygenHeadersMap: Record; +}; + +export function getMiniOxygenHandlerScript() { + return `export default { fetch: ${miniOxygenHandler} }\n${withRequestHook}`; +} + +// This function is stringified, do not use anything from outer scope here: +async function miniOxygenHandler( request: Request, - env: { - entry: Service; - assets?: Service; - logRequest: Service; - staticAssetExtensions?: string[]; - oxygenHeadersMap: Record; - }, + env: MiniOxygenHandlerEnv, context: ExecutionContext, ) { const {pathname} = new URL(request.url); @@ -40,22 +47,84 @@ export async function miniOxygenHandler( }, } satisfies RequestInit; + const handleRequest = () => env.entry.fetch(request, requestInit); + + return env.hook + ? withRequestHook({ + ...requestInit, + handleRequest, + request, + headers: requestInit.headers, + hook: env.hook, + context, + }) + : handleRequest(); +} + +type RequestHookOptions = { + handleRequest: () => Response | Promise; + request: Request; + headers?: Record; + context: ExecutionContext; + hook: Service; +}; + +/** + * @public + */ +export type RequestHookInfo = { + request: { + url: string; + method: string; + headers: Record; + }; + response: { + status: number; + statusText: string; + headers: Record; + }; + meta: { + startTimeMs: number; + endTimeMs: number; + durationMs: number; + }; +}; + +// This function is stringified, do not use anything from outer scope here: +export async function withRequestHook({ + handleRequest, + request, + headers, + hook, + context, +}: RequestHookOptions) { const startTimeMs = Date.now(); - const response = await env.entry.fetch(request, requestInit); - const durationMs = Date.now() - startTimeMs; + const response = await handleRequest(); + const endTimeMs = Date.now(); + const durationMs = endTimeMs - startTimeMs; context.waitUntil( - env.logRequest.fetch( - new Request(request.url, { - method: request.method, - signal: request.signal, - headers: { - ...requestInit.headers, - 'o2-duration-ms': String(durationMs), - 'o2-response-status': String(response.status), + hook.fetch(request.url, { + method: 'POST', + signal: request.signal, + body: JSON.stringify({ + request: { + url: request.url, + method: request.method, + headers: headers ?? Object.fromEntries(request.headers.entries()), + }, + response: { + status: response.status, + statusText: response.statusText, + headers: Object.fromEntries(response.headers.entries()), + }, + meta: { + startTimeMs, + endTimeMs, + durationMs, }, - }), - ), + } satisfies RequestHookInfo), + }), ); return response; diff --git a/packages/mini-oxygen/src/worker/index.ts b/packages/mini-oxygen/src/worker/index.ts index 6b17a5d9a0..ed950a15f9 100644 --- a/packages/mini-oxygen/src/worker/index.ts +++ b/packages/mini-oxygen/src/worker/index.ts @@ -16,11 +16,16 @@ import { buildAssetsUrl, createAssetsServer, } from './assets.js'; -import {miniOxygenHandler} from './handler.js'; +import { + getMiniOxygenHandlerScript, + type MiniOxygenHandlerEnv, + type RequestHookInfo, +} from './handler.js'; import {OXYGEN_HEADERS_MAP} from '../common/headers.js'; import {findPort} from '../common/find-port.js'; import {OXYGEN_COMPAT_PARAMS} from '../common/compat.js'; import {isO2Verbose} from '../common/debug.js'; +import type {OnlyBindings, OnlyServices} from './utils.js'; export { buildAssetsUrl, @@ -29,6 +34,7 @@ export { fetch, type RequestInit, type ResponseInit, + type RequestHookInfo, }; const DEFAULT_PUBLIC_INSPECTOR_PORT = 9229; @@ -64,7 +70,7 @@ export type MiniOxygenOptions = InputMiniflareOptions & { debug?: boolean; sourceMapPath?: string; assets?: AssetOptions; - logRequestLine?: LogRequestLine | null; + requestHook?: RequestHook | null; inspectWorkerName?: string; }; @@ -75,12 +81,12 @@ export function createMiniOxygen({ inspectorPort, assets, sourceMapPath = '', - logRequestLine, + requestHook, inspectWorkerName, ...miniflareOptions }: MiniOxygenOptions) { const mf = new Miniflare( - buildMiniflareOptions(miniflareOptions, logRequestLine, assets), + buildMiniflareOptions(miniflareOptions, requestHook, assets), ); if (!sourceMapPath) { @@ -91,11 +97,13 @@ export function createMiniOxygen({ )) || miniflareOptions.workers[0]; - if ('scriptPath' in mainWorker) { - sourceMapPath = mainWorker.scriptPath + '.map'; - } else if (Array.isArray(mainWorker?.modules)) { - const modulePath = mainWorker?.modules[0]!.path; - sourceMapPath = modulePath + '.map'; + if (mainWorker) { + if ('scriptPath' in mainWorker) { + sourceMapPath = mainWorker.scriptPath + '.map'; + } else if (Array.isArray(mainWorker?.modules)) { + const modulePath = mainWorker?.modules[0]!.path; + sourceMapPath = modulePath + '.map'; + } } } @@ -155,7 +163,7 @@ export function createMiniOxygen({ mf.setOptions( buildMiniflareOptions( {...miniflareOptions, ...newOptions}, - logRequestLine, + requestHook, assets, ), ), @@ -169,14 +177,11 @@ export function createMiniOxygen({ }; } -type LogRequestLine = ( - request: Request, - opt: {responseStatus: number; durationMs: number}, -) => void; +export type RequestHook = (info: RequestHookInfo) => void | Promise; -const defaultLogRequestLine: LogRequestLine = (request, {responseStatus}) => { +export const defaultLogRequestLine: RequestHook = ({request, response}) => { console.log( - `${request.method} ${responseStatus} ${request.url.replace( + `${request.method} ${response.status} ${request.url.replace( new URL(request.url).origin, '', )}`, @@ -208,7 +213,7 @@ const UNSAFE_OUTBOUND_SERVICE = { function buildMiniflareOptions( {workers, ...mfOverwriteOptions}: InputMiniflareOptions, - logRequestLine: LogRequestLine | null = defaultLogRequestLine, + requestHook: RequestHook | null = defaultLogRequestLine, assetsOptions?: AssetOptions, ): OutputMiniflareOptions { const entryWorker = workers.find((worker) => !!worker.name); @@ -221,16 +226,12 @@ function buildMiniflareOptions( ? STATIC_ASSET_EXTENSIONS.slice() : null; - async function logRequest(request: Request): Promise { - const durationMs = Number(request.headers.get('o2-duration-ms') || 0); - const responseStatus = Number( - request.headers.get('o2-response-status') || 200, - ); - - logRequestLine?.(request, {responseStatus, durationMs}); - - return new Response('ok'); - } + const wrappedHook = requestHook + ? async (request: Request) => { + await requestHook((await request.json()) as RequestHookInfo); + return new Response('ok'); + } + : null; const wrappedBindings = new Set( workers @@ -270,16 +271,16 @@ function buildMiniflareOptions( { name: ROUTING_WORKER_NAME, modules: true, - script: `export default { fetch: ${miniOxygenHandler.toString()} }`, + script: getMiniOxygenHandlerScript(), bindings: { staticAssetExtensions, oxygenHeadersMap, - }, + } satisfies OnlyBindings, serviceBindings: { entry: entryWorker.name, - logRequest, + ...(wrappedHook && {hook: wrappedHook}), ...(handleAssets && {assets: handleAssets}), - }, + } satisfies OnlyServices, }, ...workers.map((worker) => { const isNormalWorker = !wrappedBindings.has(worker.name); diff --git a/packages/mini-oxygen/src/worker/utils.ts b/packages/mini-oxygen/src/worker/utils.ts new file mode 100644 index 0000000000..992d27ed8d --- /dev/null +++ b/packages/mini-oxygen/src/worker/utils.ts @@ -0,0 +1,28 @@ +import type {Request} from 'miniflare'; +// --- Utility types: +type OnlyServiceKeys = Exclude< + { + [P in keyof T]: NonNullable extends {fetch: Function} ? P : never; + }[keyof T], + undefined +>; + +type OtherKeys = Exclude< + { + [P in keyof T]: NonNullable extends Function | {eval: Function} + ? P + : never; + }[keyof T], + undefined +>; + +export type OnlyServices = Pick< + {[key in keyof T]: string | ((request: Request) => Promise)}, + OnlyServiceKeys +>; + +type UnionUndefinedToNull = T extends undefined ? null : T; +export type OnlyBindings = Omit< + {[key in keyof T]: UnionUndefinedToNull}, + OnlyServiceKeys | OtherKeys +>; diff --git a/packages/mini-oxygen/tsconfig.json b/packages/mini-oxygen/tsconfig.json index c431c7e4ed..2c9211a975 100644 --- a/packages/mini-oxygen/tsconfig.json +++ b/packages/mini-oxygen/tsconfig.json @@ -3,7 +3,6 @@ "include": ["src"], "exclude": ["dist", "tests", "node_modules", ".turbo"], "compilerOptions": { - "rootDir": "src", "target": "ES2022", "outDir": "dist", "module": "NodeNext", diff --git a/packages/mini-oxygen/tsup.config.ts b/packages/mini-oxygen/tsup.config.ts index 985d622019..f2443a9678 100644 --- a/packages/mini-oxygen/tsup.config.ts +++ b/packages/mini-oxygen/tsup.config.ts @@ -6,7 +6,7 @@ fs.removeSync('./dist'); export default defineConfig([ { - entry: ['src/**/*.ts', '!src/**/*.test.ts'], + entry: ['src/**/*.ts', '!src/**/*.test.ts', '!src/vite/worker-entry.ts'], outDir: 'dist', format: 'esm', minify: false, @@ -14,4 +14,11 @@ export default defineConfig([ sourcemap: false, dts: true, }, + { + entry: ['src/vite/worker-entry.ts'], + outDir: 'dist/vite', + format: 'esm', + noExternal: [/./], + dts: false, + }, ]); diff --git a/packages/remix-oxygen/src/event-logger.ts b/packages/remix-oxygen/src/event-logger.ts index c3f5cfe30c..0ba62eec65 100644 --- a/packages/remix-oxygen/src/event-logger.ts +++ b/packages/remix-oxygen/src/event-logger.ts @@ -1,32 +1,10 @@ -// Make sure to match this type with the one in packages/cli/src/lib/request-events.ts -export type H2OEvent = { - url: string; - eventType: 'request' | 'subrequest'; - requestId?: string | null; - purpose?: string | null; - startTime: number; - endTime?: number; - cacheStatus?: 'MISS' | 'HIT' | 'STALE' | 'PUT'; - waitUntil?: ExecutionContext['waitUntil']; - graphql?: string | null; - stackInfo?: { - file?: string; - func?: string; - line?: number; - column?: number; - }; - responsePayload?: any; - responseInit?: ResponseInit; - cache?: { - status?: string; - strategy?: string; - key?: string | readonly unknown[]; - }; - displayName?: string; -}; +type H2OEvent = Parameters>[0]; let hasWarned = false; +/** + * @deprecated Only used with the classic Remix compiler + */ export function createEventLogger(appLoadContext: Record) { const context = (appLoadContext || {}) as { env?: Record; diff --git a/packages/remix-oxygen/src/server.ts b/packages/remix-oxygen/src/server.ts index 84ea529649..dc22819472 100644 --- a/packages/remix-oxygen/src/server.ts +++ b/packages/remix-oxygen/src/server.ts @@ -1,13 +1,10 @@ +/// import { createRequestHandler as createRemixRequestHandler, type AppLoadContext, type ServerBuild, } from '@remix-run/server-runtime'; -import {createEventLogger, type H2OEvent} from './event-logger'; - -declare global { - var __H2O_LOG_EVENT: undefined | ((event: H2OEvent) => void); -} +import {createEventLogger} from './event-logger'; const originalErrorToString = Error.prototype.toString; Error.prototype.toString = function () { @@ -36,7 +33,7 @@ export function createRequestHandler({ // Store logger in globalThis so it can be accessed from the worker. // The global property must be different from the binding name, // otherwise Miniflare throws an error when accessing it. - globalThis.__H2O_LOG_EVENT = createEventLogger(context); + globalThis.__H2O_LOG_EVENT ??= createEventLogger(context); } const startTime = Date.now(); diff --git a/templates/skeleton/package.json b/templates/skeleton/package.json index 69903c3179..fddac562bb 100644 --- a/templates/skeleton/package.json +++ b/templates/skeleton/package.json @@ -40,7 +40,7 @@ "eslint-plugin-hydrogen": "0.12.2", "prettier": "^2.8.4", "typescript": "^5.2.2", - "vite": "~5.1.7", + "vite": "~5.1.0", "vite-tsconfig-paths": "^4.3.1" }, "engines": { diff --git a/templates/skeleton/vite.config.ts b/templates/skeleton/vite.config.ts index 654277cbd9..b73891298a 100644 --- a/templates/skeleton/vite.config.ts +++ b/templates/skeleton/vite.config.ts @@ -1,5 +1,6 @@ import {defineConfig} from 'vite'; -import {hydrogen, oxygen} from '@shopify/cli-hydrogen/experimental-vite'; +import {hydrogen} from '@shopify/hydrogen/vite'; +import {oxygen} from '@shopify/mini-oxygen/vite'; import {vitePlugin as remix} from '@remix-run/dev'; import tsconfigPaths from 'vite-tsconfig-paths'; @@ -8,7 +9,7 @@ export default defineConfig({ hydrogen(), oxygen(), remix({ - buildDirectory: 'dist', + presets: [hydrogen.preset()], future: { v3_fetcherPersist: true, v3_relativeSplatPath: true, diff --git a/turbo.json b/turbo.json index e2a16e03cb..bed6c8ffa6 100644 --- a/turbo.json +++ b/turbo.json @@ -39,11 +39,11 @@ "@shopify/create-hydrogen#build": { "dependsOn": ["@shopify/cli-hydrogen#build"] }, + "@shopify/remix-oxygen#build": { + "dependsOn": ["@shopify/hydrogen#build"] + }, "@shopify/hydrogen#build": { - "dependsOn": [ - "@shopify/hydrogen-react#build", - "@shopify/remix-oxygen#build" - ] + "dependsOn": ["@shopify/hydrogen-react#build"] }, "hello-world#build": { "dependsOn": [