Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Content Collection cache (experimental) #8854

Merged
merged 62 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
833705c
wip: content collection refactor
natemoo-re Oct 2, 2023
73ee8dc
chore: move away from index file
natemoo-re Oct 17, 2023
3ffa3e2
wip: content collections refactor
natemoo-re Oct 17, 2023
fcc0bf3
chore: add changeset
natemoo-re Oct 17, 2023
bc7a115
fix: add renderers for content build
natemoo-re Oct 17, 2023
2df1f02
revert: renderers
natemoo-re Oct 17, 2023
4d7642b
feat: improve cache restoration
natemoo-re Oct 17, 2023
63b526b
fix: ensure empty chunk is not written
natemoo-re Oct 17, 2023
1f64229
fix: ensure content cache uses hashes
natemoo-re Oct 17, 2023
98b8022
fix: remove build warning
natemoo-re Oct 17, 2023
7238671
Update lovely-pianos-build.md
natemoo-re Oct 18, 2023
3f20c10
feat: improve output chunking
natemoo-re Oct 18, 2023
221b297
feat: improve cache restoration speed
natemoo-re Oct 18, 2023
df7f48d
feat: improve types
natemoo-re Oct 18, 2023
55c0407
fix: appease typescript
natemoo-re Oct 18, 2023
1744627
fix: get content collections working in dev again
natemoo-re Oct 18, 2023
618afa5
chore: remove TODO
natemoo-re Oct 18, 2023
bc3aac3
fix: ensure content build only runs if collections exist
natemoo-re Oct 18, 2023
d7d72dc
fix: content-module-template build extension
natemoo-re Oct 19, 2023
c2af53c
fix: noop lookupmap if content config does not exist
natemoo-re Oct 19, 2023
9606ec2
fix: getCollection _inside_ content directory
natemoo-re Oct 19, 2023
5d63dd2
chore: pass fs through dev server
natemoo-re Oct 20, 2023
591ef89
fix: use actual node fs for content config module
natemoo-re Oct 20, 2023
421a82a
chore: fix final unit tests!
natemoo-re Oct 25, 2023
1e261bd
fix(content): remove dev mode hacks
natemoo-re Oct 26, 2023
65bf4ab
Content Collections refactor cached build to happen in a single step …
natemoo-re Oct 31, 2023
32f20cd
Add `experimental.contentCollectionCache` config option (#8962)
natemoo-re Oct 31, 2023
84ccce0
chore: add experimental.contentCollectionCache tests
natemoo-re Oct 31, 2023
26deac7
chore: update changeset
natemoo-re Oct 31, 2023
853fbcc
fix: use import.meta.env?.DEV rather than import.meta.env.PROD
natemoo-re Oct 31, 2023
1dba3b0
feat(cache): add `--force` flag to content collection cache
natemoo-re Oct 31, 2023
95748be
chore: update changeset
natemoo-re Oct 31, 2023
3ed36ed
fix: only enable new method in dev + ssg
natemoo-re Nov 1, 2023
3372367
fix: test with bad reference
natemoo-re Nov 1, 2023
a8ea05d
fix: typo
natemoo-re Nov 2, 2023
d458ca6
chore: use const for resolved `astro:content` id
natemoo-re Nov 2, 2023
4961fc0
fix: test typo
natemoo-re Nov 2, 2023
312d3e2
feat(assets): move asset propagation logic to internals
natemoo-re Nov 2, 2023
6b8fa10
fix: ensure cache is removed when `--force` is passed
natemoo-re Nov 2, 2023
12a353b
Set pageData.propagatedStyles instead
matthewp Nov 2, 2023
2e0357e
Slight tweek
matthewp Nov 2, 2023
1577595
Encode names
matthewp Nov 3, 2023
0f82907
Handle css
matthewp Nov 3, 2023
a54d8bb
test: fix missing reference
natemoo-re Nov 7, 2023
88fada1
chore: fix typo
natemoo-re Nov 7, 2023
8cd15ae
fix: restore cache to content root
natemoo-re Nov 7, 2023
df7d132
feat: move output location for astro internals
natemoo-re Nov 8, 2023
adbc5e4
fix: remove cleanup after --force runs
natemoo-re Nov 8, 2023
83006f3
fix: restore client entrypoints from the build
natemoo-re Nov 8, 2023
a6a6018
fix: temporarily remove content collection cache if inline stylesheet…
natemoo-re Nov 8, 2023
ec5a32c
fix: appease typescript
natemoo-re Nov 8, 2023
34d5dde
chore: skip failing tests
natemoo-re Nov 8, 2023
ea0f630
Update .changeset/lovely-pianos-build.md
natemoo-re Nov 8, 2023
c25ee9b
fix: generate chunks in a unique folder
natemoo-re Nov 8, 2023
d5d4ab3
Update lovely-pianos-build.md
natemoo-re Nov 8, 2023
79c8819
Update lovely-pianos-build.md
natemoo-re Nov 8, 2023
240fbfc
chore: add comment
natemoo-re Nov 8, 2023
094ebe3
chore: add comments
natemoo-re Nov 8, 2023
dea43c7
chore: add comment
natemoo-re Nov 8, 2023
d9e54d5
test: ensure values are sorted
natemoo-re Nov 9, 2023
bf46f4c
Merge branch 'main' into refactor/content-collections
natemoo-re Nov 9, 2023
3d61b49
Merge branch 'main' into refactor/content-collections
natemoo-re Nov 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .changeset/lovely-pianos-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'astro': minor
---

Provides a new, experimental build cache for [Content Collections](https://docs.astro.build/en/guides/content-collections/). This includes multiple refactors to Astro's build process to optimize how Content Collections are handled, which should provide significant performance improvements for users with many collections.

You can enable the build cache by adding the following flag to your Astro config:
natemoo-re marked this conversation as resolved.
Show resolved Hide resolved

```js
// astro.config.mjs
export default {
experimental: {
contentCollectionCache: true,
},
};
```

When this experimental feature is enabled, you can bypass the local build cache by passing the `--force` flag to `astro build`.
natemoo-re marked this conversation as resolved.
Show resolved Hide resolved

```
astro build --force
```
18 changes: 4 additions & 14 deletions packages/astro/content-module.template.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,18 @@ import {
createReference,
} from 'astro/content/runtime';

export { defineCollection } from 'astro/content/runtime';
export { z } from 'astro/zod';

const contentDir = '@@CONTENT_DIR@@';

const contentEntryGlob = import.meta.glob('@@CONTENT_ENTRY_GLOB_PATH@@', {
query: { astroContentCollectionEntry: true },
});
const contentEntryGlob = '@@CONTENT_ENTRY_GLOB_PATH@@';
const contentCollectionToEntryMap = createCollectionToGlobResultMap({
globResult: contentEntryGlob,
contentDir,
});

const dataEntryGlob = import.meta.glob('@@DATA_ENTRY_GLOB_PATH@@', {
query: { astroDataCollectionEntry: true },
});
const dataEntryGlob = '@@DATA_ENTRY_GLOB_PATH@@';
const dataCollectionToEntryMap = createCollectionToGlobResultMap({
globResult: dataEntryGlob,
contentDir,
Expand All @@ -45,19 +42,12 @@ function createGlobLookup(glob) {
};
}

const renderEntryGlob = import.meta.glob('@@RENDER_ENTRY_GLOB_PATH@@', {
query: { astroRenderContent: true },
});
const renderEntryGlob = '@@RENDER_ENTRY_GLOB_PATH@@'
const collectionToRenderEntryMap = createCollectionToGlobResultMap({
globResult: renderEntryGlob,
contentDir,
});

export function defineCollection(config) {
if (!config.type) config.type = 'content';
return config;
}

export const getCollection = createGetCollection({
contentCollectionToEntryMap,
dataCollectionToEntryMap,
Expand Down
18 changes: 18 additions & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1527,6 +1527,24 @@ export interface AstroUserConfig {
*/
routingStrategy: 'prefix-always' | 'prefix-other-locales';
};
/**
* @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;
};
}

Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/assets/vite-plugin-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default function assets({
extendManualChunks(outputOptions, {
after(id) {
if (id.includes('astro/dist/assets/services/')) {
return `astro-assets-services`;
return `astro/assets-service`;
natemoo-re marked this conversation as resolved.
Show resolved Hide resolved
}
},
});
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/cli/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ export async function build({ flags }: BuildOptions) {

const inlineConfig = flagsToAstroInlineConfig(flags);

await _build(inlineConfig);
await _build(inlineConfig, { force: flags.force ?? false });
}
1 change: 1 addition & 0 deletions packages/astro/src/content/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const CONTENT_FLAG = 'astroContentCollectionEntry';
export const DATA_FLAG = 'astroDataCollectionEntry';

export const VIRTUAL_MODULE_ID = 'astro:content';
export const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID;
export const LINKS_PLACEHOLDER = '@@ASTRO-LINKS@@';
export const STYLES_PLACEHOLDER = '@@ASTRO-STYLES@@';
export const SCRIPTS_PLACEHOLDER = '@@ASTRO-SCRIPTS@@';
Expand Down
7 changes: 6 additions & 1 deletion packages/astro/src/content/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ type GlobResult = Record<string, LazyImport>;
type CollectionToEntryMap = Record<string, GlobResult>;
type GetEntryImport = (collection: string, lookupId: string) => Promise<LazyImport>;

export function defineCollection(config: any) {
if (!config.type) config.type = 'content';
return config;
}

export function createCollectionToGlobResultMap({
globResult,
contentDir,
Expand Down Expand Up @@ -69,7 +74,7 @@ export function createGetCollection({
let entries: any[] = [];
// Cache `getCollection()` calls in production only
// prevents stale cache in development
if (import.meta.env.PROD && cacheEntriesByCollection.has(collection)) {
if (!import.meta.env?.DEV && cacheEntriesByCollection.has(collection)) {
// Always return a new instance so consumers can safely mutate it
entries = [...cacheEntriesByCollection.get(collection)!];
} else {
Expand Down
9 changes: 9 additions & 0 deletions packages/astro/src/content/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,15 @@ export function parseFrontmatter(fileContents: string) {
*/
export const globalContentConfigObserver = contentObservable({ status: 'init' });

export function hasAnyContentFlag(viteId: string): boolean {
const flags = new URLSearchParams(viteId.split('?')[1] ?? '');
const flag = Array.from(flags.keys()).at(0);
if (typeof flag !== 'string') {
return false;
}
return CONTENT_FLAGS.includes(flag as any);
}

export function hasContentFlag(viteId: string, flag: (typeof CONTENT_FLAGS)[number]): boolean {
const flags = new URLSearchParams(viteId.split('?')[1] ?? '');
return flags.has(flag);
Expand Down
78 changes: 56 additions & 22 deletions packages/astro/src/content/vite-plugin-content-assets.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { extname } from 'node:path';
import { pathToFileURL } from 'node:url';
import type { Plugin } from 'vite';
import type { Plugin, Rollup } from 'vite';
import type { AstroSettings } from '../@types/astro.js';
import { moduleIsTopLevelPage, walkParentInfos } from '../core/build/graph.js';
import { getPageDataByViteID, type BuildInternals } from '../core/build/internal.js';
Expand Down Expand Up @@ -110,16 +110,16 @@ export function astroConfigBuildPlugin(
options: StaticBuildOptions,
internals: BuildInternals
): AstroBuildPlugin {
let ssrPluginContext: any = undefined;
let ssrPluginContext: Rollup.PluginContext | undefined = undefined;
return {
build: 'ssr',
targets: ['server'],
hooks: {
'build:before': ({ build }) => {
'build:before': ({ target }) => {
return {
vitePlugin: {
name: 'astro:content-build-plugin',
generateBundle() {
if (build === 'ssr') {
if (target === 'server') {
ssrPluginContext = this;
}
},
Expand All @@ -144,24 +144,43 @@ export function astroConfigBuildPlugin(
let entryLinks = new Set<string>();
let entryScripts = new Set<string>();

for (const id of Object.keys(chunk.modules)) {
for (const [pageInfo] of walkParentInfos(id, ssrPluginContext)) {
if (moduleIsTopLevelPage(pageInfo)) {
const pageViteID = pageInfo.id;
const pageData = getPageDataByViteID(internals, pageViteID);
if (!pageData) continue;
if (options.settings.config.experimental.contentCollectionCache) {
// TODO: hoisted scripts are still handled on the pageData rather than the asset propagation point
for (const id of chunk.moduleIds) {
const _entryCss = internals.propagatedStylesMap.get(id);
const _entryScripts = internals.propagatedScriptsMap.get(id);
if (_entryCss) {
for (const value of _entryCss) {
if (value.type === 'inline') entryStyles.add(value.content);
if (value.type === 'external') entryLinks.add(value.src);
}
}
if (_entryScripts) {
for (const value of _entryScripts) {
entryScripts.add(value);
}
}
}
} else {
for (const id of Object.keys(chunk.modules)) {
for (const [pageInfo] of walkParentInfos(id, ssrPluginContext!)) {
if (moduleIsTopLevelPage(pageInfo)) {
const pageViteID = pageInfo.id;
const pageData = getPageDataByViteID(internals, pageViteID);
if (!pageData) continue;

const _entryCss = pageData.propagatedStyles?.get(id);
const _entryScripts = pageData.propagatedScripts?.get(id);
if (_entryCss) {
for (const value of _entryCss) {
if (value.type === 'inline') entryStyles.add(value.content);
if (value.type === 'external') entryLinks.add(value.src);
const _entryCss = internals.propagatedStylesMap?.get(id);
const _entryScripts = pageData.propagatedScripts?.get(id);
if (_entryCss) {
for (const value of _entryCss) {
if (value.type === 'inline') entryStyles.add(value.content);
if (value.type === 'external') entryLinks.add(value.src);
}
}
}
if (_entryScripts) {
for (const value of _entryScripts) {
entryScripts.add(value);
if (_entryScripts) {
for (const value of _entryScripts) {
entryScripts.add(value);
}
}
}
}
Expand All @@ -174,12 +193,22 @@ export function astroConfigBuildPlugin(
JSON.stringify(STYLES_PLACEHOLDER),
JSON.stringify(Array.from(entryStyles))
);
} else {
newCode = newCode.replace(
JSON.stringify(STYLES_PLACEHOLDER),
"[]"
);
}
if (entryLinks.size) {
newCode = newCode.replace(
JSON.stringify(LINKS_PLACEHOLDER),
JSON.stringify(Array.from(entryLinks).map(prependBase))
);
} else {
newCode = newCode.replace(
JSON.stringify(LINKS_PLACEHOLDER),
"[]"
);
}
if (entryScripts.size) {
const entryFileNames = new Set<string>();
Expand All @@ -205,8 +234,13 @@ export function astroConfigBuildPlugin(
}))
)
);
} else {
newCode = newCode.replace(
JSON.stringify(SCRIPTS_PLACEHOLDER),
"[]"
);
}
mutate(chunk, 'server', newCode);
mutate(chunk, ['server'], newCode);
}
}
},
Expand Down
Loading