fix #1152: add the "--allow-overwrite" flag

This commit is contained in:
Evan Wallace
2021-04-22 14:01:20 -07:00
parent 4a1ce9a47f
commit 610d49bc96
10 changed files with 62 additions and 15 deletions

View File

@@ -18,6 +18,10 @@
[The TC39 proposal for this feature](https://github.com/tc39/proposal-private-fields-in-in) is currently at stage 3 but has already been shipped in Chrome 91 and has also landed in Firefox. It seems reasonably inevitable given that it's already shipping and that it's a very simple feature, so it seems appropriate to add this feature to esbuild.
* Add the `--allow-overwrite` flag ([#1152](https://github.com/evanw/esbuild/issues/1152))
This is a new flag that allows output files to overwrite input files. It's not enabled by default because doing so means overwriting your source code, which can lead to data loss if your code is not checked in. But supporting this makes certain workflows easier by avoiding the need for a temporary directory so doing this is now supported.
## 0.11.12
* Fix a bug where `-0` and `0` were collapsed to the same value ([#1159](https://github.com/evanw/esbuild/issues/1159))

View File

@@ -46,6 +46,7 @@ var helpText = func(colors logger.Colors) string {
--watch Watch mode: rebuild on file system changes
` + colors.Bold + `Advanced options:` + colors.Reset + `
--allow-overwrite Allow output files to overwrite input files
--asset-names=... Path template to use for "file" loader files
(default "[name]-[hash]")
--banner:T=... Text to be prepended to each output file of type T

View File

@@ -1900,18 +1900,22 @@ func (b *Bundle) Compile(log logger.Log, options config.Options, timer *helpers.
if !options.WriteToStdout {
// Make sure an output file never overwrites an input file
sourceAbsPaths := make(map[string]uint32)
for _, sourceIndex := range allReachableFiles {
keyPath := b.files[sourceIndex].inputFile.Source.KeyPath
if keyPath.Namespace == "file" {
lowerAbsPath := lowerCaseAbsPathForWindows(keyPath.Text)
sourceAbsPaths[lowerAbsPath] = sourceIndex
if !options.AllowOverwrite {
sourceAbsPaths := make(map[string]uint32)
for _, sourceIndex := range allReachableFiles {
keyPath := b.files[sourceIndex].inputFile.Source.KeyPath
if keyPath.Namespace == "file" {
lowerAbsPath := lowerCaseAbsPathForWindows(keyPath.Text)
sourceAbsPaths[lowerAbsPath] = sourceIndex
}
}
}
for _, outputFile := range outputFiles {
lowerAbsPath := lowerCaseAbsPathForWindows(outputFile.AbsPath)
if sourceIndex, ok := sourceAbsPaths[lowerAbsPath]; ok {
log.AddError(nil, logger.Loc{}, "Refusing to overwrite input file: "+b.files[sourceIndex].inputFile.Source.PrettyPath)
for _, outputFile := range outputFiles {
lowerAbsPath := lowerCaseAbsPathForWindows(outputFile.AbsPath)
if sourceIndex, ok := sourceAbsPaths[lowerAbsPath]; ok {
log.AddError(nil, logger.Loc{},
fmt.Sprintf("Refusing to overwrite input file %q without permission (enable \"allow overwrite\" to proceed)",
b.files[sourceIndex].inputFile.Source.PrettyPath))
}
}
}

View File

@@ -185,6 +185,7 @@ type Options struct {
MangleSyntax bool
CodeSplitting bool
WatchMode bool
AllowOverwrite bool
// Setting this to true disables warnings about code that is very likely to
// be a bug. This is used to ignore issues inside "node_modules" directories.

View File

@@ -197,12 +197,14 @@ function flagsForBuildOptions(
let absWorkingDir = getFlag(options, keys, 'absWorkingDir', mustBeString);
let stdin = getFlag(options, keys, 'stdin', mustBeObject);
let write = getFlag(options, keys, 'write', mustBeBoolean) ?? writeDefault; // Default to true if not specified
let allowOverwrite = getFlag(options, keys, 'allowOverwrite', mustBeBoolean);
let incremental = getFlag(options, keys, 'incremental', mustBeBoolean) === true;
keys.plugins = true; // "plugins" has already been read earlier
checkForInvalidFlags(options, keys, `in ${callName}() call`);
if (sourcemap) flags.push(`--sourcemap${sourcemap === true ? '' : `=${sourcemap}`}`);
if (bundle) flags.push('--bundle');
if (allowOverwrite) flags.push('--allow-overwrite');
if (watch) {
flags.push('--watch');
if (typeof watch === 'boolean') {

View File

@@ -47,6 +47,7 @@ export interface BuildOptions extends CommonOptions {
mainFields?: string[];
conditions?: string[];
write?: boolean;
allowOverwrite?: boolean;
tsconfig?: string;
outExtension?: { [ext: string]: string };
publicPath?: string;

View File

@@ -276,10 +276,11 @@ type BuildOptions struct {
EntryPoints []string
EntryPointsAdvanced []EntryPoint
Stdin *StdinOptions
Write bool
Incremental bool
Plugins []Plugin
Stdin *StdinOptions
Write bool
AllowOverwrite bool
Incremental bool
Plugins []Plugin
Watch *WatchMode
}

View File

@@ -787,6 +787,7 @@ func rebuildImpl(
MangleSyntax: buildOpts.MinifySyntax,
RemoveWhitespace: buildOpts.MinifyWhitespace,
MinifyIdentifiers: buildOpts.MinifyIdentifiers,
AllowOverwrite: buildOpts.AllowOverwrite,
ASCIIOnly: validateASCIIOnly(buildOpts.Charset),
IgnoreDCEAnnotations: validateIgnoreDCEAnnotations(buildOpts.TreeShaking),
GlobalName: validateGlobalName(log, buildOpts.GlobalName),

View File

@@ -57,6 +57,9 @@ func parseOptionsImpl(
case arg == "--splitting" && buildOpts != nil:
buildOpts.Splitting = true
case arg == "--allow-overwrite" && buildOpts != nil:
buildOpts.AllowOverwrite = true
case arg == "--watch" && buildOpts != nil:
buildOpts.Watch = &api.WatchMode{}

View File

@@ -1285,6 +1285,35 @@ body {
assert.strictEqual(meta.outputs[makePath(output + '.map')].bytes, value.outputFiles[0].contents.length)
},
async allowOverwrite({ esbuild, testDir }) {
const input = path.join(testDir, 'in.mjs')
await writeFileAsync(input, `export default FOO`)
// Fail without "allowOverwrite"
try {
await esbuild.build({
entryPoints: [input],
outfile: input,
logLevel: 'silent',
})
throw new Error('Expected build failure');
} catch (e) {
if (!e || !e.errors || !e.errors.length || !e.errors[0].text.includes('Refusing to overwrite input file'))
throw e
}
// Succeed with "allowOverwrite"
await esbuild.build({
entryPoints: [input],
outfile: input,
allowOverwrite: true,
define: { FOO: '123' },
})
const result = await import(input)
assert.strictEqual(result.default, 123)
},
async splittingRelativeSameDir({ esbuild, testDir }) {
const inputA = path.join(testDir, 'a.js')
const inputB = path.join(testDir, 'b.js')