1
1
import { parse } from "babylon-lightscript" ;
2
- import { defaultImports , lightscriptImports , lodashImports } from "./stdlib" ;
2
+ import { defaultImports , lightscriptImports , lodashImports , runtimeHelpers } from "./stdlib" ;
3
3
4
4
export default function ( babel ) {
5
5
const { types : t } = babel ;
@@ -580,6 +580,15 @@ export default function (babel) {
580
580
}
581
581
}
582
582
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
+
583
592
function makeInlineStdlibFn ( inlineFnName ) {
584
593
const fnId = t . identifier ( inlineFnName ) ;
585
594
const aParam = t . identifier ( "a" ) ;
@@ -608,6 +617,16 @@ export default function (babel) {
608
617
] ) ) ;
609
618
}
610
619
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
+
611
630
function insertStdlibImports ( path , imports : Imports , useRequire ) {
612
631
const declarations = [ ] ;
613
632
const inlines = [ ] ;
@@ -653,14 +672,21 @@ export default function (babel) {
653
672
for ( const inlineFnName of inlines ) {
654
673
inlineDeclarations . push ( makeInlineStdlibFn ( inlineFnName ) ) ;
655
674
}
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 ) ;
663
688
}
689
+ insertAfterImports ( path , helpers ) ;
664
690
}
665
691
666
692
function generateForInIterator ( path , type : "array" | "object" ) {
@@ -827,6 +853,103 @@ export default function (babel) {
827
853
}
828
854
}
829
855
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
+
830
953
function transformMatchCases ( argRef , cases ) {
831
954
return cases . reduce ( ( rootIf , path ) => {
832
955
@@ -843,6 +966,10 @@ export default function (babel) {
843
966
// add binding (and always use block bodies)
844
967
ensureBlockBody ( path , "consequent" ) ;
845
968
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
+
846
973
const bindingDecl = t . variableDeclaration ( "const" , [
847
974
t . variableDeclarator ( path . node . binding , argRef )
848
975
] ) ;
@@ -1128,6 +1255,7 @@ export default function (babel) {
1128
1255
const stdlib : Stdlib = initializeStdlib ( state . opts ) ;
1129
1256
const useRequire = state . opts . stdlib && state . opts . stdlib . require === true ;
1130
1257
const imports : Imports = { } ;
1258
+ path . scope . lscRuntimeHelpers = { } ;
1131
1259
1132
1260
path . traverse ( {
1133
1261
@@ -1451,6 +1579,7 @@ export default function (babel) {
1451
1579
} ) ;
1452
1580
1453
1581
insertStdlibImports ( path , imports , useRequire ) ;
1582
+ insertRuntimeHelpers ( path ) ;
1454
1583
}
1455
1584
1456
1585
return {
0 commit comments