mirror of
https://github.com/withastro/astro.git
synced 2024-12-30 22:03:56 -05:00
wip(cache): add full module graph implementation
This commit is contained in:
parent
e63aac94ca
commit
e5743c334c
3 changed files with 419 additions and 94 deletions
177
packages/astro/src/core/build/plugins/cache/module-graph.ts
vendored
Normal file
177
packages/astro/src/core/build/plugins/cache/module-graph.ts
vendored
Normal file
|
@ -0,0 +1,177 @@
|
|||
import { slash } from "@astrojs/internal-helpers/path";
|
||||
import { builtinModules } from "node:module";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import fs from 'node:fs';
|
||||
import { createHash } from 'node:crypto';
|
||||
|
||||
export function resolvePackageVersion(specifier: string, require: NodeRequire) {
|
||||
try {
|
||||
// External packages will not be resolved
|
||||
// We try to resolve their package.json version
|
||||
const pkgInfo = require(`${specifier}/package.json`);
|
||||
if (pkgInfo.version) {
|
||||
return pkgInfo.version;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export function checksum(path: fs.PathLike): string {
|
||||
return checksumData(fs.readFileSync(path));
|
||||
}
|
||||
|
||||
export function checksumData(data: Buffer | string): string {
|
||||
return createHash('sha1').update(data).digest('hex');
|
||||
}
|
||||
|
||||
export interface SerializedModuleGraph {
|
||||
/** Track the version of this graph format to invalidate the entire graph in case of format changes */
|
||||
version: string;
|
||||
checksums: Record<string, string>;
|
||||
graph: SerializedModuleNode[];
|
||||
}
|
||||
|
||||
export class ModuleGraph {
|
||||
// IMPORTANT: Update this version when making significant changes to the manifest format.
|
||||
// Only manifests generated with the same version number can be compared.
|
||||
static currentVersion = '1';
|
||||
version = ModuleGraph.currentVersion;
|
||||
|
||||
#nodes = new Map<string, ModuleNode>();
|
||||
#entrypoints = new Map<string, ModuleNode>();
|
||||
#root: string;
|
||||
|
||||
checksums = new Map<string, string>();
|
||||
|
||||
rehydrate(data: SerializedModuleGraph) {
|
||||
this.version = data.version;
|
||||
this.checksums = new Map(Object.entries(data.checksums));
|
||||
for (const item of data.graph) {
|
||||
const node = this.get(item.id);
|
||||
for (const importer of item.importers) {
|
||||
node.addImporter(importer);
|
||||
}
|
||||
for (const imported of item.imports) {
|
||||
node.addImport(imported);
|
||||
}
|
||||
if (item.isEntry) {
|
||||
this.addEntrypoint(item.id);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
constructor(root: URL) {
|
||||
this.#root = fileURLToPath(root);
|
||||
}
|
||||
|
||||
get entrypoints() {
|
||||
return this.#entrypoints.values();
|
||||
}
|
||||
|
||||
get components() {
|
||||
return Array.from(this.#nodes.values()).filter(n => n.id.startsWith('src/components/'));
|
||||
}
|
||||
|
||||
normalizeId(id: string): string {
|
||||
if (id.includes('?')) id = id.split('?')[0];
|
||||
if (id.startsWith(this.#root)) id = id.slice(this.#root.length);
|
||||
if (id.includes('\\')) id = slash(id);
|
||||
if (id.startsWith('/')) id = id.slice(1);
|
||||
if (id.startsWith('node_modules')) {
|
||||
return id.split('node_modules').at(-1)?.split('/').filter(x => x).at(0) ?? id;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
get(id: string) {
|
||||
id = this.normalizeId(id);
|
||||
if (this.#nodes.has(id)) return this.#getNode(id);
|
||||
return this.#addNode(id);
|
||||
}
|
||||
|
||||
addEntrypoint(id: string) {
|
||||
id = this.normalizeId(id);
|
||||
const node = this.get(id);
|
||||
node.isEntry = true;
|
||||
this.#entrypoints.set(node.id, node);
|
||||
}
|
||||
|
||||
#getNode(id: string) {
|
||||
id = this.normalizeId(id);
|
||||
return this.#nodes.get(id)!;
|
||||
}
|
||||
|
||||
#addNode(id: string) {
|
||||
id = this.normalizeId(id);
|
||||
const node = new ModuleNode(this, id);
|
||||
this.#nodes.set(id, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
toJSON(): SerializedModuleGraph {
|
||||
const checksums = Object.fromEntries(this.checksums.entries());
|
||||
const graph = Array.from(this.#nodes.values()).map(node => node.toJSON());
|
||||
return { version: this.version, checksums, graph }
|
||||
}
|
||||
}
|
||||
|
||||
export class ModuleNode {
|
||||
#graph: ModuleGraph;
|
||||
#importers = new Set<string>();
|
||||
#imports = new Set<string>();
|
||||
isEntry = false;
|
||||
|
||||
constructor(graph: ModuleGraph, public id: string) {
|
||||
this.#graph = graph;
|
||||
}
|
||||
|
||||
addImporter(id: string) {
|
||||
this.#importers.add(id);
|
||||
}
|
||||
addImport(id: string) {
|
||||
this.#imports.add(id);
|
||||
}
|
||||
|
||||
get importers() {
|
||||
return new Map(Array.from(this.#importers.values()).map(id => ([id, this.#graph.get(id)])));
|
||||
}
|
||||
get imports() {
|
||||
return new Map(Array.from(this.#imports.values()).map(id => ([id, this.#graph.get(id)])));
|
||||
}
|
||||
|
||||
doesImport(id: string, seen = new WeakSet<ModuleNode>()): boolean {
|
||||
seen.add(this);
|
||||
if (id === this.id) return true;
|
||||
if (seen.has(this)) return false;
|
||||
for (const imported of this.imports.values()) {
|
||||
seen.add(imported);
|
||||
if (imported.id === id) return true;
|
||||
if (imported.doesImport(id, seen)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
toJSON(): SerializedModuleNode {
|
||||
const isEntry = this.isEntry || undefined;
|
||||
return { id: this.id, isEntry, importers: Array.from(this.#importers), imports: Array.from(this.#imports) }
|
||||
}
|
||||
}
|
||||
|
||||
export interface SerializedModuleNode {
|
||||
id: string;
|
||||
isEntry?: boolean;
|
||||
importers: string[];
|
||||
imports: string[];
|
||||
}
|
||||
|
||||
export function isTrackableModule(id: string) {
|
||||
if (id.startsWith('\0astro:')) return true;
|
||||
if (id.startsWith('\0') || id.startsWith('virtual:')) return false
|
||||
if (id.startsWith('node:')) return false
|
||||
if (builtinModules.includes(id)) return false
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isDependency(id: string) {
|
||||
if (!id.includes('node_modules')) return false
|
||||
return true;
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import { createHash } from 'node:crypto';
|
||||
import fsMod from 'node:fs';
|
||||
import fs from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import pLimit from 'p-limit';
|
||||
import { normalizePath, type Plugin as VitePlugin } from 'vite';
|
||||
|
@ -18,9 +17,22 @@ import { copyFiles } from '../static-build.js';
|
|||
import type { StaticBuildOptions } from '../types.js';
|
||||
import { encodeName } from '../util.js';
|
||||
import { extendManualChunks } from './util.js';
|
||||
import { isTrackableModule, checksum, ModuleGraph, ModuleNode, resolvePackageVersion } from './cache/module-graph.js';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
const CONTENT_CACHE_DIR = './content/';
|
||||
const CONTENT_MANIFEST_FILE = './manifest.json';
|
||||
function getEntrypointsFromModuleNode(input: ModuleNode): ModuleNode[] {
|
||||
const result = new Set<ModuleNode>(input.isEntry ? [input] : []);
|
||||
for (const importer of input.importers.values()) {
|
||||
if (result.has(importer)) break;
|
||||
for (const node of getEntrypointsFromModuleNode(importer)) {
|
||||
result.add(node);
|
||||
}
|
||||
}
|
||||
return Array.from(result);
|
||||
}
|
||||
|
||||
const BUILD_CACHE_DIR = './build/';
|
||||
const BUILD_MANIFEST_FILE = './astro.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 = 0;
|
||||
|
@ -48,6 +60,157 @@ function createContentManifest(): ContentManifest {
|
|||
return { version: -1, entries: [], serverEntries: [], clientEntries: [] };
|
||||
}
|
||||
|
||||
function vitePluginModuleGraph(opts: StaticBuildOptions, lookupMap: ContentLookupMap): VitePlugin {
|
||||
const graph = new ModuleGraph(opts.settings.config.root);
|
||||
const rootPath = fileURLToPath(opts.settings.config.root);
|
||||
const require = createRequire(opts.settings.config.root);
|
||||
const packageResolutions = new Map<string, string>();
|
||||
|
||||
const { config } = opts.settings;
|
||||
const { cacheDir } = config;
|
||||
const buildCacheDir = new URL(BUILD_CACHE_DIR, cacheDir);
|
||||
const buildManifestFile = new URL(BUILD_MANIFEST_FILE, buildCacheDir);
|
||||
|
||||
function resolveId(id: string): string {
|
||||
// Intentionally using `||` instead of `??` here to catch empty strings
|
||||
return packageResolutions.get(id) || graph.normalizeId(id);
|
||||
}
|
||||
|
||||
const content = graph.get('astro:content');
|
||||
for (const { entries } of Object.values(lookupMap)) {
|
||||
for (const entrypoint of Object.values(entries)) {
|
||||
const node = graph.get(entrypoint);
|
||||
graph.checksums.set(graph.normalizeId(entrypoint), checksum(new URL(`./${entrypoint}`, opts.settings.config.root)));
|
||||
graph.addEntrypoint(entrypoint);
|
||||
node.addImporter('astro:content');
|
||||
content.addImport(entrypoint)
|
||||
}
|
||||
}
|
||||
|
||||
let oldModuleGraph: ModuleGraph | undefined;
|
||||
|
||||
const plugin: VitePlugin = {
|
||||
name: 'astro:module-graph',
|
||||
enforce: 'post',
|
||||
options(options) {
|
||||
if (Array.isArray(options.plugins)) {
|
||||
const index = options.plugins.findIndex(v => typeof v === 'object' && !Array.isArray(v) && v && v.name === 'astro:module-graph');
|
||||
options.plugins.splice(index, 1);
|
||||
options.plugins.splice(0, 0, plugin);
|
||||
}
|
||||
|
||||
if (fs.existsSync(buildManifestFile)) {
|
||||
const oldManifest = fs.readFileSync(buildManifestFile, { encoding: 'utf8' })
|
||||
oldModuleGraph = new ModuleGraph(opts.settings.config.root);
|
||||
oldModuleGraph.rehydrate(JSON.parse(oldManifest));
|
||||
if (oldModuleGraph.version !== ModuleGraph.currentVersion) {
|
||||
fs.rmSync(buildManifestFile);
|
||||
return;
|
||||
}
|
||||
let invalidatedNodes = new Set<ModuleNode>();
|
||||
for (let [id, oldChecksum] of oldModuleGraph.checksums) {
|
||||
if (id.startsWith('dep:')) {
|
||||
id = id.replace(/^dep\:/, '');
|
||||
const newChecksum = resolvePackageVersion(id, require);
|
||||
if (newChecksum === oldChecksum) continue;
|
||||
} else {
|
||||
const fileURL = new URL('./' + id, opts.settings.config.root);
|
||||
const newChecksum = checksum(fileURL);
|
||||
if (newChecksum === oldChecksum) continue;
|
||||
}
|
||||
invalidatedNodes.add(oldModuleGraph.get(id));
|
||||
}
|
||||
|
||||
const invalidatedEntrypoints = new Set<ModuleNode>();
|
||||
console.log(' node', Array.from(invalidatedNodes).map(n => n.id));
|
||||
for (const invalidatedNode of invalidatedNodes) {
|
||||
for (const entrypoint of getEntrypointsFromModuleNode(invalidatedNode)) {
|
||||
invalidatedEntrypoints.add(entrypoint);
|
||||
}
|
||||
}
|
||||
console.log('entrypoint', Array.from(invalidatedEntrypoints).map(n => n.id));
|
||||
}
|
||||
},
|
||||
async resolveId(id, importer) {
|
||||
const resolution = await this.resolve(id, importer, { skipSelf: true });
|
||||
if (resolution?.id) {
|
||||
const resolvedId = resolution.id;
|
||||
if (!isTrackableModule(resolvedId)) return;
|
||||
if (graph.checksums.has(resolvedId) || graph.checksums.has(`dep:${resolvedId}`)) return;
|
||||
if (resolvedId.startsWith(rootPath) && !resolvedId.includes('node_modules')) {
|
||||
graph.checksums.set(graph.normalizeId(resolvedId), checksum(graph.normalizeId(resolvedId)));
|
||||
return;
|
||||
}
|
||||
if (packageResolutions.has(resolvedId)) return;
|
||||
let packageSpecifier = id;
|
||||
if (id.startsWith('.')) {
|
||||
if (importer && packageResolutions.has(importer)) {
|
||||
packageResolutions.set(resolvedId, packageResolutions.get(importer)!);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (id.startsWith('@')) {
|
||||
packageSpecifier = id.split('/').slice(0, 2).join('/');
|
||||
} else {
|
||||
packageSpecifier = id.split('/').at(0)!;
|
||||
}
|
||||
packageResolutions.set(resolvedId, packageSpecifier);
|
||||
if (graph.checksums.has(`dep:${packageSpecifier}`)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// External packages will not be resolved
|
||||
// We try to resolve their package.json version
|
||||
const pkgInfo = require(`${packageSpecifier}/package.json`);
|
||||
if (pkgInfo.version) {
|
||||
graph.checksums.set(`dep:${packageSpecifier}`, pkgInfo.version);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
},
|
||||
async buildEnd() {
|
||||
for (let moduleId of this.getModuleIds()) {
|
||||
let isPackage = false;
|
||||
const info = this.getModuleInfo(moduleId);
|
||||
if (!info) continue;
|
||||
if (!isTrackableModule(info.id)) continue;
|
||||
const id = graph.normalizeId(resolveId(moduleId))
|
||||
if (packageResolutions.has(id)) {
|
||||
isPackage = true;
|
||||
}
|
||||
const node = graph.get(id);
|
||||
const isEntry = info.dynamicImporters.find(n => n.startsWith('\0@astro-page:'));
|
||||
if (isEntry) {
|
||||
graph.addEntrypoint(id);
|
||||
}
|
||||
for (const importer of info.importers) {
|
||||
if (!isTrackableModule(importer)) continue;
|
||||
node.addImporter(resolveId(importer));
|
||||
}
|
||||
for (const importer of info.dynamicImporters) {
|
||||
if (!isTrackableModule(importer)) continue;
|
||||
node.addImporter(resolveId(importer));
|
||||
}
|
||||
if (isPackage) continue;
|
||||
for (const imported of info.importedIds) {
|
||||
if (!isTrackableModule(imported)) continue;
|
||||
node.addImport(resolveId(imported));
|
||||
}
|
||||
for (const imported of info.dynamicallyImportedIds) {
|
||||
if (!isTrackableModule(imported)) continue;
|
||||
node.addImport(resolveId(imported));
|
||||
}
|
||||
}
|
||||
|
||||
await fs.promises.mkdir(new URL('./', buildManifestFile), { recursive: true });
|
||||
await fs.promises.writeFile(buildManifestFile, JSON.stringify(graph), {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
}
|
||||
}
|
||||
return plugin;
|
||||
}
|
||||
|
||||
function vitePluginContent(
|
||||
opts: StaticBuildOptions,
|
||||
lookupMap: ContentLookupMap,
|
||||
|
@ -56,56 +219,67 @@ function vitePluginContent(
|
|||
const { config } = opts.settings;
|
||||
const { cacheDir } = config;
|
||||
const distRoot = config.outDir;
|
||||
const distContentRoot = new URL('./content/', distRoot);
|
||||
const cachedChunks = new URL('./chunks/', opts.settings.config.cacheDir);
|
||||
const distChunks = new URL('./chunks/', opts.settings.config.outDir);
|
||||
const contentCacheDir = new URL(CONTENT_CACHE_DIR, cacheDir);
|
||||
const contentManifestFile = new URL(CONTENT_MANIFEST_FILE, contentCacheDir);
|
||||
const cache = contentCacheDir;
|
||||
const cacheTmp = new URL('./.tmp/', cache);
|
||||
let oldManifest = createContentManifest();
|
||||
const buildCacheDir = new URL(BUILD_CACHE_DIR, cacheDir);
|
||||
// const contentManifestFile = new URL(BUILD_MANIFEST_FILE, buildCacheDir);
|
||||
// let oldManifest = createContentManifest();
|
||||
let newManifest = createContentManifest();
|
||||
let entries: ContentEntries;
|
||||
// let entries: ContentEntries;
|
||||
let injectedEmptyFile = false;
|
||||
|
||||
if (fsMod.existsSync(contentManifestFile)) {
|
||||
try {
|
||||
const data = fsMod.readFileSync(contentManifestFile, { encoding: 'utf8' });
|
||||
oldManifest = JSON.parse(data);
|
||||
internals.cachedClientEntries = oldManifest.clientEntries;
|
||||
} catch {}
|
||||
}
|
||||
// if (fsMod.existsSync(contentManifestFile)) {
|
||||
// try {
|
||||
// const data = fsMod.readFileSync(contentManifestFile, { encoding: 'utf8' });
|
||||
// oldManifest = JSON.parse(data);
|
||||
// internals.cachedClientEntries = oldManifest.clientEntries;
|
||||
// } catch {}
|
||||
// }
|
||||
|
||||
return {
|
||||
name: '@astro/plugin-build-content',
|
||||
|
||||
async options(options) {
|
||||
let newOptions = Object.assign({}, options);
|
||||
newManifest = await generateContentManifest(opts, lookupMap);
|
||||
entries = getEntriesFromManifests(oldManifest, newManifest);
|
||||
// newManifest = await generateContentManifest(opts, lookupMap);
|
||||
// entries = getEntriesFromManifests(oldManifest, newManifest);
|
||||
const inputs: string[] = [];
|
||||
|
||||
// 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));
|
||||
// newOptions = addRollupInput(newOptions, inputs);
|
||||
for (const { type, entries: collectionEntries } of Object.values(lookupMap)) {
|
||||
for (const entrypoint of Object.values(collectionEntries)) {
|
||||
const fileURL = encodeURI(joinPaths(opts.settings.config.root.toString(), entrypoint));
|
||||
const input = fileURLToPath(fileURL);
|
||||
// Adds `/src/content/blog/post-1.md?astroContentCollectionEntry` as a top-level input
|
||||
const inputs = [`${input}?${collectionTypeToFlag(type)}`];
|
||||
inputs.push(`${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 from the previous build
|
||||
if (fsMod.existsSync(cachedChunks)) {
|
||||
await copyFiles(cachedChunks, distChunks, true);
|
||||
}
|
||||
// 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;
|
||||
|
||||
// // 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 from the previous build
|
||||
// if (fsMod.existsSync(buildCacheDir)) {
|
||||
// await copyFiles(buildCacheDir, distRoot, true);
|
||||
// }
|
||||
// // 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) {
|
||||
|
@ -160,7 +334,7 @@ function vitePluginContent(
|
|||
async generateBundle(_options, bundle) {
|
||||
const code = await generateContentEntryFile({
|
||||
settings: opts.settings,
|
||||
fs: fsMod,
|
||||
fs: fs,
|
||||
lookupMap,
|
||||
IS_DEV: false,
|
||||
IS_SERVER: false,
|
||||
|
@ -185,7 +359,6 @@ function vitePluginContent(
|
|||
// 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,
|
||||
|
@ -193,25 +366,15 @@ function vitePluginContent(
|
|||
// 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);
|
||||
await fsMod.promises.mkdir(contentCacheDir, { recursive: true });
|
||||
await fsMod.promises.writeFile(contentManifestFile, JSON.stringify(newManifest), {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
|
||||
const cacheExists = fsMod.existsSync(cache);
|
||||
fsMod.mkdirSync(cache, { recursive: true });
|
||||
await fsMod.promises.mkdir(cacheTmp, { recursive: true });
|
||||
await copyFiles(distContentRoot, cacheTmp, true);
|
||||
if (cacheExists) {
|
||||
await copyFiles(contentCacheDir, distContentRoot, false);
|
||||
}
|
||||
await copyFiles(cacheTmp, contentCacheDir);
|
||||
await fsMod.promises.rm(cacheTmp, { recursive: true, force: true });
|
||||
// await fsMod.promises.mkdir(new URL('./', contentManifestFile), { recursive: true });
|
||||
// await fsMod.promises.writeFile(contentManifestFile, JSON.stringify(newManifest), {
|
||||
// encoding: 'utf8',
|
||||
// });
|
||||
await copyFiles(distRoot, buildCacheDir);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -277,27 +440,23 @@ async function generateContentManifest(
|
|||
const limit = pLimit(10);
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
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)]);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
// 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)]);
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
await Promise.all(promises);
|
||||
return manifest;
|
||||
}
|
||||
|
||||
function checksum(data: string): string {
|
||||
return createHash('sha1').update(data).digest('base64');
|
||||
}
|
||||
|
||||
function collectionTypeToFlag(type: 'content' | 'data') {
|
||||
const name = type[0].toUpperCase() + type.slice(1);
|
||||
return `astro${name}CollectionEntry`;
|
||||
|
@ -307,8 +466,8 @@ export function pluginContent(
|
|||
opts: StaticBuildOptions,
|
||||
internals: BuildInternals
|
||||
): AstroBuildPlugin {
|
||||
const cachedChunks = new URL('./chunks/', opts.settings.config.cacheDir);
|
||||
const distChunks = new URL('./chunks/', opts.settings.config.outDir);
|
||||
const buildCacheDir = new URL(BUILD_CACHE_DIR, opts.settings.config.cacheDir);
|
||||
const outDir = opts.settings.config.outDir;
|
||||
|
||||
return {
|
||||
targets: ['server'],
|
||||
|
@ -320,24 +479,15 @@ export function pluginContent(
|
|||
if (isServerLikeOutput(opts.settings.config)) {
|
||||
return { vitePlugin: undefined };
|
||||
}
|
||||
if (fs.existsSync(buildCacheDir)) {
|
||||
await copyFiles(buildCacheDir, outDir, true);
|
||||
}
|
||||
|
||||
const lookupMap = await generateLookupMap({ settings: opts.settings, fs: fsMod });
|
||||
const lookupMap = await generateLookupMap({ settings: opts.settings, fs: fs });
|
||||
return {
|
||||
vitePlugin: vitePluginContent(opts, lookupMap, internals),
|
||||
vitePlugin: [vitePluginModuleGraph(opts, lookupMap), vitePluginContent(opts, lookupMap, internals)],
|
||||
};
|
||||
},
|
||||
|
||||
async 'build:post'() {
|
||||
if (!opts.settings.config.experimental.contentCollectionCache) {
|
||||
return;
|
||||
}
|
||||
if (isServerLikeOutput(opts.settings.config)) {
|
||||
return;
|
||||
}
|
||||
if (fsMod.existsSync(distChunks)) {
|
||||
await copyFiles(distChunks, cachedChunks, true);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -145,7 +145,6 @@ async function ssrBuild(
|
|||
input: Set<string>,
|
||||
container: AstroBuildPluginContainer
|
||||
) {
|
||||
const buildID = Date.now().toString();
|
||||
const { allPages, settings, viteConfig } = opts;
|
||||
const ssr = isServerLikeOutput(settings.config);
|
||||
const out = getOutputDirectory(settings.config);
|
||||
|
@ -185,7 +184,6 @@ async function ssrBuild(
|
|||
let suffix = '_[hash].mjs';
|
||||
|
||||
if (isContentCache) {
|
||||
prefix += `${buildID}/`;
|
||||
suffix = '.mjs';
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue