@yahoo/uds 3.162.0 → 3.162.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/cli/commands/purge.cjs +8 -1
  2. package/dist/cli/commands/purge.helpers.cjs +7 -0
  3. package/dist/cli/commands/purge.helpers.d.cts +3 -0
  4. package/dist/cli/commands/purge.helpers.d.ts +3 -0
  5. package/dist/cli/commands/purge.helpers.js +7 -0
  6. package/dist/cli/commands/purge.js +8 -1
  7. package/dist/components/client/Menu/Menu.ItemCheckbox.d.cts +1 -1
  8. package/dist/components/client/Menu/Menu.ItemCheckbox.d.ts +1 -1
  9. package/dist/components/client/Popover/UDSPopoverConfigProvider.d.cts +1 -1
  10. package/dist/components/client/Popover/UDSPopoverConfigProvider.d.ts +1 -1
  11. package/dist/components/client/Switch/Switch.cjs +5 -2
  12. package/dist/components/client/Switch/Switch.js +5 -2
  13. package/dist/components/client/Switch/switchStyles.cjs +2 -2
  14. package/dist/components/client/Switch/switchStyles.js +2 -2
  15. package/dist/css/dist/commands/css.cjs +2 -0
  16. package/dist/css/dist/commands/css.helpers.cjs +2 -0
  17. package/dist/css/dist/commands/css.helpers.js +2 -0
  18. package/dist/css/dist/commands/css.js +2 -0
  19. package/dist/css/dist/css/nodeUtils.cjs +28 -11
  20. package/dist/css/dist/css/nodeUtils.js +29 -12
  21. package/dist/css/dist/css/runner.cjs +8 -3
  22. package/dist/css/dist/css/runner.js +8 -3
  23. package/dist/css/dist/css/theme.d.cts +16 -0
  24. package/dist/css/dist/css/theme.d.ts +16 -0
  25. package/dist/css/dist/css/workerPool.cjs +2 -1
  26. package/dist/css/dist/css/workerPool.js +2 -1
  27. package/dist/css/dist/purger/optimized/ast/expressions.cjs +104 -11
  28. package/dist/css/dist/purger/optimized/ast/expressions.js +104 -11
  29. package/dist/css/dist/purger/optimized/ast/jsx.cjs +74 -0
  30. package/dist/css/dist/purger/optimized/ast/jsx.js +73 -0
  31. package/dist/css/dist/purger/optimized/ast/props.cjs +47 -0
  32. package/dist/css/dist/purger/optimized/ast/props.js +46 -0
  33. package/dist/css/dist/purger/optimized/ast/spread.cjs +87 -0
  34. package/dist/css/dist/purger/optimized/ast/spread.js +86 -0
  35. package/dist/css/dist/purger/optimized/purge.cjs +14 -8
  36. package/dist/css/dist/purger/optimized/purge.js +15 -9
  37. package/dist/css/dist/purger/optimized/purgeFromCode.cjs +176 -18
  38. package/dist/css/dist/purger/optimized/purgeFromCode.js +176 -19
  39. package/dist/css/dist/purger/optimized/utils/files.cjs +22 -2
  40. package/dist/css/dist/purger/optimized/utils/files.js +21 -3
  41. package/dist/hooks/useRtl.cjs +4 -8
  42. package/dist/hooks/useRtl.js +4 -8
  43. package/dist/styles/styler.d.cts +25 -25
  44. package/dist/styles/styler.d.ts +25 -25
  45. package/dist/uds/generated/componentData.cjs +2555 -2553
  46. package/dist/uds/generated/componentData.js +2555 -2553
  47. package/dist/uds/generated/tailwindPurge.cjs +1 -1
  48. package/dist/uds/generated/tailwindPurge.js +1 -1
  49. package/generated/componentData.json +3340 -3338
  50. package/generated/tailwindPurge.ts +1 -1
  51. package/package.json +1 -1
@@ -17,6 +17,9 @@ const showHelp = () => {
17
17
  require_print.print(`${require_colors.magenta("Options:")}`);
18
18
  require_print.print(` ${require_colors.cyan("--entry <path>")} Entry directory or file to scan (default: ./src)`);
19
19
  require_print.print(` ${require_colors.cyan("--output <path>")} Output safelist file (default: dist/safelist.ts)`);
20
+ require_print.print(` ${require_colors.cyan("--ignore <glob>")} Extra glob(s) to exclude from the scan (comma-separated)`);
21
+ require_print.print(` ${require_colors.cyan("--include-dev-files")} Also scan test/spec/story files (excluded by default)`);
22
+ require_print.print(` ${require_colors.cyan("--include-stories")} Also scan story files, keeping test/spec excluded`);
20
23
  require_print.print(` ${require_colors.cyan("--config <path>")} Path to uds.config.ts for legacy purge mode`);
21
24
  require_print.print(` ${require_colors.cyan("--optimized")} Use the optimized AST-based purger implementation`);
22
25
  require_print.print(` ${require_colors.cyan("--help, -h")} Show this help message`);
@@ -36,7 +39,7 @@ const makePurgeCommand = (getContext) => ({
36
39
  showHelp();
37
40
  return;
38
41
  }
39
- const { config, entry, optimized, output } = require_cli_commands_purge_helpers.getPurgeRunOptions(props.options);
42
+ const { config, entry, ignore, ignoreDevFiles, includeStories, optimized, output } = require_cli_commands_purge_helpers.getPurgeRunOptions(props.options);
40
43
  const { variants, autoVariants, componentData, packageJson, generatedPurgeCssData } = await getContext();
41
44
  if (optimized) {
42
45
  require_spinner.spinStart("Purging CSS with optimized purger...");
@@ -44,6 +47,9 @@ const makePurgeCommand = (getContext) => ({
44
47
  const result = await require_purge.purgeOptimized({
45
48
  entry,
46
49
  output,
50
+ ignore,
51
+ ignoreDevFiles,
52
+ includeStories,
47
53
  variants,
48
54
  autoVariants,
49
55
  componentData
@@ -51,6 +57,7 @@ const makePurgeCommand = (getContext) => ({
51
57
  require_spinner.spinStop("✅", `Purging done (optimized)! Generated ${result.stats.classesGenerated} classes in ${result.stats.timeMs}ms`);
52
58
  require_print.print(` Files scanned: ${result.stats.filesScanned}`);
53
59
  require_print.print(` Spreads traced: ${result.stats.spreadsTraced}`);
60
+ require_print.print(` Spreads unresolved: ${result.stats.spreadsUnresolved}`);
54
61
  require_print.print(` Expressions resolved: ${result.stats.expressionsResolved}`);
55
62
  return;
56
63
  } catch (error) {
@@ -7,11 +7,18 @@ const entryOption = (value) => {
7
7
  if (typeof value === "string") return value;
8
8
  if (Array.isArray(value) && value.every((item) => typeof item === "string")) return value;
9
9
  };
10
+ const ignoreOption = (value) => {
11
+ if (typeof value === "string") return value.split(",").map((glob) => glob.trim()).filter(Boolean);
12
+ if (Array.isArray(value) && value.every((item) => typeof item === "string")) return value;
13
+ };
10
14
  const shouldShowHelp = (options) => isTruthyFlag(options.help) || isTruthyFlag(options.h);
11
15
  const isOptimizedPurgeEnabled = (options) => isTruthyFlag(options.optimized);
12
16
  const getPurgeRunOptions = (options) => ({
13
17
  config: stringOption(options.config),
14
18
  entry: entryOption(options.entry),
19
+ ignore: ignoreOption(options.ignore),
20
+ ignoreDevFiles: !(isTruthyFlag(options["include-dev-files"]) || isTruthyFlag(options.includeDevFiles)),
21
+ includeStories: isTruthyFlag(options["include-stories"]) || isTruthyFlag(options.includeStories),
15
22
  optimized: isOptimizedPurgeEnabled(options),
16
23
  output: stringOption(options.output)
17
24
  });
@@ -4,6 +4,9 @@ type PurgeCliOptions = Record<string, unknown>;
4
4
  type PurgeRunOptions = {
5
5
  config?: string;
6
6
  entry?: string | string[];
7
+ ignore?: string[];
8
+ ignoreDevFiles: boolean;
9
+ includeStories: boolean;
7
10
  optimized: boolean;
8
11
  output?: string;
9
12
  };
@@ -4,6 +4,9 @@ type PurgeCliOptions = Record<string, unknown>;
4
4
  type PurgeRunOptions = {
5
5
  config?: string;
6
6
  entry?: string | string[];
7
+ ignore?: string[];
8
+ ignoreDevFiles: boolean;
9
+ includeStories: boolean;
7
10
  optimized: boolean;
8
11
  output?: string;
9
12
  };
@@ -6,11 +6,18 @@ const entryOption = (value) => {
6
6
  if (typeof value === "string") return value;
7
7
  if (Array.isArray(value) && value.every((item) => typeof item === "string")) return value;
8
8
  };
9
+ const ignoreOption = (value) => {
10
+ if (typeof value === "string") return value.split(",").map((glob) => glob.trim()).filter(Boolean);
11
+ if (Array.isArray(value) && value.every((item) => typeof item === "string")) return value;
12
+ };
9
13
  const shouldShowHelp = (options) => isTruthyFlag(options.help) || isTruthyFlag(options.h);
10
14
  const isOptimizedPurgeEnabled = (options) => isTruthyFlag(options.optimized);
11
15
  const getPurgeRunOptions = (options) => ({
12
16
  config: stringOption(options.config),
13
17
  entry: entryOption(options.entry),
18
+ ignore: ignoreOption(options.ignore),
19
+ ignoreDevFiles: !(isTruthyFlag(options["include-dev-files"]) || isTruthyFlag(options.includeDevFiles)),
20
+ includeStories: isTruthyFlag(options["include-stories"]) || isTruthyFlag(options.includeStories),
14
21
  optimized: isOptimizedPurgeEnabled(options),
15
22
  output: stringOption(options.output)
16
23
  });
@@ -16,6 +16,9 @@ const showHelp = () => {
16
16
  print(`${magenta("Options:")}`);
17
17
  print(` ${cyan("--entry <path>")} Entry directory or file to scan (default: ./src)`);
18
18
  print(` ${cyan("--output <path>")} Output safelist file (default: dist/safelist.ts)`);
19
+ print(` ${cyan("--ignore <glob>")} Extra glob(s) to exclude from the scan (comma-separated)`);
20
+ print(` ${cyan("--include-dev-files")} Also scan test/spec/story files (excluded by default)`);
21
+ print(` ${cyan("--include-stories")} Also scan story files, keeping test/spec excluded`);
19
22
  print(` ${cyan("--config <path>")} Path to uds.config.ts for legacy purge mode`);
20
23
  print(` ${cyan("--optimized")} Use the optimized AST-based purger implementation`);
21
24
  print(` ${cyan("--help, -h")} Show this help message`);
@@ -35,7 +38,7 @@ const makePurgeCommand = (getContext) => ({
35
38
  showHelp();
36
39
  return;
37
40
  }
38
- const { config, entry, optimized, output } = getPurgeRunOptions(props.options);
41
+ const { config, entry, ignore, ignoreDevFiles, includeStories, optimized, output } = getPurgeRunOptions(props.options);
39
42
  const { variants, autoVariants, componentData, packageJson, generatedPurgeCssData } = await getContext();
40
43
  if (optimized) {
41
44
  spinStart("Purging CSS with optimized purger...");
@@ -43,6 +46,9 @@ const makePurgeCommand = (getContext) => ({
43
46
  const result = await purgeOptimized({
44
47
  entry,
45
48
  output,
49
+ ignore,
50
+ ignoreDevFiles,
51
+ includeStories,
46
52
  variants,
47
53
  autoVariants,
48
54
  componentData
@@ -50,6 +56,7 @@ const makePurgeCommand = (getContext) => ({
50
56
  spinStop("✅", `Purging done (optimized)! Generated ${result.stats.classesGenerated} classes in ${result.stats.timeMs}ms`);
51
57
  print(` Files scanned: ${result.stats.filesScanned}`);
52
58
  print(` Spreads traced: ${result.stats.spreadsTraced}`);
59
+ print(` Spreads unresolved: ${result.stats.spreadsUnresolved}`);
53
60
  print(` Expressions resolved: ${result.stats.expressionsResolved}`);
54
61
  return;
55
62
  } catch (error) {
@@ -131,6 +131,6 @@ interface MenuItemCheckboxProps extends Omit<PressableProps, 'asChild'>, HtmlBut
131
131
  *
132
132
  * @related [Menu](https://uds.build/docs/components/menu), [Menu.Item](https://uds.build/docs/components/menu-item)
133
133
  **/
134
- declare const MenuItemCheckbox: _$react.ForwardRefExoticComponent<Omit<MenuItemCheckboxProps, "active" | "rootProps" | "hideEndIcon"> & _$react.RefAttributes<HTMLDivElement>>;
134
+ declare const MenuItemCheckbox: _$react.ForwardRefExoticComponent<Omit<MenuItemCheckboxProps, "active" | "hideEndIcon" | "rootProps"> & _$react.RefAttributes<HTMLDivElement>>;
135
135
  //#endregion
136
136
  export { MenuItemCheckbox, type MenuItemCheckboxProps };
@@ -132,6 +132,6 @@ interface MenuItemCheckboxProps extends Omit<PressableProps, 'asChild'>, HtmlBut
132
132
  *
133
133
  * @related [Menu](https://uds.build/docs/components/menu), [Menu.Item](https://uds.build/docs/components/menu-item)
134
134
  **/
135
- declare const MenuItemCheckbox: _$react.ForwardRefExoticComponent<Omit<MenuItemCheckboxProps, "active" | "rootProps" | "hideEndIcon"> & _$react.RefAttributes<HTMLDivElement>>;
135
+ declare const MenuItemCheckbox: _$react.ForwardRefExoticComponent<Omit<MenuItemCheckboxProps, "active" | "hideEndIcon" | "rootProps"> & _$react.RefAttributes<HTMLDivElement>>;
136
136
  //#endregion
137
137
  export { MenuItemCheckbox, type MenuItemCheckboxProps };
@@ -29,7 +29,7 @@ declare function usePopoverConfig(sizeProp?: PopoverSize | undefined, variantPro
29
29
  gap: SpacingAlias;
30
30
  dismissButtonSpacing: SpacingAlias;
31
31
  dismissIconSize: number;
32
- ariaKitPlacement: ("top-start" | "top-end" | "bottom-start" | "bottom-end" | ("top" | "bottom" | "left" | "right") | "left-start" | "right-start" | "left-end" | "right-end") | undefined;
32
+ ariaKitPlacement: ("bottom-end" | "bottom-start" | "top-end" | "top-start" | ("bottom" | "left" | "right" | "top") | "left-start" | "right-start" | "left-end" | "right-end") | undefined;
33
33
  animationDuration: number;
34
34
  };
35
35
  //#endregion
@@ -30,7 +30,7 @@ declare function usePopoverConfig(sizeProp?: PopoverSize | undefined, variantPro
30
30
  gap: SpacingAlias;
31
31
  dismissButtonSpacing: SpacingAlias;
32
32
  dismissIconSize: number;
33
- ariaKitPlacement: ("top-start" | "top-end" | "bottom-start" | "bottom-end" | ("top" | "bottom" | "left" | "right") | "left-start" | "right-start" | "left-end" | "right-end") | undefined;
33
+ ariaKitPlacement: ("bottom-end" | "bottom-start" | "top-end" | "top-start" | ("bottom" | "left" | "right" | "top") | "left-start" | "right-start" | "left-end" | "right-end") | undefined;
34
34
  animationDuration: number;
35
35
  };
36
36
  //#endregion
@@ -7,6 +7,7 @@ const require_components_IconSlot = require("../../IconSlot.cjs");
7
7
  const require_components_Box = require("../../Box.cjs");
8
8
  const require_hooks_usePrefersReducedMotion = require("../../../hooks/usePrefersReducedMotion.cjs");
9
9
  const require_components_client_SpringMotionConfig = require("../SpringMotionConfig.cjs");
10
+ const require_hooks_useRtl = require("../../../hooks/useRtl.cjs");
10
11
  const require_components_client_Switch_SwitchBase = require("./SwitchBase.cjs");
11
12
  let react = require("react");
12
13
  react = require_runtime.__toESM(react);
@@ -46,6 +47,7 @@ const toggleTransition = {
46
47
  **/
47
48
  const Switch = (0, react.forwardRef)(function Switch({ reduceMotion: forceReduceMotion, ...props }, parentRef) {
48
49
  const prefersReducedMotion = require_hooks_usePrefersReducedMotion.usePrefersReducedMotion();
50
+ const isRtl = require_hooks_useRtl.useRtl();
49
51
  const reduceMotion = forceReduceMotion ? "always" : "user";
50
52
  const cssAnimationDuration = prefersReducedMotion || forceReduceMotion ? "duration-0" : "duration-120";
51
53
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_client_Switch_SwitchBase.SwitchBase, {
@@ -54,14 +56,15 @@ const Switch = (0, react.forwardRef)(function Switch({ reduceMotion: forceReduce
54
56
  cssAnimationDuration,
55
57
  enableHoverTracking: true,
56
58
  renderHandle: ({ size, isOn, isOnX, showHoverEffect, disabled, onIcon, offIcon, cssAnimationDuration, handleSlotProps }) => {
59
+ const onX = isRtl ? `-${isOnX}` : isOnX;
57
60
  const motionInitialAndAnimate = {
58
61
  initial: {
59
62
  scale: 1,
60
- x: isOn ? isOnX : 0
63
+ x: isOn ? onX : 0
61
64
  },
62
65
  animate: {
63
66
  scale: showHoverEffect ? 1.05 : 1,
64
- x: isOn ? isOnX : 0
67
+ x: isOn ? onX : 0
65
68
  }
66
69
  };
67
70
  const classNames = {
@@ -5,6 +5,7 @@ import { IconSlot } from "../../IconSlot.js";
5
5
  import { Box } from "../../Box.js";
6
6
  import { usePrefersReducedMotion } from "../../../hooks/usePrefersReducedMotion.js";
7
7
  import { SpringMotionConfig } from "../SpringMotionConfig.js";
8
+ import { useRtl } from "../../../hooks/useRtl.js";
8
9
  import { SwitchBase } from "./SwitchBase.js";
9
10
  import { forwardRef } from "react";
10
11
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -42,6 +43,7 @@ const toggleTransition = {
42
43
  **/
43
44
  const Switch = forwardRef(function Switch({ reduceMotion: forceReduceMotion, ...props }, parentRef) {
44
45
  const prefersReducedMotion = usePrefersReducedMotion();
46
+ const isRtl = useRtl();
45
47
  const reduceMotion = forceReduceMotion ? "always" : "user";
46
48
  const cssAnimationDuration = prefersReducedMotion || forceReduceMotion ? "duration-0" : "duration-120";
47
49
  return /* @__PURE__ */ jsx(SwitchBase, {
@@ -50,14 +52,15 @@ const Switch = forwardRef(function Switch({ reduceMotion: forceReduceMotion, ...
50
52
  cssAnimationDuration,
51
53
  enableHoverTracking: true,
52
54
  renderHandle: ({ size, isOn, isOnX, showHoverEffect, disabled, onIcon, offIcon, cssAnimationDuration, handleSlotProps }) => {
55
+ const onX = isRtl ? `-${isOnX}` : isOnX;
53
56
  const motionInitialAndAnimate = {
54
57
  initial: {
55
58
  scale: 1,
56
- x: isOn ? isOnX : 0
59
+ x: isOn ? onX : 0
57
60
  },
58
61
  animate: {
59
62
  scale: showHoverEffect ? 1.05 : 1,
60
- x: isOn ? isOnX : 0
63
+ x: isOn ? onX : 0
61
64
  }
62
65
  };
63
66
  const classNames = {
@@ -6,11 +6,11 @@ const require_styles_styler = require("../../../styles/styler.cjs");
6
6
  const SIZE_MAP = {
7
7
  md: {
8
8
  isOnX: "1.25rem",
9
- isOnTranslateClass: require_styles_styler.cx("translate-x-[1.25rem]")
9
+ isOnTranslateClass: require_styles_styler.cx("translate-x-[1.25rem]", "rtl:translate-x-[-1.25rem]")
10
10
  },
11
11
  sm: {
12
12
  isOnX: "0.75rem",
13
- isOnTranslateClass: require_styles_styler.cx("translate-x-[0.75rem]")
13
+ isOnTranslateClass: require_styles_styler.cx("translate-x-[0.75rem]", "rtl:translate-x-[-0.75rem]")
14
14
  }
15
15
  };
16
16
  //#endregion
@@ -5,11 +5,11 @@ import { cx } from "../../../styles/styler.js";
5
5
  const SIZE_MAP = {
6
6
  md: {
7
7
  isOnX: "1.25rem",
8
- isOnTranslateClass: cx("translate-x-[1.25rem]")
8
+ isOnTranslateClass: cx("translate-x-[1.25rem]", "rtl:translate-x-[-1.25rem]")
9
9
  },
10
10
  sm: {
11
11
  isOnX: "0.75rem",
12
- isOnTranslateClass: cx("translate-x-[0.75rem]")
12
+ isOnTranslateClass: cx("translate-x-[0.75rem]", "rtl:translate-x-[-0.75rem]")
13
13
  }
14
14
  };
15
15
  //#endregion
@@ -27,6 +27,8 @@ const showHelp = () => {
27
27
  require_print.print(` ${require_colors.cyan("--verbose, -v")} List every scanned file used for purging`);
28
28
  require_print.print(` ${require_colors.cyan("--workers <n>")} Number of parallel workers (default: auto = CPU count - 1)`);
29
29
  require_print.print(` ${require_colors.cyan("--emit <mode>")} Emission mode: exhaustive (default) | selective`);
30
+ require_print.print(` ${require_colors.cyan("--include-dev-files")} Also scan test/spec/story files (excluded by default)`);
31
+ require_print.print(` ${require_colors.cyan("--include-stories")} Also scan story files, keeping test/spec excluded`);
30
32
  require_print.print(` ${require_colors.cyan("--force")} Overwrite existing uds.theme.ts (for init)`);
31
33
  require_print.print(` ${require_colors.cyan("--help, -h")} Show this help message`);
32
34
  require_print.print("");
@@ -35,6 +35,8 @@ const getCssRunOptions = (workspaceDir, options) => {
35
35
  entryOption: entryOption(options.entry),
36
36
  configOption: stringOption(options.config),
37
37
  emit: parseEmitOption(options.emit),
38
+ includeDevFiles: isTruthyFlag(options["include-dev-files"]) || isTruthyFlag(options.includeDevFiles) ? true : void 0,
39
+ includeStories: isTruthyFlag(options["include-stories"]) || isTruthyFlag(options.includeStories) ? true : void 0,
38
40
  watch: isTruthyFlag(options.watch) || isTruthyFlag(options.w),
39
41
  silent: isTruthyFlag(options.silent) || isTruthyFlag(options.s),
40
42
  verbose: isTruthyFlag(options.verbose) || isTruthyFlag(options.v),
@@ -35,6 +35,8 @@ const getCssRunOptions = (workspaceDir, options) => {
35
35
  entryOption: entryOption(options.entry),
36
36
  configOption: stringOption(options.config),
37
37
  emit: parseEmitOption(options.emit),
38
+ includeDevFiles: isTruthyFlag(options["include-dev-files"]) || isTruthyFlag(options.includeDevFiles) ? true : void 0,
39
+ includeStories: isTruthyFlag(options["include-stories"]) || isTruthyFlag(options.includeStories) ? true : void 0,
38
40
  watch: isTruthyFlag(options.watch) || isTruthyFlag(options.w),
39
41
  silent: isTruthyFlag(options.silent) || isTruthyFlag(options.s),
40
42
  verbose: isTruthyFlag(options.verbose) || isTruthyFlag(options.v),
@@ -27,6 +27,8 @@ const showHelp = () => {
27
27
  print(` ${cyan("--verbose, -v")} List every scanned file used for purging`);
28
28
  print(` ${cyan("--workers <n>")} Number of parallel workers (default: auto = CPU count - 1)`);
29
29
  print(` ${cyan("--emit <mode>")} Emission mode: exhaustive (default) | selective`);
30
+ print(` ${cyan("--include-dev-files")} Also scan test/spec/story files (excluded by default)`);
31
+ print(` ${cyan("--include-stories")} Also scan story files, keeping test/spec excluded`);
30
32
  print(` ${cyan("--force")} Overwrite existing uds.theme.ts (for init)`);
31
33
  print(` ${cyan("--help, -h")} Show this help message`);
32
34
  print("");
@@ -5,13 +5,14 @@ const require_print = require("../packages/cli/dist/lib/print.cjs");
5
5
  const require_perf = require("./perf.cjs");
6
6
  const require_purgeFromCode = require("../purger/optimized/purgeFromCode.cjs");
7
7
  const require_entryPoints = require("../../../tailwind-internal/dist/utils/entryPoints.cjs");
8
+ const require_files = require("../purger/optimized/utils/files.cjs");
8
9
  let node_path = require("node:path");
9
10
  node_path = require_runtime.__toESM(node_path, 1);
10
11
  let node_fs = require("node:fs");
11
12
  node_fs = require_runtime.__toESM(node_fs, 1);
12
- let node_module = require("node:module");
13
13
  let fast_glob = require("fast-glob");
14
14
  fast_glob = require_runtime.__toESM(fast_glob, 1);
15
+ let node_module = require("node:module");
15
16
  //#region ../css/dist/css/nodeUtils.mjs
16
17
  /*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
17
18
  const loadConfigFile = async (configPath) => {
@@ -24,11 +25,15 @@ const loadConfigFile = async (configPath) => {
24
25
  throw new Error(`Failed to load config file: ${absolutePath}\n${error instanceof Error ? error.message : "Unknown error"}`);
25
26
  }
26
27
  };
27
- const scanDirectoryForSafelist = async (dir, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives = false, pool) => {
28
+ const scanDirectoryForSafelist = async (dir, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives = false, pool, extraIgnore = [], ignoreDevFiles = true) => {
28
29
  const files = await require_perf.measureAsync("scan:glob", () => (0, fast_glob.default)("**/*.{js,jsx,mjs,cjs,ts,tsx}", {
29
30
  cwd: dir,
30
31
  absolute: true,
31
- ignore: ["**/node_modules/**"]
32
+ ignore: [
33
+ "**/node_modules/**",
34
+ ...ignoreDevFiles ? require_files.DEV_FILE_IGNORE_GLOBS : [],
35
+ ...extraIgnore
36
+ ]
32
37
  }).then((f) => f.sort((left, right) => left.localeCompare(right))));
33
38
  const processFile = (filePath) => {
34
39
  const code = node_fs.default.readFileSync(filePath, "utf-8");
@@ -38,7 +43,8 @@ const scanDirectoryForSafelist = async (dir, colorModes, variants, autoVariants,
38
43
  colorModes,
39
44
  variantDefaults,
40
45
  runtimeConfigValues,
41
- includeAllClassNamePrimitives
46
+ includeAllClassNamePrimitives,
47
+ deferWrapperSpreadResolution: true
42
48
  });
43
49
  return require_purgeFromCode.purgeFromCodeOptimized(code, {
44
50
  colorModes,
@@ -48,6 +54,7 @@ const scanDirectoryForSafelist = async (dir, colorModes, variants, autoVariants,
48
54
  autoVariants,
49
55
  componentData,
50
56
  includeAllClassNamePrimitives,
57
+ deferWrapperSpreadResolution: true,
51
58
  filePath
52
59
  });
53
60
  };
@@ -60,7 +67,8 @@ const scanDirectoryForSafelist = async (dir, colorModes, variants, autoVariants,
60
67
  components: [...allComponents],
61
68
  filesScanned: files.length,
62
69
  filesWithComponents,
63
- filePaths: files
70
+ filePaths: files,
71
+ unresolvedWrapperSpreads: results.flatMap((result) => result.unresolvedWrapperSpreads ?? [])
64
72
  };
65
73
  };
66
74
  const scanFileForSafelist = async (filePath, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives = false) => {
@@ -71,27 +79,36 @@ const scanFileForSafelist = async (filePath, colorModes, variants, autoVariants,
71
79
  variants,
72
80
  autoVariants,
73
81
  componentData,
74
- includeAllClassNamePrimitives
82
+ includeAllClassNamePrimitives,
83
+ deferWrapperSpreadResolution: true
75
84
  });
76
85
  return {
77
86
  safelist: result.safelist,
78
87
  components: result.components,
79
88
  filesScanned: 1,
80
89
  filesWithComponents: result.components.length > 0 ? 1 : 0,
81
- filePaths: [filePath]
90
+ filePaths: [filePath],
91
+ unresolvedWrapperSpreads: result.unresolvedWrapperSpreads ?? []
82
92
  };
83
93
  };
84
- const scanDirectoriesForSafelist = async (dirs, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives = false, pool) => {
94
+ const scanDirectoriesForSafelist = async (dirs, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives = false, pool, extraIgnore = [], ignoreDevFiles = true) => {
85
95
  const results = await Promise.all(dirs.map((dir) => {
86
96
  if (node_fs.default.existsSync(dir) && node_fs.default.statSync(dir).isFile()) return scanFileForSafelist(dir, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives);
87
- return scanDirectoryForSafelist(dir, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives, pool);
97
+ return scanDirectoryForSafelist(dir, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives, pool, extraIgnore, ignoreDevFiles);
88
98
  }));
99
+ const unresolvedWrapperSpreads = results.flatMap((result) => result.unresolvedWrapperSpreads);
100
+ const safelist = results.flatMap((result) => result.safelist);
101
+ if (unresolvedWrapperSpreads.length > 0) {
102
+ const sources = results.flatMap((result) => result.filePaths).map((filePath) => node_fs.default.readFileSync(filePath, "utf-8"));
103
+ safelist.push(...require_purgeFromCode.resolveWrapperSpreadSafelist(unresolvedWrapperSpreads, sources, variants));
104
+ }
89
105
  return {
90
- safelist: results.flatMap((result) => result.safelist),
106
+ safelist,
91
107
  components: [...new Set(results.flatMap((result) => result.components))],
92
108
  filesScanned: results.reduce((total, result) => total + result.filesScanned, 0),
93
109
  filesWithComponents: results.reduce((total, result) => total + result.filesWithComponents, 0),
94
- filePaths: results.flatMap((result) => result.filePaths).sort((left, right) => left.localeCompare(right))
110
+ filePaths: results.flatMap((result) => result.filePaths).sort((left, right) => left.localeCompare(right)),
111
+ unresolvedWrapperSpreads
95
112
  };
96
113
  };
97
114
  const findMonorepoRoot = (startDir) => {
@@ -2,12 +2,13 @@
2
2
  import { cyan, green, magenta, red } from "../packages/cli/dist/lib/colors.js";
3
3
  import { print } from "../packages/cli/dist/lib/print.js";
4
4
  import { measureAsync } from "./perf.js";
5
- import { purgeFromCodeOptimized } from "../purger/optimized/purgeFromCode.js";
5
+ import { purgeFromCodeOptimized, resolveWrapperSpreadSafelist } from "../purger/optimized/purgeFromCode.js";
6
6
  import { formatEntryValue } from "../../../tailwind-internal/dist/utils/entryPoints.js";
7
+ import { DEV_FILE_IGNORE_GLOBS } from "../purger/optimized/utils/files.js";
7
8
  import path from "node:path";
8
9
  import fs from "node:fs";
9
- import { createRequire } from "node:module";
10
10
  import fg from "fast-glob";
11
+ import { createRequire } from "node:module";
11
12
  //#region ../css/dist/css/nodeUtils.mjs
12
13
  /*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
13
14
  const loadConfigFile = async (configPath) => {
@@ -20,11 +21,15 @@ const loadConfigFile = async (configPath) => {
20
21
  throw new Error(`Failed to load config file: ${absolutePath}\n${error instanceof Error ? error.message : "Unknown error"}`);
21
22
  }
22
23
  };
23
- const scanDirectoryForSafelist = async (dir, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives = false, pool) => {
24
+ const scanDirectoryForSafelist = async (dir, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives = false, pool, extraIgnore = [], ignoreDevFiles = true) => {
24
25
  const files = await measureAsync("scan:glob", () => fg("**/*.{js,jsx,mjs,cjs,ts,tsx}", {
25
26
  cwd: dir,
26
27
  absolute: true,
27
- ignore: ["**/node_modules/**"]
28
+ ignore: [
29
+ "**/node_modules/**",
30
+ ...ignoreDevFiles ? DEV_FILE_IGNORE_GLOBS : [],
31
+ ...extraIgnore
32
+ ]
28
33
  }).then((f) => f.sort((left, right) => left.localeCompare(right))));
29
34
  const processFile = (filePath) => {
30
35
  const code = fs.readFileSync(filePath, "utf-8");
@@ -34,7 +39,8 @@ const scanDirectoryForSafelist = async (dir, colorModes, variants, autoVariants,
34
39
  colorModes,
35
40
  variantDefaults,
36
41
  runtimeConfigValues,
37
- includeAllClassNamePrimitives
42
+ includeAllClassNamePrimitives,
43
+ deferWrapperSpreadResolution: true
38
44
  });
39
45
  return purgeFromCodeOptimized(code, {
40
46
  colorModes,
@@ -44,6 +50,7 @@ const scanDirectoryForSafelist = async (dir, colorModes, variants, autoVariants,
44
50
  autoVariants,
45
51
  componentData,
46
52
  includeAllClassNamePrimitives,
53
+ deferWrapperSpreadResolution: true,
47
54
  filePath
48
55
  });
49
56
  };
@@ -56,7 +63,8 @@ const scanDirectoryForSafelist = async (dir, colorModes, variants, autoVariants,
56
63
  components: [...allComponents],
57
64
  filesScanned: files.length,
58
65
  filesWithComponents,
59
- filePaths: files
66
+ filePaths: files,
67
+ unresolvedWrapperSpreads: results.flatMap((result) => result.unresolvedWrapperSpreads ?? [])
60
68
  };
61
69
  };
62
70
  const scanFileForSafelist = async (filePath, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives = false) => {
@@ -67,27 +75,36 @@ const scanFileForSafelist = async (filePath, colorModes, variants, autoVariants,
67
75
  variants,
68
76
  autoVariants,
69
77
  componentData,
70
- includeAllClassNamePrimitives
78
+ includeAllClassNamePrimitives,
79
+ deferWrapperSpreadResolution: true
71
80
  });
72
81
  return {
73
82
  safelist: result.safelist,
74
83
  components: result.components,
75
84
  filesScanned: 1,
76
85
  filesWithComponents: result.components.length > 0 ? 1 : 0,
77
- filePaths: [filePath]
86
+ filePaths: [filePath],
87
+ unresolvedWrapperSpreads: result.unresolvedWrapperSpreads ?? []
78
88
  };
79
89
  };
80
- const scanDirectoriesForSafelist = async (dirs, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives = false, pool) => {
90
+ const scanDirectoriesForSafelist = async (dirs, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives = false, pool, extraIgnore = [], ignoreDevFiles = true) => {
81
91
  const results = await Promise.all(dirs.map((dir) => {
82
92
  if (fs.existsSync(dir) && fs.statSync(dir).isFile()) return scanFileForSafelist(dir, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives);
83
- return scanDirectoryForSafelist(dir, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives, pool);
93
+ return scanDirectoryForSafelist(dir, colorModes, variants, autoVariants, componentData, variantDefaults, runtimeConfigValues, includeAllClassNamePrimitives, pool, extraIgnore, ignoreDevFiles);
84
94
  }));
95
+ const unresolvedWrapperSpreads = results.flatMap((result) => result.unresolvedWrapperSpreads);
96
+ const safelist = results.flatMap((result) => result.safelist);
97
+ if (unresolvedWrapperSpreads.length > 0) {
98
+ const sources = results.flatMap((result) => result.filePaths).map((filePath) => fs.readFileSync(filePath, "utf-8"));
99
+ safelist.push(...resolveWrapperSpreadSafelist(unresolvedWrapperSpreads, sources, variants));
100
+ }
85
101
  return {
86
- safelist: results.flatMap((result) => result.safelist),
102
+ safelist,
87
103
  components: [...new Set(results.flatMap((result) => result.components))],
88
104
  filesScanned: results.reduce((total, result) => total + result.filesScanned, 0),
89
105
  filesWithComponents: results.reduce((total, result) => total + result.filesWithComponents, 0),
90
- filePaths: results.flatMap((result) => result.filePaths).sort((left, right) => left.localeCompare(right))
106
+ filePaths: results.flatMap((result) => result.filePaths).sort((left, right) => left.localeCompare(right)),
107
+ unresolvedWrapperSpreads
91
108
  };
92
109
  };
93
110
  const findMonorepoRoot = (startDir) => {
@@ -4,6 +4,7 @@ const require_colors = require("../packages/cli/dist/lib/colors.cjs");
4
4
  const require_print = require("../packages/cli/dist/lib/print.cjs");
5
5
  const require_perf = require("./perf.cjs");
6
6
  const require_entryPoints = require("../../../tailwind-internal/dist/utils/entryPoints.cjs");
7
+ const require_files = require("../purger/optimized/utils/files.cjs");
7
8
  const require_nodeUtils = require("./nodeUtils.cjs");
8
9
  const require_spinner = require("../packages/cli/dist/lib/spinner.cjs");
9
10
  const require_logger = require("../packages/cli/dist/lib/logger.cjs");
@@ -249,6 +250,10 @@ const runThemeMode = async (options, context) => {
249
250
  effectiveSilent = setup.effectiveSilent;
250
251
  log = require_logger.createLogger({ silent: effectiveSilent });
251
252
  const entryDirs = entries.map((entry) => entry.absolutePath);
253
+ const includeDevFiles = options.includeDevFiles ?? themeConfig.includeDevFiles ?? false;
254
+ const includeStories = options.includeStories ?? themeConfig.includeStories ?? false;
255
+ const ignoreDevFiles = !includeDevFiles && !includeStories;
256
+ const scanIgnore = [...themeConfig.ignore ?? [], ...!includeDevFiles && includeStories ? require_files.TEST_FILE_IGNORE_GLOBS : []];
252
257
  if (!effectiveSilent) log.spinStop("✅", "Theme configuration loaded");
253
258
  if (themeConfig.config && appConfig === require_index.defaultTokensConfig) log.warn(`App config not found: ${themeConfig.config}, using defaults`);
254
259
  const packageDirs = [];
@@ -280,7 +285,7 @@ const runThemeMode = async (options, context) => {
280
285
  return {
281
286
  packageName,
282
287
  status: "ok",
283
- scanResult: await require_nodeUtils.scanDirectoryForSafelist(packageDir, colorModes, context.variants, context.autoVariants, context.componentData, appVariantDefaults, runtimeConfigValues, true, pool)
288
+ scanResult: await require_nodeUtils.scanDirectoryForSafelist(packageDir, colorModes, context.variants, context.autoVariants, context.componentData, appVariantDefaults, runtimeConfigValues, true, pool, scanIgnore, ignoreDevFiles)
284
289
  };
285
290
  };
286
291
  const processScopedPackage = async (packageName, scopedPackageValue) => {
@@ -304,7 +309,7 @@ const runThemeMode = async (options, context) => {
304
309
  });
305
310
  };
306
311
  const [appScanResult, inheritedResults] = await Promise.all([
307
- require_perf.measureAsync("scan", () => require_nodeUtils.scanDirectoriesForSafelist(entryDirs, colorModes, context.variants, context.autoVariants, context.componentData, appVariantDefaults, runtimeConfigValues, false, pool)),
312
+ require_perf.measureAsync("scan", () => require_nodeUtils.scanDirectoriesForSafelist(entryDirs, colorModes, context.variants, context.autoVariants, context.componentData, appVariantDefaults, runtimeConfigValues, false, pool, scanIgnore, ignoreDevFiles)),
308
313
  Promise.all((themeConfig.inherit ?? []).map((packageName) => require_perf.measureAsync(`scan:inherit:${packageName}`, () => processInheritedPackage(packageName)))),
309
314
  Promise.all(Object.entries(themeConfig.scoped ?? {}).map(([packageName, scopedPackageConfig]) => processScopedPackage(packageName, scopedPackageConfig)))
310
315
  ]);
@@ -364,7 +369,7 @@ const runThemeMode = async (options, context) => {
364
369
  const packageConfig = await require_nodeUtils.loadConfigFile(isAbsolutePath(scopedPackageTarget.config.config) ? scopedPackageTarget.config.config : joinPath(scopedPackageTarget.packageRoot, scopedPackageTarget.config.config)) ?? require_index.defaultTokensConfig;
365
370
  const packageVariantDefaults = require_utils.extractVariantDefaults(packageConfig);
366
371
  const packageRuntimeConfigValues = require_utils.extractRuntimeConfigValues(packageConfig);
367
- const packageScanResult = await require_nodeUtils.scanDirectoriesForSafelist(scopedPackageTarget.entryDirs, colorModes, context.variants, context.autoVariants, context.componentData, packageVariantDefaults, packageRuntimeConfigValues, true, pool);
372
+ const packageScanResult = await require_nodeUtils.scanDirectoriesForSafelist(scopedPackageTarget.entryDirs, colorModes, context.variants, context.autoVariants, context.componentData, packageVariantDefaults, packageRuntimeConfigValues, true, pool, scanIgnore, ignoreDevFiles);
368
373
  genLog.spinStop("✅", require_runner_helpers.getScanSummaryMessage({
369
374
  label: scopedPackageTarget.packageName,
370
375
  filesScanned: packageScanResult.filesScanned,
@@ -3,6 +3,7 @@ import { gray, green, magenta } from "../packages/cli/dist/lib/colors.js";
3
3
  import { print } from "../packages/cli/dist/lib/print.js";
4
4
  import { captureMemory, clearTimings, measureAsync, printPerfSummary } from "./perf.js";
5
5
  import { resolveEntryPaths } from "../../../tailwind-internal/dist/utils/entryPoints.js";
6
+ import { TEST_FILE_IGNORE_GLOBS } from "../purger/optimized/utils/files.js";
6
7
  import { findPackageRoot, findPackageSourceDir, getPackageUdsScope, loadConfigFile, scanDirectoriesForSafelist, scanDirectoryForSafelist } from "./nodeUtils.js";
7
8
  import { spinStart, spinStop } from "../packages/cli/dist/lib/spinner.js";
8
9
  import { createLogger } from "../packages/cli/dist/lib/logger.js";
@@ -247,6 +248,10 @@ const runThemeMode = async (options, context) => {
247
248
  effectiveSilent = setup.effectiveSilent;
248
249
  log = createLogger({ silent: effectiveSilent });
249
250
  const entryDirs = entries.map((entry) => entry.absolutePath);
251
+ const includeDevFiles = options.includeDevFiles ?? themeConfig.includeDevFiles ?? false;
252
+ const includeStories = options.includeStories ?? themeConfig.includeStories ?? false;
253
+ const ignoreDevFiles = !includeDevFiles && !includeStories;
254
+ const scanIgnore = [...themeConfig.ignore ?? [], ...!includeDevFiles && includeStories ? TEST_FILE_IGNORE_GLOBS : []];
250
255
  if (!effectiveSilent) log.spinStop("✅", "Theme configuration loaded");
251
256
  if (themeConfig.config && appConfig === defaultTokensConfig) log.warn(`App config not found: ${themeConfig.config}, using defaults`);
252
257
  const packageDirs = [];
@@ -278,7 +283,7 @@ const runThemeMode = async (options, context) => {
278
283
  return {
279
284
  packageName,
280
285
  status: "ok",
281
- scanResult: await scanDirectoryForSafelist(packageDir, colorModes, context.variants, context.autoVariants, context.componentData, appVariantDefaults, runtimeConfigValues, true, pool)
286
+ scanResult: await scanDirectoryForSafelist(packageDir, colorModes, context.variants, context.autoVariants, context.componentData, appVariantDefaults, runtimeConfigValues, true, pool, scanIgnore, ignoreDevFiles)
282
287
  };
283
288
  };
284
289
  const processScopedPackage = async (packageName, scopedPackageValue) => {
@@ -302,7 +307,7 @@ const runThemeMode = async (options, context) => {
302
307
  });
303
308
  };
304
309
  const [appScanResult, inheritedResults] = await Promise.all([
305
- measureAsync("scan", () => scanDirectoriesForSafelist(entryDirs, colorModes, context.variants, context.autoVariants, context.componentData, appVariantDefaults, runtimeConfigValues, false, pool)),
310
+ measureAsync("scan", () => scanDirectoriesForSafelist(entryDirs, colorModes, context.variants, context.autoVariants, context.componentData, appVariantDefaults, runtimeConfigValues, false, pool, scanIgnore, ignoreDevFiles)),
306
311
  Promise.all((themeConfig.inherit ?? []).map((packageName) => measureAsync(`scan:inherit:${packageName}`, () => processInheritedPackage(packageName)))),
307
312
  Promise.all(Object.entries(themeConfig.scoped ?? {}).map(([packageName, scopedPackageConfig]) => processScopedPackage(packageName, scopedPackageConfig)))
308
313
  ]);
@@ -362,7 +367,7 @@ const runThemeMode = async (options, context) => {
362
367
  const packageConfig = await loadConfigFile(isAbsolutePath(scopedPackageTarget.config.config) ? scopedPackageTarget.config.config : joinPath(scopedPackageTarget.packageRoot, scopedPackageTarget.config.config)) ?? defaultTokensConfig;
363
368
  const packageVariantDefaults = extractVariantDefaults(packageConfig);
364
369
  const packageRuntimeConfigValues = extractRuntimeConfigValues(packageConfig);
365
- const packageScanResult = await scanDirectoriesForSafelist(scopedPackageTarget.entryDirs, colorModes, context.variants, context.autoVariants, context.componentData, packageVariantDefaults, packageRuntimeConfigValues, true, pool);
370
+ const packageScanResult = await scanDirectoriesForSafelist(scopedPackageTarget.entryDirs, colorModes, context.variants, context.autoVariants, context.componentData, packageVariantDefaults, packageRuntimeConfigValues, true, pool, scanIgnore, ignoreDevFiles);
366
371
  genLog.spinStop("✅", getScanSummaryMessage({
367
372
  label: scopedPackageTarget.packageName,
368
373
  filesScanned: packageScanResult.filesScanned,