From 466473b0c79e9c2c30de13edf4bee58458dca9a7 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Wed, 28 Dec 2022 00:48:00 +0000 Subject: [PATCH] fix #2776: assume extensionless files are js --- CHANGELOG.md | 30 +++++++++++++++++++ internal/bundler/bundler.go | 1 + .../bundler_tests/bundler_default_test.go | 6 ++-- scripts/js-api-tests.js | 30 +++++++++++++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42ab0905529..e76636b68e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## Unreleased + +* Loader defaults to `js` for extensionless files ([#2776](https://github.com/evanw/esbuild/issues/2776)) + + Certain packages contain files without an extension. For example, the `yargs` package contains the file `yargs/yargs` which has no extension. Node, Webpack, and Parcel can all understand code that imports `yargs/yargs` because they assume that the file is JavaScript. However, esbuild was previously unable to understand this code because it relies on the file extension to tell it how to interpret the file. With this release, esbuild will now assume files without an extension are JavaScript files. This can be customized by setting the loader for `""` (the empty string, representing files without an extension) to another loader. For example, if you want files without an extension to be treated as CSS instead, you can do that like this: + + * CLI: + + ``` + esbuild --bundle --loader:=css + ``` + + * JS: + + ```js + esbuild.build({ + bundle: true, + loader: { '': 'css' }, + }) + ``` + + * Go: + + ```go + api.Build(api.BuildOptions{ + Bundle: true, + Loader: map[string]api.Loader{"": api.LoaderCSS}, + }) + ``` + ## 0.16.11 * Avoid a syntax error in the presence of direct `eval` ([#2761](https://github.com/evanw/esbuild/issues/2761)) diff --git a/internal/bundler/bundler.go b/internal/bundler/bundler.go index 1fd04c24fff..1c859c42b86 100644 --- a/internal/bundler/bundler.go +++ b/internal/bundler/bundler.go @@ -2216,6 +2216,7 @@ func (s *scanner) validateTLA(sourceIndex uint32) tlaCheck { func DefaultExtensionToLoaderMap() map[string]config.Loader { return map[string]config.Loader{ + "": config.LoaderJS, // This represents files without an extension ".js": config.LoaderJS, ".mjs": config.LoaderJS, ".cjs": config.LoaderJS, diff --git a/internal/bundler_tests/bundler_default_test.go b/internal/bundler_tests/bundler_default_test.go index 5deddbb1e16..2998221e83b 100644 --- a/internal/bundler_tests/bundler_default_test.go +++ b/internal/bundler_tests/bundler_default_test.go @@ -1122,16 +1122,16 @@ func TestRequireBadExtension(t *testing.T) { default_suite.expectBundled(t, bundled{ files: map[string]string{ "/entry.js": ` - console.log(require('./test')) + console.log(require('./test.bad')) `, - "/test": `This is a test.`, + "/test.bad": `This is a test.`, }, entryPaths: []string{"/entry.js"}, options: config.Options{ Mode: config.ModeBundle, AbsOutputFile: "/out.js", }, - expectedScanLog: `entry.js: ERROR: Do not know how to load path: test + expectedScanLog: `entry.js: ERROR: No loader is configured for ".bad" files: test.bad `, }) } diff --git a/scripts/js-api-tests.js b/scripts/js-api-tests.js index cfd396ac348..143edcb5e50 100644 --- a/scripts/js-api-tests.js +++ b/scripts/js-api-tests.js @@ -63,6 +63,36 @@ let buildTests = { } }, + // Verify that it's possible to disable a loader by setting it to "default". + // In particular, verify that it's possible to disable the special loader "" + // for extensionless files. + async errorIfExtensionlessLoaderIsDisabled({ esbuild, testDir }) { + let entry = path.join(testDir, 'entry.js'); + let what = path.join(testDir, 'what'); + await writeFileAsync(entry, 'import "./what"') + await writeFileAsync(what, 'foo()') + await esbuild.build({ + entryPoints: [entry], + bundle: true, + write: false, + }) + try { + await esbuild.build({ + entryPoints: [entry], + bundle: true, + write: false, + logLevel: 'silent', + loader: { '': 'default' }, + }) + throw new Error('Expected build failure'); + } catch (e) { + const relPath = path.relative(process.cwd(), what).split(path.sep).join('/') + if (!e.errors || !e.errors[0] || e.errors[0].text !== 'Do not know how to load path: ' + relPath) { + throw e; + } + } + }, + async mangleCacheBuild({ esbuild }) { var result = await esbuild.build({ stdin: {