@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 +128 -0
- package/bin/sync.mjs +15 -1
- package/dist/index.d.mts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +253 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +253 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/skills/forest-alkemy-plus/SKILL.md +1 -1
- package/skills/forest-alkemy-plus/references/components.md +1 -1
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
|
-
|
|
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
|
|