diff --git a/.changeset/lovely-worms-invite.md b/.changeset/lovely-worms-invite.md index d081d2b79933..22aa0ef1900f 100644 --- a/.changeset/lovely-worms-invite.md +++ b/.changeset/lovely-worms-invite.md @@ -1,8 +1,10 @@ --- '@astrojs/deno': major '@astrojs/netlify': major -'@astrojs/image': minor +'@astrojs/image': major 'astro': major --- -Builds chunks into the `assets` folder. This simplifies configuring immutable caching with your adapter provider as all files are now in the same `assets` folder. +**Breaking Change**: client assets are built to an `_astro` directory rather than the previous `assets` directory. This setting can now be controlled by the new `build` configuration option named `assets`. + +This should simplify configuring immutable caching with your adapter provider as all files are now in the same `_astro` directory. diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 872abeaf8370..5c22fd24de7a 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -586,6 +586,25 @@ export interface AstroUserConfig { * ``` */ server?: string; + /** + * @docs + * @name build.assets + * @type {string} + * @default `'_astro'` + * @see outDir + * @version 2.0.0 + * @description + * Specifies the directory in the build output where Astro-generated assets (bundled JS and CSS for example) should live. + * + * ```js + * { + * build: { + * assets: '_custom' + * } + * } + * ``` + */ + assets?: string; /** * @docs * @name build.serverEntry diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 25ff1f5f63cd..1536d91557c8 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -10,9 +10,10 @@ import { BuildInternals, createBuildInternals, eachPrerenderedPageData, + isHoistedScript, } from '../../core/build/internal.js'; -import { emptyDir, removeDir } from '../../core/fs/index.js'; -import { prependForwardSlash } from '../../core/path.js'; +import { emptyDir, removeDir, removeEmptyDirs } from '../../core/fs/index.js'; +import { appendForwardSlash, prependForwardSlash } from '../../core/path.js'; import { isModeServerWithNoAdapter } from '../../core/util.js'; import { runHookBuildSetup } from '../../integrations/index.js'; import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js'; @@ -133,8 +134,10 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp input: [], output: { format: 'esm', - chunkFileNames: 'chunks/[name].[hash].mjs', - assetFileNames: 'assets/[name].[hash][extname]', + // Server chunks can't go in the assets (_astro) folder + // We need to keep these separate + chunkFileNames: `chunks/[name].[hash].mjs`, + assetFileNames: `${settings.config.build.assets}/[name].[hash][extname]`, ...viteConfig.build?.rollupOptions?.output, entryFileNames: opts.buildConfig.serverEntry, }, @@ -212,9 +215,9 @@ async function clientBuild( input: Array.from(input), output: { format: 'esm', - entryFileNames: 'assets/[name].[hash].js', - chunkFileNames: 'assets/chunks/[name].[hash].js', - assetFileNames: 'assets/[name].[hash][extname]', + entryFileNames: `${settings.config.build.assets}/[name].[hash].js`, + chunkFileNames: `${settings.config.build.assets}/[name].[hash].js`, + assetFileNames: `${settings.config.build.assets}/[name].[hash][extname]`, ...viteConfig.build?.rollupOptions?.output, }, preserveEntrySignatures: 'exports-only', @@ -285,25 +288,8 @@ async function cleanStaticOutput(opts: StaticBuildOptions, internals: BuildInter await fs.promises.writeFile(url, value, { encoding: 'utf8' }); }) ); - // Map directories heads from the .mjs files - const directories: Set = new Set(); - files.forEach((i) => { - const splitFilePath = i.split(path.sep); - // If the path is more than just a .mjs filename itself - if (splitFilePath.length > 1) { - directories.add(splitFilePath[0]); - } - }); - // Attempt to remove only those folders which are empty - await Promise.all( - Array.from(directories).map(async (filename) => { - const url = new URL(filename, out); - const folder = await fs.promises.readdir(url); - if (!folder.length) { - await fs.promises.rm(url, { recursive: true, force: true }); - } - }) - ); + + removeEmptyDirs(out); } } @@ -321,28 +307,10 @@ async function cleanServerOutput(opts: StaticBuildOptions) { await fs.promises.rm(url); }) ); - // Map directories heads from the .mjs files - const directories: Set = new Set(); - files.forEach((i) => { - const splitFilePath = i.split(path.sep); - // If the path is more than just a .mjs filename itself - if (splitFilePath.length > 1) { - directories.add(splitFilePath[0]); - } - }); - // Attempt to remove only those folders which are empty - await Promise.all( - Array.from(directories).map(async (filename) => { - const url = new URL(filename, out); - const dir = await glob(fileURLToPath(url)); - // Do not delete chunks/ directory! - if (filename === 'chunks') return; - if (!dir.length) { - await fs.promises.rm(url, { recursive: true, force: true }); - } - }) - ); + + removeEmptyDirs(out); } + // Clean out directly if the outDir is outside of root if (out.toString() !== opts.settings.config.outDir.toString()) { // Copy assets before cleaning directory if outside root @@ -374,10 +342,11 @@ async function ssrMoveAssets(opts: StaticBuildOptions) { const serverRoot = opts.settings.config.output === 'static' ? opts.buildConfig.client : opts.buildConfig.server; const clientRoot = opts.buildConfig.client; - const serverAssets = new URL('./assets/', serverRoot); - const clientAssets = new URL('./assets/', clientRoot); - const files = await glob('assets/**/*', { - cwd: fileURLToPath(serverRoot), + const assets = opts.settings.config.build.assets; + const serverAssets = new URL(`./${assets}/`, appendForwardSlash(serverRoot.toString())); + const clientAssets = new URL(`./${assets}/`, appendForwardSlash(clientRoot.toString())); + const files = await glob(`**/*`, { + cwd: fileURLToPath(serverAssets), }); if (files.length > 0) { @@ -385,11 +354,11 @@ async function ssrMoveAssets(opts: StaticBuildOptions) { await fs.promises.mkdir(clientAssets, { recursive: true }); await Promise.all( files.map(async (filename) => { - const currentUrl = new URL(filename, serverRoot); - const clientUrl = new URL(filename, clientRoot); + const currentUrl = new URL(filename, appendForwardSlash(serverAssets.toString())); + const clientUrl = new URL(filename, appendForwardSlash(clientAssets.toString())); return fs.promises.rename(currentUrl, clientUrl); }) ); - removeDir(serverAssets); + removeEmptyDirs(serverAssets); } } diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index 1cd49b7fbd31..a31dfef9e1b1 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -23,6 +23,7 @@ const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = { format: 'directory', client: './dist/client/', server: './dist/server/', + assets: '_astro', serverEntry: 'entry.mjs', }, server: { @@ -102,6 +103,7 @@ export const AstroConfigSchema = z.object({ .optional() .default(ASTRO_CONFIG_DEFAULTS.build.server) .transform((val) => new URL(val)), + assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets), serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry), }) .optional() @@ -246,6 +248,7 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: URL) { .optional() .default(ASTRO_CONFIG_DEFAULTS.build.server) .transform((val) => new URL(val, fileProtocolRoot)), + assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets), serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry), }) .optional() diff --git a/packages/astro/src/core/fs/index.ts b/packages/astro/src/core/fs/index.ts index 521d99835b88..4b627a3ebdfc 100644 --- a/packages/astro/src/core/fs/index.ts +++ b/packages/astro/src/core/fs/index.ts @@ -1,6 +1,7 @@ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; +import { appendForwardSlash } from '../path.js'; const isWindows = process.platform === 'win32'; @@ -10,6 +11,24 @@ export function removeDir(_dir: URL): void { fs.rmSync(dir, { recursive: true, force: true, maxRetries: 3 }); } +export function removeEmptyDirs(root: URL): void { + const dir = fileURLToPath(root); + if (!fs.statSync(dir).isDirectory()) return; + let files = fs.readdirSync(dir); + + if (files.length > 0) { + files.map(file => { + const url = new URL(`./${file}`, appendForwardSlash(root.toString())); + removeEmptyDirs(url); + }) + files = fs.readdirSync(dir); + } + + if (files.length === 0) { + fs.rmdirSync(dir); + } +} + export function emptyDir(_dir: URL, skip?: Set): void { const dir = fileURLToPath(_dir); if (!fs.existsSync(dir)) return undefined; diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js index 2f1e51a74c3c..1583891328e4 100644 --- a/packages/astro/test/0-css.test.js +++ b/packages/astro/test/0-css.test.js @@ -29,7 +29,7 @@ describe('CSS', function () { // get bundled CSS (will be hashed, hence DOM query) html = await fixture.readFile('/index.html'); $ = cheerio.load(html); - const bundledCSSHREF = $('link[rel=stylesheet][href^=/assets/]').attr('href'); + const bundledCSSHREF = $('link[rel=stylesheet][href^=/_astro/]').attr('href'); bundledCSS = (await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/'))) .replace(/\s/g, '') .replace('/n', ''); @@ -364,7 +364,7 @@ describe('CSS', function () { }); it('remove unused styles from client:load components', async () => { - const bundledAssets = await fixture.readdir('./assets'); + const bundledAssets = await fixture.readdir('./_astro'); // SvelteDynamic styles is already included in the main page css asset const unusedCssAsset = bundledAssets.find((asset) => /SvelteDynamic\..*\.css/.test(asset)); expect(unusedCssAsset, 'Found unused style ' + unusedCssAsset).to.be.undefined; diff --git a/packages/astro/test/astro-css-bundling.test.js b/packages/astro/test/astro-css-bundling.test.js index e39beb8a9ad6..415c8f39814f 100644 --- a/packages/astro/test/astro-css-bundling.test.js +++ b/packages/astro/test/astro-css-bundling.test.js @@ -5,9 +5,9 @@ import { loadFixture } from './test-utils.js'; // note: the hashes should be deterministic, but updating the file contents will change hashes // be careful not to test that the HTML simply contains CSS, because it always will! filename and quanity matter here (bundling). const EXPECTED_CSS = { - '/index.html': ['/assets/'], // don’t match hashes, which change based on content - '/one/index.html': ['/assets/'], - '/two/index.html': ['/assets/'], + '/index.html': ['/_astro/'], // don’t match hashes, which change based on content + '/one/index.html': ['/_astro/'], + '/two/index.html': ['/_astro/'], }; const UNEXPECTED_CSS = [ '/src/components/nav.css', @@ -61,12 +61,12 @@ describe('CSS Bundling', function () { }); it('there are 4 css files', async () => { - const dir = await fixture.readdir('/assets'); + const dir = await fixture.readdir('/_astro'); expect(dir).to.have.a.lengthOf(4); }); it('CSS includes hashes', async () => { - const [firstFound] = await fixture.readdir('/assets'); + const [firstFound] = await fixture.readdir('/_astro'); expect(firstFound).to.match(/[a-z]+\.[0-9a-z]{8}\.css/); }); }); diff --git a/packages/astro/test/astro-dynamic.test.js b/packages/astro/test/astro-dynamic.test.js index 78c5679a2bd6..5ed34b8ba70b 100644 --- a/packages/astro/test/astro-dynamic.test.js +++ b/packages/astro/test/astro-dynamic.test.js @@ -75,6 +75,6 @@ describe('Dynamic components subpath', () => { expect($('astro-island').html()).to.equal(''); // test 2: has component url const attr = $('astro-island').attr('component-url'); - expect(attr).to.include(`blog/assets/PersistentCounter`); + expect(attr).to.include(`blog/_astro/PersistentCounter`); }); }); diff --git a/packages/astro/test/astro-envs.test.js b/packages/astro/test/astro-envs.test.js index 534604f704bd..d2d06c28d510 100644 --- a/packages/astro/test/astro-envs.test.js +++ b/packages/astro/test/astro-envs.test.js @@ -52,7 +52,7 @@ describe('Environment Variables', () => { }); it('includes public env in client-side JS', async () => { - let dirs = await fixture.readdir('/assets'); + let dirs = await fixture.readdir('/_astro'); console.log(dirs); let found = false; @@ -62,7 +62,7 @@ describe('Environment Variables', () => { await Promise.all( dirs.map(async (path) => { if (path.endsWith('.js')) { - let js = await fixture.readFile(`/assets/${path}`); + let js = await fixture.readFile(`/_astro/${path}`); if (js.includes('BLUE_BAYOU')) { found = true; } diff --git a/packages/astro/test/astro-scripts.test.js b/packages/astro/test/astro-scripts.test.js index 35e4b8429c32..14b224a69061 100644 --- a/packages/astro/test/astro-scripts.test.js +++ b/packages/astro/test/astro-scripts.test.js @@ -73,7 +73,7 @@ describe('Scripts (hoisted and not)', () => { // test 2: inside assets let entryURL = $('script').attr('src'); - expect(entryURL.includes('assets/')).to.equal(true); + expect(entryURL.includes('_astro/')).to.equal(true); }); it('External page using non-hoist scripts that are not modules are built standalone', async () => { @@ -85,7 +85,7 @@ describe('Scripts (hoisted and not)', () => { // test 2: inside assets let entryURL = $('script').attr('src'); - expect(entryURL.includes('assets/')).to.equal(true); + expect(entryURL.includes('_astro/')).to.equal(true); }); it('Scripts added via Astro.glob are hoisted', async () => { diff --git a/packages/astro/test/build-assets.test.js b/packages/astro/test/build-assets.test.js new file mode 100644 index 000000000000..1fa17e8d20ad --- /dev/null +++ b/packages/astro/test/build-assets.test.js @@ -0,0 +1,171 @@ +import { expect } from 'chai'; +import * as cheerio from 'cheerio'; +import { loadFixture } from './test-utils.js'; +import { preact } from './fixtures/before-hydration/deps.mjs'; +import testAdapter from './test-adapter.js'; + +describe('build assets (static)', () => { + describe('with default configuration', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/build-assets/', + integrations: [preact()], + }); + await fixture.build(); + }); + + it('Populates /_astro directory', async () => { + let files = await fixture.readdir('/_astro'); + expect(files.length).to.be.greaterThan(0); + }); + + it('Defaults to flat /_astro output', async () => { + let files = await fixture.readdir('/_astro'); + for (const file of files) { + expect(file.slice(1)).to.not.contain('/'); + } + }); + + it('emits CSS assets to /_astro', async () => { + let html = await fixture.readFile('/index.html'); + let $ = cheerio.load(html); + + expect($('link[href$=".css"]').attr('href')).to.match(/^\/_astro\//); + }); + + it('emits JS assets to /_astro', async () => { + let html = await fixture.readFile('/index.html'); + let $ = cheerio.load(html); + + const island = $('astro-island'); + expect(island.length).to.eq(1); + expect(island.attr('component-url')).to.match(/^\/_astro\//); + expect(island.attr('renderer-url')).to.match(/^\/_astro\//); + }); + }); + + describe('with custom configuration', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/build-assets/', + integrations: [preact()], + build: { + assets: 'custom-assets', + }, + }); + await fixture.build(); + }); + + it('Populates /custom-assets directory', async () => { + let files = await fixture.readdir('/custom-assets'); + expect(files.length).to.be.greaterThan(0); + }); + + it('emits CSS assets to /custom-assets', async () => { + let html = await fixture.readFile('/index.html'); + let $ = cheerio.load(html); + + expect($('link[href$=".css"]').attr('href')).to.match(/^\/custom-assets\//); + }); + + it('emits JS assets to /custom-assets', async () => { + let html = await fixture.readFile('/index.html'); + let $ = cheerio.load(html); + + const island = $('astro-island'); + expect(island.length).to.eq(1); + expect(island.attr('component-url')).to.match(/^\/custom-assets\//); + expect(island.attr('renderer-url')).to.match(/^\/custom-assets\//); + }); + }); +}); + +describe('build assets (server)', () => { + describe('with default configuration', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/build-assets/', + integrations: [preact()], + adapter: testAdapter(), + }); + await fixture.build(); + }); + + it('Populates /_astro directory', async () => { + let files = await fixture.readdir('/_astro'); + expect(files.length).to.be.greaterThan(0); + }); + + it('Defaults to flat /_astro output', async () => { + let files = await fixture.readdir('/_astro'); + for (const file of files) { + expect(file.slice(1)).to.not.contain('/'); + } + }); + + it('emits CSS assets to /_astro', async () => { + let html = await fixture.readFile('/index.html'); + let $ = cheerio.load(html); + + expect($('link[href$=".css"]').attr('href')).to.match(/^\/_astro\//); + }); + + it('emits JS assets to /_astro', async () => { + let html = await fixture.readFile('/index.html'); + let $ = cheerio.load(html); + + const island = $('astro-island'); + expect(island.length).to.eq(1); + expect(island.attr('component-url')).to.match(/^\/_astro\//); + expect(island.attr('renderer-url')).to.match(/^\/_astro\//); + }); + }); + + describe('with custom configuration', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/build-assets/', + integrations: [preact()], + build: { + assets: 'custom-assets', + }, + adapter: testAdapter(), + }); + await fixture.build(); + }); + + it('Populates /custom-assets directory', async () => { + let files = await fixture.readdir('/custom-assets'); + expect(files.length).to.be.greaterThan(0); + }); + + it('emits CSS assets to /custom-assets', async () => { + let html = await fixture.readFile('/index.html'); + let $ = cheerio.load(html); + + expect($('link[href$=".css"]').attr('href')).to.match(/^\/custom-assets\//); + }); + + it('emits JS assets to /custom-assets', async () => { + let html = await fixture.readFile('/index.html'); + let $ = cheerio.load(html); + + const island = $('astro-island'); + expect(island.length).to.eq(1); + expect(island.attr('component-url')).to.match(/^\/custom-assets\//); + expect(island.attr('renderer-url')).to.match(/^\/custom-assets\//); + }); + }); +}); diff --git a/packages/astro/test/config-vite-css-target.test.js b/packages/astro/test/config-vite-css-target.test.js index b3bce1d070ef..1dc2cce32f63 100644 --- a/packages/astro/test/config-vite-css-target.test.js +++ b/packages/astro/test/config-vite-css-target.test.js @@ -24,7 +24,7 @@ describe('CSS', function () { // get bundled CSS (will be hashed, hence DOM query) html = await fixture.readFile('/index.html'); $ = cheerio.load(html); - const bundledCSSHREF = $('link[rel=stylesheet][href^=/assets/]').attr('href'); + const bundledCSSHREF = $('link[rel=stylesheet][href^=/_astro/]').attr('href'); bundledCSS = (await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/'))) .replace(/\s/g, '') .replace('/n', ''); diff --git a/packages/astro/test/css-no-code-split.test.js b/packages/astro/test/css-no-code-split.test.js index c10a9afddf6a..7fcf7efc208f 100644 --- a/packages/astro/test/css-no-code-split.test.js +++ b/packages/astro/test/css-no-code-split.test.js @@ -14,7 +14,7 @@ describe('vite.build.cssCodeSplit: false', () => { it('loads styles correctly', async () => { let html = await fixture.readFile('/index.html'); let $ = cheerio.load(html); - const cssHref = $('link[rel=stylesheet][href^=/assets/]').attr('href'); - expect(cssHref).to.match(/\/assets\/style\..*?\.css/); + const cssHref = $('link[rel=stylesheet][href^=/_astro/]').attr('href'); + expect(cssHref).to.match(/\/_astro\/style\..*?\.css/); }); }); diff --git a/packages/astro/test/fixtures/build-assets/package.json b/packages/astro/test/fixtures/build-assets/package.json new file mode 100644 index 000000000000..88a91cf2735d --- /dev/null +++ b/packages/astro/test/fixtures/build-assets/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/build-assets", + "dependencies": { + "@astrojs/preact": "workspace:*", + "astro": "workspace:*", + "preact": "^10.11.0" + } +} diff --git a/packages/astro/test/fixtures/build-assets/src/components/SomeComponent.jsx b/packages/astro/test/fixtures/build-assets/src/components/SomeComponent.jsx new file mode 100644 index 000000000000..96fd341bfda0 --- /dev/null +++ b/packages/astro/test/fixtures/build-assets/src/components/SomeComponent.jsx @@ -0,0 +1,6 @@ + +export default function() { + return ( +
Testing
+ ); +} diff --git a/packages/astro/test/fixtures/build-assets/src/pages/index.astro b/packages/astro/test/fixtures/build-assets/src/pages/index.astro new file mode 100644 index 000000000000..4cd6c54da428 --- /dev/null +++ b/packages/astro/test/fixtures/build-assets/src/pages/index.astro @@ -0,0 +1,22 @@ +--- +import SomeComponent from '../components/SomeComponent'; +--- + + + Testing + + +

Hello world!

+ + + + + + + diff --git a/packages/astro/test/postcss.test.js b/packages/astro/test/postcss.test.js index 28de600da4a5..b2d663533252 100644 --- a/packages/astro/test/postcss.test.js +++ b/packages/astro/test/postcss.test.js @@ -18,7 +18,7 @@ describe('PostCSS', function () { // get bundled CSS (will be hashed, hence DOM query) const html = await fixture.readFile('/index.html'); const $ = cheerio.load(html); - const bundledCSSHREF = $('link[rel=stylesheet][href^=/assets/]').attr('href'); + const bundledCSSHREF = $('link[rel=stylesheet][href^=/_astro/]').attr('href'); bundledCSS = (await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/'))) .replace(/\s/g, '') .replace('/n', ''); diff --git a/packages/astro/test/sourcemap.test.js b/packages/astro/test/sourcemap.test.js index 4937e2236448..146db2f2a34d 100644 --- a/packages/astro/test/sourcemap.test.js +++ b/packages/astro/test/sourcemap.test.js @@ -10,7 +10,7 @@ describe('Sourcemap', async () => { }); it('Builds sourcemap', async () => { - const dir = await fixture.readdir('./assets'); + const dir = await fixture.readdir('./_astro'); const counterMap = dir.find((file) => file.match(/^Counter\.\w+\.js\.map$/)); expect(counterMap).to.be.ok; }); diff --git a/packages/astro/test/tailwindcss.test.js b/packages/astro/test/tailwindcss.test.js index 83061248f84c..2a37c6572e2b 100644 --- a/packages/astro/test/tailwindcss.test.js +++ b/packages/astro/test/tailwindcss.test.js @@ -22,7 +22,7 @@ describe('Tailwind', () => { // get bundled CSS (will be hashed, hence DOM query) const html = await fixture.readFile('/index.html'); $ = cheerio.load(html); - const bundledCSSHREF = $('link[rel=stylesheet][href^=/assets/]').attr('href'); + const bundledCSSHREF = $('link[rel=stylesheet][href^=/_astro/]').attr('href'); bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')); }); @@ -61,7 +61,7 @@ describe('Tailwind', () => { it('handles Markdown pages', async () => { const html = await fixture.readFile('/markdown-page/index.html'); const $md = cheerio.load(html); - const bundledCSSHREF = $md('link[rel=stylesheet][href^=/assets/]').attr('href'); + const bundledCSSHREF = $md('link[rel=stylesheet][href^=/_astro/]').attr('href'); const mdBundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')); expect(mdBundledCSS, 'includes used component classes').to.match(/\.bg-purple-600{/); }); @@ -69,7 +69,7 @@ describe('Tailwind', () => { it('handles MDX pages (with integration)', async () => { const html = await fixture.readFile('/mdx-page/index.html'); const $md = cheerio.load(html); - const bundledCSSHREF = $md('link[rel=stylesheet][href^=/assets/]').attr('href'); + const bundledCSSHREF = $md('link[rel=stylesheet][href^=/_astro/]').attr('href'); const mdBundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')); expect(mdBundledCSS, 'includes used component classes').to.match(/\.bg-purple-600{/); }); diff --git a/packages/integrations/deno/src/index.ts b/packages/integrations/deno/src/index.ts index 68452be94a33..82da151888cd 100644 --- a/packages/integrations/deno/src/index.ts +++ b/packages/integrations/deno/src/index.ts @@ -7,6 +7,7 @@ import { fileURLToPath } from 'url'; interface BuildConfig { server: URL; serverEntry: string; + assets: string; } interface Options { @@ -86,7 +87,7 @@ export default function createIntegration(args?: Options): AstroIntegration { // Remove chunks, if they exist. Since we have bundled via esbuild these chunks are trash. try { const chunkFileNames = - _vite?.build?.rollupOptions?.output?.chunkFileNames ?? 'assets/chunks/chunk.[hash].mjs'; + _vite?.build?.rollupOptions?.output?.chunkFileNames ?? `chunks/chunk.[hash].mjs`; const chunkPath = npath.dirname(chunkFileNames); const chunksDirUrl = new URL(chunkPath + '/', _buildConfig.server); await fs.promises.rm(chunksDirUrl, { recursive: true, force: true }); diff --git a/packages/integrations/image/src/build/ssg.ts b/packages/integrations/image/src/build/ssg.ts index e7cde2185e44..4d5087011d04 100644 --- a/packages/integrations/image/src/build/ssg.ts +++ b/packages/integrations/image/src/build/ssg.ts @@ -172,10 +172,10 @@ export async function ssgBuild({ let outputFileURL: URL; if (isRemoteImage(src)) { - outputFileURL = new URL(path.join('./assets', path.basename(filename)), outDir); + outputFileURL = new URL(path.join(`./${config.build.assets}`, path.basename(filename)), outDir); outputFile = fileURLToPath(outputFileURL); } else { - outputFileURL = new URL(path.join('./assets', filename), outDir); + outputFileURL = new URL(path.join(`./${config.build.assets}`, filename), outDir); outputFile = fileURLToPath(outputFileURL); } diff --git a/packages/integrations/image/src/index.ts b/packages/integrations/image/src/index.ts index cc8093981d24..3767003f3223 100644 --- a/packages/integrations/image/src/index.ts +++ b/packages/integrations/image/src/index.ts @@ -21,6 +21,7 @@ const UNSUPPORTED_ADAPTERS = new Set([ interface BuildConfig { client: URL; server: URL; + assets: string; } interface ImageIntegration { @@ -130,7 +131,7 @@ export default function integration(options: IntegrationOptions = {}): AstroInte // Doing this here makes sure that base is ignored when building // staticImages to /dist, but the rendered HTML will include the // base prefix for `src`. - return prependForwardSlash(joinPaths(_config.base, 'assets', filename)); + return prependForwardSlash(joinPaths(_config.base, _buildConfig.assets, filename)); } // Helpers for building static images should only be available for SSG @@ -146,7 +147,7 @@ export default function integration(options: IntegrationOptions = {}): AstroInte // For the Squoosh service, copy all wasm files to dist/chunks. // Because the default loader is dynamically imported (above), // Vite will bundle squoosh to dist/chunks and expect to find the wasm files there - await copyWasmFiles(new URL('./assets/chunks', dir)); + await copyWasmFiles(new URL('./chunks', dir)); } if (loader && 'transform' in loader && staticImages.size > 0) { @@ -166,7 +167,7 @@ export default function integration(options: IntegrationOptions = {}): AstroInte }, 'astro:build:ssr': async () => { if (resolvedOptions.serviceEntryPoint === '@astrojs/image/squoosh') { - await copyWasmFiles(new URL('./assets/chunks/', _buildConfig.server)); + await copyWasmFiles(new URL('./chunks/', _buildConfig.server)); } }, }, diff --git a/packages/integrations/image/test/background-color-image-ssr.test.js b/packages/integrations/image/test/background-color-image-ssr.test.js index 148c57a5b923..6bc5f333f303 100644 --- a/packages/integrations/image/test/background-color-image-ssr.test.js +++ b/packages/integrations/image/test/background-color-image-ssr.test.js @@ -23,7 +23,7 @@ describe('SSR image with background', function () { f: 'jpeg', w: '256', h: '256', - href: /^\/assets\/file-icon.\w{8}.png/, + href: /^\/_astro\/file-icon.\w{8}.png/, bg: 'dimgray', }, }, @@ -34,7 +34,7 @@ describe('SSR image with background', function () { f: 'jpeg', w: '256', h: '256', - href: /^\/assets\/file-icon.\w{8}.png/, + href: /^\/_astro\/file-icon.\w{8}.png/, bg: '#696969', }, }, @@ -45,7 +45,7 @@ describe('SSR image with background', function () { f: 'jpeg', w: '256', h: '256', - href: /^\/assets\/file-icon.\w{8}.png/, + href: /^\/_astro\/file-icon.\w{8}.png/, bg: '#666', }, }, @@ -56,7 +56,7 @@ describe('SSR image with background', function () { f: 'jpeg', w: '256', h: '256', - href: /^\/assets\/file-icon.\w{8}.png/, + href: /^\/_astro\/file-icon.\w{8}.png/, bg: 'rgb(105,105,105)', }, }, @@ -67,7 +67,7 @@ describe('SSR image with background', function () { f: 'jpeg', w: '256', h: '256', - href: /^\/assets\/file-icon.\w{8}.png/, + href: /^\/_astro\/file-icon.\w{8}.png/, bg: 'rgb(105, 105, 105)', }, }, @@ -78,7 +78,7 @@ describe('SSR image with background', function () { f: 'jpeg', w: '256', h: '256', - href: /^\/assets\/file-icon.\w{8}.png/, + href: /^\/_astro\/file-icon.\w{8}.png/, bg: 'rgb(105,105,105,0.5)', }, }, @@ -89,7 +89,7 @@ describe('SSR image with background', function () { f: 'jpeg', w: '256', h: '256', - href: /^\/assets\/file-icon.\w{8}.png/, + href: /^\/_astro\/file-icon.\w{8}.png/, bg: 'rgb(105, 105, 105, 0.5)', }, }, diff --git a/packages/integrations/image/test/get-image.test.js b/packages/integrations/image/test/get-image.test.js index 6e73627355a5..74fa67feb6f7 100644 --- a/packages/integrations/image/test/get-image.test.js +++ b/packages/integrations/image/test/get-image.test.js @@ -11,7 +11,7 @@ describe('getImage', function () { }); it('Remote images works', async () => { - const assets = await fixture.readdir('/assets'); + const assets = await fixture.readdir('/_astro'); expect(assets).to.have.a.lengthOf(1); }); }); diff --git a/packages/integrations/image/test/image-ssg.test.js b/packages/integrations/image/test/image-ssg.test.js index 2b724c1e3aa3..ce91c00ea411 100644 --- a/packages/integrations/image/test/image-ssg.test.js +++ b/packages/integrations/image/test/image-ssg.test.js @@ -220,43 +220,43 @@ describe('SSG images - build', function () { { title: 'Local images', id: '#social-jpg', - regex: /^\/assets\/social.\w{8}_\w{4,10}.jpg/, + regex: /^\/_astro\/social.\w{8}_\w{4,10}.jpg/, size: { width: 506, height: 253, type: 'jpg' }, }, { title: 'Filename with spaces', id: '#spaces', - regex: /^\/assets\/introducing astro.\w{8}_\w{4,10}.webp/, + regex: /^\/_astro\/introducing astro.\w{8}_\w{4,10}.webp/, size: { width: 768, height: 414, type: 'webp' }, }, { title: 'Inline imports', id: '#inline', - regex: /^\/assets\/social.\w{8}_\w{4,10}.jpg/, + regex: /^\/_astro\/social.\w{8}_\w{4,10}.jpg/, size: { width: 506, height: 253, type: 'jpg' }, }, { title: 'Remote images', id: '#google', - regex: /^\/assets\/googlelogo_color_272x92dp_\w{4,10}.webp/, + regex: /^\/_astro\/googlelogo_color_272x92dp_\w{4,10}.webp/, size: { width: 544, height: 184, type: 'webp' }, }, { title: 'Remote without file extension', id: '#ipsum', - regex: /^\/assets\/200x300_\w{4,10}/, + regex: /^\/_astro\/200x300_\w{4,10}/, size: { width: 200, height: 300, type: 'jpg' }, }, { title: 'Public images', id: '#hero', - regex: /^\/assets\/hero_\w{4,10}.webp/, + regex: /^\/_astro\/hero_\w{4,10}.webp/, size: { width: 768, height: 414, type: 'webp' }, }, { title: 'Remote images', id: '#bg-color', - regex: /^\/assets\/googlelogo_color_272x92dp_\w{4,10}.jpeg/, + regex: /^\/_astro\/googlelogo_color_272x92dp_\w{4,10}.jpeg/, size: { width: 544, height: 184, type: 'jpg' }, }, ].forEach(({ title, id, regex, size }) => { @@ -302,43 +302,43 @@ describe('SSG images with subpath - build', function () { { title: 'Local images', id: '#social-jpg', - regex: /^\/docs\/assets\/social.\w{8}_\w{4,10}.jpg/, + regex: /^\/docs\/_astro\/social.\w{8}_\w{4,10}.jpg/, size: { width: 506, height: 253, type: 'jpg' }, }, { title: 'Filename with spaces', id: '#spaces', - regex: /^\/docs\/assets\/introducing astro.\w{8}_\w{4,10}.webp/, + regex: /^\/docs\/_astro\/introducing astro.\w{8}_\w{4,10}.webp/, size: { width: 768, height: 414, type: 'webp' }, }, { title: 'Inline imports', id: '#inline', - regex: /^\/docs\/assets\/social.\w{8}_\w{4,10}.jpg/, + regex: /^\/docs\/_astro\/social.\w{8}_\w{4,10}.jpg/, size: { width: 506, height: 253, type: 'jpg' }, }, { title: 'Remote images', id: '#google', - regex: /^\/docs\/assets\/googlelogo_color_272x92dp_\w{4,10}.webp/, + regex: /^\/docs\/_astro\/googlelogo_color_272x92dp_\w{4,10}.webp/, size: { width: 544, height: 184, type: 'webp' }, }, { title: 'Remote without file extension', id: '#ipsum', - regex: /^\/docs\/assets\/200x300_\w{4,10}/, + regex: /^\/docs\/_astro\/200x300_\w{4,10}/, size: { width: 200, height: 300, type: 'jpg' }, }, { title: 'Public images', id: '#hero', - regex: /^\/docs\/assets\/hero_\w{4,10}.webp/, + regex: /^\/docs\/_astro\/hero_\w{4,10}.webp/, size: { width: 768, height: 414, type: 'webp' }, }, { title: 'Remote images', id: '#bg-color', - regex: /^\/docs\/assets\/googlelogo_color_272x92dp_\w{4,10}.jpeg/, + regex: /^\/docs\/_astro\/googlelogo_color_272x92dp_\w{4,10}.jpeg/, size: { width: 544, height: 184, type: 'jpg' }, }, ].forEach(({ title, id, regex, size }) => { diff --git a/packages/integrations/image/test/image-ssr-build.test.js b/packages/integrations/image/test/image-ssr-build.test.js index 65c1b30839ee..4b985c0ad9e9 100644 --- a/packages/integrations/image/test/image-ssr-build.test.js +++ b/packages/integrations/image/test/image-ssr-build.test.js @@ -20,19 +20,19 @@ describe('SSR images - build', async function () { title: 'Local images', id: '#social-jpg', url: '/_image', - query: { f: 'jpg', w: '506', h: '253', href: /^\/assets\/social.\w{8}.jpg/ }, + query: { f: 'jpg', w: '506', h: '253', href: /^\/_astro\/social.\w{8}.jpg/ }, }, { title: 'Filename with spaces', id: '#spaces', url: '/_image', - query: { f: 'webp', w: '768', h: '414', href: /^\/assets\/introducing astro.\w{8}.jpg/ }, + query: { f: 'webp', w: '768', h: '414', href: /^\/_astro\/introducing astro.\w{8}.jpg/ }, }, { title: 'Inline imports', id: '#inline', url: '/_image', - query: { f: 'jpg', w: '506', h: '253', href: /^\/assets\/social.\w{8}.jpg/ }, + query: { f: 'jpg', w: '506', h: '253', href: /^\/_astro\/social.\w{8}.jpg/ }, }, { title: 'Remote images', @@ -131,7 +131,7 @@ describe('SSR images with subpath - build', function () { title: 'Local images', id: '#social-jpg', url: '/_image', - query: { f: 'jpg', w: '506', h: '253', href: /^\/docs\/assets\/social.\w{8}.jpg/ }, + query: { f: 'jpg', w: '506', h: '253', href: /^\/docs\/_astro\/social.\w{8}.jpg/ }, }, { title: 'Filename with spaces', @@ -141,14 +141,14 @@ describe('SSR images with subpath - build', function () { f: 'webp', w: '768', h: '414', - href: /^\/docs\/assets\/introducing astro.\w{8}.jpg/, + href: /^\/docs\/_astro\/introducing astro.\w{8}.jpg/, }, }, { title: 'Inline imports', id: '#inline', url: '/_image', - query: { f: 'jpg', w: '506', h: '253', href: /^\/docs\/assets\/social.\w{8}.jpg/ }, + query: { f: 'jpg', w: '506', h: '253', href: /^\/docs\/_astro\/social.\w{8}.jpg/ }, }, { title: 'Remote images', diff --git a/packages/integrations/image/test/picture-ssg.test.js b/packages/integrations/image/test/picture-ssg.test.js index b92e6f3665ed..e5da7ba1aa27 100644 --- a/packages/integrations/image/test/picture-ssg.test.js +++ b/packages/integrations/image/test/picture-ssg.test.js @@ -211,42 +211,42 @@ describe('SSG pictures - build', function () { { title: 'Local images', id: '#social-jpg', - regex: /^\/assets\/social.\w{8}_\w{4,10}.jpg/, + regex: /^\/_astro\/social.\w{8}_\w{4,10}.jpg/, size: { width: 506, height: 253, type: 'jpg' }, alt: 'Social image', }, { title: 'Filename with spaces', id: '#spaces', - regex: /^\/assets\/introducing astro.\w{8}_\w{4,10}.jpg/, + regex: /^\/_astro\/introducing astro.\w{8}_\w{4,10}.jpg/, size: { width: 768, height: 414, type: 'jpg' }, alt: 'spaces', }, { title: 'Inline images', id: '#inline', - regex: /^\/assets\/social.\w{8}_\w{4,10}.jpg/, + regex: /^\/_astro\/social.\w{8}_\w{4,10}.jpg/, size: { width: 506, height: 253, type: 'jpg' }, alt: 'Inline social image', }, { title: 'Remote images', id: '#google', - regex: /^\/assets\/googlelogo_color_272x92dp_\w{4,10}.png/, + regex: /^\/_astro\/googlelogo_color_272x92dp_\w{4,10}.png/, size: { width: 544, height: 184, type: 'png' }, alt: 'Google logo', }, { title: 'Remote without file extension', id: '#ipsum', - regex: /^\/assets\/200x300_\w{4,10}/, + regex: /^\/_astro\/200x300_\w{4,10}/, size: { width: 200, height: 300, type: 'jpg' }, alt: 'ipsum', }, { title: 'Public images', id: '#hero', - regex: /^\/assets\/hero_\w{4,10}.jpg/, + regex: /^\/_astro\/hero_\w{4,10}.jpg/, size: { width: 768, height: 414, type: 'jpg' }, alt: 'Hero image', }, @@ -318,35 +318,35 @@ describe('SSG pictures with subpath - build', function () { { title: 'Local images', id: '#social-jpg', - regex: /^\/docs\/assets\/social.\w{8}_\w{4,10}.jpg/, + regex: /^\/docs\/_astro\/social.\w{8}_\w{4,10}.jpg/, size: { width: 506, height: 253, type: 'jpg' }, alt: 'Social image', }, { title: 'Inline images', id: '#inline', - regex: /^\/docs\/assets\/social.\w{8}_\w{4,10}.jpg/, + regex: /^\/docs\/_astro\/social.\w{8}_\w{4,10}.jpg/, size: { width: 506, height: 253, type: 'jpg' }, alt: 'Inline social image', }, { title: 'Remote images', id: '#google', - regex: /^\/docs\/assets\/googlelogo_color_272x92dp_\w{4,10}.png/, + regex: /^\/docs\/_astro\/googlelogo_color_272x92dp_\w{4,10}.png/, size: { width: 544, height: 184, type: 'png' }, alt: 'Google logo', }, { title: 'Remote without file extension', id: '#ipsum', - regex: /^\/docs\/assets\/200x300_\w{4,10}/, + regex: /^\/docs\/_astro\/200x300_\w{4,10}/, size: { width: 200, height: 300, type: 'jpg' }, alt: 'ipsum', }, { title: 'Public images', id: '#hero', - regex: /^\/docs\/assets\/hero_\w{4,10}.jpg/, + regex: /^\/docs\/_astro\/hero_\w{4,10}.jpg/, size: { width: 768, height: 414, type: 'jpg' }, alt: 'Hero image', }, diff --git a/packages/integrations/image/test/picture-ssr-build.test.js b/packages/integrations/image/test/picture-ssr-build.test.js index c5852f822add..8da476166f13 100644 --- a/packages/integrations/image/test/picture-ssr-build.test.js +++ b/packages/integrations/image/test/picture-ssr-build.test.js @@ -20,21 +20,21 @@ describe('SSR pictures - build', function () { title: 'Local images', id: '#social-jpg', url: '/_image', - query: { f: 'jpg', w: '506', h: '253', href: /^\/assets\/social.\w{8}.jpg/ }, + query: { f: 'jpg', w: '506', h: '253', href: /^\/_astro\/social.\w{8}.jpg/ }, alt: 'Social image', }, { title: 'Filename with spaces', id: '#spaces', url: '/_image', - query: { w: '768', h: '414', f: 'jpg', href: /^\/assets\/introducing astro.\w{8}.jpg/ }, + query: { w: '768', h: '414', f: 'jpg', href: /^\/_astro\/introducing astro.\w{8}.jpg/ }, alt: 'spaces', }, { title: 'Inline imports', id: '#inline', url: '/_image', - query: { f: 'jpg', w: '506', h: '253', href: /^\/assets\/social.\w{8}.jpg/ }, + query: { f: 'jpg', w: '506', h: '253', href: /^\/_astro\/social.\w{8}.jpg/ }, alt: 'Inline social image', }, { @@ -131,21 +131,21 @@ describe('SSR pictures with subpath - build', function () { title: 'Local images', id: '#social-jpg', url: '/_image', - query: { f: 'jpg', w: '506', h: '253', href: /^\/docs\/assets\/social.\w{8}.jpg/ }, + query: { f: 'jpg', w: '506', h: '253', href: /^\/docs\/_astro\/social.\w{8}.jpg/ }, alt: 'Social image', }, { title: 'Filename with spaces', id: '#spaces', url: '/_image', - query: { w: '768', h: '414', f: 'jpg', href: /^\/docs\/assets\/introducing astro.\w{8}.jpg/ }, + query: { w: '768', h: '414', f: 'jpg', href: /^\/docs\/_astro\/introducing astro.\w{8}.jpg/ }, alt: 'spaces', }, { title: 'Inline imports', id: '#inline', url: '/_image', - query: { f: 'jpg', w: '506', h: '253', href: /^\/docs\/assets\/social.\w{8}.jpg/ }, + query: { f: 'jpg', w: '506', h: '253', href: /^\/docs\/_astro\/social.\w{8}.jpg/ }, alt: 'Inline social image', }, { diff --git a/packages/integrations/image/test/rotation.test.js b/packages/integrations/image/test/rotation.test.js index 49fe198b1f56..baa412e2cd03 100644 --- a/packages/integrations/image/test/rotation.test.js +++ b/packages/integrations/image/test/rotation.test.js @@ -32,7 +32,7 @@ describe('Image rotation', function () { it('Landscape images', () => { for (let i = 0; i < 9; i++) { const image = $(`#landscape-${i}`); - const regex = new RegExp(`\^/assets\/Landscape_${i}.\\w{8}_\\w{4,10}.jpg`); + const regex = new RegExp(`\^/_astro\/Landscape_${i}.\\w{8}_\\w{4,10}.jpg`); expect(image.attr('src')).to.match(regex); expect(image.attr('width')).to.equal('1800'); @@ -49,7 +49,7 @@ describe('Image rotation', function () { it('Portait images', () => { for (let i = 0; i < 9; i++) { const image = $(`#portrait-${i}`); - const regex = new RegExp(`\^/assets\/Portrait_${i}.\\w{8}_\\w{4,10}.jpg`); + const regex = new RegExp(`\^/_astro\/Portrait_${i}.\\w{8}_\\w{4,10}.jpg`); expect(image.attr('src')).to.match(regex); expect(image.attr('width')).to.equal('1200'); diff --git a/packages/integrations/image/test/with-mdx.test.js b/packages/integrations/image/test/with-mdx.test.js index 963cd9b970bb..ec7de4f62696 100644 --- a/packages/integrations/image/test/with-mdx.test.js +++ b/packages/integrations/image/test/with-mdx.test.js @@ -28,31 +28,31 @@ describe('Images in MDX - build', function () { { title: 'Local images', id: '#social-jpg', - regex: /^\/assets\/social.\w{8}_\w{4,10}.jpg/, + regex: /^\/_astro\/social.\w{8}_\w{4,10}.jpg/, size: { width: 506, height: 253, type: 'jpg' }, }, { title: 'Inline imports', id: '#inline', - regex: /^\/assets\/social.\w{8}_\w{4,10}.jpg/, + regex: /^\/_astro\/social.\w{8}_\w{4,10}.jpg/, size: { width: 506, height: 253, type: 'jpg' }, }, { title: 'Remote images', id: '#google', - regex: /^\/assets\/googlelogo_color_272x92dp_\w{4,10}.webp/, + regex: /^\/_astro\/googlelogo_color_272x92dp_\w{4,10}.webp/, size: { width: 544, height: 184, type: 'webp' }, }, { title: 'Public images', id: '#hero', - regex: /^\/assets\/hero_\w{4,10}.webp/, + regex: /^\/_astro\/hero_\w{4,10}.webp/, size: { width: 768, height: 414, type: 'webp' }, }, { title: 'Background color', id: '#bg-color', - regex: /^\/assets\/googlelogo_color_272x92dp_\w{4,10}.jpeg/, + regex: /^\/_astro\/googlelogo_color_272x92dp_\w{4,10}.jpeg/, size: { width: 544, height: 184, type: 'jpg' }, }, ].forEach(({ title, id, regex, size }) => { diff --git a/packages/integrations/netlify/src/integration-edge-functions.ts b/packages/integrations/netlify/src/integration-edge-functions.ts index 48be316d9d18..a05f8f3a0fff 100644 --- a/packages/integrations/netlify/src/integration-edge-functions.ts +++ b/packages/integrations/netlify/src/integration-edge-functions.ts @@ -10,6 +10,7 @@ interface BuildConfig { server: URL; client: URL; serverEntry: string; + assets: string; } const SHIM = `globalThis.process = { @@ -100,7 +101,7 @@ async function bundleServerEntry({ serverEntry, server }: BuildConfig, vite: any // Remove chunks, if they exist. Since we have bundled via esbuild these chunks are trash. try { const chunkFileNames = - vite?.build?.rollupOptions?.output?.chunkFileNames ?? 'assets/chunks/chunk.[hash].mjs'; + vite?.build?.rollupOptions?.output?.chunkFileNames ?? `chunks/chunk.[hash].mjs`; const chunkPath = npath.dirname(chunkFileNames); const chunksDirUrl = new URL(chunkPath + '/', server); await fs.promises.rm(chunksDirUrl, { recursive: true, force: true }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f26b44e6742..47bd78014a96 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1582,6 +1582,16 @@ importers: astro: link:../../.. preact: 10.11.3 + packages/astro/test/fixtures/build-assets: + specifiers: + '@astrojs/preact': workspace:* + astro: workspace:* + preact: ^10.11.0 + dependencies: + '@astrojs/preact': link:../../../../integrations/preact + astro: link:../../.. + preact: 10.11.3 + packages/astro/test/fixtures/client-address: specifiers: astro: workspace:*