@@ -10,6 +10,7 @@ import acornGlobals from 'acorn-globals';
10
10
import { ModuleRef , SourceFile , PackageDir , TransientCode , NodeModule , ShimModule ,
11
11
StubModule , ModuleDependency , FileNotFound } from './modules' ;
12
12
import { Transpiler } from './transpile' ;
13
+ import { BuildCache } from './cache' ;
13
14
import { Report , ReportSilent } from './ui/report' ;
14
15
15
16
@@ -88,6 +89,7 @@ class SearchPath {
88
89
class Environment {
89
90
infra : Library [ ] = [ ]
90
91
compilers : Transpiler [ ] = [ ]
92
+ cache : BuildCache = new BuildCache
91
93
report : Report = new ReportSilent
92
94
}
93
95
@@ -152,7 +154,7 @@ class AcornCrawl extends InEnvironment {
152
154
while ( wl . length > 0 ) {
153
155
var u = wl . pop ( ) . normalize ( ) , key = u . id ;
154
156
if ( ! vs . has ( key ) ) {
155
- var r = this . visitModuleRef ( u ) ;
157
+ var r = this . memo ( u , u => this . visitModuleRef ( u ) ) ;
156
158
wl . push ( ...r . deps . map ( d => d . target ) ) ;
157
159
vs . set ( key , r ) ;
158
160
}
@@ -165,6 +167,10 @@ class AcornCrawl extends InEnvironment {
165
167
return vs . get ( key ) || this . visitModuleRef ( ref ) ;
166
168
}
167
169
170
+ memo ( m : ModuleRef , op : ( m : ModuleRef ) => VisitResult ) {
171
+ return this . env . cache . memo ( m , 'visit' , op ) ;
172
+ }
173
+
168
174
visitFile ( ref : SourceFile , opts : VisitOptions = { } ) : VisitResult {
169
175
var fn = ref . filename ;
170
176
if ( fn . endsWith ( '.js' ) ) return this . visitJSFile ( ref , opts ) ;
@@ -220,7 +226,8 @@ class AcornCrawl extends InEnvironment {
220
226
221
227
m . extractImports ( ) ;
222
228
var deps = m . imports . map ( u => mkdep ( u , lookup ( u . source . value ) ) )
223
- . concat ( m . requires . map ( u => mkdep ( u , lookup ( u . arguments [ 0 ] . value ) ) ) ) ;
229
+ . concat ( m . requires . map ( u => mkdep ( u , lookup ( u . arguments [ 0 ] . value ) ) ) )
230
+ . concat ( m . exportsFrom . map ( u => mkdep ( u , lookup ( u . source . value ) ) ) ) ;
224
231
return { compiled : m , deps} ;
225
232
}
226
233
@@ -496,6 +503,11 @@ class AcornJSModule extends InEnvironment implements CompilationUnit {
496
503
return s ;
497
504
}
498
505
506
+ get exportsFrom ( ) {
507
+ return this . exports . filter ( u => this . isExportFrom ( u ) ) as
508
+ AcornTypes . ExportNamedDeclaration [ ] ;
509
+ }
510
+
499
511
isImport ( node : acorn . Node ) : node is AcornTypes . ImportDeclaration {
500
512
return AcornUtils . is ( node , 'ImportDeclaration' ) ;
501
513
}
@@ -508,7 +520,17 @@ class AcornJSModule extends InEnvironment implements CompilationUnit {
508
520
}
509
521
510
522
isExport ( node : acorn . Node ) : node is AcornTypes . ExportDeclaration {
511
- return AcornUtils . is ( node , 'ExportNamedDeclaration' ) || AcornUtils . is ( node , 'ExportDefaultDeclaration' ) ;
523
+ return AcornUtils . is ( node , 'ExportNamedDeclaration' ) ||
524
+ AcornUtils . is ( node , 'ExportDefaultDeclaration' ) ;
525
+ }
526
+
527
+ isExportFrom ( node : acorn . Node ) : node is AcornTypes . ExportNamedDeclaration {
528
+ return AcornUtils . is ( node , 'ExportNamedDeclaration' ) && ! ! node . source ;
529
+ }
530
+
531
+ _isShorthandProperty ( node : AcornTypes . Identifier ) {
532
+ return node . parents . slice ( - 2 ) . some ( p =>
533
+ AcornUtils . is ( p , "Property" ) && p . shorthand ) ;
512
534
}
513
535
514
536
process ( key : string , deps : ModuleDependency < acorn . Node > [ ] ) {
@@ -520,9 +542,12 @@ class AcornJSModule extends InEnvironment implements CompilationUnit {
520
542
} ) ,
521
543
requires = this . requires . map ( req => {
522
544
var dep = deps . find ( d => d . source === req ) ;
523
- return dep ? [ this . processRequire ( req , dep . target ) ] : [ ] ;
545
+ return dep ? this . processRequire ( req , dep . target ) : [ ] ;
546
+ } ) ,
547
+ exports = this . exports . map ( exp => {
548
+ var dep = deps . find ( d => d . source === exp ) ; // for `export .. from`
549
+ return this . processExport ( exp , dep && dep . target ) ;
524
550
} ) ,
525
- exports = this . exports . map ( exp => [ this . processExport ( exp ) ] ) ,
526
551
527
552
prog = TextSource . interpolate ( this . text ,
528
553
[ ] . concat ( ...imports , ...requires , ...exports )
@@ -543,39 +568,55 @@ class AcornJSModule extends InEnvironment implements CompilationUnit {
543
568
isDefault = true ;
544
569
}
545
570
else {
546
- var locals = [ ] ;
547
571
lhs = this . _freshVar ( ) ;
548
572
for ( let impspec of imp . specifiers ) {
549
573
assert ( impspec . type === 'ImportSpecifier' ) ;
550
574
let local = impspec . local . name , imported = impspec . imported . name ;
551
- locals . push ( ( local == imported ) ? local : `${ imported } :${ local } ` ) ;
552
- for ( let refnode of this . vars . globals . get ( local ) || [ ] ) {
553
- refs . push ( [ refnode , `${ lhs } .${ imported } ` ] ) ;
554
- }
575
+ refs . push ( ...this . updateReferences ( local , `${ lhs } .${ imported } ` ) ) ;
555
576
}
556
577
}
557
- return [ [ imp , `let ${ lhs } = ${ this . makeRequire ( ref , isDefault ) } ;` ] ] . concat ( refs ) ;
578
+ return [ [ imp , `let ${ lhs } = ${ this . makeRequire ( ref , isDefault ) } ;` ] ]
579
+ . concat ( refs ) ;
558
580
}
559
581
560
582
processRequire ( req : RequireInvocation , ref : ModuleRef ) {
561
- return [ req , this . makeRequire ( ref ) ] ;
583
+ return [ [ req , this . makeRequire ( ref ) ] ] ;
562
584
}
563
585
564
- processExport ( exp : AcornTypes . ExportDeclaration ) {
565
- var locals = [ ] , rhs : string , is = AcornUtils . is ;
586
+ processExport ( exp : AcornTypes . ExportDeclaration , ref : ModuleRef ) {
587
+ var locals = [ ] , rhs : string , is = AcornUtils . is , d = exp . declaration ;
588
+
566
589
if ( is ( exp , 'ExportNamedDeclaration' ) ) {
567
- for ( let expspec of exp . specifiers ) {
568
- assert ( expspec . type === 'ExportSpecifier' ) ;
569
- let local = expspec . local . name , exported = expspec . exported . name ;
570
- locals . push ( ( local == exported ) ? local : `${ local } :${ exported } ` ) ;
590
+ if ( d ) { // <- `export const` etc.
591
+ locals = ( < any > d ) . declarations . map ( u => u . id . name ) ; /** @todo typing & test other cases */
592
+ }
593
+ else {
594
+ for ( let expspec of exp . specifiers ) {
595
+ assert ( expspec . type === 'ExportSpecifier' ) ;
596
+ let local = expspec . local . name , exported = expspec . exported . name ;
597
+ locals . push ( ( local == exported ) ? local : `${ local } :${ exported } ` ) ;
598
+ }
571
599
}
572
- rhs = `{${ locals } }` ;
600
+
601
+ rhs = ( exp . source && ref ) // <- `export .. from`
602
+ ? `${ this . makeRequire ( ref ) } , ${ JSON . stringify ( locals ) } `
603
+ : `{${ locals } }` ;
604
+
605
+ if ( d )
606
+ return [ [ { start : exp . start , end : d . start } , '' ] , // <- remove `export` modifier
607
+ [ { start : exp . end , end :exp . end } , `\nkremlin.export(module, ${ rhs } );` ] ] ;
608
+ else
609
+ return [ [ exp , `kremlin.export(module, ${ rhs } );` ] ] ;
573
610
}
574
611
else if ( is ( exp , 'ExportDefaultDeclaration' ) ) {
575
- rhs = `{default:${ this . text . substring ( exp . declaration . start , exp . declaration . end ) } }` ;
612
+ assert ( d ) ;
613
+ // Careful incision
614
+ return [ [ { start : exp . start ,
615
+ end : d . start } , 'kremlin.export(module, {default:' ] ,
616
+ [ { start : d . end ,
617
+ end : exp . end } , '});' ] ] ;
576
618
}
577
619
else throw new Error ( `invalid export '${ exp . type } '` ) ;
578
- return [ exp , `kremlin.export(module, ${ rhs } );` ] ;
579
620
}
580
621
581
622
makeRequire ( ref : ModuleRef , isDefault : boolean = false ) {
@@ -588,6 +629,16 @@ class AcornJSModule extends InEnvironment implements CompilationUnit {
588
629
}
589
630
}
590
631
632
+ updateReferences ( name : string , expr : string ) {
633
+ var refs = [ ] ;
634
+ for ( let refnode of this . vars . globals . get ( name ) || [ ] ) {
635
+ var colon = this . _isShorthandProperty ( refnode ) ;
636
+ refs . push ( [ refnode ,
637
+ `${ colon ? name + ':' : '' } ${ expr } ` ] ) ;
638
+ }
639
+ return refs ;
640
+ }
641
+
591
642
_freshVar ( base = "" ) {
592
643
if ( ! this . vars ) this . extractVars ( ) ;
593
644
for ( let i = 0 ; ; i ++ ) {
@@ -655,6 +706,7 @@ declare namespace AcornTypes {
655
706
656
707
class ExportDeclaration extends acorn . Node {
657
708
type : "ExportNamedDeclaration" | "ExportDefaultDeclaration"
709
+ declaration ?: acorn . Node
658
710
}
659
711
660
712
class ExportNamedDeclaration extends ExportDeclaration {
@@ -665,7 +717,7 @@ declare namespace AcornTypes {
665
717
666
718
class ExportDefaultDeclaration extends ExportDeclaration {
667
719
type : "ExportDefaultDeclaration"
668
- declaration : acorn . Node
720
+ declaration : acorn . Node // not optional
669
721
}
670
722
671
723
class ExportSpecifier extends acorn . Node {
@@ -681,17 +733,23 @@ declare namespace AcornTypes {
681
733
682
734
class Identifier extends acorn . Node {
683
735
name : string
736
+ parents : acorn . Node [ ] // actually this exists in for all acorn.Nodes :/
737
+ }
738
+
739
+ class Property extends acorn . Node {
740
+ shorthand : boolean
684
741
}
685
742
686
743
}
687
744
688
745
namespace AcornUtils {
689
746
export function is ( node : acorn . Node , type : "Program" ) : node is AcornTypes . Program
690
747
export function is ( node : acorn . Node , type : "ImportSpecifier" ) : node is AcornTypes . ImportSpecifier
691
- export function is ( node : acorn . Node , type : "CallExpression" ) : node is AcornTypes . CallExpression
692
- export function is ( node : acorn . Node , type : "Identifier" ) : node is AcornTypes . Identifier
693
748
export function is ( node : acorn . Node , type : "ExportNamedDeclaration" ) : node is AcornTypes . ExportNamedDeclaration
694
749
export function is ( node : acorn . Node , type : "ExportDefaultDeclaration" ) : node is AcornTypes . ExportDefaultDeclaration
750
+ export function is ( node : acorn . Node , type : "CallExpression" ) : node is AcornTypes . CallExpression
751
+ export function is ( node : acorn . Node , type : "Identifier" ) : node is AcornTypes . Identifier
752
+ export function is ( node : acorn . Node , type : "Property" ) : node is AcornTypes . Property
695
753
export function is ( node : acorn . Node , type : string ) : boolean
696
754
697
755
export function is ( node : acorn . Node , type : string ) { return node . type === type ; }
0 commit comments