@matteoaliano/forest-ui 1.2.1 → 1.2.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.
package/bin/dense.mjs ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ // Dense mode for skill reference docs.
3
+ //
4
+ // ONE transform, lossless: strip the repeated "Import" column from every
5
+ // GFM table. The import path `@matteoaliano/forest-ui` is invariant and
6
+ // stated once at the top of the file, so `import { X } from "..."` on every
7
+ // row is pure redundancy. Names that AREN'T derivable from the component
8
+ // name (family members like TableHead/TableBody, type exports like
9
+ // AppShellNavItem) are folded into the Component cell so nothing is lost.
10
+ //
11
+ // Files with no Import column pass through byte-identical (never inflated).
12
+ //
13
+ // Used as a library by sync.mjs (densifies the shipped plugin/dogfood copies)
14
+ // and as a CLI for inspection:
15
+ // node bin/dense.mjs <file.md> # print dense version to stdout
16
+ // node bin/dense.mjs <file.md> --write # write references/dense/<file>.md
17
+ // node bin/dense.mjs <file.md> --stats # print savings to stderr
18
+ // node bin/dense.mjs <file.md> --check # assert nothing was lost
19
+
20
+ import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
21
+ import { basename, dirname, join } from "node:path";
22
+ import { fileURLToPath } from "node:url";
23
+
24
+ // Split a GFM table row on unescaped pipes (unions use `\|` as a literal pipe).
25
+ const cells = (line) =>
26
+ line
27
+ .trim()
28
+ .replace(/^\||\|$/g, "")
29
+ .split(/(?<!\\)\|/)
30
+ .map((c) => c.trim());
31
+
32
+ const bareNames = (str) =>
33
+ [...str.matchAll(/`([^`]+)`/g)].map((m) => m[1].replace(/^type\s+/, ""));
34
+
35
+ export function densify(src) {
36
+ let didStrip = false;
37
+
38
+ const transformTable = (block) => {
39
+ const rows = block.map(cells);
40
+ const importCol = rows[0].findIndex((h) => h === "Import");
41
+ if (importCol === -1) return block; // no Import column → untouched
42
+ didStrip = true;
43
+
44
+ const nameCol = 0; // Component/Hook name is always the first column
45
+ const out = rows.map((cols, i) => {
46
+ if (i > 1) {
47
+ const importNames = (cols[importCol].match(/import\s*\{([^}]*)\}/)?.[1] ?? "")
48
+ .split(",")
49
+ .map((n) => n.trim())
50
+ .filter(Boolean);
51
+ const shown = new Set(bareNames(cols[nameCol]));
52
+ const extras = importNames.filter((n) => !shown.has(n.replace(/^type\s+/, "")));
53
+ if (extras.length) cols[nameCol] += ` — also: ${extras.join(", ")}`;
54
+ }
55
+ return cols.filter((_, c) => c !== importCol);
56
+ });
57
+ return out.map((cols) => `| ${cols.join(" | ")} |`);
58
+ };
59
+
60
+ // Walk the file, transforming each contiguous table block in place.
61
+ const result = [];
62
+ let table = [];
63
+ const flush = () => {
64
+ if (table.length) result.push(...transformTable(table));
65
+ table = [];
66
+ };
67
+ for (const line of src.split("\n")) {
68
+ if (line.trim().startsWith("|")) table.push(line);
69
+ else {
70
+ flush();
71
+ result.push(line);
72
+ }
73
+ }
74
+ flush();
75
+
76
+ // Insert the invariant note once, right after the H1 — only when we stripped
77
+ // something, so files with no Import column stay byte-identical.
78
+ const h1 = result.findIndex((l) => l.startsWith("# "));
79
+ if (didStrip && h1 !== -1) {
80
+ result.splice(
81
+ h1 + 1,
82
+ 0,
83
+ "",
84
+ "> **Dense.** Import column dropped — every name imports from `@matteoaliano/forest-ui`. `— also:` lists extra names/types that import alongside.",
85
+ );
86
+ }
87
+
88
+ return result.join("\n");
89
+ }
90
+
91
+ // ── CLI ──
92
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
93
+ const [file, ...flags] = process.argv.slice(2);
94
+ if (!file) {
95
+ console.error("usage: node bin/dense.mjs <file.md> [--write|--stats|--check]");
96
+ process.exit(1);
97
+ }
98
+ const src = readFileSync(file, "utf8");
99
+ const dense = densify(src);
100
+
101
+ if (flags.includes("--check")) {
102
+ const imported = [...src.matchAll(/import\s*\{([^}]*)\}/g)]
103
+ .flatMap((m) => m[1].split(","))
104
+ .map((n) => n.trim().replace(/^type\s+/, ""))
105
+ .filter(Boolean);
106
+ const lost = imported.filter((n) => !dense.includes(n));
107
+ if (lost.length) {
108
+ console.error(`FAIL: names lost in dense output: ${lost.join(", ")}`);
109
+ process.exit(1);
110
+ }
111
+ console.error(`ok: all ${imported.length} imported names preserved`);
112
+ } else if (flags.includes("--stats")) {
113
+ const est = (s) => Math.round(s.length / 4);
114
+ const pct = (1 - dense.length / src.length) * 100;
115
+ console.error(
116
+ `${basename(file)}: ${src.length} → ${dense.length} chars ` +
117
+ `(~${est(src)} → ~${est(dense)} tokens, -${pct.toFixed(0)}%)`,
118
+ );
119
+ } else if (flags.includes("--write")) {
120
+ const dir = join(dirname(file), "dense");
121
+ mkdirSync(dir, { recursive: true });
122
+ const dest = join(dir, basename(file));
123
+ writeFileSync(dest, dense);
124
+ console.error(`wrote ${dest}`);
125
+ } else {
126
+ process.stdout.write(dense);
127
+ }
128
+ }
package/bin/sync.mjs CHANGED
@@ -14,6 +14,7 @@
14
14
  import { mkdirSync, readdirSync, cpSync, readFileSync, writeFileSync } from "node:fs";
15
15
  import { resolve, dirname } from "node:path";
16
16
  import { fileURLToPath } from "node:url";
17
+ import { densify } from "./dense.mjs";
17
18
 
18
19
  const __dirname = dirname(fileURLToPath(import.meta.url));
19
20
  const skillsDir = resolve(__dirname, "..", "skills");
@@ -47,13 +48,26 @@ const skillFolders = readdirSync(skillsDir, { withFileTypes: true })
47
48
  .filter((d) => d.isDirectory())
48
49
  .map((d) => d.name);
49
50
 
51
+ // Densify every .md in the shipped copies. Sources under skills/ stay full
52
+ // and human-readable; consumers transparently get the token-efficient form
53
+ // (same filenames, so SKILL.md links are unchanged). No-op on files without
54
+ // an Import column, so it never inflates anything.
55
+ const densifyTree = (dir) => {
56
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
57
+ const p = resolve(dir, entry.name);
58
+ if (entry.isDirectory()) densifyTree(p);
59
+ else if (entry.name.endsWith(".md")) writeFileSync(p, densify(readFileSync(p, "utf-8")));
60
+ }
61
+ };
62
+
50
63
  let synced = 0;
51
64
  for (const target of targets) {
52
65
  for (const folder of skillFolders) {
53
66
  const dest = resolve(target, folder);
54
67
  mkdirSync(dest, { recursive: true });
55
68
  cpSync(resolve(skillsDir, folder), dest, { recursive: true });
56
- console.log(` ✅ ${dest.replace(repoRoot + "/", "")}/`);
69
+ densifyTree(dest);
70
+ console.log(` ✅ ${dest.replace(repoRoot + "/", "")}/ (dense)`);
57
71
  synced++;
58
72
  }
59
73
  }
package/dist/index.d.mts CHANGED
@@ -242,6 +242,14 @@ declare const PRODUCT_ARTWORK: {
242
242
  readonly "revenue-pulse": Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
243
243
  readonly contixdb: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
244
244
  readonly feedati: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
245
+ readonly adwize: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
246
+ readonly "control-room": Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
247
+ readonly "hr-cost-manager": Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
248
+ readonly lightbox: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
249
+ readonly "product-categorizer": Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
250
+ readonly refain: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
251
+ readonly stat: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
252
+ readonly taxonomy: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
245
253
  };
246
254
  type LogoProductKey = keyof typeof PRODUCT_ARTWORK;
247
255
 
package/dist/index.d.ts CHANGED
@@ -242,6 +242,14 @@ declare const PRODUCT_ARTWORK: {
242
242
  readonly "revenue-pulse": Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
243
243
  readonly contixdb: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
244
244
  readonly feedati: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
245
+ readonly adwize: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
246
+ readonly "control-room": Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
247
+ readonly "hr-cost-manager": Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
248
+ readonly lightbox: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
249
+ readonly "product-categorizer": Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
250
+ readonly refain: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
251
+ readonly stat: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
252
+ readonly taxonomy: Record<LogoVariant$1, Partial<Record<LogoColor$1, LogoArtwork>>>;
245
253
  };
246
254
  type LogoProductKey = keyof typeof PRODUCT_ARTWORK;
247
255