@@ -29,6 +29,7 @@ type MainExport = ReturnType<typeof extractExport>
2929
3030const EXPECTED_METHOD = 'value'
3131const EXPECTED_EXPORT = 'value'
32+ const PROBABLE_CONSTANT = 'COLORS'
3233
3334export class MissingExpectedCall {
3435 constructor ( public readonly methodName : string , public readonly reason : string ) { }
@@ -54,9 +55,11 @@ type Issue = undefined
5455
5556class Constant {
5657 public readonly name : string
58+ public readonly signature : string ;
5759
58- constructor ( private readonly constant : Readonly < ProgramConstant > ) {
60+ constructor ( private readonly constant : Readonly < ProgramConstant > , source : Source ) {
5961 this . name = ( constant && isIdentifier ( constant . id ) && constant . id . name ) || '<NO-CONSTANT-NAME>'
62+ this . signature = source . getOuter ( constant . parent || constant )
6063 }
6164
6265 public get kind ( ) : ProgramConstant [ 'kind' ] {
@@ -652,38 +655,96 @@ class Entry {
652655 return false
653656 }
654657
655- public isOptimalHelper ( func : ArrowFunctionExpression | FunctionExpression | MainMethod < string > , constant : Readonly < Constant > ) : boolean {
656- if ( func . body && func . body . type === AST_NODE_TYPES . BlockStatement && func . body . body && func . body . body [ 0 ] . type === AST_NODE_TYPES . ReturnStatement ) {
657- func . body = func . body . body [ 0 ] . argument
658+ public isOptimalHelper ( func : Node , constant : Readonly < Constant > ) : boolean {
659+ const logger = getProcessLogger ( )
660+
661+ let body : Node | null
662+ let params : Parameter [ ] | null
663+
664+ switch ( func . type ) {
665+ case AST_NODE_TYPES . BlockStatement : {
666+ body = func . body [ 0 ]
667+ params = null
668+ break ;
669+ }
670+ case AST_NODE_TYPES . ReturnStatement : {
671+ body = func . argument
672+ params = null
673+ break ;
674+ }
675+ case AST_NODE_TYPES . CallExpression : {
676+ body = func
677+ params = null
678+ break ;
679+ }
680+ case AST_NODE_TYPES . MemberExpression : {
681+ body = func
682+ params = null
683+ break ;
684+ }
685+ case AST_NODE_TYPES . FunctionDeclaration : {
686+ body = func . body || null
687+ params = func . params
688+ break ;
689+ }
690+ case AST_NODE_TYPES . ArrowFunctionExpression : {
691+ body = func . body || null
692+ params = func . params
693+ break ;
694+ }
695+ case AST_NODE_TYPES . FunctionExpression : {
696+ body = func . body || null
697+ params = func . params
698+ break ;
699+ }
700+ default : {
701+ logger . log ( `~> the helper type ${ func . type } is not processable` )
702+ return false
703+ }
704+ }
705+
706+ if ( ! params ) {
707+ logger . log ( '~> could not get function parameters' )
708+ return false
709+ }
710+
711+ if ( body && body . type === AST_NODE_TYPES . BlockStatement && body . body && body . body [ 0 ] . type === AST_NODE_TYPES . ReturnStatement ) {
712+ body = body . body [ 0 ] . argument
713+ }
714+
715+ if ( ! body ) {
716+ return false
658717 }
659718
660719 if ( constant . isOptimalArray ) {
720+ logger . log ( '=> constant is optimal array' )
721+
661722 // Only looking for:
662723 //
663724 // COLORS.indexOf(param)
664- const { body } = func
665- return body
666- && isCallExpression ( body , constant . name , 'indexOf' )
667- && func . params . length === 1
668- && isIdentifier ( func . params [ 0 ] )
725+ return isCallExpression ( body , constant . name , 'indexOf' )
726+ && params . length === 1
727+ && isIdentifier ( params [ 0 ] )
669728 && body . arguments . length === 1
670- && isIdentifier ( body . arguments [ 0 ] , func . params [ 0 ] . name )
729+ && isIdentifier ( body . arguments [ 0 ] , params [ 0 ] . name )
671730 || false
672731 }
673732
674733 if ( constant . isOptimalObject ) {
734+ logger . log ( '=> constant is optimal object' )
735+
675736 // Only looking for:
676737 //
677- // COLORS[param]
678- const { body } = func
679- return body
680- && func . params . length === 1
681- && isIdentifier ( func . params [ 0 ] )
682- && isCallExpression ( body , constant . name , func . params [ 0 ] . name )
683- && body . callee . computed
738+ // REF_COLORS[param]
739+ return params . length === 1
740+ && isIdentifier ( params [ 0 ] )
741+ && constant . referencedSourceObjectName
742+ && isMemberExpression ( body , constant . referencedSourceObjectName , params [ 0 ] . name )
743+ && body . computed
684744 || false
685745 }
686746
747+ logger . log ( `~> constant is not optimal` )
687748 return false
688749 }
689750}
@@ -694,6 +755,7 @@ export class ResistorColorDuoSolution {
694755 private mainMethod : Entry
695756 private mainExport : [ NonNullable < MainExport [ 0 ] > , MainExport [ 1 ] ]
696757 private fileConstants : ProgramConstants
758+ private mainConstant : Constant | undefined ;
697759
698760 constructor ( public readonly program : Program , source : string ) {
699761 this . source = new Source ( source )
@@ -704,6 +766,12 @@ export class ResistorColorDuoSolution {
704766 // All constants at the top level that are _not_ the main method
705767 this . fileConstants = findTopLevelConstants ( program , [ 'let' , 'const' , 'var' ] )
706768 . filter ( ( declaration ) : boolean => declaration && isIdentifier ( declaration . id ) && declaration . id . name !== EXPECTED_METHOD )
769+
770+ const expectedConstant = this . fileConstants . find ( ( constant ) => isIdentifier ( constant . id , PROBABLE_CONSTANT ) ) ||
771+ // Or find the first array or object assignment
772+ this . fileConstants . find ( ( constant ) => constant . init && [ AST_NODE_TYPES . ArrayExpression , AST_NODE_TYPES . ObjectExpression ] . indexOf ( constant . init . type ) )
773+
774+ this . mainConstant = expectedConstant && new Constant ( expectedConstant , this . source ) || undefined
707775 }
708776
709777 public get entry ( ) : Readonly < Entry > {
@@ -715,7 +783,7 @@ export class ResistorColorDuoSolution {
715783 }
716784
717785 public get hasOptimalEntry ( ) : boolean {
718- return this . entry . isOptimal ( new Constant ( this . fileConstants [ 0 ] ) , this . program )
786+ return this . entry . isOptimal ( this . mainConstant , this . program )
719787 }
720788
721789 public get areFileConstantsConst ( ) : boolean {
0 commit comments