@mintlify/previewing 4.0.1152 → 4.0.1153
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/resolve-page-imports.test.d.ts +1 -0
- package/dist/__tests__/resolve-page-imports.test.js +48 -0
- package/dist/__tests__/shouldRegenerateNavForPage.test.js +8 -1
- package/dist/local-preview/listener/index.d.ts +2 -1
- package/dist/local-preview/listener/index.js +44 -4
- package/dist/local-preview/listener/resolve-page-imports.d.ts +3 -0
- package/dist/local-preview/listener/resolve-page-imports.js +98 -0
- package/dist/local-preview/listener/utils.d.ts +1 -0
- package/dist/local-preview/listener/utils.js +21 -9
- package/dist/local-preview/run.js +2 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { findAndRemoveImports, stringifyTree } from '@mintlify/common';
|
|
2
|
+
import { preparseMdxTree } from '@mintlify/prebuild';
|
|
3
|
+
import fse from 'fs-extra';
|
|
4
|
+
import { mkdtemp, rm } from 'fs/promises';
|
|
5
|
+
import { tmpdir } from 'os';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
const testPaths = vi.hoisted(() => ({
|
|
8
|
+
cmd: '',
|
|
9
|
+
public: '',
|
|
10
|
+
}));
|
|
11
|
+
vi.mock('../constants.js', () => ({
|
|
12
|
+
get CMD_EXEC_PATH() {
|
|
13
|
+
return testPaths.cmd;
|
|
14
|
+
},
|
|
15
|
+
get NEXT_PUBLIC_PATH() {
|
|
16
|
+
return testPaths.public;
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
vi.mock('../local-preview/listener/utils.js', () => ({
|
|
20
|
+
getCurrentVariables: vi.fn(async () => ({ name: 'Ada' })),
|
|
21
|
+
handleParseError: vi.fn(),
|
|
22
|
+
}));
|
|
23
|
+
import { resolvePageImports } from '../local-preview/listener/resolve-page-imports.js';
|
|
24
|
+
describe('resolvePageImports', () => {
|
|
25
|
+
let root;
|
|
26
|
+
beforeEach(async () => {
|
|
27
|
+
root = await mkdtemp(join(tmpdir(), 'resolve-page-imports-'));
|
|
28
|
+
testPaths.cmd = join(root, 'docs');
|
|
29
|
+
testPaths.public = join(root, 'public');
|
|
30
|
+
await fse.ensureDir(testPaths.cmd);
|
|
31
|
+
await fse.ensureDir(testPaths.public);
|
|
32
|
+
});
|
|
33
|
+
afterEach(async () => {
|
|
34
|
+
await rm(root, { recursive: true, force: true });
|
|
35
|
+
});
|
|
36
|
+
it('resolves newly imported source snippets with variables and nested imports', async () => {
|
|
37
|
+
await fse.outputFile(join(testPaths.cmd, 'nested.mdx'), 'export const Nested = () => <span>{{name}}</span>;');
|
|
38
|
+
await fse.outputFile(join(testPaths.cmd, 'snippet.mdx'), "import { Nested } from './nested.mdx';\n\n<Nested />");
|
|
39
|
+
const pageContent = "import Snippet from './snippet.mdx';\n\n<Snippet />";
|
|
40
|
+
const tree = await preparseMdxTree(pageContent, testPaths.cmd, join(testPaths.cmd, 'page.mdx'), vi.fn());
|
|
41
|
+
const importsResponse = await findAndRemoveImports(tree);
|
|
42
|
+
const resolvedTree = await resolvePageImports({ ...importsResponse, filename: 'page.mdx' });
|
|
43
|
+
const resolvedContent = stringifyTree(resolvedTree);
|
|
44
|
+
expect(resolvedContent).toContain('Ada');
|
|
45
|
+
expect(resolvedContent).not.toContain('{{name}}');
|
|
46
|
+
expect(resolvedContent).not.toContain("import { Nested } from './nested.mdx'");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { shouldRegenerateNavForPage } from '../local-preview/listener/utils.js';
|
|
2
|
+
import { seedFrontmatterHashForPage, shouldRegenerateNavForPage, } from '../local-preview/listener/utils.js';
|
|
3
3
|
describe('shouldRegenerateNavForPage', () => {
|
|
4
4
|
let frontmatterHashes;
|
|
5
5
|
beforeEach(() => {
|
|
@@ -30,6 +30,13 @@ describe('shouldRegenerateNavForPage', () => {
|
|
|
30
30
|
const result = await shouldRegenerateNavForPage('test.mdx', updatedContent, frontmatterHashes);
|
|
31
31
|
expect(result).toBe(false);
|
|
32
32
|
});
|
|
33
|
+
it('should return false for a content-only change after seeding frontmatter', async () => {
|
|
34
|
+
const originalContent = `---\ntitle: "Test Page"\ndescription: "A test page"\n---\n# Original Content`;
|
|
35
|
+
const updatedContent = `---\ntitle: "Test Page"\ndescription: "A test page"\n---\n# Updated Content`;
|
|
36
|
+
seedFrontmatterHashForPage('test.mdx', originalContent, frontmatterHashes);
|
|
37
|
+
const result = await shouldRegenerateNavForPage('test.mdx', updatedContent, frontmatterHashes);
|
|
38
|
+
expect(result).toBe(false);
|
|
39
|
+
});
|
|
33
40
|
it('should handle files with no frontmatter', async () => {
|
|
34
41
|
const contentNoFrontmatter = '# Just a heading\n\nSome content';
|
|
35
42
|
await shouldRegenerateNavForPage('test.mdx', contentNoFrontmatter, frontmatterHashes);
|
|
@@ -3,5 +3,6 @@ declare const listener: (triggerRefresh: () => void, options?: {
|
|
|
3
3
|
localSchema?: boolean;
|
|
4
4
|
groups?: string[];
|
|
5
5
|
}) => void;
|
|
6
|
-
|
|
6
|
+
declare const initializeFrontmatterHashCache: () => Promise<void>;
|
|
7
|
+
export { initializeFrontmatterHashCache, initializeImportCache };
|
|
7
8
|
export default listener;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { findAndRemoveImports, hasImports, getFileCategory, validate, stringifyTree, isMintIgnored, replaceVariables, } from '@mintlify/common';
|
|
3
|
-
import { createPage, MintConfigUpdater, DocsConfigUpdater, preparseMdxTree, prebuild, } from '@mintlify/prebuild';
|
|
3
|
+
import { createPage, getFileListSync, MintConfigUpdater, DocsConfigUpdater, preparseMdxTree, prebuild, } from '@mintlify/prebuild';
|
|
4
4
|
import Chalk from 'chalk';
|
|
5
5
|
import chokidar from 'chokidar';
|
|
6
6
|
import { promises as _promises } from 'fs';
|
|
@@ -18,10 +18,28 @@ import { initializeImportCache, updateImportCacheForFile, removeFromImportCache,
|
|
|
18
18
|
import { hasTrackedReferencedFile, refreshTrackedReferencedFiles } from './referencedFiles.js';
|
|
19
19
|
import { regenerateAllSnippets } from './regenerateAllSnippets.js';
|
|
20
20
|
import { resolveAllImports } from './resolveAllImports.js';
|
|
21
|
+
import { resolvePageImports } from './resolve-page-imports.js';
|
|
21
22
|
import { updateCustomLanguages, updateGeneratedNav, updateOpenApiFiles, upsertOpenApiFile, } from './update.js';
|
|
22
|
-
import { getCurrentVariables, getMintIgnoreGlobs, handleParseError, isFileSizeValid, isJsonValid, shouldRegenerateNavForPage, suppressParseError, } from './utils.js';
|
|
23
|
+
import { getCurrentVariables, getMintIgnoreGlobs, handleParseError, isFileSizeValid, isJsonValid, seedFrontmatterHashForPage, shouldRegenerateNavForPage, suppressParseError, } from './utils.js';
|
|
23
24
|
const { readFile } = _promises;
|
|
24
25
|
const frontmatterHashes = new Map();
|
|
26
|
+
const SLOW_UPDATE_LOG_THRESHOLD_MS = 1000;
|
|
27
|
+
const getElapsedMs = (startTime) => {
|
|
28
|
+
return Number((process.hrtime.bigint() - startTime) / 1000000n);
|
|
29
|
+
};
|
|
30
|
+
const formatElapsedSeconds = (durationMs) => {
|
|
31
|
+
return `${(durationMs / 1000).toFixed(2)}s`;
|
|
32
|
+
};
|
|
33
|
+
const shouldLogUpdateTiming = (durationMs) => {
|
|
34
|
+
return process.env.DEV_TIMING === 'true' || durationMs >= SLOW_UPDATE_LOG_THRESHOLD_MS;
|
|
35
|
+
};
|
|
36
|
+
const addUpdateTimingLog = (filename, category, durationMs) => {
|
|
37
|
+
if (!shouldLogUpdateTiming(durationMs)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const categoryLabel = category == null ? 'uncategorized' : category;
|
|
41
|
+
addChangeLog(_jsx(InfoLog, { message: `processed ${filename} in ${formatElapsedSeconds(durationMs)} (${categoryLabel})` }));
|
|
42
|
+
};
|
|
25
43
|
const listener = (triggerRefresh, options = {}) => {
|
|
26
44
|
const mintIgnoreGlobs = getMintIgnoreGlobs();
|
|
27
45
|
chokidar
|
|
@@ -47,7 +65,9 @@ const onAddEvent = async (filename, triggerRefresh, options) => {
|
|
|
47
65
|
return;
|
|
48
66
|
}
|
|
49
67
|
try {
|
|
68
|
+
const startTime = process.hrtime.bigint();
|
|
50
69
|
const category = await onUpdateEvent(filename, triggerRefresh, options);
|
|
70
|
+
addUpdateTimingLog(filename, category, getElapsedMs(startTime));
|
|
51
71
|
if (category !== undefined) {
|
|
52
72
|
addChangeLog(_jsx(AddedLog, { filename: filename }));
|
|
53
73
|
}
|
|
@@ -61,7 +81,9 @@ const onChangeEvent = async (filename, triggerRefresh, options) => {
|
|
|
61
81
|
return;
|
|
62
82
|
}
|
|
63
83
|
try {
|
|
84
|
+
const startTime = process.hrtime.bigint();
|
|
64
85
|
const category = await onUpdateEvent(filename, triggerRefresh, options);
|
|
86
|
+
addUpdateTimingLog(filename, category, getElapsedMs(startTime));
|
|
65
87
|
if (category !== undefined) {
|
|
66
88
|
addChangeLog(_jsx(EditedLog, { filename: filename }));
|
|
67
89
|
}
|
|
@@ -77,6 +99,7 @@ const onUnlinkEvent = async (filename, triggerRefresh, options) => {
|
|
|
77
99
|
try {
|
|
78
100
|
const importedFiles = getImportedFilesFromCache();
|
|
79
101
|
const potentialCategory = getFileCategory(filename, { importedFiles });
|
|
102
|
+
frontmatterHashes.delete(filename);
|
|
80
103
|
if (hasTrackedReferencedFile(filename)) {
|
|
81
104
|
try {
|
|
82
105
|
const { mintConfig, openApiFiles, docsConfig } = await getDocsState(handleParseError);
|
|
@@ -136,6 +159,7 @@ const onUnlinkEvent = async (filename, triggerRefresh, options) => {
|
|
|
136
159
|
await fse.emptyDir(NEXT_PROPS_PATH);
|
|
137
160
|
const prebuildResult = await prebuild(CMD_EXEC_PATH, options);
|
|
138
161
|
await initializeImportCache(CMD_EXEC_PATH, prebuildResult?.fileImportsMap);
|
|
162
|
+
await initializeFrontmatterHashCache();
|
|
139
163
|
await refreshTrackedReferencedFiles();
|
|
140
164
|
}
|
|
141
165
|
catch (err) {
|
|
@@ -193,6 +217,21 @@ const validateConfigFiles = async () => {
|
|
|
193
217
|
console.error('⚠️ Error validating configuration files:', error);
|
|
194
218
|
}
|
|
195
219
|
};
|
|
220
|
+
const initializeFrontmatterHashCache = async () => {
|
|
221
|
+
frontmatterHashes.clear();
|
|
222
|
+
const importedFiles = getImportedFilesFromCache();
|
|
223
|
+
const pageFilenames = getFileListSync(CMD_EXEC_PATH).filter((file) => {
|
|
224
|
+
return getFileCategory(file, { importedFiles }) === 'page';
|
|
225
|
+
});
|
|
226
|
+
await Promise.all(pageFilenames.map(async (filename) => {
|
|
227
|
+
try {
|
|
228
|
+
const filePath = pathUtil.join(CMD_EXEC_PATH, filename);
|
|
229
|
+
const contentStr = (await readFile(filePath)).toString();
|
|
230
|
+
seedFrontmatterHashForPage(filename, contentStr, frontmatterHashes, suppressParseError);
|
|
231
|
+
}
|
|
232
|
+
catch { }
|
|
233
|
+
}));
|
|
234
|
+
};
|
|
196
235
|
/**
|
|
197
236
|
* This function is called when a file is added or changed
|
|
198
237
|
* @param filename
|
|
@@ -244,7 +283,7 @@ const onUpdateEvent = async (filename, triggerRefresh, options = {}) => {
|
|
|
244
283
|
const tree = await preparseMdxTree(contentStr, CMD_EXEC_PATH, filePath, suppressParseError);
|
|
245
284
|
const importsResponse = await findAndRemoveImports(tree);
|
|
246
285
|
if (hasImports(importsResponse)) {
|
|
247
|
-
contentStr = stringifyTree(await
|
|
286
|
+
contentStr = stringifyTree(await resolvePageImports({ ...importsResponse, filename }));
|
|
248
287
|
}
|
|
249
288
|
const { pageContent } = await createPage(filename, contentStr, CMD_EXEC_PATH, [], [], handleParseError);
|
|
250
289
|
await fse.outputFile(targetPath, pageContent, {
|
|
@@ -311,6 +350,7 @@ const onUpdateEvent = async (filename, triggerRefresh, options = {}) => {
|
|
|
311
350
|
await fse.emptyDir(NEXT_PROPS_PATH);
|
|
312
351
|
const prebuildResult = await prebuild(CMD_EXEC_PATH, options);
|
|
313
352
|
await initializeImportCache(CMD_EXEC_PATH, prebuildResult?.fileImportsMap);
|
|
353
|
+
await initializeFrontmatterHashCache();
|
|
314
354
|
await refreshTrackedReferencedFiles();
|
|
315
355
|
}
|
|
316
356
|
catch (err) {
|
|
@@ -377,5 +417,5 @@ const onUpdateEvent = async (filename, triggerRefresh, options = {}) => {
|
|
|
377
417
|
triggerRefresh();
|
|
378
418
|
return category;
|
|
379
419
|
};
|
|
380
|
-
export { initializeImportCache };
|
|
420
|
+
export { initializeFrontmatterHashCache, initializeImportCache };
|
|
381
421
|
export default listener;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { findAndRemoveImports, hasImports, optionallyAddLeadingSlash, optionallyRemoveLeadingSlash, replaceVariables, resolveAllImports as baseResolveAllImports, resolveImportPath, topologicalSort, } from '@mintlify/common';
|
|
2
|
+
import { preparseMdxTree } from '@mintlify/prebuild';
|
|
3
|
+
import fse from 'fs-extra';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { CMD_EXEC_PATH, NEXT_PUBLIC_PATH } from '../../constants.js';
|
|
6
|
+
import { getCurrentVariables, handleParseError } from './utils.js';
|
|
7
|
+
const getImportSourcePath = async (filename) => {
|
|
8
|
+
const generatedPath = join(NEXT_PUBLIC_PATH, filename);
|
|
9
|
+
if (await fse.pathExists(generatedPath)) {
|
|
10
|
+
return {
|
|
11
|
+
baseDir: NEXT_PUBLIC_PATH,
|
|
12
|
+
path: generatedPath,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const originalPath = join(CMD_EXEC_PATH, filename);
|
|
16
|
+
if (await fse.pathExists(originalPath)) {
|
|
17
|
+
return {
|
|
18
|
+
baseDir: CMD_EXEC_PATH,
|
|
19
|
+
path: originalPath,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
};
|
|
24
|
+
const parseSnippetWithImports = async ({ filename, variables, snippetsByFilename, }) => {
|
|
25
|
+
const normalizedFilename = optionallyAddLeadingSlash(filename);
|
|
26
|
+
if (snippetsByFilename.has(normalizedFilename)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const importSource = await getImportSourcePath(optionallyRemoveLeadingSlash(normalizedFilename));
|
|
30
|
+
if (importSource == null) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const content = replaceVariables((await fse.readFile(importSource.path)).toString(), variables);
|
|
34
|
+
const tree = await preparseMdxTree(content, importSource.baseDir, importSource.path, handleParseError);
|
|
35
|
+
const importData = await findAndRemoveImports(tree);
|
|
36
|
+
const fileWithImports = {
|
|
37
|
+
filename: normalizedFilename,
|
|
38
|
+
...importData,
|
|
39
|
+
};
|
|
40
|
+
snippetsByFilename.set(normalizedFilename, fileWithImports);
|
|
41
|
+
await Promise.all(Object.keys(importData.importMap).map(async (source) => {
|
|
42
|
+
const resolvedPath = resolveImportPath(source, normalizedFilename);
|
|
43
|
+
if (resolvedPath == null) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
await parseSnippetWithImports({
|
|
47
|
+
filename: resolvedPath,
|
|
48
|
+
variables,
|
|
49
|
+
snippetsByFilename,
|
|
50
|
+
});
|
|
51
|
+
}));
|
|
52
|
+
};
|
|
53
|
+
const resolveSnippetImports = async (snippetsByFilename) => {
|
|
54
|
+
const graph = {};
|
|
55
|
+
for (const snippet of snippetsByFilename.values()) {
|
|
56
|
+
graph[snippet.filename] = Object.keys(snippet.importMap)
|
|
57
|
+
.map((source) => resolveImportPath(source, snippet.filename))
|
|
58
|
+
.filter((resolvedPath) => {
|
|
59
|
+
return resolvedPath != null && snippetsByFilename.has(resolvedPath);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const orderedFilenames = topologicalSort(graph).reverse();
|
|
63
|
+
const resolvedSnippets = [];
|
|
64
|
+
for (const filename of orderedFilenames) {
|
|
65
|
+
const snippet = snippetsByFilename.get(filename);
|
|
66
|
+
if (snippet == null) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const tree = hasImports(snippet)
|
|
70
|
+
? await baseResolveAllImports({
|
|
71
|
+
snippets: resolvedSnippets,
|
|
72
|
+
fileWithImports: snippet,
|
|
73
|
+
})
|
|
74
|
+
: snippet.tree;
|
|
75
|
+
resolvedSnippets.push({ filename: snippet.filename, tree });
|
|
76
|
+
}
|
|
77
|
+
return resolvedSnippets;
|
|
78
|
+
};
|
|
79
|
+
export const resolvePageImports = async (fileWithImports) => {
|
|
80
|
+
const variables = await getCurrentVariables();
|
|
81
|
+
const snippetsByFilename = new Map();
|
|
82
|
+
await Promise.all(Object.keys(fileWithImports.importMap).map(async (source) => {
|
|
83
|
+
const resolvedPath = resolveImportPath(source, fileWithImports.filename);
|
|
84
|
+
if (resolvedPath == null) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
await parseSnippetWithImports({
|
|
88
|
+
filename: resolvedPath,
|
|
89
|
+
variables,
|
|
90
|
+
snippetsByFilename,
|
|
91
|
+
});
|
|
92
|
+
}));
|
|
93
|
+
const snippets = await resolveSnippetImports(snippetsByFilename);
|
|
94
|
+
return await baseResolveAllImports({
|
|
95
|
+
snippets,
|
|
96
|
+
fileWithImports,
|
|
97
|
+
});
|
|
98
|
+
};
|
|
@@ -8,6 +8,7 @@ export declare const isJsonValid: (filePath: string) => {
|
|
|
8
8
|
error?: string;
|
|
9
9
|
};
|
|
10
10
|
export declare const shouldRegenerateNavForPage: (filename: string, contentStr: string, frontmatterHashes: Map<string, string>, onError?: (message: string) => void) => Promise<boolean>;
|
|
11
|
+
export declare const seedFrontmatterHashForPage: (filename: string, contentStr: string, frontmatterHashes: Map<string, string>, onError?: (message: string) => void) => boolean;
|
|
11
12
|
export declare function normalizePathForComparison(filePath: string): string;
|
|
12
13
|
export declare const handleParseError: (message: string) => void;
|
|
13
14
|
export declare const suppressParseError: () => void;
|
|
@@ -45,16 +45,8 @@ export const isJsonValid = (filePath) => {
|
|
|
45
45
|
};
|
|
46
46
|
export const shouldRegenerateNavForPage = async (filename, contentStr, frontmatterHashes, onError) => {
|
|
47
47
|
try {
|
|
48
|
-
const { attributes: currentFrontmatter } = parseFrontmatter(contentStr);
|
|
49
48
|
const prevFrontmatterHash = frontmatterHashes.get(filename);
|
|
50
|
-
const currentFrontmatterHash =
|
|
51
|
-
.createHash('md5')
|
|
52
|
-
.update(JSON.stringify(currentFrontmatter))
|
|
53
|
-
.digest('hex');
|
|
54
|
-
if (!prevFrontmatterHash) {
|
|
55
|
-
frontmatterHashes.set(filename, currentFrontmatterHash);
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
49
|
+
const currentFrontmatterHash = getFrontmatterHash(contentStr);
|
|
58
50
|
if (currentFrontmatterHash !== prevFrontmatterHash) {
|
|
59
51
|
frontmatterHashes.set(filename, currentFrontmatterHash);
|
|
60
52
|
return true;
|
|
@@ -73,6 +65,26 @@ export const shouldRegenerateNavForPage = async (filename, contentStr, frontmatt
|
|
|
73
65
|
return true;
|
|
74
66
|
}
|
|
75
67
|
};
|
|
68
|
+
export const seedFrontmatterHashForPage = (filename, contentStr, frontmatterHashes, onError) => {
|
|
69
|
+
try {
|
|
70
|
+
frontmatterHashes.set(filename, getFrontmatterHash(contentStr));
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const message = `Error parsing frontmatter for ${filename}, skipping initial nav cache: ${error instanceof Error ? error.message : String(error)}`;
|
|
75
|
+
if (onError) {
|
|
76
|
+
onError(message);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.warn(message);
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const getFrontmatterHash = (contentStr) => {
|
|
85
|
+
const { attributes: currentFrontmatter } = parseFrontmatter(contentStr);
|
|
86
|
+
return crypto.createHash('md5').update(JSON.stringify(currentFrontmatter)).digest('hex');
|
|
87
|
+
};
|
|
76
88
|
export function normalizePathForComparison(filePath) {
|
|
77
89
|
return optionallyAddLeadingSlash(filePath).toLowerCase();
|
|
78
90
|
}
|
|
@@ -7,7 +7,7 @@ import { CMD_EXEC_PATH, NEXT_PUBLIC_PATH } from '../constants.js';
|
|
|
7
7
|
import { addLog, removeLastLog } from '../logging-state.js';
|
|
8
8
|
import { LaunchLog, UpdateLog } from '../logs.js';
|
|
9
9
|
import { maybeFixMissingWindowsEnvVar } from '../util.js';
|
|
10
|
-
import listener, { initializeImportCache } from './listener/index.js';
|
|
10
|
+
import listener, { initializeFrontmatterHashCache, initializeImportCache } from './listener/index.js';
|
|
11
11
|
import { refreshTrackedReferencedFiles } from './listener/referencedFiles.js';
|
|
12
12
|
import { getLocalNetworkIp } from './network.js';
|
|
13
13
|
import { setupNext } from './setupNext.js';
|
|
@@ -145,6 +145,7 @@ export const run = async (argv) => {
|
|
|
145
145
|
process.on('SIGTERM', onExit);
|
|
146
146
|
});
|
|
147
147
|
await initializeImportCache(CMD_EXEC_PATH, argv.fileImportsMap);
|
|
148
|
+
await initializeFrontmatterHashCache();
|
|
148
149
|
await refreshTrackedReferencedFiles();
|
|
149
150
|
listener(onChange);
|
|
150
151
|
};
|