Skip to content

Commit 23c6676

Browse files
authored
refactor(auto-api.ts): chatgpt o1 cleanup (#985)
* refactor(auto-api.ts): chatgpt o1 cleanup * chore: readability
1 parent 2abd8dd commit 23c6676

File tree

1 file changed

+94
-67
lines changed

1 file changed

+94
-67
lines changed

apps/website/auto-api.ts

Lines changed: 94 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,40 @@
11
import * as fs from 'fs';
22
import { resolve } from 'path';
33
import { ViteDevServer } from 'vite';
4-
import { default as Parser, Query } from 'tree-sitter';
5-
import { default as TS } from 'tree-sitter-typescript';
4+
import Parser, { Query } from 'tree-sitter';
5+
import TS from 'tree-sitter-typescript';
6+
67
const parser = new Parser();
78

89
/**
9-
TO WHOM IT MAY CONCERN:
10-
11-
if by some reason you need to refactor the query below and don't know where to starts, below are what I consider to be the must-know parts.
12-
13-
1) Tree-Sitter query docs: https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax
14-
1b) Put particular attention to the following sections: capturing nodes, wildcard nodes, and anchors
15-
16-
2) Have a way of being able to see the tree-sitter AST in realtime. The ideal setup comes included in Neovim. In ex mode, simply run
17-
the command below and you'll have the file's AST viewer open in realtime: InspectTree
18-
**/
10+
* Tree-Sitter query docs: https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax
11+
* Pay particular attention to the following sections: capturing nodes, wildcard nodes, and anchors.
12+
*
13+
* To have a way of being able to see the Tree-Sitter AST in real-time: the ideal setup comes included in Neovim. In ex mode, simply run
14+
* the command below and you'll have the file's AST viewer open in real-time: `:InspectTree`
15+
**/
1916

2017
const query = new Query(
2118
TS.tsx,
22-
`declaration: (type_alias_declaration
23-
name: (type_identifier) @subComponentName
24-
(intersection_type
25-
(object_type
26-
27-
(comment) @comment
28-
.
29-
(property_signature
30-
name: (_) @prop
31-
type: (type_annotation (_) @type)
19+
`declaration:
20+
(type_alias_declaration
21+
name: (type_identifier) @subComponentName
22+
(intersection_type
23+
(object_type
24+
(comment) @comment
25+
.
26+
(property_signature
27+
name: (_) @prop
28+
type: (type_annotation (_) @type)
29+
)
30+
)
3231
)
3332
)
34-
)
35-
)
36-
`,
33+
`,
3734
);
35+
3836
parser.setLanguage(TS.tsx);
37+
3938
export default function autoAPI() {
4039
return {
4140
name: 'watch-monorepo-changes',
@@ -49,11 +48,13 @@ export default function autoAPI() {
4948
},
5049
};
5150
}
52-
// the object should have this general structure, arranged from parent to child
53-
// componentName:[subComponent,subcomponent,...] & componentName comes from the dir
54-
// subComponentName/type alias:[publicType,publicType,...] & subcomponent comes from the file under dir
55-
// publicType:[{ comment,prop,type },{ comment,prop,type },...] & publicType comes from type inside file
56-
// THEY UPPER-MOST KEY IS ALWAYS USED AS A HEADING
51+
52+
// The object should have this general structure, arranged from parent to child
53+
// componentName: [subComponent, subComponent, ...] & componentName comes from the directory
54+
// subComponentName/type alias: [PublicType, PublicType, ...] & subComponent comes from the file under directory
55+
// PublicType: [{ comment, prop, type }, { comment, prop, type }, ...] & PublicType comes from type inside file
56+
// THE UPPER-MOST KEY IS ALWAYS USED AS A HEADING
57+
5758
export type ComponentParts = Record<string, SubComponents>;
5859
type SubComponents = SubComponent[];
5960
export type SubComponent = Record<string, PublicType[]>;
@@ -63,93 +64,119 @@ type ParsedProps = {
6364
prop: string;
6465
type: string;
6566
};
67+
68+
/**
69+
* Note: For this code to run, you need to prefix the type with 'Public' (e.g., 'PublicMyType') in your TypeScript files
70+
*
71+
* * e.g:
72+
*
73+
* ```tsx
74+
* type PublicModalRootProps = {
75+
* //blablabla
76+
* onShow$?: QRL<() => void>;
77+
* //blablabla
78+
* onClose$?: QRL<() => void>;
79+
* //blablabla
80+
* 'bind:show'?: Signal<boolean>;
81+
* //blablabla
82+
* closeOnBackdropClick?: boolean;
83+
* //blablabla
84+
* alert?: boolean;
85+
* };
86+
* ```
87+
* This convention helps the parser identify and process the public types correctly.
88+
*
89+
* Now when you save the corresponding .mdx file, the API will be updated accordingly.
90+
*
91+
**/
92+
6693
function parseSingleComponentFromDir(
6794
path: string,
6895
ref: SubComponents,
6996
): SubComponents | undefined {
70-
const component_name = /\/([\w-]*).tsx/.exec(path);
71-
if (component_name === null || component_name[1] === null) {
72-
// may need better behavior
97+
const componentNameMatch = /[\\/](\w[\w-]*)\.tsx$/.exec(path);
98+
if (!componentNameMatch) {
99+
// May need better behavior
73100
return;
74101
}
102+
const componentName = componentNameMatch[1];
75103
const sourceCode = fs.readFileSync(path, 'utf-8');
76104
const tree = parser.parse(sourceCode);
77105
const parsed: PublicType[] = [];
78-
function topKey(obj: { [x: string]: any } | undefined) {
79-
return obj ? Object.keys(obj)[0] : '';
80-
}
106+
81107
const matches = query.matches(tree.rootNode);
82108
matches.forEach((match) => {
83-
const last: PublicType = parsed[parsed.length - 1];
109+
const last: PublicType | undefined = parsed[parsed.length - 1];
84110
let subComponentName = '';
85111
const parsedProps: ParsedProps = { comment: '', prop: '', type: '' };
86-
match.captures.forEach((lol) => {
87-
//statetements are ordered as they appear in capture array
88-
if (lol.name === 'subComponentName' && subComponentName != lol.node.text) {
89-
subComponentName = lol.node.text;
112+
match.captures.forEach((capture) => {
113+
// Statements are ordered as they appear in capture array
114+
if (capture.name === 'subComponentName' && subComponentName !== capture.node.text) {
115+
subComponentName = capture.node.text;
90116
}
91-
if (lol.name === 'comment') {
92-
//this removes the comment syntax
93-
const justText = lol.node.text.replaceAll(/[/*]/g, '');
117+
if (capture.name === 'comment') {
118+
// This removes the comment syntax
119+
const justText = capture.node.text.replace(/[/*]/g, '').trim();
94120
parsedProps.comment = justText;
95121
}
96-
97-
if (lol.name === 'prop') {
98-
parsedProps.prop = lol.node.text;
122+
if (capture.name === 'prop') {
123+
parsedProps.prop = capture.node.text;
99124
}
100-
101-
if (lol.name === 'type') {
102-
parsedProps.type = lol.node.text;
103-
if (subComponentName === topKey(last)) {
104-
last[topKey(last)].push(parsedProps);
125+
if (capture.name === 'type') {
126+
parsedProps.type = capture.node.text;
127+
const topKey = last ? Object.keys(last)[0] : '';
128+
if (subComponentName === topKey) {
129+
last[topKey].push(parsedProps);
105130
} else {
106131
parsed.push({ [subComponentName]: [parsedProps] });
107132
}
108133
}
109134
});
110135
});
111136

112-
const completeSubComponent: SubComponent = { [component_name[1]]: parsed };
137+
const completeSubComponent: SubComponent = { [componentName]: parsed };
113138
ref.push(completeSubComponent);
114139
return ref;
115140
}
116141

117142
function writeToDocs(fullPath: string, componentName: string, api: ComponentParts) {
118143
if (fullPath.includes('kit-headless')) {
119-
const relDocPath = `../website/src/routes//docs/headless/${componentName}`;
144+
const relDocPath = `../website/src/routes/docs/headless/${componentName}`;
120145
const fullDocPath = resolve(__dirname, relDocPath);
121-
const dirPath = fullDocPath.concat('/auto-api');
146+
const dirPath = resolve(fullDocPath, 'auto-api');
122147

123148
if (!fs.existsSync(dirPath)) {
124149
fs.mkdirSync(dirPath);
125150
}
126151
const json = JSON.stringify(api, null, 2);
127-
const hacky = `export const api=${json}`;
152+
const exportedApi = `export const api = ${json};`;
128153

129154
try {
130-
fs.writeFileSync(dirPath.concat('/api.ts'), hacky);
131-
console.log('auto-api: succesfully genereated new json!!! :)');
155+
fs.writeFileSync(resolve(dirPath, 'api.ts'), exportedApi);
156+
console.log('auto-api: successfully generated new JSON!');
132157
} catch (err) {
133-
return;
158+
console.error('Error writing API file:', err);
134159
}
135160
}
136161
}
162+
137163
function loopOnAllChildFiles(filePath: string) {
138-
const childComponentRegex = /\/([\w-]*).tsx$/.exec(filePath);
139-
if (childComponentRegex === null) {
164+
const childComponentMatch = /[\\/](\w[\w-]*)\.tsx$/.exec(filePath);
165+
if (!childComponentMatch) {
140166
return;
141167
}
142-
const parentDir = filePath.replace(childComponentRegex[0], '');
143-
const componentRegex = /\/(\w*)$/.exec(parentDir);
144-
if (!fs.existsSync(parentDir) || componentRegex == null) {
168+
const parentDir = filePath.replace(childComponentMatch[0], '');
169+
const componentMatch = /[\\/](\w+)$/.exec(parentDir);
170+
if (!fs.existsSync(parentDir) || !componentMatch) {
145171
return;
146172
}
147-
const componentName = componentRegex[1];
173+
const componentName = componentMatch[1];
148174
const allParts: SubComponents = [];
149175
const store: ComponentParts = { [componentName]: allParts };
176+
150177
fs.readdirSync(parentDir).forEach((fileName) => {
151-
if (/tsx$/.test(fileName)) {
152-
const fullPath = parentDir + '/' + fileName;
178+
if (/\.tsx$/.test(fileName)) {
179+
const fullPath = resolve(parentDir, fileName);
153180
parseSingleComponentFromDir(fullPath, store[componentName]);
154181
}
155182
});

0 commit comments

Comments
 (0)