@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/index.js CHANGED
@@ -1143,31 +1143,6 @@ var rl = readline.createInterface({
1143
1143
  output: process.stdout
1144
1144
  });
1145
1145
 
1146
- // src/lib/license.ts
1147
- import { readFileSync } from "fs";
1148
- import { join } from "path";
1149
- function readKeyFromDotEnv() {
1150
- try {
1151
- const content = readFileSync(join(process.cwd(), ".env"), "utf8");
1152
- for (const raw of content.split("\n")) {
1153
- const line = raw.trim();
1154
- if (!line || line.startsWith("#")) continue;
1155
- const eq = line.indexOf("=");
1156
- if (eq === -1) continue;
1157
- const key = line.slice(0, eq).trim();
1158
- if (key !== "DSFORGE_KEY") continue;
1159
- const val = line.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
1160
- return val || void 0;
1161
- }
1162
- } catch {
1163
- }
1164
- return void 0;
1165
- }
1166
- function isProUnlocked() {
1167
- const key = process.env["DSFORGE_KEY"] ?? readKeyFromDotEnv();
1168
- return typeof key === "string" && key.length > 0;
1169
- }
1170
-
1171
1146
  // src/presets/index.ts
1172
1147
  var SPACING_PRESETS = {
1173
1148
  compact: {
@@ -2612,62 +2587,7 @@ function generateThemeProvider(config) {
2612
2587
  const themeNames = Object.keys(config.themes ?? { light: {}, dark: {} });
2613
2588
  const defaultTheme = themeNames.includes("light") ? "light" : themeNames[0] ?? "light";
2614
2589
  const themeType = themeNames.map((t) => `"${t}"`).join(" | ");
2615
- const isPro = isProUnlocked();
2616
2590
  const defaultDensity = config.meta.preset ?? "comfortable";
2617
- const densityImport = isPro ? `
2618
- import "../tokens/density.css";` : "";
2619
- const densityTypes = isPro ? `
2620
- export type DensityName = "compact" | "comfortable" | "spacious";
2621
- ` : "";
2622
- const densityContextTypes = isPro ? `
2623
- export interface DensityContextValue {
2624
- density: DensityName;
2625
- setDensity: (density: DensityName) => void;
2626
- }
2627
- ` : "";
2628
- const densityContext = isPro ? `
2629
- export const DensityContext = React.createContext<DensityContextValue>({
2630
- density: "${defaultDensity}",
2631
- setDensity: () => undefined,
2632
- });
2633
-
2634
- /**
2635
- * Hook to read and change the current density.
2636
- * Must be used inside a <ThemeProvider>.
2637
- */
2638
- export function useDensity(): DensityContextValue {
2639
- return React.useContext(DensityContext);
2640
- }
2641
- ` : "";
2642
- const densityProp = isPro ? `
2643
- /** Component density. Requires density.css to be imported. Defaults to "${defaultDensity}". */
2644
- density?: DensityName;` : "";
2645
- const densityOnChangeProp = isPro ? `
2646
- /** Called when setDensity is invoked. */
2647
- onDensityChange?: (density: DensityName) => void;` : "";
2648
- const densityState = isPro ? `
2649
- const [density, setDensityState] = React.useState<DensityName>(initialDensity);
2650
-
2651
- React.useEffect(() => {
2652
- setDensityState(initialDensity);
2653
- }, [initialDensity]);
2654
-
2655
- const setDensity = React.useCallback(
2656
- (next: DensityName) => {
2657
- setDensityState(next);
2658
- onDensityChange?.(next);
2659
- },
2660
- [onDensityChange],
2661
- );
2662
- ` : "";
2663
- const densityDestructure = isPro ? `,
2664
- density: initialDensity = "${defaultDensity}",
2665
- onDensityChange,` : "";
2666
- const densityProviderOpen = isPro ? `
2667
- <DensityContext.Provider value={{ density, setDensity }}>` : "";
2668
- const densityDataAttr = isPro ? ` data-density={density}` : "";
2669
- const densityProviderClose = isPro ? `
2670
- </DensityContext.Provider>` : "";
2671
2591
  return `/**
2672
2592
  * ThemeProvider \u2014 ${config.meta.name}
2673
2593
  *
@@ -2679,27 +2599,39 @@ export function useDensity(): DensityContextValue {
2679
2599
  * import "@${config.meta.name}/tokens/light.css"; // or dark.css
2680
2600
  * import { ThemeProvider } from "@${config.meta.name}";
2681
2601
  *
2682
- * <ThemeProvider theme="light"${isPro ? ` density="${defaultDensity}"` : ""}>
2602
+ * <ThemeProvider theme="light" density="${defaultDensity}">
2683
2603
  * <App />
2684
2604
  * </ThemeProvider>
2685
2605
  */
2686
2606
 
2687
- import React from "react";${densityImport}
2607
+ import React from "react";
2608
+ import "../tokens/density.css";
2688
2609
 
2689
2610
  // \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
2690
2611
 
2691
2612
  export type ThemeName = ${themeType};
2692
- ${densityTypes}
2613
+
2614
+ export type DensityName = "compact" | "comfortable" | "spacious";
2615
+
2693
2616
  export interface ThemeContextValue {
2694
2617
  theme: ThemeName;
2695
2618
  setTheme: (theme: ThemeName) => void;
2696
2619
  }
2697
- ${densityContextTypes}
2620
+
2621
+ export interface DensityContextValue {
2622
+ density: DensityName;
2623
+ setDensity: (density: DensityName) => void;
2624
+ }
2625
+
2698
2626
  export interface ThemeProviderProps {
2699
2627
  /** Initial theme. Defaults to "${defaultTheme}". */
2700
2628
  theme?: ThemeName;
2701
2629
  /** Called when setTheme is invoked \u2014 use to persist theme preference. */
2702
- onThemeChange?: (theme: ThemeName) => void;${densityProp}${densityOnChangeProp}
2630
+ onThemeChange?: (theme: ThemeName) => void;
2631
+ /** Component density. Requires density.css to be imported. Defaults to "${defaultDensity}". */
2632
+ density?: DensityName;
2633
+ /** Called when setDensity is invoked. */
2634
+ onDensityChange?: (density: DensityName) => void;
2703
2635
  children: React.ReactNode;
2704
2636
  }
2705
2637
 
@@ -2717,12 +2649,27 @@ export const ThemeContext = React.createContext<ThemeContextValue>({
2717
2649
  export function useTheme(): ThemeContextValue {
2718
2650
  return React.useContext(ThemeContext);
2719
2651
  }
2720
- ${densityContext}
2652
+
2653
+ export const DensityContext = React.createContext<DensityContextValue>({
2654
+ density: "${defaultDensity}",
2655
+ setDensity: () => undefined,
2656
+ });
2657
+
2658
+ /**
2659
+ * Hook to read and change the current density.
2660
+ * Must be used inside a <ThemeProvider>.
2661
+ */
2662
+ export function useDensity(): DensityContextValue {
2663
+ return React.useContext(DensityContext);
2664
+ }
2665
+
2721
2666
  // \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
2722
2667
 
2723
2668
  export function ThemeProvider({
2724
2669
  theme: initialTheme = "${defaultTheme}",
2725
- onThemeChange,${densityDestructure}
2670
+ onThemeChange,
2671
+ density: initialDensity = "${defaultDensity}",
2672
+ onDensityChange,
2726
2673
  children,
2727
2674
  }: ThemeProviderProps) {
2728
2675
  const [theme, setThemeState] = React.useState<ThemeName>(initialTheme);
@@ -2738,13 +2685,29 @@ export function ThemeProvider({
2738
2685
  },
2739
2686
  [onThemeChange],
2740
2687
  );
2741
- ${densityState}
2742
- return (${densityProviderOpen}
2688
+
2689
+ const [density, setDensityState] = React.useState<DensityName>(initialDensity);
2690
+
2691
+ React.useEffect(() => {
2692
+ setDensityState(initialDensity);
2693
+ }, [initialDensity]);
2694
+
2695
+ const setDensity = React.useCallback(
2696
+ (next: DensityName) => {
2697
+ setDensityState(next);
2698
+ onDensityChange?.(next);
2699
+ },
2700
+ [onDensityChange],
2701
+ );
2702
+
2703
+ return (
2704
+ <DensityContext.Provider value={{ density, setDensity }}>
2743
2705
  <ThemeContext.Provider value={{ theme, setTheme }}>
2744
- <div data-theme={theme}${densityDataAttr} style={{ display: "contents" }}>
2706
+ <div data-theme={theme} data-density={density} style={{ display: "contents" }}>
2745
2707
  {children}
2746
2708
  </div>
2747
- </ThemeContext.Provider>${densityProviderClose}
2709
+ </ThemeContext.Provider>
2710
+ </DensityContext.Provider>
2748
2711
  );
2749
2712
  }
2750
2713
  `;
@@ -2761,9 +2724,9 @@ function generateComponentIndex(config, componentNames) {
2761
2724
  ""
2762
2725
  ];
2763
2726
  for (const name of componentNames) {
2764
- lines.push(`export * from "./${name}";`);
2727
+ lines.push(`export * from "./components/${name}/${name}";`);
2765
2728
  }
2766
- lines.push(`export * from "./ThemeProvider";`);
2729
+ lines.push(`export * from "./components/ThemeProvider/ThemeProvider";`);
2767
2730
  lines.push("");
2768
2731
  return lines.join("\n");
2769
2732
  }
@@ -3166,12 +3129,14 @@ function generatePackageJson(config, componentNames) {
3166
3129
  ),
3167
3130
  "./tokens": "./tokens/tokens.js",
3168
3131
  "./tailwind": "./tokens/tailwind.js",
3169
- "./metadata": "./metadata/index.json",
3170
3132
  ...Object.fromEntries(
3171
- componentNames.map((c) => [`./metadata/${c}`, `./metadata/${c}.json`])
3133
+ componentNames.map((c) => {
3134
+ const pascal = c.charAt(0).toUpperCase() + c.slice(1);
3135
+ return [`./components/${pascal}`, `./components/${pascal}/${pascal}.json`];
3136
+ })
3172
3137
  )
3173
3138
  },
3174
- files: ["dist", "tokens", "metadata", "CHANGELOG.md"],
3139
+ files: ["dist", "tokens", "components", "CHANGELOG.md"],
3175
3140
  scripts: {
3176
3141
  build: "tsc",
3177
3142
  prepublishOnly: "npm run build"
@@ -3211,7 +3176,7 @@ function generateTsConfig() {
3211
3176
  moduleResolution: "NodeNext",
3212
3177
  lib: ["ES2020", "DOM"],
3213
3178
  outDir: "./dist",
3214
- rootDir: "./src",
3179
+ rootDir: ".",
3215
3180
  declaration: true,
3216
3181
  declarationMap: true,
3217
3182
  sourceMap: true,
@@ -3220,7 +3185,7 @@ function generateTsConfig() {
3220
3185
  skipLibCheck: true,
3221
3186
  jsx: "react-jsx"
3222
3187
  },
3223
- include: ["src/**/*"],
3188
+ include: ["index.ts", "components/**/*"],
3224
3189
  exclude: ["node_modules", "dist"]
3225
3190
  },
3226
3191
  null,
@@ -3269,7 +3234,10 @@ function App() {
3269
3234
  ## Components
3270
3235
 
3271
3236
  ${componentNames.map(
3272
- (c) => `- **${c.charAt(0).toUpperCase() + c.slice(1)}** \u2014 see \`metadata/${c}.json\` for contract`
3237
+ (c) => {
3238
+ const pascal = c.charAt(0).toUpperCase() + c.slice(1);
3239
+ return `- **${pascal}** \u2014 see \`components/${pascal}/${pascal}.json\` for props and usage`;
3240
+ }
3273
3241
  ).join("\n")}
3274
3242
 
3275
3243
  ## Themes
@@ -3344,20 +3312,10 @@ every semantic and component token that references it.
3344
3312
 
3345
3313
  ## AI tool integration
3346
3314
 
3347
- The \`metadata/\` directory contains machine-readable component contracts.
3315
+ Each component ships with a machine-readable JSON contract (e.g. \`components/Button/Button.json\`).
3348
3316
  AI coding assistants (Copilot, Cursor, Claude Code) can read these to
3349
3317
  generate UI that respects your governance rules automatically.
3350
3318
 
3351
- \`\`\`json
3352
- // ${pkgName}/metadata/button.json
3353
- {
3354
- "component": "Button",
3355
- "allowedVariants": ["primary", "secondary", "danger", "ghost"],
3356
- "requiredProps": ["aria-label"],
3357
- "accessibilityContract": { "keyboard": true, "focusRing": true }
3358
- }
3359
- \`\`\`
3360
-
3361
3319
  ---
3362
3320
 
3363
3321
  Generated by [dsforge](https://github.com/nghitrum/dsforge) v${config.meta.version}.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nghitrum/dsforge",
3
- "version": "0.1.5-alpha.9",
3
+ "version": "0.2.0",
4
4
  "description": "AI-native design system generator — tokens → components → docs → npm",
5
5
  "keywords": [
6
6
  "design-system",