@depup/webpack 5.106.0-depup.0 → 5.109.0-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -11
- package/changes.json +16 -24
- package/hot/dev-server.js +2 -0
- package/lib/APIPlugin.js +60 -35
- package/lib/AsyncDependenciesBlock.js +3 -0
- package/lib/AutomaticPrefetchPlugin.js +6 -1
- package/lib/BannerPlugin.js +13 -5
- package/lib/Cache.js +27 -5
- package/lib/CacheFacade.js +41 -0
- package/lib/Chunk.js +71 -25
- package/lib/ChunkGraph.js +141 -9
- package/lib/ChunkGroup.js +115 -28
- package/lib/ChunkTemplate.js +9 -0
- package/lib/CircularModulesPlugin.js +190 -0
- package/lib/CleanPlugin.js +23 -26
- package/lib/CodeGenerationResults.js +19 -0
- package/lib/CompatibilityPlugin.js +54 -37
- package/lib/Compilation.js +635 -107
- package/lib/Compiler.js +93 -31
- package/lib/ConcatenationScope.js +34 -4
- package/lib/ConditionalInitFragment.js +6 -0
- package/lib/ConstPlugin.js +15 -12
- package/lib/ContextExclusionPlugin.js +3 -1
- package/lib/ContextModule.js +345 -55
- package/lib/ContextModuleFactory.js +193 -51
- package/lib/ContextReplacementPlugin.js +8 -1
- package/lib/DefinePlugin.js +498 -127
- package/lib/DependenciesBlock.js +6 -1
- package/lib/Dependency.js +159 -11
- package/lib/DependencyTemplate.js +7 -1
- package/lib/DependencyTemplates.js +4 -0
- package/lib/DotenvPlugin.js +3 -0
- package/lib/DynamicEntryPlugin.js +3 -1
- package/lib/EntryOptionPlugin.js +42 -2
- package/lib/EntryPlugin.js +4 -1
- package/lib/Entrypoint.js +5 -1
- package/lib/EnvironmentPlugin.js +3 -2
- package/lib/ErrorHelpers.js +7 -0
- package/lib/EvalDevToolModulePlugin.js +3 -1
- package/lib/EvalSourceMapDevToolPlugin.js +11 -11
- package/lib/ExportsInfo.js +399 -108
- package/lib/ExportsInfoApiPlugin.js +4 -3
- package/lib/ExternalModule.js +376 -78
- package/lib/ExternalModuleFactoryPlugin.js +75 -3
- package/lib/ExternalsPlugin.js +3 -1
- package/lib/FileSystemInfo.js +575 -149
- package/lib/FlagAllModulesAsUsedPlugin.js +3 -6
- package/lib/FlagDependencyExportsPlugin.js +82 -9
- package/lib/FlagDependencyUsagePlugin.js +92 -6
- package/lib/FlagEntryExportAsUsedPlugin.js +4 -1
- package/lib/Generator.js +24 -3
- package/lib/HotModuleReplacementPlugin.js +110 -44
- package/lib/IgnorePlugin.js +5 -2
- package/lib/IgnoreWarningsPlugin.js +2 -1
- package/lib/InitFragment.js +15 -0
- package/lib/JavascriptMetaInfoPlugin.js +9 -6
- package/lib/LazyBarrel.js +389 -0
- package/lib/LibraryTemplatePlugin.js +2 -1
- package/lib/LoaderOptionsPlugin.js +3 -1
- package/lib/LoaderTargetPlugin.js +3 -1
- package/lib/MainTemplate.js +15 -0
- package/lib/ManifestPlugin.js +9 -2
- package/lib/Module.js +121 -60
- package/lib/ModuleFactory.js +6 -1
- package/lib/ModuleFilenameHelpers.js +5 -2
- package/lib/ModuleGraph.js +69 -2
- package/lib/ModuleGraphConnection.js +9 -0
- package/lib/ModuleInfoHeaderPlugin.js +5 -0
- package/lib/ModuleNotFoundError.js +3 -83
- package/lib/ModuleProfile.js +27 -1
- package/lib/ModuleSourceTypeConstants.js +52 -19
- package/lib/ModuleTemplate.js +10 -0
- package/lib/ModuleTypeConstants.js +20 -4
- package/lib/MultiCompiler.js +59 -4
- package/lib/MultiStats.js +9 -0
- package/lib/MultiWatching.js +6 -0
- package/lib/NoEmitOnErrorsPlugin.js +1 -1
- package/lib/NodeStuffPlugin.js +78 -46
- package/lib/NormalModule.js +485 -181
- package/lib/NormalModuleFactory.js +148 -29
- package/lib/NormalModuleReplacementPlugin.js +3 -1
- package/lib/NullFactory.js +1 -0
- package/lib/OptionsApply.js +1 -0
- package/lib/Parser.js +3 -1
- package/lib/PlatformPlugin.js +2 -1
- package/lib/PrefetchPlugin.js +4 -1
- package/lib/ProgressPlugin.js +351 -161
- package/lib/ProvidePlugin.js +6 -3
- package/lib/RawModule.js +32 -16
- package/lib/RecordIdsPlugin.js +9 -0
- package/lib/RequestShortener.js +8 -0
- package/lib/ResolverFactory.js +5 -0
- package/lib/RuntimeGlobals.js +58 -5
- package/lib/RuntimeModule.js +22 -7
- package/lib/RuntimePlugin.js +104 -26
- package/lib/RuntimeTemplate.js +616 -37
- package/lib/SelfModuleFactory.js +3 -0
- package/lib/SourceMapDevToolModuleOptionsPlugin.js +2 -0
- package/lib/SourceMapDevToolPlugin.js +341 -63
- package/lib/Stats.js +6 -0
- package/lib/Template.js +29 -3
- package/lib/TemplatedPathPlugin.js +505 -138
- package/lib/UseStrictPlugin.js +3 -2
- package/lib/WarnCaseSensitiveModulesPlugin.js +72 -3
- package/lib/WarnDeprecatedOptionPlugin.js +6 -2
- package/lib/WarnNoModeSetPlugin.js +18 -2
- package/lib/WatchIgnorePlugin.js +5 -1
- package/lib/Watching.js +25 -3
- package/lib/WebpackError.js +3 -74
- package/lib/WebpackIsIncludedPlugin.js +4 -2
- package/lib/WebpackOptionsApply.js +88 -25
- package/lib/WebpackOptionsDefaulter.js +1 -0
- package/lib/asset/AssetBytesGenerator.js +28 -10
- package/lib/asset/AssetBytesParser.js +1 -0
- package/lib/asset/AssetGenerator.js +199 -60
- package/lib/asset/AssetModule.js +47 -0
- package/lib/asset/AssetModulesPlugin.js +115 -24
- package/lib/asset/AssetParser.js +4 -2
- package/lib/asset/AssetSourceGenerator.js +26 -8
- package/lib/asset/AssetSourceParser.js +1 -0
- package/lib/asset/RawDataUrlModule.js +23 -13
- package/lib/asset/WebManifestGenerator.js +164 -0
- package/lib/asset/WebManifestParser.js +130 -0
- package/lib/async-modules/AsyncModuleHelpers.js +1 -0
- package/lib/async-modules/AwaitDependenciesInitFragment.js +18 -4
- package/lib/async-modules/InferAsyncModulesPlugin.js +1 -1
- package/lib/async-modules/isGeneratorLowered.js +26 -0
- package/lib/buildChunkGraph.js +107 -15
- package/lib/bun/BunTargetPlugin.js +42 -0
- package/lib/cache/AddBuildDependenciesPlugin.js +3 -1
- package/lib/cache/AddManagedPathsPlugin.js +5 -1
- package/lib/cache/IdleFileCachePlugin.js +6 -1
- package/lib/cache/MemoryCachePlugin.js +1 -1
- package/lib/cache/MemoryWithGcCachePlugin.js +4 -1
- package/lib/cache/PackFileCacheStrategy.js +43 -7
- package/lib/cache/ResolverCachePlugin.js +12 -1
- package/lib/cache/getLazyHashedEtag.js +13 -2
- package/lib/cache/mergeEtags.js +4 -0
- package/lib/cli.js +142 -20
- package/lib/config/browserslistTargetHandler.js +141 -0
- package/lib/config/defaults.js +591 -41
- package/lib/config/defineConfig.js +31 -0
- package/lib/config/normalization.js +46 -1
- package/lib/config/target.js +200 -2
- package/lib/container/ContainerEntryDependency.js +2 -0
- package/lib/container/ContainerEntryModule.js +27 -14
- package/lib/container/ContainerEntryModuleFactory.js +1 -0
- package/lib/container/ContainerExposedDependency.js +6 -2
- package/lib/container/ContainerPlugin.js +2 -1
- package/lib/container/ContainerReferencePlugin.js +3 -2
- package/lib/container/FallbackDependency.js +9 -7
- package/lib/container/FallbackItemDependency.js +1 -0
- package/lib/container/FallbackModule.js +22 -9
- package/lib/container/FallbackModuleFactory.js +1 -0
- package/lib/container/ModuleFederationPlugin.js +12 -26
- package/lib/container/RemoteModule.js +33 -10
- package/lib/container/RemoteRuntimeModule.js +15 -12
- package/lib/container/RemoteToExternalDependency.js +1 -0
- package/lib/container/options.js +7 -0
- package/lib/css/CssGenerator.js +534 -272
- package/lib/css/CssInjectStyleRuntimeModule.js +86 -70
- package/lib/css/CssLoadingRuntimeModule.js +156 -77
- package/lib/{CssModule.js → css/CssModule.js} +72 -37
- package/lib/css/CssModulesPlugin.js +356 -204
- package/lib/css/CssParser.js +3452 -1950
- package/lib/css/syntax.js +4058 -0
- package/lib/debug/ProfilingPlugin.js +33 -2
- package/lib/deno/DenoTargetPlugin.js +41 -0
- package/lib/dependencies/AMDDefineDependency.js +29 -17
- package/lib/dependencies/AMDDefineDependencyParserPlugin.js +19 -6
- package/lib/dependencies/AMDPlugin.js +6 -3
- package/lib/dependencies/AMDRequireArrayDependency.js +13 -11
- package/lib/dependencies/AMDRequireContextDependency.js +10 -11
- package/lib/dependencies/AMDRequireDependenciesBlock.js +1 -0
- package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +28 -14
- package/lib/dependencies/AMDRequireDependency.js +28 -20
- package/lib/dependencies/AMDRequireItemDependency.js +1 -0
- package/lib/dependencies/AMDRuntimeModules.js +3 -0
- package/lib/dependencies/CachedConstDependency.js +9 -1
- package/lib/dependencies/CommonJsDependencyHelpers.js +104 -0
- package/lib/dependencies/CommonJsExportRequireDependency.js +138 -38
- package/lib/dependencies/CommonJsExportsDependency.js +74 -20
- package/lib/dependencies/CommonJsExportsParserPlugin.js +246 -15
- package/lib/dependencies/CommonJsFullRequireDependency.js +62 -27
- package/lib/dependencies/CommonJsImportsParserPlugin.js +187 -24
- package/lib/dependencies/CommonJsPlugin.js +7 -4
- package/lib/dependencies/CommonJsRequireContextDependency.js +13 -13
- package/lib/dependencies/CommonJsRequireDependency.js +135 -13
- package/lib/dependencies/CommonJsSelfReferenceDependency.js +60 -16
- package/lib/dependencies/ConstDependency.js +22 -12
- package/lib/dependencies/ContextDependency.js +13 -2
- package/lib/dependencies/ContextDependencyHelpers.js +7 -4
- package/lib/dependencies/ContextDependencyTemplateAsId.js +2 -1
- package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +1 -0
- package/lib/dependencies/ContextElementDependency.js +27 -14
- package/lib/dependencies/CreateRequireParserPlugin.js +14 -7
- package/lib/dependencies/CreateScriptUrlDependency.js +9 -7
- package/lib/dependencies/CriticalDependencyWarning.js +3 -1
- package/lib/dependencies/CssIcssExportDependency.js +682 -368
- package/lib/dependencies/CssIcssImportDependency.js +62 -17
- package/lib/dependencies/CssIcssSymbolDependency.js +36 -17
- package/lib/dependencies/CssImportDependency.js +37 -1
- package/lib/dependencies/CssUrlDependency.js +87 -14
- package/lib/dependencies/DelegatedSourceDependency.js +1 -0
- package/lib/dependencies/DllEntryDependency.js +12 -13
- package/lib/dependencies/DynamicExports.js +5 -0
- package/lib/dependencies/EntryDependency.js +1 -0
- package/lib/dependencies/ExportBindingInitFragment.js +164 -0
- package/lib/dependencies/ExportsInfoDependency.js +18 -12
- package/lib/dependencies/ExternalModuleDependency.js +13 -5
- package/lib/dependencies/ExternalModuleInitFragment.js +10 -5
- package/lib/dependencies/ExternalModuleInitFragmentDependency.js +19 -12
- package/lib/dependencies/HarmonyAcceptDependency.js +24 -26
- package/lib/dependencies/HarmonyAcceptImportDependency.js +2 -0
- package/lib/dependencies/HarmonyCompatibilityDependency.js +33 -14
- package/lib/dependencies/HarmonyDetectionParserPlugin.js +108 -30
- package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +32 -2
- package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +70 -33
- package/lib/dependencies/HarmonyExportExpressionDependency.js +140 -46
- package/lib/dependencies/HarmonyExportHeaderDependency.js +11 -9
- package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +253 -37
- package/lib/dependencies/HarmonyExportInitFragment.js +28 -20
- package/lib/dependencies/HarmonyExportSpecifierDependency.js +88 -18
- package/lib/dependencies/HarmonyExports.js +6 -1
- package/lib/dependencies/HarmonyImportDependency.js +59 -3
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +139 -146
- package/lib/dependencies/HarmonyImportGuard.js +426 -0
- package/lib/dependencies/HarmonyImportSideEffectDependency.js +22 -1
- package/lib/dependencies/HarmonyImportSpecifierDependency.js +136 -17
- package/lib/{HarmonyLinkingError.js → dependencies/HarmonyLinkingError.js} +6 -3
- package/lib/dependencies/HarmonyModulesPlugin.js +11 -1
- package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +2 -1
- package/lib/dependencies/HtmlEntryDependency.js +1295 -0
- package/lib/dependencies/HtmlInlineHtmlDependency.js +107 -0
- package/lib/dependencies/HtmlInlineScriptDependency.js +126 -0
- package/lib/dependencies/HtmlInlineStyleDependency.js +152 -0
- package/lib/dependencies/HtmlSourceDependency.js +172 -0
- package/lib/dependencies/ImportContextDependency.js +9 -9
- package/lib/dependencies/ImportDependency.js +92 -3
- package/lib/dependencies/ImportEagerDependency.js +2 -0
- package/lib/dependencies/ImportMetaContextDependency.js +1 -0
- package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +35 -19
- package/lib/dependencies/ImportMetaContextPlugin.js +40 -8
- package/lib/dependencies/ImportMetaGlobDependency.js +81 -0
- package/lib/dependencies/ImportMetaGlobDependencyParserPlugin.js +78 -0
- package/lib/dependencies/ImportMetaGlobHelpers.js +660 -0
- package/lib/dependencies/ImportMetaHotAcceptDependency.js +2 -0
- package/lib/dependencies/ImportMetaHotDeclineDependency.js +2 -0
- package/lib/dependencies/ImportMetaPlugin.js +595 -209
- package/lib/dependencies/ImportMetaResolveDependency.js +90 -0
- package/lib/dependencies/ImportParserPlugin.js +114 -27
- package/lib/dependencies/ImportPhase.js +17 -11
- package/lib/dependencies/ImportPlugin.js +2 -1
- package/lib/dependencies/ImportWeakDependency.js +3 -0
- package/lib/dependencies/JsonExportsDependency.js +15 -10
- package/lib/dependencies/LoaderDependency.js +2 -0
- package/lib/dependencies/LoaderImportDependency.js +3 -0
- package/lib/dependencies/LoaderPlugin.js +9 -2
- package/lib/dependencies/LocalModule.js +15 -12
- package/lib/dependencies/LocalModuleDependency.js +15 -13
- package/lib/dependencies/LocalModulesHelpers.js +3 -0
- package/lib/dependencies/ModuleDecoratorDependency.js +16 -10
- package/lib/dependencies/ModuleDependency.js +14 -0
- package/lib/dependencies/ModuleDependencyTemplateAsId.js +1 -0
- package/lib/dependencies/ModuleDependencyTemplateAsRequireId.js +1 -0
- package/lib/dependencies/ModuleHotAcceptDependency.js +2 -0
- package/lib/dependencies/ModuleHotDeclineDependency.js +2 -0
- package/lib/dependencies/ModuleInitFragmentDependency.js +19 -11
- package/lib/dependencies/NullDependency.js +2 -0
- package/lib/dependencies/PrefetchDependency.js +1 -0
- package/lib/dependencies/ProvidedDependency.js +36 -27
- package/lib/dependencies/PureExpressionDependency.js +14 -10
- package/lib/dependencies/RequireContextDependency.js +1 -0
- package/lib/dependencies/RequireContextDependencyParserPlugin.js +2 -1
- package/lib/dependencies/RequireContextPlugin.js +2 -1
- package/lib/dependencies/RequireEnsureDependenciesBlock.js +1 -0
- package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +4 -6
- package/lib/dependencies/RequireEnsureDependency.js +16 -13
- package/lib/dependencies/RequireEnsureItemDependency.js +1 -0
- package/lib/dependencies/RequireEnsurePlugin.js +2 -1
- package/lib/dependencies/RequireHeaderDependency.js +7 -4
- package/lib/dependencies/RequireIncludeDependency.js +2 -0
- package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +11 -12
- package/lib/dependencies/RequireIncludePlugin.js +2 -1
- package/lib/{RequireJsStuffPlugin.js → dependencies/RequireJsStuffPlugin.js} +9 -8
- package/lib/dependencies/RequireResolveContextDependency.js +10 -11
- package/lib/dependencies/RequireResolveDependency.js +2 -0
- package/lib/dependencies/RequireResolveHeaderDependency.js +8 -5
- package/lib/dependencies/RuntimeRequirementsDependency.js +11 -8
- package/lib/dependencies/StaticExportsDependency.js +13 -10
- package/lib/dependencies/SystemPlugin.js +9 -6
- package/lib/dependencies/SystemRuntimeModule.js +1 -0
- package/lib/dependencies/TopLevelAwaitDependency.js +93 -0
- package/lib/dependencies/URLContextDependency.js +8 -7
- package/lib/dependencies/URLDependency.js +157 -33
- package/lib/dependencies/URLPlugin.js +2 -0
- package/lib/dependencies/UnsupportedDependency.js +12 -13
- package/lib/dependencies/WebAssemblyExportImportedDependency.js +15 -16
- package/lib/dependencies/WebAssemblyImportDependency.js +19 -18
- package/lib/dependencies/WebpackIsIncludedDependency.js +3 -0
- package/lib/dependencies/{WorkerPlugin.js → WorkerAndWorkletPlugin.js} +364 -114
- package/lib/dependencies/WorkerDependency.js +111 -27
- package/lib/dependencies/WorkletDependency.js +278 -0
- package/lib/dependencies/getFunctionExpression.js +1 -0
- package/lib/dependencies/processExportInfo.js +14 -11
- package/lib/{DelegatedModule.js → dll/DelegatedModule.js} +71 -44
- package/lib/{DelegatedModuleFactoryPlugin.js → dll/DelegatedModuleFactoryPlugin.js} +27 -4
- package/lib/{DelegatedPlugin.js → dll/DelegatedPlugin.js} +5 -3
- package/lib/{DllEntryPlugin.js → dll/DllEntryPlugin.js} +9 -5
- package/lib/{DllModule.js → dll/DllModule.js} +36 -24
- package/lib/{DllModuleFactory.js → dll/DllModuleFactory.js} +5 -4
- package/lib/{DllPlugin.js → dll/DllPlugin.js} +24 -6
- package/lib/{DllReferencePlugin.js → dll/DllReferencePlugin.js} +26 -18
- package/lib/{LibManifestPlugin.js → dll/LibManifestPlugin.js} +14 -10
- package/lib/electron/ElectronTargetPlugin.js +24 -5
- package/lib/{AbstractMethodError.js → errors/AbstractMethodError.js} +10 -1
- package/lib/{AsyncDependencyToInitialChunkError.js → errors/AsyncDependencyToInitialChunkError.js} +8 -3
- package/lib/errors/BuildCycleError.js +1 -1
- package/lib/{ChunkRenderError.js → errors/ChunkRenderError.js} +1 -1
- package/lib/{CodeGenerationError.js → errors/CodeGenerationError.js} +1 -1
- package/lib/{CommentCompilationWarning.js → errors/CommentCompilationWarning.js} +9 -3
- package/lib/{ConcurrentCompilationError.js → errors/ConcurrentCompilationError.js} +5 -2
- package/lib/{EnvironmentNotSupportAsyncWarning.js → errors/EnvironmentNotSupportAsyncWarning.js} +5 -5
- package/lib/{HookWebpackError.js → errors/HookWebpackError.js} +20 -16
- package/lib/{IgnoreErrorModuleFactory.js → errors/IgnoreErrorModuleFactory.js} +7 -4
- package/lib/{InvalidDependenciesModuleWarning.js → errors/InvalidDependenciesModuleWarning.js} +6 -3
- package/lib/errors/JSONParseError.js +115 -0
- package/lib/errors/LoaderLoadingError.js +20 -0
- package/lib/{ModuleBuildError.js → errors/ModuleBuildError.js} +22 -16
- package/lib/{ModuleDependencyError.js → errors/ModuleDependencyError.js} +10 -3
- package/lib/{ModuleDependencyWarning.js → errors/ModuleDependencyWarning.js} +13 -5
- package/lib/{ModuleError.js → errors/ModuleError.js} +10 -14
- package/lib/{ModuleHashingError.js → errors/ModuleHashingError.js} +5 -1
- package/lib/errors/ModuleNotFoundError.js +94 -0
- package/lib/errors/ModuleParseError.js +141 -0
- package/lib/{ModuleRestoreError.js → errors/ModuleRestoreError.js} +4 -1
- package/lib/{ModuleStoreError.js → errors/ModuleStoreError.js} +5 -1
- package/lib/{ModuleWarning.js → errors/ModuleWarning.js} +13 -14
- package/lib/{NodeStuffInWebError.js → errors/NodeStuffInWebError.js} +7 -5
- package/lib/errors/NonErrorEmittedError.js +30 -0
- package/lib/{UnhandledSchemeError.js → errors/UnhandledSchemeError.js} +9 -2
- package/lib/{UnsupportedFeatureWarning.js → errors/UnsupportedFeatureWarning.js} +4 -3
- package/lib/errors/WebpackError.js +84 -0
- package/lib/esm/ExportWebpackRequireRuntimeModule.js +2 -0
- package/lib/esm/ModuleChunkFormatPlugin.js +52 -15
- package/lib/esm/ModuleChunkLoadingPlugin.js +11 -3
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +71 -49
- package/lib/hmr/HotModuleReplacement.runtime.js +175 -30
- package/lib/hmr/HotModuleReplacementRuntimeModule.js +8 -0
- package/lib/hmr/JavascriptHotModuleReplacement.runtime.js +173 -26
- package/lib/hmr/JavascriptHotModuleReplacementHelper.js +1 -0
- package/lib/hmr/LazyCompilationPlugin.js +154 -7
- package/lib/hmr/lazyCompilationBackend.js +34 -8
- package/lib/html/HtmlGenerator.js +1470 -0
- package/lib/html/HtmlModule.js +41 -0
- package/lib/html/HtmlModulesPlugin.js +1111 -0
- package/lib/html/HtmlParser.js +1970 -0
- package/lib/html/favicon.svg +1 -0
- package/lib/html/syntax.js +9020 -0
- package/lib/ids/ChunkModuleIdRangePlugin.js +3 -1
- package/lib/ids/DeterministicChunkIdsPlugin.js +3 -1
- package/lib/ids/DeterministicModuleIdsPlugin.js +3 -1
- package/lib/ids/HashedModuleIdsPlugin.js +2 -1
- package/lib/ids/IdHelpers.js +25 -2
- package/lib/ids/NamedChunkIdsPlugin.js +3 -1
- package/lib/ids/NamedModuleIdsPlugin.js +3 -1
- package/lib/ids/NaturalChunkIdsPlugin.js +1 -1
- package/lib/ids/NaturalModuleIdsPlugin.js +1 -1
- package/lib/ids/OccurrenceChunkIdsPlugin.js +2 -1
- package/lib/ids/OccurrenceModuleIdsPlugin.js +4 -1
- package/lib/ids/SyncModuleIdsPlugin.js +3 -1
- package/lib/index.js +66 -15
- package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +5 -6
- package/lib/javascript/BasicEvaluatedExpression.js +370 -49
- package/lib/javascript/ChunkFormatHelpers.js +2 -1
- package/lib/javascript/ChunkHelpers.js +1 -0
- package/lib/javascript/CommonJsChunkFormatPlugin.js +1 -1
- package/lib/javascript/EnableChunkLoadingPlugin.js +6 -1
- package/lib/javascript/JavascriptGenerator.js +213 -51
- package/lib/javascript/JavascriptModule.js +54 -0
- package/lib/javascript/JavascriptModulesPlugin.js +579 -183
- package/lib/javascript/JavascriptParser.js +1289 -462
- package/lib/javascript/JavascriptParserHelpers.js +10 -7
- package/lib/javascript/StartupHelpers.js +5 -0
- package/lib/javascript/syntax.js +4380 -0
- package/lib/json/JsonData.js +5 -0
- package/lib/json/JsonGenerator.js +21 -0
- package/lib/json/JsonModule.js +40 -0
- package/lib/json/JsonModulesPlugin.js +8 -1
- package/lib/json/JsonParser.js +18 -27
- package/lib/library/AbstractLibraryPlugin.js +17 -2
- package/lib/library/AmdLibraryPlugin.js +8 -0
- package/lib/library/AssignLibraryPlugin.js +21 -3
- package/lib/library/EnableLibraryPlugin.js +8 -2
- package/lib/library/ExportPropertyLibraryPlugin.js +10 -0
- package/lib/{FalseIIFEUmdWarning.js → library/FalseIIFEUmdWarning.js} +2 -1
- package/lib/library/JsonpLibraryPlugin.js +8 -0
- package/lib/library/ModuleLibraryPlugin.js +154 -7
- package/lib/library/SystemLibraryPlugin.js +11 -3
- package/lib/library/UmdLibraryPlugin.js +16 -0
- package/lib/loaders/LoaderRunner.js +714 -0
- package/lib/loaders/loadLoader.js +100 -0
- package/lib/logging/Logger.js +17 -0
- package/lib/logging/createConsoleLogger.js +7 -0
- package/lib/logging/runtime.js +2 -0
- package/lib/logging/truncateArgs.js +2 -0
- package/lib/node/CommonJsChunkLoadingPlugin.js +5 -1
- package/lib/node/NodeEnvironmentPlugin.js +7 -3
- package/lib/node/NodeSourcePlugin.js +1 -1
- package/lib/node/NodeTargetPlugin.js +4 -63
- package/lib/node/NodeTemplatePlugin.js +3 -1
- package/lib/node/NodeWatchFileSystem.js +40 -22
- package/lib/node/ReadFileChunkLoadingRuntimeModule.js +13 -9
- package/lib/node/ReadFileCompileAsyncWasmPlugin.js +23 -1
- package/lib/node/ReadFileCompileWasmPlugin.js +18 -1
- package/lib/node/RequireChunkLoadingRuntimeModule.js +21 -16
- package/lib/node/nodeBuiltins.js +80 -0
- package/lib/node/nodeConsole.js +116 -64
- package/lib/optimize/AggressiveMergingPlugin.js +3 -1
- package/lib/optimize/AggressiveSplittingPlugin.js +6 -1
- package/lib/optimize/ConcatenatedModule.js +585 -116
- package/lib/optimize/ConstExportsPlugin.js +211 -0
- package/lib/optimize/EnsureChunkConditionsPlugin.js +2 -1
- package/lib/optimize/FlagIncludedChunksPlugin.js +15 -4
- package/lib/optimize/InlineExports.js +178 -0
- package/lib/optimize/InnerGraph.js +374 -262
- package/lib/optimize/InnerGraphPlugin.js +246 -117
- package/lib/optimize/LimitChunkCountPlugin.js +9 -0
- package/lib/optimize/MangleExportsPlugin.js +5 -1
- package/lib/optimize/MergeDuplicateChunksPlugin.js +2 -0
- package/lib/optimize/MinChunkSizePlugin.js +2 -1
- package/lib/optimize/MinMaxSizeWarning.js +5 -4
- package/lib/optimize/ModuleConcatenationPlugin.js +159 -34
- package/lib/optimize/RealContentHashPlugin.js +125 -50
- package/lib/optimize/RemoveEmptyChunksPlugin.js +2 -1
- package/lib/optimize/RemoveParentModulesPlugin.js +3 -1
- package/lib/optimize/RuntimeChunkPlugin.js +2 -1
- package/lib/optimize/SideEffectsFlagPlugin.js +381 -55
- package/lib/optimize/SplitChunksPlugin.js +54 -6
- package/lib/performance/AssetsOverSizeLimitWarning.js +3 -2
- package/lib/performance/EntrypointsOverSizeLimitWarning.js +3 -2
- package/lib/performance/NoAsyncChunksWarning.js +5 -3
- package/lib/performance/SizeLimitsPlugin.js +8 -2
- package/lib/prefetch/ChunkPrefetchFunctionRuntimeModule.js +1 -0
- package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +21 -0
- package/lib/prefetch/ChunkPrefetchStartupRuntimeModule.js +1 -0
- package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +5 -1
- package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +25 -13
- package/lib/prefetch/ResourceHintPlugin.js +663 -0
- package/lib/prefetch/ResourceHintRuntimeModule.js +149 -0
- package/lib/prefetch/StartupAssetHintRuntimeModule.js +210 -0
- package/lib/prefetch/parseResourceHintOptions.js +103 -0
- package/lib/rules/BasicEffectRulePlugin.js +3 -0
- package/lib/rules/BasicMatcherRulePlugin.js +3 -0
- package/lib/rules/ObjectMatcherRulePlugin.js +3 -0
- package/lib/rules/RuleSetCompiler.js +132 -0
- package/lib/rules/UseEffectRulePlugin.js +10 -3
- package/lib/runtime/AsyncModuleGeneratorRuntimeModule.js +56 -0
- package/lib/runtime/AsyncModuleRuntimeModule.js +52 -33
- package/lib/runtime/AutoPublicPathRuntimeModule.js +22 -9
- package/lib/runtime/BaseUriRuntimeModule.js +1 -0
- package/lib/runtime/ChunkNameRuntimeModule.js +1 -0
- package/lib/runtime/CommonJsWrapRuntimeModule.js +37 -0
- package/lib/runtime/CompatGetDefaultExportRuntimeModule.js +2 -1
- package/lib/runtime/CompatRuntimeModule.js +2 -0
- package/lib/runtime/CreateFakeNamespaceObjectRuntimeModule.js +7 -4
- package/lib/runtime/CreateScriptRuntimeModule.js +1 -0
- package/lib/runtime/CreateScriptUrlRuntimeModule.js +1 -0
- package/lib/runtime/DefinePropertyGettersRuntimeModule.js +34 -4
- package/lib/runtime/EnsureChunkRuntimeModule.js +1 -0
- package/lib/runtime/GetChunkFilenameRuntimeModule.js +88 -8
- package/lib/runtime/GetFullHashRuntimeModule.js +1 -0
- package/lib/runtime/GetMainFilenameRuntimeModule.js +1 -0
- package/lib/runtime/GetTrustedTypesPolicyRuntimeModule.js +2 -1
- package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +59 -0
- package/lib/runtime/GlobalRuntimeModule.js +1 -0
- package/lib/runtime/HasOwnPropertyRuntimeModule.js +2 -1
- package/lib/runtime/HelperRuntimeModule.js +5 -0
- package/lib/runtime/LoadScriptRuntimeModule.js +28 -37
- package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +354 -37
- package/lib/runtime/MakeNamespaceObjectRuntimeModule.js +2 -1
- package/lib/runtime/NonceRuntimeModule.js +1 -0
- package/lib/runtime/OnChunksLoadedRuntimeModule.js +5 -4
- package/lib/runtime/PublicPathRuntimeModule.js +1 -0
- package/lib/runtime/RelativeUrlRuntimeModule.js +4 -2
- package/lib/runtime/RuntimeIdRuntimeModule.js +1 -0
- package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +38 -0
- package/lib/runtime/StartupChunkDependenciesPlugin.js +13 -1
- package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +2 -1
- package/lib/runtime/StartupEntrypointRuntimeModule.js +5 -3
- package/lib/runtime/SystemContextRuntimeModule.js +1 -0
- package/lib/runtime/ToBinaryRuntimeModule.js +1 -0
- package/lib/runtime/WorkerRuntimeModule.js +33 -0
- package/lib/schemes/DataUriPlugin.js +14 -2
- package/lib/schemes/FileUriPlugin.js +1 -1
- package/lib/schemes/HttpUriPlugin.js +86 -4
- package/lib/schemes/VirtualUrlPlugin.js +8 -4
- package/lib/serialization/AggregateErrorSerializer.js +9 -6
- package/lib/serialization/ArraySerializer.js +6 -8
- package/lib/serialization/BinaryMiddleware.js +552 -463
- package/lib/serialization/DateObjectSerializer.js +4 -2
- package/lib/serialization/ErrorObjectSerializer.js +10 -6
- package/lib/serialization/FileMiddleware.js +50 -3
- package/lib/serialization/MapObjectSerializer.js +7 -9
- package/lib/serialization/NullPrototypeObjectSerializer.js +9 -9
- package/lib/serialization/ObjectMiddleware.js +73 -13
- package/lib/serialization/PlainObjectSerializer.js +16 -8
- package/lib/serialization/RegExpObjectSerializer.js +4 -2
- package/lib/serialization/Serializer.js +6 -0
- package/lib/serialization/SerializerMiddleware.js +14 -2
- package/lib/serialization/SetObjectSerializer.js +6 -8
- package/lib/serialization/SingleItemMiddleware.js +3 -0
- package/lib/sharing/ConsumeSharedFallbackDependency.js +1 -0
- package/lib/sharing/ConsumeSharedModule.js +21 -7
- package/lib/sharing/ConsumeSharedPlugin.js +11 -11
- package/lib/sharing/ConsumeSharedRuntimeModule.js +55 -45
- package/lib/sharing/ProvideForSharedDependency.js +1 -0
- package/lib/sharing/ProvideSharedDependency.js +34 -15
- package/lib/sharing/ProvideSharedModule.js +42 -12
- package/lib/sharing/ProvideSharedModuleFactory.js +1 -0
- package/lib/sharing/ProvideSharedPlugin.js +13 -8
- package/lib/sharing/SharePlugin.js +5 -1
- package/lib/sharing/ShareRuntimeModule.js +19 -16
- package/lib/sharing/resolveMatchedConfigs.js +6 -2
- package/lib/sharing/utils.js +9 -1
- package/lib/stats/DefaultStatsFactoryPlugin.js +94 -12
- package/lib/stats/DefaultStatsPresetPlugin.js +14 -2
- package/lib/stats/DefaultStatsPrinterPlugin.js +64 -18
- package/lib/stats/StatsFactory.js +39 -18
- package/lib/stats/StatsPrinter.js +16 -2
- package/lib/typescript/TypeScriptPlugin.js +210 -0
- package/lib/url/URLParserPlugin.js +111 -23
- package/lib/util/ArrayHelpers.js +1 -0
- package/lib/util/ArrayQueue.js +10 -5
- package/lib/util/AsyncQueue.js +154 -74
- package/lib/util/Hash.js +2 -2
- package/lib/util/IterableHelpers.js +3 -0
- package/lib/util/LazyBucketSortedSet.js +26 -0
- package/lib/util/LazySet.js +45 -4
- package/lib/util/LocConverter.js +64 -0
- package/lib/util/ParallelismFactorCalculator.js +1 -0
- package/lib/util/Queue.js +6 -3
- package/lib/util/Semaphore.js +15 -1
- package/lib/util/SetHelpers.js +4 -1
- package/lib/util/SortableSet.js +7 -1
- package/lib/util/SourceProcessor.js +119 -0
- package/lib/util/StackedCacheMap.js +20 -3
- package/lib/util/StackedMap.js +162 -43
- package/lib/util/StringXor.js +1 -1
- package/lib/util/TupleQueue.js +7 -3
- package/lib/util/TupleSet.js +14 -0
- package/lib/util/URLAbsoluteSpecifier.js +1 -0
- package/lib/util/WeakTupleMap.js +60 -1
- package/lib/util/binarySearchBounds.js +3 -2
- package/lib/util/chainedImports.js +3 -3
- package/lib/util/cleverMerge.js +19 -2
- package/lib/util/comparators.js +35 -4
- package/lib/util/compileBooleanMatcher.js +9 -0
- package/lib/util/concatenate.js +80 -24
- package/lib/util/conventions.js +46 -1
- package/lib/util/createHash.js +0 -1
- package/lib/util/createHooksRegistry.js +43 -0
- package/lib/util/createMappings.js +118 -0
- package/lib/util/dataURL.js +3 -2
- package/lib/util/deprecation.js +19 -0
- package/lib/util/deterministicGrouping.js +46 -17
- package/lib/util/extractSourceMap.js +2 -1
- package/lib/util/extractUrlAndGlobal.js +1 -0
- package/lib/util/findGraphRoots.js +7 -0
- package/lib/{formatLocation.js → util/formatLocation.js} +4 -2
- package/lib/{SizeFormatHelpers.js → util/formatSize.js} +10 -3
- package/lib/util/fs.js +87 -14
- package/lib/util/generateDebugId.js +1 -0
- package/lib/util/globUtils.js +710 -0
- package/lib/util/hash/BatchedHash.js +1 -0
- package/lib/util/hash/BulkUpdateHash.js +1 -0
- package/lib/util/hash/DebugHash.js +1 -0
- package/lib/util/hash/hash-digest.js +32 -15
- package/lib/util/hash/md4.js +1 -1
- package/lib/util/hash/wasm-hash.js +5 -0
- package/lib/util/hash/xxhash64.js +1 -1
- package/lib/util/identifier.js +97 -6
- package/lib/util/implicitTypeLoaderFallback.js +62 -0
- package/lib/util/internalSerializables.js +102 -60
- package/lib/util/magicComment.js +149 -7
- package/lib/util/makeSerializable.js +7 -0
- package/lib/util/memoize.js +2 -0
- package/lib/util/mimeTypes.js +176 -0
- package/lib/util/nonNumericOnlyHash.js +31 -1
- package/lib/util/numberHash.js +2 -2
- package/lib/util/parseJson.js +41 -0
- package/lib/util/processAsyncTree.js +8 -0
- package/lib/util/property.js +9 -2
- package/lib/util/publicPathPlaceholder.js +64 -0
- package/lib/util/registerExternalSerializer.js +24 -4
- package/lib/util/removeBOM.js +1 -0
- package/lib/util/runtime.js +32 -0
- package/lib/util/semver.js +15 -0
- package/lib/util/serialization.js +2 -0
- package/lib/util/smartGrouping.js +8 -0
- package/lib/util/source.js +23 -0
- package/lib/util/topologicalSort.js +69 -0
- package/lib/validateSchema.js +1 -0
- package/lib/wasm/EnableWasmLoadingPlugin.js +15 -1
- package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +25 -18
- package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +34 -22
- package/lib/wasm-async/AsyncWasmModule.js +77 -0
- package/lib/wasm-async/AsyncWebAssemblyGenerator.js +6 -0
- package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +6 -1
- package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +22 -126
- package/lib/wasm-async/AsyncWebAssemblyParser.js +3 -2
- package/lib/wasm-async/UniversalCompileAsyncWasmPlugin.js +13 -2
- package/lib/wasm-sync/SyncWasmModule.js +39 -0
- package/lib/wasm-sync/UnsupportedWebAssemblyFeatureError.js +7 -3
- package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +86 -19
- package/lib/wasm-sync/WasmFinalizeExportsPlugin.js +4 -3
- package/lib/wasm-sync/WebAssemblyGenerator.js +27 -1
- package/lib/wasm-sync/WebAssemblyInInitialChunkError.js +9 -3
- package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +4 -0
- package/lib/wasm-sync/WebAssemblyModulesPlugin.js +20 -1
- package/lib/wasm-sync/WebAssemblyParser.js +9 -3
- package/lib/wasm-sync/WebAssemblyUtils.js +2 -0
- package/lib/web/FetchCompileAsyncWasmPlugin.js +29 -1
- package/lib/web/FetchCompileWasmPlugin.js +27 -1
- package/lib/web/JsonpChunkLoadingPlugin.js +11 -1
- package/lib/web/JsonpChunkLoadingRuntimeModule.js +58 -64
- package/lib/web/JsonpTemplatePlugin.js +2 -1
- package/lib/webpack.js +78 -1
- package/lib/webworker/ImportScriptsChunkLoadingPlugin.js +10 -1
- package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +15 -10
- package/lib/webworker/WebWorkerTemplatePlugin.js +1 -1
- package/module.d.ts +21 -0
- package/package.json +79 -77
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +1117 -79
- package/schemas/plugins/{DllPlugin.check.d.ts → HtmlGeneratorOptions.check.d.ts} +1 -1
- package/schemas/plugins/HtmlGeneratorOptions.check.js +6 -0
- package/schemas/plugins/HtmlGeneratorOptions.json +3 -0
- package/schemas/plugins/{DllReferencePlugin.check.d.ts → HtmlParserOptions.check.d.ts} +1 -1
- package/schemas/plugins/HtmlParserOptions.check.js +6 -0
- package/schemas/plugins/HtmlParserOptions.json +3 -0
- package/schemas/plugins/ProgressPlugin.check.js +1 -1
- package/schemas/plugins/ProgressPlugin.json +38 -0
- package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
- package/schemas/plugins/container/ContainerReferencePlugin.json +2 -0
- package/schemas/plugins/container/ExternalsType.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.json +2 -0
- package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.d.ts +7 -0
- package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +6 -0
- package/schemas/plugins/css/CssAutoOrModuleParserOptions.json +3 -0
- package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
- package/schemas/plugins/css/CssParserOptions.check.js +1 -1
- package/schemas/plugins/dll/DllPlugin.check.d.ts +7 -0
- package/schemas/plugins/dll/DllReferencePlugin.check.d.ts +7 -0
- package/types.d.ts +8608 -1159
- package/lib/CaseSensitiveModulesWarning.js +0 -72
- package/lib/GraphHelpers.js +0 -46
- package/lib/ModuleParseError.js +0 -125
- package/lib/NoModeWarning.js +0 -23
- package/lib/css/CssMergeStyleSheetsRuntimeModule.js +0 -56
- package/lib/css/walkCssTokens.js +0 -1843
- package/lib/util/AppendOnlyStackedSet.js +0 -78
- /package/schemas/plugins/{DllPlugin.check.js → dll/DllPlugin.check.js} +0 -0
- /package/schemas/plugins/{DllPlugin.json → dll/DllPlugin.json} +0 -0
- /package/schemas/plugins/{DllReferencePlugin.check.js → dll/DllReferencePlugin.check.js} +0 -0
- /package/schemas/plugins/{DllReferencePlugin.json → dll/DllReferencePlugin.json} +0 -0
|
@@ -0,0 +1,4380 @@
|
|
|
1
|
+
/*
|
|
2
|
+
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
3
|
+
Author Alexander Akait @alexander-akait
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
// cspell:ignore yuku binop prec Prec
|
|
9
|
+
|
|
10
|
+
const { Parser: BaseParser, tokTypes } = require("acorn");
|
|
11
|
+
|
|
12
|
+
// acorn exports its token-context table but leaves it out of its public types
|
|
13
|
+
const tokContexts =
|
|
14
|
+
/** @type {Record<string, unknown>} */
|
|
15
|
+
(
|
|
16
|
+
/** @type {{ tokContexts: Record<string, unknown> }} */
|
|
17
|
+
(/** @type {unknown} */ (require("acorn"))).tokContexts
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
/** @typedef {{ token: string, isExpr: boolean, preserveSpace?: boolean, override?: unknown }} TokContextShim acorn TokContext fields read by the owned tokenizer */
|
|
21
|
+
|
|
22
|
+
// acorn's token contexts used by the inlined finishToken context updates
|
|
23
|
+
const CTX_B_STAT = /** @type {TokContextShim} */ (tokContexts.b_stat);
|
|
24
|
+
const CTX_B_EXPR = /** @type {TokContextShim} */ (tokContexts.b_expr);
|
|
25
|
+
const CTX_P_STAT = /** @type {TokContextShim} */ (tokContexts.p_stat);
|
|
26
|
+
const CTX_P_EXPR = /** @type {TokContextShim} */ (tokContexts.p_expr);
|
|
27
|
+
const CTX_F_STAT = /** @type {TokContextShim} */ (tokContexts.f_stat);
|
|
28
|
+
const CTX_F_EXPR = /** @type {TokContextShim} */ (tokContexts.f_expr);
|
|
29
|
+
|
|
30
|
+
// acorn exports its keyword→TokenType map but leaves it out of its public
|
|
31
|
+
// types; used by the word-classification lookups below.
|
|
32
|
+
const keywordTypes =
|
|
33
|
+
/** @type {Record<string, TokenType>} */
|
|
34
|
+
(
|
|
35
|
+
/** @type {{ keywordTypes: Record<string, TokenType> }} */
|
|
36
|
+
(/** @type {unknown} */ (require("acorn"))).keywordTypes
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/** @typedef {import("acorn").Options} Options */
|
|
40
|
+
/** @typedef {import("acorn").Position} Position */
|
|
41
|
+
/** @typedef {import("acorn").Node} Node */
|
|
42
|
+
/** @typedef {import("acorn").Identifier} Identifier */
|
|
43
|
+
/** @typedef {import("acorn").ImportAttribute} ImportAttribute */
|
|
44
|
+
/** @typedef {import("acorn").ImportDefaultSpecifier} ImportDefaultSpecifier */
|
|
45
|
+
/** @typedef {import("acorn").ImportExpression} ImportExpression */
|
|
46
|
+
/** @typedef {import("acorn").Expression} Expression */
|
|
47
|
+
/** @typedef {import("acorn").ImportSpecifier | import("acorn").ImportDefaultSpecifier | import("acorn").ImportNamespaceSpecifier} AnyImportSpecifier */
|
|
48
|
+
/** @typedef {import("acorn").TokenType} TokenType */
|
|
49
|
+
/** @typedef {TokenType & { beforeExpr: boolean, isAssign?: boolean, prefix?: boolean, postfix?: boolean, binop: number | null, updateContext?: (prevType: TokenType) => void }} TokenTypeInternal acorn's internal TokenType fields, absent from its public types */
|
|
50
|
+
/** @typedef {import("estree").SourceLocation} SourceLocation */
|
|
51
|
+
/** @typedef {[number, number]} Range */
|
|
52
|
+
/** @typedef {"defer" | "source"} ImportPhase */
|
|
53
|
+
/** @typedef {import("estree").Comment & { start: number, end: number }} CollectedComment comment as JavascriptParser exposes it */
|
|
54
|
+
|
|
55
|
+
// Symbol-keyed so they stay out of for-in, Object.keys and JSON.stringify
|
|
56
|
+
// over AST nodes.
|
|
57
|
+
const kSource = Symbol("source");
|
|
58
|
+
const kRange = Symbol("range");
|
|
59
|
+
const kText = Symbol("text");
|
|
60
|
+
const kTextStart = Symbol("text start");
|
|
61
|
+
|
|
62
|
+
// Marks import attributes parsed from the legacy `assert {...}` syntax.
|
|
63
|
+
const LEGACY_ASSERT_ATTRIBUTES = Symbol("assert");
|
|
64
|
+
|
|
65
|
+
// acorn's binding types and scope flags, stable across acorn 8
|
|
66
|
+
const BIND_VAR = 1;
|
|
67
|
+
const BIND_LEXICAL = 2;
|
|
68
|
+
const SCOPE_TOP = 1;
|
|
69
|
+
const SCOPE_SIMPLE_CATCH = 32;
|
|
70
|
+
// SCOPE_TOP | SCOPE_FUNCTION | SCOPE_CLASS_STATIC_BLOCK
|
|
71
|
+
const SCOPE_VAR = 0b100000011;
|
|
72
|
+
|
|
73
|
+
// ASCII identifier-continuation chars ($ 0-9 A-Z _ a-z); css/html-style
|
|
74
|
+
// Uint8Array table so the tokenizer fast path is one load per char
|
|
75
|
+
const IDENT_CHAR = new Uint8Array(128);
|
|
76
|
+
IDENT_CHAR[36] = 1;
|
|
77
|
+
IDENT_CHAR[95] = 1;
|
|
78
|
+
for (let i = 48; i <= 57; i++) IDENT_CHAR[i] = 1;
|
|
79
|
+
for (let i = 65; i <= 90; i++) IDENT_CHAR[i] = 1;
|
|
80
|
+
for (let i = 97; i <= 122; i++) IDENT_CHAR[i] = 1;
|
|
81
|
+
|
|
82
|
+
// ASCII identifier-start chars (IDENT_CHAR minus 0-9), for token dispatch in
|
|
83
|
+
// the owned `nextToken` loop.
|
|
84
|
+
const IDENT_START = new Uint8Array(128);
|
|
85
|
+
IDENT_START[36] = 1;
|
|
86
|
+
IDENT_START[95] = 1;
|
|
87
|
+
for (let i = 65; i <= 90; i++) IDENT_START[i] = 1;
|
|
88
|
+
for (let i = 97; i <= 122; i++) IDENT_START[i] = 1;
|
|
89
|
+
|
|
90
|
+
// Single-char punctuators that acorn's `getTokenFromCode` reads as just
|
|
91
|
+
// `++pos; finishToken(type)` (no value, no operator state machine). Dispatching
|
|
92
|
+
// them from `nextToken`'s char table skips the extra `getTokenFromCode` call and
|
|
93
|
+
// its switch for the commonest tokens in JS ( ) { } [ ] ; , : — `0` is "not a
|
|
94
|
+
// simple punctuator" since token types are truthy objects.
|
|
95
|
+
const SIMPLE_PUNCT = Array.from({ length: 128 });
|
|
96
|
+
SIMPLE_PUNCT[40] = tokTypes.parenL;
|
|
97
|
+
SIMPLE_PUNCT[41] = tokTypes.parenR;
|
|
98
|
+
SIMPLE_PUNCT[59] = tokTypes.semi;
|
|
99
|
+
SIMPLE_PUNCT[44] = tokTypes.comma;
|
|
100
|
+
SIMPLE_PUNCT[91] = tokTypes.bracketL;
|
|
101
|
+
SIMPLE_PUNCT[93] = tokTypes.bracketR;
|
|
102
|
+
SIMPLE_PUNCT[123] = tokTypes.braceL;
|
|
103
|
+
SIMPLE_PUNCT[125] = tokTypes.braceR;
|
|
104
|
+
SIMPLE_PUNCT[58] = tokTypes.colon;
|
|
105
|
+
|
|
106
|
+
// Char classification for the owned `nextToken` (yuku's ws_class): one table
|
|
107
|
+
// load steers both the whitespace skip loop and the token dispatch. Token
|
|
108
|
+
// classes sort below CLS_SPACE so the skip loop exits on a single compare.
|
|
109
|
+
const CLS_OTHER = 0;
|
|
110
|
+
const CLS_IDENT = 1;
|
|
111
|
+
const CLS_PUNCT = 2;
|
|
112
|
+
const CLS_DOT = 3;
|
|
113
|
+
const CLS_EQ = 4;
|
|
114
|
+
const CLS_UNICODE = 5;
|
|
115
|
+
const CLS_SPACE = 6;
|
|
116
|
+
const CLS_NEWLINE = 7;
|
|
117
|
+
const CLS_SLASH = 8;
|
|
118
|
+
// Full `charCodeAt` range so the scan loop needs no `code > 127` branch per
|
|
119
|
+
// character: every non-ASCII code unit classifies as CLS_UNICODE (which sorts
|
|
120
|
+
// below CLS_SPACE, so the loop exits on the same single compare) and the
|
|
121
|
+
// dispatch delegates it to acorn's unicode-aware paths.
|
|
122
|
+
const CHAR_CLASS = new Uint8Array(0x10000).fill(CLS_UNICODE, 128);
|
|
123
|
+
for (let i = 0; i < 128; i++) {
|
|
124
|
+
if (IDENT_START[i] === 1 || i === 92) CHAR_CLASS[i] = CLS_IDENT;
|
|
125
|
+
else if (SIMPLE_PUNCT[i] !== undefined) CHAR_CLASS[i] = CLS_PUNCT;
|
|
126
|
+
}
|
|
127
|
+
CHAR_CLASS[46] = CLS_DOT;
|
|
128
|
+
CHAR_CLASS[61] = CLS_EQ;
|
|
129
|
+
CHAR_CLASS[32] = CLS_SPACE;
|
|
130
|
+
CHAR_CLASS[9] = CLS_SPACE;
|
|
131
|
+
CHAR_CLASS[11] = CLS_SPACE;
|
|
132
|
+
CHAR_CLASS[12] = CLS_SPACE;
|
|
133
|
+
CHAR_CLASS[10] = CLS_NEWLINE;
|
|
134
|
+
CHAR_CLASS[13] = CLS_NEWLINE;
|
|
135
|
+
CHAR_CLASS[47] = CLS_SLASH;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Drop-in replacement for acorn's `Node` that materializes `loc` and `range`
|
|
139
|
+
* on first access instead of allocating them during parsing. Most nodes never
|
|
140
|
+
* get either read, which saves three objects and an array per node.
|
|
141
|
+
*/
|
|
142
|
+
class LazyLocNode {
|
|
143
|
+
/**
|
|
144
|
+
* @param {number} pos start offset
|
|
145
|
+
*/
|
|
146
|
+
constructor(pos) {
|
|
147
|
+
this.type = "";
|
|
148
|
+
this.start = pos;
|
|
149
|
+
this.end = 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Memoized in a symbol slot — a plain store is far cheaper than making the
|
|
154
|
+
* property own via defineProperty, and the slot stays invisible to for-in,
|
|
155
|
+
* Object.keys and JSON.stringify. No `loc` is served at all — locations
|
|
156
|
+
* are derived from offsets via `JavascriptParser#getLocation`.
|
|
157
|
+
* @returns {Range} source range
|
|
158
|
+
*/
|
|
159
|
+
get range() {
|
|
160
|
+
const cached = this[kRange];
|
|
161
|
+
if (cached !== undefined) return cached;
|
|
162
|
+
/** @type {Range} */
|
|
163
|
+
const range = [this.start, this.end];
|
|
164
|
+
if (this.end > 0) this[kRange] = range;
|
|
165
|
+
return range;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @param {Range} value source range
|
|
170
|
+
*/
|
|
171
|
+
set range(value) {
|
|
172
|
+
this[kRange] = value;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Single-shape `Identifier`, the most common node: all fields are assigned in
|
|
178
|
+
* one constructor, so every instance is born on its final hidden class instead
|
|
179
|
+
* of transitioning through acorn's start-empty-then-mutate construction.
|
|
180
|
+
*/
|
|
181
|
+
class IdentifierNode {
|
|
182
|
+
/**
|
|
183
|
+
* @param {number} start start offset
|
|
184
|
+
* @param {number} end end offset
|
|
185
|
+
* @param {string} name identifier name
|
|
186
|
+
*/
|
|
187
|
+
constructor(start, end, name) {
|
|
188
|
+
this.type = "Identifier";
|
|
189
|
+
this.start = start;
|
|
190
|
+
this.end = end;
|
|
191
|
+
this.name = name;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Single-shape `Literal`; `bigint` and `regex` stay post-construction
|
|
197
|
+
* additions since both are rare.
|
|
198
|
+
*/
|
|
199
|
+
class LiteralNode {
|
|
200
|
+
/**
|
|
201
|
+
* @param {number} start start offset
|
|
202
|
+
* @param {number} end end offset
|
|
203
|
+
* @param {unknown} value literal value
|
|
204
|
+
* @param {string} raw literal source text
|
|
205
|
+
*/
|
|
206
|
+
constructor(start, end, value, raw) {
|
|
207
|
+
this.type = "Literal";
|
|
208
|
+
this.start = start;
|
|
209
|
+
this.end = end;
|
|
210
|
+
this.value = value;
|
|
211
|
+
this.raw = raw;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Single-shape `MemberExpression`. `optional` is a real field on every
|
|
217
|
+
* instance since webpack always parses with `ecmaVersion >= 11`.
|
|
218
|
+
*/
|
|
219
|
+
class MemberExpressionNode {
|
|
220
|
+
/**
|
|
221
|
+
* @param {number} start start offset
|
|
222
|
+
* @param {number} end end offset
|
|
223
|
+
* @param {Expression} object object expression
|
|
224
|
+
* @param {Node} property property node
|
|
225
|
+
* @param {boolean} computed whether the access is computed (`a[b]`)
|
|
226
|
+
* @param {boolean} optional whether the access is optional (`a?.b`)
|
|
227
|
+
*/
|
|
228
|
+
constructor(start, end, object, property, computed, optional) {
|
|
229
|
+
this.type = "MemberExpression";
|
|
230
|
+
this.start = start;
|
|
231
|
+
this.end = end;
|
|
232
|
+
this.object = object;
|
|
233
|
+
this.property = property;
|
|
234
|
+
this.computed = computed;
|
|
235
|
+
this.optional = optional;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Single-shape `CallExpression`; `optional` as in `MemberExpressionNode`.
|
|
241
|
+
*/
|
|
242
|
+
class CallExpressionNode {
|
|
243
|
+
/**
|
|
244
|
+
* @param {number} start start offset
|
|
245
|
+
* @param {number} end end offset
|
|
246
|
+
* @param {Expression} callee callee expression
|
|
247
|
+
* @param {Node[]} args call arguments
|
|
248
|
+
* @param {boolean} optional whether the call is optional (`a?.()`)
|
|
249
|
+
*/
|
|
250
|
+
constructor(start, end, callee, args, optional) {
|
|
251
|
+
this.type = "CallExpression";
|
|
252
|
+
this.start = start;
|
|
253
|
+
this.end = end;
|
|
254
|
+
this.callee = callee;
|
|
255
|
+
this.arguments = args;
|
|
256
|
+
this.optional = optional;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Single-shape `ThisExpression`.
|
|
262
|
+
*/
|
|
263
|
+
class ThisNode {
|
|
264
|
+
/**
|
|
265
|
+
* @param {number} start start offset
|
|
266
|
+
* @param {number} end end offset
|
|
267
|
+
*/
|
|
268
|
+
constructor(start, end) {
|
|
269
|
+
this.type = "ThisExpression";
|
|
270
|
+
this.start = start;
|
|
271
|
+
this.end = end;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Single-shape `BinaryExpression`/`LogicalExpression` — identical field sets,
|
|
277
|
+
* so both node types share one hidden class.
|
|
278
|
+
*/
|
|
279
|
+
class BinaryNode {
|
|
280
|
+
/**
|
|
281
|
+
* @param {number} start start offset
|
|
282
|
+
* @param {number} end end offset
|
|
283
|
+
* @param {"BinaryExpression" | "LogicalExpression"} type node type
|
|
284
|
+
* @param {Expression} left left operand
|
|
285
|
+
* @param {string} operator operator text
|
|
286
|
+
* @param {Expression} right right operand
|
|
287
|
+
*/
|
|
288
|
+
constructor(start, end, type, left, operator, right) {
|
|
289
|
+
this.type = type;
|
|
290
|
+
this.start = start;
|
|
291
|
+
this.end = end;
|
|
292
|
+
this.left = left;
|
|
293
|
+
this.operator = operator;
|
|
294
|
+
this.right = right;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Single-shape `AssignmentExpression`.
|
|
300
|
+
*/
|
|
301
|
+
class AssignmentNode {
|
|
302
|
+
/**
|
|
303
|
+
* @param {number} start start offset
|
|
304
|
+
* @param {number} end end offset
|
|
305
|
+
* @param {string} operator assignment operator text
|
|
306
|
+
* @param {Node} left assignment target
|
|
307
|
+
* @param {Expression} right assigned value
|
|
308
|
+
*/
|
|
309
|
+
constructor(start, end, operator, left, right) {
|
|
310
|
+
this.type = "AssignmentExpression";
|
|
311
|
+
this.start = start;
|
|
312
|
+
this.end = end;
|
|
313
|
+
this.operator = operator;
|
|
314
|
+
this.left = left;
|
|
315
|
+
this.right = right;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Single-shape `UnaryExpression`/`UpdateExpression` — identical field sets,
|
|
321
|
+
* so both node types share one hidden class.
|
|
322
|
+
*/
|
|
323
|
+
class UnaryNode {
|
|
324
|
+
/**
|
|
325
|
+
* @param {number} start start offset
|
|
326
|
+
* @param {number} end end offset
|
|
327
|
+
* @param {"UnaryExpression" | "UpdateExpression"} type node type
|
|
328
|
+
* @param {string} operator operator text
|
|
329
|
+
* @param {boolean} prefix whether the operator is prefixed
|
|
330
|
+
* @param {Expression} argument operand
|
|
331
|
+
*/
|
|
332
|
+
constructor(start, end, type, operator, prefix, argument) {
|
|
333
|
+
this.type = type;
|
|
334
|
+
this.start = start;
|
|
335
|
+
this.end = end;
|
|
336
|
+
this.operator = operator;
|
|
337
|
+
this.prefix = prefix;
|
|
338
|
+
this.argument = argument;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Single-shape `VariableDeclaration` (statement position; `for` heads keep the
|
|
344
|
+
* generic node since their caller finishes them).
|
|
345
|
+
*/
|
|
346
|
+
class VariableDeclarationNode {
|
|
347
|
+
/**
|
|
348
|
+
* @param {number} start start offset
|
|
349
|
+
* @param {number} end end offset
|
|
350
|
+
* @param {Node[]} declarations declarators
|
|
351
|
+
* @param {string} kind declaration kind (`var`/`let`/`const`/`using`)
|
|
352
|
+
*/
|
|
353
|
+
constructor(start, end, declarations, kind) {
|
|
354
|
+
this.type = "VariableDeclaration";
|
|
355
|
+
this.start = start;
|
|
356
|
+
this.end = end;
|
|
357
|
+
this.declarations = declarations;
|
|
358
|
+
this.kind = kind;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Single-shape `VariableDeclarator`.
|
|
364
|
+
*/
|
|
365
|
+
class VariableDeclaratorNode {
|
|
366
|
+
/**
|
|
367
|
+
* @param {number} start start offset
|
|
368
|
+
* @param {number} end end offset
|
|
369
|
+
* @param {Node} id binding target
|
|
370
|
+
* @param {Expression | null} init initializer
|
|
371
|
+
*/
|
|
372
|
+
constructor(start, end, id, init) {
|
|
373
|
+
this.type = "VariableDeclarator";
|
|
374
|
+
this.start = start;
|
|
375
|
+
this.end = end;
|
|
376
|
+
this.id = id;
|
|
377
|
+
this.init = init;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Single-shape `ExpressionStatement`.
|
|
383
|
+
*/
|
|
384
|
+
class ExpressionStatementNode {
|
|
385
|
+
/**
|
|
386
|
+
* @param {number} start start offset
|
|
387
|
+
* @param {number} end end offset
|
|
388
|
+
* @param {Expression} expression the statement's expression
|
|
389
|
+
*/
|
|
390
|
+
constructor(start, end, expression) {
|
|
391
|
+
this.type = "ExpressionStatement";
|
|
392
|
+
this.start = start;
|
|
393
|
+
this.end = end;
|
|
394
|
+
this.expression = expression;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Single-shape `BlockStatement`.
|
|
400
|
+
*/
|
|
401
|
+
class BlockStatementNode {
|
|
402
|
+
/**
|
|
403
|
+
* @param {number} start start offset
|
|
404
|
+
* @param {number} end end offset
|
|
405
|
+
* @param {Node[]} body statements
|
|
406
|
+
*/
|
|
407
|
+
constructor(start, end, body) {
|
|
408
|
+
this.type = "BlockStatement";
|
|
409
|
+
this.start = start;
|
|
410
|
+
this.end = end;
|
|
411
|
+
this.body = body;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Single-shape `IfStatement`.
|
|
417
|
+
*/
|
|
418
|
+
class IfStatementNode {
|
|
419
|
+
/**
|
|
420
|
+
* @param {number} start start offset
|
|
421
|
+
* @param {number} end end offset
|
|
422
|
+
* @param {Expression} test condition
|
|
423
|
+
* @param {Node} consequent then-branch
|
|
424
|
+
* @param {Node | null} alternate else-branch
|
|
425
|
+
*/
|
|
426
|
+
constructor(start, end, test, consequent, alternate) {
|
|
427
|
+
this.type = "IfStatement";
|
|
428
|
+
this.start = start;
|
|
429
|
+
this.end = end;
|
|
430
|
+
this.test = test;
|
|
431
|
+
this.consequent = consequent;
|
|
432
|
+
this.alternate = alternate;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Single-shape `ReturnStatement`.
|
|
438
|
+
*/
|
|
439
|
+
class ReturnStatementNode {
|
|
440
|
+
/**
|
|
441
|
+
* @param {number} start start offset
|
|
442
|
+
* @param {number} end end offset
|
|
443
|
+
* @param {Expression | null} argument returned expression
|
|
444
|
+
*/
|
|
445
|
+
constructor(start, end, argument) {
|
|
446
|
+
this.type = "ReturnStatement";
|
|
447
|
+
this.start = start;
|
|
448
|
+
this.end = end;
|
|
449
|
+
this.argument = argument;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Single-shape `ConditionalExpression`.
|
|
455
|
+
*/
|
|
456
|
+
class ConditionalExpressionNode {
|
|
457
|
+
/**
|
|
458
|
+
* @param {number} start start offset
|
|
459
|
+
* @param {number} end end offset
|
|
460
|
+
* @param {Expression} test condition
|
|
461
|
+
* @param {Expression} consequent then-value
|
|
462
|
+
* @param {Expression} alternate else-value
|
|
463
|
+
*/
|
|
464
|
+
constructor(start, end, test, consequent, alternate) {
|
|
465
|
+
this.type = "ConditionalExpression";
|
|
466
|
+
this.start = start;
|
|
467
|
+
this.end = end;
|
|
468
|
+
this.test = test;
|
|
469
|
+
this.consequent = consequent;
|
|
470
|
+
this.alternate = alternate;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Single-shape `NewExpression`.
|
|
476
|
+
*/
|
|
477
|
+
class NewExpressionNode {
|
|
478
|
+
/**
|
|
479
|
+
* @param {number} start start offset
|
|
480
|
+
* @param {number} end end offset
|
|
481
|
+
* @param {Expression} callee constructed expression
|
|
482
|
+
* @param {Expression[]} args constructor arguments
|
|
483
|
+
*/
|
|
484
|
+
constructor(start, end, callee, args) {
|
|
485
|
+
this.type = "NewExpression";
|
|
486
|
+
this.start = start;
|
|
487
|
+
this.end = end;
|
|
488
|
+
this.callee = callee;
|
|
489
|
+
this.arguments = args;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Single-shape `ArrayExpression`.
|
|
495
|
+
*/
|
|
496
|
+
class ArrayExpressionNode {
|
|
497
|
+
/**
|
|
498
|
+
* @param {number} start start offset
|
|
499
|
+
* @param {number} end end offset
|
|
500
|
+
* @param {(Expression | null)[]} elements array elements (`null` for holes)
|
|
501
|
+
*/
|
|
502
|
+
constructor(start, end, elements) {
|
|
503
|
+
this.type = "ArrayExpression";
|
|
504
|
+
this.start = start;
|
|
505
|
+
this.end = end;
|
|
506
|
+
this.elements = elements;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Single-shape `TemplateLiteral`.
|
|
512
|
+
*/
|
|
513
|
+
class TemplateLiteralNode {
|
|
514
|
+
/**
|
|
515
|
+
* @param {number} start start offset
|
|
516
|
+
* @param {number} end end offset
|
|
517
|
+
* @param {Expression[]} expressions substitution expressions
|
|
518
|
+
* @param {Node[]} quasis template chunks
|
|
519
|
+
*/
|
|
520
|
+
constructor(start, end, expressions, quasis) {
|
|
521
|
+
this.type = "TemplateLiteral";
|
|
522
|
+
this.start = start;
|
|
523
|
+
this.end = end;
|
|
524
|
+
this.expressions = expressions;
|
|
525
|
+
this.quasis = quasis;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Single-shape `TemplateElement`.
|
|
531
|
+
*/
|
|
532
|
+
class TemplateElementNode {
|
|
533
|
+
/**
|
|
534
|
+
* @param {number} start start offset
|
|
535
|
+
* @param {number} end end offset
|
|
536
|
+
* @param {{ raw: string, cooked: string | null }} value chunk text
|
|
537
|
+
* @param {boolean} tail whether this is the closing chunk
|
|
538
|
+
*/
|
|
539
|
+
constructor(start, end, value, tail) {
|
|
540
|
+
this.type = "TemplateElement";
|
|
541
|
+
this.start = start;
|
|
542
|
+
this.end = end;
|
|
543
|
+
this.value = value;
|
|
544
|
+
this.tail = tail;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Single-shape `ObjectExpression`/`ObjectPattern` — identical field sets, so
|
|
550
|
+
* both node types share one hidden class.
|
|
551
|
+
*/
|
|
552
|
+
class ObjectNode {
|
|
553
|
+
/**
|
|
554
|
+
* @param {number} start start offset
|
|
555
|
+
* @param {number} end end offset
|
|
556
|
+
* @param {"ObjectExpression" | "ObjectPattern"} type node type
|
|
557
|
+
* @param {Node[]} properties properties
|
|
558
|
+
*/
|
|
559
|
+
constructor(start, end, type, properties) {
|
|
560
|
+
this.type = type;
|
|
561
|
+
this.start = start;
|
|
562
|
+
this.end = end;
|
|
563
|
+
this.properties = properties;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Pre-shaped `Property`: acorn fills property nodes through shared
|
|
569
|
+
* subroutines (`parsePropertyName`/`parsePropertyValue`), so instead of
|
|
570
|
+
* rebuilding that flow the fields are all declared up-front and acorn's
|
|
571
|
+
* writes land in existing slots — one hidden class, no transitions (yuku's
|
|
572
|
+
* decoder emits `Property` with this fixed shape). Every non-throwing acorn
|
|
573
|
+
* branch assigns `computed`, `key`, `value` and `kind`; `finishNode` sets
|
|
574
|
+
* `type` and `end`.
|
|
575
|
+
*/
|
|
576
|
+
class PropertyNode {
|
|
577
|
+
/**
|
|
578
|
+
* @param {number} start start offset
|
|
579
|
+
*/
|
|
580
|
+
constructor(start) {
|
|
581
|
+
this.type = "";
|
|
582
|
+
this.start = start;
|
|
583
|
+
this.end = 0;
|
|
584
|
+
this.method = false;
|
|
585
|
+
this.shorthand = false;
|
|
586
|
+
this.computed = false;
|
|
587
|
+
/** @type {Node | null} */
|
|
588
|
+
this.key = null;
|
|
589
|
+
/** @type {Node | null} */
|
|
590
|
+
this.value = null;
|
|
591
|
+
this.kind = "";
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Single-shape `SpreadElement`/`RestElement` — identical field sets, so both
|
|
597
|
+
* node types share one hidden class.
|
|
598
|
+
*/
|
|
599
|
+
class RestSpreadNode {
|
|
600
|
+
/**
|
|
601
|
+
* @param {number} start start offset
|
|
602
|
+
* @param {number} end end offset
|
|
603
|
+
* @param {"SpreadElement" | "RestElement"} type node type
|
|
604
|
+
* @param {Node} argument spread/rest argument
|
|
605
|
+
*/
|
|
606
|
+
constructor(start, end, type, argument) {
|
|
607
|
+
this.type = type;
|
|
608
|
+
this.start = start;
|
|
609
|
+
this.end = end;
|
|
610
|
+
this.argument = argument;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Shared zero-length arguments array for `new X` without parens, mirroring
|
|
615
|
+
// acorn's module-level `empty`.
|
|
616
|
+
/** @type {Expression[]} */
|
|
617
|
+
const EMPTY_NEW_ARGS = [];
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Mirror of acorn's module-level `isLocalVariableAccess`.
|
|
621
|
+
* @param {Node} node checked node
|
|
622
|
+
* @returns {boolean} whether the node reads a local variable
|
|
623
|
+
*/
|
|
624
|
+
const isLocalVariableAccess = (node) =>
|
|
625
|
+
node.type === "Identifier" ||
|
|
626
|
+
(node.type === "ParenthesizedExpression" &&
|
|
627
|
+
isLocalVariableAccess(
|
|
628
|
+
/** @type {Node} */ (
|
|
629
|
+
/** @type {Node & { expression?: Node }} */ (node).expression
|
|
630
|
+
)
|
|
631
|
+
));
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Mirror of acorn's module-level `isPrivateFieldAccess`.
|
|
635
|
+
* @param {Node} node checked node
|
|
636
|
+
* @returns {boolean} whether the node accesses a private field
|
|
637
|
+
*/
|
|
638
|
+
const isPrivateFieldAccess = (node) =>
|
|
639
|
+
(node.type === "MemberExpression" &&
|
|
640
|
+
/** @type {Node} */ (
|
|
641
|
+
/** @type {Node & { property?: Node }} */ (node).property
|
|
642
|
+
).type === "PrivateIdentifier") ||
|
|
643
|
+
(node.type === "ChainExpression" &&
|
|
644
|
+
isPrivateFieldAccess(
|
|
645
|
+
/** @type {Node} */ (
|
|
646
|
+
/** @type {Node & { expression?: Node }} */ (node).expression
|
|
647
|
+
)
|
|
648
|
+
)) ||
|
|
649
|
+
(node.type === "ParenthesizedExpression" &&
|
|
650
|
+
isPrivateFieldAccess(
|
|
651
|
+
/** @type {Node} */ (
|
|
652
|
+
/** @type {Node & { expression?: Node }} */ (node).expression
|
|
653
|
+
)
|
|
654
|
+
));
|
|
655
|
+
|
|
656
|
+
// the dedicated node classes serve `range` exactly like LazyLocNode
|
|
657
|
+
for (const NodeClass of [
|
|
658
|
+
IdentifierNode,
|
|
659
|
+
LiteralNode,
|
|
660
|
+
MemberExpressionNode,
|
|
661
|
+
CallExpressionNode,
|
|
662
|
+
ThisNode,
|
|
663
|
+
BinaryNode,
|
|
664
|
+
AssignmentNode,
|
|
665
|
+
UnaryNode,
|
|
666
|
+
VariableDeclarationNode,
|
|
667
|
+
VariableDeclaratorNode,
|
|
668
|
+
ExpressionStatementNode,
|
|
669
|
+
BlockStatementNode,
|
|
670
|
+
IfStatementNode,
|
|
671
|
+
ReturnStatementNode,
|
|
672
|
+
ConditionalExpressionNode,
|
|
673
|
+
NewExpressionNode,
|
|
674
|
+
ArrayExpressionNode,
|
|
675
|
+
TemplateLiteralNode,
|
|
676
|
+
TemplateElementNode,
|
|
677
|
+
ObjectNode,
|
|
678
|
+
PropertyNode,
|
|
679
|
+
RestSpreadNode
|
|
680
|
+
]) {
|
|
681
|
+
for (const key of ["range"]) {
|
|
682
|
+
Object.defineProperty(
|
|
683
|
+
NodeClass.prototype,
|
|
684
|
+
key,
|
|
685
|
+
/** @type {PropertyDescriptor} */
|
|
686
|
+
(Object.getOwnPropertyDescriptor(LazyLocNode.prototype, key))
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Comment collected without slicing its text out of the source: only magic
|
|
693
|
+
* comments and pure annotations ever get their text read, so the slice is
|
|
694
|
+
* deferred to the first `value` access and memoized like `loc`.
|
|
695
|
+
*/
|
|
696
|
+
class LazyComment {
|
|
697
|
+
/**
|
|
698
|
+
* @param {boolean} block whether this is a block comment
|
|
699
|
+
* @param {number} textStart offset right after the comment opener
|
|
700
|
+
* @param {number} start start offset
|
|
701
|
+
* @param {number} end end offset
|
|
702
|
+
* @param {string} source full source text for the lazy `value` slice
|
|
703
|
+
*/
|
|
704
|
+
constructor(block, textStart, start, end, source) {
|
|
705
|
+
/** @type {"Block" | "Line"} */
|
|
706
|
+
this.type = block ? "Block" : "Line";
|
|
707
|
+
this.start = start;
|
|
708
|
+
this.end = end;
|
|
709
|
+
/** @type {Range} */
|
|
710
|
+
this.range = [start, end];
|
|
711
|
+
this[kSource] = source;
|
|
712
|
+
this[kTextStart] = textStart;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* @returns {string} comment text without the delimiters
|
|
717
|
+
*/
|
|
718
|
+
get value() {
|
|
719
|
+
const cached = this[kText];
|
|
720
|
+
if (cached !== undefined) return cached;
|
|
721
|
+
return (this[kText] = this[kSource].slice(
|
|
722
|
+
this[kTextStart],
|
|
723
|
+
this.type === "Block" ? this.end - 2 : this.end
|
|
724
|
+
));
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* @param {string} value comment text
|
|
729
|
+
*/
|
|
730
|
+
set value(value) {
|
|
731
|
+
this[kText] = value;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Replaces acorn's array-backed `Scope`: membership checks in `declareName`
|
|
737
|
+
* are `indexOf` there, which goes quadratic on files with thousands of
|
|
738
|
+
* bindings per scope (bundled or minified inputs). The three Sets are
|
|
739
|
+
* allocated lazily — most scopes declare into only one (module `functions` is
|
|
740
|
+
* always empty), so ~⅔ of the Sets are never needed.
|
|
741
|
+
*/
|
|
742
|
+
class Scope {
|
|
743
|
+
/**
|
|
744
|
+
* @param {number} flags scope flags
|
|
745
|
+
*/
|
|
746
|
+
constructor(flags) {
|
|
747
|
+
this.flags = flags;
|
|
748
|
+
/** @type {Set<string> | undefined} */
|
|
749
|
+
this.var = undefined;
|
|
750
|
+
/** @type {Set<string> | undefined} */
|
|
751
|
+
this.lexical = undefined;
|
|
752
|
+
/** @type {Set<string> | undefined} */
|
|
753
|
+
this.functions = undefined;
|
|
754
|
+
// first lexically-declared name; stands in for acorn's `lexical[0]`
|
|
755
|
+
// (the catch parameter of a simple catch scope)
|
|
756
|
+
/** @type {string | undefined} */
|
|
757
|
+
this.firstLexical = undefined;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Acorn's methods and state used by `WebpackParser` but missing from its
|
|
763
|
+
* public types, plus `WebpackParser`'s own fields, so overridden methods can
|
|
764
|
+
* declare `this` precisely.
|
|
765
|
+
* @typedef {import("acorn").Parser & {
|
|
766
|
+
* type: TokenType,
|
|
767
|
+
* value: unknown,
|
|
768
|
+
* start: number,
|
|
769
|
+
* startLoc?: Position,
|
|
770
|
+
* containsEsc: boolean,
|
|
771
|
+
* exprAllowed: boolean,
|
|
772
|
+
* options: Options,
|
|
773
|
+
* end: number,
|
|
774
|
+
* lastTokEnd: number,
|
|
775
|
+
* canInsertSemicolon: () => boolean,
|
|
776
|
+
* nextToken: () => void,
|
|
777
|
+
* next: (ignoreEscapeSequenceInKeyword?: boolean) => void,
|
|
778
|
+
* eat: (type: TokenType) => boolean,
|
|
779
|
+
* expect: (type: TokenType) => void,
|
|
780
|
+
* afterTrailingComma: (type: TokenType, notNext?: boolean) => boolean,
|
|
781
|
+
* unexpected: (pos?: number) => never,
|
|
782
|
+
* raise: (pos: number, message: string) => never,
|
|
783
|
+
* raiseRecoverable: (pos: number, message: string) => void,
|
|
784
|
+
* isContextual: (name: string) => boolean,
|
|
785
|
+
* parseIdent: (liberal?: boolean) => Identifier,
|
|
786
|
+
* parseLiteral: (value: unknown) => Node,
|
|
787
|
+
* awaitIdentPos: number,
|
|
788
|
+
* lastTokStart: number,
|
|
789
|
+
* yieldPos: number,
|
|
790
|
+
* awaitPos: number,
|
|
791
|
+
* parseExpression: () => Expression,
|
|
792
|
+
* parseSpread: (refDestructuringErrors?: DestructuringErrorsShim | null) => Node,
|
|
793
|
+
* braceIsBlock: (prevType: TokenType) => boolean,
|
|
794
|
+
* _gapHasNewline: () => boolean,
|
|
795
|
+
* parseExprList: (close: TokenType, allowTrailingComma: boolean, allowEmpty: boolean, refDestructuringErrors?: DestructuringErrorsShim | null) => Expression[],
|
|
796
|
+
* parsePrivateIdent: () => Node,
|
|
797
|
+
* parseTemplate: (opts: { isTagged: boolean }) => Node,
|
|
798
|
+
* shouldParseAsyncArrow: () => boolean,
|
|
799
|
+
* parseSubscriptAsyncArrow: (startPos: number, startLoc: Position | undefined, exprList: Expression[], forInit: boolean | string) => Expression,
|
|
800
|
+
* checkPatternErrors: (refDestructuringErrors: DestructuringErrorsShim, isAssign: boolean) => void,
|
|
801
|
+
* checkYieldAwaitInDefaultParams: () => void,
|
|
802
|
+
* checkExpressionErrors: (refDestructuringErrors?: DestructuringErrorsShim | null, andThrow?: boolean) => boolean,
|
|
803
|
+
* parseSubscript: (base: Expression, startPos: number, startLoc: Position | undefined, noCalls: boolean | undefined, maybeAsyncArrow: boolean, optionalChained: boolean, forInit: boolean | string) => Expression,
|
|
804
|
+
* parseExprAtom: (refDestructuringErrors?: DestructuringErrorsShim | null, forInit?: boolean | string, forNew?: boolean) => Expression,
|
|
805
|
+
* buildBinary: (startPos: number, startLoc: Position | undefined, left: Expression, right: Expression, op: string, logical: boolean) => Expression,
|
|
806
|
+
* parseMaybeAssign: (forInit?: boolean | string, refDestructuringErrors?: DestructuringErrorsShim | null, afterLeftParse?: (this: unknown, left: Expression, startPos: number, startLoc?: Position) => Expression) => Expression,
|
|
807
|
+
* parseMaybeConditional: (forInit?: boolean | string, refDestructuringErrors?: DestructuringErrorsShim | null) => Expression,
|
|
808
|
+
* parseMaybeUnary: (refDestructuringErrors: DestructuringErrorsShim | null, sawUnary: boolean, incDec: boolean, forInit?: boolean | string) => Expression,
|
|
809
|
+
* parseExprSubscripts: (refDestructuringErrors?: DestructuringErrorsShim | null, forInit?: boolean | string) => Expression,
|
|
810
|
+
* parseAwait: (forInit?: boolean | string) => Expression,
|
|
811
|
+
* canAwait: boolean,
|
|
812
|
+
* privateNameStack: unknown[],
|
|
813
|
+
* semicolon: () => void,
|
|
814
|
+
* exitScope: () => void,
|
|
815
|
+
* parseStatement: (context: string | null, topLevel?: boolean, exports?: unknown) => Node,
|
|
816
|
+
* parseBindingAtom: () => Node,
|
|
817
|
+
* parseVarStatement: (node: Node, kind: string, allowMissingInitializer?: boolean) => Node,
|
|
818
|
+
* parseVar: (node: Node, isFor: boolean, kind: string, allowMissingInitializer?: boolean) => Node,
|
|
819
|
+
* parseExpressionStatement: (node: Node, expr: Expression) => Node,
|
|
820
|
+
* parseParenExpression: () => Expression,
|
|
821
|
+
* parseIfStatement: (node: Node) => Node,
|
|
822
|
+
* parseReturnStatement: (node: Node) => Node,
|
|
823
|
+
* insertSemicolon: () => boolean,
|
|
824
|
+
* allowReturn: boolean,
|
|
825
|
+
* allowNewDotTarget: boolean,
|
|
826
|
+
* parseExprOps: (forInit?: boolean | string, refDestructuringErrors?: DestructuringErrorsShim | null) => Expression,
|
|
827
|
+
* parseExprOp: (left: Expression, leftStartPos: number, leftStartLoc: Position | undefined, minPrec: number, forInit?: boolean | string) => Expression,
|
|
828
|
+
* _deStack: DestructuringErrorsShim[],
|
|
829
|
+
* _deDepth: number,
|
|
830
|
+
* _ecmaVersion: number,
|
|
831
|
+
* _noLocations: boolean,
|
|
832
|
+
* _validRegexpFlags: string,
|
|
833
|
+
* _propHashFastPath: boolean,
|
|
834
|
+
* _propHashStack: { proto: boolean }[],
|
|
835
|
+
* _propHashDepth: number,
|
|
836
|
+
* _acquireDestructuringErrors: () => DestructuringErrorsShim,
|
|
837
|
+
* _releaseDestructuringErrors: () => void,
|
|
838
|
+
* parseSubscripts: (base: Expression, startPos: number, startLoc: Position | undefined, noCalls?: boolean, forInit?: boolean | string) => Expression,
|
|
839
|
+
* parseNew: () => Expression,
|
|
840
|
+
* parseTemplateElement: (opts: { isTagged: boolean }) => Node,
|
|
841
|
+
* parseBlock: (createNewLexicalScope?: boolean, node?: Node, exitStrict?: boolean) => Node,
|
|
842
|
+
* parseYield: (forInit?: boolean | string) => Expression,
|
|
843
|
+
* toAssignable: (node: Node, isBinding?: boolean, refDestructuringErrors?: DestructuringErrorsShim | null) => Node,
|
|
844
|
+
* checkLValPattern: (expr: Node, bindingType?: number, checkClashes?: unknown) => void,
|
|
845
|
+
* checkUnreserved: (ref: Identifier) => void,
|
|
846
|
+
* enterScope: (flags: number) => void,
|
|
847
|
+
* readRegexp: () => void,
|
|
848
|
+
* potentialArrowAt: number,
|
|
849
|
+
* potentialArrowInForAwait: boolean,
|
|
850
|
+
* overrideContext: (tokenCtx: unknown) => void,
|
|
851
|
+
* parseFunction: (node: Node, statement: number, allowExpressionBody?: boolean, isAsync?: boolean, forInit?: boolean | string) => Expression,
|
|
852
|
+
* parseArrowExpression: (node: Node, params: Node[], isAsync: boolean, forInit?: boolean | string) => Expression,
|
|
853
|
+
* _subscriptFastPath: boolean,
|
|
854
|
+
* checkLValSimple: (expr: Node, bindingType?: number) => void,
|
|
855
|
+
* startNode: () => Node,
|
|
856
|
+
* startNodeAt: (pos: number, loc?: Position) => Node,
|
|
857
|
+
* finishNode: (node: Node, type: string) => Node,
|
|
858
|
+
* readWord1: () => string,
|
|
859
|
+
* readWord: () => void,
|
|
860
|
+
* readToken: (code: number) => void,
|
|
861
|
+
* getTokenFromCode: (code: number) => void,
|
|
862
|
+
* fullCharCodeAtPos: () => number,
|
|
863
|
+
* skipSpace: () => void,
|
|
864
|
+
* skipLineComment: (startSkip: number) => void,
|
|
865
|
+
* skipBlockComment: () => void,
|
|
866
|
+
* readString: (quote: number) => void,
|
|
867
|
+
* readNumber: (startsWithDot: boolean) => void,
|
|
868
|
+
* readRadixNumber: (radix: number) => void,
|
|
869
|
+
* readTmplToken: () => void,
|
|
870
|
+
* finishToken: (type: TokenType, value?: unknown) => void,
|
|
871
|
+
* context: TokContextShim[],
|
|
872
|
+
* pos: number,
|
|
873
|
+
* input: string,
|
|
874
|
+
* scopeStack: Scope[],
|
|
875
|
+
* currentScope: () => Scope,
|
|
876
|
+
* currentThisScope: () => Scope,
|
|
877
|
+
* currentVarScope: () => Scope,
|
|
878
|
+
* keywords: RegExp,
|
|
879
|
+
* reservedWords: RegExp,
|
|
880
|
+
* reservedWordsStrict: RegExp,
|
|
881
|
+
* reservedWordsStrictBind: RegExp,
|
|
882
|
+
* strict: boolean,
|
|
883
|
+
* inGenerator: boolean,
|
|
884
|
+
* inGeneratorContext: () => boolean,
|
|
885
|
+
* inAsync: boolean,
|
|
886
|
+
* inClassStaticBlock: boolean,
|
|
887
|
+
* _wordLookups: WordLookups,
|
|
888
|
+
* treatFunctionsAsVar: boolean,
|
|
889
|
+
* treatFunctionsAsVarInScope: (scope: Scope) => boolean,
|
|
890
|
+
* inModule: boolean,
|
|
891
|
+
* undefinedExports: Record<string, Node>,
|
|
892
|
+
* parseObj: (isPattern: boolean, refDestructuringErrors?: DestructuringErrorsShim | null) => Node,
|
|
893
|
+
* parseProperty: (isPattern: boolean, refDestructuringErrors?: DestructuringErrorsShim | null) => Node,
|
|
894
|
+
* parsePropertyName: (prop: Node) => Node,
|
|
895
|
+
* parsePropertyValue: (prop: Node, isPattern: boolean, isGenerator: boolean, isAsync: boolean, startPos: number | undefined, startLoc: Position | undefined, refDestructuringErrors: DestructuringErrorsShim | null | undefined, containsEsc: boolean) => void,
|
|
896
|
+
* isAsyncProp: (prop: Node) => boolean,
|
|
897
|
+
* checkPropClash: (prop: Node, propHash: Record<string, unknown>, refDestructuringErrors?: DestructuringErrorsShim | null) => void,
|
|
898
|
+
* parseImport: (node: Node) => Node,
|
|
899
|
+
* parseExport: (node: Node, exports: unknown) => Node,
|
|
900
|
+
* parseImportSpecifiers: () => AnyImportSpecifier[],
|
|
901
|
+
* parseImportAttribute: () => ImportAttribute,
|
|
902
|
+
* parseExprImport: (forNew: boolean) => Expression,
|
|
903
|
+
* parseImportMeta: (node: Node) => Expression,
|
|
904
|
+
* parseDynamicImport: (node: Node) => Expression,
|
|
905
|
+
* _lazy: boolean,
|
|
906
|
+
* _importPhase: ImportPhase | null,
|
|
907
|
+
* _importPhasesEnabled: boolean,
|
|
908
|
+
* _lazyComments: CollectedComment[] | undefined,
|
|
909
|
+
* _newlineBefore: 0 | 1 | 2,
|
|
910
|
+
* _fullTokenFastPath: boolean,
|
|
911
|
+
* _stmtFastPath: boolean,
|
|
912
|
+
* isLet: (context?: string | null) => boolean,
|
|
913
|
+
* parseLabeledStatement: (node: Node, maybeName: string, expr: Identifier, context: string | null) => Node,
|
|
914
|
+
* _parseVarInto: (declarations: Node[], isFor: boolean, kind: string, allowMissingInitializer?: boolean) => void,
|
|
915
|
+
* _parseVarStatementAt: (start: number, kind: string, allowMissingInitializer?: boolean) => Node,
|
|
916
|
+
* _parseIfStatementAt: (start: number) => Node,
|
|
917
|
+
* _parseReturnStatementAt: (start: number) => Node,
|
|
918
|
+
* _parseExpressionStatementAt: (start: number, expr: Expression) => Node,
|
|
919
|
+
* _moduleFallback: boolean,
|
|
920
|
+
* _moduleSyntaxSeen: boolean,
|
|
921
|
+
* _tryModuleFallback: () => boolean,
|
|
922
|
+
* }} ParserInternals
|
|
923
|
+
*/
|
|
924
|
+
|
|
925
|
+
// internal methods are absent from acorn's types, so super calls do not
|
|
926
|
+
// type-check; call through a typed view of the base prototype instead
|
|
927
|
+
const base = /** @type {ParserInternals} */ (
|
|
928
|
+
/** @type {unknown} */ (BaseParser.prototype)
|
|
929
|
+
);
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* Acorn's internal destructuring-errors record; the class itself is not
|
|
933
|
+
* exported. Owned methods must create records with the same hidden class the
|
|
934
|
+
* rest of the expression parser reads, or every record field access there
|
|
935
|
+
* turns polymorphic.
|
|
936
|
+
* @typedef {{ shorthandAssign: number, trailingComma: number, parenthesizedAssign: number, parenthesizedBind: number, doubleProto: number }} DestructuringErrorsShim
|
|
937
|
+
*/
|
|
938
|
+
|
|
939
|
+
// Capture the class at module load: parse one expression through a probe
|
|
940
|
+
// whose `checkExpressionErrors` sees the record the base parser created.
|
|
941
|
+
/** @type {{ new (): DestructuringErrorsShim } | null} */
|
|
942
|
+
const DestructuringErrorsClass = (() => {
|
|
943
|
+
/** @type {{ new (): DestructuringErrorsShim } | null} */
|
|
944
|
+
let captured = null;
|
|
945
|
+
class Probe extends BaseParser {
|
|
946
|
+
/**
|
|
947
|
+
* @param {DestructuringErrorsShim | null} refDestructuringErrors record to inspect
|
|
948
|
+
* @param {boolean=} andThrow whether to throw on error
|
|
949
|
+
* @returns {boolean} whether an error position was set
|
|
950
|
+
*/
|
|
951
|
+
checkExpressionErrors(refDestructuringErrors, andThrow) {
|
|
952
|
+
if (refDestructuringErrors) {
|
|
953
|
+
captured =
|
|
954
|
+
/** @type {{ new (): DestructuringErrorsShim }} */
|
|
955
|
+
(refDestructuringErrors.constructor);
|
|
956
|
+
}
|
|
957
|
+
return /** @type {ParserInternals} */ (
|
|
958
|
+
/** @type {unknown} */ (base)
|
|
959
|
+
).checkExpressionErrors.call(this, refDestructuringErrors, andThrow);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
Probe.parse("a", { ecmaVersion: 2020 });
|
|
963
|
+
// cast: the closure assignment above is invisible to control-flow analysis
|
|
964
|
+
return /** @type {{ new (): DestructuringErrorsShim } | null} */ (captured);
|
|
965
|
+
})();
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* @returns {DestructuringErrorsShim} fresh destructuring-errors record on acorn's own class (plain-object fallback if the capture ever fails)
|
|
969
|
+
*/
|
|
970
|
+
const createDestructuringErrors = () => {
|
|
971
|
+
const DestructuringErrors = DestructuringErrorsClass;
|
|
972
|
+
return DestructuringErrors !== null
|
|
973
|
+
? new DestructuringErrors()
|
|
974
|
+
: {
|
|
975
|
+
shorthandAssign: -1,
|
|
976
|
+
trailingComma: -1,
|
|
977
|
+
parenthesizedAssign: -1,
|
|
978
|
+
parenthesizedBind: -1,
|
|
979
|
+
doubleProto: -1
|
|
980
|
+
};
|
|
981
|
+
};
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Reserved-word classification for `checkUnreserved`'s single lookup:
|
|
985
|
+
* `1` keyword, `2` reserved in sloppy and strict mode, `3` reserved in strict
|
|
986
|
+
* mode only.
|
|
987
|
+
* @typedef {1 | 2 | 3} ReservedKind
|
|
988
|
+
*/
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* @typedef {object} WordLookups
|
|
992
|
+
* @property {Map<string, TokenType>} keywords keyword name → token type
|
|
993
|
+
* @property {Map<string, ReservedKind>} reservedKinds identifier name → reserved kind
|
|
994
|
+
* @property {number} reservedMaxLen longest key in `reservedKinds`
|
|
995
|
+
* @property {{ test: (name: string) => boolean }} reservedBindTest strict-mode binding check, a Set-backed stand-in for acorn's `reservedWordsStrictBind` regexp
|
|
996
|
+
*/
|
|
997
|
+
|
|
998
|
+
// One entry per distinct keyword/reserved-word set; webpack parses with a
|
|
999
|
+
// single option set, making this effectively a one-time build shared across
|
|
1000
|
+
// every parse.
|
|
1001
|
+
/** @type {Map<string, WordLookups>} */
|
|
1002
|
+
const wordLookupsCache = new Map();
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* @param {string} word interned candidate
|
|
1006
|
+
* @param {string} input source code
|
|
1007
|
+
* @param {number} start word start offset
|
|
1008
|
+
* @param {number} len word length (already known equal to `word.length`)
|
|
1009
|
+
* @returns {boolean} whether the source span spells `word`
|
|
1010
|
+
*/
|
|
1011
|
+
const sameWord = (word, input, start, len) => {
|
|
1012
|
+
for (let i = 0; i < len; i++) {
|
|
1013
|
+
if (word.charCodeAt(i) !== input.charCodeAt(start + i)) return false;
|
|
1014
|
+
}
|
|
1015
|
+
return true;
|
|
1016
|
+
};
|
|
1017
|
+
|
|
1018
|
+
// Direct-mapped identifier cache for `readWord1`: one slot per hash, verified
|
|
1019
|
+
// by char compare, overwritten on collision. Shared across parses — hits are
|
|
1020
|
+
// content-checked, so a stale entry is merely a miss. Only words short enough
|
|
1021
|
+
// to be flat V8 strings (never slices retaining their whole source) are stored.
|
|
1022
|
+
const WORD_CACHE_MASK = 0x1fff;
|
|
1023
|
+
/** @type {(string | null)[]} */
|
|
1024
|
+
const WORD_CACHE = Array.from({ length: WORD_CACHE_MASK + 1 }, () => null);
|
|
1025
|
+
const WORD_CACHE_MAX_LEN = 12;
|
|
1026
|
+
|
|
1027
|
+
// Multi-char operator strings for `finishOp` (`=>`, `===`, `&&=`, …), keyed by
|
|
1028
|
+
// their char codes packed 7 bits apart — collision-free for ASCII operators up
|
|
1029
|
+
// to acorn's maximum of 4 chars (`>>>=`), so the set stays ~40 entries.
|
|
1030
|
+
/** @type {Map<number, string>} */
|
|
1031
|
+
const OP_CACHE = new Map();
|
|
1032
|
+
|
|
1033
|
+
// Sticky mirrors of acorn's `skipWhiteSpace` / string-literal / `lineBreak`
|
|
1034
|
+
// regexes for the owned `strictDirective` (they scan at an offset, no slice).
|
|
1035
|
+
const STRICT_SKIP_WS = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g;
|
|
1036
|
+
const STRICT_LITERAL = /(?:'((?:\\[^]|[^'\\])*?)'|"((?:\\[^]|[^"\\])*?)")/y;
|
|
1037
|
+
const STRICT_LINE_BREAK = /\r\n?|\n|\u2028|\u2029/;
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* @param {RegExp} re acorn `wordsRegexp` output (`^(?:a|b|c)$`)
|
|
1041
|
+
* @returns {Set<string>} the alternation's words
|
|
1042
|
+
*/
|
|
1043
|
+
const wordsRegexpToSet = (re) => {
|
|
1044
|
+
const match = /^\^\(\?:(.*)\)\$$/.exec(re.source);
|
|
1045
|
+
const body = match ? match[1] : "";
|
|
1046
|
+
return new Set(body ? body.split("|") : []);
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
// One-entry identity memo in front of the string-keyed cache: acorn's
|
|
1050
|
+
// `wordsRegexp` interns its regexps, so identity captures the whole word set,
|
|
1051
|
+
// and builds construct thousands of parsers with one option set — this makes
|
|
1052
|
+
// the per-construction lookup three compares instead of a long key concat.
|
|
1053
|
+
/** @type {RegExp | undefined} */
|
|
1054
|
+
let lastKeywordsRe;
|
|
1055
|
+
/** @type {RegExp | undefined} */
|
|
1056
|
+
let lastReservedRe;
|
|
1057
|
+
/** @type {RegExp | undefined} */
|
|
1058
|
+
let lastReservedStrictRe;
|
|
1059
|
+
/** @type {WordLookups | undefined} */
|
|
1060
|
+
let lastWordLookups;
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* Mirrors acorn's `keywords` / `reservedWords` / `reservedWordsStrict` regexps
|
|
1064
|
+
* as Map/Set lookups. Membership is the hot per-word test in `readWord` and
|
|
1065
|
+
* `checkUnreserved`, and a hash lookup beats an anchored alternation regexp.
|
|
1066
|
+
* @param {ParserInternals} parser parser instance
|
|
1067
|
+
* @returns {WordLookups} lookups for this parser's keyword set
|
|
1068
|
+
*/
|
|
1069
|
+
const getWordLookups = (parser) => {
|
|
1070
|
+
if (
|
|
1071
|
+
parser.keywords === lastKeywordsRe &&
|
|
1072
|
+
parser.reservedWords === lastReservedRe &&
|
|
1073
|
+
parser.reservedWordsStrict === lastReservedStrictRe
|
|
1074
|
+
) {
|
|
1075
|
+
return /** @type {WordLookups} */ (lastWordLookups);
|
|
1076
|
+
}
|
|
1077
|
+
// module vs script share a keyword set but differ in reserved words, so the
|
|
1078
|
+
// key must cover all three regexps
|
|
1079
|
+
const key = `${parser.keywords.source}\n${parser.reservedWords.source}\n${parser.reservedWordsStrict.source}`;
|
|
1080
|
+
lastKeywordsRe = parser.keywords;
|
|
1081
|
+
lastReservedRe = parser.reservedWords;
|
|
1082
|
+
lastReservedStrictRe = parser.reservedWordsStrict;
|
|
1083
|
+
const cached = wordLookupsCache.get(key);
|
|
1084
|
+
if (cached !== undefined) {
|
|
1085
|
+
lastWordLookups = cached;
|
|
1086
|
+
return cached;
|
|
1087
|
+
}
|
|
1088
|
+
/** @type {Map<string, TokenType>} */
|
|
1089
|
+
const keywords = new Map();
|
|
1090
|
+
// acorn's keyword regexp is a subset of keywordTypes for the ecmaVersion
|
|
1091
|
+
for (const name of Object.keys(keywordTypes)) {
|
|
1092
|
+
if (parser.keywords.test(name)) keywords.set(name, keywordTypes[name]);
|
|
1093
|
+
}
|
|
1094
|
+
const reserved = wordsRegexpToSet(parser.reservedWords);
|
|
1095
|
+
/** @type {Map<string, ReservedKind>} */
|
|
1096
|
+
const reservedKinds = new Map();
|
|
1097
|
+
for (const name of reserved) reservedKinds.set(name, 2);
|
|
1098
|
+
for (const name of wordsRegexpToSet(parser.reservedWordsStrict)) {
|
|
1099
|
+
if (!reserved.has(name)) reservedKinds.set(name, 3);
|
|
1100
|
+
}
|
|
1101
|
+
// keyword classification wins, matching acorn's keyword-first check
|
|
1102
|
+
for (const name of keywords.keys()) reservedKinds.set(name, 1);
|
|
1103
|
+
const reservedBind = wordsRegexpToSet(parser.reservedWordsStrictBind);
|
|
1104
|
+
let reservedMaxLen = 0;
|
|
1105
|
+
for (const name of reservedKinds.keys()) {
|
|
1106
|
+
if (name.length > reservedMaxLen) reservedMaxLen = name.length;
|
|
1107
|
+
}
|
|
1108
|
+
/** @type {WordLookups} */
|
|
1109
|
+
const lookups = {
|
|
1110
|
+
keywords,
|
|
1111
|
+
reservedKinds,
|
|
1112
|
+
reservedMaxLen,
|
|
1113
|
+
reservedBindTest: { test: (name) => reservedBind.has(name) }
|
|
1114
|
+
};
|
|
1115
|
+
wordLookupsCache.set(key, lookups);
|
|
1116
|
+
lastWordLookups = lookups;
|
|
1117
|
+
return lookups;
|
|
1118
|
+
};
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* webpack's parser: acorn plus lazy `range` (no `loc` at all), Set-based scopes,
|
|
1122
|
+
* tokenizer fast paths, import attributes and import phases (with acorn's
|
|
1123
|
+
* `!forNew` guard, unlike the former `acorn-import-phases` package).
|
|
1124
|
+
*/
|
|
1125
|
+
class WebpackParser extends BaseParser {
|
|
1126
|
+
/**
|
|
1127
|
+
* @param {Options & { lazyNodes?: boolean, lazyComments?: CollectedComment[], importPhases?: boolean, moduleFallback?: boolean }} options options
|
|
1128
|
+
* @param {string} input source code
|
|
1129
|
+
* @param {number=} startPos start position
|
|
1130
|
+
*/
|
|
1131
|
+
constructor(options, input, startPos) {
|
|
1132
|
+
const lazy = options.lazyNodes === true;
|
|
1133
|
+
// JavascriptParser._parse pre-disables acorn's tracking, so the
|
|
1134
|
+
// defensive copy only runs for direct callers
|
|
1135
|
+
if (lazy && (options.locations || options.ranges)) {
|
|
1136
|
+
options = { ...options, locations: false, ranges: false };
|
|
1137
|
+
}
|
|
1138
|
+
super(options, input, startPos);
|
|
1139
|
+
// acorn sets this.keywords/reservedWords in its constructor; parsing
|
|
1140
|
+
// (and thus readWord) only starts later in parse(), so this is ready
|
|
1141
|
+
this._wordLookups = getWordLookups(
|
|
1142
|
+
/** @type {ParserInternals} */ (/** @type {unknown} */ (this))
|
|
1143
|
+
);
|
|
1144
|
+
// acorn only calls `.test()` on reservedWordsStrictBind (in
|
|
1145
|
+
// checkLValSimple); swap its regexp for the Set-backed check
|
|
1146
|
+
/** @type {{ reservedWordsStrictBind: { test: (name: string) => boolean } }} */
|
|
1147
|
+
(/** @type {unknown} */ (this)).reservedWordsStrictBind =
|
|
1148
|
+
this._wordLookups.reservedBindTest;
|
|
1149
|
+
// per-token option probes cached once: acorn normalizes options in
|
|
1150
|
+
// `getOptions` before the constructor body runs and never mutates them
|
|
1151
|
+
const normalizedOptions = /** @type {ParserInternals} */ (
|
|
1152
|
+
/** @type {unknown} */ (this)
|
|
1153
|
+
).options;
|
|
1154
|
+
this._ecmaVersion = /** @type {number} */ (normalizedOptions.ecmaVersion);
|
|
1155
|
+
this._noLocations = !normalizedOptions.locations;
|
|
1156
|
+
// lazy mode: nodes get only offsets, gating the owned tokenizer and
|
|
1157
|
+
// statement fast paths
|
|
1158
|
+
this._lazy = lazy;
|
|
1159
|
+
// lazy comment collection must not race a user-provided onComment
|
|
1160
|
+
/** @type {CollectedComment[] | undefined} */
|
|
1161
|
+
this._lazyComments =
|
|
1162
|
+
lazy && !options.onComment ? options.lazyComments : undefined;
|
|
1163
|
+
// acorn skips a hashbang inside its constructor, before `_lazyComments`
|
|
1164
|
+
// above exists — reconstruct the comment the override missed
|
|
1165
|
+
if (
|
|
1166
|
+
this._lazyComments !== undefined &&
|
|
1167
|
+
!startPos &&
|
|
1168
|
+
this.options.allowHashBang &&
|
|
1169
|
+
input.startsWith("#!")
|
|
1170
|
+
) {
|
|
1171
|
+
this._lazyComments.push(
|
|
1172
|
+
new LazyComment(
|
|
1173
|
+
false,
|
|
1174
|
+
2,
|
|
1175
|
+
0,
|
|
1176
|
+
/** @type {ParserInternals} */ (/** @type {unknown} */ (this)).pos,
|
|
1177
|
+
input
|
|
1178
|
+
)
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
/** @type {ImportPhase | null} */
|
|
1182
|
+
this._importPhase = null;
|
|
1183
|
+
this._importPhasesEnabled = options.importPhases === true;
|
|
1184
|
+
// auto source type: parse as module first, downgrade to script in place
|
|
1185
|
+
// (instead of a second full parse) when script-only syntax is hit
|
|
1186
|
+
this._moduleFallback = options.moduleFallback === true;
|
|
1187
|
+
// set once a module-only construct is parsed; blocks the downgrade
|
|
1188
|
+
this._moduleSyntaxSeen = false;
|
|
1189
|
+
// the owned parseSubscript assumes optional chaining exists (it bakes
|
|
1190
|
+
// `optional` into the node shape), so gate it on the normalized version
|
|
1191
|
+
this._subscriptFastPath = lazy && this._ecmaVersion >= 11;
|
|
1192
|
+
// the owned getTokenFromCode bakes in every ES2021 operator (?., ??=,
|
|
1193
|
+
// &&=, ...), so it needs at least that version
|
|
1194
|
+
this._fullTokenFastPath = lazy && this._ecmaVersion >= 12;
|
|
1195
|
+
// whether the gap before the current token holds a line terminator:
|
|
1196
|
+
// 0 no, 1 yes, 2 unknown (canInsertSemicolon then scans the gap)
|
|
1197
|
+
/** @type {0 | 1 | 2} */
|
|
1198
|
+
this._newlineBefore = 2;
|
|
1199
|
+
// LIFO pool for call-scoped destructuring-errors records; depth resets
|
|
1200
|
+
// implicitly since a raise aborts the whole parse
|
|
1201
|
+
/** @type {DestructuringErrorsShim[]} */
|
|
1202
|
+
this._deStack = [];
|
|
1203
|
+
this._deDepth = 0;
|
|
1204
|
+
// LIFO pool for `parseObj`'s prop-clash records: acorn's ES6+
|
|
1205
|
+
// `checkPropClash` only ever touches `.proto`, so one record per nesting
|
|
1206
|
+
// depth suffices; an overriding subclass gets the fresh `{}` acorn expects
|
|
1207
|
+
this._propHashFastPath =
|
|
1208
|
+
/** @type {ParserInternals} */ (/** @type {unknown} */ (this))
|
|
1209
|
+
.checkPropClash === base.checkPropClash;
|
|
1210
|
+
/** @type {{ proto: boolean }[]} */
|
|
1211
|
+
this._propHashStack = [];
|
|
1212
|
+
this._propHashDepth = 0;
|
|
1213
|
+
// `readRegexp`'s flag whitelist depends only on the ecmaVersion
|
|
1214
|
+
this._validRegexpFlags = `gim${this._ecmaVersion >= 6 ? "uy" : ""}${
|
|
1215
|
+
this._ecmaVersion >= 9 ? "s" : ""
|
|
1216
|
+
}${this._ecmaVersion >= 13 ? "d" : ""}${this._ecmaVersion >= 15 ? "v" : ""}`;
|
|
1217
|
+
// the owned parseStatement inlines these methods, so a parser plugin
|
|
1218
|
+
// overriding any of them turns the statement fast path off
|
|
1219
|
+
const proto = WebpackParser.prototype;
|
|
1220
|
+
this._stmtFastPath =
|
|
1221
|
+
lazy &&
|
|
1222
|
+
this.parseVarStatement === proto.parseVarStatement &&
|
|
1223
|
+
this.parseVar === proto.parseVar &&
|
|
1224
|
+
this.parseIfStatement === proto.parseIfStatement &&
|
|
1225
|
+
this.parseReturnStatement === proto.parseReturnStatement &&
|
|
1226
|
+
this.parseExpressionStatement === proto.parseExpressionStatement;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
/**
|
|
1230
|
+
* Fetches a destructuring-errors record from the pool: acorn allocates one
|
|
1231
|
+
* per expression parse and drops it at the end of the call, so strictly
|
|
1232
|
+
* call-scoped users can reuse records instead. Pair every acquire with a
|
|
1233
|
+
* `_releaseDestructuringErrors` on each non-throwing exit.
|
|
1234
|
+
* @returns {DestructuringErrorsShim} reset record
|
|
1235
|
+
* @this {ParserInternals}
|
|
1236
|
+
*/
|
|
1237
|
+
_acquireDestructuringErrors() {
|
|
1238
|
+
const stack = this._deStack;
|
|
1239
|
+
const depth = this._deDepth++;
|
|
1240
|
+
const cached = stack[depth];
|
|
1241
|
+
if (cached !== undefined) {
|
|
1242
|
+
cached.shorthandAssign =
|
|
1243
|
+
cached.trailingComma =
|
|
1244
|
+
cached.parenthesizedAssign =
|
|
1245
|
+
cached.parenthesizedBind =
|
|
1246
|
+
cached.doubleProto =
|
|
1247
|
+
-1;
|
|
1248
|
+
return cached;
|
|
1249
|
+
}
|
|
1250
|
+
return (stack[depth] = createDestructuringErrors());
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/**
|
|
1254
|
+
* @returns {void}
|
|
1255
|
+
* @this {ParserInternals}
|
|
1256
|
+
*/
|
|
1257
|
+
_releaseDestructuringErrors() {
|
|
1258
|
+
this._deDepth--;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// ----- tokenizer fast paths -----
|
|
1262
|
+
|
|
1263
|
+
/**
|
|
1264
|
+
* Owned per-token loop: acorn's `nextToken` chains `skipSpace` →
|
|
1265
|
+
* `fullCharCodeAtPos` → `readToken` → `isIdentifierStart` with a dead
|
|
1266
|
+
* `locations` check at each step. For the common lazy, non-template context
|
|
1267
|
+
* this folds whitespace and comment skipping and the ASCII token dispatch
|
|
1268
|
+
* into one function so nothing re-enters acorn's per-step option checks.
|
|
1269
|
+
* Template/`preserveSpace` contexts and non-lazy mode use acorn's tokenizer.
|
|
1270
|
+
* @returns {void}
|
|
1271
|
+
* @this {ParserInternals}
|
|
1272
|
+
*/
|
|
1273
|
+
nextToken() {
|
|
1274
|
+
const context = this.context;
|
|
1275
|
+
const curContext = context[context.length - 1];
|
|
1276
|
+
if (
|
|
1277
|
+
!this._lazy ||
|
|
1278
|
+
!curContext ||
|
|
1279
|
+
curContext.preserveSpace ||
|
|
1280
|
+
curContext.override
|
|
1281
|
+
) {
|
|
1282
|
+
this._newlineBefore = 2;
|
|
1283
|
+
return base.nextToken.call(this);
|
|
1284
|
+
}
|
|
1285
|
+
const input = this.input;
|
|
1286
|
+
const len = input.length;
|
|
1287
|
+
let pos = this.pos;
|
|
1288
|
+
// line terminators are flagged while skipping (yuku's
|
|
1289
|
+
// `line_terminator_before`), so ASI checks need no gap re-scan. A
|
|
1290
|
+
// delegated path (acorn's html-comment handling) may have consumed part
|
|
1291
|
+
// of the gap before re-entering — start at "unknown" then.
|
|
1292
|
+
/** @type {0 | 1 | 2} */
|
|
1293
|
+
let newline = pos === this.lastTokEnd ? 0 : 2;
|
|
1294
|
+
// one CHAR_CLASS load classifies each char for both the skip loop and
|
|
1295
|
+
// the token dispatch below (yuku's ws_class/ident/punct tables in one)
|
|
1296
|
+
let code = 0;
|
|
1297
|
+
let cls = CLS_OTHER;
|
|
1298
|
+
while (pos < len) {
|
|
1299
|
+
code = input.charCodeAt(pos);
|
|
1300
|
+
cls = CHAR_CLASS[code];
|
|
1301
|
+
if (cls < CLS_SPACE) {
|
|
1302
|
+
if (cls === CLS_UNICODE) {
|
|
1303
|
+
// unicode whitespace / line terminators: acorn consumes the rest
|
|
1304
|
+
this.pos = pos;
|
|
1305
|
+
base.skipSpace.call(this);
|
|
1306
|
+
pos = this.pos;
|
|
1307
|
+
if (newline === 0) newline = 2;
|
|
1308
|
+
code = pos < len ? input.charCodeAt(pos) : 0;
|
|
1309
|
+
cls = CHAR_CLASS[code];
|
|
1310
|
+
}
|
|
1311
|
+
break;
|
|
1312
|
+
}
|
|
1313
|
+
if (cls === CLS_SPACE) {
|
|
1314
|
+
// space, tab, VT, FF (no CRLF/line bookkeeping in lazy mode)
|
|
1315
|
+
pos++;
|
|
1316
|
+
} else if (cls === CLS_NEWLINE) {
|
|
1317
|
+
newline = 1;
|
|
1318
|
+
pos++;
|
|
1319
|
+
} else {
|
|
1320
|
+
const next = input.charCodeAt(pos + 1);
|
|
1321
|
+
if (next === 42) {
|
|
1322
|
+
this.pos = pos;
|
|
1323
|
+
this.skipBlockComment();
|
|
1324
|
+
pos = this.pos;
|
|
1325
|
+
// the comment body may hold a line terminator
|
|
1326
|
+
if (newline === 0) newline = 2;
|
|
1327
|
+
} else if (next === 47) {
|
|
1328
|
+
this.pos = pos;
|
|
1329
|
+
this.skipLineComment(2);
|
|
1330
|
+
pos = this.pos;
|
|
1331
|
+
} else {
|
|
1332
|
+
// a division/regexp token, not a comment
|
|
1333
|
+
cls = CLS_OTHER;
|
|
1334
|
+
break;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
this._newlineBefore = newline;
|
|
1339
|
+
this.pos = pos;
|
|
1340
|
+
this.start = pos;
|
|
1341
|
+
if (pos >= len) return this.finishToken(tokTypes.eof);
|
|
1342
|
+
switch (cls) {
|
|
1343
|
+
case CLS_IDENT:
|
|
1344
|
+
return this.readWord();
|
|
1345
|
+
case CLS_PUNCT:
|
|
1346
|
+
this.pos = pos + 1;
|
|
1347
|
+
return this.finishToken(/** @type {TokenType} */ (SIMPLE_PUNCT[code]));
|
|
1348
|
+
case CLS_DOT: {
|
|
1349
|
+
// `.` not starting `.5` or `...`: skip readToken_dot's re-dispatch
|
|
1350
|
+
const next = input.charCodeAt(pos + 1);
|
|
1351
|
+
if ((next < 48 || next > 57) && next !== 46) {
|
|
1352
|
+
this.pos = pos + 1;
|
|
1353
|
+
return this.finishToken(tokTypes.dot);
|
|
1354
|
+
}
|
|
1355
|
+
return this.getTokenFromCode(code);
|
|
1356
|
+
}
|
|
1357
|
+
case CLS_EQ: {
|
|
1358
|
+
// `=` not starting `==` or `=>`: skip readToken_eq_excl + finishOp slice
|
|
1359
|
+
const next = input.charCodeAt(pos + 1);
|
|
1360
|
+
if (next !== 61 && next !== 62) {
|
|
1361
|
+
this.pos = pos + 1;
|
|
1362
|
+
return this.finishToken(tokTypes.eq, "=");
|
|
1363
|
+
}
|
|
1364
|
+
return this.getTokenFromCode(code);
|
|
1365
|
+
}
|
|
1366
|
+
case CLS_UNICODE:
|
|
1367
|
+
return this.readToken(this.fullCharCodeAtPos());
|
|
1368
|
+
default:
|
|
1369
|
+
return this.getTokenFromCode(code);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
/**
|
|
1374
|
+
* Lazy-mode `finishToken`: acorn probes `options.locations` for a dead
|
|
1375
|
+
* `endLoc` write on every token and reaches `updateContext` through an extra
|
|
1376
|
+
* method call. Skip the probe and inline acorn's `updateContext` body — this
|
|
1377
|
+
* runs once per token. Other modes use acorn's.
|
|
1378
|
+
* @param {TokenType} type token type
|
|
1379
|
+
* @param {unknown=} value token value
|
|
1380
|
+
* @returns {void}
|
|
1381
|
+
* @this {ParserInternals}
|
|
1382
|
+
*/
|
|
1383
|
+
finishToken(type, value) {
|
|
1384
|
+
if (!this._lazy) {
|
|
1385
|
+
return base.finishToken.call(this, type, value);
|
|
1386
|
+
}
|
|
1387
|
+
this.end = this.pos;
|
|
1388
|
+
const prevType = this.type;
|
|
1389
|
+
this.type = type;
|
|
1390
|
+
this.value = value;
|
|
1391
|
+
const internal = /** @type {TokenTypeInternal} */ (type);
|
|
1392
|
+
// acorn's updateContext, inlined: keyword-after-dot forbids an expression,
|
|
1393
|
+
// else the token type's own context hook runs, else `exprAllowed` follows
|
|
1394
|
+
// the type's `beforeExpr` (the branch that makes `/` after a value divide)
|
|
1395
|
+
if (type === tokTypes.name) {
|
|
1396
|
+
// name.updateContext, inlined for the commonest token: only `of` /
|
|
1397
|
+
// `yield` (outside a `.` access, ES6+) can re-allow an expression
|
|
1398
|
+
this.exprAllowed =
|
|
1399
|
+
((value === "of" && !this.exprAllowed) ||
|
|
1400
|
+
(value === "yield" && this.inGeneratorContext())) &&
|
|
1401
|
+
prevType !== tokTypes.dot &&
|
|
1402
|
+
this._ecmaVersion >= 6;
|
|
1403
|
+
} else if (type.keyword && prevType === tokTypes.dot) {
|
|
1404
|
+
this.exprAllowed = false;
|
|
1405
|
+
} else if (type === tokTypes.parenR || type === tokTypes.braceR) {
|
|
1406
|
+
// parenR/braceR.updateContext, inlined
|
|
1407
|
+
const context = this.context;
|
|
1408
|
+
if (context.length === 1) {
|
|
1409
|
+
this.exprAllowed = true;
|
|
1410
|
+
} else {
|
|
1411
|
+
let out = /** @type {TokContextShim} */ (context.pop());
|
|
1412
|
+
if (
|
|
1413
|
+
out === CTX_B_STAT &&
|
|
1414
|
+
/** @type {TokContextShim} */ (context[context.length - 1]).token ===
|
|
1415
|
+
"function"
|
|
1416
|
+
) {
|
|
1417
|
+
out = /** @type {TokContextShim} */ (context.pop());
|
|
1418
|
+
}
|
|
1419
|
+
this.exprAllowed = !out.isExpr;
|
|
1420
|
+
}
|
|
1421
|
+
} else if (type === tokTypes.braceL) {
|
|
1422
|
+
// braceL.updateContext, inlined
|
|
1423
|
+
this.context.push(this.braceIsBlock(prevType) ? CTX_B_STAT : CTX_B_EXPR);
|
|
1424
|
+
this.exprAllowed = true;
|
|
1425
|
+
} else if (type === tokTypes.parenL) {
|
|
1426
|
+
// parenL.updateContext, inlined
|
|
1427
|
+
const statementParens =
|
|
1428
|
+
prevType === tokTypes._if ||
|
|
1429
|
+
prevType === tokTypes._for ||
|
|
1430
|
+
prevType === tokTypes._with ||
|
|
1431
|
+
prevType === tokTypes._while;
|
|
1432
|
+
this.context.push(statementParens ? CTX_P_STAT : CTX_P_EXPR);
|
|
1433
|
+
this.exprAllowed = true;
|
|
1434
|
+
} else if (internal.updateContext) {
|
|
1435
|
+
internal.updateContext.call(this, prevType);
|
|
1436
|
+
} else {
|
|
1437
|
+
this.exprAllowed = internal.beforeExpr;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
/**
|
|
1442
|
+
* Owned `braceIsBlock`, acorn's verbatim except the line-terminator probe:
|
|
1443
|
+
* acorn slices the inter-token gap and runs a regexp; `_gapHasNewline`
|
|
1444
|
+
* answers from the tokenizer's newline flag (scanning only when unknown).
|
|
1445
|
+
* @param {TokenType} prevType type of the previous token
|
|
1446
|
+
* @returns {boolean} whether a `{` opens a block in this context
|
|
1447
|
+
* @this {ParserInternals}
|
|
1448
|
+
*/
|
|
1449
|
+
braceIsBlock(prevType) {
|
|
1450
|
+
const context = this.context;
|
|
1451
|
+
const parent = /** @type {TokContextShim} */ (context[context.length - 1]);
|
|
1452
|
+
if (parent === CTX_F_EXPR || parent === CTX_F_STAT) return true;
|
|
1453
|
+
if (
|
|
1454
|
+
prevType === tokTypes.colon &&
|
|
1455
|
+
(parent === CTX_B_STAT || parent === CTX_B_EXPR)
|
|
1456
|
+
) {
|
|
1457
|
+
return !parent.isExpr;
|
|
1458
|
+
}
|
|
1459
|
+
// after `return`, or after `yield`/`of` (name with exprAllowed), a line
|
|
1460
|
+
// terminator decides between block and expression
|
|
1461
|
+
if (
|
|
1462
|
+
prevType === tokTypes._return ||
|
|
1463
|
+
(prevType === tokTypes.name && this.exprAllowed)
|
|
1464
|
+
) {
|
|
1465
|
+
return this._gapHasNewline();
|
|
1466
|
+
}
|
|
1467
|
+
if (
|
|
1468
|
+
prevType === tokTypes._else ||
|
|
1469
|
+
prevType === tokTypes.semi ||
|
|
1470
|
+
prevType === tokTypes.eof ||
|
|
1471
|
+
prevType === tokTypes.parenR ||
|
|
1472
|
+
prevType === tokTypes.arrow
|
|
1473
|
+
) {
|
|
1474
|
+
return true;
|
|
1475
|
+
}
|
|
1476
|
+
if (prevType === tokTypes.braceL) return parent === CTX_B_STAT;
|
|
1477
|
+
if (
|
|
1478
|
+
prevType === tokTypes._var ||
|
|
1479
|
+
prevType === tokTypes._const ||
|
|
1480
|
+
prevType === tokTypes.name
|
|
1481
|
+
) {
|
|
1482
|
+
return false;
|
|
1483
|
+
}
|
|
1484
|
+
return !this.exprAllowed;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
/**
|
|
1488
|
+
* Whether the gap before the current token holds a line terminator, served
|
|
1489
|
+
* from the owned tokenizer's flag when known.
|
|
1490
|
+
* @returns {boolean} whether a line terminator precedes the current token
|
|
1491
|
+
* @this {ParserInternals}
|
|
1492
|
+
*/
|
|
1493
|
+
_gapHasNewline() {
|
|
1494
|
+
const newlineBefore = this._newlineBefore;
|
|
1495
|
+
if (newlineBefore !== 2) return newlineBefore === 1;
|
|
1496
|
+
const input = this.input;
|
|
1497
|
+
const end = this.start;
|
|
1498
|
+
for (let i = this.lastTokEnd; i < end; i++) {
|
|
1499
|
+
const ch = input.charCodeAt(i);
|
|
1500
|
+
// LF, CR, LS, PS — acorn's `lineBreak` alternation
|
|
1501
|
+
if (ch === 10 || ch === 13 || ch === 0x2028 || ch === 0x2029) {
|
|
1502
|
+
// memoize: the gap is fixed until the next token is read, and
|
|
1503
|
+
// `nextToken` rewrites the flag — ASI probes often repeat per token
|
|
1504
|
+
// (e.g. name atoms behind a /*#__PURE__*/ comment)
|
|
1505
|
+
this._newlineBefore = 1;
|
|
1506
|
+
return true;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
this._newlineBefore = 0;
|
|
1510
|
+
return false;
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* Owned per-token advance: acorn's `next` writes `lastTokEndLoc`/
|
|
1515
|
+
* `lastTokStartLoc` and probes `options.onToken` on every token, both dead in
|
|
1516
|
+
* lazy mode (locations off, no token stream), leaving only the two offset
|
|
1517
|
+
* writes and the keyword-escape guard. Other modes use acorn's.
|
|
1518
|
+
* @param {boolean=} ignoreEscapeSequenceInKeyword whether an escape in a keyword is allowed here
|
|
1519
|
+
* @returns {void}
|
|
1520
|
+
* @this {ParserInternals}
|
|
1521
|
+
*/
|
|
1522
|
+
next(ignoreEscapeSequenceInKeyword) {
|
|
1523
|
+
if (!this._lazy) {
|
|
1524
|
+
return base.next.call(this, ignoreEscapeSequenceInKeyword);
|
|
1525
|
+
}
|
|
1526
|
+
const type = this.type;
|
|
1527
|
+
if (!ignoreEscapeSequenceInKeyword && type.keyword && this.containsEsc) {
|
|
1528
|
+
this.raiseRecoverable(
|
|
1529
|
+
this.start,
|
|
1530
|
+
`Escape sequence in keyword ${type.keyword}`
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
this.lastTokEnd = this.end;
|
|
1534
|
+
this.lastTokStart = this.start;
|
|
1535
|
+
this.nextToken();
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
/**
|
|
1539
|
+
* Owned `finishOp`: acorn slices the operator text out of the source for
|
|
1540
|
+
* every operator token, allocating a fresh 2-4 char string per `=>`, `===`,
|
|
1541
|
+
* `&&` etc. Serve those from `OP_CACHE` instead; single-char operators keep
|
|
1542
|
+
* the direct slice, which V8 serves from its single-character table.
|
|
1543
|
+
* @param {TokenType} type token type
|
|
1544
|
+
* @param {number} size operator length
|
|
1545
|
+
* @returns {void}
|
|
1546
|
+
* @this {ParserInternals}
|
|
1547
|
+
*/
|
|
1548
|
+
finishOp(type, size) {
|
|
1549
|
+
const pos = this.pos;
|
|
1550
|
+
const input = this.input;
|
|
1551
|
+
if (size === 1) {
|
|
1552
|
+
this.pos = pos + 1;
|
|
1553
|
+
return this.finishToken(type, input.slice(pos, pos + 1));
|
|
1554
|
+
}
|
|
1555
|
+
let key = input.charCodeAt(pos) | (input.charCodeAt(pos + 1) << 7);
|
|
1556
|
+
if (size > 2) {
|
|
1557
|
+
key |= input.charCodeAt(pos + 2) << 14;
|
|
1558
|
+
if (size > 3) key |= input.charCodeAt(pos + 3) << 21;
|
|
1559
|
+
}
|
|
1560
|
+
let str = OP_CACHE.get(key);
|
|
1561
|
+
if (str === undefined) {
|
|
1562
|
+
str = input.slice(pos, pos + size);
|
|
1563
|
+
OP_CACHE.set(key, str);
|
|
1564
|
+
}
|
|
1565
|
+
this.pos = pos + size;
|
|
1566
|
+
return this.finishToken(type, str);
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
/**
|
|
1570
|
+
* Owned `getTokenFromCode`: acorn dispatches operators through per-family
|
|
1571
|
+
* `readToken_*` methods that each end in `finishOp`'s source slice. Resolve
|
|
1572
|
+
* every operator by direct char peeks to a static string instead (yuku's
|
|
1573
|
+
* `scanPunctuation`) — no method chain, no slice, no `OP_CACHE` probe. The
|
|
1574
|
+
* HTML-comment forms (`<!--`, `-->`), `#`, and unknown chars delegate.
|
|
1575
|
+
* @param {number} code current char code
|
|
1576
|
+
* @returns {void}
|
|
1577
|
+
* @this {ParserInternals}
|
|
1578
|
+
*/
|
|
1579
|
+
getTokenFromCode(code) {
|
|
1580
|
+
if (!this._fullTokenFastPath) {
|
|
1581
|
+
return base.getTokenFromCode.call(this, code);
|
|
1582
|
+
}
|
|
1583
|
+
const input = this.input;
|
|
1584
|
+
const pos = this.pos;
|
|
1585
|
+
switch (code) {
|
|
1586
|
+
case 46: {
|
|
1587
|
+
// '.': number, ellipsis or plain dot
|
|
1588
|
+
const next = input.charCodeAt(pos + 1);
|
|
1589
|
+
if (next >= 48 && next <= 57) return this.readNumber(true);
|
|
1590
|
+
if (next === 46 && input.charCodeAt(pos + 2) === 46) {
|
|
1591
|
+
this.pos = pos + 3;
|
|
1592
|
+
return this.finishToken(tokTypes.ellipsis);
|
|
1593
|
+
}
|
|
1594
|
+
this.pos = pos + 1;
|
|
1595
|
+
return this.finishToken(tokTypes.dot);
|
|
1596
|
+
}
|
|
1597
|
+
case 47: {
|
|
1598
|
+
// '/': regexp in expression position, otherwise /= or /
|
|
1599
|
+
if (this.exprAllowed) {
|
|
1600
|
+
this.pos = pos + 1;
|
|
1601
|
+
return this.readRegexp();
|
|
1602
|
+
}
|
|
1603
|
+
if (input.charCodeAt(pos + 1) === 61) {
|
|
1604
|
+
this.pos = pos + 2;
|
|
1605
|
+
return this.finishToken(tokTypes.assign, "/=");
|
|
1606
|
+
}
|
|
1607
|
+
this.pos = pos + 1;
|
|
1608
|
+
return this.finishToken(tokTypes.slash, "/");
|
|
1609
|
+
}
|
|
1610
|
+
case 37: {
|
|
1611
|
+
// '%': %= or %
|
|
1612
|
+
if (input.charCodeAt(pos + 1) === 61) {
|
|
1613
|
+
this.pos = pos + 2;
|
|
1614
|
+
return this.finishToken(tokTypes.assign, "%=");
|
|
1615
|
+
}
|
|
1616
|
+
this.pos = pos + 1;
|
|
1617
|
+
return this.finishToken(tokTypes.modulo, "%");
|
|
1618
|
+
}
|
|
1619
|
+
case 42: {
|
|
1620
|
+
// '*': **=, **, *= or *
|
|
1621
|
+
const next = input.charCodeAt(pos + 1);
|
|
1622
|
+
if (next === 42) {
|
|
1623
|
+
if (input.charCodeAt(pos + 2) === 61) {
|
|
1624
|
+
this.pos = pos + 3;
|
|
1625
|
+
return this.finishToken(tokTypes.assign, "**=");
|
|
1626
|
+
}
|
|
1627
|
+
this.pos = pos + 2;
|
|
1628
|
+
return this.finishToken(tokTypes.starstar, "**");
|
|
1629
|
+
}
|
|
1630
|
+
if (next === 61) {
|
|
1631
|
+
this.pos = pos + 2;
|
|
1632
|
+
return this.finishToken(tokTypes.assign, "*=");
|
|
1633
|
+
}
|
|
1634
|
+
this.pos = pos + 1;
|
|
1635
|
+
return this.finishToken(tokTypes.star, "*");
|
|
1636
|
+
}
|
|
1637
|
+
case 124: {
|
|
1638
|
+
// '|': ||=, ||, |= or |
|
|
1639
|
+
const next = input.charCodeAt(pos + 1);
|
|
1640
|
+
if (next === 124) {
|
|
1641
|
+
if (input.charCodeAt(pos + 2) === 61) {
|
|
1642
|
+
this.pos = pos + 3;
|
|
1643
|
+
return this.finishToken(tokTypes.assign, "||=");
|
|
1644
|
+
}
|
|
1645
|
+
this.pos = pos + 2;
|
|
1646
|
+
return this.finishToken(tokTypes.logicalOR, "||");
|
|
1647
|
+
}
|
|
1648
|
+
if (next === 61) {
|
|
1649
|
+
this.pos = pos + 2;
|
|
1650
|
+
return this.finishToken(tokTypes.assign, "|=");
|
|
1651
|
+
}
|
|
1652
|
+
this.pos = pos + 1;
|
|
1653
|
+
return this.finishToken(tokTypes.bitwiseOR, "|");
|
|
1654
|
+
}
|
|
1655
|
+
case 38: {
|
|
1656
|
+
// '&': &&=, &&, &= or &
|
|
1657
|
+
const next = input.charCodeAt(pos + 1);
|
|
1658
|
+
if (next === 38) {
|
|
1659
|
+
if (input.charCodeAt(pos + 2) === 61) {
|
|
1660
|
+
this.pos = pos + 3;
|
|
1661
|
+
return this.finishToken(tokTypes.assign, "&&=");
|
|
1662
|
+
}
|
|
1663
|
+
this.pos = pos + 2;
|
|
1664
|
+
return this.finishToken(tokTypes.logicalAND, "&&");
|
|
1665
|
+
}
|
|
1666
|
+
if (next === 61) {
|
|
1667
|
+
this.pos = pos + 2;
|
|
1668
|
+
return this.finishToken(tokTypes.assign, "&=");
|
|
1669
|
+
}
|
|
1670
|
+
this.pos = pos + 1;
|
|
1671
|
+
return this.finishToken(tokTypes.bitwiseAND, "&");
|
|
1672
|
+
}
|
|
1673
|
+
case 94: {
|
|
1674
|
+
// '^': ^= or ^
|
|
1675
|
+
if (input.charCodeAt(pos + 1) === 61) {
|
|
1676
|
+
this.pos = pos + 2;
|
|
1677
|
+
return this.finishToken(tokTypes.assign, "^=");
|
|
1678
|
+
}
|
|
1679
|
+
this.pos = pos + 1;
|
|
1680
|
+
return this.finishToken(tokTypes.bitwiseXOR, "^");
|
|
1681
|
+
}
|
|
1682
|
+
case 43: {
|
|
1683
|
+
// '+': ++, += or +
|
|
1684
|
+
const next = input.charCodeAt(pos + 1);
|
|
1685
|
+
if (next === 43) {
|
|
1686
|
+
this.pos = pos + 2;
|
|
1687
|
+
return this.finishToken(tokTypes.incDec, "++");
|
|
1688
|
+
}
|
|
1689
|
+
if (next === 61) {
|
|
1690
|
+
this.pos = pos + 2;
|
|
1691
|
+
return this.finishToken(tokTypes.assign, "+=");
|
|
1692
|
+
}
|
|
1693
|
+
this.pos = pos + 1;
|
|
1694
|
+
return this.finishToken(tokTypes.plusMin, "+");
|
|
1695
|
+
}
|
|
1696
|
+
case 45: {
|
|
1697
|
+
// '-': --, -= or -; `-->` may open an HTML line comment
|
|
1698
|
+
const next = input.charCodeAt(pos + 1);
|
|
1699
|
+
if (next === 45) {
|
|
1700
|
+
if (input.charCodeAt(pos + 2) === 62 && !this.inModule) {
|
|
1701
|
+
return base.getTokenFromCode.call(this, code);
|
|
1702
|
+
}
|
|
1703
|
+
this.pos = pos + 2;
|
|
1704
|
+
return this.finishToken(tokTypes.incDec, "--");
|
|
1705
|
+
}
|
|
1706
|
+
if (next === 61) {
|
|
1707
|
+
this.pos = pos + 2;
|
|
1708
|
+
return this.finishToken(tokTypes.assign, "-=");
|
|
1709
|
+
}
|
|
1710
|
+
this.pos = pos + 1;
|
|
1711
|
+
return this.finishToken(tokTypes.plusMin, "-");
|
|
1712
|
+
}
|
|
1713
|
+
case 60: {
|
|
1714
|
+
// '<': <<=, <<, <= or <; `<!--` opens an HTML line comment
|
|
1715
|
+
const next = input.charCodeAt(pos + 1);
|
|
1716
|
+
if (next === 60) {
|
|
1717
|
+
if (input.charCodeAt(pos + 2) === 61) {
|
|
1718
|
+
this.pos = pos + 3;
|
|
1719
|
+
return this.finishToken(tokTypes.assign, "<<=");
|
|
1720
|
+
}
|
|
1721
|
+
this.pos = pos + 2;
|
|
1722
|
+
return this.finishToken(tokTypes.bitShift, "<<");
|
|
1723
|
+
}
|
|
1724
|
+
if (
|
|
1725
|
+
next === 33 &&
|
|
1726
|
+
!this.inModule &&
|
|
1727
|
+
input.charCodeAt(pos + 2) === 45 &&
|
|
1728
|
+
input.charCodeAt(pos + 3) === 45
|
|
1729
|
+
) {
|
|
1730
|
+
return base.getTokenFromCode.call(this, code);
|
|
1731
|
+
}
|
|
1732
|
+
if (next === 61) {
|
|
1733
|
+
this.pos = pos + 2;
|
|
1734
|
+
return this.finishToken(tokTypes.relational, "<=");
|
|
1735
|
+
}
|
|
1736
|
+
this.pos = pos + 1;
|
|
1737
|
+
return this.finishToken(tokTypes.relational, "<");
|
|
1738
|
+
}
|
|
1739
|
+
case 62: {
|
|
1740
|
+
// '>': >>>=, >>>, >>=, >>, >= or >
|
|
1741
|
+
const next = input.charCodeAt(pos + 1);
|
|
1742
|
+
if (next === 62) {
|
|
1743
|
+
if (input.charCodeAt(pos + 2) === 62) {
|
|
1744
|
+
if (input.charCodeAt(pos + 3) === 61) {
|
|
1745
|
+
this.pos = pos + 4;
|
|
1746
|
+
return this.finishToken(tokTypes.assign, ">>>=");
|
|
1747
|
+
}
|
|
1748
|
+
this.pos = pos + 3;
|
|
1749
|
+
return this.finishToken(tokTypes.bitShift, ">>>");
|
|
1750
|
+
}
|
|
1751
|
+
if (input.charCodeAt(pos + 2) === 61) {
|
|
1752
|
+
this.pos = pos + 3;
|
|
1753
|
+
return this.finishToken(tokTypes.assign, ">>=");
|
|
1754
|
+
}
|
|
1755
|
+
this.pos = pos + 2;
|
|
1756
|
+
return this.finishToken(tokTypes.bitShift, ">>");
|
|
1757
|
+
}
|
|
1758
|
+
if (next === 61) {
|
|
1759
|
+
this.pos = pos + 2;
|
|
1760
|
+
return this.finishToken(tokTypes.relational, ">=");
|
|
1761
|
+
}
|
|
1762
|
+
this.pos = pos + 1;
|
|
1763
|
+
return this.finishToken(tokTypes.relational, ">");
|
|
1764
|
+
}
|
|
1765
|
+
case 61: {
|
|
1766
|
+
// '=': ===, ==, => or =
|
|
1767
|
+
const next = input.charCodeAt(pos + 1);
|
|
1768
|
+
if (next === 61) {
|
|
1769
|
+
if (input.charCodeAt(pos + 2) === 61) {
|
|
1770
|
+
this.pos = pos + 3;
|
|
1771
|
+
return this.finishToken(tokTypes.equality, "===");
|
|
1772
|
+
}
|
|
1773
|
+
this.pos = pos + 2;
|
|
1774
|
+
return this.finishToken(tokTypes.equality, "==");
|
|
1775
|
+
}
|
|
1776
|
+
if (next === 62) {
|
|
1777
|
+
this.pos = pos + 2;
|
|
1778
|
+
return this.finishToken(tokTypes.arrow);
|
|
1779
|
+
}
|
|
1780
|
+
this.pos = pos + 1;
|
|
1781
|
+
return this.finishToken(tokTypes.eq, "=");
|
|
1782
|
+
}
|
|
1783
|
+
case 33: {
|
|
1784
|
+
// '!': !==, != or !
|
|
1785
|
+
const next = input.charCodeAt(pos + 1);
|
|
1786
|
+
if (next === 61) {
|
|
1787
|
+
if (input.charCodeAt(pos + 2) === 61) {
|
|
1788
|
+
this.pos = pos + 3;
|
|
1789
|
+
return this.finishToken(tokTypes.equality, "!==");
|
|
1790
|
+
}
|
|
1791
|
+
this.pos = pos + 2;
|
|
1792
|
+
return this.finishToken(tokTypes.equality, "!=");
|
|
1793
|
+
}
|
|
1794
|
+
this.pos = pos + 1;
|
|
1795
|
+
return this.finishToken(tokTypes.prefix, "!");
|
|
1796
|
+
}
|
|
1797
|
+
case 63: {
|
|
1798
|
+
// '?': ?. (not before a digit), ??=, ?? or ?
|
|
1799
|
+
const next = input.charCodeAt(pos + 1);
|
|
1800
|
+
if (next === 46) {
|
|
1801
|
+
const next2 = input.charCodeAt(pos + 2);
|
|
1802
|
+
if (next2 < 48 || next2 > 57) {
|
|
1803
|
+
this.pos = pos + 2;
|
|
1804
|
+
return this.finishToken(tokTypes.questionDot, "?.");
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
if (next === 63) {
|
|
1808
|
+
if (input.charCodeAt(pos + 2) === 61) {
|
|
1809
|
+
this.pos = pos + 3;
|
|
1810
|
+
return this.finishToken(tokTypes.assign, "??=");
|
|
1811
|
+
}
|
|
1812
|
+
this.pos = pos + 2;
|
|
1813
|
+
return this.finishToken(tokTypes.coalesce, "??");
|
|
1814
|
+
}
|
|
1815
|
+
this.pos = pos + 1;
|
|
1816
|
+
return this.finishToken(tokTypes.question, "?");
|
|
1817
|
+
}
|
|
1818
|
+
case 126: {
|
|
1819
|
+
// '~'
|
|
1820
|
+
this.pos = pos + 1;
|
|
1821
|
+
return this.finishToken(tokTypes.prefix, "~");
|
|
1822
|
+
}
|
|
1823
|
+
case 96: {
|
|
1824
|
+
// '`'
|
|
1825
|
+
this.pos = pos + 1;
|
|
1826
|
+
return this.finishToken(tokTypes.backQuote);
|
|
1827
|
+
}
|
|
1828
|
+
case 48: {
|
|
1829
|
+
// '0': radix literals split off before the decimal reader
|
|
1830
|
+
const next = input.charCodeAt(pos + 1);
|
|
1831
|
+
if (next === 120 || next === 88) return this.readRadixNumber(16);
|
|
1832
|
+
if (next === 111 || next === 79) return this.readRadixNumber(8);
|
|
1833
|
+
if (next === 98 || next === 66) return this.readRadixNumber(2);
|
|
1834
|
+
return this.readNumber(false);
|
|
1835
|
+
}
|
|
1836
|
+
case 49:
|
|
1837
|
+
case 50:
|
|
1838
|
+
case 51:
|
|
1839
|
+
case 52:
|
|
1840
|
+
case 53:
|
|
1841
|
+
case 54:
|
|
1842
|
+
case 55:
|
|
1843
|
+
case 56:
|
|
1844
|
+
case 57:
|
|
1845
|
+
return this.readNumber(false);
|
|
1846
|
+
case 34:
|
|
1847
|
+
case 39:
|
|
1848
|
+
return this.readString(code);
|
|
1849
|
+
default:
|
|
1850
|
+
return base.getTokenFromCode.call(this, code);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
/**
|
|
1855
|
+
* ASCII fast path for acorn's `readWord1`, which pays a surrogate-aware
|
|
1856
|
+
* method call and a range-check helper per character. Escapes, non-ASCII
|
|
1857
|
+
* and astral input restart the base implementation from the word start.
|
|
1858
|
+
* Words are deduplicated through `WORD_CACHE` so repeated identifiers —
|
|
1859
|
+
* which dominate real code — reuse one string instead of slicing a fresh
|
|
1860
|
+
* one per occurrence; sharing also keeps their cached string hashes warm
|
|
1861
|
+
* for the keyword/scope Map lookups downstream.
|
|
1862
|
+
* @this {ParserInternals}
|
|
1863
|
+
* @returns {string} the word
|
|
1864
|
+
*/
|
|
1865
|
+
readWord1() {
|
|
1866
|
+
const input = this.input;
|
|
1867
|
+
const start = this.pos;
|
|
1868
|
+
const len = input.length;
|
|
1869
|
+
// djb2-style hash folded into the scan loop (yuku scans each lexeme in
|
|
1870
|
+
// one pass); computing it for the rare over-long words is cheaper than
|
|
1871
|
+
// re-walking every word in a second pass
|
|
1872
|
+
let hash = 0;
|
|
1873
|
+
let pos = start;
|
|
1874
|
+
while (pos < len) {
|
|
1875
|
+
const ch = input.charCodeAt(pos);
|
|
1876
|
+
if (ch < 128) {
|
|
1877
|
+
if (IDENT_CHAR[ch] === 0) {
|
|
1878
|
+
// backslash escape: restart cold so escape rules see the word
|
|
1879
|
+
if (ch === 92) return base.readWord1.call(this);
|
|
1880
|
+
break;
|
|
1881
|
+
}
|
|
1882
|
+
hash = (Math.imul(hash, 33) + ch) | 0;
|
|
1883
|
+
pos++;
|
|
1884
|
+
} else {
|
|
1885
|
+
return base.readWord1.call(this);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
this.containsEsc = false;
|
|
1889
|
+
this.pos = pos;
|
|
1890
|
+
const wordLen = pos - start;
|
|
1891
|
+
// Single-char words skip the cache (V8 serves those slices from its
|
|
1892
|
+
// single-character table without allocating); long words skip it too.
|
|
1893
|
+
if (wordLen >= 2 && wordLen <= WORD_CACHE_MAX_LEN) {
|
|
1894
|
+
const slot = hash & WORD_CACHE_MASK;
|
|
1895
|
+
const cached = WORD_CACHE[slot];
|
|
1896
|
+
if (
|
|
1897
|
+
cached !== null &&
|
|
1898
|
+
cached.length === wordLen &&
|
|
1899
|
+
sameWord(cached, input, start, wordLen)
|
|
1900
|
+
) {
|
|
1901
|
+
return cached;
|
|
1902
|
+
}
|
|
1903
|
+
const word = input.slice(start, pos);
|
|
1904
|
+
WORD_CACHE[slot] = word;
|
|
1905
|
+
return word;
|
|
1906
|
+
}
|
|
1907
|
+
return input.slice(start, pos);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
/**
|
|
1911
|
+
* String fast path: one scan that both finds the closing quote and vets the
|
|
1912
|
+
* span (the former `indexOf` + verify-loop pair read every string twice).
|
|
1913
|
+
* Escapes, newlines in the span, old ecmaVersions (LS/PS terminate strings
|
|
1914
|
+
* there) and location tracking restart acorn's implementation, which also
|
|
1915
|
+
* produces its exact errors.
|
|
1916
|
+
* @this {ParserInternals}
|
|
1917
|
+
* @param {number} quote quote char code
|
|
1918
|
+
* @returns {void}
|
|
1919
|
+
*/
|
|
1920
|
+
readString(quote) {
|
|
1921
|
+
if (!this._noLocations || this._ecmaVersion < 10) {
|
|
1922
|
+
return base.readString.call(this, quote);
|
|
1923
|
+
}
|
|
1924
|
+
const input = this.input;
|
|
1925
|
+
const len = input.length;
|
|
1926
|
+
const start = this.pos + 1;
|
|
1927
|
+
let pos = start;
|
|
1928
|
+
for (;;) {
|
|
1929
|
+
if (pos >= len) this.raise(this.start, "Unterminated string constant");
|
|
1930
|
+
const ch = input.charCodeAt(pos);
|
|
1931
|
+
if (ch === quote) break;
|
|
1932
|
+
// backslash, LF, CR
|
|
1933
|
+
if (ch === 92 || ch === 10 || ch === 13) {
|
|
1934
|
+
return base.readString.call(this, quote);
|
|
1935
|
+
}
|
|
1936
|
+
pos++;
|
|
1937
|
+
}
|
|
1938
|
+
this.pos = pos + 1;
|
|
1939
|
+
this.finishToken(tokTypes.string, input.slice(start, pos));
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
/**
|
|
1943
|
+
* Number fast path: plain integer literals (no leading zero, up to 15
|
|
1944
|
+
* digits so the float is exact) are accumulated numerically — no slice,
|
|
1945
|
+
* no parseFloat, no separator handling. Everything else (dots, exponents,
|
|
1946
|
+
* bigints, separators, octal forms) restarts acorn's implementation.
|
|
1947
|
+
* @this {ParserInternals}
|
|
1948
|
+
* @param {boolean} startsWithDot whether the number started with a dot
|
|
1949
|
+
* @returns {void}
|
|
1950
|
+
*/
|
|
1951
|
+
readNumber(startsWithDot) {
|
|
1952
|
+
if (startsWithDot) return base.readNumber.call(this, startsWithDot);
|
|
1953
|
+
const input = this.input;
|
|
1954
|
+
const start = this.pos;
|
|
1955
|
+
const len = input.length;
|
|
1956
|
+
const first = input.charCodeAt(start);
|
|
1957
|
+
let pos;
|
|
1958
|
+
if (first === 48) {
|
|
1959
|
+
const c1 = start + 1 < len ? input.charCodeAt(start + 1) : 0;
|
|
1960
|
+
if (c1 === 46) {
|
|
1961
|
+
// `0.<digits>`
|
|
1962
|
+
pos = start + 1;
|
|
1963
|
+
} else if (c1 > 127 || IDENT_CHAR[c1] === 1) {
|
|
1964
|
+
// 0x/0o/0b, 0e…, 0n, 0_, legacy `0NN`, or `0`+identifier: acorn
|
|
1965
|
+
return base.readNumber.call(this, startsWithDot);
|
|
1966
|
+
} else {
|
|
1967
|
+
// bare `0` before punctuation/operator/whitespace/EOF
|
|
1968
|
+
this.pos = start + 1;
|
|
1969
|
+
return this.finishToken(tokTypes.num, 0);
|
|
1970
|
+
}
|
|
1971
|
+
} else if (first > 48 && first <= 57) {
|
|
1972
|
+
// integer digits, accumulated numerically for the integer-only case
|
|
1973
|
+
let value = first - 48;
|
|
1974
|
+
pos = start + 1;
|
|
1975
|
+
while (pos < len) {
|
|
1976
|
+
const ch = input.charCodeAt(pos);
|
|
1977
|
+
if (ch >= 48 && ch <= 57) {
|
|
1978
|
+
value = value * 10 + (ch - 48);
|
|
1979
|
+
pos++;
|
|
1980
|
+
} else {
|
|
1981
|
+
break;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
const after = pos < len ? input.charCodeAt(pos) : 0;
|
|
1985
|
+
if (after !== 46) {
|
|
1986
|
+
// no fraction: exponent, separator, bigint suffix or a trailing
|
|
1987
|
+
// identifier char all need acorn's full handling and exact errors
|
|
1988
|
+
if (
|
|
1989
|
+
after === 101 ||
|
|
1990
|
+
after === 69 ||
|
|
1991
|
+
after === 95 ||
|
|
1992
|
+
after === 110 ||
|
|
1993
|
+
after > 127 ||
|
|
1994
|
+
IDENT_CHAR[after] === 1
|
|
1995
|
+
) {
|
|
1996
|
+
return base.readNumber.call(this, startsWithDot);
|
|
1997
|
+
}
|
|
1998
|
+
// 15 digits always fit exactly into a double
|
|
1999
|
+
if (pos - start > 15) {
|
|
2000
|
+
return base.readNumber.call(this, startsWithDot);
|
|
2001
|
+
}
|
|
2002
|
+
this.pos = pos;
|
|
2003
|
+
return this.finishToken(tokTypes.num, value);
|
|
2004
|
+
}
|
|
2005
|
+
// a fraction follows the integer part
|
|
2006
|
+
} else {
|
|
2007
|
+
return base.readNumber.call(this, startsWithDot);
|
|
2008
|
+
}
|
|
2009
|
+
// decimal fraction: `pos` is at the '.'
|
|
2010
|
+
pos++;
|
|
2011
|
+
while (pos < len) {
|
|
2012
|
+
const ch = input.charCodeAt(pos);
|
|
2013
|
+
if (ch >= 48 && ch <= 57) pos++;
|
|
2014
|
+
else break;
|
|
2015
|
+
}
|
|
2016
|
+
const after = pos < len ? input.charCodeAt(pos) : 0;
|
|
2017
|
+
// exponent, a second dot, separator, bigint suffix or trailing identifier
|
|
2018
|
+
if (
|
|
2019
|
+
after === 46 ||
|
|
2020
|
+
after === 101 ||
|
|
2021
|
+
after === 69 ||
|
|
2022
|
+
after === 95 ||
|
|
2023
|
+
after === 110 ||
|
|
2024
|
+
after > 127 ||
|
|
2025
|
+
IDENT_CHAR[after] === 1
|
|
2026
|
+
) {
|
|
2027
|
+
return base.readNumber.call(this, startsWithDot);
|
|
2028
|
+
}
|
|
2029
|
+
this.pos = pos;
|
|
2030
|
+
this.finishToken(tokTypes.num, Number.parseFloat(input.slice(start, pos)));
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
/**
|
|
2034
|
+
* Owned `strictDirective`: acorn's runs an anchored literal regex over
|
|
2035
|
+
* `this.input.slice(start)` — a fresh sliced string per (sloppy-mode)
|
|
2036
|
+
* function body. Sticky regexes at the offset scan the same grammar with no
|
|
2037
|
+
* slice. Same directive-prologue semantics, including the ASI tail checks.
|
|
2038
|
+
* @param {number} start offset of the function body's first statement
|
|
2039
|
+
* @returns {boolean} true when a 'use strict' directive leads the prologue
|
|
2040
|
+
* @this {ParserInternals & { input: string, options: { ecmaVersion: number } }}
|
|
2041
|
+
*/
|
|
2042
|
+
strictDirective(start) {
|
|
2043
|
+
if (/** @type {number} */ (this.options.ecmaVersion) < 5) return false;
|
|
2044
|
+
const input = this.input;
|
|
2045
|
+
for (;;) {
|
|
2046
|
+
// Skip whitespace and comments (acorn's `skipWhiteSpace`, sticky).
|
|
2047
|
+
STRICT_SKIP_WS.lastIndex = start;
|
|
2048
|
+
start += /** @type {RegExpExecArray} */ (STRICT_SKIP_WS.exec(input))[0]
|
|
2049
|
+
.length;
|
|
2050
|
+
STRICT_LITERAL.lastIndex = start;
|
|
2051
|
+
const match = STRICT_LITERAL.exec(input);
|
|
2052
|
+
if (!match) return false;
|
|
2053
|
+
if ((match[1] || match[2]) === "use strict") {
|
|
2054
|
+
STRICT_SKIP_WS.lastIndex = start + match[0].length;
|
|
2055
|
+
const spaceAfter = /** @type {RegExpExecArray} */ (
|
|
2056
|
+
STRICT_SKIP_WS.exec(input)
|
|
2057
|
+
);
|
|
2058
|
+
const end = spaceAfter.index + spaceAfter[0].length;
|
|
2059
|
+
const next = input.charAt(end);
|
|
2060
|
+
return (
|
|
2061
|
+
next === ";" ||
|
|
2062
|
+
next === "}" ||
|
|
2063
|
+
(STRICT_LINE_BREAK.test(spaceAfter[0]) &&
|
|
2064
|
+
!(
|
|
2065
|
+
/[(`.[+\-/*%<>=,?^&]/.test(next) ||
|
|
2066
|
+
(next === "!" && input.charAt(end + 1) === "=")
|
|
2067
|
+
))
|
|
2068
|
+
);
|
|
2069
|
+
}
|
|
2070
|
+
start += match[0].length;
|
|
2071
|
+
|
|
2072
|
+
// Skip semicolon, if any.
|
|
2073
|
+
STRICT_SKIP_WS.lastIndex = start;
|
|
2074
|
+
start += /** @type {RegExpExecArray} */ (STRICT_SKIP_WS.exec(input))[0]
|
|
2075
|
+
.length;
|
|
2076
|
+
if (input[start] === ";") start++;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
/**
|
|
2081
|
+
* Template fast path: when the chunk contains no backslash and no CR, the
|
|
2082
|
+
* cooked value is one slice (LF/LS/PS cook to themselves). Escapes, CR
|
|
2083
|
+
* normalization and location tracking restart acorn's implementation,
|
|
2084
|
+
* which also produces its exact errors.
|
|
2085
|
+
* @this {ParserInternals}
|
|
2086
|
+
* @returns {void}
|
|
2087
|
+
*/
|
|
2088
|
+
readTmplToken() {
|
|
2089
|
+
if (!this._noLocations) return base.readTmplToken.call(this);
|
|
2090
|
+
const input = this.input;
|
|
2091
|
+
const start = this.pos;
|
|
2092
|
+
const len = input.length;
|
|
2093
|
+
let pos = start;
|
|
2094
|
+
while (pos < len) {
|
|
2095
|
+
const ch = input.charCodeAt(pos);
|
|
2096
|
+
if (ch === 96 || (ch === 36 && input.charCodeAt(pos + 1) === 123)) {
|
|
2097
|
+
if (
|
|
2098
|
+
pos === this.start &&
|
|
2099
|
+
(this.type === tokTypes.template ||
|
|
2100
|
+
this.type === tokTypes.invalidTemplate)
|
|
2101
|
+
) {
|
|
2102
|
+
if (ch === 36) {
|
|
2103
|
+
this.pos = pos + 2;
|
|
2104
|
+
return this.finishToken(tokTypes.dollarBraceL);
|
|
2105
|
+
}
|
|
2106
|
+
this.pos = pos + 1;
|
|
2107
|
+
return this.finishToken(tokTypes.backQuote);
|
|
2108
|
+
}
|
|
2109
|
+
this.pos = pos;
|
|
2110
|
+
return this.finishToken(tokTypes.template, input.slice(start, pos));
|
|
2111
|
+
}
|
|
2112
|
+
// backslash and CR need acorn's cooked-string building
|
|
2113
|
+
if (ch === 92 || ch === 13) {
|
|
2114
|
+
return base.readTmplToken.call(this);
|
|
2115
|
+
}
|
|
2116
|
+
pos++;
|
|
2117
|
+
}
|
|
2118
|
+
this.raise(this.start, "Unterminated template");
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
/**
|
|
2122
|
+
* Fast path for the common run of plain ASCII whitespace; comments,
|
|
2123
|
+
* unicode whitespace and location tracking delegate to acorn.
|
|
2124
|
+
* @this {ParserInternals & { pos: number }}
|
|
2125
|
+
* @returns {void}
|
|
2126
|
+
*/
|
|
2127
|
+
skipSpace() {
|
|
2128
|
+
if (!this._noLocations) return base.skipSpace.call(this);
|
|
2129
|
+
const input = this.input;
|
|
2130
|
+
const len = input.length;
|
|
2131
|
+
let pos = this.pos;
|
|
2132
|
+
while (pos < len) {
|
|
2133
|
+
const ch = input.charCodeAt(pos);
|
|
2134
|
+
// 9-13 and 32 cover tab, LF, VT, FF, CR and space
|
|
2135
|
+
if (ch === 32 || (ch > 8 && ch < 14)) {
|
|
2136
|
+
pos++;
|
|
2137
|
+
} else if (ch === 47 || ch > 127) {
|
|
2138
|
+
// comments or unicode whitespace: let acorn handle the rest
|
|
2139
|
+
this.pos = pos;
|
|
2140
|
+
return base.skipSpace.call(this);
|
|
2141
|
+
} else {
|
|
2142
|
+
break;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
this.pos = pos;
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
// ----- word classification (Map/Set lookups, replaces acorn's regexps) -----
|
|
2149
|
+
|
|
2150
|
+
/**
|
|
2151
|
+
* Replaces acorn's `readWord`, whose `this.keywords.test(word)` runs an
|
|
2152
|
+
* anchored alternation regexp for every identifier and keyword token; a
|
|
2153
|
+
* Map lookup on the same keyword set is cheaper.
|
|
2154
|
+
* @this {ParserInternals}
|
|
2155
|
+
* @returns {void}
|
|
2156
|
+
*/
|
|
2157
|
+
readWord() {
|
|
2158
|
+
const word = this.readWord1();
|
|
2159
|
+
let type = tokTypes.name;
|
|
2160
|
+
const len = word.length;
|
|
2161
|
+
// every acorn keyword is 2-10 lowercase ASCII chars (`do`…`instanceof`),
|
|
2162
|
+
// so most identifiers skip the Map probe entirely (yuku gates its keyword
|
|
2163
|
+
// switch the same way)
|
|
2164
|
+
if (len >= 2 && len <= 10) {
|
|
2165
|
+
const first = word.charCodeAt(0);
|
|
2166
|
+
if (first >= 97 && first <= 122) {
|
|
2167
|
+
type = this._wordLookups.keywords.get(word) || tokTypes.name;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
this.finishToken(type, word);
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
/**
|
|
2174
|
+
* Mirror of acorn's `checkUnreserved` with its two per-identifier regexp
|
|
2175
|
+
* tests (`keywords` and `reservedWords`/`reservedWordsStrict`) folded into a
|
|
2176
|
+
* single `reservedKinds` lookup — one hash probe instead of two, and the
|
|
2177
|
+
* common plain identifier misses it and returns. Branches and error
|
|
2178
|
+
* messages match acorn exactly.
|
|
2179
|
+
* @param {Identifier} ref identifier node
|
|
2180
|
+
* @this {ParserInternals}
|
|
2181
|
+
*/
|
|
2182
|
+
checkUnreserved(ref) {
|
|
2183
|
+
const { start, end, name } = ref;
|
|
2184
|
+
// every reserved word and special name below is short lowercase ASCII, so
|
|
2185
|
+
// most identifiers exit on this shape gate before any string compare
|
|
2186
|
+
// (`yield`/`await`/`arguments` all pass it: lengths 5/5/9 ≤ reservedMaxLen,
|
|
2187
|
+
// which keywords like `instanceof` keep at ≥ 10)
|
|
2188
|
+
const lookups = this._wordLookups;
|
|
2189
|
+
const nameLen = name.length;
|
|
2190
|
+
if (nameLen < 2 || nameLen > lookups.reservedMaxLen) return;
|
|
2191
|
+
const firstCC = name.charCodeAt(0);
|
|
2192
|
+
if (firstCC < 97 || firstCC > 122) return;
|
|
2193
|
+
// name-first ordering: acorn's `inGenerator`/`inAsync` are getters that
|
|
2194
|
+
// walk the scope stack, so gate them behind the cheap string compare —
|
|
2195
|
+
// a plain identifier never triggers them
|
|
2196
|
+
if (name === "yield" && this.inGenerator) {
|
|
2197
|
+
this.raiseRecoverable(
|
|
2198
|
+
start,
|
|
2199
|
+
"Cannot use 'yield' as identifier inside a generator"
|
|
2200
|
+
);
|
|
2201
|
+
} else if (name === "await" && this.inAsync) {
|
|
2202
|
+
this.raiseRecoverable(
|
|
2203
|
+
start,
|
|
2204
|
+
"Cannot use 'await' as identifier inside an async function"
|
|
2205
|
+
);
|
|
2206
|
+
}
|
|
2207
|
+
if (name === "arguments" && !(this.currentThisScope().flags & SCOPE_VAR)) {
|
|
2208
|
+
this.raiseRecoverable(
|
|
2209
|
+
start,
|
|
2210
|
+
"Cannot use 'arguments' in class field initializer"
|
|
2211
|
+
);
|
|
2212
|
+
}
|
|
2213
|
+
if ((name === "arguments" || name === "await") && this.inClassStaticBlock) {
|
|
2214
|
+
this.raise(
|
|
2215
|
+
start,
|
|
2216
|
+
`Cannot use ${name} in class static initialization block`
|
|
2217
|
+
);
|
|
2218
|
+
}
|
|
2219
|
+
const kind = lookups.reservedKinds.get(name);
|
|
2220
|
+
if (kind === undefined) return;
|
|
2221
|
+
if (kind === 1) {
|
|
2222
|
+
this.raise(start, `Unexpected keyword '${name}'`);
|
|
2223
|
+
}
|
|
2224
|
+
if (
|
|
2225
|
+
/** @type {number} */ (this.options.ecmaVersion) < 6 &&
|
|
2226
|
+
this.input.slice(start, end).includes("\\")
|
|
2227
|
+
) {
|
|
2228
|
+
return;
|
|
2229
|
+
}
|
|
2230
|
+
if (kind === 2 || (kind === 3 && this.strict)) {
|
|
2231
|
+
if (name === "await" && !this.inAsync) {
|
|
2232
|
+
this.raiseRecoverable(
|
|
2233
|
+
start,
|
|
2234
|
+
"Cannot use keyword 'await' outside an async function"
|
|
2235
|
+
);
|
|
2236
|
+
}
|
|
2237
|
+
this.raiseRecoverable(start, `The keyword '${name}' is reserved`);
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
/**
|
|
2242
|
+
* Replaces acorn's `canInsertSemicolon`, whose line-break check slices the
|
|
2243
|
+
* inter-token gap and runs a regexp on it for every ASI decision (hundreds
|
|
2244
|
+
* of thousands per file). Scan the gap for a line terminator instead — no
|
|
2245
|
+
* slice, no regexp.
|
|
2246
|
+
* @returns {boolean} whether a semicolon may be inserted here
|
|
2247
|
+
* @this {ParserInternals}
|
|
2248
|
+
*/
|
|
2249
|
+
canInsertSemicolon() {
|
|
2250
|
+
if (this.type === tokTypes.eof || this.type === tokTypes.braceR) {
|
|
2251
|
+
return true;
|
|
2252
|
+
}
|
|
2253
|
+
// the owned nextToken already classified the gap; 2 (comment/unicode in
|
|
2254
|
+
// the gap, or a token from acorn's tokenizer) falls back to the scan
|
|
2255
|
+
return this._gapHasNewline();
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
// ----- comment collection without eager text slicing -----
|
|
2259
|
+
|
|
2260
|
+
/**
|
|
2261
|
+
* Replaces acorn's `skipLineComment` when comments are collected lazily:
|
|
2262
|
+
* the same scan, but no text slice and no position objects. Acorn calls
|
|
2263
|
+
* this for `//`, hashbangs and HTML-style comments (varying `startSkip`).
|
|
2264
|
+
* @this {ParserInternals}
|
|
2265
|
+
* @param {number} startSkip length of the comment opener
|
|
2266
|
+
* @returns {void}
|
|
2267
|
+
*/
|
|
2268
|
+
skipLineComment(startSkip) {
|
|
2269
|
+
const comments = this._lazyComments;
|
|
2270
|
+
if (comments === undefined) {
|
|
2271
|
+
return base.skipLineComment.call(this, startSkip);
|
|
2272
|
+
}
|
|
2273
|
+
const input = this.input;
|
|
2274
|
+
const start = this.pos;
|
|
2275
|
+
const len = input.length;
|
|
2276
|
+
let pos = start + startSkip;
|
|
2277
|
+
while (pos < len) {
|
|
2278
|
+
const ch = input.charCodeAt(pos);
|
|
2279
|
+
// LF, CR, LS, PS terminate the comment but are not part of it
|
|
2280
|
+
if (ch === 10 || ch === 13 || ch === 0x2028 || ch === 0x2029) break;
|
|
2281
|
+
pos++;
|
|
2282
|
+
}
|
|
2283
|
+
this.pos = pos;
|
|
2284
|
+
comments.push(new LazyComment(false, start + startSkip, start, pos, input));
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
/**
|
|
2288
|
+
* Replaces acorn's `skipBlockComment` when comments are collected lazily.
|
|
2289
|
+
* Locations are always off in lazy mode, so line breaks need no handling.
|
|
2290
|
+
* @this {ParserInternals}
|
|
2291
|
+
* @returns {void}
|
|
2292
|
+
*/
|
|
2293
|
+
skipBlockComment() {
|
|
2294
|
+
const comments = this._lazyComments;
|
|
2295
|
+
if (comments === undefined) {
|
|
2296
|
+
return base.skipBlockComment.call(this);
|
|
2297
|
+
}
|
|
2298
|
+
const start = this.pos;
|
|
2299
|
+
const end = this.input.indexOf("*/", (this.pos += 2));
|
|
2300
|
+
if (end === -1) this.raise(this.pos - 2, "Unterminated comment");
|
|
2301
|
+
this.pos = end + 2;
|
|
2302
|
+
comments.push(
|
|
2303
|
+
new LazyComment(true, start + 2, start, this.pos, this.input)
|
|
2304
|
+
);
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
// ----- lazy range -----
|
|
2308
|
+
|
|
2309
|
+
/**
|
|
2310
|
+
* @returns {Node} new node
|
|
2311
|
+
* @this {ParserInternals}
|
|
2312
|
+
*/
|
|
2313
|
+
startNode() {
|
|
2314
|
+
if (!this._lazy) return base.startNode.call(this);
|
|
2315
|
+
return new LazyLocNode(this.start);
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
/**
|
|
2319
|
+
* @param {number} pos start offset
|
|
2320
|
+
* @param {Position=} loc start position when acorn tracks locations
|
|
2321
|
+
* @returns {Node} new node
|
|
2322
|
+
* @this {ParserInternals}
|
|
2323
|
+
*/
|
|
2324
|
+
startNodeAt(pos, loc) {
|
|
2325
|
+
if (!this._lazy) return base.startNodeAt.call(this, pos, loc);
|
|
2326
|
+
return new LazyLocNode(pos);
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
/**
|
|
2330
|
+
* Lazy-mode `finishNode`: acorn's `locations`/`ranges` writes are dead when
|
|
2331
|
+
* `range` is served lazily and `loc` not at all, so skip them and the `finishNodeAt`
|
|
2332
|
+
* indirection. Runs once per node.
|
|
2333
|
+
* @param {Node} node node to finish
|
|
2334
|
+
* @param {string} type node type
|
|
2335
|
+
* @returns {Node} the finished node
|
|
2336
|
+
* @this {ParserInternals}
|
|
2337
|
+
*/
|
|
2338
|
+
finishNode(node, type) {
|
|
2339
|
+
if (!this._lazy) return base.finishNode.call(this, node, type);
|
|
2340
|
+
node.type = type;
|
|
2341
|
+
node.end = this.lastTokEnd;
|
|
2342
|
+
return node;
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
/**
|
|
2346
|
+
* Mirror of acorn's `copyNode`, which bypasses `startNodeAt` via
|
|
2347
|
+
* `new Node(...)` and would otherwise produce non-lazy nodes.
|
|
2348
|
+
* @param {Node} node node to copy
|
|
2349
|
+
* @returns {Node} copied node
|
|
2350
|
+
* @this {ParserInternals}
|
|
2351
|
+
*/
|
|
2352
|
+
copyNode(node) {
|
|
2353
|
+
const newNode = this.startNodeAt(node.start, this.startLoc);
|
|
2354
|
+
const from = /** @type {Record<string, unknown>} */ (
|
|
2355
|
+
/** @type {unknown} */ (node)
|
|
2356
|
+
);
|
|
2357
|
+
const to = /** @type {Record<string, unknown>} */ (
|
|
2358
|
+
/** @type {unknown} */ (newNode)
|
|
2359
|
+
);
|
|
2360
|
+
for (const prop in from) to[prop] = from[prop];
|
|
2361
|
+
return newNode;
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
// ----- owned node construction (single-shape nodes, replaces acorn's
|
|
2365
|
+
// start-empty-then-mutate flow one node type at a time) -----
|
|
2366
|
+
|
|
2367
|
+
/**
|
|
2368
|
+
* Owned `parseIdent` for the common name-token case: builds the finished
|
|
2369
|
+
* `IdentifierNode` directly, skipping acorn's `parseIdentNode`/`startNode`/
|
|
2370
|
+
* `finishNode` chain and its keyword branches. Keyword-as-identifier
|
|
2371
|
+
* (`obj.class`) and non-lazy mode delegate to acorn.
|
|
2372
|
+
* @param {boolean=} liberal whether reserved words are allowed
|
|
2373
|
+
* @returns {Identifier} identifier node
|
|
2374
|
+
* @this {ParserInternals}
|
|
2375
|
+
*/
|
|
2376
|
+
parseIdent(liberal) {
|
|
2377
|
+
if (this.type !== tokTypes.name || !this._lazy) {
|
|
2378
|
+
return base.parseIdent.call(this, liberal);
|
|
2379
|
+
}
|
|
2380
|
+
const node = /** @type {Identifier} */ (
|
|
2381
|
+
/** @type {unknown} */ (
|
|
2382
|
+
new IdentifierNode(
|
|
2383
|
+
this.start,
|
|
2384
|
+
this.end,
|
|
2385
|
+
/** @type {string} */ (this.value)
|
|
2386
|
+
)
|
|
2387
|
+
)
|
|
2388
|
+
);
|
|
2389
|
+
this.next(Boolean(liberal));
|
|
2390
|
+
if (!liberal) {
|
|
2391
|
+
this.checkUnreserved(node);
|
|
2392
|
+
if (node.name === "await" && !this.awaitIdentPos) {
|
|
2393
|
+
this.awaitIdentPos = node.start;
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
return node;
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
/**
|
|
2400
|
+
* Owned `parseSubscript`, an exact-semantics copy of acorn 8's with the
|
|
2401
|
+
* node construction replaced: member and call nodes are built fully-formed
|
|
2402
|
+
* after their property/arguments parse (the half-built node was never
|
|
2403
|
+
* reachable during it), landing on `MemberExpressionNode`/
|
|
2404
|
+
* `CallExpressionNode`'s single shapes. Pre-optional-chaining ecmaVersions
|
|
2405
|
+
* and non-lazy mode delegate to acorn.
|
|
2406
|
+
* @param {Expression} baseExpr subscript base
|
|
2407
|
+
* @param {number} startPos expression start offset
|
|
2408
|
+
* @param {Position | undefined} startLoc expression start position
|
|
2409
|
+
* @param {boolean | undefined} noCalls whether calls are forbidden (`new` callee)
|
|
2410
|
+
* @param {boolean} maybeAsyncArrow whether this may be an async arrow head
|
|
2411
|
+
* @param {boolean} optionalChained whether the chain is already optional
|
|
2412
|
+
* @param {boolean | string} forInit for-init context flag
|
|
2413
|
+
* @returns {Expression} subscript element or `baseExpr` when done
|
|
2414
|
+
* @this {ParserInternals}
|
|
2415
|
+
*/
|
|
2416
|
+
parseSubscript(
|
|
2417
|
+
baseExpr,
|
|
2418
|
+
startPos,
|
|
2419
|
+
startLoc,
|
|
2420
|
+
noCalls,
|
|
2421
|
+
maybeAsyncArrow,
|
|
2422
|
+
optionalChained,
|
|
2423
|
+
forInit
|
|
2424
|
+
) {
|
|
2425
|
+
if (!this._subscriptFastPath) {
|
|
2426
|
+
return base.parseSubscript.call(
|
|
2427
|
+
this,
|
|
2428
|
+
baseExpr,
|
|
2429
|
+
startPos,
|
|
2430
|
+
startLoc,
|
|
2431
|
+
noCalls,
|
|
2432
|
+
maybeAsyncArrow,
|
|
2433
|
+
optionalChained,
|
|
2434
|
+
forInit
|
|
2435
|
+
);
|
|
2436
|
+
}
|
|
2437
|
+
const optional = this.eat(tokTypes.questionDot);
|
|
2438
|
+
if (noCalls && optional) {
|
|
2439
|
+
this.raise(
|
|
2440
|
+
this.lastTokStart,
|
|
2441
|
+
"Optional chaining cannot appear in the callee of new expressions"
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
const computed = this.eat(tokTypes.bracketL);
|
|
2446
|
+
if (
|
|
2447
|
+
computed ||
|
|
2448
|
+
(optional &&
|
|
2449
|
+
this.type !== tokTypes.parenL &&
|
|
2450
|
+
this.type !== tokTypes.backQuote) ||
|
|
2451
|
+
this.eat(tokTypes.dot)
|
|
2452
|
+
) {
|
|
2453
|
+
/** @type {Node} */
|
|
2454
|
+
let property;
|
|
2455
|
+
if (computed) {
|
|
2456
|
+
property = this.parseExpression();
|
|
2457
|
+
this.expect(tokTypes.bracketR);
|
|
2458
|
+
} else if (
|
|
2459
|
+
this.type === tokTypes.privateId &&
|
|
2460
|
+
/** @type {string} */ (baseExpr.type) !== "Super"
|
|
2461
|
+
) {
|
|
2462
|
+
property = this.parsePrivateIdent();
|
|
2463
|
+
} else {
|
|
2464
|
+
property = this.parseIdent(this.options.allowReserved !== "never");
|
|
2465
|
+
}
|
|
2466
|
+
return /** @type {Expression} */ (
|
|
2467
|
+
/** @type {unknown} */ (
|
|
2468
|
+
new MemberExpressionNode(
|
|
2469
|
+
startPos,
|
|
2470
|
+
this.lastTokEnd,
|
|
2471
|
+
baseExpr,
|
|
2472
|
+
property,
|
|
2473
|
+
computed,
|
|
2474
|
+
optional
|
|
2475
|
+
)
|
|
2476
|
+
)
|
|
2477
|
+
);
|
|
2478
|
+
} else if (!noCalls && this.eat(tokTypes.parenL)) {
|
|
2479
|
+
const refDestructuringErrors = this._acquireDestructuringErrors();
|
|
2480
|
+
const oldYieldPos = this.yieldPos;
|
|
2481
|
+
const oldAwaitPos = this.awaitPos;
|
|
2482
|
+
const oldAwaitIdentPos = this.awaitIdentPos;
|
|
2483
|
+
this.yieldPos = 0;
|
|
2484
|
+
this.awaitPos = 0;
|
|
2485
|
+
this.awaitIdentPos = 0;
|
|
2486
|
+
const exprList = this.parseExprList(
|
|
2487
|
+
tokTypes.parenR,
|
|
2488
|
+
true,
|
|
2489
|
+
false,
|
|
2490
|
+
refDestructuringErrors
|
|
2491
|
+
);
|
|
2492
|
+
if (maybeAsyncArrow && !optional && this.shouldParseAsyncArrow()) {
|
|
2493
|
+
this.checkPatternErrors(refDestructuringErrors, false);
|
|
2494
|
+
this._releaseDestructuringErrors();
|
|
2495
|
+
this.checkYieldAwaitInDefaultParams();
|
|
2496
|
+
if (this.awaitIdentPos > 0) {
|
|
2497
|
+
this.raise(
|
|
2498
|
+
this.awaitIdentPos,
|
|
2499
|
+
"Cannot use 'await' as identifier inside an async function"
|
|
2500
|
+
);
|
|
2501
|
+
}
|
|
2502
|
+
this.yieldPos = oldYieldPos;
|
|
2503
|
+
this.awaitPos = oldAwaitPos;
|
|
2504
|
+
this.awaitIdentPos = oldAwaitIdentPos;
|
|
2505
|
+
return this.parseSubscriptAsyncArrow(
|
|
2506
|
+
startPos,
|
|
2507
|
+
startLoc,
|
|
2508
|
+
exprList,
|
|
2509
|
+
forInit
|
|
2510
|
+
);
|
|
2511
|
+
}
|
|
2512
|
+
this.checkExpressionErrors(refDestructuringErrors, true);
|
|
2513
|
+
this._releaseDestructuringErrors();
|
|
2514
|
+
this.yieldPos = oldYieldPos || this.yieldPos;
|
|
2515
|
+
this.awaitPos = oldAwaitPos || this.awaitPos;
|
|
2516
|
+
this.awaitIdentPos = oldAwaitIdentPos || this.awaitIdentPos;
|
|
2517
|
+
return /** @type {Expression} */ (
|
|
2518
|
+
/** @type {unknown} */ (
|
|
2519
|
+
new CallExpressionNode(
|
|
2520
|
+
startPos,
|
|
2521
|
+
this.lastTokEnd,
|
|
2522
|
+
baseExpr,
|
|
2523
|
+
exprList,
|
|
2524
|
+
optional
|
|
2525
|
+
)
|
|
2526
|
+
)
|
|
2527
|
+
);
|
|
2528
|
+
} else if (this.type === tokTypes.backQuote) {
|
|
2529
|
+
if (optional || optionalChained) {
|
|
2530
|
+
this.raise(
|
|
2531
|
+
this.start,
|
|
2532
|
+
"Optional chaining cannot appear in the tag of tagged template expressions"
|
|
2533
|
+
);
|
|
2534
|
+
}
|
|
2535
|
+
const node = this.startNodeAt(startPos, startLoc);
|
|
2536
|
+
/** @type {Node & { tag?: Expression, quasi?: Node }} */
|
|
2537
|
+
(node).tag = baseExpr;
|
|
2538
|
+
/** @type {Node & { tag?: Expression, quasi?: Node }} */
|
|
2539
|
+
(node).quasi = this.parseTemplate({ isTagged: true });
|
|
2540
|
+
return /** @type {Expression} */ (
|
|
2541
|
+
/** @type {unknown} */ (
|
|
2542
|
+
this.finishNode(node, "TaggedTemplateExpression")
|
|
2543
|
+
)
|
|
2544
|
+
);
|
|
2545
|
+
}
|
|
2546
|
+
return baseExpr;
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
/**
|
|
2550
|
+
* Owned `parseStatement` for the hot statement heads: acorn starts a node
|
|
2551
|
+
* before dispatching, but the owned statement parsers build their own
|
|
2552
|
+
* single-shape nodes, so that started node was one discarded allocation per
|
|
2553
|
+
* statement. Dispatch the common heads (`var`/`let`/`const`, `if`, `return`,
|
|
2554
|
+
* blocks and plain expression statements) without it; everything rarer, the
|
|
2555
|
+
* `name`-token ambiguities (`let`/`async`/`using`/`await` heads) and
|
|
2556
|
+
* plugin-overridden parsers delegate to acorn.
|
|
2557
|
+
* @param {string | null} context statement context
|
|
2558
|
+
* @param {boolean=} topLevel whether parsing top-level statements
|
|
2559
|
+
* @param {unknown=} exports export tracking object
|
|
2560
|
+
* @returns {Node} statement
|
|
2561
|
+
* @this {ParserInternals}
|
|
2562
|
+
*/
|
|
2563
|
+
parseStatement(context, topLevel, exports) {
|
|
2564
|
+
if (!this._stmtFastPath) {
|
|
2565
|
+
return base.parseStatement.call(this, context, topLevel, exports);
|
|
2566
|
+
}
|
|
2567
|
+
const startType = this.type;
|
|
2568
|
+
switch (startType) {
|
|
2569
|
+
case tokTypes._var:
|
|
2570
|
+
case tokTypes._const: {
|
|
2571
|
+
const kind = /** @type {string} */ (this.value);
|
|
2572
|
+
if (context && kind !== "var") this.unexpected();
|
|
2573
|
+
return this._parseVarStatementAt(this.start, kind);
|
|
2574
|
+
}
|
|
2575
|
+
case tokTypes._if:
|
|
2576
|
+
return this._parseIfStatementAt(this.start);
|
|
2577
|
+
case tokTypes._return:
|
|
2578
|
+
return this._parseReturnStatementAt(this.start);
|
|
2579
|
+
case tokTypes.braceL:
|
|
2580
|
+
// the owned parseBlock never reads the started node
|
|
2581
|
+
return this.parseBlock(true);
|
|
2582
|
+
case tokTypes._break:
|
|
2583
|
+
case tokTypes._continue:
|
|
2584
|
+
case tokTypes._debugger:
|
|
2585
|
+
case tokTypes._do:
|
|
2586
|
+
case tokTypes._for:
|
|
2587
|
+
case tokTypes._function:
|
|
2588
|
+
case tokTypes._class:
|
|
2589
|
+
case tokTypes._switch:
|
|
2590
|
+
case tokTypes._throw:
|
|
2591
|
+
case tokTypes._try:
|
|
2592
|
+
case tokTypes._while:
|
|
2593
|
+
case tokTypes._with:
|
|
2594
|
+
case tokTypes.semi:
|
|
2595
|
+
case tokTypes._export:
|
|
2596
|
+
case tokTypes._import:
|
|
2597
|
+
return base.parseStatement.call(this, context, topLevel, exports);
|
|
2598
|
+
default: {
|
|
2599
|
+
if (startType === tokTypes.name) {
|
|
2600
|
+
const value = this.value;
|
|
2601
|
+
if (value === "let") {
|
|
2602
|
+
if (this.isLet(context)) {
|
|
2603
|
+
// mirrors acorn's `context && kind !== "var"` rejection
|
|
2604
|
+
if (context) this.unexpected();
|
|
2605
|
+
return this._parseVarStatementAt(this.start, "let");
|
|
2606
|
+
}
|
|
2607
|
+
// `let` as a plain identifier: expression/label tail below
|
|
2608
|
+
} else if (
|
|
2609
|
+
value === "async" ||
|
|
2610
|
+
value === "using" ||
|
|
2611
|
+
value === "await"
|
|
2612
|
+
) {
|
|
2613
|
+
// async functions and using declarations keep acorn's
|
|
2614
|
+
// lookahead-heavy classification
|
|
2615
|
+
return base.parseStatement.call(this, context, topLevel, exports);
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
// unambiguous expression statement, with acorn's label tail
|
|
2619
|
+
const start = this.start;
|
|
2620
|
+
const maybeName = this.value;
|
|
2621
|
+
const expr = this.parseExpression();
|
|
2622
|
+
if (
|
|
2623
|
+
startType === tokTypes.name &&
|
|
2624
|
+
expr.type === "Identifier" &&
|
|
2625
|
+
this.eat(tokTypes.colon)
|
|
2626
|
+
) {
|
|
2627
|
+
// labels are rare enough to pay for the started node
|
|
2628
|
+
return this.parseLabeledStatement(
|
|
2629
|
+
this.startNodeAt(start),
|
|
2630
|
+
/** @type {string} */ (maybeName),
|
|
2631
|
+
/** @type {Identifier} */ (expr),
|
|
2632
|
+
context
|
|
2633
|
+
);
|
|
2634
|
+
}
|
|
2635
|
+
return this._parseExpressionStatementAt(start, expr);
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
/**
|
|
2641
|
+
* Owned `parseVarStatement`: the passed started node is filled by
|
|
2642
|
+
* `parseVar` as acorn expects, then the finished statement lands on
|
|
2643
|
+
* `VariableDeclarationNode`'s single shape. Non-lazy mode delegates.
|
|
2644
|
+
* @param {Node} node started statement node from `parseStatement`
|
|
2645
|
+
* @param {string} kind declaration kind
|
|
2646
|
+
* @param {boolean=} allowMissingInitializer whether `const x;` is allowed
|
|
2647
|
+
* @returns {Node} variable declaration
|
|
2648
|
+
* @this {ParserInternals}
|
|
2649
|
+
*/
|
|
2650
|
+
parseVarStatement(node, kind, allowMissingInitializer) {
|
|
2651
|
+
if (!this._lazy) {
|
|
2652
|
+
return base.parseVarStatement.call(
|
|
2653
|
+
this,
|
|
2654
|
+
node,
|
|
2655
|
+
kind,
|
|
2656
|
+
allowMissingInitializer
|
|
2657
|
+
);
|
|
2658
|
+
}
|
|
2659
|
+
return this._parseVarStatementAt(node.start, kind, allowMissingInitializer);
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2662
|
+
/**
|
|
2663
|
+
* Statement-position `var`/`let`/`const` without a started node: the
|
|
2664
|
+
* declaration lands directly on `VariableDeclarationNode`'s single shape.
|
|
2665
|
+
* @param {number} start statement start offset
|
|
2666
|
+
* @param {string} kind declaration kind
|
|
2667
|
+
* @param {boolean=} allowMissingInitializer whether `const x;` is allowed
|
|
2668
|
+
* @returns {Node} variable declaration
|
|
2669
|
+
* @this {ParserInternals}
|
|
2670
|
+
*/
|
|
2671
|
+
_parseVarStatementAt(start, kind, allowMissingInitializer) {
|
|
2672
|
+
this.next();
|
|
2673
|
+
/** @type {Node[]} */
|
|
2674
|
+
const declarations = [];
|
|
2675
|
+
this._parseVarInto(declarations, false, kind, allowMissingInitializer);
|
|
2676
|
+
this.semicolon();
|
|
2677
|
+
return /** @type {Node} */ (
|
|
2678
|
+
/** @type {unknown} */ (
|
|
2679
|
+
new VariableDeclarationNode(start, this.lastTokEnd, declarations, kind)
|
|
2680
|
+
)
|
|
2681
|
+
);
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
/**
|
|
2685
|
+
* Owned `parseVar`, an exact-semantics copy of acorn 8's that builds each
|
|
2686
|
+
* declarator fully-formed on `VariableDeclaratorNode`'s single shape. The
|
|
2687
|
+
* passed node keeps receiving `declarations`/`kind` because
|
|
2688
|
+
* `parseForStatement` finishes it itself. Non-lazy mode delegates.
|
|
2689
|
+
* @param {Node} node declaration node to fill
|
|
2690
|
+
* @param {boolean} isFor whether parsing a `for` head
|
|
2691
|
+
* @param {string} kind declaration kind
|
|
2692
|
+
* @param {boolean=} allowMissingInitializer whether `const x;` is allowed
|
|
2693
|
+
* @returns {Node} the filled node
|
|
2694
|
+
* @this {ParserInternals}
|
|
2695
|
+
*/
|
|
2696
|
+
parseVar(node, isFor, kind, allowMissingInitializer) {
|
|
2697
|
+
if (!this._lazy) {
|
|
2698
|
+
return base.parseVar.call(
|
|
2699
|
+
this,
|
|
2700
|
+
node,
|
|
2701
|
+
isFor,
|
|
2702
|
+
kind,
|
|
2703
|
+
allowMissingInitializer
|
|
2704
|
+
);
|
|
2705
|
+
}
|
|
2706
|
+
/** @type {Node[]} */
|
|
2707
|
+
const declarations = [];
|
|
2708
|
+
const target =
|
|
2709
|
+
/** @type {Node & { declarations: Node[], kind: string }} */ (node);
|
|
2710
|
+
target.declarations = declarations;
|
|
2711
|
+
target.kind = kind;
|
|
2712
|
+
this._parseVarInto(declarations, isFor, kind, allowMissingInitializer);
|
|
2713
|
+
return node;
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2716
|
+
/**
|
|
2717
|
+
* The declarator loop of the owned `parseVar`, shared with the owned
|
|
2718
|
+
* `parseStatement`'s node-free statement path.
|
|
2719
|
+
* @param {Node[]} declarations output array for the declarators
|
|
2720
|
+
* @param {boolean} isFor whether parsing a `for` head
|
|
2721
|
+
* @param {string} kind declaration kind
|
|
2722
|
+
* @param {boolean=} allowMissingInitializer whether `const x;` is allowed
|
|
2723
|
+
* @returns {void}
|
|
2724
|
+
* @this {ParserInternals}
|
|
2725
|
+
*/
|
|
2726
|
+
_parseVarInto(declarations, isFor, kind, allowMissingInitializer) {
|
|
2727
|
+
const ecmaVersion = this._ecmaVersion;
|
|
2728
|
+
const usingKind = kind === "using" || kind === "await using";
|
|
2729
|
+
for (;;) {
|
|
2730
|
+
const declStart = this.start;
|
|
2731
|
+
const id = usingKind ? this.parseIdent() : this.parseBindingAtom();
|
|
2732
|
+
this.checkLValPattern(
|
|
2733
|
+
id,
|
|
2734
|
+
kind === "var" ? BIND_VAR : BIND_LEXICAL,
|
|
2735
|
+
false
|
|
2736
|
+
);
|
|
2737
|
+
/** @type {Expression | null} */
|
|
2738
|
+
let init = null;
|
|
2739
|
+
if (this.eat(tokTypes.eq)) {
|
|
2740
|
+
init = this.parseMaybeAssign(isFor);
|
|
2741
|
+
} else if (
|
|
2742
|
+
!allowMissingInitializer &&
|
|
2743
|
+
kind === "const" &&
|
|
2744
|
+
!(
|
|
2745
|
+
this.type === tokTypes._in ||
|
|
2746
|
+
(ecmaVersion >= 6 && this.isContextual("of"))
|
|
2747
|
+
)
|
|
2748
|
+
) {
|
|
2749
|
+
this.unexpected();
|
|
2750
|
+
} else if (
|
|
2751
|
+
!allowMissingInitializer &&
|
|
2752
|
+
usingKind &&
|
|
2753
|
+
ecmaVersion >= 17 &&
|
|
2754
|
+
this.type !== tokTypes._in &&
|
|
2755
|
+
!this.isContextual("of")
|
|
2756
|
+
) {
|
|
2757
|
+
this.raise(
|
|
2758
|
+
this.lastTokEnd,
|
|
2759
|
+
`Missing initializer in ${kind} declaration`
|
|
2760
|
+
);
|
|
2761
|
+
} else if (
|
|
2762
|
+
!allowMissingInitializer &&
|
|
2763
|
+
id.type !== "Identifier" &&
|
|
2764
|
+
!(isFor && (this.type === tokTypes._in || this.isContextual("of")))
|
|
2765
|
+
) {
|
|
2766
|
+
this.raise(
|
|
2767
|
+
this.lastTokEnd,
|
|
2768
|
+
"Complex binding patterns require an initialization value"
|
|
2769
|
+
);
|
|
2770
|
+
}
|
|
2771
|
+
declarations.push(
|
|
2772
|
+
/** @type {Node} */ (
|
|
2773
|
+
/** @type {unknown} */ (
|
|
2774
|
+
new VariableDeclaratorNode(declStart, this.lastTokEnd, id, init)
|
|
2775
|
+
)
|
|
2776
|
+
)
|
|
2777
|
+
);
|
|
2778
|
+
if (!this.eat(tokTypes.comma)) break;
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
/**
|
|
2783
|
+
* Owned `parseExpressionStatement`: the statement lands on
|
|
2784
|
+
* `ExpressionStatementNode`'s single shape (the passed started node is
|
|
2785
|
+
* discarded, matching acorn's observable output). Non-lazy mode delegates.
|
|
2786
|
+
* @param {Node} node started statement node from `parseStatement`
|
|
2787
|
+
* @param {Expression} expr the parsed expression
|
|
2788
|
+
* @returns {Node} expression statement
|
|
2789
|
+
* @this {ParserInternals}
|
|
2790
|
+
*/
|
|
2791
|
+
parseExpressionStatement(node, expr) {
|
|
2792
|
+
if (!this._lazy) {
|
|
2793
|
+
return base.parseExpressionStatement.call(this, node, expr);
|
|
2794
|
+
}
|
|
2795
|
+
return this._parseExpressionStatementAt(node.start, expr);
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
/**
|
|
2799
|
+
* `parseExpressionStatement` without a started node.
|
|
2800
|
+
* @param {number} start statement start offset
|
|
2801
|
+
* @param {Expression} expr the parsed expression
|
|
2802
|
+
* @returns {Node} expression statement
|
|
2803
|
+
* @this {ParserInternals}
|
|
2804
|
+
*/
|
|
2805
|
+
_parseExpressionStatementAt(start, expr) {
|
|
2806
|
+
this.semicolon();
|
|
2807
|
+
return /** @type {Node} */ (
|
|
2808
|
+
/** @type {unknown} */ (
|
|
2809
|
+
new ExpressionStatementNode(start, this.lastTokEnd, expr)
|
|
2810
|
+
)
|
|
2811
|
+
);
|
|
2812
|
+
}
|
|
2813
|
+
|
|
2814
|
+
/**
|
|
2815
|
+
* Owned `parseBlock`, an exact-semantics copy of acorn 8's landing on
|
|
2816
|
+
* `BlockStatementNode`'s single shape. Non-lazy mode delegates.
|
|
2817
|
+
* @param {boolean=} createNewLexicalScope whether the block opens a scope
|
|
2818
|
+
* @param {Node=} node started statement node, when called from `parseStatement`
|
|
2819
|
+
* @param {boolean=} exitStrict whether to restore sloppy mode at the end
|
|
2820
|
+
* @returns {Node} block statement
|
|
2821
|
+
* @this {ParserInternals}
|
|
2822
|
+
*/
|
|
2823
|
+
parseBlock(createNewLexicalScope, node, exitStrict) {
|
|
2824
|
+
if (!this._lazy) {
|
|
2825
|
+
return base.parseBlock.call(
|
|
2826
|
+
this,
|
|
2827
|
+
createNewLexicalScope,
|
|
2828
|
+
node,
|
|
2829
|
+
exitStrict
|
|
2830
|
+
);
|
|
2831
|
+
}
|
|
2832
|
+
if (createNewLexicalScope === undefined) createNewLexicalScope = true;
|
|
2833
|
+
const start = this.start;
|
|
2834
|
+
/** @type {Node[]} */
|
|
2835
|
+
const body = [];
|
|
2836
|
+
this.expect(tokTypes.braceL);
|
|
2837
|
+
if (createNewLexicalScope) this.enterScope(0);
|
|
2838
|
+
while (this.type !== tokTypes.braceR) {
|
|
2839
|
+
body.push(this.parseStatement(null));
|
|
2840
|
+
}
|
|
2841
|
+
if (exitStrict) this.strict = false;
|
|
2842
|
+
this.next();
|
|
2843
|
+
if (createNewLexicalScope) this.exitScope();
|
|
2844
|
+
return /** @type {Node} */ (
|
|
2845
|
+
/** @type {unknown} */ (
|
|
2846
|
+
new BlockStatementNode(start, this.lastTokEnd, body)
|
|
2847
|
+
)
|
|
2848
|
+
);
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
/**
|
|
2852
|
+
* Owned `parseIfStatement` landing on `IfStatementNode`'s single shape.
|
|
2853
|
+
* Non-lazy mode delegates to acorn.
|
|
2854
|
+
* @param {Node} node started statement node from `parseStatement`
|
|
2855
|
+
* @returns {Node} if statement
|
|
2856
|
+
* @this {ParserInternals}
|
|
2857
|
+
*/
|
|
2858
|
+
parseIfStatement(node) {
|
|
2859
|
+
if (!this._lazy) return base.parseIfStatement.call(this, node);
|
|
2860
|
+
return this._parseIfStatementAt(node.start);
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2863
|
+
/**
|
|
2864
|
+
* `parseIfStatement` without a started node.
|
|
2865
|
+
* @param {number} start statement start offset
|
|
2866
|
+
* @returns {Node} if statement
|
|
2867
|
+
* @this {ParserInternals}
|
|
2868
|
+
*/
|
|
2869
|
+
_parseIfStatementAt(start) {
|
|
2870
|
+
this.next();
|
|
2871
|
+
const test = this.parseParenExpression();
|
|
2872
|
+
// function declarations are allowed in branches outside strict mode
|
|
2873
|
+
const consequent = this.parseStatement("if");
|
|
2874
|
+
const alternate = this.eat(tokTypes._else)
|
|
2875
|
+
? this.parseStatement("if")
|
|
2876
|
+
: null;
|
|
2877
|
+
return /** @type {Node} */ (
|
|
2878
|
+
/** @type {unknown} */ (
|
|
2879
|
+
new IfStatementNode(start, this.lastTokEnd, test, consequent, alternate)
|
|
2880
|
+
)
|
|
2881
|
+
);
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2884
|
+
/**
|
|
2885
|
+
* Owned `parseReturnStatement` landing on `ReturnStatementNode`'s single
|
|
2886
|
+
* shape. Non-lazy mode delegates to acorn.
|
|
2887
|
+
* @param {Node} node started statement node from `parseStatement`
|
|
2888
|
+
* @returns {Node} return statement
|
|
2889
|
+
* @this {ParserInternals}
|
|
2890
|
+
*/
|
|
2891
|
+
parseReturnStatement(node) {
|
|
2892
|
+
if (!this._lazy) {
|
|
2893
|
+
if (!this.allowReturn) this._tryModuleFallback();
|
|
2894
|
+
return base.parseReturnStatement.call(this, node);
|
|
2895
|
+
}
|
|
2896
|
+
return this._parseReturnStatementAt(node.start);
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
/**
|
|
2900
|
+
* In `auto` source type a top-level `return` is script-only syntax. When the
|
|
2901
|
+
* strict module parse reaches it with no module construct seen yet, downgrade
|
|
2902
|
+
* to a sloppy script in place: the prefix parsed under stricter rules stays a
|
|
2903
|
+
* valid sloppy prefix, so the second full parse is avoided.
|
|
2904
|
+
* @returns {boolean} true when the parse was downgraded to script
|
|
2905
|
+
* @this {ParserInternals}
|
|
2906
|
+
*/
|
|
2907
|
+
_tryModuleFallback() {
|
|
2908
|
+
if (
|
|
2909
|
+
!this._moduleFallback ||
|
|
2910
|
+
this._moduleSyntaxSeen ||
|
|
2911
|
+
!(this.currentVarScope().flags & SCOPE_TOP)
|
|
2912
|
+
) {
|
|
2913
|
+
return false;
|
|
2914
|
+
}
|
|
2915
|
+
this.options.allowReturnOutsideFunction = true;
|
|
2916
|
+
// acorn stamps Program.sourceType from this at parseTopLevel's end
|
|
2917
|
+
this.options.sourceType = "script";
|
|
2918
|
+
this.strict = false;
|
|
2919
|
+
this.inModule = false;
|
|
2920
|
+
this._moduleFallback = false;
|
|
2921
|
+
return true;
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
/**
|
|
2925
|
+
* `parseReturnStatement` without a started node.
|
|
2926
|
+
* @param {number} start statement start offset
|
|
2927
|
+
* @returns {Node} return statement
|
|
2928
|
+
* @this {ParserInternals}
|
|
2929
|
+
*/
|
|
2930
|
+
_parseReturnStatementAt(start) {
|
|
2931
|
+
if (!this.allowReturn && !this._tryModuleFallback()) {
|
|
2932
|
+
this.raise(this.start, "'return' outside of function");
|
|
2933
|
+
}
|
|
2934
|
+
this.next();
|
|
2935
|
+
/** @type {Expression | null} */
|
|
2936
|
+
let argument = null;
|
|
2937
|
+
// `return` takes an optional argument, so eagerly look for a semicolon
|
|
2938
|
+
if (!(this.eat(tokTypes.semi) || this.insertSemicolon())) {
|
|
2939
|
+
argument = this.parseExpression();
|
|
2940
|
+
this.semicolon();
|
|
2941
|
+
}
|
|
2942
|
+
return /** @type {Node} */ (
|
|
2943
|
+
/** @type {unknown} */ (
|
|
2944
|
+
new ReturnStatementNode(start, this.lastTokEnd, argument)
|
|
2945
|
+
)
|
|
2946
|
+
);
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2949
|
+
/**
|
|
2950
|
+
* Owned `parseSpread` landing on `RestSpreadNode`'s single shape.
|
|
2951
|
+
* Non-lazy mode delegates.
|
|
2952
|
+
* @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
|
|
2953
|
+
* @returns {Node} spread element
|
|
2954
|
+
* @this {ParserInternals}
|
|
2955
|
+
*/
|
|
2956
|
+
parseSpread(refDestructuringErrors) {
|
|
2957
|
+
if (!this._lazy) {
|
|
2958
|
+
return base.parseSpread.call(this, refDestructuringErrors);
|
|
2959
|
+
}
|
|
2960
|
+
const start = this.start;
|
|
2961
|
+
this.next();
|
|
2962
|
+
const argument = this.parseMaybeAssign(false, refDestructuringErrors);
|
|
2963
|
+
return /** @type {Node} */ (
|
|
2964
|
+
/** @type {unknown} */ (
|
|
2965
|
+
new RestSpreadNode(start, this.lastTokEnd, "SpreadElement", argument)
|
|
2966
|
+
)
|
|
2967
|
+
);
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
/**
|
|
2971
|
+
* Owned `parseMaybeConditional` landing on
|
|
2972
|
+
* `ConditionalExpressionNode`'s single shape. Non-lazy mode delegates.
|
|
2973
|
+
* @param {boolean | string=} forInit for-init context flag
|
|
2974
|
+
* @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
|
|
2975
|
+
* @returns {Expression} expression node
|
|
2976
|
+
* @this {ParserInternals}
|
|
2977
|
+
*/
|
|
2978
|
+
parseMaybeConditional(forInit, refDestructuringErrors) {
|
|
2979
|
+
if (!this._lazy) {
|
|
2980
|
+
return base.parseMaybeConditional.call(
|
|
2981
|
+
this,
|
|
2982
|
+
forInit,
|
|
2983
|
+
refDestructuringErrors
|
|
2984
|
+
);
|
|
2985
|
+
}
|
|
2986
|
+
const startPos = this.start;
|
|
2987
|
+
const expr = this.parseExprOps(forInit, refDestructuringErrors);
|
|
2988
|
+
if (this.checkExpressionErrors(refDestructuringErrors)) return expr;
|
|
2989
|
+
if (
|
|
2990
|
+
!(expr.type === "ArrowFunctionExpression" && expr.start === startPos) &&
|
|
2991
|
+
this.eat(tokTypes.question)
|
|
2992
|
+
) {
|
|
2993
|
+
const consequent = this.parseMaybeAssign();
|
|
2994
|
+
this.expect(tokTypes.colon);
|
|
2995
|
+
const alternate = this.parseMaybeAssign(forInit);
|
|
2996
|
+
return /** @type {Expression} */ (
|
|
2997
|
+
/** @type {unknown} */ (
|
|
2998
|
+
new ConditionalExpressionNode(
|
|
2999
|
+
startPos,
|
|
3000
|
+
this.lastTokEnd,
|
|
3001
|
+
expr,
|
|
3002
|
+
consequent,
|
|
3003
|
+
alternate
|
|
3004
|
+
)
|
|
3005
|
+
)
|
|
3006
|
+
);
|
|
3007
|
+
}
|
|
3008
|
+
return expr;
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
/**
|
|
3012
|
+
* Owned `parseNew`, an exact-semantics copy of acorn 8's: `NewExpression`
|
|
3013
|
+
* lands on `NewExpressionNode`'s single shape (zero-argument calls share
|
|
3014
|
+
* one empty array like acorn's `empty`); the rare `new.target` path keeps
|
|
3015
|
+
* the generic node. Non-lazy mode delegates.
|
|
3016
|
+
* @returns {Expression} new expression or meta property
|
|
3017
|
+
* @this {ParserInternals}
|
|
3018
|
+
*/
|
|
3019
|
+
parseNew() {
|
|
3020
|
+
if (!this._lazy) return base.parseNew.call(this);
|
|
3021
|
+
if (this.containsEsc) {
|
|
3022
|
+
this.raiseRecoverable(this.start, "Escape sequence in keyword new");
|
|
3023
|
+
}
|
|
3024
|
+
const nodeStart = this.start;
|
|
3025
|
+
this.next();
|
|
3026
|
+
if (this._ecmaVersion >= 6 && this.type === tokTypes.dot) {
|
|
3027
|
+
const node =
|
|
3028
|
+
/** @type {Node & { meta?: Node, property?: Identifier }} */
|
|
3029
|
+
(this.startNodeAt(nodeStart, undefined));
|
|
3030
|
+
const meta =
|
|
3031
|
+
/** @type {Node & { name?: string }} */
|
|
3032
|
+
(this.startNodeAt(nodeStart, undefined));
|
|
3033
|
+
meta.name = "new";
|
|
3034
|
+
node.meta = this.finishNode(/** @type {Node} */ (meta), "Identifier");
|
|
3035
|
+
this.next();
|
|
3036
|
+
const containsEsc = this.containsEsc;
|
|
3037
|
+
node.property = this.parseIdent(true);
|
|
3038
|
+
if (node.property.name !== "target") {
|
|
3039
|
+
this.raiseRecoverable(
|
|
3040
|
+
node.property.start,
|
|
3041
|
+
"The only valid meta property for new is 'new.target'"
|
|
3042
|
+
);
|
|
3043
|
+
}
|
|
3044
|
+
if (containsEsc) {
|
|
3045
|
+
this.raiseRecoverable(
|
|
3046
|
+
nodeStart,
|
|
3047
|
+
"'new.target' must not contain escaped characters"
|
|
3048
|
+
);
|
|
3049
|
+
}
|
|
3050
|
+
if (!this.allowNewDotTarget) {
|
|
3051
|
+
this.raiseRecoverable(
|
|
3052
|
+
nodeStart,
|
|
3053
|
+
"'new.target' can only be used in functions and class static block"
|
|
3054
|
+
);
|
|
3055
|
+
}
|
|
3056
|
+
return /** @type {Expression} */ (
|
|
3057
|
+
/** @type {unknown} */ (this.finishNode(node, "MetaProperty"))
|
|
3058
|
+
);
|
|
3059
|
+
}
|
|
3060
|
+
const startPos = this.start;
|
|
3061
|
+
const startLoc = this.startLoc;
|
|
3062
|
+
const callee = this.parseSubscripts(
|
|
3063
|
+
this.parseExprAtom(null, false, true),
|
|
3064
|
+
startPos,
|
|
3065
|
+
startLoc,
|
|
3066
|
+
true,
|
|
3067
|
+
false
|
|
3068
|
+
);
|
|
3069
|
+
if (/** @type {string} */ (callee.type) === "Super") {
|
|
3070
|
+
this.raiseRecoverable(startPos, "Invalid use of 'super'");
|
|
3071
|
+
}
|
|
3072
|
+
/** @type {Expression[]} */
|
|
3073
|
+
const args = this.eat(tokTypes.parenL)
|
|
3074
|
+
? this.parseExprList(tokTypes.parenR, true, false)
|
|
3075
|
+
: EMPTY_NEW_ARGS;
|
|
3076
|
+
return /** @type {Expression} */ (
|
|
3077
|
+
/** @type {unknown} */ (
|
|
3078
|
+
new NewExpressionNode(nodeStart, this.lastTokEnd, callee, args)
|
|
3079
|
+
)
|
|
3080
|
+
);
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
/**
|
|
3084
|
+
* Owned `parseTemplateElement` landing on `TemplateElementNode`'s single
|
|
3085
|
+
* shape; matches acorn's CRLF normalization and invalid-escape handling.
|
|
3086
|
+
* Non-lazy mode delegates.
|
|
3087
|
+
* @param {{ isTagged: boolean }} opts whether the template is tagged
|
|
3088
|
+
* @returns {Node} template element
|
|
3089
|
+
* @this {ParserInternals}
|
|
3090
|
+
*/
|
|
3091
|
+
parseTemplateElement(opts) {
|
|
3092
|
+
if (!this._lazy) return base.parseTemplateElement.call(this, opts);
|
|
3093
|
+
const start = this.start;
|
|
3094
|
+
/** @type {{ raw: string, cooked: string | null }} */
|
|
3095
|
+
let value;
|
|
3096
|
+
if (this.type === tokTypes.invalidTemplate) {
|
|
3097
|
+
if (!opts.isTagged) {
|
|
3098
|
+
this.raiseRecoverable(
|
|
3099
|
+
this.start,
|
|
3100
|
+
"Bad escape sequence in untagged template literal"
|
|
3101
|
+
);
|
|
3102
|
+
}
|
|
3103
|
+
value = {
|
|
3104
|
+
raw: /** @type {string} */ (this.value).replace(/\r\n?/g, "\n"),
|
|
3105
|
+
cooked: null
|
|
3106
|
+
};
|
|
3107
|
+
} else {
|
|
3108
|
+
const cooked = /** @type {string} */ (this.value);
|
|
3109
|
+
// Every escape cooks strictly shorter and CRLF shortens, while a lone CR
|
|
3110
|
+
// cooks to the LF the raw normalization would produce — so an
|
|
3111
|
+
// equal-length cooked string IS the normalized raw. That covers every
|
|
3112
|
+
// fast-path chunk (no backslash, no CR): raw and cooked share one string.
|
|
3113
|
+
value = {
|
|
3114
|
+
raw:
|
|
3115
|
+
cooked.length === this.end - this.start
|
|
3116
|
+
? cooked
|
|
3117
|
+
: this.input.slice(this.start, this.end).replace(/\r\n?/g, "\n"),
|
|
3118
|
+
cooked
|
|
3119
|
+
};
|
|
3120
|
+
}
|
|
3121
|
+
this.next();
|
|
3122
|
+
return /** @type {Node} */ (
|
|
3123
|
+
/** @type {unknown} */ (
|
|
3124
|
+
new TemplateElementNode(
|
|
3125
|
+
start,
|
|
3126
|
+
this.lastTokEnd,
|
|
3127
|
+
value,
|
|
3128
|
+
this.type === tokTypes.backQuote
|
|
3129
|
+
)
|
|
3130
|
+
)
|
|
3131
|
+
);
|
|
3132
|
+
}
|
|
3133
|
+
|
|
3134
|
+
/**
|
|
3135
|
+
* Owned `parseTemplate` landing on `TemplateLiteralNode`'s single shape.
|
|
3136
|
+
* Non-lazy mode delegates.
|
|
3137
|
+
* @param {{ isTagged?: boolean }=} opts whether the template is tagged
|
|
3138
|
+
* @returns {Node} template literal
|
|
3139
|
+
* @this {ParserInternals}
|
|
3140
|
+
*/
|
|
3141
|
+
parseTemplate(opts) {
|
|
3142
|
+
if (!this._lazy) {
|
|
3143
|
+
return base.parseTemplate.call(
|
|
3144
|
+
this,
|
|
3145
|
+
/** @type {{ isTagged: boolean }} */ (opts)
|
|
3146
|
+
);
|
|
3147
|
+
}
|
|
3148
|
+
const isTagged = (opts !== undefined && opts.isTagged) === true;
|
|
3149
|
+
const start = this.start;
|
|
3150
|
+
this.next();
|
|
3151
|
+
/** @type {Expression[]} */
|
|
3152
|
+
const expressions = [];
|
|
3153
|
+
// one options object for all chunks (only `isTagged` is ever read)
|
|
3154
|
+
const eltOpts = { isTagged };
|
|
3155
|
+
let curElt = /** @type {Node & { tail?: boolean }} */ (
|
|
3156
|
+
this.parseTemplateElement(eltOpts)
|
|
3157
|
+
);
|
|
3158
|
+
const quasis = [/** @type {Node} */ (curElt)];
|
|
3159
|
+
while (!curElt.tail) {
|
|
3160
|
+
if (this.type === tokTypes.eof) {
|
|
3161
|
+
this.raise(this.pos, "Unterminated template literal");
|
|
3162
|
+
}
|
|
3163
|
+
this.expect(tokTypes.dollarBraceL);
|
|
3164
|
+
expressions.push(this.parseExpression());
|
|
3165
|
+
this.expect(tokTypes.braceR);
|
|
3166
|
+
curElt = /** @type {Node & { tail?: boolean }} */ (
|
|
3167
|
+
this.parseTemplateElement(eltOpts)
|
|
3168
|
+
);
|
|
3169
|
+
quasis.push(/** @type {Node} */ (curElt));
|
|
3170
|
+
}
|
|
3171
|
+
this.next();
|
|
3172
|
+
return /** @type {Node} */ (
|
|
3173
|
+
/** @type {unknown} */ (
|
|
3174
|
+
new TemplateLiteralNode(start, this.lastTokEnd, expressions, quasis)
|
|
3175
|
+
)
|
|
3176
|
+
);
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
/**
|
|
3180
|
+
* Owned `parseMaybeUnary`, an exact-semantics copy of acorn 8's: prefix
|
|
3181
|
+
* unary/update and postfix update nodes are built fully-formed on
|
|
3182
|
+
* `UnaryNode`'s shared single shape. Non-lazy mode delegates to acorn.
|
|
3183
|
+
* @param {DestructuringErrorsShim | null} refDestructuringErrors destructuring errors to fill
|
|
3184
|
+
* @param {boolean} sawUnary whether a unary operator was already consumed
|
|
3185
|
+
* @param {boolean} incDec whether the caller was an update operator
|
|
3186
|
+
* @param {boolean | string=} forInit for-init context flag
|
|
3187
|
+
* @returns {Expression} expression node
|
|
3188
|
+
* @this {ParserInternals}
|
|
3189
|
+
*/
|
|
3190
|
+
parseMaybeUnary(refDestructuringErrors, sawUnary, incDec, forInit) {
|
|
3191
|
+
if (!this._lazy) {
|
|
3192
|
+
return base.parseMaybeUnary.call(
|
|
3193
|
+
this,
|
|
3194
|
+
refDestructuringErrors,
|
|
3195
|
+
sawUnary,
|
|
3196
|
+
incDec,
|
|
3197
|
+
forInit
|
|
3198
|
+
);
|
|
3199
|
+
}
|
|
3200
|
+
const startPos = this.start;
|
|
3201
|
+
const startLoc = this.startLoc;
|
|
3202
|
+
/** @type {Expression} */
|
|
3203
|
+
let expr;
|
|
3204
|
+
if (this.isContextual("await") && this.canAwait) {
|
|
3205
|
+
expr = this.parseAwait(forInit);
|
|
3206
|
+
sawUnary = true;
|
|
3207
|
+
} else if (/** @type {TokenTypeInternal} */ (this.type).prefix) {
|
|
3208
|
+
const update = this.type === tokTypes.incDec;
|
|
3209
|
+
const operator = /** @type {string} */ (this.value);
|
|
3210
|
+
this.next();
|
|
3211
|
+
const argument = this.parseMaybeUnary(null, true, update, forInit);
|
|
3212
|
+
this.checkExpressionErrors(refDestructuringErrors, true);
|
|
3213
|
+
if (update) {
|
|
3214
|
+
this.checkLValSimple(argument);
|
|
3215
|
+
} else if (
|
|
3216
|
+
this.strict &&
|
|
3217
|
+
operator === "delete" &&
|
|
3218
|
+
isLocalVariableAccess(argument)
|
|
3219
|
+
) {
|
|
3220
|
+
this.raiseRecoverable(
|
|
3221
|
+
startPos,
|
|
3222
|
+
"Deleting local variable in strict mode"
|
|
3223
|
+
);
|
|
3224
|
+
} else if (operator === "delete" && isPrivateFieldAccess(argument)) {
|
|
3225
|
+
this.raiseRecoverable(startPos, "Private fields can not be deleted");
|
|
3226
|
+
} else {
|
|
3227
|
+
sawUnary = true;
|
|
3228
|
+
}
|
|
3229
|
+
expr = /** @type {Expression} */ (
|
|
3230
|
+
/** @type {unknown} */ (
|
|
3231
|
+
new UnaryNode(
|
|
3232
|
+
startPos,
|
|
3233
|
+
this.lastTokEnd,
|
|
3234
|
+
update ? "UpdateExpression" : "UnaryExpression",
|
|
3235
|
+
operator,
|
|
3236
|
+
true,
|
|
3237
|
+
argument
|
|
3238
|
+
)
|
|
3239
|
+
)
|
|
3240
|
+
);
|
|
3241
|
+
} else if (!sawUnary && this.type === tokTypes.privateId) {
|
|
3242
|
+
if (
|
|
3243
|
+
(forInit || this.privateNameStack.length === 0) &&
|
|
3244
|
+
this.options.checkPrivateFields
|
|
3245
|
+
) {
|
|
3246
|
+
this.unexpected();
|
|
3247
|
+
}
|
|
3248
|
+
expr = /** @type {Expression} */ (
|
|
3249
|
+
/** @type {unknown} */ (this.parsePrivateIdent())
|
|
3250
|
+
);
|
|
3251
|
+
// a private name is only valid as `#x in obj`
|
|
3252
|
+
if (this.type !== tokTypes._in) this.unexpected();
|
|
3253
|
+
} else {
|
|
3254
|
+
expr = this.parseExprSubscripts(refDestructuringErrors, forInit);
|
|
3255
|
+
if (this.checkExpressionErrors(refDestructuringErrors)) return expr;
|
|
3256
|
+
while (
|
|
3257
|
+
/** @type {TokenTypeInternal} */ (this.type).postfix &&
|
|
3258
|
+
!this.canInsertSemicolon()
|
|
3259
|
+
) {
|
|
3260
|
+
const operator = /** @type {string} */ (this.value);
|
|
3261
|
+
this.checkLValSimple(expr);
|
|
3262
|
+
this.next();
|
|
3263
|
+
expr = /** @type {Expression} */ (
|
|
3264
|
+
/** @type {unknown} */ (
|
|
3265
|
+
new UnaryNode(
|
|
3266
|
+
startPos,
|
|
3267
|
+
this.lastTokEnd,
|
|
3268
|
+
"UpdateExpression",
|
|
3269
|
+
operator,
|
|
3270
|
+
false,
|
|
3271
|
+
expr
|
|
3272
|
+
)
|
|
3273
|
+
)
|
|
3274
|
+
);
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3278
|
+
if (!incDec && this.eat(tokTypes.starstar)) {
|
|
3279
|
+
if (sawUnary) {
|
|
3280
|
+
this.unexpected(this.lastTokStart);
|
|
3281
|
+
} else {
|
|
3282
|
+
return this.buildBinary(
|
|
3283
|
+
startPos,
|
|
3284
|
+
startLoc,
|
|
3285
|
+
expr,
|
|
3286
|
+
this.parseMaybeUnary(null, false, false, forInit),
|
|
3287
|
+
"**",
|
|
3288
|
+
false
|
|
3289
|
+
);
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
return expr;
|
|
3293
|
+
}
|
|
3294
|
+
|
|
3295
|
+
/**
|
|
3296
|
+
* Owned `parseMaybeAssign`, an exact-semantics copy of acorn 8's: the
|
|
3297
|
+
* operator is captured before `next()` and the `AssignmentExpression` is
|
|
3298
|
+
* built fully-formed on `AssignmentNode`'s single shape after the
|
|
3299
|
+
* right-hand parse. Yield and non-lazy mode delegate to acorn.
|
|
3300
|
+
* @param {boolean | string=} forInit for-init context flag
|
|
3301
|
+
* @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
|
|
3302
|
+
* @param {((this: unknown, left: Expression, startPos: number, startLoc?: Position) => Expression)=} afterLeftParse hook applied to the parsed left side
|
|
3303
|
+
* @returns {Expression} expression node
|
|
3304
|
+
* @this {ParserInternals}
|
|
3305
|
+
*/
|
|
3306
|
+
parseMaybeAssign(forInit, refDestructuringErrors, afterLeftParse) {
|
|
3307
|
+
if (!this._lazy) {
|
|
3308
|
+
return base.parseMaybeAssign.call(
|
|
3309
|
+
this,
|
|
3310
|
+
forInit,
|
|
3311
|
+
refDestructuringErrors,
|
|
3312
|
+
afterLeftParse
|
|
3313
|
+
);
|
|
3314
|
+
}
|
|
3315
|
+
if (this.isContextual("yield")) {
|
|
3316
|
+
if (this.inGenerator) return this.parseYield(forInit);
|
|
3317
|
+
// the tokenizer assumed an expression follows `yield`, but this
|
|
3318
|
+
// isn't that kind of yield
|
|
3319
|
+
this.exprAllowed = false;
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
let ownDestructuringErrors = false;
|
|
3323
|
+
let oldParenAssign = -1;
|
|
3324
|
+
let oldTrailingComma = -1;
|
|
3325
|
+
let oldDoubleProto = -1;
|
|
3326
|
+
if (refDestructuringErrors) {
|
|
3327
|
+
oldParenAssign = refDestructuringErrors.parenthesizedAssign;
|
|
3328
|
+
oldTrailingComma = refDestructuringErrors.trailingComma;
|
|
3329
|
+
oldDoubleProto = refDestructuringErrors.doubleProto;
|
|
3330
|
+
refDestructuringErrors.parenthesizedAssign =
|
|
3331
|
+
refDestructuringErrors.trailingComma = -1;
|
|
3332
|
+
} else {
|
|
3333
|
+
refDestructuringErrors = this._acquireDestructuringErrors();
|
|
3334
|
+
ownDestructuringErrors = true;
|
|
3335
|
+
}
|
|
3336
|
+
|
|
3337
|
+
const startPos = this.start;
|
|
3338
|
+
const startLoc = this.startLoc;
|
|
3339
|
+
if (this.type === tokTypes.parenL || this.type === tokTypes.name) {
|
|
3340
|
+
this.potentialArrowAt = this.start;
|
|
3341
|
+
this.potentialArrowInForAwait = forInit === "await";
|
|
3342
|
+
}
|
|
3343
|
+
let left = this.parseMaybeConditional(forInit, refDestructuringErrors);
|
|
3344
|
+
if (afterLeftParse) {
|
|
3345
|
+
left = afterLeftParse.call(this, left, startPos, startLoc);
|
|
3346
|
+
}
|
|
3347
|
+
if (/** @type {TokenTypeInternal} */ (this.type).isAssign) {
|
|
3348
|
+
const operator = /** @type {string} */ (this.value);
|
|
3349
|
+
if (this.type === tokTypes.eq) {
|
|
3350
|
+
left = /** @type {Expression} */ (
|
|
3351
|
+
this.toAssignable(left, false, refDestructuringErrors)
|
|
3352
|
+
);
|
|
3353
|
+
}
|
|
3354
|
+
if (!ownDestructuringErrors) {
|
|
3355
|
+
refDestructuringErrors.parenthesizedAssign =
|
|
3356
|
+
refDestructuringErrors.trailingComma =
|
|
3357
|
+
refDestructuringErrors.doubleProto =
|
|
3358
|
+
-1;
|
|
3359
|
+
}
|
|
3360
|
+
if (refDestructuringErrors.shorthandAssign >= left.start) {
|
|
3361
|
+
// shorthand default was used correctly
|
|
3362
|
+
refDestructuringErrors.shorthandAssign = -1;
|
|
3363
|
+
}
|
|
3364
|
+
if (this.type === tokTypes.eq) this.checkLValPattern(left);
|
|
3365
|
+
else this.checkLValSimple(left);
|
|
3366
|
+
// the own record's last read was above, so the nested right-side
|
|
3367
|
+
// parse below can already reuse its pool slot
|
|
3368
|
+
if (ownDestructuringErrors) this._releaseDestructuringErrors();
|
|
3369
|
+
this.next();
|
|
3370
|
+
const right = this.parseMaybeAssign(forInit);
|
|
3371
|
+
if (oldDoubleProto > -1) {
|
|
3372
|
+
refDestructuringErrors.doubleProto = oldDoubleProto;
|
|
3373
|
+
}
|
|
3374
|
+
return /** @type {Expression} */ (
|
|
3375
|
+
/** @type {unknown} */ (
|
|
3376
|
+
new AssignmentNode(startPos, this.lastTokEnd, operator, left, right)
|
|
3377
|
+
)
|
|
3378
|
+
);
|
|
3379
|
+
} else if (ownDestructuringErrors) {
|
|
3380
|
+
this.checkExpressionErrors(refDestructuringErrors, true);
|
|
3381
|
+
this._releaseDestructuringErrors();
|
|
3382
|
+
}
|
|
3383
|
+
if (oldParenAssign > -1) {
|
|
3384
|
+
refDestructuringErrors.parenthesizedAssign = oldParenAssign;
|
|
3385
|
+
}
|
|
3386
|
+
if (oldTrailingComma > -1) {
|
|
3387
|
+
refDestructuringErrors.trailingComma = oldTrailingComma;
|
|
3388
|
+
}
|
|
3389
|
+
return left;
|
|
3390
|
+
}
|
|
3391
|
+
|
|
3392
|
+
/**
|
|
3393
|
+
* Owned `parseExprOps`, an exact copy of acorn 8's, so the hot expression
|
|
3394
|
+
* spine (`parseMaybeAssign` → here → `parseMaybeUnary`) stays monomorphic
|
|
3395
|
+
* on owned code. Non-lazy mode delegates.
|
|
3396
|
+
* @param {boolean | string=} forInit for-init context flag
|
|
3397
|
+
* @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
|
|
3398
|
+
* @returns {Expression} expression node
|
|
3399
|
+
* @this {ParserInternals}
|
|
3400
|
+
*/
|
|
3401
|
+
parseExprOps(forInit, refDestructuringErrors) {
|
|
3402
|
+
if (!this._lazy) {
|
|
3403
|
+
return base.parseExprOps.call(this, forInit, refDestructuringErrors);
|
|
3404
|
+
}
|
|
3405
|
+
const startPos = this.start;
|
|
3406
|
+
const startLoc = this.startLoc;
|
|
3407
|
+
const expr = this.parseMaybeUnary(
|
|
3408
|
+
refDestructuringErrors || null,
|
|
3409
|
+
false,
|
|
3410
|
+
false,
|
|
3411
|
+
forInit
|
|
3412
|
+
);
|
|
3413
|
+
if (this.checkExpressionErrors(refDestructuringErrors)) return expr;
|
|
3414
|
+
return expr.start === startPos && expr.type === "ArrowFunctionExpression"
|
|
3415
|
+
? expr
|
|
3416
|
+
: this.parseExprOp(expr, startPos, startLoc, -1, forInit);
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3419
|
+
/**
|
|
3420
|
+
* Owned `parseExprOp`, an exact-semantics copy of acorn 8's with the
|
|
3421
|
+
* same-precedence continuation turned from tail recursion into a loop —
|
|
3422
|
+
* `a + b + c + d` runs one frame instead of one per operator. Non-lazy mode
|
|
3423
|
+
* delegates.
|
|
3424
|
+
* @param {Expression} left left operand
|
|
3425
|
+
* @param {number} leftStartPos expression start offset
|
|
3426
|
+
* @param {Position | undefined} leftStartLoc expression start position
|
|
3427
|
+
* @param {number} minPrec minimal binding precedence to continue
|
|
3428
|
+
* @param {boolean | string=} forInit for-init context flag (excludes `in`)
|
|
3429
|
+
* @returns {Expression} expression node
|
|
3430
|
+
* @this {ParserInternals}
|
|
3431
|
+
*/
|
|
3432
|
+
parseExprOp(left, leftStartPos, leftStartLoc, minPrec, forInit) {
|
|
3433
|
+
if (!this._lazy) {
|
|
3434
|
+
return base.parseExprOp.call(
|
|
3435
|
+
this,
|
|
3436
|
+
left,
|
|
3437
|
+
leftStartPos,
|
|
3438
|
+
leftStartLoc,
|
|
3439
|
+
minPrec,
|
|
3440
|
+
forInit
|
|
3441
|
+
);
|
|
3442
|
+
}
|
|
3443
|
+
for (;;) {
|
|
3444
|
+
const type = /** @type {TokenTypeInternal} */ (this.type);
|
|
3445
|
+
let prec = type.binop;
|
|
3446
|
+
// acorn's TokenType sets binop to null (never undefined) when absent
|
|
3447
|
+
if (
|
|
3448
|
+
prec === null ||
|
|
3449
|
+
prec <= minPrec ||
|
|
3450
|
+
(forInit && type === tokTypes._in)
|
|
3451
|
+
) {
|
|
3452
|
+
return left;
|
|
3453
|
+
}
|
|
3454
|
+
const logical =
|
|
3455
|
+
type === tokTypes.logicalOR || type === tokTypes.logicalAND;
|
|
3456
|
+
const coalesce = type === tokTypes.coalesce;
|
|
3457
|
+
if (coalesce) {
|
|
3458
|
+
// acorn parses `??`'s right at logical precedence so the mixing
|
|
3459
|
+
// check below sees any unparenthesized `&&`/`||` as a sibling
|
|
3460
|
+
prec = /** @type {number} */ (
|
|
3461
|
+
/** @type {TokenTypeInternal} */ (tokTypes.logicalAND).binop
|
|
3462
|
+
);
|
|
3463
|
+
}
|
|
3464
|
+
const op = /** @type {string} */ (this.value);
|
|
3465
|
+
this.next();
|
|
3466
|
+
const startPos = this.start;
|
|
3467
|
+
const right = this.parseExprOp(
|
|
3468
|
+
this.parseMaybeUnary(null, false, false, forInit),
|
|
3469
|
+
startPos,
|
|
3470
|
+
this.startLoc,
|
|
3471
|
+
prec,
|
|
3472
|
+
forInit
|
|
3473
|
+
);
|
|
3474
|
+
left = this.buildBinary(
|
|
3475
|
+
leftStartPos,
|
|
3476
|
+
leftStartLoc,
|
|
3477
|
+
left,
|
|
3478
|
+
right,
|
|
3479
|
+
op,
|
|
3480
|
+
logical || coalesce
|
|
3481
|
+
);
|
|
3482
|
+
if (
|
|
3483
|
+
(logical && this.type === tokTypes.coalesce) ||
|
|
3484
|
+
(coalesce &&
|
|
3485
|
+
(this.type === tokTypes.logicalOR ||
|
|
3486
|
+
this.type === tokTypes.logicalAND))
|
|
3487
|
+
) {
|
|
3488
|
+
this.raiseRecoverable(
|
|
3489
|
+
this.start,
|
|
3490
|
+
"Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses"
|
|
3491
|
+
);
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
/**
|
|
3497
|
+
* Owned `buildBinary` (acorn calls it only from `parseExprOp`): binary and
|
|
3498
|
+
* logical nodes are built fully-formed on `BinaryNode`'s single shape.
|
|
3499
|
+
* Non-lazy mode delegates to acorn.
|
|
3500
|
+
* @param {number} startPos expression start offset
|
|
3501
|
+
* @param {Position | undefined} startLoc expression start position
|
|
3502
|
+
* @param {Expression} left left operand
|
|
3503
|
+
* @param {Expression} right right operand
|
|
3504
|
+
* @param {string} op operator text
|
|
3505
|
+
* @param {boolean} logical whether this is a logical/coalesce expression
|
|
3506
|
+
* @returns {Expression} binary or logical expression node
|
|
3507
|
+
* @this {ParserInternals}
|
|
3508
|
+
*/
|
|
3509
|
+
buildBinary(startPos, startLoc, left, right, op, logical) {
|
|
3510
|
+
if (!this._lazy) {
|
|
3511
|
+
return base.buildBinary.call(
|
|
3512
|
+
this,
|
|
3513
|
+
startPos,
|
|
3514
|
+
startLoc,
|
|
3515
|
+
left,
|
|
3516
|
+
right,
|
|
3517
|
+
op,
|
|
3518
|
+
logical
|
|
3519
|
+
);
|
|
3520
|
+
}
|
|
3521
|
+
const rightNode = /** @type {Node} */ (/** @type {unknown} */ (right));
|
|
3522
|
+
if (rightNode.type === "PrivateIdentifier") {
|
|
3523
|
+
this.raise(
|
|
3524
|
+
rightNode.start,
|
|
3525
|
+
"Private identifier can only be left side of binary expression"
|
|
3526
|
+
);
|
|
3527
|
+
}
|
|
3528
|
+
return /** @type {Expression} */ (
|
|
3529
|
+
/** @type {unknown} */ (
|
|
3530
|
+
new BinaryNode(
|
|
3531
|
+
startPos,
|
|
3532
|
+
this.lastTokEnd,
|
|
3533
|
+
logical ? "LogicalExpression" : "BinaryExpression",
|
|
3534
|
+
left,
|
|
3535
|
+
op,
|
|
3536
|
+
right
|
|
3537
|
+
)
|
|
3538
|
+
)
|
|
3539
|
+
);
|
|
3540
|
+
}
|
|
3541
|
+
|
|
3542
|
+
/**
|
|
3543
|
+
* Owned `parseExprAtom` for the hot atoms: identifiers (with acorn's exact
|
|
3544
|
+
* async-function/async-arrow detection), number/string literals, keyword
|
|
3545
|
+
* literals (`true`/`false`/`null` — on the same `LiteralNode` shape as the
|
|
3546
|
+
* rest) and `this`. Everything else, and non-lazy mode, delegates to acorn.
|
|
3547
|
+
* @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
|
|
3548
|
+
* @param {boolean | string=} forInit for-init context flag
|
|
3549
|
+
* @param {boolean=} forNew whether parsed as a `new` callee
|
|
3550
|
+
* @returns {Expression} expression atom
|
|
3551
|
+
* @this {ParserInternals}
|
|
3552
|
+
*/
|
|
3553
|
+
parseExprAtom(refDestructuringErrors, forInit, forNew) {
|
|
3554
|
+
if (!this._lazy) {
|
|
3555
|
+
return base.parseExprAtom.call(
|
|
3556
|
+
this,
|
|
3557
|
+
refDestructuringErrors,
|
|
3558
|
+
forInit,
|
|
3559
|
+
forNew
|
|
3560
|
+
);
|
|
3561
|
+
}
|
|
3562
|
+
// division in expression position: the tokenizer got confused, force a
|
|
3563
|
+
// regexp re-read (mirrors the top of acorn's parseExprAtom)
|
|
3564
|
+
if (this.type === tokTypes.slash) this.readRegexp();
|
|
3565
|
+
const type = this.type;
|
|
3566
|
+
if (type === tokTypes.name) {
|
|
3567
|
+
const canBeArrow = this.potentialArrowAt === this.start;
|
|
3568
|
+
const startPos = this.start;
|
|
3569
|
+
const startLoc = this.startLoc;
|
|
3570
|
+
const containsEsc = this.containsEsc;
|
|
3571
|
+
let id = this.parseIdent(false);
|
|
3572
|
+
// `async` compare first — almost every identifier fails it, skipping
|
|
3573
|
+
// the version probe (all operands are pure)
|
|
3574
|
+
if (
|
|
3575
|
+
id.name === "async" &&
|
|
3576
|
+
!containsEsc &&
|
|
3577
|
+
this._ecmaVersion >= 8 &&
|
|
3578
|
+
!this.canInsertSemicolon() &&
|
|
3579
|
+
this.eat(tokTypes._function)
|
|
3580
|
+
) {
|
|
3581
|
+
this.overrideContext(tokContexts.f_expr);
|
|
3582
|
+
return this.parseFunction(
|
|
3583
|
+
this.startNodeAt(startPos, startLoc),
|
|
3584
|
+
0,
|
|
3585
|
+
false,
|
|
3586
|
+
true,
|
|
3587
|
+
forInit
|
|
3588
|
+
);
|
|
3589
|
+
}
|
|
3590
|
+
if (canBeArrow && !this.canInsertSemicolon()) {
|
|
3591
|
+
if (this.eat(tokTypes.arrow)) {
|
|
3592
|
+
return this.parseArrowExpression(
|
|
3593
|
+
this.startNodeAt(startPos, startLoc),
|
|
3594
|
+
[id],
|
|
3595
|
+
false,
|
|
3596
|
+
forInit
|
|
3597
|
+
);
|
|
3598
|
+
}
|
|
3599
|
+
if (
|
|
3600
|
+
id.name === "async" &&
|
|
3601
|
+
!containsEsc &&
|
|
3602
|
+
this._ecmaVersion >= 8 &&
|
|
3603
|
+
this.type === tokTypes.name &&
|
|
3604
|
+
(!this.potentialArrowInForAwait ||
|
|
3605
|
+
this.value !== "of" ||
|
|
3606
|
+
this.containsEsc)
|
|
3607
|
+
) {
|
|
3608
|
+
id = this.parseIdent(false);
|
|
3609
|
+
if (this.canInsertSemicolon() || !this.eat(tokTypes.arrow)) {
|
|
3610
|
+
this.unexpected();
|
|
3611
|
+
}
|
|
3612
|
+
return this.parseArrowExpression(
|
|
3613
|
+
this.startNodeAt(startPos, startLoc),
|
|
3614
|
+
[id],
|
|
3615
|
+
true,
|
|
3616
|
+
forInit
|
|
3617
|
+
);
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
return id;
|
|
3621
|
+
}
|
|
3622
|
+
if (type === tokTypes.num || type === tokTypes.string) {
|
|
3623
|
+
return /** @type {Expression} */ (
|
|
3624
|
+
/** @type {unknown} */ (this.parseLiteral(this.value))
|
|
3625
|
+
);
|
|
3626
|
+
}
|
|
3627
|
+
if (
|
|
3628
|
+
type === tokTypes._null ||
|
|
3629
|
+
type === tokTypes._true ||
|
|
3630
|
+
type === tokTypes._false
|
|
3631
|
+
) {
|
|
3632
|
+
const node = new LiteralNode(
|
|
3633
|
+
this.start,
|
|
3634
|
+
this.end,
|
|
3635
|
+
type === tokTypes._null ? null : type === tokTypes._true,
|
|
3636
|
+
/** @type {string} */ (type.keyword)
|
|
3637
|
+
);
|
|
3638
|
+
this.next();
|
|
3639
|
+
return /** @type {Expression} */ (/** @type {unknown} */ (node));
|
|
3640
|
+
}
|
|
3641
|
+
if (type === tokTypes._this) {
|
|
3642
|
+
const node = new ThisNode(this.start, this.end);
|
|
3643
|
+
this.next();
|
|
3644
|
+
return /** @type {Expression} */ (/** @type {unknown} */ (node));
|
|
3645
|
+
}
|
|
3646
|
+
if (type === tokTypes.bracketL) {
|
|
3647
|
+
const start = this.start;
|
|
3648
|
+
this.next();
|
|
3649
|
+
const elements = this.parseExprList(
|
|
3650
|
+
tokTypes.bracketR,
|
|
3651
|
+
true,
|
|
3652
|
+
true,
|
|
3653
|
+
refDestructuringErrors
|
|
3654
|
+
);
|
|
3655
|
+
return /** @type {Expression} */ (
|
|
3656
|
+
/** @type {unknown} */ (
|
|
3657
|
+
new ArrayExpressionNode(start, this.lastTokEnd, elements)
|
|
3658
|
+
)
|
|
3659
|
+
);
|
|
3660
|
+
}
|
|
3661
|
+
return base.parseExprAtom.call(
|
|
3662
|
+
this,
|
|
3663
|
+
refDestructuringErrors,
|
|
3664
|
+
forInit,
|
|
3665
|
+
forNew
|
|
3666
|
+
);
|
|
3667
|
+
}
|
|
3668
|
+
|
|
3669
|
+
/**
|
|
3670
|
+
* Owned `parseObj`, an exact-semantics copy of acorn 8's landing on
|
|
3671
|
+
* `ObjectNode`'s single shape (the ES5 trailing-comma gate is dropped —
|
|
3672
|
+
* the fast path requires ES11+). Non-lazy mode delegates.
|
|
3673
|
+
* @param {boolean} isPattern whether parsing a binding pattern
|
|
3674
|
+
* @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
|
|
3675
|
+
* @returns {Node} object expression or pattern
|
|
3676
|
+
* @this {ParserInternals}
|
|
3677
|
+
*/
|
|
3678
|
+
parseObj(isPattern, refDestructuringErrors) {
|
|
3679
|
+
if (!this._subscriptFastPath) {
|
|
3680
|
+
return base.parseObj.call(this, isPattern, refDestructuringErrors);
|
|
3681
|
+
}
|
|
3682
|
+
const start = this.start;
|
|
3683
|
+
let first = true;
|
|
3684
|
+
// acorn's ES6+ `checkPropClash` only ever reads/writes `.proto`, so the
|
|
3685
|
+
// record is pooled by nesting depth (like `_deStack`); a subclass override
|
|
3686
|
+
// might write arbitrary keys and gets a fresh `{}` instead. Depth resets
|
|
3687
|
+
// implicitly since a raise aborts the whole parse.
|
|
3688
|
+
const pooled = this._propHashFastPath;
|
|
3689
|
+
/** @type {Record<string, unknown>} */
|
|
3690
|
+
let propHash;
|
|
3691
|
+
if (pooled) {
|
|
3692
|
+
const stack = this._propHashStack;
|
|
3693
|
+
const depth = this._propHashDepth++;
|
|
3694
|
+
const cached = stack[depth];
|
|
3695
|
+
if (cached !== undefined) {
|
|
3696
|
+
cached.proto = false;
|
|
3697
|
+
propHash = cached;
|
|
3698
|
+
} else {
|
|
3699
|
+
propHash = stack[depth] = { proto: false };
|
|
3700
|
+
}
|
|
3701
|
+
} else {
|
|
3702
|
+
propHash = {};
|
|
3703
|
+
}
|
|
3704
|
+
/** @type {Node[]} */
|
|
3705
|
+
const properties = [];
|
|
3706
|
+
this.next();
|
|
3707
|
+
while (!this.eat(tokTypes.braceR)) {
|
|
3708
|
+
if (!first) {
|
|
3709
|
+
this.expect(tokTypes.comma);
|
|
3710
|
+
if (this.afterTrailingComma(tokTypes.braceR)) break;
|
|
3711
|
+
} else {
|
|
3712
|
+
first = false;
|
|
3713
|
+
}
|
|
3714
|
+
const prop = this.parseProperty(isPattern, refDestructuringErrors);
|
|
3715
|
+
if (!isPattern) {
|
|
3716
|
+
this.checkPropClash(prop, propHash, refDestructuringErrors);
|
|
3717
|
+
}
|
|
3718
|
+
properties.push(prop);
|
|
3719
|
+
}
|
|
3720
|
+
if (pooled) this._propHashDepth--;
|
|
3721
|
+
return /** @type {Node} */ (
|
|
3722
|
+
/** @type {unknown} */ (
|
|
3723
|
+
new ObjectNode(
|
|
3724
|
+
start,
|
|
3725
|
+
this.lastTokEnd,
|
|
3726
|
+
isPattern ? "ObjectPattern" : "ObjectExpression",
|
|
3727
|
+
properties
|
|
3728
|
+
)
|
|
3729
|
+
)
|
|
3730
|
+
);
|
|
3731
|
+
}
|
|
3732
|
+
|
|
3733
|
+
/**
|
|
3734
|
+
* Owned `parseProperty`, an exact-semantics copy of acorn 8's: spread/rest
|
|
3735
|
+
* land fully-formed on `RestSpreadNode` and properties start pre-shaped on
|
|
3736
|
+
* `PropertyNode`, which acorn's shared `parsePropertyName`/
|
|
3737
|
+
* `parsePropertyValue` then fill in place (ES9+ semantics assumed via the
|
|
3738
|
+
* ES11 fast-path gate). Non-lazy mode delegates.
|
|
3739
|
+
* @param {boolean} isPattern whether parsing a binding pattern
|
|
3740
|
+
* @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
|
|
3741
|
+
* @returns {Node} property, spread element or rest element
|
|
3742
|
+
* @this {ParserInternals}
|
|
3743
|
+
*/
|
|
3744
|
+
parseProperty(isPattern, refDestructuringErrors) {
|
|
3745
|
+
if (!this._subscriptFastPath) {
|
|
3746
|
+
return base.parseProperty.call(this, isPattern, refDestructuringErrors);
|
|
3747
|
+
}
|
|
3748
|
+
const nodeStart = this.start;
|
|
3749
|
+
if (this.eat(tokTypes.ellipsis)) {
|
|
3750
|
+
if (isPattern) {
|
|
3751
|
+
const argument = /** @type {Node} */ (
|
|
3752
|
+
/** @type {unknown} */ (this.parseIdent(false))
|
|
3753
|
+
);
|
|
3754
|
+
if (this.type === tokTypes.comma) {
|
|
3755
|
+
this.raiseRecoverable(
|
|
3756
|
+
this.start,
|
|
3757
|
+
"Comma is not permitted after the rest element"
|
|
3758
|
+
);
|
|
3759
|
+
}
|
|
3760
|
+
return /** @type {Node} */ (
|
|
3761
|
+
/** @type {unknown} */ (
|
|
3762
|
+
new RestSpreadNode(
|
|
3763
|
+
nodeStart,
|
|
3764
|
+
this.lastTokEnd,
|
|
3765
|
+
"RestElement",
|
|
3766
|
+
argument
|
|
3767
|
+
)
|
|
3768
|
+
)
|
|
3769
|
+
);
|
|
3770
|
+
}
|
|
3771
|
+
const argument = /** @type {Node} */ (
|
|
3772
|
+
/** @type {unknown} */ (
|
|
3773
|
+
this.parseMaybeAssign(false, refDestructuringErrors)
|
|
3774
|
+
)
|
|
3775
|
+
);
|
|
3776
|
+
// disallow trailing comma via `this.toAssignable()`
|
|
3777
|
+
if (
|
|
3778
|
+
this.type === tokTypes.comma &&
|
|
3779
|
+
refDestructuringErrors &&
|
|
3780
|
+
refDestructuringErrors.trailingComma < 0
|
|
3781
|
+
) {
|
|
3782
|
+
refDestructuringErrors.trailingComma = this.start;
|
|
3783
|
+
}
|
|
3784
|
+
return /** @type {Node} */ (
|
|
3785
|
+
/** @type {unknown} */ (
|
|
3786
|
+
new RestSpreadNode(
|
|
3787
|
+
nodeStart,
|
|
3788
|
+
this.lastTokEnd,
|
|
3789
|
+
"SpreadElement",
|
|
3790
|
+
argument
|
|
3791
|
+
)
|
|
3792
|
+
)
|
|
3793
|
+
);
|
|
3794
|
+
}
|
|
3795
|
+
const prop = /** @type {Node} */ (
|
|
3796
|
+
/** @type {unknown} */ (new PropertyNode(nodeStart))
|
|
3797
|
+
);
|
|
3798
|
+
let isGenerator = false;
|
|
3799
|
+
/** @type {number | undefined} */
|
|
3800
|
+
let startPos;
|
|
3801
|
+
/** @type {Position | undefined} */
|
|
3802
|
+
let startLoc;
|
|
3803
|
+
if (isPattern || refDestructuringErrors) {
|
|
3804
|
+
startPos = this.start;
|
|
3805
|
+
startLoc = this.startLoc;
|
|
3806
|
+
}
|
|
3807
|
+
if (!isPattern) isGenerator = this.eat(tokTypes.star);
|
|
3808
|
+
const containsEsc = this.containsEsc;
|
|
3809
|
+
this.parsePropertyName(prop);
|
|
3810
|
+
let isAsync = false;
|
|
3811
|
+
if (!isPattern && !containsEsc && !isGenerator && this.isAsyncProp(prop)) {
|
|
3812
|
+
isAsync = true;
|
|
3813
|
+
isGenerator = this.eat(tokTypes.star);
|
|
3814
|
+
this.parsePropertyName(prop);
|
|
3815
|
+
}
|
|
3816
|
+
this.parsePropertyValue(
|
|
3817
|
+
prop,
|
|
3818
|
+
isPattern,
|
|
3819
|
+
isGenerator,
|
|
3820
|
+
isAsync,
|
|
3821
|
+
startPos,
|
|
3822
|
+
startLoc,
|
|
3823
|
+
refDestructuringErrors,
|
|
3824
|
+
containsEsc
|
|
3825
|
+
);
|
|
3826
|
+
return this.finishNode(prop, "Property");
|
|
3827
|
+
}
|
|
3828
|
+
|
|
3829
|
+
/**
|
|
3830
|
+
* Owned `parseLiteral`: builds the finished `LiteralNode` directly; the
|
|
3831
|
+
* `bigint` branch matches acorn's. Non-lazy mode delegates to acorn.
|
|
3832
|
+
* @param {unknown} value literal value
|
|
3833
|
+
* @returns {Node} literal node
|
|
3834
|
+
* @this {ParserInternals}
|
|
3835
|
+
*/
|
|
3836
|
+
parseLiteral(value) {
|
|
3837
|
+
if (!this._lazy) return base.parseLiteral.call(this, value);
|
|
3838
|
+
const start = this.start;
|
|
3839
|
+
const end = this.end;
|
|
3840
|
+
const raw = this.input.slice(start, end);
|
|
3841
|
+
const node = new LiteralNode(start, end, value, raw);
|
|
3842
|
+
if (raw.charCodeAt(raw.length - 1) === 110) {
|
|
3843
|
+
// acorn falls back to normalizing `raw` when `BigInt` is missing;
|
|
3844
|
+
// every Node version webpack supports has it, so `value` is set
|
|
3845
|
+
/** @type {LiteralNode & { bigint?: string }} */ (node).bigint =
|
|
3846
|
+
/** @type {bigint} */ (value).toString();
|
|
3847
|
+
}
|
|
3848
|
+
this.next();
|
|
3849
|
+
return /** @type {Node} */ (/** @type {unknown} */ (node));
|
|
3850
|
+
}
|
|
3851
|
+
|
|
3852
|
+
/**
|
|
3853
|
+
* Single-construction regexp literals: acorn validates the pattern and
|
|
3854
|
+
* then builds the value with a second `new RegExp`. This override scans
|
|
3855
|
+
* like acorn, keeps acorn's flag validation (for its exact messages) and
|
|
3856
|
+
* lets one `new RegExp` be both the V8-backed validation and the value.
|
|
3857
|
+
* @this {ParserInternals & { regexpState: unknown }}
|
|
3858
|
+
* @returns {void}
|
|
3859
|
+
*/
|
|
3860
|
+
readRegexp() {
|
|
3861
|
+
const input = this.input;
|
|
3862
|
+
const start = this.pos;
|
|
3863
|
+
const len = input.length;
|
|
3864
|
+
let escaped = false;
|
|
3865
|
+
let inClass = false;
|
|
3866
|
+
let pos = start;
|
|
3867
|
+
for (;;) {
|
|
3868
|
+
if (pos >= len) this.raise(start, "Unterminated regular expression");
|
|
3869
|
+
const ch = input.charCodeAt(pos);
|
|
3870
|
+
// LF, CR, LS, PS
|
|
3871
|
+
if (ch === 10 || ch === 13 || ch === 0x2028 || ch === 0x2029) {
|
|
3872
|
+
this.raise(start, "Unterminated regular expression");
|
|
3873
|
+
}
|
|
3874
|
+
if (escaped) {
|
|
3875
|
+
escaped = false;
|
|
3876
|
+
} else {
|
|
3877
|
+
if (ch === 91) inClass = true;
|
|
3878
|
+
else if (ch === 93 && inClass) inClass = false;
|
|
3879
|
+
else if (ch === 47 && !inClass) break;
|
|
3880
|
+
escaped = ch === 92;
|
|
3881
|
+
}
|
|
3882
|
+
pos++;
|
|
3883
|
+
}
|
|
3884
|
+
const pattern = input.slice(start, pos);
|
|
3885
|
+
this.pos = pos + 1;
|
|
3886
|
+
const flagsStart = this.pos;
|
|
3887
|
+
const flags = this.readWord1();
|
|
3888
|
+
if (this.containsEsc) this.unexpected(flagsStart);
|
|
3889
|
+
|
|
3890
|
+
// acorn's per-ecmaVersion flag validation, kept for its exact errors;
|
|
3891
|
+
// the whitelist is precomputed in the constructor
|
|
3892
|
+
const ecmaVersion = this._ecmaVersion;
|
|
3893
|
+
const validFlags = this._validRegexpFlags;
|
|
3894
|
+
let hasU = false;
|
|
3895
|
+
let hasV = false;
|
|
3896
|
+
for (let i = 0; i < flags.length; i++) {
|
|
3897
|
+
const flag = flags.charAt(i);
|
|
3898
|
+
if (!validFlags.includes(flag)) {
|
|
3899
|
+
this.raise(start, "Invalid regular expression flag");
|
|
3900
|
+
}
|
|
3901
|
+
if (flags.includes(flag, i + 1)) {
|
|
3902
|
+
this.raise(start, "Duplicate regular expression flag");
|
|
3903
|
+
}
|
|
3904
|
+
if (flag === "u") hasU = true;
|
|
3905
|
+
if (flag === "v") hasV = true;
|
|
3906
|
+
}
|
|
3907
|
+
if (ecmaVersion >= 15 && hasU && hasV) {
|
|
3908
|
+
this.raise(start, "Invalid regular expression flag");
|
|
3909
|
+
}
|
|
3910
|
+
|
|
3911
|
+
let value = null;
|
|
3912
|
+
try {
|
|
3913
|
+
value = new RegExp(pattern, flags);
|
|
3914
|
+
} catch (err) {
|
|
3915
|
+
// V8's verdict on the pattern, like validateRegExpPattern below
|
|
3916
|
+
this.raiseRecoverable(start, /** @type {Error} */ (err).message);
|
|
3917
|
+
}
|
|
3918
|
+
return this.finishToken(tokTypes.regexp, { pattern, flags, value });
|
|
3919
|
+
}
|
|
3920
|
+
|
|
3921
|
+
// ----- regexp validation (V8-backed, replaces acorn's JS revalidation) -----
|
|
3922
|
+
|
|
3923
|
+
/**
|
|
3924
|
+
* Acorn constructs the literal's `RegExp` value right after this hook, so
|
|
3925
|
+
* V8 validates every pattern anyway; acorn's own JS copy of
|
|
3926
|
+
* that validation costs several percent of parse time. Raise from V8's
|
|
3927
|
+
* verdict instead — invalid patterns still fail the module build, only
|
|
3928
|
+
* exotic engine-specific message texts may differ.
|
|
3929
|
+
* @param {{ start: number, source: string, flags: string }} state acorn regexp validation state
|
|
3930
|
+
* @this {ParserInternals}
|
|
3931
|
+
*/
|
|
3932
|
+
validateRegExpPattern(state) {
|
|
3933
|
+
try {
|
|
3934
|
+
// eslint-disable-next-line no-new
|
|
3935
|
+
new RegExp(state.source, state.flags);
|
|
3936
|
+
} catch (err) {
|
|
3937
|
+
this.raiseRecoverable(state.start, /** @type {Error} */ (err).message);
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
|
|
3941
|
+
// ----- scope tracking (Set-based, replaces acorn's array + indexOf) -----
|
|
3942
|
+
|
|
3943
|
+
/**
|
|
3944
|
+
* @param {number} flags scope flags
|
|
3945
|
+
* @this {ParserInternals}
|
|
3946
|
+
*/
|
|
3947
|
+
enterScope(flags) {
|
|
3948
|
+
this.scopeStack.push(new Scope(flags));
|
|
3949
|
+
}
|
|
3950
|
+
|
|
3951
|
+
/**
|
|
3952
|
+
* Set-backed replacement for acorn's `declareName` on Set-backed scopes.
|
|
3953
|
+
* @param {string} name declared name
|
|
3954
|
+
* @param {number} bindingType acorn BIND_* binding type
|
|
3955
|
+
* @param {number} pos source offset for redeclaration errors
|
|
3956
|
+
* @this {ParserInternals}
|
|
3957
|
+
*/
|
|
3958
|
+
declareName(name, bindingType, pos) {
|
|
3959
|
+
let redeclared = false;
|
|
3960
|
+
if (bindingType === BIND_LEXICAL) {
|
|
3961
|
+
const scope = this.currentScope();
|
|
3962
|
+
redeclared =
|
|
3963
|
+
(scope.lexical !== undefined && scope.lexical.has(name)) ||
|
|
3964
|
+
(scope.functions !== undefined && scope.functions.has(name)) ||
|
|
3965
|
+
(scope.var !== undefined && scope.var.has(name));
|
|
3966
|
+
if (scope.lexical === undefined) {
|
|
3967
|
+
scope.firstLexical = name;
|
|
3968
|
+
scope.lexical = new Set();
|
|
3969
|
+
}
|
|
3970
|
+
scope.lexical.add(name);
|
|
3971
|
+
if (this.inModule && scope.flags & SCOPE_TOP) {
|
|
3972
|
+
delete this.undefinedExports[name];
|
|
3973
|
+
}
|
|
3974
|
+
} else if (bindingType === /* BIND_SIMPLE_CATCH */ 4) {
|
|
3975
|
+
const scope = this.currentScope();
|
|
3976
|
+
if (scope.lexical === undefined) {
|
|
3977
|
+
scope.firstLexical = name;
|
|
3978
|
+
scope.lexical = new Set();
|
|
3979
|
+
}
|
|
3980
|
+
scope.lexical.add(name);
|
|
3981
|
+
} else if (bindingType === /* BIND_FUNCTION */ 3) {
|
|
3982
|
+
const scope = this.currentScope();
|
|
3983
|
+
redeclared = this.treatFunctionsAsVar
|
|
3984
|
+
? scope.lexical !== undefined && scope.lexical.has(name)
|
|
3985
|
+
: (scope.lexical !== undefined && scope.lexical.has(name)) ||
|
|
3986
|
+
(scope.var !== undefined && scope.var.has(name));
|
|
3987
|
+
(scope.functions || (scope.functions = new Set())).add(name);
|
|
3988
|
+
} else {
|
|
3989
|
+
for (let i = this.scopeStack.length - 1; i >= 0; --i) {
|
|
3990
|
+
const scope = this.scopeStack[i];
|
|
3991
|
+
if (
|
|
3992
|
+
(scope.lexical !== undefined &&
|
|
3993
|
+
scope.lexical.has(name) &&
|
|
3994
|
+
!(
|
|
3995
|
+
scope.flags & SCOPE_SIMPLE_CATCH && scope.firstLexical === name
|
|
3996
|
+
)) ||
|
|
3997
|
+
// lazy-Set check first: `functions` is undefined for almost all
|
|
3998
|
+
// scopes, and the method call walks no state worth paying for then
|
|
3999
|
+
(scope.functions !== undefined &&
|
|
4000
|
+
!this.treatFunctionsAsVarInScope(scope) &&
|
|
4001
|
+
scope.functions.has(name))
|
|
4002
|
+
) {
|
|
4003
|
+
redeclared = true;
|
|
4004
|
+
break;
|
|
4005
|
+
}
|
|
4006
|
+
(scope.var || (scope.var = new Set())).add(name);
|
|
4007
|
+
if (this.inModule && scope.flags & SCOPE_TOP) {
|
|
4008
|
+
delete this.undefinedExports[name];
|
|
4009
|
+
}
|
|
4010
|
+
if (scope.flags & SCOPE_VAR) break;
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
if (redeclared) {
|
|
4014
|
+
this.raiseRecoverable(
|
|
4015
|
+
pos,
|
|
4016
|
+
`Identifier '${name}' has already been declared`
|
|
4017
|
+
);
|
|
4018
|
+
}
|
|
4019
|
+
}
|
|
4020
|
+
|
|
4021
|
+
/**
|
|
4022
|
+
* Set-backed replacement for acorn's `checkLocalExport` on Set-backed scopes.
|
|
4023
|
+
* @param {Identifier} id exported identifier
|
|
4024
|
+
* @this {ParserInternals}
|
|
4025
|
+
*/
|
|
4026
|
+
checkLocalExport(id) {
|
|
4027
|
+
const topScope = this.scopeStack[0];
|
|
4028
|
+
if (
|
|
4029
|
+
!(topScope.lexical !== undefined && topScope.lexical.has(id.name)) &&
|
|
4030
|
+
!(topScope.var !== undefined && topScope.var.has(id.name))
|
|
4031
|
+
) {
|
|
4032
|
+
this.undefinedExports[id.name] = id;
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
|
|
4036
|
+
// ----- import attributes (`with { ... }` / legacy `assert { ... }`) -----
|
|
4037
|
+
|
|
4038
|
+
/**
|
|
4039
|
+
* @returns {ImportAttribute[]} import attributes
|
|
4040
|
+
* @this {ParserInternals}
|
|
4041
|
+
*/
|
|
4042
|
+
parseWithClause() {
|
|
4043
|
+
/** @type {ImportAttribute[] & { [LEGACY_ASSERT_ATTRIBUTES]?: boolean }} */
|
|
4044
|
+
const nodes = [];
|
|
4045
|
+
|
|
4046
|
+
const isAssertLegacy = this.value === "assert";
|
|
4047
|
+
|
|
4048
|
+
if (isAssertLegacy) {
|
|
4049
|
+
if (!this.eat(tokTypes.name)) {
|
|
4050
|
+
return nodes;
|
|
4051
|
+
}
|
|
4052
|
+
} else if (!this.eat(tokTypes._with)) {
|
|
4053
|
+
return nodes;
|
|
4054
|
+
}
|
|
4055
|
+
|
|
4056
|
+
this.expect(tokTypes.braceL);
|
|
4057
|
+
|
|
4058
|
+
/** @type {Record<string, boolean>} */
|
|
4059
|
+
const attributeKeys = {};
|
|
4060
|
+
let first = true;
|
|
4061
|
+
|
|
4062
|
+
while (!this.eat(tokTypes.braceR)) {
|
|
4063
|
+
if (!first) {
|
|
4064
|
+
this.expect(tokTypes.comma);
|
|
4065
|
+
if (this.afterTrailingComma(tokTypes.braceR)) {
|
|
4066
|
+
break;
|
|
4067
|
+
}
|
|
4068
|
+
} else {
|
|
4069
|
+
first = false;
|
|
4070
|
+
}
|
|
4071
|
+
|
|
4072
|
+
const attr = this.parseImportAttribute();
|
|
4073
|
+
const keyName =
|
|
4074
|
+
/** @type {string} */
|
|
4075
|
+
(attr.key.type === "Identifier" ? attr.key.name : attr.key.value);
|
|
4076
|
+
|
|
4077
|
+
if (Object.prototype.hasOwnProperty.call(attributeKeys, keyName)) {
|
|
4078
|
+
this.raiseRecoverable(
|
|
4079
|
+
attr.key.start,
|
|
4080
|
+
`Duplicate attribute key '${keyName}'`
|
|
4081
|
+
);
|
|
4082
|
+
}
|
|
4083
|
+
|
|
4084
|
+
attributeKeys[keyName] = true;
|
|
4085
|
+
nodes.push(attr);
|
|
4086
|
+
}
|
|
4087
|
+
|
|
4088
|
+
if (isAssertLegacy) {
|
|
4089
|
+
nodes[LEGACY_ASSERT_ATTRIBUTES] = true;
|
|
4090
|
+
}
|
|
4091
|
+
|
|
4092
|
+
return nodes;
|
|
4093
|
+
}
|
|
4094
|
+
|
|
4095
|
+
// ----- import phases (`import defer/source`, `import.defer/source()`) -----
|
|
4096
|
+
|
|
4097
|
+
/**
|
|
4098
|
+
* @param {Node & { phase?: ImportPhase }} node import declaration node
|
|
4099
|
+
* @returns {Node} finished node
|
|
4100
|
+
* @this {ParserInternals}
|
|
4101
|
+
*/
|
|
4102
|
+
parseImport(node) {
|
|
4103
|
+
this._moduleSyntaxSeen = true;
|
|
4104
|
+
this._importPhase = null;
|
|
4105
|
+
const result = base.parseImport.call(this, node);
|
|
4106
|
+
if (this._importPhase) node.phase = this._importPhase;
|
|
4107
|
+
return result;
|
|
4108
|
+
}
|
|
4109
|
+
|
|
4110
|
+
/**
|
|
4111
|
+
* Owned `parseExport` only to flag module syntax for the auto-fallback guard;
|
|
4112
|
+
* parsing itself delegates to acorn.
|
|
4113
|
+
* @param {Node} node started export node
|
|
4114
|
+
* @param {unknown} exports export-name tracking object
|
|
4115
|
+
* @returns {Node} export declaration
|
|
4116
|
+
* @this {ParserInternals}
|
|
4117
|
+
*/
|
|
4118
|
+
parseExport(node, exports) {
|
|
4119
|
+
this._moduleSyntaxSeen = true;
|
|
4120
|
+
return base.parseExport.call(this, node, exports);
|
|
4121
|
+
}
|
|
4122
|
+
|
|
4123
|
+
/**
|
|
4124
|
+
* Owned `parseAwait` only to flag top-level await (module-only) for the
|
|
4125
|
+
* auto-fallback guard; await inside a function is not module syntax.
|
|
4126
|
+
* @param {boolean | string=} forInit for-loop init context flag
|
|
4127
|
+
* @returns {Expression} await expression
|
|
4128
|
+
* @this {ParserInternals}
|
|
4129
|
+
*/
|
|
4130
|
+
parseAwait(forInit) {
|
|
4131
|
+
if (this.inModule && this.currentVarScope().flags & SCOPE_TOP) {
|
|
4132
|
+
this._moduleSyntaxSeen = true;
|
|
4133
|
+
}
|
|
4134
|
+
return base.parseAwait.call(this, forInit);
|
|
4135
|
+
}
|
|
4136
|
+
|
|
4137
|
+
/**
|
|
4138
|
+
* @returns {AnyImportSpecifier[]} import specifiers
|
|
4139
|
+
* @this {ParserInternals}
|
|
4140
|
+
*/
|
|
4141
|
+
parseImportSpecifiers() {
|
|
4142
|
+
if (!this._importPhasesEnabled) {
|
|
4143
|
+
return base.parseImportSpecifiers.call(this);
|
|
4144
|
+
}
|
|
4145
|
+
|
|
4146
|
+
/** @type {ImportPhase | null} */
|
|
4147
|
+
const phase = this.isContextual("defer")
|
|
4148
|
+
? "defer"
|
|
4149
|
+
: this.isContextual("source")
|
|
4150
|
+
? "source"
|
|
4151
|
+
: null;
|
|
4152
|
+
if (!phase) return base.parseImportSpecifiers.call(this);
|
|
4153
|
+
|
|
4154
|
+
const phaseId = this.parseIdent();
|
|
4155
|
+
if (this.isContextual("from") || this.type === tokTypes.comma) {
|
|
4156
|
+
// `defer`/`source` was the default import name, not a phase modifier
|
|
4157
|
+
const defaultSpecifier =
|
|
4158
|
+
/** @type {ImportDefaultSpecifier} */
|
|
4159
|
+
(
|
|
4160
|
+
this.startNodeAt(
|
|
4161
|
+
phaseId.start,
|
|
4162
|
+
phaseId.loc ? phaseId.loc.start : undefined
|
|
4163
|
+
)
|
|
4164
|
+
);
|
|
4165
|
+
defaultSpecifier.local = phaseId;
|
|
4166
|
+
this.checkLValSimple(phaseId, BIND_LEXICAL);
|
|
4167
|
+
|
|
4168
|
+
/** @type {AnyImportSpecifier[]} */
|
|
4169
|
+
const nodes = [
|
|
4170
|
+
/** @type {ImportDefaultSpecifier} */
|
|
4171
|
+
(this.finishNode(defaultSpecifier, "ImportDefaultSpecifier"))
|
|
4172
|
+
];
|
|
4173
|
+
if (this.eat(tokTypes.comma)) {
|
|
4174
|
+
if (this.type !== tokTypes.star && this.type !== tokTypes.braceL) {
|
|
4175
|
+
this.unexpected();
|
|
4176
|
+
}
|
|
4177
|
+
nodes.push(...base.parseImportSpecifiers.call(this));
|
|
4178
|
+
}
|
|
4179
|
+
return nodes;
|
|
4180
|
+
}
|
|
4181
|
+
|
|
4182
|
+
this._importPhase = phase;
|
|
4183
|
+
|
|
4184
|
+
if (phase === "defer") {
|
|
4185
|
+
if (this.type !== tokTypes.star) {
|
|
4186
|
+
this.raiseRecoverable(
|
|
4187
|
+
phaseId.start,
|
|
4188
|
+
"'import defer' can only be used with namespace imports ('import defer * as identifierName from ...')."
|
|
4189
|
+
);
|
|
4190
|
+
}
|
|
4191
|
+
} else if (this.type !== tokTypes.name) {
|
|
4192
|
+
this.raiseRecoverable(
|
|
4193
|
+
phaseId.start,
|
|
4194
|
+
"'import source' can only be used with direct identifier specifier imports."
|
|
4195
|
+
);
|
|
4196
|
+
}
|
|
4197
|
+
|
|
4198
|
+
const specifiers = base.parseImportSpecifiers.call(this);
|
|
4199
|
+
|
|
4200
|
+
if (
|
|
4201
|
+
phase === "source" &&
|
|
4202
|
+
specifiers.some((s) => s.type !== "ImportDefaultSpecifier")
|
|
4203
|
+
) {
|
|
4204
|
+
this.raiseRecoverable(
|
|
4205
|
+
phaseId.start,
|
|
4206
|
+
"'import source' can only be used with direct identifier specifier imports ('import source identifierName from ...')."
|
|
4207
|
+
);
|
|
4208
|
+
}
|
|
4209
|
+
|
|
4210
|
+
return specifiers;
|
|
4211
|
+
}
|
|
4212
|
+
|
|
4213
|
+
/**
|
|
4214
|
+
* @param {boolean} forNew whether parsed as the operand of `new`
|
|
4215
|
+
* @returns {Expression} expression node
|
|
4216
|
+
* @this {ParserInternals}
|
|
4217
|
+
*/
|
|
4218
|
+
parseExprImport(forNew) {
|
|
4219
|
+
const node = base.parseExprImport.call(this, forNew);
|
|
4220
|
+
|
|
4221
|
+
if (
|
|
4222
|
+
this._importPhasesEnabled &&
|
|
4223
|
+
node.type === "MetaProperty" &&
|
|
4224
|
+
(node.property.name === "defer" || node.property.name === "source")
|
|
4225
|
+
) {
|
|
4226
|
+
if (this.type === tokTypes.parenL) {
|
|
4227
|
+
if (forNew) {
|
|
4228
|
+
// same guard acorn applies to `new import(...)`
|
|
4229
|
+
this.raise(node.start, "import call cannot be the target of `new`");
|
|
4230
|
+
}
|
|
4231
|
+
const dynImport =
|
|
4232
|
+
/** @type {ImportExpression & { phase?: ImportPhase }} */
|
|
4233
|
+
(
|
|
4234
|
+
this.parseDynamicImport(
|
|
4235
|
+
this.startNodeAt(
|
|
4236
|
+
node.start,
|
|
4237
|
+
node.loc ? node.loc.start : undefined
|
|
4238
|
+
)
|
|
4239
|
+
)
|
|
4240
|
+
);
|
|
4241
|
+
dynImport.phase = node.property.name;
|
|
4242
|
+
return dynImport;
|
|
4243
|
+
}
|
|
4244
|
+
this.raiseRecoverable(
|
|
4245
|
+
node.start,
|
|
4246
|
+
`'import.${node.property.name}' can only be used in a dynamic import.`
|
|
4247
|
+
);
|
|
4248
|
+
}
|
|
4249
|
+
|
|
4250
|
+
return node;
|
|
4251
|
+
}
|
|
4252
|
+
|
|
4253
|
+
/**
|
|
4254
|
+
* @param {Node & { property?: Identifier }} node started node with `meta` set to `import`
|
|
4255
|
+
* @returns {Expression} MetaProperty node
|
|
4256
|
+
* @this {ParserInternals}
|
|
4257
|
+
*/
|
|
4258
|
+
parseImportMeta(node) {
|
|
4259
|
+
if (!this._importPhasesEnabled) {
|
|
4260
|
+
// base only accepts `import.meta` here, which is module-only
|
|
4261
|
+
this._moduleSyntaxSeen = true;
|
|
4262
|
+
return base.parseImportMeta.call(this, node);
|
|
4263
|
+
}
|
|
4264
|
+
|
|
4265
|
+
this.next();
|
|
4266
|
+
|
|
4267
|
+
const containsEsc = this.containsEsc;
|
|
4268
|
+
const property = this.parseIdent(true);
|
|
4269
|
+
node.property = property;
|
|
4270
|
+
const { name } = property;
|
|
4271
|
+
|
|
4272
|
+
// only `import.meta` is module-only; `import.defer`/`.source` are dynamic
|
|
4273
|
+
if (name === "meta") this._moduleSyntaxSeen = true;
|
|
4274
|
+
|
|
4275
|
+
if (name !== "meta" && name !== "defer" && name !== "source") {
|
|
4276
|
+
this.raiseRecoverable(
|
|
4277
|
+
property.start,
|
|
4278
|
+
"The only valid meta property for import is 'import.meta'"
|
|
4279
|
+
);
|
|
4280
|
+
}
|
|
4281
|
+
if (containsEsc) {
|
|
4282
|
+
this.raiseRecoverable(
|
|
4283
|
+
node.start,
|
|
4284
|
+
`'import.${name}' must not contain escaped characters`
|
|
4285
|
+
);
|
|
4286
|
+
}
|
|
4287
|
+
if (
|
|
4288
|
+
name === "meta" &&
|
|
4289
|
+
this.options.sourceType !== "module" &&
|
|
4290
|
+
!this.options.allowImportExportEverywhere
|
|
4291
|
+
) {
|
|
4292
|
+
this.raiseRecoverable(
|
|
4293
|
+
node.start,
|
|
4294
|
+
"Cannot use 'import.meta' outside a module"
|
|
4295
|
+
);
|
|
4296
|
+
}
|
|
4297
|
+
|
|
4298
|
+
return /** @type {Expression} */ (this.finishNode(node, "MetaProperty"));
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
|
|
4302
|
+
/** @typedef {import("../Dependency").SourcePosition} SourcePosition */
|
|
4303
|
+
|
|
4304
|
+
/**
|
|
4305
|
+
* Whether a raw string literal contains a legacy octal (`\47`, `\0` followed by
|
|
4306
|
+
* a digit) or non-octal decimal (`\8`, `\9`) escape — all SyntaxErrors in
|
|
4307
|
+
* strict mode. Escaped backslashes (`\\`) and `\x` / `\u` escapes are skipped.
|
|
4308
|
+
* @param {string} raw raw string literal text, including quotes
|
|
4309
|
+
* @returns {boolean} true when a strict-forbidden escape is present
|
|
4310
|
+
*/
|
|
4311
|
+
const hasOctalEscape = (raw) => {
|
|
4312
|
+
for (let i = 0; i < raw.length; i++) {
|
|
4313
|
+
if (raw.charCodeAt(i) !== 92) continue;
|
|
4314
|
+
const next = raw.charCodeAt(i + 1);
|
|
4315
|
+
if (next === 92) {
|
|
4316
|
+
i++;
|
|
4317
|
+
continue;
|
|
4318
|
+
}
|
|
4319
|
+
// `\0` is a valid NUL escape unless a digit follows it.
|
|
4320
|
+
if (next === 48) {
|
|
4321
|
+
const after = raw.charCodeAt(i + 2);
|
|
4322
|
+
if (after >= 48 && after <= 57) return true;
|
|
4323
|
+
i++;
|
|
4324
|
+
continue;
|
|
4325
|
+
}
|
|
4326
|
+
if (next >= 49 && next <= 57) return true;
|
|
4327
|
+
}
|
|
4328
|
+
return false;
|
|
4329
|
+
};
|
|
4330
|
+
|
|
4331
|
+
// Location decoding for lazy-mode output: nodes carry only offsets (the parser
|
|
4332
|
+
// skips acorn's location tracking), so line/column are derived on demand from
|
|
4333
|
+
// these two helpers.
|
|
4334
|
+
|
|
4335
|
+
/**
|
|
4336
|
+
* Offset of each line's first character. Char-code scan matching acorn's
|
|
4337
|
+
* `lineBreak` semantics (CRLF is one break): a regex `exec` loop here
|
|
4338
|
+
* allocates a match array per line.
|
|
4339
|
+
* @param {string} source source code
|
|
4340
|
+
* @returns {number[]} line start offsets
|
|
4341
|
+
*/
|
|
4342
|
+
const buildLineStarts = (source) => {
|
|
4343
|
+
const len = source.length;
|
|
4344
|
+
const lineStarts = [0];
|
|
4345
|
+
for (let i = 0; i < len; i++) {
|
|
4346
|
+
const ch = source.charCodeAt(i);
|
|
4347
|
+
if (ch === 10) {
|
|
4348
|
+
lineStarts.push(i + 1);
|
|
4349
|
+
} else if (ch === 13) {
|
|
4350
|
+
if (source.charCodeAt(i + 1) === 10) i++;
|
|
4351
|
+
lineStarts.push(i + 1);
|
|
4352
|
+
} else if (ch === 0x2028 || ch === 0x2029) {
|
|
4353
|
+
lineStarts.push(i + 1);
|
|
4354
|
+
}
|
|
4355
|
+
}
|
|
4356
|
+
return lineStarts;
|
|
4357
|
+
};
|
|
4358
|
+
|
|
4359
|
+
/**
|
|
4360
|
+
* Binary search for the line containing the offset.
|
|
4361
|
+
* @param {number[]} lineStarts line start offsets
|
|
4362
|
+
* @param {number} offset source offset
|
|
4363
|
+
* @returns {SourcePosition} position (1-based line, 0-based column)
|
|
4364
|
+
*/
|
|
4365
|
+
const positionAt = (lineStarts, offset) => {
|
|
4366
|
+
let lo = 0;
|
|
4367
|
+
let hi = lineStarts.length - 1;
|
|
4368
|
+
while (lo < hi) {
|
|
4369
|
+
const mid = (lo + hi + 1) >>> 1;
|
|
4370
|
+
if (lineStarts[mid] <= offset) lo = mid;
|
|
4371
|
+
else hi = mid - 1;
|
|
4372
|
+
}
|
|
4373
|
+
return { line: lo + 1, column: offset - lineStarts[lo] };
|
|
4374
|
+
};
|
|
4375
|
+
|
|
4376
|
+
module.exports.LEGACY_ASSERT_ATTRIBUTES = LEGACY_ASSERT_ATTRIBUTES;
|
|
4377
|
+
module.exports.WebpackParser = WebpackParser;
|
|
4378
|
+
module.exports.buildLineStarts = buildLineStarts;
|
|
4379
|
+
module.exports.hasOctalEscape = hasOctalEscape;
|
|
4380
|
+
module.exports.positionAt = positionAt;
|