Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose real import.meta properties, plus __dirname and __filename for Node.js #9284

Open
101arrowz opened this issue Oct 2, 2023 · 4 comments

Comments

@101arrowz
Copy link
Member

101arrowz commented Oct 2, 2023

🙋 feature request

Related: #7623, #8924, #7727

At the moment Parcel transforms import.meta.url (and the Node.js-specific cousins __dirname and __filename) with no user-land way to actually access the runtime values of these properties short of using eval to circumvent Parcel's AST walker. This has caused problems even within Parcel itself (#7948, #9172 (comment)), and Parcel uses a special __parcel__URL__ function to actually emit the runtime value.

Developers use import.meta.url and its cousins primarily for reflection purposes, i.e. they use it to change the behavior of their code based on the current state of the runtime. The main use cases I've seen are forking workers and accessing a location on disk relative to the runtime file-path. By setting these values at build time, Parcel removes what is in my opinion the main value of these properties; they essentially become useful for debugging and nothing else.

💁 Possible Solution

Just like how we have a "fake" module @parcel/service-worker to get the service worker manifest information at runtime, something like@parcel/reflection could exist where Parcel emits runtime reflection information.

import { importMeta } from '@parcel/reflection';

console.log(importMeta.url); // Actually useful now!

// Will be helpful if/once Parcel supports import.meta.resolve
console.log(importMeta.resolve('./runtime-thing-parcel-doesn't-know-about.js')); 

🔦 Context

I'm trying to get a path relative to the location of the final JavaScript file in a Node.js environment. I could use any of __dirname, __filename, or import.meta.url to do this, but Parcel rewrites all of them. Although the value of something like new URL('other-asset', import.meta.url) is obvious, import.meta.url seems quite useless at the moment because Parcel has to rewrite it to a fake absolute path like file:///path/to/src.ts, which can't really be used for anything but logging.

Right now, I've resorted to doing the following:

import { fileURLToPath } from 'url';

const actualRelativePath = fileURLToPath(new __parcel__URL__('my/relative/path'));

I'd like to be able to do something less hacky.

@101arrowz
Copy link
Member Author

While we're at it, it would be nice to have access to projectRoot, packageRoot, and other currently Parcel-internal properties too.

@SZharkov
Copy link

Use process.cwd() instead of import.meta.url, that worked for me.

@limond
Copy link

limond commented Jun 4, 2024

I needed a way to get the path of the built esm bundle in node.js for production and a file path at least near to the original source in development (e.g. running with jest).

I mean this is even more hacky than other solutions but still it is a way to get import.meta.url in the current bundle output.

I wrote a macro importMetaUrl.ts:

import type {MacroContext} from '@parcel/macros';

export function importMetaUrl(this: MacroContext | void) {
  if (this?.addAsset) {
    this.addAsset({
      type: 'js',
      content: 'const importMetaUrl = import.meta.url',
    });
    return new Function("return importMetaUrl");
  }
  return () => import.meta.url;
}

Source:

import {importMetaUrl} from './importMetaUrl' with {type: "macro"};
//...
const start = importMetaUrl()();

Generated bundle (esm for node.js):

const start = function anonymous() {
   return importMetaUrl;
}();
//...
const importMetaUrl = import.meta.url

The macro adds a javascript asset which seems to be written to the bundle directly. If importMetaUrl is called from parcel, the returned function is inlined. if the function is called outside of parcel, a function is returned that just returns import.meta.url.

Now start contains the absolute URL of the bundled script in production and the URL of the macro in development which is fine for my use case. But I would be glad for guidance of how to do this idiomatically.

@lortimer
Copy link

I ran into OP's problem today trying to use parcel to transpile a NodeJS + Express app that serves a REST API alongside my web application. I call app.use(express.static(path.join(__dirname, "public"))); to statically serve the frontend files, but when I use parcel to build the server, __dirname points to my source folder. I expected it to point to the dist folder where parcel puts the transpiled files.

If I missed something in the docs and there's a way to get the location of the file where it lives, not where it came from, I'd love to know. If not, I'd vote to add this feature.

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

No branches or pull requests

4 participants