diff --git a/cmd/esbuild/main.go b/cmd/esbuild/main.go index 74ced83ae61..4f950beff2e 100644 --- a/cmd/esbuild/main.go +++ b/cmd/esbuild/main.go @@ -93,6 +93,8 @@ var helpText = func(colors logger.Colors) string { eof | linked | external, default eof when bundling and inline otherwise) --line-limit=... Lines longer than this will be wrap onto a new line + --local-css-prefix=... Add a prefix to local-css classes if + minify-identifiers is enabled --log-level=... Disable logging (verbose | debug | info | warning | error | silent, default info) --log-limit=... Maximum message count or 0 to disable (default 6) diff --git a/internal/config/config.go b/internal/config/config.go index 615d6882eaa..446c9ef773f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -474,6 +474,7 @@ type Options struct { TreeShaking bool DropDebugger bool MangleQuoted bool + LocalCSSPrefix string Platform Platform OutputFormat Format NeedsMetafile bool diff --git a/internal/linker/linker.go b/internal/linker/linker.go index 219fe8e614b..4e88729ad11 100644 --- a/internal/linker/linker.go +++ b/internal/linker/linker.go @@ -493,10 +493,10 @@ func (c *linkerContext) mangleLocalCSS(usedLocalNames map[string]bool) { nextName := 0 for _, symbolCount := range sorted { - name := minifier.NumberToMinifiedName(nextName) + name := c.options.LocalCSSPrefix + minifier.NumberToMinifiedName(nextName) for globalNames[name] || usedLocalNames[name] { nextName++ - name = minifier.NumberToMinifiedName(nextName) + name = c.options.LocalCSSPrefix + minifier.NumberToMinifiedName(nextName) } // Turn this local name into a global one diff --git a/lib/shared/common.ts b/lib/shared/common.ts index c323016ba55..3ff3ffe48d6 100644 --- a/lib/shared/common.ts +++ b/lib/shared/common.ts @@ -277,6 +277,7 @@ function flagsForBuildOptions( let write = getFlag(options, keys, 'write', mustBeBoolean) ?? writeDefault; // Default to true if not specified let allowOverwrite = getFlag(options, keys, 'allowOverwrite', mustBeBoolean) let mangleCache = getFlag(options, keys, 'mangleCache', mustBeObject) + let localCSSPrefix = getFlag(options, keys, 'localCSSPrefix', mustBeString) keys.plugins = true; // "plugins" has already been read earlier checkForInvalidFlags(options, keys, `in ${callName}() call`) @@ -304,6 +305,7 @@ function flagsForBuildOptions( if (entryNames) flags.push(`--entry-names=${entryNames}`) if (chunkNames) flags.push(`--chunk-names=${chunkNames}`) if (assetNames) flags.push(`--asset-names=${assetNames}`) + if (localCSSPrefix) flags.push(`--local-css-prefix=${localCSSPrefix}`) if (mainFields) { let values: string[] = [] for (let value of mainFields) { diff --git a/lib/shared/types.ts b/lib/shared/types.ts index c7053070fe0..e0bccb750fb 100644 --- a/lib/shared/types.ts +++ b/lib/shared/types.ts @@ -168,6 +168,8 @@ export interface BuildOptions extends CommonOptions { absWorkingDir?: string /** Documentation: https://esbuild.github.io/api/#node-paths */ nodePaths?: string[]; // The "NODE_PATH" variable from Node.js + /** Documentation: https://esbuild.github.io/api/#local-css-prefix */ + localCSSPrefix?: string; } export interface StdinOptions { diff --git a/pkg/api/api.go b/pkg/api/api.go index 08a597ec2a0..08231893eaa 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -296,6 +296,7 @@ type BuildOptions struct { TreeShaking TreeShaking // Documentation: https://esbuild.github.io/api/#tree-shaking IgnoreAnnotations bool // Documentation: https://esbuild.github.io/api/#ignore-annotations LegalComments LegalComments // Documentation: https://esbuild.github.io/api/#legal-comments + LocalCSSPrefix string // Documentation: https://esbuild.github.io/api/#local-css-prefix JSX JSX // Documentation: https://esbuild.github.io/api/#jsx-mode JSXFactory string // Documentation: https://esbuild.github.io/api/#jsx-factory diff --git a/pkg/api/api_impl.go b/pkg/api/api_impl.go index 7c737779e0f..a6e6a45d4f2 100644 --- a/pkg/api/api_impl.go +++ b/pkg/api/api_impl.go @@ -28,6 +28,7 @@ import ( "github.com/evanw/esbuild/internal/compat" "github.com/evanw/esbuild/internal/config" "github.com/evanw/esbuild/internal/css_ast" + "github.com/evanw/esbuild/internal/css_lexer" "github.com/evanw/esbuild/internal/fs" "github.com/evanw/esbuild/internal/graph" "github.com/evanw/esbuild/internal/helpers" @@ -524,6 +525,23 @@ func validateJSXExpr(log logger.Log, text string, name string) config.DefineExpr return config.DefineExpr{} } +func validateCSSIdentifier(log logger.Log, ident string) string { + for i, c := range ident { + if i == 0 { + if !css_lexer.IsNameStart(c) { + log.AddError(nil, logger.Range{}, fmt.Sprintf("Invalid CSS prefix: %q", ident)) + return "" + } + } else { + if !css_lexer.IsNameContinue(c) { + log.AddError(nil, logger.Range{}, fmt.Sprintf("Invalid CSS prefix: %q", ident)) + return "" + } + } + } + return ident +} + func validateDefines( log logger.Log, defines map[string]string, @@ -1257,6 +1275,7 @@ func validateBuildOptions( MangleProps: validateRegex(log, "mangle props", buildOpts.MangleProps), ReserveProps: validateRegex(log, "reserve props", buildOpts.ReserveProps), MangleQuoted: buildOpts.MangleQuoted == MangleQuotedTrue, + LocalCSSPrefix: validateCSSIdentifier(log, buildOpts.LocalCSSPrefix), DropLabels: append([]string{}, buildOpts.DropLabels...), DropDebugger: (buildOpts.Drop & DropDebugger) != 0, AllowOverwrite: buildOpts.AllowOverwrite, diff --git a/pkg/cli/cli_impl.go b/pkg/cli/cli_impl.go index 9a617069126..dead52b55f0 100644 --- a/pkg/cli/cli_impl.go +++ b/pkg/cli/cli_impl.go @@ -204,6 +204,10 @@ func parseOptionsImpl( value := arg[len("--mangle-cache="):] extras.mangleCache = &value + case strings.HasPrefix(arg, "--local-css-prefix=") && buildOpts != nil: + value := arg[len("--local-css-prefix="):] + buildOpts.LocalCSSPrefix = value + case strings.HasPrefix(arg, "--drop:"): value := arg[len("--drop:"):] switch value {