11import { parse } from "babylon-lightscript" ;
2- import { defaultImports , lightscriptImports , lodashImports } from "./stdlib" ;
2+ import { defaultImports , lightscriptImports , lodashImports , runtimeHelpers } from "./stdlib" ;
33
44export default function ( babel ) {
55 const { types : t } = babel ;
@@ -580,6 +580,15 @@ export default function (babel) {
580580 }
581581 }
582582
583+ function collectRuntimeHelper ( path , helperName ) {
584+ const programScope = path . scope . getProgramParent ( ) ;
585+ const helpers = programScope . lscRuntimeHelpers ;
586+ if ( ! helpers [ helperName ] ) {
587+ helpers [ helperName ] = programScope . generateUidIdentifier ( helperName ) ;
588+ }
589+ return helpers [ helperName ] ;
590+ }
591+
583592 function makeInlineStdlibFn ( inlineFnName ) {
584593 const fnId = t . identifier ( inlineFnName ) ;
585594 const aParam = t . identifier ( "a" ) ;
@@ -608,6 +617,16 @@ export default function (babel) {
608617 ] ) ) ;
609618 }
610619
620+ function insertAfterImports ( path , nodes ) {
621+ // insert inline fns before the first statement which isn't an import statement
622+ for ( const p of path . get ( "body" ) ) {
623+ if ( ! p . isImportDeclaration ( ) ) {
624+ p . insertBefore ( nodes ) ;
625+ break ;
626+ }
627+ }
628+ }
629+
611630 function insertStdlibImports ( path , imports : Imports , useRequire ) {
612631 const declarations = [ ] ;
613632 const inlines = [ ] ;
@@ -653,14 +672,21 @@ export default function (babel) {
653672 for ( const inlineFnName of inlines ) {
654673 inlineDeclarations . push ( makeInlineStdlibFn ( inlineFnName ) ) ;
655674 }
656- // insert inline fns before the first statement which isn't an import statement
657- for ( const p of path . get ( "body" ) ) {
658- if ( ! p . isImportDeclaration ( ) ) {
659- p . insertBefore ( inlineDeclarations ) ;
660- break ;
661- }
662- }
675+ insertAfterImports ( path , inlineDeclarations ) ;
676+ }
677+ }
678+
679+ function insertRuntimeHelpers ( path ) {
680+ const helpers = [ ] ;
681+ for ( const helperName in path . scope . lscRuntimeHelpers ) {
682+ const fn = runtimeHelpers [ helperName ] ;
683+ const uid = path . scope . lscRuntimeHelpers [ helperName ] ;
684+ const fnAST = babel . template ( fn . toString ( ) ) ( {
685+ [ helperName ] : uid ,
686+ } ) ;
687+ helpers . push ( fnAST ) ;
663688 }
689+ insertAfterImports ( path , helpers ) ;
664690 }
665691
666692 function generateForInIterator ( path , type : "array" | "object" ) {
@@ -827,6 +853,103 @@ export default function (babel) {
827853 }
828854 }
829855
856+ function extendAndChain ( andChainPath , condition ) {
857+ if ( ! andChainPath . node ) {
858+ andChainPath . replaceWith ( condition ) ;
859+ } else {
860+ andChainPath . replaceWith ( t . logicalExpression ( "&&" , andChainPath . node , condition ) ) ;
861+ }
862+ }
863+
864+ function buildAnd ( left , right ) {
865+ if ( left && right ) {
866+ return t . logicalExpression ( "&&" , left , right ) ;
867+ } else if ( left ) {
868+ return left ;
869+ } else if ( right ) {
870+ return right ;
871+ } else {
872+ return t . booleanLiteral ( true ) ;
873+ }
874+ }
875+
876+ function buildTestForBinding ( test , bindingPath , argRef ) {
877+ if ( bindingPath . isObjectPattern ( ) ) {
878+ const isObjUid = collectRuntimeHelper ( bindingPath , "hasProps" ) ;
879+
880+ const propsToCheck = [ ] ;
881+ const childPatterns = [ ] ; // list of [propName, path] tuples
882+ for ( const propPath of bindingPath . get ( "properties" ) ) {
883+ const propName = propPath . get ( "key" ) . node . name ;
884+
885+ if ( propPath . get ( "value" ) . isAssignmentPattern ( ) ) {
886+ if ( propPath . get ( "value.left" ) . isPattern ( ) ) {
887+ childPatterns . push ( [ propName , propPath . get ( "value.left" ) , propPath . get ( "value.right" ) . node ] ) ;
888+ }
889+ } else {
890+ const propStr = t . stringLiteral ( propName ) ;
891+ propsToCheck . push ( propStr ) ;
892+
893+ if ( propPath . get ( "value" ) . isPattern ( ) ) {
894+ childPatterns . push ( [ propName , propPath . get ( "value" ) ] ) ;
895+ }
896+ }
897+ }
898+
899+ const isObjCall = t . callExpression ( isObjUid , [ argRef , t . arrayExpression ( propsToCheck ) ] ) ;
900+ test = buildAnd ( test , isObjCall ) ;
901+
902+ for ( const [ propName , childPatternPath , defaultObj = null ] of childPatterns ) {
903+ const propertyArgRef = t . memberExpression ( argRef , t . identifier ( propName ) ) ;
904+
905+ if ( defaultObj ) {
906+ test = buildAnd ( test , t . logicalExpression ( "||" ,
907+ buildTestForBinding ( null , childPatternPath , propertyArgRef ) ,
908+ buildTestForBinding ( null , childPatternPath , defaultObj )
909+ ) ) ;
910+ } else {
911+ test = buildTestForBinding ( test , childPatternPath , propertyArgRef ) ;
912+ }
913+ }
914+ } else if ( bindingPath . isArrayPattern ( ) ) {
915+ const hasLengthUid = collectRuntimeHelper ( bindingPath , "hasLength" ) ;
916+
917+ const childPatterns = [ ] ; // list of [index, path] tuples.
918+ let minLength = 0 ;
919+ let maxLength = 0 ;
920+ bindingPath . get ( "elements" ) . forEach ( ( elemPath , i ) => {
921+ if ( elemPath . isAssignmentPattern ( ) ) {
922+ ++ maxLength ;
923+ if ( elemPath . get ( "left" ) . isPattern ( ) ) {
924+ childPatterns . push ( [ i , elemPath . get ( "left" ) ] ) ;
925+ }
926+ } else if ( elemPath . isRestElement ( ) ) {
927+ maxLength = null ;
928+ } else {
929+ ++ minLength ;
930+ ++ maxLength ;
931+ if ( elemPath . isPattern ( ) ) {
932+ childPatterns . push ( [ i , elemPath ] ) ;
933+ }
934+ }
935+ } ) ;
936+
937+ const hasLengthCall = t . callExpression ( hasLengthUid , [
938+ argRef ,
939+ t . numericLiteral ( minLength ) ,
940+ maxLength === null ? null : t . numericLiteral ( maxLength )
941+ ] . filter ( x => x !== null ) ) ;
942+ test = buildAnd ( test , hasLengthCall ) ;
943+
944+ for ( const [ index , childPatternPath ] of childPatterns ) {
945+ const elementArgRef = t . memberExpression ( argRef , t . numericLiteral ( index ) , true ) ;
946+ test = buildTestForBinding ( test , childPatternPath , elementArgRef ) ;
947+ }
948+ } else throw new TypeError ( `Expected Pattern , got ${bindingPath . node . type } `) ;
949+
950+ return test ;
951+ }
952+
830953 function transformMatchCases ( argRef , cases ) {
831954 return cases . reduce ( ( rootIf , path ) => {
832955
@@ -843,6 +966,10 @@ export default function (babel) {
843966 // add binding (and always use block bodies)
844967 ensureBlockBody ( path , "consequent" ) ;
845968 if ( path . node . binding ) {
969+ const bindingTest = buildTestForBinding ( null , path . get ( "binding" ) , argRef ) ;
970+ const testWithBindingTest = buildAnd ( path . get ( "test" ) . node , bindingTest )
971+ path . get ( "test" ) . replaceWith ( testWithBindingTest ) ;
972+
846973 const bindingDecl = t . variableDeclaration ( "const" , [
847974 t . variableDeclarator ( path . node . binding , argRef )
848975 ] ) ;
@@ -1128,6 +1255,7 @@ export default function (babel) {
11281255 const stdlib : Stdlib = initializeStdlib ( state . opts ) ;
11291256 const useRequire = state . opts . stdlib && state . opts . stdlib . require === true ;
11301257 const imports : Imports = { } ;
1258+ path . scope . lscRuntimeHelpers = { } ;
11311259
11321260 path . traverse ( {
11331261
@@ -1451,6 +1579,7 @@ export default function (babel) {
14511579 } ) ;
14521580
14531581 insertStdlibImports ( path , imports , useRequire ) ;
1582+ insertRuntimeHelpers ( path ) ;
14541583 }
14551584
14561585 return {
0 commit comments