@epic-web/workshop-utils 4.28.6 → 5.0.0

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.
@@ -1,5 +1,4 @@
1
1
  import * as C from '@epic-web/cachified';
2
- import { type Cache as CachifiedCache } from '@epic-web/cachified';
3
2
  import { type Timings } from './timing.server.js';
4
3
  export declare const solutionAppCache: {
5
4
  name: string;
@@ -817,13 +816,6 @@ export declare const compiledMarkdownCache: {
817
816
  get: (key: string) => C.CacheEntry<string> | undefined;
818
817
  delete: (key: string) => boolean;
819
818
  };
820
- export type CachedEmbeddedFilesList = Record<string, string[]>;
821
- export declare const embeddedFilesCache: {
822
- name: string;
823
- set: (key: string, value: C.CacheEntry<CachedEmbeddedFilesList | undefined>) => C.CacheEntry<CachedEmbeddedFilesList | undefined>;
824
- get: (key: string) => C.CacheEntry<CachedEmbeddedFilesList | undefined> | undefined;
825
- delete: (key: string) => boolean;
826
- };
827
819
  export declare const compiledCodeCache: {
828
820
  name: string;
829
821
  set: (key: string, value: C.CacheEntry<string>) => C.CacheEntry<string>;
@@ -836,7 +828,12 @@ export declare const ogCache: {
836
828
  get: (key: string) => C.CacheEntry<string> | undefined;
837
829
  delete: (key: string) => boolean;
838
830
  };
839
- export declare const fsCache: CachifiedCache;
831
+ export declare const compiledInstructionMarkdownCache: C.Cache<{
832
+ code: string;
833
+ title: string | null;
834
+ epicVideoEmbeds: Array<string>;
835
+ }>;
836
+ export declare const fsCache: C.Cache<unknown>;
840
837
  export declare function getAllFileCacheEntries(): Promise<any[]>;
841
838
  export declare function deleteCache(): Promise<null | undefined>;
842
839
  export declare function makeSingletonCache<CacheEntryType>(name: string): {
@@ -845,6 +842,7 @@ export declare function makeSingletonCache<CacheEntryType>(name: string): {
845
842
  get: (key: string) => C.CacheEntry<CacheEntryType> | undefined;
846
843
  delete: (key: string) => boolean;
847
844
  };
845
+ export declare function makeSingletonFsCache<CacheEntryType>(name: string): C.Cache<CacheEntryType>;
848
846
  export declare function cachified<Value>({ request, timings, key, timingKey, ...options }: Omit<C.CachifiedOptions<Value>, 'forceFresh'> & {
849
847
  request?: Request;
850
848
  timings?: Timings;
@@ -1 +1 @@
1
- {"version":3,"file":"cache.server.d.ts","sourceRoot":"","sources":["../../src/cache.server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache.server.d.ts","sourceRoot":"","sources":["../../src/cache.server.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import os from 'os';
2
2
  import path from 'path';
3
3
  import * as C from '@epic-web/cachified';
4
- import { verboseReporter, } from '@epic-web/cachified';
4
+ import { verboseReporter } from '@epic-web/cachified';
5
5
  import { remember } from '@epic-web/remember';
6
6
  import fsExtra from 'fs-extra';
7
7
  import { LRUCache } from 'lru-cache';
@@ -15,43 +15,11 @@ export const appsCache = makeSingletonCache('AppsCache');
15
15
  export const diffCodeCache = makeSingletonCache('DiffCodeCache');
16
16
  export const diffFilesCache = makeSingletonCache('DiffFilesCache');
17
17
  export const compiledMarkdownCache = makeSingletonCache('CompiledMarkdownCache');
18
- export const embeddedFilesCache = makeSingletonCache('EmbeddedFilesCache');
19
18
  export const compiledCodeCache = makeSingletonCache('CompiledCodeCache');
20
19
  export const ogCache = makeSingletonCache('OgCache');
20
+ export const compiledInstructionMarkdownCache = makeSingletonFsCache('CompiledInstructionMarkdownCache');
21
21
  const cacheDir = path.join(os.homedir(), '.epicshop', 'cache');
22
- export const fsCache = {
23
- name: 'Filesystem cache',
24
- async get(key) {
25
- try {
26
- const filePath = path.join(cacheDir, md5(key));
27
- const data = await fsExtra.readJSON(filePath);
28
- if (data.entry)
29
- return data.entry;
30
- // this is just here for migration purposes. Earlier versions of the cache
31
- // did not store the key in the cache file with the value under "entry".
32
- return null;
33
- }
34
- catch (error) {
35
- if (error instanceof Error &&
36
- 'code' in error &&
37
- error.code === 'ENOENT') {
38
- return null;
39
- }
40
- throw error;
41
- }
42
- },
43
- async set(key, entry) {
44
- const filePath = path.join(cacheDir, md5(key));
45
- await fsExtra.ensureDir(path.dirname(filePath));
46
- // store the key in the cache file because it's md5 hashed and the key has
47
- // helpful debugging information.
48
- await fsExtra.writeJSON(filePath, { key, entry });
49
- },
50
- async delete(key) {
51
- const filePath = path.join(cacheDir, md5(key));
52
- await fsExtra.remove(filePath);
53
- },
54
- };
22
+ export const fsCache = makeSingletonFsCache('FsCache');
55
23
  export async function getAllFileCacheEntries() {
56
24
  const files = await fsExtra.readdir(cacheDir);
57
25
  const entries = await Promise.all(files
@@ -96,6 +64,41 @@ export function makeSingletonCache(name) {
96
64
  return lru;
97
65
  });
98
66
  }
67
+ export function makeSingletonFsCache(name) {
68
+ return remember(name, () => {
69
+ const cacheDir = path.join(os.homedir(), '.epicshop', 'cache', name);
70
+ const fsCache = {
71
+ name: `Filesystem cache (${name})`,
72
+ async get(key) {
73
+ try {
74
+ const filePath = path.join(cacheDir, md5(key));
75
+ const data = await fsExtra.readJSON(filePath);
76
+ if (data.entry)
77
+ return data.entry;
78
+ return null;
79
+ }
80
+ catch (error) {
81
+ if (error instanceof Error &&
82
+ 'code' in error &&
83
+ error.code === 'ENOENT') {
84
+ return null;
85
+ }
86
+ throw error;
87
+ }
88
+ },
89
+ async set(key, entry) {
90
+ const filePath = path.join(cacheDir, md5(key));
91
+ await fsExtra.ensureDir(path.dirname(filePath));
92
+ await fsExtra.writeJSON(filePath, { key, entry });
93
+ },
94
+ async delete(key) {
95
+ const filePath = path.join(cacheDir, md5(key));
96
+ await fsExtra.remove(filePath);
97
+ },
98
+ };
99
+ return fsCache;
100
+ });
101
+ }
99
102
  export async function cachified({ request, timings, key, timingKey = key.length > 18 ? `${key.slice(0, 7)}...${key.slice(-8)}` : key, ...options }) {
100
103
  const forceFresh = await shouldForceFresh({
101
104
  forceFresh: options.forceFresh,
@@ -1 +1 @@
1
- {"version":3,"file":"cache.server.js","sourceRoot":"","sources":["../../src/cache.server.ts"],"names":[],"mappings":"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","sourcesContent":["import os from 'os'\nimport path from 'path'\nimport * as C from '@epic-web/cachified'\nimport {\n\tverboseReporter,\n\ttype CacheEntry,\n\ttype Cache as CachifiedCache,\n} from '@epic-web/cachified'\nimport { remember } from '@epic-web/remember'\nimport fsExtra from 'fs-extra'\nimport { LRUCache } from 'lru-cache'\nimport md5 from 'md5-hex'\nimport {\n\ttype App,\n\ttype ExampleApp,\n\ttype PlaygroundApp,\n\ttype ProblemApp,\n\ttype SolutionApp,\n} from './apps.server.js'\nimport { cachifiedTimingReporter, type Timings } from './timing.server.js'\n\nexport const solutionAppCache =\n\tmakeSingletonCache<SolutionApp>('SolutionAppCache')\nexport const problemAppCache = makeSingletonCache<ProblemApp>('ProblemAppCache')\nexport const exampleAppCache = makeSingletonCache<ExampleApp>('ExampleAppCache')\nexport const playgroundAppCache =\n\tmakeSingletonCache<PlaygroundApp>('PlaygroundAppCache')\nexport const appsCache = makeSingletonCache<App>('AppsCache')\nexport const diffCodeCache = makeSingletonCache<string>('DiffCodeCache')\nexport const diffFilesCache = makeSingletonCache<string>('DiffFilesCache')\nexport const compiledMarkdownCache = makeSingletonCache<string>(\n\t'CompiledMarkdownCache',\n)\nexport type CachedEmbeddedFilesList = Record<string, string[]>\nexport const embeddedFilesCache = makeSingletonCache<\n\tCachedEmbeddedFilesList | undefined\n>('EmbeddedFilesCache')\nexport const compiledCodeCache = makeSingletonCache<string>('CompiledCodeCache')\nexport const ogCache = makeSingletonCache<string>('OgCache')\n\nconst cacheDir = path.join(os.homedir(), '.epicshop', 'cache')\n\nexport const fsCache: CachifiedCache = {\n\tname: 'Filesystem cache',\n\tasync get(key) {\n\t\ttry {\n\t\t\tconst filePath = path.join(cacheDir, md5(key))\n\n\t\t\tconst data = await fsExtra.readJSON(filePath)\n\t\t\tif (data.entry) return data.entry\n\t\t\t// this is just here for migration purposes. Earlier versions of the cache\n\t\t\t// did not store the key in the cache file with the value under \"entry\".\n\t\t\treturn null\n\t\t} catch (error: unknown) {\n\t\t\tif (\n\t\t\t\terror instanceof Error &&\n\t\t\t\t'code' in error &&\n\t\t\t\terror.code === 'ENOENT'\n\t\t\t) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t},\n\tasync set(key, entry) {\n\t\tconst filePath = path.join(cacheDir, md5(key))\n\t\tawait fsExtra.ensureDir(path.dirname(filePath))\n\t\t// store the key in the cache file because it's md5 hashed and the key has\n\t\t// helpful debugging information.\n\t\tawait fsExtra.writeJSON(filePath, { key, entry })\n\t},\n\tasync delete(key) {\n\t\tconst filePath = path.join(cacheDir, md5(key))\n\t\tawait fsExtra.remove(filePath)\n\t},\n}\n\nexport async function getAllFileCacheEntries() {\n\tconst files = await fsExtra.readdir(cacheDir)\n\tconst entries = await Promise.all(\n\t\tfiles\n\t\t\t.map(async (file) => {\n\t\t\t\tconst filePath = path.join(cacheDir, file)\n\t\t\t\tconst data = await fsExtra.readJSON(filePath)\n\t\t\t\treturn data\n\t\t\t})\n\t\t\t.filter(Boolean),\n\t)\n\treturn entries\n}\n\nexport async function deleteCache() {\n\tif (process.env.EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tif (await fsExtra.exists(cacheDir)) {\n\t\t\tawait fsExtra.remove(cacheDir)\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(`Error deleting the cache in ${cacheDir}`, error)\n\t}\n}\n\nexport function makeSingletonCache<CacheEntryType>(name: string) {\n\treturn remember(name, () => {\n\t\tconst lruInstance = new LRUCache<string, CacheEntry<CacheEntryType>>({\n\t\t\tmax: 1000,\n\t\t})\n\n\t\tconst lru = {\n\t\t\tname,\n\t\t\tset: (key, value) => {\n\t\t\t\tconst ttl = C.totalTtl(value.metadata)\n\t\t\t\tlruInstance.set(key, value, {\n\t\t\t\t\tttl: ttl === Infinity ? undefined : ttl,\n\t\t\t\t\tstart: value.metadata.createdTime,\n\t\t\t\t})\n\t\t\t\treturn value\n\t\t\t},\n\t\t\tget: (key) => lruInstance.get(key),\n\t\t\tdelete: (key) => lruInstance.delete(key),\n\t\t} satisfies C.Cache<CacheEntryType>\n\n\t\treturn lru\n\t})\n}\n\nexport async function cachified<Value>({\n\trequest,\n\ttimings,\n\tkey,\n\ttimingKey = key.length > 18 ? `${key.slice(0, 7)}...${key.slice(-8)}` : key,\n\t...options\n}: Omit<C.CachifiedOptions<Value>, 'forceFresh'> & {\n\trequest?: Request\n\ttimings?: Timings\n\tforceFresh?: boolean | string\n\ttimingKey?: string\n}): Promise<Value> {\n\tconst forceFresh = await shouldForceFresh({\n\t\tforceFresh: options.forceFresh,\n\t\trequest,\n\t\tkey,\n\t})\n\treturn C.cachified(\n\t\t{\n\t\t\t...options,\n\t\t\tkey,\n\t\t\tforceFresh,\n\t\t},\n\t\tC.mergeReporters(\n\t\t\tcachifiedTimingReporter(timings, timingKey),\n\t\t\tprocess.env.EPICSHOP_DEBUG_CACHE ? verboseReporter() : undefined,\n\t\t),\n\t)\n}\n\nexport async function shouldForceFresh({\n\tforceFresh,\n\trequest,\n\tkey,\n}: {\n\tforceFresh?: boolean | string\n\trequest?: Request\n\tkey?: string\n}) {\n\tif (typeof forceFresh === 'boolean') return forceFresh\n\tif (typeof forceFresh === 'string' && key) {\n\t\treturn forceFresh.split(',').includes(key)\n\t}\n\n\tif (!request) return false\n\tconst fresh = new URL(request.url).searchParams.get('fresh')\n\tif (typeof fresh !== 'string') return false\n\tif (fresh === '') return true\n\tif (!key) return false\n\n\treturn fresh.split(',').includes(key)\n}\n"]}
1
+ {"version":3,"file":"cache.server.js","sourceRoot":"","sources":["../../src/cache.server.ts"],"names":[],"mappings":"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","sourcesContent":["import os from 'os'\nimport path from 'path'\nimport * as C from '@epic-web/cachified'\nimport { verboseReporter, type CacheEntry } from '@epic-web/cachified'\nimport { remember } from '@epic-web/remember'\nimport fsExtra from 'fs-extra'\nimport { LRUCache } from 'lru-cache'\nimport md5 from 'md5-hex'\nimport {\n\ttype App,\n\ttype ExampleApp,\n\ttype PlaygroundApp,\n\ttype ProblemApp,\n\ttype SolutionApp,\n} from './apps.server.js'\nimport { cachifiedTimingReporter, type Timings } from './timing.server.js'\n\nexport const solutionAppCache =\n\tmakeSingletonCache<SolutionApp>('SolutionAppCache')\nexport const problemAppCache = makeSingletonCache<ProblemApp>('ProblemAppCache')\nexport const exampleAppCache = makeSingletonCache<ExampleApp>('ExampleAppCache')\nexport const playgroundAppCache =\n\tmakeSingletonCache<PlaygroundApp>('PlaygroundAppCache')\nexport const appsCache = makeSingletonCache<App>('AppsCache')\nexport const diffCodeCache = makeSingletonCache<string>('DiffCodeCache')\nexport const diffFilesCache = makeSingletonCache<string>('DiffFilesCache')\nexport const compiledMarkdownCache = makeSingletonCache<string>(\n\t'CompiledMarkdownCache',\n)\nexport const compiledCodeCache = makeSingletonCache<string>('CompiledCodeCache')\nexport const ogCache = makeSingletonCache<string>('OgCache')\nexport const compiledInstructionMarkdownCache = makeSingletonFsCache<{\n\tcode: string\n\ttitle: string | null\n\tepicVideoEmbeds: Array<string>\n}>('CompiledInstructionMarkdownCache')\n\nconst cacheDir = path.join(os.homedir(), '.epicshop', 'cache')\n\nexport const fsCache = makeSingletonFsCache('FsCache')\n\nexport async function getAllFileCacheEntries() {\n\tconst files = await fsExtra.readdir(cacheDir)\n\tconst entries = await Promise.all(\n\t\tfiles\n\t\t\t.map(async (file) => {\n\t\t\t\tconst filePath = path.join(cacheDir, file)\n\t\t\t\tconst data = await fsExtra.readJSON(filePath)\n\t\t\t\treturn data\n\t\t\t})\n\t\t\t.filter(Boolean),\n\t)\n\treturn entries\n}\n\nexport async function deleteCache() {\n\tif (process.env.EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tif (await fsExtra.exists(cacheDir)) {\n\t\t\tawait fsExtra.remove(cacheDir)\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(`Error deleting the cache in ${cacheDir}`, error)\n\t}\n}\n\nexport function makeSingletonCache<CacheEntryType>(name: string) {\n\treturn remember(name, () => {\n\t\tconst lruInstance = new LRUCache<string, CacheEntry<CacheEntryType>>({\n\t\t\tmax: 1000,\n\t\t})\n\n\t\tconst lru = {\n\t\t\tname,\n\t\t\tset: (key, value) => {\n\t\t\t\tconst ttl = C.totalTtl(value.metadata)\n\t\t\t\tlruInstance.set(key, value, {\n\t\t\t\t\tttl: ttl === Infinity ? undefined : ttl,\n\t\t\t\t\tstart: value.metadata.createdTime,\n\t\t\t\t})\n\t\t\t\treturn value\n\t\t\t},\n\t\t\tget: (key) => lruInstance.get(key),\n\t\t\tdelete: (key) => lruInstance.delete(key),\n\t\t} satisfies C.Cache<CacheEntryType>\n\n\t\treturn lru\n\t})\n}\n\nexport function makeSingletonFsCache<CacheEntryType>(name: string) {\n\treturn remember(name, () => {\n\t\tconst cacheDir = path.join(os.homedir(), '.epicshop', 'cache', name)\n\n\t\tconst fsCache: C.Cache<CacheEntryType> = {\n\t\t\tname: `Filesystem cache (${name})`,\n\t\t\tasync get(key) {\n\t\t\t\ttry {\n\t\t\t\t\tconst filePath = path.join(cacheDir, md5(key))\n\t\t\t\t\tconst data = await fsExtra.readJSON(filePath)\n\t\t\t\t\tif (data.entry) return data.entry\n\t\t\t\t\treturn null\n\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\tif (\n\t\t\t\t\t\terror instanceof Error &&\n\t\t\t\t\t\t'code' in error &&\n\t\t\t\t\t\terror.code === 'ENOENT'\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t},\n\t\t\tasync set(key, entry) {\n\t\t\t\tconst filePath = path.join(cacheDir, md5(key))\n\t\t\t\tawait fsExtra.ensureDir(path.dirname(filePath))\n\t\t\t\tawait fsExtra.writeJSON(filePath, { key, entry })\n\t\t\t},\n\t\t\tasync delete(key) {\n\t\t\t\tconst filePath = path.join(cacheDir, md5(key))\n\t\t\t\tawait fsExtra.remove(filePath)\n\t\t\t},\n\t\t}\n\n\t\treturn fsCache\n\t})\n}\n\nexport async function cachified<Value>({\n\trequest,\n\ttimings,\n\tkey,\n\ttimingKey = key.length > 18 ? `${key.slice(0, 7)}...${key.slice(-8)}` : key,\n\t...options\n}: Omit<C.CachifiedOptions<Value>, 'forceFresh'> & {\n\trequest?: Request\n\ttimings?: Timings\n\tforceFresh?: boolean | string\n\ttimingKey?: string\n}): Promise<Value> {\n\tconst forceFresh = await shouldForceFresh({\n\t\tforceFresh: options.forceFresh,\n\t\trequest,\n\t\tkey,\n\t})\n\treturn C.cachified(\n\t\t{\n\t\t\t...options,\n\t\t\tkey,\n\t\t\tforceFresh,\n\t\t},\n\t\tC.mergeReporters(\n\t\t\tcachifiedTimingReporter(timings, timingKey),\n\t\t\tprocess.env.EPICSHOP_DEBUG_CACHE ? verboseReporter() : undefined,\n\t\t),\n\t)\n}\n\nexport async function shouldForceFresh({\n\tforceFresh,\n\trequest,\n\tkey,\n}: {\n\tforceFresh?: boolean | string\n\trequest?: Request\n\tkey?: string\n}) {\n\tif (typeof forceFresh === 'boolean') return forceFresh\n\tif (typeof forceFresh === 'string' && key) {\n\t\treturn forceFresh.split(',').includes(key)\n\t}\n\n\tif (!request) return false\n\tconst fresh = new URL(request.url).searchParams.get('fresh')\n\tif (typeof fresh !== 'string') return false\n\tif (fresh === '') return true\n\tif (!key) return false\n\n\treturn fresh.split(',').includes(key)\n}\n"]}
@@ -1,11 +1,12 @@
1
- export declare function compileMdx(file: string, { request, forceFresh }?: {
1
+ import { type Timings } from './timing.server.js';
2
+ export declare function compileMdx(file: string, { request, timings, forceFresh, }?: {
2
3
  request?: Request;
4
+ timings?: Timings;
3
5
  forceFresh?: boolean;
4
6
  }): Promise<{
5
7
  code: string;
6
8
  title: string | null;
7
- epicVideoEmbeds: Array<string>;
9
+ epicVideoEmbeds: string[];
8
10
  }>;
9
11
  export declare function compileMarkdownString(markdownString: string): Promise<string>;
10
- export declare function isEmbeddedFile(filePath: string): Promise<boolean>;
11
12
  //# sourceMappingURL=compile-mdx.server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compile-mdx.server.d.ts","sourceRoot":"","sources":["../../src/compile-mdx.server.ts"],"names":[],"mappings":"AAkHA,wBAAsB,UAAU,CAC/B,IAAI,EAAE,MAAM,EACZ,EAAE,OAAO,EAAE,UAAU,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GACvE,OAAO,CAAC;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CAC9B,CAAC,CA6HD;AAED,wBAAsB,qBAAqB,CAAC,cAAc,EAAE,MAAM,mBA8BjE;AAmFD,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,oBAQpD"}
1
+ {"version":3,"file":"compile-mdx.server.d.ts","sourceRoot":"","sources":["../../src/compile-mdx.server.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAsDjD,wBAAsB,UAAU,CAC/B,IAAI,EAAE,MAAM,EACZ,EACC,OAAO,EACP,OAAO,EACP,UAAU,GACV,GAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;CACf;;;;GA4BN;AAuED,wBAAsB,qBAAqB,CAAC,cAAc,EAAE,MAAM,mBA8BjE"}
@@ -1,19 +1,13 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { cachified } from '@epic-web/cachified';
4
- import { remember } from '@epic-web/remember';
5
3
  import { remarkCodeBlocksShiki } from '@kentcdodds/md-temp';
6
- import fsExtra from 'fs-extra';
7
- import md5 from 'md5-hex';
8
4
  import { bundleMDX } from 'mdx-bundler';
9
5
  import PQueue from 'p-queue';
10
6
  import remarkAutolinkHeadings from 'remark-autolink-headings';
11
7
  import emoji from 'remark-emoji';
12
8
  import gfm from 'remark-gfm';
13
9
  import { visit } from 'unist-util-visit';
14
- import { compiledMarkdownCache, embeddedFilesCache, shouldForceFresh, } from './cache.server.js';
15
- import { remarkCodeFile, } from './codefile-mdx.server.js';
16
- const cacheDir = path.join(process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd(), './node_modules/.cache/compile-mdx');
10
+ import { cachified, compiledInstructionMarkdownCache, compiledMarkdownCache, shouldForceFresh, } from './cache.server.js';
17
11
  function trimCodeBlocks() {
18
12
  return async function transformer(tree) {
19
13
  visit(tree, 'element', (preNode) => {
@@ -55,63 +49,35 @@ const rehypePlugins = [
55
49
  remarkCodeBlocksShiki,
56
50
  removePreContainerDivs,
57
51
  ];
58
- function checkFileExists(file) {
59
- return fs.promises.access(file, fs.constants.F_OK).then(() => true, () => false);
60
- }
61
52
  const verboseLog = process.env.EPICSHOP_VERBOSE_LOG === 'true' ? console.log : () => { };
62
- /**
63
- * @param embeddedFiles {string[]} - list of embedded files
64
- * @param lastCompiledTime {number} - timestamp indicating the last time mdx file was compiled
65
- * @returns true if all embedded file mtimeMs are older then the file compiled time,
66
- * false if we need update
67
- */
68
- function validateEmbeddedFiles(embeddedFiles, lastCompiledTime) {
69
- if (process.env.NODE_ENV !== 'development')
70
- return Promise.resolve(true);
71
- return Promise.all(Array.from(embeddedFiles).map(async ({ file }) => {
72
- const stat = await fs.promises.stat(file).catch(() => ({ mtimeMs: 0 }));
73
- return lastCompiledTime > stat.mtimeMs || Promise.reject();
74
- })).then(() => true, () => false);
75
- }
76
- export async function compileMdx(file, { request, forceFresh } = {}) {
77
- if (!(await checkFileExists(file))) {
78
- throw new Error(`File does not exist: ${file}`);
53
+ export async function compileMdx(file, { request, timings, forceFresh, } = {}) {
54
+ const stat = await fs.promises
55
+ .stat(file)
56
+ .catch((error) => ({ error }));
57
+ if ('error' in stat) {
58
+ throw new Error(`File stat cannot be read: ${stat.error}`);
79
59
  }
80
- let cachedEmbeddedFiles = new Map();
81
- const stat = await fs.promises.stat(file);
82
- const cacheLocation = path.join(cacheDir, `${md5(file)}.json`);
83
- const requireFresh = await shouldForceFresh({
84
- forceFresh,
85
- request,
86
- key: cacheLocation,
87
- });
88
- if (!requireFresh && (await checkFileExists(cacheLocation))) {
89
- try {
90
- const cached = JSON.parse(await fs.promises.readFile(cacheLocation, 'utf-8'));
91
- cachedEmbeddedFiles = new Map(Object.entries(cached.value.embeddedFiles ?? {}));
92
- const compiledTime = cached.value.compiledTime ?? 0;
93
- const warningCancled = process.env.NODE_ENV === 'development'
94
- ? cached?.value?.warningCancled ?? false
95
- : false;
96
- if (compiledTime > stat.mtimeMs &&
97
- !warningCancled &&
98
- (await validateEmbeddedFiles(cachedEmbeddedFiles.values(), compiledTime))) {
99
- return cached.value;
100
- }
101
- }
102
- catch (error) {
103
- console.error(`Error reading cached file: ${cacheLocation}`, error);
104
- void fs.promises.unlink(cacheLocation);
60
+ const key = `file:${file}`;
61
+ forceFresh = await shouldForceFresh({ forceFresh, request, key });
62
+ const existingCacheEntry = await compiledInstructionMarkdownCache.get(key);
63
+ if (!forceFresh && existingCacheEntry) {
64
+ const compiledTime = existingCacheEntry.metadata.createdTime;
65
+ if (stat.mtimeMs <= compiledTime) {
66
+ return existingCacheEntry.value;
105
67
  }
106
68
  }
69
+ return cachified({
70
+ key,
71
+ cache: compiledInstructionMarkdownCache,
72
+ request,
73
+ timings,
74
+ forceFresh,
75
+ getFreshValue: () => compileMdxImpl(file),
76
+ });
77
+ }
78
+ async function compileMdxImpl(file) {
107
79
  let title = null;
108
80
  const epicVideoEmbeds = [];
109
- const codeFileData = {
110
- mdxFile: file,
111
- cacheLocation,
112
- cachedEmbeddedFiles,
113
- embeddedFiles: new Map(),
114
- };
115
81
  try {
116
82
  verboseLog(`Compiling ${file}`);
117
83
  const bundleResult = await queuedBundleMDX({
@@ -136,9 +102,13 @@ export async function compileMdx(file, { request, forceFresh } = {}) {
136
102
  },
137
103
  () => (tree) => {
138
104
  visit(tree, 'mdxJsxFlowElement', (jsxEl) => {
105
+ // @ts-expect-error no idea why this started being an issue suddenly 🤷‍♂️
139
106
  if (jsxEl.name !== 'EpicVideo')
140
107
  return;
141
- const urlAttr = jsxEl.attributes.find((a) => a.type === 'mdxJsxAttribute' && a.name === 'url');
108
+ // @ts-expect-error no idea why this started being an issue suddenly 🤷‍♂️
109
+ const urlAttr = jsxEl.attributes.find(
110
+ // @ts-expect-error no idea why this started being an issue suddenly 🤷‍♂️
111
+ (a) => a.type === 'mdxJsxAttribute' && a.name === 'url');
142
112
  if (!urlAttr)
143
113
  return;
144
114
  let url = urlAttr.value;
@@ -149,7 +119,6 @@ export async function compileMdx(file, { request, forceFresh } = {}) {
149
119
  epicVideoEmbeds.push(url);
150
120
  });
151
121
  },
152
- () => remarkCodeFile(codeFileData),
153
122
  emoji,
154
123
  ];
155
124
  options.rehypePlugins = [
@@ -165,17 +134,6 @@ export async function compileMdx(file, { request, forceFresh } = {}) {
165
134
  if (!bundleResult)
166
135
  throw new Error(`Timeout for file: ${file}`);
167
136
  const result = { code: bundleResult.code, title, epicVideoEmbeds };
168
- await fsExtra.ensureDir(cacheDir);
169
- await fs.promises.writeFile(cacheLocation, JSON.stringify({
170
- value: {
171
- ...result,
172
- compiledTime: Date.now(),
173
- embeddedFiles: codeFileData.embeddedFiles.size
174
- ? Object.fromEntries(codeFileData.embeddedFiles)
175
- : undefined,
176
- },
177
- }));
178
- await updateEmbeddedFilesCache(codeFileData);
179
137
  return result;
180
138
  }
181
139
  catch (error) {
@@ -219,79 +177,6 @@ export async function compileMarkdownString(markdownString) {
219
177
  },
220
178
  });
221
179
  }
222
- const modifiedEmbeddedFilesTime = remember('modified_embedded_files_time', () => new Map());
223
- const EMBEDDED_FILES_CACHE_KEY = 'embeddedFilesCache';
224
- async function updateEmbeddedFilesCache({ mdxFile, embeddedFiles, }) {
225
- if (mdxFile.includes('playground'))
226
- return;
227
- let cachedList = await getEmbeddedFilesCache();
228
- const hash = cachedList ? md5(JSON.stringify(cachedList)) : null;
229
- // make sure we get clean list before updating it
230
- if (cachedList) {
231
- for (const [key, value] of Object.entries(cachedList)) {
232
- cachedList[key] = value.filter((item) => item !== mdxFile);
233
- if (cachedList[key]?.length === 0) {
234
- delete cachedList[key];
235
- }
236
- }
237
- }
238
- if (embeddedFiles.size) {
239
- if (!cachedList) {
240
- cachedList = {};
241
- }
242
- const files = Array.from(new Set(Array.from(embeddedFiles.values()).map(({ file }) => file))).sort();
243
- for (const file of files) {
244
- cachedList[file] = [...(cachedList[file] ?? []), mdxFile];
245
- }
246
- }
247
- if (cachedList && hash !== md5(JSON.stringify(cachedList))) {
248
- await fsExtra.ensureDir(cacheDir);
249
- const embeddedFilesLocation = path.join(cacheDir, 'embeddedFiles.json');
250
- modifiedEmbeddedFilesTime.set(EMBEDDED_FILES_CACHE_KEY, Date.now());
251
- await fs.promises.writeFile(embeddedFilesLocation, JSON.stringify({ ...cachedList }));
252
- }
253
- }
254
- async function getEmbeddedFilesCache() {
255
- const key = EMBEDDED_FILES_CACHE_KEY;
256
- function getForceFresh(cacheEntry) {
257
- if (!cacheEntry)
258
- return true;
259
- const latestModifiedTime = modifiedEmbeddedFilesTime.get(key);
260
- if (!latestModifiedTime)
261
- return undefined;
262
- return latestModifiedTime > cacheEntry.metadata.createdTime
263
- ? true
264
- : undefined;
265
- }
266
- return cachified({
267
- key,
268
- cache: embeddedFilesCache,
269
- ttl: 1000 * 60 * 60 * 24,
270
- forceFresh: getForceFresh(embeddedFilesCache.get(key)),
271
- getFreshValue: async () => {
272
- try {
273
- const embeddedFilesLocation = path.join(cacheDir, 'embeddedFiles.json');
274
- if (await checkFileExists(embeddedFilesLocation)) {
275
- return JSON.parse(await fs.promises.readFile(embeddedFilesLocation, 'utf-8'));
276
- }
277
- }
278
- catch {
279
- console.error(`Unable to read 'embeddedFiles.json' from: `, cacheDir);
280
- }
281
- return undefined;
282
- },
283
- });
284
- }
285
- export async function isEmbeddedFile(filePath) {
286
- if (process.env.NODE_ENV !== 'development')
287
- return false;
288
- const embeddedFilesList = await getEmbeddedFilesCache();
289
- if (embeddedFilesList) {
290
- const embeddedFiles = Object.keys(embeddedFilesList);
291
- return embeddedFiles.includes(filePath.replace(/\\/g, '/'));
292
- }
293
- return false;
294
- }
295
180
  let _queue = null;
296
181
  async function getQueue() {
297
182
  if (_queue)
@@ -1 +1 @@
1
- {"version":3,"file":"compile-mdx.server.js","sourceRoot":"","sources":["../../src/compile-mdx.server.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { cachified, type CacheEntry } from '@epic-web/cachified'\nimport { remember } from '@epic-web/remember'\nimport { remarkCodeBlocksShiki } from '@kentcdodds/md-temp'\nimport fsExtra from 'fs-extra'\nimport { type Element, type Root as HastRoot } from 'hast'\nimport md5 from 'md5-hex'\nimport { type Root as MdastRoot } from 'mdast'\nimport { bundleMDX } from 'mdx-bundler'\nimport PQueue from 'p-queue'\nimport remarkAutolinkHeadings from 'remark-autolink-headings'\nimport emoji from 'remark-emoji'\nimport gfm from 'remark-gfm'\nimport { type PluggableList } from 'unified'\nimport { visit } from 'unist-util-visit'\nimport {\n\tcompiledMarkdownCache,\n\tembeddedFilesCache,\n\tshouldForceFresh,\n\ttype CachedEmbeddedFilesList,\n} from './cache.server.js'\nimport {\n\tremarkCodeFile,\n\ttype CodeFileData,\n\ttype EmbeddedFile,\n} from './codefile-mdx.server.js'\n\nconst cacheDir = path.join(\n\tprocess.env.EPICSHOP_CONTEXT_CWD ?? process.cwd(),\n\t'./node_modules/.cache/compile-mdx',\n)\n\nfunction trimCodeBlocks() {\n\treturn async function transformer(tree: HastRoot) {\n\t\tvisit(tree, 'element', (preNode: Element) => {\n\t\t\tif (preNode.tagName !== 'pre' || !preNode.children.length) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst codeNode = preNode.children[0]\n\t\t\tif (\n\t\t\t\t!codeNode ||\n\t\t\t\tcodeNode.type !== 'element' ||\n\t\t\t\tcodeNode.tagName !== 'code'\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst [codeStringNode] = codeNode.children\n\t\t\tif (!codeStringNode) return\n\n\t\t\tif (codeStringNode.type !== 'text') {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`trimCodeBlocks: Unexpected: codeStringNode type is not \"text\": ${codeStringNode.type}`,\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcodeStringNode.value = codeStringNode.value.trimEnd()\n\t\t})\n\t}\n}\n\nfunction removePreContainerDivs() {\n\treturn async function preContainerDivsTransformer(tree: HastRoot) {\n\t\tvisit(\n\t\t\ttree,\n\t\t\t{ type: 'element', tagName: 'pre' },\n\t\t\tfunction visitor(node, index, parent) {\n\t\t\t\tif (parent?.type !== 'element') return\n\t\t\t\tif (parent.tagName !== 'div') return\n\t\t\t\tif (parent.children.length !== 1 && index === 0) return\n\t\t\t\tObject.assign(parent, node)\n\t\t\t},\n\t\t)\n\t}\n}\n\nconst rehypePlugins = [\n\ttrimCodeBlocks,\n\tremarkCodeBlocksShiki,\n\tremovePreContainerDivs,\n] satisfies PluggableList\n\nfunction checkFileExists(file: string) {\n\treturn fs.promises.access(file, fs.constants.F_OK).then(\n\t\t() => true,\n\t\t() => false,\n\t)\n}\n\nconst verboseLog =\n\tprocess.env.EPICSHOP_VERBOSE_LOG === 'true' ? console.log : () => {}\n\n/**\n * @param embeddedFiles {string[]} - list of embedded files\n * @param lastCompiledTime {number} - timestamp indicating the last time mdx file was compiled\n * @returns true if all embedded file mtimeMs are older then the file compiled time,\n * false if we need update\n */\nfunction validateEmbeddedFiles(\n\tembeddedFiles: IterableIterator<EmbeddedFile>,\n\tlastCompiledTime: number,\n): Promise<boolean> {\n\tif (process.env.NODE_ENV !== 'development') return Promise.resolve(true)\n\treturn Promise.all(\n\t\tArray.from(embeddedFiles).map(async ({ file }) => {\n\t\t\tconst stat = await fs.promises.stat(file).catch(() => ({ mtimeMs: 0 }))\n\t\t\treturn lastCompiledTime > stat.mtimeMs || Promise.reject()\n\t\t}),\n\t).then(\n\t\t() => true,\n\t\t() => false,\n\t)\n}\n\nexport async function compileMdx(\n\tfile: string,\n\t{ request, forceFresh }: { request?: Request; forceFresh?: boolean } = {},\n): Promise<{\n\tcode: string\n\ttitle: string | null\n\tepicVideoEmbeds: Array<string>\n}> {\n\tif (!(await checkFileExists(file))) {\n\t\tthrow new Error(`File does not exist: ${file}`)\n\t}\n\n\tlet cachedEmbeddedFiles = new Map<string, EmbeddedFile>()\n\n\tconst stat = await fs.promises.stat(file)\n\tconst cacheLocation = path.join(cacheDir, `${md5(file)}.json`)\n\n\tconst requireFresh = await shouldForceFresh({\n\t\tforceFresh,\n\t\trequest,\n\t\tkey: cacheLocation,\n\t})\n\tif (!requireFresh && (await checkFileExists(cacheLocation))) {\n\t\ttry {\n\t\t\tconst cached = JSON.parse(\n\t\t\t\tawait fs.promises.readFile(cacheLocation, 'utf-8'),\n\t\t\t) as any\n\n\t\t\tcachedEmbeddedFiles = new Map(\n\t\t\t\tObject.entries(cached.value.embeddedFiles ?? {}),\n\t\t\t)\n\n\t\t\tconst compiledTime = cached.value.compiledTime ?? 0\n\t\t\tconst warningCancled =\n\t\t\t\tprocess.env.NODE_ENV === 'development'\n\t\t\t\t\t? cached?.value?.warningCancled ?? false\n\t\t\t\t\t: false\n\t\t\tif (\n\t\t\t\tcompiledTime > stat.mtimeMs &&\n\t\t\t\t!warningCancled &&\n\t\t\t\t(await validateEmbeddedFiles(\n\t\t\t\t\tcachedEmbeddedFiles.values(),\n\t\t\t\t\tcompiledTime,\n\t\t\t\t))\n\t\t\t) {\n\t\t\t\treturn cached.value\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error reading cached file: ${cacheLocation}`, error)\n\t\t\tvoid fs.promises.unlink(cacheLocation)\n\t\t}\n\t}\n\tlet title: string | null = null\n\tconst epicVideoEmbeds: Array<string> = []\n\tconst codeFileData = {\n\t\tmdxFile: file,\n\t\tcacheLocation,\n\t\tcachedEmbeddedFiles,\n\t\tembeddedFiles: new Map<string, EmbeddedFile>(),\n\t}\n\n\ttry {\n\t\tverboseLog(`Compiling ${file}`)\n\t\tconst bundleResult = await queuedBundleMDX({\n\t\t\tfile,\n\t\t\tcwd: path.dirname(file),\n\t\t\tmdxOptions(options) {\n\t\t\t\toptions.remarkPlugins = [\n\t\t\t\t\t...(options.remarkPlugins ?? []),\n\t\t\t\t\t[remarkAutolinkHeadings, { behavior: 'wrap' }],\n\t\t\t\t\tgfm,\n\t\t\t\t\t() => (tree: MdastRoot) => {\n\t\t\t\t\t\tvisit(tree, 'heading', (node) => {\n\t\t\t\t\t\t\tif (title) return\n\t\t\t\t\t\t\tif (node.depth === 1) {\n\t\t\t\t\t\t\t\tvisit(node, 'text', (textNode) => {\n\t\t\t\t\t\t\t\t\ttitle = textNode.value.trim()\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t\ttitle = title ? title.replace(/^\\d+\\. /, '').trim() : null\n\t\t\t\t\t},\n\t\t\t\t\t() => (tree: MdastRoot) => {\n\t\t\t\t\t\tvisit(tree, 'mdxJsxFlowElement', (jsxEl) => {\n\t\t\t\t\t\t\tif (jsxEl.name !== 'EpicVideo') return\n\t\t\t\t\t\t\tconst urlAttr = jsxEl.attributes.find(\n\t\t\t\t\t\t\t\t(a) => a.type === 'mdxJsxAttribute' && a.name === 'url',\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tif (!urlAttr) return\n\t\t\t\t\t\t\tlet url = urlAttr.value\n\t\t\t\t\t\t\tif (typeof url !== 'string') return\n\t\t\t\t\t\t\tif (url.endsWith('/')) url = url.slice(0, -1)\n\t\t\t\t\t\t\tepicVideoEmbeds.push(url)\n\t\t\t\t\t\t})\n\t\t\t\t\t},\n\t\t\t\t\t() => remarkCodeFile(codeFileData),\n\t\t\t\t\temoji,\n\t\t\t\t]\n\t\t\t\toptions.rehypePlugins = [\n\t\t\t\t\t...(options.rehypePlugins ?? []),\n\t\t\t\t\t...rehypePlugins,\n\t\t\t\t]\n\t\t\t\toptions.mdxExtensions = ['.mdx', '.md']\n\t\t\t\toptions.format = 'mdx'\n\t\t\t\toptions.development = false\n\t\t\t\treturn options\n\t\t\t},\n\t\t})\n\t\tif (!bundleResult) throw new Error(`Timeout for file: ${file}`)\n\n\t\tconst result = { code: bundleResult.code, title, epicVideoEmbeds }\n\t\tawait fsExtra.ensureDir(cacheDir)\n\t\tawait fs.promises.writeFile(\n\t\t\tcacheLocation,\n\t\t\tJSON.stringify({\n\t\t\t\tvalue: {\n\t\t\t\t\t...result,\n\t\t\t\t\tcompiledTime: Date.now(),\n\t\t\t\t\tembeddedFiles: codeFileData.embeddedFiles.size\n\t\t\t\t\t\t? Object.fromEntries(codeFileData.embeddedFiles)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t},\n\t\t\t}),\n\t\t)\n\t\tawait updateEmbeddedFilesCache(codeFileData)\n\t\treturn result\n\t} catch (error: unknown) {\n\t\tconsole.error(`Compilation error for file: `, file, error)\n\t\tthrow error\n\t} finally {\n\t\tverboseLog(`Successfully compiled ${file}`)\n\t}\n}\n\nexport async function compileMarkdownString(markdownString: string) {\n\treturn cachified({\n\t\tkey: markdownString,\n\t\tcache: compiledMarkdownCache,\n\t\tttl: 1000 * 60 * 60 * 24,\n\t\tgetFreshValue: async () => {\n\t\t\ttry {\n\t\t\t\tverboseLog(`Compiling string`, markdownString)\n\t\t\t\tconst result = await queuedBundleMDX({\n\t\t\t\t\tsource: markdownString,\n\t\t\t\t\tmdxOptions(options) {\n\t\t\t\t\t\toptions.rehypePlugins = [\n\t\t\t\t\t\t\t...(options.rehypePlugins ?? []),\n\t\t\t\t\t\t\t...rehypePlugins,\n\t\t\t\t\t\t]\n\t\t\t\t\t\toptions.development = false\n\t\t\t\t\t\treturn options\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tif (!result) throw new Error(`Timed out compiling markdown string`)\n\n\t\t\t\treturn result.code\n\t\t\t} catch (error: unknown) {\n\t\t\t\tconsole.error(`Compilation error for code: `, markdownString, error)\n\t\t\t\tthrow error\n\t\t\t} finally {\n\t\t\t\tverboseLog(`Successfully compiled string`, markdownString)\n\t\t\t}\n\t\t},\n\t})\n}\n\nconst modifiedEmbeddedFilesTime = remember(\n\t'modified_embedded_files_time',\n\t() => new Map<string, number>(),\n)\n\nconst EMBEDDED_FILES_CACHE_KEY = 'embeddedFilesCache'\n\nasync function updateEmbeddedFilesCache({\n\tmdxFile,\n\tembeddedFiles,\n}: CodeFileData) {\n\tif (mdxFile.includes('playground')) return\n\tlet cachedList = await getEmbeddedFilesCache()\n\tconst hash = cachedList ? md5(JSON.stringify(cachedList)) : null\n\n\t// make sure we get clean list before updating it\n\tif (cachedList) {\n\t\tfor (const [key, value] of Object.entries(cachedList)) {\n\t\t\tcachedList[key] = value.filter((item) => item !== mdxFile)\n\t\t\tif (cachedList[key]?.length === 0) {\n\t\t\t\tdelete cachedList[key]\n\t\t\t}\n\t\t}\n\t}\n\n\tif (embeddedFiles.size) {\n\t\tif (!cachedList) {\n\t\t\tcachedList = {}\n\t\t}\n\t\tconst files = Array.from(\n\t\t\tnew Set(Array.from(embeddedFiles.values()).map(({ file }) => file)),\n\t\t).sort()\n\t\tfor (const file of files) {\n\t\t\tcachedList[file] = [...(cachedList[file] ?? []), mdxFile]\n\t\t}\n\t}\n\n\tif (cachedList && hash !== md5(JSON.stringify(cachedList))) {\n\t\tawait fsExtra.ensureDir(cacheDir)\n\t\tconst embeddedFilesLocation = path.join(cacheDir, 'embeddedFiles.json')\n\t\tmodifiedEmbeddedFilesTime.set(EMBEDDED_FILES_CACHE_KEY, Date.now())\n\t\tawait fs.promises.writeFile(\n\t\t\tembeddedFilesLocation,\n\t\t\tJSON.stringify({ ...cachedList }),\n\t\t)\n\t}\n}\n\nasync function getEmbeddedFilesCache() {\n\tconst key = EMBEDDED_FILES_CACHE_KEY\n\n\tfunction getForceFresh(cacheEntry: CacheEntry | null | undefined) {\n\t\tif (!cacheEntry) return true\n\t\tconst latestModifiedTime = modifiedEmbeddedFilesTime.get(key)\n\t\tif (!latestModifiedTime) return undefined\n\t\treturn latestModifiedTime > cacheEntry.metadata.createdTime\n\t\t\t? true\n\t\t\t: undefined\n\t}\n\n\treturn cachified({\n\t\tkey,\n\t\tcache: embeddedFilesCache,\n\t\tttl: 1000 * 60 * 60 * 24,\n\t\tforceFresh: getForceFresh(embeddedFilesCache.get(key)),\n\t\tgetFreshValue: async () => {\n\t\t\ttry {\n\t\t\t\tconst embeddedFilesLocation = path.join(cacheDir, 'embeddedFiles.json')\n\t\t\t\tif (await checkFileExists(embeddedFilesLocation)) {\n\t\t\t\t\treturn JSON.parse(\n\t\t\t\t\t\tawait fs.promises.readFile(embeddedFilesLocation, 'utf-8'),\n\t\t\t\t\t) as CachedEmbeddedFilesList\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tconsole.error(`Unable to read 'embeddedFiles.json' from: `, cacheDir)\n\t\t\t}\n\t\t\treturn undefined\n\t\t},\n\t})\n}\n\nexport async function isEmbeddedFile(filePath: string) {\n\tif (process.env.NODE_ENV !== 'development') return false\n\tconst embeddedFilesList = await getEmbeddedFilesCache()\n\tif (embeddedFilesList) {\n\t\tconst embeddedFiles = Object.keys(embeddedFilesList)\n\t\treturn embeddedFiles.includes(filePath.replace(/\\\\/g, '/'))\n\t}\n\treturn false\n}\n\nlet _queue: PQueue | null = null\nasync function getQueue() {\n\tif (_queue) return _queue\n\n\t_queue = new PQueue({\n\t\tconcurrency: 1,\n\t\tthrowOnTimeout: true,\n\t\ttimeout: 1000 * 60,\n\t})\n\treturn _queue\n}\n\n// We have to use a queue because we can't run more than one of these at a time\n// or we'll hit an out of memory error because esbuild uses a lot of memory...\nasync function queuedBundleMDX(...args: Parameters<typeof bundleMDX>) {\n\tconst queue = await getQueue()\n\tconst result = await queue.add(() => bundleMDX(...args))\n\treturn result\n}\n\n// TODO: Fix these\n/*\neslint\n\t\"@typescript-eslint/no-unsafe-assignment\": \"off\",\n\t\"@typescript-eslint/no-unsafe-member-access\": \"off\",\n\t\"@typescript-eslint/no-unnecessary-condition\": \"off\",\n\t\"@typescript-eslint/no-unsafe-argument\": \"off\",\n*/\n"]}
1
+ {"version":3,"file":"compile-mdx.server.js","sourceRoot":"","sources":["../../src/compile-mdx.server.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { remarkCodeBlocksShiki } from '@kentcdodds/md-temp'\nimport { type Element, type Root as HastRoot } from 'hast'\nimport { type Root as MdastRoot } from 'mdast'\nimport { bundleMDX } from 'mdx-bundler'\nimport PQueue from 'p-queue'\nimport remarkAutolinkHeadings from 'remark-autolink-headings'\nimport emoji from 'remark-emoji'\nimport gfm from 'remark-gfm'\nimport { type PluggableList } from 'unified'\nimport { visit } from 'unist-util-visit'\nimport {\n\tcachified,\n\tcompiledInstructionMarkdownCache,\n\tcompiledMarkdownCache,\n\tshouldForceFresh,\n} from './cache.server.js'\nimport { type Timings } from './timing.server.js'\n\nfunction trimCodeBlocks() {\n\treturn async function transformer(tree: HastRoot) {\n\t\tvisit(tree, 'element', (preNode: Element) => {\n\t\t\tif (preNode.tagName !== 'pre' || !preNode.children.length) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst codeNode = preNode.children[0]\n\t\t\tif (\n\t\t\t\t!codeNode ||\n\t\t\t\tcodeNode.type !== 'element' ||\n\t\t\t\tcodeNode.tagName !== 'code'\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst [codeStringNode] = codeNode.children\n\t\t\tif (!codeStringNode) return\n\n\t\t\tif (codeStringNode.type !== 'text') {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`trimCodeBlocks: Unexpected: codeStringNode type is not \"text\": ${codeStringNode.type}`,\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcodeStringNode.value = codeStringNode.value.trimEnd()\n\t\t})\n\t}\n}\n\nfunction removePreContainerDivs() {\n\treturn async function preContainerDivsTransformer(tree: HastRoot) {\n\t\tvisit(\n\t\t\ttree,\n\t\t\t{ type: 'element', tagName: 'pre' },\n\t\t\tfunction visitor(node, index, parent) {\n\t\t\t\tif (parent?.type !== 'element') return\n\t\t\t\tif (parent.tagName !== 'div') return\n\t\t\t\tif (parent.children.length !== 1 && index === 0) return\n\t\t\t\tObject.assign(parent, node)\n\t\t\t},\n\t\t)\n\t}\n}\n\nconst rehypePlugins = [\n\ttrimCodeBlocks,\n\tremarkCodeBlocksShiki,\n\tremovePreContainerDivs,\n] satisfies PluggableList\n\nconst verboseLog =\n\tprocess.env.EPICSHOP_VERBOSE_LOG === 'true' ? console.log : () => {}\n\nexport async function compileMdx(\n\tfile: string,\n\t{\n\t\trequest,\n\t\ttimings,\n\t\tforceFresh,\n\t}: {\n\t\trequest?: Request\n\t\ttimings?: Timings\n\t\tforceFresh?: boolean\n\t} = {},\n) {\n\tconst stat = await fs.promises\n\t\t.stat(file)\n\t\t.catch((error: unknown) => ({ error }))\n\tif ('error' in stat) {\n\t\tthrow new Error(`File stat cannot be read: ${stat.error}`)\n\t}\n\n\tconst key = `file:${file}`\n\tforceFresh = await shouldForceFresh({ forceFresh, request, key })\n\n\tconst existingCacheEntry = await compiledInstructionMarkdownCache.get(key)\n\tif (!forceFresh && existingCacheEntry) {\n\t\tconst compiledTime = existingCacheEntry.metadata.createdTime\n\t\tif (stat.mtimeMs <= compiledTime) {\n\t\t\treturn existingCacheEntry.value\n\t\t}\n\t}\n\n\treturn cachified({\n\t\tkey,\n\t\tcache: compiledInstructionMarkdownCache,\n\t\trequest,\n\t\ttimings,\n\t\tforceFresh,\n\t\tgetFreshValue: () => compileMdxImpl(file),\n\t})\n}\n\nasync function compileMdxImpl(file: string): Promise<{\n\tcode: string\n\ttitle: string | null\n\tepicVideoEmbeds: Array<string>\n}> {\n\tlet title: string | null = null\n\tconst epicVideoEmbeds: Array<string> = []\n\n\ttry {\n\t\tverboseLog(`Compiling ${file}`)\n\t\tconst bundleResult = await queuedBundleMDX({\n\t\t\tfile,\n\t\t\tcwd: path.dirname(file),\n\t\t\tmdxOptions(options) {\n\t\t\t\toptions.remarkPlugins = [\n\t\t\t\t\t...(options.remarkPlugins ?? []),\n\t\t\t\t\t[remarkAutolinkHeadings, { behavior: 'wrap' }],\n\t\t\t\t\tgfm,\n\t\t\t\t\t() => (tree: MdastRoot) => {\n\t\t\t\t\t\tvisit(tree, 'heading', (node) => {\n\t\t\t\t\t\t\tif (title) return\n\t\t\t\t\t\t\tif (node.depth === 1) {\n\t\t\t\t\t\t\t\tvisit(node, 'text', (textNode) => {\n\t\t\t\t\t\t\t\t\ttitle = textNode.value.trim()\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t\ttitle = title ? title.replace(/^\\d+\\. /, '').trim() : null\n\t\t\t\t\t},\n\t\t\t\t\t() => (tree: MdastRoot) => {\n\t\t\t\t\t\tvisit(tree, 'mdxJsxFlowElement', (jsxEl) => {\n\t\t\t\t\t\t\t// @ts-expect-error no idea why this started being an issue suddenly 🤷‍♂️\n\t\t\t\t\t\t\tif (jsxEl.name !== 'EpicVideo') return\n\t\t\t\t\t\t\t// @ts-expect-error no idea why this started being an issue suddenly 🤷‍♂️\n\t\t\t\t\t\t\tconst urlAttr = jsxEl.attributes.find(\n\t\t\t\t\t\t\t\t// @ts-expect-error no idea why this started being an issue suddenly 🤷‍♂️\n\t\t\t\t\t\t\t\t(a) => a.type === 'mdxJsxAttribute' && a.name === 'url',\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tif (!urlAttr) return\n\t\t\t\t\t\t\tlet url = urlAttr.value\n\t\t\t\t\t\t\tif (typeof url !== 'string') return\n\t\t\t\t\t\t\tif (url.endsWith('/')) url = url.slice(0, -1)\n\t\t\t\t\t\t\tepicVideoEmbeds.push(url)\n\t\t\t\t\t\t})\n\t\t\t\t\t},\n\t\t\t\t\temoji,\n\t\t\t\t]\n\t\t\t\toptions.rehypePlugins = [\n\t\t\t\t\t...(options.rehypePlugins ?? []),\n\t\t\t\t\t...rehypePlugins,\n\t\t\t\t]\n\t\t\t\toptions.mdxExtensions = ['.mdx', '.md']\n\t\t\t\toptions.format = 'mdx'\n\t\t\t\toptions.development = false\n\t\t\t\treturn options\n\t\t\t},\n\t\t})\n\t\tif (!bundleResult) throw new Error(`Timeout for file: ${file}`)\n\n\t\tconst result = { code: bundleResult.code, title, epicVideoEmbeds }\n\t\treturn result\n\t} catch (error: unknown) {\n\t\tconsole.error(`Compilation error for file: `, file, error)\n\t\tthrow error\n\t} finally {\n\t\tverboseLog(`Successfully compiled ${file}`)\n\t}\n}\n\nexport async function compileMarkdownString(markdownString: string) {\n\treturn cachified({\n\t\tkey: markdownString,\n\t\tcache: compiledMarkdownCache,\n\t\tttl: 1000 * 60 * 60 * 24,\n\t\tgetFreshValue: async () => {\n\t\t\ttry {\n\t\t\t\tverboseLog(`Compiling string`, markdownString)\n\t\t\t\tconst result = await queuedBundleMDX({\n\t\t\t\t\tsource: markdownString,\n\t\t\t\t\tmdxOptions(options) {\n\t\t\t\t\t\toptions.rehypePlugins = [\n\t\t\t\t\t\t\t...(options.rehypePlugins ?? []),\n\t\t\t\t\t\t\t...rehypePlugins,\n\t\t\t\t\t\t]\n\t\t\t\t\t\toptions.development = false\n\t\t\t\t\t\treturn options\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tif (!result) throw new Error(`Timed out compiling markdown string`)\n\n\t\t\t\treturn result.code\n\t\t\t} catch (error: unknown) {\n\t\t\t\tconsole.error(`Compilation error for code: `, markdownString, error)\n\t\t\t\tthrow error\n\t\t\t} finally {\n\t\t\t\tverboseLog(`Successfully compiled string`, markdownString)\n\t\t\t}\n\t\t},\n\t})\n}\n\nlet _queue: PQueue | null = null\nasync function getQueue() {\n\tif (_queue) return _queue\n\n\t_queue = new PQueue({\n\t\tconcurrency: 1,\n\t\tthrowOnTimeout: true,\n\t\ttimeout: 1000 * 60,\n\t})\n\treturn _queue\n}\n\n// We have to use a queue because we can't run more than one of these at a time\n// or we'll hit an out of memory error because esbuild uses a lot of memory...\nasync function queuedBundleMDX(...args: Parameters<typeof bundleMDX>) {\n\tconst queue = await getQueue()\n\tconst result = await queue.add(() => bundleMDX(...args))\n\treturn result\n}\n\n// TODO: Fix these\n/*\neslint\n\t\"@typescript-eslint/no-unsafe-assignment\": \"off\",\n\t\"@typescript-eslint/no-unsafe-member-access\": \"off\",\n\t\"@typescript-eslint/no-unnecessary-condition\": \"off\",\n\t\"@typescript-eslint/no-unsafe-argument\": \"off\",\n*/\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epic-web/workshop-utils",
3
- "version": "4.28.6",
3
+ "version": "5.0.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -19,7 +19,6 @@
19
19
  "./db.server": "./src/db.server.ts",
20
20
  "./timing.server": "./src/timing.server.ts",
21
21
  "./compile-mdx.server": "./src/compile-mdx.server.ts",
22
- "./codefile-mdx.server": "./src/codefile-mdx.server.ts",
23
22
  "./change-tracker.server": "./src/change-tracker.server.ts",
24
23
  "./git.server": "./src/git.server.ts",
25
24
  "./iframe-sync": "./src/iframe-sync.ts",
@@ -81,13 +80,6 @@
81
80
  "default": "./dist/esm/compile-mdx.server.js"
82
81
  }
83
82
  },
84
- "./codefile-mdx.server": {
85
- "import": {
86
- "source": "./src/codefile-mdx.server.ts",
87
- "types": "./dist/esm/codefile-mdx.server.d.ts",
88
- "default": "./dist/esm/codefile-mdx.server.js"
89
- }
90
- },
91
83
  "./change-tracker.server": {
92
84
  "import": {
93
85
  "source": "./src/change-tracker.server.ts",
@@ -1,16 +0,0 @@
1
- import { type Root as MdastRoot } from 'mdast';
2
- export type EmbeddedFile = {
3
- error?: boolean;
4
- file: string;
5
- hash: string;
6
- line?: number;
7
- warning?: string;
8
- };
9
- export type CodeFileData = {
10
- mdxFile: string;
11
- cacheLocation: string;
12
- cachedEmbeddedFiles: Map<string, EmbeddedFile>;
13
- embeddedFiles: Map<string, EmbeddedFile>;
14
- };
15
- export declare function remarkCodeFile(data: CodeFileData): (tree: MdastRoot) => Promise<void>;
16
- //# sourceMappingURL=codefile-mdx.server.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"codefile-mdx.server.d.ts","sourceRoot":"","sources":["../../src/codefile-mdx.server.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,IAAI,IAAI,SAAS,EAAiC,MAAM,OAAO,CAAA;AAuB7E,MAAM,MAAM,YAAY,GAAG;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IAC9C,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;CACxC,CAAA;AA8LD,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,UA4IA,SAAS,mBAczD"}
@@ -1,274 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { createProcessor } from '@mdx-js/mdx';
4
- import md5 from 'md5-hex';
5
- import { removePosition } from 'unist-util-remove-position';
6
- import { visit } from 'unist-util-visit';
7
- import * as z from 'zod';
8
- const APP_TYPES = ['problem', 'solution', 'playground'];
9
- const safePath = (s) => s.replace(/\\/g, '/');
10
- const REG_EXP = /^(?:\d+(?:-\d+)?,)*\d+(?:-\d+)?$/;
11
- const isValidRangeFormat = (value) => value ? REG_EXP.test(value) : true;
12
- const transformRange = (value) => value?.split(',').map((range) => {
13
- const [start, end] = range.split('-').map(Number);
14
- return [start, end ?? start];
15
- });
16
- const isRangeBounded = (range, ctx, lines) => {
17
- if (!lines || !Array.isArray(range))
18
- return;
19
- if (range.flat().some((r) => r < 1 || r > lines)) {
20
- ctx.addIssue({
21
- code: z.ZodIssueCode.custom,
22
- message: `Range must be between 1 and ${lines}`,
23
- });
24
- }
25
- };
26
- const isRangeInOrder = (range) => Array.isArray(range)
27
- ? range.every(([a, b]) => !isNaN(Number(a)) && !isNaN(Number(b)) && b >= a)
28
- : true;
29
- const isRangesNonOverlapping = (range) => {
30
- if (!Array.isArray(range))
31
- return true;
32
- return range.every(([a], i) => i === 0 || (range[i - 1]?.[1] ?? 0) < a);
33
- };
34
- let fileContentCache = new Map();
35
- async function getFileContent(filePath) {
36
- if (fileContentCache.has(filePath)) {
37
- return fileContentCache.get(filePath);
38
- }
39
- try {
40
- const content = await fs.promises.readFile(filePath, 'utf-8');
41
- const fileContent = content.split('\n');
42
- fileContentCache.set(filePath, fileContent);
43
- return fileContent;
44
- }
45
- catch {
46
- console.warn(`@epic-web/workshop-app - invalid CodeFile.\nCould not read file: ${filePath}\n`);
47
- }
48
- }
49
- async function validateProps(props, appDir) {
50
- let validRange;
51
- let linesCount = 0;
52
- const BooleanSchema = z
53
- .nullable(z.string())
54
- .optional()
55
- .refine((v) => ['true', 'false', null, undefined].includes(v), 'optional boolean key can be "true", "false", null or undefined')
56
- .transform((v) => v === null || Boolean(v));
57
- const RangeSchema = z
58
- .string()
59
- .optional()
60
- .refine(isValidRangeFormat, 'Invalid range format')
61
- .transform(transformRange)
62
- .superRefine((val, ctx) => isRangeBounded(val, ctx, linesCount))
63
- .refine(isRangeInOrder, 'Range must be in order low-high');
64
- const inputSchema = z
65
- .object({
66
- file: z
67
- .string()
68
- .nonempty()
69
- .transform(async (file, ctx) => {
70
- const fullPath = path.join(appDir, file);
71
- const content = await getFileContent(fullPath);
72
- if (!content) {
73
- ctx.addIssue({
74
- code: z.ZodIssueCode.custom,
75
- message: `Could not read file`,
76
- fatal: true,
77
- });
78
- return z.NEVER;
79
- }
80
- linesCount = content.length;
81
- // @mdx-js/mdx parser can NOT handle relative path with backslashes
82
- return {
83
- fullPath: safePath(fullPath),
84
- filePath: safePath(file),
85
- content,
86
- };
87
- }),
88
- range: RangeSchema.refine((range) => {
89
- const isValid = isRangesNonOverlapping(range);
90
- // we use this value in highlight refine
91
- validRange = isValid ? range : undefined;
92
- return isValid;
93
- }, 'Ranges must not overlap'),
94
- highlight: RangeSchema.refine((highlight) => {
95
- if (!Array.isArray(highlight) || !Array.isArray(validRange)) {
96
- return z.NEVER;
97
- }
98
- return highlight.every(([hStart, hEnd]) => validRange?.some(([rStart, rEnd]) => hStart >= rStart && hEnd <= rEnd));
99
- }, 'Highlight range must be within defined range')
100
- .transform(() => props.highlight)
101
- .optional(),
102
- nonumber: BooleanSchema,
103
- nocopy: BooleanSchema,
104
- buttons: z
105
- .string()
106
- .optional()
107
- .transform((str) => str ? str.split(',') : [])
108
- .refine((arr) => arr.every((item) => APP_TYPES.includes(item)), {
109
- message: `Buttons can only be any of ${APP_TYPES.join(',')}`,
110
- }),
111
- })
112
- .strict();
113
- return inputSchema.safeParseAsync(props);
114
- }
115
- async function createErrorNotification(node, errors, mdxFile, appType) {
116
- const filename = path.basename(mdxFile);
117
- const startLine = node.position?.start.line;
118
- const endLine = node.position?.end.line;
119
- const codeFence = async () => {
120
- if (startLine && endLine) {
121
- const contentStr = await getFileContent(mdxFile);
122
- const content = contentStr?.slice(startLine - 1, endLine).join('\n');
123
- if (content) {
124
- return `
125
- \`\`\`tsx filename=${filename} start=${startLine} nocopy
126
- ${content}
127
- \`\`\``.trim();
128
- }
129
- }
130
- return '';
131
- };
132
- const mdxSource = `
133
- <CodeFileNotification variant="error" file="${filename}" line="${startLine}" type="${appType}">
134
- <callout-danger class="notification">
135
- <div className="title">CodeFile Error: invalid input</div>
136
- ${errors.map((error) => `<div>${error}</div>`).join('')}
137
- ${await codeFence()}
138
- </callout-danger>
139
- </CodeFileNotification>`;
140
- return mdxToMdast(mdxSource);
141
- }
142
- // based on https://github.com/sindresorhus/strip-indent
143
- function stripIndent(string) {
144
- const match = string.match(/^[ \t]*(?=\S)/gm);
145
- const indent = match?.reduce((r, a) => Math.min(r, a.length), Infinity) ?? 0;
146
- if (indent === 0) {
147
- return string;
148
- }
149
- const regex = new RegExp(`^[ \\t]{${indent}}`, 'gm');
150
- return string.replace(regex, '');
151
- }
152
- function mdxToMdast(mdx) {
153
- const processor = createProcessor();
154
- const mdast = processor.parse(mdx.trim());
155
- removePosition(mdast, { force: true });
156
- return mdast.type === 'root' ? mdast.children : [mdast];
157
- }
158
- export function remarkCodeFile(data) {
159
- fileContentCache = new Map();
160
- const mdxFile = data.mdxFile;
161
- const appDir = path.dirname(mdxFile);
162
- const appType = mdxFile.includes('problem')
163
- ? 'problem'
164
- : mdxFile.includes('solution')
165
- ? 'solution'
166
- : 'other'; // not in exercise
167
- async function replaceCodeFileNode({ node, parent, }) {
168
- if (!parent) {
169
- console.warn('Unexpected error: replaceCodeFileNode called without a Parent');
170
- return;
171
- }
172
- const index = parent.children.indexOf(node);
173
- if (index === -1) {
174
- console.warn('Unexpected error: replaceCodeFileNode could not find node index in Parent');
175
- return;
176
- }
177
- const attributes = node.attributes;
178
- const props = {};
179
- for (const { name, value } of attributes) {
180
- props[name] = value;
181
- }
182
- const result = await validateProps(props, appDir);
183
- if (!result.success) {
184
- const errors = result.error.issues.map(({ message, path }) => path[0] ? `${message}: ${path[0]}="${props[path[0]]}"` : message);
185
- const notification = await createErrorNotification(node, errors, mdxFile, appType);
186
- parent.children.splice(index, 1, ...notification);
187
- data.embeddedFiles.set('invalid input', {
188
- error: true,
189
- file: props.file,
190
- hash: '',
191
- line: node.position?.start.line ?? 1,
192
- });
193
- return;
194
- }
195
- const { file: { content, filePath, fullPath }, highlight, range, } = result.data;
196
- const language = path.extname(filePath).substring(1);
197
- const meta = [`filename=${filePath}`];
198
- // nonumbers nocopy ....
199
- Object.entries(result.data).forEach(([key, val]) => typeof val === 'boolean' && val && meta.push(`${key}=true`));
200
- if (result.data.buttons) {
201
- meta.push(`buttons=${result.data.buttons.join(',')}`);
202
- meta.push(`type=${appType}`);
203
- meta.push(`fullpath=${fullPath}`);
204
- // Avoid the headache of finding the separator on the client side
205
- // path.sep is always / on client side
206
- meta.push(`sep=${path.sep}`);
207
- }
208
- if (highlight?.length) {
209
- meta.push(`lines=${highlight}`);
210
- }
211
- const fileSections = range?.length ? range : [[1, content.length]];
212
- const rangesContent = [];
213
- const preNodes = [];
214
- for (const [start, end] of fileSections) {
215
- const rangeContent = stripIndent(content.slice(start ? start - 1 : 0, end).join('\n'));
216
- rangesContent.push(rangeContent);
217
- const mdxSource = `
218
- \`\`\`${language} ${meta.concat(`start=${start}`).join(' ')}
219
- ${rangeContent}
220
- \`\`\``;
221
- preNodes.push(...mdxToMdast(mdxSource));
222
- }
223
- /**
224
- * Show a warning above the file content if the range we show changed in the
225
- * embedded file and the range in <CodeFile range="a-b"> did not change.
226
- * The warning will be removed automatically after the <CodeFile> range changes
227
- * or by canceling it from the UI.
228
- */
229
- const embeddedKey = md5(fullPath + JSON.stringify(range));
230
- const contentHash = md5(rangesContent.join(','));
231
- const newData = {
232
- file: fullPath,
233
- hash: contentHash,
234
- };
235
- const cachedData = data.cachedEmbeddedFiles.get(embeddedKey);
236
- if (cachedData &&
237
- // If a warning existed previously and its hash matched the current hash,
238
- // then the changes were reverted and the warning will be remove
239
- ((cachedData.warning && cachedData.warning !== contentHash) ??
240
- (!cachedData.warning && cachedData.hash !== contentHash))) {
241
- // keep previously saved warning or previous hash
242
- newData.warning = cachedData.warning ?? cachedData.hash;
243
- const startLine = node.position?.start.line ?? 1;
244
- newData.line = startLine;
245
- const mdxFilename = path.basename(mdxFile);
246
- const filename = path.basename(filePath);
247
- const warning = `
248
- <CodeFileNotification variant="warning" file="${mdxFilename}" line="${startLine}" type="${appType}"
249
- cacheLocation="${data.cacheLocation}" embeddedKey="${embeddedKey}">
250
- <callout-warning class="notification">
251
- <div className="title">CodeFile Warning:</div>
252
- <div>file ${filename} content was changed, review 'range' and 'highlight' inputs</div>
253
- </callout-warning>
254
- </CodeFileNotification>`;
255
- preNodes.unshift(...mdxToMdast(warning));
256
- }
257
- data.embeddedFiles.set(embeddedKey, newData);
258
- // replace <CodeFile> with embedded file content
259
- parent.children.splice(index, 1, ...preNodes);
260
- }
261
- return async function codeFileTransformer(tree) {
262
- const codeFiles = [];
263
- const filter = { type: 'mdxJsxFlowElement', name: 'CodeFile' };
264
- visit(tree, filter, ((node, _index, parent) => {
265
- codeFiles.push({ node, parent });
266
- }));
267
- for (const props of codeFiles) {
268
- await replaceCodeFileNode(props);
269
- }
270
- // cleanup
271
- fileContentCache = new Map();
272
- };
273
- }
274
- //# sourceMappingURL=codefile-mdx.server.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"codefile-mdx.server.js","sourceRoot":"","sources":["../../src/codefile-mdx.server.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport { createProcessor } from '@mdx-js/mdx'\nimport md5 from 'md5-hex'\nimport { type Root as MdastRoot, type Parent, type RootContent } from 'mdast'\nimport {\n\ttype MdxJsxAttribute,\n\ttype MdxJsxFlowElement,\n} from 'mdast-util-mdx-jsx'\nimport { removePosition } from 'unist-util-remove-position'\nimport { visit, type Visitor } from 'unist-util-visit'\nimport * as z from 'zod'\n\ntype CodeFile = {\n\tnode: MdxJsxFlowElement\n\tparent: Parent | null | undefined\n}\n\ntype RangeArray = [number, number][] | undefined\n\ntype CodeFileProps = Record<string, unknown>\n\ntype PathContentMap = Map<string, string[]>\n\nconst APP_TYPES = ['problem', 'solution', 'playground'] as const\ntype AppTypes = typeof APP_TYPES\n\nexport type EmbeddedFile = {\n\terror?: boolean\n\tfile: string\n\thash: string\n\tline?: number\n\twarning?: string\n}\n\nexport type CodeFileData = {\n\tmdxFile: string\n\tcacheLocation: string\n\tcachedEmbeddedFiles: Map<string, EmbeddedFile>\n\tembeddedFiles: Map<string, EmbeddedFile>\n}\n\nconst safePath = (s: string) => s.replace(/\\\\/g, '/')\n\nconst REG_EXP = /^(?:\\d+(?:-\\d+)?,)*\\d+(?:-\\d+)?$/\n\nconst isValidRangeFormat = (value: string | undefined) =>\n\tvalue ? REG_EXP.test(value) : true\n\nconst transformRange = (value: string | undefined) =>\n\tvalue?.split(',').map((range) => {\n\t\tconst [start, end] = range.split('-').map(Number)\n\t\treturn [start, end ?? start] as [number, number]\n\t})\n\nconst isRangeBounded = (\n\trange: RangeArray,\n\tctx: z.RefinementCtx,\n\tlines: number,\n) => {\n\tif (!lines || !Array.isArray(range)) return\n\tif (range.flat().some((r) => r < 1 || r > lines)) {\n\t\tctx.addIssue({\n\t\t\tcode: z.ZodIssueCode.custom,\n\t\t\tmessage: `Range must be between 1 and ${lines}`,\n\t\t})\n\t}\n}\n\nconst isRangeInOrder = (range: RangeArray) =>\n\tArray.isArray(range)\n\t\t? range.every(([a, b]) => !isNaN(Number(a)) && !isNaN(Number(b)) && b >= a)\n\t\t: true\n\nconst isRangesNonOverlapping = (range: RangeArray) => {\n\tif (!Array.isArray(range)) return true\n\treturn range.every(([a], i) => i === 0 || (range[i - 1]?.[1] ?? 0) < a)\n}\n\nlet fileContentCache: PathContentMap = new Map()\nasync function getFileContent(filePath: string) {\n\tif (fileContentCache.has(filePath)) {\n\t\treturn fileContentCache.get(filePath)\n\t}\n\ttry {\n\t\tconst content = await fs.promises.readFile(filePath, 'utf-8')\n\t\tconst fileContent = content.split('\\n')\n\t\tfileContentCache.set(filePath, fileContent)\n\t\treturn fileContent\n\t} catch {\n\t\tconsole.warn(\n\t\t\t`@epic-web/workshop-app - invalid CodeFile.\\nCould not read file: ${filePath}\\n`,\n\t\t)\n\t}\n}\n\nasync function validateProps(props: CodeFileProps, appDir: string) {\n\tlet validRange: RangeArray\n\tlet linesCount = 0\n\n\tconst BooleanSchema = z\n\t\t.nullable(z.string())\n\t\t.optional()\n\t\t.refine(\n\t\t\t(v) => ['true', 'false', null, undefined].includes(v),\n\t\t\t'optional boolean key can be \"true\", \"false\", null or undefined',\n\t\t)\n\t\t.transform((v) => v === null || Boolean(v))\n\n\tconst RangeSchema = z\n\t\t.string()\n\t\t.optional()\n\t\t.refine(isValidRangeFormat, 'Invalid range format')\n\t\t.transform(transformRange)\n\t\t.superRefine((val, ctx) => isRangeBounded(val, ctx, linesCount))\n\t\t.refine(isRangeInOrder, 'Range must be in order low-high')\n\n\tconst inputSchema = z\n\t\t.object({\n\t\t\tfile: z\n\t\t\t\t.string()\n\t\t\t\t.nonempty()\n\t\t\t\t.transform(async (file, ctx) => {\n\t\t\t\t\tconst fullPath = path.join(appDir, file)\n\t\t\t\t\tconst content = await getFileContent(fullPath)\n\t\t\t\t\tif (!content) {\n\t\t\t\t\t\tctx.addIssue({\n\t\t\t\t\t\t\tcode: z.ZodIssueCode.custom,\n\t\t\t\t\t\t\tmessage: `Could not read file`,\n\t\t\t\t\t\t\tfatal: true,\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn z.NEVER\n\t\t\t\t\t}\n\t\t\t\t\tlinesCount = content.length\n\t\t\t\t\t// @mdx-js/mdx parser can NOT handle relative path with backslashes\n\t\t\t\t\treturn {\n\t\t\t\t\t\tfullPath: safePath(fullPath),\n\t\t\t\t\t\tfilePath: safePath(file),\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\trange: RangeSchema.refine((range) => {\n\t\t\t\tconst isValid = isRangesNonOverlapping(range)\n\t\t\t\t// we use this value in highlight refine\n\t\t\t\tvalidRange = isValid ? range : undefined\n\t\t\t\treturn isValid\n\t\t\t}, 'Ranges must not overlap'),\n\t\t\thighlight: RangeSchema.refine((highlight) => {\n\t\t\t\tif (!Array.isArray(highlight) || !Array.isArray(validRange)) {\n\t\t\t\t\treturn z.NEVER\n\t\t\t\t}\n\t\t\t\treturn highlight.every(([hStart, hEnd]) =>\n\t\t\t\t\tvalidRange?.some(\n\t\t\t\t\t\t([rStart, rEnd]) => hStart >= rStart && hEnd <= rEnd,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}, 'Highlight range must be within defined range')\n\t\t\t\t.transform(() => props.highlight as Array<string>)\n\t\t\t\t.optional(),\n\t\t\tnonumber: BooleanSchema,\n\t\t\tnocopy: BooleanSchema,\n\t\t\tbuttons: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.transform((str) =>\n\t\t\t\t\tstr ? (str.split(',') as unknown as AppTypes) : [],\n\t\t\t\t)\n\t\t\t\t.refine((arr) => arr.every((item) => APP_TYPES.includes(item)), {\n\t\t\t\t\tmessage: `Buttons can only be any of ${APP_TYPES.join(',')}`,\n\t\t\t\t}),\n\t\t})\n\t\t.strict()\n\n\treturn inputSchema.safeParseAsync(props)\n}\n\nasync function createErrorNotification(\n\tnode: MdxJsxFlowElement,\n\terrors: string[],\n\tmdxFile: string,\n\tappType: string,\n) {\n\tconst filename = path.basename(mdxFile)\n\tconst startLine = node.position?.start.line\n\tconst endLine = node.position?.end.line\n\n\tconst codeFence = async () => {\n\t\tif (startLine && endLine) {\n\t\t\tconst contentStr = await getFileContent(mdxFile)\n\t\t\tconst content = contentStr?.slice(startLine - 1, endLine).join('\\n')\n\t\t\tif (content) {\n\t\t\t\treturn `\n\\`\\`\\`tsx filename=${filename} start=${startLine} nocopy\n${content}\n\\`\\`\\``.trim()\n\t\t\t}\n\t\t}\n\t\treturn ''\n\t}\n\n\tconst mdxSource = `\n<CodeFileNotification variant=\"error\" file=\"${filename}\" line=\"${startLine}\" type=\"${appType}\">\n <callout-danger class=\"notification\">\n <div className=\"title\">CodeFile Error: invalid input</div>\n ${errors.map((error) => `<div>${error}</div>`).join('')}\n${await codeFence()}\n </callout-danger>\n</CodeFileNotification>`\n\n\treturn mdxToMdast(mdxSource)\n}\n\n// based on https://github.com/sindresorhus/strip-indent\nfunction stripIndent(string: string) {\n\tconst match = string.match(/^[ \\t]*(?=\\S)/gm)\n\tconst indent = match?.reduce((r, a) => Math.min(r, a.length), Infinity) ?? 0\n\tif (indent === 0) {\n\t\treturn string\n\t}\n\tconst regex = new RegExp(`^[ \\\\t]{${indent}}`, 'gm')\n\treturn string.replace(regex, '')\n}\n\nfunction mdxToMdast(mdx: string) {\n\tconst processor = createProcessor()\n\tconst mdast = processor.parse(mdx.trim()) as MdastRoot | RootContent\n\tremovePosition(mdast, { force: true })\n\treturn mdast.type === 'root' ? mdast.children : [mdast]\n}\n\nexport function remarkCodeFile(data: CodeFileData) {\n\tfileContentCache = new Map()\n\tconst mdxFile = data.mdxFile\n\tconst appDir = path.dirname(mdxFile)\n\tconst appType = mdxFile.includes('problem')\n\t\t? 'problem'\n\t\t: mdxFile.includes('solution')\n\t\t\t? 'solution'\n\t\t\t: 'other' // not in exercise\n\n\tasync function replaceCodeFileNode({\n\t\tnode,\n\t\tparent,\n\t}: CodeFile): Promise<void> {\n\t\tif (!parent) {\n\t\t\tconsole.warn(\n\t\t\t\t'Unexpected error: replaceCodeFileNode called without a Parent',\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tconst index = parent.children.indexOf(node)\n\t\tif (index === -1) {\n\t\t\tconsole.warn(\n\t\t\t\t'Unexpected error: replaceCodeFileNode could not find node index in Parent',\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tconst attributes = node.attributes as MdxJsxAttribute[]\n\t\tconst props: CodeFileProps = {}\n\t\tfor (const { name, value } of attributes) {\n\t\t\tprops[name] = value\n\t\t}\n\n\t\tconst result = await validateProps(props, appDir)\n\t\tif (!result.success) {\n\t\t\tconst errors = result.error.issues.map(({ message, path }) =>\n\t\t\t\tpath[0] ? `${message}: ${path[0]}=\"${props[path[0]]}\"` : message,\n\t\t\t)\n\t\t\tconst notification = await createErrorNotification(\n\t\t\t\tnode,\n\t\t\t\terrors,\n\t\t\t\tmdxFile,\n\t\t\t\tappType,\n\t\t\t)\n\t\t\tparent.children.splice(index, 1, ...notification)\n\n\t\t\tdata.embeddedFiles.set('invalid input', {\n\t\t\t\terror: true,\n\t\t\t\tfile: props.file as string,\n\t\t\t\thash: '',\n\t\t\t\tline: node.position?.start.line ?? 1,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tconst {\n\t\t\tfile: { content, filePath, fullPath },\n\t\t\thighlight,\n\t\t\trange,\n\t\t} = result.data\n\t\tconst language = path.extname(filePath).substring(1)\n\t\tconst meta = [`filename=${filePath}`]\n\n\t\t// nonumbers nocopy ....\n\t\tObject.entries(result.data).forEach(\n\t\t\t([key, val]) =>\n\t\t\t\ttypeof val === 'boolean' && val && meta.push(`${key}=true`),\n\t\t)\n\n\t\tif (result.data.buttons) {\n\t\t\tmeta.push(`buttons=${result.data.buttons.join(',')}`)\n\t\t\tmeta.push(`type=${appType}`)\n\t\t\tmeta.push(`fullpath=${fullPath}`)\n\t\t\t// Avoid the headache of finding the separator on the client side\n\t\t\t// path.sep is always / on client side\n\t\t\tmeta.push(`sep=${path.sep}`)\n\t\t}\n\n\t\tif (highlight?.length) {\n\t\t\tmeta.push(`lines=${highlight}`)\n\t\t}\n\n\t\tconst fileSections = range?.length ? range : [[1, content.length]]\n\t\tconst rangesContent = []\n\t\tconst preNodes = []\n\t\tfor (const [start, end] of fileSections) {\n\t\t\tconst rangeContent = stripIndent(\n\t\t\t\tcontent.slice(start ? start - 1 : 0, end).join('\\n'),\n\t\t\t)\n\t\t\trangesContent.push(rangeContent)\n\t\t\tconst mdxSource = `\n\\`\\`\\`${language} ${meta.concat(`start=${start}`).join(' ')}\n${rangeContent}\n\\`\\`\\``\n\t\t\tpreNodes.push(...mdxToMdast(mdxSource))\n\t\t}\n\n\t\t/**\n\t\t * Show a warning above the file content if the range we show changed in the\n\t\t * embedded file and the range in <CodeFile range=\"a-b\"> did not change.\n\t\t * The warning will be removed automatically after the <CodeFile> range changes\n\t\t * or by canceling it from the UI.\n\t\t */\n\t\tconst embeddedKey = md5(fullPath + JSON.stringify(range))\n\t\tconst contentHash = md5(rangesContent.join(','))\n\t\tconst newData: EmbeddedFile = {\n\t\t\tfile: fullPath,\n\t\t\thash: contentHash,\n\t\t}\n\t\tconst cachedData = data.cachedEmbeddedFiles.get(embeddedKey)\n\n\t\tif (\n\t\t\tcachedData &&\n\t\t\t// If a warning existed previously and its hash matched the current hash,\n\t\t\t// then the changes were reverted and the warning will be remove\n\t\t\t((cachedData.warning && cachedData.warning !== contentHash) ??\n\t\t\t\t(!cachedData.warning && cachedData.hash !== contentHash))\n\t\t) {\n\t\t\t// keep previously saved warning or previous hash\n\t\t\tnewData.warning = cachedData.warning ?? cachedData.hash\n\t\t\tconst startLine = node.position?.start.line ?? 1\n\t\t\tnewData.line = startLine\n\t\t\tconst mdxFilename = path.basename(mdxFile)\n\t\t\tconst filename = path.basename(filePath)\n\t\t\tconst warning = `\n<CodeFileNotification variant=\"warning\" file=\"${mdxFilename}\" line=\"${startLine}\" type=\"${appType}\"\ncacheLocation=\"${data.cacheLocation}\" embeddedKey=\"${embeddedKey}\">\n <callout-warning class=\"notification\">\n <div className=\"title\">CodeFile Warning:</div>\n <div>file ${filename} content was changed, review 'range' and 'highlight' inputs</div>\n </callout-warning>\n</CodeFileNotification>`\n\t\t\tpreNodes.unshift(...mdxToMdast(warning))\n\t\t}\n\t\tdata.embeddedFiles.set(embeddedKey, newData)\n\n\t\t// replace <CodeFile> with embedded file content\n\t\tparent.children.splice(index, 1, ...preNodes)\n\t}\n\n\treturn async function codeFileTransformer(tree: MdastRoot) {\n\t\tconst codeFiles: CodeFile[] = []\n\t\tconst filter = { type: 'mdxJsxFlowElement', name: 'CodeFile' } as const\n\t\tvisit<MdastRoot, typeof filter>(tree, filter, ((node, _index, parent) => {\n\t\t\tcodeFiles.push({ node, parent })\n\t\t}) as Visitor<MdxJsxFlowElement, Parent>)\n\n\t\tfor (const props of codeFiles) {\n\t\t\tawait replaceCodeFileNode(props)\n\t\t}\n\n\t\t// cleanup\n\t\tfileContentCache = new Map()\n\t}\n}\n"]}