Add option for browser style origin based import resolution
Would you consider a browser style module resolution mode like this?
This allows setting a base (origin) directory so that any import xyz from '/util/item.js' always resolves relative to that directory.
I believe Node discourages this and makes it difficult, but browsers happily load unbundled es6 modules this way against the origin url. It makes it much easier to read and maintain modules that commonly get imported as utilities deeper in a directory structure, instead of having to write import xyz from '../../../util/item.js'.
I've got a project full of es6 imports like this, so I wrote this up. It is rather similar to the baseUrl config, but that seems by design to work only for Node module paths which don't really make sense in a browser context. I figured best not to muddy the concepts.
A bit niche, but might be useful to others who don't work with Node. If I can do anything to make this more useful for you, I'm all ears!
I believe the existing path alias feature already does this. You can configure a tsconfig.json or jsconfig.json file with the paths and baseUrl parameters like this:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"/*": ["./*"]
}
}
}
Here's the example I used for testing:
// ./entry.js
import xyz from '/util/item.js'
console.log(xyz)
// ./util/item.js
export default 123
Building these three files with esbuild --bundle entry.js gives this:
(() => {
// util/item.js
const item_default = 123;
// entry.js
console.log(item_default);
})();
This also appears to work with the TypeScript compiler, if you want type checking. The existing approach of configuring path aliases seems superior to me from a new option like this because a) it follows an existing standard that already integrates with other tools and b) it allows you to configure this differently for different directories if you need to instead of only being able to configure it once globally.
I just tried that jsconfig.json (and a number of others before I wrote the above) with no luck, I still get no resolution of those sorts of paths.
A config file does seem like a better solution and originally that is what I tried to do with jsconfig.json. I dug into the code (very possible I'm missing something, never worked in Go and still trying to understand the codebase) and it looked like this use case was being explicitly not supported for non-module-paths:
func (r *resolver) resolveWithoutSymlinks(sourcePath string, importPath string) (string, ResolveStatus) {
...
if IsNonModulePath(importPath) {
if absolute, ok := r.loadAsFileOrDirectory(r.fs.Join(sourceDir, importPath)); ok {
result = absolute
} else {
return "", ResolveMissing
}
} else {
...
if absolute, ok := r.loadNodeModules(importPath, sourceDirInfo); ok {
IsNonModulePath:
func IsNonModulePath(path string) bool {
return strings.HasPrefix(path, "/") || strings.HasPrefix(path, "./") ||
strings.HasPrefix(path, "../") || path == "." || path == ".."
}
I traced through this a few times, and it looked to me like all the jsconfig.json stuff only applied to modules in the node sense, that IsNonModulePath would return false on, going through loadNodeModules which actually seems to use js/tsconfig.json.
I take it the jsconfig.json should actually work then? My thinking was not to try to pollute that functionality if those were existing standards not meant to support this.
I'll try to construct a test case to reproduce what I'm seeing with your jsconfig.json example or trace through and figure out what's happening.
this is so fun I'm going to watch this for a while see how you how you come up