From 8679954bf647529e0f2134053866fc507e64c5e3 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 30 Sep 2024 16:04:11 +0200 Subject: [PATCH] feat(next): remove CCC (#12081) Co-authored-by: Sarah Rainsberger --- .changeset/wise-carrots-float.md | 29 + .../vite-plugin-content-virtual-mod.ts | 110 +--- packages/astro/src/core/build/index.ts | 9 - packages/astro/src/core/build/internal.ts | 5 - .../astro/src/core/build/plugins/index.ts | 2 - .../src/core/build/plugins/plugin-content.ts | 524 ------------------ packages/astro/src/core/build/static-build.ts | 60 +- packages/astro/src/core/config/schema.ts | 5 - packages/astro/src/core/util.ts | 8 - packages/astro/src/integrations/hooks.ts | 10 +- packages/astro/src/types/public/config.ts | 19 - .../astro/src/types/public/integrations.ts | 1 - packages/astro/src/vite-plugin-astro/index.ts | 7 - ...ntal-content-collection-references.test.js | 166 ------ ...ent-collections-cache-invalidation.test.js | 137 ----- ...collections-css-inline-stylesheets.test.js | 303 ---------- ...imental-content-collections-render.test.js | 152 ----- .../experimental-content-collections.test.js | 392 ------------- 18 files changed, 59 insertions(+), 1880 deletions(-) create mode 100644 .changeset/wise-carrots-float.md delete mode 100644 packages/astro/src/core/build/plugins/plugin-content.ts delete mode 100644 packages/astro/test/experimental-content-collection-references.test.js delete mode 100644 packages/astro/test/experimental-content-collections-cache-invalidation.test.js delete mode 100644 packages/astro/test/experimental-content-collections-css-inline-stylesheets.test.js delete mode 100644 packages/astro/test/experimental-content-collections-render.test.js delete mode 100644 packages/astro/test/experimental-content-collections.test.js diff --git a/.changeset/wise-carrots-float.md b/.changeset/wise-carrots-float.md new file mode 100644 index 000000000000..a7d43f1f1b0c --- /dev/null +++ b/.changeset/wise-carrots-float.md @@ -0,0 +1,29 @@ +--- +'astro': minor +--- + +Removes the experimental `contentCollectionsCache` introduced in `3.5.0`. + +Astro Content Layer API independently solves some of the caching and performance issues with legacy content collections that this strategy attempted to address. This feature has been replaced with continued work on improvements to the content layer. If you were using this experimental feature, you must now remove the flag from your Astro config as it no longer exists: + +```diff +export default defineConfig({ + experimental: { +- contentCollectionsCache: true + } +}) +``` + +The `cacheManifest` boolean argument is no longer passed to the `astro:build:done` integration hook: + +```diff +const integration = { + name: "my-integration", + hooks: { + "astro:build:done": ({ +- cacheManifest, + logger + }) => {} + } +} +``` diff --git a/packages/astro/src/content/vite-plugin-content-virtual-mod.ts b/packages/astro/src/content/vite-plugin-content-virtual-mod.ts index 0c2448493cea..cc9486715fbc 100644 --- a/packages/astro/src/content/vite-plugin-content-virtual-mod.ts +++ b/packages/astro/src/content/vite-plugin-content-virtual-mod.ts @@ -1,13 +1,12 @@ +import { dataToEsm } from '@rollup/pluginutils'; +import glob from 'fast-glob'; import nodeFs from 'node:fs'; import { extname } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; -import { dataToEsm } from '@rollup/pluginutils'; -import glob from 'fast-glob'; import pLimit from 'p-limit'; import type { Plugin } from 'vite'; -import { encodeName } from '../core/build/util.js'; import { AstroError, AstroErrorData } from '../core/errors/index.js'; -import { appendForwardSlash, removeFileExtension } from '../core/path.js'; +import { appendForwardSlash } from '../core/path.js'; import { rootRelativePath } from '../core/viteUtils.js'; import type { AstroSettings } from '../types/astro.js'; import type { AstroPluginMetadata } from '../vite-plugin-astro/index.js'; @@ -52,7 +51,6 @@ export function astroContentVirtualModPlugin({ fs, }: AstroContentVirtualModPluginParams): Plugin { let IS_DEV = false; - const IS_SERVER = settings.buildOutput === 'server'; let dataStoreFile: URL; return { name: 'astro-content-virtual-mod-plugin', @@ -63,15 +61,7 @@ export function astroContentVirtualModPlugin({ }, async resolveId(id) { if (id === VIRTUAL_MODULE_ID) { - if (!settings.config.experimental.contentCollectionCache) { - return RESOLVED_VIRTUAL_MODULE_ID; - } - if (IS_DEV || IS_SERVER) { - return RESOLVED_VIRTUAL_MODULE_ID; - } else { - // For SSG (production), we will build this file ourselves - return { id: RESOLVED_VIRTUAL_MODULE_ID, external: true }; - } + return RESOLVED_VIRTUAL_MODULE_ID; } if (id === DATA_STORE_VIRTUAL_ID) { return RESOLVED_DATA_STORE_VIRTUAL_ID; @@ -117,8 +107,6 @@ export function astroContentVirtualModPlugin({ settings, fs, lookupMap, - IS_DEV, - IS_SERVER, isClient, }); @@ -167,17 +155,6 @@ export function astroContentVirtualModPlugin({ return fs.readFileSync(modules, 'utf-8'); } }, - renderChunk(code, chunk) { - if (!settings.config.experimental.contentCollectionCache) { - return; - } - if (code.includes(RESOLVED_VIRTUAL_MODULE_ID)) { - const depth = chunk.fileName.split('/').length - 1; - const prefix = depth > 0 ? '../'.repeat(depth) : './'; - return code.replaceAll(RESOLVED_VIRTUAL_MODULE_ID, `${prefix}content/entry.mjs`); - } - }, - configureServer(server) { const dataStorePath = fileURLToPath(dataStoreFile); @@ -214,15 +191,11 @@ export function astroContentVirtualModPlugin({ export async function generateContentEntryFile({ settings, lookupMap, - IS_DEV, - IS_SERVER, isClient, }: { settings: AstroSettings; fs: typeof nodeFs; lookupMap: ContentLookupMap; - IS_DEV: boolean; - IS_SERVER: boolean; isClient: boolean; }) { const contentPaths = getContentPaths(settings.config); @@ -231,33 +204,23 @@ export async function generateContentEntryFile({ let contentEntryGlobResult: string; let dataEntryGlobResult: string; let renderEntryGlobResult: string; - if (IS_DEV || IS_SERVER || !settings.config.experimental.contentCollectionCache) { - const contentEntryConfigByExt = getEntryConfigByExtMap(settings.contentEntryTypes); - const contentEntryExts = [...contentEntryConfigByExt.keys()]; - const dataEntryExts = getDataEntryExts(settings); - const createGlob = (value: string[], flag: string) => - `import.meta.glob(${JSON.stringify(value)}, { query: { ${flag}: true } })`; - contentEntryGlobResult = createGlob( - globWithUnderscoresIgnored(relContentDir, contentEntryExts), - CONTENT_FLAG, - ); - dataEntryGlobResult = createGlob( - globWithUnderscoresIgnored(relContentDir, dataEntryExts), - DATA_FLAG, - ); - renderEntryGlobResult = createGlob( - globWithUnderscoresIgnored(relContentDir, contentEntryExts), - CONTENT_RENDER_FLAG, - ); - } else { - contentEntryGlobResult = getStringifiedCollectionFromLookup( - 'content', - relContentDir, - lookupMap, - ); - dataEntryGlobResult = getStringifiedCollectionFromLookup('data', relContentDir, lookupMap); - renderEntryGlobResult = getStringifiedCollectionFromLookup('render', relContentDir, lookupMap); - } + const contentEntryConfigByExt = getEntryConfigByExtMap(settings.contentEntryTypes); + const contentEntryExts = [...contentEntryConfigByExt.keys()]; + const dataEntryExts = getDataEntryExts(settings); + const createGlob = (value: string[], flag: string) => + `import.meta.glob(${JSON.stringify(value)}, { query: { ${flag}: true } })`; + contentEntryGlobResult = createGlob( + globWithUnderscoresIgnored(relContentDir, contentEntryExts), + CONTENT_FLAG, + ); + dataEntryGlobResult = createGlob( + globWithUnderscoresIgnored(relContentDir, dataEntryExts), + DATA_FLAG, + ); + renderEntryGlobResult = createGlob( + globWithUnderscoresIgnored(relContentDir, contentEntryExts), + CONTENT_RENDER_FLAG, + ); let virtualModContents: string; if (isClient) { @@ -278,37 +241,6 @@ export async function generateContentEntryFile({ return virtualModContents; } -function getStringifiedCollectionFromLookup( - wantedType: 'content' | 'data' | 'render', - relContentDir: string, - lookupMap: ContentLookupMap, -) { - let str = '{'; - // In dev, we don't need to normalize the import specifier at all. Vite handles it. - let normalize = (slug: string) => slug; - // For prod builds, we need to transform from `/src/content/**/*.{md,mdx,json,yaml}` to a relative `./**/*.mjs` import - if (process.env.NODE_ENV === 'production') { - const suffix = wantedType === 'render' ? '.entry.mjs' : '.mjs'; - normalize = (slug: string) => - `${removeFileExtension(encodeName(slug)).replace(relContentDir, './')}${suffix}`; - } else { - let suffix = ''; - if (wantedType === 'content') suffix = CONTENT_FLAG; - else if (wantedType === 'data') suffix = DATA_FLAG; - else if (wantedType === 'render') suffix = CONTENT_RENDER_FLAG; - normalize = (slug: string) => `${slug}?${suffix}`; - } - for (const { type, entries } of Object.values(lookupMap)) { - if (type === wantedType || (wantedType === 'render' && type === 'content')) { - for (const slug of Object.values(entries)) { - str += `\n "${slug}": () => import("${normalize(slug)}"),`; - } - } - } - str += '\n}'; - return str; -} - /** * Generate a map from a collection + slug to the local file path. * This is used internally to resolve entry imports when using `getEntry()`. diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 1757dc65c914..bd50268eac99 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -61,14 +61,6 @@ export default async function build( const settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root)); if (inlineConfig.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)'); - } - } await clearContentLayerCache({ settings, logger, fs }); } @@ -239,7 +231,6 @@ class AstroBuilder { .map((pageData) => pageData.route) .concat(hasServerIslands ? getServerIslandRouteData(this.settings.config) : []), logging: this.logger, - cacheManifest: internals.cacheManifestUsed, }); if (this.logger.level && levels[this.logger.level()] <= levels['info']) { diff --git a/packages/astro/src/core/build/internal.ts b/packages/astro/src/core/build/internal.ts index 98fe20c14507..2de34467eb24 100644 --- a/packages/astro/src/core/build/internal.ts +++ b/packages/astro/src/core/build/internal.ts @@ -72,9 +72,6 @@ export interface BuildInternals { */ discoveredScripts: Set; - cachedClientEntries: string[]; - cacheManifestUsed: boolean; - /** * Map of propagated module ids (usually something like `/Users/...blog.mdx?astroPropagatedAssets`) * to a set of stylesheets that it uses. @@ -104,7 +101,6 @@ export interface BuildInternals { */ export function createBuildInternals(): BuildInternals { return { - cachedClientEntries: [], cssModuleToChunkIdMap: new Map(), inlinedScripts: new Map(), entrySpecifierToBundleMap: new Map(), @@ -121,7 +117,6 @@ export function createBuildInternals(): BuildInternals { staticFiles: new Set(), componentMetadata: new Map(), entryPoints: new Map(), - cacheManifestUsed: false, prerenderOnlyChunks: [], }; } diff --git a/packages/astro/src/core/build/plugins/index.ts b/packages/astro/src/core/build/plugins/index.ts index 533ac98a4786..8f814946dbe5 100644 --- a/packages/astro/src/core/build/plugins/index.ts +++ b/packages/astro/src/core/build/plugins/index.ts @@ -4,7 +4,6 @@ import type { AstroBuildPluginContainer } from '../plugin.js'; import { pluginAnalyzer } from './plugin-analyzer.js'; import { pluginChunks } from './plugin-chunks.js'; import { pluginComponentEntry } from './plugin-component-entry.js'; -import { pluginContent } from './plugin-content.js'; import { pluginCSS } from './plugin-css.js'; import { pluginInternals } from './plugin-internals.js'; import { pluginManifest } from './plugin-manifest.js'; @@ -23,7 +22,6 @@ export function registerAllPlugins({ internals, options, register }: AstroBuildP register(pluginRenderers(options)); register(pluginMiddleware(options, internals)); register(pluginPages(options, internals)); - register(pluginContent(options, internals)); register(pluginCSS(options, internals)); register(astroHeadBuildPlugin(internals)); register(pluginPrerender(options, internals)); diff --git a/packages/astro/src/core/build/plugins/plugin-content.ts b/packages/astro/src/core/build/plugins/plugin-content.ts deleted file mode 100644 index 260489fd1f80..000000000000 --- a/packages/astro/src/core/build/plugins/plugin-content.ts +++ /dev/null @@ -1,524 +0,0 @@ -import { createHash } from 'node:crypto'; -import fsMod from 'node:fs'; -import { fileURLToPath } from 'node:url'; -import glob from 'fast-glob'; -import pLimit from 'p-limit'; -import { type Plugin as VitePlugin, normalizePath } from 'vite'; -import { CONTENT_RENDER_FLAG, PROPAGATED_ASSET_FLAG } from '../../../content/consts.js'; -import { type ContentLookupMap, hasContentFlag } from '../../../content/utils.js'; -import { - generateContentEntryFile, - generateLookupMap, -} from '../../../content/vite-plugin-content-virtual-mod.js'; -import type { AstroConfig } from '../../../types/public/config.js'; -import { configPaths } from '../../config/index.js'; -import { emptyDir } from '../../fs/index.js'; -import { - appendForwardSlash, - joinPaths, - removeFileExtension, - removeLeadingForwardSlash, -} from '../../path.js'; -import { isContentCollectionsCacheEnabled } from '../../util.js'; -import { addRollupInput } from '../add-rollup-input.js'; -import { CHUNKS_PATH, CONTENT_PATH } from '../consts.js'; -import type { BuildInternals } from '../internal.js'; -import type { AstroBuildPlugin } from '../plugin.js'; -import { copyFiles } from '../static-build.js'; -import type { StaticBuildOptions } from '../types.js'; -import { encodeName } from '../util.js'; -import { extendManualChunks } from './util.js'; - -const CONTENT_CACHE_DIR = './' + CONTENT_PATH; -const CONTENT_MANIFEST_FILE = './manifest.json'; -// IMPORTANT: Update this version when making significant changes to the manifest format. -// Only manifests generated with the same version number can be compared. -const CONTENT_MANIFEST_VERSION = 1; - -interface ContentManifestKey { - collection: string; - type: 'content' | 'data'; - entry: string; -} - -interface ContentManifest { - version: number; - entries: [ContentManifestKey, string][]; - // Tracks components that should be included in the server build - // When the cache is restored, these might no longer be referenced - serverEntries: string[]; - // Tracks components that should be passed to the client build - // When the cache is restored, these might no longer be referenced - clientEntries: string[]; - // Hash of the lockfiles, pnpm-lock.yaml, package-lock.json, etc. - // Kept so that installing new packages results in a full rebuild. - lockfiles: string; - // Hash of the Astro config. Changing options results in invalidating the cache. - configs: string; -} - -const virtualEmptyModuleId = `virtual:empty-content`; -const resolvedVirtualEmptyModuleId = `\0${virtualEmptyModuleId}`; -const NO_MANIFEST_VERSION = -1 as const; - -function createContentManifest(): ContentManifest { - return { - version: NO_MANIFEST_VERSION, - entries: [], - serverEntries: [], - clientEntries: [], - lockfiles: '', - configs: '', - }; -} - -const getContentRoot = (config: AstroConfig) => new URL('./content/', config.outDir); -const getContentCacheDir = (config: AstroConfig) => new URL(CONTENT_CACHE_DIR, config.cacheDir); -const getCacheTmp = (contentCacheDir: URL) => new URL('./.tmp/', contentCacheDir); - -function vitePluginContent( - opts: StaticBuildOptions, - lookupMap: ContentLookupMap, - internals: BuildInternals, - cachedBuildOutput: Array<{ cached: URL; dist: URL }>, -): VitePlugin { - const { config } = opts.settings; - const distContentRoot = getContentRoot(config); - const contentCacheDir = getContentCacheDir(config); - const contentManifestFile = new URL(CONTENT_MANIFEST_FILE, contentCacheDir); - let oldManifest = createContentManifest(); - let newManifest = createContentManifest(); - let entries: ContentEntries; - let injectedEmptyFile = false; - let currentManifestState: ReturnType = 'valid'; - - if (fsMod.existsSync(contentManifestFile)) { - try { - const data = fsMod.readFileSync(contentManifestFile, { encoding: 'utf8' }); - oldManifest = JSON.parse(data); - } catch {} - } - - return { - name: '@astro/plugin-build-content', - - async options(options) { - let newOptions = Object.assign({}, options); - newManifest = await generateContentManifest(opts, lookupMap); - entries = getEntriesFromManifests(oldManifest, newManifest); - - // If the manifest is valid, use the cached client entries as nothing has changed - currentManifestState = manifestState(oldManifest, newManifest); - if (currentManifestState === 'valid') { - internals.cachedClientEntries = oldManifest.clientEntries; - } else { - let logReason = ''; - switch (currentManifestState) { - case 'config-mismatch': - logReason = 'Astro config has changed'; - break; - case 'lockfile-mismatch': - logReason = 'Lockfiles have changed'; - break; - case 'no-entries': - logReason = 'No content collections entries cached'; - break; - case 'version-mismatch': - logReason = 'The cache manifest version has changed'; - break; - case 'no-manifest': - logReason = 'No content manifest was found in the cache'; - break; - } - opts.logger.info('build', `Cache invalid, rebuilding from source. Reason: ${logReason}.`); - } - - // Of the cached entries, these ones need to be rebuilt - for (const { type, entry } of entries.buildFromSource) { - const fileURL = encodeURI(joinPaths(opts.settings.config.root.toString(), entry)); - const input = fileURLToPath(fileURL); - // Adds `/src/content/blog/post-1.md?astroContentCollectionEntry` as a top-level input - const inputs = [`${input}?${collectionTypeToFlag(type)}`]; - if (type === 'content') { - // Content entries also need to include the version with the RENDER flag - inputs.push(`${input}?${CONTENT_RENDER_FLAG}`); - } - newOptions = addRollupInput(newOptions, inputs); - } - - // Restores cached chunks and assets from the previous build - // If the manifest state is not valid then it needs to rebuild everything - // so don't do that in this case. - if (currentManifestState === 'valid') { - for (const { cached, dist } of cachedBuildOutput) { - if (fsMod.existsSync(cached)) { - await copyFiles(cached, dist, true); - } - } - // Copy over the content cache now so that new files override it - const cacheExists = fsMod.existsSync(contentCacheDir); - if (cacheExists) { - await copyFiles(contentCacheDir, distContentRoot, false); - } - } - - // If nothing needs to be rebuilt, we inject a fake entrypoint to appease Rollup - if (entries.buildFromSource.length === 0) { - newOptions = addRollupInput(newOptions, [virtualEmptyModuleId]); - injectedEmptyFile = true; - } - return newOptions; - }, - - outputOptions(outputOptions) { - const rootPath = normalizePath(fileURLToPath(opts.settings.config.root)); - const srcPath = normalizePath(fileURLToPath(opts.settings.config.srcDir)); - const entryCache = new Map(); - extendManualChunks(outputOptions, { - before(id, meta) { - if (id.startsWith(srcPath) && id.slice(srcPath.length).startsWith('content')) { - const info = meta.getModuleInfo(id); - if ( - info?.dynamicImporters.length === 1 && - hasContentFlag(info.dynamicImporters[0], PROPAGATED_ASSET_FLAG) - ) { - const [srcRelativePath] = id.replace(rootPath, '/').split('?'); - const resultId = encodeName( - `${removeLeadingForwardSlash(removeFileExtension(srcRelativePath))}.render.mjs`, - ); - return resultId; - } - const [srcRelativePath, flag] = id.replace(rootPath, '/').split('?'); - const collectionEntry = findEntryFromSrcRelativePath( - lookupMap, - srcRelativePath, - entryCache, - ); - if (collectionEntry) { - let suffix = '.mjs'; - if (flag === PROPAGATED_ASSET_FLAG) { - suffix = '.entry.mjs'; - } - id = - removeLeadingForwardSlash( - removeFileExtension(encodeName(id.replace(srcPath, '/'))), - ) + suffix; - return id; - } - } - }, - }); - }, - - resolveId(id) { - if (id === virtualEmptyModuleId) { - return resolvedVirtualEmptyModuleId; - } - }, - - async load(id) { - if (id === resolvedVirtualEmptyModuleId) { - return { - code: `// intentionally left empty!\nexport default {}`, - }; - } - }, - - async generateBundle(_options, bundle) { - const code = await generateContentEntryFile({ - settings: opts.settings, - fs: fsMod, - lookupMap, - IS_DEV: false, - IS_SERVER: false, - isClient: false, - }); - this.emitFile({ - type: 'prebuilt-chunk', - code, - fileName: 'content/entry.mjs', - }); - if (!injectedEmptyFile) return; - Object.keys(bundle).forEach((key) => { - const mod = bundle[key]; - if (mod.type === 'asset') return; - if (mod.facadeModuleId === resolvedVirtualEmptyModuleId) { - delete bundle[key]; - } - }); - }, - - async writeBundle() { - // These are stored in the manifest to ensure that they are included in the build - // in case they aren't referenced _outside_ of the cached content. - // We can use this info in the manifest to run a proper client build again. - const clientComponents = new Set([ - ...oldManifest.clientEntries, - ...internals.discoveredHydratedComponents.keys(), - ...internals.discoveredClientOnlyComponents.keys(), - ...internals.discoveredScripts, - ]); - // Likewise, these are server modules that might not be referenced - // once the cached items are excluded from the build process - const serverComponents = new Set([ - ...oldManifest.serverEntries, - ...internals.discoveredHydratedComponents.keys(), - ]); - newManifest.serverEntries = Array.from(serverComponents); - newManifest.clientEntries = Array.from(clientComponents); - - const cacheExists = fsMod.existsSync(contentCacheDir); - // If the manifest is invalid, empty the cache so that we can create a new one. - if (cacheExists && currentManifestState !== 'valid') { - emptyDir(contentCacheDir); - } - - await fsMod.promises.mkdir(contentCacheDir, { recursive: true }); - await fsMod.promises.writeFile(contentManifestFile, JSON.stringify(newManifest), { - encoding: 'utf8', - }); - }, - }; -} - -function findEntryFromSrcRelativePath( - lookupMap: ContentLookupMap, - srcRelativePath: string, - entryCache: Map, -) { - let value = entryCache.get(srcRelativePath); - if (value) return value; - for (const collection of Object.values(lookupMap)) { - for (const entry of Object.values(collection)) { - for (const entryFile of Object.values(entry)) { - if (entryFile === srcRelativePath) { - value = entryFile; - entryCache.set(srcRelativePath, entryFile); - return value; - } - } - } - } -} - -interface ContentEntries { - restoreFromCache: ContentManifestKey[]; - buildFromSource: ContentManifestKey[]; -} - -function getEntriesFromManifests( - oldManifest: ContentManifest, - newManifest: ContentManifest, -): ContentEntries { - const { entries: oldEntries } = oldManifest; - const { entries: newEntries } = newManifest; - let entries: ContentEntries = { restoreFromCache: [], buildFromSource: [] }; - - const newEntryMap = new Map(newEntries); - if (manifestState(oldManifest, newManifest) !== 'valid') { - entries.buildFromSource = Array.from(newEntryMap.keys()); - return entries; - } - const oldEntryHashMap = new Map( - oldEntries.map(([key, hash]) => [hash, key]), - ); - - for (const [entry, hash] of newEntryMap) { - if (oldEntryHashMap.has(hash)) { - entries.restoreFromCache.push(entry); - } else { - entries.buildFromSource.push(entry); - } - } - - return entries; -} - -type ManifestState = - | 'valid' - | 'no-manifest' - | 'version-mismatch' - | 'no-entries' - | 'lockfile-mismatch' - | 'config-mismatch'; - -function manifestState(oldManifest: ContentManifest, newManifest: ContentManifest): ManifestState { - // There isn't an existing manifest. - if (oldManifest.version === NO_MANIFEST_VERSION) { - return 'no-manifest'; - } - // Version mismatch, always invalid - if (oldManifest.version !== newManifest.version) { - return 'version-mismatch'; - } - if (oldManifest.entries.length === 0) { - return 'no-entries'; - } - // Lockfiles have changed or there is no lockfile at all. - if (oldManifest.lockfiles !== newManifest.lockfiles || newManifest.lockfiles === '') { - return 'lockfile-mismatch'; - } - // Config has changed. - if (oldManifest.configs !== newManifest.configs) { - return 'config-mismatch'; - } - return 'valid'; -} - -async function generateContentManifest( - opts: StaticBuildOptions, - lookupMap: ContentLookupMap, -): Promise { - let manifest = createContentManifest(); - manifest.version = CONTENT_MANIFEST_VERSION; - const limit = pLimit(10); - const promises: Promise[] = []; - - for (const [collection, { type, entries }] of Object.entries(lookupMap)) { - for (const entry of Object.values(entries)) { - const key: ContentManifestKey = { collection, type, entry }; - const fileURL = new URL(encodeURI(joinPaths(opts.settings.config.root.toString(), entry))); - promises.push( - limit(async () => { - const data = await fsMod.promises.readFile(fileURL, { encoding: 'utf8' }); - manifest.entries.push([key, checksum(data, fileURL.toString())]); - }), - ); - } - } - - const [lockfiles, configs] = await Promise.all([ - lockfilesHash(opts.settings.config.root), - configHash(opts.settings.config.root), - ]); - - manifest.lockfiles = lockfiles; - manifest.configs = configs; - - await Promise.all(promises); - return manifest; -} - -async function pushBufferInto(fileURL: URL, buffers: Uint8Array[]) { - try { - const handle = await fsMod.promises.open(fileURL, 'r'); - const data = await handle.readFile(); - buffers.push(data); - await handle.close(); - } catch { - // File doesn't exist, ignore - } -} - -async function lockfilesHash(root: URL) { - // Order is important so don't change this. - const lockfiles = ['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock', 'bun.lockb']; - const datas: Uint8Array[] = []; - const promises: Promise[] = []; - for (const lockfileName of lockfiles) { - const fileURL = new URL(`./${lockfileName}`, root); - promises.push(pushBufferInto(fileURL, datas)); - } - await Promise.all(promises); - return checksum(...datas); -} - -async function configHash(root: URL) { - const configFileNames = configPaths; - for (const configPath of configFileNames) { - try { - const fileURL = new URL(`./${configPath}`, root); - const data = await fsMod.promises.readFile(fileURL); - const hash = checksum(data); - return hash; - } catch { - // File doesn't exist - } - } - // No config file, still create a hash since we can compare nothing against nothing. - return checksum(`export default {}`); -} - -function checksum(...datas: string[] | Uint8Array[]): string { - const hash = createHash('sha1'); - datas.forEach((data) => hash.update(data)); - return hash.digest('base64'); -} - -function collectionTypeToFlag(type: 'content' | 'data') { - const name = type[0].toUpperCase() + type.slice(1); - return `astro${name}CollectionEntry`; -} - -export async function copyContentToCache(opts: StaticBuildOptions) { - const { config } = opts.settings; - const distContentRoot = getContentRoot(config); - const contentCacheDir = getContentCacheDir(config); - const cacheTmp = getCacheTmp(contentCacheDir); - - await fsMod.promises.mkdir(cacheTmp, { recursive: true }); - await copyFiles(distContentRoot, cacheTmp, true); - await copyFiles(cacheTmp, contentCacheDir); - - // Read the files from `dist/content/*` and `dist/chunks/*` so that - // we can clean them out of the dist folder - let files: string[] = []; - await Promise.all([ - glob(`**/*.{mjs,json}`, { - cwd: fileURLToPath(cacheTmp), - }).then((f) => files.push(...f.map((file) => CONTENT_PATH + file))), - glob(`**/*.{mjs,json}`, { - cwd: fileURLToPath(new URL('./' + CHUNKS_PATH, config.outDir)), - }).then((f) => files.push(...f.map((file) => CHUNKS_PATH + file))), - ]); - - // Remove the tmp folder that's no longer needed. - await fsMod.promises.rm(cacheTmp, { recursive: true, force: true }); - - return files; -} - -export function pluginContent( - opts: StaticBuildOptions, - internals: BuildInternals, -): AstroBuildPlugin { - const { cacheDir, outDir } = opts.settings.config; - - const chunksFolder = './' + CHUNKS_PATH; - const assetsFolder = './' + appendForwardSlash(opts.settings.config.build.assets); - // These are build output that is kept in the cache. - const cachedBuildOutput = [ - { cached: new URL(chunksFolder, cacheDir), dist: new URL(chunksFolder, outDir) }, - { cached: new URL(assetsFolder, cacheDir), dist: new URL(assetsFolder, outDir) }, - ]; - - return { - targets: ['server'], - hooks: { - async 'build:before'() { - if (!isContentCollectionsCacheEnabled(opts.settings)) { - return { vitePlugin: undefined }; - } - const lookupMap = await generateLookupMap({ settings: opts.settings, fs: fsMod }); - return { - vitePlugin: vitePluginContent(opts, lookupMap, internals, cachedBuildOutput), - }; - }, - - async 'build:post'() { - if (!isContentCollectionsCacheEnabled(opts.settings)) { - return; - } - // Cache build output of chunks and assets - const promises: Promise[] = []; - for (const { cached, dist } of cachedBuildOutput) { - if (fsMod.existsSync(dist)) { - promises.push(copyFiles(dist, cached, true)); - } - } - - if (promises.length) await Promise.all(promises); - }, - }, - }; -} diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 0503543f46c1..24b16af984df 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -1,24 +1,17 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath, pathToFileURL } from 'node:url'; import { teardown } from '@astrojs/compiler'; import glob from 'fast-glob'; import { bgGreen, black, green } from 'kleur/colors'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; import * as vite from 'vite'; -import { PROPAGATED_ASSET_FLAG } from '../../content/consts.js'; -import { - getSymlinkedContentCollections, - hasAnyContentFlag, - reverseSymlink, -} from '../../content/utils.js'; import { type BuildInternals, createBuildInternals } from '../../core/build/internal.js'; import { emptyDir, removeEmptyDirs } from '../../core/fs/index.js'; -import { appendForwardSlash, prependForwardSlash, removeFileExtension } from '../../core/path.js'; +import { appendForwardSlash, prependForwardSlash } from '../../core/path.js'; import { runHookBuildSetup } from '../../integrations/hooks.js'; import { getOutputDirectory } from '../../prerender/utils.js'; import type { RouteData } from '../../types/public/internal.js'; import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js'; -import type { Logger } from '../logger/core.js'; import { routeIsRedirect } from '../redirects/index.js'; import { getOutDirWithinCwd } from './common.js'; import { CHUNKS_PATH } from './consts.js'; @@ -26,7 +19,6 @@ import { generatePages } from './generate.js'; import { trackPageData } from './internal.js'; import { type AstroBuildPluginContainer, createPluginContainer } from './plugin.js'; import { registerAllPlugins } from './plugins/index.js'; -import { copyContentToCache } from './plugins/plugin-content.js'; import { RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID } from './plugins/plugin-manifest.js'; import { ASTRO_PAGE_RESOLVED_MODULE_ID } from './plugins/plugin-pages.js'; import { RESOLVED_RENDERERS_MODULE_ID } from './plugins/plugin-renderers.js'; @@ -36,7 +28,7 @@ import type { StaticBuildOptions } from './types.js'; import { encodeName, getTimeStat, viteBuildReturnToRollupOutputs } from './util.js'; export async function viteBuild(opts: StaticBuildOptions) { - const { allPages, settings, logger } = opts; + const { allPages, settings } = opts; settings.timer.start('SSR build'); @@ -72,7 +64,7 @@ export async function viteBuild(opts: StaticBuildOptions) { // Build your project (SSR application code, assets, client JS, etc.) const ssrTime = performance.now(); opts.logger.info('build', `Building ${settings.config.output} entrypoints...`); - const ssrOutput = await ssrBuild(opts, internals, pageInput, container, logger); + const ssrOutput = await ssrBuild(opts, internals, pageInput, container); opts.logger.info('build', green(`✓ Completed in ${getTimeStat(ssrTime, performance.now())}.`)); settings.timer.end('SSR build'); @@ -84,7 +76,6 @@ export async function viteBuild(opts: StaticBuildOptions) { .filter((a) => typeof a === 'string') as string[]; const clientInput = new Set([ - ...internals.cachedClientEntries, ...internals.discoveredHydratedComponents.keys(), ...internals.discoveredClientOnlyComponents.keys(), ...rendererClientEntrypoints, @@ -102,9 +93,6 @@ export async function viteBuild(opts: StaticBuildOptions) { const clientOutputs = viteBuildReturnToRollupOutputs(clientOutput ?? []); await runPostBuildHooks(container, ssrOutputs, clientOutputs); let contentFileNames: string[] | undefined = undefined; - if (opts.settings.config.experimental.contentCollectionCache) { - contentFileNames = await copyContentToCache(opts); - } settings.timer.end('Client build'); // Free up memory @@ -160,17 +148,12 @@ async function ssrBuild( internals: BuildInternals, input: Set, container: AstroBuildPluginContainer, - logger: Logger, ) { - const buildID = Date.now().toString(); const { allPages, settings, viteConfig } = opts; const ssr = settings.buildOutput === 'server'; const out = getOutputDirectory(settings); const routes = Object.values(allPages).flatMap((pageData) => pageData.route); - const isContentCache = !ssr && settings.config.experimental.contentCollectionCache; const { lastVitePlugins, vitePlugins } = await container.runBeforeHook('server', input); - const contentDir = new URL('./src/content', settings.config.root); - const symlinks = await getSymlinkedContentCollections({ contentDir, logger, fs }); const viteBuildConfig: vite.InlineConfig = { ...viteConfig, mode: viteConfig.mode || 'production', @@ -191,9 +174,9 @@ async function ssrBuild( preserveEntrySignatures: 'exports-only', input: [], output: { - hoistTransitiveImports: isContentCache, + hoistTransitiveImports: false, format: 'esm', - minifyInternalExports: !isContentCache, + minifyInternalExports: true, // Server chunks can't go in the assets (_astro) folder // We need to keep these separate chunkFileNames(chunkInfo) { @@ -201,18 +184,6 @@ async function ssrBuild( let prefix = CHUNKS_PATH; let suffix = '_[hash].mjs'; - if (isContentCache) { - prefix += `${buildID}/`; - suffix = '.mjs'; - - if (name.includes('/content/')) { - const parts = name.split('/'); - if (parts.at(1) === 'content') { - return encodeName(parts.slice(1).join('/')); - } - } - } - // Sometimes chunks have the `@_@astro` suffix due to SSR logic. Remove it! // TODO: refactor our build logic to avoid this if (name.includes(ASTRO_PAGE_EXTENSION_POST_PATTERN)) { @@ -244,21 +215,6 @@ async function ssrBuild( return 'manifest_[hash].mjs'; } else if (chunkInfo.facadeModuleId === settings.adapter?.serverEntrypoint) { return 'adapter_[hash].mjs'; - } else if ( - settings.config.experimental.contentCollectionCache && - chunkInfo.facadeModuleId && - hasAnyContentFlag(chunkInfo.facadeModuleId) - ) { - const moduleId = reverseSymlink({ - symlinks, - entry: chunkInfo.facadeModuleId, - contentDir, - }); - const [srcRelative, flag] = moduleId.split('/src/')[1].split('?'); - if (flag === PROPAGATED_ASSET_FLAG) { - return encodeName(`${removeFileExtension(srcRelative)}.entry.mjs`); - } - return encodeName(`${removeFileExtension(srcRelative)}.mjs`); } else { return '[name].mjs'; } diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index ec2ac3805115..cec3a291f340 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -90,7 +90,6 @@ export const ASTRO_CONFIG_DEFAULTS = { validateSecrets: false, }, experimental: { - contentCollectionCache: false, clientPrerender: false, contentIntellisense: false, }, @@ -510,10 +509,6 @@ export const AstroConfigSchema = z.object({ .default(ASTRO_CONFIG_DEFAULTS.env), experimental: z .object({ - contentCollectionCache: z - .boolean() - .optional() - .default(ASTRO_CONFIG_DEFAULTS.experimental.contentCollectionCache), clientPrerender: z .boolean() .optional() diff --git a/packages/astro/src/core/util.ts b/packages/astro/src/core/util.ts index 3375deaef3e3..3d676d5bdd5e 100644 --- a/packages/astro/src/core/util.ts +++ b/packages/astro/src/core/util.ts @@ -158,14 +158,6 @@ export function isEndpoint(file: URL, settings: AstroSettings): boolean { return !endsWithPageExt(file, settings) && !file.toString().includes('?astro'); } -export function isContentCollectionsCacheEnabled(settings: AstroSettings): boolean { - return ( - settings.config.experimental.contentCollectionCache && - // contentCollectionsCache is an SSG only feature - settings.buildOutput !== 'server' - ); -} - export function resolveJsToTs(filePath: string) { if (filePath.endsWith('.jsx') && !fs.existsSync(filePath)) { const tryPath = filePath.slice(0, -4) + '.tsx'; diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 55297de87a75..7d8fe6e0196c 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -579,16 +579,9 @@ type RunHookBuildDone = { pages: string[]; routes: RouteData[]; logging: Logger; - cacheManifest: boolean; }; -export async function runHookBuildDone({ - settings, - pages, - routes, - logging, - cacheManifest, -}: RunHookBuildDone) { +export async function runHookBuildDone({ settings, pages, routes, logging }: RunHookBuildDone) { const dir = settings.buildOutput === 'server' ? settings.config.build.client : settings.config.outDir; await fsMod.promises.mkdir(dir, { recursive: true }); @@ -605,7 +598,6 @@ export async function runHookBuildDone({ dir, routes: integrationRoutes, logger, - cacheManifest, }), logger: logging, }); diff --git a/packages/astro/src/types/public/config.ts b/packages/astro/src/types/public/config.ts index 257f1ffc10d3..93703fb5fb88 100644 --- a/packages/astro/src/types/public/config.ts +++ b/packages/astro/src/types/public/config.ts @@ -1552,25 +1552,6 @@ export interface AstroUserConfig { * These flags are not guaranteed to be stable. */ experimental?: { - /** - * @docs - * @name experimental.contentCollectionCache - * @type {boolean} - * @default `false` - * @version 3.5.0 - * @description - * Enables a persistent cache for content collections when building in static mode. - * - * ```js - * { - * experimental: { - * contentCollectionCache: true, - * }, - * } - * ``` - */ - contentCollectionCache?: boolean; - /** * @docs * @name experimental.clientPrerender diff --git a/packages/astro/src/types/public/integrations.ts b/packages/astro/src/types/public/integrations.ts index 73a25f63caae..d31a1651f90a 100644 --- a/packages/astro/src/types/public/integrations.ts +++ b/packages/astro/src/types/public/integrations.ts @@ -227,7 +227,6 @@ export interface BaseIntegrationHooks { dir: URL; routes: IntegrationRouteData[]; logger: AstroIntegrationLogger; - cacheManifest: boolean; }) => void | Promise; 'astro:route:setup': (options: { route: RouteOptions; diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts index 8d520f71e087..b6216d48ad88 100644 --- a/packages/astro/src/vite-plugin-astro/index.ts +++ b/packages/astro/src/vite-plugin-astro/index.ts @@ -98,13 +98,6 @@ export default function astro({ settings, logger }: AstroPluginOptions): vite.Pl // `compile` should re-set `filename` in `astroFileToCompileMetadata` if (code != null) await compile(code, filename); } - // When cached we might load client-side scripts during the build - else if (config.experimental.contentCollectionCache) { - await this.load({ - id: filename, - resolveDependencies: false, - }); - } compileMetadata = astroFileToCompileMetadata.get(filename); } diff --git a/packages/astro/test/experimental-content-collection-references.test.js b/packages/astro/test/experimental-content-collection-references.test.js deleted file mode 100644 index ab458bbd8d7b..000000000000 --- a/packages/astro/test/experimental-content-collection-references.test.js +++ /dev/null @@ -1,166 +0,0 @@ -import assert from 'node:assert/strict'; -import { after, before, describe, it } from 'node:test'; -import * as cheerio from 'cheerio'; -import { fixLineEndings, loadFixture } from './test-utils.js'; - -describe('Experimental Content Collections cache - references', () => { - let fixture; - let devServer; - before(async () => { - fixture = await loadFixture({ - root: './fixtures/content-collection-references/', - experimental: { contentCollectionCache: true }, - }); - }); - - after(async () => await fixture.clean()); - - const modes = ['dev', 'prod']; - - for (const mode of modes) { - describe(mode, () => { - before(async () => { - if (mode === 'prod') { - await fixture.build(); - } else if (mode === 'dev') { - devServer = await fixture.startDevServer(); - } - }); - - after(async () => { - if (mode === 'dev') devServer?.stop(); - }); - - describe(`JSON result`, () => { - let json; - before(async () => { - if (mode === 'prod') { - const rawJson = await fixture.readFile('/welcome-data.json'); - json = JSON.parse(rawJson); - } else if (mode === 'dev') { - const rawJsonResponse = await fixture.fetch('/welcome-data.json'); - const rawJson = await rawJsonResponse.text(); - json = JSON.parse(rawJson); - } - }); - - it('Returns expected keys', () => { - assert.equal(json.hasOwnProperty('welcomePost'), true); - assert.equal(json.hasOwnProperty('banner'), true); - assert.equal(json.hasOwnProperty('author'), true); - assert.equal(json.hasOwnProperty('relatedPosts'), true); - }); - - it('Returns `banner` data', () => { - const { banner } = json; - assert.equal(banner.hasOwnProperty('data'), true); - assert.equal(banner.id, 'welcome'); - assert.equal(banner.collection, 'banners'); - assert.equal( - banner.data.alt, - 'Futuristic landscape with chrome buildings and blue skies', - ); - - assert.equal(banner.data.src.width, 400); - assert.equal(banner.data.src.height, 225); - assert.equal(banner.data.src.format, 'jpg'); - assert.equal(banner.data.src.src.includes('the-future'), true); - }); - - it('Returns `author` data', () => { - const { author } = json; - assert.equal(author.hasOwnProperty('data'), true); - assert.deepEqual(author, { - id: 'nate-moore', - collection: 'authors', - data: { - name: 'Nate Something Moore', - twitter: 'https://twitter.com/n_moore', - }, - }); - }); - - it('Returns `relatedPosts` data', () => { - const { relatedPosts } = json; - assert.equal(Array.isArray(relatedPosts), true); - const topLevelInfo = relatedPosts.map(({ data, body, ...meta }) => ({ - ...meta, - body: fixLineEndings(body).trim(), - })); - assert.deepEqual(topLevelInfo, [ - { - id: 'related-1.md', - slug: 'related-1', - body: '# Related post 1\n\nThis is related to the welcome post.', - collection: 'blog', - }, - { - id: 'related-2.md', - slug: 'related-2', - body: '# Related post 2\n\nThis is related to the welcome post.', - collection: 'blog', - }, - ]); - const postData = relatedPosts.map(({ data }) => data); - assert.deepEqual(postData, [ - { - title: 'Related post 1', - banner: { id: 'welcome', collection: 'banners' }, - author: { id: 'fred-schott', collection: 'authors' }, - }, - { - title: 'Related post 2', - banner: { id: 'welcome', collection: 'banners' }, - author: { id: 'ben-holmes', collection: 'authors' }, - }, - ]); - }); - }); - - describe(`Render result`, () => { - let $; - before(async () => { - if (mode === 'prod') { - const html = await fixture.readFile('/welcome/index.html'); - $ = cheerio.load(html); - } else if (mode === 'dev') { - const htmlResponse = await fixture.fetch('/welcome'); - const html = await htmlResponse.text(); - $ = cheerio.load(html); - } - }); - - it('Renders `banner` data', () => { - const banner = $('img[data-banner]'); - assert.equal(banner.length, 1); - assert.equal(banner.attr('src').includes('the-future'), true); - assert.equal( - banner.attr('alt'), - 'Futuristic landscape with chrome buildings and blue skies', - ); - assert.equal(banner.attr('width'), '400'); - assert.equal(banner.attr('height'), '225'); - }); - - it('Renders `author` data', () => { - const author = $('a[data-author-name]'); - assert.equal(author.length, 1); - assert.equal(author.attr('href'), 'https://twitter.com/n_moore'); - assert.equal(author.text(), 'Nate Something Moore'); - }); - - it('Renders `relatedPosts` data', () => { - const relatedPosts = $('ul[data-related-posts]'); - assert.equal(relatedPosts.length, 1); - const relatedPost1 = relatedPosts.find('li').eq(0); - - assert.equal(relatedPost1.find('a').attr('href'), '/blog/related-1'); - assert.equal(relatedPost1.find('a').text(), 'Related post 1'); - const relatedPost2 = relatedPosts.find('li').eq(1); - assert.equal(relatedPost2.find('a').attr('href'), '/blog/related-2'); - assert.equal(relatedPost2.find('a').text(), 'Related post 2'); - }); - }); - }); - } -}); diff --git a/packages/astro/test/experimental-content-collections-cache-invalidation.test.js b/packages/astro/test/experimental-content-collections-cache-invalidation.test.js deleted file mode 100644 index c359950a0f22..000000000000 --- a/packages/astro/test/experimental-content-collections-cache-invalidation.test.js +++ /dev/null @@ -1,137 +0,0 @@ -import assert from 'node:assert/strict'; -import fs from 'node:fs'; -import { after, before, describe, it } from 'node:test'; -import { copyFiles } from '../dist/core/build/static-build.js'; -import { loadFixture } from './test-utils.js'; - -describe('Experimental Content Collections cache - invalidation', () => { - class CacheBackup { - constructor(root, relCacheDir) { - this.root = new URL(root, import.meta.url); - this.cacheDir = new URL(relCacheDir, this.root); - this.tmpDir = new URL(`./tmp` + relCacheDir.slice(1), this.root); - } - - backup() { - this.rmTmp(); - copyFiles(this.cacheDir, this.tmpDir); - } - - restore() { - fs.rmSync(this.cacheDir, { recursive: true }); - copyFiles(this.tmpDir, this.cacheDir); - } - - rmTmp() { - fs.rmSync(this.tmpDir, { force: true, recursive: true }); - } - } - - class ManifestTestPlugin { - used = false; - - plugin() { - return { - name: '@test/manifest-used', - hooks: { - 'astro:build:done': ({ cacheManifest }) => { - this.used = cacheManifest; - }, - }, - }; - } - } - - describe('manifest version', () => { - let fixture, - backup, - /** @type {ManifestTestPlugin} */ - testPlugin; - before(async () => { - testPlugin = new ManifestTestPlugin(); - fixture = await loadFixture({ - root: './fixtures/content-collections-cache-invalidation/', - cacheDir: './cache/version-mismatch/', - experimental: { contentCollectionCache: true }, - integrations: [testPlugin.plugin()], - }); - backup = new CacheBackup( - './fixtures/content-collections-cache-invalidation/', - './cache/version-mismatch/', - ); - backup.backup(); - await fixture.build(); - }); - - after(async () => { - backup.restore(); - //await fixture.clean(); - }); - - it('Manifest was not used', () => { - assert.equal(testPlugin.used, false, 'manifest not used because of version mismatch'); - }); - }); - - describe('lockfiles', () => { - let fixture, - backup, - /** @type {ManifestTestPlugin} */ - testPlugin; - before(async () => { - testPlugin = new ManifestTestPlugin(); - fixture = await loadFixture({ - root: './fixtures/content-collections-cache-invalidation/', - cacheDir: './cache/lockfile-mismatch/', - experimental: { contentCollectionCache: true }, - integrations: [testPlugin.plugin()], - }); - backup = new CacheBackup( - './fixtures/content-collections-cache-invalidation/', - './cache/lockfile-mismatch/', - ); - backup.backup(); - await fixture.build(); - }); - - after(async () => { - backup.restore(); - //await fixture.clean(); - }); - - it('Manifest was not used', () => { - assert.equal(testPlugin.used, false, 'manifest not used because of lockfile mismatch'); - }); - }); - - describe('duplicate content', () => { - let fixture, - backup, - /** @type {ManifestTestPlugin} */ - testPlugin; - before(async () => { - testPlugin = new ManifestTestPlugin(); - fixture = await loadFixture({ - root: './fixtures/content-collections-same-contents/', - cacheDir: './cache/same-contents/', - experimental: { contentCollectionCache: true }, - integrations: [testPlugin.plugin()], - }); - backup = new CacheBackup( - './fixtures/content-collections-same-contents/', - './cache/same-contents/', - ); - backup.backup(); - await fixture.build(); - }); - - after(async () => { - backup.restore(); - //await fixture.clean(); - }); - - it('Manifest was not used', () => { - assert.equal(testPlugin.used, false, 'manifest not used because of lockfile mismatch'); - }); - }); -}); diff --git a/packages/astro/test/experimental-content-collections-css-inline-stylesheets.test.js b/packages/astro/test/experimental-content-collections-css-inline-stylesheets.test.js deleted file mode 100644 index 2c61563c6834..000000000000 --- a/packages/astro/test/experimental-content-collections-css-inline-stylesheets.test.js +++ /dev/null @@ -1,303 +0,0 @@ -import assert from 'node:assert/strict'; -import { after, before, describe, it } from 'node:test'; -import * as cheerio from 'cheerio'; -import testAdapter from './test-adapter.js'; -import { loadFixture } from './test-utils.js'; - -describe('Experimental Content Collections cache inlineStylesheets', () => { - let fixture; - - before(async () => { - fixture = await loadFixture({ - // inconsequential config that differs between tests - // to bust cache and prevent modules and their state - // from being reused - site: 'https://test.dev/', - root: './fixtures/css-inline-stylesheets-2/', - output: 'static', - build: { - inlineStylesheets: 'never', - }, - experimental: { - contentCollectionCache: true, - }, - }); - await fixture.build(); - }); - - after(async () => await fixture.clean()); - - it('Does not render any