@jk2908/cxx 0.3.1 → 0.3.3

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.
@@ -2,14 +2,14 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/build.ts", "../src/config.ts", "../src/lru.ts", "../src/utils.ts", "../src/vite/index.ts", "../src/logger.ts"],
4
4
  "sourcesContent": [
5
- "import { createHash } from 'node:crypto'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport { transform } from 'lightningcss'\nimport MagicString, { type SourceMap } from 'magic-string'\n\nimport type { BuildContext, PluginConfig, Tags } from './types.js'\nimport { AUTOGEN_MSG, GENERATED_DIR, PKG_NAME } from './config.js'\nimport { LruCache } from './lru.js'\nimport { pascalise } from './utils.js'\n\n// decode bytes to a string, then stringify into a safe literal. `${bytes}` only\n// works by accident for Buffer; a plain Uint8Array stringifies as `97,123,125`\nconst decoder = new TextDecoder()\n\n/**\n * LRU cache of `collect` results by file id + last seen source + config so watcher and transform\n * can reuse the same parsed template/tags.\n */\nconst collectCache = new LruCache<{\n\tsource: string\n\tconfig: string\n\ttemplate: string\n\ttags: Tags\n\tmap: SourceMap | null\n}>()\n\n/**\n * LRU cache for file content. Stores the last written content per file path so `maybeWrite`\n * can skip disk i/o when nothing has changed.\n */\nconst fileCache = new LruCache<string>()\n\n/**\n * Serialises type file writes inside a single process so overlapping `writeTypes` calls can't\n * interleave or land out of order.\n */\nlet writeChain: Promise<void> = Promise.resolve()\n\n/**\n * Returns whether the error was caused by a missing file or directory.\n */\nexport function isENOENT(err: unknown) {\n\treturn err instanceof Error && 'code' in err && err.code === 'ENOENT'\n}\n\n/**\n * Write a file by first writing to a unique temporary file and then renaming it into place, so\n * concurrent writers (e.g. Turbopack loader workers) never observe a partial file.\n */\nasync function writeFileAtomic(filePath: string, content: string) {\n\tconst tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`\n\n\tawait fs.writeFile(tmp, content)\n\tawait fs.rename(tmp, filePath)\n}\n\n/**\n * Run a write task after all previously queued writes have settled. A rejection doesn't break the\n * chain for later tasks.\n */\nfunction enqueueWrite<T>(task: () => Promise<T>) {\n\t// wait for the previous write to finish, then run this one, even if the previous one failed\n\tconst run = writeChain.then(task, task)\n\n\t// move the queue on to this write, but turn either outcome into success so it never rejects\n\twriteChain = run.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t)\n\n\t// return the real promise, so the caller still sees this write's value or error\n\treturn run\n}\n\n/**\n * Write a file only if the content actually differs from what is already on disk.\n */\nexport async function maybeWrite(filePath: string, content: string) {\n\tconst cached = fileCache.get(filePath)\n\t// fast path: already wrote this exact content in this process\n\tif (cached === content) return false\n\n\t// verify against disk on a cold start (or when the cache is stale) so we\n\t// don't needlessly bump the mtime and invalidate downstream watchers\n\ttry {\n\t\tif ((await fs.readFile(filePath, 'utf-8')) === content) {\n\t\t\tfileCache.set(filePath, content)\n\t\t\treturn false\n\t\t}\n\t} catch (err) {\n\t\tif (!isENOENT(err)) throw err\n\t}\n\n\tawait writeFileAtomic(filePath, content)\n\tfileCache.set(filePath, content)\n\n\treturn true\n}\n\n// matches `[css, classes, href] = cxx\\`...\\`` and `cxx.tag<T>('name')\\`...\\``\n// capture groups: 1 optional `export `, 2-4 assigned variable names, 5 plain\n// css body, 6 quote kind, 7 tag name, 8 tagged css body\nconst regex =\n\t/((?:export\\s+)?)(?:const|var|let)\\s*\\[(\\w*)\\s*,?\\s*(\\w*)\\s*,?\\s*(\\w*)\\s*\\]\\s*=\\s*cxx(?:\\s*`([\\s\\S]*?)`|\\s*\\.\\s*tag(?:\\s*<[^>]+>)?\\s*\\(\\s*(['\"])(.*?)\\6\\s*\\)\\s*`([\\s\\S]*?)`)/gm\n\nconst IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/\n\n/**\n * Hash transformed CSS into a stable href for style deduplication.\n */\nfunction hashCss(code: Uint8Array) {\n\treturn `cxx-${createHash('sha256').update(code).digest('base64url').slice(0, 12)}`\n}\n\nexport class DuplicateTagError extends Error {\n\tname = 'DuplicateTagError'\n\n\tconstructor(tag: string) {\n\t\tsuper(`Tag ${tag} has already been seen. Tags must be unique across your application`)\n\t}\n}\n\nexport class InvalidTagError extends Error {\n\tname = 'InvalidTagError'\n\n\tconstructor(tag: string) {\n\t\tsuper(\n\t\t\t`Tag \"${tag}\" does not map to a valid TypeScript identifier. ` +\n\t\t\t\t`Use letters, numbers, spaces, \"_\" or \"-\"`,\n\t\t)\n\t}\n}\n\n/**\n * Transform `cxx` templates in one source file and collect any exported tag types.\n */\nexport function collect(source: string, id: string, config: PluginConfig) {\n\tconst configKey = JSON.stringify(config)\n\n\t// reuse the last result when the source and config are unchanged\n\tconst cached = collectCache.get(id)\n\tif (cached?.source === source && cached.config === configKey) return cached\n\n\tconst tags: Tags = new Map()\n\tconst magic = new MagicString(source)\n\tlet matched = false\n\n\t// the regex is global, so reset its cursor before scanning\n\tregex.lastIndex = 0\n\n\tlet match: RegExpExecArray | null\n\n\t// walk every cxx template and replace it in place\n\twhile ((match = regex.exec(source))) {\n\t\tmatched = true\n\n\t\t// capture groups: export, three bindings, plain css, (quote), tag, tagged css\n\t\tconst [, exp, varOne, varTwo, varThree, plainCss, , tag, taggedCss] = match\n\t\tconst css = plainCss ?? taggedCss\n\n\t\t// compile the CSS to minified CSS Modules\n\t\tconst { code, exports = {} } = transform({\n\t\t\tminify: true,\n\t\t\tcssModules: true,\n\t\t\tfilename: id,\n\t\t\tcode: new TextEncoder().encode(css),\n\t\t})\n\n\t\tconst href = hashCss(code)\n\t\t// map each class name to its generated (scoped) name\n\t\tconst styles = Object.fromEntries(\n\t\t\tObject.entries(exports).map(([k, v]) => [k, v.name]),\n\t\t)\n\n\t\tif (tag) {\n\t\t\tconst suffix =\n\t\t\t\ttypeof config.typeSuffix === 'string'\n\t\t\t\t\t? config.typeSuffix\n\t\t\t\t\t: config.typeSuffix === false\n\t\t\t\t\t\t? ''\n\t\t\t\t\t\t: 'Classes'\n\t\t\tconst tagWithSuffix = pascalise(`${tag}${suffix}`)\n\n\t\t\tif (!IDENTIFIER.test(tagWithSuffix)) throw new InvalidTagError(tag)\n\t\t\tif (tags.has(tagWithSuffix)) throw new DuplicateTagError(tagWithSuffix)\n\n\t\t\t// side effect but necessary\n\t\t\ttags.set(tagWithSuffix, Object.keys(styles))\n\t\t}\n\n\t\tconst statements: string[] = []\n\n\t\tif (varOne) {\n\t\t\tstatements.push(`${exp}const ${varOne} = ${JSON.stringify(decoder.decode(code))}`)\n\t\t}\n\n\t\tif (varTwo) {\n\t\t\tstatements.push(`${exp}const ${varTwo} = ${JSON.stringify(styles)}`)\n\t\t}\n\n\t\tif (varThree) {\n\t\t\tstatements.push(`${exp}const ${varThree} = ${JSON.stringify(href)}`)\n\t\t}\n\n\t\t// swap the template for the generated declarations, keeping the export keyword\n\t\tmagic.overwrite(match.index, match.index + match[0].length, statements.join('\\n'))\n\t}\n\n\tconst template = magic.toString()\n\t// only build a map when something was actually rewritten\n\tconst map = matched\n\t\t? magic.generateMap({ source: id, includeContent: true, hires: true })\n\t\t: null\n\tconst result = { source, config: configKey, template, tags, map }\n\n\tcollectCache.set(id, result)\n\n\treturn result\n}\n\n/**\n * Render the generated declaration file contents for a set of collected tags.\n */\nfunction renderTypes(tags: Tags) {\n\treturn [\n\t\tAUTOGEN_MSG,\n\t\t'',\n\t\t`import '${PKG_NAME}'`,\n\t\t'',\n\t\t`declare module '${PKG_NAME}' {`,\n\t\ttagsToType(tags),\n\t\t'}',\n\t\t'',\n\t].join('\\n')\n}\n\n/**\n * Write (or remove) the generated `index.d.ts` for a full tag surface.\n */\nasync function writeRenderedTypes(tags: Tags) {\n\tconst filePath = path.join(GENERATED_DIR, 'index.d.ts')\n\n\tif (!tags.size) {\n\t\t// `force` makes this a no-op when the file is already absent\n\t\tawait fs.rm(filePath, { force: true })\n\t\tfileCache.delete(filePath)\n\n\t\treturn\n\t}\n\n\tawait fs.mkdir(GENERATED_DIR, { recursive: true })\n\tawait maybeWrite(filePath, renderTypes(tags))\n}\n\n/**\n * Write the generated declaration file for the current set of collected tags.\n */\nexport function writeTypes(tags: Tags) {\n\treturn enqueueWrite(() => writeRenderedTypes(tags))\n}\n\nconst PARTIAL_DIR = path.join(GENERATED_DIR, 'tags')\n\nfunction partialPath(filePath: string) {\n\treturn path.join(\n\t\tPARTIAL_DIR,\n\t\t`${createHash('sha1').update(filePath).digest('hex')}.json`,\n\t)\n}\n\n/**\n * Read every per-file partial written by the Next loader workers and merge them into one tag map.\n * Partials whose source file no longer exists are pruned so deleted files drop out of the types.\n */\nasync function readPartials() {\n\tlet entries: string[]\n\n\ttry {\n\t\tentries = await fs.readdir(PARTIAL_DIR)\n\t} catch (err) {\n\t\tif (isENOENT(err)) return new Map()\n\t\tthrow err\n\t}\n\n\tconst tags: Tags = new Map()\n\n\tfor (const entry of entries) {\n\t\tif (!entry.endsWith('.json')) continue\n\n\t\tconst partial = path.join(PARTIAL_DIR, entry)\n\t\tlet parsed: { file: string; tags: Record<string, string[]> }\n\n\t\ttry {\n\t\t\tparsed = JSON.parse(await fs.readFile(partial, 'utf-8'))\n\t\t} catch {\n\t\t\tcontinue\n\t\t}\n\n\t\ttry {\n\t\t\tawait fs.access(parsed.file)\n\t\t} catch {\n\t\t\tawait fs.rm(partial, { force: true })\n\t\t\tcontinue\n\t\t}\n\n\t\tfor (const [name, classes] of Object.entries(parsed.tags)) {\n\t\t\tif (tags.has(name)) throw new DuplicateTagError(name)\n\t\t\ttags.set(name, classes)\n\t\t}\n\t}\n\n\treturn tags\n}\n\n/**\n * Next-specific write path. Turbopack spreads loaders across worker processes, so each worker\n * persists its own per-file partial and rebuilds the shared declaration file from all of them.\n */\nexport function writeTypesForFile(filePath: string, tags: Tags) {\n\treturn enqueueWrite(async () => {\n\t\tawait fs.mkdir(PARTIAL_DIR, { recursive: true })\n\n\t\tconst partial = partialPath(filePath)\n\n\t\tif (!tags.size) {\n\t\t\tawait fs.rm(partial, { force: true })\n\t\t} else {\n\t\t\tawait writeFileAtomic(\n\t\t\t\tpartial,\n\t\t\t\tJSON.stringify({ file: filePath, tags: Object.fromEntries(tags) }),\n\t\t\t)\n\t\t}\n\n\t\tawait writeRenderedTypes(await readPartials())\n\t})\n}\n\n/**\n * Render collected tags as a module augmentation for the package entrypoint.\n */\nexport function tagsToType(tags: Tags) {\n\treturn [...tags]\n\t\t.map(([tag, classes]) => {\n\t\t\tconst union =\n\t\t\t\tclasses.length > 0\n\t\t\t\t\t? classes\n\t\t\t\t\t\t\t.toSorted((a, b) => a.length - b.length)\n\t\t\t\t\t\t\t.map(c => `'${c}'`)\n\t\t\t\t\t\t\t.join(' | ')\n\t\t\t\t\t: 'never'\n\n\t\t\treturn `\\texport type ${tag} = ${union}`\n\t\t})\n\t\t.join('\\n')\n}\n\n/**\n * Merge per-file tag maps into one application-wide tag map.\n */\nexport function flattenTags(tagsByFile: BuildContext['tagsByFile']) {\n\tconst tags = new Map<string, string[]>()\n\n\tfor (const fileTags of tagsByFile.values()) {\n\t\tfor (const [name, classes] of fileTags) {\n\t\t\tif (tags.has(name)) throw new DuplicateTagError(name)\n\t\t\ttags.set(name, classes)\n\t\t}\n\t}\n\n\treturn tags\n}\n",
5
+ "import { createHash } from 'node:crypto'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport { transform } from 'lightningcss'\nimport MagicString, { type SourceMap } from 'magic-string'\n\nimport type { PluginConfig, Tags } from './types.js'\nimport { AUTOGEN_MSG, GENERATED_DIR, PKG_NAME } from './config.js'\nimport { LruCache } from './lru.js'\nimport { pascalise } from './utils.js'\n\n// decode bytes to a string, then stringify into a safe literal. `${bytes}` only\n// works by accident for Buffer; a plain Uint8Array stringifies as `97,123,125`\nconst decoder = new TextDecoder()\nconst encoder = new TextEncoder()\n\n/**\n * LRU cache of `collect` results by file id + last seen source + config so watcher and transform\n * can reuse the same parsed template/tags.\n */\nconst collectCache = new LruCache<{\n\tsource: string\n\tconfig: string\n\ttemplate: string\n\ttags: Tags\n\tmap: SourceMap | null\n}>()\n\n/**\n * Serialises type file writes inside a single process so overlapping writes can't interleave or\n * land out of order.\n */\nlet writeChain: Promise<void> = Promise.resolve()\n\n/**\n * Returns whether the error was caused by a missing file or directory.\n */\nexport function isENOENT(err: unknown) {\n\treturn err instanceof Error && 'code' in err && err.code === 'ENOENT'\n}\n\n/**\n * Write a file by writing to a unique temporary file and renaming it into place, so concurrent\n * writers never observe a partial file. The temp file is cleaned up if the rename fails.\n */\nasync function writeFileAtomic(filePath: string, content: string) {\n\tconst tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`\n\n\tawait fs.writeFile(tmp, content)\n\n\ttry {\n\t\tawait fs.rename(tmp, filePath)\n\t} catch (err) {\n\t\tawait fs.rm(tmp, { force: true })\n\t\tthrow err\n\t}\n}\n\n/**\n * Run a write task after all previously queued writes have settled. A rejection doesn't break the\n * chain for later tasks.\n */\nfunction enqueueWrite<T>(task: () => Promise<T>) {\n\t// wait for the previous write to finish, then run this one, even if the previous one failed\n\tconst run = writeChain.then(task, task)\n\n\t// move the queue on to this write, but turn either outcome into success so it never rejects\n\twriteChain = run.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t)\n\n\t// return the real promise, so the caller still sees this write's value or error\n\treturn run\n}\n\n/**\n * Write a file only if the content differs from what is already on disk, so watchers are not woken\n * by a needless mtime change.\n */\nexport async function maybeWrite(filePath: string, content: string) {\n\ttry {\n\t\tif ((await fs.readFile(filePath, 'utf-8')) === content) return false\n\t} catch (err) {\n\t\tif (!isENOENT(err)) throw err\n\t}\n\n\tawait writeFileAtomic(filePath, content)\n\n\treturn true\n}\n\n// matches `[css, classes, href] = cxx\\`...\\`` and `cxx.tag<T>('name')\\`...\\``\n// capture groups: 1 optional `export `, 2-4 assigned variable names, 5 plain\n// css body, 6 quote kind, 7 tag name, 8 tagged css body\nconst regex =\n\t/((?:export\\s+)?)(?:const|var|let)\\s*\\[(\\w*)\\s*,?\\s*(\\w*)\\s*,?\\s*(\\w*)\\s*\\]\\s*=\\s*cxx(?:\\s*`([\\s\\S]*?)`|\\s*\\.\\s*tag(?:\\s*<[^>]+>)?\\s*\\(\\s*(['\"])(.*?)\\6\\s*\\)\\s*`([\\s\\S]*?)`)/gm\n\nconst IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/\n\n/**\n * Hash transformed CSS into a stable href for style deduplication.\n */\nfunction hashCss(code: Uint8Array) {\n\treturn `cxx-${createHash('sha256').update(code).digest('base64url').slice(0, 12)}`\n}\n\nexport class DuplicateTagError extends Error {\n\tname = 'DuplicateTagError'\n\n\tconstructor(tag: string) {\n\t\tsuper(`Tag ${tag} has already been seen. Tags must be unique across your application`)\n\t}\n}\n\nexport class InvalidTagError extends Error {\n\tname = 'InvalidTagError'\n\n\tconstructor(tag: string) {\n\t\tsuper(\n\t\t\t`Tag \"${tag}\" does not map to a valid TypeScript identifier. ` +\n\t\t\t\t`Use letters, numbers, spaces, \"_\" or \"-\"`,\n\t\t)\n\t}\n}\n\n/**\n * Transform `cxx` templates in one source file and collect any exported tag types.\n */\nexport function collect(source: string, id: string, config: PluginConfig) {\n\tconst configKey = JSON.stringify(config)\n\n\t// reuse the last result when the source and config are unchanged\n\tconst cached = collectCache.get(id)\n\tif (cached?.source === source && cached.config === configKey) return cached\n\n\tconst tags: Tags = new Map()\n\tconst magic = new MagicString(source)\n\tlet matched = false\n\n\t// the regex is global, so reset its cursor before scanning\n\tregex.lastIndex = 0\n\n\tlet match: RegExpExecArray | null\n\n\t// walk every cxx template and replace it in place\n\twhile ((match = regex.exec(source))) {\n\t\tmatched = true\n\n\t\t// capture groups: export, three bindings, plain css, (quote), tag, tagged css\n\t\tconst [, exp, varOne, varTwo, varThree, plainCss, , tag, taggedCss] = match\n\t\tconst css = plainCss ?? taggedCss\n\t\tconst matchIndex = match.index\n\n\t\t// compile the CSS to minified CSS Modules, reporting the source line on failure\n\t\tconst { code, exports: classExports = {} } = (() => {\n\t\t\ttry {\n\t\t\t\treturn transform({\n\t\t\t\t\tminify: true,\n\t\t\t\t\tcssModules: true,\n\t\t\t\t\tfilename: id,\n\t\t\t\t\tcode: encoder.encode(css),\n\t\t\t\t})\n\t\t\t} catch (err) {\n\t\t\t\tconst line = source.slice(0, matchIndex).split('\\n').length\n\t\t\t\tthrow new Error(`Failed to compile cxx CSS at ${id}:${line}`, { cause: err })\n\t\t\t}\n\t\t})()\n\n\t\tconst href = hashCss(code)\n\t\t// map each class name to its generated (scoped) name\n\t\tconst styles = Object.fromEntries(\n\t\t\tObject.entries(classExports).map(([k, v]) => [k, v.name]),\n\t\t)\n\n\t\tif (tag) {\n\t\t\tconst suffix =\n\t\t\t\ttypeof config.typeSuffix === 'string'\n\t\t\t\t\t? config.typeSuffix\n\t\t\t\t\t: config.typeSuffix === false\n\t\t\t\t\t\t? ''\n\t\t\t\t\t\t: 'Classes'\n\t\t\tconst tagWithSuffix = pascalise(`${tag}${suffix}`)\n\n\t\t\tif (!IDENTIFIER.test(tagWithSuffix)) throw new InvalidTagError(tag)\n\t\t\tif (tags.has(tagWithSuffix)) throw new DuplicateTagError(tagWithSuffix)\n\n\t\t\t// side effect but necessary\n\t\t\ttags.set(tagWithSuffix, Object.keys(styles))\n\t\t}\n\n\t\tconst statements: string[] = []\n\n\t\tif (varOne) {\n\t\t\tstatements.push(`${exp}const ${varOne} = ${JSON.stringify(decoder.decode(code))}`)\n\t\t}\n\n\t\tif (varTwo) {\n\t\t\tstatements.push(`${exp}const ${varTwo} = ${JSON.stringify(styles)}`)\n\t\t}\n\n\t\tif (varThree) {\n\t\t\tstatements.push(`${exp}const ${varThree} = ${JSON.stringify(href)}`)\n\t\t}\n\n\t\t// swap the template for the generated declarations, keeping the export keyword\n\t\tmagic.overwrite(matchIndex, matchIndex + match[0].length, statements.join('\\n'))\n\t}\n\n\tconst template = magic.toString()\n\t// only build a map when something was actually rewritten\n\tconst map = matched\n\t\t? magic.generateMap({ source: id, includeContent: true, hires: true })\n\t\t: null\n\tconst result = { source, config: configKey, template, tags, map }\n\n\tcollectCache.set(id, result)\n\n\treturn result\n}\n\n/**\n * Render the generated declaration file contents for a set of collected tags.\n */\nfunction renderTypes(tags: Tags) {\n\treturn [\n\t\tAUTOGEN_MSG,\n\t\t'',\n\t\t`import '${PKG_NAME}'`,\n\t\t'',\n\t\t`declare module '${PKG_NAME}' {`,\n\t\ttagsToType(tags),\n\t\t'}',\n\t\t'',\n\t].join('\\n')\n}\n\n/**\n * Write (or remove) the generated `index.d.ts` for a full tag surface.\n */\nasync function writeRenderedTypes(tags: Tags) {\n\tconst filePath = path.join(GENERATED_DIR, 'index.d.ts')\n\n\tif (!tags.size) {\n\t\tawait fs.rm(filePath, { force: true })\n\t\treturn\n\t}\n\n\tawait fs.mkdir(GENERATED_DIR, { recursive: true })\n\tawait maybeWrite(filePath, renderTypes(tags))\n}\n\nconst PARTIAL_DIR = path.join(GENERATED_DIR, 'tags')\n\n// wait this long after the last file before rebuilding, so a burst of files\n// only re-reads the partials once\nconst REBUILD_WAIT = 100\n\nlet rebuildTimer: ReturnType<typeof setTimeout> | null = null\nlet rebuildError: unknown = null\n\nfunction partialPath(filePath: string) {\n\treturn path.join(\n\t\tPARTIAL_DIR,\n\t\t`${createHash('sha1').update(filePath).digest('hex')}.json`,\n\t)\n}\n\n/**\n * The partial directory changes whenever any process adds, removes or overwrites a partial, so\n * its mtime tells us whether another writer moved while we were rebuilding.\n */\nasync function partialDirMtime() {\n\ttry {\n\t\treturn (await fs.stat(PARTIAL_DIR)).mtimeMs\n\t} catch (err) {\n\t\tif (isENOENT(err)) return 0\n\t\tthrow err\n\t}\n}\n\n/**\n * Read every per-file partial and merge them into one tag map. Partials whose source file no\n * longer exists are pruned so deleted files drop out of the types.\n */\nasync function readPartials() {\n\tlet entries: string[]\n\n\ttry {\n\t\tentries = await fs.readdir(PARTIAL_DIR)\n\t} catch (err) {\n\t\tif (isENOENT(err)) return new Map()\n\t\tthrow err\n\t}\n\n\tconst tags: Tags = new Map()\n\n\tfor (const entry of entries) {\n\t\tif (!entry.endsWith('.json')) continue\n\n\t\tconst partial = path.join(PARTIAL_DIR, entry)\n\t\tlet parsed: { file: string; tags: Record<string, string[]> }\n\n\t\ttry {\n\t\t\tparsed = JSON.parse(await fs.readFile(partial, 'utf-8'))\n\t\t} catch {\n\t\t\tcontinue\n\t\t}\n\n\t\ttry {\n\t\t\tawait fs.access(parsed.file)\n\t\t} catch {\n\t\t\tawait fs.rm(partial, { force: true })\n\t\t\tcontinue\n\t\t}\n\n\t\tfor (const [name, classes] of Object.entries(parsed.tags)) {\n\t\t\tif (tags.has(name)) throw new DuplicateTagError(name)\n\t\t\ttags.set(name, classes)\n\t\t}\n\t}\n\n\treturn tags\n}\n\n/**\n * Persist one file's tags to its own partial, then rebuild the shared types soon. Rebuilding is\n * debounced and serialised, so a large build does not re-read every partial per file.\n */\nexport function writeTypesForFile(filePath: string, tags: Tags) {\n\treturn enqueueWrite(async () => {\n\t\tawait fs.mkdir(PARTIAL_DIR, { recursive: true })\n\n\t\tconst partial = partialPath(filePath)\n\n\t\tif (!tags.size) {\n\t\t\tawait fs.rm(partial, { force: true })\n\t\t} else {\n\t\t\tawait writeFileAtomic(\n\t\t\t\tpartial,\n\t\t\t\tJSON.stringify({ file: filePath, tags: Object.fromEntries(tags) }),\n\t\t\t)\n\t\t}\n\t}).then(() => {\n\t\tscheduleRebuild()\n\t})\n}\n\nfunction scheduleRebuild() {\n\tif (rebuildTimer) clearTimeout(rebuildTimer)\n\n\trebuildTimer = setTimeout(() => {\n\t\trebuildTimer = null\n\t\tflushTypes().catch(err => {\n\t\t\trebuildError = err\n\t\t})\n\t}, REBUILD_WAIT)\n}\n\n/**\n * Rebuild `index.d.ts` from every partial now. If another writer touches a partial mid-rebuild,\n * rebuild once more so the final write reflects every partial. Bounded to avoid spinning.\n */\nexport function flushTypes() {\n\tif (rebuildTimer) {\n\t\tclearTimeout(rebuildTimer)\n\t\trebuildTimer = null\n\t}\n\n\treturn enqueueWrite(async () => {\n\t\tlet before = await partialDirMtime()\n\n\t\tfor (let attempt = 0; attempt < 3; attempt++) {\n\t\t\tawait writeRenderedTypes(await readPartials())\n\n\t\t\tconst after = await partialDirMtime()\n\t\t\tif (after === before) return\n\n\t\t\tbefore = after\n\t\t}\n\t})\n}\n\n/**\n * Return and clear any error from a background rebuild, so a caller can fail loudly.\n */\nexport function takeRebuildError() {\n\tconst err = rebuildError\n\trebuildError = null\n\treturn err\n}\n\n/**\n * Render collected tags as a module augmentation for the package entrypoint.\n */\nexport function tagsToType(tags: Tags) {\n\treturn [...tags]\n\t\t.toSorted(([a], [b]) => a.localeCompare(b))\n\t\t.map(([tag, classes]) => {\n\t\t\tconst union =\n\t\t\t\tclasses.length > 0\n\t\t\t\t\t? classes\n\t\t\t\t\t\t\t.toSorted((a, b) => a.length - b.length)\n\t\t\t\t\t\t\t.map(c => `'${c}'`)\n\t\t\t\t\t\t\t.join(' | ')\n\t\t\t\t\t: 'never'\n\n\t\t\treturn `\\texport type ${tag} = ${union}`\n\t\t})\n\t\t.join('\\n')\n}\n",
6
6
  "export const NAME = 'cxx'\nexport const PKG_NAME = `@jk2908/${NAME}`\nexport const GENERATED_DIR = `.${NAME}`\nexport const AUTOGEN_MSG = `// auto-generated by ${NAME}`\n",
7
7
  "export const CACHE_MAX_SIZE = 100\n\nexport class LruCache<T> {\n\t#cache: Map<string, T> = new Map()\n\t#maxSize: number\n\n\tconstructor(maxSize = CACHE_MAX_SIZE) {\n\t\tthis.#maxSize = maxSize\n\t}\n\n\tget size() {\n\t\treturn this.#cache.size\n\t}\n\n\t/**\n\t * Retrieve a cached value and promote it to most-recently-used so it won't\n\t * be evicted while still actively referenced.\n\t */\n\tget(key: string) {\n\t\tconst cached = this.#cache.get(key)\n\t\tif (this.#cache.has(key)) this.set(key, cached as T)\n\n\t\treturn cached\n\t}\n\n\t/**\n\t * Promote an existing cache entry to most-recently-used by deleting and\n\t * re-inserting it, which moves it to the end of the Map's iteration\n\t * order. If the cache is at capacity, evict the least recently used\n\t * entry (front) before inserting.\n\t */\n\tset(key: string, value: T) {\n\t\t// drop any existing entry\n\t\tthis.#cache.delete(key)\n\n\t\tif (this.#cache.size >= this.#maxSize) {\n\t\t\tconst lru = this.#cache.keys().next().value\n\t\t\tif (lru !== undefined) this.#cache.delete(lru)\n\t\t}\n\n\t\tthis.#cache.set(key, value)\n\t}\n\n\tdelete(key: string) {\n\t\treturn this.#cache.delete(key)\n\t}\n\n\tclear() {\n\t\treturn this.#cache.clear()\n\t}\n}\n",
8
- "export function capitalise(str: string) {\n\treturn str.charAt(0).toUpperCase() + str.slice(1)\n}\n\nexport function pascalise(str: string) {\n\treturn str\n\t\t.split(/[ _-]+/)\n\t\t.map(capitalise)\n\t\t.join('')\n}\n\nexport type Batcher<K, V> = {\n\t/**\n\t * Queue a value for a key. Queueing the same key again replaces the previous value.\n\t */\n\tadd: (key: K, value: V) => void\n\t/**\n\t * Run any waiting work now and resolve once it has finished.\n\t */\n\tflush: () => Promise<void>\n}\n\n/**\n * Group bursts of work and run them together. Every key queued within `wait` is\n * included once, and a key queued while a batch is running is handled in the next\n * batch instead of replacing the one in progress.\n */\nexport function createBatcher<K, V>(\n\twait: number,\n\tflush: (batch: Map<K, V>) => void | Promise<void>,\n): Batcher<K, V> {\n\t// work waiting to run, keyed so each key is only queued once\n\tconst pending = new Map<K, V>()\n\t// waits for a burst of changes to settle before running\n\tlet timer: ReturnType<typeof setTimeout> | null = null\n\t// true while a batch is running, so we never start a second one\n\tlet running = false\n\n\tasync function drain() {\n\t\t// a run is already going, so let it pick up the new work\n\t\tif (running) return\n\t\trunning = true\n\n\t\ttry {\n\t\t\t// keep looping while there is work, including work added during a flush\n\t\t\twhile (pending.size > 0) {\n\t\t\t\t// snapshot the queued work\n\t\t\t\tconst batch = new Map(pending)\n\t\t\t\t// empty the queue right away, so anything added during flush waits for the next loop\n\t\t\t\tpending.clear()\n\n\t\t\t\tawait flush(batch)\n\t\t\t}\n\t\t} finally {\n\t\t\t// always allow the next run, even if flush threw\n\t\t\trunning = false\n\t\t}\n\t}\n\n\tfunction schedule() {\n\t\t// cancel the previous wait, so the timer restarts from now\n\t\tif (timer) clearTimeout(timer)\n\n\t\t// run once the calls stop coming for `wait` ms\n\t\ttimer = setTimeout(() => {\n\t\t\t// the timer has fired, so drop the stale handle\n\t\t\ttimer = null\n\t\t\t// start the run without waiting for it (this is a timer callback)\n\t\t\tvoid drain()\n\t\t}, wait)\n\t}\n\n\treturn {\n\t\tadd(key, value) {\n\t\t\t// the newest value for this key wins\n\t\t\tpending.set(key, value)\n\t\t\tschedule()\n\t\t},\n\t\tflush() {\n\t\t\t// run now instead of waiting out the timer\n\t\t\tif (timer) {\n\t\t\t\tclearTimeout(timer)\n\t\t\t\ttimer = null\n\t\t\t}\n\n\t\t\treturn drain()\n\t\t},\n\t}\n}\n",
9
- "import { realpathSync } from 'node:fs'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport { type Plugin, type ResolvedConfig, type ViteDevServer } from 'vite'\n\nimport type { BuildContext, PluginConfig } from '../types.js'\nimport { collect, flattenTags, isENOENT, writeTypes } from '../build.js'\nimport { GENERATED_DIR } from '../config.js'\nimport { Logger } from '../logger.js'\nimport { createBatcher } from '../utils.js'\n\nconst ACCEPTED_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx'])\n\ntype WatchEvent = 'add' | 'change' | 'unlink'\n\nfunction normaliseWatchPath(p: string) {\n\treturn p.replace(/\\\\/g, '/')\n}\n\nexport default function cxx(pluginConfig: PluginConfig = {}) {\n\tconst buildContext = {\n\t\ttagsByFile: new Map(),\n\t} satisfies BuildContext\n\n\tconst logger = new Logger()\n\n\t// resolved from Vite's root in `configResolved`, not from `process.cwd()`\n\tlet watchRoot = normaliseWatchPath(process.cwd())\n\n\tfunction resolveWatchFile(filePath: string) {\n\t\tconst absolutePath = path.resolve(watchRoot, filePath)\n\t\tconst parentPath = path.dirname(absolutePath)\n\n\t\ttry {\n\t\t\tconst resolvedParentPath = normaliseWatchPath(realpathSync.native(parentPath))\n\n\t\t\treturn normaliseWatchPath(\n\t\t\t\tpath.join(resolvedParentPath, path.basename(absolutePath)),\n\t\t\t)\n\t\t} catch {\n\t\t\treturn normaliseWatchPath(absolutePath)\n\t\t}\n\t}\n\n\t// returns the canonical, watchable path or null when the file is irrelevant\n\tfunction watchedFile(filePath: string): string | null {\n\t\tconst resolvedPath = resolveWatchFile(filePath)\n\n\t\tif (\n\t\t\tresolvedPath.startsWith(`${watchRoot}/`) &&\n\t\t\t!resolvedPath.includes(`/${GENERATED_DIR}/`) &&\n\t\t\tACCEPTED_EXTENSIONS.has(path.extname(resolvedPath))\n\t\t) {\n\t\t\treturn resolvedPath\n\t\t}\n\n\t\treturn null\n\t}\n\n\tasync function onEvent(event: WatchEvent, file: string) {\n\t\tconst hadTags = buildContext.tagsByFile.has(file)\n\n\t\tif (event === 'unlink') {\n\t\t\tif (!hadTags) return\n\n\t\t\tbuildContext.tagsByFile.delete(file)\n\t\t\tawait writeTypes(flattenTags(buildContext.tagsByFile))\n\t\t\tlogger.info(`updated types from ${file}`)\n\n\t\t\treturn\n\t\t}\n\n\t\tlet source: string\n\n\t\ttry {\n\t\t\tsource = await fs.readFile(file, 'utf-8')\n\t\t} catch (err) {\n\t\t\tif (!isENOENT(err)) throw err\n\t\t\tif (!hadTags) return\n\n\t\t\tbuildContext.tagsByFile.delete(file)\n\t\t\tawait writeTypes(flattenTags(buildContext.tagsByFile))\n\t\t\tlogger.info(`updated types from ${file}`)\n\n\t\t\treturn\n\t\t}\n\n\t\tconst { tags } = collect(source, file, pluginConfig)\n\t\tconst hasTags = tags.size > 0\n\n\t\tif (hasTags) {\n\t\t\tbuildContext.tagsByFile.set(file, tags)\n\t\t} else {\n\t\t\tbuildContext.tagsByFile.delete(file)\n\t\t}\n\n\t\tif (!hadTags && !hasTags) return\n\n\t\tawait writeTypes(flattenTags(buildContext.tagsByFile))\n\t\tlogger.info(`updated types from ${file}`)\n\t}\n\n\t// collect change events that arrive close together and handle each file once.\n\t// The file path is the key, so changes to different files are all kept, and a\n\t// change that arrives during a rebuild runs after that rebuild finishes.\n\tconst queue = createBatcher<string, WatchEvent>(75, async batch => {\n\t\tfor (const [file, event] of batch) {\n\t\t\ttry {\n\t\t\t\tawait onEvent(event, file)\n\t\t\t} catch (err) {\n\t\t\t\tlogger.error(`failed to update types from ${file}`, err)\n\t\t\t}\n\t\t}\n\t})\n\n\treturn {\n\t\tname: 'cxx',\n\t\tenforce: 'pre',\n\t\tconfig() {\n\t\t\tconst ignore = pluginConfig.watch?.ignore\n\t\t\tif (!ignore?.length) return null\n\n\t\t\t// Vite concatenates `server.watch.ignored`, so only return the additions\n\t\t\treturn {\n\t\t\t\tserver: {\n\t\t\t\t\twatch: {\n\t\t\t\t\t\tignored: ignore,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t\tconfigResolved(config: ResolvedConfig) {\n\t\t\tconst root = pluginConfig.watch?.root ?? config.root\n\n\t\t\ttry {\n\t\t\t\twatchRoot = normaliseWatchPath(realpathSync.native(root))\n\t\t\t} catch {\n\t\t\t\twatchRoot = normaliseWatchPath(root)\n\t\t\t}\n\t\t},\n\t\ttransform(code, id) {\n\t\t\tif (id.startsWith('\\0') || id.includes('/node_modules/')) return null\n\t\t\t// cheap bail-out before running the regex/lightningcss on every module\n\t\t\tif (!code.includes('cxx')) return null\n\n\t\t\tconst cleanId = id.split('?')[0]\n\t\t\tif (!ACCEPTED_EXTENSIONS.has(path.extname(cleanId))) return null\n\n\t\t\tconst { template, tags, map } = collect(code, cleanId, pluginConfig)\n\t\t\tif (template === code) return null\n\n\t\t\tconst resolved = resolveWatchFile(cleanId)\n\n\t\t\tif (tags.size) {\n\t\t\t\tbuildContext.tagsByFile.set(resolved, tags)\n\t\t\t} else {\n\t\t\t\tbuildContext.tagsByFile.delete(resolved)\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcode: template,\n\t\t\t\tmap,\n\t\t\t}\n\t\t},\n\t\tconfigureServer(server: ViteDevServer) {\n\t\t\tlogger.info('Watching for changes...')\n\n\t\t\tserver.watcher\n\t\t\t\t.on('add', (p: string) => {\n\t\t\t\t\tconst file = watchedFile(p)\n\t\t\t\t\tif (file) queue.add(file, 'add')\n\t\t\t\t})\n\t\t\t\t.on('change', (p: string) => {\n\t\t\t\t\tconst file = watchedFile(p)\n\t\t\t\t\tif (file) queue.add(file, 'change')\n\t\t\t\t})\n\t\t\t\t.on('unlink', (p: string) => {\n\t\t\t\t\tconst file = watchedFile(p)\n\t\t\t\t\tif (file) queue.add(file, 'unlink')\n\t\t\t\t})\n\t\t},\n\t\tasync writeBundle() {\n\t\t\tawait writeTypes(flattenTags(buildContext.tagsByFile))\n\t\t},\n\t} satisfies Plugin\n}\n",
8
+ "export function capitalise(str: string) {\n\treturn str.charAt(0).toUpperCase() + str.slice(1)\n}\n\nexport function pascalise(str: string) {\n\treturn str\n\t\t.split(/[ _-]+/)\n\t\t.map(capitalise)\n\t\t.join('')\n}\n\nexport type Batcher<K, V> = {\n\t/**\n\t * Queue a value for a key. Queueing the same key again replaces the previous value.\n\t */\n\tadd: (key: K, value: V) => void\n\t/**\n\t * Run any waiting work now and resolve once it has finished.\n\t */\n\tflush: () => Promise<void>\n}\n\n/**\n * Group bursts of work and run them together. Every key queued within `wait` is\n * included once, and a key queued while a batch is running is handled in the next\n * batch instead of replacing the one in progress.\n */\nexport function createBatcher<K, V>(\n\twait: number,\n\tflush: (batch: Map<K, V>) => void | Promise<void>,\n): Batcher<K, V> {\n\t// work waiting to run, keyed so each key is only queued once\n\tconst pending = new Map<K, V>()\n\t// waits for a burst of changes to settle before running\n\tlet timer: ReturnType<typeof setTimeout> | null = null\n\t// the run in progress, so `flush` can wait for it and we never start a second\n\tlet current: Promise<void> | null = null\n\n\tfunction drain() {\n\t\t// a run is already going, so let it pick up the new work\n\t\tif (current) return current\n\n\t\tconst run = (async () => {\n\t\t\t// keep looping while there is work, including work added during a flush\n\t\t\twhile (pending.size > 0) {\n\t\t\t\t// snapshot the queued work\n\t\t\t\tconst batch = new Map(pending)\n\t\t\t\t// empty the queue right away, so anything added during flush waits for the next loop\n\t\t\t\tpending.clear()\n\n\t\t\t\tawait flush(batch)\n\t\t\t}\n\t\t})()\n\n\t\tcurrent = run\n\t\trun.then(\n\t\t\t() => {\n\t\t\t\tif (current === run) current = null\n\t\t\t},\n\t\t\t() => {\n\t\t\t\tif (current === run) current = null\n\t\t\t},\n\t\t)\n\n\t\treturn run\n\t}\n\n\tfunction schedule() {\n\t\t// cancel the previous wait, so the timer restarts from now\n\t\tif (timer) clearTimeout(timer)\n\n\t\t// run once the calls stop coming for `wait` ms\n\t\ttimer = setTimeout(() => {\n\t\t\t// the timer has fired, so drop the stale handle\n\t\t\ttimer = null\n\t\t\t// the flush callback handles its own errors, so just start the run\n\t\t\tvoid drain().catch(() => {})\n\t\t}, wait)\n\t}\n\n\treturn {\n\t\tadd(key, value) {\n\t\t\t// the newest value for this key wins\n\t\t\tpending.set(key, value)\n\t\t\tschedule()\n\t\t},\n\t\tflush() {\n\t\t\t// run now instead of waiting out the timer\n\t\t\tif (timer) {\n\t\t\t\tclearTimeout(timer)\n\t\t\t\ttimer = null\n\t\t\t}\n\n\t\t\treturn drain()\n\t\t},\n\t}\n}\n",
9
+ "import { realpathSync } from 'node:fs'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport { type Plugin, type ResolvedConfig, type ViteDevServer } from 'vite'\n\nimport type { PluginConfig } from '../types.js'\nimport {\n\tcollect,\n\tflushTypes,\n\tisENOENT,\n\ttakeRebuildError,\n\twriteTypesForFile,\n} from '../build.js'\nimport { GENERATED_DIR } from '../config.js'\nimport { Logger } from '../logger.js'\nimport { createBatcher } from '../utils.js'\n\nconst ACCEPTED_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx'])\n\ntype WatchEvent = 'add' | 'change' | 'unlink'\n\nfunction normaliseWatchPath(p: string) {\n\treturn p.replace(/\\\\/g, '/')\n}\n\n/** Tiny glob matcher for the ignore patterns we support (`**`, `*`, `?`). */\nfunction globToRegExp(glob: string) {\n\tconst pattern = glob\n\t\t.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n\t\t.replace(/\\*\\*(\\/|$)/g, '.*$1')\n\t\t.replace(/\\*/g, '[^/]*')\n\t\t.replace(/\\?/g, '[^/]')\n\n\treturn new RegExp(`^${pattern}$`)\n}\n\nexport default function cxx(pluginConfig: PluginConfig = {}) {\n\tconst logger = new Logger()\n\n\t// resolved from Vite's root in `configResolved`, not from `process.cwd()`\n\tlet watchRoot = normaliseWatchPath(process.cwd())\n\tconst ignored = (pluginConfig.watch?.ignore ?? []).map(globToRegExp)\n\n\tfunction resolveWatchFile(filePath: string) {\n\t\tconst absolutePath = path.resolve(watchRoot, filePath)\n\t\tconst parentPath = path.dirname(absolutePath)\n\n\t\ttry {\n\t\t\tconst resolvedParentPath = normaliseWatchPath(realpathSync.native(parentPath))\n\n\t\t\treturn normaliseWatchPath(\n\t\t\t\tpath.join(resolvedParentPath, path.basename(absolutePath)),\n\t\t\t)\n\t\t} catch {\n\t\t\treturn normaliseWatchPath(absolutePath)\n\t\t}\n\t}\n\n\t/** Returns the canonical, watchable path, or null when the file is irrelevant. */\n\tfunction watchedFile(filePath: string): string | null {\n\t\tconst resolvedPath = resolveWatchFile(filePath)\n\n\t\tif (\n\t\t\tresolvedPath.startsWith(`${watchRoot}/`) &&\n\t\t\t!resolvedPath.includes(`/${GENERATED_DIR}/`) &&\n\t\t\tACCEPTED_EXTENSIONS.has(path.extname(resolvedPath)) &&\n\t\t\t!ignored.some(regexp => regexp.test(resolvedPath))\n\t\t) {\n\t\t\treturn resolvedPath\n\t\t}\n\n\t\treturn null\n\t}\n\n\tasync function recordFile(file: string) {\n\t\tlet source: string\n\n\t\ttry {\n\t\t\tsource = await fs.readFile(file, 'utf-8')\n\t\t} catch (err) {\n\t\t\tif (!isENOENT(err)) throw err\n\t\t\tawait writeTypesForFile(file, new Map())\n\t\t\treturn\n\t\t}\n\n\t\tconst tags = source.includes('cxx')\n\t\t\t? collect(source, file, pluginConfig).tags\n\t\t\t: new Map()\n\t\tawait writeTypesForFile(file, tags)\n\t}\n\n\t/**\n\t * Seed the type surface from the source tree, so a fresh checkout has types before any file is\n\t * transformed or edited.\n\t */\n\tasync function scan(dir: string) {\n\t\tlet entries\n\n\t\ttry {\n\t\t\tentries = await fs.readdir(dir, { withFileTypes: true })\n\t\t} catch {\n\t\t\treturn\n\t\t}\n\n\t\tawait Promise.all(\n\t\t\tentries.map(async entry => {\n\t\t\t\tif (entry.name.startsWith('.') || entry.name === 'node_modules') return\n\n\t\t\t\tconst full = path.join(dir, entry.name)\n\n\t\t\t\tif (entry.isDirectory()) {\n\t\t\t\t\tawait scan(full)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (!ACCEPTED_EXTENSIONS.has(path.extname(entry.name))) return\n\n\t\t\t\tconst resolved = resolveWatchFile(full)\n\t\t\t\tif (ignored.some(regexp => regexp.test(resolved))) return\n\n\t\t\t\tawait recordFile(resolved)\n\t\t\t}),\n\t\t)\n\t}\n\n\t// collect change events that arrive close together and handle each file once.\n\t// The file path is the key, so changes to different files are all kept, and a\n\t// change that arrives during a rebuild runs after that rebuild finishes.\n\tconst queue = createBatcher<string, WatchEvent>(75, async batch => {\n\t\tfor (const [file, event] of batch) {\n\t\t\ttry {\n\t\t\t\tif (event === 'unlink') {\n\t\t\t\t\tawait writeTypesForFile(file, new Map())\n\t\t\t\t} else {\n\t\t\t\t\tawait recordFile(file)\n\t\t\t\t}\n\n\t\t\t\tlogger.info(`updated types from ${file}`)\n\t\t\t} catch (err) {\n\t\t\t\tlogger.error(`failed to update types from ${file}`, err)\n\t\t\t}\n\t\t}\n\t})\n\n\treturn {\n\t\tname: 'cxx',\n\t\tenforce: 'pre',\n\t\tconfigResolved(config: ResolvedConfig) {\n\t\t\tconst root = pluginConfig.watch?.root ?? config.root\n\n\t\t\ttry {\n\t\t\t\twatchRoot = normaliseWatchPath(realpathSync.native(root))\n\t\t\t} catch {\n\t\t\t\twatchRoot = normaliseWatchPath(root)\n\t\t\t}\n\t\t},\n\t\ttransform(code, id) {\n\t\t\tif (id.startsWith('\\0') || id.includes('/node_modules/')) return null\n\n\t\t\t// cheap bail-out before running the regex/lightningcss on every module\n\t\t\tif (!code.includes('cxx')) return null\n\n\t\t\tconst cleanId = id.split('?')[0]\n\n\t\t\tif (!ACCEPTED_EXTENSIONS.has(path.extname(cleanId))) return null\n\n\t\t\tconst { template, tags, map } = collect(code, cleanId, pluginConfig)\n\n\t\t\tif (template === code) return null\n\n\t\t\tvoid writeTypesForFile(resolveWatchFile(cleanId), tags)\n\n\t\t\treturn {\n\t\t\t\tcode: template,\n\t\t\t\tmap,\n\t\t\t}\n\t\t},\n\t\tconfigureServer(server: ViteDevServer) {\n\t\t\tlogger.info('Watching for changes...')\n\n\t\t\tserver.watcher\n\t\t\t\t.on('add', (p: string) => {\n\t\t\t\t\tconst file = watchedFile(p)\n\t\t\t\t\tif (file) queue.add(file, 'add')\n\t\t\t\t})\n\t\t\t\t.on('change', (p: string) => {\n\t\t\t\t\tconst file = watchedFile(p)\n\t\t\t\t\tif (file) queue.add(file, 'change')\n\t\t\t\t})\n\t\t\t\t.on('unlink', (p: string) => {\n\t\t\t\t\tconst file = watchedFile(p)\n\t\t\t\t\tif (file) queue.add(file, 'unlink')\n\t\t\t\t})\n\n\t\t\tvoid scan(watchRoot)\n\t\t\t\t.then(() => flushTypes())\n\t\t\t\t.then(() => {\n\t\t\t\t\tconst err = takeRebuildError()\n\t\t\t\t\tif (err) logger.error('failed to generate types', err)\n\t\t\t\t})\n\t\t\t\t.catch(err => logger.error('failed to scan for cxx templates', err))\n\t\t},\n\t\tasync writeBundle() {\n\t\t\tawait flushTypes()\n\t\t},\n\t} satisfies Plugin\n}\n",
10
10
  "const LEVELS = {\n\tdebug: 0,\n\tinfo: 1,\n\twarn: 2,\n\terror: 3,\n\tfatal: 4,\n} as const\n\nexport type LogLevel = keyof typeof LEVELS\n\nconst defaultLevel: LogLevel = process.env['NODE_ENV'] === 'production' ? 'error' : 'info'\n\ntype LogEntry = {\n\tts: number\n\tlevel: LogLevel\n\tmessage: string\n\terror?:\n\t\t| Error\n\t\t| {\n\t\t\t\tmessage: string\n\t\t\t\tstack?: string\n\t\t\t\tcause?: unknown\n\t\t }\n}\n\n/**\n * Log messages with different severity levels.\n */\nexport class Logger {\n\t#level: LogLevel\n\n\tconstructor(\n\t\tlevel?: LogLevel,\n\t\tpublic name?: string,\n\t) {\n\t\tthis.#level = level ?? defaultLevel\n\t}\n\n\t/**\n\t * Convert a value to an Error instance.\n\t */\n\tstatic toError(err: unknown) {\n\t\treturn err instanceof Error ? err : new Error(String(err), { cause: err })\n\t}\n\n\t/**\n\t * Stringify the error for logging.\n\t */\n\tstatic print(err: unknown) {\n\t\tif (err instanceof Error) {\n\t\t\treturn err.message + (err.stack ? `\\n${err.stack}` : '')\n\t\t}\n\n\t\t// for plain objects, attempt to stringify with indentation\n\t\t// for readability\n\t\tif (typeof err === 'object' && err !== null) {\n\t\t\ttry {\n\t\t\t\treturn JSON.stringify(err, null, 2)\n\t\t\t} catch {\n\t\t\t\t// if stringify fails (e.g. circular reference), fall back\n\t\t\t\t// to basic string conversion\n\t\t\t\treturn String(err)\n\t\t\t}\n\t\t}\n\n\t\treturn String(err)\n\t}\n\n\tset level(level: LogLevel) {\n\t\tthis.#level = level\n\t}\n\n\tget level() {\n\t\treturn this.#level\n\t}\n\n\t/**\n\t * Log a message with a specific level.\n\t */\n\tlog(level: LogLevel, message: string, error?: Error) {\n\t\tif (LEVELS[level] < LEVELS[this.level]) return\n\n\t\tconst entry: LogEntry = {\n\t\t\tts: Date.now(),\n\t\t\tlevel,\n\t\t\tmessage,\n\t\t}\n\n\t\tif (level === 'error' || level === 'fatal') {\n\t\t\tentry.error = error ? Logger.toError(error) : new Error(message)\n\t\t}\n\n\t\tconst line = `${this.name ? `[${this.name}]` : ''} [${entry.ts}] [${level.toUpperCase()}] ${message}`\n\t\tconst extra = entry.error ? `\\n${Logger.print(entry.error)}` : ''\n\n\t\tif (level === 'warn') {\n\t\t\tconsole.warn(line, extra)\n\t\t\treturn\n\t\t}\n\n\t\tif (level === 'error' || level === 'fatal') {\n\t\t\tconsole.error(line, extra)\n\t\t\treturn\n\t\t}\n\n\t\tconsole.log(line, extra)\n\t}\n\n\t/**\n\t * Log a debug message.\n\t */\n\tdebug(...messages: string[]) {\n\t\tthis.log('debug', messages.join(' '))\n\t}\n\n\t/**\n\t * Log an info message.\n\t */\n\tinfo(...messages: string[]) {\n\t\tthis.log('info', messages.join(' '))\n\t}\n\n\t/**\n\t * Log a warning message.\n\t */\n\twarn(...messages: string[]) {\n\t\tthis.log('warn', messages.join(' '))\n\t}\n\n\t/**\n\t * Log an error message.\n\t */\n\terror(message: string, error?: unknown) {\n\t\tthis.log('error', message, error === undefined ? undefined : Logger.toError(error))\n\t}\n\n\t/**\n\t * Log a fatal error message.\n\t */\n\tfatal(message: string, error?: unknown) {\n\t\tthis.log('fatal', message, error === undefined ? undefined : Logger.toError(error))\n\t}\n}\n"
11
11
  ],
12
- "mappings": "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",
13
- "debugId": "8BACD1ACA42BF59D64756E2164756E21",
12
+ "mappings": "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",
13
+ "debugId": "7C0503EFBFB9E21F64756E2164756E21",
14
14
  "names": []
15
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jk2908/cxx",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Utilities for generating styles in React 19",
5
5
  "keywords": [
6
6
  "css",
@@ -50,17 +50,17 @@
50
50
  "prepack": "npm run build",
51
51
  "test": "bunx vitest"
52
52
  },
53
- "dependencies": {
54
- "lightningcss": "^1.27.0",
55
- "magic-string": "^0.30.21"
56
- },
53
+ "dependencies": {
54
+ "lightningcss": "^1.27.0",
55
+ "magic-string": "^0.30.21"
56
+ },
57
57
  "devDependencies": {
58
58
  "@types/bun": "^1.3.13",
59
59
  "@types/node": "^24.0.1",
60
60
  "@types/react": "^19.2.17",
61
61
  "@types/react-dom": "^19.2.3",
62
62
  "@typescript/native-preview": "^7.0.0-dev.20260421.2",
63
- "next": "^16.3.7",
63
+ "next": "^16.3.7",
64
64
  "react": "^19.2.7",
65
65
  "react-dom": "^19.2.7",
66
66
  "typescript": "^5.5.4",
package/src/build.ts CHANGED
@@ -5,7 +5,7 @@ import path from 'node:path'
5
5
  import { transform } from 'lightningcss'
6
6
  import MagicString, { type SourceMap } from 'magic-string'
7
7
 
8
- import type { BuildContext, PluginConfig, Tags } from './types.js'
8
+ import type { PluginConfig, Tags } from './types.js'
9
9
  import { AUTOGEN_MSG, GENERATED_DIR, PKG_NAME } from './config.js'
10
10
  import { LruCache } from './lru.js'
11
11
  import { pascalise } from './utils.js'
@@ -13,6 +13,7 @@ import { pascalise } from './utils.js'
13
13
  // decode bytes to a string, then stringify into a safe literal. `${bytes}` only
14
14
  // works by accident for Buffer; a plain Uint8Array stringifies as `97,123,125`
15
15
  const decoder = new TextDecoder()
16
+ const encoder = new TextEncoder()
16
17
 
17
18
  /**
18
19
  * LRU cache of `collect` results by file id + last seen source + config so watcher and transform
@@ -27,14 +28,8 @@ const collectCache = new LruCache<{
27
28
  }>()
28
29
 
29
30
  /**
30
- * LRU cache for file content. Stores the last written content per file path so `maybeWrite`
31
- * can skip disk i/o when nothing has changed.
32
- */
33
- const fileCache = new LruCache<string>()
34
-
35
- /**
36
- * Serialises type file writes inside a single process so overlapping `writeTypes` calls can't
37
- * interleave or land out of order.
31
+ * Serialises type file writes inside a single process so overlapping writes can't interleave or
32
+ * land out of order.
38
33
  */
39
34
  let writeChain: Promise<void> = Promise.resolve()
40
35
 
@@ -46,14 +41,20 @@ export function isENOENT(err: unknown) {
46
41
  }
47
42
 
48
43
  /**
49
- * Write a file by first writing to a unique temporary file and then renaming it into place, so
50
- * concurrent writers (e.g. Turbopack loader workers) never observe a partial file.
44
+ * Write a file by writing to a unique temporary file and renaming it into place, so concurrent
45
+ * writers never observe a partial file. The temp file is cleaned up if the rename fails.
51
46
  */
52
47
  async function writeFileAtomic(filePath: string, content: string) {
53
48
  const tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`
54
49
 
55
50
  await fs.writeFile(tmp, content)
56
- await fs.rename(tmp, filePath)
51
+
52
+ try {
53
+ await fs.rename(tmp, filePath)
54
+ } catch (err) {
55
+ await fs.rm(tmp, { force: true })
56
+ throw err
57
+ }
57
58
  }
58
59
 
59
60
  /**
@@ -75,26 +76,17 @@ function enqueueWrite<T>(task: () => Promise<T>) {
75
76
  }
76
77
 
77
78
  /**
78
- * Write a file only if the content actually differs from what is already on disk.
79
+ * Write a file only if the content differs from what is already on disk, so watchers are not woken
80
+ * by a needless mtime change.
79
81
  */
80
82
  export async function maybeWrite(filePath: string, content: string) {
81
- const cached = fileCache.get(filePath)
82
- // fast path: already wrote this exact content in this process
83
- if (cached === content) return false
84
-
85
- // verify against disk on a cold start (or when the cache is stale) so we
86
- // don't needlessly bump the mtime and invalidate downstream watchers
87
83
  try {
88
- if ((await fs.readFile(filePath, 'utf-8')) === content) {
89
- fileCache.set(filePath, content)
90
- return false
91
- }
84
+ if ((await fs.readFile(filePath, 'utf-8')) === content) return false
92
85
  } catch (err) {
93
86
  if (!isENOENT(err)) throw err
94
87
  }
95
88
 
96
89
  await writeFileAtomic(filePath, content)
97
- fileCache.set(filePath, content)
98
90
 
99
91
  return true
100
92
  }
@@ -159,19 +151,27 @@ export function collect(source: string, id: string, config: PluginConfig) {
159
151
  // capture groups: export, three bindings, plain css, (quote), tag, tagged css
160
152
  const [, exp, varOne, varTwo, varThree, plainCss, , tag, taggedCss] = match
161
153
  const css = plainCss ?? taggedCss
162
-
163
- // compile the CSS to minified CSS Modules
164
- const { code, exports = {} } = transform({
165
- minify: true,
166
- cssModules: true,
167
- filename: id,
168
- code: new TextEncoder().encode(css),
169
- })
154
+ const matchIndex = match.index
155
+
156
+ // compile the CSS to minified CSS Modules, reporting the source line on failure
157
+ const { code, exports: classExports = {} } = (() => {
158
+ try {
159
+ return transform({
160
+ minify: true,
161
+ cssModules: true,
162
+ filename: id,
163
+ code: encoder.encode(css),
164
+ })
165
+ } catch (err) {
166
+ const line = source.slice(0, matchIndex).split('\n').length
167
+ throw new Error(`Failed to compile cxx CSS at ${id}:${line}`, { cause: err })
168
+ }
169
+ })()
170
170
 
171
171
  const href = hashCss(code)
172
172
  // map each class name to its generated (scoped) name
173
173
  const styles = Object.fromEntries(
174
- Object.entries(exports).map(([k, v]) => [k, v.name]),
174
+ Object.entries(classExports).map(([k, v]) => [k, v.name]),
175
175
  )
176
176
 
177
177
  if (tag) {
@@ -205,7 +205,7 @@ export function collect(source: string, id: string, config: PluginConfig) {
205
205
  }
206
206
 
207
207
  // swap the template for the generated declarations, keeping the export keyword
208
- magic.overwrite(match.index, match.index + match[0].length, statements.join('\n'))
208
+ magic.overwrite(matchIndex, matchIndex + match[0].length, statements.join('\n'))
209
209
  }
210
210
 
211
211
  const template = magic.toString()
@@ -243,10 +243,7 @@ async function writeRenderedTypes(tags: Tags) {
243
243
  const filePath = path.join(GENERATED_DIR, 'index.d.ts')
244
244
 
245
245
  if (!tags.size) {
246
- // `force` makes this a no-op when the file is already absent
247
246
  await fs.rm(filePath, { force: true })
248
- fileCache.delete(filePath)
249
-
250
247
  return
251
248
  }
252
249
 
@@ -254,15 +251,15 @@ async function writeRenderedTypes(tags: Tags) {
254
251
  await maybeWrite(filePath, renderTypes(tags))
255
252
  }
256
253
 
257
- /**
258
- * Write the generated declaration file for the current set of collected tags.
259
- */
260
- export function writeTypes(tags: Tags) {
261
- return enqueueWrite(() => writeRenderedTypes(tags))
262
- }
263
-
264
254
  const PARTIAL_DIR = path.join(GENERATED_DIR, 'tags')
265
255
 
256
+ // wait this long after the last file before rebuilding, so a burst of files
257
+ // only re-reads the partials once
258
+ const REBUILD_WAIT = 100
259
+
260
+ let rebuildTimer: ReturnType<typeof setTimeout> | null = null
261
+ let rebuildError: unknown = null
262
+
266
263
  function partialPath(filePath: string) {
267
264
  return path.join(
268
265
  PARTIAL_DIR,
@@ -271,8 +268,21 @@ function partialPath(filePath: string) {
271
268
  }
272
269
 
273
270
  /**
274
- * Read every per-file partial written by the Next loader workers and merge them into one tag map.
275
- * Partials whose source file no longer exists are pruned so deleted files drop out of the types.
271
+ * The partial directory changes whenever any process adds, removes or overwrites a partial, so
272
+ * its mtime tells us whether another writer moved while we were rebuilding.
273
+ */
274
+ async function partialDirMtime() {
275
+ try {
276
+ return (await fs.stat(PARTIAL_DIR)).mtimeMs
277
+ } catch (err) {
278
+ if (isENOENT(err)) return 0
279
+ throw err
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Read every per-file partial and merge them into one tag map. Partials whose source file no
285
+ * longer exists are pruned so deleted files drop out of the types.
276
286
  */
277
287
  async function readPartials() {
278
288
  let entries: string[]
@@ -315,8 +325,8 @@ async function readPartials() {
315
325
  }
316
326
 
317
327
  /**
318
- * Next-specific write path. Turbopack spreads loaders across worker processes, so each worker
319
- * persists its own per-file partial and rebuilds the shared declaration file from all of them.
328
+ * Persist one file's tags to its own partial, then rebuild the shared types soon. Rebuilding is
329
+ * debounced and serialised, so a large build does not re-read every partial per file.
320
330
  */
321
331
  export function writeTypesForFile(filePath: string, tags: Tags) {
322
332
  return enqueueWrite(async () => {
@@ -332,16 +342,61 @@ export function writeTypesForFile(filePath: string, tags: Tags) {
332
342
  JSON.stringify({ file: filePath, tags: Object.fromEntries(tags) }),
333
343
  )
334
344
  }
345
+ }).then(() => {
346
+ scheduleRebuild()
347
+ })
348
+ }
335
349
 
336
- await writeRenderedTypes(await readPartials())
350
+ function scheduleRebuild() {
351
+ if (rebuildTimer) clearTimeout(rebuildTimer)
352
+
353
+ rebuildTimer = setTimeout(() => {
354
+ rebuildTimer = null
355
+ flushTypes().catch(err => {
356
+ rebuildError = err
357
+ })
358
+ }, REBUILD_WAIT)
359
+ }
360
+
361
+ /**
362
+ * Rebuild `index.d.ts` from every partial now. If another writer touches a partial mid-rebuild,
363
+ * rebuild once more so the final write reflects every partial. Bounded to avoid spinning.
364
+ */
365
+ export function flushTypes() {
366
+ if (rebuildTimer) {
367
+ clearTimeout(rebuildTimer)
368
+ rebuildTimer = null
369
+ }
370
+
371
+ return enqueueWrite(async () => {
372
+ let before = await partialDirMtime()
373
+
374
+ for (let attempt = 0; attempt < 3; attempt++) {
375
+ await writeRenderedTypes(await readPartials())
376
+
377
+ const after = await partialDirMtime()
378
+ if (after === before) return
379
+
380
+ before = after
381
+ }
337
382
  })
338
383
  }
339
384
 
385
+ /**
386
+ * Return and clear any error from a background rebuild, so a caller can fail loudly.
387
+ */
388
+ export function takeRebuildError() {
389
+ const err = rebuildError
390
+ rebuildError = null
391
+ return err
392
+ }
393
+
340
394
  /**
341
395
  * Render collected tags as a module augmentation for the package entrypoint.
342
396
  */
343
397
  export function tagsToType(tags: Tags) {
344
398
  return [...tags]
399
+ .toSorted(([a], [b]) => a.localeCompare(b))
345
400
  .map(([tag, classes]) => {
346
401
  const union =
347
402
  classes.length > 0
@@ -355,19 +410,3 @@ export function tagsToType(tags: Tags) {
355
410
  })
356
411
  .join('\n')
357
412
  }
358
-
359
- /**
360
- * Merge per-file tag maps into one application-wide tag map.
361
- */
362
- export function flattenTags(tagsByFile: BuildContext['tagsByFile']) {
363
- const tags = new Map<string, string[]>()
364
-
365
- for (const fileTags of tagsByFile.values()) {
366
- for (const [name, classes] of fileTags) {
367
- if (tags.has(name)) throw new DuplicateTagError(name)
368
- tags.set(name, classes)
369
- }
370
- }
371
-
372
- return tags
373
- }
@@ -5,7 +5,14 @@ const loader = function (source) {
5
5
  const { pluginConfig } = this.getOptions()
6
6
  const logger = new Logger()
7
7
 
8
- const { template } = processFile(source, this.resourcePath, pluginConfig, logger)
8
+ const { template, map } = processFile(source, this.resourcePath, pluginConfig, logger)
9
+
10
+ // hand the transform's source map back to webpack/turbopack when they support it
11
+ if (map && typeof this.callback === 'function') {
12
+ this.callback(null, template, map)
13
+
14
+ return
15
+ }
9
16
 
10
17
  return template
11
18
  }
package/src/next/index.ts CHANGED
@@ -7,7 +7,7 @@ import { fileURLToPath } from 'node:url'
7
7
 
8
8
  import type { Logger } from '../logger.js'
9
9
  import type { PluginConfig } from '../types.js'
10
- import { collect, isENOENT, writeTypesForFile } from '../build.js'
10
+ import { collect, isENOENT, takeRebuildError, writeTypesForFile } from '../build.js'
11
11
  import { GENERATED_DIR } from '../config.js'
12
12
 
13
13
  const nextDir = path.dirname(fileURLToPath(import.meta.url))
@@ -56,7 +56,9 @@ async function onEvent(event: Event['type'], filePath: string) {
56
56
  return
57
57
  }
58
58
 
59
- const { tags } = collect(source, filePath, pluginConfig)
59
+ const tags = source.includes('cxx')
60
+ ? collect(source, filePath, pluginConfig).tags
61
+ : new Map()
60
62
 
61
63
  await writeTypesForFile(filePath, tags)
62
64
  logger.info(`updated types from ${filePath}`)
@@ -150,6 +152,15 @@ export function processFile(
150
152
  ) {
151
153
  const resolvedFilePath = path.resolve(filePath)
152
154
 
155
+ // skip files that cannot contain a template, including node_modules
156
+ if (!source.includes('cxx') || /[\\/]node_modules[\\/]/.test(resolvedFilePath)) {
157
+ return { template: source, tags: new Map(), map: null }
158
+ }
159
+
160
+ // surface a duplicate-tag error from a previous background rebuild
161
+ const priorError = takeRebuildError()
162
+ if (priorError) throw priorError
163
+
153
164
  if (process.env['NODE_ENV'] !== 'production') {
154
165
  watchedFiles.set(resolvedFilePath, { pluginConfig, logger })
155
166
  ensureWatcher(pluginConfig, logger)
@@ -157,9 +168,7 @@ export function processFile(
157
168
 
158
169
  const result = collect(source, resolvedFilePath, pluginConfig)
159
170
 
160
- void writeTypesForFile(resolvedFilePath, result.tags).catch(err => {
161
- logger.error(`failed to write types for ${resolvedFilePath}`, err)
162
- })
171
+ void writeTypesForFile(resolvedFilePath, result.tags)
163
172
 
164
173
  return result
165
174
  }
package/src/types.ts CHANGED
@@ -9,17 +9,13 @@ export type Tags = Map<string, string[]>
9
9
  export type PluginConfig = {
10
10
  typeSuffix?: string | false
11
11
  watch?: {
12
- // root used for type-regeneration watching; defaults to the Vite root / Next turbopack root
12
+ /** Root used for type-regeneration watching. Defaults to the Vite root or Next Turbopack root. */
13
13
  root?: string
14
- // extra globs to ignore on top of the defaults
14
+ /** Extra globs to ignore on top of the defaults. */
15
15
  ignore?: string[]
16
16
  }
17
17
  }
18
18
 
19
- export type BuildContext = {
20
- tagsByFile: Map<string, Tags>
21
- }
22
-
23
19
  export type Tag = <ClassName extends string = string>(
24
20
  name: string,
25
21
  ) => (_: TemplateStringsArray) => Result<ClassName>
package/src/utils.ts CHANGED
@@ -33,15 +33,14 @@ export function createBatcher<K, V>(
33
33
  const pending = new Map<K, V>()
34
34
  // waits for a burst of changes to settle before running
35
35
  let timer: ReturnType<typeof setTimeout> | null = null
36
- // true while a batch is running, so we never start a second one
37
- let running = false
36
+ // the run in progress, so `flush` can wait for it and we never start a second
37
+ let current: Promise<void> | null = null
38
38
 
39
- async function drain() {
39
+ function drain() {
40
40
  // a run is already going, so let it pick up the new work
41
- if (running) return
42
- running = true
41
+ if (current) return current
43
42
 
44
- try {
43
+ const run = (async () => {
45
44
  // keep looping while there is work, including work added during a flush
46
45
  while (pending.size > 0) {
47
46
  // snapshot the queued work
@@ -51,10 +50,19 @@ export function createBatcher<K, V>(
51
50
 
52
51
  await flush(batch)
53
52
  }
54
- } finally {
55
- // always allow the next run, even if flush threw
56
- running = false
57
- }
53
+ })()
54
+
55
+ current = run
56
+ run.then(
57
+ () => {
58
+ if (current === run) current = null
59
+ },
60
+ () => {
61
+ if (current === run) current = null
62
+ },
63
+ )
64
+
65
+ return run
58
66
  }
59
67
 
60
68
  function schedule() {
@@ -65,8 +73,8 @@ export function createBatcher<K, V>(
65
73
  timer = setTimeout(() => {
66
74
  // the timer has fired, so drop the stale handle
67
75
  timer = null
68
- // start the run without waiting for it (this is a timer callback)
69
- void drain()
76
+ // the flush callback handles its own errors, so just start the run
77
+ void drain().catch(() => {})
70
78
  }, wait)
71
79
  }
72
80