diff --git a/lib/NodeStuffPlugin.js b/lib/NodeStuffPlugin.js index 68ebd5ccb..43b58f982 100644 --- a/lib/NodeStuffPlugin.js +++ b/lib/NodeStuffPlugin.js @@ -16,7 +16,8 @@ const ConstDependency = require("./dependencies/ConstDependency"); const ExternalModuleDependency = require("./dependencies/ExternalModuleDependency"); const { evaluateToString, - expressionIsUnsupported + expressionIsUnsupported, + toConstantDependency } = require("./javascript/JavascriptParserHelpers"); const { relative } = require("./util/fs"); const { parseResource } = require("./util/identifier"); @@ -102,6 +103,57 @@ class NodeStuffPlugin { }); } + /** + * Helper function to define import.meta properties + * @param {string} propertyName The full property name (e.g., "import.meta.dirname") + * @param {() => string} valueGetter Function to get the property value + * @param {string=} warning The warning, if applicable + */ + const defineImportMetaProperty = ( + propertyName, + valueGetter, + warning + ) => { + // typeof import.meta.property + parser.hooks.typeof + .for(propertyName) + .tap( + PLUGIN_NAME, + toConstantDependency(parser, JSON.stringify("string")) + ); + + // import.meta.property expression + parser.hooks.expression + .for(propertyName) + .tap(PLUGIN_NAME, (expr) => { + const dep = new ConstDependency( + JSON.stringify(valueGetter()), + /** @type {Range} */ (expr.range) + ); + dep.loc = /** @type {DependencyLocation} */ (expr.loc); + parser.state.module.addPresentationalDependency(dep); + + if (warning) { + parser.state.module.addWarning( + new NodeStuffInWebError(dep.loc, propertyName, warning) + ); + } + return true; + }); + + // evaluateTypeof + parser.hooks.evaluateTypeof + .for(propertyName) + .tap(PLUGIN_NAME, evaluateToString("string")); + + // evaluateIdentifier + parser.hooks.evaluateIdentifier + .for(propertyName) + .tap(PLUGIN_NAME, (expr) => + evaluateToString(valueGetter())(expr) + ); + }; + /** * @param {string} expressionName expression name * @param {(module: NormalModule) => string} fn function @@ -211,6 +263,35 @@ class NodeStuffPlugin { return evaluateToString(resource.path)(expr); }); } + + // Handle import.meta.filename + if (localOptions.__filename) { + switch (localOptions.__filename) { + case "mock": + defineImportMetaProperty( + "import.meta.filename", + () => "/index.js" + ); + break; + case "warn-mock": + defineImportMetaProperty( + "import.meta.filename", + () => "/index.js", + "import.meta.filename is a Node.js feature and isn't available in browsers." + ); + break; + case true: + defineImportMetaProperty("import.meta.filename", () => + relative( + /** @type {InputFileSystem} */ (compiler.inputFileSystem), + context, + parser.state.module.resource + ) + ); + break; + } + } + if (localOptions.__dirname) { switch (localOptions.__dirname) { case "mock": @@ -254,6 +335,31 @@ class NodeStuffPlugin { )(expr); }); } + + // Handle import.meta.dirname + if (localOptions.__dirname) { + switch (localOptions.__dirname) { + case "mock": + defineImportMetaProperty("import.meta.dirname", () => "/"); + break; + case "warn-mock": + defineImportMetaProperty( + "import.meta.dirname", + () => "/", + "import.meta.dirname is a Node.js feature and isn't available in browsers." + ); + break; + case true: + defineImportMetaProperty("import.meta.dirname", () => + relative( + /** @type {InputFileSystem} */ (compiler.inputFileSystem), + context, + /** @type {string} */ (parser.state.module.context) + ) + ); + break; + } + } parser.hooks.expression .for("require.extensions") .tap( diff --git a/lib/dependencies/ImportMetaPlugin.js b/lib/dependencies/ImportMetaPlugin.js index 7d0d5731b..e8a77a737 100644 --- a/lib/dependencies/ImportMetaPlugin.js +++ b/lib/dependencies/ImportMetaPlugin.js @@ -19,7 +19,6 @@ const { evaluateToString, toConstantDependency } = require("../javascript/JavascriptParserHelpers"); -const { parseResource } = require("../util/identifier"); const memoize = require("../util/memoize"); const propertyAccess = require("../util/propertyAccess"); const ConstDependency = require("./ConstDependency"); @@ -197,68 +196,6 @@ class ImportMetaPlugin { .setRange(/** @type {Range} */ (expr.range)) ); - // import.meta.dirname - parser.hooks.typeof - .for("import.meta.dirname") - .tap( - PLUGIN_NAME, - toConstantDependency(parser, JSON.stringify("string")) - ); - parser.hooks.expression - .for("import.meta.dirname") - .tap(PLUGIN_NAME, (expr) => { - const dep = new ConstDependency( - JSON.stringify( - /** @type {string} */ (parser.state.module.context) - ), - /** @type {Range} */ (expr.range) - ); - dep.loc = /** @type {DependencyLocation} */ (expr.loc); - parser.state.module.addPresentationalDependency(dep); - return true; - }); - parser.hooks.evaluateTypeof - .for("import.meta.dirname") - .tap(PLUGIN_NAME, evaluateToString("string")); - parser.hooks.evaluateIdentifier - .for("import.meta.dirname") - .tap(PLUGIN_NAME, (expr) => - evaluateToString( - /** @type {string} */ - (parser.state.module.context) - )(expr) - ); - - // import.meta.filename - parser.hooks.typeof - .for("import.meta.filename") - .tap( - PLUGIN_NAME, - toConstantDependency(parser, JSON.stringify("string")) - ); - parser.hooks.expression - .for("import.meta.filename") - .tap(PLUGIN_NAME, (expr) => { - const resource = parseResource(parser.state.module.resource); - const dep = new ConstDependency( - JSON.stringify(resource.path), - /** @type {Range} */ (expr.range) - ); - dep.loc = /** @type {DependencyLocation} */ (expr.loc); - parser.state.module.addPresentationalDependency(dep); - return true; - }); - parser.hooks.evaluateTypeof - .for("import.meta.filename") - .tap(PLUGIN_NAME, evaluateToString("string")); - parser.hooks.evaluateIdentifier - .for("import.meta.filename") - .tap(PLUGIN_NAME, (expr) => - new BasicEvaluatedExpression() - .setString(parseResource(parser.state.module.resource).path) - .setRange(/** @type {Range} */ (expr.range)) - ); - // import.meta.webpack parser.hooks.typeof .for("import.meta.webpack") diff --git a/test/cases/import-meta/dirname-filename/index.js b/test/cases/import-meta/dirname-filename/index.js index 95c12d93e..d0a48fec4 100644 --- a/test/cases/import-meta/dirname-filename/index.js +++ b/test/cases/import-meta/dirname-filename/index.js @@ -1,5 +1,4 @@ -const fs = require("fs"); -const path = require("path"); +const path = require("path/posix"); it("should be a string (import.meta.filename)", function() { expect(import.meta.filename).toBeTypeOf("string"); @@ -16,5 +15,4 @@ it("should be a string (import.meta.dirname)", function() { it("should be able to be used to get this file", function() { const filePath = path.join(import.meta.dirname, "index.js"); expect(filePath).toBe(import.meta.filename); - expect(fs.existsSync(filePath)).toBe(true); });