Skip to content

Commit 599677a

Browse files
authored
invalidate snippet cache when code changes (#290)
* invalidate snippet cache when code changes * incorporate lockfile * drive-by fix * use mtimeMs * belt and braces
1 parent f9b389e commit 599677a

File tree

1 file changed

+56
-2
lines changed

1 file changed

+56
-2
lines changed

packages/site-kit/src/lib/markdown/renderer.ts

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { codeToHtml, createCssVariablesTheme } from 'shiki';
88
import { transformerTwoslash } from '@shikijs/twoslash';
99
import { SHIKI_LANGUAGE_MAP, escape, normalizeSlugify, smart_quotes, transform } from './utils';
1010
import type { Modules } from './index';
11+
import { fileURLToPath } from 'node:url';
1112

1213
interface SnippetOptions {
1314
file: string | null;
@@ -508,6 +509,37 @@ function find_nearest_node_modules(file: string): string | null {
508509
return null;
509510
}
510511

512+
/**
513+
* Get the `mtime` of the most recently modified file in a dependency graph,
514+
* excluding imports from `node_modules`
515+
*/
516+
function get_mtime(file: string, seen = new Set<string>()) {
517+
if (seen.has(file)) return -1;
518+
seen.add(file);
519+
520+
let mtime = fs.statSync(file).mtimeMs;
521+
const content = fs.readFileSync(file, 'utf-8');
522+
523+
for (const [_, source] of content.matchAll(/^import(?:.+?\s+from\s+)?['"](.+)['"];?$/gm)) {
524+
if (source[0] !== '.') continue;
525+
526+
let resolved = path.resolve(file, '..', source);
527+
if (!fs.existsSync(resolved)) resolved += '.ts';
528+
if (!fs.existsSync(resolved))
529+
throw new Error(`Could not resolve ${source} relative to ${file}`);
530+
531+
mtime = Math.max(mtime, get_mtime(resolved, seen));
532+
}
533+
534+
return mtime;
535+
}
536+
537+
const mtime = Math.max(
538+
get_mtime(fileURLToPath(import.meta.url)),
539+
fs.statSync('node_modules').mtimeMs,
540+
fs.statSync('../../pnpm-lock.yaml').mtimeMs
541+
);
542+
511543
/**
512544
* Utility function to work with code snippet caching.
513545
*
@@ -526,12 +558,26 @@ async function create_snippet_cache(should: boolean) {
526558
const cache = new Map();
527559
const directory = find_nearest_node_modules(import.meta.url) + '/.snippets';
528560

561+
if (fs.existsSync(directory)) {
562+
for (const dir of fs.readdirSync(directory)) {
563+
if (!fs.statSync(`${directory}/${dir}`).isDirectory() || +dir < mtime) {
564+
fs.rmSync(`${directory}/${dir}`, { force: true, recursive: true });
565+
}
566+
}
567+
} else {
568+
fs.mkdirSync(directory);
569+
}
570+
571+
try {
572+
fs.mkdirSync(`${directory}/${mtime}`);
573+
} catch {}
574+
529575
function get_file(source: string) {
530576
const hash = createHash('sha256');
531577
hash.update(source);
532578
const digest = hash.digest().toString('base64').replace(/\//g, '-');
533579

534-
return `${directory}/${digest}.html`;
580+
return `${directory}/${mtime}/${digest}.html`;
535581
}
536582

537583
return {
@@ -681,7 +727,15 @@ async function syntax_highlight({
681727
html = await codeToHtml(source, {
682728
lang: 'ts',
683729
theme,
684-
transformers: [transformerTwoslash({})]
730+
transformers: [
731+
transformerTwoslash({
732+
twoslashOptions: {
733+
compilerOptions: {
734+
types: ['svelte', '@sveltejs/kit']
735+
}
736+
}
737+
})
738+
]
685739
});
686740
} catch (e) {
687741
console.error(`Error compiling snippet in ${filename}`);

0 commit comments

Comments
 (0)