diff --git a/packages/astro/src/cli/build/index.ts b/packages/astro/src/cli/build/index.ts index 15ff584317d9..63c2f0d0e1c5 100644 --- a/packages/astro/src/cli/build/index.ts +++ b/packages/astro/src/cli/build/index.ts @@ -15,6 +15,10 @@ export async function build({ flags }: BuildOptions) { tables: { Flags: [ ['--outDir ', `Specify the output directory for the build.`], + [ + '--force', + 'Clear the content layer and content collection cache, forcing a full rebuild.', + ], ['--help (-h)', 'See all available flags.'], ], }, diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 9989c01548ae..a55667c421dd 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -11,6 +11,7 @@ import type { RuntimeMode, } from '../../@types/astro.js'; import { injectImageEndpoint } from '../../assets/endpoint/config.js'; +import { DATA_STORE_FILE } from '../../content/consts.js'; import { telemetry } from '../../events/index.js'; import { eventCliSession } from '../../events/session.js'; import { @@ -67,12 +68,20 @@ export default async function build( const logger = createNodeLogger(inlineConfig); const { userConfig, astroConfig } = await resolveConfig(inlineConfig, 'build'); telemetry.record(eventCliSession('build', userConfig)); - if (astroConfig.experimental.contentCollectionCache && options.force) { - const contentCacheDir = new URL('./content/', astroConfig.cacheDir); - if (fs.existsSync(contentCacheDir)) { - logger.debug('content', 'clearing content cache'); - await fs.promises.rm(contentCacheDir, { force: true, recursive: true }); - logger.warn('content', 'content cache cleared (force)'); + if (options.force) { + if (astroConfig.experimental.contentCollectionCache) { + const contentCacheDir = new URL('./content/', astroConfig.cacheDir); + if (fs.existsSync(contentCacheDir)) { + logger.debug('content', 'clearing content cache'); + await fs.promises.rm(contentCacheDir, { force: true, recursive: true }); + logger.warn('content', 'content cache cleared (force)'); + } + } + const dataStore = new URL(DATA_STORE_FILE, astroConfig.cacheDir); + if (fs.existsSync(dataStore)) { + logger.debug('content', 'clearing data store'); + await fs.promises.rm(dataStore, { force: true }); + logger.warn('content', 'data store cleared (force)'); } } diff --git a/packages/astro/test/content-layer.test.js b/packages/astro/test/content-layer.test.js index e7b607a8c574..be856ba4d3f0 100644 --- a/packages/astro/test/content-layer.test.js +++ b/packages/astro/test/content-layer.test.js @@ -20,7 +20,7 @@ describe('Content Layer', () => { await fs .unlink(new URL('./node_modules/.astro/data-store.json', fixture.config.root)) .catch(() => {}); - await fixture.build({}); + await fixture.build(); const rawJson = await fixture.readFile('/collections.json'); json = JSON.parse(rawJson); }); @@ -133,6 +133,21 @@ describe('Content Layer', () => { id: 'tabby', }); }); + + it('updates the store on new builds', async () => { + assert.equal(json.increment.data.lastValue, 1); + await fixture.build(); + const newJson = JSON.parse(await fixture.readFile('/collections.json')); + assert.equal(newJson.increment.data.lastValue, 2); + }); + + it('clears the store on new build with force flag', async () => { + let newJson = JSON.parse(await fixture.readFile('/collections.json')); + assert.equal(newJson.increment.data.lastValue, 2); + await fixture.build({}, { force: true }); + newJson = JSON.parse(await fixture.readFile('/collections.json')); + assert.equal(newJson.increment.data.lastValue, 1); + }); }); describe('Dev', () => { diff --git a/packages/astro/test/fixtures/content-layer/src/content/config.ts b/packages/astro/test/fixtures/content-layer/src/content/config.ts index 38b6bc52644f..fd9dd5ad7c9a 100644 --- a/packages/astro/test/fixtures/content-layer/src/content/config.ts +++ b/packages/astro/test/fixtures/content-layer/src/content/config.ts @@ -69,21 +69,21 @@ const cats = defineCollection({ }), }); - // Absolute paths should also work const absoluteRoot = new URL('../../content-outside-src', import.meta.url); const spacecraft = defineCollection({ type: 'experimental_content', loader: glob({ pattern: '*.md', base: absoluteRoot }), - schema: ({ image }) => z.object({ - title: z.string(), - description: z.string(), - publishedDate: z.string(), - tags: z.array(z.string()), - heroImage: image().optional(), - cat: reference('cats').optional(), - }), + schema: ({ image }) => + z.object({ + title: z.string(), + description: z.string(), + publishedDate: z.string(), + tags: z.array(z.string()), + heroImage: image().optional(), + cat: reference('cats').optional(), + }), }); const numbers = defineCollection({ @@ -91,4 +91,24 @@ const numbers = defineCollection({ loader: glob({ pattern: 'src/data/glob-data/*', base: '.' }), }); -export const collections = { blog, dogs, cats, numbers, spacecraft }; +const increment = defineCollection({ + type: 'experimental_data', + loader: { + name: 'increment-loader', + load: async ({ store }) => { + const entry = store.get<{ lastValue: number }>('value'); + const lastValue: number = entry?.data.lastValue ?? 0; + store.set({ + id: 'value', + data: { + lastValue: lastValue + 1, + }, + }); + }, + }, + schema: z.object({ + lastValue: z.number(), + }), +}); + +export const collections = { blog, dogs, cats, numbers, spacecraft, increment }; diff --git a/packages/astro/test/fixtures/content-layer/src/pages/collections.json.js b/packages/astro/test/fixtures/content-layer/src/pages/collections.json.js index 43e4df23005c..618377dae4ff 100644 --- a/packages/astro/test/fixtures/content-layer/src/pages/collections.json.js +++ b/packages/astro/test/fixtures/content-layer/src/pages/collections.json.js @@ -11,5 +11,7 @@ export async function GET() { const entryWithReference = await getEntry('spacecraft', 'columbia-copy') const referencedEntry = await getEntry(entryWithReference.data.cat) - return Response.json({ customLoader, fileLoader, dataEntry, simpleLoader, entryWithReference, referencedEntry }); + const increment = await getEntry('increment', 'value') + + return Response.json({ customLoader, fileLoader, dataEntry, simpleLoader, entryWithReference, referencedEntry, increment }); } diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js index acb2f1e5cba0..d3cdb30d2527 100644 --- a/packages/astro/test/test-utils.js +++ b/packages/astro/test/test-utils.js @@ -157,9 +157,12 @@ export async function loadFixture(inlineConfig) { let devServer; return { - build: async (extraInlineConfig = {}) => { + build: async (extraInlineConfig = {}, options = {}) => { process.env.NODE_ENV = 'production'; - return build(mergeConfig(inlineConfig, extraInlineConfig), { teardownCompiler: false }); + return build(mergeConfig(inlineConfig, extraInlineConfig), { + teardownCompiler: false, + ...options, + }); }, sync, check: async (opts) => {