@@ -6,6 +6,8 @@ import * as toolbox from 'gluegun';
6
6
import immutable from 'immutable' ;
7
7
import type { IPFSHTTPClient } from 'ipfs-http-client' ;
8
8
import yaml from 'js-yaml' ;
9
+ import memo from 'memoizee' ;
10
+ import { parseSync } from '@babel/core' ;
9
11
import { Spinner , step , withSpinner } from '../command-helpers/spinner' ;
10
12
import debug from '../debug' ;
11
13
import { applyMigrations } from '../migrations' ;
@@ -16,6 +18,18 @@ import * as asc from './asc';
16
18
17
19
const compilerDebug = debug ( 'graph-cli:compiler' ) ;
18
20
21
+ /** memoize the reading of the file so we don't have to read it every time */
22
+ const readFile = memo ( ( filename : string ) => fs . readFileSync ( filename , 'utf-8' ) ) ;
23
+
24
+ /** Memoized parser for Babel, so we can simply just read from cache */
25
+ const babelAst = memo ( ( filename : string ) => {
26
+ const data = readFile ( filename ) ;
27
+ return parseSync ( data , {
28
+ presets : [ '@babel/preset-typescript' ] ,
29
+ filename,
30
+ } ) ;
31
+ } ) ;
32
+
19
33
interface CompilerOptions {
20
34
ipfs : any ;
21
35
subgraphManifest : string ;
@@ -350,12 +364,19 @@ export default class Compiler {
350
364
351
365
try {
352
366
const dataSourceName = dataSource . getIn ( [ 'name' ] ) ;
353
-
354
367
const baseDir = this . sourceDir ;
355
368
const absoluteMappingPath = path . resolve ( baseDir , mappingPath ) ;
356
369
const inputFile = path . relative ( baseDir , absoluteMappingPath ) ;
370
+
357
371
this . _validateMappingContent ( absoluteMappingPath ) ;
358
372
373
+ const eventHandlers = dataSource . getIn ( [ 'mapping' , 'eventHandlers' ] ) ;
374
+ // TODO: improve the types
375
+ for ( const eventHandler of ( eventHandlers as any ) . toJS ( ) ) {
376
+ compilerDebug ( 'Validating Event Handler %s' , eventHandler . handler ) ;
377
+ this . _validateHandler ( absoluteMappingPath , eventHandler . handler ) ;
378
+ }
379
+
359
380
// If the file has already been compiled elsewhere, just use that output
360
381
// file and return early
361
382
const inputCacheKey = this . cacheKeyForFile ( absoluteMappingPath ) ;
@@ -431,6 +452,13 @@ export default class Compiler {
431
452
const inputFile = path . relative ( baseDir , absoluteMappingPath ) ;
432
453
this . _validateMappingContent ( absoluteMappingPath ) ;
433
454
455
+ const eventHandlers = templateName . getIn ( [ 'mapping' , 'eventHandlers' ] ) ;
456
+ // TODO: improve the types
457
+ for ( const eventHandler of ( eventHandlers as any ) . toJS ( ) ) {
458
+ compilerDebug ( 'Validating Template handler %s' , eventHandler . handler ) ;
459
+ this . _validateHandler ( absoluteMappingPath , eventHandler . handler ) ;
460
+ }
461
+
434
462
// If the file has already been compiled elsewhere, just use that output
435
463
// file and return early
436
464
const inputCacheKey = this . cacheKeyForFile ( absoluteMappingPath ) ;
@@ -489,7 +517,7 @@ export default class Compiler {
489
517
}
490
518
491
519
_validateMappingContent ( filePath : string ) {
492
- const data = fs . readFileSync ( filePath ) ;
520
+ const data = readFile ( filePath ) ;
493
521
if ( this . blockIpfsMethods && ( data . includes ( 'ipfs.cat' ) || data . includes ( 'ipfs.map' ) ) ) {
494
522
throw Error ( `
495
523
Subgraph Studio does not support mappings with ipfs methods.
@@ -499,6 +527,31 @@ export default class Compiler {
499
527
}
500
528
}
501
529
530
+ _validateHandler ( filePath : string , handlerName : string ) {
531
+ const baselAst = babelAst ( filePath ) ;
532
+
533
+ const body = baselAst ?. program . body ;
534
+
535
+ if ( ! body ) {
536
+ throw Error ( `Could not parse ${ filePath } ` ) ;
537
+ }
538
+
539
+ const exportedFunctionNames = body
540
+ . map ( statement => {
541
+ if (
542
+ statement . type === 'ExportNamedDeclaration' &&
543
+ statement ?. declaration ?. type === 'FunctionDeclaration'
544
+ ) {
545
+ return statement . declaration . id ?. name ;
546
+ }
547
+ } )
548
+ . filter ( Boolean ) ;
549
+
550
+ if ( ! exportedFunctionNames . includes ( handlerName ) ) {
551
+ throw Error ( `Could not find handler '${ handlerName } ' in ${ filePath } ` ) ;
552
+ }
553
+ }
554
+
502
555
async writeSubgraphToOutputDirectory ( protocol : Protocol , subgraph : immutable . Map < any , any > ) {
503
556
const displayDir = `${ this . displayPath ( this . options . outputDir ) } ${ toolbox . filesystem . separator } ` ;
504
557
0 commit comments