Add support for copying files and folders.

This commit is contained in:
2021-12-19 14:31:57 +01:00
parent 161cb79b88
commit 311339685c
450 changed files with 232338 additions and 3 deletions

View File

@ -0,0 +1,583 @@
package config
import (
"fmt"
"regexp"
"strings"
"sync"
"github.com/evanw/esbuild/internal/ast"
"github.com/evanw/esbuild/internal/compat"
"github.com/evanw/esbuild/internal/js_ast"
"github.com/evanw/esbuild/internal/logger"
)
type JSXOptions struct {
Factory JSXExpr
Fragment JSXExpr
Parse bool
Preserve bool
}
type JSXExpr struct {
Parts []string
Constant js_ast.E
}
type TSOptions struct {
Parse bool
NoAmbiguousLessThan bool
}
type Platform uint8
const (
PlatformBrowser Platform = iota
PlatformNode
PlatformNeutral
)
type StrictOptions struct {
// Loose: "class Foo { foo = 1 }" => "class Foo { constructor() { this.foo = 1; } }"
// Strict: "class Foo { foo = 1 }" => "class Foo { constructor() { __publicField(this, 'foo', 1); } }"
//
// The disadvantage of strictness here is code bloat and performance. The
// advantage is following the class field specification accurately. For
// example, loose mode will incorrectly trigger setter methods while strict
// mode won't.
ClassFields bool
}
type SourceMap uint8
const (
SourceMapNone SourceMap = iota
SourceMapInline
SourceMapLinkedWithComment
SourceMapExternalWithoutComment
SourceMapInlineAndExternal
)
type LegalComments uint8
const (
LegalCommentsInline LegalComments = iota
LegalCommentsNone
LegalCommentsEndOfFile
LegalCommentsLinkedWithComment
LegalCommentsExternalWithoutComment
)
func (lc LegalComments) HasExternalFile() bool {
return lc == LegalCommentsLinkedWithComment || lc == LegalCommentsExternalWithoutComment
}
type Loader int
const (
LoaderNone Loader = iota
LoaderJS
LoaderJSX
LoaderTS
LoaderTSNoAmbiguousLessThan // Used with ".mts" and ".cts"
LoaderTSX
LoaderJSON
LoaderText
LoaderBase64
LoaderDataURL
LoaderFile
LoaderBinary
LoaderCSS
LoaderDefault
)
func (loader Loader) IsTypeScript() bool {
switch loader {
case LoaderTS, LoaderTSNoAmbiguousLessThan, LoaderTSX:
return true
default:
return false
}
}
func (loader Loader) CanHaveSourceMap() bool {
switch loader {
case LoaderJS, LoaderJSX, LoaderTS, LoaderTSNoAmbiguousLessThan, LoaderTSX, LoaderCSS:
return true
default:
return false
}
}
type Format uint8
const (
// This is used when not bundling. It means to preserve whatever form the
// import or export was originally in. ES6 syntax stays ES6 syntax and
// CommonJS syntax stays CommonJS syntax.
FormatPreserve Format = iota
// IIFE stands for immediately-invoked function expression. That looks like
// this:
//
// (() => {
// ... bundled code ...
// })();
//
// If the optional GlobalName is configured, then we'll write out this:
//
// let globalName = (() => {
// ... bundled code ...
// return exports;
// })();
//
FormatIIFE
// The CommonJS format looks like this:
//
// ... bundled code ...
// module.exports = exports;
//
FormatCommonJS
// The ES module format looks like this:
//
// ... bundled code ...
// export {...};
//
FormatESModule
)
func (f Format) KeepES6ImportExportSyntax() bool {
return f == FormatPreserve || f == FormatESModule
}
func (f Format) String() string {
switch f {
case FormatIIFE:
return "iife"
case FormatCommonJS:
return "cjs"
case FormatESModule:
return "esm"
}
return ""
}
type StdinInfo struct {
Loader Loader
Contents string
SourceFile string
AbsResolveDir string
}
type WildcardPattern struct {
Prefix string
Suffix string
}
type ExternalModules struct {
NodeModules map[string]bool
AbsPaths map[string]bool
Patterns []WildcardPattern
}
type Mode uint8
const (
ModePassThrough Mode = iota
ModeConvertFormat
ModeBundle
)
type MaybeBool uint8
const (
Unspecified MaybeBool = iota
True
False
)
type Options struct {
Mode Mode
ModuleType js_ast.ModuleType
PreserveSymlinks bool
RemoveWhitespace bool
MinifyIdentifiers bool
MangleSyntax bool
ProfilerNames bool
CodeSplitting bool
WatchMode bool
AllowOverwrite bool
LegalComments LegalComments
// If true, make sure to generate a single file that can be written to stdout
WriteToStdout bool
OmitRuntimeForTests bool
UnusedImportsTS UnusedImportsTS
UseDefineForClassFields MaybeBool
ASCIIOnly bool
KeepNames bool
IgnoreDCEAnnotations bool
TreeShaking bool
Defines *ProcessedDefines
TS TSOptions
JSX JSXOptions
Platform Platform
TargetFromAPI TargetFromAPI
UnsupportedJSFeatures compat.JSFeature
UnsupportedCSSFeatures compat.CSSFeature
TSTarget *TSTarget
// This is the original information that was used to generate the
// unsupported feature sets above. It's used for error messages.
OriginalTargetEnv string
ExtensionOrder []string
MainFields []string
Conditions []string
AbsNodePaths []string // The "NODE_PATH" variable from Node.js
ExternalModules ExternalModules
AbsOutputFile string
AbsOutputDir string
AbsOutputBase string
OutputExtensionJS string
OutputExtensionCSS string
GlobalName []string
TsConfigOverride string
ExtensionToLoader map[string]Loader
OutputFormat Format
PublicPath string
InjectAbsPaths []string
InjectedDefines []InjectedDefine
InjectedFiles []InjectedFile
JSBanner string
JSFooter string
CSSBanner string
CSSFooter string
EntryPathTemplate []PathTemplate
ChunkPathTemplate []PathTemplate
AssetPathTemplate []PathTemplate
Plugins []Plugin
NeedsMetafile bool
SourceMap SourceMap
SourceRoot string
ExcludeSourcesContent bool
Stdin *StdinInfo
}
type TargetFromAPI uint8
const (
// In this state, the "target" field in "tsconfig.json" is respected
TargetWasUnconfigured TargetFromAPI = iota
// In this state, the "target" field in "tsconfig.json" is overridden
TargetWasConfigured
// In this state, "useDefineForClassFields" is true unless overridden
TargetWasConfiguredIncludingESNext
)
type UnusedImportsTS uint8
const (
// "import { unused } from 'foo'" => "" (TypeScript's default behavior)
UnusedImportsRemoveStmt UnusedImportsTS = iota
// "import { unused } from 'foo'" => "import 'foo'" ("importsNotUsedAsValues" != "remove")
UnusedImportsKeepStmtRemoveValues
// "import { unused } from 'foo'" => "import { unused } from 'foo'" ("preserveValueImports" == true)
UnusedImportsKeepValues
)
func UnusedImportsFromTsconfigValues(preserveImportsNotUsedAsValues bool, preserveValueImports bool) UnusedImportsTS {
if preserveValueImports {
return UnusedImportsKeepValues
}
if preserveImportsNotUsedAsValues {
return UnusedImportsKeepStmtRemoveValues
}
return UnusedImportsRemoveStmt
}
type TSTarget struct {
Source logger.Source
Range logger.Range
Target string
UnsupportedJSFeatures compat.JSFeature
}
type PathPlaceholder uint8
const (
NoPlaceholder PathPlaceholder = iota
// The relative path from the original parent directory to the configured
// "outbase" directory, or to the lowest common ancestor directory
DirPlaceholder
// The original name of the file, or the manual chunk name, or the name of
// the type of output file ("entry" or "chunk" or "asset")
NamePlaceholder
// A hash of the contents of this file, and the contents and output paths of
// all dependencies (except for their hash placeholders)
HashPlaceholder
// The original extension of the file, or the name of the output file
// (e.g. "css", "svg", "png")
ExtPlaceholder
)
type PathTemplate struct {
Data string
Placeholder PathPlaceholder
}
type PathPlaceholders struct {
Dir *string
Name *string
Hash *string
Ext *string
}
func (placeholders PathPlaceholders) Get(placeholder PathPlaceholder) *string {
switch placeholder {
case DirPlaceholder:
return placeholders.Dir
case NamePlaceholder:
return placeholders.Name
case HashPlaceholder:
return placeholders.Hash
case ExtPlaceholder:
return placeholders.Ext
}
return nil
}
func TemplateToString(template []PathTemplate) string {
if len(template) == 1 && template[0].Placeholder == NoPlaceholder {
// Avoid allocations in this case
return template[0].Data
}
sb := strings.Builder{}
for _, part := range template {
sb.WriteString(part.Data)
switch part.Placeholder {
case DirPlaceholder:
sb.WriteString("[dir]")
case NamePlaceholder:
sb.WriteString("[name]")
case HashPlaceholder:
sb.WriteString("[hash]")
case ExtPlaceholder:
sb.WriteString("[ext]")
}
}
return sb.String()
}
func HasPlaceholder(template []PathTemplate, placeholder PathPlaceholder) bool {
for _, part := range template {
if part.Placeholder == placeholder {
return true
}
}
return false
}
func SubstituteTemplate(template []PathTemplate, placeholders PathPlaceholders) []PathTemplate {
// Don't allocate if no substitution is possible and the template is already minimal
shouldSubstitute := false
for i, part := range template {
if placeholders.Get(part.Placeholder) != nil || (part.Placeholder == NoPlaceholder && i+1 < len(template)) {
shouldSubstitute = true
break
}
}
if !shouldSubstitute {
return template
}
// Otherwise, substitute and merge as appropriate
result := make([]PathTemplate, 0, len(template))
for _, part := range template {
if sub := placeholders.Get(part.Placeholder); sub != nil {
part.Data += *sub
part.Placeholder = NoPlaceholder
}
if last := len(result) - 1; last >= 0 && result[last].Placeholder == NoPlaceholder {
last := &result[last]
last.Data += part.Data
last.Placeholder = part.Placeholder
} else {
result = append(result, part)
}
}
return result
}
func ShouldCallRuntimeRequire(mode Mode, outputFormat Format) bool {
return mode == ModeBundle && outputFormat != FormatCommonJS
}
type InjectedDefine struct {
Source logger.Source
Data js_ast.E
Name string
}
type InjectedFile struct {
Source logger.Source
Exports []InjectableExport
DefineName string
}
type InjectableExport struct {
Alias string
Loc logger.Loc
}
var filterMutex sync.Mutex
var filterCache map[string]*regexp.Regexp
func compileFilter(filter string) (result *regexp.Regexp) {
if filter == "" {
// Must provide a filter
return nil
}
ok := false
// Cache hit?
(func() {
filterMutex.Lock()
defer filterMutex.Unlock()
if filterCache != nil {
result, ok = filterCache[filter]
}
})()
if ok {
return
}
// Cache miss
result, err := regexp.Compile(filter)
if err != nil {
return nil
}
// Cache for next time
filterMutex.Lock()
defer filterMutex.Unlock()
if filterCache == nil {
filterCache = make(map[string]*regexp.Regexp)
}
filterCache[filter] = result
return
}
func CompileFilterForPlugin(pluginName string, kind string, filter string) (*regexp.Regexp, error) {
if filter == "" {
return nil, fmt.Errorf("[%s] %q is missing a filter", pluginName, kind)
}
result := compileFilter(filter)
if result == nil {
return nil, fmt.Errorf("[%s] %q filter is not a valid Go regular expression: %q", pluginName, kind, filter)
}
return result, nil
}
func PluginAppliesToPath(path logger.Path, filter *regexp.Regexp, namespace string) bool {
return (namespace == "" || path.Namespace == namespace) && filter.MatchString(path.Text)
}
////////////////////////////////////////////////////////////////////////////////
// Plugin API
type Plugin struct {
Name string
OnStart []OnStart
OnResolve []OnResolve
OnLoad []OnLoad
}
type OnStart struct {
Name string
Callback func() OnStartResult
}
type OnStartResult struct {
Msgs []logger.Msg
ThrownError error
}
type OnResolve struct {
Name string
Filter *regexp.Regexp
Namespace string
Callback func(OnResolveArgs) OnResolveResult
}
type OnResolveArgs struct {
Path string
Importer logger.Path
ResolveDir string
Kind ast.ImportKind
PluginData interface{}
}
type OnResolveResult struct {
PluginName string
Path logger.Path
External bool
IsSideEffectFree bool
PluginData interface{}
Msgs []logger.Msg
ThrownError error
AbsWatchFiles []string
AbsWatchDirs []string
}
type OnLoad struct {
Name string
Filter *regexp.Regexp
Namespace string
Callback func(OnLoadArgs) OnLoadResult
}
type OnLoadArgs struct {
Path logger.Path
PluginData interface{}
}
type OnLoadResult struct {
PluginName string
Contents *string
AbsResolveDir string
Loader Loader
PluginData interface{}
Msgs []logger.Msg
ThrownError error
AbsWatchFiles []string
AbsWatchDirs []string
}

View File

@ -0,0 +1,969 @@
package config
import (
"math"
"strings"
"sync"
"github.com/evanw/esbuild/internal/js_ast"
"github.com/evanw/esbuild/internal/logger"
)
var processedGlobalsMutex sync.Mutex
var processedGlobals *ProcessedDefines
// If something is in this list, then a direct identifier expression or property
// access chain matching this will be assumed to have no side effects and will
// be removed.
//
// This also means code is allowed to be reordered past things in this list. For
// example, if "console.log" is in this list, permitting reordering allows for
// "if (a) console.log(b); else console.log(c)" to be reordered and transformed
// into "console.log(a ? b : c)". Notice that "a" and "console.log" are in a
// different order, which can only happen if evaluating the "console.log"
// property access can be assumed to not change the value of "a".
//
// Note that membership in this list says nothing about whether calling any of
// these functions has any side effects. It only says something about
// referencing these function without calling them.
var knownGlobals = [][]string{
// These global identifiers should exist in all JavaScript environments. This
// deliberately omits "NaN", "Infinity", and "undefined" because these are
// treated as automatically-inlined constants instead of identifiers.
{"Array"},
{"Boolean"},
{"Function"},
{"Math"},
{"Number"},
{"Object"},
{"RegExp"},
{"String"},
// Object: Static methods
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#Static_methods
{"Object", "assign"},
{"Object", "create"},
{"Object", "defineProperties"},
{"Object", "defineProperty"},
{"Object", "entries"},
{"Object", "freeze"},
{"Object", "fromEntries"},
{"Object", "getOwnPropertyDescriptor"},
{"Object", "getOwnPropertyDescriptors"},
{"Object", "getOwnPropertyNames"},
{"Object", "getOwnPropertySymbols"},
{"Object", "getPrototypeOf"},
{"Object", "is"},
{"Object", "isExtensible"},
{"Object", "isFrozen"},
{"Object", "isSealed"},
{"Object", "keys"},
{"Object", "preventExtensions"},
{"Object", "seal"},
{"Object", "setPrototypeOf"},
{"Object", "values"},
// Object: Instance methods
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#Instance_methods
{"Object", "prototype", "__defineGetter__"},
{"Object", "prototype", "__defineSetter__"},
{"Object", "prototype", "__lookupGetter__"},
{"Object", "prototype", "__lookupSetter__"},
{"Object", "prototype", "hasOwnProperty"},
{"Object", "prototype", "isPrototypeOf"},
{"Object", "prototype", "propertyIsEnumerable"},
{"Object", "prototype", "toLocaleString"},
{"Object", "prototype", "toString"},
{"Object", "prototype", "unwatch"},
{"Object", "prototype", "valueOf"},
{"Object", "prototype", "watch"},
// Math: Static properties
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math#Static_properties
{"Math", "E"},
{"Math", "LN10"},
{"Math", "LN2"},
{"Math", "LOG10E"},
{"Math", "LOG2E"},
{"Math", "PI"},
{"Math", "SQRT1_2"},
{"Math", "SQRT2"},
// Math: Static methods
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math#Static_methods
{"Math", "abs"},
{"Math", "acos"},
{"Math", "acosh"},
{"Math", "asin"},
{"Math", "asinh"},
{"Math", "atan"},
{"Math", "atan2"},
{"Math", "atanh"},
{"Math", "cbrt"},
{"Math", "ceil"},
{"Math", "clz32"},
{"Math", "cos"},
{"Math", "cosh"},
{"Math", "exp"},
{"Math", "expm1"},
{"Math", "floor"},
{"Math", "fround"},
{"Math", "hypot"},
{"Math", "imul"},
{"Math", "log"},
{"Math", "log10"},
{"Math", "log1p"},
{"Math", "log2"},
{"Math", "max"},
{"Math", "min"},
{"Math", "pow"},
{"Math", "random"},
{"Math", "round"},
{"Math", "sign"},
{"Math", "sin"},
{"Math", "sinh"},
{"Math", "sqrt"},
{"Math", "tan"},
{"Math", "tanh"},
{"Math", "trunc"},
// Reflect: Static methods
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect#static_methods
{"Reflect", "apply"},
{"Reflect", "construct"},
{"Reflect", "defineProperty"},
{"Reflect", "deleteProperty"},
{"Reflect", "get"},
{"Reflect", "getOwnPropertyDescriptor"},
{"Reflect", "getPrototypeOf"},
{"Reflect", "has"},
{"Reflect", "isExtensible"},
{"Reflect", "ownKeys"},
{"Reflect", "preventExtensions"},
{"Reflect", "set"},
{"Reflect", "setPrototypeOf"},
// Other globals present in both the browser and node (except "eval" because
// it has special behavior)
{"AbortController"},
{"AbortSignal"},
{"AggregateError"},
{"ArrayBuffer"},
{"BigInt"},
{"DataView"},
{"Date"},
{"Error"},
{"EvalError"},
{"Event"},
{"EventTarget"},
{"Float32Array"},
{"Float64Array"},
{"Int16Array"},
{"Int32Array"},
{"Int8Array"},
{"Intl"},
{"JSON"},
{"Map"},
{"MessageChannel"},
{"MessageEvent"},
{"MessagePort"},
{"Promise"},
{"Proxy"},
{"RangeError"},
{"ReferenceError"},
{"Reflect"},
{"Set"},
{"Symbol"},
{"SyntaxError"},
{"TextDecoder"},
{"TextEncoder"},
{"TypeError"},
{"URIError"},
{"URL"},
{"URLSearchParams"},
{"Uint16Array"},
{"Uint32Array"},
{"Uint8Array"},
{"Uint8ClampedArray"},
{"WeakMap"},
{"WeakSet"},
{"WebAssembly"},
{"clearInterval"},
{"clearTimeout"},
{"console"},
{"decodeURI"},
{"decodeURIComponent"},
{"encodeURI"},
{"encodeURIComponent"},
{"escape"},
{"globalThis"},
{"isFinite"},
{"isNaN"},
{"parseFloat"},
{"parseInt"},
{"queueMicrotask"},
{"setInterval"},
{"setTimeout"},
{"unescape"},
// Console method references are assumed to have no side effects
// https://developer.mozilla.org/en-US/docs/Web/API/console
{"console", "assert"},
{"console", "clear"},
{"console", "count"},
{"console", "countReset"},
{"console", "debug"},
{"console", "dir"},
{"console", "dirxml"},
{"console", "error"},
{"console", "group"},
{"console", "groupCollapsed"},
{"console", "groupEnd"},
{"console", "info"},
{"console", "log"},
{"console", "table"},
{"console", "time"},
{"console", "timeEnd"},
{"console", "timeLog"},
{"console", "trace"},
{"console", "warn"},
// CSSOM APIs
{"CSSAnimation"},
{"CSSFontFaceRule"},
{"CSSImportRule"},
{"CSSKeyframeRule"},
{"CSSKeyframesRule"},
{"CSSMediaRule"},
{"CSSNamespaceRule"},
{"CSSPageRule"},
{"CSSRule"},
{"CSSRuleList"},
{"CSSStyleDeclaration"},
{"CSSStyleRule"},
{"CSSStyleSheet"},
{"CSSSupportsRule"},
{"CSSTransition"},
// SVG DOM
{"SVGAElement"},
{"SVGAngle"},
{"SVGAnimateElement"},
{"SVGAnimateMotionElement"},
{"SVGAnimateTransformElement"},
{"SVGAnimatedAngle"},
{"SVGAnimatedBoolean"},
{"SVGAnimatedEnumeration"},
{"SVGAnimatedInteger"},
{"SVGAnimatedLength"},
{"SVGAnimatedLengthList"},
{"SVGAnimatedNumber"},
{"SVGAnimatedNumberList"},
{"SVGAnimatedPreserveAspectRatio"},
{"SVGAnimatedRect"},
{"SVGAnimatedString"},
{"SVGAnimatedTransformList"},
{"SVGAnimationElement"},
{"SVGCircleElement"},
{"SVGClipPathElement"},
{"SVGComponentTransferFunctionElement"},
{"SVGDefsElement"},
{"SVGDescElement"},
{"SVGElement"},
{"SVGEllipseElement"},
{"SVGFEBlendElement"},
{"SVGFEColorMatrixElement"},
{"SVGFEComponentTransferElement"},
{"SVGFECompositeElement"},
{"SVGFEConvolveMatrixElement"},
{"SVGFEDiffuseLightingElement"},
{"SVGFEDisplacementMapElement"},
{"SVGFEDistantLightElement"},
{"SVGFEDropShadowElement"},
{"SVGFEFloodElement"},
{"SVGFEFuncAElement"},
{"SVGFEFuncBElement"},
{"SVGFEFuncGElement"},
{"SVGFEFuncRElement"},
{"SVGFEGaussianBlurElement"},
{"SVGFEImageElement"},
{"SVGFEMergeElement"},
{"SVGFEMergeNodeElement"},
{"SVGFEMorphologyElement"},
{"SVGFEOffsetElement"},
{"SVGFEPointLightElement"},
{"SVGFESpecularLightingElement"},
{"SVGFESpotLightElement"},
{"SVGFETileElement"},
{"SVGFETurbulenceElement"},
{"SVGFilterElement"},
{"SVGForeignObjectElement"},
{"SVGGElement"},
{"SVGGeometryElement"},
{"SVGGradientElement"},
{"SVGGraphicsElement"},
{"SVGImageElement"},
{"SVGLength"},
{"SVGLengthList"},
{"SVGLineElement"},
{"SVGLinearGradientElement"},
{"SVGMPathElement"},
{"SVGMarkerElement"},
{"SVGMaskElement"},
{"SVGMatrix"},
{"SVGMetadataElement"},
{"SVGNumber"},
{"SVGNumberList"},
{"SVGPathElement"},
{"SVGPatternElement"},
{"SVGPoint"},
{"SVGPointList"},
{"SVGPolygonElement"},
{"SVGPolylineElement"},
{"SVGPreserveAspectRatio"},
{"SVGRadialGradientElement"},
{"SVGRect"},
{"SVGRectElement"},
{"SVGSVGElement"},
{"SVGScriptElement"},
{"SVGSetElement"},
{"SVGStopElement"},
{"SVGStringList"},
{"SVGStyleElement"},
{"SVGSwitchElement"},
{"SVGSymbolElement"},
{"SVGTSpanElement"},
{"SVGTextContentElement"},
{"SVGTextElement"},
{"SVGTextPathElement"},
{"SVGTextPositioningElement"},
{"SVGTitleElement"},
{"SVGTransform"},
{"SVGTransformList"},
{"SVGUnitTypes"},
{"SVGUseElement"},
{"SVGViewElement"},
// Other browser APIs
//
// This list contains all globals present in modern versions of Chrome, Safari,
// and Firefox except for the following properties, since they have a side effect
// of triggering layout (https://gist.github.com/paulirish/5d52fb081b3570c81e3a):
//
// - scrollX
// - scrollY
// - innerWidth
// - innerHeight
// - pageXOffset
// - pageYOffset
//
// The following globals have also been removed since they sometimes throw an
// exception when accessed, which is a side effect (for more information see
// https://stackoverflow.com/a/33047477):
//
// - localStorage
// - sessionStorage
//
{"AnalyserNode"},
{"Animation"},
{"AnimationEffect"},
{"AnimationEvent"},
{"AnimationPlaybackEvent"},
{"AnimationTimeline"},
{"Attr"},
{"Audio"},
{"AudioBuffer"},
{"AudioBufferSourceNode"},
{"AudioDestinationNode"},
{"AudioListener"},
{"AudioNode"},
{"AudioParam"},
{"AudioProcessingEvent"},
{"AudioScheduledSourceNode"},
{"BarProp"},
{"BeforeUnloadEvent"},
{"BiquadFilterNode"},
{"Blob"},
{"BlobEvent"},
{"ByteLengthQueuingStrategy"},
{"CDATASection"},
{"CSS"},
{"CanvasGradient"},
{"CanvasPattern"},
{"CanvasRenderingContext2D"},
{"ChannelMergerNode"},
{"ChannelSplitterNode"},
{"CharacterData"},
{"ClipboardEvent"},
{"CloseEvent"},
{"Comment"},
{"CompositionEvent"},
{"ConvolverNode"},
{"CountQueuingStrategy"},
{"Crypto"},
{"CustomElementRegistry"},
{"CustomEvent"},
{"DOMException"},
{"DOMImplementation"},
{"DOMMatrix"},
{"DOMMatrixReadOnly"},
{"DOMParser"},
{"DOMPoint"},
{"DOMPointReadOnly"},
{"DOMQuad"},
{"DOMRect"},
{"DOMRectList"},
{"DOMRectReadOnly"},
{"DOMStringList"},
{"DOMStringMap"},
{"DOMTokenList"},
{"DataTransfer"},
{"DataTransferItem"},
{"DataTransferItemList"},
{"DelayNode"},
{"Document"},
{"DocumentFragment"},
{"DocumentTimeline"},
{"DocumentType"},
{"DragEvent"},
{"DynamicsCompressorNode"},
{"Element"},
{"ErrorEvent"},
{"EventSource"},
{"File"},
{"FileList"},
{"FileReader"},
{"FocusEvent"},
{"FontFace"},
{"FormData"},
{"GainNode"},
{"Gamepad"},
{"GamepadButton"},
{"GamepadEvent"},
{"Geolocation"},
{"GeolocationPositionError"},
{"HTMLAllCollection"},
{"HTMLAnchorElement"},
{"HTMLAreaElement"},
{"HTMLAudioElement"},
{"HTMLBRElement"},
{"HTMLBaseElement"},
{"HTMLBodyElement"},
{"HTMLButtonElement"},
{"HTMLCanvasElement"},
{"HTMLCollection"},
{"HTMLDListElement"},
{"HTMLDataElement"},
{"HTMLDataListElement"},
{"HTMLDetailsElement"},
{"HTMLDirectoryElement"},
{"HTMLDivElement"},
{"HTMLDocument"},
{"HTMLElement"},
{"HTMLEmbedElement"},
{"HTMLFieldSetElement"},
{"HTMLFontElement"},
{"HTMLFormControlsCollection"},
{"HTMLFormElement"},
{"HTMLFrameElement"},
{"HTMLFrameSetElement"},
{"HTMLHRElement"},
{"HTMLHeadElement"},
{"HTMLHeadingElement"},
{"HTMLHtmlElement"},
{"HTMLIFrameElement"},
{"HTMLImageElement"},
{"HTMLInputElement"},
{"HTMLLIElement"},
{"HTMLLabelElement"},
{"HTMLLegendElement"},
{"HTMLLinkElement"},
{"HTMLMapElement"},
{"HTMLMarqueeElement"},
{"HTMLMediaElement"},
{"HTMLMenuElement"},
{"HTMLMetaElement"},
{"HTMLMeterElement"},
{"HTMLModElement"},
{"HTMLOListElement"},
{"HTMLObjectElement"},
{"HTMLOptGroupElement"},
{"HTMLOptionElement"},
{"HTMLOptionsCollection"},
{"HTMLOutputElement"},
{"HTMLParagraphElement"},
{"HTMLParamElement"},
{"HTMLPictureElement"},
{"HTMLPreElement"},
{"HTMLProgressElement"},
{"HTMLQuoteElement"},
{"HTMLScriptElement"},
{"HTMLSelectElement"},
{"HTMLSlotElement"},
{"HTMLSourceElement"},
{"HTMLSpanElement"},
{"HTMLStyleElement"},
{"HTMLTableCaptionElement"},
{"HTMLTableCellElement"},
{"HTMLTableColElement"},
{"HTMLTableElement"},
{"HTMLTableRowElement"},
{"HTMLTableSectionElement"},
{"HTMLTemplateElement"},
{"HTMLTextAreaElement"},
{"HTMLTimeElement"},
{"HTMLTitleElement"},
{"HTMLTrackElement"},
{"HTMLUListElement"},
{"HTMLUnknownElement"},
{"HTMLVideoElement"},
{"HashChangeEvent"},
{"Headers"},
{"History"},
{"IDBCursor"},
{"IDBCursorWithValue"},
{"IDBDatabase"},
{"IDBFactory"},
{"IDBIndex"},
{"IDBKeyRange"},
{"IDBObjectStore"},
{"IDBOpenDBRequest"},
{"IDBRequest"},
{"IDBTransaction"},
{"IDBVersionChangeEvent"},
{"Image"},
{"ImageData"},
{"InputEvent"},
{"IntersectionObserver"},
{"IntersectionObserverEntry"},
{"KeyboardEvent"},
{"KeyframeEffect"},
{"Location"},
{"MediaCapabilities"},
{"MediaElementAudioSourceNode"},
{"MediaEncryptedEvent"},
{"MediaError"},
{"MediaList"},
{"MediaQueryList"},
{"MediaQueryListEvent"},
{"MediaRecorder"},
{"MediaSource"},
{"MediaStream"},
{"MediaStreamAudioDestinationNode"},
{"MediaStreamAudioSourceNode"},
{"MediaStreamTrack"},
{"MediaStreamTrackEvent"},
{"MimeType"},
{"MimeTypeArray"},
{"MouseEvent"},
{"MutationEvent"},
{"MutationObserver"},
{"MutationRecord"},
{"NamedNodeMap"},
{"Navigator"},
{"Node"},
{"NodeFilter"},
{"NodeIterator"},
{"NodeList"},
{"Notification"},
{"OfflineAudioCompletionEvent"},
{"Option"},
{"OscillatorNode"},
{"PageTransitionEvent"},
{"Path2D"},
{"Performance"},
{"PerformanceEntry"},
{"PerformanceMark"},
{"PerformanceMeasure"},
{"PerformanceNavigation"},
{"PerformanceObserver"},
{"PerformanceObserverEntryList"},
{"PerformanceResourceTiming"},
{"PerformanceTiming"},
{"PeriodicWave"},
{"Plugin"},
{"PluginArray"},
{"PointerEvent"},
{"PopStateEvent"},
{"ProcessingInstruction"},
{"ProgressEvent"},
{"PromiseRejectionEvent"},
{"RTCCertificate"},
{"RTCDTMFSender"},
{"RTCDTMFToneChangeEvent"},
{"RTCDataChannel"},
{"RTCDataChannelEvent"},
{"RTCIceCandidate"},
{"RTCPeerConnection"},
{"RTCPeerConnectionIceEvent"},
{"RTCRtpReceiver"},
{"RTCRtpSender"},
{"RTCRtpTransceiver"},
{"RTCSessionDescription"},
{"RTCStatsReport"},
{"RTCTrackEvent"},
{"RadioNodeList"},
{"Range"},
{"ReadableStream"},
{"Request"},
{"ResizeObserver"},
{"ResizeObserverEntry"},
{"Response"},
{"Screen"},
{"ScriptProcessorNode"},
{"SecurityPolicyViolationEvent"},
{"Selection"},
{"ShadowRoot"},
{"SourceBuffer"},
{"SourceBufferList"},
{"SpeechSynthesisEvent"},
{"SpeechSynthesisUtterance"},
{"StaticRange"},
{"Storage"},
{"StorageEvent"},
{"StyleSheet"},
{"StyleSheetList"},
{"Text"},
{"TextMetrics"},
{"TextTrack"},
{"TextTrackCue"},
{"TextTrackCueList"},
{"TextTrackList"},
{"TimeRanges"},
{"TrackEvent"},
{"TransitionEvent"},
{"TreeWalker"},
{"UIEvent"},
{"VTTCue"},
{"ValidityState"},
{"VisualViewport"},
{"WaveShaperNode"},
{"WebGLActiveInfo"},
{"WebGLBuffer"},
{"WebGLContextEvent"},
{"WebGLFramebuffer"},
{"WebGLProgram"},
{"WebGLQuery"},
{"WebGLRenderbuffer"},
{"WebGLRenderingContext"},
{"WebGLSampler"},
{"WebGLShader"},
{"WebGLShaderPrecisionFormat"},
{"WebGLSync"},
{"WebGLTexture"},
{"WebGLUniformLocation"},
{"WebKitCSSMatrix"},
{"WebSocket"},
{"WheelEvent"},
{"Window"},
{"Worker"},
{"XMLDocument"},
{"XMLHttpRequest"},
{"XMLHttpRequestEventTarget"},
{"XMLHttpRequestUpload"},
{"XMLSerializer"},
{"XPathEvaluator"},
{"XPathExpression"},
{"XPathResult"},
{"XSLTProcessor"},
{"alert"},
{"atob"},
{"blur"},
{"btoa"},
{"cancelAnimationFrame"},
{"captureEvents"},
{"close"},
{"closed"},
{"confirm"},
{"customElements"},
{"devicePixelRatio"},
{"document"},
{"event"},
{"fetch"},
{"find"},
{"focus"},
{"frameElement"},
{"frames"},
{"getComputedStyle"},
{"getSelection"},
{"history"},
{"indexedDB"},
{"isSecureContext"},
{"length"},
{"location"},
{"locationbar"},
{"matchMedia"},
{"menubar"},
{"moveBy"},
{"moveTo"},
{"name"},
{"navigator"},
{"onabort"},
{"onafterprint"},
{"onanimationend"},
{"onanimationiteration"},
{"onanimationstart"},
{"onbeforeprint"},
{"onbeforeunload"},
{"onblur"},
{"oncanplay"},
{"oncanplaythrough"},
{"onchange"},
{"onclick"},
{"oncontextmenu"},
{"oncuechange"},
{"ondblclick"},
{"ondrag"},
{"ondragend"},
{"ondragenter"},
{"ondragleave"},
{"ondragover"},
{"ondragstart"},
{"ondrop"},
{"ondurationchange"},
{"onemptied"},
{"onended"},
{"onerror"},
{"onfocus"},
{"ongotpointercapture"},
{"onhashchange"},
{"oninput"},
{"oninvalid"},
{"onkeydown"},
{"onkeypress"},
{"onkeyup"},
{"onlanguagechange"},
{"onload"},
{"onloadeddata"},
{"onloadedmetadata"},
{"onloadstart"},
{"onlostpointercapture"},
{"onmessage"},
{"onmousedown"},
{"onmouseenter"},
{"onmouseleave"},
{"onmousemove"},
{"onmouseout"},
{"onmouseover"},
{"onmouseup"},
{"onoffline"},
{"ononline"},
{"onpagehide"},
{"onpageshow"},
{"onpause"},
{"onplay"},
{"onplaying"},
{"onpointercancel"},
{"onpointerdown"},
{"onpointerenter"},
{"onpointerleave"},
{"onpointermove"},
{"onpointerout"},
{"onpointerover"},
{"onpointerup"},
{"onpopstate"},
{"onprogress"},
{"onratechange"},
{"onrejectionhandled"},
{"onreset"},
{"onresize"},
{"onscroll"},
{"onseeked"},
{"onseeking"},
{"onselect"},
{"onstalled"},
{"onstorage"},
{"onsubmit"},
{"onsuspend"},
{"ontimeupdate"},
{"ontoggle"},
{"ontransitioncancel"},
{"ontransitionend"},
{"ontransitionrun"},
{"ontransitionstart"},
{"onunhandledrejection"},
{"onunload"},
{"onvolumechange"},
{"onwaiting"},
{"onwebkitanimationend"},
{"onwebkitanimationiteration"},
{"onwebkitanimationstart"},
{"onwebkittransitionend"},
{"onwheel"},
{"open"},
{"opener"},
{"origin"},
{"outerHeight"},
{"outerWidth"},
{"parent"},
{"performance"},
{"personalbar"},
{"postMessage"},
{"print"},
{"prompt"},
{"releaseEvents"},
{"requestAnimationFrame"},
{"resizeBy"},
{"resizeTo"},
{"screen"},
{"screenLeft"},
{"screenTop"},
{"screenX"},
{"screenY"},
{"scroll"},
{"scrollBy"},
{"scrollTo"},
{"scrollbars"},
{"self"},
{"speechSynthesis"},
{"status"},
{"statusbar"},
{"stop"},
{"toolbar"},
{"top"},
{"webkitURL"},
{"window"},
}
type DefineArgs struct {
Loc logger.Loc
FindSymbol func(logger.Loc, string) js_ast.Ref
SymbolForDefine func(int) js_ast.Ref
}
type DefineFunc func(DefineArgs) js_ast.E
type DefineData struct {
DefineFunc DefineFunc
// True if accessing this value is known to not have any side effects. For
// example, a bare reference to "Object.create" can be removed because it
// does not have any observable side effects.
CanBeRemovedIfUnused bool
// True if a call to this value is known to not have any side effects. For
// example, a bare call to "Object()" can be removed because it does not
// have any observable side effects.
CallCanBeUnwrappedIfUnused bool
}
func mergeDefineData(old DefineData, new DefineData) DefineData {
if old.CanBeRemovedIfUnused {
new.CanBeRemovedIfUnused = true
}
if old.CallCanBeUnwrappedIfUnused {
new.CallCanBeUnwrappedIfUnused = true
}
return new
}
type DotDefine struct {
Parts []string
Data DefineData
}
type ProcessedDefines struct {
IdentifierDefines map[string]DefineData
DotDefines map[string][]DotDefine
}
// This transformation is expensive, so we only want to do it once. Make sure
// to only call processDefines() once per compilation. Unfortunately Golang
// doesn't have an efficient way to copy a map and the overhead of copying
// all of the properties into a new map once for every new parser noticeably
// slows down our benchmarks.
func ProcessDefines(userDefines map[string]DefineData) ProcessedDefines {
// Optimization: reuse known globals if there are no user-specified defines
hasUserDefines := len(userDefines) != 0
if !hasUserDefines {
processedGlobalsMutex.Lock()
if processedGlobals != nil {
defer processedGlobalsMutex.Unlock()
return *processedGlobals
}
processedGlobalsMutex.Unlock()
}
result := ProcessedDefines{
IdentifierDefines: make(map[string]DefineData),
DotDefines: make(map[string][]DotDefine),
}
// Mark these property accesses as free of side effects. That means they can
// be removed if their result is unused. We can't just remove all unused
// property accesses since property accesses can have side effects. For
// example, the property access "a.b.c" has the side effect of throwing an
// exception if "a.b" is undefined.
for _, parts := range knownGlobals {
tail := parts[len(parts)-1]
if len(parts) == 1 {
result.IdentifierDefines[tail] = DefineData{CanBeRemovedIfUnused: true}
} else {
result.DotDefines[tail] = append(result.DotDefines[tail], DotDefine{Parts: parts, Data: DefineData{CanBeRemovedIfUnused: true}})
}
}
// Swap in certain literal values because those can be constant folded
result.IdentifierDefines["undefined"] = DefineData{
DefineFunc: func(DefineArgs) js_ast.E { return js_ast.EUndefinedShared },
}
result.IdentifierDefines["NaN"] = DefineData{
DefineFunc: func(DefineArgs) js_ast.E { return &js_ast.ENumber{Value: math.NaN()} },
}
result.IdentifierDefines["Infinity"] = DefineData{
DefineFunc: func(DefineArgs) js_ast.E { return &js_ast.ENumber{Value: math.Inf(1)} },
}
// Then copy the user-specified defines in afterwards, which will overwrite
// any known globals above.
for key, data := range userDefines {
parts := strings.Split(key, ".")
// Identifier defines are special-cased
if len(parts) == 1 {
result.IdentifierDefines[key] = mergeDefineData(result.IdentifierDefines[key], data)
continue
}
tail := parts[len(parts)-1]
dotDefines := result.DotDefines[tail]
found := false
// Try to merge with existing dot defines first
for i, define := range dotDefines {
if arePartsEqual(parts, define.Parts) {
define := &dotDefines[i]
define.Data = mergeDefineData(define.Data, data)
found = true
break
}
}
if !found {
dotDefines = append(dotDefines, DotDefine{Parts: parts, Data: data})
}
result.DotDefines[tail] = dotDefines
}
// Potentially cache the result for next time
if !hasUserDefines {
processedGlobalsMutex.Lock()
defer processedGlobalsMutex.Unlock()
if processedGlobals == nil {
processedGlobals = &result
}
}
return result
}
func arePartsEqual(a []string, b []string) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}