Reputation: 7297
In the past, I used app-module-path
whenever I wanted to have relative paths in my Node.js apps. If I use ES Modules through the .mjs
format, how do I have the same functionality where a certain directory path becomes relative?
In an alternative way, would I be able to assign an alias to a directory instead so all relative paths are relative to that alias much like how ./
is an alias for a path being relative to the current directory.
Upvotes: 5
Views: 18894
Reputation: 153
A more 2019 version, working for ES Modules.
The easiest way I've found so far is to use the experimental feature --loader
.
I use something like that to be able to require import { SOME_CONSTANT } from '@config'
or import { createConnection } from '@server/connection'
.
The loader code:
import path from 'path'
import fs from 'fs'
export function resolve (specifier, parentModuleURL, defaultResolver) {
specifier = specifier.replace(/^@/, path.resolve('.') + '/src/')
specifier = fs.existsSync(specifier) && fs.lstatSync(specifier).isDirectory() ? `${specifier}/index` : specifier
specifier += '.js'
return defaultResolver(specifier, parentModuleURL)
}
Then node --experimental-modules --loader ./moduleResolver.js ./myScriptWithoutExtension
NOTE: you don't need to use .mjs
extension if you specify a "type": "module"
in the nearest package.json
. You can stay extensionless.
NOTE2: you can replace the src
string with where you usually put your code, you could even have a process.env.NODE_ENV
based resolution.
NOTE3: If you give a directory to @
, it will expect to find an index.js
file.
NOTE4: You can use whateveer you want for aliasing, just replace the regex
Upvotes: 2
Reputation: 222474
It's possible to give aliases to certain paths for CommonJS modules that are loaded with require
by monkey-patching built-in module
module.
ES modules provide a way to change module loading behaviour by specifying custom ES module loader, as explained in this related answer.
This way root source path (which will be specified relatively to loader location) can be mapped to some alias (@
is conventional for front-end projects):
custom-loader.mjs
import path from 'path';
const ROOT_PATH = new URL(path.dirname(import.meta.url) + '/src').pathname;
export function resolve(specifier, parentModuleURL, defaultResolver) {
specifier = specifier.replace(/^@/, ROOT_PATH);
return defaultResolver(specifier, parentModuleURL);
}
Which is used like:
node --experimental-modules --loader ./custom-loader.mjs ./app.mjs
Notice that this provides a behaviour that isn't natural for ES modules, this may affect how this code is treated by other tools like IDEs.
Upvotes: 2