next-yak 5.2.1 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/loaders/css-loader.js +28 -16
- package/dist/loaders/css-loader.js.map +1 -1
- package/dist/withYak/index.cjs +3 -6
- package/dist/withYak/index.cjs.map +1 -1
- package/dist/withYak/index.js +3 -6
- package/dist/withYak/index.js.map +1 -1
- package/loaders/lib/resolveCrossFileSelectors.ts +50 -17
- package/package.json +2 -2
- package/runtime/__tests__/attrs.test.tsx +4 -3
- package/withYak/index.ts +4 -8
|
@@ -5,7 +5,6 @@ import { relative } from "path";
|
|
|
5
5
|
import babel from "@babel/core";
|
|
6
6
|
import path from "path";
|
|
7
7
|
import babelPlugin from "@babel/plugin-syntax-typescript";
|
|
8
|
-
import { getCssModuleLocalIdent } from "next/dist/build/webpack/config/blocks/css/loaders/getCssModuleLocalIdent.js";
|
|
9
8
|
var yakCssImportRegex = (
|
|
10
9
|
// Make mixin and selector non optional once we dropped support for the babel plugin
|
|
11
10
|
/--yak-css-import\:\s*url\("([^"]+)",?(|mixin|selector)\)(;?)/g
|
|
@@ -61,23 +60,15 @@ async function resolveCrossFileConstant(loader, pathContext, css) {
|
|
|
61
60
|
const { position, size, importKind, specifier, semicolon } = matches[i];
|
|
62
61
|
const resolved = resolvedValues[i];
|
|
63
62
|
if (importKind === "selector") {
|
|
64
|
-
if (resolved.type
|
|
63
|
+
if (resolved.type !== "styled-component" && resolved.type !== "constant") {
|
|
65
64
|
throw new Error(
|
|
66
|
-
`Found
|
|
65
|
+
`Found ${resolved.type} but expected a selector - did you forget a semicolon after \`${specifier.join(
|
|
67
66
|
"."
|
|
68
67
|
)}\`?`
|
|
69
68
|
);
|
|
70
69
|
}
|
|
71
70
|
}
|
|
72
|
-
const replacement = resolved.type === "styled-component" ?
|
|
73
|
-
{
|
|
74
|
-
rootContext: loader.rootContext,
|
|
75
|
-
resourcePath: resolved.from
|
|
76
|
-
},
|
|
77
|
-
null,
|
|
78
|
-
resolved.name,
|
|
79
|
-
{}
|
|
80
|
-
)})` : resolved.value + // resolved.value can be of two different types:
|
|
71
|
+
const replacement = resolved.type === "styled-component" ? resolved.value : resolved.value + // resolved.value can be of two different types:
|
|
81
72
|
// - mixin:
|
|
82
73
|
// ${mixinName};
|
|
83
74
|
// - constant:
|
|
@@ -161,6 +152,7 @@ async function parseFile(loader, filePath) {
|
|
|
161
152
|
});
|
|
162
153
|
const exports = await parseExports(await sourceContents, isTSX);
|
|
163
154
|
const mixins = parseMixins(await tranformedSource);
|
|
155
|
+
Object.assign(exports, parseStyledComponents(await tranformedSource));
|
|
164
156
|
await Promise.all(
|
|
165
157
|
Object.entries(mixins).map(async ([name, { value, nameParts }]) => {
|
|
166
158
|
const resolvedValue = await resolveCrossFileConstant(
|
|
@@ -235,9 +227,12 @@ async function parseExports(sourceContents, isTSX) {
|
|
|
235
227
|
} else if (node.declaration?.type === "VariableDeclaration") {
|
|
236
228
|
node.declaration.declarations.forEach((declaration) => {
|
|
237
229
|
if (declaration.id.type === "Identifier" && declaration.init) {
|
|
238
|
-
|
|
230
|
+
const parsed = parseExportValueExpression(
|
|
239
231
|
declaration.init
|
|
240
232
|
);
|
|
233
|
+
if (parsed) {
|
|
234
|
+
exports[declaration.id.name] = parsed;
|
|
235
|
+
}
|
|
241
236
|
}
|
|
242
237
|
});
|
|
243
238
|
}
|
|
@@ -293,6 +288,19 @@ function parseMixins(sourceContents) {
|
|
|
293
288
|
}
|
|
294
289
|
return mixins;
|
|
295
290
|
}
|
|
291
|
+
function parseStyledComponents(sourceContents) {
|
|
292
|
+
const styledParts = sourceContents.split("/*YAK EXPORTED STYLED:");
|
|
293
|
+
let styledComponents = {};
|
|
294
|
+
for (let i = 1; i < styledParts.length; i++) {
|
|
295
|
+
const [comment] = styledParts[i].split("*/", 1);
|
|
296
|
+
const [componentName, className] = comment.split(":");
|
|
297
|
+
styledComponents[componentName] = {
|
|
298
|
+
type: "styled-component",
|
|
299
|
+
value: `:global(.${className})`
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
return styledComponents;
|
|
303
|
+
}
|
|
296
304
|
function unpackTSAsExpression(node) {
|
|
297
305
|
if (node.type === "TSAsExpression") {
|
|
298
306
|
return unpackTSAsExpression(node.expression);
|
|
@@ -302,7 +310,7 @@ function unpackTSAsExpression(node) {
|
|
|
302
310
|
function parseExportValueExpression(node) {
|
|
303
311
|
const expression = unpackTSAsExpression(node);
|
|
304
312
|
if (expression.type === "CallExpression" || expression.type === "TaggedTemplateExpression") {
|
|
305
|
-
return { type: "styled-component" };
|
|
313
|
+
return { type: "styled-component", value: void 0 };
|
|
306
314
|
} else if (expression.type === "StringLiteral" || expression.type === "NumericLiteral") {
|
|
307
315
|
return { type: "constant", value: expression.value };
|
|
308
316
|
} else if (expression.type === "UnaryExpression" && expression.operator === "-" && expression.argument.type === "NumericLiteral") {
|
|
@@ -319,9 +327,12 @@ function parseObjectExpression(node) {
|
|
|
319
327
|
for (const property of node.properties) {
|
|
320
328
|
if (property.type === "ObjectProperty" && property.key.type === "Identifier") {
|
|
321
329
|
const key = property.key.name;
|
|
322
|
-
|
|
330
|
+
const parsed = parseExportValueExpression(
|
|
323
331
|
property.value
|
|
324
332
|
);
|
|
333
|
+
if (parsed) {
|
|
334
|
+
result[key] = parsed;
|
|
335
|
+
}
|
|
325
336
|
}
|
|
326
337
|
}
|
|
327
338
|
return result;
|
|
@@ -375,7 +386,8 @@ async function resolveModuleSpecifierRecursively(loader, module, specifier) {
|
|
|
375
386
|
return {
|
|
376
387
|
type: "styled-component",
|
|
377
388
|
from: module.filePath,
|
|
378
|
-
name: specifier[specifier.length - 1]
|
|
389
|
+
name: specifier[specifier.length - 1],
|
|
390
|
+
value: exportValue.value
|
|
379
391
|
};
|
|
380
392
|
} else if (exportValue.type === "constant") {
|
|
381
393
|
return { type: "constant", value: exportValue.value };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../loaders/css-loader.ts","../../loaders/lib/resolveCrossFileSelectors.ts"],"sourcesContent":["import { relative } from \"path\";\nimport type { LoaderContext } from \"webpack\";\nimport type { YakConfigOptions } from \"../withYak/index.js\";\nimport { resolveCrossFileConstant } from \"./lib/resolveCrossFileSelectors.js\";\n\n/**\n * Transform typescript to css\n *\n * This loader takes the cached result from the yak tsloader\n * and extracts the css from the generated comments\n */\nexport default async function cssExtractLoader(\n this: LoaderContext<YakConfigOptions>,\n // Instead of the source code, we receive the extracted css\n // from the yak-swc transformation\n _code: string,\n sourceMap: string | undefined,\n): Promise<string | void> {\n const callback = this.async();\n // Load the module from the original typescript request (without !=! and the query)\n return this.loadModule(this.resourcePath, (err, source) => {\n if (err) {\n return callback(err);\n }\n if (!source) {\n return callback(\n new Error(`Source code for ${this.resourcePath} is empty`),\n );\n }\n const { experiments } = this.getOptions();\n const debugLog = createDebugLogger(this, experiments?.debug);\n\n debugLog(\"ts\", source);\n const css = extractCss(source);\n debugLog(\"css\", css);\n\n return resolveCrossFileConstant(this, this.context, css).then((result) => {\n debugLog(\"css resolved\", css);\n return callback(null, result, sourceMap);\n }, callback);\n });\n}\n\nfunction extractCss(code: string | Buffer<ArrayBufferLike>): string {\n let codeString: string;\n\n if (typeof code === \"string\") {\n codeString = code;\n } else if (code instanceof Buffer) {\n codeString = code.toString(\"utf-8\");\n } else if (code instanceof ArrayBuffer) {\n codeString = new TextDecoder(\"utf-8\").decode(code);\n } else {\n throw new Error(\n \"Invalid input type: code must be string, Buffer, or ArrayBuffer\",\n );\n }\n\n const codeParts = codeString.split(\"/*YAK Extracted CSS:\\n\");\n let result = \"\";\n for (let i = 1; i < codeParts.length; i++) {\n const codeUntilEnd = codeParts[i].split(\"*/\")[0];\n result += codeUntilEnd;\n }\n if (result) {\n result = \"/* cssmodules-pure-no-check */\\n\" + result;\n }\n\n return result;\n}\n\nfunction createDebugLogger(\n loaderContext: LoaderContext<YakConfigOptions>,\n debugOptions: Required<YakConfigOptions>[\"experiments\"][\"debug\"],\n) {\n if (\n !debugOptions ||\n (debugOptions !== true &&\n debugOptions.filter &&\n !debugOptions.filter(loaderContext.resourcePath))\n ) {\n return () => {};\n }\n const debugType = debugOptions === true ? \"ts\" : debugOptions.type;\n return (\n messageType: \"ts\" | \"css\" | \"css resolved\",\n message: string | Buffer<ArrayBufferLike> | undefined,\n ) => {\n if (messageType === debugType || debugType === \"all\") {\n console.log(\n \"🐮 Yak\",\n messageType,\n \"\\n\",\n loaderContext._compiler\n ? relative(\n loaderContext._compiler.context,\n loaderContext.resourcePath,\n )\n : loaderContext.resourcePath,\n \"\\n\\n\",\n message,\n );\n }\n };\n}\n","import babel from \"@babel/core\";\nimport path from \"path\";\n// @ts-expect-error - this is used by babel directly so we ignore that it is not typed\nimport babelPlugin from \"@babel/plugin-syntax-typescript\";\nimport { getCssModuleLocalIdent } from \"next/dist/build/webpack/config/blocks/css/loaders/getCssModuleLocalIdent.js\";\nimport type { Compilation, LoaderContext } from \"webpack\";\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\nconst compilationCache = new WeakMap<\n Compilation,\n {\n parsedFiles: Map<string, ParsedFile>;\n }\n>();\n\nconst getCompilationCache = (loader: LoaderContext<{}>) => {\n const compilation = loader._compilation;\n if (!compilation) {\n throw new Error(\"Webpack compilation object not available\");\n }\n let cache = compilationCache.get(compilation);\n if (!cache) {\n cache = {\n parsedFiles: new Map(),\n };\n compilationCache.set(compilation, cache);\n }\n return cache;\n};\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 loader: LoaderContext<{}>,\n pathContext: string,\n css: string,\n): Promise<string> {\n // Search for --yak-css-import: url(\"path/to/module\") in the css\n const matches = [...css.matchAll(yakCssImportRegex)].map((match) => {\n const [fullMatch, encodedArguments, importKind, semicolon] = match;\n const [moduleSpecifier, ...specifier] = encodedArguments\n .split(\":\")\n .map((entry) => decodeURIComponent(entry));\n return {\n encodedArguments,\n moduleSpecifier,\n specifier,\n importKind,\n semicolon,\n position: match.index!,\n size: fullMatch.length,\n };\n });\n if (matches.length === 0) return css;\n\n try {\n // Resolve all imports concurrently\n const resolvedValues = await Promise.all(\n matches.map(async ({ moduleSpecifier, specifier }) => {\n const parsedModule = await parseModule(\n loader,\n moduleSpecifier,\n pathContext,\n );\n\n const resolvedValue = await resolveModuleSpecifierRecursively(\n loader,\n parsedModule,\n specifier,\n );\n\n return resolvedValue;\n }),\n );\n\n // Replace the imports with the resolved values\n let result = css;\n for (let i = matches.length - 1; i >= 0; i--) {\n const { position, size, importKind, specifier, semicolon } = matches[i];\n const resolved = resolvedValues[i];\n\n if (importKind === \"selector\") {\n if (resolved.type === \"mixin\") {\n throw new Error(\n `Found mixin but expected a selector - did you forget a semicolon after \\`${specifier.join(\n \".\",\n )}\\`?`,\n );\n }\n }\n\n const replacement =\n resolved.type === \"styled-component\"\n ? `:global(.${getCssModuleLocalIdent(\n {\n rootContext: loader.rootContext,\n resourcePath: resolved.from,\n },\n null,\n resolved.name,\n {},\n )})`\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))\n ? \"\"\n : semicolon);\n\n result =\n result.slice(0, position) +\n String(replacement) +\n result.slice(position + size);\n }\n\n return result;\n } catch (error) {\n throw new Error(\n `Error resolving cross-file selectors: ${\n (error as Error).message\n }\\nFile: ${loader.resourcePath}`,\n );\n }\n}\n\n/**\n * Resolves a module by wrapping loader.resolve in a promise\n */\nexport async function resolveModule(\n loader: LoaderContext<{}>,\n moduleSpecifier: string,\n context: string,\n): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n loader.resolve(context, moduleSpecifier, (err, result) => {\n if (err) return reject(err);\n if (!result)\n return reject(new Error(`Could not resolve ${moduleSpecifier}`));\n resolve(result);\n });\n });\n}\n\n/**\n * Resolves a module specifier to a parsed file\n *\n * e.g.:\n * ```\n * parseModule(loader, \"./theme\", \"/path/to/styles.ts\")\n * // -> { type: 'regular', secondary: { type: 'constant', value: '#00ff00' } } }, filePath: '/path/to/theme.ts' }\n * ```\n */\nasync function parseModule(\n loader: LoaderContext<{}>,\n moduleSpecifier: string,\n context: string,\n): Promise<ParsedFile> {\n const cache = getCompilationCache(loader).parsedFiles;\n\n // The cache key is valid for the entire project so it can be reused\n // for different source files\n const resolvedModule = await resolveModule(loader, moduleSpecifier, context);\n\n let parsedFile = cache.get(resolvedModule);\n if (!parsedFile) {\n parsedFile = await parseFile(loader, resolvedModule);\n\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 cache.set(resolvedModule, parsedFile);\n }\n // on file change, invalidate the cache\n loader.addDependency(parsedFile.filePath);\n return parsedFile;\n}\n\nasync function parseFile(\n loader: LoaderContext<{}>,\n filePath: string,\n): Promise<ParsedFile> {\n const isYak =\n filePath.endsWith(\".yak.ts\") ||\n filePath.endsWith(\".yak.tsx\") ||\n filePath.endsWith(\".yak.js\") ||\n filePath.endsWith(\".yak.jsx\");\n const isTSX = filePath.endsWith(\".tsx\");\n\n try {\n if (isYak) {\n const module: Record<string, unknown> =\n await loader.importModule(filePath);\n const mappedModule = Object.fromEntries(\n Object.entries(module).map(([key, value]): [string, ParsedExport] => {\n if (typeof value === \"string\" || typeof value === \"number\") {\n return [key, { type: \"constant\" as const, value }];\n } else if (\n value &&\n (typeof value === \"object\" || Array.isArray(value))\n ) {\n return [key, { type: \"record\" as const, value }];\n } else {\n return [key, { type: \"unsupported\" as const, hint: String(value) }];\n }\n }),\n );\n return { type: \"yak\", exports: mappedModule, filePath };\n }\n const sourceContents = new Promise<string>((resolve, reject) =>\n loader.fs.readFile(filePath, \"utf-8\", (err, result) => {\n if (err) return reject(err);\n resolve(result || \"\");\n }),\n );\n\n const tranformedSource = new Promise<string>((resolve, reject) => {\n loader.loadModule(filePath, (err, source) => {\n if (err) return reject(err);\n let sourceString: string;\n if (typeof source === \"string\") {\n sourceString = source;\n } else if (source instanceof Buffer) {\n sourceString = source.toString(\"utf-8\");\n } else if (source instanceof ArrayBuffer) {\n sourceString = new TextDecoder(\"utf-8\").decode(source);\n } else {\n throw new Error(\n \"Invalid input type: code must be string, Buffer, or ArrayBuffer\",\n );\n }\n resolve(sourceString || \"\");\n });\n });\n\n const exports = await parseExports(await sourceContents, isTSX);\n const mixins = parseMixins(await tranformedSource);\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 await Promise.all(\n Object.entries(mixins).map(async ([name, { value, nameParts }]) => {\n const resolvedValue = await resolveCrossFileConstant(\n loader,\n path.dirname(filePath),\n value,\n );\n if (nameParts.length === 1) {\n exports[name] = { type: \"mixin\", value: resolvedValue };\n } else {\n let exportEntry: undefined | ParsedExport = exports[nameParts[0]];\n if (!exportEntry) {\n exportEntry = { type: \"record\", value: {} };\n exports[nameParts[0]] = exportEntry;\n } else if (exportEntry.type !== \"record\") {\n throw new Error(\n `Error parsing file ${filePath}: ${nameParts[0]} is not a record`,\n );\n }\n let current = exportEntry.value as Record<any, ParsedExport>;\n for (let i = 1; i < nameParts.length - 1; i++) {\n let next = current[nameParts[i]];\n if (!next) {\n next = { type: \"record\", value: {} };\n current[nameParts[i]] = next;\n } else if (next.type !== \"record\") {\n throw new Error(\n `Error parsing file ${filePath}: ${nameParts[i]} is not a record`,\n );\n }\n current = next.value;\n }\n current[nameParts[nameParts.length - 1]] = {\n type: \"mixin\",\n value: resolvedValue,\n };\n }\n }),\n );\n\n return {\n type: \"regular\",\n exports,\n filePath,\n };\n } catch (error) {\n throw new Error(\n `Error parsing file ${filePath}: ${(error as Error).message}`,\n );\n }\n}\n\nasync function parseExports(\n sourceContents: string,\n isTSX: boolean,\n): Promise<Record<string, ParsedExport>> {\n let exports: Record<string, ParsedExport> = {};\n\n try {\n babel.transformSync(sourceContents, {\n configFile: false,\n plugins: [\n [babelPlugin, { isTSX }],\n [\n (): babel.PluginObj => ({\n visitor: {\n ExportNamedDeclaration({ node }) {\n if (node.source) {\n node.specifiers.forEach((specifier) => {\n if (\n specifier.type === \"ExportSpecifier\" &&\n specifier.exported.type === \"Identifier\" &&\n specifier.local.type === \"Identifier\"\n ) {\n exports[specifier.exported.name] = {\n type: \"re-export\",\n from: node.source!.value,\n imported: specifier.local.name,\n };\n }\n });\n } else if (node.declaration?.type === \"VariableDeclaration\") {\n node.declaration.declarations.forEach((declaration) => {\n if (\n declaration.id.type === \"Identifier\" &&\n declaration.init\n ) {\n exports[declaration.id.name] = parseExportValueExpression(\n declaration.init,\n );\n }\n });\n }\n },\n ExportDeclaration({ node }) {\n if (\"specifiers\" in node && node.source) {\n const { specifiers, source } = node;\n specifiers.forEach((specifier) => {\n // export * as color from \"./colors\";\n if (\n specifier.type === \"ExportNamespaceSpecifier\" &&\n specifier.exported.type === \"Identifier\"\n ) {\n exports[specifier.exported.name] = {\n type: \"star-export\",\n from: [source.value],\n };\n }\n });\n }\n },\n ExportAllDeclaration({ node }) {\n if (Object.keys(exports).length === 0) {\n exports[\"*\"] ||= {\n type: \"star-export\",\n from: [],\n };\n if (exports[\"*\"].type !== \"star-export\") {\n throw new Error(\"Invalid star export state\");\n }\n exports[\"*\"].from.push(node.source.value);\n }\n },\n },\n }),\n ],\n ],\n });\n\n return exports;\n } catch (error) {\n throw new Error(`Error parsing exports: ${(error as Error).message}`);\n }\n}\n\nfunction parseMixins(\n sourceContents: string,\n): Record<string, { type: \"mixin\"; value: string; nameParts: string[] }> {\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<\n string,\n { type: \"mixin\"; value: string; nameParts: string[] }\n > = {};\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\n/**\n * Unpacks a TSAsExpression to its expression value\n */\nfunction unpackTSAsExpression(\n node: babel.types.TSAsExpression | babel.types.Expression,\n): babel.types.Expression {\n if (node.type === \"TSAsExpression\") {\n return unpackTSAsExpression(node.expression);\n }\n return node;\n}\n\nfunction parseExportValueExpression(\n node: babel.types.Expression,\n): ParsedExport {\n // ignores `as` casts so it doesn't interfere with the ast node type detection\n const expression = unpackTSAsExpression(node);\n if (\n expression.type === \"CallExpression\" ||\n expression.type === \"TaggedTemplateExpression\"\n ) {\n return { type: \"styled-component\" };\n } else if (\n expression.type === \"StringLiteral\" ||\n expression.type === \"NumericLiteral\"\n ) {\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 (\n expression.type === \"TemplateLiteral\" &&\n expression.quasis.length === 1\n ) {\n return { type: \"constant\", value: expression.quasis[0].value.raw };\n } else if (expression.type === \"ObjectExpression\") {\n return { type: \"record\", value: parseObjectExpression(expression) };\n }\n return { type: \"unsupported\", hint: expression.type };\n}\n\nfunction parseObjectExpression(\n node: babel.types.ObjectExpression,\n): Record<string, ParsedExport> {\n let result: Record<string, ParsedExport> = {};\n for (const property of node.properties) {\n if (\n property.type === \"ObjectProperty\" &&\n property.key.type === \"Identifier\"\n ) {\n const key = property.key.name;\n result[key] = parseExportValueExpression(\n property.value as babel.types.Expression,\n );\n }\n }\n return result;\n}\n\n/**\n * Follows a specifier recursively until it finds its constant value\n * for example here it follows \"colors.primary\"\n *\n * ```\n * resolveModuleSpecifierRecursively(loader, \"@/theme\", [\"colors\", \"primary\"], \"colors:primary\")`\n * // -> { type: 'constant', value: '#ff0000' }\n * ```\n *\n * example structure:\n *\n * styles.ts:\n * ```\n * import { colors } from \"@/theme\";\n * export const button = css`color: ${colors.primary}`;\n * ```\n *\n * theme.ts:\n * ```\n * export { colors } from \"./colors\";\n * ```\n *\n * colors.ts:\n * ```\n * export const colors = { primary: \"#ff0000\" };\n * ```\n *\n */\nasync function resolveModuleSpecifierRecursively(\n loader: LoaderContext<{}>,\n module: ParsedFile,\n specifier: string[],\n): Promise<ResolvedExport> {\n try {\n const exportName = specifier[0];\n let exportValue = module.exports[exportName];\n // Follow star exports if there is only a single one\n // and the export does not exist in the current module\n if (exportValue === undefined) {\n const starExport = module.exports[\"*\"];\n if (starExport?.type === \"star-export\") {\n if (starExport.from.length > 1) {\n throw new Error(\n `Could not resolve ${specifier.join(\".\")} in module ${\n module.filePath\n } - Multiple star exports are not supported for performance reasons`,\n );\n }\n exportValue = {\n type: \"re-export\" as const,\n from: starExport.from[0],\n imported: exportName,\n };\n } else {\n throw new Error(\n `Could not resolve \"${specifier.join(\".\")}\" in module ${\n module.filePath\n }`,\n );\n }\n }\n // Follow reexport\n // e.g. export { colors as primaryColors } from \"./colors\";\n if (exportValue.type === \"re-export\") {\n const importedModule = await parseModule(\n loader,\n exportValue.from,\n path.dirname(module.filePath),\n );\n return resolveModuleSpecifierRecursively(loader, importedModule, [\n exportValue.imported,\n ...specifier.slice(1),\n ]);\n }\n // Namespace export\n // e.g. export * as colors from \"./colors\";\n else if (exportValue.type === \"star-export\") {\n const importedModule = await parseModule(\n loader,\n exportValue.from[0],\n path.dirname(module.filePath),\n );\n return resolveModuleSpecifierRecursively(\n loader,\n importedModule,\n specifier.slice(1),\n );\n }\n\n if (exportValue.type === \"styled-component\") {\n return {\n type: \"styled-component\",\n from: module.filePath,\n name: specifier[specifier.length - 1],\n };\n } else if (exportValue.type === \"constant\") {\n return { type: \"constant\", value: exportValue.value };\n } else if (exportValue.type === \"record\") {\n let current: any = exportValue.value;\n let depth = 0;\n /// Drill down the specifier e.g. colors.primary\n do {\n if (typeof current === \"string\" || typeof current === \"number\") {\n return {\n type: \"constant\" as const,\n value: current,\n };\n } else if (\n !current ||\n (typeof current !== \"object\" && !Array.isArray(current))\n ) {\n throw new Error(\n `Error unpacking Record/Array \"${exportName}\".\\nKey \"${\n specifier[depth]\n }\" was of type \"${typeof current}\" but only String and Number are supported`,\n );\n }\n depth++;\n // mixins in .yak files are wrapped inside an object with a __yak key\n if (depth === specifier.length && \"__yak\" in current) {\n return { type: \"mixin\", value: current[\"__yak\"] };\n } else if (depth === specifier.length && \"value\" in current) {\n return { type: \"constant\", value: current[\"value\"] };\n } else {\n current = current[specifier[depth]];\n }\n } while (current);\n if (specifier[depth] === undefined) {\n throw new Error(\n `Error unpacking Record/Array - could not extract \\`${specifier\n .slice(0, depth)\n .join(\".\")}\\` is not a string or number`,\n );\n }\n throw new Error(\n `Error unpacking Record/Array - could not extract \\`${\n specifier[depth]\n }\\` from \\`${specifier.slice(0, depth).join(\".\")}\\``,\n );\n } else if (exportValue.type === \"mixin\") {\n return { type: \"mixin\", value: exportValue.value };\n }\n throw new Error(\n `Error unpacking Record/Array - unexpected exportValue \"${\n exportValue.type\n }\" for specifier \"${specifier.join(\".\")}\"`,\n );\n } catch (error) {\n throw new Error(\n `Error resolving from module ${module.filePath}: ${\n (error as Error).message\n }\\nExtracted values: ${JSON.stringify(module.exports, null, 2)}`,\n );\n }\n}\n\ntype ParsedFile =\n | { type: \"regular\"; exports: Record<string, ParsedExport>; filePath: string }\n | { type: \"yak\"; exports: Record<string, ParsedExport>; filePath: string };\n\ntype ParsedExport =\n | { type: \"styled-component\" }\n | { type: \"mixin\"; value: string }\n | { type: \"constant\"; value: string | number }\n | { type: \"record\"; value: Record<any, ParsedExport> | {} }\n | { type: \"unsupported\"; hint?: string }\n | { type: \"re-export\"; from: string; imported: string }\n | { type: \"star-export\"; from: string[] };\n\ntype ResolvedExport =\n | { type: \"styled-component\"; from: string; name: string }\n | { type: \"mixin\"; value: string | number }\n | { type: \"constant\"; value: string | number };\n"],"mappings":";AAAA,SAAS,gBAAgB;;;ACAzB,OAAO,WAAW;AAClB,OAAO,UAAU;AAEjB,OAAO,iBAAiB;AACxB,SAAS,8BAA8B;AAGvC,IAAM;AAAA;AAAA,EAEJ;AAAA;AAEF,IAAM,mBAAmB,oBAAI,QAK3B;AAEF,IAAM,sBAAsB,CAAC,WAA8B;AACzD,QAAM,cAAc,OAAO;AAC3B,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,MAAI,QAAQ,iBAAiB,IAAI,WAAW;AAC5C,MAAI,CAAC,OAAO;AACV,YAAQ;AAAA,MACN,aAAa,oBAAI,IAAI;AAAA,IACvB;AACA,qBAAiB,IAAI,aAAa,KAAK;AAAA,EACzC;AACA,SAAO;AACT;AAqBA,eAAsB,yBACpB,QACA,aACA,KACiB;AAEjB,QAAM,UAAU,CAAC,GAAG,IAAI,SAAS,iBAAiB,CAAC,EAAE,IAAI,CAAC,UAAU;AAClE,UAAM,CAAC,WAAW,kBAAkB,YAAY,SAAS,IAAI;AAC7D,UAAM,CAAC,iBAAiB,GAAG,SAAS,IAAI,iBACrC,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,mBAAmB,KAAK,CAAC;AAC3C,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,MAAM,UAAU;AAAA,IAClB;AAAA,EACF,CAAC;AACD,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,MAAI;AAEF,UAAM,iBAAiB,MAAM,QAAQ;AAAA,MACnC,QAAQ,IAAI,OAAO,EAAE,iBAAiB,UAAU,MAAM;AACpD,cAAM,eAAe,MAAM;AAAA,UACzB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAEA,cAAM,gBAAgB,MAAM;AAAA,UAC1B;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAEA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAGA,QAAI,SAAS;AACb,aAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,YAAM,EAAE,UAAU,MAAM,YAAY,WAAW,UAAU,IAAI,QAAQ,CAAC;AACtE,YAAM,WAAW,eAAe,CAAC;AAEjC,UAAI,eAAe,YAAY;AAC7B,YAAI,SAAS,SAAS,SAAS;AAC7B,gBAAM,IAAI;AAAA,YACR,4EAA4E,UAAU;AAAA,cACpF;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAEA,YAAM,cACJ,SAAS,SAAS,qBACd,YAAY;AAAA,QACV;AAAA,UACE,aAAa,OAAO;AAAA,UACpB,cAAc,SAAS;AAAA,QACzB;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT,CAAC;AAAA,MACH,CAAC,MACD,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAQR,CAAC,KAAK,GAAG,EAAE,SAAS,OAAO,SAAS,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,IAC3D,KACA;AAEV,eACE,OAAO,MAAM,GAAG,QAAQ,IACxB,OAAO,WAAW,IAClB,OAAO,MAAM,WAAW,IAAI;AAAA,IAChC;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,yCACG,MAAgB,OACnB;AAAA,QAAW,OAAO,YAAY;AAAA,IAChC;AAAA,EACF;AACF;AAKA,eAAsB,cACpB,QACA,iBACA,SACiB;AACjB,SAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC9C,WAAO,QAAQ,SAAS,iBAAiB,CAAC,KAAK,WAAW;AACxD,UAAI,IAAK,QAAO,OAAO,GAAG;AAC1B,UAAI,CAAC;AACH,eAAO,OAAO,IAAI,MAAM,qBAAqB,eAAe,EAAE,CAAC;AACjE,cAAQ,MAAM;AAAA,IAChB,CAAC;AAAA,EACH,CAAC;AACH;AAWA,eAAe,YACb,QACA,iBACA,SACqB;AACrB,QAAM,QAAQ,oBAAoB,MAAM,EAAE;AAI1C,QAAM,iBAAiB,MAAM,cAAc,QAAQ,iBAAiB,OAAO;AAE3E,MAAI,aAAa,MAAM,IAAI,cAAc;AACzC,MAAI,CAAC,YAAY;AACf,iBAAa,MAAM,UAAU,QAAQ,cAAc;AAKnD,UAAM,IAAI,gBAAgB,UAAU;AAAA,EACtC;AAEA,SAAO,cAAc,WAAW,QAAQ;AACxC,SAAO;AACT;AAEA,eAAe,UACb,QACA,UACqB;AACrB,QAAM,QACJ,SAAS,SAAS,SAAS,KAC3B,SAAS,SAAS,UAAU,KAC5B,SAAS,SAAS,SAAS,KAC3B,SAAS,SAAS,UAAU;AAC9B,QAAM,QAAQ,SAAS,SAAS,MAAM;AAEtC,MAAI;AACF,QAAI,OAAO;AACT,YAAM,SACJ,MAAM,OAAO,aAAa,QAAQ;AACpC,YAAM,eAAe,OAAO;AAAA,QAC1B,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAA8B;AACnE,cAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU;AAC1D,mBAAO,CAAC,KAAK,EAAE,MAAM,YAAqB,MAAM,CAAC;AAAA,UACnD,WACE,UACC,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,IACjD;AACA,mBAAO,CAAC,KAAK,EAAE,MAAM,UAAmB,MAAM,CAAC;AAAA,UACjD,OAAO;AACL,mBAAO,CAAC,KAAK,EAAE,MAAM,eAAwB,MAAM,OAAO,KAAK,EAAE,CAAC;AAAA,UACpE;AAAA,QACF,CAAC;AAAA,MACH;AACA,aAAO,EAAE,MAAM,OAAO,SAAS,cAAc,SAAS;AAAA,IACxD;AACA,UAAM,iBAAiB,IAAI;AAAA,MAAgB,CAAC,SAAS,WACnD,OAAO,GAAG,SAAS,UAAU,SAAS,CAAC,KAAK,WAAW;AACrD,YAAI,IAAK,QAAO,OAAO,GAAG;AAC1B,gBAAQ,UAAU,EAAE;AAAA,MACtB,CAAC;AAAA,IACH;AAEA,UAAM,mBAAmB,IAAI,QAAgB,CAAC,SAAS,WAAW;AAChE,aAAO,WAAW,UAAU,CAAC,KAAK,WAAW;AAC3C,YAAI,IAAK,QAAO,OAAO,GAAG;AAC1B,YAAI;AACJ,YAAI,OAAO,WAAW,UAAU;AAC9B,yBAAe;AAAA,QACjB,WAAW,kBAAkB,QAAQ;AACnC,yBAAe,OAAO,SAAS,OAAO;AAAA,QACxC,WAAW,kBAAkB,aAAa;AACxC,yBAAe,IAAI,YAAY,OAAO,EAAE,OAAO,MAAM;AAAA,QACvD,OAAO;AACL,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AACA,gBAAQ,gBAAgB,EAAE;AAAA,MAC5B,CAAC;AAAA,IACH,CAAC;AAED,UAAM,UAAU,MAAM,aAAa,MAAM,gBAAgB,KAAK;AAC9D,UAAM,SAAS,YAAY,MAAM,gBAAgB;AAKjD,UAAM,QAAQ;AAAA,MACZ,OAAO,QAAQ,MAAM,EAAE,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,UAAU,CAAC,MAAM;AACjE,cAAM,gBAAgB,MAAM;AAAA,UAC1B;AAAA,UACA,KAAK,QAAQ,QAAQ;AAAA,UACrB;AAAA,QACF;AACA,YAAI,UAAU,WAAW,GAAG;AAC1B,kBAAQ,IAAI,IAAI,EAAE,MAAM,SAAS,OAAO,cAAc;AAAA,QACxD,OAAO;AACL,cAAI,cAAwC,QAAQ,UAAU,CAAC,CAAC;AAChE,cAAI,CAAC,aAAa;AAChB,0BAAc,EAAE,MAAM,UAAU,OAAO,CAAC,EAAE;AAC1C,oBAAQ,UAAU,CAAC,CAAC,IAAI;AAAA,UAC1B,WAAW,YAAY,SAAS,UAAU;AACxC,kBAAM,IAAI;AAAA,cACR,sBAAsB,QAAQ,KAAK,UAAU,CAAC,CAAC;AAAA,YACjD;AAAA,UACF;AACA,cAAI,UAAU,YAAY;AAC1B,mBAAS,IAAI,GAAG,IAAI,UAAU,SAAS,GAAG,KAAK;AAC7C,gBAAI,OAAO,QAAQ,UAAU,CAAC,CAAC;AAC/B,gBAAI,CAAC,MAAM;AACT,qBAAO,EAAE,MAAM,UAAU,OAAO,CAAC,EAAE;AACnC,sBAAQ,UAAU,CAAC,CAAC,IAAI;AAAA,YAC1B,WAAW,KAAK,SAAS,UAAU;AACjC,oBAAM,IAAI;AAAA,gBACR,sBAAsB,QAAQ,KAAK,UAAU,CAAC,CAAC;AAAA,cACjD;AAAA,YACF;AACA,sBAAU,KAAK;AAAA,UACjB;AACA,kBAAQ,UAAU,UAAU,SAAS,CAAC,CAAC,IAAI;AAAA,YACzC,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,sBAAsB,QAAQ,KAAM,MAAgB,OAAO;AAAA,IAC7D;AAAA,EACF;AACF;AAEA,eAAe,aACb,gBACA,OACuC;AACvC,MAAI,UAAwC,CAAC;AAE7C,MAAI;AACF,UAAM,cAAc,gBAAgB;AAAA,MAClC,YAAY;AAAA,MACZ,SAAS;AAAA,QACP,CAAC,aAAa,EAAE,MAAM,CAAC;AAAA,QACvB;AAAA,UACE,OAAwB;AAAA,YACtB,SAAS;AAAA,cACP,uBAAuB,EAAE,KAAK,GAAG;AAC/B,oBAAI,KAAK,QAAQ;AACf,uBAAK,WAAW,QAAQ,CAAC,cAAc;AACrC,wBACE,UAAU,SAAS,qBACnB,UAAU,SAAS,SAAS,gBAC5B,UAAU,MAAM,SAAS,cACzB;AACA,8BAAQ,UAAU,SAAS,IAAI,IAAI;AAAA,wBACjC,MAAM;AAAA,wBACN,MAAM,KAAK,OAAQ;AAAA,wBACnB,UAAU,UAAU,MAAM;AAAA,sBAC5B;AAAA,oBACF;AAAA,kBACF,CAAC;AAAA,gBACH,WAAW,KAAK,aAAa,SAAS,uBAAuB;AAC3D,uBAAK,YAAY,aAAa,QAAQ,CAAC,gBAAgB;AACrD,wBACE,YAAY,GAAG,SAAS,gBACxB,YAAY,MACZ;AACA,8BAAQ,YAAY,GAAG,IAAI,IAAI;AAAA,wBAC7B,YAAY;AAAA,sBACd;AAAA,oBACF;AAAA,kBACF,CAAC;AAAA,gBACH;AAAA,cACF;AAAA,cACA,kBAAkB,EAAE,KAAK,GAAG;AAC1B,oBAAI,gBAAgB,QAAQ,KAAK,QAAQ;AACvC,wBAAM,EAAE,YAAY,OAAO,IAAI;AAC/B,6BAAW,QAAQ,CAAC,cAAc;AAEhC,wBACE,UAAU,SAAS,8BACnB,UAAU,SAAS,SAAS,cAC5B;AACA,8BAAQ,UAAU,SAAS,IAAI,IAAI;AAAA,wBACjC,MAAM;AAAA,wBACN,MAAM,CAAC,OAAO,KAAK;AAAA,sBACrB;AAAA,oBACF;AAAA,kBACF,CAAC;AAAA,gBACH;AAAA,cACF;AAAA,cACA,qBAAqB,EAAE,KAAK,GAAG;AAC7B,oBAAI,OAAO,KAAK,OAAO,EAAE,WAAW,GAAG;AACrC,0BAAQ,GAAG,MAAM;AAAA,oBACf,MAAM;AAAA,oBACN,MAAM,CAAC;AAAA,kBACT;AACA,sBAAI,QAAQ,GAAG,EAAE,SAAS,eAAe;AACvC,0BAAM,IAAI,MAAM,2BAA2B;AAAA,kBAC7C;AACA,0BAAQ,GAAG,EAAE,KAAK,KAAK,KAAK,OAAO,KAAK;AAAA,gBAC1C;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,0BAA2B,MAAgB,OAAO,EAAE;AAAA,EACtE;AACF;AAEA,SAAS,YACP,gBACuE;AAKvE,QAAM,aAAa,eAAe,MAAM,uBAAuB;AAC/D,MAAI,SAGA,CAAC;AAEL,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAM,CAAC,OAAO,IAAI,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;AAC7C,UAAM,WAAW,QAAQ,QAAQ,IAAI;AACrC,UAAM,OAAO,QAAQ,MAAM,GAAG,QAAQ;AACtC,UAAM,QAAQ,QAAQ,MAAM,WAAW,CAAC;AACxC,WAAO,IAAI,IAAI;AAAA,MACb,MAAM;AAAA,MACN;AAAA,MACA,WAAW,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,mBAAmB,IAAI,CAAC;AAAA,IACnE;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,qBACP,MACwB;AACxB,MAAI,KAAK,SAAS,kBAAkB;AAClC,WAAO,qBAAqB,KAAK,UAAU;AAAA,EAC7C;AACA,SAAO;AACT;AAEA,SAAS,2BACP,MACc;AAEd,QAAM,aAAa,qBAAqB,IAAI;AAC5C,MACE,WAAW,SAAS,oBACpB,WAAW,SAAS,4BACpB;AACA,WAAO,EAAE,MAAM,mBAAmB;AAAA,EACpC,WACE,WAAW,SAAS,mBACpB,WAAW,SAAS,kBACpB;AACA,WAAO,EAAE,MAAM,YAAY,OAAO,WAAW,MAAM;AAAA,EACrD,WACE,WAAW,SAAS,qBACpB,WAAW,aAAa,OACxB,WAAW,SAAS,SAAS,kBAC7B;AACA,WAAO,EAAE,MAAM,YAAY,OAAO,CAAC,WAAW,SAAS,MAAM;AAAA,EAC/D,WACE,WAAW,SAAS,qBACpB,WAAW,OAAO,WAAW,GAC7B;AACA,WAAO,EAAE,MAAM,YAAY,OAAO,WAAW,OAAO,CAAC,EAAE,MAAM,IAAI;AAAA,EACnE,WAAW,WAAW,SAAS,oBAAoB;AACjD,WAAO,EAAE,MAAM,UAAU,OAAO,sBAAsB,UAAU,EAAE;AAAA,EACpE;AACA,SAAO,EAAE,MAAM,eAAe,MAAM,WAAW,KAAK;AACtD;AAEA,SAAS,sBACP,MAC8B;AAC9B,MAAI,SAAuC,CAAC;AAC5C,aAAW,YAAY,KAAK,YAAY;AACtC,QACE,SAAS,SAAS,oBAClB,SAAS,IAAI,SAAS,cACtB;AACA,YAAM,MAAM,SAAS,IAAI;AACzB,aAAO,GAAG,IAAI;AAAA,QACZ,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AA8BA,eAAe,kCACb,QACA,QACA,WACyB;AACzB,MAAI;AACF,UAAM,aAAa,UAAU,CAAC;AAC9B,QAAI,cAAc,OAAO,QAAQ,UAAU;AAG3C,QAAI,gBAAgB,QAAW;AAC7B,YAAM,aAAa,OAAO,QAAQ,GAAG;AACrC,UAAI,YAAY,SAAS,eAAe;AACtC,YAAI,WAAW,KAAK,SAAS,GAAG;AAC9B,gBAAM,IAAI;AAAA,YACR,qBAAqB,UAAU,KAAK,GAAG,CAAC,cACtC,OAAO,QACT;AAAA,UACF;AAAA,QACF;AACA,sBAAc;AAAA,UACZ,MAAM;AAAA,UACN,MAAM,WAAW,KAAK,CAAC;AAAA,UACvB,UAAU;AAAA,QACZ;AAAA,MACF,OAAO;AACL,cAAM,IAAI;AAAA,UACR,sBAAsB,UAAU,KAAK,GAAG,CAAC,eACvC,OAAO,QACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,YAAY,SAAS,aAAa;AACpC,YAAM,iBAAiB,MAAM;AAAA,QAC3B;AAAA,QACA,YAAY;AAAA,QACZ,KAAK,QAAQ,OAAO,QAAQ;AAAA,MAC9B;AACA,aAAO,kCAAkC,QAAQ,gBAAgB;AAAA,QAC/D,YAAY;AAAA,QACZ,GAAG,UAAU,MAAM,CAAC;AAAA,MACtB,CAAC;AAAA,IACH,WAGS,YAAY,SAAS,eAAe;AAC3C,YAAM,iBAAiB,MAAM;AAAA,QAC3B;AAAA,QACA,YAAY,KAAK,CAAC;AAAA,QAClB,KAAK,QAAQ,OAAO,QAAQ;AAAA,MAC9B;AACA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,UAAU,MAAM,CAAC;AAAA,MACnB;AAAA,IACF;AAEA,QAAI,YAAY,SAAS,oBAAoB;AAC3C,aAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM,OAAO;AAAA,QACb,MAAM,UAAU,UAAU,SAAS,CAAC;AAAA,MACtC;AAAA,IACF,WAAW,YAAY,SAAS,YAAY;AAC1C,aAAO,EAAE,MAAM,YAAY,OAAO,YAAY,MAAM;AAAA,IACtD,WAAW,YAAY,SAAS,UAAU;AACxC,UAAI,UAAe,YAAY;AAC/B,UAAI,QAAQ;AAEZ,SAAG;AACD,YAAI,OAAO,YAAY,YAAY,OAAO,YAAY,UAAU;AAC9D,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,QACF,WACE,CAAC,WACA,OAAO,YAAY,YAAY,CAAC,MAAM,QAAQ,OAAO,GACtD;AACA,gBAAM,IAAI;AAAA,YACR,iCAAiC,UAAU;AAAA,OACzC,UAAU,KAAK,CACjB,kBAAkB,OAAO,OAAO;AAAA,UAClC;AAAA,QACF;AACA;AAEA,YAAI,UAAU,UAAU,UAAU,WAAW,SAAS;AACpD,iBAAO,EAAE,MAAM,SAAS,OAAO,QAAQ,OAAO,EAAE;AAAA,QAClD,WAAW,UAAU,UAAU,UAAU,WAAW,SAAS;AAC3D,iBAAO,EAAE,MAAM,YAAY,OAAO,QAAQ,OAAO,EAAE;AAAA,QACrD,OAAO;AACL,oBAAU,QAAQ,UAAU,KAAK,CAAC;AAAA,QACpC;AAAA,MACF,SAAS;AACT,UAAI,UAAU,KAAK,MAAM,QAAW;AAClC,cAAM,IAAI;AAAA,UACR,sDAAsD,UACnD,MAAM,GAAG,KAAK,EACd,KAAK,GAAG,CAAC;AAAA,QACd;AAAA,MACF;AACA,YAAM,IAAI;AAAA,QACR,sDACE,UAAU,KAAK,CACjB,aAAa,UAAU,MAAM,GAAG,KAAK,EAAE,KAAK,GAAG,CAAC;AAAA,MAClD;AAAA,IACF,WAAW,YAAY,SAAS,SAAS;AACvC,aAAO,EAAE,MAAM,SAAS,OAAO,YAAY,MAAM;AAAA,IACnD;AACA,UAAM,IAAI;AAAA,MACR,0DACE,YAAY,IACd,oBAAoB,UAAU,KAAK,GAAG,CAAC;AAAA,IACzC;AAAA,EACF,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,+BAA+B,OAAO,QAAQ,KAC3C,MAAgB,OACnB;AAAA,oBAAuB,KAAK,UAAU,OAAO,SAAS,MAAM,CAAC,CAAC;AAAA,IAChE;AAAA,EACF;AACF;;;ADxnBA,eAAO,iBAIL,OACA,WACwB;AACxB,QAAM,WAAW,KAAK,MAAM;AAE5B,SAAO,KAAK,WAAW,KAAK,cAAc,CAAC,KAAK,WAAW;AACzD,QAAI,KAAK;AACP,aAAO,SAAS,GAAG;AAAA,IACrB;AACA,QAAI,CAAC,QAAQ;AACX,aAAO;AAAA,QACL,IAAI,MAAM,mBAAmB,KAAK,YAAY,WAAW;AAAA,MAC3D;AAAA,IACF;AACA,UAAM,EAAE,YAAY,IAAI,KAAK,WAAW;AACxC,UAAM,WAAW,kBAAkB,MAAM,aAAa,KAAK;AAE3D,aAAS,MAAM,MAAM;AACrB,UAAM,MAAM,WAAW,MAAM;AAC7B,aAAS,OAAO,GAAG;AAEnB,WAAO,yBAAyB,MAAM,KAAK,SAAS,GAAG,EAAE,KAAK,CAAC,WAAW;AACxE,eAAS,gBAAgB,GAAG;AAC5B,aAAO,SAAS,MAAM,QAAQ,SAAS;AAAA,IACzC,GAAG,QAAQ;AAAA,EACb,CAAC;AACH;AAEA,SAAS,WAAW,MAAgD;AAClE,MAAI;AAEJ,MAAI,OAAO,SAAS,UAAU;AAC5B,iBAAa;AAAA,EACf,WAAW,gBAAgB,QAAQ;AACjC,iBAAa,KAAK,SAAS,OAAO;AAAA,EACpC,WAAW,gBAAgB,aAAa;AACtC,iBAAa,IAAI,YAAY,OAAO,EAAE,OAAO,IAAI;AAAA,EACnD,OAAO;AACL,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY,WAAW,MAAM,wBAAwB;AAC3D,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAM,eAAe,UAAU,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC;AAC/C,cAAU;AAAA,EACZ;AACA,MAAI,QAAQ;AACV,aAAS,qCAAqC;AAAA,EAChD;AAEA,SAAO;AACT;AAEA,SAAS,kBACP,eACA,cACA;AACA,MACE,CAAC,gBACA,iBAAiB,QAChB,aAAa,UACb,CAAC,aAAa,OAAO,cAAc,YAAY,GACjD;AACA,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AACA,QAAM,YAAY,iBAAiB,OAAO,OAAO,aAAa;AAC9D,SAAO,CACL,aACA,YACG;AACH,QAAI,gBAAgB,aAAa,cAAc,OAAO;AACpD,cAAQ;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc,YACV;AAAA,UACE,cAAc,UAAU;AAAA,UACxB,cAAc;AAAA,QAChB,IACA,cAAc;AAAA,QAClB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../loaders/css-loader.ts","../../loaders/lib/resolveCrossFileSelectors.ts"],"sourcesContent":["import { relative } from \"path\";\nimport type { LoaderContext } from \"webpack\";\nimport type { YakConfigOptions } from \"../withYak/index.js\";\nimport { resolveCrossFileConstant } from \"./lib/resolveCrossFileSelectors.js\";\n\n/**\n * Transform typescript to css\n *\n * This loader takes the cached result from the yak tsloader\n * and extracts the css from the generated comments\n */\nexport default async function cssExtractLoader(\n this: LoaderContext<YakConfigOptions>,\n // Instead of the source code, we receive the extracted css\n // from the yak-swc transformation\n _code: string,\n sourceMap: string | undefined,\n): Promise<string | void> {\n const callback = this.async();\n // Load the module from the original typescript request (without !=! and the query)\n return this.loadModule(this.resourcePath, (err, source) => {\n if (err) {\n return callback(err);\n }\n if (!source) {\n return callback(\n new Error(`Source code for ${this.resourcePath} is empty`),\n );\n }\n const { experiments } = this.getOptions();\n const debugLog = createDebugLogger(this, experiments?.debug);\n\n debugLog(\"ts\", source);\n const css = extractCss(source);\n debugLog(\"css\", css);\n\n return resolveCrossFileConstant(this, this.context, css).then((result) => {\n debugLog(\"css resolved\", css);\n return callback(null, result, sourceMap);\n }, callback);\n });\n}\n\nfunction extractCss(code: string | Buffer<ArrayBufferLike>): string {\n let codeString: string;\n\n if (typeof code === \"string\") {\n codeString = code;\n } else if (code instanceof Buffer) {\n codeString = code.toString(\"utf-8\");\n } else if (code instanceof ArrayBuffer) {\n codeString = new TextDecoder(\"utf-8\").decode(code);\n } else {\n throw new Error(\n \"Invalid input type: code must be string, Buffer, or ArrayBuffer\",\n );\n }\n\n const codeParts = codeString.split(\"/*YAK Extracted CSS:\\n\");\n let result = \"\";\n for (let i = 1; i < codeParts.length; i++) {\n const codeUntilEnd = codeParts[i].split(\"*/\")[0];\n result += codeUntilEnd;\n }\n if (result) {\n result = \"/* cssmodules-pure-no-check */\\n\" + result;\n }\n\n return result;\n}\n\nfunction createDebugLogger(\n loaderContext: LoaderContext<YakConfigOptions>,\n debugOptions: Required<YakConfigOptions>[\"experiments\"][\"debug\"],\n) {\n if (\n !debugOptions ||\n (debugOptions !== true &&\n debugOptions.filter &&\n !debugOptions.filter(loaderContext.resourcePath))\n ) {\n return () => {};\n }\n const debugType = debugOptions === true ? \"ts\" : debugOptions.type;\n return (\n messageType: \"ts\" | \"css\" | \"css resolved\",\n message: string | Buffer<ArrayBufferLike> | undefined,\n ) => {\n if (messageType === debugType || debugType === \"all\") {\n console.log(\n \"🐮 Yak\",\n messageType,\n \"\\n\",\n loaderContext._compiler\n ? relative(\n loaderContext._compiler.context,\n loaderContext.resourcePath,\n )\n : loaderContext.resourcePath,\n \"\\n\\n\",\n message,\n );\n }\n };\n}\n","import babel from \"@babel/core\";\nimport path from \"path\";\n// @ts-expect-error - this is used by babel directly so we ignore that it is not typed\nimport babelPlugin from \"@babel/plugin-syntax-typescript\";\nimport type { Compilation, LoaderContext } from \"webpack\";\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\nconst compilationCache = new WeakMap<\n Compilation,\n {\n parsedFiles: Map<string, ParsedFile>;\n }\n>();\n\nconst getCompilationCache = (loader: LoaderContext<{}>) => {\n const compilation = loader._compilation;\n if (!compilation) {\n throw new Error(\"Webpack compilation object not available\");\n }\n let cache = compilationCache.get(compilation);\n if (!cache) {\n cache = {\n parsedFiles: new Map(),\n };\n compilationCache.set(compilation, cache);\n }\n return cache;\n};\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 loader: LoaderContext<{}>,\n pathContext: string,\n css: string,\n): Promise<string> {\n // Search for --yak-css-import: url(\"path/to/module\") in the css\n const matches = [...css.matchAll(yakCssImportRegex)].map((match) => {\n const [fullMatch, encodedArguments, importKind, semicolon] = match;\n const [moduleSpecifier, ...specifier] = encodedArguments\n .split(\":\")\n .map((entry) => decodeURIComponent(entry));\n return {\n encodedArguments,\n moduleSpecifier,\n specifier,\n importKind,\n semicolon,\n position: match.index!,\n size: fullMatch.length,\n };\n });\n if (matches.length === 0) return css;\n\n try {\n // Resolve all imports concurrently\n const resolvedValues = await Promise.all(\n matches.map(async ({ moduleSpecifier, specifier }) => {\n const parsedModule = await parseModule(\n loader,\n moduleSpecifier,\n pathContext,\n );\n\n const resolvedValue = await resolveModuleSpecifierRecursively(\n loader,\n parsedModule,\n specifier,\n );\n\n return resolvedValue;\n }),\n );\n\n // Replace the imports with the resolved values\n let result = css;\n for (let i = matches.length - 1; i >= 0; i--) {\n const { position, size, importKind, specifier, semicolon } = matches[i];\n const resolved = resolvedValues[i];\n\n if (importKind === \"selector\") {\n if (\n resolved.type !== \"styled-component\" &&\n resolved.type !== \"constant\"\n ) {\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 const 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))\n ? \"\"\n : semicolon);\n\n result =\n result.slice(0, position) +\n String(replacement) +\n result.slice(position + size);\n }\n\n return result;\n } catch (error) {\n throw new Error(\n `Error resolving cross-file selectors: ${\n (error as Error).message\n }\\nFile: ${loader.resourcePath}`,\n );\n }\n}\n\n/**\n * Resolves a module by wrapping loader.resolve in a promise\n */\nexport async function resolveModule(\n loader: LoaderContext<{}>,\n moduleSpecifier: string,\n context: string,\n): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n loader.resolve(context, moduleSpecifier, (err, result) => {\n if (err) return reject(err);\n if (!result)\n return reject(new Error(`Could not resolve ${moduleSpecifier}`));\n resolve(result);\n });\n });\n}\n\n/**\n * Resolves a module specifier to a parsed file\n *\n * e.g.:\n * ```\n * parseModule(loader, \"./theme\", \"/path/to/styles.ts\")\n * // -> { type: 'regular', secondary: { type: 'constant', value: '#00ff00' } } }, filePath: '/path/to/theme.ts' }\n * ```\n */\nasync function parseModule(\n loader: LoaderContext<{}>,\n moduleSpecifier: string,\n context: string,\n): Promise<ParsedFile> {\n const cache = getCompilationCache(loader).parsedFiles;\n\n // The cache key is valid for the entire project so it can be reused\n // for different source files\n const resolvedModule = await resolveModule(loader, moduleSpecifier, context);\n\n let parsedFile = cache.get(resolvedModule);\n if (!parsedFile) {\n parsedFile = await parseFile(loader, resolvedModule);\n\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 cache.set(resolvedModule, parsedFile);\n }\n // on file change, invalidate the cache\n loader.addDependency(parsedFile.filePath);\n return parsedFile;\n}\n\nasync function parseFile(\n loader: LoaderContext<{}>,\n filePath: string,\n): Promise<ParsedFile> {\n const isYak =\n filePath.endsWith(\".yak.ts\") ||\n filePath.endsWith(\".yak.tsx\") ||\n filePath.endsWith(\".yak.js\") ||\n filePath.endsWith(\".yak.jsx\");\n const isTSX = filePath.endsWith(\".tsx\");\n\n try {\n if (isYak) {\n const module: Record<string, unknown> =\n await loader.importModule(filePath);\n const mappedModule = Object.fromEntries(\n Object.entries(module).map(([key, value]): [string, ParsedExport] => {\n if (typeof value === \"string\" || typeof value === \"number\") {\n return [key, { type: \"constant\" as const, value }];\n } else if (\n value &&\n (typeof value === \"object\" || Array.isArray(value))\n ) {\n return [key, { type: \"record\" as const, value }];\n } else {\n return [key, { type: \"unsupported\" as const, hint: String(value) }];\n }\n }),\n );\n return { type: \"yak\", exports: mappedModule, filePath };\n }\n const sourceContents = new Promise<string>((resolve, reject) =>\n loader.fs.readFile(filePath, \"utf-8\", (err, result) => {\n if (err) return reject(err);\n resolve(result || \"\");\n }),\n );\n\n const tranformedSource = new Promise<string>((resolve, reject) => {\n loader.loadModule(filePath, (err, source) => {\n if (err) return reject(err);\n let sourceString: string;\n if (typeof source === \"string\") {\n sourceString = source;\n } else if (source instanceof Buffer) {\n sourceString = source.toString(\"utf-8\");\n } else if (source instanceof ArrayBuffer) {\n sourceString = new TextDecoder(\"utf-8\").decode(source);\n } else {\n throw new Error(\n \"Invalid input type: code must be string, Buffer, or ArrayBuffer\",\n );\n }\n resolve(sourceString || \"\");\n });\n });\n\n const exports = await parseExports(await sourceContents, isTSX);\n const mixins = parseMixins(await tranformedSource);\n Object.assign(exports, parseStyledComponents(await tranformedSource));\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 await Promise.all(\n Object.entries(mixins).map(async ([name, { value, nameParts }]) => {\n const resolvedValue = await resolveCrossFileConstant(\n loader,\n path.dirname(filePath),\n value,\n );\n if (nameParts.length === 1) {\n exports[name] = { type: \"mixin\", value: resolvedValue };\n } else {\n let exportEntry: undefined | ParsedExport = exports[nameParts[0]];\n if (!exportEntry) {\n exportEntry = { type: \"record\", value: {} };\n exports[nameParts[0]] = exportEntry;\n } else if (exportEntry.type !== \"record\") {\n throw new Error(\n `Error parsing file ${filePath}: ${nameParts[0]} is not a record`,\n );\n }\n let current = exportEntry.value as Record<any, ParsedExport>;\n for (let i = 1; i < nameParts.length - 1; i++) {\n let next = current[nameParts[i]];\n if (!next) {\n next = { type: \"record\", value: {} };\n current[nameParts[i]] = next;\n } else if (next.type !== \"record\") {\n throw new Error(\n `Error parsing file ${filePath}: ${nameParts[i]} is not a record`,\n );\n }\n current = next.value;\n }\n current[nameParts[nameParts.length - 1]] = {\n type: \"mixin\",\n value: resolvedValue,\n };\n }\n }),\n );\n\n return {\n type: \"regular\",\n exports,\n filePath,\n };\n } catch (error) {\n throw new Error(\n `Error parsing file ${filePath}: ${(error as Error).message}`,\n );\n }\n}\n\nasync function parseExports(\n sourceContents: string,\n isTSX: boolean,\n): Promise<Record<string, ParsedExport>> {\n let exports: Record<string, ParsedExport> = {};\n\n try {\n babel.transformSync(sourceContents, {\n configFile: false,\n plugins: [\n [babelPlugin, { isTSX }],\n [\n (): babel.PluginObj => ({\n visitor: {\n ExportNamedDeclaration({ node }) {\n if (node.source) {\n node.specifiers.forEach((specifier) => {\n if (\n specifier.type === \"ExportSpecifier\" &&\n specifier.exported.type === \"Identifier\" &&\n specifier.local.type === \"Identifier\"\n ) {\n exports[specifier.exported.name] = {\n type: \"re-export\",\n from: node.source!.value,\n imported: specifier.local.name,\n };\n }\n });\n } else if (node.declaration?.type === \"VariableDeclaration\") {\n node.declaration.declarations.forEach((declaration) => {\n if (\n declaration.id.type === \"Identifier\" &&\n declaration.init\n ) {\n const parsed = parseExportValueExpression(\n declaration.init,\n );\n if (parsed) {\n exports[declaration.id.name] = parsed;\n }\n }\n });\n }\n },\n ExportDeclaration({ node }) {\n if (\"specifiers\" in node && node.source) {\n const { specifiers, source } = node;\n specifiers.forEach((specifier) => {\n // export * as color from \"./colors\";\n if (\n specifier.type === \"ExportNamespaceSpecifier\" &&\n specifier.exported.type === \"Identifier\"\n ) {\n exports[specifier.exported.name] = {\n type: \"star-export\",\n from: [source.value],\n };\n }\n });\n }\n },\n ExportAllDeclaration({ node }) {\n if (Object.keys(exports).length === 0) {\n exports[\"*\"] ||= {\n type: \"star-export\",\n from: [],\n };\n if (exports[\"*\"].type !== \"star-export\") {\n throw new Error(\"Invalid star export state\");\n }\n exports[\"*\"].from.push(node.source.value);\n }\n },\n },\n }),\n ],\n ],\n });\n\n return exports;\n } catch (error) {\n throw new Error(`Error parsing exports: ${(error as Error).message}`);\n }\n}\n\nfunction parseMixins(\n sourceContents: string,\n): Record<string, { type: \"mixin\"; value: string; nameParts: string[] }> {\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<\n string,\n { type: \"mixin\"; value: string; nameParts: string[] }\n > = {};\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(\n sourceContents: string,\n): Record<string, { type: \"styled-component\"; value: string }> {\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<\n string,\n { type: \"styled-component\"; value: string }\n > = {};\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 value: `:global(.${className})`,\n };\n }\n\n return styledComponents;\n}\n\n/**\n * Unpacks a TSAsExpression to its expression value\n */\nfunction unpackTSAsExpression(\n node: babel.types.TSAsExpression | babel.types.Expression,\n): babel.types.Expression {\n if (node.type === \"TSAsExpression\") {\n return unpackTSAsExpression(node.expression);\n }\n return node;\n}\n\nfunction parseExportValueExpression(\n node: babel.types.Expression,\n): ParsedExport {\n // ignores `as` casts so it doesn't interfere with the ast node type detection\n const expression = unpackTSAsExpression(node);\n if (\n expression.type === \"CallExpression\" ||\n expression.type === \"TaggedTemplateExpression\"\n ) {\n // The value will be set by parseStyledComponents\n return { type: \"styled-component\", value: undefined };\n } else if (\n expression.type === \"StringLiteral\" ||\n expression.type === \"NumericLiteral\"\n ) {\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 (\n expression.type === \"TemplateLiteral\" &&\n expression.quasis.length === 1\n ) {\n return { type: \"constant\", value: expression.quasis[0].value.raw };\n } else if (expression.type === \"ObjectExpression\") {\n return { type: \"record\", value: parseObjectExpression(expression) };\n }\n return { type: \"unsupported\", hint: expression.type };\n}\n\nfunction parseObjectExpression(\n node: babel.types.ObjectExpression,\n): Record<string, ParsedExport> {\n let result: Record<string, ParsedExport> = {};\n for (const property of node.properties) {\n if (\n property.type === \"ObjectProperty\" &&\n property.key.type === \"Identifier\"\n ) {\n const key = property.key.name;\n const parsed = parseExportValueExpression(\n property.value as babel.types.Expression,\n );\n if (parsed) {\n result[key] = parsed;\n }\n }\n }\n return result;\n}\n\n/**\n * Follows a specifier recursively until it finds its constant value\n * for example here it follows \"colors.primary\"\n *\n * ```\n * resolveModuleSpecifierRecursively(loader, \"@/theme\", [\"colors\", \"primary\"], \"colors:primary\")`\n * // -> { type: 'constant', value: '#ff0000' }\n * ```\n *\n * example structure:\n *\n * styles.ts:\n * ```\n * import { colors } from \"@/theme\";\n * export const button = css`color: ${colors.primary}`;\n * ```\n *\n * theme.ts:\n * ```\n * export { colors } from \"./colors\";\n * ```\n *\n * colors.ts:\n * ```\n * export const colors = { primary: \"#ff0000\" };\n * ```\n *\n */\nasync function resolveModuleSpecifierRecursively(\n loader: LoaderContext<{}>,\n module: ParsedFile,\n specifier: string[],\n): Promise<ResolvedExport> {\n try {\n const exportName = specifier[0];\n let exportValue = module.exports[exportName];\n // Follow star exports if there is only a single one\n // and the export does not exist in the current module\n if (exportValue === undefined) {\n const starExport = module.exports[\"*\"];\n if (starExport?.type === \"star-export\") {\n if (starExport.from.length > 1) {\n throw new Error(\n `Could not resolve ${specifier.join(\".\")} in module ${\n module.filePath\n } - Multiple star exports are not supported for performance reasons`,\n );\n }\n exportValue = {\n type: \"re-export\" as const,\n from: starExport.from[0],\n imported: exportName,\n };\n } else {\n throw new Error(\n `Could not resolve \"${specifier.join(\".\")}\" in module ${\n module.filePath\n }`,\n );\n }\n }\n // Follow reexport\n // e.g. export { colors as primaryColors } from \"./colors\";\n if (exportValue.type === \"re-export\") {\n const importedModule = await parseModule(\n loader,\n exportValue.from,\n path.dirname(module.filePath),\n );\n return resolveModuleSpecifierRecursively(loader, importedModule, [\n exportValue.imported,\n ...specifier.slice(1),\n ]);\n }\n // Namespace export\n // e.g. export * as colors from \"./colors\";\n else if (exportValue.type === \"star-export\") {\n const importedModule = await parseModule(\n loader,\n exportValue.from[0],\n path.dirname(module.filePath),\n );\n return resolveModuleSpecifierRecursively(\n loader,\n importedModule,\n specifier.slice(1),\n );\n }\n\n if (exportValue.type === \"styled-component\") {\n return {\n type: \"styled-component\",\n from: module.filePath,\n name: specifier[specifier.length - 1],\n value: exportValue.value,\n };\n } else if (exportValue.type === \"constant\") {\n return { type: \"constant\", value: exportValue.value };\n } else if (exportValue.type === \"record\") {\n let current: any = exportValue.value;\n let depth = 0;\n /// Drill down the specifier e.g. colors.primary\n do {\n if (typeof current === \"string\" || typeof current === \"number\") {\n return {\n type: \"constant\" as const,\n value: current,\n };\n } else if (\n !current ||\n (typeof current !== \"object\" && !Array.isArray(current))\n ) {\n throw new Error(\n `Error unpacking Record/Array \"${exportName}\".\\nKey \"${\n specifier[depth]\n }\" was of type \"${typeof current}\" but only String and Number are supported`,\n );\n }\n depth++;\n // mixins in .yak files are wrapped inside an object with a __yak key\n if (depth === specifier.length && \"__yak\" in current) {\n return { type: \"mixin\", value: current[\"__yak\"] };\n } else if (depth === specifier.length && \"value\" in current) {\n return { type: \"constant\", value: current[\"value\"] };\n } else {\n current = current[specifier[depth]];\n }\n } while (current);\n if (specifier[depth] === undefined) {\n throw new Error(\n `Error unpacking Record/Array - could not extract \\`${specifier\n .slice(0, depth)\n .join(\".\")}\\` is not a string or number`,\n );\n }\n throw new Error(\n `Error unpacking Record/Array - could not extract \\`${\n specifier[depth]\n }\\` from \\`${specifier.slice(0, depth).join(\".\")}\\``,\n );\n } else if (exportValue.type === \"mixin\") {\n return { type: \"mixin\", value: exportValue.value };\n }\n throw new Error(\n `Error unpacking Record/Array - unexpected exportValue \"${\n exportValue.type\n }\" for specifier \"${specifier.join(\".\")}\"`,\n );\n } catch (error) {\n throw new Error(\n `Error resolving from module ${module.filePath}: ${\n (error as Error).message\n }\\nExtracted values: ${JSON.stringify(module.exports, null, 2)}`,\n );\n }\n}\n\ntype ParsedFile =\n | { type: \"regular\"; exports: Record<string, ParsedExport>; filePath: string }\n | { type: \"yak\"; exports: Record<string, ParsedExport>; filePath: string };\n\ntype ParsedExport =\n | { type: \"styled-component\"; value: string | undefined }\n | { type: \"mixin\"; value: string }\n | { type: \"constant\"; value: string | number }\n | { type: \"record\"; value: Record<any, ParsedExport> | {} }\n | { type: \"unsupported\"; hint?: string }\n | { type: \"re-export\"; from: string; imported: string }\n | { type: \"star-export\"; from: string[] };\n\ntype ResolvedExport =\n | {\n type: \"styled-component\";\n from: string;\n name: string;\n value: string | undefined;\n }\n | { type: \"mixin\"; value: string | number }\n | { type: \"constant\"; value: string | number };\n"],"mappings":";AAAA,SAAS,gBAAgB;;;ACAzB,OAAO,WAAW;AAClB,OAAO,UAAU;AAEjB,OAAO,iBAAiB;AAGxB,IAAM;AAAA;AAAA,EAEJ;AAAA;AAEF,IAAM,mBAAmB,oBAAI,QAK3B;AAEF,IAAM,sBAAsB,CAAC,WAA8B;AACzD,QAAM,cAAc,OAAO;AAC3B,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,MAAI,QAAQ,iBAAiB,IAAI,WAAW;AAC5C,MAAI,CAAC,OAAO;AACV,YAAQ;AAAA,MACN,aAAa,oBAAI,IAAI;AAAA,IACvB;AACA,qBAAiB,IAAI,aAAa,KAAK;AAAA,EACzC;AACA,SAAO;AACT;AAqBA,eAAsB,yBACpB,QACA,aACA,KACiB;AAEjB,QAAM,UAAU,CAAC,GAAG,IAAI,SAAS,iBAAiB,CAAC,EAAE,IAAI,CAAC,UAAU;AAClE,UAAM,CAAC,WAAW,kBAAkB,YAAY,SAAS,IAAI;AAC7D,UAAM,CAAC,iBAAiB,GAAG,SAAS,IAAI,iBACrC,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,mBAAmB,KAAK,CAAC;AAC3C,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,MAAM,UAAU;AAAA,IAClB;AAAA,EACF,CAAC;AACD,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,MAAI;AAEF,UAAM,iBAAiB,MAAM,QAAQ;AAAA,MACnC,QAAQ,IAAI,OAAO,EAAE,iBAAiB,UAAU,MAAM;AACpD,cAAM,eAAe,MAAM;AAAA,UACzB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAEA,cAAM,gBAAgB,MAAM;AAAA,UAC1B;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAEA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAGA,QAAI,SAAS;AACb,aAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,YAAM,EAAE,UAAU,MAAM,YAAY,WAAW,UAAU,IAAI,QAAQ,CAAC;AACtE,YAAM,WAAW,eAAe,CAAC;AAEjC,UAAI,eAAe,YAAY;AAC7B,YACE,SAAS,SAAS,sBAClB,SAAS,SAAS,YAClB;AACA,gBAAM,IAAI;AAAA,YACR,SACE,SAAS,IACX,iEAAiE,UAAU;AAAA,cACzE;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAEA,YAAM,cACJ,SAAS,SAAS,qBACd,SAAS,QACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAQR,CAAC,KAAK,GAAG,EAAE,SAAS,OAAO,SAAS,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,IAC3D,KACA;AAEV,eACE,OAAO,MAAM,GAAG,QAAQ,IACxB,OAAO,WAAW,IAClB,OAAO,MAAM,WAAW,IAAI;AAAA,IAChC;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,yCACG,MAAgB,OACnB;AAAA,QAAW,OAAO,YAAY;AAAA,IAChC;AAAA,EACF;AACF;AAKA,eAAsB,cACpB,QACA,iBACA,SACiB;AACjB,SAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC9C,WAAO,QAAQ,SAAS,iBAAiB,CAAC,KAAK,WAAW;AACxD,UAAI,IAAK,QAAO,OAAO,GAAG;AAC1B,UAAI,CAAC;AACH,eAAO,OAAO,IAAI,MAAM,qBAAqB,eAAe,EAAE,CAAC;AACjE,cAAQ,MAAM;AAAA,IAChB,CAAC;AAAA,EACH,CAAC;AACH;AAWA,eAAe,YACb,QACA,iBACA,SACqB;AACrB,QAAM,QAAQ,oBAAoB,MAAM,EAAE;AAI1C,QAAM,iBAAiB,MAAM,cAAc,QAAQ,iBAAiB,OAAO;AAE3E,MAAI,aAAa,MAAM,IAAI,cAAc;AACzC,MAAI,CAAC,YAAY;AACf,iBAAa,MAAM,UAAU,QAAQ,cAAc;AAKnD,UAAM,IAAI,gBAAgB,UAAU;AAAA,EACtC;AAEA,SAAO,cAAc,WAAW,QAAQ;AACxC,SAAO;AACT;AAEA,eAAe,UACb,QACA,UACqB;AACrB,QAAM,QACJ,SAAS,SAAS,SAAS,KAC3B,SAAS,SAAS,UAAU,KAC5B,SAAS,SAAS,SAAS,KAC3B,SAAS,SAAS,UAAU;AAC9B,QAAM,QAAQ,SAAS,SAAS,MAAM;AAEtC,MAAI;AACF,QAAI,OAAO;AACT,YAAM,SACJ,MAAM,OAAO,aAAa,QAAQ;AACpC,YAAM,eAAe,OAAO;AAAA,QAC1B,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAA8B;AACnE,cAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU;AAC1D,mBAAO,CAAC,KAAK,EAAE,MAAM,YAAqB,MAAM,CAAC;AAAA,UACnD,WACE,UACC,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,IACjD;AACA,mBAAO,CAAC,KAAK,EAAE,MAAM,UAAmB,MAAM,CAAC;AAAA,UACjD,OAAO;AACL,mBAAO,CAAC,KAAK,EAAE,MAAM,eAAwB,MAAM,OAAO,KAAK,EAAE,CAAC;AAAA,UACpE;AAAA,QACF,CAAC;AAAA,MACH;AACA,aAAO,EAAE,MAAM,OAAO,SAAS,cAAc,SAAS;AAAA,IACxD;AACA,UAAM,iBAAiB,IAAI;AAAA,MAAgB,CAAC,SAAS,WACnD,OAAO,GAAG,SAAS,UAAU,SAAS,CAAC,KAAK,WAAW;AACrD,YAAI,IAAK,QAAO,OAAO,GAAG;AAC1B,gBAAQ,UAAU,EAAE;AAAA,MACtB,CAAC;AAAA,IACH;AAEA,UAAM,mBAAmB,IAAI,QAAgB,CAAC,SAAS,WAAW;AAChE,aAAO,WAAW,UAAU,CAAC,KAAK,WAAW;AAC3C,YAAI,IAAK,QAAO,OAAO,GAAG;AAC1B,YAAI;AACJ,YAAI,OAAO,WAAW,UAAU;AAC9B,yBAAe;AAAA,QACjB,WAAW,kBAAkB,QAAQ;AACnC,yBAAe,OAAO,SAAS,OAAO;AAAA,QACxC,WAAW,kBAAkB,aAAa;AACxC,yBAAe,IAAI,YAAY,OAAO,EAAE,OAAO,MAAM;AAAA,QACvD,OAAO;AACL,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AACA,gBAAQ,gBAAgB,EAAE;AAAA,MAC5B,CAAC;AAAA,IACH,CAAC;AAED,UAAM,UAAU,MAAM,aAAa,MAAM,gBAAgB,KAAK;AAC9D,UAAM,SAAS,YAAY,MAAM,gBAAgB;AACjD,WAAO,OAAO,SAAS,sBAAsB,MAAM,gBAAgB,CAAC;AAKpE,UAAM,QAAQ;AAAA,MACZ,OAAO,QAAQ,MAAM,EAAE,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,UAAU,CAAC,MAAM;AACjE,cAAM,gBAAgB,MAAM;AAAA,UAC1B;AAAA,UACA,KAAK,QAAQ,QAAQ;AAAA,UACrB;AAAA,QACF;AACA,YAAI,UAAU,WAAW,GAAG;AAC1B,kBAAQ,IAAI,IAAI,EAAE,MAAM,SAAS,OAAO,cAAc;AAAA,QACxD,OAAO;AACL,cAAI,cAAwC,QAAQ,UAAU,CAAC,CAAC;AAChE,cAAI,CAAC,aAAa;AAChB,0BAAc,EAAE,MAAM,UAAU,OAAO,CAAC,EAAE;AAC1C,oBAAQ,UAAU,CAAC,CAAC,IAAI;AAAA,UAC1B,WAAW,YAAY,SAAS,UAAU;AACxC,kBAAM,IAAI;AAAA,cACR,sBAAsB,QAAQ,KAAK,UAAU,CAAC,CAAC;AAAA,YACjD;AAAA,UACF;AACA,cAAI,UAAU,YAAY;AAC1B,mBAAS,IAAI,GAAG,IAAI,UAAU,SAAS,GAAG,KAAK;AAC7C,gBAAI,OAAO,QAAQ,UAAU,CAAC,CAAC;AAC/B,gBAAI,CAAC,MAAM;AACT,qBAAO,EAAE,MAAM,UAAU,OAAO,CAAC,EAAE;AACnC,sBAAQ,UAAU,CAAC,CAAC,IAAI;AAAA,YAC1B,WAAW,KAAK,SAAS,UAAU;AACjC,oBAAM,IAAI;AAAA,gBACR,sBAAsB,QAAQ,KAAK,UAAU,CAAC,CAAC;AAAA,cACjD;AAAA,YACF;AACA,sBAAU,KAAK;AAAA,UACjB;AACA,kBAAQ,UAAU,UAAU,SAAS,CAAC,CAAC,IAAI;AAAA,YACzC,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,sBAAsB,QAAQ,KAAM,MAAgB,OAAO;AAAA,IAC7D;AAAA,EACF;AACF;AAEA,eAAe,aACb,gBACA,OACuC;AACvC,MAAI,UAAwC,CAAC;AAE7C,MAAI;AACF,UAAM,cAAc,gBAAgB;AAAA,MAClC,YAAY;AAAA,MACZ,SAAS;AAAA,QACP,CAAC,aAAa,EAAE,MAAM,CAAC;AAAA,QACvB;AAAA,UACE,OAAwB;AAAA,YACtB,SAAS;AAAA,cACP,uBAAuB,EAAE,KAAK,GAAG;AAC/B,oBAAI,KAAK,QAAQ;AACf,uBAAK,WAAW,QAAQ,CAAC,cAAc;AACrC,wBACE,UAAU,SAAS,qBACnB,UAAU,SAAS,SAAS,gBAC5B,UAAU,MAAM,SAAS,cACzB;AACA,8BAAQ,UAAU,SAAS,IAAI,IAAI;AAAA,wBACjC,MAAM;AAAA,wBACN,MAAM,KAAK,OAAQ;AAAA,wBACnB,UAAU,UAAU,MAAM;AAAA,sBAC5B;AAAA,oBACF;AAAA,kBACF,CAAC;AAAA,gBACH,WAAW,KAAK,aAAa,SAAS,uBAAuB;AAC3D,uBAAK,YAAY,aAAa,QAAQ,CAAC,gBAAgB;AACrD,wBACE,YAAY,GAAG,SAAS,gBACxB,YAAY,MACZ;AACA,4BAAM,SAAS;AAAA,wBACb,YAAY;AAAA,sBACd;AACA,0BAAI,QAAQ;AACV,gCAAQ,YAAY,GAAG,IAAI,IAAI;AAAA,sBACjC;AAAA,oBACF;AAAA,kBACF,CAAC;AAAA,gBACH;AAAA,cACF;AAAA,cACA,kBAAkB,EAAE,KAAK,GAAG;AAC1B,oBAAI,gBAAgB,QAAQ,KAAK,QAAQ;AACvC,wBAAM,EAAE,YAAY,OAAO,IAAI;AAC/B,6BAAW,QAAQ,CAAC,cAAc;AAEhC,wBACE,UAAU,SAAS,8BACnB,UAAU,SAAS,SAAS,cAC5B;AACA,8BAAQ,UAAU,SAAS,IAAI,IAAI;AAAA,wBACjC,MAAM;AAAA,wBACN,MAAM,CAAC,OAAO,KAAK;AAAA,sBACrB;AAAA,oBACF;AAAA,kBACF,CAAC;AAAA,gBACH;AAAA,cACF;AAAA,cACA,qBAAqB,EAAE,KAAK,GAAG;AAC7B,oBAAI,OAAO,KAAK,OAAO,EAAE,WAAW,GAAG;AACrC,0BAAQ,GAAG,MAAM;AAAA,oBACf,MAAM;AAAA,oBACN,MAAM,CAAC;AAAA,kBACT;AACA,sBAAI,QAAQ,GAAG,EAAE,SAAS,eAAe;AACvC,0BAAM,IAAI,MAAM,2BAA2B;AAAA,kBAC7C;AACA,0BAAQ,GAAG,EAAE,KAAK,KAAK,KAAK,OAAO,KAAK;AAAA,gBAC1C;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,0BAA2B,MAAgB,OAAO,EAAE;AAAA,EACtE;AACF;AAEA,SAAS,YACP,gBACuE;AAKvE,QAAM,aAAa,eAAe,MAAM,uBAAuB;AAC/D,MAAI,SAGA,CAAC;AAEL,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAM,CAAC,OAAO,IAAI,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;AAC7C,UAAM,WAAW,QAAQ,QAAQ,IAAI;AACrC,UAAM,OAAO,QAAQ,MAAM,GAAG,QAAQ;AACtC,UAAM,QAAQ,QAAQ,MAAM,WAAW,CAAC;AACxC,WAAO,IAAI,IAAI;AAAA,MACb,MAAM;AAAA,MACN;AAAA,MACA,WAAW,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,mBAAmB,IAAI,CAAC;AAAA,IACnE;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,sBACP,gBAC6D;AAG7D,QAAM,cAAc,eAAe,MAAM,wBAAwB;AACjE,MAAI,mBAGA,CAAC;AAEL,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,UAAM,CAAC,OAAO,IAAI,YAAY,CAAC,EAAE,MAAM,MAAM,CAAC;AAC9C,UAAM,CAAC,eAAe,SAAS,IAAI,QAAQ,MAAM,GAAG;AACpD,qBAAiB,aAAa,IAAI;AAAA,MAChC,MAAM;AAAA,MACN,OAAO,YAAY,SAAS;AAAA,IAC9B;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,qBACP,MACwB;AACxB,MAAI,KAAK,SAAS,kBAAkB;AAClC,WAAO,qBAAqB,KAAK,UAAU;AAAA,EAC7C;AACA,SAAO;AACT;AAEA,SAAS,2BACP,MACc;AAEd,QAAM,aAAa,qBAAqB,IAAI;AAC5C,MACE,WAAW,SAAS,oBACpB,WAAW,SAAS,4BACpB;AAEA,WAAO,EAAE,MAAM,oBAAoB,OAAO,OAAU;AAAA,EACtD,WACE,WAAW,SAAS,mBACpB,WAAW,SAAS,kBACpB;AACA,WAAO,EAAE,MAAM,YAAY,OAAO,WAAW,MAAM;AAAA,EACrD,WACE,WAAW,SAAS,qBACpB,WAAW,aAAa,OACxB,WAAW,SAAS,SAAS,kBAC7B;AACA,WAAO,EAAE,MAAM,YAAY,OAAO,CAAC,WAAW,SAAS,MAAM;AAAA,EAC/D,WACE,WAAW,SAAS,qBACpB,WAAW,OAAO,WAAW,GAC7B;AACA,WAAO,EAAE,MAAM,YAAY,OAAO,WAAW,OAAO,CAAC,EAAE,MAAM,IAAI;AAAA,EACnE,WAAW,WAAW,SAAS,oBAAoB;AACjD,WAAO,EAAE,MAAM,UAAU,OAAO,sBAAsB,UAAU,EAAE;AAAA,EACpE;AACA,SAAO,EAAE,MAAM,eAAe,MAAM,WAAW,KAAK;AACtD;AAEA,SAAS,sBACP,MAC8B;AAC9B,MAAI,SAAuC,CAAC;AAC5C,aAAW,YAAY,KAAK,YAAY;AACtC,QACE,SAAS,SAAS,oBAClB,SAAS,IAAI,SAAS,cACtB;AACA,YAAM,MAAM,SAAS,IAAI;AACzB,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,MACX;AACA,UAAI,QAAQ;AACV,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AA8BA,eAAe,kCACb,QACA,QACA,WACyB;AACzB,MAAI;AACF,UAAM,aAAa,UAAU,CAAC;AAC9B,QAAI,cAAc,OAAO,QAAQ,UAAU;AAG3C,QAAI,gBAAgB,QAAW;AAC7B,YAAM,aAAa,OAAO,QAAQ,GAAG;AACrC,UAAI,YAAY,SAAS,eAAe;AACtC,YAAI,WAAW,KAAK,SAAS,GAAG;AAC9B,gBAAM,IAAI;AAAA,YACR,qBAAqB,UAAU,KAAK,GAAG,CAAC,cACtC,OAAO,QACT;AAAA,UACF;AAAA,QACF;AACA,sBAAc;AAAA,UACZ,MAAM;AAAA,UACN,MAAM,WAAW,KAAK,CAAC;AAAA,UACvB,UAAU;AAAA,QACZ;AAAA,MACF,OAAO;AACL,cAAM,IAAI;AAAA,UACR,sBAAsB,UAAU,KAAK,GAAG,CAAC,eACvC,OAAO,QACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,YAAY,SAAS,aAAa;AACpC,YAAM,iBAAiB,MAAM;AAAA,QAC3B;AAAA,QACA,YAAY;AAAA,QACZ,KAAK,QAAQ,OAAO,QAAQ;AAAA,MAC9B;AACA,aAAO,kCAAkC,QAAQ,gBAAgB;AAAA,QAC/D,YAAY;AAAA,QACZ,GAAG,UAAU,MAAM,CAAC;AAAA,MACtB,CAAC;AAAA,IACH,WAGS,YAAY,SAAS,eAAe;AAC3C,YAAM,iBAAiB,MAAM;AAAA,QAC3B;AAAA,QACA,YAAY,KAAK,CAAC;AAAA,QAClB,KAAK,QAAQ,OAAO,QAAQ;AAAA,MAC9B;AACA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,UAAU,MAAM,CAAC;AAAA,MACnB;AAAA,IACF;AAEA,QAAI,YAAY,SAAS,oBAAoB;AAC3C,aAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM,OAAO;AAAA,QACb,MAAM,UAAU,UAAU,SAAS,CAAC;AAAA,QACpC,OAAO,YAAY;AAAA,MACrB;AAAA,IACF,WAAW,YAAY,SAAS,YAAY;AAC1C,aAAO,EAAE,MAAM,YAAY,OAAO,YAAY,MAAM;AAAA,IACtD,WAAW,YAAY,SAAS,UAAU;AACxC,UAAI,UAAe,YAAY;AAC/B,UAAI,QAAQ;AAEZ,SAAG;AACD,YAAI,OAAO,YAAY,YAAY,OAAO,YAAY,UAAU;AAC9D,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,QACF,WACE,CAAC,WACA,OAAO,YAAY,YAAY,CAAC,MAAM,QAAQ,OAAO,GACtD;AACA,gBAAM,IAAI;AAAA,YACR,iCAAiC,UAAU;AAAA,OACzC,UAAU,KAAK,CACjB,kBAAkB,OAAO,OAAO;AAAA,UAClC;AAAA,QACF;AACA;AAEA,YAAI,UAAU,UAAU,UAAU,WAAW,SAAS;AACpD,iBAAO,EAAE,MAAM,SAAS,OAAO,QAAQ,OAAO,EAAE;AAAA,QAClD,WAAW,UAAU,UAAU,UAAU,WAAW,SAAS;AAC3D,iBAAO,EAAE,MAAM,YAAY,OAAO,QAAQ,OAAO,EAAE;AAAA,QACrD,OAAO;AACL,oBAAU,QAAQ,UAAU,KAAK,CAAC;AAAA,QACpC;AAAA,MACF,SAAS;AACT,UAAI,UAAU,KAAK,MAAM,QAAW;AAClC,cAAM,IAAI;AAAA,UACR,sDAAsD,UACnD,MAAM,GAAG,KAAK,EACd,KAAK,GAAG,CAAC;AAAA,QACd;AAAA,MACF;AACA,YAAM,IAAI;AAAA,QACR,sDACE,UAAU,KAAK,CACjB,aAAa,UAAU,MAAM,GAAG,KAAK,EAAE,KAAK,GAAG,CAAC;AAAA,MAClD;AAAA,IACF,WAAW,YAAY,SAAS,SAAS;AACvC,aAAO,EAAE,MAAM,SAAS,OAAO,YAAY,MAAM;AAAA,IACnD;AACA,UAAM,IAAI;AAAA,MACR,0DACE,YAAY,IACd,oBAAoB,UAAU,KAAK,GAAG,CAAC;AAAA,IACzC;AAAA,EACF,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,+BAA+B,OAAO,QAAQ,KAC3C,MAAgB,OACnB;AAAA,oBAAuB,KAAK,UAAU,OAAO,SAAS,MAAM,CAAC,CAAC;AAAA,IAChE;AAAA,EACF;AACF;;;ADppBA,eAAO,iBAIL,OACA,WACwB;AACxB,QAAM,WAAW,KAAK,MAAM;AAE5B,SAAO,KAAK,WAAW,KAAK,cAAc,CAAC,KAAK,WAAW;AACzD,QAAI,KAAK;AACP,aAAO,SAAS,GAAG;AAAA,IACrB;AACA,QAAI,CAAC,QAAQ;AACX,aAAO;AAAA,QACL,IAAI,MAAM,mBAAmB,KAAK,YAAY,WAAW;AAAA,MAC3D;AAAA,IACF;AACA,UAAM,EAAE,YAAY,IAAI,KAAK,WAAW;AACxC,UAAM,WAAW,kBAAkB,MAAM,aAAa,KAAK;AAE3D,aAAS,MAAM,MAAM;AACrB,UAAM,MAAM,WAAW,MAAM;AAC7B,aAAS,OAAO,GAAG;AAEnB,WAAO,yBAAyB,MAAM,KAAK,SAAS,GAAG,EAAE,KAAK,CAAC,WAAW;AACxE,eAAS,gBAAgB,GAAG;AAC5B,aAAO,SAAS,MAAM,QAAQ,SAAS;AAAA,IACzC,GAAG,QAAQ;AAAA,EACb,CAAC;AACH;AAEA,SAAS,WAAW,MAAgD;AAClE,MAAI;AAEJ,MAAI,OAAO,SAAS,UAAU;AAC5B,iBAAa;AAAA,EACf,WAAW,gBAAgB,QAAQ;AACjC,iBAAa,KAAK,SAAS,OAAO;AAAA,EACpC,WAAW,gBAAgB,aAAa;AACtC,iBAAa,IAAI,YAAY,OAAO,EAAE,OAAO,IAAI;AAAA,EACnD,OAAO;AACL,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY,WAAW,MAAM,wBAAwB;AAC3D,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAM,eAAe,UAAU,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC;AAC/C,cAAU;AAAA,EACZ;AACA,MAAI,QAAQ;AACV,aAAS,qCAAqC;AAAA,EAChD;AAEA,SAAO;AACT;AAEA,SAAS,kBACP,eACA,cACA;AACA,MACE,CAAC,gBACA,iBAAiB,QAChB,aAAa,UACb,CAAC,aAAa,OAAO,cAAc,YAAY,GACjD;AACA,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AACA,QAAM,YAAY,iBAAiB,OAAO,OAAO,aAAa;AAC9D,SAAO,CACL,aACA,YACG;AACH,QAAI,gBAAgB,aAAa,cAAc,OAAO;AACpD,cAAQ;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc,YACV;AAAA,UACE,cAAc,UAAU;AAAA,UACxB,cAAc;AAAA,QAChB,IACA,cAAc;AAAA,QAClB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/dist/withYak/index.cjs
CHANGED
|
@@ -33,21 +33,18 @@ __export(index_exports, {
|
|
|
33
33
|
withYak: () => withYak
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
var import_node_fs = require("fs");
|
|
36
37
|
var import_node_path = __toESM(require("path"), 1);
|
|
37
38
|
var import_node_url = require("url");
|
|
38
|
-
var import_node_fs = require("fs");
|
|
39
|
-
var import_node_path2 = require("path");
|
|
40
|
-
var import_module = require("module");
|
|
41
39
|
var import_meta = {};
|
|
42
|
-
var currentDir = typeof __dirname !== "undefined" ? __dirname : (0,
|
|
43
|
-
var { resolve } = (0, import_module.createRequire)(currentDir + "/index.js");
|
|
40
|
+
var currentDir = typeof __dirname !== "undefined" ? __dirname : (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
44
41
|
var addYak = (yakOptions, nextConfig) => {
|
|
45
42
|
const previousConfig = nextConfig.webpack;
|
|
46
43
|
const minify = yakOptions.minify !== void 0 ? yakOptions.minify : process.env.NODE_ENV === "production";
|
|
47
44
|
nextConfig.experimental ||= {};
|
|
48
45
|
nextConfig.experimental.swcPlugins ||= [];
|
|
49
46
|
nextConfig.experimental.swcPlugins.push([
|
|
50
|
-
|
|
47
|
+
"yak-swc",
|
|
51
48
|
{
|
|
52
49
|
minify,
|
|
53
50
|
basePath: currentDir,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../withYak/index.ts"],"sourcesContent":["/// <reference types=\"node\" />\nimport {
|
|
1
|
+
{"version":3,"sources":["../../withYak/index.ts"],"sourcesContent":["/// <reference types=\"node\" />\nimport { existsSync } from \"node:fs\";\nimport path, { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { NextConfig } from \"../../example/node_modules/next/dist/server/config.js\";\n\nconst currentDir =\n typeof __dirname !== \"undefined\"\n ? __dirname\n : dirname(fileURLToPath(import.meta.url));\n\nexport type YakConfigOptions = {\n /**\n * Generate compact CSS class and variable names.\n * @defaultValue\n * enabled if NODE_ENV is set to `production`, otherwise disabled\n */\n minify?: boolean;\n contextPath?: string;\n /**\n * Optional prefix for generated CSS identifiers.\n * This can be used to ensure unique class names across different applications\n * or to add organization-specific prefixes.\n */\n prefix?: string;\n /**\n * Adds `displayName` to each component for better React DevTools debugging\n * - Enabled by default in development mode\n * - Disabled by default in production\n * - Increases bundle size slightly when enabled\n */\n displayNames?: boolean;\n experiments?: {\n debug?:\n | boolean\n | {\n filter?: (path: string) => boolean;\n type: \"all\" | \"ts\" | \"css\" | \"css resolved\";\n };\n };\n};\n\nconst addYak = (yakOptions: YakConfigOptions, nextConfig: NextConfig) => {\n const previousConfig = nextConfig.webpack;\n const minify =\n yakOptions.minify !== undefined\n ? yakOptions.minify\n : process.env.NODE_ENV === \"production\";\n\n nextConfig.experimental ||= {};\n nextConfig.experimental.swcPlugins ||= [];\n nextConfig.experimental.swcPlugins.push([\n \"yak-swc\",\n {\n minify,\n basePath: currentDir,\n prefix: yakOptions.prefix,\n displayNames: yakOptions.displayNames ?? !minify,\n },\n ]);\n\n nextConfig.webpack = (webpackConfig, options) => {\n if (previousConfig) {\n webpackConfig = previousConfig(webpackConfig, options);\n }\n\n webpackConfig.module.rules.push({\n test: /\\.yak\\.module\\.css$/,\n loader: path.join(currentDir, \"../loaders/css-loader.js\"),\n options: yakOptions,\n });\n\n // With the following alias the internal next-yak code\n // is able to import a context which works for server components\n const yakContext = resolveYakContext(\n yakOptions.contextPath,\n webpackConfig.context || process.cwd(),\n );\n if (yakContext) {\n webpackConfig.resolve.alias[\"next-yak/context/baseContext\"] = yakContext;\n }\n\n return webpackConfig;\n };\n return nextConfig;\n};\n\n/**\n * Try to resolve yak\n */\nfunction resolveYakContext(contextPath: string | undefined, cwd: string) {\n const yakContext = contextPath\n ? path.resolve(cwd, contextPath)\n : path.resolve(cwd, \"yak.context\");\n const extensions = [\"\", \".ts\", \".tsx\", \".js\", \".jsx\"];\n for (const extension in extensions) {\n const fileName = yakContext + extensions[extension];\n if (existsSync(fileName)) {\n return fileName;\n }\n }\n if (contextPath) {\n throw new Error(`Could not find yak context file at ${yakContext}`);\n }\n}\n\n// Wrapper to allow sync, async, and function configuration of Next.js\n/**\n * Add Yak to your Next.js app\n *\n * @usage\n *\n * ```ts\n * // next.config.js\n * const { withYak } = require(\"next-yak/withYak\");\n * const nextConfig = {\n * // your next config here\n * };\n * module.exports = withYak(nextConfig);\n * ```\n *\n * With a custom yakConfig\n *\n * ```ts\n * // next.config.js\n * const { withYak } = require(\"next-yak/withYak\");\n * const nextConfig = {\n * // your next config here\n * };\n * const yakConfig = {\n * // Optional prefix for generated CSS identifiers\n * prefix: \"my-app\",\n * // Other yak config options...\n * };\n * module.exports = withYak(yakConfig, nextConfig);\n * ```\n */\nexport const withYak: {\n <\n T extends\n | Record<string, any>\n | ((...args: any[]) => Record<string, any>)\n | ((...args: any[]) => Promise<Record<string, any>>),\n >(\n yakOptions: YakConfigOptions,\n nextConfig: T,\n ): T;\n // no yakConfig\n <\n T extends\n | Record<string, any>\n | ((...args: any[]) => Record<string, any>)\n | ((...args: any[]) => Promise<Record<string, any>>),\n >(\n nextConfig: T,\n _?: undefined,\n ): T;\n} = (maybeYakOptions, nextConfig) => {\n if (nextConfig === undefined) {\n return withYak({}, maybeYakOptions);\n }\n // If the second parameter is present the first parameter must be a YakConfigOptions\n const yakOptions = maybeYakOptions as YakConfigOptions;\n if (typeof nextConfig === \"function\") {\n /**\n * A NextConfig can be a sync or async function\n * https://nextjs.org/docs/pages/api-reference/next-config-js\n * @param {any[]} args\n */\n return (...args) => {\n /** Dynamic Next Configs can be async or sync */\n const config = nextConfig(...args) as NextConfig | Promise<NextConfig>;\n return config instanceof Promise\n ? config.then((config) => addYak(yakOptions, config))\n : addYak(yakOptions, config);\n };\n }\n return addYak(yakOptions, nextConfig);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,qBAA2B;AAC3B,uBAA8B;AAC9B,sBAA8B;AAH9B;AAMA,IAAM,aACJ,OAAO,cAAc,cACjB,gBACA,8BAAQ,+BAAc,YAAY,GAAG,CAAC;AAiC5C,IAAM,SAAS,CAAC,YAA8B,eAA2B;AACvE,QAAM,iBAAiB,WAAW;AAClC,QAAM,SACJ,WAAW,WAAW,SAClB,WAAW,SACX,QAAQ,IAAI,aAAa;AAE/B,aAAW,iBAAiB,CAAC;AAC7B,aAAW,aAAa,eAAe,CAAC;AACxC,aAAW,aAAa,WAAW,KAAK;AAAA,IACtC;AAAA,IACA;AAAA,MACE;AAAA,MACA,UAAU;AAAA,MACV,QAAQ,WAAW;AAAA,MACnB,cAAc,WAAW,gBAAgB,CAAC;AAAA,IAC5C;AAAA,EACF,CAAC;AAED,aAAW,UAAU,CAAC,eAAe,YAAY;AAC/C,QAAI,gBAAgB;AAClB,sBAAgB,eAAe,eAAe,OAAO;AAAA,IACvD;AAEA,kBAAc,OAAO,MAAM,KAAK;AAAA,MAC9B,MAAM;AAAA,MACN,QAAQ,iBAAAA,QAAK,KAAK,YAAY,0BAA0B;AAAA,MACxD,SAAS;AAAA,IACX,CAAC;AAID,UAAM,aAAa;AAAA,MACjB,WAAW;AAAA,MACX,cAAc,WAAW,QAAQ,IAAI;AAAA,IACvC;AACA,QAAI,YAAY;AACd,oBAAc,QAAQ,MAAM,8BAA8B,IAAI;AAAA,IAChE;AAEA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKA,SAAS,kBAAkB,aAAiC,KAAa;AACvE,QAAM,aAAa,cACf,iBAAAA,QAAK,QAAQ,KAAK,WAAW,IAC7B,iBAAAA,QAAK,QAAQ,KAAK,aAAa;AACnC,QAAM,aAAa,CAAC,IAAI,OAAO,QAAQ,OAAO,MAAM;AACpD,aAAW,aAAa,YAAY;AAClC,UAAM,WAAW,aAAa,WAAW,SAAS;AAClD,YAAI,2BAAW,QAAQ,GAAG;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,aAAa;AACf,UAAM,IAAI,MAAM,sCAAsC,UAAU,EAAE;AAAA,EACpE;AACF;AAiCO,IAAM,UAoBT,CAAC,iBAAiB,eAAe;AACnC,MAAI,eAAe,QAAW;AAC5B,WAAO,QAAQ,CAAC,GAAG,eAAe;AAAA,EACpC;AAEA,QAAM,aAAa;AACnB,MAAI,OAAO,eAAe,YAAY;AAMpC,WAAO,IAAI,SAAS;AAElB,YAAM,SAAS,WAAW,GAAG,IAAI;AACjC,aAAO,kBAAkB,UACrB,OAAO,KAAK,CAACC,YAAW,OAAO,YAAYA,OAAM,CAAC,IAClD,OAAO,YAAY,MAAM;AAAA,IAC/B;AAAA,EACF;AACA,SAAO,OAAO,YAAY,UAAU;AACtC;","names":["path","config"]}
|
package/dist/withYak/index.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
// withYak/index.ts
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
2
|
import { existsSync } from "node:fs";
|
|
5
|
-
import { dirname } from "node:path";
|
|
6
|
-
import {
|
|
3
|
+
import path, { dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
7
5
|
var currentDir = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
var { resolve } = createRequire(currentDir + "/index.js");
|
|
9
6
|
var addYak = (yakOptions, nextConfig) => {
|
|
10
7
|
const previousConfig = nextConfig.webpack;
|
|
11
8
|
const minify = yakOptions.minify !== void 0 ? yakOptions.minify : process.env.NODE_ENV === "production";
|
|
12
9
|
nextConfig.experimental ||= {};
|
|
13
10
|
nextConfig.experimental.swcPlugins ||= [];
|
|
14
11
|
nextConfig.experimental.swcPlugins.push([
|
|
15
|
-
|
|
12
|
+
"yak-swc",
|
|
16
13
|
{
|
|
17
14
|
minify,
|
|
18
15
|
basePath: currentDir,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../withYak/index.ts"],"sourcesContent":["/// <reference types=\"node\" />\nimport {
|
|
1
|
+
{"version":3,"sources":["../../withYak/index.ts"],"sourcesContent":["/// <reference types=\"node\" />\nimport { existsSync } from \"node:fs\";\nimport path, { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { NextConfig } from \"../../example/node_modules/next/dist/server/config.js\";\n\nconst currentDir =\n typeof __dirname !== \"undefined\"\n ? __dirname\n : dirname(fileURLToPath(import.meta.url));\n\nexport type YakConfigOptions = {\n /**\n * Generate compact CSS class and variable names.\n * @defaultValue\n * enabled if NODE_ENV is set to `production`, otherwise disabled\n */\n minify?: boolean;\n contextPath?: string;\n /**\n * Optional prefix for generated CSS identifiers.\n * This can be used to ensure unique class names across different applications\n * or to add organization-specific prefixes.\n */\n prefix?: string;\n /**\n * Adds `displayName` to each component for better React DevTools debugging\n * - Enabled by default in development mode\n * - Disabled by default in production\n * - Increases bundle size slightly when enabled\n */\n displayNames?: boolean;\n experiments?: {\n debug?:\n | boolean\n | {\n filter?: (path: string) => boolean;\n type: \"all\" | \"ts\" | \"css\" | \"css resolved\";\n };\n };\n};\n\nconst addYak = (yakOptions: YakConfigOptions, nextConfig: NextConfig) => {\n const previousConfig = nextConfig.webpack;\n const minify =\n yakOptions.minify !== undefined\n ? yakOptions.minify\n : process.env.NODE_ENV === \"production\";\n\n nextConfig.experimental ||= {};\n nextConfig.experimental.swcPlugins ||= [];\n nextConfig.experimental.swcPlugins.push([\n \"yak-swc\",\n {\n minify,\n basePath: currentDir,\n prefix: yakOptions.prefix,\n displayNames: yakOptions.displayNames ?? !minify,\n },\n ]);\n\n nextConfig.webpack = (webpackConfig, options) => {\n if (previousConfig) {\n webpackConfig = previousConfig(webpackConfig, options);\n }\n\n webpackConfig.module.rules.push({\n test: /\\.yak\\.module\\.css$/,\n loader: path.join(currentDir, \"../loaders/css-loader.js\"),\n options: yakOptions,\n });\n\n // With the following alias the internal next-yak code\n // is able to import a context which works for server components\n const yakContext = resolveYakContext(\n yakOptions.contextPath,\n webpackConfig.context || process.cwd(),\n );\n if (yakContext) {\n webpackConfig.resolve.alias[\"next-yak/context/baseContext\"] = yakContext;\n }\n\n return webpackConfig;\n };\n return nextConfig;\n};\n\n/**\n * Try to resolve yak\n */\nfunction resolveYakContext(contextPath: string | undefined, cwd: string) {\n const yakContext = contextPath\n ? path.resolve(cwd, contextPath)\n : path.resolve(cwd, \"yak.context\");\n const extensions = [\"\", \".ts\", \".tsx\", \".js\", \".jsx\"];\n for (const extension in extensions) {\n const fileName = yakContext + extensions[extension];\n if (existsSync(fileName)) {\n return fileName;\n }\n }\n if (contextPath) {\n throw new Error(`Could not find yak context file at ${yakContext}`);\n }\n}\n\n// Wrapper to allow sync, async, and function configuration of Next.js\n/**\n * Add Yak to your Next.js app\n *\n * @usage\n *\n * ```ts\n * // next.config.js\n * const { withYak } = require(\"next-yak/withYak\");\n * const nextConfig = {\n * // your next config here\n * };\n * module.exports = withYak(nextConfig);\n * ```\n *\n * With a custom yakConfig\n *\n * ```ts\n * // next.config.js\n * const { withYak } = require(\"next-yak/withYak\");\n * const nextConfig = {\n * // your next config here\n * };\n * const yakConfig = {\n * // Optional prefix for generated CSS identifiers\n * prefix: \"my-app\",\n * // Other yak config options...\n * };\n * module.exports = withYak(yakConfig, nextConfig);\n * ```\n */\nexport const withYak: {\n <\n T extends\n | Record<string, any>\n | ((...args: any[]) => Record<string, any>)\n | ((...args: any[]) => Promise<Record<string, any>>),\n >(\n yakOptions: YakConfigOptions,\n nextConfig: T,\n ): T;\n // no yakConfig\n <\n T extends\n | Record<string, any>\n | ((...args: any[]) => Record<string, any>)\n | ((...args: any[]) => Promise<Record<string, any>>),\n >(\n nextConfig: T,\n _?: undefined,\n ): T;\n} = (maybeYakOptions, nextConfig) => {\n if (nextConfig === undefined) {\n return withYak({}, maybeYakOptions);\n }\n // If the second parameter is present the first parameter must be a YakConfigOptions\n const yakOptions = maybeYakOptions as YakConfigOptions;\n if (typeof nextConfig === \"function\") {\n /**\n * A NextConfig can be a sync or async function\n * https://nextjs.org/docs/pages/api-reference/next-config-js\n * @param {any[]} args\n */\n return (...args) => {\n /** Dynamic Next Configs can be async or sync */\n const config = nextConfig(...args) as NextConfig | Promise<NextConfig>;\n return config instanceof Promise\n ? config.then((config) => addYak(yakOptions, config))\n : addYak(yakOptions, config);\n };\n }\n return addYak(yakOptions, nextConfig);\n};\n"],"mappings":";AACA,SAAS,kBAAkB;AAC3B,OAAO,QAAQ,eAAe;AAC9B,SAAS,qBAAqB;AAG9B,IAAM,aACJ,OAAO,cAAc,cACjB,YACA,QAAQ,cAAc,YAAY,GAAG,CAAC;AAiC5C,IAAM,SAAS,CAAC,YAA8B,eAA2B;AACvE,QAAM,iBAAiB,WAAW;AAClC,QAAM,SACJ,WAAW,WAAW,SAClB,WAAW,SACX,QAAQ,IAAI,aAAa;AAE/B,aAAW,iBAAiB,CAAC;AAC7B,aAAW,aAAa,eAAe,CAAC;AACxC,aAAW,aAAa,WAAW,KAAK;AAAA,IACtC;AAAA,IACA;AAAA,MACE;AAAA,MACA,UAAU;AAAA,MACV,QAAQ,WAAW;AAAA,MACnB,cAAc,WAAW,gBAAgB,CAAC;AAAA,IAC5C;AAAA,EACF,CAAC;AAED,aAAW,UAAU,CAAC,eAAe,YAAY;AAC/C,QAAI,gBAAgB;AAClB,sBAAgB,eAAe,eAAe,OAAO;AAAA,IACvD;AAEA,kBAAc,OAAO,MAAM,KAAK;AAAA,MAC9B,MAAM;AAAA,MACN,QAAQ,KAAK,KAAK,YAAY,0BAA0B;AAAA,MACxD,SAAS;AAAA,IACX,CAAC;AAID,UAAM,aAAa;AAAA,MACjB,WAAW;AAAA,MACX,cAAc,WAAW,QAAQ,IAAI;AAAA,IACvC;AACA,QAAI,YAAY;AACd,oBAAc,QAAQ,MAAM,8BAA8B,IAAI;AAAA,IAChE;AAEA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKA,SAAS,kBAAkB,aAAiC,KAAa;AACvE,QAAM,aAAa,cACf,KAAK,QAAQ,KAAK,WAAW,IAC7B,KAAK,QAAQ,KAAK,aAAa;AACnC,QAAM,aAAa,CAAC,IAAI,OAAO,QAAQ,OAAO,MAAM;AACpD,aAAW,aAAa,YAAY;AAClC,UAAM,WAAW,aAAa,WAAW,SAAS;AAClD,QAAI,WAAW,QAAQ,GAAG;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,aAAa;AACf,UAAM,IAAI,MAAM,sCAAsC,UAAU,EAAE;AAAA,EACpE;AACF;AAiCO,IAAM,UAoBT,CAAC,iBAAiB,eAAe;AACnC,MAAI,eAAe,QAAW;AAC5B,WAAO,QAAQ,CAAC,GAAG,eAAe;AAAA,EACpC;AAEA,QAAM,aAAa;AACnB,MAAI,OAAO,eAAe,YAAY;AAMpC,WAAO,IAAI,SAAS;AAElB,YAAM,SAAS,WAAW,GAAG,IAAI;AACjC,aAAO,kBAAkB,UACrB,OAAO,KAAK,CAACA,YAAW,OAAO,YAAYA,OAAM,CAAC,IAClD,OAAO,YAAY,MAAM;AAAA,IAC/B;AAAA,EACF;AACA,SAAO,OAAO,YAAY,UAAU;AACtC;","names":["config"]}
|
|
@@ -2,7 +2,6 @@ import babel from "@babel/core";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
// @ts-expect-error - this is used by babel directly so we ignore that it is not typed
|
|
4
4
|
import babelPlugin from "@babel/plugin-syntax-typescript";
|
|
5
|
-
import { getCssModuleLocalIdent } from "next/dist/build/webpack/config/blocks/css/loaders/getCssModuleLocalIdent.js";
|
|
6
5
|
import type { Compilation, LoaderContext } from "webpack";
|
|
7
6
|
|
|
8
7
|
const yakCssImportRegex =
|
|
@@ -100,9 +99,14 @@ export async function resolveCrossFileConstant(
|
|
|
100
99
|
const resolved = resolvedValues[i];
|
|
101
100
|
|
|
102
101
|
if (importKind === "selector") {
|
|
103
|
-
if (
|
|
102
|
+
if (
|
|
103
|
+
resolved.type !== "styled-component" &&
|
|
104
|
+
resolved.type !== "constant"
|
|
105
|
+
) {
|
|
104
106
|
throw new Error(
|
|
105
|
-
`Found
|
|
107
|
+
`Found ${
|
|
108
|
+
resolved.type
|
|
109
|
+
} but expected a selector - did you forget a semicolon after \`${specifier.join(
|
|
106
110
|
".",
|
|
107
111
|
)}\`?`,
|
|
108
112
|
);
|
|
@@ -111,15 +115,7 @@ export async function resolveCrossFileConstant(
|
|
|
111
115
|
|
|
112
116
|
const replacement =
|
|
113
117
|
resolved.type === "styled-component"
|
|
114
|
-
?
|
|
115
|
-
{
|
|
116
|
-
rootContext: loader.rootContext,
|
|
117
|
-
resourcePath: resolved.from,
|
|
118
|
-
},
|
|
119
|
-
null,
|
|
120
|
-
resolved.name,
|
|
121
|
-
{},
|
|
122
|
-
)})`
|
|
118
|
+
? resolved.value
|
|
123
119
|
: resolved.value +
|
|
124
120
|
// resolved.value can be of two different types:
|
|
125
121
|
// - mixin:
|
|
@@ -259,6 +255,7 @@ async function parseFile(
|
|
|
259
255
|
|
|
260
256
|
const exports = await parseExports(await sourceContents, isTSX);
|
|
261
257
|
const mixins = parseMixins(await tranformedSource);
|
|
258
|
+
Object.assign(exports, parseStyledComponents(await tranformedSource));
|
|
262
259
|
|
|
263
260
|
// Recursively resolve cross-file constants in mixins
|
|
264
261
|
// e.g. cross file mixins inside a cross file mixin
|
|
@@ -350,9 +347,12 @@ async function parseExports(
|
|
|
350
347
|
declaration.id.type === "Identifier" &&
|
|
351
348
|
declaration.init
|
|
352
349
|
) {
|
|
353
|
-
|
|
350
|
+
const parsed = parseExportValueExpression(
|
|
354
351
|
declaration.init,
|
|
355
352
|
);
|
|
353
|
+
if (parsed) {
|
|
354
|
+
exports[declaration.id.name] = parsed;
|
|
355
|
+
}
|
|
356
356
|
}
|
|
357
357
|
});
|
|
358
358
|
}
|
|
@@ -425,6 +425,29 @@ function parseMixins(
|
|
|
425
425
|
return mixins;
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
+
function parseStyledComponents(
|
|
429
|
+
sourceContents: string,
|
|
430
|
+
): Record<string, { type: "styled-component"; value: string }> {
|
|
431
|
+
// cross-file Styled Components are always in the following format:
|
|
432
|
+
// /*YAK EXPORTED STYLED:ComponentName:ClassName*/
|
|
433
|
+
const styledParts = sourceContents.split("/*YAK EXPORTED STYLED:");
|
|
434
|
+
let styledComponents: Record<
|
|
435
|
+
string,
|
|
436
|
+
{ type: "styled-component"; value: string }
|
|
437
|
+
> = {};
|
|
438
|
+
|
|
439
|
+
for (let i = 1; i < styledParts.length; i++) {
|
|
440
|
+
const [comment] = styledParts[i].split("*/", 1);
|
|
441
|
+
const [componentName, className] = comment.split(":");
|
|
442
|
+
styledComponents[componentName] = {
|
|
443
|
+
type: "styled-component",
|
|
444
|
+
value: `:global(.${className})`,
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return styledComponents;
|
|
449
|
+
}
|
|
450
|
+
|
|
428
451
|
/**
|
|
429
452
|
* Unpacks a TSAsExpression to its expression value
|
|
430
453
|
*/
|
|
@@ -446,7 +469,8 @@ function parseExportValueExpression(
|
|
|
446
469
|
expression.type === "CallExpression" ||
|
|
447
470
|
expression.type === "TaggedTemplateExpression"
|
|
448
471
|
) {
|
|
449
|
-
|
|
472
|
+
// The value will be set by parseStyledComponents
|
|
473
|
+
return { type: "styled-component", value: undefined };
|
|
450
474
|
} else if (
|
|
451
475
|
expression.type === "StringLiteral" ||
|
|
452
476
|
expression.type === "NumericLiteral"
|
|
@@ -479,9 +503,12 @@ function parseObjectExpression(
|
|
|
479
503
|
property.key.type === "Identifier"
|
|
480
504
|
) {
|
|
481
505
|
const key = property.key.name;
|
|
482
|
-
|
|
506
|
+
const parsed = parseExportValueExpression(
|
|
483
507
|
property.value as babel.types.Expression,
|
|
484
508
|
);
|
|
509
|
+
if (parsed) {
|
|
510
|
+
result[key] = parsed;
|
|
511
|
+
}
|
|
485
512
|
}
|
|
486
513
|
}
|
|
487
514
|
return result;
|
|
@@ -581,6 +608,7 @@ async function resolveModuleSpecifierRecursively(
|
|
|
581
608
|
type: "styled-component",
|
|
582
609
|
from: module.filePath,
|
|
583
610
|
name: specifier[specifier.length - 1],
|
|
611
|
+
value: exportValue.value,
|
|
584
612
|
};
|
|
585
613
|
} else if (exportValue.type === "constant") {
|
|
586
614
|
return { type: "constant", value: exportValue.value };
|
|
@@ -648,7 +676,7 @@ type ParsedFile =
|
|
|
648
676
|
| { type: "yak"; exports: Record<string, ParsedExport>; filePath: string };
|
|
649
677
|
|
|
650
678
|
type ParsedExport =
|
|
651
|
-
| { type: "styled-component" }
|
|
679
|
+
| { type: "styled-component"; value: string | undefined }
|
|
652
680
|
| { type: "mixin"; value: string }
|
|
653
681
|
| { type: "constant"; value: string | number }
|
|
654
682
|
| { type: "record"; value: Record<any, ParsedExport> | {} }
|
|
@@ -657,6 +685,11 @@ type ParsedExport =
|
|
|
657
685
|
| { type: "star-export"; from: string[] };
|
|
658
686
|
|
|
659
687
|
type ResolvedExport =
|
|
660
|
-
| {
|
|
688
|
+
| {
|
|
689
|
+
type: "styled-component";
|
|
690
|
+
from: string;
|
|
691
|
+
name: string;
|
|
692
|
+
value: string | undefined;
|
|
693
|
+
}
|
|
661
694
|
| { type: "mixin"; value: string | number }
|
|
662
695
|
| { type: "constant"; value: string | number };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-yak",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@babel/core": "7.26.0",
|
|
67
67
|
"@babel/plugin-syntax-typescript": "7.25.9",
|
|
68
|
-
"yak-swc": "5.
|
|
68
|
+
"yak-swc": "5.3.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/babel__core": "7.20.5",
|
|
@@ -359,17 +359,18 @@ it("should remap props", () => {
|
|
|
359
359
|
const Comp = styled.button.attrs<{ primary?: boolean; $submit?: boolean }>(
|
|
360
360
|
(p) => ({
|
|
361
361
|
type: p.$submit ? "submit" : "button",
|
|
362
|
-
$
|
|
362
|
+
$disabled: p.disabled,
|
|
363
363
|
}),
|
|
364
|
-
)<{ $
|
|
364
|
+
)<{ $disabled?: boolean }>``;
|
|
365
365
|
|
|
366
366
|
expect(getSnapshot(<Comp />)).toMatchInlineSnapshot(`
|
|
367
367
|
<button
|
|
368
368
|
type="button"
|
|
369
369
|
/>
|
|
370
370
|
`);
|
|
371
|
-
expect(getSnapshot(<Comp
|
|
371
|
+
expect(getSnapshot(<Comp disabled />)).toMatchInlineSnapshot(`
|
|
372
372
|
<button
|
|
373
|
+
disabled=""
|
|
373
374
|
type="button"
|
|
374
375
|
/>
|
|
375
376
|
`);
|
package/withYak/index.ts
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { NextConfig } from "../../example/node_modules/next/dist/server/config.js";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
2
|
import { existsSync } from "node:fs";
|
|
6
|
-
import { dirname } from "node:path";
|
|
7
|
-
import {
|
|
3
|
+
import path, { dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { NextConfig } from "../../example/node_modules/next/dist/server/config.js";
|
|
8
6
|
|
|
9
7
|
const currentDir =
|
|
10
8
|
typeof __dirname !== "undefined"
|
|
11
9
|
? __dirname
|
|
12
10
|
: dirname(fileURLToPath(import.meta.url));
|
|
13
11
|
|
|
14
|
-
const { resolve } = createRequire(currentDir + "/index.js");
|
|
15
|
-
|
|
16
12
|
export type YakConfigOptions = {
|
|
17
13
|
/**
|
|
18
14
|
* Generate compact CSS class and variable names.
|
|
@@ -54,7 +50,7 @@ const addYak = (yakOptions: YakConfigOptions, nextConfig: NextConfig) => {
|
|
|
54
50
|
nextConfig.experimental ||= {};
|
|
55
51
|
nextConfig.experimental.swcPlugins ||= [];
|
|
56
52
|
nextConfig.experimental.swcPlugins.push([
|
|
57
|
-
|
|
53
|
+
"yak-swc",
|
|
58
54
|
{
|
|
59
55
|
minify,
|
|
60
56
|
basePath: currentDir,
|