Skip to content

Commit c1a196c

Browse files
jsumnersUlisesGascon
authored andcommittedDec 11, 2023
esm: add import.meta.dirname and import.meta.filename
PR-URL: #48740 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Jiawen Geng <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Stephen Belanger <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Geoffrey Booth <[email protected]>
1 parent 91a0944 commit c1a196c

File tree

7 files changed

+77
-3
lines changed

7 files changed

+77
-3
lines changed
 

‎benchmark/fixtures/esm-dir-file.mjs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import assert from 'assert';
2+
assert.ok(import.meta.dirname);
3+
assert.ok(import.meta.filename);
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(async function () {
2+
for (let i = 0; i < 1000; i += 1) {
3+
await import(`./esm-dir-file.mjs?i=${i}`);
4+
}
5+
}());

‎benchmark/misc/startup.js

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const bench = common.createBenchmark(main, {
99
script: [
1010
'benchmark/fixtures/require-builtins',
1111
'test/fixtures/semicolon',
12+
'benchmark/fixtures/load-esm-dir-file',
1213
],
1314
mode: ['process', 'worker'],
1415
count: [30],

‎doc/api/esm.md

+34-1
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,35 @@ modules it can be used to load ES modules.
332332
The `import.meta` meta property is an `Object` that contains the following
333333
properties.
334334

335+
### `import.meta.dirname`
336+
337+
<!-- YAML
338+
added: REPLACEME
339+
-->
340+
341+
> Stability: 1.2 - Release candidate
342+
343+
* {string} The directory name of the current module. This is the same as the
344+
[`path.dirname()`][] of the [`import.meta.filename`][].
345+
346+
> **Caveat**: only present on `file:` modules.
347+
348+
### `import.meta.filename`
349+
350+
<!-- YAML
351+
added: REPLACEME
352+
-->
353+
354+
> Stability: 1.2 - Release candidate
355+
356+
* {string} The full absolute path and filename of the current module, with
357+
* symlinks resolved.
358+
* This is the same as the [`url.fileURLToPath()`][] of the
359+
* [`import.meta.url`][].
360+
361+
> **Caveat** only local modules support this property. Modules not using the
362+
> `file:` protocol will not provide it.
363+
335364
### `import.meta.url`
336365

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

528557
`__filename` and `__dirname` use cases can be replicated via
529-
[`import.meta.url`][].
558+
[`import.meta.filename`][] and [`import.meta.dirname`][].
530559

531560
#### No Addon Loading
532561

@@ -1107,13 +1136,17 @@ resolution for ESM specifiers is [commonjs-extension-resolution-loader][].
11071136
[`data:` URLs]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
11081137
[`export`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
11091138
[`import()`]: #import-expressions
1139+
[`import.meta.dirname`]: #importmetadirname
1140+
[`import.meta.filename`]: #importmetafilename
11101141
[`import.meta.resolve`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta/resolve
11111142
[`import.meta.url`]: #importmetaurl
11121143
[`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
11131144
[`module.createRequire()`]: module.md#modulecreaterequirefilename
11141145
[`module.syncBuiltinESMExports()`]: module.md#modulesyncbuiltinesmexports
11151146
[`package.json`]: packages.md#nodejs-packagejson-field-definitions
1147+
[`path.dirname()`]: path.md#pathdirnamepath
11161148
[`process.dlopen`]: process.md#processdlopenmodule-filename-flags
1149+
[`url.fileURLToPath()`]: url.md#urlfileurltopathurl
11171150
[cjs-module-lexer]: https://github.com/nodejs/cjs-module-lexer/tree/1.2.2
11181151
[commonjs-extension-resolution-loader]: https://github.com/nodejs/loaders-test/tree/main/commonjs-extension-resolution-loader
11191152
[custom https loader]: module.md#import-from-https

‎lib/internal/modules/esm/initialize_import_meta.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
'use strict';
22

3+
const { StringPrototypeStartsWith } = primordials;
34
const { getOptionValue } = require('internal/options');
5+
const { fileURLToPath } = require('internal/url');
6+
const { dirname } = require('path');
47
const experimentalImportMetaResolve = getOptionValue('--experimental-import-meta-resolve');
58

69
/**
@@ -45,12 +48,20 @@ function createImportMetaResolve(defaultParentURL, loader, allowParentURL) {
4548
* @param {object} meta
4649
* @param {{url: string}} context
4750
* @param {typeof import('./loader.js').ModuleLoader} loader Reference to the current module loader
48-
* @returns {{url: string, resolve?: Function}}
51+
* @returns {{dirname?: string, filename?: string, url: string, resolve?: Function}}
4952
*/
5053
function initializeImportMeta(meta, context, loader) {
5154
const { url } = context;
5255

5356
// Alphabetical
57+
if (StringPrototypeStartsWith(url, 'file:') === true) {
58+
// These only make sense for locally loaded modules,
59+
// i.e. network modules are not supported.
60+
const filePath = fileURLToPath(url);
61+
meta.dirname = dirname(filePath);
62+
meta.filename = filePath;
63+
}
64+
5465
if (!loader || loader.allowImportMetaResolve) {
5566
meta.resolve = createImportMetaResolve(url, loader, experimentalImportMetaResolve);
5667
}

‎test/es-module/test-esm-import-meta.mjs

+15-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import assert from 'assert';
33

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

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

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

1919
const urlReg = /^file:\/\/\/.*\/test\/es-module\/test-esm-import-meta\.mjs$/;
2020
assert(import.meta.url.match(urlReg));
21+
22+
// Match *nix paths: `/some/path/test/es-module`
23+
// Match Windows paths: `d:\\some\\path\\test\\es-module`
24+
const dirReg = /^(\/|\w:\\).*(\/|\\)test(\/|\\)es-module$/;
25+
assert.match(import.meta.dirname, dirReg);
26+
27+
// Match *nix paths: `/some/path/test/es-module/test-esm-import-meta.mjs`
28+
// Match Windows paths: `d:\\some\\path\\test\\es-module\\test-esm-import-meta.js`
29+
const fileReg = /^(\/|\w:\\).*(\/|\\)test(\/|\\)es-module(\/|\\)test-esm-import-meta\.mjs$/;
30+
assert.match(import.meta.filename, fileReg);
31+
32+
// Verify that `data:` imports do not behave like `file:` imports.
33+
import dataDirname from 'data:text/javascript,export default "dirname" in import.meta';
34+
assert.strictEqual(dataDirname, false);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include <node_api.h>
2+
3+
EXTERN_C_START
4+
napi_value Init(napi_env env, napi_value exports);
5+
EXTERN_C_END
6+
7+
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

0 commit comments

Comments
 (0)
Please sign in to comment.