@nghitrum/dsforge 0.1.5-alpha.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  generateChangelog,
4
4
  generatePackageJson,
5
5
  generateTsConfig
6
- } from "../chunk-QHE35QQQ.js";
6
+ } from "../chunk-ZZRPNO6Z.js";
7
7
  import {
8
8
  CONTROL_SIZE_PRESETS,
9
9
  PRESETS,
@@ -11,9 +11,8 @@ import {
11
11
  RADIUS_PRESETS,
12
12
  SPACING_PRESETS,
13
13
  applyPreset,
14
- buildSemanticSpacing,
15
- isProUnlocked
16
- } from "../chunk-YUPXTQZ5.js";
14
+ buildSemanticSpacing
15
+ } from "../chunk-5YT3VNE6.js";
17
16
 
18
17
  // src/cli/index.ts
19
18
  import { program } from "commander";
@@ -738,15 +737,7 @@ async function runInit(cwd, options) {
738
737
  }
739
738
  const name = rawName.replace(/\s+/g, "-").toLowerCase();
740
739
  let preset;
741
- if (!isProUnlocked()) {
742
- if (options.preset && options.preset !== "comfortable") {
743
- logger.hint(
744
- `Preset "${options.preset}" requires dsforge Pro`,
745
- `Set DSFORGE_KEY to unlock compact and spacious. Using comfortable.`
746
- );
747
- }
748
- preset = "comfortable";
749
- } else if (options.preset && VALID_PRESETS.includes(options.preset)) {
740
+ if (options.preset && VALID_PRESETS.includes(options.preset)) {
750
741
  preset = options.preset;
751
742
  } else {
752
743
  const answer = await ask(
@@ -1907,192 +1898,6 @@ function generateCssFiles(config, resolution) {
1907
1898
  return files;
1908
1899
  }
1909
1900
 
1910
- // src/generators/metadata/generator.ts
1911
- var COMPONENT_DEFAULTS = {
1912
- button: {
1913
- description: "Triggers an action or navigation. The primary interactive element.",
1914
- role: "action-trigger",
1915
- hierarchyLevel: "primary",
1916
- interactionModel: "synchronous",
1917
- layoutImpact: "inline",
1918
- destructive: false,
1919
- sizes: ["sm", "md", "lg"]
1920
- },
1921
- input: {
1922
- description: "Accepts user text input. Use with a label for accessibility.",
1923
- role: "data-entry",
1924
- hierarchyLevel: "primary",
1925
- interactionModel: "synchronous",
1926
- layoutImpact: "block",
1927
- destructive: false,
1928
- sizes: ["sm", "md", "lg"]
1929
- },
1930
- card: {
1931
- description: "Groups related content with optional header, body, and footer slots.",
1932
- role: "content-container",
1933
- hierarchyLevel: "utility",
1934
- interactionModel: "none",
1935
- layoutImpact: "block",
1936
- destructive: false
1937
- },
1938
- badge: {
1939
- description: "Compact label for status, categories, or counts. Display-only \u2014 not interactive.",
1940
- role: "status-indicator",
1941
- hierarchyLevel: "utility",
1942
- interactionModel: "none",
1943
- layoutImpact: "inline",
1944
- destructive: false,
1945
- sizes: ["sm", "md", "lg"]
1946
- },
1947
- checkbox: {
1948
- description: "Binary toggle for boolean values. Supports indeterminate state for partial selections.",
1949
- role: "data-entry",
1950
- hierarchyLevel: "primary",
1951
- interactionModel: "synchronous",
1952
- layoutImpact: "inline",
1953
- destructive: false,
1954
- sizes: ["sm", "md", "lg"]
1955
- },
1956
- radio: {
1957
- description: "Single selection within a mutually exclusive group. Always use inside RadioGroup.",
1958
- role: "data-entry",
1959
- hierarchyLevel: "primary",
1960
- interactionModel: "synchronous",
1961
- layoutImpact: "inline",
1962
- destructive: false,
1963
- sizes: ["sm", "md", "lg"]
1964
- },
1965
- select: {
1966
- description: "Dropdown picker for selecting from a list of options. Wraps native <select> for accessibility.",
1967
- role: "data-entry",
1968
- hierarchyLevel: "primary",
1969
- interactionModel: "synchronous",
1970
- layoutImpact: "block",
1971
- destructive: false,
1972
- sizes: ["sm", "md", "lg"]
1973
- },
1974
- toast: {
1975
- description: "Feedback messages for user actions. Alert is inline; Toast is an overlay with auto-dismiss.",
1976
- role: "feedback",
1977
- hierarchyLevel: "utility",
1978
- interactionModel: "asynchronous",
1979
- layoutImpact: "overlay",
1980
- destructive: false
1981
- },
1982
- spinner: {
1983
- description: "Loading indicator for async operations. Use with an accessible label for screen readers.",
1984
- role: "loading-indicator",
1985
- hierarchyLevel: "utility",
1986
- interactionModel: "asynchronous",
1987
- layoutImpact: "inline",
1988
- destructive: false,
1989
- sizes: ["xs", "sm", "md", "lg", "xl"]
1990
- }
1991
- };
1992
- function buildComponentMetadata(componentName, rule, config) {
1993
- const defaults = COMPONENT_DEFAULTS[componentName.toLowerCase()] ?? {};
1994
- const variants = rule.allowedVariants ?? ["default"];
1995
- const requiredProps = rule.requiredProps ?? [];
1996
- const tokens = {};
1997
- for (const [tokenName] of Object.entries(rule.tokens ?? {})) {
1998
- tokens[tokenName] = `--${tokenName}`;
1999
- }
2000
- const meta = {
2001
- component: pascalCase(componentName),
2002
- version: config.meta.version,
2003
- description: defaults.description ?? `A ${componentName} component.`,
2004
- role: defaults.role ?? "ui-element",
2005
- hierarchyLevel: defaults.hierarchyLevel ?? "utility",
2006
- interactionModel: defaults.interactionModel ?? "none",
2007
- layoutImpact: defaults.layoutImpact ?? "inline",
2008
- destructive: componentName.toLowerCase().includes("delete") || variants.includes("danger"),
2009
- allowedVariants: variants,
2010
- defaultVariant: variants[0] ?? "default",
2011
- requiredProps,
2012
- optionalProps: buildOptionalProps(componentName, defaults),
2013
- tokens,
2014
- accessibilityContract: {
2015
- keyboard: rule.a11y?.keyboard ?? true,
2016
- focusRing: rule.a11y?.focusRing ?? true,
2017
- ariaLabel: rule.a11y?.ariaLabel ?? "optional",
2018
- ...rule.a11y?.role ? { role: rule.a11y.role } : {}
2019
- },
2020
- governanceRules: {
2021
- ...rule.maxWidth ? { maxWidth: rule.maxWidth } : {},
2022
- ...rule.allowedRadius ? { allowedRadius: rule.allowedRadius } : {},
2023
- ...rule.allowedShadows ? { allowedShadows: rule.allowedShadows } : {},
2024
- ...rule.colorPalette ? { colorPalette: rule.colorPalette } : {}
2025
- }
2026
- };
2027
- if (defaults.sizes) {
2028
- meta.sizes = defaults.sizes;
2029
- }
2030
- return meta;
2031
- }
2032
- function buildOptionalProps(componentName, _defaults) {
2033
- const common = ["className", "style", "id", "data-testid"];
2034
- const byComponent = {
2035
- button: [
2036
- "size",
2037
- "loading",
2038
- "disabled",
2039
- "fullWidth",
2040
- "iconLeft",
2041
- "iconRight",
2042
- "onClick"
2043
- ],
2044
- input: [
2045
- "size",
2046
- "disabled",
2047
- "label",
2048
- "helperText",
2049
- "errorMessage",
2050
- "placeholder",
2051
- "startAdornment",
2052
- "endAdornment",
2053
- "onChange"
2054
- ],
2055
- card: ["maxWidth", "noPadding", "onClick"],
2056
- badge: ["size", "dot"],
2057
- checkbox: ["size", "disabled", "label", "helperText", "indeterminate", "checked", "onChange"],
2058
- radio: ["size", "disabled", "label", "value", "onChange"],
2059
- select: ["size", "disabled", "label", "helperText", "errorMessage", "placeholder", "options", "fullWidth", "onChange"],
2060
- toast: ["variant", "title", "dismissible", "duration", "onDismiss"],
2061
- spinner: ["size", "variant", "label"]
2062
- };
2063
- return [...byComponent[componentName.toLowerCase()] ?? [], ...common];
2064
- }
2065
- function pascalCase(str) {
2066
- return str.charAt(0).toUpperCase() + str.slice(1);
2067
- }
2068
- function buildIndexMetadata(config, componentNames, tokenCount) {
2069
- return {
2070
- name: config.meta.name,
2071
- version: config.meta.version,
2072
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2073
- components: componentNames.map(pascalCase),
2074
- tokenCount,
2075
- themes: Object.keys(config.themes ?? {})
2076
- };
2077
- }
2078
- function generateMetadata(config, rules, tokenCount) {
2079
- const files = [];
2080
- const componentNames = Object.keys(rules);
2081
- for (const [componentName, rule] of Object.entries(rules)) {
2082
- const metadata = buildComponentMetadata(componentName, rule, config);
2083
- files.push({
2084
- filename: `${componentName}.json`,
2085
- content: JSON.stringify(metadata, null, 2)
2086
- });
2087
- }
2088
- const index = buildIndexMetadata(config, componentNames, tokenCount);
2089
- files.push({
2090
- filename: "index.json",
2091
- content: JSON.stringify(index, null, 2)
2092
- });
2093
- return files;
2094
- }
2095
-
2096
1901
  // src/adapters/react/components/button.ts
2097
1902
  function generateButton(config, rule) {
2098
1903
  const variants = rule?.allowedVariants ?? [
@@ -4111,62 +3916,7 @@ function generateThemeProvider(config) {
4111
3916
  const themeNames = Object.keys(config.themes ?? { light: {}, dark: {} });
4112
3917
  const defaultTheme = themeNames.includes("light") ? "light" : themeNames[0] ?? "light";
4113
3918
  const themeType = themeNames.map((t) => `"${t}"`).join(" | ");
4114
- const isPro = isProUnlocked();
4115
3919
  const defaultDensity = config.meta.preset ?? "comfortable";
4116
- const densityImport = isPro ? `
4117
- import "../tokens/density.css";` : "";
4118
- const densityTypes = isPro ? `
4119
- export type DensityName = "compact" | "comfortable" | "spacious";
4120
- ` : "";
4121
- const densityContextTypes = isPro ? `
4122
- export interface DensityContextValue {
4123
- density: DensityName;
4124
- setDensity: (density: DensityName) => void;
4125
- }
4126
- ` : "";
4127
- const densityContext = isPro ? `
4128
- export const DensityContext = React.createContext<DensityContextValue>({
4129
- density: "${defaultDensity}",
4130
- setDensity: () => undefined,
4131
- });
4132
-
4133
- /**
4134
- * Hook to read and change the current density.
4135
- * Must be used inside a <ThemeProvider>.
4136
- */
4137
- export function useDensity(): DensityContextValue {
4138
- return React.useContext(DensityContext);
4139
- }
4140
- ` : "";
4141
- const densityProp = isPro ? `
4142
- /** Component density. Requires density.css to be imported. Defaults to "${defaultDensity}". */
4143
- density?: DensityName;` : "";
4144
- const densityOnChangeProp = isPro ? `
4145
- /** Called when setDensity is invoked. */
4146
- onDensityChange?: (density: DensityName) => void;` : "";
4147
- const densityState = isPro ? `
4148
- const [density, setDensityState] = React.useState<DensityName>(initialDensity);
4149
-
4150
- React.useEffect(() => {
4151
- setDensityState(initialDensity);
4152
- }, [initialDensity]);
4153
-
4154
- const setDensity = React.useCallback(
4155
- (next: DensityName) => {
4156
- setDensityState(next);
4157
- onDensityChange?.(next);
4158
- },
4159
- [onDensityChange],
4160
- );
4161
- ` : "";
4162
- const densityDestructure = isPro ? `,
4163
- density: initialDensity = "${defaultDensity}",
4164
- onDensityChange,` : "";
4165
- const densityProviderOpen = isPro ? `
4166
- <DensityContext.Provider value={{ density, setDensity }}>` : "";
4167
- const densityDataAttr = isPro ? ` data-density={density}` : "";
4168
- const densityProviderClose = isPro ? `
4169
- </DensityContext.Provider>` : "";
4170
3920
  return `/**
4171
3921
  * ThemeProvider \u2014 ${config.meta.name}
4172
3922
  *
@@ -4178,27 +3928,39 @@ export function useDensity(): DensityContextValue {
4178
3928
  * import "@${config.meta.name}/tokens/light.css"; // or dark.css
4179
3929
  * import { ThemeProvider } from "@${config.meta.name}";
4180
3930
  *
4181
- * <ThemeProvider theme="light"${isPro ? ` density="${defaultDensity}"` : ""}>
3931
+ * <ThemeProvider theme="light" density="${defaultDensity}">
4182
3932
  * <App />
4183
3933
  * </ThemeProvider>
4184
3934
  */
4185
3935
 
4186
- import React from "react";${densityImport}
3936
+ import React from "react";
3937
+ import "../tokens/density.css";
4187
3938
 
4188
3939
  // \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4189
3940
 
4190
3941
  export type ThemeName = ${themeType};
4191
- ${densityTypes}
3942
+
3943
+ export type DensityName = "compact" | "comfortable" | "spacious";
3944
+
4192
3945
  export interface ThemeContextValue {
4193
3946
  theme: ThemeName;
4194
3947
  setTheme: (theme: ThemeName) => void;
4195
3948
  }
4196
- ${densityContextTypes}
3949
+
3950
+ export interface DensityContextValue {
3951
+ density: DensityName;
3952
+ setDensity: (density: DensityName) => void;
3953
+ }
3954
+
4197
3955
  export interface ThemeProviderProps {
4198
3956
  /** Initial theme. Defaults to "${defaultTheme}". */
4199
3957
  theme?: ThemeName;
4200
3958
  /** Called when setTheme is invoked \u2014 use to persist theme preference. */
4201
- onThemeChange?: (theme: ThemeName) => void;${densityProp}${densityOnChangeProp}
3959
+ onThemeChange?: (theme: ThemeName) => void;
3960
+ /** Component density. Requires density.css to be imported. Defaults to "${defaultDensity}". */
3961
+ density?: DensityName;
3962
+ /** Called when setDensity is invoked. */
3963
+ onDensityChange?: (density: DensityName) => void;
4202
3964
  children: React.ReactNode;
4203
3965
  }
4204
3966
 
@@ -4216,12 +3978,27 @@ export const ThemeContext = React.createContext<ThemeContextValue>({
4216
3978
  export function useTheme(): ThemeContextValue {
4217
3979
  return React.useContext(ThemeContext);
4218
3980
  }
4219
- ${densityContext}
3981
+
3982
+ export const DensityContext = React.createContext<DensityContextValue>({
3983
+ density: "${defaultDensity}",
3984
+ setDensity: () => undefined,
3985
+ });
3986
+
3987
+ /**
3988
+ * Hook to read and change the current density.
3989
+ * Must be used inside a <ThemeProvider>.
3990
+ */
3991
+ export function useDensity(): DensityContextValue {
3992
+ return React.useContext(DensityContext);
3993
+ }
3994
+
4220
3995
  // \u2500\u2500\u2500 Provider \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4221
3996
 
4222
3997
  export function ThemeProvider({
4223
3998
  theme: initialTheme = "${defaultTheme}",
4224
- onThemeChange,${densityDestructure}
3999
+ onThemeChange,
4000
+ density: initialDensity = "${defaultDensity}",
4001
+ onDensityChange,
4225
4002
  children,
4226
4003
  }: ThemeProviderProps) {
4227
4004
  const [theme, setThemeState] = React.useState<ThemeName>(initialTheme);
@@ -4237,13 +4014,29 @@ export function ThemeProvider({
4237
4014
  },
4238
4015
  [onThemeChange],
4239
4016
  );
4240
- ${densityState}
4241
- return (${densityProviderOpen}
4017
+
4018
+ const [density, setDensityState] = React.useState<DensityName>(initialDensity);
4019
+
4020
+ React.useEffect(() => {
4021
+ setDensityState(initialDensity);
4022
+ }, [initialDensity]);
4023
+
4024
+ const setDensity = React.useCallback(
4025
+ (next: DensityName) => {
4026
+ setDensityState(next);
4027
+ onDensityChange?.(next);
4028
+ },
4029
+ [onDensityChange],
4030
+ );
4031
+
4032
+ return (
4033
+ <DensityContext.Provider value={{ density, setDensity }}>
4242
4034
  <ThemeContext.Provider value={{ theme, setTheme }}>
4243
- <div data-theme={theme}${densityDataAttr} style={{ display: "contents" }}>
4035
+ <div data-theme={theme} data-density={density} style={{ display: "contents" }}>
4244
4036
  {children}
4245
4037
  </div>
4246
- </ThemeContext.Provider>${densityProviderClose}
4038
+ </ThemeContext.Provider>
4039
+ </DensityContext.Provider>
4247
4040
  );
4248
4041
  }
4249
4042
  `;
@@ -4260,9 +4053,9 @@ function generateComponentIndex(config, componentNames) {
4260
4053
  ""
4261
4054
  ];
4262
4055
  for (const name of componentNames) {
4263
- lines.push(`export * from "./${name}";`);
4056
+ lines.push(`export * from "./components/${name}/${name}";`);
4264
4057
  }
4265
- lines.push(`export * from "./ThemeProvider";`);
4058
+ lines.push(`export * from "./components/ThemeProvider/ThemeProvider";`);
4266
4059
  lines.push("");
4267
4060
  return lines.join("\n");
4268
4061
  }
@@ -4900,13 +4693,11 @@ async function runGenerate(cwd, options) {
4900
4693
  await writeFile(path3.join(tokensDir, filename), content);
4901
4694
  logger.dim(` \u2192 tokens/${filename}`);
4902
4695
  }
4903
- if (isProUnlocked()) {
4904
- await writeFile(
4905
- path3.join(tokensDir, "density.css"),
4906
- emitDensityCss(config)
4907
- );
4908
- logger.dim(` \u2192 tokens/density.css`);
4909
- }
4696
+ await writeFile(
4697
+ path3.join(tokensDir, "density.css"),
4698
+ emitDensityCss(config)
4699
+ );
4700
+ logger.dim(` \u2192 tokens/density.css`);
4910
4701
  const tokenFiles = reactAdapter.generateTokenFiles(config, resolution);
4911
4702
  for (const { filename, content } of tokenFiles) {
4912
4703
  await writeFile(path3.join(tokensDir, filename), content);
@@ -4916,21 +4707,53 @@ async function runGenerate(cwd, options) {
4916
4707
  }
4917
4708
  if (!only || only === "components") {
4918
4709
  logger.step("Generating React components...");
4919
- const srcDir = path3.join(outRoot, "src");
4920
- await ensureDir(srcDir);
4710
+ const componentsDir = path3.join(outRoot, "components");
4711
+ await ensureDir(componentsDir);
4921
4712
  const generatedNames = [];
4713
+ const generatedComponentJsons = [];
4714
+ const flatTokens = {};
4715
+ for (const [k, v] of Object.entries(resolution.tokens)) {
4716
+ flatTokens[`--${k.replace(/^(global|semantic|component)\./, "")}`] = v;
4717
+ }
4718
+ const lightOverrides = config.themes?.["light"] ?? {};
4719
+ const darkOverrides = config.themes?.["dark"] ?? {};
4720
+ const lightCssVars = {
4721
+ ...flatTokens,
4722
+ ...Object.fromEntries(Object.entries(lightOverrides).map(([k, v]) => [`--${k}`, String(v)]))
4723
+ };
4724
+ const darkCssVars = {
4725
+ ...flatTokens,
4726
+ ...Object.fromEntries(Object.entries(darkOverrides).map(([k, v]) => [`--${k}`, String(v)]))
4727
+ };
4728
+ const resolvedCssVars = { light: lightCssVars, dark: darkCssVars };
4729
+ const { generateComponentJson } = await import("../generateComponentJson-XBEUWCW6.js");
4730
+ const { generateComponentMetadata } = await import("../generateComponentMetadata-2L5VNERD.js");
4922
4731
  for (const componentName of REACT_COMPONENTS) {
4732
+ const pascalName = componentName.charAt(0).toUpperCase() + componentName.slice(1);
4923
4733
  try {
4924
4734
  const { filename, content } = reactAdapter.generateComponent(
4925
4735
  componentName,
4926
4736
  config,
4927
4737
  rules[componentName]
4928
4738
  );
4929
- await writeFile(path3.join(srcDir, filename), content);
4930
- logger.dim(` \u2192 src/${filename}`);
4931
- generatedNames.push(
4932
- componentName.charAt(0).toUpperCase() + componentName.slice(1)
4739
+ const componentSubDir = path3.join(componentsDir, pascalName);
4740
+ await ensureDir(componentSubDir);
4741
+ await writeFile(path3.join(componentSubDir, filename), content);
4742
+ logger.dim(` \u2192 components/${pascalName}/${filename}`);
4743
+ generatedNames.push(pascalName);
4744
+ const componentJson = generateComponentJson(pascalName, resolvedCssVars);
4745
+ generatedComponentJsons.push(componentJson);
4746
+ await writeFile(
4747
+ path3.join(componentSubDir, `${pascalName}.json`),
4748
+ JSON.stringify(componentJson, null, 2)
4749
+ );
4750
+ logger.dim(` \u2192 components/${pascalName}/${pascalName}.json`);
4751
+ const metadata = generateComponentMetadata(pascalName);
4752
+ await writeFile(
4753
+ path3.join(componentSubDir, `${pascalName}.metadata.json`),
4754
+ JSON.stringify(metadata, null, 2)
4933
4755
  );
4756
+ logger.dim(` \u2192 components/${pascalName}/${pascalName}.metadata.json`);
4934
4757
  } catch (err) {
4935
4758
  logger.warn(
4936
4759
  `[dsforge] Could not generate ${componentName} \u2014 ${err.message}`
@@ -4938,23 +4761,31 @@ async function runGenerate(cwd, options) {
4938
4761
  }
4939
4762
  }
4940
4763
  const { filename: tpFile, content: tpContent } = reactAdapter.generateThemeProvider(config);
4941
- await writeFile(path3.join(srcDir, tpFile), tpContent);
4942
- logger.dim(` \u2192 src/${tpFile}`);
4764
+ const tpDir = path3.join(componentsDir, "ThemeProvider");
4765
+ await ensureDir(tpDir);
4766
+ await writeFile(path3.join(tpDir, tpFile), tpContent);
4767
+ logger.dim(` \u2192 components/ThemeProvider/${tpFile}`);
4768
+ try {
4769
+ const tpJson = generateComponentJson("ThemeProvider", resolvedCssVars);
4770
+ generatedComponentJsons.push(tpJson);
4771
+ await writeFile(
4772
+ path3.join(tpDir, "ThemeProvider.json"),
4773
+ JSON.stringify(tpJson, null, 2)
4774
+ );
4775
+ logger.dim(` \u2192 components/ThemeProvider/ThemeProvider.json`);
4776
+ const tpMeta = generateComponentMetadata("ThemeProvider");
4777
+ await writeFile(
4778
+ path3.join(tpDir, "ThemeProvider.metadata.json"),
4779
+ JSON.stringify(tpMeta, null, 2)
4780
+ );
4781
+ logger.dim(` \u2192 components/ThemeProvider/ThemeProvider.metadata.json`);
4782
+ } catch {
4783
+ }
4943
4784
  const { filename: idxFile, content: idxContent } = reactAdapter.generateComponentIndex(config, generatedNames);
4944
- await writeFile(path3.join(srcDir, idxFile), idxContent);
4945
- logger.dim(` \u2192 src/${idxFile}`);
4785
+ await writeFile(path3.join(outRoot, idxFile), idxContent);
4786
+ logger.dim(` \u2192 ${idxFile}`);
4946
4787
  logger.success(`${generatedNames.length} components generated`);
4947
- }
4948
- if (isProUnlocked() && (!only || only === "metadata")) {
4949
- logger.step("Writing AI metadata...");
4950
- const metaDir = path3.join(outRoot, "metadata");
4951
- await ensureDir(metaDir);
4952
- const metaFiles = generateMetadata(config, fullRules, tokenCount);
4953
- for (const { filename, content } of metaFiles) {
4954
- await writeFile(path3.join(metaDir, filename), content);
4955
- logger.dim(` \u2192 metadata/${filename}`);
4956
- }
4957
- logger.success(`Metadata written (${metaFiles.length} files)`);
4788
+ globalThis["__dsforgGeneratedJsons"] = generatedComponentJsons;
4958
4789
  }
4959
4790
  if (!only) {
4960
4791
  logger.step("Writing package files...");
@@ -4965,28 +4796,80 @@ async function runGenerate(cwd, options) {
4965
4796
  const tsConfig = generateTsConfig();
4966
4797
  await writeFile(path3.join(outRoot, "tsconfig.json"), tsConfig);
4967
4798
  logger.dim(` \u2192 tsconfig.json`);
4968
- const { generateReadme } = await import("../emitter-KNYIQTS5.js");
4799
+ const { generateReadme } = await import("../emitter-IC77G4QF.js");
4969
4800
  await writeFile(
4970
4801
  path3.join(outRoot, "README.md"),
4971
4802
  generateReadme(config, componentNames)
4972
4803
  );
4973
4804
  logger.dim(` \u2192 README.md`);
4974
4805
  const changelogPath = path3.join(outRoot, "CHANGELOG.md");
4975
- const fsExtra2 = await import("fs-extra");
4976
- const fsE2 = fsExtra2.default ?? fsExtra2;
4806
+ const fsExtra = await import("fs-extra");
4807
+ const fsE2 = fsExtra.default ?? fsExtra;
4977
4808
  if (!await fsE2.pathExists(changelogPath)) {
4978
4809
  await writeFile(changelogPath, generateChangelog(config));
4979
4810
  logger.dim(` \u2192 CHANGELOG.md (seeded)`);
4980
4811
  }
4981
4812
  logger.success(`Package files written`);
4982
4813
  }
4814
+ if (!only || only === "components") {
4815
+ logger.step("Writing registry + AI outputs...");
4816
+ const generatedJsons = globalThis["__dsforgGeneratedJsons"] ?? [];
4817
+ const { generateRegistry } = await import("../generateRegistry-3MEZDJAJ.js");
4818
+ const {
4819
+ generateSystemPrompt,
4820
+ generateComponentsJson,
4821
+ generateCursorContext,
4822
+ generateCopilotInstructions
4823
+ } = await import("../generateAiFolder-3OOFWBH7.js");
4824
+ const systemName = config.meta.name;
4825
+ const version = config.meta.version;
4826
+ const registry = generateRegistry(systemName, version, generatedJsons);
4827
+ await writeFile(path3.join(outRoot, "registry.json"), JSON.stringify(registry, null, 2));
4828
+ logger.dim(` \u2192 registry.json`);
4829
+ const { COMPONENT_METADATA_DEFINITIONS } = await import("../componentDefinitions-5LFCNFQY.js");
4830
+ const metadataList = generatedJsons.map((c) => COMPONENT_METADATA_DEFINITIONS[c.name]).filter((m) => Boolean(m));
4831
+ const flatTokensForAi = {};
4832
+ for (const [k, v] of Object.entries(resolution.tokens)) {
4833
+ const cssVar2 = `--${k.replace(/^(global|semantic|component)\./, "")}`;
4834
+ flatTokensForAi[cssVar2] = {
4835
+ light: config.themes?.["light"]?.[cssVar2.slice(2)] ?? v,
4836
+ dark: config.themes?.["dark"]?.[cssVar2.slice(2)] ?? v
4837
+ };
4838
+ }
4839
+ const aiDir = path3.join(outRoot, "ai");
4840
+ await ensureDir(aiDir);
4841
+ const cursorDir = path3.join(aiDir, ".cursor");
4842
+ await ensureDir(cursorDir);
4843
+ const componentNames = generatedJsons.map((c) => c.name);
4844
+ await writeFile(
4845
+ path3.join(aiDir, "system-prompt.md"),
4846
+ generateSystemPrompt(systemName, flatTokensForAi, componentNames)
4847
+ );
4848
+ logger.dim(` \u2192 ai/system-prompt.md`);
4849
+ await writeFile(
4850
+ path3.join(aiDir, "components.json"),
4851
+ generateComponentsJson(systemName, generatedJsons, metadataList)
4852
+ );
4853
+ logger.dim(` \u2192 ai/components.json`);
4854
+ await writeFile(
4855
+ path3.join(cursorDir, "context.md"),
4856
+ generateCursorContext(systemName)
4857
+ );
4858
+ logger.dim(` \u2192 ai/.cursor/context.md`);
4859
+ await writeFile(
4860
+ path3.join(outRoot, "copilot-instructions.md"),
4861
+ generateCopilotInstructions(systemName)
4862
+ );
4863
+ logger.dim(` \u2192 copilot-instructions.md`);
4864
+ logger.success(`Registry + AI outputs written`);
4865
+ }
4983
4866
  logger.step("Generating showcase...");
4984
- const { generateShowcase } = await import("../html-DJJSDXRX.js");
4867
+ const { generateShowcase } = await import("../html-4DD6GOHE.js");
4985
4868
  const showcaseHtml = generateShowcase(config, resolution);
4986
4869
  await writeFile(path3.join(outRoot, "showcase.html"), showcaseHtml);
4987
4870
  logger.dim(` \u2192 showcase.html`);
4988
- const fsExtra = await import("fs-extra");
4989
- const fsE = fsExtra.default ?? fsExtra;
4871
+ const fsExtraShowcase = await import("fs-extra");
4872
+ const fsE = fsExtraShowcase.default ?? fsExtraShowcase;
4990
4873
  const faviconSrc = path3.join(cwd, "assets", "favicon.svg");
4991
4874
  const faviconDest = path3.join(outRoot, "assets", "favicon.svg");
4992
4875
  if (await fsE.pathExists(faviconSrc)) {
@@ -5368,7 +5251,7 @@ async function runMenu() {
5368
5251
  // package.json
5369
5252
  var package_default = {
5370
5253
  name: "@nghitrum/dsforge",
5371
- version: "0.1.5-alpha.9",
5254
+ version: "0.2.0",
5372
5255
  description: "AI-native design system generator \u2014 tokens \u2192 components \u2192 docs \u2192 npm",
5373
5256
  keywords: [
5374
5257
  "design-system",
@@ -0,0 +1,8 @@
1
+ import {
2
+ COMPONENT_JSON_DEFINITIONS,
3
+ COMPONENT_METADATA_DEFINITIONS
4
+ } from "./chunk-A7VW6SII.js";
5
+ export {
6
+ COMPONENT_JSON_DEFINITIONS,
7
+ COMPONENT_METADATA_DEFINITIONS
8
+ };
@@ -3,7 +3,7 @@ import {
3
3
  generatePackageJson,
4
4
  generateReadme,
5
5
  generateTsConfig
6
- } from "./chunk-QHE35QQQ.js";
6
+ } from "./chunk-ZZRPNO6Z.js";
7
7
  export {
8
8
  generateChangelog,
9
9
  generatePackageJson,