From c22e071db8a8f7231de714beab36fbe3090cb00a Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Wed, 13 May 2020 02:00:41 -0700 Subject: [PATCH] fix #76: add support for stdin/stdout --- cmd/esbuild/main.go | 202 ++++++++++++++++++----------- internal/bundler/bundler.go | 85 +++++++++--- internal/logging/logging.go | 4 +- internal/logging/logging_darwin.go | 5 +- internal/logging/logging_linux.go | 5 +- internal/logging/logging_other.go | 4 +- scripts/verify-source-map.js | 123 ++++++++++++------ 7 files changed, 288 insertions(+), 140 deletions(-) diff --git a/cmd/esbuild/main.go b/cmd/esbuild/main.go index bf4055e8fee..2e81140e0e1 100644 --- a/cmd/esbuild/main.go +++ b/cmd/esbuild/main.go @@ -19,6 +19,50 @@ import ( "github.com/evanw/esbuild/internal/resolver" ) +const helpText = ` +Usage: + esbuild [options] [entry points] + +Options: + --name=... The name of the module + --bundle Bundle all dependencies into the output files + --outfile=... The output file (for one entry point) + --outdir=... The output directory (for multiple entry points) + --sourcemap Emit a source map + --error-limit=... Maximum error count or 0 to disable (default 10) + --target=... Language target (default esnext) + --platform=... Platform target (browser or node, default browser) + --external:M Exclude module M from the bundle + --format=... Output format (iife or cjs) + --color=... Force use of color terminal escapes (true or false) + + --minify Sets all --minify-* flags + --minify-whitespace Remove whitespace + --minify-identifiers Shorten identifiers + --minify-syntax Use equivalent but shorter syntax + + --define:K=V Substitute K with V while parsing + --jsx-factory=... What to use instead of React.createElement + --jsx-fragment=... What to use instead of React.Fragment + --loader:X=L Use loader L to load file extension X, where L is + one of: js, jsx, ts, tsx, json, text, base64 + + --trace=... Write a CPU trace to this file + --cpuprofile=... Write a CPU profile to this file + --version Print the current version and exit (` + esbuildVersion + `) + +Examples: + # Produces dist/entry_point.js and dist/entry_point.js.map + esbuild --bundle entry_point.js --outdir=dist --minify --sourcemap + + # Allow JSX syntax in .js files + esbuild --bundle entry_point.js --outfile=out.js --loader:.js=jsx + + # Substitute the identifier RELEASE for the literal true + esbuild example.js --outfile=out.js --define:RELEASE=true + +` + type argsObject struct { traceFile string cpuprofileFile string @@ -34,7 +78,7 @@ func exitWithError(text string) { colorBold := "" colorReset := "" - if logging.StderrTerminalInfo().UseColorEscapes { + if logging.GetTerminalInfo(os.Stderr).UseColorEscapes { colorRed = "\033[1;31m" colorBold = "\033[0;1m" colorReset = "\033[0m" @@ -90,30 +134,25 @@ func (args *argsObject) parseDefine(key string, value string) bool { return true } -func (args *argsObject) parseLoader(key string, value string) bool { - var loader bundler.Loader - - switch value { +func (args *argsObject) parseLoader(text string) bundler.Loader { + switch text { case "js": - loader = bundler.LoaderJS + return bundler.LoaderJS case "jsx": - loader = bundler.LoaderJSX + return bundler.LoaderJSX case "ts": - loader = bundler.LoaderTS + return bundler.LoaderTS case "tsx": - loader = bundler.LoaderTSX + return bundler.LoaderTSX case "json": - loader = bundler.LoaderJSON + return bundler.LoaderJSON case "text": - loader = bundler.LoaderText + return bundler.LoaderText case "base64": - loader = bundler.LoaderBase64 + return bundler.LoaderBase64 default: - return false + return bundler.LoaderNone } - - args.bundleOptions.ExtensionToLoader[key] = loader - return true } func (args *argsObject) parseMemberExpression(text string) ([]string, bool) { @@ -225,8 +264,20 @@ func parseArgs(fs fs.FS, rawArgs []string) (argsObject, error) { if len(extension) < 2 || strings.ContainsRune(extension[1:], '.') { return argsObject{}, fmt.Errorf("Invalid file extension: %s", arg) } - if !args.parseLoader(extension, loader) { + parsedLoader := args.parseLoader(loader) + if parsedLoader == bundler.LoaderNone { return argsObject{}, fmt.Errorf("Invalid loader: %s", arg) + } else { + args.bundleOptions.ExtensionToLoader[extension] = parsedLoader + } + + case strings.HasPrefix(arg, "--loader="): + loader := arg[len("--loader="):] + parsedLoader := args.parseLoader(loader) + if parsedLoader == bundler.LoaderNone { + return argsObject{}, fmt.Errorf("Invalid loader: %s", arg) + } else { + args.bundleOptions.LoaderForStdin = parsedLoader } case strings.HasPrefix(arg, "--target="): @@ -318,8 +369,8 @@ func parseArgs(fs fs.FS, rawArgs []string) (argsObject, error) { } } - if args.bundleOptions.AbsOutputFile != "" && len(args.entryPaths) > 1 { - return argsObject{}, fmt.Errorf("Use --outdir instead of --outfile when there are multiple entry points") + if args.bundleOptions.AbsOutputDir == "" && len(args.entryPaths) > 1 { + return argsObject{}, fmt.Errorf("Must provide --outdir when there are multiple input files") } if args.bundleOptions.AbsOutputFile != "" && args.bundleOptions.AbsOutputDir != "" { @@ -341,18 +392,43 @@ func parseArgs(fs fs.FS, rawArgs []string) (argsObject, error) { } } + if len(args.entryPaths) > 0 { + // Disallow the "--loader=" form when not reading from stdin + if args.bundleOptions.LoaderForStdin != bundler.LoaderNone { + return argsObject{}, fmt.Errorf("Must provide file extension for --loader") + } + + // Write to stdout by default if there's only one input file + if len(args.entryPaths) == 1 && args.bundleOptions.AbsOutputFile == "" && args.bundleOptions.AbsOutputDir == "" { + args.bundleOptions.WriteToStdout = true + } + } else if !logging.GetTerminalInfo(os.Stdin).IsTTY { + // If called with no input files and we're not a TTY, read from stdin instead + args.entryPaths = append(args.entryPaths, "") + + // Default to reading JavaScript from stdin + if args.bundleOptions.LoaderForStdin == bundler.LoaderNone { + args.bundleOptions.LoaderForStdin = bundler.LoaderJS + } + + // Write to stdout if no input file is provided + if args.bundleOptions.AbsOutputFile == "" { + if args.bundleOptions.AbsOutputDir != "" { + return argsObject{}, fmt.Errorf("Cannot use --outdir when reading from stdin") + } + args.bundleOptions.WriteToStdout = true + } + } + return args, nil } func main() { - // Show usage information if called with no arguments - showHelp := len(os.Args) < 2 - for _, arg := range os.Args { // Show help if a common help flag is provided if arg == "-h" || arg == "-help" || arg == "--help" || arg == "/?" { - showHelp = true - break + fmt.Fprintf(os.Stderr, "%s", helpText) + os.Exit(0) } // Special-case the version flag here @@ -369,54 +445,6 @@ func main() { } } - // Show help and exit if requested - if showHelp { - fmt.Print(` -Usage: - esbuild [options] [entry points] - -Options: - --name=... The name of the module - --bundle Bundle all dependencies into the output files - --outfile=... The output file (for one entry point) - --outdir=... The output directory (for multiple entry points) - --sourcemap Emit a source map - --error-limit=... Maximum error count or 0 to disable (default 10) - --target=... Language target (default esnext) - --platform=... Platform target (browser or node, default browser) - --external:M Exclude module M from the bundle - --format=... Output format (iife or cjs) - --color=... Force use of color terminal escapes (true or false) - - --minify Sets all --minify-* flags - --minify-whitespace Remove whitespace - --minify-identifiers Shorten identifiers - --minify-syntax Use equivalent but shorter syntax - - --define:K=V Substitute K with V while parsing - --jsx-factory=... What to use instead of React.createElement - --jsx-fragment=... What to use instead of React.Fragment - --loader:X=L Use loader L to load file extension X, where L is - one of: js, jsx, ts, tsx, json, text, base64 - - --trace=... Write a CPU trace to this file - --cpuprofile=... Write a CPU profile to this file - --version Print the current version and exit (` + esbuildVersion + `) - -Examples: - # Produces dist/entry_point.js and dist/entry_point.js.map - esbuild --bundle entry_point.js --outdir=dist --minify --sourcemap - - # Allow JSX syntax in .js files - esbuild --bundle entry_point.js --outfile=out.js --loader:.js=jsx - - # Substitute the identifier RELEASE for the literal true - esbuild example.js --outfile=out.js --define:RELEASE=true - -`) - os.Exit(0) - } - start := time.Now() fs := fs.RealFS() args, err := parseArgs(fs, os.Args[1:]) @@ -424,9 +452,14 @@ Examples: exitWithError(err.Error()) } - // Show usage information if called with no files + // Handle when there are no input files (including implicit stdin) if len(args.entryPaths) == 0 { - exitWithError("No files specified") + if len(os.Args) < 2 { + fmt.Fprintf(os.Stderr, "%s", helpText) + os.Exit(0) + } else { + exitWithError("No input files") + } } // Capture the defer statements below so the "done" message comes last @@ -477,7 +510,9 @@ Examples: } }() - fmt.Fprintf(os.Stderr, "Done in %dms\n", time.Since(start).Nanoseconds()/1000000) + if !args.bundleOptions.WriteToStdout { + fmt.Fprintf(os.Stderr, "Done in %dms\n", time.Since(start).Nanoseconds()/1000000) + } } func run(fs fs.FS, args argsObject) { @@ -509,13 +544,24 @@ func run(fs fs.FS, args argsObject) { // Write out the results for _, item := range result { + // Special-case writing to stdout + if args.bundleOptions.WriteToStdout { + _, err := os.Stdout.Write(item.JsContents) + if err != nil { + exitWithError(fmt.Sprintf("Failed to write to stdout: %s", err.Error())) + } + continue + } + // Write out the JavaScript file err := ioutil.WriteFile(item.JsAbsPath, []byte(item.JsContents), 0644) path := resolver.PrettyPath(item.JsAbsPath) if err != nil { exitWithError(fmt.Sprintf("Failed to write to %s (%s)", path, err.Error())) } - fmt.Fprintf(os.Stderr, "Wrote to %s (%s)\n", path, toSize(len(item.JsContents))) + if !args.bundleOptions.WriteToStdout { + fmt.Fprintf(os.Stderr, "Wrote to %s (%s)\n", path, toSize(len(item.JsContents))) + } // Also write the source map if args.bundleOptions.SourceMap { @@ -524,7 +570,9 @@ func run(fs fs.FS, args argsObject) { if err != nil { exitWithError(fmt.Sprintf("Failed to write to %s: (%s)", path, err.Error())) } - fmt.Fprintf(os.Stderr, "Wrote to %s (%s)\n", path, toSize(len(item.SourceMapContents))) + if !args.bundleOptions.WriteToStdout { + fmt.Fprintf(os.Stderr, "Wrote to %s (%s)\n", path, toSize(len(item.SourceMapContents))) + } } } } diff --git a/internal/bundler/bundler.go b/internal/bundler/bundler.go index e2be5b9e4bd..d4c99b354eb 100644 --- a/internal/bundler/bundler.go +++ b/internal/bundler/bundler.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/base64" "fmt" + "io/ioutil" + "os" "sort" "strings" "sync" @@ -58,7 +60,15 @@ func parseFile( extension = path[lastDot:] } - switch bundleOptions.ExtensionToLoader[extension] { + // Pick the loader based on the file extension + loader := bundleOptions.ExtensionToLoader[extension] + + // Special-case reading from stdin + if bundleOptions.LoaderForStdin != LoaderNone && source.IsStdin { + loader = bundleOptions.LoaderForStdin + } + + switch loader { case LoaderJS: ast, ok := parser.Parse(log, source, parseOptions) results <- parseResult{source.Index, ast, ok} @@ -142,26 +152,46 @@ func ScanBundle( }() } - maybeParseFile := func(path string, importSource logging.Source, pathRange ast.Range, isDisabled bool) (uint32, bool) { + type parseFileFlags struct { + isEntryPoint bool + isDisabled bool + } + + maybeParseFile := func(path string, importSource logging.Source, pathRange ast.Range, flags parseFileFlags) (uint32, bool) { sourceIndex, ok := visited[path] if !ok { sourceIndex = uint32(len(sources)) - visited[path] = sourceIndex + isStdin := bundleOptions.LoaderForStdin != LoaderNone && flags.isEntryPoint + prettyPath := path + if !isStdin { + prettyPath = res.PrettyPath(path) + visited[path] = sourceIndex + } contents := "" // Disabled files are left empty - if !isDisabled { - contents, ok = res.Read(path) - if !ok { - log.AddRangeError(importSource, pathRange, fmt.Sprintf("Could not read from file: %s", path)) - return 0, false + if !flags.isDisabled { + if isStdin { + bytes, err := ioutil.ReadAll(os.Stdin) + if err != nil { + log.AddRangeError(importSource, pathRange, fmt.Sprintf("Could not read from stdin: %s", err.Error())) + return 0, false + } + contents = string(bytes) + } else { + contents, ok = res.Read(path) + if !ok { + log.AddRangeError(importSource, pathRange, fmt.Sprintf("Could not read from file: %s", path)) + return 0, false + } } } source := logging.Source{ Index: sourceIndex, + IsStdin: isStdin, AbsolutePath: path, - PrettyPath: res.PrettyPath(path), + PrettyPath: prettyPath, Contents: contents, } sources = append(sources, source) @@ -174,7 +204,8 @@ func ScanBundle( entryPoints := []uint32{} for _, path := range entryPaths { - if sourceIndex, ok := maybeParseFile(path, logging.Source{}, ast.Range{}, false /* isDisabled */); ok { + flags := parseFileFlags{isEntryPoint: true} + if sourceIndex, ok := maybeParseFile(path, logging.Source{}, ast.Range{}, flags); ok { entryPoints = append(entryPoints, sourceIndex) } } @@ -193,7 +224,8 @@ func ScanBundle( switch path, status := res.Resolve(sourcePath, pathText); status { case resolver.ResolveEnabled, resolver.ResolveDisabled: - if sourceIndex, ok := maybeParseFile(path, source, pathRange, status == resolver.ResolveDisabled); ok { + flags := parseFileFlags{isDisabled: status == resolver.ResolveDisabled} + if sourceIndex, ok := maybeParseFile(path, source, pathRange, flags); ok { resolvedImports[pathText] = sourceIndex filteredImportPaths = append(filteredImportPaths, importPath) } @@ -281,6 +313,13 @@ type BundleOptions struct { ExtensionToLoader map[string]Loader OutputFormat Format + // If this isn't LoaderNone, all entry point contents are assumed to come + // from stdin and must be loaded with this loader + LoaderForStdin Loader + + // If true, make sure to generate a single file that can be written to stdout + WriteToStdout bool + omitRuntimeForTests bool } @@ -594,13 +633,21 @@ func (b *Bundle) generateSourceMapForEntryPoint( buffer = append(buffer, '"') // Finish the source map - item.SourceMapAbsPath = item.JsAbsPath + ".map" - item.SourceMapContents = append(buffer, ",\n \"names\": []\n}\n"...) + buffer = append(buffer, ",\n \"names\": []\n}\n"...) + + // Generate the output if options.RemoveWhitespace { item.JsContents = removeTrailing(item.JsContents, '\n') } - item.JsContents = append(item.JsContents, - ("//# sourceMappingURL=" + b.fs.Base(item.SourceMapAbsPath) + "\n")...) + if options.WriteToStdout { + item.JsContents = append(item.JsContents, + ("//# sourceMappingURL=data:application/json;base64," + base64.StdEncoding.EncodeToString(buffer) + "\n")...) + } else { + item.SourceMapAbsPath = item.JsAbsPath + ".map" + item.SourceMapContents = buffer + item.JsContents = append(item.JsContents, + ("//# sourceMappingURL=" + b.fs.Base(item.SourceMapAbsPath) + "\n")...) + } } func (b *Bundle) mergeAllSymbolsIntoOneMap(files []file) *ast.SymbolMap { @@ -1674,7 +1721,9 @@ func (b *Bundle) deterministicDependenciesOfEntryPoint( } func (b *Bundle) outputFileForEntryPoint(entryPoint uint32, options *BundleOptions) string { - if options.AbsOutputFile != "" { + if options.WriteToStdout { + return "" + } else if options.AbsOutputFile != "" { return b.fs.Base(options.AbsOutputFile) } name := b.fs.Base(b.sources[entryPoint].AbsolutePath) @@ -1693,7 +1742,9 @@ func (b *Bundle) outputFileForEntryPoint(entryPoint uint32, options *BundleOptio } func (b *Bundle) outputPathForEntryPoint(entryPoint uint32, jsName string, options *BundleOptions) string { - if options.AbsOutputDir != "" { + if options.WriteToStdout { + return "" + } else if options.AbsOutputDir != "" { return b.fs.Join(options.AbsOutputDir, jsName) } else { return b.fs.Join(b.fs.Dir(b.sources[entryPoint].AbsolutePath), jsName) diff --git a/internal/logging/logging.go b/internal/logging/logging.go index 32b8e47ea89..a890912f40a 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -34,6 +34,7 @@ type Msg struct { type Source struct { Index uint32 + IsStdin bool AbsolutePath string PrettyPath string Contents string @@ -100,6 +101,7 @@ func (counts MsgCounts) String() string { } type TerminalInfo struct { + IsTTY bool UseColorEscapes bool Width int } @@ -108,7 +110,7 @@ func NewStderrLog(options StderrOptions) (Log, func() MsgCounts) { msgs := make(chan Msg) done := make(chan MsgCounts) log := NewLog(msgs) - terminalInfo := StderrTerminalInfo() + terminalInfo := GetTerminalInfo(os.Stderr) switch options.Color { case ColorNever: diff --git a/internal/logging/logging_darwin.go b/internal/logging/logging_darwin.go index b9a7cddb59a..b75eed0aa3c 100644 --- a/internal/logging/logging_darwin.go +++ b/internal/logging/logging_darwin.go @@ -18,11 +18,12 @@ type winsize struct { ws_ypixel uint16 } -func StderrTerminalInfo() (info TerminalInfo) { - fd := os.Stderr.Fd() +func GetTerminalInfo(file *os.File) (info TerminalInfo) { + fd := file.Fd() // Is stderr a terminal? if _, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA); err == nil { + info.IsTTY = true info.UseColorEscapes = true // Get the width of the window diff --git a/internal/logging/logging_linux.go b/internal/logging/logging_linux.go index b55cd5c7f97..8d6fa6abf11 100644 --- a/internal/logging/logging_linux.go +++ b/internal/logging/logging_linux.go @@ -18,11 +18,12 @@ type winsize struct { ws_ypixel uint16 } -func StderrTerminalInfo() (info TerminalInfo) { - fd := os.Stderr.Fd() +func GetTerminalInfo(file *os.File) (info TerminalInfo) { + fd := file.Fd() // Is stderr a terminal? if _, err := unix.IoctlGetTermios(int(fd), unix.TCGETS); err == nil { + info.IsTTY = true info.UseColorEscapes = true // Get the width of the window diff --git a/internal/logging/logging_other.go b/internal/logging/logging_other.go index f74d440bf2b..2e2b76287b4 100644 --- a/internal/logging/logging_other.go +++ b/internal/logging/logging_other.go @@ -3,8 +3,10 @@ package logging +import "os" + const SupportsColorEscapes = false -func StderrTerminalInfo() TerminalInfo { +func GetTerminalInfo(*os.File) TerminalInfo { return TerminalInfo{} } diff --git a/scripts/verify-source-map.js b/scripts/verify-source-map.js index 78b18e49936..ea093cd8c14 100644 --- a/scripts/verify-source-map.js +++ b/scripts/verify-source-map.js @@ -72,58 +72,99 @@ const testCaseTypeScriptRuntime = { `, } +const testCaseStdin = { + '': ` + function a0() { a1("a0") } + function a1() { a2("a1") } + function a2() { throw new Error("a2") } + a0() + `, +} + async function check(kind, testCase, toSearch, flags) { let failed = 0 - const recordCheck = (success, message) => { - if (!success) { - failed++ - console.error(`❌ [${kind}] ${message}`) + + try { + const recordCheck = (success, message) => { + if (!success) { + failed++ + console.error(`❌ [${kind}] ${message}`) + } } - } - const tempDir = path.join(__dirname, '.verify-source-map' + tempDirCount++) - try { await util.promisify(fs.mkdir)(tempDir) } catch (e) { } + const tempDir = path.join(__dirname, '.verify-source-map' + tempDirCount++) + try { await util.promisify(fs.mkdir)(tempDir) } catch (e) { } - for (const name in testCase) { - await util.promisify(fs.writeFile)(path.join(tempDir, name), testCase[name]) - } + for (const name in testCase) { + if (name !== '') { + await util.promisify(fs.writeFile)(path.join(tempDir, name), testCase[name]) + } + } + + const esbuildPath = buildBinary() + const files = Object.keys(testCase) + const args = ['--sourcemap'].concat(flags) + const isStdin = '' in testCase + let stdout = '' + + await new Promise((resolve, reject) => { + if (!isStdin) args.unshift(files[0]) + const child = childProcess.spawn(esbuildPath, args, { cwd: tempDir, stdio: 'pipe' }) + if (isStdin) child.stdin.write(testCase['']) + child.stdin.end() + child.stdout.on('data', chunk => stdout += chunk.toString()) + child.stdout.on('end', resolve) + child.on('error', reject) + }) + + let outJs + let outJsMap + + if (stdout !== '') { + outJs = stdout + recordCheck(outJs.includes(`//# sourceMappingURL=data:application/json;base64,`), `.js file contains source map`) + outJsMap = Buffer.from(outJs.slice(outJs.indexOf('base64,') + 'base64,'.length).trim(), 'base64').toString() + } - const esbuildPath = buildBinary() - const files = Object.keys(testCase) - const args = [files[0], '--sourcemap', '--outfile=out.js'].concat(flags) - await util.promisify(childProcess.execFile)(esbuildPath, args, { cwd: tempDir, stdio: 'pipe' }) + else { + outJs = await util.promisify(fs.readFile)(path.join(tempDir, 'out.js'), 'utf8') + recordCheck(outJs.includes(`//# sourceMappingURL=out.js.map\n`), `.js file links to .js.map`) + outJsMap = await util.promisify(fs.readFile)(path.join(tempDir, 'out.js.map'), 'utf8') + } + + const map = await new SourceMapConsumer(outJsMap) - const outJs = await util.promisify(fs.readFile)(path.join(tempDir, 'out.js'), 'utf8') - const outJsMap = await util.promisify(fs.readFile)(path.join(tempDir, 'out.js.map'), 'utf8') - const map = await new SourceMapConsumer(outJsMap) + for (const id of toSearch) { + const inSource = isStdin ? '' : files.find(x => x.startsWith(id[0])) + const inJs = testCase[inSource] + const inIndex = inJs.indexOf(`"${id}"`) + const outIndex = outJs.indexOf(`"${id}"`) - const isLinked = outJs.includes(`//# sourceMappingURL=out.js.map\n`) - recordCheck(isLinked, `.js file links to .js.map`) + if (inIndex < 0) throw new Error(`Failed to find "${id}" in input`) + if (outIndex < 0) throw new Error(`Failed to find "${id}" in output`) - for (const id of toSearch) { - const inSource = files.find(x => x.startsWith(id[0])) - const inJs = testCase[inSource] - const inIndex = inJs.indexOf(`"${id}"`) - const outIndex = outJs.indexOf(`"${id}"`) + const inLines = inJs.slice(0, inIndex).split('\n') + const inLine = inLines.length + const inColumn = inLines[inLines.length - 1].length - if (inIndex < 0) throw new Error(`Failed to find "${id}" in input`) - if (outIndex < 0) throw new Error(`Failed to find "${id}" in output`) + const outLines = outJs.slice(0, outIndex).split('\n') + const outLine = outLines.length + const outColumn = outLines[outLines.length - 1].length - const inLines = inJs.slice(0, inIndex).split('\n') - const inLine = inLines.length - const inColumn = inLines[inLines.length - 1].length + const { source, line, column } = map.originalPositionFor({ line: outLine, column: outColumn }) + const expected = JSON.stringify({ source: inSource, line: inLine, column: inColumn }) + const observed = JSON.stringify({ source, line, column }) + recordCheck(expected === observed, `expected: ${expected} observed: ${observed}`) + } - const outLines = outJs.slice(0, outIndex).split('\n') - const outLine = outLines.length - const outColumn = outLines[outLines.length - 1].length + rimraf.sync(tempDir, { disableGlob: true }) + } - const { source, line, column } = map.originalPositionFor({ line: outLine, column: outColumn }) - const expected = JSON.stringify({ source: inSource, line: inLine, column: inColumn }) - const observed = JSON.stringify({ source, line, column }) - recordCheck(expected === observed, `expected: ${expected} observed: ${observed}`) + catch (e) { + console.error(`❌ [${kind}] ${e && e.message || e}`) + failed++ } - rimraf.sync(tempDir, { disableGlob: true }) return failed } @@ -133,9 +174,11 @@ async function main() { const flags = minify ? ['--minify'] : [] const suffix = minify ? '-min' : '' promises.push( - check('commonjs' + suffix, testCaseCommonJS, toSearchBundle, flags.concat('--bundle')), - check('es6' + suffix, testCaseES6, toSearchBundle, flags.concat('--bundle')), - check('ts' + suffix, testCaseTypeScriptRuntime, toSearchNoBundle, flags), + check('commonjs' + suffix, testCaseCommonJS, toSearchBundle, flags.concat('--outfile=out.js', '--bundle')), + check('es6' + suffix, testCaseES6, toSearchBundle, flags.concat('--outfile=out.js', '--bundle')), + check('ts' + suffix, testCaseTypeScriptRuntime, toSearchNoBundle, flags.concat('--outfile=out.js')), + check('stdin' + suffix, testCaseStdin, toSearchNoBundle, flags.concat('--outfile=out.js')), + check('stdin-stdout' + suffix, testCaseStdin, toSearchNoBundle, flags), ) }