@yak/react 10.0.0-rc.1 → 10.0.0-rc.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.
- package/README.md +3 -3
- package/dist/context/index.d.ts +2 -2
- package/dist/context/index.js.map +1 -1
- package/dist/cross-file-resolver/index.js +19 -0
- package/dist/cross-file-resolver/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/isolated-source-eval/index.js +2 -2
- package/dist/isolated-source-eval/index.js.map +1 -1
- package/dist/isolated-source-eval/worker.js +4 -1
- package/dist/isolated-source-eval/worker.js.map +1 -1
- package/dist/loaders/turbo-loader.d.ts +8 -0
- package/dist/loaders/turbo-loader.js +23 -4
- package/dist/loaders/turbo-loader.js.map +1 -1
- package/dist/loaders/vite-plugin.d.ts +2 -4
- package/dist/loaders/vite-plugin.js +52 -31
- package/dist/loaders/vite-plugin.js.map +1 -1
- package/dist/loaders/webpack-loader.js +19 -0
- package/dist/loaders/webpack-loader.js.map +1 -1
- package/dist/rsbuild/index.js +2 -1
- package/dist/rsbuild/index.js.map +1 -1
- package/dist/withYak/index.js +1 -1
- package/dist/withYak/index.js.map +1 -1
- package/loaders/turbo-loader.ts +21 -4
- package/package.json +4 -4
- package/rsbuild/index.ts +1 -1
- package/runtime/context/index.tsx +3 -3
- package/withYak/allow-global-css.ts +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|

|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@yak/react)
|
|
6
|
-
[](https://github.com/
|
|
6
|
+
[](https://github.com/galaxuslabs/yak/blob/main/LICENSE)
|
|
7
7
|
|
|
8
8
|
**@yak/react** is a build-time CSS-in-JS library powered by a Rust SWC plugin. Write styled-components syntax, get zero-runtime CSS extraction and full React Server Components compatibility.
|
|
9
9
|
|
|
@@ -108,8 +108,8 @@ const nextConfig = {
|
|
|
108
108
|
|
|
109
109
|
## License
|
|
110
110
|
|
|
111
|
-
**@yak/react** is licensed under the [MIT License](https://github.com/
|
|
111
|
+
**@yak/react** is licensed under the [MIT License](https://github.com/galaxuslabs/yak/blob/main/LICENSE).
|
|
112
112
|
|
|
113
113
|
## Contributing
|
|
114
114
|
|
|
115
|
-
Contributions are welcome! The [contributing guide](https://github.com/
|
|
115
|
+
Contributions are welcome! The [contributing guide](https://github.com/galaxuslabs/yak/blob/main/CONTRIBUTION.md) helps you get started.
|
package/dist/context/index.d.ts
CHANGED
|
@@ -4,13 +4,13 @@ export interface YakTheme {}
|
|
|
4
4
|
/**
|
|
5
5
|
* Returns the current yak theme context
|
|
6
6
|
*
|
|
7
|
-
* @see https://github.com/
|
|
7
|
+
* @see https://github.com/galaxuslabs/yak/blob/main/packages/yak-react/runtime/context/README.md
|
|
8
8
|
*/
|
|
9
9
|
export declare const useTheme: () => YakTheme;
|
|
10
10
|
/**
|
|
11
11
|
* Yak theme context provider
|
|
12
12
|
*
|
|
13
|
-
* @see https://github.com/
|
|
13
|
+
* @see https://github.com/galaxuslabs/yak/blob/main/packages/yak-react/runtime/context/README.md
|
|
14
14
|
*/
|
|
15
15
|
export declare const YakThemeProvider: ({ children, theme }: {
|
|
16
16
|
children: ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../runtime/context/index.tsx"],"sourcesContent":["\"use client\";\n//\n// This file is the client component (browser & ssr) version of index.server.tsx\n//\nimport React, { ReactNode, createContext, use } from \"react\";\n\nexport interface YakTheme {}\n\n/**\n * The yak theme context\n * @see https://github.com/
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../runtime/context/index.tsx"],"sourcesContent":["\"use client\";\n//\n// This file is the client component (browser & ssr) version of index.server.tsx\n//\nimport React, { ReactNode, createContext, use } from \"react\";\n\nexport interface YakTheme {}\n\n/**\n * The yak theme context\n * @see https://github.com/galaxuslabs/yak/blob/main/packages/yak-react/runtime/context/README.md\n */\nconst YakContext = createContext<YakTheme>({});\n\n/**\n * Returns the current yak theme context\n *\n * @see https://github.com/galaxuslabs/yak/blob/main/packages/yak-react/runtime/context/README.md\n */\nexport const useTheme = (): YakTheme => {\n const context = use(YakContext);\n return context instanceof Promise ? use(context) : context;\n};\n\n/**\n * Yak theme context provider\n *\n * @see https://github.com/galaxuslabs/yak/blob/main/packages/yak-react/runtime/context/README.md\n */\nexport const YakThemeProvider = ({\n children,\n theme = {},\n}: {\n children: ReactNode;\n theme?: YakTheme;\n}) => <YakContext.Provider value={theme}>{children}</YakContext.Provider>;\n"],"mappings":";;;;;AAYA,MAAM,aAAa,cAAwB,CAAC,CAAC;AAO7C,MAAa,iBAA2B;CACtC,MAAM,UAAU,IAAI,UAAU;CAC9B,OAAO,mBAAmB,UAAU,IAAI,OAAO,IAAI;AACrD;AAOA,MAAa,oBAAoB,EAC/B,UACA,QAAQ,CAAC,QAIL,oCAAC,WAAW,UAAZ,EAAqB,OAAO,MAAsC,GAA9B,QAA8B"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { posix, win32 } from "node:path";
|
|
1
2
|
import { parse } from "@babel/parser";
|
|
2
3
|
|
|
3
4
|
//#region ../yak-internals/cross-file-resolver/parseModule.ts
|
|
@@ -90,6 +91,23 @@ function objectToModuleExport(object) {
|
|
|
90
91
|
}));
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region ../yak-internals/cross-file-resolver/rewriteRelativeCssUrls.ts
|
|
96
|
+
function rewriteRelativeCssUrls(css, source, consumer) {
|
|
97
|
+
if (!css.includes("url(")) return css;
|
|
98
|
+
const path = /^[a-z]:[\\/]/i.test(consumer) ? win32 : posix;
|
|
99
|
+
const prefix = path.relative(path.dirname(consumer), path.dirname(source.split("?")[0]));
|
|
100
|
+
if (path.isAbsolute(prefix)) return css;
|
|
101
|
+
return css.replace(cssUrlRegex, (match, double, single, bare) => {
|
|
102
|
+
const url = double ?? single ?? bare ?? "";
|
|
103
|
+
if (!url.startsWith("./") && !url.startsWith("../")) return match;
|
|
104
|
+
const quote = double !== void 0 ? "\"" : single !== void 0 ? "'" : "";
|
|
105
|
+
const rewritten = path.join(prefix, url).replace(/\\/g, "/");
|
|
106
|
+
return `url(${quote}${rewritten.startsWith("../") ? "" : "./"}${rewritten}${quote})`;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const cssUrlRegex = /url\(\s*(?:"([^"*]*)"|'([^'*]*)'|([^'"()\s*]*))\s*\)/g;
|
|
110
|
+
|
|
93
111
|
//#endregion
|
|
94
112
|
//#region ../yak-internals/cross-file-resolver/Errors.ts
|
|
95
113
|
var CauseError = class CauseError extends Error {
|
|
@@ -154,6 +172,7 @@ async function uncachedResolveCrossFileConstant(context, filePath, css) {
|
|
|
154
172
|
}
|
|
155
173
|
replacement = resolved.type === "styled-component" ? resolved.value : resolved.value + (["}", ";"].includes(String(resolved.value).trimEnd().slice(-1)) ? "" : semicolon);
|
|
156
174
|
}
|
|
175
|
+
if (resolved.type !== "unresolved-tag" && resolved.source !== filePath) replacement = rewriteRelativeCssUrls(String(replacement), resolved.source, filePath);
|
|
157
176
|
result = result.slice(0, position) + String(replacement) + result.slice(position + size);
|
|
158
177
|
}
|
|
159
178
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../yak-internals/cross-file-resolver/parseModule.ts","../../../yak-internals/cross-file-resolver/Errors.ts","../../../yak-internals/cross-file-resolver/resolveCrossFileConstant.ts","../../../yak-internals/packageNames.ts","../../../yak-internals/loaders/lib/parseExports.ts"],"sourcesContent":["import { type Cache } from \"./types.js\";\n\nexport async function parseModule(\n context: ParseContext,\n modulePath: string,\n): Promise<ParsedModule> {\n try {\n const isYak =\n modulePath.endsWith(\".yak.ts\") ||\n modulePath.endsWith(\".yak.tsx\") ||\n modulePath.endsWith(\".yak.js\") ||\n modulePath.endsWith(\".yak.jsx\");\n\n // handle yak file by evaluating and mapping exported value to the\n // `ModuleExport` format. This operation is not cached to always get a fresh\n // value from those modules\n if (isYak && context.evaluateYakModule) {\n const yakModule = await context.evaluateYakModule(modulePath);\n const yakExports = objectToModuleExport(yakModule);\n\n return {\n type: \"yak\",\n exports: { importYak: false, named: yakExports, all: [] },\n path: modulePath,\n };\n }\n\n if (context.cache?.parse === undefined) {\n return await uncachedParseModule(context, modulePath);\n }\n\n const cached = context.cache.parse.get(modulePath);\n if (cached === undefined) {\n // We cache the parsed file to avoid re-parsing it.\n // It's ok, that initial parallel requests to the same file will parse it multiple times.\n // This avoid deadlocks do to the fact that we load multiple modules in the chain for cross file references.\n const parsedModule = await uncachedParseModule(context, modulePath);\n\n context.cache.parse.set(modulePath, parsedModule);\n if (context.cache.parse.addDependency) {\n context.cache.parse.addDependency(modulePath, modulePath);\n }\n return parsedModule;\n }\n\n return cached;\n } catch (error) {\n const causeMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`Error parsing file \"${modulePath}\"\\n Caused by: ${causeMessage}`);\n }\n}\n\nexport async function uncachedParseModule(\n context: ParseContext,\n modulePath: string,\n): Promise<ParsedModule> {\n const exports = await context.extractExports(modulePath);\n\n // early exit if no yak import was found\n if (!exports.importYak) {\n return {\n type: \"regular\",\n path: modulePath,\n exports,\n };\n }\n\n const transformed = await context.getTransformed(modulePath);\n const mixins = parseMixins(transformed.code);\n const styledComponents = parseStyledComponents(transformed.code);\n\n return {\n type: \"regular\",\n path: modulePath,\n js: transformed,\n exports,\n styledComponents,\n mixins,\n };\n}\n\nfunction parseMixins(sourceContents: string): Record<string, Mixin> {\n // Mixins are always in the following format:\n // /*YAK EXPORTED MIXIN:fancy:aspectRatio:16:9\n // css\n // */\n const mixinParts = sourceContents.split(\"/*YAK EXPORTED MIXIN:\");\n let mixins: Record<string, { type: \"mixin\"; value: string; nameParts: string[] }> = {};\n\n for (let i = 1; i < mixinParts.length; i++) {\n const [comment] = mixinParts[i].split(\"*/\", 1);\n const position = comment.indexOf(\"\\n\");\n const name = comment.slice(0, position);\n const value = comment.slice(position + 1);\n mixins[name] = {\n type: \"mixin\",\n value,\n nameParts: name.split(\":\").map((part) => decodeURIComponent(part)),\n };\n }\n return mixins;\n}\n\nfunction parseStyledComponents(sourceContents: string): Record<string, StyledComponent> {\n // cross-file Styled Components are always in the following format:\n // /*YAK EXPORTED STYLED:ComponentName:ClassName*/\n const styledParts = sourceContents.split(\"/*YAK EXPORTED STYLED:\");\n let styledComponents: Record<string, StyledComponent> = {};\n\n for (let i = 1; i < styledParts.length; i++) {\n const [comment] = styledParts[i].split(\"*/\", 1);\n const [componentName, className] = comment.split(\":\");\n styledComponents[componentName] = {\n type: \"styled-component\",\n nameParts: componentName.split(\".\"),\n value: `.${className}`,\n };\n }\n\n return styledComponents;\n}\n\nfunction objectToModuleExport(object: object) {\n return Object.fromEntries(\n Object.entries(object).map(([key, value]): [string, ModuleExport] => {\n if (typeof value === \"string\" || typeof value === \"number\") {\n return [key, { type: \"constant\" as const, value }];\n } else if (value && (typeof value === \"object\" || Array.isArray(value))) {\n return [key, { type: \"record\" as const, value: objectToModuleExport(value) }];\n } else {\n return [key, { type: \"unsupported\" as const, hint: String(value) }];\n }\n }),\n );\n}\n\nexport type ParseContext = {\n cache?: { parse?: Cache<ParsedModule> };\n evaluateYakModule?: (\n modulePath: string,\n ) => Promise<Record<string, unknown>> | Record<string, unknown>;\n extractExports: (modulePath: string) => Promise<ModuleExports> | ModuleExports;\n getTransformed: (\n modulePath: string,\n ) => Promise<{ code: string; map?: string }> | { code: string; map?: string };\n};\n\nexport type ModuleExports = {\n importYak: boolean;\n named: Record<string, ModuleExport>;\n all: string[];\n};\n\nexport type ConstantExport = { type: \"constant\"; value: string | number };\nexport type RecordExport = {\n type: \"record\";\n value: Record<string, ModuleExport>;\n};\nexport type UnsupportedExport = {\n type: \"unsupported\";\n hint?: string;\n /**\n * Source location of the offending expression. Populated by the parser\n * when it has access to source text; left undefined for runtime-evaluated\n * yak modules. The error formatter is responsible for any rendering.\n */\n source?: UnsupportedExportSource;\n};\n\nexport type UnsupportedExportSource = {\n /** 1-based line number, 0-based column — same convention as Babel `loc`. */\n start: { line: number; column: number };\n end: { line: number; column: number };\n /** The text of `start.line` from the original source — kept so the\n * error formatter can render a snippet without re-reading the file. */\n lineText: string;\n};\nexport type ReExport = { type: \"re-export\"; name: string; from: string };\nexport type NamespaceReExport = { type: \"namespace-re-export\"; from: string };\nexport type TagTemplateExport = { type: \"tag-template\" };\n\nexport type ModuleExport =\n | ConstantExport\n | TagTemplateExport\n | RecordExport\n | UnsupportedExport\n | ReExport\n | NamespaceReExport;\n\nexport type ParsedModule = {\n path: string;\n exports: ModuleExports;\n} & (\n | {\n type: \"regular\";\n js?: { code: string; map?: string };\n styledComponents?: Record<string, StyledComponent>;\n mixins?: Record<string, Mixin>;\n }\n | {\n type: \"yak\";\n }\n);\n\nexport type StyledComponent = {\n type: \"styled-component\";\n value: string;\n nameParts: string[];\n};\n\nexport type Mixin = { type: \"mixin\"; value: string; nameParts: string[] };\n","export class CauseError extends Error {\n circular?: boolean;\n constructor(message: string, options?: { cause?: unknown }) {\n super(\n `${message}${options?.cause ? `\\n Caused by: ${typeof options.cause === \"object\" && options.cause !== null && \"message\" in options.cause ? options.cause.message : String(options.cause)}` : \"\"}`,\n );\n\n if (options?.cause instanceof CauseError && options.cause.circular) {\n this.circular = true;\n }\n }\n}\n\nexport class ResolveError extends CauseError {}\n\n/**\n * Thrown for `unsupported` exports. Subclassed so the surrounding\n * `resolveModuleExport` catch can recognise that the error already\n * carries its own \"Unable to resolve … in module …\" wrapper and\n * doesn't need another one stacked on top.\n */\nexport class UnsupportedExportError extends ResolveError {}\n\nexport class CircularDependencyError extends CauseError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.circular = true;\n }\n}\n","import type {\n ConstantExport,\n ModuleExport,\n ParsedModule,\n RecordExport,\n UnsupportedExportSource,\n} from \"./parseModule.js\";\nimport { Cache } from \"./types.js\";\nimport {\n CauseError,\n CircularDependencyError,\n ResolveError,\n UnsupportedExportError,\n} from \"./Errors.js\";\n\nconst yakCssImportRegex =\n // Make mixin and selector non optional once we dropped support for the babel plugin\n /--yak-css-import:\\s*url\\(\"([^\"]+)\",?(|mixin|selector)\\)(;?)/g;\n\n/**\n * Resolves cross-file selectors in css files\n *\n * e.g.:\n * theme.ts:\n * ```ts\n * export const colors = {\n * primary: \"#ff0000\",\n * secondary: \"#00ff00\",\n * };\n * ```\n *\n * styles.ts:\n * ```ts\n * import { colors } from \"./theme\";\n * export const button = css`\n * background-color: ${colors.primary};\n * `;\n */\nexport async function resolveCrossFileConstant(\n context: ResolveContext,\n filePath: string,\n css: string,\n): Promise<{ resolved: string; dependencies: string[] }> {\n const resolveCrossFileConstant = context.cache?.resolveCrossFileConstant;\n if (resolveCrossFileConstant === undefined) {\n return uncachedResolveCrossFileConstant(context, filePath, css);\n }\n\n const cacheKey = await sha1(filePath + \":\" + css);\n\n const cached = resolveCrossFileConstant.get(cacheKey);\n\n if (cached === undefined) {\n const resolvedCrossFilConstantPromise = uncachedResolveCrossFileConstant(\n context,\n filePath,\n css,\n );\n resolveCrossFileConstant.set(cacheKey, resolvedCrossFilConstantPromise);\n\n if (resolveCrossFileConstant.addDependency) {\n resolveCrossFileConstant.addDependency(cacheKey, filePath);\n resolvedCrossFilConstantPromise.then((value) => {\n for (const dep of value.dependencies) {\n resolveCrossFileConstant!.addDependency!(cacheKey, dep);\n }\n });\n }\n\n return resolvedCrossFilConstantPromise;\n }\n\n return cached;\n}\n\nexport async function uncachedResolveCrossFileConstant(\n context: ResolveContext,\n filePath: string,\n css: string,\n): Promise<{ resolved: string; dependencies: string[] }> {\n const yakImports = await parseYakCssImport(context, filePath, css);\n\n if (yakImports.length === 0) {\n return { resolved: css, dependencies: [] };\n }\n\n try {\n const dependencies = new Set<string>();\n\n const resolvedValues = await Promise.all(\n yakImports.map(async ({ moduleSpecifier, specifier }) => {\n const { resolved: resolvedModule } = await resolveModule(context, moduleSpecifier);\n\n const resolvedValue = await resolveModuleSpecifierRecursively(\n context,\n resolvedModule,\n specifier,\n );\n\n for (const dependency of resolvedValue.from) {\n dependencies.add(dependency);\n }\n\n return resolvedValue;\n }),\n );\n\n // Replace the imports with the resolved values\n let result = css;\n for (let i = yakImports.length - 1; i >= 0; i--) {\n const { position, size, importKind, specifier, semicolon } = yakImports[i];\n const resolved = resolvedValues[i];\n\n let replacement: string;\n\n if (resolved.type === \"unresolved-tag\") {\n // tag that could not be resolved to styled-components or mixins are\n // interpolated to produce valid CSS with minimal impact (since we don't\n // know what the value should actually be). For mixins (CSS rules) we\n // interpolate an empty string, and for selectors we interpolate\n // \"undefined\" (a selector that would match nothing)\n replacement = importKind === \"mixin\" ? \"\" : \"undefined\";\n } else {\n if (importKind === \"selector\") {\n if (resolved.type !== \"styled-component\" && resolved.type !== \"constant\") {\n throw new Error(\n `Found \"${\n resolved.type\n }\" but expected a selector - did you forget a semicolon after \"${specifier.join(\n \".\",\n )}\"?`,\n );\n }\n }\n\n replacement =\n resolved.type === \"styled-component\"\n ? resolved.value\n : resolved.value +\n // resolved.value can be of two different types:\n // - mixin:\n // ${mixinName};\n // - constant:\n // color: ${value};\n // For mixins the semicolon is already included in the value\n // but for constants it has to be added manually\n ([\"}\", \";\"].includes(String(resolved.value).trimEnd().slice(-1)) ? \"\" : semicolon);\n }\n\n result = result.slice(0, position) + String(replacement) + result.slice(position + size);\n }\n\n return { resolved: result, dependencies: Array.from(dependencies) };\n } catch (error) {\n throw new CauseError(`Error while resolving cross-file selectors in file \"${filePath}\"`, {\n cause: error,\n });\n }\n}\n\n/**\n * Search for --yak-css-import: url(\"path/to/module\") in the css\n */\nasync function parseYakCssImport(\n context: ResolveContext,\n filePath: string,\n css: string,\n): Promise<YakCssImport[]> {\n const yakImports: YakCssImport[] = [];\n\n for (const match of css.matchAll(yakCssImportRegex)) {\n const [fullMatch, encodedArguments, importKind, semicolon] = match;\n const [moduleSpecifier, ...specifier] = encodedArguments\n .split(\":\")\n .map((entry) => decodeURIComponent(entry));\n\n yakImports.push({\n encodedArguments,\n moduleSpecifier: await context.resolve(moduleSpecifier, filePath),\n specifier,\n importKind: importKind as YakImportKind,\n semicolon,\n position: match.index,\n size: fullMatch.length,\n });\n }\n\n return yakImports;\n}\n\nasync function resolveModule(context: ResolveContext, filePath: string) {\n if (context.cache?.resolve === undefined) {\n return uncachedResolveModule(context, filePath);\n }\n\n const cached = context.cache.resolve.get(filePath);\n if (cached === undefined) {\n const resolvedPromise = uncachedResolveModule(context, filePath);\n context.cache.resolve.set(filePath, resolvedPromise);\n\n if (context.cache.resolve.addDependency) {\n context.cache.resolve.addDependency(filePath, filePath);\n resolvedPromise.then((value) => {\n for (const dep of value.dependencies) {\n context.cache!.resolve!.addDependency!(filePath, dep);\n }\n });\n }\n\n return resolvedPromise;\n }\n\n return cached;\n}\n\nasync function uncachedResolveModule(\n context: ResolveContext,\n filePath: string,\n): Promise<{ resolved: ResolvedModule; dependencies: string[] }> {\n const parsedModule = await context.parse(filePath);\n\n const exports = parsedModule.exports as ResolvedExports;\n\n if (parsedModule.type !== \"regular\") {\n return {\n resolved: {\n path: parsedModule.path,\n exports,\n },\n dependencies: [],\n };\n }\n\n const dependencies = new Set<string>();\n\n // Reconcile styled-component \"name\" structure with export structure\n if (parsedModule.styledComponents) {\n Object.values(parsedModule.styledComponents).map((styledComponent) => {\n if (styledComponent.nameParts.length === 1) {\n exports.named[styledComponent.nameParts[0]] = {\n type: \"styled-component\",\n className: styledComponent.value,\n };\n } else {\n let exportEntry = exports.named[styledComponent.nameParts[0]];\n\n if (!exportEntry) {\n exportEntry = { type: \"record\", value: {} };\n exports.named[styledComponent.nameParts[0]] = exportEntry;\n } else if (exportEntry.type !== \"record\") {\n throw new CauseError(`Error parsing file \"${parsedModule.path}\"`, {\n cause: `\"${styledComponent.nameParts[0]}\" is not a record`,\n });\n }\n\n let current = exportEntry.value;\n for (let i = 1; i < styledComponent.nameParts.length - 1; i++) {\n let next = current[styledComponent.nameParts[i]];\n if (!next) {\n next = { type: \"record\", value: {} };\n current[styledComponent.nameParts[i]] = next;\n } else if (next.type !== \"record\") {\n throw new CauseError(`Error parsing file \"${parsedModule.path}\"`, {\n cause: `\"${styledComponent.nameParts.slice(0, i + 1).join(\".\")}\" is not a record`,\n });\n }\n current = next.value;\n }\n current[styledComponent.nameParts[styledComponent.nameParts.length - 1]] = {\n type: \"styled-component\",\n className: styledComponent.value,\n };\n }\n });\n }\n\n // Recursively resolve cross-file constants in mixins\n // e.g. cross file mixins inside a cross file mixin\n // or a cross file selector inside a cross file mixin\n if (parsedModule.mixins) {\n await Promise.all(\n Object.values(parsedModule.mixins).map(async (mixin) => {\n const { resolved, dependencies: deps } = await resolveCrossFileConstant(\n context,\n parsedModule.path,\n mixin.value,\n );\n\n for (const dep of deps) {\n dependencies.add(dep);\n }\n\n if (mixin.nameParts.length === 1) {\n exports.named[mixin.nameParts[0]] = {\n type: \"mixin\",\n value: resolved,\n };\n } else {\n let exportEntry = exports.named[mixin.nameParts[0]];\n\n if (!exportEntry) {\n exportEntry = { type: \"record\", value: {} };\n exports.named[mixin.nameParts[0]] = exportEntry;\n } else if (exportEntry.type !== \"record\") {\n throw new CauseError(`Error parsing file \"${parsedModule.path}\"`, {\n cause: `\"${mixin.nameParts[0]}\" is not a record`,\n });\n }\n\n let current = exportEntry.value;\n for (let i = 1; i < mixin.nameParts.length - 1; i++) {\n let next = current[mixin.nameParts[i]];\n if (!next) {\n next = { type: \"record\", value: {} };\n current[mixin.nameParts[i]] = next;\n } else if (next.type !== \"record\") {\n throw new CauseError(`Error parsing file \"${parsedModule.path}\"`, {\n cause: `\"${mixin.nameParts.slice(0, i + 1).join(\".\")}\" is not a record`,\n });\n }\n current = next.value;\n }\n current[mixin.nameParts[mixin.nameParts.length - 1]] = {\n type: \"mixin\",\n value: resolved,\n };\n }\n }),\n );\n }\n return {\n resolved: {\n path: parsedModule.path,\n exports,\n },\n dependencies: Array.from(dependencies),\n };\n}\n\nasync function resolveModuleSpecifierRecursively(\n context: ResolveContext,\n resolvedModule: ResolvedModule,\n specifiers: string[],\n seen = new Set<string>(),\n): Promise<ResolvedCssImport> {\n const exportName = specifiers[0];\n const exportValue = resolvedModule.exports.named[exportName];\n if (exportValue !== undefined) {\n if (seen.has(resolvedModule.path + \":\" + exportName)) {\n throw new CircularDependencyError(\n `Unable to resolve \"${specifiers.join(\".\")}\" in module \"${resolvedModule.path}\"`,\n { cause: \"Circular dependency detected\" },\n );\n }\n\n seen.add(resolvedModule.path + \":\" + exportName);\n return resolveModuleExport(context, resolvedModule.path, exportValue, specifiers, seen);\n }\n\n let i = 1;\n for (const from of resolvedModule.exports.all) {\n if (context.exportAllLimit && i++ > context.exportAllLimit) {\n throw new ResolveError(\n `Unable to resolve \"${specifiers.join(\".\")}\" in module \"${resolvedModule.path}\"`,\n {\n cause: `More than ${context.exportAllLimit} star exports are not supported for performance reasons`,\n },\n );\n }\n\n try {\n const resolved = await resolveModuleExport(\n context,\n resolvedModule.path,\n {\n type: \"re-export\",\n from,\n name: exportName,\n },\n specifiers,\n seen,\n );\n\n if (seen.has(resolvedModule.path + \":*\")) {\n throw new CircularDependencyError(\n `Unable to resolve \"${specifiers.join(\".\")}\" in module \"${resolvedModule.path}\"`,\n { cause: \"Circular dependency detected\" },\n );\n }\n\n seen.add(resolvedModule.path + \":*\");\n\n return resolved;\n } catch (error) {\n // ignore resolve error, it means the specifier was not found in the\n // current module, we just have to continue the loop.\n if (!(error instanceof ResolveError)) {\n throw error;\n }\n // if the cause of the error is a circular dependency down the road do not\n // ignore the error\n if (error.circular) {\n throw error;\n }\n }\n }\n\n throw new ResolveError(`Unable to resolve \"${specifiers.join(\".\")}\"`, {\n cause: `no matching export found in module \"${resolvedModule.path}\"`,\n });\n}\n\nasync function resolveModuleExport(\n context: ResolveContext,\n filePath: string,\n moduleExport: ResolvedExport,\n specifiers: string[],\n seen: Set<string>,\n): Promise<ResolvedCssImport> {\n const failureMessage = `Unable to resolve \"${specifiers.join(\".\")}\" in module \"${filePath}\"`;\n try {\n switch (moduleExport.type) {\n case \"re-export\": {\n const { resolved: reExportedModule } = await resolveModule(\n context,\n await context.resolve(moduleExport.from, filePath),\n );\n const resolved = await resolveModuleSpecifierRecursively(\n context,\n reExportedModule,\n [moduleExport.name, ...specifiers.slice(1)],\n seen,\n );\n if (resolved) {\n resolved.from.push(filePath);\n }\n return resolved;\n }\n case \"namespace-re-export\": {\n const { resolved: reExportedModule } = await resolveModule(\n context,\n await context.resolve(moduleExport.from, filePath),\n );\n const resolved = await resolveModuleSpecifierRecursively(\n context,\n reExportedModule,\n specifiers.slice(1),\n seen,\n );\n if (resolved) {\n resolved.from.push(filePath);\n }\n return resolved;\n }\n case \"styled-component\": {\n return {\n type: \"styled-component\",\n from: [filePath],\n source: filePath,\n name: specifiers[specifiers.length - 1],\n value: moduleExport.className,\n };\n }\n // usually at this point `tag-template` exports where already resolved to\n // styled-components if a matching styled-component comment was generated\n // by yak-swc. So resolving a value to a `tag-template` at this stage\n // would mean that the user tried to use the result of a call to a\n // different tag-template than yak's styled in a template. This is usually\n // invalid.\n //\n // But there is an issue with Nextjs. Next build in two passes, once for\n // the server bundle, once for the client bundle. During the server-side\n // build, each module with the `\"use client\"` directive is transformed to\n // throw errors if the exported symbol are used. This transformation\n // removes the comments generated by `@yak/swc`, so instead of the expected\n // `styled-component`, calls to `styled` resolve to a `tag-template`\n // (because no classname was found in the now absent comments).\n //\n // To summarize, if a \"use client\" bundle exports a styled component that\n // is used in a \"standard\" module, the resolve logic would throw with\n // \"unknown type tag-template\".\n //\n // To avoid this error, the resolve logic must handle those `tag-template`\n // with a special type `unresolved-tag`. Those will be interpolated to\n // valid CSS with minimal effect (to avoid CSS syntax error in the case of\n // Nextjs server build)\n case \"tag-template\": {\n return {\n type: \"unresolved-tag\",\n from: [filePath],\n source: filePath,\n name: specifiers[specifiers.length - 1],\n };\n }\n case \"constant\": {\n return {\n type: \"constant\",\n from: [filePath],\n source: filePath,\n value: moduleExport.value,\n };\n }\n case \"record\": {\n const resolvedInRecord = resolveSpecifierInRecord(\n moduleExport,\n specifiers[0],\n specifiers.slice(1),\n );\n return resolveModuleExport(context, filePath, resolvedInRecord, specifiers, seen);\n }\n case \"mixin\": {\n return {\n type: \"mixin\",\n from: [filePath],\n source: filePath,\n value: moduleExport.value,\n };\n }\n case \"unsupported\": {\n throw new UnsupportedExportError(failureMessage, {\n cause: explainUnsupported(\n filePath,\n specifiers.join(\".\"),\n moduleExport.hint,\n moduleExport.source,\n ),\n });\n }\n }\n } catch (error) {\n // UnsupportedExportError already carries this frame's \"Unable to resolve …\"\n // wrapper, so it bubbles up unchanged to avoid duplicating the line.\n if (error instanceof UnsupportedExportError) {\n throw error;\n }\n throw new ResolveError(failureMessage, { cause: error });\n }\n}\n\nfunction explainUnsupported(\n filePath: string,\n specifier: string,\n hint: string | undefined,\n source: UnsupportedExportSource | undefined,\n): string {\n const isYakFile = /\\.yak\\.(?:ts|tsx|js|jsx)$/.test(filePath);\n const docs =\n \"https://yak.js.org/docs/migration-from-styled-components#move-some-code-to-yak-files\";\n const snippet = source ? renderSourceSnippet(filePath, source, hint ?? \"\") : undefined;\n\n const lines: string[] = [];\n if (isYakFile) {\n const got = hint ? ` (got \\`${hint}\\`)` : \"\";\n lines.push(`\\`${specifier}\\` evaluated to a value that cannot be inlined into CSS${got}.`);\n if (snippet) lines.push(snippet);\n lines.push(\n ` help: replace it with a string, number, or plain object/array of those`,\n ` see: ${docs}`,\n );\n return lines.join(\"\\n\");\n }\n\n const got = hint ? ` (got a ${hint})` : \"\";\n lines.push(`\\`${specifier}\\` is not a string or number literal${got}.`);\n if (snippet) lines.push(snippet);\n lines.push(\n ` help: rename \"${filePath}\" to \"${suggestYakFileName(filePath)}\" so its exports run at build time`,\n ` (or replace \\`${specifier}\\` with a literal value)`,\n ` see: ${docs}`,\n );\n return lines.join(\"\\n\");\n}\n\nfunction suggestYakFileName(filePath: string): string {\n const match = filePath.match(/^(.*)\\.(ts|tsx|js|jsx)$/);\n if (!match) return filePath;\n return `${match[1]}.yak.${match[2]}`;\n}\n\n/**\n * Render a Rust-compiler-style snippet from a structural source location:\n *\n * --> /foo/colors.ts:5:18\n * |\n * 5 | export const bg = `var(${v})`;\n * | ^^^^^^^^^^^^ TemplateLiteral\n */\nfunction renderSourceSnippet(\n filePath: string,\n source: UnsupportedExportSource,\n label: string,\n): string {\n const startLine = source.start.line;\n const startCol = source.start.column;\n const sameLine = source.end.line === startLine;\n const caretLen = sameLine\n ? Math.max(1, source.end.column - startCol)\n : Math.max(1, source.lineText.length - startCol);\n\n const lineNumStr = String(startLine);\n const gutterPad = \" \".repeat(lineNumStr.length);\n\n return [\n ` --> ${filePath}:${startLine}:${startCol + 1}`,\n ` ${gutterPad} |`,\n ` ${lineNumStr} | ${source.lineText}`,\n ` ${gutterPad} | ${\" \".repeat(startCol)}${\"^\".repeat(caretLen)} ${label}`,\n ].join(\"\\n\");\n}\n\nfunction resolveSpecifierInRecord(\n record: ExtendedRecordExport,\n name: string,\n specifiers: string[],\n): ConstantExport | ResolvedStyledComponent | ResolvedMixin {\n if (specifiers.length === 0) {\n throw new ResolveError(\"did not expect an object\");\n }\n let depth = 0;\n let current: ResolvedExport = record;\n while (current && current.type === \"record\" && depth < specifiers.length) {\n current = current.value[specifiers[depth]];\n depth += 1;\n }\n\n if (current === undefined || depth !== specifiers.length) {\n throw new ResolveError(\n `Unable to resolve \"${specifiers.join(\".\")}\" in object/array \"${name}\"`,\n { cause: \"path not found\" },\n );\n }\n\n if (\n current.type === \"constant\" ||\n current.type === \"styled-component\" ||\n current.type === \"mixin\"\n ) {\n return current;\n }\n\n // mixins in .yak files are wrapped inside an object with a __yak key\n if (\n current.type === \"record\" &&\n \"__yak\" in current.value &&\n current.value.__yak.type === \"constant\"\n ) {\n return { type: \"mixin\", value: String(current.value.__yak.value) };\n }\n\n throw new ResolveError(`Unable to resolve \"${specifiers.join(\".\")}\" in object/array \"${name}\"`, {\n cause: \"only string and numbers are supported\",\n });\n}\n\n/**\n * hex SHA-1 hash of a message using webcrypto\n * Keeps yak independent from node api (therefore executable in browser)\n */\nasync function sha1(message: string) {\n const resultBuffer = await globalThis.crypto.subtle.digest(\n \"SHA-1\",\n new TextEncoder().encode(message),\n );\n return Array.from(new Uint8Array(resultBuffer), (byte) =>\n byte.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n}\n\ntype ResolvedCssImport =\n | {\n type: \"styled-component\";\n source: string;\n from: string[];\n name: string;\n value: string;\n }\n | {\n type: \"unresolved-tag\";\n source: string;\n from: string[];\n name: string;\n }\n | { type: \"mixin\"; source: string; from: string[]; value: string | number }\n | {\n type: \"constant\";\n source: string;\n from: string[];\n value: string | number;\n };\n\nexport type ResolveContext = {\n parse: (modulePath: string) => Promise<ParsedModule> | ParsedModule;\n cache?: {\n resolve?: Cache<Promise<{ resolved: ResolvedModule; dependencies: string[] }>>;\n resolveCrossFileConstant?: Cache<Promise<{ resolved: string; dependencies: string[] }>>;\n };\n exportAllLimit?: number;\n resolve: (specifier: string, importer: string) => Promise<string> | string;\n};\n\ntype YakImportKind = \"mixin\" | \"selector\";\n\ntype YakCssImport = {\n encodedArguments: string;\n moduleSpecifier: string;\n specifier: string[];\n importKind: YakImportKind;\n semicolon: string;\n position: number;\n size: number;\n};\n\nexport type ExtendedRecordExport = {\n type: \"record\";\n value: Record<string, ResolvedExport>;\n};\n\nexport type ResolvedMixin = { type: \"mixin\"; value: string };\nexport type ResolvedStyledComponent = {\n type: \"styled-component\";\n className: string;\n};\n\nexport type ResolvedExport =\n | Exclude<ModuleExport, RecordExport>\n | ExtendedRecordExport\n | ResolvedStyledComponent\n | ResolvedMixin;\n\nexport type ResolvedExports = {\n named: Record<string, ResolvedExport>;\n all: string[];\n};\n\nexport type ResolvedModule = {\n path: string;\n exports: ResolvedExports;\n};\n","/**\n * Import sources of all yak runtime packages, for code that must detect any of them.\n * Mirrors YakPackage in yak-swc's yak_imports.rs\n */\nexport const yakPackageNames = [\"@yak/react\", \"@yak/solid\"] as const;\n","import { parse } from \"@babel/parser\";\nimport type { Expression, ObjectExpression, TSAsExpression } from \"@babel/types\";\nimport {\n ModuleExport,\n ModuleExports,\n UnsupportedExportSource,\n} from \"../../cross-file-resolver/parseModule.js\";\nimport { yakPackageNames } from \"../../packageNames.js\";\n\n/**\n * Extracts a module's exports for the cross-file resolver by parsing with babel. Nothing is executed.\n *\n * Handles `export const`, export lists (`export { x }`), re-exports with a `from`,\n * `export *`, and `export default`.\n * Anything else becomes an `unsupported` entry with its source location, or is skipped.\n *\n * Throws if the source doesn't parse.\n */\nexport async function parseExports(sourceContents: string): Promise<ModuleExports> {\n try {\n const ast = parse(sourceContents, {\n sourceType: \"module\",\n plugins: [\"jsx\", \"typescript\"] as const,\n });\n\n // Derive importYak from top-level imports (no traverse needed)\n const importYak = ast.program.body.some(\n (node) =>\n node.type === \"ImportDeclaration\" &&\n (yakPackageNames as readonly string[]).includes(node.source.value),\n );\n\n const moduleExports: ModuleExports = {\n importYak,\n named: {},\n all: [],\n };\n\n // Track variable declarations for default export lookup\n const variableDeclarations: Record<string, Expression> = {};\n // Track top-level import bindings so exported imports become re-exports\n const importBindings: Record<string, ModuleExport> = {};\n // Local export lists (`export { x }`), resolved after the loop\n // because the list can come before the declaration\n const localExportSpecifiers: { local: string; exported: string }[] = [];\n let defaultIdentifier: string | null = null;\n\n for (const node of ast.program.body) {\n if (node.type === \"ImportDeclaration\") {\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportSpecifier\") {\n importBindings[specifier.local.name] = {\n type: \"re-export\",\n from: node.source.value,\n name:\n specifier.imported.type === \"Identifier\"\n ? specifier.imported.name\n : specifier.imported.value,\n };\n } else if (specifier.type === \"ImportDefaultSpecifier\") {\n importBindings[specifier.local.name] = {\n type: \"re-export\",\n from: node.source.value,\n name: \"default\",\n };\n } else if (specifier.type === \"ImportNamespaceSpecifier\") {\n importBindings[specifier.local.name] = {\n type: \"namespace-re-export\",\n from: node.source.value,\n };\n }\n }\n }\n\n // Track top-level variable declarations for default export lookup\n if (node.type === \"VariableDeclaration\") {\n for (const decl of node.declarations) {\n if (decl.id.type === \"Identifier\" && decl.init) {\n variableDeclarations[decl.id.name] = decl.init;\n }\n }\n }\n\n if (node.type === \"ExportNamedDeclaration\") {\n if (node.source) {\n // export { x } from \"./file\", export { x as y } from \"./file\"\n for (const specifier of node.specifiers) {\n if (\n specifier.type === \"ExportSpecifier\" &&\n specifier.exported.type === \"Identifier\" &&\n specifier.local.type === \"Identifier\"\n ) {\n moduleExports.named[specifier.exported.name] = {\n type: \"re-export\",\n from: node.source.value,\n name: specifier.local.name,\n };\n }\n // export * as ns from \"./file\"\n if (\n specifier.type === \"ExportNamespaceSpecifier\" &&\n specifier.exported.type === \"Identifier\"\n ) {\n moduleExports.named[specifier.exported.name] = {\n type: \"namespace-re-export\",\n from: node.source.value,\n };\n }\n }\n } else if (!node.declaration) {\n // export { x }, export { x as y }\n for (const specifier of node.specifiers) {\n if (\n specifier.type === \"ExportSpecifier\" &&\n specifier.exported.type === \"Identifier\" &&\n specifier.local.type === \"Identifier\"\n ) {\n localExportSpecifiers.push({\n local: specifier.local.name,\n exported: specifier.exported.name,\n });\n }\n }\n } else if (node.declaration.type === \"VariableDeclaration\") {\n // export const x = ...\n for (const declaration of node.declaration.declarations) {\n if (declaration.id.type === \"Identifier\" && declaration.init) {\n variableDeclarations[declaration.id.name] = declaration.init;\n const parsed = parseExportValueExpression(declaration.init, sourceContents);\n if (parsed) {\n moduleExports.named[declaration.id.name] = parsed;\n }\n }\n }\n }\n }\n\n if (node.type === \"ExportDefaultDeclaration\") {\n if (node.declaration.type === \"Identifier\") {\n // e.g. export default variableName;\n // Save the identifier name to look up later\n defaultIdentifier = node.declaration.name;\n } else if (\n node.declaration.type === \"FunctionDeclaration\" ||\n node.declaration.type === \"ClassDeclaration\"\n ) {\n // e.g. export default function() {...} or export default class {...}\n moduleExports.named[\"default\"] = {\n type: \"unsupported\",\n hint: node.declaration.type,\n source: extractUnsupportedSource(node.declaration.loc, sourceContents),\n };\n } else {\n // e.g. export default { ... } or export default \"value\"\n moduleExports.named[\"default\"] = parseExportValueExpression(\n node.declaration as Expression,\n sourceContents,\n );\n }\n }\n\n // export * from \"./file\"\n if (node.type === \"ExportAllDeclaration\") {\n moduleExports.all.push(node.source.value);\n }\n }\n\n // Look up the value of an exported identifier from local declarations or imports\n const resolveIdentifier = (name: string): ModuleExport | undefined => {\n if (variableDeclarations[name]) {\n return parseExportValueExpression(variableDeclarations[name], sourceContents);\n }\n return importBindings[name];\n };\n\n for (const { local, exported } of localExportSpecifiers) {\n const resolved = resolveIdentifier(local);\n if (resolved) {\n moduleExports.named[exported] = resolved;\n }\n }\n\n // If we found a default export that's an identifier, look up its value\n if (defaultIdentifier) {\n const resolved = resolveIdentifier(defaultIdentifier);\n if (resolved) {\n moduleExports.named[\"default\"] = resolved;\n }\n }\n\n return moduleExports;\n } catch (error) {\n throw new Error(`Error parsing exports: ${(error as Error).message}`);\n }\n}\n\n/**\n * Unpacks TS type assertions (as, satisfies) to the underlying expression\n */\nfunction unpackTSAsExpression(node: TSAsExpression | Expression): Expression {\n if (node.type === \"TSAsExpression\" || node.type === \"TSSatisfiesExpression\") {\n return unpackTSAsExpression((node as TSAsExpression).expression);\n }\n return node;\n}\n\nfunction parseExportValueExpression(node: Expression, code?: string): ModuleExport {\n // ignores `as` casts so it doesn't interfere with the ast node type detection\n const expression = unpackTSAsExpression(node);\n if (expression.type === \"CallExpression\" || expression.type === \"TaggedTemplateExpression\") {\n return { type: \"tag-template\" };\n } else if (expression.type === \"StringLiteral\" || expression.type === \"NumericLiteral\") {\n return { type: \"constant\", value: expression.value };\n } else if (\n expression.type === \"UnaryExpression\" &&\n expression.operator === \"-\" &&\n expression.argument.type === \"NumericLiteral\"\n ) {\n return { type: \"constant\", value: -expression.argument.value };\n } else if (expression.type === \"TemplateLiteral\" && expression.quasis.length === 1) {\n return { type: \"constant\", value: expression.quasis[0].value.raw };\n } else if (expression.type === \"ObjectExpression\") {\n return {\n type: \"record\",\n value: parseObjectExpression(expression, code),\n };\n }\n return {\n type: \"unsupported\",\n hint: expression.type,\n source: extractUnsupportedSource(expression.loc, code),\n };\n}\n\nfunction parseObjectExpression(\n node: ObjectExpression,\n code?: string,\n): Record<string, ModuleExport> {\n let result: Record<string, ModuleExport> = {};\n for (const property of node.properties) {\n if (property.type === \"ObjectProperty\" && property.key.type === \"Identifier\") {\n const key = property.key.name;\n const parsed = parseExportValueExpression(property.value as Expression, code);\n if (parsed) {\n result[key] = parsed;\n }\n }\n }\n return result;\n}\n\n/**\n * Pull the structural source-location data the error formatter needs to\n * render a snippet — the formatter (not this parser) is responsible for\n * any presentation. Returns undefined if loc or source text is missing.\n */\nfunction extractUnsupportedSource(\n loc:\n | {\n start: { line: number; column: number };\n end: { line: number; column: number };\n }\n | null\n | undefined,\n code: string | undefined,\n): UnsupportedExportSource | undefined {\n if (!loc || !code) return undefined;\n const lineText = code.split(/\\r?\\n/)[loc.start.line - 1];\n if (lineText === undefined) return undefined;\n return {\n start: { line: loc.start.line, column: loc.start.column },\n end: { line: loc.end.line, column: loc.end.column },\n lineText,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../yak-internals/cross-file-resolver/parseModule.ts","../../../yak-internals/cross-file-resolver/rewriteRelativeCssUrls.ts","../../../yak-internals/cross-file-resolver/Errors.ts","../../../yak-internals/cross-file-resolver/resolveCrossFileConstant.ts","../../../yak-internals/packageNames.ts","../../../yak-internals/loaders/lib/parseExports.ts"],"sourcesContent":["import { type Cache } from \"./types.js\";\n\nexport async function parseModule(\n context: ParseContext,\n modulePath: string,\n): Promise<ParsedModule> {\n try {\n const isYak =\n modulePath.endsWith(\".yak.ts\") ||\n modulePath.endsWith(\".yak.tsx\") ||\n modulePath.endsWith(\".yak.js\") ||\n modulePath.endsWith(\".yak.jsx\");\n\n // handle yak file by evaluating and mapping exported value to the\n // `ModuleExport` format. This operation is not cached to always get a fresh\n // value from those modules\n if (isYak && context.evaluateYakModule) {\n const yakModule = await context.evaluateYakModule(modulePath);\n const yakExports = objectToModuleExport(yakModule);\n\n return {\n type: \"yak\",\n exports: { importYak: false, named: yakExports, all: [] },\n path: modulePath,\n };\n }\n\n if (context.cache?.parse === undefined) {\n return await uncachedParseModule(context, modulePath);\n }\n\n const cached = context.cache.parse.get(modulePath);\n if (cached === undefined) {\n // We cache the parsed file to avoid re-parsing it.\n // It's ok, that initial parallel requests to the same file will parse it multiple times.\n // This avoid deadlocks do to the fact that we load multiple modules in the chain for cross file references.\n const parsedModule = await uncachedParseModule(context, modulePath);\n\n context.cache.parse.set(modulePath, parsedModule);\n if (context.cache.parse.addDependency) {\n context.cache.parse.addDependency(modulePath, modulePath);\n }\n return parsedModule;\n }\n\n return cached;\n } catch (error) {\n const causeMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`Error parsing file \"${modulePath}\"\\n Caused by: ${causeMessage}`);\n }\n}\n\nexport async function uncachedParseModule(\n context: ParseContext,\n modulePath: string,\n): Promise<ParsedModule> {\n const exports = await context.extractExports(modulePath);\n\n // early exit if no yak import was found\n if (!exports.importYak) {\n return {\n type: \"regular\",\n path: modulePath,\n exports,\n };\n }\n\n const transformed = await context.getTransformed(modulePath);\n const mixins = parseMixins(transformed.code);\n const styledComponents = parseStyledComponents(transformed.code);\n\n return {\n type: \"regular\",\n path: modulePath,\n js: transformed,\n exports,\n styledComponents,\n mixins,\n };\n}\n\nfunction parseMixins(sourceContents: string): Record<string, Mixin> {\n // Mixins are always in the following format:\n // /*YAK EXPORTED MIXIN:fancy:aspectRatio:16:9\n // css\n // */\n const mixinParts = sourceContents.split(\"/*YAK EXPORTED MIXIN:\");\n let mixins: Record<string, { type: \"mixin\"; value: string; nameParts: string[] }> = {};\n\n for (let i = 1; i < mixinParts.length; i++) {\n const [comment] = mixinParts[i].split(\"*/\", 1);\n const position = comment.indexOf(\"\\n\");\n const name = comment.slice(0, position);\n const value = comment.slice(position + 1);\n mixins[name] = {\n type: \"mixin\",\n value,\n nameParts: name.split(\":\").map((part) => decodeURIComponent(part)),\n };\n }\n return mixins;\n}\n\nfunction parseStyledComponents(sourceContents: string): Record<string, StyledComponent> {\n // cross-file Styled Components are always in the following format:\n // /*YAK EXPORTED STYLED:ComponentName:ClassName*/\n const styledParts = sourceContents.split(\"/*YAK EXPORTED STYLED:\");\n let styledComponents: Record<string, StyledComponent> = {};\n\n for (let i = 1; i < styledParts.length; i++) {\n const [comment] = styledParts[i].split(\"*/\", 1);\n const [componentName, className] = comment.split(\":\");\n styledComponents[componentName] = {\n type: \"styled-component\",\n nameParts: componentName.split(\".\"),\n value: `.${className}`,\n };\n }\n\n return styledComponents;\n}\n\nfunction objectToModuleExport(object: object) {\n return Object.fromEntries(\n Object.entries(object).map(([key, value]): [string, ModuleExport] => {\n if (typeof value === \"string\" || typeof value === \"number\") {\n return [key, { type: \"constant\" as const, value }];\n } else if (value && (typeof value === \"object\" || Array.isArray(value))) {\n return [key, { type: \"record\" as const, value: objectToModuleExport(value) }];\n } else {\n return [key, { type: \"unsupported\" as const, hint: String(value) }];\n }\n }),\n );\n}\n\nexport type ParseContext = {\n cache?: { parse?: Cache<ParsedModule> };\n evaluateYakModule?: (\n modulePath: string,\n ) => Promise<Record<string, unknown>> | Record<string, unknown>;\n extractExports: (modulePath: string) => Promise<ModuleExports> | ModuleExports;\n getTransformed: (\n modulePath: string,\n ) => Promise<{ code: string; map?: string }> | { code: string; map?: string };\n};\n\nexport type ModuleExports = {\n importYak: boolean;\n named: Record<string, ModuleExport>;\n all: string[];\n};\n\nexport type ConstantExport = { type: \"constant\"; value: string | number };\nexport type RecordExport = {\n type: \"record\";\n value: Record<string, ModuleExport>;\n};\nexport type UnsupportedExport = {\n type: \"unsupported\";\n hint?: string;\n /**\n * Source location of the offending expression. Populated by the parser\n * when it has access to source text; left undefined for runtime-evaluated\n * yak modules. The error formatter is responsible for any rendering.\n */\n source?: UnsupportedExportSource;\n};\n\nexport type UnsupportedExportSource = {\n /** 1-based line number, 0-based column — same convention as Babel `loc`. */\n start: { line: number; column: number };\n end: { line: number; column: number };\n /** The text of `start.line` from the original source — kept so the\n * error formatter can render a snippet without re-reading the file. */\n lineText: string;\n};\nexport type ReExport = { type: \"re-export\"; name: string; from: string };\nexport type NamespaceReExport = { type: \"namespace-re-export\"; from: string };\nexport type TagTemplateExport = { type: \"tag-template\" };\n\nexport type ModuleExport =\n | ConstantExport\n | TagTemplateExport\n | RecordExport\n | UnsupportedExport\n | ReExport\n | NamespaceReExport;\n\nexport type ParsedModule = {\n path: string;\n exports: ModuleExports;\n} & (\n | {\n type: \"regular\";\n js?: { code: string; map?: string };\n styledComponents?: Record<string, StyledComponent>;\n mixins?: Record<string, Mixin>;\n }\n | {\n type: \"yak\";\n }\n);\n\nexport type StyledComponent = {\n type: \"styled-component\";\n value: string;\n nameParts: string[];\n};\n\nexport type Mixin = { type: \"mixin\"; value: string; nameParts: string[] };\n","import { posix, win32 } from \"node:path\";\n\n/**\n * A mixin keeps the relative url() paths of the file that defines it. When the\n * cross-file resolver inlines the mixin into a file in another directory, the\n * bundler resolves those paths from the new file, so they have to change:\n *\n * // src/icons/mixins.ts\n * export const sun = css`background: url(\"./sun.avif\");`;\n * // src/pages/home.tsx\n * const Hero = styled.div`${sun}`;\n * -> background: url(\"../icons/sun.avif\");\n *\n * Only paths that start with \"./\" or \"../\" change. \"/sun.avif\", \"data:\" and\n * \"https:\" urls point to the same place from every file.\n */\nexport function rewriteRelativeCssUrls(css: string, source: string, consumer: string): string {\n if (!css.includes(\"url(\")) return css;\n // Windows paths arrive as \"C:\\app\\a.tsx\" from webpack and as \"C:/app/a.tsx\" from\n // Vite. win32 reads both, and choosing it by the path keeps the result the same on\n // every OS, which lets the unit tests cover Windows paths.\n const path = /^[a-z]:[\\\\/]/i.test(consumer) ? win32 : posix;\n // A query on the mixin import, as in `import { sun } from \"./mixins.ts?v=2\"`, is not part of the path\n const prefix = path.relative(path.dirname(consumer), path.dirname(source.split(\"?\")[0]));\n // Two Windows drives have no relative path between them, so relative() returns\n // the absolute directory of the source and the urls stay as they are\n if (path.isAbsolute(prefix)) return css;\n\n return css.replace(cssUrlRegex, (match, double?: string, single?: string, bare?: string) => {\n const url = double ?? single ?? bare ?? \"\";\n if (!url.startsWith(\"./\") && !url.startsWith(\"../\")) return match;\n const quote = double !== undefined ? '\"' : single !== undefined ? \"'\" : \"\";\n // \"../icons\" + \"./sun.avif\" -> \"../icons/sun.avif\", \"img\" + \"./a.png\" -> \"./img/a.png\"\n const rewritten = path.join(prefix, url).replace(/\\\\/g, \"/\");\n return `url(${quote}${rewritten.startsWith(\"../\") ? \"\" : \"./\"}${rewritten}${quote})`;\n });\n}\n\n/**\n * url(\"a.png\"), url('a.png') or url(a.png), with optional spaces inside the parentheses.\n * The url has no \"*\": without that, a match could start in a comment and run into the\n * next rule, so `/* url(\"./ *\\/ .a:before { content: \")\" }` would rewrite the rule.\n */\nconst cssUrlRegex = /url\\(\\s*(?:\"([^\"*]*)\"|'([^'*]*)'|([^'\"()\\s*]*))\\s*\\)/g;\n","export class CauseError extends Error {\n circular?: boolean;\n constructor(message: string, options?: { cause?: unknown }) {\n super(\n `${message}${options?.cause ? `\\n Caused by: ${typeof options.cause === \"object\" && options.cause !== null && \"message\" in options.cause ? options.cause.message : String(options.cause)}` : \"\"}`,\n );\n\n if (options?.cause instanceof CauseError && options.cause.circular) {\n this.circular = true;\n }\n }\n}\n\nexport class ResolveError extends CauseError {}\n\n/**\n * Thrown for `unsupported` exports. Subclassed so the surrounding\n * `resolveModuleExport` catch can recognise that the error already\n * carries its own \"Unable to resolve … in module …\" wrapper and\n * doesn't need another one stacked on top.\n */\nexport class UnsupportedExportError extends ResolveError {}\n\nexport class CircularDependencyError extends CauseError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.circular = true;\n }\n}\n","import type {\n ConstantExport,\n ModuleExport,\n ParsedModule,\n RecordExport,\n UnsupportedExportSource,\n} from \"./parseModule.js\";\nimport { Cache } from \"./types.js\";\nimport { rewriteRelativeCssUrls } from \"./rewriteRelativeCssUrls.js\";\nimport {\n CauseError,\n CircularDependencyError,\n ResolveError,\n UnsupportedExportError,\n} from \"./Errors.js\";\n\nconst yakCssImportRegex =\n // Make mixin and selector non optional once we dropped support for the babel plugin\n /--yak-css-import:\\s*url\\(\"([^\"]+)\",?(|mixin|selector)\\)(;?)/g;\n\n/**\n * Resolves cross-file selectors in css files\n *\n * e.g.:\n * theme.ts:\n * ```ts\n * export const colors = {\n * primary: \"#ff0000\",\n * secondary: \"#00ff00\",\n * };\n * ```\n *\n * styles.ts:\n * ```ts\n * import { colors } from \"./theme\";\n * export const button = css`\n * background-color: ${colors.primary};\n * `;\n */\nexport async function resolveCrossFileConstant(\n context: ResolveContext,\n filePath: string,\n css: string,\n): Promise<{ resolved: string; dependencies: string[] }> {\n const resolveCrossFileConstant = context.cache?.resolveCrossFileConstant;\n if (resolveCrossFileConstant === undefined) {\n return uncachedResolveCrossFileConstant(context, filePath, css);\n }\n\n const cacheKey = await sha1(filePath + \":\" + css);\n\n const cached = resolveCrossFileConstant.get(cacheKey);\n\n if (cached === undefined) {\n const resolvedCrossFilConstantPromise = uncachedResolveCrossFileConstant(\n context,\n filePath,\n css,\n );\n resolveCrossFileConstant.set(cacheKey, resolvedCrossFilConstantPromise);\n\n if (resolveCrossFileConstant.addDependency) {\n resolveCrossFileConstant.addDependency(cacheKey, filePath);\n resolvedCrossFilConstantPromise.then((value) => {\n for (const dep of value.dependencies) {\n resolveCrossFileConstant!.addDependency!(cacheKey, dep);\n }\n });\n }\n\n return resolvedCrossFilConstantPromise;\n }\n\n return cached;\n}\n\nexport async function uncachedResolveCrossFileConstant(\n context: ResolveContext,\n filePath: string,\n css: string,\n): Promise<{ resolved: string; dependencies: string[] }> {\n const yakImports = await parseYakCssImport(context, filePath, css);\n\n if (yakImports.length === 0) {\n return { resolved: css, dependencies: [] };\n }\n\n try {\n const dependencies = new Set<string>();\n\n const resolvedValues = await Promise.all(\n yakImports.map(async ({ moduleSpecifier, specifier }) => {\n const { resolved: resolvedModule } = await resolveModule(context, moduleSpecifier);\n\n const resolvedValue = await resolveModuleSpecifierRecursively(\n context,\n resolvedModule,\n specifier,\n );\n\n for (const dependency of resolvedValue.from) {\n dependencies.add(dependency);\n }\n\n return resolvedValue;\n }),\n );\n\n // Replace the imports with the resolved values\n let result = css;\n for (let i = yakImports.length - 1; i >= 0; i--) {\n const { position, size, importKind, specifier, semicolon } = yakImports[i];\n const resolved = resolvedValues[i];\n\n let replacement: string;\n\n if (resolved.type === \"unresolved-tag\") {\n // tag that could not be resolved to styled-components or mixins are\n // interpolated to produce valid CSS with minimal impact (since we don't\n // know what the value should actually be). For mixins (CSS rules) we\n // interpolate an empty string, and for selectors we interpolate\n // \"undefined\" (a selector that would match nothing)\n replacement = importKind === \"mixin\" ? \"\" : \"undefined\";\n } else {\n if (importKind === \"selector\") {\n if (resolved.type !== \"styled-component\" && resolved.type !== \"constant\") {\n throw new Error(\n `Found \"${\n resolved.type\n }\" but expected a selector - did you forget a semicolon after \"${specifier.join(\n \".\",\n )}\"?`,\n );\n }\n }\n\n replacement =\n resolved.type === \"styled-component\"\n ? resolved.value\n : resolved.value +\n // resolved.value can be of two different types:\n // - mixin:\n // ${mixinName};\n // - constant:\n // color: ${value};\n // For mixins the semicolon is already included in the value\n // but for constants it has to be added manually\n ([\"}\", \";\"].includes(String(resolved.value).trimEnd().slice(-1)) ? \"\" : semicolon);\n }\n\n // A mixin from another file keeps its own relative url() paths, see rewriteRelativeCssUrls\n if (resolved.type !== \"unresolved-tag\" && resolved.source !== filePath) {\n replacement = rewriteRelativeCssUrls(String(replacement), resolved.source, filePath);\n }\n\n result = result.slice(0, position) + String(replacement) + result.slice(position + size);\n }\n\n return { resolved: result, dependencies: Array.from(dependencies) };\n } catch (error) {\n throw new CauseError(`Error while resolving cross-file selectors in file \"${filePath}\"`, {\n cause: error,\n });\n }\n}\n\n/**\n * Search for --yak-css-import: url(\"path/to/module\") in the css\n */\nasync function parseYakCssImport(\n context: ResolveContext,\n filePath: string,\n css: string,\n): Promise<YakCssImport[]> {\n const yakImports: YakCssImport[] = [];\n\n for (const match of css.matchAll(yakCssImportRegex)) {\n const [fullMatch, encodedArguments, importKind, semicolon] = match;\n const [moduleSpecifier, ...specifier] = encodedArguments\n .split(\":\")\n .map((entry) => decodeURIComponent(entry));\n\n yakImports.push({\n encodedArguments,\n moduleSpecifier: await context.resolve(moduleSpecifier, filePath),\n specifier,\n importKind: importKind as YakImportKind,\n semicolon,\n position: match.index,\n size: fullMatch.length,\n });\n }\n\n return yakImports;\n}\n\nasync function resolveModule(context: ResolveContext, filePath: string) {\n if (context.cache?.resolve === undefined) {\n return uncachedResolveModule(context, filePath);\n }\n\n const cached = context.cache.resolve.get(filePath);\n if (cached === undefined) {\n const resolvedPromise = uncachedResolveModule(context, filePath);\n context.cache.resolve.set(filePath, resolvedPromise);\n\n if (context.cache.resolve.addDependency) {\n context.cache.resolve.addDependency(filePath, filePath);\n resolvedPromise.then((value) => {\n for (const dep of value.dependencies) {\n context.cache!.resolve!.addDependency!(filePath, dep);\n }\n });\n }\n\n return resolvedPromise;\n }\n\n return cached;\n}\n\nasync function uncachedResolveModule(\n context: ResolveContext,\n filePath: string,\n): Promise<{ resolved: ResolvedModule; dependencies: string[] }> {\n const parsedModule = await context.parse(filePath);\n\n const exports = parsedModule.exports as ResolvedExports;\n\n if (parsedModule.type !== \"regular\") {\n return {\n resolved: {\n path: parsedModule.path,\n exports,\n },\n dependencies: [],\n };\n }\n\n const dependencies = new Set<string>();\n\n // Reconcile styled-component \"name\" structure with export structure\n if (parsedModule.styledComponents) {\n Object.values(parsedModule.styledComponents).map((styledComponent) => {\n if (styledComponent.nameParts.length === 1) {\n exports.named[styledComponent.nameParts[0]] = {\n type: \"styled-component\",\n className: styledComponent.value,\n };\n } else {\n let exportEntry = exports.named[styledComponent.nameParts[0]];\n\n if (!exportEntry) {\n exportEntry = { type: \"record\", value: {} };\n exports.named[styledComponent.nameParts[0]] = exportEntry;\n } else if (exportEntry.type !== \"record\") {\n throw new CauseError(`Error parsing file \"${parsedModule.path}\"`, {\n cause: `\"${styledComponent.nameParts[0]}\" is not a record`,\n });\n }\n\n let current = exportEntry.value;\n for (let i = 1; i < styledComponent.nameParts.length - 1; i++) {\n let next = current[styledComponent.nameParts[i]];\n if (!next) {\n next = { type: \"record\", value: {} };\n current[styledComponent.nameParts[i]] = next;\n } else if (next.type !== \"record\") {\n throw new CauseError(`Error parsing file \"${parsedModule.path}\"`, {\n cause: `\"${styledComponent.nameParts.slice(0, i + 1).join(\".\")}\" is not a record`,\n });\n }\n current = next.value;\n }\n current[styledComponent.nameParts[styledComponent.nameParts.length - 1]] = {\n type: \"styled-component\",\n className: styledComponent.value,\n };\n }\n });\n }\n\n // Recursively resolve cross-file constants in mixins\n // e.g. cross file mixins inside a cross file mixin\n // or a cross file selector inside a cross file mixin\n if (parsedModule.mixins) {\n await Promise.all(\n Object.values(parsedModule.mixins).map(async (mixin) => {\n const { resolved, dependencies: deps } = await resolveCrossFileConstant(\n context,\n parsedModule.path,\n mixin.value,\n );\n\n for (const dep of deps) {\n dependencies.add(dep);\n }\n\n if (mixin.nameParts.length === 1) {\n exports.named[mixin.nameParts[0]] = {\n type: \"mixin\",\n value: resolved,\n };\n } else {\n let exportEntry = exports.named[mixin.nameParts[0]];\n\n if (!exportEntry) {\n exportEntry = { type: \"record\", value: {} };\n exports.named[mixin.nameParts[0]] = exportEntry;\n } else if (exportEntry.type !== \"record\") {\n throw new CauseError(`Error parsing file \"${parsedModule.path}\"`, {\n cause: `\"${mixin.nameParts[0]}\" is not a record`,\n });\n }\n\n let current = exportEntry.value;\n for (let i = 1; i < mixin.nameParts.length - 1; i++) {\n let next = current[mixin.nameParts[i]];\n if (!next) {\n next = { type: \"record\", value: {} };\n current[mixin.nameParts[i]] = next;\n } else if (next.type !== \"record\") {\n throw new CauseError(`Error parsing file \"${parsedModule.path}\"`, {\n cause: `\"${mixin.nameParts.slice(0, i + 1).join(\".\")}\" is not a record`,\n });\n }\n current = next.value;\n }\n current[mixin.nameParts[mixin.nameParts.length - 1]] = {\n type: \"mixin\",\n value: resolved,\n };\n }\n }),\n );\n }\n return {\n resolved: {\n path: parsedModule.path,\n exports,\n },\n dependencies: Array.from(dependencies),\n };\n}\n\nasync function resolveModuleSpecifierRecursively(\n context: ResolveContext,\n resolvedModule: ResolvedModule,\n specifiers: string[],\n seen = new Set<string>(),\n): Promise<ResolvedCssImport> {\n const exportName = specifiers[0];\n const exportValue = resolvedModule.exports.named[exportName];\n if (exportValue !== undefined) {\n if (seen.has(resolvedModule.path + \":\" + exportName)) {\n throw new CircularDependencyError(\n `Unable to resolve \"${specifiers.join(\".\")}\" in module \"${resolvedModule.path}\"`,\n { cause: \"Circular dependency detected\" },\n );\n }\n\n seen.add(resolvedModule.path + \":\" + exportName);\n return resolveModuleExport(context, resolvedModule.path, exportValue, specifiers, seen);\n }\n\n let i = 1;\n for (const from of resolvedModule.exports.all) {\n if (context.exportAllLimit && i++ > context.exportAllLimit) {\n throw new ResolveError(\n `Unable to resolve \"${specifiers.join(\".\")}\" in module \"${resolvedModule.path}\"`,\n {\n cause: `More than ${context.exportAllLimit} star exports are not supported for performance reasons`,\n },\n );\n }\n\n try {\n const resolved = await resolveModuleExport(\n context,\n resolvedModule.path,\n {\n type: \"re-export\",\n from,\n name: exportName,\n },\n specifiers,\n seen,\n );\n\n if (seen.has(resolvedModule.path + \":*\")) {\n throw new CircularDependencyError(\n `Unable to resolve \"${specifiers.join(\".\")}\" in module \"${resolvedModule.path}\"`,\n { cause: \"Circular dependency detected\" },\n );\n }\n\n seen.add(resolvedModule.path + \":*\");\n\n return resolved;\n } catch (error) {\n // ignore resolve error, it means the specifier was not found in the\n // current module, we just have to continue the loop.\n if (!(error instanceof ResolveError)) {\n throw error;\n }\n // if the cause of the error is a circular dependency down the road do not\n // ignore the error\n if (error.circular) {\n throw error;\n }\n }\n }\n\n throw new ResolveError(`Unable to resolve \"${specifiers.join(\".\")}\"`, {\n cause: `no matching export found in module \"${resolvedModule.path}\"`,\n });\n}\n\nasync function resolveModuleExport(\n context: ResolveContext,\n filePath: string,\n moduleExport: ResolvedExport,\n specifiers: string[],\n seen: Set<string>,\n): Promise<ResolvedCssImport> {\n const failureMessage = `Unable to resolve \"${specifiers.join(\".\")}\" in module \"${filePath}\"`;\n try {\n switch (moduleExport.type) {\n case \"re-export\": {\n const { resolved: reExportedModule } = await resolveModule(\n context,\n await context.resolve(moduleExport.from, filePath),\n );\n const resolved = await resolveModuleSpecifierRecursively(\n context,\n reExportedModule,\n [moduleExport.name, ...specifiers.slice(1)],\n seen,\n );\n if (resolved) {\n resolved.from.push(filePath);\n }\n return resolved;\n }\n case \"namespace-re-export\": {\n const { resolved: reExportedModule } = await resolveModule(\n context,\n await context.resolve(moduleExport.from, filePath),\n );\n const resolved = await resolveModuleSpecifierRecursively(\n context,\n reExportedModule,\n specifiers.slice(1),\n seen,\n );\n if (resolved) {\n resolved.from.push(filePath);\n }\n return resolved;\n }\n case \"styled-component\": {\n return {\n type: \"styled-component\",\n from: [filePath],\n source: filePath,\n name: specifiers[specifiers.length - 1],\n value: moduleExport.className,\n };\n }\n // usually at this point `tag-template` exports where already resolved to\n // styled-components if a matching styled-component comment was generated\n // by yak-swc. So resolving a value to a `tag-template` at this stage\n // would mean that the user tried to use the result of a call to a\n // different tag-template than yak's styled in a template. This is usually\n // invalid.\n //\n // But there is an issue with Nextjs. Next build in two passes, once for\n // the server bundle, once for the client bundle. During the server-side\n // build, each module with the `\"use client\"` directive is transformed to\n // throw errors if the exported symbol are used. This transformation\n // removes the comments generated by `@yak/swc`, so instead of the expected\n // `styled-component`, calls to `styled` resolve to a `tag-template`\n // (because no classname was found in the now absent comments).\n //\n // To summarize, if a \"use client\" bundle exports a styled component that\n // is used in a \"standard\" module, the resolve logic would throw with\n // \"unknown type tag-template\".\n //\n // To avoid this error, the resolve logic must handle those `tag-template`\n // with a special type `unresolved-tag`. Those will be interpolated to\n // valid CSS with minimal effect (to avoid CSS syntax error in the case of\n // Nextjs server build)\n case \"tag-template\": {\n return {\n type: \"unresolved-tag\",\n from: [filePath],\n source: filePath,\n name: specifiers[specifiers.length - 1],\n };\n }\n case \"constant\": {\n return {\n type: \"constant\",\n from: [filePath],\n source: filePath,\n value: moduleExport.value,\n };\n }\n case \"record\": {\n const resolvedInRecord = resolveSpecifierInRecord(\n moduleExport,\n specifiers[0],\n specifiers.slice(1),\n );\n return resolveModuleExport(context, filePath, resolvedInRecord, specifiers, seen);\n }\n case \"mixin\": {\n return {\n type: \"mixin\",\n from: [filePath],\n source: filePath,\n value: moduleExport.value,\n };\n }\n case \"unsupported\": {\n throw new UnsupportedExportError(failureMessage, {\n cause: explainUnsupported(\n filePath,\n specifiers.join(\".\"),\n moduleExport.hint,\n moduleExport.source,\n ),\n });\n }\n }\n } catch (error) {\n // UnsupportedExportError already carries this frame's \"Unable to resolve …\"\n // wrapper, so it bubbles up unchanged to avoid duplicating the line.\n if (error instanceof UnsupportedExportError) {\n throw error;\n }\n throw new ResolveError(failureMessage, { cause: error });\n }\n}\n\nfunction explainUnsupported(\n filePath: string,\n specifier: string,\n hint: string | undefined,\n source: UnsupportedExportSource | undefined,\n): string {\n const isYakFile = /\\.yak\\.(?:ts|tsx|js|jsx)$/.test(filePath);\n const docs =\n \"https://yak.js.org/docs/migration-from-styled-components#move-some-code-to-yak-files\";\n const snippet = source ? renderSourceSnippet(filePath, source, hint ?? \"\") : undefined;\n\n const lines: string[] = [];\n if (isYakFile) {\n const got = hint ? ` (got \\`${hint}\\`)` : \"\";\n lines.push(`\\`${specifier}\\` evaluated to a value that cannot be inlined into CSS${got}.`);\n if (snippet) lines.push(snippet);\n lines.push(\n ` help: replace it with a string, number, or plain object/array of those`,\n ` see: ${docs}`,\n );\n return lines.join(\"\\n\");\n }\n\n const got = hint ? ` (got a ${hint})` : \"\";\n lines.push(`\\`${specifier}\\` is not a string or number literal${got}.`);\n if (snippet) lines.push(snippet);\n lines.push(\n ` help: rename \"${filePath}\" to \"${suggestYakFileName(filePath)}\" so its exports run at build time`,\n ` (or replace \\`${specifier}\\` with a literal value)`,\n ` see: ${docs}`,\n );\n return lines.join(\"\\n\");\n}\n\nfunction suggestYakFileName(filePath: string): string {\n const match = filePath.match(/^(.*)\\.(ts|tsx|js|jsx)$/);\n if (!match) return filePath;\n return `${match[1]}.yak.${match[2]}`;\n}\n\n/**\n * Render a Rust-compiler-style snippet from a structural source location:\n *\n * --> /foo/colors.ts:5:18\n * |\n * 5 | export const bg = `var(${v})`;\n * | ^^^^^^^^^^^^ TemplateLiteral\n */\nfunction renderSourceSnippet(\n filePath: string,\n source: UnsupportedExportSource,\n label: string,\n): string {\n const startLine = source.start.line;\n const startCol = source.start.column;\n const sameLine = source.end.line === startLine;\n const caretLen = sameLine\n ? Math.max(1, source.end.column - startCol)\n : Math.max(1, source.lineText.length - startCol);\n\n const lineNumStr = String(startLine);\n const gutterPad = \" \".repeat(lineNumStr.length);\n\n return [\n ` --> ${filePath}:${startLine}:${startCol + 1}`,\n ` ${gutterPad} |`,\n ` ${lineNumStr} | ${source.lineText}`,\n ` ${gutterPad} | ${\" \".repeat(startCol)}${\"^\".repeat(caretLen)} ${label}`,\n ].join(\"\\n\");\n}\n\nfunction resolveSpecifierInRecord(\n record: ExtendedRecordExport,\n name: string,\n specifiers: string[],\n): ConstantExport | ResolvedStyledComponent | ResolvedMixin {\n if (specifiers.length === 0) {\n throw new ResolveError(\"did not expect an object\");\n }\n let depth = 0;\n let current: ResolvedExport = record;\n while (current && current.type === \"record\" && depth < specifiers.length) {\n current = current.value[specifiers[depth]];\n depth += 1;\n }\n\n if (current === undefined || depth !== specifiers.length) {\n throw new ResolveError(\n `Unable to resolve \"${specifiers.join(\".\")}\" in object/array \"${name}\"`,\n { cause: \"path not found\" },\n );\n }\n\n if (\n current.type === \"constant\" ||\n current.type === \"styled-component\" ||\n current.type === \"mixin\"\n ) {\n return current;\n }\n\n // mixins in .yak files are wrapped inside an object with a __yak key\n if (\n current.type === \"record\" &&\n \"__yak\" in current.value &&\n current.value.__yak.type === \"constant\"\n ) {\n return { type: \"mixin\", value: String(current.value.__yak.value) };\n }\n\n throw new ResolveError(`Unable to resolve \"${specifiers.join(\".\")}\" in object/array \"${name}\"`, {\n cause: \"only string and numbers are supported\",\n });\n}\n\n/**\n * hex SHA-1 hash of a message using webcrypto\n * Keeps yak independent from node api (therefore executable in browser)\n */\nasync function sha1(message: string) {\n const resultBuffer = await globalThis.crypto.subtle.digest(\n \"SHA-1\",\n new TextEncoder().encode(message),\n );\n return Array.from(new Uint8Array(resultBuffer), (byte) =>\n byte.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n}\n\ntype ResolvedCssImport =\n | {\n type: \"styled-component\";\n source: string;\n from: string[];\n name: string;\n value: string;\n }\n | {\n type: \"unresolved-tag\";\n source: string;\n from: string[];\n name: string;\n }\n | { type: \"mixin\"; source: string; from: string[]; value: string | number }\n | {\n type: \"constant\";\n source: string;\n from: string[];\n value: string | number;\n };\n\nexport type ResolveContext = {\n parse: (modulePath: string) => Promise<ParsedModule> | ParsedModule;\n cache?: {\n resolve?: Cache<Promise<{ resolved: ResolvedModule; dependencies: string[] }>>;\n resolveCrossFileConstant?: Cache<Promise<{ resolved: string; dependencies: string[] }>>;\n };\n exportAllLimit?: number;\n resolve: (specifier: string, importer: string) => Promise<string> | string;\n};\n\ntype YakImportKind = \"mixin\" | \"selector\";\n\ntype YakCssImport = {\n encodedArguments: string;\n moduleSpecifier: string;\n specifier: string[];\n importKind: YakImportKind;\n semicolon: string;\n position: number;\n size: number;\n};\n\nexport type ExtendedRecordExport = {\n type: \"record\";\n value: Record<string, ResolvedExport>;\n};\n\nexport type ResolvedMixin = { type: \"mixin\"; value: string };\nexport type ResolvedStyledComponent = {\n type: \"styled-component\";\n className: string;\n};\n\nexport type ResolvedExport =\n | Exclude<ModuleExport, RecordExport>\n | ExtendedRecordExport\n | ResolvedStyledComponent\n | ResolvedMixin;\n\nexport type ResolvedExports = {\n named: Record<string, ResolvedExport>;\n all: string[];\n};\n\nexport type ResolvedModule = {\n path: string;\n exports: ResolvedExports;\n};\n","/**\n * Import sources of all yak runtime packages, for code that must detect any of them.\n * Mirrors YakPackage in yak-swc's yak_imports.rs\n */\nexport const yakPackageNames = [\"@yak/react\", \"@yak/solid\"] as const;\n","import { parse } from \"@babel/parser\";\nimport type { Expression, ObjectExpression, TSAsExpression } from \"@babel/types\";\nimport {\n ModuleExport,\n ModuleExports,\n UnsupportedExportSource,\n} from \"../../cross-file-resolver/parseModule.js\";\nimport { yakPackageNames } from \"../../packageNames.js\";\n\n/**\n * Extracts a module's exports for the cross-file resolver by parsing with babel. Nothing is executed.\n *\n * Handles `export const`, export lists (`export { x }`), re-exports with a `from`,\n * `export *`, and `export default`.\n * Anything else becomes an `unsupported` entry with its source location, or is skipped.\n *\n * Throws if the source doesn't parse.\n */\nexport async function parseExports(sourceContents: string): Promise<ModuleExports> {\n try {\n const ast = parse(sourceContents, {\n sourceType: \"module\",\n plugins: [\"jsx\", \"typescript\"] as const,\n });\n\n // Derive importYak from top-level imports (no traverse needed)\n const importYak = ast.program.body.some(\n (node) =>\n node.type === \"ImportDeclaration\" &&\n (yakPackageNames as readonly string[]).includes(node.source.value),\n );\n\n const moduleExports: ModuleExports = {\n importYak,\n named: {},\n all: [],\n };\n\n // Track variable declarations for default export lookup\n const variableDeclarations: Record<string, Expression> = {};\n // Track top-level import bindings so exported imports become re-exports\n const importBindings: Record<string, ModuleExport> = {};\n // Local export lists (`export { x }`), resolved after the loop\n // because the list can come before the declaration\n const localExportSpecifiers: { local: string; exported: string }[] = [];\n let defaultIdentifier: string | null = null;\n\n for (const node of ast.program.body) {\n if (node.type === \"ImportDeclaration\") {\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportSpecifier\") {\n importBindings[specifier.local.name] = {\n type: \"re-export\",\n from: node.source.value,\n name:\n specifier.imported.type === \"Identifier\"\n ? specifier.imported.name\n : specifier.imported.value,\n };\n } else if (specifier.type === \"ImportDefaultSpecifier\") {\n importBindings[specifier.local.name] = {\n type: \"re-export\",\n from: node.source.value,\n name: \"default\",\n };\n } else if (specifier.type === \"ImportNamespaceSpecifier\") {\n importBindings[specifier.local.name] = {\n type: \"namespace-re-export\",\n from: node.source.value,\n };\n }\n }\n }\n\n // Track top-level variable declarations for default export lookup\n if (node.type === \"VariableDeclaration\") {\n for (const decl of node.declarations) {\n if (decl.id.type === \"Identifier\" && decl.init) {\n variableDeclarations[decl.id.name] = decl.init;\n }\n }\n }\n\n if (node.type === \"ExportNamedDeclaration\") {\n if (node.source) {\n // export { x } from \"./file\", export { x as y } from \"./file\"\n for (const specifier of node.specifiers) {\n if (\n specifier.type === \"ExportSpecifier\" &&\n specifier.exported.type === \"Identifier\" &&\n specifier.local.type === \"Identifier\"\n ) {\n moduleExports.named[specifier.exported.name] = {\n type: \"re-export\",\n from: node.source.value,\n name: specifier.local.name,\n };\n }\n // export * as ns from \"./file\"\n if (\n specifier.type === \"ExportNamespaceSpecifier\" &&\n specifier.exported.type === \"Identifier\"\n ) {\n moduleExports.named[specifier.exported.name] = {\n type: \"namespace-re-export\",\n from: node.source.value,\n };\n }\n }\n } else if (!node.declaration) {\n // export { x }, export { x as y }\n for (const specifier of node.specifiers) {\n if (\n specifier.type === \"ExportSpecifier\" &&\n specifier.exported.type === \"Identifier\" &&\n specifier.local.type === \"Identifier\"\n ) {\n localExportSpecifiers.push({\n local: specifier.local.name,\n exported: specifier.exported.name,\n });\n }\n }\n } else if (node.declaration.type === \"VariableDeclaration\") {\n // export const x = ...\n for (const declaration of node.declaration.declarations) {\n if (declaration.id.type === \"Identifier\" && declaration.init) {\n variableDeclarations[declaration.id.name] = declaration.init;\n const parsed = parseExportValueExpression(declaration.init, sourceContents);\n if (parsed) {\n moduleExports.named[declaration.id.name] = parsed;\n }\n }\n }\n }\n }\n\n if (node.type === \"ExportDefaultDeclaration\") {\n if (node.declaration.type === \"Identifier\") {\n // e.g. export default variableName;\n // Save the identifier name to look up later\n defaultIdentifier = node.declaration.name;\n } else if (\n node.declaration.type === \"FunctionDeclaration\" ||\n node.declaration.type === \"ClassDeclaration\"\n ) {\n // e.g. export default function() {...} or export default class {...}\n moduleExports.named[\"default\"] = {\n type: \"unsupported\",\n hint: node.declaration.type,\n source: extractUnsupportedSource(node.declaration.loc, sourceContents),\n };\n } else {\n // e.g. export default { ... } or export default \"value\"\n moduleExports.named[\"default\"] = parseExportValueExpression(\n node.declaration as Expression,\n sourceContents,\n );\n }\n }\n\n // export * from \"./file\"\n if (node.type === \"ExportAllDeclaration\") {\n moduleExports.all.push(node.source.value);\n }\n }\n\n // Look up the value of an exported identifier from local declarations or imports\n const resolveIdentifier = (name: string): ModuleExport | undefined => {\n if (variableDeclarations[name]) {\n return parseExportValueExpression(variableDeclarations[name], sourceContents);\n }\n return importBindings[name];\n };\n\n for (const { local, exported } of localExportSpecifiers) {\n const resolved = resolveIdentifier(local);\n if (resolved) {\n moduleExports.named[exported] = resolved;\n }\n }\n\n // If we found a default export that's an identifier, look up its value\n if (defaultIdentifier) {\n const resolved = resolveIdentifier(defaultIdentifier);\n if (resolved) {\n moduleExports.named[\"default\"] = resolved;\n }\n }\n\n return moduleExports;\n } catch (error) {\n throw new Error(`Error parsing exports: ${(error as Error).message}`);\n }\n}\n\n/**\n * Unpacks TS type assertions (as, satisfies) to the underlying expression\n */\nfunction unpackTSAsExpression(node: TSAsExpression | Expression): Expression {\n if (node.type === \"TSAsExpression\" || node.type === \"TSSatisfiesExpression\") {\n return unpackTSAsExpression((node as TSAsExpression).expression);\n }\n return node;\n}\n\nfunction parseExportValueExpression(node: Expression, code?: string): ModuleExport {\n // ignores `as` casts so it doesn't interfere with the ast node type detection\n const expression = unpackTSAsExpression(node);\n if (expression.type === \"CallExpression\" || expression.type === \"TaggedTemplateExpression\") {\n return { type: \"tag-template\" };\n } else if (expression.type === \"StringLiteral\" || expression.type === \"NumericLiteral\") {\n return { type: \"constant\", value: expression.value };\n } else if (\n expression.type === \"UnaryExpression\" &&\n expression.operator === \"-\" &&\n expression.argument.type === \"NumericLiteral\"\n ) {\n return { type: \"constant\", value: -expression.argument.value };\n } else if (expression.type === \"TemplateLiteral\" && expression.quasis.length === 1) {\n return { type: \"constant\", value: expression.quasis[0].value.raw };\n } else if (expression.type === \"ObjectExpression\") {\n return {\n type: \"record\",\n value: parseObjectExpression(expression, code),\n };\n }\n return {\n type: \"unsupported\",\n hint: expression.type,\n source: extractUnsupportedSource(expression.loc, code),\n };\n}\n\nfunction parseObjectExpression(\n node: ObjectExpression,\n code?: string,\n): Record<string, ModuleExport> {\n let result: Record<string, ModuleExport> = {};\n for (const property of node.properties) {\n if (property.type === \"ObjectProperty\" && property.key.type === \"Identifier\") {\n const key = property.key.name;\n const parsed = parseExportValueExpression(property.value as Expression, code);\n if (parsed) {\n result[key] = parsed;\n }\n }\n }\n return result;\n}\n\n/**\n * Pull the structural source-location data the error formatter needs to\n * render a snippet — the formatter (not this parser) is responsible for\n * any presentation. Returns undefined if loc or source text is missing.\n */\nfunction extractUnsupportedSource(\n loc:\n | {\n start: { line: number; column: number };\n end: { line: number; column: number };\n }\n | null\n | undefined,\n code: string | undefined,\n): UnsupportedExportSource | undefined {\n if (!loc || !code) return undefined;\n const lineText = code.split(/\\r?\\n/)[loc.start.line - 1];\n if (lineText === undefined) return undefined;\n return {\n start: { line: loc.start.line, column: loc.start.column },\n end: { line: loc.end.line, column: loc.end.column },\n lineText,\n };\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,13 +4,13 @@ interface YakTheme {}
|
|
|
4
4
|
/**
|
|
5
5
|
* Returns the current yak theme context
|
|
6
6
|
*
|
|
7
|
-
* @see https://github.com/
|
|
7
|
+
* @see https://github.com/galaxuslabs/yak/blob/main/packages/yak-react/runtime/context/README.md
|
|
8
8
|
*/
|
|
9
9
|
export declare const useTheme: () => YakTheme;
|
|
10
10
|
/**
|
|
11
11
|
* Yak theme context provider
|
|
12
12
|
*
|
|
13
|
-
* @see https://github.com/
|
|
13
|
+
* @see https://github.com/galaxuslabs/yak/blob/main/packages/yak-react/runtime/context/README.md
|
|
14
14
|
*/
|
|
15
15
|
export declare const YakThemeProvider: ({ children, theme }: {
|
|
16
16
|
children: ReactNode;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { isAbsolute } from "node:path";
|
|
2
|
+
import { dirname, isAbsolute, join } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { Worker } from "node:worker_threads";
|
|
5
5
|
//#region ../yak-internals/isolated-source-eval/loader-hooks.ts
|
|
@@ -35,7 +35,7 @@ function createLoaderDataUrl() {
|
|
|
35
35
|
function resolveWorkerPath() {
|
|
36
36
|
const jsPath = fileURLToPath(new URL("./worker.js", import.meta.url));
|
|
37
37
|
if (existsSync(jsPath)) return jsPath;
|
|
38
|
-
return fileURLToPath(
|
|
38
|
+
return join(dirname(fileURLToPath(import.meta.url)), "worker.ts");
|
|
39
39
|
}
|
|
40
40
|
function bootWorker() {
|
|
41
41
|
const workerPath = resolveWorkerPath();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../yak-internals/isolated-source-eval/loader-hooks.ts","../../../yak-internals/isolated-source-eval/evaluator.ts"],"sourcesContent":["/**\n * ESM loader hook source, embedded as a string constant.\n *\n * This MUST be plain JavaScript — not TypeScript. The loader thread receives\n * this code via a `data:` URL passed to `module.register()`. Node's loader\n * thread does not run data: URLs through strip-types, so any TypeScript\n * syntax here would cause a SyntaxError at registration time.\n *\n * The hook uses a MessagePort (received in `initialize`) to communicate\n * collected dependencies back to the worker thread on demand. Dependencies\n * are accumulated in a Set during `resolve()` calls, then flushed as a\n * batch when the worker requests them after an evaluation completes. This\n * batching avoids per-import message overhead and keeps dependency tracking\n * tightly coupled to a single evaluation.\n *\n * Only `file://` URLs outside of `node_modules` are tracked. Node built-ins\n * (`node:*`) and third-party packages are excluded because consumers only\n * need to invalidate on project source file changes.\n *\n * `fileURLToPath` converts `file://` URLs into platform-correct absolute\n * paths (avoids the leading-slash issue `URL.pathname` has on Windows).\n */\nconst LOADER_HOOK_SOURCE = `\nimport { fileURLToPath } from \"node:url\";\n\nlet port;\nconst deps = new Set();\n\nexport function initialize(data) {\n port = data.port;\n port.on(\"message\", (msg) => {\n if (msg === \"getDeps\") {\n port.postMessage([...deps]);\n deps.clear();\n }\n });\n}\n\nexport async function resolve(specifier, context, nextResolve) {\n const result = await nextResolve(specifier);\n if (result.url.startsWith(\"file://\") && !result.url.includes(\"/node_modules/\")) {\n deps.add(fileURLToPath(result.url));\n }\n return result;\n}\n`;\n\n/**\n * Pre-computed data URL for the loader hook source.\n *\n * The same URL is reused across workers — each `module.register()` call\n * creates an independent loader instance with its own dependency Set\n * regardless of URL identity.\n */\nconst LOADER_DATA_URL = `data:text/javascript;base64,${Buffer.from(LOADER_HOOK_SOURCE).toString(\"base64\")}`;\n\n/** Returns the loader hook as a base64 `data:` URL. */\nexport function createLoaderDataUrl(): string {\n return LOADER_DATA_URL;\n}\n","import { existsSync } from \"node:fs\";\nimport { isAbsolute } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Worker } from \"node:worker_threads\";\nimport { createLoaderDataUrl } from \"./loader-hooks.ts\";\n\nexport type EvaluateResult =\n | { ok: true; value: Record<string, unknown>; dependencies: string[] }\n | { ok: false; error: { message: string; stack: string } };\n\nexport interface Evaluator {\n /** Evaluate a module and return its exports plus the full transitive dependency list. */\n evaluate(absolutePath: string): Promise<EvaluateResult>;\n /** Clear cached results for every entry point that transitively depends on the given file(s). */\n invalidate(...absolutePaths: string[]): void;\n /** Clear the entire result cache and swap workers. */\n invalidateAll(): void;\n /** Return entry point paths that would be affected by invalidate(absolutePath). */\n getDependentsOf(absolutePath: string): string[];\n /** Terminate both workers and reject any pending evaluations. */\n dispose(): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Wire format for messages from the worker back to the main thread. */\ntype WorkerResult =\n | {\n type: \"result\";\n id: number;\n ok: true;\n value: Record<string, unknown>;\n dependencies: string[];\n }\n | {\n type: \"result\";\n id: number;\n ok: false;\n error: { message: string; stack: string };\n dependencies: string[];\n };\n\ninterface PendingEvaluation {\n id: number;\n absolutePath: string;\n resolve: (result: EvaluateResult) => void;\n reject: (error: Error) => void;\n /** Number of times this evaluation has been retried due to staleness detection. */\n retryCount: number;\n}\n\n/**\n * Resolves the worker entry point path.\n *\n * In production the compiled `.js` file exists in `dist/` — we prefer that.\n * During development (vitest runs against source), only the `.ts` file exists.\n * Node 22.18.0+ handles `.ts` natively via strip-types, so the fallback works\n * without any flags or loaders.\n */\nfunction resolveWorkerPath(): string {\n const jsPath = fileURLToPath(new URL(\"./worker.js\", import.meta.url));\n if (existsSync(jsPath)) {\n return jsPath;\n }\n return fileURLToPath(new URL(\"./worker.ts\", import.meta.url));\n}\n\n/**\n * Boots a worker and returns a handle with a ready promise.\n *\n * The loader hook data URL is passed via `workerData` rather than having\n * the worker import it from a sibling module. This avoids a module\n * resolution mismatch: source files use `.ts` extensions (required by\n * Node strip-types) while compiled output uses `.js` (rewritten by tsc\n * via `rewriteRelativeImportExtensions`). Since the worker file would need\n * to import loader-hooks with the right extension for its context, passing\n * the data URL through workerData eliminates the problem entirely.\n */\nfunction bootWorker(): { worker: Worker; ready: Promise<void> } {\n const workerPath = resolveWorkerPath();\n const loaderDataUrl = createLoaderDataUrl();\n const worker = new Worker(workerPath, {\n workerData: { loaderDataUrl },\n });\n const ready = new Promise<void>((resolve) => {\n const onMessage = (msg: { type: string }) => {\n if (msg.type === \"ready\") {\n worker.off(\"message\", onMessage);\n resolve();\n }\n };\n worker.on(\"message\", onMessage);\n });\n return { worker, ready };\n}\n\n/** Default timeout for a single evaluation in milliseconds. */\nconst DEFAULT_EVAL_TIMEOUT_MS = 30_000;\n\n/** Maximum number of staleness retries before giving up with an error. */\nconst MAX_STALENESS_RETRIES = 3;\n\n/** Creates an evaluator that runs modules in isolated worker threads with dependency tracking. */\nexport async function createEvaluator(): Promise<Evaluator> {\n const resultCache = new Map<string, EvaluateResult>();\n /**\n * Tracks in-flight evaluation promises by path. When `evaluate()` is called\n * for a path that's already queued or being evaluated, the same promise is\n * returned instead of queuing a duplicate. This prevents a second worker\n * evaluation from producing incomplete dependencies (the ESM resolve hook\n * only fires on cache misses — a second import on the same worker would\n * miss transitive deps that are already in the ESM cache).\n */\n const inflight = new Map<string, Promise<EvaluateResult>>();\n\n /**\n * Two dependency maps provide O(1) lookups in both directions:\n *\n * - `reverseDeps`: file path → set of entry points that depend on it.\n * Consulted during `invalidate()` to find which cached results to clear.\n *\n * - `forwardDeps`: entry point → set of all its transitive dependencies.\n * Needed to clean up stale reverse entries when an entry point is\n * re-evaluated and its dependency tree has changed (e.g. an import\n * was removed). Without this, removed dependencies would accumulate\n * phantom reverse entries that never get cleaned up.\n */\n const reverseDeps = new Map<string, Set<string>>();\n const forwardDeps = new Map<string, Set<string>>();\n\n let nextId = 0;\n let currentEval: PendingEvaluation | null = null;\n let currentTimeout: ReturnType<typeof setTimeout> | null = null;\n const queue: PendingEvaluation[] = [];\n /**\n * Tracks files passed to `invalidate()` while an evaluation is in-flight.\n * When an evaluation completes, `handleResult()` checks whether any reported\n * dependency intersects this set. If so, the result is stale — it's discarded,\n * workers are swapped, and the evaluation is retried on a clean worker.\n *\n * This closes the \"cold start\" gap: if `evaluate(\"A\")` is dispatched before\n * `invalidate(\"B\")` is called, and A depends on B, the dependency graph\n * doesn't know about A→B yet. Without this set, the invalidation would be\n * silently missed.\n *\n * Cleared in `swapWorkers()` because the new primary has a clean ESM cache.\n */\n const invalidatedDuringEval = new Set<string>();\n let disposed = false;\n\n /**\n * Two workers are maintained at all times:\n *\n * - **primary**: handles all evaluate() calls. Accumulates Node's ESM\n * module cache over time, making repeated evaluations fast (~1-3ms).\n *\n * - **shadow**: pre-booted but completely idle. Never evaluates anything,\n * so its module cache stays empty. On invalidation, the primary is\n * terminated (stale cache) and the shadow is promoted to primary\n * instantly (no boot latency). A new shadow is booted in the background.\n *\n * This pattern trades ~30ms of background boot time for instant\n * invalidation. The alternative — reusing a single worker and clearing\n * its ESM cache — isn't possible because Node doesn't expose an API\n * to clear the module cache.\n */\n let primary = bootWorker();\n let shadow = bootWorker();\n\n await Promise.all([primary.ready, shadow.ready]);\n\n /**\n * Allow the process to exit naturally when the event loop is otherwise empty.\n * Without this, the workers keep the event loop alive indefinitely — causing\n * hangs in multi-environment Vite builds where no hook reliably disposes\n * the evaluator after all environments are done.\n * Must be called after setupMessageHandler since .on() can re-ref the worker.\n */\n function unrefWorkers(primaryWorker: Worker, shadowWorker: Worker) {\n primaryWorker.unref();\n shadowWorker.unref();\n }\n\n function setupMessageHandler(workerObj: { worker: Worker }) {\n workerObj.worker.on(\"message\", (msg: WorkerResult) => {\n if (msg.type !== \"result\") return;\n handleResult(msg);\n });\n\n workerObj.worker.on(\"error\", (err: Error) => {\n if (currentEval) {\n clearEvalTimeout();\n const pending = currentEval;\n currentEval = null;\n const result: EvaluateResult = {\n ok: false,\n error: { message: err.message, stack: err.stack ?? \"\" },\n };\n updateDeps(pending.absolutePath, [pending.absolutePath]);\n resultCache.set(pending.absolutePath, result);\n inflight.delete(pending.absolutePath);\n pending.resolve(result);\n processQueue();\n }\n });\n }\n\n setupMessageHandler(primary);\n unrefWorkers(primary.worker, shadow.worker);\n\n function clearEvalTimeout() {\n if (currentTimeout !== null) {\n clearTimeout(currentTimeout);\n currentTimeout = null;\n }\n }\n\n function handleResult(msg: WorkerResult) {\n if (!currentEval) return;\n if (msg.id !== currentEval.id) return;\n\n clearEvalTimeout();\n\n const pending = currentEval;\n currentEval = null;\n\n // Staleness check: if a file was invalidated while this evaluation was\n // in-flight and the result depends on it, discard the result and retry\n // on a clean worker. Only checked for successes — errors don't report\n // deps, and the file watcher will trigger another invalidation when the\n // source is fixed.\n if (msg.ok && invalidatedDuringEval.size > 0) {\n const isStale = msg.dependencies.some((dep) => invalidatedDuringEval.has(dep));\n if (isStale) {\n if (pending.retryCount >= MAX_STALENESS_RETRIES) {\n pending.resolve({\n ok: false,\n error: {\n message: `Evaluation of ${pending.absolutePath} exceeded maximum staleness retries (${MAX_STALENESS_RETRIES})`,\n stack: \"\",\n },\n });\n processQueue();\n return;\n }\n pending.retryCount++;\n swapWorkers();\n queue.unshift(pending);\n processQueue();\n return;\n }\n }\n\n let result: EvaluateResult;\n if (msg.ok) {\n result = { ok: true, value: msg.value, dependencies: msg.dependencies };\n updateDeps(pending.absolutePath, msg.dependencies);\n } else {\n result = { ok: false, error: msg.error };\n // Use the partial dependencies reported by the worker (files resolved\n // before the error). This ensures transitive deps are registered in\n // reverseDeps so that fixing a broken file triggers re-evaluation.\n updateDeps(pending.absolutePath, msg.dependencies);\n }\n\n resultCache.set(pending.absolutePath, result);\n inflight.delete(pending.absolutePath);\n pending.resolve(result);\n processQueue();\n }\n\n /**\n * Reconciles the dependency graph after an evaluation.\n *\n * First removes all old reverse entries for this entry point (using\n * forwardDeps as the source of truth), then writes the new forward\n * and reverse mappings. This ensures that if a dependency was removed\n * between evaluations, it no longer points back to this entry point.\n */\n function updateDeps(entryPoint: string, deps: string[]) {\n const oldDeps = forwardDeps.get(entryPoint);\n if (oldDeps) {\n for (const dep of oldDeps) {\n const entries = reverseDeps.get(dep);\n if (entries) {\n entries.delete(entryPoint);\n if (entries.size === 0) {\n reverseDeps.delete(dep);\n }\n }\n }\n }\n\n const depSet = new Set(deps);\n forwardDeps.set(entryPoint, depSet);\n\n for (const dep of depSet) {\n let entries = reverseDeps.get(dep);\n if (!entries) {\n entries = new Set();\n reverseDeps.set(dep, entries);\n }\n entries.add(entryPoint);\n }\n }\n\n /**\n * Evaluations are serialized — one at a time per worker. This prevents\n * interleaved dependency tracking: the loader hook accumulates deps in\n * a single Set and flushes it after each evaluation. Concurrent imports\n * would mix dependencies from different entry points.\n *\n * Waits for the primary worker to be ready before sending work. This\n * matters after a swap: the promoted shadow may still be booting.\n */\n function processQueue() {\n if (currentEval || queue.length === 0 || disposed) return;\n\n const next = queue.shift()!;\n\n // A prior evaluation for the same path may have completed and cached\n // while this item was queued (e.g. after invalidation + re-queue).\n // Resolving from cache avoids dispatching to a worker where the module\n // is already in the ESM cache (which would produce incomplete deps).\n const alreadyCached = resultCache.get(next.absolutePath);\n if (alreadyCached) {\n inflight.delete(next.absolutePath);\n next.resolve(alreadyCached);\n processQueue();\n return;\n }\n\n currentEval = next;\n\n primary.ready.then(() => {\n if (disposed || currentEval !== next) return;\n primary.worker.postMessage({\n type: \"evaluate\",\n id: next.id,\n absolutePath: next.absolutePath,\n });\n\n currentTimeout = setTimeout(() => {\n if (currentEval !== next) return;\n currentEval = null;\n currentTimeout = null;\n\n const result: EvaluateResult = {\n ok: false,\n error: {\n message: `Evaluation of ${next.absolutePath} timed out after ${DEFAULT_EVAL_TIMEOUT_MS}ms`,\n stack: \"\",\n },\n };\n updateDeps(next.absolutePath, [next.absolutePath]);\n resultCache.set(next.absolutePath, result);\n inflight.delete(next.absolutePath);\n next.resolve(result);\n\n swapWorkers();\n processQueue();\n }, DEFAULT_EVAL_TIMEOUT_MS);\n });\n }\n\n /**\n * Terminates the primary (stale module cache), promotes the shadow\n * (clean cache, instant), and boots a new shadow in the background.\n *\n * `removeAllListeners` prevents the terminated worker from delivering\n * late messages or error events that would confuse the result handler.\n */\n function swapWorkers() {\n primary.worker.removeAllListeners();\n primary.worker.terminate();\n\n primary = shadow;\n setupMessageHandler(primary);\n\n shadow = bootWorker();\n unrefWorkers(primary.worker, shadow.worker);\n\n // The new primary has a clean ESM cache, so any previously tracked\n // invalidations are no longer relevant — the fresh worker will\n // re-import everything from disk.\n invalidatedDuringEval.clear();\n }\n\n function evaluate(absolutePath: string): Promise<EvaluateResult> {\n if (disposed) {\n return Promise.reject(new Error(\"Evaluator has been disposed\"));\n }\n\n if (!isAbsolute(absolutePath)) {\n return Promise.reject(\n new Error(`evaluate() requires an absolute path, got: ${absolutePath}`),\n );\n }\n\n const cached = resultCache.get(absolutePath);\n if (cached) {\n return Promise.resolve(cached);\n }\n\n // Deduplicate: if an evaluation is already queued or in-flight for this\n // path, return the same promise. This prevents a second worker evaluation\n // that would produce incomplete dependencies (the ESM resolve hook only\n // fires on cache misses).\n const pending = inflight.get(absolutePath);\n if (pending) return pending;\n\n const promise = new Promise<EvaluateResult>((resolve, reject) => {\n const id = nextId++;\n queue.push({ id, absolutePath, resolve, reject, retryCount: 0 });\n processQueue();\n });\n\n inflight.set(absolutePath, promise);\n return promise;\n }\n\n function invalidate(...absolutePaths: string[]): void {\n if (absolutePaths.length === 0) return;\n\n // Track all invalidations for cold-start detection (in-flight evaluations\n // whose dependency graph isn't populated yet).\n for (const path of absolutePaths) {\n invalidatedDuringEval.add(path);\n }\n\n // Collect all affected entry points across all changed files.\n const allEntryPoints = new Set<string>();\n for (const path of absolutePaths) {\n const entryPoints = reverseDeps.get(path);\n if (entryPoints) {\n for (const entry of entryPoints) {\n allEntryPoints.add(entry);\n }\n }\n }\n\n // If no cached entry points are affected, return early. The cold-start\n // set (invalidatedDuringEval) will catch in-flight evaluations.\n if (allEntryPoints.size === 0) return;\n\n // Clear caches and clean dependency graphs for all affected entry points.\n for (const entry of allEntryPoints) {\n resultCache.delete(entry);\n inflight.delete(entry);\n\n const deps = forwardDeps.get(entry);\n if (deps) {\n for (const dep of deps) {\n const entries = reverseDeps.get(dep);\n if (entries) {\n entries.delete(entry);\n if (entries.size === 0) {\n reverseDeps.delete(dep);\n }\n }\n }\n forwardDeps.delete(entry);\n }\n }\n\n // If an evaluation is in-flight on the worker we're about to terminate,\n // capture it and re-queue at the front. The caller's promise will\n // resolve transparently with the result from the promoted shadow worker.\n clearEvalTimeout();\n const inflightEval = currentEval;\n currentEval = null;\n\n swapWorkers();\n\n if (inflightEval) {\n queue.unshift(inflightEval);\n }\n\n processQueue();\n }\n\n function invalidateAll(): void {\n resultCache.clear();\n inflight.clear();\n forwardDeps.clear();\n reverseDeps.clear();\n\n clearEvalTimeout();\n const inflightEval = currentEval;\n currentEval = null;\n\n swapWorkers();\n\n if (inflightEval) {\n queue.unshift(inflightEval);\n }\n\n processQueue();\n }\n\n function getDependentsOf(absolutePath: string): string[] {\n const entryPoints = reverseDeps.get(absolutePath);\n if (!entryPoints) return [];\n return [...entryPoints];\n }\n\n async function dispose(): Promise<void> {\n if (disposed) return;\n disposed = true;\n\n clearEvalTimeout();\n\n if (currentEval) {\n currentEval.reject(new Error(\"Evaluator has been disposed\"));\n currentEval = null;\n }\n for (const pending of queue) {\n pending.reject(new Error(\"Evaluator has been disposed\"));\n }\n queue.length = 0;\n inflight.clear();\n\n await Promise.all([primary.worker.terminate(), shadow.worker.terminate()]);\n }\n\n return {\n evaluate,\n invalidate,\n invalidateAll,\n getDependentsOf,\n dispose,\n [Symbol.asyncDispose]: dispose,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../yak-internals/isolated-source-eval/loader-hooks.ts","../../../yak-internals/isolated-source-eval/evaluator.ts"],"sourcesContent":["/**\n * ESM loader hook source, embedded as a string constant.\n *\n * This MUST be plain JavaScript — not TypeScript. The loader thread receives\n * this code via a `data:` URL passed to `module.register()`. Node's loader\n * thread does not run data: URLs through strip-types, so any TypeScript\n * syntax here would cause a SyntaxError at registration time.\n *\n * The hook uses a MessagePort (received in `initialize`) to communicate\n * collected dependencies back to the worker thread on demand. Dependencies\n * are accumulated in a Set during `resolve()` calls, then flushed as a\n * batch when the worker requests them after an evaluation completes. This\n * batching avoids per-import message overhead and keeps dependency tracking\n * tightly coupled to a single evaluation.\n *\n * Only `file://` URLs outside of `node_modules` are tracked. Node built-ins\n * (`node:*`) and third-party packages are excluded because consumers only\n * need to invalidate on project source file changes.\n *\n * `fileURLToPath` converts `file://` URLs into platform-correct absolute\n * paths (avoids the leading-slash issue `URL.pathname` has on Windows).\n */\nconst LOADER_HOOK_SOURCE = `\nimport { fileURLToPath } from \"node:url\";\n\nlet port;\nconst deps = new Set();\n\nexport function initialize(data) {\n port = data.port;\n port.on(\"message\", (msg) => {\n if (msg === \"getDeps\") {\n port.postMessage([...deps]);\n deps.clear();\n }\n });\n}\n\nexport async function resolve(specifier, context, nextResolve) {\n const result = await nextResolve(specifier);\n if (result.url.startsWith(\"file://\") && !result.url.includes(\"/node_modules/\")) {\n deps.add(fileURLToPath(result.url));\n }\n return result;\n}\n`;\n\n/**\n * Pre-computed data URL for the loader hook source.\n *\n * The same URL is reused across workers — each `module.register()` call\n * creates an independent loader instance with its own dependency Set\n * regardless of URL identity.\n */\nconst LOADER_DATA_URL = `data:text/javascript;base64,${Buffer.from(LOADER_HOOK_SOURCE).toString(\"base64\")}`;\n\n/** Returns the loader hook as a base64 `data:` URL. */\nexport function createLoaderDataUrl(): string {\n return LOADER_DATA_URL;\n}\n","import { existsSync } from \"node:fs\";\nimport { dirname, isAbsolute, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Worker } from \"node:worker_threads\";\nimport { createLoaderDataUrl } from \"./loader-hooks.ts\";\n\nexport type EvaluateResult =\n | { ok: true; value: Record<string, unknown>; dependencies: string[] }\n | { ok: false; error: { message: string; stack: string } };\n\nexport interface Evaluator {\n /** Evaluate a module and return its exports plus the full transitive dependency list. */\n evaluate(absolutePath: string): Promise<EvaluateResult>;\n /** Clear cached results for every entry point that transitively depends on the given file(s). */\n invalidate(...absolutePaths: string[]): void;\n /** Clear the entire result cache and swap workers. */\n invalidateAll(): void;\n /** Return entry point paths that would be affected by invalidate(absolutePath). */\n getDependentsOf(absolutePath: string): string[];\n /** Terminate both workers and reject any pending evaluations. */\n dispose(): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/** Wire format for messages from the worker back to the main thread. */\ntype WorkerResult =\n | {\n type: \"result\";\n id: number;\n ok: true;\n value: Record<string, unknown>;\n dependencies: string[];\n }\n | {\n type: \"result\";\n id: number;\n ok: false;\n error: { message: string; stack: string };\n dependencies: string[];\n };\n\ninterface PendingEvaluation {\n id: number;\n absolutePath: string;\n resolve: (result: EvaluateResult) => void;\n reject: (error: Error) => void;\n /** Number of times this evaluation has been retried due to staleness detection. */\n retryCount: number;\n}\n\n/**\n * Resolves the worker entry point path.\n *\n * In production the compiled `.js` file exists in `dist/` — we prefer that.\n * During development (vitest runs against source), only the `.ts` file exists.\n * Node 22.18.0+ handles `.ts` natively via strip-types, so the fallback works\n * without any flags or loaders.\n */\nfunction resolveWorkerPath(): string {\n const jsPath = fileURLToPath(new URL(\"./worker.js\", import.meta.url));\n if (existsSync(jsPath)) {\n return jsPath;\n }\n // No `new URL()` here: turbopack resolve it statically and warns, because `dist/` has no `.ts` file\n return join(dirname(fileURLToPath(import.meta.url)), \"worker.ts\");\n}\n\n/**\n * Boots a worker and returns a handle with a ready promise.\n *\n * The loader hook data URL is passed via `workerData` rather than having\n * the worker import it from a sibling module. This avoids a module\n * resolution mismatch: source files use `.ts` extensions (required by\n * Node strip-types) while compiled output uses `.js` (rewritten by tsc\n * via `rewriteRelativeImportExtensions`). Since the worker file would need\n * to import loader-hooks with the right extension for its context, passing\n * the data URL through workerData eliminates the problem entirely.\n */\nfunction bootWorker(): { worker: Worker; ready: Promise<void> } {\n const workerPath = resolveWorkerPath();\n const loaderDataUrl = createLoaderDataUrl();\n const worker = new Worker(workerPath, {\n workerData: { loaderDataUrl },\n });\n const ready = new Promise<void>((resolve) => {\n const onMessage = (msg: { type: string }) => {\n if (msg.type === \"ready\") {\n worker.off(\"message\", onMessage);\n resolve();\n }\n };\n worker.on(\"message\", onMessage);\n });\n return { worker, ready };\n}\n\n/** Default timeout for a single evaluation in milliseconds. */\nconst DEFAULT_EVAL_TIMEOUT_MS = 30_000;\n\n/** Maximum number of staleness retries before giving up with an error. */\nconst MAX_STALENESS_RETRIES = 3;\n\n/** Creates an evaluator that runs modules in isolated worker threads with dependency tracking. */\nexport async function createEvaluator(): Promise<Evaluator> {\n const resultCache = new Map<string, EvaluateResult>();\n /**\n * Tracks in-flight evaluation promises by path. When `evaluate()` is called\n * for a path that's already queued or being evaluated, the same promise is\n * returned instead of queuing a duplicate. This prevents a second worker\n * evaluation from producing incomplete dependencies (the ESM resolve hook\n * only fires on cache misses — a second import on the same worker would\n * miss transitive deps that are already in the ESM cache).\n */\n const inflight = new Map<string, Promise<EvaluateResult>>();\n\n /**\n * Two dependency maps provide O(1) lookups in both directions:\n *\n * - `reverseDeps`: file path → set of entry points that depend on it.\n * Consulted during `invalidate()` to find which cached results to clear.\n *\n * - `forwardDeps`: entry point → set of all its transitive dependencies.\n * Needed to clean up stale reverse entries when an entry point is\n * re-evaluated and its dependency tree has changed (e.g. an import\n * was removed). Without this, removed dependencies would accumulate\n * phantom reverse entries that never get cleaned up.\n */\n const reverseDeps = new Map<string, Set<string>>();\n const forwardDeps = new Map<string, Set<string>>();\n\n let nextId = 0;\n let currentEval: PendingEvaluation | null = null;\n let currentTimeout: ReturnType<typeof setTimeout> | null = null;\n const queue: PendingEvaluation[] = [];\n /**\n * Tracks files passed to `invalidate()` while an evaluation is in-flight.\n * When an evaluation completes, `handleResult()` checks whether any reported\n * dependency intersects this set. If so, the result is stale — it's discarded,\n * workers are swapped, and the evaluation is retried on a clean worker.\n *\n * This closes the \"cold start\" gap: if `evaluate(\"A\")` is dispatched before\n * `invalidate(\"B\")` is called, and A depends on B, the dependency graph\n * doesn't know about A→B yet. Without this set, the invalidation would be\n * silently missed.\n *\n * Cleared in `swapWorkers()` because the new primary has a clean ESM cache.\n */\n const invalidatedDuringEval = new Set<string>();\n let disposed = false;\n\n /**\n * Two workers are maintained at all times:\n *\n * - **primary**: handles all evaluate() calls. Accumulates Node's ESM\n * module cache over time, making repeated evaluations fast (~1-3ms).\n *\n * - **shadow**: pre-booted but completely idle. Never evaluates anything,\n * so its module cache stays empty. On invalidation, the primary is\n * terminated (stale cache) and the shadow is promoted to primary\n * instantly (no boot latency). A new shadow is booted in the background.\n *\n * This pattern trades ~30ms of background boot time for instant\n * invalidation. The alternative — reusing a single worker and clearing\n * its ESM cache — isn't possible because Node doesn't expose an API\n * to clear the module cache.\n */\n let primary = bootWorker();\n let shadow = bootWorker();\n\n await Promise.all([primary.ready, shadow.ready]);\n\n /**\n * Allow the process to exit naturally when the event loop is otherwise empty.\n * Without this, the workers keep the event loop alive indefinitely — causing\n * hangs in multi-environment Vite builds where no hook reliably disposes\n * the evaluator after all environments are done.\n * Must be called after setupMessageHandler since .on() can re-ref the worker.\n */\n function unrefWorkers(primaryWorker: Worker, shadowWorker: Worker) {\n primaryWorker.unref();\n shadowWorker.unref();\n }\n\n function setupMessageHandler(workerObj: { worker: Worker }) {\n workerObj.worker.on(\"message\", (msg: WorkerResult) => {\n if (msg.type !== \"result\") return;\n handleResult(msg);\n });\n\n workerObj.worker.on(\"error\", (err: Error) => {\n if (currentEval) {\n clearEvalTimeout();\n const pending = currentEval;\n currentEval = null;\n const result: EvaluateResult = {\n ok: false,\n error: { message: err.message, stack: err.stack ?? \"\" },\n };\n updateDeps(pending.absolutePath, [pending.absolutePath]);\n resultCache.set(pending.absolutePath, result);\n inflight.delete(pending.absolutePath);\n pending.resolve(result);\n processQueue();\n }\n });\n }\n\n setupMessageHandler(primary);\n unrefWorkers(primary.worker, shadow.worker);\n\n function clearEvalTimeout() {\n if (currentTimeout !== null) {\n clearTimeout(currentTimeout);\n currentTimeout = null;\n }\n }\n\n function handleResult(msg: WorkerResult) {\n if (!currentEval) return;\n if (msg.id !== currentEval.id) return;\n\n clearEvalTimeout();\n\n const pending = currentEval;\n currentEval = null;\n\n // Staleness check: if a file was invalidated while this evaluation was\n // in-flight and the result depends on it, discard the result and retry\n // on a clean worker. Only checked for successes — errors don't report\n // deps, and the file watcher will trigger another invalidation when the\n // source is fixed.\n if (msg.ok && invalidatedDuringEval.size > 0) {\n const isStale = msg.dependencies.some((dep) => invalidatedDuringEval.has(dep));\n if (isStale) {\n if (pending.retryCount >= MAX_STALENESS_RETRIES) {\n pending.resolve({\n ok: false,\n error: {\n message: `Evaluation of ${pending.absolutePath} exceeded maximum staleness retries (${MAX_STALENESS_RETRIES})`,\n stack: \"\",\n },\n });\n processQueue();\n return;\n }\n pending.retryCount++;\n swapWorkers();\n queue.unshift(pending);\n processQueue();\n return;\n }\n }\n\n let result: EvaluateResult;\n if (msg.ok) {\n result = { ok: true, value: msg.value, dependencies: msg.dependencies };\n updateDeps(pending.absolutePath, msg.dependencies);\n } else {\n result = { ok: false, error: msg.error };\n // Use the partial dependencies reported by the worker (files resolved\n // before the error). This ensures transitive deps are registered in\n // reverseDeps so that fixing a broken file triggers re-evaluation.\n updateDeps(pending.absolutePath, msg.dependencies);\n }\n\n resultCache.set(pending.absolutePath, result);\n inflight.delete(pending.absolutePath);\n pending.resolve(result);\n processQueue();\n }\n\n /**\n * Reconciles the dependency graph after an evaluation.\n *\n * First removes all old reverse entries for this entry point (using\n * forwardDeps as the source of truth), then writes the new forward\n * and reverse mappings. This ensures that if a dependency was removed\n * between evaluations, it no longer points back to this entry point.\n */\n function updateDeps(entryPoint: string, deps: string[]) {\n const oldDeps = forwardDeps.get(entryPoint);\n if (oldDeps) {\n for (const dep of oldDeps) {\n const entries = reverseDeps.get(dep);\n if (entries) {\n entries.delete(entryPoint);\n if (entries.size === 0) {\n reverseDeps.delete(dep);\n }\n }\n }\n }\n\n const depSet = new Set(deps);\n forwardDeps.set(entryPoint, depSet);\n\n for (const dep of depSet) {\n let entries = reverseDeps.get(dep);\n if (!entries) {\n entries = new Set();\n reverseDeps.set(dep, entries);\n }\n entries.add(entryPoint);\n }\n }\n\n /**\n * Evaluations are serialized — one at a time per worker. This prevents\n * interleaved dependency tracking: the loader hook accumulates deps in\n * a single Set and flushes it after each evaluation. Concurrent imports\n * would mix dependencies from different entry points.\n *\n * Waits for the primary worker to be ready before sending work. This\n * matters after a swap: the promoted shadow may still be booting.\n */\n function processQueue() {\n if (currentEval || queue.length === 0 || disposed) return;\n\n const next = queue.shift()!;\n\n // A prior evaluation for the same path may have completed and cached\n // while this item was queued (e.g. after invalidation + re-queue).\n // Resolving from cache avoids dispatching to a worker where the module\n // is already in the ESM cache (which would produce incomplete deps).\n const alreadyCached = resultCache.get(next.absolutePath);\n if (alreadyCached) {\n inflight.delete(next.absolutePath);\n next.resolve(alreadyCached);\n processQueue();\n return;\n }\n\n currentEval = next;\n\n primary.ready.then(() => {\n if (disposed || currentEval !== next) return;\n primary.worker.postMessage({\n type: \"evaluate\",\n id: next.id,\n absolutePath: next.absolutePath,\n });\n\n currentTimeout = setTimeout(() => {\n if (currentEval !== next) return;\n currentEval = null;\n currentTimeout = null;\n\n const result: EvaluateResult = {\n ok: false,\n error: {\n message: `Evaluation of ${next.absolutePath} timed out after ${DEFAULT_EVAL_TIMEOUT_MS}ms`,\n stack: \"\",\n },\n };\n updateDeps(next.absolutePath, [next.absolutePath]);\n resultCache.set(next.absolutePath, result);\n inflight.delete(next.absolutePath);\n next.resolve(result);\n\n swapWorkers();\n processQueue();\n }, DEFAULT_EVAL_TIMEOUT_MS);\n });\n }\n\n /**\n * Terminates the primary (stale module cache), promotes the shadow\n * (clean cache, instant), and boots a new shadow in the background.\n *\n * `removeAllListeners` prevents the terminated worker from delivering\n * late messages or error events that would confuse the result handler.\n */\n function swapWorkers() {\n primary.worker.removeAllListeners();\n primary.worker.terminate();\n\n primary = shadow;\n setupMessageHandler(primary);\n\n shadow = bootWorker();\n unrefWorkers(primary.worker, shadow.worker);\n\n // The new primary has a clean ESM cache, so any previously tracked\n // invalidations are no longer relevant — the fresh worker will\n // re-import everything from disk.\n invalidatedDuringEval.clear();\n }\n\n function evaluate(absolutePath: string): Promise<EvaluateResult> {\n if (disposed) {\n return Promise.reject(new Error(\"Evaluator has been disposed\"));\n }\n\n if (!isAbsolute(absolutePath)) {\n return Promise.reject(\n new Error(`evaluate() requires an absolute path, got: ${absolutePath}`),\n );\n }\n\n const cached = resultCache.get(absolutePath);\n if (cached) {\n return Promise.resolve(cached);\n }\n\n // Deduplicate: if an evaluation is already queued or in-flight for this\n // path, return the same promise. This prevents a second worker evaluation\n // that would produce incomplete dependencies (the ESM resolve hook only\n // fires on cache misses).\n const pending = inflight.get(absolutePath);\n if (pending) return pending;\n\n const promise = new Promise<EvaluateResult>((resolve, reject) => {\n const id = nextId++;\n queue.push({ id, absolutePath, resolve, reject, retryCount: 0 });\n processQueue();\n });\n\n inflight.set(absolutePath, promise);\n return promise;\n }\n\n function invalidate(...absolutePaths: string[]): void {\n if (absolutePaths.length === 0) return;\n\n // Track all invalidations for cold-start detection (in-flight evaluations\n // whose dependency graph isn't populated yet).\n for (const path of absolutePaths) {\n invalidatedDuringEval.add(path);\n }\n\n // Collect all affected entry points across all changed files.\n const allEntryPoints = new Set<string>();\n for (const path of absolutePaths) {\n const entryPoints = reverseDeps.get(path);\n if (entryPoints) {\n for (const entry of entryPoints) {\n allEntryPoints.add(entry);\n }\n }\n }\n\n // If no cached entry points are affected, return early. The cold-start\n // set (invalidatedDuringEval) will catch in-flight evaluations.\n if (allEntryPoints.size === 0) return;\n\n // Clear caches and clean dependency graphs for all affected entry points.\n for (const entry of allEntryPoints) {\n resultCache.delete(entry);\n inflight.delete(entry);\n\n const deps = forwardDeps.get(entry);\n if (deps) {\n for (const dep of deps) {\n const entries = reverseDeps.get(dep);\n if (entries) {\n entries.delete(entry);\n if (entries.size === 0) {\n reverseDeps.delete(dep);\n }\n }\n }\n forwardDeps.delete(entry);\n }\n }\n\n // If an evaluation is in-flight on the worker we're about to terminate,\n // capture it and re-queue at the front. The caller's promise will\n // resolve transparently with the result from the promoted shadow worker.\n clearEvalTimeout();\n const inflightEval = currentEval;\n currentEval = null;\n\n swapWorkers();\n\n if (inflightEval) {\n queue.unshift(inflightEval);\n }\n\n processQueue();\n }\n\n function invalidateAll(): void {\n resultCache.clear();\n inflight.clear();\n forwardDeps.clear();\n reverseDeps.clear();\n\n clearEvalTimeout();\n const inflightEval = currentEval;\n currentEval = null;\n\n swapWorkers();\n\n if (inflightEval) {\n queue.unshift(inflightEval);\n }\n\n processQueue();\n }\n\n function getDependentsOf(absolutePath: string): string[] {\n const entryPoints = reverseDeps.get(absolutePath);\n if (!entryPoints) return [];\n return [...entryPoints];\n }\n\n async function dispose(): Promise<void> {\n if (disposed) return;\n disposed = true;\n\n clearEvalTimeout();\n\n if (currentEval) {\n currentEval.reject(new Error(\"Evaluator has been disposed\"));\n currentEval = null;\n }\n for (const pending of queue) {\n pending.reject(new Error(\"Evaluator has been disposed\"));\n }\n queue.length = 0;\n inflight.clear();\n\n await Promise.all([primary.worker.terminate(), shadow.worker.terminate()]);\n }\n\n return {\n evaluate,\n invalidate,\n invalidateAll,\n getDependentsOf,\n dispose,\n [Symbol.asyncDispose]: dispose,\n };\n}\n"],"mappings":";;;;;AAsDA,MAAM,kBAAkB,+BAA+B,OAAO,KAAK;;;;;;;;;;;;;;;;;;;;;;;CAAkB,CAAC,CAAC,SAAS,QAAQ;AAGxG,SAAgB,sBAA8B;CAC5C,OAAO;AACT;;;ACDA,SAAS,oBAA4B;CACnC,MAAM,SAAS,cAAc,IAAI,IAAI,eAAe,YAAY,GAAG,CAAC;CACpE,IAAI,WAAW,MAAM,GACnB,OAAO;CAGT,OAAO,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,WAAW;AAClE;AAaA,SAAS,aAAuD;CAC9D,MAAM,aAAa,kBAAkB;CACrC,MAAM,gBAAgB,oBAAoB;CAC1C,MAAM,SAAS,IAAI,OAAO,YAAY,EACpC,YAAY,EAAE,cAAc,EAC9B,CAAC;CAUD,OAAO;EAAE;EAAQ,OAAA,IATC,SAAe,YAAY;GAC3C,MAAM,aAAa,QAA0B;IAC3C,IAAI,IAAI,SAAS,SAAS;KACxB,OAAO,IAAI,WAAW,SAAS;KAC/B,QAAQ;IACV;GACF;GACA,OAAO,GAAG,WAAW,SAAS;EAChC,CACqB;CAAE;AACzB;AAGA,MAAM,0BAA0B;AAGhC,MAAM,wBAAwB;AAG9B,eAAsB,kBAAsC;CAC1D,MAAM,8BAAc,IAAI,IAA4B;CASpD,MAAM,2BAAW,IAAI,IAAqC;CAc1D,MAAM,8BAAc,IAAI,IAAyB;CACjD,MAAM,8BAAc,IAAI,IAAyB;CAEjD,IAAI,SAAS;CACb,IAAI,cAAwC;CAC5C,IAAI,iBAAuD;CAC3D,MAAM,QAA6B,CAAC;CAcpC,MAAM,wCAAwB,IAAI,IAAY;CAC9C,IAAI,WAAW;CAkBf,IAAI,UAAU,WAAW;CACzB,IAAI,SAAS,WAAW;CAExB,MAAM,QAAQ,IAAI,CAAC,QAAQ,OAAO,OAAO,KAAK,CAAC;CAS/C,SAAS,aAAa,eAAuB,cAAsB;EACjE,cAAc,MAAM;EACpB,aAAa,MAAM;CACrB;CAEA,SAAS,oBAAoB,WAA+B;EAC1D,UAAU,OAAO,GAAG,YAAY,QAAsB;GACpD,IAAI,IAAI,SAAS,UAAU;GAC3B,aAAa,GAAG;EAClB,CAAC;EAED,UAAU,OAAO,GAAG,UAAU,QAAe;GAC3C,IAAI,aAAa;IACf,iBAAiB;IACjB,MAAM,UAAU;IAChB,cAAc;IACd,MAAM,SAAyB;KAC7B,IAAI;KACJ,OAAO;MAAE,SAAS,IAAI;MAAS,OAAO,IAAI,SAAS;KAAG;IACxD;IACA,WAAW,QAAQ,cAAc,CAAC,QAAQ,YAAY,CAAC;IACvD,YAAY,IAAI,QAAQ,cAAc,MAAM;IAC5C,SAAS,OAAO,QAAQ,YAAY;IACpC,QAAQ,QAAQ,MAAM;IACtB,aAAa;GACf;EACF,CAAC;CACH;CAEA,oBAAoB,OAAO;CAC3B,aAAa,QAAQ,QAAQ,OAAO,MAAM;CAE1C,SAAS,mBAAmB;EAC1B,IAAI,mBAAmB,MAAM;GAC3B,aAAa,cAAc;GAC3B,iBAAiB;EACnB;CACF;CAEA,SAAS,aAAa,KAAmB;EACvC,IAAI,CAAC,aAAa;EAClB,IAAI,IAAI,OAAO,YAAY,IAAI;EAE/B,iBAAiB;EAEjB,MAAM,UAAU;EAChB,cAAc;EAOd,IAAI,IAAI,MAAM,sBAAsB,OAAO,GACzB;OAAA,IAAI,aAAa,MAAM,QAAQ,sBAAsB,IAAI,GAAG,CAClE,GAAG;IACX,IAAI,QAAQ,cAAc,uBAAuB;KAC/C,QAAQ,QAAQ;MACd,IAAI;MACJ,OAAO;OACL,SAAS,iBAAiB,QAAQ,aAAa,uCAAuC,sBAAsB;OAC5G,OAAO;MACT;KACF,CAAC;KACD,aAAa;KACb;IACF;IACA,QAAQ;IACR,YAAY;IACZ,MAAM,QAAQ,OAAO;IACrB,aAAa;IACb;GACF;;EAGF,IAAI;EACJ,IAAI,IAAI,IAAI;GACV,SAAS;IAAE,IAAI;IAAM,OAAO,IAAI;IAAO,cAAc,IAAI;GAAa;GACtE,WAAW,QAAQ,cAAc,IAAI,YAAY;EACnD,OAAO;GACL,SAAS;IAAE,IAAI;IAAO,OAAO,IAAI;GAAM;GAIvC,WAAW,QAAQ,cAAc,IAAI,YAAY;EACnD;EAEA,YAAY,IAAI,QAAQ,cAAc,MAAM;EAC5C,SAAS,OAAO,QAAQ,YAAY;EACpC,QAAQ,QAAQ,MAAM;EACtB,aAAa;CACf;CAUA,SAAS,WAAW,YAAoB,MAAgB;EACtD,MAAM,UAAU,YAAY,IAAI,UAAU;EAC1C,IAAI,SACF,KAAK,MAAM,OAAO,SAAS;GACzB,MAAM,UAAU,YAAY,IAAI,GAAG;GACnC,IAAI,SAAS;IACX,QAAQ,OAAO,UAAU;IACzB,IAAI,QAAQ,SAAS,GACnB,YAAY,OAAO,GAAG;GAE1B;EACF;EAGF,MAAM,SAAS,IAAI,IAAI,IAAI;EAC3B,YAAY,IAAI,YAAY,MAAM;EAElC,KAAK,MAAM,OAAO,QAAQ;GACxB,IAAI,UAAU,YAAY,IAAI,GAAG;GACjC,IAAI,CAAC,SAAS;IACZ,0BAAU,IAAI,IAAI;IAClB,YAAY,IAAI,KAAK,OAAO;GAC9B;GACA,QAAQ,IAAI,UAAU;EACxB;CACF;CAWA,SAAS,eAAe;EACtB,IAAI,eAAe,MAAM,WAAW,KAAK,UAAU;EAEnD,MAAM,OAAO,MAAM,MAAM;EAMzB,MAAM,gBAAgB,YAAY,IAAI,KAAK,YAAY;EACvD,IAAI,eAAe;GACjB,SAAS,OAAO,KAAK,YAAY;GACjC,KAAK,QAAQ,aAAa;GAC1B,aAAa;GACb;EACF;EAEA,cAAc;EAEd,QAAQ,MAAM,WAAW;GACvB,IAAI,YAAY,gBAAgB,MAAM;GACtC,QAAQ,OAAO,YAAY;IACzB,MAAM;IACN,IAAI,KAAK;IACT,cAAc,KAAK;GACrB,CAAC;GAED,iBAAiB,iBAAiB;IAChC,IAAI,gBAAgB,MAAM;IAC1B,cAAc;IACd,iBAAiB;IAEjB,MAAM,SAAyB;KAC7B,IAAI;KACJ,OAAO;MACL,SAAS,iBAAiB,KAAK,aAAa,mBAAmB,wBAAwB;MACvF,OAAO;KACT;IACF;IACA,WAAW,KAAK,cAAc,CAAC,KAAK,YAAY,CAAC;IACjD,YAAY,IAAI,KAAK,cAAc,MAAM;IACzC,SAAS,OAAO,KAAK,YAAY;IACjC,KAAK,QAAQ,MAAM;IAEnB,YAAY;IACZ,aAAa;GACf,GAAG,uBAAuB;EAC5B,CAAC;CACH;CASA,SAAS,cAAc;EACrB,QAAQ,OAAO,mBAAmB;EAClC,QAAQ,OAAO,UAAU;EAEzB,UAAU;EACV,oBAAoB,OAAO;EAE3B,SAAS,WAAW;EACpB,aAAa,QAAQ,QAAQ,OAAO,MAAM;EAK1C,sBAAsB,MAAM;CAC9B;CAEA,SAAS,SAAS,cAA+C;EAC/D,IAAI,UACF,OAAO,QAAQ,uBAAO,IAAI,MAAM,6BAA6B,CAAC;EAGhE,IAAI,CAAC,WAAW,YAAY,GAC1B,OAAO,QAAQ,uBACb,IAAI,MAAM,8CAA8C,cAAc,CACxE;EAGF,MAAM,SAAS,YAAY,IAAI,YAAY;EAC3C,IAAI,QACF,OAAO,QAAQ,QAAQ,MAAM;EAO/B,MAAM,UAAU,SAAS,IAAI,YAAY;EACzC,IAAI,SAAS,OAAO;EAEpB,MAAM,UAAU,IAAI,SAAyB,SAAS,WAAW;GAC/D,MAAM,KAAK;GACX,MAAM,KAAK;IAAE;IAAI;IAAc;IAAS;IAAQ,YAAY;GAAE,CAAC;GAC/D,aAAa;EACf,CAAC;EAED,SAAS,IAAI,cAAc,OAAO;EAClC,OAAO;CACT;CAEA,SAAS,WAAW,GAAG,eAA+B;EACpD,IAAI,cAAc,WAAW,GAAG;EAIhC,KAAK,MAAM,QAAQ,eACjB,sBAAsB,IAAI,IAAI;EAIhC,MAAM,iCAAiB,IAAI,IAAY;EACvC,KAAK,MAAM,QAAQ,eAAe;GAChC,MAAM,cAAc,YAAY,IAAI,IAAI;GACxC,IAAI,aACF,KAAK,MAAM,SAAS,aAClB,eAAe,IAAI,KAAK;EAG9B;EAIA,IAAI,eAAe,SAAS,GAAG;EAG/B,KAAK,MAAM,SAAS,gBAAgB;GAClC,YAAY,OAAO,KAAK;GACxB,SAAS,OAAO,KAAK;GAErB,MAAM,OAAO,YAAY,IAAI,KAAK;GAClC,IAAI,MAAM;IACR,KAAK,MAAM,OAAO,MAAM;KACtB,MAAM,UAAU,YAAY,IAAI,GAAG;KACnC,IAAI,SAAS;MACX,QAAQ,OAAO,KAAK;MACpB,IAAI,QAAQ,SAAS,GACnB,YAAY,OAAO,GAAG;KAE1B;IACF;IACA,YAAY,OAAO,KAAK;GAC1B;EACF;EAKA,iBAAiB;EACjB,MAAM,eAAe;EACrB,cAAc;EAEd,YAAY;EAEZ,IAAI,cACF,MAAM,QAAQ,YAAY;EAG5B,aAAa;CACf;CAEA,SAAS,gBAAsB;EAC7B,YAAY,MAAM;EAClB,SAAS,MAAM;EACf,YAAY,MAAM;EAClB,YAAY,MAAM;EAElB,iBAAiB;EACjB,MAAM,eAAe;EACrB,cAAc;EAEd,YAAY;EAEZ,IAAI,cACF,MAAM,QAAQ,YAAY;EAG5B,aAAa;CACf;CAEA,SAAS,gBAAgB,cAAgC;EACvD,MAAM,cAAc,YAAY,IAAI,YAAY;EAChD,IAAI,CAAC,aAAa,OAAO,CAAC;EAC1B,OAAO,CAAC,GAAG,WAAW;CACxB;CAEA,eAAe,UAAyB;EACtC,IAAI,UAAU;EACd,WAAW;EAEX,iBAAiB;EAEjB,IAAI,aAAa;GACf,YAAY,uBAAO,IAAI,MAAM,6BAA6B,CAAC;GAC3D,cAAc;EAChB;EACA,KAAK,MAAM,WAAW,OACpB,QAAQ,uBAAO,IAAI,MAAM,6BAA6B,CAAC;EAEzD,MAAM,SAAS;EACf,SAAS,MAAM;EAEf,MAAM,QAAQ,IAAI,CAAC,QAAQ,OAAO,UAAU,GAAG,OAAO,OAAO,UAAU,CAAC,CAAC;CAC3E;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;GACC,OAAO,eAAe;CACzB;AACF"}
|
|
@@ -21,7 +21,10 @@ function requestDeps() {
|
|
|
21
21
|
parentPort.on("message", async (msg) => {
|
|
22
22
|
if (msg.type !== "evaluate") return;
|
|
23
23
|
try {
|
|
24
|
-
const ns = await import(
|
|
24
|
+
const ns = await import(
|
|
25
|
+
/* turbopackIgnore: true */
|
|
26
|
+
pathToFileURL(msg.absolutePath).href
|
|
27
|
+
);
|
|
25
28
|
const deps = await requestDeps();
|
|
26
29
|
if (!deps.includes(msg.absolutePath)) deps.unshift(msg.absolutePath);
|
|
27
30
|
const value = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.js","names":[],"sources":["../../../yak-internals/isolated-source-eval/worker.ts"],"sourcesContent":["/**\n * Worker thread entry point for isolated module evaluation.\n *\n * Each worker is a full Node.js environment with its own ESM module cache.\n * Modules are loaded via dynamic `import()` — Node 22.18.0+ strips TypeScript\n * types natively, so no compilation step or flags are needed.\n *\n * The loader hook data URL is received via `workerData` rather than\n * importing it from a sibling module. This avoids a cross-file import\n * resolution problem: Node's strip-types requires `.ts` extensions in\n * source, but compiled output uses `.js`. Passing the URL through\n * `workerData` sidesteps this entirely since the worker file has no\n * local imports beyond node built-ins.\n */\nimport { parentPort, workerData } from \"node:worker_threads\";\nimport { MessageChannel } from \"node:worker_threads\";\nimport { pathToFileURL } from \"node:url\";\n\n/**\n * We import `register` from `node:module` instead of calling `module.register()`\n * directly. Node's strip-types parser sees the bare `module` identifier as a\n * CJS global, and combined with top-level `await` (or other ESM syntax), it\n * throws \"Cannot determine intended module format\". Using the explicit import\n * avoids this ambiguity entirely.\n */\nimport { register } from \"node:module\";\n\nif (!parentPort) {\n throw new Error(\"This file must be run as a worker thread\");\n}\n\nconst loaderDataUrl: string = workerData.loaderDataUrl;\n\n/**\n * MessageChannel for communicating with the loader hook.\n * port1 stays in this worker thread; port2 is transferred to the loader\n * thread via `register()`'s transferList. This is the only way to get\n * data out of loader hooks, since they run in an isolated loader thread\n * that cannot access worker_threads.parentPort.\n */\nconst { port1, port2 } = new MessageChannel();\n\nregister(loaderDataUrl, {\n parentURL: import.meta.url,\n data: { port: port2 },\n transferList: [port2],\n});\n\n/**\n * Requests the accumulated dependency list from the loader hook.\n *\n * Called after each `import()` completes. The loader hook clears its\n * internal Set on each request, so dependencies are scoped to the\n * most recent evaluation. This is safe because evaluations are\n * serialized — only one runs at a time per worker.\n */\nfunction requestDeps(): Promise<string[]> {\n return new Promise((resolve) => {\n port1.once(\"message\", (deps: string[]) => {\n resolve(deps);\n });\n port1.postMessage(\"getDeps\");\n });\n}\n\nparentPort.on(\"message\", async (msg: { type: string; id: number; absolutePath: string }) => {\n if (msg.type !== \"evaluate\") return;\n\n try {\n const fileUrl = pathToFileURL(msg.absolutePath).href;\n const ns = await import(fileUrl);\n\n const deps = await requestDeps();\n\n // The entry point may not appear in the loader hook's dependency set\n // if it was already in the ESM cache from a previous evaluation.\n // The resolve hook only fires on cache misses.\n if (!deps.includes(msg.absolutePath)) {\n deps.unshift(msg.absolutePath);\n }\n\n // Module namespace objects are exotic objects that fail structured clone\n // (postMessage would throw). Copying to a plain object is required.\n const value: Record<string, unknown> = {};\n for (const key of Object.keys(ns)) {\n value[key] = ns[key];\n }\n\n parentPort!.postMessage({\n type: \"result\",\n id: msg.id,\n ok: true,\n value,\n dependencies: deps,\n });\n } catch (err: unknown) {\n // Catches both evaluation errors (syntax errors, runtime exceptions)\n // and structured clone failures (e.g. when exports contain functions,\n // Symbols, or other non-cloneable values). Both are returned as\n // { ok: false } results — the caller does not need to distinguish.\n const error =\n err instanceof Error\n ? { message: err.message, stack: err.stack ?? \"\" }\n : { message: String(err), stack: \"\" };\n\n // Flush partial dependencies that the loader hook tracked before the\n // error. This allows the evaluator to register them in the reverse\n // dep map so that fixing the broken file triggers re-evaluation.\n const deps = await requestDeps();\n if (!deps.includes(msg.absolutePath)) {\n deps.unshift(msg.absolutePath);\n }\n\n parentPort!.postMessage({\n type: \"result\",\n id: msg.id,\n ok: false,\n error,\n dependencies: deps,\n });\n }\n});\n\nparentPort.postMessage({ type: \"ready\" });\n"],"mappings":";;;;AA2BA,IAAI,CAAC,YACH,MAAM,IAAI,MAAM,0CAA0C;AAG5D,MAAM,gBAAwB,WAAW;AASzC,MAAM,EAAE,OAAO,UAAU,IAAI,eAAe;AAE5C,SAAS,eAAe;CACtB,WAAW,YAAY;CACvB,MAAM,EAAE,MAAM,MAAM;CACpB,cAAc,CAAC,KAAK;AACtB,CAAC;AAUD,SAAS,cAAiC;CACxC,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,KAAK,YAAY,SAAmB;GACxC,QAAQ,IAAI;EACd,CAAC;EACD,MAAM,YAAY,SAAS;CAC7B,CAAC;AACH;AAEA,WAAW,GAAG,WAAW,OAAO,QAA4D;CAC1F,IAAI,IAAI,SAAS,YAAY;CAE7B,IAAI;
|
|
1
|
+
{"version":3,"file":"worker.js","names":[],"sources":["../../../yak-internals/isolated-source-eval/worker.ts"],"sourcesContent":["/**\n * Worker thread entry point for isolated module evaluation.\n *\n * Each worker is a full Node.js environment with its own ESM module cache.\n * Modules are loaded via dynamic `import()` — Node 22.18.0+ strips TypeScript\n * types natively, so no compilation step or flags are needed.\n *\n * The loader hook data URL is received via `workerData` rather than\n * importing it from a sibling module. This avoids a cross-file import\n * resolution problem: Node's strip-types requires `.ts` extensions in\n * source, but compiled output uses `.js`. Passing the URL through\n * `workerData` sidesteps this entirely since the worker file has no\n * local imports beyond node built-ins.\n */\nimport { parentPort, workerData } from \"node:worker_threads\";\nimport { MessageChannel } from \"node:worker_threads\";\nimport { pathToFileURL } from \"node:url\";\n\n/**\n * We import `register` from `node:module` instead of calling `module.register()`\n * directly. Node's strip-types parser sees the bare `module` identifier as a\n * CJS global, and combined with top-level `await` (or other ESM syntax), it\n * throws \"Cannot determine intended module format\". Using the explicit import\n * avoids this ambiguity entirely.\n */\nimport { register } from \"node:module\";\n\nif (!parentPort) {\n throw new Error(\"This file must be run as a worker thread\");\n}\n\nconst loaderDataUrl: string = workerData.loaderDataUrl;\n\n/**\n * MessageChannel for communicating with the loader hook.\n * port1 stays in this worker thread; port2 is transferred to the loader\n * thread via `register()`'s transferList. This is the only way to get\n * data out of loader hooks, since they run in an isolated loader thread\n * that cannot access worker_threads.parentPort.\n */\nconst { port1, port2 } = new MessageChannel();\n\nregister(loaderDataUrl, {\n parentURL: import.meta.url,\n data: { port: port2 },\n transferList: [port2],\n});\n\n/**\n * Requests the accumulated dependency list from the loader hook.\n *\n * Called after each `import()` completes. The loader hook clears its\n * internal Set on each request, so dependencies are scoped to the\n * most recent evaluation. This is safe because evaluations are\n * serialized — only one runs at a time per worker.\n */\nfunction requestDeps(): Promise<string[]> {\n return new Promise((resolve) => {\n port1.once(\"message\", (deps: string[]) => {\n resolve(deps);\n });\n port1.postMessage(\"getDeps\");\n });\n}\n\nparentPort.on(\"message\", async (msg: { type: string; id: number; absolutePath: string }) => {\n if (msg.type !== \"evaluate\") return;\n\n try {\n const fileUrl = pathToFileURL(msg.absolutePath).href;\n // The path is only known at runtime, so bundlers must not try to resolve it\n const ns = await import(/* turbopackIgnore: true */ fileUrl);\n\n const deps = await requestDeps();\n\n // The entry point may not appear in the loader hook's dependency set\n // if it was already in the ESM cache from a previous evaluation.\n // The resolve hook only fires on cache misses.\n if (!deps.includes(msg.absolutePath)) {\n deps.unshift(msg.absolutePath);\n }\n\n // Module namespace objects are exotic objects that fail structured clone\n // (postMessage would throw). Copying to a plain object is required.\n const value: Record<string, unknown> = {};\n for (const key of Object.keys(ns)) {\n value[key] = ns[key];\n }\n\n parentPort!.postMessage({\n type: \"result\",\n id: msg.id,\n ok: true,\n value,\n dependencies: deps,\n });\n } catch (err: unknown) {\n // Catches both evaluation errors (syntax errors, runtime exceptions)\n // and structured clone failures (e.g. when exports contain functions,\n // Symbols, or other non-cloneable values). Both are returned as\n // { ok: false } results — the caller does not need to distinguish.\n const error =\n err instanceof Error\n ? { message: err.message, stack: err.stack ?? \"\" }\n : { message: String(err), stack: \"\" };\n\n // Flush partial dependencies that the loader hook tracked before the\n // error. This allows the evaluator to register them in the reverse\n // dep map so that fixing the broken file triggers re-evaluation.\n const deps = await requestDeps();\n if (!deps.includes(msg.absolutePath)) {\n deps.unshift(msg.absolutePath);\n }\n\n parentPort!.postMessage({\n type: \"result\",\n id: msg.id,\n ok: false,\n error,\n dependencies: deps,\n });\n }\n});\n\nparentPort.postMessage({ type: \"ready\" });\n"],"mappings":";;;;AA2BA,IAAI,CAAC,YACH,MAAM,IAAI,MAAM,0CAA0C;AAG5D,MAAM,gBAAwB,WAAW;AASzC,MAAM,EAAE,OAAO,UAAU,IAAI,eAAe;AAE5C,SAAS,eAAe;CACtB,WAAW,YAAY;CACvB,MAAM,EAAE,MAAM,MAAM;CACpB,cAAc,CAAC,KAAK;AACtB,CAAC;AAUD,SAAS,cAAiC;CACxC,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,KAAK,YAAY,SAAmB;GACxC,QAAQ,IAAI;EACd,CAAC;EACD,MAAM,YAAY,SAAS;CAC7B,CAAC;AACH;AAEA,WAAW,GAAG,WAAW,OAAO,QAA4D;CAC1F,IAAI,IAAI,SAAS,YAAY;CAE7B,IAAI;EAGF,MAAM,KAAK,MAAM;;GAFD,cAAc,IAAI,YAAY,CAAC,CAAC;;EAIhD,MAAM,OAAO,MAAM,YAAY;EAK/B,IAAI,CAAC,KAAK,SAAS,IAAI,YAAY,GACjC,KAAK,QAAQ,IAAI,YAAY;EAK/B,MAAM,QAAiC,CAAC;EACxC,KAAK,MAAM,OAAO,OAAO,KAAK,EAAE,GAC9B,MAAM,OAAO,GAAG;EAGlB,WAAY,YAAY;GACtB,MAAM;GACN,IAAI,IAAI;GACR,IAAI;GACJ;GACA,cAAc;EAChB,CAAC;CACH,SAAS,KAAc;EAKrB,MAAM,QACJ,eAAe,QACX;GAAE,SAAS,IAAI;GAAS,OAAO,IAAI,SAAS;EAAG,IAC/C;GAAE,SAAS,OAAO,GAAG;GAAG,OAAO;EAAG;EAKxC,MAAM,OAAO,MAAM,YAAY;EAC/B,IAAI,CAAC,KAAK,SAAS,IAAI,YAAY,GACjC,KAAK,QAAQ,IAAI,YAAY;EAG/B,WAAY,YAAY;GACtB,MAAM;GACN,IAAI,IAAI;GACR,IAAI;GACJ;GACA,cAAc;EAChB,CAAC;CACH;AACF,CAAC;AAED,WAAW,YAAY,EAAE,MAAM,QAAQ,CAAC"}
|