1616
1717/// <reference types="node" />
1818
19+ import { exec } from "node:child_process" ;
1920import {
20- readdirSync ,
21- statSync ,
21+ copyFileSync ,
2222 existsSync ,
23- rmSync ,
2423 mkdirSync ,
25- copyFileSync ,
26- writeFileSync
27- } from 'fs' ;
28- import { join , relative , dirname , resolve , sep } from 'path' ;
29- import { exec } from 'child_process' ;
30- import { promisify } from 'util' ;
24+ readdirSync ,
25+ rmSync ,
26+ statSync ,
27+ writeFileSync ,
28+ } from "node:fs" ;
29+ import { dirname , join , relative , resolve , sep } from "node:path" ;
30+ import { promisify } from "node:util" ;
3131
3232const execAsync = promisify ( exec ) ;
33- const TEMP_ROOT = ' .tsc_check' ;
33+ const TEMP_ROOT = " .tsc_check" ;
3434
3535interface Project {
3636 files : string [ ] ;
@@ -45,14 +45,18 @@ interface CheckResult {
4545}
4646
4747// Helper to recursively find all files with a specific extension
48- function findFiles ( dir : string , extension : string , fileList : string [ ] = [ ] ) : string [ ] {
48+ function findFiles (
49+ dir : string ,
50+ extension : string ,
51+ fileList : string [ ] = [ ] ,
52+ ) : string [ ] {
4953 const files = readdirSync ( dir ) ;
5054 for ( const file of files ) {
51- if ( file . endsWith ( ' .js' ) ) continue ;
55+ if ( file . endsWith ( " .js" ) ) continue ;
5256 const filePath = join ( dir , file ) ;
5357 const stat = statSync ( filePath ) ;
5458 if ( stat . isDirectory ( ) ) {
55- if ( file !== ' node_modules' && file !== ' .git' && file !== TEMP_ROOT ) {
59+ if ( file !== " node_modules" && file !== " .git" && file !== TEMP_ROOT ) {
5660 findFiles ( filePath , extension , fileList ) ;
5761 }
5862 } else if ( file . endsWith ( extension ) ) {
@@ -64,10 +68,14 @@ function findFiles(dir: string, extension: string, fileList: string[] = []): str
6468
6569// Find all directories containing appsscript.json
6670function findProjectRoots ( rootDir : string ) : string [ ] {
67- return findFiles ( rootDir , ' appsscript.json' ) . map ( ( f ) => dirname ( f ) ) ;
71+ return findFiles ( rootDir , " appsscript.json" ) . map ( ( f ) => dirname ( f ) ) ;
6872}
6973
70- function createProjects ( rootDir : string , projectRoots : string [ ] , allGsFiles : string [ ] ) : Project [ ] {
74+ function createProjects (
75+ rootDir : string ,
76+ projectRoots : string [ ] ,
77+ allGsFiles : string [ ] ,
78+ ) : Project [ ] {
7179 // Holds files that belong to a formal Apps Script project (defined by the presence of appsscript.json).
7280 const projectGroups = new Map < string , string [ ] > ( ) ;
7381
@@ -107,7 +115,7 @@ function createProjects(rootDir: string, projectRoots: string[], allGsFiles: str
107115 projects . push ( {
108116 files,
109117 name : `Project: ${ relative ( rootDir , dir ) } ` ,
110- path : relative ( rootDir , dir )
118+ path : relative ( rootDir , dir ) ,
111119 } ) ;
112120 }
113121 } ) ;
@@ -116,100 +124,109 @@ function createProjects(rootDir: string, projectRoots: string[], allGsFiles: str
116124 projects . push ( {
117125 files,
118126 name : `Loose Project: ${ relative ( rootDir , dir ) } ` ,
119- path : relative ( rootDir , dir )
127+ path : relative ( rootDir , dir ) ,
120128 } ) ;
121129 }
122130 } ) ;
123131
124132 return projects ;
125133}
126134
127- async function checkProject ( project : Project , rootDir : string ) : Promise < CheckResult > {
128- const projectNameSafe = project . name . replace ( / [ ^ a - z A - Z 0 - 9 ] / g, '_' ) ;
135+ async function checkProject (
136+ project : Project ,
137+ rootDir : string ,
138+ ) : Promise < CheckResult > {
139+ const projectNameSafe = project . name . replace ( / [ ^ a - z A - Z 0 - 9 ] / g, "_" ) ;
129140 const projectTempDir = join ( TEMP_ROOT , projectNameSafe ) ;
130-
141+
131142 // Synchronous setup is fine as it's fast and avoids race conditions on mkdir if we were sharing dirs (we aren't)
132- mkdirSync ( projectTempDir , { recursive : true } ) ;
143+ mkdirSync ( projectTempDir , { recursive : true } ) ;
133144
134145 for ( const file of project . files ) {
135146 const fileRelPath = relative ( rootDir , file ) ;
136- const destPath = join ( projectTempDir , fileRelPath . replace ( / \. g s $ / , ' .js' ) ) ;
147+ const destPath = join ( projectTempDir , fileRelPath . replace ( / \. g s $ / , " .js" ) ) ;
137148 const destDir = dirname ( destPath ) ;
138- mkdirSync ( destDir , { recursive : true } ) ;
149+ mkdirSync ( destDir , { recursive : true } ) ;
139150 copyFileSync ( file , destPath ) ;
140151 }
141152
142153 const tsConfig = {
143- extends : ' ../../tsconfig.json' ,
154+ extends : " ../../tsconfig.json" ,
144155 compilerOptions : {
145156 noEmit : true ,
146157 allowJs : true ,
147158 checkJs : true ,
148- typeRoots : [ resolve ( rootDir , ' node_modules/@types' ) ]
159+ typeRoots : [ resolve ( rootDir , " node_modules/@types" ) ] ,
149160 } ,
150- include : [ ' **/*.js' ]
161+ include : [ " **/*.js" ] ,
151162 } ;
152163
153164 writeFileSync (
154- join ( projectTempDir , ' tsconfig.json' ) ,
155- JSON . stringify ( tsConfig , null , 2 )
165+ join ( projectTempDir , " tsconfig.json" ) ,
166+ JSON . stringify ( tsConfig , null , 2 ) ,
156167 ) ;
157168
158169 try {
159- await execAsync ( `tsc -p "${ projectTempDir } "` , { cwd : rootDir } ) ;
160- return { name : project . name , success : true , output : '' } ;
161- } catch ( e : any ) {
162- const rawOutput = ( e . stdout || '' ) + ( e . stderr || '' ) ;
163-
164- const rewritten = rawOutput . split ( '\n' ) . map ( ( line : string ) => {
165- if ( line . includes ( projectTempDir ) ) {
166- let newLine = line . split ( projectTempDir + sep ) . pop ( ) ;
167- if ( ! newLine ) {
168- return line ;
170+ await execAsync ( `tsc -p \"${ projectTempDir } \"` , { cwd : rootDir } ) ;
171+ return { name : project . name , success : true , output : "" } ;
172+ } catch ( e ) {
173+ const err = e as { stdout ?: string ; stderr ?: string } ;
174+ const rawOutput = ( err . stdout ?? "" ) + ( err . stderr || "" ) ;
175+
176+ const rewritten = rawOutput
177+ . split ( "\n" )
178+ . map ( ( line : string ) => {
179+ if ( line . includes ( projectTempDir ) ) {
180+ let newLine = line . split ( projectTempDir + sep ) . pop ( ) ;
181+ if ( ! newLine ) {
182+ return line ;
183+ }
184+ newLine = newLine . replace ( / \. j s ( : | \( ) / g, ".gs$1" ) ;
185+ return newLine ;
169186 }
170- newLine = newLine . replace ( / \. j s ( : | \( ) / g, '.gs$1' ) ;
171- return newLine ;
172- }
173- return line ;
174- } ) . join ( '\n' ) ;
187+ return line ;
188+ } )
189+ . join ( "\n" ) ;
175190
176- return { name : project . name , success : false , output : rewritten } ;
191+ return { name : project . name , success : false , output : rewritten } ;
177192 }
178193}
179194
180195async function main ( ) {
181196 try {
182- const rootDir = resolve ( '.' ) ;
197+ const rootDir = resolve ( "." ) ;
183198 const args = process . argv . slice ( 2 ) ;
184- const searchArg = args . find ( arg => arg !== '--' ) ;
185-
199+ const searchArg = args . find ( ( arg ) => arg !== "--" ) ;
200+
186201 // 1. Discovery
187202 const projectRoots = findProjectRoots ( rootDir ) ;
188- const allGsFiles = findFiles ( rootDir , ' .gs' ) ;
203+ const allGsFiles = findFiles ( rootDir , " .gs" ) ;
189204
190205 // 2. Grouping
191206 const projects = createProjects ( rootDir , projectRoots , allGsFiles ) ;
192207
193208 // 3. Filtering
194- const projectsToCheck = projects . filter ( p => {
209+ const projectsToCheck = projects . filter ( ( p ) => {
195210 return ! searchArg || p . path . startsWith ( searchArg ) ;
196211 } ) ;
197212
198213 if ( projectsToCheck . length === 0 ) {
199- console . log ( ' No projects found matching the search path.' ) ;
214+ console . log ( " No projects found matching the search path." ) ;
200215 return ;
201216 }
202217
203218 // 4. Setup
204219 if ( existsSync ( TEMP_ROOT ) ) {
205- rmSync ( TEMP_ROOT , { recursive : true , force : true } ) ;
220+ rmSync ( TEMP_ROOT , { recursive : true , force : true } ) ;
206221 }
207222 mkdirSync ( TEMP_ROOT ) ;
208223
209224 console . log ( `Checking ${ projectsToCheck . length } projects in parallel...` ) ;
210225
211226 // 5. Parallel Execution
212- const results = await Promise . all ( projectsToCheck . map ( p => checkProject ( p , rootDir ) ) ) ;
227+ const results = await Promise . all (
228+ projectsToCheck . map ( ( p ) => checkProject ( p , rootDir ) ) ,
229+ ) ;
213230
214231 // 6. Reporting
215232 let hasError = false ;
@@ -222,18 +239,17 @@ async function main() {
222239 }
223240
224241 if ( hasError ) {
225- console . error ( ' \nOne or more checks failed.' ) ;
242+ console . error ( " \nOne or more checks failed." ) ;
226243 process . exit ( 1 ) ;
227244 } else {
228- console . log ( ' \nAll checks passed.' ) ;
245+ console . log ( " \nAll checks passed." ) ;
229246 }
230-
231247 } catch ( err ) {
232- console . error ( ' Unexpected error:' , err ) ;
248+ console . error ( " Unexpected error:" , err ) ;
233249 process . exit ( 1 ) ;
234250 } finally {
235251 if ( existsSync ( TEMP_ROOT ) ) {
236- rmSync ( TEMP_ROOT , { recursive : true , force : true } ) ;
252+ rmSync ( TEMP_ROOT , { recursive : true , force : true } ) ;
237253 }
238254 }
239255}
0 commit comments