@@ -116,7 +116,8 @@ type globResolveResult struct {
116116
117117type tlaCheck struct {
118118 parent ast.Index32
119- depth uint32
119+ depth ast.Index32
120+ pass uint32
120121 importRecordIndex uint32
121122}
122123
@@ -2810,47 +2811,87 @@ func (s *scanner) processScannedFiles(entryPointMeta []graph.EntryPoint) []scann
28102811 s .results [sourceIndex ] = result
28112812 }
28122813
2814+ // Traverse the graph to check top-level await
2815+ if s .iterativelyValidateTLA () {
2816+ s .reportInvalidTLA ()
2817+ }
2818+
28132819 // The linker operates on an array of files, so construct that now. This
28142820 // can't be constructed earlier because we generate new parse results for
28152821 // JavaScript stub files for CSS imports above.
28162822 files := make ([]scannerFile , len (s .results ))
28172823 for sourceIndex := range s .results {
28182824 if result := & s .results [sourceIndex ]; result .ok {
2819- s .validateTLA (uint32 (sourceIndex ))
28202825 files [sourceIndex ] = result .file
28212826 }
28222827 }
28232828
28242829 return files
28252830}
28262831
2827- func (s * scanner ) validateTLA (sourceIndex uint32 ) tlaCheck {
2832+ func (s * scanner ) iterativelyValidateTLA () bool {
2833+ pass := uint32 (1 )
2834+ hasTLA := false
2835+
2836+ // Iterate until a fixed point has been reached to handle graph cycles
2837+ for {
2838+ didChange := false
2839+ for sourceIndex := range s .results {
2840+ s .recursivelyValidateTLA (uint32 (sourceIndex ), pass , & didChange )
2841+ }
2842+ if ! didChange {
2843+ return hasTLA
2844+ }
2845+ pass ++
2846+ hasTLA = true
2847+ }
2848+ }
2849+
2850+ func (s * scanner ) recursivelyValidateTLA (sourceIndex uint32 , pass uint32 , didChange * bool ) tlaCheck {
28282851 result := & s .results [sourceIndex ]
28292852
2830- if result .ok && result .tlaCheck .depth == 0 {
2853+ // Use a "pass" integer instead of a separate "visited" set
2854+ if result .ok && result .tlaCheck .pass != pass {
2855+ result .tlaCheck .pass = pass
2856+
28312857 if repr , ok := result .file .inputFile .Repr .(* graph.JSRepr ); ok {
2832- result . tlaCheck . depth = 1
2833- if repr .AST .LiveTopLevelAwaitKeyword .Len > 0 {
2858+ // If this module contains top-level await, set its parent to itself
2859+ if repr .AST .LiveTopLevelAwaitKeyword .Len > 0 && result . tlaCheck . parent . GetIndex () != sourceIndex {
28342860 result .tlaCheck .parent = ast .MakeIndex32 (sourceIndex )
2861+ result .tlaCheck .depth = ast .MakeIndex32 (1 )
2862+ * didChange = true
28352863 }
28362864
2865+ // Check all import statements and require calls (only import statements are valid)
28372866 for importRecordIndex , record := range repr .AST .ImportRecords {
28382867 if record .SourceIndex .IsValid () && (record .Kind == ast .ImportRequire || record .Kind == ast .ImportStmt ) {
2839- parent := s .validateTLA (record .SourceIndex .GetIndex ())
2840- if ! parent .parent .IsValid () {
2841- continue
2842- }
2868+ parent := s .recursivelyValidateTLA (record .SourceIndex .GetIndex (), pass , didChange )
28432869
2844- // Follow any import chains
2845- if record .Kind == ast .ImportStmt && (! result .tlaCheck .parent .IsValid () || parent .depth < result .tlaCheck .depth ) {
2846- result .tlaCheck .depth = parent .depth + 1
2870+ // Track the shallowest top-level await parent (used to report invalid import chains later on)
2871+ if record .Kind == ast .ImportStmt && parent .depth .GetIndex () < result .tlaCheck .depth .GetIndex ()- 1 {
28472872 result .tlaCheck .parent = record .SourceIndex
2873+ result .tlaCheck .depth = ast .MakeIndex32 (parent .depth .GetIndex () + 1 )
28482874 result .tlaCheck .importRecordIndex = uint32 (importRecordIndex )
2875+ * didChange = true
28492876 continue
28502877 }
2878+ }
2879+ }
2880+ }
2881+ }
2882+
2883+ return result .tlaCheck
2884+ }
28512885
2886+ func (s * scanner ) reportInvalidTLA () {
2887+ for sourceIndex := range s .results {
2888+ result := & s .results [sourceIndex ]
2889+
2890+ if result .ok && result .tlaCheck .parent .IsValid () {
2891+ if repr , ok := result .file .inputFile .Repr .(* graph.JSRepr ); ok {
2892+ for _ , record := range repr .AST .ImportRecords {
28522893 // Require of a top-level await chain is forbidden
2853- if record .Kind == ast .ImportRequire {
2894+ if record .Kind == ast .ImportRequire && record . SourceIndex . IsValid () && s . results [ record . SourceIndex . GetIndex ()]. tlaCheck . parent . IsValid () {
28542895 var notes []logger.MsgData
28552896 var tlaPrettyPaths logger.PrettyPaths
28562897 otherSourceIndex := record .SourceIndex .GetIndex ()
@@ -2899,18 +2940,14 @@ func (s *scanner) validateTLA(sourceIndex uint32) tlaCheck {
28992940 s .log .AddErrorWithNotes (& tracker , record .Range , text , notes )
29002941 }
29012942 }
2902- }
29032943
2904- // Make sure that if we wrap this module in a closure, the closure is also
2905- // async. This happens when you call "import()" on this module and code
2906- // splitting is off.
2907- if result .tlaCheck .parent .IsValid () {
2944+ // Make sure that if we wrap this module in a closure, the closure is also
2945+ // async. This happens when you call "import()" on this module and code
2946+ // splitting is off.
29082947 repr .Meta .IsAsyncOrHasAsyncDependency = true
29092948 }
29102949 }
29112950 }
2912-
2913- return result .tlaCheck
29142951}
29152952
29162953func DefaultExtensionToLoaderMap () map [string ]config.Loader {
0 commit comments