diff --git a/CHANGELOG.md b/CHANGELOG.md index 884273be1cb..f6d8503ee07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,10 @@ Note that using this feature means esbuild will potentially do a lot of file system I/O to find all possible files that might match the pattern. This is by design, and is not a bug. If this is a concern, I recommend either avoiding the `/**/` pattern (e.g. by not putting a `/` before a wildcard) or using this feature only in directory subtrees which do not have many files that don't match the pattern (e.g. making a subdirectory for your JSON files and explicitly including that subdirectory in the pattern). +* Path aliases in `tsconfig.json` no longer count as packages ([#2792](https://github.com/evanw/esbuild/issues/2792), [#3003](https://github.com/evanw/esbuild/issues/3003), [#3160](https://github.com/evanw/esbuild/issues/3160), [#3238](https://github.com/evanw/esbuild/issues/3238)) + + Setting `--packages=external` tells esbuild to make all import paths external when they look like a package path. For example, an import of `./foo/bar` is not a package path and won't be external while an import of `foo/bar` is a package path and will be external. However, the [`paths` field](https://www.typescriptlang.org/tsconfig#paths) in `tsconfig.json` allows you to create import paths that look like package paths but that do not resolve to packages. People do not want these paths to count as package paths. So with this release, the behavior of `--packages=external` has been changed to happen after the `tsconfig.json` path remapping step. + * Use the `local-css` loader for `.module.css` files by default ([#20](https://github.com/evanw/esbuild/issues/20)) With this release the `css` loader is still used for `.css` files except that `.module.css` files now use the `local-css` loader. This is a common convention in the web development community. If you need `.module.css` files to use the `css` loader instead, then you can override this behavior with `--loader:.module.css=css`. diff --git a/internal/bundler_tests/bundler_tsconfig_test.go b/internal/bundler_tests/bundler_tsconfig_test.go index d58e9680bd1..fea7d775921 100644 --- a/internal/bundler_tests/bundler_tsconfig_test.go +++ b/internal/bundler_tests/bundler_tsconfig_test.go @@ -2457,3 +2457,34 @@ func TestTsconfigIgnoreInsideNodeModules(t *testing.T) { }, }) } + +func TestTsconfigJsonPackagesExternal(t *testing.T) { + tsconfig_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/Users/user/project/src/entry.js": ` + import truePkg from 'pkg1' + import falsePkg from 'internal/pkg2' + truePkg() + falsePkg() + `, + "/Users/user/project/src/tsconfig.json": ` + { + "compilerOptions": { + "paths": { + "internal/*": ["./stuff/*"] + } + } + } + `, + "/Users/user/project/src/stuff/pkg2.js": ` + export default success + `, + }, + entryPaths: []string{"/Users/user/project/src/entry.js"}, + options: config.Options{ + Mode: config.ModeBundle, + AbsOutputFile: "/Users/user/project/out.js", + ExternalPackages: true, + }, + }) +} diff --git a/internal/bundler_tests/snapshots/snapshots_tsconfig.txt b/internal/bundler_tests/snapshots/snapshots_tsconfig.txt index b70cf0d70aa..fb5ae67b20f 100644 --- a/internal/bundler_tests/snapshots/snapshots_tsconfig.txt +++ b/internal/bundler_tests/snapshots/snapshots_tsconfig.txt @@ -319,6 +319,19 @@ TestTsconfigJsonOverrideNodeModules // Users/user/project/other/foo-good.ts console.log("good"); +================================================================================ +TestTsconfigJsonPackagesExternal +---------- /Users/user/project/out.js ---------- +// Users/user/project/src/entry.js +import truePkg from "pkg1"; + +// Users/user/project/src/stuff/pkg2.js +var pkg2_default = success; + +// Users/user/project/src/entry.js +truePkg(); +pkg2_default(); + ================================================================================ TestTsconfigJsonTrailingCommaAllowed ---------- /Users/user/project/out.js ---------- diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index 4b20f2fc0e2..0fa0effbf6d 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -436,19 +436,6 @@ func (res *Resolver) Resolve(sourceDir string, importPath string, kind ast.Impor }, debugMeta } - // "import 'pkg'" when all packages are external (vs. "import './pkg'") - if r.options.ExternalPackages && IsPackagePath(importPath) && !r.fs.IsAbs(importPath) && !strings.HasPrefix(importPath, "#") { - if r.debugLogs != nil { - r.debugLogs.addNote("Marking this path as external because it's a package path") - } - - r.flushDebugLogs(flushDueToSuccess) - return &ResolveResult{ - PathPair: PathPair{Primary: logger.Path{Text: importPath}}, - IsExternal: true, - }, debugMeta - } - // "import fs from 'fs'" if r.options.Platform == config.PlatformNode && BuiltInNodeModules[importPath] { if r.debugLogs != nil { @@ -1010,12 +997,48 @@ func (r resolverQuery) resolveWithoutSymlinks(sourceDir string, sourceDirInfo *d } } + // First, check path overrides from the nearest enclosing TypeScript "tsconfig.json" file + if sourceDirInfo != nil { + if tsConfigJSON := r.tsConfigForDir(sourceDirInfo); tsConfigJSON != nil { + // Try path substitutions first + if tsConfigJSON.Paths != nil { + if absolute, ok, diffCase := r.matchTSConfigPaths(tsConfigJSON, importPath); ok { + return &ResolveResult{PathPair: absolute, DifferentCase: diffCase} + } + } + + // Try looking up the path relative to the base URL + if tsConfigJSON.BaseURL != nil { + basePath := r.fs.Join(*tsConfigJSON.BaseURL, importPath) + if absolute, ok, diffCase := r.loadAsFileOrDirectory(basePath); ok { + return &ResolveResult{PathPair: absolute, DifferentCase: diffCase} + } + } + } + } + // Check both relative and package paths for CSS URL tokens, with relative // paths taking precedence over package paths to match Webpack behavior. isPackagePath := IsPackagePath(importPath) checkRelative := !isPackagePath || r.kind.IsFromCSS() checkPackage := isPackagePath + // "import 'pkg'" when all packages are external (vs. "import './pkg'"). This + // is deliberately done after we check for "tsconfig.json" aliases because + // people want to be able to make things look like packages but have them not + // be packages. + if r.options.ExternalPackages && isPackagePath && !strings.HasPrefix(importPath, "#") { + if r.debugLogs != nil { + r.debugLogs.addNote("Marking this path as external because it's a package path") + } + + r.flushDebugLogs(flushDueToSuccess) + return &ResolveResult{ + PathPair: PathPair{Primary: logger.Path{Text: importPath}}, + IsExternal: true, + } + } + if checkRelative { absPath := r.fs.Join(sourceDir, importPath) @@ -2295,24 +2318,6 @@ func (r resolverQuery) loadNodeModules(importPath string, dirInfo *dirInfo, forb defer r.debugLogs.decreaseIndent() } - // First, check path overrides from the nearest enclosing TypeScript "tsconfig.json" file - if tsConfigJSON := r.tsConfigForDir(dirInfo); tsConfigJSON != nil { - // Try path substitutions first - if tsConfigJSON.Paths != nil { - if absolute, ok, diffCase := r.matchTSConfigPaths(tsConfigJSON, importPath); ok { - return absolute, true, diffCase - } - } - - // Try looking up the path relative to the base URL - if tsConfigJSON.BaseURL != nil { - basePath := r.fs.Join(*tsConfigJSON.BaseURL, importPath) - if absolute, ok, diffCase := r.loadAsFileOrDirectory(basePath); ok { - return absolute, true, diffCase - } - } - } - // Find the parent directory with the "package.json" file dirInfoPackageJSON := dirInfo for dirInfoPackageJSON != nil && dirInfoPackageJSON.packageJSON == nil {