Skip to content

feat: Support import.meta.filename/dirname/resolve#10573

Closed
magic-akari wants to merge 8 commits intoweb-infra-dev:chore/add-import-meta-desctructuring-hookfrom
magic-akari:fix/issue-8008
Closed

feat: Support import.meta.filename/dirname/resolve#10573
magic-akari wants to merge 8 commits intoweb-infra-dev:chore/add-import-meta-desctructuring-hookfrom
magic-akari:fix/issue-8008

Conversation

@magic-akari
Copy link
Copy Markdown
Contributor

@magic-akari magic-akari commented Jun 5, 2025

Summary

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).

@netlify
Copy link
Copy Markdown

netlify bot commented Jun 5, 2025

Deploy Preview for rspack canceled.

Built without sensitive environment variables

Name Link
🔨 Latest commit 42aec2e
🔍 Latest deploy log https://app.netlify.com/projects/rspack/deploys/69086a28ddae3c0008e2390d

@github-actions github-actions bot added the release: feature release: feature related release(mr only) label Jun 5, 2025
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Jun 5, 2025

CodSpeed Performance Report

Merging #10573 will not alter performance

Comparing magic-akari:fix/issue-8008 (b6acc0a) with chore/add-import-meta-desctructuring-hook (127450f)

Summary

✅ 17 untouched

@magic-akari
Copy link
Copy Markdown
Contributor Author

magic-akari commented Jun 5, 2025

I haven't yet implemented import.meta.resolve, which provides functionality similar to require.resolve but follows ESM resolution rules.
Since we're currently unsure about the best approach to implement this (and would welcome insights if you have them), I've decided to hold off on implementation for now.

done.

@magic-akari
Copy link
Copy Markdown
Contributor Author

magic-akari commented Jun 5, 2025

On Windows CI, import.meta.url handles file paths differently (using three slashes versus two). Since this PR doesn't address that specific behavior, we'll defer fixing it for now.

- 	expect('file://<TEST_TOOLS_ROOT>/tests/builtinCases/plugin-javascript/import-meta/index.js').toBe(url);
- 	expect('file://<TEST_TOOLS_ROOT>/tests/builtinCases/plugin-javascript/import-meta/index.js').toBe(url);
- 	expect("my" + 'file://<TEST_TOOLS_ROOT>/tests/builtinCases/plugin-javascript/import-meta/index.js').toBe("my" + url);
+ 	expect('file:///<TEST_TOOLS_ROOT>/tests/builtinCases/plugin-javascript/import-meta/index.js').toBe(url);
+ 	expect('file:///<TEST_TOOLS_ROOT>/tests/builtinCases/plugin-javascript/import-meta/index.js').toBe(url);
+ 	expect("my" + 'file:///<TEST_TOOLS_ROOT>/tests/builtinCases/plugin-javascript/import-meta/index.js').toBe("my" + url);

@magic-akari magic-akari changed the title feat: Support import.meta.filename and import.meta.dirname feat: Support import.meta.filename/dirname/resolve Jun 5, 2025
@magic-akari magic-akari marked this pull request as ready for review June 5, 2025 08:47
@magic-akari magic-akari marked this pull request as draft June 6, 2025 02:18
"./tests/builtinCases/plugin-javascript/import-meta/index.js",
).toString();

const filename = "/index.js";
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that __filename is handled by crates/rspack_plugin_rstest/src/parser_plugin.rs using a different strategy.

@magic-akari magic-akari marked this pull request as ready for review June 6, 2025 02:57
.to_string()
}

// This is the same as the url.fileURLToPath() of the import.meta.url
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using PathBuf::from directly might be more efficient. However, considering that the Resource might contain a query string – which can introduce various edge cases like ./x.js?foo=/../y.js – processing it through a URL parser is safer.

/// Resource with absolute path, query and fragment
pub resource: String,

@chenjiahan chenjiahan requested review from LingyuCoder and h-a-n-a and removed request for LingyuCoder June 11, 2025 09:00
@chenjiahan
Copy link
Copy Markdown
Member

@LingyuCoder @h-a-n-a cc~

@chenjiahan
Copy link
Copy Markdown
Member

@LingyuCoder can you help to review this PR?

@chenjiahan
Copy link
Copy Markdown
Member

@LingyuCoder 😅

@LingyuCoder
Copy link
Copy Markdown
Contributor

LGTM, im not sure if a new experiment option should be added @chenjiahan

LingyuCoder
LingyuCoder previously approved these changes Jul 22, 2025
@chenjiahan
Copy link
Copy Markdown
Member

@LingyuCoder Can you help to check this issue (webpack/webpack#18320) and provide some advice?

@LingyuCoder
Copy link
Copy Markdown
Contributor

@LingyuCoder Can you help to check this issue (webpack/webpack#18320) and provide some advice?

I just think it should align with the behavior of Node.js and adding an new experiment option to facilitate modifications in case of inconsistent behaviors after the implementation of webpack in the future.

@LingyuCoder
Copy link
Copy Markdown
Contributor

Since this is not a very stable specification, it's uncertain whether it is consistent across bundlers and Node.js. Considering that rspack should currently align with webpack in this behavior, you might consider submitting a PR to webpack and getting their advice too 😊

@karlhorky
Copy link
Copy Markdown

A PR has now been merged in webpack:

Copilot AI review requested due to automatic review settings November 3, 2025 05:57
@magic-akari magic-akari marked this pull request as draft November 3, 2025 05:58
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for import.meta.filename, import.meta.dirname, and import.meta.resolve properties to rspack's JavaScript plugin, extending the existing import.meta API implementation to provide Node.js-compatible functionality.

Key Changes:

  • Adds three new import.meta properties: filename, dirname, and resolve with full parsing and code generation support
  • Implements dependency tracking for import.meta.resolve() calls via new ImportMetaResolveDependency and ImportMetaResolveHeaderDependency types
  • Refactors existing conditional logic to use more idiomatic match expressions

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/rspack-test-tools/tests/builtinCases/plugin-javascript/import-meta/rspack.config.js Adds test configuration enabling Node.js externals preset
packages/rspack-test-tools/tests/builtinCases/plugin-javascript/import-meta/index.js Adds comprehensive test cases for the new import.meta properties
packages/rspack-test-tools/tests/builtinCases/plugin-javascript/import-meta/__snapshots__/output.snap.txt Expected output snapshot showing code transformation results
crates/rspack_plugin_javascript/src/visitors/dependency/util.rs Adds constant definitions for new expression names
crates/rspack_plugin_javascript/src/plugin/impl_plugin_for_js_plugin.rs Registers dependency factories and templates for import.meta.resolve
crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs Core implementation of new properties and resolve functionality
crates/rspack_plugin_javascript/src/dependency/esm/mod.rs Exports new dependency types
crates/rspack_plugin_javascript/src/dependency/esm/import_meta_resolve_header_dependency.rs Dependency for replacing import.meta.resolve calls with comments
crates/rspack_plugin_javascript/src/dependency/esm/import_meta_resolve_dependency.rs Dependency for resolving module paths in import.meta.resolve
crates/rspack_plugin_circular_dependencies/src/lib.rs Updates circular dependency checker to recognize ImportMetaResolve
crates/rspack_core/src/dependency/dependency_type.rs Adds ImportMetaResolve dependency type

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +84 to +88
parser.add_dependency(import_meta_resolve_header_dependency);
} else {
self.process_import_meta_resolve_item(parser, &param);
parser.add_dependency(import_meta_resolve_header_dependency);
}
Copy link

Copilot AI Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import_meta_resolve_header_dependency is added in both branches of the conditional. This code duplication can be eliminated by moving the parser.add_dependency(import_meta_resolve_header_dependency) call after the if-else block to reduce redundancy.

Suggested change
parser.add_dependency(import_meta_resolve_header_dependency);
} else {
self.process_import_meta_resolve_item(parser, &param);
parser.add_dependency(import_meta_resolve_header_dependency);
}
} else {
self.process_import_meta_resolve_item(parser, &param);
}
parser.add_dependency(import_meta_resolve_header_dependency);

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +37
Url::from_file_path(parser.resource_data.resource())
.expect("should be a path")
.to_file_path()
.expect("should be a path")
.to_string_lossy()
.into_owned()
Copy link

Copilot AI Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import_meta_filename method performs Url::from_file_path() followed by to_file_path(), which converts a path to URL and back to a path. This is unnecessarily complex and potentially lossy. Consider directly using parser.resource_data.resource().to_string_lossy().into_owned() instead.

Suggested change
Url::from_file_path(parser.resource_data.resource())
.expect("should be a path")
.to_file_path()
.expect("should be a path")
.to_string_lossy()
.into_owned()
parser.resource_data.resource().to_string_lossy().into_owned()

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +41 to +50
fn import_meta_dirname(&self, parser: &JavascriptParser) -> String {
Url::from_file_path(parser.resource_data.resource())
.expect("should be a path")
.to_file_path()
.expect("should be a path")
.parent()
.expect("should have a parent")
.to_string_lossy()
.into_owned()
}
Copy link

Copilot AI Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to import_meta_filename, this method performs unnecessary URL conversion roundtrip. Consider directly using parser.resource_data.resource().parent().expect(\"should have a parent\").to_string_lossy().into_owned() to simplify the logic.

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +4
const url = pathToFileURL(
"./tests/builtinCases/plugin-javascript/import-meta/index.js",
).toString();
Copy link

Copilot AI Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable url.

Suggested change
const url = pathToFileURL(
"./tests/builtinCases/plugin-javascript/import-meta/index.js",
).toString();

Copilot uses AI. Check for mistakes.
@magic-akari
Copy link
Copy Markdown
Contributor Author

magic-akari commented Nov 3, 2025

Hi team! 👋

I noticed that webpack's implementation for import.meta.dirname and import.meta.filename is in NodeStuffPlugin (from webpack PR #20050), so I attempted to migrate our implementation to follow the same architecture.

✅ What's Working

I've successfully migrated the member access handling to NodeStuffPlugin:

// Now in NodeStuffPlugin
import.meta.dirname  // ✅ Respects node.dirname config
import.meta.filename // ✅ Respects node.filename config

This works well and follows webpack's pattern using the unified handle_dirname_or_filename method, matching webpack's dirnameAndFilenameHandler approach.

Supported Node Options

The implementation reuses the existing NodeStuffPlugin logic for CommonJS __dirname/__filename, which already handles various node configuration options:

  • false
  • mock
  • warn-mock
  • node-module
  • eval-only
  • true

⚠️ Challenge: Destructuring Support

However, I encountered an architectural limitation with destructuring assignment:

const { dirname, filename } = import.meta

How webpack handles this

In webpack, destructuring is handled through a hook mechanism:

  1. ImportMetaPlugin processes destructuring and calls hooks.propertyInDestructuring for each property:

    for (const prop of referencedPropertiesInDestructuring) {
        const value = hooks.propertyInDestructuring.call(prop);
        if (value) {
            str += value;  // Use value from hook
            continue;
        }
        // Otherwise handle in ImportMetaPlugin
    }
  2. NodeStuffPlugin registers handlers on this hook to provide values based on node.dirname/node.filename config:

    hooks.propertyInDestructuring.tap(PLUGIN_NAME, (usingProperty) => {
        if (usingProperty.id === property) {
            return `${property}: ${fn(parser.state.module)},`;
        }
    });
  3. This allows NodeStuffPlugin to inject the correct values during destructuring

The rspack limitation

In rspack, we don't have a propertyInDestructuring hook:

  • ImportMetaPlugin directly handles all destructured properties in the meta_property method
  • There's no mechanism for NodeStuffPlugin to intercept and provide node-config-aware values
  • This means destructuring currently returns simple file paths, ignoring node config
// Current rspack implementation in ImportMetaPlugin
match prop.id.as_str() {
    "url" => { /* ... */ }
    "filename" => {
        // NodeStuffPlugin cannot intercept this!
        content.push(format!(r#"filename: "{}""#, self.import_meta_filename(parser)));
    }
    "dirname" => {
        // NodeStuffPlugin cannot intercept this!
        content.push(format!(r#"dirname: "{}""#, self.import_meta_dirname(parser)));
    }
    // ...
}

🤔 Question for the Team

I'd appreciate your guidance on which approach to take. Here are the three options I see:

Option 1: Don't migrate to NodeStuffPlugin - Keep implementation in ImportMetaPlugin, ignoring node options

  • ❌ Creates architectural inconsistency with webpack

Option 2: Migrate to NodeStuffPlugin - Member access respects node options, but destructuring doesn't

  • ✅ Member access behavior aligns with webpack
  • ❌ Introduces internal inconsistency in rspack (member access and destructuring behave differently)

Option 3: Wait for hook mechanism - Implement propertyInDestructuring hook first, then complete the migration

  • ✅ Full webpack parity
  • ⚠️ Requires Rspack team investment to implement the hook mechanism

I'm happy to proceed with any of these approaches based on your preference. Thank you for your guidance!

📚 References

@LingyuCoder
Copy link
Copy Markdown
Contributor

Option 3: Wait for hook mechanism - Implement propertyInDestructuring hook first, then complete the migration

  • ✅ Full webpack parity
  • ⚠️ Requires Rspack team investment to implement the hook mechanism

Since the propertyInDestructuring is a plugin private hook of ImportMetaPlugin rather than a parser hook, and it should not affect the parser. So I think we can follow webpack's design and add this hook to ImportMetaPlugin to support the behavior of the NodeStuffPlugin. What is your opinion? @ahabhgk

@ahabhgk
Copy link
Copy Markdown
Contributor

ahabhgk commented Nov 10, 2025

I'm ok with the current implementation, only thing need to change is ImportMetaPlugin need to read the config from parser.compiler_options.node. IMO the propertyInDestructuring is a bit make code complex

@magic-akari magic-akari changed the base branch from main to chore/add-import-meta-desctructuring-hook November 23, 2025 14:24
@magic-akari magic-akari force-pushed the fix/issue-8008 branch 2 times, most recently from d0c1d94 to 71a53d0 Compare November 23, 2025 17:15
@magic-akari
Copy link
Copy Markdown
Contributor Author

Just to follow up, is the configCases/module-variables test case deprecated? I still can't seem to find the corresponding files in webpack repo.

@LingyuCoder
Copy link
Copy Markdown
Contributor

Just to follow up, is the configCases/module-variables test case deprecated? I still can't seem to find the corresponding files in webpack repo.

This case is still in use and it should be a test case specific to rspack.

@magic-akari
Copy link
Copy Markdown
Contributor Author

Webpack import.meta.dirname/filename Feature History

This document was generated by AI to summarize the webpack commits that implemented support for import.meta.dirname and import.meta.filename.

Commit Timeline

1. a576d0f31 - 2025-10-28

feat: added support for import.meta.dirname and import.meta.filename

Files Modified:

  • lib/NodeStuffPlugin.js - Core implementation
  • lib/config/defaults.js
  • lib/dependencies/CommonJsPlugin.js
  • lib/dependencies/ExternalModuleInitFragment.js
  • Added test cases in test/configCases/node/filename-and-dirname/

Features:

  • Initial implementation of import.meta.dirname and import.meta.filename support
  • Extended NodeStuffPlugin to handle ESM modules by registering handlers for import.meta.dirname and import.meta.filename
  • Support for various node.__filename and node.__dirname configuration values:
    • true - Replace with relative path from context
    • false - Keep import.meta.filename/dirname as-is (for Node.js runtime)
    • "mock" - Replace with /index.js or /
    • "warn-mock" - Same as mock but with warning
    • "node-module" - Use fileURLToPath(import.meta.url) polyfill
    • "eval-only" - Keep as import.meta.filename/dirname for ESM output

Key Implementation:

// Keep `import.meta.filename` in code when node.__filename is false
if (
    localOptions.__filename === false &&
    filename === "import.meta.filename"
) {
    parser.hooks.expression
        .for(filename)
        .tap(PLUGIN_NAME, toConstantDependency(parser, filename));
}

2. 78ddad1ba - 2025-10-31

feat: support destructuring for import.meta.filename and import.meta.dirname

Files Modified:

  • lib/NodeStuffPlugin.js - Major refactoring
  • lib/WebpackOptionsApply.js
  • lib/dependencies/ExternalModuleInitFragmentDependency.js (new file)
  • lib/dependencies/ImportMetaPlugin.js - Added hook system
  • lib/javascript/JavascriptParser.js

Features:

  • Support for destructuring syntax: const { dirname, filename } = import.meta;
  • Added ImportMetaPlugin.getCompilationHooks(compilation).propertyInDestructuring hook
  • Modified ImportMetaPlugin.unhandledExpressionMemberChain to skip dirname and filename
  • When parserOptions.node === false, still register handlers to preserve import.meta.dirname/filename as-is

Key Implementation:

// In ImportMetaPlugin - skip dirname/filename in unhandledExpressionMemberChain
if (
    members[0] === "env" ||
    members[0] === "dirname" ||
    members[0] === "filename"
) {
    return true;
}

// In NodeStuffPlugin - handle parserOptions.node === false
if (b && parserOptions.node === false) {
    // Keep `import.meta.dirname` and `import.meta.filename` in code
    setModuleConstant(parser, "import.meta.dirname", () => "import.meta.dirname", "dirname");
    setModuleConstant(parser, "import.meta.filename", () => "import.meta.filename", "filename");
    return;
}

3. 8651ffc23 - 2025-10-31

feat: handle import.meta.main

Files Modified:

  • lib/dependencies/ImportMetaPlugin.js

Features:

  • Added support for import.meta.main property
  • Not directly related to dirname/filename but part of the import.meta enhancement series

4. a2938e240 - 2025-11-07

feat: support __dirname/__filename/import.meta.dirname/import.meta.filename for universal target

Files Modified:

  • lib/NodeStuffPlugin.js
  • Added test cases for universal target

Features:

  • Extended support to work with target: "universal"
  • Ensures consistent behavior across different target configurations

5. 2259e2cc9 - 2025-11-19

fix: compatibility import.meta.filename and import.meta.dirname with eval devtools

Files Modified:

  • lib/NodeStuffPlugin.js
  • lib/dependencies/CachedConstDependency.js
  • lib/dependencies/ExternalModuleDependency.js
  • lib/dependencies/ExternalModuleInitFragment.js
  • lib/dependencies/ExternalModuleInitFragmentDependency.js

Features:

  • Fixed compatibility issues when using devtool: "eval"
  • Ensured proper handling of external module dependencies in eval context
  • Added more test configurations for eval devtool scenarios

6. 05cac61aa - 2025-11-25

feat: enhance import.meta.env to support object access

Files Modified:

  • lib/dependencies/ImportMetaPlugin.js

Features:

  • Enhanced import.meta.env support
  • Not directly related to dirname/filename but shares similar patterns in ImportMetaPlugin

Summary of Key Behaviors

Configuration import.meta.dirname Output
node.__dirname: true "/relative/path" (from context)
node.__dirname: false import.meta.dirname (preserved)
node.__dirname: "mock" "/"
node.__dirname: "warn-mock" "/" + warning
node.__dirname: "node-module" dirname(fileURLToPath(import.meta.url))
node.__dirname: "eval-only" import.meta.dirname (ESM) / __dirname (CJS)
node: false import.meta.dirname (preserved)
parser.javascript.node: false import.meta.dirname (preserved)

@LingyuCoder LingyuCoder deleted the branch web-infra-dev:chore/add-import-meta-desctructuring-hook November 27, 2025 05:54
@magic-akari
Copy link
Copy Markdown
Contributor Author

Hi @LingyuCoder, I expected this PR would automatically rebase onto main after chore/add-import-meta-desctructuring-hook was merged, but it seems to have been closed unexpectedly instead.
Would you mind helping to reopen this PR? Thanks!

@magic-akari
Copy link
Copy Markdown
Contributor Author

Never mind! I've already opened a new PR(#12317) instead. Thanks anyway!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release: feature release: feature related release(mr only)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Support import.meta.resolve + Node Specific Meta Values

6 participants