11import * as fs from 'fs' ;
22import { resolve } from 'path' ;
33import { ViteDevServer } from 'vite' ;
4+ import { default as Parser , Query } from 'tree-sitter' ;
5+ import { default as TS } from 'tree-sitter-typescript' ;
6+ const parser = new Parser ( ) ;
7+
8+ /**
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+ **/
19+
20+ const query = new Query (
21+ 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)
32+ )
33+ )
34+ )
35+ )
36+ ` ,
37+ ) ;
38+ parser . setLanguage ( TS . tsx ) ;
439export default function autoAPI ( ) {
540 return {
641 name : 'watch-monorepo-changes' ,
@@ -15,9 +50,9 @@ export default function autoAPI() {
1550 } ;
1651}
1752// the object should have this general structure, arranged from parent to child
18- // componentName:[subComponent,subcomponent,...]
19- // subComponentName:[publicType,publicType,...]
20- // publicType:[{ comment,prop,type },{ comment,prop,type },...]
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
2156// THEY UPPER-MOST KEY IS ALWAYS USED AS A HEADING
2257export type ComponentParts = Record < string , SubComponents > ;
2358type SubComponents = SubComponent [ ] ;
@@ -28,51 +63,57 @@ type ParsedProps = {
2863 prop : string ;
2964 type : string ;
3065} ;
31- function parseSingleComponentFromDir ( path : string , ref : SubComponents ) {
66+ function parseSingleComponentFromDir (
67+ path : string ,
68+ ref : SubComponents ,
69+ ) : SubComponents | undefined {
3270 const component_name = / \/ ( [ \w - ] * ) .t s x / . exec ( path ) ;
3371 if ( component_name === null || component_name [ 1 ] === null ) {
3472 // may need better behavior
3573 return ;
3674 }
3775 const sourceCode = fs . readFileSync ( path , 'utf-8' ) ;
38- const comments = extractPublicTypes ( sourceCode ) ;
76+ const tree = parser . parse ( sourceCode ) ;
3977 const parsed : PublicType [ ] = [ ] ;
40- for ( const comment of comments ) {
41- const api = extractComments ( comment . string ) ;
42- const pair : PublicType = { [ comment . label ] : api } ;
43- parsed . push ( pair ) ;
78+ function topKey ( obj : { [ x : string ] : any } | undefined ) {
79+ return obj ? Object . keys ( obj ) [ 0 ] : '' ;
4480 }
81+ const matches = query . matches ( tree . rootNode ) ;
82+ matches . forEach ( ( match ) => {
83+ const last : PublicType = parsed [ parsed . length - 1 ] ;
84+ let subComponentName = '' ;
85+ 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 ;
90+ }
91+ if ( lol . name === 'comment' ) {
92+ //this removes the comment syntax
93+ const justText = lol . node . text . replaceAll ( / [ / * ] / g, '' ) ;
94+ parsedProps . comment = justText ;
95+ }
96+
97+ if ( lol . name === 'prop' ) {
98+ parsedProps . prop = lol . node . text ;
99+ }
100+
101+ if ( lol . name === 'type' ) {
102+ parsedProps . type = lol . node . text ;
103+ if ( subComponentName === topKey ( last ) ) {
104+ last [ topKey ( last ) ] . push ( parsedProps ) ;
105+ } else {
106+ parsed . push ( { [ subComponentName ] : [ parsedProps ] } ) ;
107+ }
108+ }
109+ } ) ;
110+ } ) ;
111+
45112 const completeSubComponent : SubComponent = { [ component_name [ 1 ] ] : parsed } ;
46113 ref . push ( completeSubComponent ) ;
47114 return ref ;
48115}
49116
50- function extractPublicTypes ( strg : string ) {
51- const getPublicTypes = / t y p e P u b l i c ( [ A - Z ] [ \w ] * ) * [ \w \W ] * ?{ ( [ \w | \W ] * ?) } ( ; | & ) / gm;
52- const cms = [ ] ;
53- let groups ;
54- while ( ( groups = getPublicTypes . exec ( strg ) ) !== null ) {
55- const string = groups [ 2 ] ;
56- cms . push ( { label : groups [ 1 ] , string } ) ;
57- }
58- return cms ;
59- }
60- function extractComments ( strg : string ) : ParsedProps [ ] {
61- const magical_regex =
62- / ^ \s * ?\/ [ * ] { 2 } \n ? ( [ \w | \W | ] * ?) \s * [ * ] { 1 , 2 } [ / ] \n [ ] * ( [ \w | \W ] * ?) : ( [ \w | \W ] * ?) ; ? $ / gm;
63-
64- const cms = [ ] ;
65- let groups ;
66-
67- while ( ( groups = magical_regex . exec ( strg ) ) !== null ) {
68- const trimStart = / ^ * | ( \* * ) / g;
69- const comment = groups [ 1 ] . replaceAll ( trimStart , '' ) ;
70- const prop = groups [ 2 ] ;
71- const type = groups [ 3 ] ;
72- cms . push ( { comment, prop, type } ) ;
73- }
74- return cms ;
75- }
76117function writeToDocs ( fullPath : string , componentName : string , api : ComponentParts ) {
77118 if ( fullPath . includes ( 'kit-headless' ) ) {
78119 const relDocPath = `../website/src/routes//docs/headless/${ componentName } ` ;
0 commit comments