@epic-web/workshop-utils 5.0.0 → 5.0.2

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 +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 { 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"]}
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\tforceFresh = stat.mtimeMs > existingCacheEntry.metadata.createdTime\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"]}
@@ -268,7 +268,6 @@ declare const WorkshopConfigSchema: z.ZodEffects<z.ZodObject<{
268
268
  } | undefined;
269
269
  }>;
270
270
  export type WorkshopConfig = z.infer<typeof WorkshopConfigSchema>;
271
- export declare function bustWorkshopConfigCache(): void;
272
271
  export declare function getWorkshopConfig(): WorkshopConfig;
273
272
  export declare function getStackBlitzUrl({ fullPath, title, type, }: {
274
273
  fullPath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.server.d.ts","sourceRoot":"","sources":["../../src/config.server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,YAAY,QAAoD,CAAA;AAE7E,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;EAWjC,CAAA;AAUF,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgEvB,CAAA;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAIjE,wBAAgB,uBAAuB,SAEtC;AAED,wBAAgB,iBAAiB,IAAI,cAAc,CA0DlD;AAED,wBAAsB,gBAAgB,CAAC,EACtC,QAAQ,EACR,KAAK,EACL,IAAI,GACJ,EAAE;IACF,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACZ,0BA6BA;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;GA0ElD"}
1
+ {"version":3,"file":"config.server.d.ts","sourceRoot":"","sources":["../../src/config.server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,YAAY,QAAoD,CAAA;AAI7E,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;EAWjC,CAAA;AAUF,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgEvB,CAAA;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAUjE,wBAAgB,iBAAiB,IAAI,cAAc,CAgElD;AAED,wBAAsB,gBAAgB,CAAC,EACtC,QAAQ,EACR,KAAK,EACL,IAAI,GACJ,EAAE;IACF,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACZ,0BA6BA;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;GA0ElD"}
@@ -2,6 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { z } from 'zod';
4
4
  export const workshopRoot = process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd();
5
+ const rootPkgJson = path.join(workshopRoot, 'package.json');
5
6
  export const StackBlitzConfigSchema = z.object({
6
7
  // we default this to `${exerciseTitle} (${type})`
7
8
  title: z.string().optional(),
@@ -79,13 +80,15 @@ const WorkshopConfigSchema = z
79
80
  },
80
81
  };
81
82
  });
82
- let cachedConfig = null;
83
- export function bustWorkshopConfigCache() {
84
- cachedConfig = null;
85
- }
83
+ const configCache = {
84
+ config: null,
85
+ modified: 0,
86
+ };
86
87
  export function getWorkshopConfig() {
87
- if (cachedConfig)
88
- return cachedConfig;
88
+ if (configCache.config &&
89
+ configCache.modified > fs.statSync(rootPkgJson).mtimeMs) {
90
+ return configCache.config;
91
+ }
89
92
  const packageJsonPath = path.join(workshopRoot, 'package.json');
90
93
  let packageJson;
91
94
  try {
@@ -116,7 +119,8 @@ export function getWorkshopConfig() {
116
119
  }
117
120
  try {
118
121
  const parsedConfig = WorkshopConfigSchema.parse(epicshopConfig);
119
- cachedConfig = parsedConfig;
122
+ configCache.config = parsedConfig;
123
+ configCache.modified = fs.statSync(rootPkgJson).mtimeMs;
120
124
  return parsedConfig;
121
125
  }
122
126
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"config.server.js","sourceRoot":"","sources":["../../src/config.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAA;AAE7E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,kDAAkD;IAClD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,gDAAgD;IAChD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,qEAAqE;IACrE,8CAA8C;IAC9C,IAAI,EAAE,CAAC;SACL,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;SACrE,QAAQ,EAAE;IACZ,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAA;AAEF,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC9B,CAAC,CAAA;AAEF,gDAAgD;AAChD,MAAM,oBAAoB,GAAG,CAAC;KAC5B,MAAM,CAAC;IACP,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,UAAU,EAAE,gBAAgB,CAAC,QAAQ,EAAE;IACvC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACvC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACvC,OAAO,EAAE,CAAC;SACR,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,iBAAiB,CAAC;QAC3C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC;QAC9C,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC;QAChD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC;QACrC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC3B,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC;QAC3D,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;KAC3C,CAAC;SACD,OAAO,CAAC,EAAE,CAAC;IACb,eAAe,EAAE,CAAC;SAChB,MAAM,EAAE;SACR,OAAO,CACP,qEAAqE,CACrE;IACF,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,gBAAgB,EAAE,sBAAsB,CAAC,QAAQ,EAAE;IACnD,KAAK,EAAE,CAAC;SACN,MAAM,CAAC;QACP,QAAQ,EAAE,CAAC;aACT,MAAM,EAAE;aACR,OAAO,CACP,0JAA0J,CAC1J;QACF,QAAQ,EAAE,CAAC;aACT,MAAM,EAAE;aACR,OAAO,CACP,0LAA0L,CAC1L;KACF,CAAC;SACD,OAAO,CAAC,EAAE,CAAC;IACb,OAAO,EAAE,CAAC;SACR,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;KAClC,CAAC;SACD,OAAO,CAAC,EAAE,CAAC;IACb,OAAO,EAAE,CAAC;SACR,MAAM,CAAC;QACP,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KACjC,CAAC;SACD,QAAQ,EAAE;IACZ,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;CAChD,CAAC;KACD,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE;IACnB,OAAO;QACN,GAAG,IAAI;QACP,OAAO,EAAE;YACR,GAAG,IAAI,CAAC,OAAO;YACf,gBAAgB,EACf,IAAI,CAAC,OAAO,CAAC,gBAAgB,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW;YAC1D,8BAA8B;YAC9B,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,gBAAgB;YAChD,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,gBAAgB;SAChD;KACD,CAAA;AACF,CAAC,CAAC,CAAA;AAIH,IAAI,YAAY,GAA0B,IAAI,CAAA;AAE9C,MAAM,UAAU,uBAAuB;IACtC,YAAY,GAAG,IAAI,CAAA;AACpB,CAAC;AAED,MAAM,UAAU,iBAAiB;IAChC,IAAI,YAAY;QAAE,OAAO,YAAY,CAAA;IAErC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,CAAA;IAC/D,IAAI,WAAgB,CAAA;IAEpB,IAAI,CAAC;QACJ,MAAM,kBAAkB,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAA;QACnE,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAA;IAC7C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,KAAK,CAAC,CAAA;QAC9D,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CACd,6BAA6B,eAAe,wEAAwE,CACpH,CAAA;QACF,CAAC;aAAM,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CACd,mCAAmC,eAAe,4CAA4C,CAC9F,CAAA;QACF,CAAC;QACD,MAAM,IAAI,KAAK,CACd,4CAA4C,eAAe,EAAE,CAC7D,CAAA;IACF,CAAC;IAED,MAAM,cAAc,GAAG,WAAW,CAAC,QAAQ,IAAI,EAAE,CAAA;IAEjD,+CAA+C;IAC/C,IAAI,cAAc,CAAC,UAAU,EAAE,CAAC;QAC/B,cAAc,CAAC,UAAU,GAAG,GAAG,cAAc,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAA;IACxF,CAAC;SAAM,IAAI,cAAc,CAAC,UAAU,EAAE,CAAC;QACtC,cAAc,CAAC,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,OAAO,CAC5D,oBAAoB,EACpB,EAAE,CACF,CAAA;QACD,cAAc,CAAC,UAAU,GAAG,GAAG,cAAc,CAAC,UAAU,YAAY,CAAA;IACrE,CAAC;SAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACd,2EAA2E,CAC3E,CAAA;IACF,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,YAAY,GAAG,oBAAoB,CAAC,KAAK,CAAC,cAAc,CAAC,CAAA;QAC/D,YAAY,GAAG,YAAY,CAAA;QAC3B,OAAO,YAAY,CAAA;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,KAAK,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;YACjC,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,EAAE,CAAA;YACvC,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,WAAW,CAAC;iBAC/D,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,KAAK,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;iBAC3D,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,CAAA;YACpC,MAAM,IAAI,KAAK,CACd,qCAAqC,eAAe,MAAM,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACpF,CAAA;QACF,CAAC;QACD,MAAM,KAAK,CAAA;IACZ,CAAC;AACF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EACtC,QAAQ,EACR,KAAK,EACL,IAAI,GAKJ;IACA,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAA;IAC1C,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAA;IAE9C,IAAI,SAAS,CAAC,gBAAgB,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAEpD,IAAI,mBAAmB,GAAG,cAAc,CAAC,UAAU,CAAA;IAEnD,MAAM,aAAa,GAAG,IAAI,GAAG,CAC5B,mBAAmB,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CACjD,CAAA;IAED,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,CAAA;IAEzC,MAAM,gBAAgB,GAAG;QACxB,GAAG,SAAS,CAAC,gBAAgB;QAC7B,KAAK,EAAE,SAAS,CAAC,gBAAgB,EAAE,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG;KAChE,CAAA;IAED,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,gBAA0C,CAAC,CAAA;IAE9E,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,CAAA;IAEvE,MAAM,aAAa,GAAG,IAAI,GAAG,CAC5B,UAAU,UAAU,IAAI,YAAY,IAAI,MAAM,EAAE,EAChD,wBAAwB,CACxB,CAAA;IAED,OAAO,aAAa,CAAC,QAAQ,EAAE,CAAA;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IAClD,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAA;IAE1C,IAAI,cAAc,GAAwB,EAAE,CAAA;IAC5C,IAAI,OAAO,GAA2B,EAAE,CAAA;IAExC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAA;IAC3D,MAAM,iBAAiB,GAAG,MAAM,EAAE,CAAC,QAAQ;SACzC,MAAM,CAAC,eAAe,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC;SAC1C,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;SAChB,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAA;IAEpB,IAAI,iBAAiB,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACrB,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAChE,CAAA;QACR,cAAc,GAAG,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAA;QACnC,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,EAAE,CAAA;IAC5B,CAAC;IAED,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;QAChC,gBAAgB,EAAE,sBAAsB,CAAC,QAAQ,EAAE;aACjD,QAAQ,EAAE;aACV,SAAS,CAAC,CAAC,mBAAmB,EAAE,EAAE;YAClC,IAAI,mBAAmB,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAA;YAE7C,OAAO;gBACN,GAAG,cAAc,CAAC,gBAAgB;gBAClC,GAAG,mBAAmB;aACtB,CAAA;QACF,CAAC,CAAC;QACH,OAAO,EAAE,CAAC;aACR,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;iBACR,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,OAAO,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC;SAClD,CAAC;aACD,OAAO,CAAC,EAAE,CAAC;QACb,OAAO,EAAE,CAAC;aACR,MAAM,CAAC;YACP,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC3B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SAC1B,CAAC;aACD,OAAO,CAAC,EAAE,CAAC;QACb,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,YAAY,CAAC;KACxE,CAAC,CAAA;IAEF,MAAM,SAAS,GAAG;QACjB,gBAAgB,EAAE,cAAc,CAAC,gBAAgB;QACjD,OAAO,EAAE;YACR,OAAO,EAAE,cAAc,CAAC,OAAO,EAAE,OAAO;SACxC;QACD,OAAO,EAAE;YACR,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,GAAG,EAAE,OAAO,CAAC,GAAG;SAChB;QACD,YAAY,EAAE,cAAc,CAAC,YAAY;KACzC,CAAA;IAED,IAAI,CAAC;QACJ,OAAO,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,KAAK,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;YACjC,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,EAAE,CAAA;YACvC,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,WAAW,CAAC;iBAC/D,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,KAAK,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;iBAC3D,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,CAAA;YACpC,MAAM,IAAI,KAAK,CACd,iCAAiC,QAAQ,MAAM,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzE,CAAA;QACF,CAAC;QACD,MAAM,KAAK,CAAA;IACZ,CAAC;AACF,CAAC","sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport { z } from 'zod'\n\nexport const workshopRoot = process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd()\n\nexport const StackBlitzConfigSchema = z.object({\n\t// we default this to `${exerciseTitle} (${type})`\n\ttitle: z.string().optional(),\n\t// stackblitz defaults this to dev automatically\n\tstartScript: z.string().optional(),\n\t// if no value is provided, then stackblitz defaults this to whatever\n\t// looks best based on the width of the screen\n\tview: z\n\t\t.union([z.literal('editor'), z.literal('preview'), z.literal('both')])\n\t\t.optional(),\n\tfile: z.string().optional(),\n})\n\nconst InstructorSchema = z.object({\n\tname: z.string().optional(),\n\tavatar: z.string().optional(),\n\t𝕏: z.string().optional(),\n\txHandle: z.string().optional(),\n})\n\n// most defaults are for backwards compatibility\nconst WorkshopConfigSchema = z\n\t.object({\n\t\ttitle: z.string(),\n\t\tsubtitle: z.string().optional(),\n\t\tinstructor: InstructorSchema.optional(),\n\t\tepicWorkshopHost: z.string().optional(),\n\t\tepicWorkshopSlug: z.string().optional(),\n\t\tproduct: z\n\t\t\t.object({\n\t\t\t\thost: z.string().default('www.epicweb.dev'),\n\t\t\t\tdisplayName: z.string().default('EpicWeb.dev'),\n\t\t\t\tdisplayNameShort: z.string().default('Epic Web'),\n\t\t\t\tlogo: z.string().default('/logo.svg'),\n\t\t\t\tslug: z.string().optional(),\n\t\t\t\tdiscordChannelId: z.string().default('1161045224907341972'),\n\t\t\t\tdiscordTags: z.array(z.string()).optional(),\n\t\t\t})\n\t\t\t.default({}),\n\t\tonboardingVideo: z\n\t\t\t.string()\n\t\t\t.default(\n\t\t\t\t'https://www.epicweb.dev/tips/get-started-with-the-epic-workshop-app',\n\t\t\t),\n\t\tgithubRepo: z.string(),\n\t\tgithubRoot: z.string(),\n\t\tstackBlitzConfig: StackBlitzConfigSchema.optional(),\n\t\tforms: z\n\t\t\t.object({\n\t\t\t\tworkshop: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.default(\n\t\t\t\t\t\t'https://docs.google.com/forms/d/e/1FAIpQLSdRmj9p8-5zyoqRzxp3UpqSbC3aFkweXvvJIKes0a5s894gzg/viewform?hl=en&embedded=true&entry.2123647600={workshopTitle}',\n\t\t\t\t\t),\n\t\t\t\texercise: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.default(\n\t\t\t\t\t\t'https://docs.google.com/forms/d/e/1FAIpQLSf3o9xyjQepTlOTH5Z7ZwkeSTdXh6YWI_RGc9KiyD3oUN0p6w/viewform?hl=en&embedded=true&entry.1836176234={workshopTitle}&entry.428900931={exerciseTitle}',\n\t\t\t\t\t),\n\t\t\t})\n\t\t\t.default({}),\n\t\ttestTab: z\n\t\t\t.object({\n\t\t\t\tenabled: z.boolean().default(true),\n\t\t\t})\n\t\t\t.default({}),\n\t\tscripts: z\n\t\t\t.object({\n\t\t\t\tpostupdate: z.string().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t\tinitialRoute: z.string().optional().default('/'),\n\t})\n\t.transform((data) => {\n\t\treturn {\n\t\t\t...data,\n\t\t\tproduct: {\n\t\t\t\t...data.product,\n\t\t\t\tdisplayNameShort:\n\t\t\t\t\tdata.product.displayNameShort ?? data.product.displayName,\n\t\t\t\t// for backwards compatibility\n\t\t\t\thost: data.product.host ?? data.epicWorkshopHost,\n\t\t\t\tslug: data.product.slug ?? data.epicWorkshopSlug,\n\t\t\t},\n\t\t}\n\t})\n\nexport type WorkshopConfig = z.infer<typeof WorkshopConfigSchema>\n\nlet cachedConfig: WorkshopConfig | null = null\n\nexport function bustWorkshopConfigCache() {\n\tcachedConfig = null\n}\n\nexport function getWorkshopConfig(): WorkshopConfig {\n\tif (cachedConfig) return cachedConfig\n\n\tconst packageJsonPath = path.join(workshopRoot, 'package.json')\n\tlet packageJson: any\n\n\ttry {\n\t\tconst packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8')\n\t\tpackageJson = JSON.parse(packageJsonContent)\n\t} catch (error) {\n\t\tconsole.error(`Error reading or parsing package.json:`, error)\n\t\tif (error instanceof Error && error.message.includes('ENOENT')) {\n\t\t\tthrow new Error(\n\t\t\t\t`package.json not found at ${packageJsonPath}. Please ensure you're running the command from the correct directory.`,\n\t\t\t)\n\t\t} else if (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid JSON in package.json at ${packageJsonPath}. Please check the file for syntax errors.`,\n\t\t\t)\n\t\t}\n\t\tthrow new Error(\n\t\t\t`Could not find and parse package.json at ${packageJsonPath}`,\n\t\t)\n\t}\n\n\tconst epicshopConfig = packageJson.epicshop || {}\n\n\t// Set githubRepo and githubRoot before parsing\n\tif (epicshopConfig.githubRepo) {\n\t\tepicshopConfig.githubRoot = `${epicshopConfig.githubRepo.replace(/\\/$/, '')}/tree/main`\n\t} else if (epicshopConfig.githubRoot) {\n\t\tepicshopConfig.githubRepo = epicshopConfig.githubRoot.replace(\n\t\t\t/\\/(blob|tree)\\/.*$/,\n\t\t\t'',\n\t\t)\n\t\tepicshopConfig.githubRoot = `${epicshopConfig.githubRepo}/tree/main`\n\t} else {\n\t\tthrow new Error(\n\t\t\t'Either githubRepo or githubRoot is required in the epicshop configuration',\n\t\t)\n\t}\n\n\ttry {\n\t\tconst parsedConfig = WorkshopConfigSchema.parse(epicshopConfig)\n\t\tcachedConfig = parsedConfig\n\t\treturn parsedConfig\n\t} catch (error) {\n\t\tif (error instanceof z.ZodError) {\n\t\t\tconst flattenedErrors = error.flatten()\n\t\t\tconst errorMessages = Object.entries(flattenedErrors.fieldErrors)\n\t\t\t\t.map(([field, errors]) => `${field}: ${errors?.join(', ')}`)\n\t\t\t\t.concat(flattenedErrors.formErrors)\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid epicshop configuration in ${packageJsonPath}:\\n${errorMessages.join('\\n')}`,\n\t\t\t)\n\t\t}\n\t\tthrow error\n\t}\n}\n\nexport async function getStackBlitzUrl({\n\tfullPath,\n\ttitle,\n\ttype,\n}: {\n\tfullPath: string\n\ttitle: string\n\ttype: string\n}) {\n\tconst workshopConfig = getWorkshopConfig()\n\tconst appConfig = await getAppConfig(fullPath)\n\n\tif (appConfig.stackBlitzConfig === null) return null\n\n\tlet githubRootUrlString = workshopConfig.githubRoot\n\n\tconst githubRootUrl = new URL(\n\t\tgithubRootUrlString.replace(/\\/blob\\//, '/tree/'),\n\t)\n\n\tconst githubPart = githubRootUrl.pathname\n\n\tconst stackBlitzConfig = {\n\t\t...appConfig.stackBlitzConfig,\n\t\ttitle: appConfig.stackBlitzConfig?.title ?? `${title} (${type})`,\n\t}\n\n\tconst params = new URLSearchParams(stackBlitzConfig as Record<string, string>)\n\n\tconst relativePath = fullPath.replace(`${workshopRoot}${path.sep}`, '')\n\n\tconst stackBlitzUrl = new URL(\n\t\t`/github${githubPart}/${relativePath}?${params}`,\n\t\t'https://stackblitz.com',\n\t)\n\n\treturn stackBlitzUrl.toString()\n}\n\nexport async function getAppConfig(fullPath: string) {\n\tconst workshopConfig = getWorkshopConfig()\n\n\tlet epicshopConfig: Record<string, any> = {}\n\tlet scripts: Record<string, string> = {}\n\n\tconst packageJsonPath = path.join(fullPath, 'package.json')\n\tconst packageJsonExists = await fs.promises\n\t\t.access(packageJsonPath, fs.constants.F_OK)\n\t\t.then(() => true)\n\t\t.catch(() => false)\n\n\tif (packageJsonExists) {\n\t\tconst pkg = JSON.parse(\n\t\t\tawait fs.promises.readFile(path.join(fullPath, 'package.json'), 'utf8'),\n\t\t) as any\n\t\tepicshopConfig = pkg.epicshop ?? {}\n\t\tscripts = pkg.scripts ?? {}\n\t}\n\n\tconst AppConfigSchema = z.object({\n\t\tstackBlitzConfig: StackBlitzConfigSchema.nullable()\n\t\t\t.optional()\n\t\t\t.transform((appStackBlitzConfig) => {\n\t\t\t\tif (appStackBlitzConfig === null) return null\n\n\t\t\t\treturn {\n\t\t\t\t\t...workshopConfig.stackBlitzConfig,\n\t\t\t\t\t...appStackBlitzConfig,\n\t\t\t\t}\n\t\t\t}),\n\t\ttestTab: z\n\t\t\t.object({\n\t\t\t\tenabled: z\n\t\t\t\t\t.boolean()\n\t\t\t\t\t.optional()\n\t\t\t\t\t.default(workshopConfig.testTab?.enabled ?? true),\n\t\t\t})\n\t\t\t.default({}),\n\t\tscripts: z\n\t\t\t.object({\n\t\t\t\ttest: z.string().optional(),\n\t\t\t\tdev: z.string().optional(),\n\t\t\t})\n\t\t\t.default({}),\n\t\tinitialRoute: z.string().optional().default(workshopConfig.initialRoute),\n\t})\n\n\tconst appConfig = {\n\t\tstackBlitzConfig: epicshopConfig.stackBlitzConfig,\n\t\ttestTab: {\n\t\t\tenabled: epicshopConfig.testTab?.enabled,\n\t\t},\n\t\tscripts: {\n\t\t\ttest: scripts.test,\n\t\t\tdev: scripts.dev,\n\t\t},\n\t\tinitialRoute: epicshopConfig.initialRoute,\n\t}\n\n\ttry {\n\t\treturn AppConfigSchema.parse(appConfig)\n\t} catch (error) {\n\t\tif (error instanceof z.ZodError) {\n\t\t\tconst flattenedErrors = error.flatten()\n\t\t\tconst errorMessages = Object.entries(flattenedErrors.fieldErrors)\n\t\t\t\t.map(([field, errors]) => `${field}: ${errors?.join(', ')}`)\n\t\t\t\t.concat(flattenedErrors.formErrors)\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid app configuration for ${fullPath}:\\n${errorMessages.join('\\n')}`,\n\t\t\t)\n\t\t}\n\t\tthrow error\n\t}\n}\n"]}
1
+ {"version":3,"file":"config.server.js","sourceRoot":"","sources":["../../src/config.server.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport { z } from 'zod'\n\nexport const workshopRoot = process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd()\n\nconst rootPkgJson = path.join(workshopRoot, 'package.json')\n\nexport const StackBlitzConfigSchema = z.object({\n\t// we default this to `${exerciseTitle} (${type})`\n\ttitle: z.string().optional(),\n\t// stackblitz defaults this to dev automatically\n\tstartScript: z.string().optional(),\n\t// if no value is provided, then stackblitz defaults this to whatever\n\t// looks best based on the width of the screen\n\tview: z\n\t\t.union([z.literal('editor'), z.literal('preview'), z.literal('both')])\n\t\t.optional(),\n\tfile: z.string().optional(),\n})\n\nconst InstructorSchema = z.object({\n\tname: z.string().optional(),\n\tavatar: z.string().optional(),\n\t𝕏: z.string().optional(),\n\txHandle: z.string().optional(),\n})\n\n// most defaults are for backwards compatibility\nconst WorkshopConfigSchema = z\n\t.object({\n\t\ttitle: z.string(),\n\t\tsubtitle: z.string().optional(),\n\t\tinstructor: InstructorSchema.optional(),\n\t\tepicWorkshopHost: z.string().optional(),\n\t\tepicWorkshopSlug: z.string().optional(),\n\t\tproduct: z\n\t\t\t.object({\n\t\t\t\thost: z.string().default('www.epicweb.dev'),\n\t\t\t\tdisplayName: z.string().default('EpicWeb.dev'),\n\t\t\t\tdisplayNameShort: z.string().default('Epic Web'),\n\t\t\t\tlogo: z.string().default('/logo.svg'),\n\t\t\t\tslug: z.string().optional(),\n\t\t\t\tdiscordChannelId: z.string().default('1161045224907341972'),\n\t\t\t\tdiscordTags: z.array(z.string()).optional(),\n\t\t\t})\n\t\t\t.default({}),\n\t\tonboardingVideo: z\n\t\t\t.string()\n\t\t\t.default(\n\t\t\t\t'https://www.epicweb.dev/tips/get-started-with-the-epic-workshop-app',\n\t\t\t),\n\t\tgithubRepo: z.string(),\n\t\tgithubRoot: z.string(),\n\t\tstackBlitzConfig: StackBlitzConfigSchema.optional(),\n\t\tforms: z\n\t\t\t.object({\n\t\t\t\tworkshop: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.default(\n\t\t\t\t\t\t'https://docs.google.com/forms/d/e/1FAIpQLSdRmj9p8-5zyoqRzxp3UpqSbC3aFkweXvvJIKes0a5s894gzg/viewform?hl=en&embedded=true&entry.2123647600={workshopTitle}',\n\t\t\t\t\t),\n\t\t\t\texercise: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.default(\n\t\t\t\t\t\t'https://docs.google.com/forms/d/e/1FAIpQLSf3o9xyjQepTlOTH5Z7ZwkeSTdXh6YWI_RGc9KiyD3oUN0p6w/viewform?hl=en&embedded=true&entry.1836176234={workshopTitle}&entry.428900931={exerciseTitle}',\n\t\t\t\t\t),\n\t\t\t})\n\t\t\t.default({}),\n\t\ttestTab: z\n\t\t\t.object({\n\t\t\t\tenabled: z.boolean().default(true),\n\t\t\t})\n\t\t\t.default({}),\n\t\tscripts: z\n\t\t\t.object({\n\t\t\t\tpostupdate: z.string().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t\tinitialRoute: z.string().optional().default('/'),\n\t})\n\t.transform((data) => {\n\t\treturn {\n\t\t\t...data,\n\t\t\tproduct: {\n\t\t\t\t...data.product,\n\t\t\t\tdisplayNameShort:\n\t\t\t\t\tdata.product.displayNameShort ?? data.product.displayName,\n\t\t\t\t// for backwards compatibility\n\t\t\t\thost: data.product.host ?? data.epicWorkshopHost,\n\t\t\t\tslug: data.product.slug ?? data.epicWorkshopSlug,\n\t\t\t},\n\t\t}\n\t})\n\nexport type WorkshopConfig = z.infer<typeof WorkshopConfigSchema>\n\nconst configCache: {\n\tconfig: WorkshopConfig | null\n\tmodified: number\n} = {\n\tconfig: null,\n\tmodified: 0,\n}\n\nexport function getWorkshopConfig(): WorkshopConfig {\n\tif (\n\t\tconfigCache.config &&\n\t\tconfigCache.modified > fs.statSync(rootPkgJson).mtimeMs\n\t) {\n\t\treturn configCache.config\n\t}\n\n\tconst packageJsonPath = path.join(workshopRoot, 'package.json')\n\tlet packageJson: any\n\n\ttry {\n\t\tconst packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8')\n\t\tpackageJson = JSON.parse(packageJsonContent)\n\t} catch (error) {\n\t\tconsole.error(`Error reading or parsing package.json:`, error)\n\t\tif (error instanceof Error && error.message.includes('ENOENT')) {\n\t\t\tthrow new Error(\n\t\t\t\t`package.json not found at ${packageJsonPath}. Please ensure you're running the command from the correct directory.`,\n\t\t\t)\n\t\t} else if (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid JSON in package.json at ${packageJsonPath}. Please check the file for syntax errors.`,\n\t\t\t)\n\t\t}\n\t\tthrow new Error(\n\t\t\t`Could not find and parse package.json at ${packageJsonPath}`,\n\t\t)\n\t}\n\n\tconst epicshopConfig = packageJson.epicshop || {}\n\n\t// Set githubRepo and githubRoot before parsing\n\tif (epicshopConfig.githubRepo) {\n\t\tepicshopConfig.githubRoot = `${epicshopConfig.githubRepo.replace(/\\/$/, '')}/tree/main`\n\t} else if (epicshopConfig.githubRoot) {\n\t\tepicshopConfig.githubRepo = epicshopConfig.githubRoot.replace(\n\t\t\t/\\/(blob|tree)\\/.*$/,\n\t\t\t'',\n\t\t)\n\t\tepicshopConfig.githubRoot = `${epicshopConfig.githubRepo}/tree/main`\n\t} else {\n\t\tthrow new Error(\n\t\t\t'Either githubRepo or githubRoot is required in the epicshop configuration',\n\t\t)\n\t}\n\n\ttry {\n\t\tconst parsedConfig = WorkshopConfigSchema.parse(epicshopConfig)\n\t\tconfigCache.config = parsedConfig\n\t\tconfigCache.modified = fs.statSync(rootPkgJson).mtimeMs\n\t\treturn parsedConfig\n\t} catch (error) {\n\t\tif (error instanceof z.ZodError) {\n\t\t\tconst flattenedErrors = error.flatten()\n\t\t\tconst errorMessages = Object.entries(flattenedErrors.fieldErrors)\n\t\t\t\t.map(([field, errors]) => `${field}: ${errors?.join(', ')}`)\n\t\t\t\t.concat(flattenedErrors.formErrors)\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid epicshop configuration in ${packageJsonPath}:\\n${errorMessages.join('\\n')}`,\n\t\t\t)\n\t\t}\n\t\tthrow error\n\t}\n}\n\nexport async function getStackBlitzUrl({\n\tfullPath,\n\ttitle,\n\ttype,\n}: {\n\tfullPath: string\n\ttitle: string\n\ttype: string\n}) {\n\tconst workshopConfig = getWorkshopConfig()\n\tconst appConfig = await getAppConfig(fullPath)\n\n\tif (appConfig.stackBlitzConfig === null) return null\n\n\tlet githubRootUrlString = workshopConfig.githubRoot\n\n\tconst githubRootUrl = new URL(\n\t\tgithubRootUrlString.replace(/\\/blob\\//, '/tree/'),\n\t)\n\n\tconst githubPart = githubRootUrl.pathname\n\n\tconst stackBlitzConfig = {\n\t\t...appConfig.stackBlitzConfig,\n\t\ttitle: appConfig.stackBlitzConfig?.title ?? `${title} (${type})`,\n\t}\n\n\tconst params = new URLSearchParams(stackBlitzConfig as Record<string, string>)\n\n\tconst relativePath = fullPath.replace(`${workshopRoot}${path.sep}`, '')\n\n\tconst stackBlitzUrl = new URL(\n\t\t`/github${githubPart}/${relativePath}?${params}`,\n\t\t'https://stackblitz.com',\n\t)\n\n\treturn stackBlitzUrl.toString()\n}\n\nexport async function getAppConfig(fullPath: string) {\n\tconst workshopConfig = getWorkshopConfig()\n\n\tlet epicshopConfig: Record<string, any> = {}\n\tlet scripts: Record<string, string> = {}\n\n\tconst packageJsonPath = path.join(fullPath, 'package.json')\n\tconst packageJsonExists = await fs.promises\n\t\t.access(packageJsonPath, fs.constants.F_OK)\n\t\t.then(() => true)\n\t\t.catch(() => false)\n\n\tif (packageJsonExists) {\n\t\tconst pkg = JSON.parse(\n\t\t\tawait fs.promises.readFile(path.join(fullPath, 'package.json'), 'utf8'),\n\t\t) as any\n\t\tepicshopConfig = pkg.epicshop ?? {}\n\t\tscripts = pkg.scripts ?? {}\n\t}\n\n\tconst AppConfigSchema = z.object({\n\t\tstackBlitzConfig: StackBlitzConfigSchema.nullable()\n\t\t\t.optional()\n\t\t\t.transform((appStackBlitzConfig) => {\n\t\t\t\tif (appStackBlitzConfig === null) return null\n\n\t\t\t\treturn {\n\t\t\t\t\t...workshopConfig.stackBlitzConfig,\n\t\t\t\t\t...appStackBlitzConfig,\n\t\t\t\t}\n\t\t\t}),\n\t\ttestTab: z\n\t\t\t.object({\n\t\t\t\tenabled: z\n\t\t\t\t\t.boolean()\n\t\t\t\t\t.optional()\n\t\t\t\t\t.default(workshopConfig.testTab?.enabled ?? true),\n\t\t\t})\n\t\t\t.default({}),\n\t\tscripts: z\n\t\t\t.object({\n\t\t\t\ttest: z.string().optional(),\n\t\t\t\tdev: z.string().optional(),\n\t\t\t})\n\t\t\t.default({}),\n\t\tinitialRoute: z.string().optional().default(workshopConfig.initialRoute),\n\t})\n\n\tconst appConfig = {\n\t\tstackBlitzConfig: epicshopConfig.stackBlitzConfig,\n\t\ttestTab: {\n\t\t\tenabled: epicshopConfig.testTab?.enabled,\n\t\t},\n\t\tscripts: {\n\t\t\ttest: scripts.test,\n\t\t\tdev: scripts.dev,\n\t\t},\n\t\tinitialRoute: epicshopConfig.initialRoute,\n\t}\n\n\ttry {\n\t\treturn AppConfigSchema.parse(appConfig)\n\t} catch (error) {\n\t\tif (error instanceof z.ZodError) {\n\t\t\tconst flattenedErrors = error.flatten()\n\t\t\tconst errorMessages = Object.entries(flattenedErrors.fieldErrors)\n\t\t\t\t.map(([field, errors]) => `${field}: ${errors?.join(', ')}`)\n\t\t\t\t.concat(flattenedErrors.formErrors)\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid app configuration for ${fullPath}:\\n${errorMessages.join('\\n')}`,\n\t\t\t)\n\t\t}\n\t\tthrow error\n\t}\n}\n"]}
@@ -0,0 +1,7 @@
1
+ declare function getDirModifiedTime(dir: string, { forceFresh }?: {
2
+ forceFresh?: boolean;
3
+ }): Promise<number>;
4
+ export declare function modifiedMoreRecentlyThan(time: number, ...dirs: Array<string>): Promise<boolean>;
5
+ declare function queuedGetDirModifiedTime(...args: Parameters<typeof getDirModifiedTime>): Promise<number>;
6
+ export { queuedGetDirModifiedTime as getDirModifiedTime };
7
+ //# sourceMappingURL=modified-time.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modified-time.server.d.ts","sourceRoot":"","sources":["../../src/modified-time.server.ts"],"names":[],"mappings":"AAMA,iBAAe,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,EAAE,UAAkB,EAAE,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GACnD,OAAO,CAAC,MAAM,CAAC,CASjB;AA2CD,wBAAsB,wBAAwB,CAC7C,IAAI,EAAE,MAAM,EACZ,GAAG,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,oBAStB;AAgBD,iBAAe,wBAAwB,CACtC,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,mBAK9C;AAED,OAAO,EAAE,wBAAwB,IAAI,kBAAkB,EAAE,CAAA"}
@@ -0,0 +1,80 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { isGitIgnored } from 'globby';
4
+ import PQueue from 'p-queue';
5
+ import { cachified, dirModifiedTimeCache } from './cache.server.js';
6
+ async function getDirModifiedTime(dir, { forceFresh = false } = {}) {
7
+ const result = await cachified({
8
+ key: dir,
9
+ cache: dirModifiedTimeCache,
10
+ ttl: 200,
11
+ forceFresh,
12
+ getFreshValue: () => getDirModifiedTimeImpl(dir),
13
+ });
14
+ return result;
15
+ }
16
+ async function getDirModifiedTimeImpl(dir) {
17
+ const isIgnored = await isGitIgnored({ cwd: dir });
18
+ const files = await fs.promises
19
+ .readdir(dir, { withFileTypes: true })
20
+ .catch(() => []);
21
+ const modifiedTimes = [];
22
+ for (const file of files) {
23
+ // Skip ignored files
24
+ if (isIgnored(file.name))
25
+ continue;
26
+ const filePath = path.join(dir, file.name);
27
+ if (file.isDirectory()) {
28
+ modifiedTimes.push(await getDirModifiedTime(filePath));
29
+ }
30
+ else {
31
+ try {
32
+ const { mtimeMs } = await fs.promises.stat(filePath);
33
+ modifiedTimes.push(mtimeMs);
34
+ }
35
+ catch {
36
+ // ignore errors (e.g., file access permissions, file has been moved or deleted)
37
+ }
38
+ }
39
+ }
40
+ try {
41
+ const { mtimeMs } = await fs.promises.stat(dir);
42
+ modifiedTimes.push(mtimeMs);
43
+ }
44
+ catch {
45
+ // ignore errors (e.g., file access permissions, file has been moved or deleted)
46
+ }
47
+ return Math.max(-1, ...modifiedTimes);
48
+ }
49
+ // this will return true as soon as one of the directories has been found to
50
+ // have been modified more recently than the given time
51
+ // TODO: this could be improved by not waiting for entire directories to be
52
+ // scanned and instead stopping the scan as soon as we find a file that was
53
+ // modified more recently than the given time
54
+ export async function modifiedMoreRecentlyThan(time, ...dirs) {
55
+ const modifiedTimePromises = dirs.map((dir) => getDirModifiedTime(dir));
56
+ const allFinishedPromise = Promise.all(modifiedTimePromises);
57
+ const firstMoreRecentPromise = modifiedTimePromises.map((p) => p.then((t) => (t > time ? true : allFinishedPromise.then(() => false))));
58
+ const firstMoreRecent = await Promise.race(firstMoreRecentPromise);
59
+ return firstMoreRecent;
60
+ }
61
+ let _queue = null;
62
+ function getQueue() {
63
+ if (_queue)
64
+ return _queue;
65
+ _queue = new PQueue({
66
+ concurrency: 10,
67
+ throwOnTimeout: true,
68
+ timeout: 1000 * 60,
69
+ });
70
+ return _queue;
71
+ }
72
+ // We have to use a queue because we can't run more than one of these at a time
73
+ // or we'll hit an out of memory error because esbuild uses a lot of memory...
74
+ async function queuedGetDirModifiedTime(...args) {
75
+ const queue = getQueue();
76
+ const result = await queue.add(() => getDirModifiedTime(...args));
77
+ return result || -1;
78
+ }
79
+ export { queuedGetDirModifiedTime as getDirModifiedTime };
80
+ //# sourceMappingURL=modified-time.server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modified-time.server.js","sourceRoot":"","sources":["../../src/modified-time.server.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport { isGitIgnored } from 'globby'\nimport PQueue from 'p-queue'\nimport { cachified, dirModifiedTimeCache } from './cache.server.js'\n\nasync function getDirModifiedTime(\n\tdir: string,\n\t{ forceFresh = false }: { forceFresh?: boolean } = {},\n): Promise<number> {\n\tconst result = await cachified({\n\t\tkey: dir,\n\t\tcache: dirModifiedTimeCache,\n\t\tttl: 200,\n\t\tforceFresh,\n\t\tgetFreshValue: () => getDirModifiedTimeImpl(dir),\n\t})\n\treturn result\n}\n\nasync function getDirModifiedTimeImpl(dir: string): Promise<number> {\n\tconst isIgnored = await isGitIgnored({ cwd: dir })\n\tconst files = await fs.promises\n\t\t.readdir(dir, { withFileTypes: true })\n\t\t.catch(() => [])\n\n\tconst modifiedTimes: Array<number> = []\n\n\tfor (const file of files) {\n\t\t// Skip ignored files\n\t\tif (isIgnored(file.name)) continue\n\n\t\tconst filePath = path.join(dir, file.name)\n\n\t\tif (file.isDirectory()) {\n\t\t\tmodifiedTimes.push(await getDirModifiedTime(filePath))\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tconst { mtimeMs } = await fs.promises.stat(filePath)\n\t\t\t\tmodifiedTimes.push(mtimeMs)\n\t\t\t} catch {\n\t\t\t\t// ignore errors (e.g., file access permissions, file has been moved or deleted)\n\t\t\t}\n\t\t}\n\t}\n\n\ttry {\n\t\tconst { mtimeMs } = await fs.promises.stat(dir)\n\t\tmodifiedTimes.push(mtimeMs)\n\t} catch {\n\t\t// ignore errors (e.g., file access permissions, file has been moved or deleted)\n\t}\n\n\treturn Math.max(-1, ...modifiedTimes)\n}\n\n// this will return true as soon as one of the directories has been found to\n// have been modified more recently than the given time\n// TODO: this could be improved by not waiting for entire directories to be\n// scanned and instead stopping the scan as soon as we find a file that was\n// modified more recently than the given time\nexport async function modifiedMoreRecentlyThan(\n\ttime: number,\n\t...dirs: Array<string>\n) {\n\tconst modifiedTimePromises = dirs.map((dir) => getDirModifiedTime(dir))\n\tconst allFinishedPromise = Promise.all(modifiedTimePromises)\n\tconst firstMoreRecentPromise = modifiedTimePromises.map((p) =>\n\t\tp.then((t) => (t > time ? true : allFinishedPromise.then(() => false))),\n\t)\n\tconst firstMoreRecent = await Promise.race(firstMoreRecentPromise)\n\treturn firstMoreRecent\n}\n\nlet _queue: PQueue | null = null\nfunction getQueue() {\n\tif (_queue) return _queue\n\n\t_queue = new PQueue({\n\t\tconcurrency: 10,\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 queuedGetDirModifiedTime(\n\t...args: Parameters<typeof getDirModifiedTime>\n) {\n\tconst queue = getQueue()\n\tconst result = await queue.add(() => getDirModifiedTime(...args))\n\treturn result || -1\n}\n\nexport { queuedGetDirModifiedTime as getDirModifiedTime }\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epic-web/workshop-utils",
3
- "version": "5.0.0",
3
+ "version": "5.0.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -18,8 +18,8 @@
18
18
  "./config.server": "./src/config.server.ts",
19
19
  "./db.server": "./src/db.server.ts",
20
20
  "./timing.server": "./src/timing.server.ts",
21
+ "./modified-time.server": "./src/modified-time.server.ts",
21
22
  "./compile-mdx.server": "./src/compile-mdx.server.ts",
22
- "./change-tracker.server": "./src/change-tracker.server.ts",
23
23
  "./git.server": "./src/git.server.ts",
24
24
  "./iframe-sync": "./src/iframe-sync.ts",
25
25
  "./playwright.server": "./src/playwright.server.ts",
@@ -73,6 +73,13 @@
73
73
  "default": "./dist/esm/timing.server.js"
74
74
  }
75
75
  },
76
+ "./modified-time.server": {
77
+ "import": {
78
+ "source": "./src/modified-time.server.ts",
79
+ "types": "./dist/esm/modified-time.server.d.ts",
80
+ "default": "./dist/esm/modified-time.server.js"
81
+ }
82
+ },
76
83
  "./compile-mdx.server": {
77
84
  "import": {
78
85
  "source": "./src/compile-mdx.server.ts",
@@ -80,13 +87,6 @@
80
87
  "default": "./dist/esm/compile-mdx.server.js"
81
88
  }
82
89
  },
83
- "./change-tracker.server": {
84
- "import": {
85
- "source": "./src/change-tracker.server.ts",
86
- "types": "./dist/esm/change-tracker.server.d.ts",
87
- "default": "./dist/esm/change-tracker.server.js"
88
- }
89
- },
90
90
  "./git.server": {
91
91
  "import": {
92
92
  "source": "./src/git.server.ts",
@@ -160,7 +160,7 @@
160
160
  "chai": "^5.1.1",
161
161
  "chai-dom": "^1.12.0",
162
162
  "chalk": "^5.3.0",
163
- "chokidar": "^3.6.0",
163
+ "chokidar": "^4.0.1",
164
164
  "close-with-grace": "^1.3.0",
165
165
  "cross-spawn": "^7.0.3",
166
166
  "execa": "^9.1.0",
@@ -1,8 +0,0 @@
1
- import chokidar from 'chokidar';
2
- import closeWithGrace from 'close-with-grace';
3
- declare global {
4
- var __change_tracker_watcher__: ReturnType<typeof chokidar.watch> | undefined, __change_tracker_close_with_grace_return__: ReturnType<typeof closeWithGrace>;
5
- }
6
- export declare function getWatcher(): chokidar.FSWatcher | null;
7
- export declare function getOptionalWatcher(): chokidar.FSWatcher | undefined;
8
- //# sourceMappingURL=change-tracker.server.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"change-tracker.server.d.ts","sourceRoot":"","sources":["../../src/change-tracker.server.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAC/B,OAAO,cAAc,MAAM,kBAAkB,CAAA;AAE7C,OAAO,CAAC,MAAM,CAAC;IACd,IAAI,0BAA0B,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,CAAC,GAAG,SAAS,EAC5E,0CAA0C,EAAE,UAAU,CACrD,OAAO,cAAc,CACrB,CAAA;CACF;AAID,wBAAgB,UAAU,8BAuBzB;AAED,wBAAgB,kBAAkB,mCAEjC"}
@@ -1,32 +0,0 @@
1
- import chokidar from 'chokidar';
2
- import closeWithGrace from 'close-with-grace';
3
- let watcher = global.__change_tracker_watcher__;
4
- export function getWatcher() {
5
- if (process.env.EPICSHOP_DEPLOYED ??
6
- process.env.EPICSHOP_ENABLE_WATCHER !== 'true') {
7
- return null;
8
- }
9
- if (watcher)
10
- return watcher;
11
- const workshopRoot = process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd();
12
- watcher = chokidar.watch(workshopRoot, {
13
- ignoreInitial: true,
14
- ignored: [
15
- '**/.git/**',
16
- '**/node_modules/**',
17
- '**/build/**',
18
- '**/public/build/**',
19
- '**/playwright-report/**',
20
- '**/dist/**',
21
- '**/.cache/**',
22
- ],
23
- });
24
- global.__change_tracker_watcher__ = watcher;
25
- return watcher;
26
- }
27
- export function getOptionalWatcher() {
28
- return watcher;
29
- }
30
- global.__change_tracker_close_with_grace_return__?.uninstall();
31
- global.__change_tracker_close_with_grace_return__ = closeWithGrace(() => watcher?.close());
32
- //# sourceMappingURL=change-tracker.server.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"change-tracker.server.js","sourceRoot":"","sources":["../../src/change-tracker.server.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAC/B,OAAO,cAAc,MAAM,kBAAkB,CAAA;AAS7C,IAAI,OAAO,GAAG,MAAM,CAAC,0BAA0B,CAAA;AAE/C,MAAM,UAAU,UAAU;IACzB,IACC,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAC7B,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,MAAM,EAC7C,CAAC;QACF,OAAO,IAAI,CAAA;IACZ,CAAC;IACD,IAAI,OAAO;QAAE,OAAO,OAAO,CAAA;IAC3B,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAA;IACtE,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE;QACtC,aAAa,EAAE,IAAI;QACnB,OAAO,EAAE;YACR,YAAY;YACZ,oBAAoB;YACpB,aAAa;YACb,oBAAoB;YACpB,yBAAyB;YACzB,YAAY;YACZ,cAAc;SACd;KACD,CAAC,CAAA;IACF,MAAM,CAAC,0BAA0B,GAAG,OAAO,CAAA;IAC3C,OAAO,OAAO,CAAA;AACf,CAAC;AAED,MAAM,UAAU,kBAAkB;IACjC,OAAO,OAAO,CAAA;AACf,CAAC;AAED,MAAM,CAAC,0CAA0C,EAAE,SAAS,EAAE,CAAA;AAC9D,MAAM,CAAC,0CAA0C,GAAG,cAAc,CAAC,GAAG,EAAE,CACvE,OAAO,EAAE,KAAK,EAAE,CAChB,CAAA","sourcesContent":["import chokidar from 'chokidar'\nimport closeWithGrace from 'close-with-grace'\n\ndeclare global {\n\tvar __change_tracker_watcher__: ReturnType<typeof chokidar.watch> | undefined,\n\t\t__change_tracker_close_with_grace_return__: ReturnType<\n\t\t\ttypeof closeWithGrace\n\t\t>\n}\n\nlet watcher = global.__change_tracker_watcher__\n\nexport function getWatcher() {\n\tif (\n\t\tprocess.env.EPICSHOP_DEPLOYED ??\n\t\tprocess.env.EPICSHOP_ENABLE_WATCHER !== 'true'\n\t) {\n\t\treturn null\n\t}\n\tif (watcher) return watcher\n\tconst workshopRoot = process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd()\n\twatcher = chokidar.watch(workshopRoot, {\n\t\tignoreInitial: true,\n\t\tignored: [\n\t\t\t'**/.git/**',\n\t\t\t'**/node_modules/**',\n\t\t\t'**/build/**',\n\t\t\t'**/public/build/**',\n\t\t\t'**/playwright-report/**',\n\t\t\t'**/dist/**',\n\t\t\t'**/.cache/**',\n\t\t],\n\t})\n\tglobal.__change_tracker_watcher__ = watcher\n\treturn watcher\n}\n\nexport function getOptionalWatcher() {\n\treturn watcher\n}\n\nglobal.__change_tracker_close_with_grace_return__?.uninstall()\nglobal.__change_tracker_close_with_grace_return__ = closeWithGrace(() =>\n\twatcher?.close(),\n)\n"]}