UNPKG

next-yak

Version:

next-yak is a CSS-in-JS solution tailored for Next.js that seamlessly combines the expressive power of styled-components syntax with efficient build-time extraction of CSS using Next.js's built-in CSS configuration

1 lines 13.2 kB
{"version":3,"file":"index.cjs","names":["path"],"sources":["../../withYak/index.ts"],"sourcesContent":["/// <reference types=\"node\" />\nimport type { NextConfig } from \"next\";\nimport { existsSync } from \"node:fs\";\nimport path, { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst currentDir =\n typeof __dirname !== \"undefined\" ? __dirname : 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 /**\n * Debug logging for transformed files.\n * - `true` - log all files\n * - `object` - filter by pattern and/or output types (at least one required)\n */\n debug?:\n | true\n | { pattern: string; types?: Array<\"ts\" | \"css\" | \"css-resolved\"> }\n | { pattern?: string; types: Array<\"ts\" | \"css\" | \"css-resolved\"> };\n transpilationMode?: \"CssModule\" | \"Css\";\n /**\n * Suppress deprecation warnings for :global() selectors during migration period\n * @defaultValue false\n */\n suppressDeprecationWarnings?: boolean;\n };\n};\n\nconst addYak = (yakOptions: YakConfigOptions, nextConfig: NextConfig) => {\n const minify = yakOptions.minify ?? process.env.NODE_ENV === \"production\";\n const yakPluginOptions = {\n minify,\n basePath: currentDir,\n prefix: yakOptions.prefix,\n displayNames: yakOptions.displayNames ?? !minify,\n suppressDeprecationWarnings: yakOptions.experiments?.suppressDeprecationWarnings ?? false,\n reactRefreshReg: true,\n };\n\n const transpilation = yakOptions.experiments?.transpilationMode ?? \"CssModule\";\n const cssExtension = transpilation === \"CssModule\" ? \".yak.module.css\" : \".yak.css\";\n\n if (process.env.TURBOPACK === \"1\" || process.env.TURBOPACK === \"auto\") {\n addYakTurbopack(nextConfig, yakOptions, {\n ...yakPluginOptions,\n importMode: {\n value: \"data:text/css;base64,\",\n transpilation: \"Css\",\n encoding: \"Base64\",\n },\n });\n } else {\n addYakWebpack(nextConfig, yakOptions, {\n ...yakPluginOptions,\n importMode: {\n value: `./{{__BASE_NAME__}}${cssExtension}!=!./{{__BASE_NAME__}}?./{{__BASE_NAME__}}${cssExtension}`,\n transpilation,\n encoding: \"None\",\n },\n });\n }\n return nextConfig;\n};\n\n/**\n * Configure Turbopack with yak loader for CSS-in-JS transformation\n * @param nextConfig - Next.js configuration object\n * @param yakOptions - Yak configuration options\n * @param yakPluginOptions - Processed plugin options for yak-swc\n */\nfunction addYakTurbopack(\n nextConfig: NextConfig,\n yakOptions: YakConfigOptions,\n yakPluginOptions: {\n minify: boolean;\n basePath: string;\n prefix?: string;\n displayNames: boolean;\n importMode: {\n value: string;\n transpilation: string;\n encoding: string;\n };\n },\n) {\n // turbopack can't handle options with undefined values, so we remove them\n const yakLoader = removeUndefinedRecursive({\n loader: path.join(currentDir, \"../loaders/turbo-loader.cjs\"),\n options: {\n yakOptions: yakOptions,\n yakPluginOptions: yakPluginOptions,\n },\n }) as { loader: string; options: {} };\n\n nextConfig.turbopack ||= {};\n nextConfig.turbopack.rules ||= {};\n\n const ruleKey = \"*.{js,jsx,cjs,mjs,ts,tsx,cts,mts}\";\n const rule = {\n loaders: [] as { loader: string; options: {} }[],\n ...nextConfig.turbopack.rules[ruleKey],\n };\n rule.loaders.push(yakLoader);\n nextConfig.turbopack.rules[ruleKey] = rule;\n\n // Configure resolveAlias for custom yak context (similar to webpack)\n // This allows users to provide a custom context file that will be used\n // instead of the default baseContext\n const yakContext = resolveYakContext(yakOptions.contextPath, process.cwd());\n if (yakContext) {\n nextConfig.turbopack.resolveAlias ||= {};\n nextConfig.turbopack.resolveAlias[\"next-yak/context/baseContext\"] =\n // This is a hack around the fact that turbopack currently only supports relative paths\n // turbopack: \"server relative imports are not implemented yet\"\n // Relative is quite dangerous here as it relies on the cwd being the starting point\n `./${path.relative(process.cwd(), yakContext)}`;\n }\n}\n\n/**\n * Configure Webpack with yak SWC plugin and webpack loader for CSS-in-JS transformation\n * @param nextConfig - Next.js configuration object\n * @param yakOptions - Yak configuration options\n * @param yakPluginOptions - Processed plugin options for yak-swc\n */\nfunction addYakWebpack(\n nextConfig: NextConfig,\n yakOptions: YakConfigOptions,\n yakPluginOptions: {\n minify: boolean;\n basePath: string;\n prefix?: string;\n displayNames: boolean;\n importMode: {\n value: string;\n transpilation: string;\n encoding: string;\n };\n },\n) {\n // Add SWC plugin for Webpack\n nextConfig.experimental ||= {};\n nextConfig.experimental.swcPlugins ||= [];\n nextConfig.experimental.swcPlugins.push([\"yak-swc\", yakPluginOptions]);\n\n // Configure webpack loader\n const previousConfig = nextConfig.webpack;\n nextConfig.webpack = (webpackConfig, options) => {\n if (previousConfig) {\n webpackConfig = previousConfig(webpackConfig, options);\n }\n\n webpackConfig.module.rules.push({\n test:\n yakOptions.experiments?.transpilationMode === \"Css\" ? /\\.yak\\.css$/ : /\\.yak\\.module\\.css$/,\n loader: path.join(currentDir, \"../loaders/webpack-loader.cjs\"),\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}\n\n/**\n * Recursively removes undefined values from an object or array.\n *\n * This function deeply traverses the input object/array and creates a new structure\n * with all undefined values filtered out. For objects, properties with undefined values\n * are omitted. For arrays, undefined elements are removed from the result.\n *\n * @param obj - The object or array to process\n * @returns A new object/array with undefined values removed, or the original value if no changes were needed\n */\nfunction removeUndefinedRecursive<T>(obj: T): {} {\n if (typeof obj !== \"object\" || obj === null) {\n return obj as {};\n }\n\n if (Array.isArray(obj)) {\n const filtered: unknown[] = [];\n for (let i = 0; i < obj.length; i++) {\n const processed = removeUndefinedRecursive(obj[i]);\n if (processed !== undefined) {\n filtered.push(processed);\n }\n }\n return filtered as {};\n }\n\n const newObj: Record<string, unknown> = {};\n let hasChanges = false;\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const value = removeUndefinedRecursive((obj as any)[key]);\n if (value !== undefined) {\n newObj[key] = value;\n hasChanges = true;\n }\n }\n }\n\n return hasChanges ? (newObj as {}) : obj;\n}\n\n/**\n * Try to resolve yak\n */\nexport function 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":"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"}