diff --git a/packages/parser/src/fs.ts b/packages/parser/src/fs.ts index 12a8e73789..a66c7d3632 100644 --- a/packages/parser/src/fs.ts +++ b/packages/parser/src/fs.ts @@ -40,6 +40,7 @@ export async function load(userRoot: string, filepath: string, loadedSource: Rec } const markdownFiles: Record = {} + const watchFiles: Record> = {} const slides: SlideInfo[] = [] async function loadMarkdown(path: string, range?: string, frontmatterOverride?: Record, importers?: SourceSlideInfo[]) { @@ -48,6 +49,7 @@ export async function load(userRoot: string, filepath: string, loadedSource: Rec const raw = loadedSource[path] ?? fs.readFileSync(path, 'utf-8') md = await parse(raw, path, extensions) markdownFiles[path] = md + watchFiles[path] = new Set() } const directImporter = importers?.at(-1) @@ -125,7 +127,7 @@ export async function load(userRoot: string, filepath: string, loadedSource: Rec headmatter, features: detectFeatures(slides.map(s => s.source.raw).join('')), markdownFiles, - watchFiles: Object.keys(markdownFiles).map(slash), + watchFiles, } } diff --git a/packages/slidev/node/syntax/transform/snippet.ts b/packages/slidev/node/syntax/transform/snippet.ts index 28ec40b7f1..62dc18fdac 100644 --- a/packages/slidev/node/syntax/transform/snippet.ts +++ b/packages/slidev/node/syntax/transform/snippet.ts @@ -84,7 +84,7 @@ function findRegion(lines: Array, regionName: string) { * captures: ['/path/to/file.extension', '#region', 'language', '{meta}'] */ export function transformSnippet({ s, slide, options }: MarkdownTransformContext) { - const data = options.data + const watchFiles = options.data.watchFiles const dir = path.dirname(slide.source?.filepath ?? options.entry ?? options.userRoot) s.replace( @@ -97,7 +97,8 @@ export function transformSnippet({ s, slide, options }: MarkdownTransformContext : path.resolve(dir, filepath), ) - data.watchFiles.push(src) + watchFiles[src] ??= new Set() + watchFiles[src].add(slide.index) const isAFile = fs.statSync(src).isFile() if (!fs.existsSync(src) || !isAFile) { @@ -108,9 +109,6 @@ export function transformSnippet({ s, slide, options }: MarkdownTransformContext let content = fs.readFileSync(src, 'utf8') - slide.snippetsUsed ??= {} - slide.snippetsUsed[src] = content - if (regionName) { const lines = content.split(/\r?\n/) const region = findRegion(lines, regionName.slice(1)) diff --git a/packages/slidev/node/vite/loaders.ts b/packages/slidev/node/vite/loaders.ts index 5c801358d0..0d26241e12 100644 --- a/packages/slidev/node/vite/loaders.ts +++ b/packages/slidev/node/vite/loaders.ts @@ -39,7 +39,7 @@ export function createSlidesLoader( options: ResolvedSlidevOptions, serverOptions: SlidevServerOptions, ): Plugin { - const hmrPages = new Set() + const hmrSlidesIndexes = new Set() let server: ViteDevServer | undefined let skipHmr: { filePath: string, fileContent: string } | null = null @@ -53,7 +53,7 @@ export function createSlidesLoader( function updateServerWatcher() { if (!server) return - server.watcher.add(data.watchFiles) + server.watcher.add(Object.keys(data.watchFiles)) } function getFrontmatter(pageNo: number) { @@ -87,7 +87,7 @@ export function createSlidesLoader( const slide = data.slides[idx] if (body.content && body.content !== slide.source.content) - hmrPages.add(idx) + hmrSlidesIndexes.add(idx) if (body.content) slide.content = slide.source.content = body.content @@ -123,9 +123,14 @@ export function createSlidesLoader( }, async handleHotUpdate(ctx) { - if (!data.watchFiles.includes(ctx.file)) + const forceChangedSlides = data.watchFiles[ctx.file] + if (!forceChangedSlides) return + for (const index of forceChangedSlides) { + hmrSlidesIndexes.add(index) + } + const newData = await serverOptions.loadData?.({ [ctx.file]: await ctx.read(), }) @@ -142,12 +147,12 @@ export function createSlidesLoader( if (data.slides.length !== newData.slides.length) { moduleIds.add(templateSlides.id) - range(newData.slides.length).map(i => hmrPages.add(i)) + range(newData.slides.length).map(i => hmrSlidesIndexes.add(i)) } if (!equal(data.headmatter.defaults, newData.headmatter.defaults)) { moduleIds.add(templateSlides.id) - range(data.slides.length).map(i => hmrPages.add(i)) + range(data.slides.length).map(i => hmrSlidesIndexes.add(i)) } if (!equal(data.config, newData.config)) @@ -166,7 +171,7 @@ export function createSlidesLoader( const b = newData.slides[i] if ( - !hmrPages.has(i) + !hmrSlidesIndexes.has(i) && a.content.trim() === b.content.trim() && a.title?.trim() === b.title?.trim() && equal(a.frontmatter, b.frontmatter) @@ -191,16 +196,16 @@ export function createSlidesLoader( data: withRenderedNote(newData.slides[i]), }, ) - hmrPages.add(i) + hmrSlidesIndexes.add(i) } Object.assign(data, newData) Object.assign(utils, createDataUtils(newData, clientRoot, roots)) - if (hmrPages.size > 0) + if (hmrSlidesIndexes.size > 0) moduleIds.add(templateTitleRendererMd.id) - const vueModules = Array.from(hmrPages) + const vueModules = Array.from(hmrSlidesIndexes) .flatMap((idx) => { const frontmatter = ctx.server.moduleGraph.getModuleById(getSourceId(idx, 'frontmatter')) const main = ctx.server.moduleGraph.getModuleById(getSourceId(idx, 'md')) @@ -212,7 +217,7 @@ export function createSlidesLoader( ] }) - hmrPages.clear() + hmrSlidesIndexes.clear() const moduleEntries = [ ...ctx.modules.filter(i => i.id === templateMonacoRunDeps.id || i.id === templateMonacoTypes.id), diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index e7d9b60040..aa7c004ce9 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -51,7 +51,6 @@ export interface SlideInfo extends SlideInfoBase { * The source slide where the content is from */ source: SourceSlideInfo - snippetsUsed?: LoadedSnippets noteHTML?: string } @@ -112,7 +111,10 @@ export interface SlidevData { features: SlidevDetectedFeatures themeMeta?: SlidevThemeMeta markdownFiles: Record - watchFiles: string[] + /** + * From watched files to indexes of slides that must be reloaded regardless of the loaded content + */ + watchFiles: Record> } export interface SlidevPreparserExtension { @@ -137,5 +139,3 @@ export interface SlideRoute { */ component: Component } - -export type LoadedSnippets = Record diff --git a/packages/vscode/src/projects.ts b/packages/vscode/src/projects.ts index 31b36c59d5..9f44b69e8b 100644 --- a/packages/vscode/src/projects.ts +++ b/packages/vscode/src/projects.ts @@ -75,7 +75,7 @@ export function useProjects() { onScopeDispose(() => fsWatcher.dispose()) fsWatcher.onDidChange(async (uri) => { - const path = slash(uri.fsPath).toLowerCase() + const path = slash(uri.fsPath) logger.info(`File ${path} changed.`) const startMs = Date.now() if (pendingUpdate) @@ -83,7 +83,7 @@ export function useProjects() { const thisUpdate = pendingUpdate = { cancelled: false } const effects: (() => void)[] = [] for (const project of projects.values()) { - if (!project.data.watchFiles.some(f => f.toLowerCase() === path)) + if (!project.data.markdownFiles[path]) continue if (existsSync(project.entry)) { diff --git a/packages/vscode/src/views/projectsTree.ts b/packages/vscode/src/views/projectsTree.ts index e67b86a367..b7f3b08bd0 100644 --- a/packages/vscode/src/views/projectsTree.ts +++ b/packages/vscode/src/views/projectsTree.ts @@ -47,7 +47,7 @@ export const useProjectsTree = createSingletonComposable(() => { const treeData = computed(() => { return [...projects.values()].map(project => ({ treeItem: getProjectTreeItem(project), - children: project.data.watchFiles + children: Object.keys(project.data.markdownFiles) .filter(file => file.toLowerCase() !== project.entry.toLowerCase()) .map(file => ({ treeItem: getFileTreeItem(file) })), })) diff --git a/test/_tutils.ts b/test/_tutils.ts index 76a565beb3..fbbe427c08 100644 --- a/test/_tutils.ts +++ b/test/_tutils.ts @@ -13,7 +13,7 @@ export function createTransformContext(code: string, shiki?: any): MarkdownTrans slides: [ {} as any, ], - watchFiles: [], + watchFiles: {}, config: {} as any, features: {}, },