@@ -50,6 +50,59 @@ const DEFAULT_IMPORTS = `${NODE_IMPORTS}; const { ${ELECTRON_MODULES.join(
50
50
', ' ,
51
51
) } } = require('electron');`;
52
52
53
+ async function typeCheckFiles (
54
+ tempDir : string ,
55
+ filenameMapping : Map < string , string > ,
56
+ filenames : string [ ] ,
57
+ ) {
58
+ const tscExec = path . join ( require . resolve ( 'typescript' ) , '..' , '..' , 'bin' , 'tsc' ) ;
59
+ const args = [
60
+ tscExec ,
61
+ '--noEmit' ,
62
+ '--checkJs' ,
63
+ '--pretty' ,
64
+ path . join ( tempDir , 'electron.d.ts' ) ,
65
+ path . join ( tempDir , 'ambient.d.ts' ) ,
66
+ ...filenames ,
67
+ ] ;
68
+ const { status, stderr, stdout } = await spawnAsync ( process . execPath , args ) ;
69
+
70
+ if ( stderr ) {
71
+ throw new Error ( stderr ) ;
72
+ }
73
+
74
+ // Replace the temp file paths with the original source filename
75
+ let correctedOutput = stdout . replace (
76
+ new RegExp (
77
+ `${ path . relative ( process . cwd ( ) , tempDir ) } ${ path . sep } ` . replace ( / \\ / g, path . posix . sep ) ,
78
+ 'g' ,
79
+ ) ,
80
+ '' ,
81
+ ) ;
82
+
83
+ // Strip any @ts -expect-error/@ts-ignore comments we added
84
+ correctedOutput = correctedOutput . replace ( / \/ \/ @ t s - (?: e x p e c t - e r r o r | i g n o r e ) / g, '' ) ;
85
+
86
+ if ( correctedOutput . trim ( ) ) {
87
+ for ( const [ filename , originalFilename ] of filenameMapping . entries ( ) ) {
88
+ correctedOutput = correctedOutput . replace (
89
+ new RegExp ( path . basename ( filename ) , 'g' ) ,
90
+ originalFilename ,
91
+ ) ;
92
+ }
93
+
94
+ console . log ( correctedOutput ) ;
95
+ }
96
+
97
+ return status ;
98
+ }
99
+
100
+ function parseDirectives ( directive : string , value : string ) {
101
+ return findCurlyBracedDirectives ( directive , value )
102
+ . map ( ( parsed ) => parsed . match ( / ^ ( [ ^ : \r \n \t \f \v ] + ) : \s ? ( .+ ) $ / ) )
103
+ . filter ( ( parsed ) : parsed is RegExpMatchArray => parsed !== null ) ;
104
+ }
105
+
53
106
// TODO(dsanders11): Refactor to make this script general purpose and
54
107
// not tied to Electron - will require passing in the list of modules
55
108
// as a CLI option, probably a file since there's a lot of info
@@ -59,7 +112,8 @@ async function main(workspaceRoot: string, globs: string[], { ignoreGlobs = [] }
59
112
60
113
try {
61
114
const filenames : string [ ] = [ ] ;
62
- const originalFilenames : Map < string , string > = new Map ( ) ;
115
+ const originalFilenames = new Map < string , string > ( ) ;
116
+ const windowTypeFilenames = new Set < string > ( ) ;
63
117
64
118
// Copy over the typings so that a relative path can be used
65
119
fs . copyFileSync ( path . join ( process . cwd ( ) , 'electron.d.ts' ) , path . join ( tempDir , 'electron.d.ts' ) ) ;
@@ -87,17 +141,19 @@ async function main(workspaceRoot: string, globs: string[], { ignoreGlobs = [] }
87
141
?. match ( / \B @ t s - i g n o r e = \[ ( [ \d , ] * ) \] \B / ) ?. [ 1 ]
88
142
. split ( ',' )
89
143
. map ( ( line ) => parseInt ( line ) ) ;
90
- const tsTypeLines = codeBlock . meta
91
- ? findCurlyBracedDirectives ( '@ts-type' , codeBlock . meta )
92
- . map ( ( directive ) => directive . match ( / ^ ( [ ^ : \r \n \t \f \v ] + ) : \s ? ( .+ ) $ / ) )
93
- . filter ( ( directive ) : directive is RegExpMatchArray => directive !== null )
144
+ const tsTypeLines = codeBlock . meta ? parseDirectives ( '@ts-type' , codeBlock . meta ) : [ ] ;
145
+ const tsWindowTypeLines = codeBlock . meta
146
+ ? parseDirectives ( '@ts-window-type' , codeBlock . meta )
94
147
: [ ] ;
95
148
96
- if ( tsNoCheck && ( tsExpectErrorLines || tsIgnoreLines || tsTypeLines . length ) ) {
149
+ if (
150
+ tsNoCheck &&
151
+ ( tsExpectErrorLines || tsIgnoreLines || tsTypeLines . length || tsWindowTypeLines . length )
152
+ ) {
97
153
console . log (
98
154
`${ filepath } :${ line } :${
99
155
indent + 1
100
- } : Code block has both @ts-nocheck and @ts-expect-error/@ts-ignore/@ts-type, they conflict`,
156
+ } : Code block has both @ts-nocheck and @ts-expect-error/@ts-ignore/@ts-type/@ts-window-type , they conflict`,
101
157
) ;
102
158
errors = true ;
103
159
continue ;
@@ -131,6 +187,7 @@ async function main(workspaceRoot: string, globs: string[], { ignoreGlobs = [] }
131
187
const codeLines = codeBlock . value . split ( '\n' ) ;
132
188
let insertedInitialLine = false ;
133
189
let types = '';
190
+ let windowTypes = '';
134
191
135
192
const insertComment = ( comment : string , line : number ) => {
136
193
// Inserting additional lines will make the tsc output
@@ -194,8 +251,8 @@ async function main(workspaceRoot: string, globs: string[], { ignoreGlobs = [] }
194
251
. replace ( / \. / g, '-' ) } -${ line } .js`,
195
252
) ;
196
253
197
- // Blocks can have @ts -type={name:type} in their info string
198
- // (1-based lines) to declare a global variable for a block
254
+ // Blocks can have @ts -type={name:type} in their info
255
+ // string to declare a global variable for a block
199
256
if ( tsTypeLines . length ) {
200
257
// To support this feature, generate a random name for a
201
258
// module, generate an ambient module declaration for the
@@ -213,55 +270,40 @@ async function main(workspaceRoot: string, globs: string[], { ignoreGlobs = [] }
213
270
. join ( ',' ) } } = require('${ moduleName } ')`;
214
271
}
215
272
216
- fs . writeFileSync ( filename , `// @ts-check\n${ imports } \n${ blankLines } ${ code } \n${ types } ` ) ;
273
+ // Blocks can have @ts -window-type={name:type} in their
274
+ // info string to extend the Window object for a block
275
+ if ( tsWindowTypeLines . length ) {
276
+ const extraTypes = tsWindowTypeLines
277
+ . map ( ( type ) => ` ${ type [ 1 ] } : ${ type [ 2 ] } ;` )
278
+ . join ( '\n' ) ;
279
+ // Needs an export {} at the end to make TypeScript happy
280
+ windowTypes = `declare global {\n interface Window {\n${ extraTypes } \n }\n}\n\nexport {};\n\n` ;
281
+ fs . writeFileSync ( filename . replace ( / .j s $ / , '-window.d.ts' ) , windowTypes ) ;
282
+ windowTypeFilenames . add ( filename ) ;
283
+ } else {
284
+ filenames . push ( filename ) ;
285
+ }
217
286
218
- filenames . push ( filename ) ;
287
+ fs . writeFileSync ( filename , `// @ts-check\n ${ imports } \n ${ blankLines } ${ code } \n ${ types } ` ) ;
219
288
originalFilenames . set ( filename , filepath ) ;
220
289
}
221
290
}
222
291
223
292
fs . writeFileSync ( path . join ( tempDir , 'ambient.d.ts' ) , ambientModules ) ;
224
293
225
- for ( const chunk of chunkFilenames ( filenames ) ) {
226
- const tscExec = path . join ( require . resolve ( 'typescript' ) , '..' , '..' , 'bin' , 'tsc' ) ;
227
- const args = [
228
- tscExec ,
229
- '--noEmit' ,
230
- '--checkJs' ,
231
- '--pretty' ,
232
- path . join ( tempDir , 'electron.d.ts' ) ,
233
- path . join ( tempDir , 'ambient.d.ts' ) ,
234
- ...chunk ,
235
- ] ;
236
- const { status, stderr, stdout } = await spawnAsync ( process . execPath , args ) ;
237
-
238
- if ( stderr ) {
239
- throw new Error ( stderr ) ;
240
- }
241
-
242
- // Replace the temp file paths with the original source filename
243
- let correctedOutput = stdout . replace (
244
- new RegExp (
245
- `${ path . relative ( process . cwd ( ) , tempDir ) } ${ path . sep } ` . replace ( / \\ / g, path . posix . sep ) ,
246
- 'g' ,
247
- ) ,
248
- '' ,
249
- ) ;
250
-
251
- // Strip any @ts -expect-error/@ts-ignore comments we added
252
- correctedOutput = correctedOutput . replace ( / \/ \/ @ t s - (?: e x p e c t - e r r o r | i g n o r e ) / g, '' ) ;
253
-
254
- if ( correctedOutput . trim ( ) ) {
255
- for ( const [ filename , originalFilename ] of originalFilenames . entries ( ) ) {
256
- correctedOutput = correctedOutput . replace (
257
- new RegExp ( path . basename ( filename ) , 'g' ) ,
258
- originalFilename ,
259
- ) ;
260
- }
261
-
262
- console . log ( correctedOutput ) ;
263
- }
294
+ // Files for code blocks with window types have to be processed separately
295
+ // since window types are by nature global, and would bleed between blocks
296
+ for ( const filename of windowTypeFilenames ) {
297
+ const status = await typeCheckFiles ( tempDir , originalFilenames , [
298
+ filename . replace ( / .j s $ / , '-window.d.ts' ) ,
299
+ filename ,
300
+ ] ) ;
301
+ errors = errors || status !== 0 ;
302
+ }
264
303
304
+ // For the rest of the files, run them all at once so it doesn't take forever
305
+ for ( const chunk of chunkFilenames ( filenames ) ) {
306
+ const status = await typeCheckFiles ( tempDir , originalFilenames , chunk ) ;
265
307
errors = errors || status !== 0 ;
266
308
}
267
309
0 commit comments