Skip to content

Commit

Permalink
fix #364: "chmod +x" for hashbang files
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Sep 11, 2020
1 parent e0592a5 commit b808c0d
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 23 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

* Mark output files with a hashbang as executable ([#364](https://github.com/evanw/esbuild/issues/364))

Output files that start with a hashbang line such as `#!/usr/bin/env node` will now automatically be marked as executable. This lets you run them directly in a Unix-like shell without using the `node` command.

## 0.6.34

* Fix parsing of `type;` statements followed by an identifier in TypeScript ([#377](https://github.com/evanw/esbuild/pull/377))
Expand Down
2 changes: 2 additions & 0 deletions internal/bundler/bundler.go
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,8 @@ type OutputFile struct {
// about this file in JSON format. This is a partial JSON file that will be
// fully assembled later.
jsonMetadataChunk []byte

IsExecutable bool
}

type lineColumnOffset struct {
Expand Down
3 changes: 3 additions & 0 deletions internal/bundler/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -3023,6 +3023,7 @@ func (c *linkerContext) generateChunk(chunk *chunkInfo) func([]ast.ImportRecord)
newline = ""
}
newlineBeforeComment := false
isExecutable := false

if chunk.isEntryPoint {
file := &c.files[chunk.sourceIndex]
Expand All @@ -3033,6 +3034,7 @@ func (c *linkerContext) generateChunk(chunk *chunkInfo) func([]ast.ImportRecord)
prevOffset.advanceString(hashbang)
j.AddString(hashbang)
newlineBeforeComment = true
isExecutable = true
}

// Add the top-level directive if present
Expand Down Expand Up @@ -3262,6 +3264,7 @@ func (c *linkerContext) generateChunk(chunk *chunkInfo) func([]ast.ImportRecord)
AbsPath: c.fs.Join(c.options.AbsOutputDir, chunk.relPath()),
Contents: jsContents,
jsonMetadataChunk: jsonMetadataChunk,
IsExecutable: isExecutable,
})
return results
}
Expand Down
52 changes: 29 additions & 23 deletions pkg/api/api_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,49 +522,55 @@ func buildImpl(buildOpts BuildOptions) BuildResult {

// Stop now if there were errors
if !log.HasErrors() {
// Return the results
outputFiles = make([]OutputFile, len(results))
for i, result := range results {
if options.WriteToStdout {
result.AbsPath = "<stdout>"
}
outputFiles[i] = OutputFile{
Path: result.AbsPath,
Contents: result.Contents,
}
}

if buildOpts.Write {
// Special-case writing to stdout
if options.WriteToStdout {
if len(outputFiles) != 1 {
if len(results) != 1 {
log.AddError(nil, logger.Loc{}, fmt.Sprintf(
"Internal error: did not expect to generate %d files when writing to stdout", len(outputFiles)))
} else if _, err := os.Stdout.Write(outputFiles[0].Contents); err != nil {
"Internal error: did not expect to generate %d files when writing to stdout", len(results)))
} else if _, err := os.Stdout.Write(results[0].Contents); err != nil {
log.AddError(nil, logger.Loc{}, fmt.Sprintf(
"Failed to write to stdout: %s", err.Error()))
}
} else {
// Write out files in parallel
waitGroup := sync.WaitGroup{}
waitGroup.Add(len(outputFiles))
for _, outputFile := range outputFiles {
go func(outputFile OutputFile) {
waitGroup.Add(len(results))
for _, result := range results {
go func(result bundler.OutputFile) {
fs.BeforeFileOpen()
defer fs.AfterFileClose()
if err := os.MkdirAll(filepath.Dir(outputFile.Path), 0755); err != nil {
if err := os.MkdirAll(filepath.Dir(result.AbsPath), 0755); err != nil {
log.AddError(nil, logger.Loc{}, fmt.Sprintf(
"Failed to create output directory: %s", err.Error()))
} else if err := ioutil.WriteFile(outputFile.Path, outputFile.Contents, 0644); err != nil {
log.AddError(nil, logger.Loc{}, fmt.Sprintf(
"Failed to write to output file: %s", err.Error()))
} else {
var mode os.FileMode = 0644
if result.IsExecutable {
mode = 0755
}
if err := ioutil.WriteFile(result.AbsPath, result.Contents, mode); err != nil {
log.AddError(nil, logger.Loc{}, fmt.Sprintf(
"Failed to write to output file: %s", err.Error()))
}
}
waitGroup.Done()
}(outputFile)
}(result)
}
waitGroup.Wait()
}
}

// Return the results
outputFiles = make([]OutputFile, len(results))
for i, result := range results {
if options.WriteToStdout {
result.AbsPath = "<stdout>"
}
outputFiles[i] = OutputFile{
Path: result.AbsPath,
Contents: result.Contents,
}
}
}
}
}
Expand Down

0 comments on commit b808c0d

Please sign in to comment.