esm: add import.meta.dirname and import.meta.filename

PR-URL: https://github.com/nodejs/node/pull/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>
This commit is contained in:
James Sumners 2023-10-31 17:11:15 -04:00 committed by GitHub
parent 2a49973703
commit 3daa0a6c04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 77 additions and 3 deletions

View File

@ -0,0 +1,3 @@
import assert from 'assert';
assert.ok(import.meta.dirname);
assert.ok(import.meta.filename);

View File

@ -0,0 +1,5 @@
(async function () {
for (let i = 0; i < 1000; i += 1) {
await import(`./esm-dir-file.mjs?i=${i}`);
}
}());

View File

@ -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],

View File

@ -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.
@ -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
@ -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

View File

@ -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');
/**
@ -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);
}

View File

@ -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);
@ -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);

View File

@ -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)