Skip to content

Commit

Permalink
esm: add __dirname and __filename
Browse files Browse the repository at this point in the history
  • Loading branch information
jsumners committed Jul 11, 2023
1 parent 41f7056 commit 0946c5a
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 9 deletions.
36 changes: 29 additions & 7 deletions doc/api/esm.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,31 @@ modules it can be used to load ES modules.
The `import.meta` meta property is an `Object` that contains the following
properties.

### `import.meta.__dirname`

<!-- YAML
added: REPLACEME
-->

* {string} The directory name of the current module. This is the same as the
[`path.dirname()`][] of the [`import.meta.__filename`][].

> **Caveat** only local modules support this property. Network modules will
> not provide it.
### `import.meta.__filename`

<!-- YAML
added: REPLACEME
-->

* {string} The file name of the current module. This is the current module
file's absolute path with symlinks resolved. This is the same as the
[`url.fileURLToPath()`][] of the [`import.meta.url`][].

> **Caveat** only local modules support this property. Network modules will
> not provide it.
### `import.meta.url`

* {string} The absolute `file:` URL of the module.
Expand Down Expand Up @@ -483,13 +508,6 @@ In most cases, the ES module `import` can be used to load CommonJS modules.
If needed, a `require` function can be constructed within an ES module using
[`module.createRequire()`][].
#### No `__filename` or `__dirname`
These CommonJS variables are not available in ES modules.
`__filename` and `__dirname` use cases can be replicated via
[`import.meta.url`][].
#### No Addon Loading
[Addons][] are not currently supported with ES module imports.
Expand Down Expand Up @@ -1606,17 +1624,21 @@ for ESM specifiers is [commonjs-extension-resolution-loader][].
[`data:` URLs]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
[`export`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
[`import()`]: #import-expressions
[`import.meta.__dirname`]: #importmeta__dirname
[`import.meta.__filename`]: #importmeta__filename
[`import.meta.resolve`]: #importmetaresolvespecifier-parent
[`import.meta.url`]: #importmetaurl
[`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
[`module.createRequire()`]: module.md#modulecreaterequirefilename
[`module.register()`]: module.md#moduleregister
[`module.syncBuiltinESMExports()`]: module.md#modulesyncbuiltinesmexports
[`package.json`]: packages.md#nodejs-packagejson-field-definitions
[`path.dirname()`]: path.md#dirname
[`port.ref()`]: https://nodejs.org/dist/latest-v17.x/docs/api/worker_threads.html#portref
[`port.unref()`]: https://nodejs.org/dist/latest-v17.x/docs/api/worker_threads.html#portunref
[`process.dlopen`]: process.md#processdlopenmodule-filename-flags
[`string`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
[`url.fileURLToPath()`]: url.md#fileurltopath
[`util.TextDecoder`]: util.md#class-utiltextdecoder
[cjs-module-lexer]: https://github.com/nodejs/cjs-module-lexer/tree/1.2.2
[commonjs-extension-resolution-loader]: https://github.com/nodejs/loaders-test/tree/main/commonjs-extension-resolution-loader
Expand Down
28 changes: 27 additions & 1 deletion lib/internal/modules/esm/initialize_import_meta.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use strict';

const { getOptionValue } = require('internal/options');
const { fileURLToPath } = require('url');
const { dirname } = require('path');
const experimentalImportMetaResolve = getOptionValue('--experimental-import-meta-resolve');

/**
Expand Down Expand Up @@ -32,12 +34,16 @@ function createImportMetaResolve(defaultParentUrl, loader) {
* @param {object} meta
* @param {{url: string}} context
* @param {typeof import('./loader.js').ModuleLoader} loader Reference to the current module loader
* @returns {{url: string, resolve?: Function}}
* @returns {{__dirname?: string, __filename?: string, url: string, resolve?: Function}}
*/
function initializeImportMeta(meta, context, loader) {
const { url } = context;

// Alphabetical
const moduleMeta = resolveModuleMeta(url);
meta.__dirname = moduleMeta.__dirname;
meta.__filename = moduleMeta.__filename;

if (experimentalImportMetaResolve && loader.loaderType !== 'internal') {
meta.resolve = createImportMetaResolve(url, loader);
}
Expand All @@ -47,6 +53,26 @@ function initializeImportMeta(meta, context, loader) {
return meta;
}

/**
* Inspect the module URL and convert it to `__dirname` and `__filename`
* references if it does not point to a network module.
* @param {string} url
* @returns {{__dirname?: string, __filename?: string}}
*/
function resolveModuleMeta(url) {
if (url.slice(0, 7) !== 'file://') {
// These only make sense for locally loaded modules,
// i.e. network modules are not supported.
return { __dirname: undefined, __filename: undefined };
}

const filePath = fileURLToPath(url);
return {
__dirname: dirname(filePath),
__filename: filePath,
};
}

module.exports = {
initializeImportMeta,
};
8 changes: 7 additions & 1 deletion test/es-module/test-esm-import-meta.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import assert from 'assert';

assert.strictEqual(Object.getPrototypeOf(import.meta), null);

const keys = ['url'];
const keys = ['__dirname', '__filename', 'url'];
assert.deepStrictEqual(Reflect.ownKeys(import.meta), keys);

const descriptors = Object.getOwnPropertyDescriptors(import.meta);
Expand All @@ -18,3 +18,9 @@ for (const descriptor of Object.values(descriptors)) {

const urlReg = /^file:\/\/\/.*\/test\/es-module\/test-esm-import-meta\.mjs$/;
assert(import.meta.url.match(urlReg));

const dirReg = /^\/.*\/test\/es-module$/;
assert(import.meta.__dirname.match(dirReg));

const fileReg = /^\/.*\/test\/es-module\/test-esm-import-meta\.mjs$/;
assert(import.meta.__filename.match(fileReg));

0 comments on commit 0946c5a

Please sign in to comment.