1+ import  {  Entity  }  from  './types' 
2+ import  {  Octokit  }  from  '@octokit/core' 
3+ import  fetch  from  'node-fetch' 
4+ import  yaml  from  'js-yaml' 
5+ import  {  readdir ,  readFile  }  from  'fs/promises' 
6+ import  last  from  'lodash/last' 
7+ import  {  logToFile  }  from  './debug' 
8+ 
9+ let  entities : Entity [ ]  =  [ ] 
10+ 
11+ const  octokit  =  new  Octokit ( {  auth : process . env . GITHUB_TOKEN  } ) 
12+ 
13+ // load locales if not yet loaded 
14+ export  const  loadOrGetEntities  =  async  ( )  =>  { 
15+     if  ( entities . length  ===  0 )  { 
16+         entities  =  await  loadEntities ( ) 
17+     } 
18+     return  entities 
19+ } 
20+ 
21+ export  const  loadFromGitHub  =  async  ( )  =>  { 
22+     const  entities : Entity [ ]  =  [ ] 
23+     console . log ( `-> loading entities repo` ) 
24+ 
25+     const  options  =  { 
26+         owner : 'StateOfJS' , 
27+         repo : 'entities' , 
28+         path : '' 
29+     } 
30+ 
31+     const  contents  =  await  octokit . request ( 'GET /repos/{owner}/{repo}/contents/{path}' ,  options ) 
32+     const  files  =  contents . data  as  any [ ] 
33+ 
34+     // loop over repo contents and fetch raw yaml files 
35+     for  ( const  file  of  files )  { 
36+         const  extension : string  =  last ( file ?. name . split ( '.' ) )  ||  '' 
37+         if  ( [ 'yml' ,  'yaml' ] . includes ( extension ) )  { 
38+             const  response  =  await  fetch ( file . download_url ) 
39+             const  contents  =  await  response . text ( ) 
40+             try  { 
41+                 const  yamlContents : any  =  yaml . load ( contents ) 
42+                 const  category  =  file . name . replace ( './' ,  '' ) . replace ( '.yml' ,  '' ) 
43+                 yamlContents . forEach ( ( entity : Entity )  =>  { 
44+                     const  tags  =  entity . tags  ? [ ...entity . tags ,  category ]  : [ category ] 
45+                     entities . push ( { 
46+                         ...entity , 
47+                         category, 
48+                         tags
49+                     } ) 
50+                 } ) 
51+             }  catch  ( error )  { 
52+                 console . log ( `// Error loading file ${ file . name }  ) 
53+                 console . log ( error ) 
54+             } 
55+         } 
56+     } 
57+     return  entities 
58+ } 
59+ 
60+ // when developing locally, load from local files 
61+ export  const  loadLocally  =  async  ( )  =>  { 
62+     console . log ( `-> loading entities locally` ) 
63+ 
64+     const  entities : Entity [ ]  =  [ ] 
65+ 
66+     const  devDir  =  __dirname . split ( '/' ) . slice ( 1 ,  - 2 ) . join ( '/' ) 
67+     const  path  =  `/${ devDir }  
68+     const  files  =  await  readdir ( path ) 
69+     const  yamlFiles  =  files . filter ( ( f : String )  =>  f . includes ( '.yml' ) ) 
70+ 
71+     // loop over dir contents and fetch raw yaml files 
72+     for  ( const  fileName  of  yamlFiles )  { 
73+         const  filePath  =  path  +  '/'  +  fileName 
74+         const  contents  =  await  readFile ( filePath ,  'utf8' ) 
75+         const  yamlContents : any  =  yaml . load ( contents ) 
76+         const  category  =  fileName . replace ( './' ,  '' ) . replace ( '.yml' ,  '' ) 
77+         yamlContents . forEach ( ( entity : Entity )  =>  { 
78+             const  tags  =  entity . tags  ? [ ...entity . tags ,  category ]  : [ category ] 
79+             entities . push ( { 
80+                 ...entity , 
81+                 category, 
82+                 tags
83+             } ) 
84+         } ) 
85+     } 
86+ 
87+     return  entities 
88+ } 
89+ 
90+ // load locales contents through GitHub API or locally 
91+ export  const  loadEntities  =  async  ( )  =>  { 
92+     console . log ( '// loading entities' ) 
93+ 
94+     const  entities : Entity [ ]  = 
95+         process . env . LOAD_LOCALES  ===  'local'  ? await  loadLocally ( )  : await  loadFromGitHub ( ) 
96+     console . log ( '// done loading entities' ) 
97+ 
98+     return  entities 
99+ } 
100+ 
101+ export  const  initEntities  =  async  ( )  =>  { 
102+   console . log ( '// initializing locales…' ) 
103+   const  entities  =  await  loadOrGetEntities ( ) 
104+   logToFile ( 'entities.json' ,  entities ,  {  mode : 'overwrite'  } ) 
105+ } 
106+ 
107+ export  const  getEntities  =  async  ( {  type,  tag,  tags } : {  type ?: string ;  tag ?: string ,  tags ?: string [ ]  } )  =>  { 
108+   let  entities  =  await  loadOrGetEntities ( ) 
109+   if  ( type )  { 
110+       entities  =  entities . filter ( e  =>  e . type  ===  type ) 
111+   } 
112+   if  ( tag )  { 
113+       entities  =  entities . filter ( e  =>  e . tags  &&  e . tags . includes ( tag ) ) 
114+   } 
115+   if  ( tags )  { 
116+       entities  =  entities . filter ( e  =>  tags . every ( t  =>  e . tags  &&  e . tags . includes ( t ) ) ) 
117+   } 
118+   return  entities 
119+ } 
120+ 
121+ // Look up entities by id, name, or aliases (case-insensitive) 
122+ export  const  getEntity  =  async  ( {  id } : {  id : string  } )  =>  { 
123+   const  entities  =  await  loadOrGetEntities ( ) 
124+ 
125+   if  ( ! id  ||  typeof  id  !==  'string' )  { 
126+       return 
127+   } 
128+ 
129+   const  lowerCaseId  =  id . toLowerCase ( ) 
130+   const  entity  =  entities . find ( e  =>  { 
131+       return  ( 
132+           ( e . id  &&  e . id . toLowerCase ( )  ===  lowerCaseId )  || 
133+           ( e . id  &&  e . id . toLowerCase ( ) . replace ( / \- / g,  '_' )  ===  lowerCaseId )  || 
134+           ( e . name  &&  e . name . toLowerCase ( )  ===  lowerCaseId )  || 
135+           ( e . aliases  &&  e . aliases . find ( ( a : string )  =>  a . toLowerCase ( )  ===  lowerCaseId ) ) 
136+       ) 
137+   } ) 
138+ 
139+   return  entity  ||  { } 
140+ } 
0 commit comments