@texturehq/edges 1.7.1 → 1.7.3
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/components.manifest.json +914 -1406
- package/dist/index.cjs +9 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +98 -275
- package/dist/index.d.ts +98 -275
- package/dist/index.js +9 -9
- package/dist/index.js.map +1 -1
- package/dist/{server-DAd0A202.d.cts → server-7ZzifHri.d.cts} +191 -8
- package/dist/{server-DAd0A202.d.ts → server-7ZzifHri.d.ts} +191 -8
- package/dist/server.cjs +13 -13
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +13 -13
- package/dist/server.js.map +1 -1
- package/dist/styles.css +5 -16
- package/dist/utilities.manifest.json +4 -1039
- package/package.json +2 -2
- package/scripts/generate-edges-docs.js +970 -0
- package/scripts/setup-cursor-rules.js +18 -0
- package/scripts/generate-components-manifest.js +0 -188
- package/scripts/generate-utilities-manifest.js +0 -392
|
@@ -16,6 +16,24 @@ const setupCursorRules = () => {
|
|
|
16
16
|
// ignore
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
// Clean up legacy generated edges context files
|
|
20
|
+
const legacyFiles = [
|
|
21
|
+
path.join(cwd, ".cursor/rules/edges-components-old.mdc"),
|
|
22
|
+
path.join(cwd, ".claude/edges-components.md"),
|
|
23
|
+
// Add any other legacy file names here as needed
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
legacyFiles.forEach((file) => {
|
|
27
|
+
if (fs.existsSync(file)) {
|
|
28
|
+
try {
|
|
29
|
+
fs.unlinkSync(file);
|
|
30
|
+
console.log(`🧹 Cleaned up legacy file: ${path.relative(cwd, file)}`);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
// Ignore cleanup errors
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
19
37
|
const cursorTemplatesDir = path.join(
|
|
20
38
|
path.dirname(new URL(import.meta.url).pathname),
|
|
21
39
|
"../templates/cursor-rules"
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
// generate-components-manifest.js
|
|
2
|
-
// Build-time script that scans exported components and creates dist/components.manifest.json
|
|
3
|
-
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import path from "path";
|
|
6
|
-
|
|
7
|
-
const ROOT = process.cwd();
|
|
8
|
-
const SRC_DIR = path.join(ROOT, "src");
|
|
9
|
-
const DIST_DIR = path.join(ROOT, "dist");
|
|
10
|
-
|
|
11
|
-
function read(file) {
|
|
12
|
-
return fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function ensureDir(dir) {
|
|
16
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function extractExportsFromIndex(indexContent) {
|
|
20
|
-
// Matches: export { Button } from "./components/Button";
|
|
21
|
-
const re = /export\s+\{\s*([A-Za-z0-9_,\s]+)\s*\}\s+from\s+"\.\/components\/([A-Za-z0-9_/.-]+)"/g;
|
|
22
|
-
const results = [];
|
|
23
|
-
let m;
|
|
24
|
-
while ((m = re.exec(indexContent))) {
|
|
25
|
-
const exported = m[1]
|
|
26
|
-
.split(",")
|
|
27
|
-
.map((s) => s.trim())
|
|
28
|
-
.filter(Boolean);
|
|
29
|
-
const rel = m[2];
|
|
30
|
-
exported.forEach((name) => {
|
|
31
|
-
// Only keep PascalCase (components); ignore sub-exports like Tab, TabList if not directory-named
|
|
32
|
-
results.push({ name, relPath: rel });
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
return results;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function findComponentFile(relPath) {
|
|
39
|
-
// Components can be in directories like Button/Button.tsx or charts/ChartAxis/ChartAxis.tsx
|
|
40
|
-
const base = path.join(SRC_DIR, "components", relPath);
|
|
41
|
-
// Get the component name (last part of the path)
|
|
42
|
-
const componentName = relPath.split("/").pop();
|
|
43
|
-
const candidates = [
|
|
44
|
-
path.join(base, `${componentName}.tsx`),
|
|
45
|
-
path.join(base, `${componentName}.ts`),
|
|
46
|
-
path.join(base, `index.tsx`),
|
|
47
|
-
path.join(base, `index.ts`),
|
|
48
|
-
];
|
|
49
|
-
for (const f of candidates) {
|
|
50
|
-
if (fs.existsSync(f)) return f;
|
|
51
|
-
}
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function getJsdocAbove(content, idx) {
|
|
56
|
-
// Find the last JSDoc block before idx
|
|
57
|
-
const upTo = content.slice(0, idx);
|
|
58
|
-
const start = upTo.lastIndexOf("/**");
|
|
59
|
-
const end = upTo.lastIndexOf("*/");
|
|
60
|
-
if (start !== -1 && end !== -1 && end > start) {
|
|
61
|
-
const block = upTo.slice(start + 3, end).trim();
|
|
62
|
-
return block
|
|
63
|
-
.split("\n")
|
|
64
|
-
.map((l) => l.replace(/^\s*\*\s?/, "").trim())
|
|
65
|
-
.join(" ")
|
|
66
|
-
.trim();
|
|
67
|
-
}
|
|
68
|
-
return "";
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function extractProps(content, componentName) {
|
|
72
|
-
// Try `export interface <Name>Props` first
|
|
73
|
-
const ifaceRe = new RegExp(
|
|
74
|
-
`export\\s+interface\\s+${componentName}Props\\s*(?:extends\\s+[^{]+)?\\{([\\s\\S]*?)\\}`,
|
|
75
|
-
"m"
|
|
76
|
-
);
|
|
77
|
-
let m = ifaceRe.exec(content);
|
|
78
|
-
if (!m) {
|
|
79
|
-
// Try generic name: any exported *Props
|
|
80
|
-
const anyIfaceRe =
|
|
81
|
-
/export\s+interface\s+([A-Za-z0-9_]+Props)\s*(?:extends\s+[^{]+)?\{([\s\S]*?)\}/m;
|
|
82
|
-
m = anyIfaceRe.exec(content);
|
|
83
|
-
}
|
|
84
|
-
if (!m) {
|
|
85
|
-
// Try type alias
|
|
86
|
-
const typeRe = new RegExp(
|
|
87
|
-
`export\\s+type\\s+${componentName}Props\\s*=\\s*(?:[^{&]+&\s*)?([\\s\\S]*?)\\}`,
|
|
88
|
-
"m"
|
|
89
|
-
);
|
|
90
|
-
m = typeRe.exec(content);
|
|
91
|
-
}
|
|
92
|
-
const props = [];
|
|
93
|
-
if (m) {
|
|
94
|
-
const body = m[2] || m[1] || "";
|
|
95
|
-
const lines = body
|
|
96
|
-
.split("\n")
|
|
97
|
-
.map((l) => l.trim())
|
|
98
|
-
.filter((l) => !!l && !l.startsWith("//"));
|
|
99
|
-
for (const line of lines) {
|
|
100
|
-
// Match: name?: Type; or name: Type;
|
|
101
|
-
const pm = /^(readonly\s+)?([A-Za-z0-9_]+)\??:\s*([^;]+);?/.exec(line);
|
|
102
|
-
if (pm) {
|
|
103
|
-
const name = pm[2];
|
|
104
|
-
const type = pm[3].trim();
|
|
105
|
-
props.push({ name, type });
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return props;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function run() {
|
|
113
|
-
try {
|
|
114
|
-
const indexPath = path.join(SRC_DIR, "index.ts");
|
|
115
|
-
const indexContent = read(indexPath);
|
|
116
|
-
const exports = extractExportsFromIndex(indexContent);
|
|
117
|
-
|
|
118
|
-
const components = [];
|
|
119
|
-
for (const { name, relPath } of exports) {
|
|
120
|
-
// Only treat PascalCase names as components
|
|
121
|
-
if (!/^[A-Z]/.test(name)) continue;
|
|
122
|
-
|
|
123
|
-
const componentFile = findComponentFile(relPath);
|
|
124
|
-
if (!componentFile) continue;
|
|
125
|
-
const content = read(componentFile);
|
|
126
|
-
|
|
127
|
-
// Description: JSDoc above various export patterns
|
|
128
|
-
let idx = -1;
|
|
129
|
-
let description = "";
|
|
130
|
-
|
|
131
|
-
// Try multiple patterns to find the component export
|
|
132
|
-
const patterns = [
|
|
133
|
-
`export function ${name}`,
|
|
134
|
-
`export const ${name}:`,
|
|
135
|
-
`export const ${name} =`,
|
|
136
|
-
`export class ${name}`,
|
|
137
|
-
new RegExp(`export\\s+const\\s+${name}\\s*[:=]`),
|
|
138
|
-
new RegExp(`export\\s+function\\s+${name}\\s*[(<]`),
|
|
139
|
-
new RegExp(`export\\s+class\\s+${name}\\s*[{<]`),
|
|
140
|
-
new RegExp(`export\\s+(interface|type)\\s+${name}Props\\b`),
|
|
141
|
-
];
|
|
142
|
-
|
|
143
|
-
for (const pattern of patterns) {
|
|
144
|
-
if (typeof pattern === "string") {
|
|
145
|
-
idx = content.indexOf(pattern);
|
|
146
|
-
} else {
|
|
147
|
-
const match = content.search(pattern);
|
|
148
|
-
if (match !== -1) idx = match;
|
|
149
|
-
}
|
|
150
|
-
if (idx !== -1) break;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// If we found an export, get the JSDoc above it
|
|
154
|
-
if (idx !== -1) {
|
|
155
|
-
description = getJsdocAbove(content, idx);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const props = extractProps(content, name);
|
|
159
|
-
|
|
160
|
-
components.push({
|
|
161
|
-
name,
|
|
162
|
-
importRoot: "@texturehq/edges",
|
|
163
|
-
importPath: `@texturehq/edges/components/${name}`,
|
|
164
|
-
description,
|
|
165
|
-
props,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
ensureDir(DIST_DIR);
|
|
170
|
-
const manifest = {
|
|
171
|
-
version: process.env.npm_package_version || "unknown",
|
|
172
|
-
generatedAt: new Date().toISOString(),
|
|
173
|
-
components: components.sort((a, b) => a.name.localeCompare(b.name)),
|
|
174
|
-
};
|
|
175
|
-
fs.writeFileSync(
|
|
176
|
-
path.join(DIST_DIR, "components.manifest.json"),
|
|
177
|
-
JSON.stringify(manifest, null, 2)
|
|
178
|
-
);
|
|
179
|
-
console.log(
|
|
180
|
-
`Generated ${path.join("dist", "components.manifest.json")} with ${components.length} components.`
|
|
181
|
-
);
|
|
182
|
-
} catch (err) {
|
|
183
|
-
console.error("Failed to generate components manifest:", err);
|
|
184
|
-
process.exitCode = 1;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
run();
|
|
@@ -1,392 +0,0 @@
|
|
|
1
|
-
// generate-utilities-manifest.js
|
|
2
|
-
// Build-time script that scans exported utilities and creates dist/utilities.manifest.json
|
|
3
|
-
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import path from "path";
|
|
6
|
-
|
|
7
|
-
const ROOT = process.cwd();
|
|
8
|
-
const SRC_DIR = path.join(ROOT, "src");
|
|
9
|
-
const DIST_DIR = path.join(ROOT, "dist");
|
|
10
|
-
|
|
11
|
-
function read(file) {
|
|
12
|
-
return fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function ensureDir(dir) {
|
|
16
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Extract JSDoc comment from text above a function/const
|
|
21
|
-
*/
|
|
22
|
-
function extractJSDoc(content, functionName) {
|
|
23
|
-
// Look for JSDoc block immediately before the function/const declaration
|
|
24
|
-
const patterns = [
|
|
25
|
-
// Function declaration: /** ... */ export function name OR export async function name
|
|
26
|
-
new RegExp(
|
|
27
|
-
`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+(?:async\\s+)?function\\s+${functionName}\\b`,
|
|
28
|
-
"s"
|
|
29
|
-
),
|
|
30
|
-
// Arrow function: /** ... */ export const name =
|
|
31
|
-
new RegExp(`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+const\\s+${functionName}\\s*=`, "s"),
|
|
32
|
-
// Type export: /** ... */ export type name
|
|
33
|
-
new RegExp(`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+type\\s+${functionName}\\b`, "s"),
|
|
34
|
-
// Interface export: /** ... */ export interface name
|
|
35
|
-
new RegExp(
|
|
36
|
-
`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+interface\\s+${functionName}\\b`,
|
|
37
|
-
"s"
|
|
38
|
-
),
|
|
39
|
-
];
|
|
40
|
-
|
|
41
|
-
for (const pattern of patterns) {
|
|
42
|
-
const match = content.match(pattern);
|
|
43
|
-
if (match) {
|
|
44
|
-
const jsdocMatch = match[0].match(/\/\*\*([\s\S]*?)\*\//);
|
|
45
|
-
if (jsdocMatch) {
|
|
46
|
-
// Clean up the JSDoc comment
|
|
47
|
-
const cleaned = jsdocMatch[1]
|
|
48
|
-
.split("\n")
|
|
49
|
-
.map((line) => line.replace(/^\s*\*\s?/, "").trim())
|
|
50
|
-
.filter((line) => line && !line.startsWith("@"))
|
|
51
|
-
.join(" ")
|
|
52
|
-
.trim();
|
|
53
|
-
return cleaned;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return "";
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Extract @param tags from JSDoc
|
|
62
|
-
*/
|
|
63
|
-
function extractParams(content, functionName) {
|
|
64
|
-
const patterns = [
|
|
65
|
-
new RegExp(
|
|
66
|
-
`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+(?:async\\s+)?function\\s+${functionName}\\b`,
|
|
67
|
-
"s"
|
|
68
|
-
),
|
|
69
|
-
new RegExp(`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+const\\s+${functionName}\\s*=`, "s"),
|
|
70
|
-
];
|
|
71
|
-
|
|
72
|
-
for (const pattern of patterns) {
|
|
73
|
-
const match = content.match(pattern);
|
|
74
|
-
if (match) {
|
|
75
|
-
const jsdocMatch = match[0].match(/\/\*\*([\s\S]*?)\*\//);
|
|
76
|
-
if (jsdocMatch) {
|
|
77
|
-
const params = [];
|
|
78
|
-
const lines = jsdocMatch[1].split("\n");
|
|
79
|
-
for (const line of lines) {
|
|
80
|
-
const paramMatch = line.match(/@param\s+(?:\{([^}]+)\})?\s*(\S+)\s*-?\s*(.*)/);
|
|
81
|
-
if (paramMatch) {
|
|
82
|
-
params.push({
|
|
83
|
-
name: paramMatch[2],
|
|
84
|
-
type: paramMatch[1] || "unknown",
|
|
85
|
-
description: paramMatch[3].trim(),
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return params;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return [];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Extract @returns tag from JSDoc
|
|
98
|
-
*/
|
|
99
|
-
function extractReturns(content, functionName) {
|
|
100
|
-
const patterns = [
|
|
101
|
-
new RegExp(
|
|
102
|
-
`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+(?:async\\s+)?function\\s+${functionName}\\b`,
|
|
103
|
-
"s"
|
|
104
|
-
),
|
|
105
|
-
new RegExp(`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+const\\s+${functionName}\\s*=`, "s"),
|
|
106
|
-
];
|
|
107
|
-
|
|
108
|
-
for (const pattern of patterns) {
|
|
109
|
-
const match = content.match(pattern);
|
|
110
|
-
if (match) {
|
|
111
|
-
const jsdocMatch = match[0].match(/\/\*\*([\s\S]*?)\*\//);
|
|
112
|
-
if (jsdocMatch) {
|
|
113
|
-
const lines = jsdocMatch[1].split("\n");
|
|
114
|
-
for (const line of lines) {
|
|
115
|
-
const returnMatch = line.match(/@returns?\s+(?:\{([^}]+)\})?\s*(.*)/);
|
|
116
|
-
if (returnMatch) {
|
|
117
|
-
return {
|
|
118
|
-
type: returnMatch[1] || "unknown",
|
|
119
|
-
description: returnMatch[2].trim(),
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Extract @example from JSDoc
|
|
131
|
-
*/
|
|
132
|
-
function extractExample(content, functionName) {
|
|
133
|
-
const patterns = [
|
|
134
|
-
new RegExp(
|
|
135
|
-
`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+(?:async\\s+)?function\\s+${functionName}\\b`,
|
|
136
|
-
"s"
|
|
137
|
-
),
|
|
138
|
-
new RegExp(`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+const\\s+${functionName}\\s*=`, "s"),
|
|
139
|
-
];
|
|
140
|
-
|
|
141
|
-
for (const pattern of patterns) {
|
|
142
|
-
const match = content.match(pattern);
|
|
143
|
-
if (match) {
|
|
144
|
-
const jsdocMatch = match[0].match(/\/\*\*([\s\S]*?)\*\//);
|
|
145
|
-
if (jsdocMatch) {
|
|
146
|
-
const exampleMatch = jsdocMatch[1].match(/@example\s*([\s\S]*?)(?=@\w+|$)/);
|
|
147
|
-
if (exampleMatch) {
|
|
148
|
-
// Clean up the example
|
|
149
|
-
const example = exampleMatch[1]
|
|
150
|
-
.split("\n")
|
|
151
|
-
.map((line) => line.replace(/^\s*\*\s?/, ""))
|
|
152
|
-
.join("\n")
|
|
153
|
-
.trim();
|
|
154
|
-
// Remove ```typescript and ``` markers if present
|
|
155
|
-
return example
|
|
156
|
-
.replace(/^```\w*\n?/, "")
|
|
157
|
-
.replace(/\n?```$/, "")
|
|
158
|
-
.trim();
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
return null;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Extract exported functions from a file
|
|
168
|
-
*/
|
|
169
|
-
function extractExports(filePath) {
|
|
170
|
-
const content = read(filePath);
|
|
171
|
-
if (!content) return [];
|
|
172
|
-
|
|
173
|
-
const utilities = [];
|
|
174
|
-
|
|
175
|
-
// Match export function declarations
|
|
176
|
-
const functionPattern = /export\s+(?:async\s+)?function\s+(\w+)/g;
|
|
177
|
-
let match;
|
|
178
|
-
while ((match = functionPattern.exec(content))) {
|
|
179
|
-
const name = match[1];
|
|
180
|
-
utilities.push({
|
|
181
|
-
name,
|
|
182
|
-
type: "function",
|
|
183
|
-
description: extractJSDoc(content, name),
|
|
184
|
-
params: extractParams(content, name),
|
|
185
|
-
returns: extractReturns(content, name),
|
|
186
|
-
example: extractExample(content, name),
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Match export const arrow functions and constants
|
|
191
|
-
const constPattern = /export\s+const\s+(\w+)\s*(?::\s*[^=]+)?\s*=/g;
|
|
192
|
-
while ((match = constPattern.exec(content))) {
|
|
193
|
-
const name = match[1];
|
|
194
|
-
// Check if it's likely a function (has => or function keyword after =)
|
|
195
|
-
const afterMatch = content.slice(
|
|
196
|
-
match.index + match[0].length,
|
|
197
|
-
match.index + match[0].length + 50
|
|
198
|
-
);
|
|
199
|
-
const isFunction = afterMatch.includes("=>") || afterMatch.includes("function");
|
|
200
|
-
|
|
201
|
-
utilities.push({
|
|
202
|
-
name,
|
|
203
|
-
type: isFunction ? "function" : "constant",
|
|
204
|
-
description: extractJSDoc(content, name),
|
|
205
|
-
params: isFunction ? extractParams(content, name) : [],
|
|
206
|
-
returns: isFunction ? extractReturns(content, name) : null,
|
|
207
|
-
example: extractExample(content, name),
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Match export type/interface (for documentation purposes)
|
|
212
|
-
const typePattern = /export\s+(?:type|interface)\s+(\w+)/g;
|
|
213
|
-
while ((match = typePattern.exec(content))) {
|
|
214
|
-
const name = match[1];
|
|
215
|
-
utilities.push({
|
|
216
|
-
name,
|
|
217
|
-
type: "type",
|
|
218
|
-
description: extractJSDoc(content, name),
|
|
219
|
-
example: extractExample(content, name),
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
return utilities;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Scan a directory for utility files
|
|
228
|
-
*/
|
|
229
|
-
function scanDirectory(dir, category) {
|
|
230
|
-
const utilities = [];
|
|
231
|
-
|
|
232
|
-
if (!fs.existsSync(dir)) return utilities;
|
|
233
|
-
|
|
234
|
-
const files = fs.readdirSync(dir);
|
|
235
|
-
|
|
236
|
-
for (const file of files) {
|
|
237
|
-
const filePath = path.join(dir, file);
|
|
238
|
-
const stat = fs.statSync(filePath);
|
|
239
|
-
|
|
240
|
-
if (stat.isDirectory() && file !== "__tests__" && file !== "tests") {
|
|
241
|
-
// Recursively scan subdirectories
|
|
242
|
-
utilities.push(...scanDirectory(filePath, `${category}/${file}`));
|
|
243
|
-
} else if ((file.endsWith(".ts") || file.endsWith(".tsx")) && !file.endsWith(".d.ts")) {
|
|
244
|
-
// Skip test files, stories, and type definition files
|
|
245
|
-
if (file.includes(".test.") || file.includes(".spec.") || file.includes(".stories.")) {
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const funcs = extractExports(filePath);
|
|
250
|
-
for (const func of funcs) {
|
|
251
|
-
utilities.push({
|
|
252
|
-
...func,
|
|
253
|
-
category,
|
|
254
|
-
file: path.relative(SRC_DIR, filePath),
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
return utilities;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function run() {
|
|
264
|
-
try {
|
|
265
|
-
console.log("Generating utilities manifest...");
|
|
266
|
-
|
|
267
|
-
const manifest = {
|
|
268
|
-
version: process.env.npm_package_version || "unknown",
|
|
269
|
-
generatedAt: new Date().toISOString(),
|
|
270
|
-
categories: {},
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
// Scan hooks directory
|
|
274
|
-
const hooksDir = path.join(SRC_DIR, "hooks");
|
|
275
|
-
const hooks = scanDirectory(hooksDir, "hooks");
|
|
276
|
-
if (hooks.length > 0) {
|
|
277
|
-
manifest.categories.hooks = {
|
|
278
|
-
description:
|
|
279
|
-
"React hooks for common functionality like debouncing, local storage, and time controls",
|
|
280
|
-
utilities: hooks.sort((a, b) => a.name.localeCompare(b.name)),
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Scan utils directory
|
|
285
|
-
const utilsDir = path.join(SRC_DIR, "utils");
|
|
286
|
-
|
|
287
|
-
// Formatting utilities
|
|
288
|
-
const formattingDir = path.join(utilsDir, "formatting");
|
|
289
|
-
const formatting = scanDirectory(formattingDir, "formatting");
|
|
290
|
-
if (formatting.length > 0) {
|
|
291
|
-
manifest.categories.formatting = {
|
|
292
|
-
description:
|
|
293
|
-
"Comprehensive formatting utilities for text, numbers, dates, currency, energy, temperature, distance, and more",
|
|
294
|
-
utilities: formatting.sort((a, b) => a.name.localeCompare(b.name)),
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Chart utilities
|
|
299
|
-
const chartsDir = path.join(utilsDir, "charts");
|
|
300
|
-
const charts = scanDirectory(chartsDir, "charts");
|
|
301
|
-
const chartExportUtils = extractExports(path.join(utilsDir, "chartExport.ts"));
|
|
302
|
-
const chartUtils = extractExports(path.join(utilsDir, "charts.ts"));
|
|
303
|
-
const allChartUtils = [
|
|
304
|
-
...charts,
|
|
305
|
-
...chartExportUtils.map((f) => ({ ...f, category: "charts", file: "utils/chartExport.ts" })),
|
|
306
|
-
...chartUtils.map((f) => ({ ...f, category: "charts", file: "utils/charts.ts" })),
|
|
307
|
-
];
|
|
308
|
-
|
|
309
|
-
if (allChartUtils.length > 0) {
|
|
310
|
-
manifest.categories.charts = {
|
|
311
|
-
description: "Chart utilities for data visualization, scaling, and export functionality",
|
|
312
|
-
utilities: allChartUtils.sort((a, b) => a.name.localeCompare(b.name)),
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Color utilities
|
|
317
|
-
const colorUtils = extractExports(path.join(utilsDir, "colors.ts"));
|
|
318
|
-
if (colorUtils.length > 0) {
|
|
319
|
-
manifest.categories.colors = {
|
|
320
|
-
description:
|
|
321
|
-
"Color management utilities for theme-aware color resolution, contrast calculation, and palette generation",
|
|
322
|
-
utilities: colorUtils
|
|
323
|
-
.map((f) => ({ ...f, category: "colors", file: "utils/colors.ts" }))
|
|
324
|
-
.sort((a, b) => a.name.localeCompare(b.name)),
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// General utilities from utils/index.ts
|
|
329
|
-
const generalUtils = extractExports(path.join(utilsDir, "index.ts"));
|
|
330
|
-
if (generalUtils.length > 0) {
|
|
331
|
-
manifest.categories.general = {
|
|
332
|
-
description:
|
|
333
|
-
"General utility functions for focus management, Tailwind class composition, and theme utilities",
|
|
334
|
-
utilities: generalUtils
|
|
335
|
-
.map((f) => ({ ...f, category: "general", file: "utils/index.ts" }))
|
|
336
|
-
.sort((a, b) => a.name.localeCompare(b.name)),
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Add summary
|
|
341
|
-
const totalUtilities = Object.values(manifest.categories).reduce(
|
|
342
|
-
(sum, cat) => sum + cat.utilities.length,
|
|
343
|
-
0
|
|
344
|
-
);
|
|
345
|
-
|
|
346
|
-
manifest.summary = {
|
|
347
|
-
totalUtilities,
|
|
348
|
-
totalCategories: Object.keys(manifest.categories).length,
|
|
349
|
-
categories: Object.keys(manifest.categories).map((cat) => ({
|
|
350
|
-
name: cat,
|
|
351
|
-
count: manifest.categories[cat].utilities.length,
|
|
352
|
-
})),
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
// Add import information
|
|
356
|
-
manifest.importInfo = {
|
|
357
|
-
package: "@texturehq/edges",
|
|
358
|
-
examples: [
|
|
359
|
-
{
|
|
360
|
-
description: "Import specific utilities",
|
|
361
|
-
code: 'import { formatNumber, formatCurrency, useDebounce } from "@texturehq/edges"',
|
|
362
|
-
},
|
|
363
|
-
{
|
|
364
|
-
description: "Import all formatting utilities",
|
|
365
|
-
code: 'import * as formatting from "@texturehq/edges"',
|
|
366
|
-
},
|
|
367
|
-
{
|
|
368
|
-
description: "Import hooks",
|
|
369
|
-
code: 'import { useDebounce, useLocalStorage } from "@texturehq/edges"',
|
|
370
|
-
},
|
|
371
|
-
{
|
|
372
|
-
description: "Import color utilities",
|
|
373
|
-
code: 'import { getResolvedColor, isLightColor } from "@texturehq/edges"',
|
|
374
|
-
},
|
|
375
|
-
],
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
// Write the manifest
|
|
379
|
-
ensureDir(DIST_DIR);
|
|
380
|
-
const outputPath = path.join(DIST_DIR, "utilities.manifest.json");
|
|
381
|
-
fs.writeFileSync(outputPath, JSON.stringify(manifest, null, 2));
|
|
382
|
-
|
|
383
|
-
console.log(
|
|
384
|
-
`Generated ${path.join("dist", "utilities.manifest.json")} with ${totalUtilities} utilities in ${Object.keys(manifest.categories).length} categories.`
|
|
385
|
-
);
|
|
386
|
-
} catch (err) {
|
|
387
|
-
console.error("Failed to generate utilities manifest:", err);
|
|
388
|
-
process.exitCode = 1;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
run();
|