Skip to content

Commit

Permalink
Merge branch 'alternate/cache-definition-instead-of-render' of https:…
Browse files Browse the repository at this point in the history
…//github.com/astro-expansion/domain-expansion into alternate/cache-definition-instead-of-render
  • Loading branch information
louisescher committed Dec 26, 2024
2 parents 5e5b906 + 7b1abb2 commit 7e36882
Show file tree
Hide file tree
Showing 44 changed files with 2,170 additions and 138 deletions.
5 changes: 5 additions & 0 deletions .changeset/mean-experts-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@domain-expansion/astro": minor
---

Add configuration for components using shared state
5 changes: 4 additions & 1 deletion .changeset/pre.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@
"initialVersions": {
"docs": "0.0.1",
"@domain-expansion/astro": "0.0.0",
"playground": "0.0.1"
"playground": "0.0.1",
"@domain-expansion-test/basic": "0.0.0"
},
"changesets": [
"angry-ties-poke",
"fresh-walls-repeat",
"healthy-bobcats-allow",
"mean-experts-pretend",
"nice-crabs-smash",
"selfish-laws-protect",
"sharp-lemons-drop",
"six-poems-notice",
"stupid-deers-think",
"ten-hornets-exercise"
]
}
5 changes: 5 additions & 0 deletions .changeset/stupid-deers-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@domain-expansion/astro": minor
---

Eagerly normalize loaded chunks
14 changes: 14 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# docs

## 0.0.1-beta.6

### Patch Changes

- Updated dependencies [cf9e4ea]
- @domain-expansion/astro@0.1.0-beta.7

## 0.0.1-beta.5

### Patch Changes

- Updated dependencies [8684bcb]
- @domain-expansion/astro@0.1.0-beta.6

## 0.0.1-beta.4

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "docs",
"type": "module",
"private": true,
"version": "0.0.1-beta.4",
"version": "0.0.1-beta.6",
"scripts": {
"dev": "astro dev",
"start": "node ./dist/server/entry.mjs",
Expand Down
12 changes: 12 additions & 0 deletions package/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# @domain-expansion/astro

## 0.1.0-beta.7

### Minor Changes

- cf9e4ea: Eagerly normalize loaded chunks

## 0.1.0-beta.6

### Minor Changes

- 8684bcb: Add configuration for components using shared state

## 0.1.0-beta.5

### Minor Changes
Expand Down
20 changes: 14 additions & 6 deletions package/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@domain-expansion/astro",
"version": "0.1.0-beta.5",
"version": "0.1.0-beta.7",
"description": "Expanding™ the™ bits™ since™ 2024-12-12™",
"contributors": [
"Luiz Ferraz (https://github.com/Fryuni)",
Expand All @@ -18,7 +18,7 @@
"publishConfig": {
"access": "public"
},
"sideEffects": false,
"sideEffects": true,
"exports": {
".": {
"types": "./dist/index.d.ts",
Expand All @@ -30,11 +30,14 @@
],
"scripts": {
"dev": "tsup --watch",
"build": "tsup"
"build": "tsup",
"test": "vitest run",
"test:dev": "vitest",
"test:debug": "vitest run --inspect --no-file-parallelism --testTimeout 1000000"
},
"type": "module",
"peerDependencies": {
"astro": "^5.0.0"
"astro": "catalog:"
},
"dependencies": {
"@inox-tools/utils": "^0.3.0",
Expand All @@ -48,12 +51,17 @@
"pathe": "^1.1.2"
},
"devDependencies": {
"astro-integration-kit": "^0.17.0",
"@inox-tools/astro-tests": "^0.2.1",
"@types/debug": "^4.1.12",
"@types/hash-sum": "^1.0.2",
"@types/node": "^22.10.2",
"@vitest/coverage-v8": "2.1.8",
"@vitest/ui": "^2.1.8",
"astro-integration-kit": "^0.17.0",
"jest-extended": "^4.0.2",
"rollup": "^4.29.0",
"tsup": "^8.3.5",
"vite": "^6.0.3"
"vite": "^6.0.3",
"vitest": "^2.1.8"
}
}
14 changes: 13 additions & 1 deletion package/src/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { type MaybePromise, type Thunk } from "./utils.js";
import { type PersistedMetadata, RenderFileStore } from "./renderFileStore.js";
import { inMemoryCacheHit, inMemoryCacheMiss } from "./metrics.js";
import { MemoryCache } from "./inMemoryLRU.js";
import { FactoryValueClone } from "./factoryValueClone.ts";

const debug = rootDebug.extend('cache');

Expand All @@ -26,6 +27,17 @@ export class Cache {
return this.persisted.initialize();
}

public async flush(): Promise<void> {
await this.persisted.flush();

this.valueCache.clear();
this.metadataCache.clear();

const self = this as any;
delete self.valueCache;
delete self.metadataCache;
}

public saveRenderValue({ key, factoryValue, ...options }: {
key: string,
factoryValue: AstroFactoryReturnValue,
Expand All @@ -34,7 +46,7 @@ export class Cache {
}): Promise<ValueThunk> {
const promise = options.persist
? this.persisted.saveRenderValue(key, factoryValue)
: RenderFileStore.denormalizeValue(factoryValue).then(result => result.clone);
: FactoryValueClone.makeResultClone(factoryValue);
if (!options.skipInMemory) this.valueCache.storeLoading(key, promise);
return promise;
}
Expand Down
39 changes: 33 additions & 6 deletions package/src/contextTracking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import * as fs from "node:fs";
import type { renderEntry } from "astro/content/runtime";
import type { UnresolvedImageTransform } from "astro";
import { getSystemErrorName, types } from "node:util";
import { createResolver } from "astro-integration-kit";
import type { Cache } from './cache.js';

export type ContextTracking = {
assetServiceCalls: Array<{
Expand All @@ -27,6 +29,28 @@ export type ContextTracking = {
const debug = rootDebug.extend('context-tracking');
const contextTracking = new AsyncLocalStorage<ContextTracking>();

let cachingOptions: {
cache: Cache,
root: string,
routeEntrypoints: string[],
componentHashes: Map<string, string>,
cacheComponents: false | 'in-memory' | 'persistent',
cachePages: boolean,
componentsHaveSharedState: boolean,
resolver: ReturnType<typeof createResolver>['resolve'],
};

export function setCachingOptions(options: Omit<typeof cachingOptions, 'resolver'>) {
cachingOptions = {
...options,
resolver: createResolver(options.root).resolve,
};
}

export function getCachingOptions(): typeof cachingOptions {
return cachingOptions;
}

export function makeContextTracking(): {
runIn: <T>(fn: () => T) => T,
collect: () => ContextTracking,
Expand Down Expand Up @@ -69,15 +93,14 @@ export function getCurrentContext(): ContextTracking | undefined {

const assetTrackingSym = Symbol.for('@domain-expansion:astro-asset-tracking');
(globalThis as any)[assetTrackingSym] = (original: typeof getImage): typeof getImage => {
runtime.getImage = original;
debug('Wrapping getImage');
return async (options) => {
return runtime.getImage = async (options) => {
const result = await original(options);

const context = contextTracking.getStore();
if (context) {
const val: PersistedMetadata['assetServiceCalls'][number] = {
options,
options: result.rawOptions,
resultingAttributes: result.attributes,
};
debug('Collected getImage call', val);
Expand All @@ -90,7 +113,12 @@ const assetTrackingSym = Symbol.for('@domain-expansion:astro-asset-tracking');

export async function computeEntryHash(filePath: string): Promise<string> {
try {
return createHash('sha1').update(await fs.promises.readFile(filePath)).digest().toString('hex');
return createHash('sha1')
.update(
await fs.promises.readFile(cachingOptions.resolver(filePath)),
)
.digest()
.toString('hex');
} catch (err) {
if (
types.isNativeError(err)
Expand All @@ -108,9 +136,8 @@ export async function computeEntryHash(filePath: string): Promise<string> {

const ccRenderTrackingSym = Symbol.for('@domain-expansion:astro-cc-render-tracking');
(globalThis as any)[ccRenderTrackingSym] = (original: typeof renderEntry): typeof renderEntry => {
runtime.renderEntry = original;
debug('Wrapping renderEntry');
return async (entry) => {
return runtime.renderEntry = async (entry) => {
const context = contextTracking.getStore();
if (!context) return original(entry);

Expand Down
75 changes: 75 additions & 0 deletions package/src/factoryValueClone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import type { RenderTemplateResult } from "astro/runtime/server/render/astro/render-template.js";
import { runtime, type Thunk } from "./utils.ts";
import type { RenderDestination, RenderDestinationChunk } from "astro/runtime/server/render/common.js";
import type { HeadAndContent } from "astro/runtime/server/render/astro/head-and-content.js";
import type { AstroFactoryReturnValue } from "astro/runtime/server/render/astro/factory.js";

export namespace FactoryValueClone {
export function makeResultClone(
value: AstroFactoryReturnValue
): Promise<Thunk<AstroFactoryReturnValue>> {
if (value instanceof Response) {
return makeResponseClone(value);
}

if (runtime.isHeadAndContent(value)) {
return makeHeadAndContentClone(value);
}

return makeRenderTemplateClone(value);
}

export async function makeResponseClone(value: Response): Promise<Thunk<Response>> {
const body = await value.arrayBuffer();
return () => new Response(body, value);
}

export async function makeRenderTemplateClone(
value: RenderTemplateResult
): Promise<Thunk<RenderTemplateResult>> {
const chunks = await renderTemplateToChunks(value);
return () => renderTemplateFromChunks(chunks);
}

export async function makeHeadAndContentClone(
value: HeadAndContent,
): Promise<Thunk<HeadAndContent>> {
const chunks = await renderTemplateToChunks(value.content);
return () => runtime.createHeadAndContent(value.head, renderTemplateFromChunks(chunks));
}

export function renderTemplateFromChunks(
chunks: RenderDestinationChunk[]
): RenderTemplateResult {
const template = runtime.renderTemplate(Object.assign([], { raw: [] }));

return Object.assign(template, {
render: (destination: RenderDestination) => {
return new Promise<void>(resolve => {
setImmediate(() => {
for (const chunk of chunks) {
destination.write(chunk);
}

resolve();
});
});
}
})
}

export async function renderTemplateToChunks(value: RenderTemplateResult): Promise<RenderDestinationChunk[]> {
const chunks: RenderDestinationChunk[] = [];

const cachedDestination: RenderDestination = {
write(chunk) {
// Drop empty chunks
if (chunk) chunks.push(chunk);
},
};

await value.render(cachedDestination);

return chunks;
}
}
4 changes: 4 additions & 0 deletions package/src/inMemoryLRU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,8 @@ export class MemoryCache<T> {
this.#cache.delete(key);
});
}

public clear(): void {
this.#cache.clear();
}
}
Loading

0 comments on commit 7e36882

Please sign in to comment.