@yahoo/uds 3.123.0 → 3.124.0-beta.1

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.
Files changed (51) hide show
  1. package/dist/components/client/Menu/Menu.ItemCheckbox.d.cts +1 -1
  2. package/dist/components/client/Menu/Menu.ItemCheckbox.d.ts +1 -1
  3. package/dist/components/client/Toast/UDSToastConfigProvider.d.cts +5 -5
  4. package/dist/components/client/Toast/UDSToastConfigProvider.d.ts +5 -5
  5. package/dist/styles/styler.d.cts +34 -34
  6. package/dist/styles/styler.d.ts +34 -34
  7. package/dist/tailwind/dist/commands/css.cjs +17 -3
  8. package/dist/tailwind/dist/commands/css.d.cts.map +1 -1
  9. package/dist/tailwind/dist/commands/css.d.ts.map +1 -1
  10. package/dist/tailwind/dist/commands/css.helpers.cjs +2 -1
  11. package/dist/tailwind/dist/commands/css.helpers.js +2 -1
  12. package/dist/tailwind/dist/commands/css.helpers.js.map +1 -1
  13. package/dist/tailwind/dist/commands/css.js +17 -3
  14. package/dist/tailwind/dist/commands/css.js.map +1 -1
  15. package/dist/tailwind/dist/css/generate.cjs +3 -0
  16. package/dist/tailwind/dist/css/generate.helpers.cjs +14 -6
  17. package/dist/tailwind/dist/css/generate.helpers.js +15 -7
  18. package/dist/tailwind/dist/css/generate.helpers.js.map +1 -1
  19. package/dist/tailwind/dist/css/generate.js +3 -0
  20. package/dist/tailwind/dist/css/generate.js.map +1 -1
  21. package/dist/tailwind/dist/css/nodeUtils.cjs +91 -20
  22. package/dist/tailwind/dist/css/nodeUtils.js +90 -21
  23. package/dist/tailwind/dist/css/nodeUtils.js.map +1 -1
  24. package/dist/tailwind/dist/css/postcss.cjs +22 -1
  25. package/dist/tailwind/dist/css/postcss.helpers.cjs +12 -1
  26. package/dist/tailwind/dist/css/postcss.helpers.js +11 -1
  27. package/dist/tailwind/dist/css/postcss.helpers.js.map +1 -1
  28. package/dist/tailwind/dist/css/postcss.js +22 -2
  29. package/dist/tailwind/dist/css/postcss.js.map +1 -1
  30. package/dist/tailwind/dist/css/runner.cjs +171 -20
  31. package/dist/tailwind/dist/css/runner.helpers.cjs +58 -6
  32. package/dist/tailwind/dist/css/runner.helpers.js +54 -7
  33. package/dist/tailwind/dist/css/runner.helpers.js.map +1 -1
  34. package/dist/tailwind/dist/css/runner.js +172 -20
  35. package/dist/tailwind/dist/css/runner.js.map +1 -1
  36. package/dist/tailwind/dist/css/theme.d.cts +11 -0
  37. package/dist/tailwind/dist/css/theme.d.cts.map +1 -1
  38. package/dist/tailwind/dist/css/theme.d.ts +11 -0
  39. package/dist/tailwind/dist/css/theme.d.ts.map +1 -1
  40. package/dist/tailwind/dist/css/theme.js.map +1 -1
  41. package/dist/tailwind/dist/purger/optimized/ast/expressions.cjs +26 -3
  42. package/dist/tailwind/dist/purger/optimized/ast/expressions.js +26 -3
  43. package/dist/tailwind/dist/purger/optimized/ast/expressions.js.map +1 -1
  44. package/dist/tailwind/dist/tailwind/utils/getFontStyles.d.cts +1 -1
  45. package/dist/tailwind/dist/tailwind/utils/getFontStyles.d.ts +1 -1
  46. package/dist/tailwind/dist/tailwind/utils/getShadowStyles.d.cts +4 -4
  47. package/dist/tailwind/dist/tailwind/utils/getShadowStyles.d.ts +4 -4
  48. package/dist/uds/generated/componentData.cjs +1152 -1152
  49. package/dist/uds/generated/componentData.js +1152 -1152
  50. package/generated/componentData.json +1738 -1738
  51. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"nodeUtils.js","names":[],"sources":["../../src/css/nodeUtils.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport { cyan, green, magenta, print, red } from '@yahoo/uds-cli/lib';\nimport fg from 'fast-glob';\n\nimport type { SerializedComponentInfo } from '../commands/generateComponentData';\nimport type { RuntimeConfigValues, VariantDefaults } from '../purger/optimized/purgeFromCode';\nimport { purgeFromCodeOptimized } from '../purger/optimized/purgeFromCode';\nimport type { EntryValue } from '../utils/entryPoints';\nimport { formatEntryValue } from '../utils/entryPoints';\n\nconst loadConfigFile = async <T>(configPath: string): Promise<T | null> => {\n const absolutePath = path.isAbsolute(configPath)\n ? configPath\n : path.join(process.cwd(), configPath);\n\n if (!fs.existsSync(absolutePath)) {\n return null;\n }\n\n try {\n const module = await import(absolutePath);\n return module.default ?? module.config ?? module;\n } catch (error) {\n throw new Error(\n `Failed to load config file: ${absolutePath}\\n${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n};\n\nconst scanDirectoryForSafelist = async (\n dir: string,\n colorModes: ('dark' | 'light')[],\n variants: Record<string, Record<string, string>>,\n autoVariants: Record<string, Record<string, string>>,\n componentData: Record<string, SerializedComponentInfo>,\n variantDefaults?: VariantDefaults,\n runtimeConfigValues?: RuntimeConfigValues,\n includeAllClassNamePrimitives = false,\n): Promise<{ safelist: string[]; components: string[]; filesScanned: number }> => {\n const files = await fg('**/*.{js,jsx,ts,tsx}', {\n cwd: dir,\n absolute: true,\n ignore: ['**/node_modules/**'],\n });\n\n const results = await Promise.all(\n files.map(async (filePath) => {\n const code = fs.readFileSync(filePath, 'utf-8');\n return purgeFromCodeOptimized(code, {\n colorModes,\n variantDefaults,\n runtimeConfigValues,\n variants,\n autoVariants,\n componentData,\n includeAllClassNamePrimitives,\n filePath,\n });\n }),\n );\n\n const allClasses = results.flatMap((result) => result.safelist);\n const allComponents = new Set(results.flatMap((result) => result.components));\n\n return {\n safelist: allClasses,\n components: [...allComponents],\n filesScanned: files.length,\n };\n};\n\nconst scanFileForSafelist = async (\n filePath: string,\n colorModes: ('dark' | 'light')[],\n variants: Record<string, Record<string, string>>,\n autoVariants: Record<string, Record<string, string>>,\n componentData: Record<string, SerializedComponentInfo>,\n variantDefaults?: VariantDefaults,\n runtimeConfigValues?: RuntimeConfigValues,\n includeAllClassNamePrimitives = false,\n): Promise<{ safelist: string[]; components: string[]; filesScanned: number }> => {\n const code = fs.readFileSync(filePath, 'utf-8');\n const result = await purgeFromCodeOptimized(code, {\n colorModes,\n variantDefaults,\n runtimeConfigValues,\n variants,\n autoVariants,\n componentData,\n includeAllClassNamePrimitives,\n });\n\n return {\n safelist: result.safelist,\n components: result.components,\n filesScanned: 1,\n };\n};\n\nconst scanDirectoriesForSafelist = async (\n dirs: string[],\n colorModes: ('dark' | 'light')[],\n variants: Record<string, Record<string, string>>,\n autoVariants: Record<string, Record<string, string>>,\n componentData: Record<string, SerializedComponentInfo>,\n variantDefaults?: VariantDefaults,\n runtimeConfigValues?: RuntimeConfigValues,\n includeAllClassNamePrimitives = false,\n): Promise<{ safelist: string[]; components: string[]; filesScanned: number }> => {\n const results = await Promise.all(\n dirs.map((dir) => {\n if (fs.existsSync(dir) && fs.statSync(dir).isFile()) {\n return scanFileForSafelist(\n dir,\n colorModes,\n variants,\n autoVariants,\n componentData,\n variantDefaults,\n runtimeConfigValues,\n includeAllClassNamePrimitives,\n );\n }\n\n return scanDirectoryForSafelist(\n dir,\n colorModes,\n variants,\n autoVariants,\n componentData,\n variantDefaults,\n runtimeConfigValues,\n includeAllClassNamePrimitives,\n );\n }),\n );\n\n return {\n safelist: results.flatMap((result) => result.safelist),\n components: [...new Set(results.flatMap((result) => result.components))],\n filesScanned: results.reduce((total, result) => total + result.filesScanned, 0),\n };\n};\n\nconst findMonorepoRoot = (startDir: string): string | null => {\n const findUp = (currentDir: string): string | null => {\n if (fs.existsSync(path.join(currentDir, 'packages'))) {\n return currentDir;\n }\n\n const parentDir = path.dirname(currentDir);\n return parentDir === currentDir ? null : findUp(parentDir);\n };\n\n return findUp(startDir);\n};\n\nconst resolvePackageRoot = (workspaceDir: string, packageName: string): string | null => {\n const require = createRequire(import.meta.url);\n\n try {\n const resolvedPackageJson = require.resolve(path.join(packageName, 'package.json'), {\n paths: [workspaceDir],\n });\n return path.dirname(resolvedPackageJson);\n } catch {\n return null;\n }\n};\n\nconst findPackageRootInMonorepo = (monorepoRoot: string, packageName: string): string | null => {\n const packageJsonPaths = fg.sync('packages/**/package.json', {\n cwd: monorepoRoot,\n absolute: true,\n ignore: ['**/node_modules/**', '**/dist/**'],\n });\n\n const matchingPath = packageJsonPaths.find((pkgJsonPath) => {\n try {\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')) as { name?: string };\n return pkgJson.name === packageName;\n } catch {\n return false;\n }\n });\n\n return matchingPath ? path.dirname(matchingPath) : null;\n};\n\nconst buildPackageSourceCandidates = (\n workspaceDir: string,\n packageName: string,\n packageRoot: string | null,\n): string[] => {\n return [\n packageRoot ? path.join(packageRoot, 'src') : null,\n packageRoot ? path.join(packageRoot, 'lib') : null,\n packageRoot ? path.join(packageRoot, 'dist') : null,\n packageRoot,\n path.join(workspaceDir, 'node_modules', packageName, 'src'),\n path.join(workspaceDir, 'node_modules', packageName, 'lib'),\n path.join(workspaceDir, 'node_modules', packageName, 'dist'),\n path.join(workspaceDir, 'node_modules', packageName),\n ].filter(Boolean) as string[];\n};\n\nconst getFirstExistingPath = (candidates: string[]): string | null =>\n candidates.find((candidate) => fs.existsSync(candidate)) ?? null;\n\nconst findPackageSourceDir = (packageName: string): string | null => {\n const workspaceDir = process.cwd();\n const monorepoRoot = findMonorepoRoot(workspaceDir);\n\n let packageRoot = resolvePackageRoot(workspaceDir, packageName);\n if (!packageRoot && monorepoRoot) {\n packageRoot = findPackageRootInMonorepo(monorepoRoot, packageName);\n }\n\n return getFirstExistingPath(buildPackageSourceCandidates(workspaceDir, packageName, packageRoot));\n};\n\nconst scaffoldThemeConfig = async (options: {\n outputPath?: string;\n configPath?: string;\n entry?: EntryValue;\n force?: boolean;\n}): Promise<void> => {\n const workspaceDir = process.cwd();\n const outputPath = options.outputPath ?? 'uds.theme.ts';\n const absoluteOutputPath = path.isAbsolute(outputPath)\n ? outputPath\n : path.join(workspaceDir, outputPath);\n\n if (fs.existsSync(absoluteOutputPath) && !options.force) {\n print(red(`Error: ${outputPath} already exists. Use --force to overwrite.`));\n process.exitCode = 1;\n return;\n }\n\n const configPath = options.configPath ?? './uds.config.ts';\n const entry = options.entry ?? './src';\n const serializedEntry = formatEntryValue(entry);\n\n const template = `import { defineTheme } from '@yahoo/uds';\n\n/**\n * UDS Theme Configuration\n * \n * This file configures CSS generation for your app and shared packages.\n * Run \\`uds css\\` to generate optimized CSS.\n */\nexport default defineTheme({\n // Path to your uds.config.ts file\n config: '${configPath}',\n\n // Entry directory or .tsx file path for scanning your app code\n entry: ${serializedEntry},\n\n // Color modes to include (light mode is always in :root)\n colorModes: ['dark'],\n\n // Packages that inherit your app's theme (merged into main uds.css)\n // inherit: ['@your-org/shared-ui'],\n\n // CSS generation options (all optional)\n // css: {\n // safelist: [],\n // preflight: true,\n // fontFaceDeclarations: true,\n // optimization: {\n // removeUnusedFonts: false,\n // removeEmptyRules: true,\n // deduplicateScopedCss: true\n // }\n // }\n});\n`;\n\n fs.writeFileSync(absoluteOutputPath, template);\n\n print('');\n print(green('✅ Created uds.theme.ts'));\n print('');\n print(`${magenta('Next steps:')}`);\n print(` 1. Review and customize ${cyan(outputPath)}`);\n print(` 2. Run ${cyan('uds css')} to generate CSS`);\n print('');\n};\n\nexport {\n findPackageSourceDir,\n loadConfigFile,\n scaffoldThemeConfig,\n scanDirectoriesForSafelist,\n scanDirectoryForSafelist,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"nodeUtils.js","names":[],"sources":["../../src/css/nodeUtils.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport { cyan, green, magenta, print, red } from '@yahoo/uds-cli/lib';\nimport fg from 'fast-glob';\n\nimport type { SerializedComponentInfo } from '../commands/generateComponentData';\nimport type { RuntimeConfigValues, VariantDefaults } from '../purger/optimized/purgeFromCode';\nimport { purgeFromCodeOptimized } from '../purger/optimized/purgeFromCode';\nimport type { EntryValue } from '../utils/entryPoints';\nimport { formatEntryValue } from '../utils/entryPoints';\n\ntype SafelistScanResult = {\n safelist: string[];\n components: string[];\n filesScanned: number;\n filesWithComponents: number;\n filePaths: string[];\n};\n\nconst loadConfigFile = async <T>(configPath: string): Promise<T | null> => {\n const absolutePath = path.isAbsolute(configPath)\n ? configPath\n : path.join(process.cwd(), configPath);\n\n if (!fs.existsSync(absolutePath)) {\n return null;\n }\n\n try {\n const module = await import(absolutePath);\n return module.default ?? module.config ?? module;\n } catch (error) {\n throw new Error(\n `Failed to load config file: ${absolutePath}\\n${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n};\n\nconst scanDirectoryForSafelist = async (\n dir: string,\n colorModes: ('dark' | 'light')[],\n variants: Record<string, Record<string, string>>,\n autoVariants: Record<string, Record<string, string>>,\n componentData: Record<string, SerializedComponentInfo>,\n variantDefaults?: VariantDefaults,\n runtimeConfigValues?: RuntimeConfigValues,\n includeAllClassNamePrimitives = false,\n): Promise<SafelistScanResult> => {\n const files = (\n await fg('**/*.{js,jsx,mjs,cjs,ts,tsx}', {\n cwd: dir,\n absolute: true,\n ignore: ['**/node_modules/**'],\n })\n ).sort((left, right) => left.localeCompare(right));\n\n const results = await Promise.all(\n files.map(async (filePath) => {\n const code = fs.readFileSync(filePath, 'utf-8');\n return purgeFromCodeOptimized(code, {\n colorModes,\n variantDefaults,\n runtimeConfigValues,\n variants,\n autoVariants,\n componentData,\n includeAllClassNamePrimitives,\n filePath,\n });\n }),\n );\n\n const allClasses = results.flatMap((result) => result.safelist);\n const allComponents = new Set(results.flatMap((result) => result.components));\n const filesWithComponents = results.filter((result) => result.components.length > 0).length;\n\n return {\n safelist: allClasses,\n components: [...allComponents],\n filesScanned: files.length,\n filesWithComponents,\n filePaths: files,\n };\n};\n\nconst scanFileForSafelist = async (\n filePath: string,\n colorModes: ('dark' | 'light')[],\n variants: Record<string, Record<string, string>>,\n autoVariants: Record<string, Record<string, string>>,\n componentData: Record<string, SerializedComponentInfo>,\n variantDefaults?: VariantDefaults,\n runtimeConfigValues?: RuntimeConfigValues,\n includeAllClassNamePrimitives = false,\n): Promise<SafelistScanResult> => {\n const code = fs.readFileSync(filePath, 'utf-8');\n const result = await purgeFromCodeOptimized(code, {\n colorModes,\n variantDefaults,\n runtimeConfigValues,\n variants,\n autoVariants,\n componentData,\n includeAllClassNamePrimitives,\n });\n\n return {\n safelist: result.safelist,\n components: result.components,\n filesScanned: 1,\n filesWithComponents: result.components.length > 0 ? 1 : 0,\n filePaths: [filePath],\n };\n};\n\nconst scanDirectoriesForSafelist = async (\n dirs: string[],\n colorModes: ('dark' | 'light')[],\n variants: Record<string, Record<string, string>>,\n autoVariants: Record<string, Record<string, string>>,\n componentData: Record<string, SerializedComponentInfo>,\n variantDefaults?: VariantDefaults,\n runtimeConfigValues?: RuntimeConfigValues,\n includeAllClassNamePrimitives = false,\n): Promise<SafelistScanResult> => {\n const results = await Promise.all(\n dirs.map((dir) => {\n if (fs.existsSync(dir) && fs.statSync(dir).isFile()) {\n return scanFileForSafelist(\n dir,\n colorModes,\n variants,\n autoVariants,\n componentData,\n variantDefaults,\n runtimeConfigValues,\n includeAllClassNamePrimitives,\n );\n }\n\n return scanDirectoryForSafelist(\n dir,\n colorModes,\n variants,\n autoVariants,\n componentData,\n variantDefaults,\n runtimeConfigValues,\n includeAllClassNamePrimitives,\n );\n }),\n );\n\n return {\n safelist: results.flatMap((result) => result.safelist),\n components: [...new Set(results.flatMap((result) => result.components))],\n filesScanned: results.reduce((total, result) => total + result.filesScanned, 0),\n filesWithComponents: results.reduce((total, result) => total + result.filesWithComponents, 0),\n filePaths: results\n .flatMap((result) => result.filePaths)\n .sort((left, right) => left.localeCompare(right)),\n };\n};\n\nconst findMonorepoRoot = (startDir: string): string | null => {\n const findUp = (currentDir: string): string | null => {\n if (fs.existsSync(path.join(currentDir, 'packages'))) {\n return currentDir;\n }\n\n const parentDir = path.dirname(currentDir);\n return parentDir === currentDir ? null : findUp(parentDir);\n };\n\n return findUp(startDir);\n};\n\nconst readPackageName = (packageJsonPath: string): string | null => {\n try {\n const pkgJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) as { name?: string };\n return typeof pkgJson.name === 'string' ? pkgJson.name : null;\n } catch {\n return null;\n }\n};\n\nconst findPackageRootFromResolvedEntry = (\n resolvedEntryPath: string,\n packageName: string,\n): string | null => {\n let currentPath = fs.statSync(resolvedEntryPath).isDirectory()\n ? resolvedEntryPath\n : path.dirname(resolvedEntryPath);\n\n while (true) {\n const packageJsonPath = path.join(currentPath, 'package.json');\n if (fs.existsSync(packageJsonPath) && readPackageName(packageJsonPath) === packageName) {\n return currentPath;\n }\n\n const parentPath = path.dirname(currentPath);\n if (parentPath === currentPath) {\n return null;\n }\n\n currentPath = parentPath;\n }\n};\n\nconst findPackageRootInNodeModules = (startDir: string, packageName: string): string | null => {\n let currentDir = startDir;\n\n while (true) {\n const packageJsonPath = path.join(currentDir, 'node_modules', packageName, 'package.json');\n if (fs.existsSync(packageJsonPath)) {\n return path.dirname(packageJsonPath);\n }\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) {\n return null;\n }\n\n currentDir = parentDir;\n }\n};\n\nconst resolvePackageRoot = (workspaceDir: string, packageName: string): string | null => {\n const requireFromWorkspace = createRequire(path.join(workspaceDir, 'package.json'));\n\n try {\n const resolvedPackageJson = requireFromWorkspace.resolve(\n path.join(packageName, 'package.json'),\n );\n return path.dirname(resolvedPackageJson);\n } catch {\n try {\n const resolvedPackageEntry = requireFromWorkspace.resolve(packageName);\n const packageRoot = findPackageRootFromResolvedEntry(resolvedPackageEntry, packageName);\n if (packageRoot) {\n return packageRoot;\n }\n } catch {\n // Fall through to explicit node_modules lookup below.\n }\n\n return findPackageRootInNodeModules(workspaceDir, packageName);\n }\n};\n\ntype PackageJsonShape = {\n uds?: {\n scope?: string;\n };\n};\n\nconst findPackageRootInMonorepo = (monorepoRoot: string, packageName: string): string | null => {\n const packageJsonPaths = fg.sync('packages/**/package.json', {\n cwd: monorepoRoot,\n absolute: true,\n ignore: ['**/node_modules/**', '**/dist/**'],\n });\n\n const matchingPath = packageJsonPaths.find((pkgJsonPath) => {\n return readPackageName(pkgJsonPath) === packageName;\n });\n\n return matchingPath ? path.dirname(matchingPath) : null;\n};\n\nconst findPackageRoot = (packageName: string): string | null => {\n const workspaceDir = process.cwd();\n const monorepoRoot = findMonorepoRoot(workspaceDir);\n\n let packageRoot = resolvePackageRoot(workspaceDir, packageName);\n if (!packageRoot && monorepoRoot) {\n packageRoot = findPackageRootInMonorepo(monorepoRoot, packageName);\n }\n\n return packageRoot;\n};\n\nconst buildPackageSourceCandidates = (\n workspaceDir: string,\n packageName: string,\n packageRoot: string | null,\n): string[] => {\n return [\n packageRoot ? path.join(packageRoot, 'src') : null,\n packageRoot ? path.join(packageRoot, 'lib') : null,\n packageRoot ? path.join(packageRoot, 'dist') : null,\n packageRoot,\n path.join(workspaceDir, 'node_modules', packageName, 'src'),\n path.join(workspaceDir, 'node_modules', packageName, 'lib'),\n path.join(workspaceDir, 'node_modules', packageName, 'dist'),\n path.join(workspaceDir, 'node_modules', packageName),\n ].filter(Boolean) as string[];\n};\n\nconst getFirstExistingPath = (candidates: string[]): string | null =>\n candidates.find((candidate) => fs.existsSync(candidate)) ?? null;\n\nconst findPackageSourceDir = (packageName: string): string | null => {\n const workspaceDir = process.cwd();\n const packageRoot = findPackageRoot(packageName);\n\n return getFirstExistingPath(buildPackageSourceCandidates(workspaceDir, packageName, packageRoot));\n};\n\nconst getPackageUdsScope = (packageName: string): string | null => {\n const packageRoot = findPackageRoot(packageName);\n if (!packageRoot) {\n return null;\n }\n\n const packageJsonPath = path.join(packageRoot, 'package.json');\n if (!fs.existsSync(packageJsonPath)) {\n return null;\n }\n\n try {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) as PackageJsonShape;\n const scope = packageJson.uds?.scope;\n return typeof scope === 'string' && scope.trim().length > 0 ? scope.trim() : null;\n } catch {\n return null;\n }\n};\n\nconst scaffoldThemeConfig = async (options: {\n outputPath?: string;\n configPath?: string;\n entry?: EntryValue;\n force?: boolean;\n}): Promise<void> => {\n const workspaceDir = process.cwd();\n const outputPath = options.outputPath ?? 'uds.theme.ts';\n const absoluteOutputPath = path.isAbsolute(outputPath)\n ? outputPath\n : path.join(workspaceDir, outputPath);\n\n if (fs.existsSync(absoluteOutputPath) && !options.force) {\n print(red(`Error: ${outputPath} already exists. Use --force to overwrite.`));\n process.exitCode = 1;\n return;\n }\n\n const configPath = options.configPath ?? './uds.config.ts';\n const entry = options.entry ?? './src';\n const serializedEntry = formatEntryValue(entry);\n\n const template = `import { defineTheme } from '@yahoo/uds';\n\n/**\n * UDS Theme Configuration\n * \n * This file configures CSS generation for your app and shared packages.\n * Run \\`uds css\\` to generate optimized CSS.\n */\nexport default defineTheme({\n // Path to your uds.config.ts file\n config: '${configPath}',\n\n // Entry directory or .tsx file path for scanning your app code\n entry: ${serializedEntry},\n\n // Color modes to include (light mode is always in :root)\n colorModes: ['dark'],\n\n // Packages that inherit your app's theme (merged into main uds.css)\n // inherit: ['@your-org/shared-ui'],\n\n // Packages that generate separate scoped CSS outputs.\n // Scope class is discovered from each package.json uds.scope.\n // When entry is omitted, the package is scanned the same way as inherit.\n // scoped: {\n // '@your-org/shared-ui': './packages/shared-ui/uds.config.ts'\n // }\n //\n // Or use the expanded form for custom output paths:\n // scoped: {\n // '@your-org/shared-ui': {\n // config: './packages/shared-ui/uds.config.ts',\n // entry: './src',\n // // outFile defaults to dist/shared-ui.css\n // outFile: 'dist/shared-ui.css'\n // },\n // },\n\n // CSS generation options (all optional)\n // css: {\n // safelist: [],\n // preflight: true,\n // fontFaceDeclarations: true,\n // optimization: {\n // removeUnusedFonts: false,\n // removeEmptyRules: true,\n // deduplicateScopedCss: true\n // }\n // }\n});\n`;\n\n fs.writeFileSync(absoluteOutputPath, template);\n\n print('');\n print(green('✅ Created uds.theme.ts'));\n print('');\n print(`${magenta('Next steps:')}`);\n print(` 1. Review and customize ${cyan(outputPath)}`);\n print(` 2. Run ${cyan('uds css')} to generate CSS`);\n print('');\n};\n\nexport {\n findPackageRoot,\n findPackageSourceDir,\n getPackageUdsScope,\n loadConfigFile,\n scaffoldThemeConfig,\n scanDirectoriesForSafelist,\n scanDirectoryForSafelist,\n};\nexport type { SafelistScanResult };\n"],"mappings":"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"}
@@ -2,6 +2,26 @@
2
2
  const require_postcss_helpers = require('./postcss.helpers.cjs');
3
3
 
4
4
  //#region src/css/postcss.ts
5
+ const fixScopedSelfOrParentSelectorsPlugin = (scopeClass) => {
6
+ return {
7
+ postcssPlugin: "fix-scoped-self-or-parent-selectors",
8
+ Once(root) {
9
+ root.walkRules((rule) => {
10
+ if (!rule.selector.includes(scopeClass)) return;
11
+ const nextSelectors = rule.selectors.reduce((acc, selector) => {
12
+ const sameElementSelector = require_postcss_helpers.getScopedSelfOrParentAlternativeSelector(selector, scopeClass);
13
+ if (sameElementSelector && !rule.selectors.includes(sameElementSelector)) return [
14
+ ...acc,
15
+ selector,
16
+ sameElementSelector
17
+ ];
18
+ return [...acc, selector];
19
+ }, []);
20
+ if (nextSelectors.length !== rule.selectors.length) rule.selectors = nextSelectors;
21
+ });
22
+ }
23
+ };
24
+ };
5
25
  /**
6
26
  * PostCSS plugin to fix color mode selectors for scoped CSS.
7
27
  *
@@ -31,4 +51,5 @@ const fixScopedColorModeSelectorsPlugin = (scopeClass) => {
31
51
  };
32
52
 
33
53
  //#endregion
34
- exports.fixScopedColorModeSelectorsPlugin = fixScopedColorModeSelectorsPlugin;
54
+ exports.fixScopedColorModeSelectorsPlugin = fixScopedColorModeSelectorsPlugin;
55
+ exports.fixScopedSelfOrParentSelectorsPlugin = fixScopedSelfOrParentSelectorsPlugin;
@@ -3,6 +3,7 @@
3
3
  //#region src/css/postcss.helpers.ts
4
4
  const COLOR_MODE_RE = /^(\.(uds-color-mode-(?:dark|light)))(\s+.+)?$/;
5
5
  const escapeForRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6
+ const SCOPED_SELF_OR_PARENT_RE = /^(\.[^\s>+~]+)(.*)$/;
6
7
  const getScopedColorModeAlternativeSelector = (selector, scopeClass, variant = "descendant") => {
7
8
  const scopeMatch = selector.match(new RegExp(`^(${escapeForRegExp(scopeClass)})\\s+(.+)$`));
8
9
  if (!scopeMatch) return null;
@@ -23,6 +24,16 @@ const buildScopedColorModeSelectorList = (ruleSelector, scopeClass) => {
23
24
  ];
24
25
  }, []).join(", ");
25
26
  };
27
+ const getScopedSelfOrParentAlternativeSelector = (selector, scopeClass) => {
28
+ const scopeMatch = selector.match(new RegExp(`^(${escapeForRegExp(scopeClass)})\\s+(.+)$`));
29
+ if (!scopeMatch) return null;
30
+ const targetMatch = scopeMatch[2].match(SCOPED_SELF_OR_PARENT_RE);
31
+ if (!targetMatch) return null;
32
+ const scopedTarget = targetMatch[1];
33
+ if (/^\.uds-color-mode-(?:dark|light)(?:$|[.#[:])/.test(scopedTarget)) return null;
34
+ return `${scopeClass}${scopedTarget}${targetMatch[2]}`;
35
+ };
26
36
 
27
37
  //#endregion
28
- exports.buildScopedColorModeSelectorList = buildScopedColorModeSelectorList;
38
+ exports.buildScopedColorModeSelectorList = buildScopedColorModeSelectorList;
39
+ exports.getScopedSelfOrParentAlternativeSelector = getScopedSelfOrParentAlternativeSelector;
@@ -2,6 +2,7 @@
2
2
  //#region src/css/postcss.helpers.ts
3
3
  const COLOR_MODE_RE = /^(\.(uds-color-mode-(?:dark|light)))(\s+.+)?$/;
4
4
  const escapeForRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5
+ const SCOPED_SELF_OR_PARENT_RE = /^(\.[^\s>+~]+)(.*)$/;
5
6
  const getScopedColorModeAlternativeSelector = (selector, scopeClass, variant = "descendant") => {
6
7
  const scopeMatch = selector.match(new RegExp(`^(${escapeForRegExp(scopeClass)})\\s+(.+)$`));
7
8
  if (!scopeMatch) return null;
@@ -22,7 +23,16 @@ const buildScopedColorModeSelectorList = (ruleSelector, scopeClass) => {
22
23
  ];
23
24
  }, []).join(", ");
24
25
  };
26
+ const getScopedSelfOrParentAlternativeSelector = (selector, scopeClass) => {
27
+ const scopeMatch = selector.match(new RegExp(`^(${escapeForRegExp(scopeClass)})\\s+(.+)$`));
28
+ if (!scopeMatch) return null;
29
+ const targetMatch = scopeMatch[2].match(SCOPED_SELF_OR_PARENT_RE);
30
+ if (!targetMatch) return null;
31
+ const scopedTarget = targetMatch[1];
32
+ if (/^\.uds-color-mode-(?:dark|light)(?:$|[.#[:])/.test(scopedTarget)) return null;
33
+ return `${scopeClass}${scopedTarget}${targetMatch[2]}`;
34
+ };
25
35
 
26
36
  //#endregion
27
- export { buildScopedColorModeSelectorList };
37
+ export { buildScopedColorModeSelectorList, getScopedSelfOrParentAlternativeSelector };
28
38
  //# sourceMappingURL=postcss.helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"postcss.helpers.js","names":[],"sources":["../../src/css/postcss.helpers.ts"],"sourcesContent":["const COLOR_MODE_RE = /^(\\.(uds-color-mode-(?:dark|light)))(\\s+.+)?$/;\n\nconst escapeForRegExp = (value: string): string => value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\ntype ScopedColorModeSelectorVariant = 'descendant' | 'same-element';\n\nconst getScopedColorModeAlternativeSelector = (\n selector: string,\n scopeClass: string,\n variant: ScopedColorModeSelectorVariant = 'descendant',\n): string | null => {\n const scopeMatch = selector.match(new RegExp(`^(${escapeForRegExp(scopeClass)})\\\\s+(.+)$`));\n\n if (!scopeMatch) {\n return null;\n }\n\n const afterScope = scopeMatch[2];\n const colorModeMatch = afterScope.match(COLOR_MODE_RE);\n if (!colorModeMatch) {\n return null;\n }\n\n const colorModeClass = colorModeMatch[1];\n const rest = colorModeMatch[3] ?? '';\n return variant === 'same-element'\n ? `${colorModeClass}${scopeClass}${rest}`\n : `${colorModeClass} ${scopeClass}${rest}`;\n};\n\nconst buildScopedColorModeSelectorList = (ruleSelector: string, scopeClass: string): string => {\n const selectors = ruleSelector.split(',').map((value) => value.trim());\n const nextSelectors = selectors.reduce<string[]>((acc, selector) => {\n const altSelectors = [\n getScopedColorModeAlternativeSelector(selector, scopeClass),\n getScopedColorModeAlternativeSelector(selector, scopeClass, 'same-element'),\n ].filter((value): value is string => Boolean(value));\n const nextAltSelectors = altSelectors.filter(\n (altSelector) => !selectors.includes(altSelector) && !acc.includes(altSelector),\n );\n\n return [...acc, selector, ...nextAltSelectors];\n }, []);\n\n return nextSelectors.join(', ');\n};\n\nexport { buildScopedColorModeSelectorList, getScopedColorModeAlternativeSelector };\n"],"mappings":";;AAAA,MAAM,gBAAgB;AAEtB,MAAM,mBAAmB,UAA0B,MAAM,QAAQ,uBAAuB,OAAO;AAI/F,MAAM,yCACJ,UACA,YACA,UAA0C,iBACxB;CAClB,MAAM,aAAa,SAAS,MAAM,IAAI,OAAO,KAAK,gBAAgB,WAAW,CAAC,YAAY,CAAC;AAE3F,KAAI,CAAC,WACH,QAAO;CAIT,MAAM,iBADa,WAAW,GACI,MAAM,cAAc;AACtD,KAAI,CAAC,eACH,QAAO;CAGT,MAAM,iBAAiB,eAAe;CACtC,MAAM,OAAO,eAAe,MAAM;AAClC,QAAO,YAAY,iBACf,GAAG,iBAAiB,aAAa,SACjC,GAAG,eAAe,GAAG,aAAa;;AAGxC,MAAM,oCAAoC,cAAsB,eAA+B;CAC7F,MAAM,YAAY,aAAa,MAAM,IAAI,CAAC,KAAK,UAAU,MAAM,MAAM,CAAC;AAatE,QAZsB,UAAU,QAAkB,KAAK,aAAa;EAKlE,MAAM,mBAJe,CACnB,sCAAsC,UAAU,WAAW,EAC3D,sCAAsC,UAAU,YAAY,eAAe,CAC5E,CAAC,QAAQ,UAA2B,QAAQ,MAAM,CAAC,CACd,QACnC,gBAAgB,CAAC,UAAU,SAAS,YAAY,IAAI,CAAC,IAAI,SAAS,YAAY,CAChF;AAED,SAAO;GAAC,GAAG;GAAK;GAAU,GAAG;GAAiB;IAC7C,EAAE,CAAC,CAEe,KAAK,KAAK"}
1
+ {"version":3,"file":"postcss.helpers.js","names":[],"sources":["../../src/css/postcss.helpers.ts"],"sourcesContent":["const COLOR_MODE_RE = /^(\\.(uds-color-mode-(?:dark|light)))(\\s+.+)?$/;\n\nconst escapeForRegExp = (value: string): string => value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\ntype ScopedColorModeSelectorVariant = 'descendant' | 'same-element';\n\nconst SCOPED_SELF_OR_PARENT_RE = /^(\\.[^\\s>+~]+)(.*)$/;\n\nconst getScopedColorModeAlternativeSelector = (\n selector: string,\n scopeClass: string,\n variant: ScopedColorModeSelectorVariant = 'descendant',\n): string | null => {\n const scopeMatch = selector.match(new RegExp(`^(${escapeForRegExp(scopeClass)})\\\\s+(.+)$`));\n\n if (!scopeMatch) {\n return null;\n }\n\n const afterScope = scopeMatch[2];\n const colorModeMatch = afterScope.match(COLOR_MODE_RE);\n if (!colorModeMatch) {\n return null;\n }\n\n const colorModeClass = colorModeMatch[1];\n const rest = colorModeMatch[3] ?? '';\n return variant === 'same-element'\n ? `${colorModeClass}${scopeClass}${rest}`\n : `${colorModeClass} ${scopeClass}${rest}`;\n};\n\nconst buildScopedColorModeSelectorList = (ruleSelector: string, scopeClass: string): string => {\n const selectors = ruleSelector.split(',').map((value) => value.trim());\n const nextSelectors = selectors.reduce<string[]>((acc, selector) => {\n const altSelectors = [\n getScopedColorModeAlternativeSelector(selector, scopeClass),\n getScopedColorModeAlternativeSelector(selector, scopeClass, 'same-element'),\n ].filter((value): value is string => Boolean(value));\n const nextAltSelectors = altSelectors.filter(\n (altSelector) => !selectors.includes(altSelector) && !acc.includes(altSelector),\n );\n\n return [...acc, selector, ...nextAltSelectors];\n }, []);\n\n return nextSelectors.join(', ');\n};\n\nconst getScopedSelfOrParentAlternativeSelector = (\n selector: string,\n scopeClass: string,\n): string | null => {\n const scopeMatch = selector.match(new RegExp(`^(${escapeForRegExp(scopeClass)})\\\\s+(.+)$`));\n\n if (!scopeMatch) {\n return null;\n }\n\n const afterScope = scopeMatch[2];\n const targetMatch = afterScope.match(SCOPED_SELF_OR_PARENT_RE);\n if (!targetMatch) {\n return null;\n }\n\n const scopedTarget = targetMatch[1];\n if (/^\\.uds-color-mode-(?:dark|light)(?:$|[.#[:])/.test(scopedTarget)) {\n return null;\n }\n\n return `${scopeClass}${scopedTarget}${targetMatch[2]}`;\n};\n\nconst buildScopedSelfOrParentSelectorList = (ruleSelector: string, scopeClass: string): string => {\n const selectors = ruleSelector.split(',').map((value) => value.trim());\n const nextSelectors = selectors.reduce<string[]>((acc, selector) => {\n const sameElementSelector = getScopedSelfOrParentAlternativeSelector(selector, scopeClass);\n const nextAltSelectors: string[] = [];\n\n if (\n sameElementSelector &&\n !selectors.includes(sameElementSelector) &&\n !acc.includes(sameElementSelector)\n ) {\n nextAltSelectors.push(sameElementSelector);\n }\n\n return [...acc, selector, ...nextAltSelectors];\n }, []);\n\n return nextSelectors.join(', ');\n};\n\nexport {\n buildScopedColorModeSelectorList,\n buildScopedSelfOrParentSelectorList,\n getScopedColorModeAlternativeSelector,\n getScopedSelfOrParentAlternativeSelector,\n};\n"],"mappings":";;AAAA,MAAM,gBAAgB;AAEtB,MAAM,mBAAmB,UAA0B,MAAM,QAAQ,uBAAuB,OAAO;AAI/F,MAAM,2BAA2B;AAEjC,MAAM,yCACJ,UACA,YACA,UAA0C,iBACxB;CAClB,MAAM,aAAa,SAAS,MAAM,IAAI,OAAO,KAAK,gBAAgB,WAAW,CAAC,YAAY,CAAC;AAE3F,KAAI,CAAC,WACH,QAAO;CAIT,MAAM,iBADa,WAAW,GACI,MAAM,cAAc;AACtD,KAAI,CAAC,eACH,QAAO;CAGT,MAAM,iBAAiB,eAAe;CACtC,MAAM,OAAO,eAAe,MAAM;AAClC,QAAO,YAAY,iBACf,GAAG,iBAAiB,aAAa,SACjC,GAAG,eAAe,GAAG,aAAa;;AAGxC,MAAM,oCAAoC,cAAsB,eAA+B;CAC7F,MAAM,YAAY,aAAa,MAAM,IAAI,CAAC,KAAK,UAAU,MAAM,MAAM,CAAC;AAatE,QAZsB,UAAU,QAAkB,KAAK,aAAa;EAKlE,MAAM,mBAJe,CACnB,sCAAsC,UAAU,WAAW,EAC3D,sCAAsC,UAAU,YAAY,eAAe,CAC5E,CAAC,QAAQ,UAA2B,QAAQ,MAAM,CAAC,CACd,QACnC,gBAAgB,CAAC,UAAU,SAAS,YAAY,IAAI,CAAC,IAAI,SAAS,YAAY,CAChF;AAED,SAAO;GAAC,GAAG;GAAK;GAAU,GAAG;GAAiB;IAC7C,EAAE,CAAC,CAEe,KAAK,KAAK;;AAGjC,MAAM,4CACJ,UACA,eACkB;CAClB,MAAM,aAAa,SAAS,MAAM,IAAI,OAAO,KAAK,gBAAgB,WAAW,CAAC,YAAY,CAAC;AAE3F,KAAI,CAAC,WACH,QAAO;CAIT,MAAM,cADa,WAAW,GACC,MAAM,yBAAyB;AAC9D,KAAI,CAAC,YACH,QAAO;CAGT,MAAM,eAAe,YAAY;AACjC,KAAI,+CAA+C,KAAK,aAAa,CACnE,QAAO;AAGT,QAAO,GAAG,aAAa,eAAe,YAAY"}
@@ -1,7 +1,27 @@
1
1
  /*! © 2026 Yahoo, Inc. UDS Tailwind and Purger v0.0.0-development */
2
- import { buildScopedColorModeSelectorList } from "./postcss.helpers.js";
2
+ import { buildScopedColorModeSelectorList, getScopedSelfOrParentAlternativeSelector } from "./postcss.helpers.js";
3
3
 
4
4
  //#region src/css/postcss.ts
5
+ const fixScopedSelfOrParentSelectorsPlugin = (scopeClass) => {
6
+ return {
7
+ postcssPlugin: "fix-scoped-self-or-parent-selectors",
8
+ Once(root) {
9
+ root.walkRules((rule) => {
10
+ if (!rule.selector.includes(scopeClass)) return;
11
+ const nextSelectors = rule.selectors.reduce((acc, selector) => {
12
+ const sameElementSelector = getScopedSelfOrParentAlternativeSelector(selector, scopeClass);
13
+ if (sameElementSelector && !rule.selectors.includes(sameElementSelector)) return [
14
+ ...acc,
15
+ selector,
16
+ sameElementSelector
17
+ ];
18
+ return [...acc, selector];
19
+ }, []);
20
+ if (nextSelectors.length !== rule.selectors.length) rule.selectors = nextSelectors;
21
+ });
22
+ }
23
+ };
24
+ };
5
25
  /**
6
26
  * PostCSS plugin to fix color mode selectors for scoped CSS.
7
27
  *
@@ -31,5 +51,5 @@ const fixScopedColorModeSelectorsPlugin = (scopeClass) => {
31
51
  };
32
52
 
33
53
  //#endregion
34
- export { fixScopedColorModeSelectorsPlugin };
54
+ export { fixScopedColorModeSelectorsPlugin, fixScopedSelfOrParentSelectorsPlugin };
35
55
  //# sourceMappingURL=postcss.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"postcss.js","names":[],"sources":["../../src/css/postcss.ts"],"sourcesContent":["import type postcss from 'postcss';\n\nimport { buildScopedColorModeSelectorList } from './postcss.helpers';\n\n/**\n * PostCSS plugin to fix color mode selectors for scoped CSS.\n *\n * postcss-scope converts selectors like:\n * `.uds-color-mode-dark .foo` → `.scope .uds-color-mode-dark .foo`\n *\n * But the color mode class is typically on <html> or <body>, which is an\n * ANCESTOR of the scope, not a descendant. This plugin adds alternative\n * selectors so color modes work correctly:\n *\n * `.scope .uds-color-mode-dark .foo` → `.scope .uds-color-mode-dark .foo, .uds-color-mode-dark .scope .foo, .uds-color-mode-dark.scope .foo`\n *\n * This ensures styles apply whether the color mode class is inside or outside the scope.\n */\nconst fixScopedColorModeSelectorsPlugin = (scopeClass: string): postcss.Plugin => {\n const colorModePattern = /\\.(uds-color-mode-(?:dark|light))/;\n\n return {\n postcssPlugin: 'fix-scoped-color-mode-selectors',\n // Use Once to process all rules after parsing is complete\n Once(root) {\n root.walkRules((rule) => {\n // Skip if selector doesn't contain both scope and color mode\n if (!rule.selector.includes(scopeClass) || !colorModePattern.test(rule.selector)) {\n return;\n }\n\n const nextSelector = buildScopedColorModeSelectorList(rule.selector, scopeClass);\n if (nextSelector !== rule.selector) {\n rule.selector = nextSelector;\n }\n });\n },\n };\n};\nexport { fixScopedColorModeSelectorsPlugin };\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkBA,MAAM,qCAAqC,eAAuC;CAChF,MAAM,mBAAmB;AAEzB,QAAO;EACL,eAAe;EAEf,KAAK,MAAM;AACT,QAAK,WAAW,SAAS;AAEvB,QAAI,CAAC,KAAK,SAAS,SAAS,WAAW,IAAI,CAAC,iBAAiB,KAAK,KAAK,SAAS,CAC9E;IAGF,MAAM,eAAe,iCAAiC,KAAK,UAAU,WAAW;AAChF,QAAI,iBAAiB,KAAK,SACxB,MAAK,WAAW;KAElB;;EAEL"}
1
+ {"version":3,"file":"postcss.js","names":[],"sources":["../../src/css/postcss.ts"],"sourcesContent":["import type postcss from 'postcss';\n\nimport {\n buildScopedColorModeSelectorList,\n getScopedSelfOrParentAlternativeSelector,\n} from './postcss.helpers';\n\nconst fixScopedSelfOrParentSelectorsPlugin = (scopeClass: string): postcss.Plugin => {\n return {\n postcssPlugin: 'fix-scoped-self-or-parent-selectors',\n Once(root) {\n root.walkRules((rule) => {\n if (!rule.selector.includes(scopeClass)) {\n return;\n }\n\n const nextSelectors = rule.selectors.reduce<string[]>((acc, selector) => {\n const sameElementSelector = getScopedSelfOrParentAlternativeSelector(\n selector,\n scopeClass,\n );\n\n if (sameElementSelector && !rule.selectors.includes(sameElementSelector)) {\n return [...acc, selector, sameElementSelector];\n }\n\n return [...acc, selector];\n }, []);\n\n if (nextSelectors.length !== rule.selectors.length) {\n rule.selectors = nextSelectors;\n }\n });\n },\n };\n};\n\n/**\n * PostCSS plugin to fix color mode selectors for scoped CSS.\n *\n * postcss-scope converts selectors like:\n * `.uds-color-mode-dark .foo` → `.scope .uds-color-mode-dark .foo`\n *\n * But the color mode class is typically on <html> or <body>, which is an\n * ANCESTOR of the scope, not a descendant. This plugin adds alternative\n * selectors so color modes work correctly:\n *\n * `.scope .uds-color-mode-dark .foo` → `.scope .uds-color-mode-dark .foo, .uds-color-mode-dark .scope .foo, .uds-color-mode-dark.scope .foo`\n *\n * This ensures styles apply whether the color mode class is inside or outside the scope.\n */\nconst fixScopedColorModeSelectorsPlugin = (scopeClass: string): postcss.Plugin => {\n const colorModePattern = /\\.(uds-color-mode-(?:dark|light))/;\n\n return {\n postcssPlugin: 'fix-scoped-color-mode-selectors',\n // Use Once to process all rules after parsing is complete\n Once(root) {\n root.walkRules((rule) => {\n // Skip if selector doesn't contain both scope and color mode\n if (!rule.selector.includes(scopeClass) || !colorModePattern.test(rule.selector)) {\n return;\n }\n\n const nextSelector = buildScopedColorModeSelectorList(rule.selector, scopeClass);\n if (nextSelector !== rule.selector) {\n rule.selector = nextSelector;\n }\n });\n },\n };\n};\nexport { fixScopedColorModeSelectorsPlugin, fixScopedSelfOrParentSelectorsPlugin };\n"],"mappings":";;;;AAOA,MAAM,wCAAwC,eAAuC;AACnF,QAAO;EACL,eAAe;EACf,KAAK,MAAM;AACT,QAAK,WAAW,SAAS;AACvB,QAAI,CAAC,KAAK,SAAS,SAAS,WAAW,CACrC;IAGF,MAAM,gBAAgB,KAAK,UAAU,QAAkB,KAAK,aAAa;KACvE,MAAM,sBAAsB,yCAC1B,UACA,WACD;AAED,SAAI,uBAAuB,CAAC,KAAK,UAAU,SAAS,oBAAoB,CACtE,QAAO;MAAC,GAAG;MAAK;MAAU;MAAoB;AAGhD,YAAO,CAAC,GAAG,KAAK,SAAS;OACxB,EAAE,CAAC;AAEN,QAAI,cAAc,WAAW,KAAK,UAAU,OAC1C,MAAK,YAAY;KAEnB;;EAEL;;;;;;;;;;;;;;;;AAiBH,MAAM,qCAAqC,eAAuC;CAChF,MAAM,mBAAmB;AAEzB,QAAO;EACL,eAAe;EAEf,KAAK,MAAM;AACT,QAAK,WAAW,SAAS;AAEvB,QAAI,CAAC,KAAK,SAAS,SAAS,WAAW,IAAI,CAAC,iBAAiB,KAAK,KAAK,SAAS,CAC9E;IAGF,MAAM,eAAe,iCAAiC,KAAK,UAAU,WAAW;AAChF,QAAI,iBAAiB,KAAK,SACxB,MAAK,WAAW;KAElB;;EAEL"}
@@ -8,19 +8,60 @@ const require_entryPoints = require('../utils/entryPoints.cjs');
8
8
  const require_nodeUtils = require('./nodeUtils.cjs');
9
9
  const require_index = require('../config/dist/index.cjs');
10
10
  const require_safelist = require('../purger/optimized/utils/safelist.cjs');
11
+ const require_runner_helpers = require('./runner.helpers.cjs');
11
12
  const require_utils = require('./utils.cjs');
12
13
  const require_generate_helpers = require('./generate.helpers.cjs');
13
14
  const require_generate = require('./generate.cjs');
14
- const require_runner_helpers = require('./runner.helpers.cjs');
15
15
  let node_fs = require("node:fs");
16
16
  node_fs = require_runtime.__toESM(node_fs);
17
- let node_path = require("node:path");
18
- node_path = require_runtime.__toESM(node_path);
19
17
 
20
18
  //#region src/css/runner.ts
19
+ const getScopedPackageOutputPath = (packageName, outFile) => {
20
+ if (typeof outFile === "string" && outFile.trim().length > 0) return outFile;
21
+ return `dist/${packageName.split("/").pop() ?? packageName}.css`;
22
+ };
23
+ const normalizeScopedPackageConfig = (scopedPackageValue) => {
24
+ if (typeof scopedPackageValue === "string") return { config: scopedPackageValue };
25
+ return scopedPackageValue;
26
+ };
27
+ const resolveScopedEntryDirs = (packageRoot, packageDir, entry) => {
28
+ if (entry === void 0) return [packageDir];
29
+ return require_entryPoints.resolveEntryPaths(entry, packageRoot).map((resolvedEntry) => resolvedEntry.absolutePath);
30
+ };
31
+ const PATH_SEPARATOR = "/";
32
+ const normalizePath = (value) => value.replace(/\\/g, PATH_SEPARATOR);
33
+ const isAbsolutePath = (value) => {
34
+ const normalizedValue = normalizePath(value);
35
+ return normalizedValue.startsWith(PATH_SEPARATOR) || /^[A-Za-z]:\//.test(normalizedValue);
36
+ };
37
+ const joinPath = (...parts) => {
38
+ const filteredParts = parts.filter((part) => part.length > 0);
39
+ if (filteredParts.length === 0) return "";
40
+ return filteredParts.map((part, index) => {
41
+ const normalizedPart = normalizePath(part);
42
+ if (index === 0) return normalizedPart.replace(/\/+$/g, "");
43
+ return normalizedPart.replace(/^\/+|\/+$/g, "");
44
+ }).join(PATH_SEPARATOR).replace(/\/+/g, PATH_SEPARATOR).replace(/\/\.\//g, PATH_SEPARATOR);
45
+ };
46
+ const dirnamePath = (value) => {
47
+ const normalizedValue = normalizePath(value).replace(/\/+$/g, "");
48
+ const lastSeparatorIndex = normalizedValue.lastIndexOf(PATH_SEPARATOR);
49
+ if (lastSeparatorIndex <= 0) return lastSeparatorIndex === 0 ? PATH_SEPARATOR : ".";
50
+ return normalizedValue.slice(0, lastSeparatorIndex);
51
+ };
52
+ const basenamePath = (value) => {
53
+ const normalizedValue = normalizePath(value).replace(/\/+$/g, "");
54
+ const lastSeparatorIndex = normalizedValue.lastIndexOf(PATH_SEPARATOR);
55
+ return lastSeparatorIndex >= 0 ? normalizedValue.slice(lastSeparatorIndex + 1) : normalizedValue;
56
+ };
57
+ const resolveOutputPath = (workspaceDir, outFile) => isAbsolutePath(outFile) ? outFile : joinPath(workspaceDir, outFile);
58
+ const ensureOutputDirectory = (outputPath) => {
59
+ const outputDir = dirnamePath(outputPath);
60
+ if (!node_fs.default.existsSync(outputDir)) node_fs.default.mkdirSync(outputDir, { recursive: true });
61
+ };
21
62
  const SOURCE_FILE_PATTERN = /\.(jsx?|tsx?)$/i;
22
- const getWatchDirs = (dirs) => [...new Set(dirs.filter((dir) => !dir.split(node_path.default.sep).includes("node_modules")))];
23
- const isCoveredByRecursiveWatch = (watchPath, targetPath) => targetPath === watchPath || targetPath.startsWith(`${watchPath}${node_path.default.sep}`);
63
+ const getWatchDirs = (dirs) => [...new Set(dirs.filter((dir) => !normalizePath(dir).split(PATH_SEPARATOR).includes("node_modules")))];
64
+ const isCoveredByRecursiveWatch = (watchPath, targetPath) => targetPath === watchPath || targetPath.startsWith(`${watchPath}${PATH_SEPARATOR}`);
24
65
  const getEntryWatchTargets = (entries) => {
25
66
  const directoryTargets = getWatchDirs(entries.filter((entry) => entry.kind === "directory").map((entry) => entry.absolutePath)).map((watchPath) => ({
26
67
  watchPath,
@@ -44,7 +85,7 @@ const watchSourceFiles = (targets, onFileChange) => {
44
85
  targets.forEach((target) => {
45
86
  node_fs.default.watch(target.watchPath, { recursive: target.recursive }, (_eventType, filename) => {
46
87
  if (!filename || !SOURCE_FILE_PATTERN.test(filename)) return;
47
- if (target.fileNames && !target.fileNames.includes(node_path.default.basename(String(filename)))) return;
88
+ if (target.fileNames && !target.fileNames.includes(basenamePath(String(filename)))) return;
48
89
  onFileChange();
49
90
  });
50
91
  });
@@ -82,8 +123,13 @@ const createQueuedRegenerator = (options) => {
82
123
  };
83
124
  return regenerate;
84
125
  };
126
+ const printVerboseScanFiles = (log, workspaceDir, filePaths) => {
127
+ require_runner_helpers.getVerboseScanFileList(workspaceDir, filePaths).forEach((filePath) => {
128
+ log.listItem(filePath);
129
+ });
130
+ };
85
131
  const runCssCommand = async (options, context) => {
86
- if (!node_fs.default.existsSync(node_path.default.join(options.workspaceDir, String(options.themeConfigPath)))) {
132
+ if (!node_fs.default.existsSync(joinPath(options.workspaceDir, String(options.themeConfigPath)))) {
87
133
  await runSimpleMode(options, context);
88
134
  return;
89
135
  }
@@ -104,7 +150,8 @@ const runSimpleMode = async (options, context) => {
104
150
  scope: options.scope,
105
151
  configPath,
106
152
  isWatch: options.watch,
107
- silent: options.silent
153
+ silent: options.silent,
154
+ verbose: options.verbose
108
155
  });
109
156
  if (options.watch) await runSimpleModeWatch(options, context, entry, configPath, result.packageDirs ?? []);
110
157
  } catch (error) {
@@ -184,7 +231,7 @@ const loadThemeModeSetup = async (options) => {
184
231
  };
185
232
  const runThemeMode = async (options, context) => {
186
233
  const workspaceDir = options.workspaceDir;
187
- const outputPath = node_path.default.isAbsolute(options.outFile) ? options.outFile : node_path.default.join(workspaceDir, String(options.outFile));
234
+ const outputPath = resolveOutputPath(workspaceDir, String(options.outFile));
188
235
  let effectiveSilent = options.silent;
189
236
  let log = require_logger.createLogger({ silent: effectiveSilent });
190
237
  log.spinStart("Loading theme configuration...");
@@ -205,12 +252,21 @@ const runThemeMode = async (options, context) => {
205
252
  const generateThemeModeCSS = async (opts) => {
206
253
  const genStartTime = performance.now();
207
254
  const genLog = opts?.isWatch ? require_logger.createLogger({ silent: true }) : log;
255
+ const scopedCssOutputs = [];
208
256
  genLog.spinStart("Scanning app code...");
209
257
  const appScanResult = await require_nodeUtils.scanDirectoriesForSafelist(entryDirs, colorModes, context.variants, context.autoVariants, context.componentData, appVariantDefaults, runtimeConfigValues);
210
- genLog.spinStop("✅", `Scanned ${appScanResult.filesScanned} files`);
258
+ genLog.spinStop("✅", require_runner_helpers.getScanSummaryMessage({
259
+ label: "app",
260
+ filesScanned: appScanResult.filesScanned,
261
+ filesWithComponents: appScanResult.filesWithComponents,
262
+ componentCount: appScanResult.components.length,
263
+ mode: "app"
264
+ }));
265
+ if (options.verbose) printVerboseScanFiles(genLog, workspaceDir, appScanResult.filePaths);
211
266
  genLog.spinStart("Generating main CSS...");
212
267
  const inheritedClasses = [...appScanResult.safelist];
213
268
  const inheritedComponents = new Set(appScanResult.components);
269
+ const scopedPackageTargets = [];
214
270
  const processInheritedPackage = async (packageName) => {
215
271
  genLog.spinStart(`Processing package: ${packageName}...`);
216
272
  const packageDir = require_nodeUtils.findPackageSourceDir(packageName);
@@ -222,12 +278,50 @@ const runThemeMode = async (options, context) => {
222
278
  const packageScanResult = await require_nodeUtils.scanDirectoryForSafelist(packageDir, colorModes, context.variants, context.autoVariants, context.componentData, appVariantDefaults, runtimeConfigValues, true);
223
279
  inheritedClasses.push(...packageScanResult.safelist);
224
280
  packageScanResult.components.forEach((comp) => inheritedComponents.add(comp));
225
- genLog.spinStop("✅", `${packageName}: ${packageScanResult.filesScanned} files (inherit)`);
281
+ genLog.spinStop("✅", require_runner_helpers.getScanSummaryMessage({
282
+ label: packageName,
283
+ filesScanned: packageScanResult.filesScanned,
284
+ filesWithComponents: packageScanResult.filesWithComponents,
285
+ componentCount: packageScanResult.components.length,
286
+ mode: "inherit"
287
+ }));
288
+ if (options.verbose) printVerboseScanFiles(genLog, workspaceDir, packageScanResult.filePaths);
289
+ };
290
+ const processScopedPackage = async (packageName, scopedPackageValue) => {
291
+ genLog.spinStart(`Processing scoped package: ${packageName}...`);
292
+ const scopedPackageConfig = normalizeScopedPackageConfig(scopedPackageValue);
293
+ const packageRoot = require_nodeUtils.findPackageRoot(packageName);
294
+ const packageDir = require_nodeUtils.findPackageSourceDir(packageName);
295
+ if (!packageRoot || !packageDir) {
296
+ genLog.spinStop("⚠️", `Scoped package not found: ${packageName}`);
297
+ return;
298
+ }
299
+ const scopeClass = require_nodeUtils.getPackageUdsScope(packageName);
300
+ if (!scopeClass) {
301
+ genLog.spinStop("⚠️", `Scoped package missing package.json uds.scope: ${packageName}`);
302
+ return;
303
+ }
304
+ const entryDirs = resolveScopedEntryDirs(packageRoot, packageDir, scopedPackageConfig.entry);
305
+ entryDirs.forEach((entryDir) => {
306
+ if (!packageDirs.includes(entryDir)) packageDirs.push(entryDir);
307
+ });
308
+ scopedPackageTargets.push({
309
+ packageName,
310
+ packageRoot,
311
+ packageDir,
312
+ entryDirs,
313
+ scopeClass,
314
+ config: scopedPackageConfig
315
+ });
226
316
  };
227
317
  await (themeConfig.inherit ?? []).reduce(async (promise, packageName) => {
228
318
  await promise;
229
319
  await processInheritedPackage(packageName);
230
320
  }, Promise.resolve());
321
+ await Object.entries(themeConfig.scoped ?? {}).reduce(async (promise, [packageName, scopedPackageConfig]) => {
322
+ await promise;
323
+ await processScopedPackage(packageName, scopedPackageConfig);
324
+ }, Promise.resolve());
231
325
  const mainSafelist = require_safelist.deduplicateSafelist([
232
326
  ...inheritedClasses,
233
327
  ...require_safelist.getThemeAndScaleClasses(colorModes),
@@ -244,24 +338,80 @@ const runThemeMode = async (options, context) => {
244
338
  ...require_generate_helpers.getPruneVarSafelist(themeConfig.css)
245
339
  ]
246
340
  });
247
- const outputDir = node_path.default.dirname(outputPath);
248
- if (!node_fs.default.existsSync(outputDir)) node_fs.default.mkdirSync(outputDir, { recursive: true });
341
+ ensureOutputDirectory(outputPath);
249
342
  node_fs.default.writeFileSync(outputPath, mainCssResult.css);
250
- genLog.spinStop("✅", require_runner_helpers.getMainCssSummaryMessage({
343
+ for (const scopedPackageTarget of scopedPackageTargets) {
344
+ const packageConfig = await require_nodeUtils.loadConfigFile(isAbsolutePath(scopedPackageTarget.config.config) ? scopedPackageTarget.config.config : joinPath(scopedPackageTarget.packageRoot, scopedPackageTarget.config.config)) ?? require_index.defaultTokensConfig;
345
+ const packageVariantDefaults = require_utils.extractVariantDefaults(packageConfig);
346
+ const packageRuntimeConfigValues = require_utils.extractRuntimeConfigValues(packageConfig);
347
+ const packageScanResult = await require_nodeUtils.scanDirectoriesForSafelist(scopedPackageTarget.entryDirs, colorModes, context.variants, context.autoVariants, context.componentData, packageVariantDefaults, packageRuntimeConfigValues, true);
348
+ genLog.spinStop("✅", require_runner_helpers.getScanSummaryMessage({
349
+ label: scopedPackageTarget.packageName,
350
+ filesScanned: packageScanResult.filesScanned,
351
+ filesWithComponents: packageScanResult.filesWithComponents,
352
+ componentCount: packageScanResult.components.length,
353
+ mode: "scoped"
354
+ }));
355
+ if (options.verbose) printVerboseScanFiles(genLog, workspaceDir, packageScanResult.filePaths);
356
+ const packageSafelist = require_safelist.deduplicateSafelist([
357
+ ...packageScanResult.safelist,
358
+ ...require_safelist.getThemeAndScaleClasses(colorModes),
359
+ ...require_safelist.getInternalSafelistClasses()
360
+ ]);
361
+ const scopedCssResult = await require_generate.generateCSS([...themeConfig.css?.safelist ?? [], ...packageSafelist], packageConfig, {
362
+ scope: scopedPackageTarget.scopeClass,
363
+ contentDir: scopedPackageTarget.entryDirs,
364
+ cssOptions: themeConfig.css,
365
+ referenceCss: themeConfig.css?.optimization?.deduplicateScopedCss === false ? void 0 : mainCssResult.css,
366
+ safeVarPrefixes: [
367
+ ...require_utils.getMotionVarPrefixes(context.componentData, [...packageScanResult.components]),
368
+ ...require_utils.getConfigurableCssVariables(),
369
+ ...require_generate_helpers.getPruneVarSafelist(themeConfig.css)
370
+ ]
371
+ });
372
+ const scopedOutputPath = resolveOutputPath(workspaceDir, getScopedPackageOutputPath(scopedPackageTarget.packageName, scopedPackageTarget.config.outFile));
373
+ ensureOutputDirectory(scopedOutputPath);
374
+ node_fs.default.writeFileSync(scopedOutputPath, scopedCssResult.css);
375
+ scopedCssOutputs.push({
376
+ label: scopedPackageTarget.packageName,
377
+ outputPath: scopedOutputPath,
378
+ sizeGzipBytes: scopedCssResult.sizeGzipBytes,
379
+ optimizationStats: scopedCssResult.optimizationStats
380
+ });
381
+ }
382
+ const mainCssSummary = require_runner_helpers.getMainCssSummaryMessage({
251
383
  sizeGzipBytes: mainCssResult.sizeGzipBytes,
252
384
  optimizationStats: mainCssResult.optimizationStats,
253
385
  formatBytes: require_utils.formatBytes
254
- }));
386
+ });
387
+ genLog.spinStop("✅", mainCssSummary.summaryLine);
388
+ mainCssSummary.detailLines.forEach((detailLine) => {
389
+ genLog.print(detailLine);
390
+ });
391
+ scopedCssOutputs.forEach((scopedCssOutput) => {
392
+ const scopedCssSummary = require_runner_helpers.getScopedCssSummaryMessage(scopedCssOutput.label, {
393
+ sizeGzipBytes: scopedCssOutput.sizeGzipBytes,
394
+ optimizationStats: scopedCssOutput.optimizationStats,
395
+ formatBytes: require_utils.formatBytes
396
+ });
397
+ genLog.spinStop("✅", scopedCssSummary.summaryLine);
398
+ scopedCssSummary.detailLines.forEach((detailLine) => {
399
+ genLog.print(detailLine);
400
+ });
401
+ });
255
402
  const duration = Math.round(performance.now() - genStartTime);
403
+ const outputFileSection = require_runner_helpers.getOutputFileSection(workspaceDir, [outputPath, ...scopedCssOutputs.map((scopedCssOutput) => scopedCssOutput.outputPath)]);
256
404
  genLog.newline();
257
- genLog.print(require_colors.green("CSS generation complete!"));
258
- genLog.newline();
259
- genLog.label("Output file:", outputPath);
405
+ genLog.print(outputFileSection.label);
406
+ outputFileSection.paths.forEach((filePath) => {
407
+ genLog.listItem(filePath);
408
+ });
260
409
  genLog.newline();
261
- genLog.print(`${require_colors.magenta("Total time:")} ${duration}ms`);
410
+ genLog.print(`${require_colors.magenta("Total time:")} ${require_runner_helpers.formatCssDuration(duration)}`);
262
411
  return {
263
412
  duration,
264
413
  outputPath,
414
+ outputPaths: outputFileSection.paths,
265
415
  packageDirs
266
416
  };
267
417
  };
@@ -292,7 +442,8 @@ const runThemeMode = async (options, context) => {
292
442
  onSuccess: (result) => {
293
443
  if (!effectiveSilent) {
294
444
  const updatedAt = (/* @__PURE__ */ new Date()).toLocaleTimeString();
295
- log.print(`${require_colors.gray(`[${updatedAt}]`)} ${require_colors.green(`CSS updated (${result?.duration}ms)`)}`);
445
+ const updatedMessage = result?.duration == null ? "CSS updated" : `CSS updated (${require_runner_helpers.formatCssDuration(result.duration)})`;
446
+ log.print(`${require_colors.gray(`[${updatedAt}]`)} ${require_colors.green(updatedMessage)}`);
296
447
  log.newline();
297
448
  }
298
449
  },
@@ -4,13 +4,60 @@ let node_path = require("node:path");
4
4
  node_path = require_runtime.__toESM(node_path);
5
5
 
6
6
  //#region src/css/runner.helpers.ts
7
- const getMainCssSummaryMessage = (options) => {
8
- const { optimizationStats, sizeGzipBytes, formatBytes } = options;
9
- if (!optimizationStats) return `Main CSS: ${formatBytes(sizeGzipBytes)} gzip`;
7
+ const getDisplayPath = (cwd, targetPath) => {
8
+ const relativePath = node_path.default.relative(cwd, targetPath);
9
+ return relativePath.length === 0 ? "." : relativePath;
10
+ };
11
+ const getVerboseScanFileList = (cwd, filePaths) => [...new Set(filePaths.map((filePath) => getDisplayPath(cwd, filePath)))].sort((left, right) => left.localeCompare(right));
12
+ const getOptimizationDetailLines = (optimizationStats) => {
13
+ const detailLines = [];
14
+ if (optimizationStats.fontFacesRemoved > 0) detailLines.push(` • removed ${optimizationStats.fontFacesRemoved} unused @font-face`);
15
+ if (optimizationStats.emptyRulesRemoved > 0) detailLines.push(` • removed ${optimizationStats.emptyRulesRemoved} empty rules`);
16
+ return detailLines;
17
+ };
18
+ const formatCssDuration = (durationMs) => {
19
+ return `${(Math.floor(durationMs / 100) / 10).toFixed(1)}s`;
20
+ };
21
+ const getCssSummaryMessage = (options) => {
22
+ const { label, optimizationStats, sizeGzipBytes, formatBytes } = options;
23
+ const summaryLine = `${label}: ${formatBytes(sizeGzipBytes)} gzip`;
24
+ if (!optimizationStats) return {
25
+ summaryLine,
26
+ detailLines: []
27
+ };
10
28
  const { originalSizeGzip, fontFacesRemoved, emptyRulesRemoved } = optimizationStats;
11
- const savedBytesGzip = originalSizeGzip - sizeGzipBytes;
12
- if (savedBytesGzip <= 0) return `Main CSS: ${formatBytes(sizeGzipBytes)} gzip`;
13
- return `Main CSS: ${formatBytes(sizeGzipBytes)} gzip)(optimized: saved ${formatBytes(savedBytesGzip)} gzip, ${fontFacesRemoved} unused @font-face, ${emptyRulesRemoved} empty rules removed)`;
29
+ if (originalSizeGzip - sizeGzipBytes <= 0) return {
30
+ summaryLine,
31
+ detailLines: []
32
+ };
33
+ return {
34
+ summaryLine,
35
+ detailLines: getOptimizationDetailLines({
36
+ originalSizeGzip,
37
+ fontFacesRemoved,
38
+ emptyRulesRemoved
39
+ })
40
+ };
41
+ };
42
+ const getMainCssSummaryMessage = (options) => {
43
+ return getCssSummaryMessage({
44
+ label: "Main CSS",
45
+ ...options
46
+ });
47
+ };
48
+ const getScopedCssSummaryMessage = (packageName, options) => getCssSummaryMessage({
49
+ label: `${packageName} CSS`,
50
+ ...options
51
+ });
52
+ const getOutputFileSection = (cwd, outputPaths) => ({
53
+ label: "Output files:",
54
+ paths: outputPaths.map((outputPath) => getDisplayPath(cwd, outputPath))
55
+ });
56
+ const getScanSummaryMessage = (options) => {
57
+ const nonComponentFileCount = Math.max(options.filesScanned - options.filesWithComponents, 0);
58
+ const fileLabel = nonComponentFileCount === 1 ? "file" : "files";
59
+ const componentLabel = options.componentCount === 1 ? "component" : "components";
60
+ return `${options.label}: ${nonComponentFileCount} ${fileLabel}, ${options.componentCount} ${componentLabel} (${options.mode})`;
14
61
  };
15
62
  const getWatchDirectoryGroups = (entryDirs, dirs) => {
16
63
  const watchDirs = [...new Set(dirs.filter((dir) => !dir.split(node_path.default.sep).includes("node_modules")))];
@@ -21,5 +68,10 @@ const getWatchDirectoryGroups = (entryDirs, dirs) => {
21
68
  };
22
69
 
23
70
  //#endregion
71
+ exports.formatCssDuration = formatCssDuration;
24
72
  exports.getMainCssSummaryMessage = getMainCssSummaryMessage;
73
+ exports.getOutputFileSection = getOutputFileSection;
74
+ exports.getScanSummaryMessage = getScanSummaryMessage;
75
+ exports.getScopedCssSummaryMessage = getScopedCssSummaryMessage;
76
+ exports.getVerboseScanFileList = getVerboseScanFileList;
25
77
  exports.getWatchDirectoryGroups = getWatchDirectoryGroups;