gowebbuild/vendor/github.com/evanw/esbuild/internal/config/config.go

584 lines
12 KiB
Go
Raw Normal View History

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
}