Skip to content

Commit 62a3943

Browse files
committed
fix #1691: support "imports" in "package.json"
1 parent aac02ae commit 62a3943

File tree

6 files changed

+589
-158
lines changed

6 files changed

+589
-158
lines changed

CHANGELOG.md

+41
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,47 @@
22

33
## Unreleased
44

5+
* Add support for `imports` in `package.json` ([#1691](https://github.com/evanw/esbuild/issues/1691))
6+
7+
This release adds basic support for the `imports` field in `package.json`. It behaves similarly to the `exports` field but only applies to import paths that start with `#`. The `imports` field provides a way for a package to remap its own internal imports for itself, while the `exports` field provides a way for a package to remap its external exports for other packages. This is useful because the `imports` field respects the currently-configured conditions which means that the import mapping can change at run-time. For example:
8+
9+
```
10+
$ cat entry.mjs
11+
import '#example'
12+
13+
$ cat package.json
14+
{
15+
"imports": {
16+
"#example": {
17+
"foo": "./example.foo.mjs",
18+
"default": "./example.mjs"
19+
}
20+
}
21+
}
22+
23+
$ cat example.foo.mjs
24+
console.log('foo is enabled')
25+
26+
$ cat example.mjs
27+
console.log('foo is disabled')
28+
29+
$ node entry.mjs
30+
foo is disabled
31+
32+
$ node --conditions=foo entry.mjs
33+
foo is enabled
34+
```
35+
36+
Now that esbuild supports this feature too, import paths starting with `#` and any provided conditions will be respected when bundling:
37+
38+
```
39+
$ esbuild --bundle entry.mjs | node
40+
foo is disabled
41+
42+
$ esbuild --conditions=foo --bundle entry.mjs | node
43+
foo is enabled
44+
```
45+
546
* Fix using `npm rebuild` with the `esbuild` package ([#1703](https://github.com/evanw/esbuild/issues/1703))
647

748
Version 0.13.4 accidentally introduced a regression in the install script where running `npm rebuild` multiple times could fail after the second time. The install script creates a copy of the binary executable using [`link`](https://man7.org/linux/man-pages/man2/link.2.html) followed by [`rename`](https://www.man7.org/linux/man-pages/man2/rename.2.html). Using `link` creates a hard link which saves space on the file system, and `rename` is used for safety since it atomically replaces the destination.

internal/bundler/bundler_packagejson_test.go

+159
Original file line numberDiff line numberDiff line change
@@ -1929,3 +1929,162 @@ Users/user/project/src/entry.js: note: Import from "pkg/extra/other/file.js" to
19291929
`,
19301930
})
19311931
}
1932+
1933+
func TestPackageJsonImports(t *testing.T) {
1934+
packagejson_suite.expectBundled(t, bundled{
1935+
files: map[string]string{
1936+
"/Users/user/project/src/entry.js": `
1937+
import '#top-level'
1938+
import '#nested/path.js'
1939+
import '#star/c.js'
1940+
import '#slash/d.js'
1941+
`,
1942+
"/Users/user/project/src/package.json": `
1943+
{
1944+
"imports": {
1945+
"#top-level": "./a.js",
1946+
"#nested/path.js": "./b.js",
1947+
"#star/*": "./some-star/*",
1948+
"#slash/": "./some-slash/"
1949+
}
1950+
}
1951+
`,
1952+
"/Users/user/project/src/a.js": `console.log('a.js')`,
1953+
"/Users/user/project/src/b.js": `console.log('b.js')`,
1954+
"/Users/user/project/src/some-star/c.js": `console.log('c.js')`,
1955+
"/Users/user/project/src/some-slash/d.js": `console.log('d.js')`,
1956+
},
1957+
entryPaths: []string{"/Users/user/project/src/entry.js"},
1958+
options: config.Options{
1959+
Mode: config.ModeBundle,
1960+
AbsOutputFile: "/Users/user/project/out.js",
1961+
},
1962+
})
1963+
}
1964+
1965+
func TestPackageJsonImportsRemapToOtherPackage(t *testing.T) {
1966+
packagejson_suite.expectBundled(t, bundled{
1967+
files: map[string]string{
1968+
"/Users/user/project/src/entry.js": `
1969+
import '#top-level'
1970+
import '#nested/path.js'
1971+
import '#star/c.js'
1972+
import '#slash/d.js'
1973+
`,
1974+
"/Users/user/project/src/package.json": `
1975+
{
1976+
"imports": {
1977+
"#top-level": "pkg/a.js",
1978+
"#nested/path.js": "pkg/b.js",
1979+
"#star/*": "pkg/some-star/*",
1980+
"#slash/": "pkg/some-slash/"
1981+
}
1982+
}
1983+
`,
1984+
"/Users/user/project/src/node_modules/pkg/a.js": `console.log('a.js')`,
1985+
"/Users/user/project/src/node_modules/pkg/b.js": `console.log('b.js')`,
1986+
"/Users/user/project/src/node_modules/pkg/some-star/c.js": `console.log('c.js')`,
1987+
"/Users/user/project/src/node_modules/pkg/some-slash/d.js": `console.log('d.js')`,
1988+
},
1989+
entryPaths: []string{"/Users/user/project/src/entry.js"},
1990+
options: config.Options{
1991+
Mode: config.ModeBundle,
1992+
AbsOutputFile: "/Users/user/project/out.js",
1993+
},
1994+
})
1995+
}
1996+
1997+
func TestPackageJsonImportsErrorMissingRemappedPackage(t *testing.T) {
1998+
packagejson_suite.expectBundled(t, bundled{
1999+
files: map[string]string{
2000+
"/Users/user/project/src/entry.js": `
2001+
import '#foo'
2002+
`,
2003+
"/Users/user/project/src/package.json": `
2004+
{
2005+
"imports": {
2006+
"#foo": "bar"
2007+
}
2008+
}
2009+
`,
2010+
},
2011+
entryPaths: []string{"/Users/user/project/src/entry.js"},
2012+
options: config.Options{
2013+
Mode: config.ModeBundle,
2014+
AbsOutputFile: "/Users/user/project/out.js",
2015+
},
2016+
expectedScanLog: `Users/user/project/src/entry.js: error: Could not resolve "#foo" (mark it as external to exclude it from the bundle)
2017+
Users/user/project/src/package.json: note: The remapped path "bar" could not be resolved
2018+
`,
2019+
})
2020+
}
2021+
2022+
func TestPackageJsonImportsInvalidPackageConfiguration(t *testing.T) {
2023+
packagejson_suite.expectBundled(t, bundled{
2024+
files: map[string]string{
2025+
"/Users/user/project/src/entry.js": `
2026+
import '#foo'
2027+
`,
2028+
"/Users/user/project/src/package.json": `
2029+
{
2030+
"imports": "#foo"
2031+
}
2032+
`,
2033+
},
2034+
entryPaths: []string{"/Users/user/project/src/entry.js"},
2035+
options: config.Options{
2036+
Mode: config.ModeBundle,
2037+
AbsOutputFile: "/Users/user/project/out.js",
2038+
},
2039+
expectedScanLog: `Users/user/project/src/entry.js: error: Could not resolve "#foo" (mark it as external to exclude it from the bundle)
2040+
Users/user/project/src/package.json: note: The package configuration has an invalid value here
2041+
Users/user/project/src/package.json: warning: The value for "imports" must be an object
2042+
`,
2043+
})
2044+
}
2045+
2046+
func TestPackageJsonImportsErrorEqualsHash(t *testing.T) {
2047+
packagejson_suite.expectBundled(t, bundled{
2048+
files: map[string]string{
2049+
"/Users/user/project/src/entry.js": `
2050+
import '#'
2051+
`,
2052+
"/Users/user/project/src/package.json": `
2053+
{
2054+
"imports": {}
2055+
}
2056+
`,
2057+
},
2058+
entryPaths: []string{"/Users/user/project/src/entry.js"},
2059+
options: config.Options{
2060+
Mode: config.ModeBundle,
2061+
AbsOutputFile: "/Users/user/project/out.js",
2062+
},
2063+
expectedScanLog: `Users/user/project/src/entry.js: error: Could not resolve "#" (mark it as external to exclude it from the bundle)
2064+
Users/user/project/src/package.json: note: This "imports" map was ignored because the module specifier "#" is invalid
2065+
`,
2066+
})
2067+
}
2068+
2069+
func TestPackageJsonImportsErrorStartsWithHashSlash(t *testing.T) {
2070+
packagejson_suite.expectBundled(t, bundled{
2071+
files: map[string]string{
2072+
"/Users/user/project/src/entry.js": `
2073+
import '#/foo'
2074+
`,
2075+
"/Users/user/project/src/package.json": `
2076+
{
2077+
"imports": {}
2078+
}
2079+
`,
2080+
},
2081+
entryPaths: []string{"/Users/user/project/src/entry.js"},
2082+
options: config.Options{
2083+
Mode: config.ModeBundle,
2084+
AbsOutputFile: "/Users/user/project/out.js",
2085+
},
2086+
expectedScanLog: `Users/user/project/src/entry.js: error: Could not resolve "#/foo" (mark it as external to exclude it from the bundle)
2087+
Users/user/project/src/package.json: note: This "imports" map was ignored because the module specifier "#/foo" is invalid
2088+
`,
2089+
})
2090+
}

internal/bundler/snapshots/snapshots_packagejson.txt

+30
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,36 @@ var require_require = __commonJS({
544544
// Users/user/project/src/entry.js
545545
require_require();
546546

547+
================================================================================
548+
TestPackageJsonImports
549+
---------- /Users/user/project/out.js ----------
550+
// Users/user/project/src/a.js
551+
console.log("a.js");
552+
553+
// Users/user/project/src/b.js
554+
console.log("b.js");
555+
556+
// Users/user/project/src/some-star/c.js
557+
console.log("c.js");
558+
559+
// Users/user/project/src/some-slash/d.js
560+
console.log("d.js");
561+
562+
================================================================================
563+
TestPackageJsonImportsRemapToOtherPackage
564+
---------- /Users/user/project/out.js ----------
565+
// Users/user/project/src/node_modules/pkg/a.js
566+
console.log("a.js");
567+
568+
// Users/user/project/src/node_modules/pkg/b.js
569+
console.log("b.js");
570+
571+
// Users/user/project/src/node_modules/pkg/some-star/c.js
572+
console.log("c.js");
573+
574+
// Users/user/project/src/node_modules/pkg/some-slash/d.js
575+
console.log("d.js");
576+
547577
================================================================================
548578
TestPackageJsonMain
549579
---------- /Users/user/project/out.js ----------

0 commit comments

Comments
 (0)