From 5d7ac55cba37391a5ee0239940a4a36638e26414 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Fri, 24 Jun 2022 08:40:14 -0400 Subject: [PATCH] fix #2347: strip `"use strict"` when targeting ESM --- CHANGELOG.md | 16 +++++ internal/bundler/bundler_default_test.go | 36 ++++++++++- internal/bundler/bundler_tsconfig_test.go | 59 ++++++++++++++++++- internal/bundler/linker.go | 5 +- .../bundler/snapshots/snapshots_default.txt | 42 +++++++++---- .../bundler/snapshots/snapshots_tsconfig.txt | 28 ++++++++- 6 files changed, 170 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32feda31a22..00d21135ab9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,22 @@ * `.woff` => `font/woff` * `.woff2` => `font/woff2` +* Remove `"use strict";` when targeting ESM ([#2347](https://github.com/evanw/esbuild/issues/2347)) + + All ES module code is automatically in strict mode, so a `"use strict";` directive is unnecessary. With this release, esbuild will now remove the `"use strict";` directive if the output format is ESM. This change makes the generated output file a few bytes smaller: + + ```js + // Original code + 'use strict' + export let foo = 123 + + // Old output (with --format=esm --minify) + "use strict";let t=123;export{t as foo}; + + // New output (with --format=esm --minify) + let t=123;export{t as foo}; + ``` + ## 0.14.47 * Make global names more compact when `||=` is available ([#2331](https://github.com/evanw/esbuild/issues/2331)) diff --git a/internal/bundler/bundler_default_test.go b/internal/bundler/bundler_default_test.go index 33335f1618b..389d46c72b9 100644 --- a/internal/bundler/bundler_default_test.go +++ b/internal/bundler/bundler_default_test.go @@ -2788,7 +2788,41 @@ func TestUseStrictDirectiveBundleIssue1837(t *testing.T) { }) } -func TestUseStrictDirectiveBundleIssue2264(t *testing.T) { +func TestUseStrictDirectiveBundleIIFEIssue2264(t *testing.T) { + default_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/entry.js": ` + 'use strict' + export let a = 1 + `, + }, + entryPaths: []string{"/entry.js"}, + options: config.Options{ + Mode: config.ModeBundle, + AbsOutputFile: "/out.js", + OutputFormat: config.FormatIIFE, + }, + }) +} + +func TestUseStrictDirectiveBundleCJSIssue2264(t *testing.T) { + default_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/entry.js": ` + 'use strict' + export let a = 1 + `, + }, + entryPaths: []string{"/entry.js"}, + options: config.Options{ + Mode: config.ModeBundle, + AbsOutputFile: "/out.js", + OutputFormat: config.FormatCommonJS, + }, + }) +} + +func TestUseStrictDirectiveBundleESMIssue2264(t *testing.T) { default_suite.expectBundled(t, bundled{ files: map[string]string{ "/entry.js": ` diff --git a/internal/bundler/bundler_tsconfig_test.go b/internal/bundler/bundler_tsconfig_test.go index 6897d5e7eb6..8aa8c74a87a 100644 --- a/internal/bundler/bundler_tsconfig_test.go +++ b/internal/bundler/bundler_tsconfig_test.go @@ -1837,7 +1837,7 @@ func TestTsConfigAlwaysStrictTrueEmitDirectiveFormat(t *testing.T) { }) } -func TestTsConfigAlwaysStrictTrueEmitDirectiveBundle(t *testing.T) { +func TestTsConfigAlwaysStrictTrueEmitDirectiveBundleIIFE(t *testing.T) { tsconfig_suite.expectBundled(t, bundled{ files: map[string]string{ "/Users/user/project/src/implicit.ts": ` @@ -1860,6 +1860,63 @@ func TestTsConfigAlwaysStrictTrueEmitDirectiveBundle(t *testing.T) { options: config.Options{ Mode: config.ModeBundle, AbsOutputDir: "/Users/user/project/out", + OutputFormat: config.FormatIIFE, + }, + }) +} + +func TestTsConfigAlwaysStrictTrueEmitDirectiveBundleCJS(t *testing.T) { + tsconfig_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/Users/user/project/src/implicit.ts": ` + console.log('this file should start with "use strict"') + `, + "/Users/user/project/src/explicit.ts": ` + 'use strict' + console.log('this file should start with "use strict"') + `, + "/Users/user/project/tsconfig.json": `{ + "compilerOptions": { + "alwaysStrict": true + } + }`, + }, + entryPaths: []string{ + "/Users/user/project/src/implicit.ts", + "/Users/user/project/src/explicit.ts", + }, + options: config.Options{ + Mode: config.ModeBundle, + AbsOutputDir: "/Users/user/project/out", + OutputFormat: config.FormatCommonJS, + }, + }) +} + +func TestTsConfigAlwaysStrictTrueEmitDirectiveBundleESM(t *testing.T) { + tsconfig_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/Users/user/project/src/implicit.ts": ` + console.log('this file should not start with "use strict"') + `, + "/Users/user/project/src/explicit.ts": ` + 'use strict' + console.log('this file should not start with "use strict"') + `, + "/Users/user/project/tsconfig.json": `{ + "compilerOptions": { + "alwaysStrict": true + } + }`, + }, + entryPaths: []string{ + "/Users/user/project/src/implicit.ts", + "/Users/user/project/src/explicit.ts", + }, + options: config.Options{ + Mode: config.ModeBundle, + AbsOutputDir: "/Users/user/project/out", + OutputFormat: config.FormatESModule, }, }) } diff --git a/internal/bundler/linker.go b/internal/bundler/linker.go index 413af4ee678..ec6b691fa4f 100644 --- a/internal/bundler/linker.go +++ b/internal/bundler/linker.go @@ -4871,8 +4871,9 @@ func (c *linkerContext) generateChunkJS(chunks []chunkInfo, chunkIndex int, chun isExecutable = true } - // Add the top-level directive if present - if repr.AST.Directive != "" { + // Add the top-level directive if present (but omit "use strict" in ES + // modules because all ES modules are automatically in strict mode) + if repr.AST.Directive != "" && (repr.AST.Directive != "use strict" || c.options.OutputFormat != config.FormatESModule) { quoted := string(js_printer.QuoteForJSON(repr.AST.Directive, c.options.ASCIIOnly)) + ";" + newline prevOffset.AdvanceString(quoted) j.AddString(quoted) diff --git a/internal/bundler/snapshots/snapshots_default.txt b/internal/bundler/snapshots/snapshots_default.txt index caa936a5adc..70fd4957342 100644 --- a/internal/bundler/snapshots/snapshots_default.txt +++ b/internal/bundler/snapshots/snapshots_default.txt @@ -3927,6 +3927,37 @@ await foo; for await (foo of bar) ; +================================================================================ +TestUseStrictDirectiveBundleCJSIssue2264 +---------- /out.js ---------- +"use strict"; + +// entry.js +var entry_exports = {}; +__export(entry_exports, { + a: () => a +}); +module.exports = __toCommonJS(entry_exports); +var a = 1; + +================================================================================ +TestUseStrictDirectiveBundleESMIssue2264 +---------- /out.js ---------- +// entry.js +var a = 1; +export { + a +}; + +================================================================================ +TestUseStrictDirectiveBundleIIFEIssue2264 +---------- /out.js ---------- +"use strict"; +(() => { + // entry.js + var a = 1; +})(); + ================================================================================ TestUseStrictDirectiveBundleIssue1837 ---------- /out.js ---------- @@ -3953,17 +3984,6 @@ TestUseStrictDirectiveBundleIssue1837 console.log(require_cjs()); })(); -================================================================================ -TestUseStrictDirectiveBundleIssue2264 ----------- /out.js ---------- -"use strict"; - -// entry.js -var a = 1; -export { - a -}; - ================================================================================ TestUseStrictDirectiveMinifyNoBundle ---------- /out.js ---------- diff --git a/internal/bundler/snapshots/snapshots_tsconfig.txt b/internal/bundler/snapshots/snapshots_tsconfig.txt index 09622581d52..3bf3e3b6f01 100644 --- a/internal/bundler/snapshots/snapshots_tsconfig.txt +++ b/internal/bundler/snapshots/snapshots_tsconfig.txt @@ -14,7 +14,7 @@ var import_util = __toESM(require_util()); console.log((0, import_util.default)()); ================================================================================ -TestTsConfigAlwaysStrictTrueEmitDirectiveBundle +TestTsConfigAlwaysStrictTrueEmitDirectiveBundleCJS ---------- /Users/user/project/out/implicit.js ---------- "use strict"; @@ -27,6 +27,32 @@ console.log('this file should start with "use strict"'); // Users/user/project/src/explicit.ts console.log('this file should start with "use strict"'); +================================================================================ +TestTsConfigAlwaysStrictTrueEmitDirectiveBundleESM +---------- /Users/user/project/out/implicit.js ---------- +// Users/user/project/src/implicit.ts +console.log('this file should not start with "use strict"'); + +---------- /Users/user/project/out/explicit.js ---------- +// Users/user/project/src/explicit.ts +console.log('this file should not start with "use strict"'); + +================================================================================ +TestTsConfigAlwaysStrictTrueEmitDirectiveBundleIIFE +---------- /Users/user/project/out/implicit.js ---------- +"use strict"; +(() => { + // Users/user/project/src/implicit.ts + console.log('this file should start with "use strict"'); +})(); + +---------- /Users/user/project/out/explicit.js ---------- +"use strict"; +(() => { + // Users/user/project/src/explicit.ts + console.log('this file should start with "use strict"'); +})(); + ================================================================================ TestTsConfigAlwaysStrictTrueEmitDirectiveFormat ---------- /Users/user/project/out/implicit.js ----------