Skip to content

Commit

Permalink
esm: add import.meta.dirname and import.meta.filename
Browse files Browse the repository at this point in the history
PR-URL: #48740
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
  • Loading branch information
jsumners authored and UlisesGascon committed Dec 11, 2023
1 parent 91a0944 commit c1a196c
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 3 deletions.
3 changes: 3 additions & 0 deletions benchmark/fixtures/esm-dir-file.mjs
@@ -0,0 +1,3 @@
import assert from 'assert';
assert.ok(import.meta.dirname);
assert.ok(import.meta.filename);
5 changes: 5 additions & 0 deletions benchmark/fixtures/load-esm-dir-file.js
@@ -0,0 +1,5 @@
(async function () {
for (let i = 0; i < 1000; i += 1) {
await import(`./esm-dir-file.mjs?i=${i}`);
}
}());
1 change: 1 addition & 0 deletions benchmark/misc/startup.js
Expand Up @@ -9,6 +9,7 @@ const bench = common.createBenchmark(main, {
script: [
'benchmark/fixtures/require-builtins',
'test/fixtures/semicolon',
'benchmark/fixtures/load-esm-dir-file',
],
mode: ['process', 'worker'],
count: [30],
Expand Down
35 changes: 34 additions & 1 deletion doc/api/esm.md
Expand Up @@ -332,6 +332,35 @@ 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
-->

> Stability: 1.2 - Release candidate
* {string} The directory name of the current module. This is the same as the
[`path.dirname()`][] of the [`import.meta.filename`][].

> **Caveat**: only present on `file:` modules.
### `import.meta.filename`

<!-- YAML
added: REPLACEME
-->

> Stability: 1.2 - Release candidate
* {string} The full absolute path and filename of the current module, with
* symlinks resolved.
* This is the same as the [`url.fileURLToPath()`][] of the
* [`import.meta.url`][].

> **Caveat** only local modules support this property. Modules not using the
> `file:` protocol will not provide it.
### `import.meta.url`

* {string} The absolute `file:` URL of the module.
Expand Down Expand Up @@ -526,7 +555,7 @@ If needed, a `require` function can be constructed within an ES module using
These CommonJS variables are not available in ES modules.

`__filename` and `__dirname` use cases can be replicated via
[`import.meta.url`][].
[`import.meta.filename`][] and [`import.meta.dirname`][].

#### No Addon Loading

Expand Down Expand Up @@ -1107,13 +1136,17 @@ resolution 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`]: #importmetadirname
[`import.meta.filename`]: #importmetafilename
[`import.meta.resolve`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta/resolve
[`import.meta.url`]: #importmetaurl
[`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
[`module.createRequire()`]: module.md#modulecreaterequirefilename
[`module.syncBuiltinESMExports()`]: module.md#modulesyncbuiltinesmexports
[`package.json`]: packages.md#nodejs-packagejson-field-definitions
[`path.dirname()`]: path.md#pathdirnamepath
[`process.dlopen`]: process.md#processdlopenmodule-filename-flags
[`url.fileURLToPath()`]: url.md#urlfileurltopathurl
[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
[custom https loader]: module.md#import-from-https
Expand Down
13 changes: 12 additions & 1 deletion lib/internal/modules/esm/initialize_import_meta.js
@@ -1,6 +1,9 @@
'use strict';

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

/**
Expand Down Expand Up @@ -45,12 +48,20 @@ function createImportMetaResolve(defaultParentURL, loader, allowParentURL) {
* @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
if (StringPrototypeStartsWith(url, 'file:') === true) {
// These only make sense for locally loaded modules,
// i.e. network modules are not supported.
const filePath = fileURLToPath(url);
meta.dirname = dirname(filePath);
meta.filename = filePath;
}

if (!loader || loader.allowImportMetaResolve) {
meta.resolve = createImportMetaResolve(url, loader, experimentalImportMetaResolve);
}
Expand Down
16 changes: 15 additions & 1 deletion test/es-module/test-esm-import-meta.mjs
Expand Up @@ -3,7 +3,7 @@ import assert from 'assert';

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

const keys = ['resolve', 'url'];
const keys = ['dirname', 'filename', 'resolve', 'url'];
assert.deepStrictEqual(Reflect.ownKeys(import.meta), keys);

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

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

// Match *nix paths: `/some/path/test/es-module`
// Match Windows paths: `d:\\some\\path\\test\\es-module`
const dirReg = /^(\/|\w:\\).*(\/|\\)test(\/|\\)es-module$/;
assert.match(import.meta.dirname, dirReg);

// Match *nix paths: `/some/path/test/es-module/test-esm-import-meta.mjs`
// Match Windows paths: `d:\\some\\path\\test\\es-module\\test-esm-import-meta.js`
const fileReg = /^(\/|\w:\\).*(\/|\\)test(\/|\\)es-module(\/|\\)test-esm-import-meta\.mjs$/;
assert.match(import.meta.filename, fileReg);

// Verify that `data:` imports do not behave like `file:` imports.
import dataDirname from 'data:text/javascript,export default "dirname" in import.meta';
assert.strictEqual(dataDirname, false);
7 changes: 7 additions & 0 deletions test/js-native-api/test_cannot_run_js/entry_point.c
@@ -0,0 +1,7 @@
#include <node_api.h>

EXTERN_C_START
napi_value Init(napi_env env, napi_value exports);
EXTERN_C_END

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

0 comments on commit c1a196c

Please sign in to comment.