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

Means to detect if the current code is run via --import #53882

Open
timfish opened this issue Jul 17, 2024 · 16 comments
Open

Means to detect if the current code is run via --import #53882

timfish opened this issue Jul 17, 2024 · 16 comments
Labels
esm Issues and PRs related to the ECMAScript Modules implementation. feature request Issues that request new features to be added to Node.js. loaders Issues and PRs related to ES module loaders

Comments

@timfish
Copy link
Contributor

timfish commented Jul 17, 2024

What is the problem this feature will solve?

Code might want to determine if it's running via main app entry point or via a module imported via --import. For example, a library might instruct users to run the code via --import and might want to warn users if this is not the case.

What is the feature you are proposing to solve the problem?

Not sure what the API should be, maybe process.isEntryPoint or something like that?

The entry point is already marked on globalThis via a private symbol:

if (isEntryPoint) {
globalThis[entry_point_module_private_symbol] = this.module;
}

What alternatives have you considered?

Currently the only way I can think to detect this would be to parse new Error().stack to find the entry point file and then parse and compare to process.execArgv. This is not trivial since you'd need to resolve bare specifiers to their actual source files.

@timfish timfish added the feature request Issues that request new features to be added to Node.js. label Jul 17, 2024
@timfish timfish changed the title Means to detect if the current code is run via --import on the app entry point Means to detect if the current code is run via --import Jul 17, 2024
@RedYetiDev RedYetiDev added esm Issues and PRs related to the ECMAScript Modules implementation. loaders Issues and PRs related to ES module loaders labels Jul 17, 2024
@sosoba
Copy link
Contributor

sosoba commented Jul 17, 2024

Code might want to determine if it's running via main app entry point or via a module imported via --import.

You can recognize this via:
https://nodejs.org/api/worker_threads.html#workerismainthread

@timfish
Copy link
Contributor Author

timfish commented Jul 17, 2024

isMainThread is to check for main thread vs worker threads.

It does not tell you if code has been loaded via --import as can be seen from this example:

import.mjs

import { isMainThread } from "worker_threads";
console.log("import.mjs", isMainThread);

app.mjs

import { isMainThread } from "worker_threads";
console.log("app.mjs", isMainThread);
node --import=./import.mjs ./app.mjs
import.mjs true
app.mjs true

@RedYetiDev
Copy link
Member

import.js

import { isMainThread } from "worker_threads";
const {
    privateSymbols: {
        entry_point_module_private_symbol,
    },
} = internalBinding('util');

console.log("[IMPORT] worker_threads.isMainThread =", isMainThread);
console.log("[IMPORT] entry_point_module_private_symbol = ", globalThis[entry_point_module_private_symbol])

main.js

import { isMainThread } from "worker_threads";
const {
    privateSymbols: {
        entry_point_module_private_symbol,
    },
} = internalBinding('util');

console.log("[MAIN] worker_threads.isMainThread =", isMainThread);
console.log("[MAIN] entry_point_module_private_symbol = ", globalThis[entry_point_module_private_symbol])

node --expose-internals -r internal/test/binding --import ./import.js main.js
(node:20231) internal/test/binding: These APIs are for internal testing only. Do not use them.
(Use `node --trace-warnings ...` to show where the warning was created)
[IMPORT] worker_threads.isMainThread = true
[IMPORT] entry_point_module_private_symbol =  undefined
[MAIN] worker_threads.isMainThread = true
[MAIN] entry_point_module_private_symbol =  ModuleWrap {
  sourceMapURL: undefined,
  url: 'file:///XYZ/main.js'
}

@RedYetiDev
Copy link
Member

RedYetiDev commented Jul 17, 2024

@timfish One way to check if your script was imported is to use the cache:

node --import ./import.js main.js
[IMPORT] false
[MAIN] true

import.js

import module from 'node:module';

console.log('[IMPORT]', Object.values(module._pathCache).includes(import.meta.filename))

// Other imports

main.js

import module from 'node:module';

console.log('[MAIN]', Object.values(module._pathCache).includes(import.meta.filename))

@timfish
Copy link
Contributor Author

timfish commented Jul 17, 2024

Thanks @RedYetiDev!

Is _pathCache documented anywhere and safe to use?

This does have the downside that it's only reliable the first time it's called. Can it be removed from the cache so it's correct every time it's called?

check.mjs

import module from "node:module";

export function check(type) {
  console.log(
    `[${type}]`,
    Object.values(module._pathCache).includes(import.meta.filename)
  );
}

import.mjs

import { check } from "./check.mjs";
check("import");

app.mjs

import { check } from "./check.mjs";
check("app");
node --import ./import.mjs app.mjs
[import] false
[app] false

It looks like this would be easy to add to Node, I'm happy to do a PR if an API can be agreed!

const {
    privateSymbols: {
        entry_point_module_private_symbol,
    },
} = internalBinding('util');

const isEntryPoint = globalThis[entry_point_module_private_symbol])

@RedYetiDev
Copy link
Member

RedYetiDev commented Jul 17, 2024

I don't know to much about whether the API is stable (because it's used internally) so CC @nodejs/loaders


As for the addition of the API, I believe #49440 discussed something similar. This may be a duplicate of that, I don't really know.

@ljharb
Copy link
Member

ljharb commented Jul 17, 2024

Does a similar approach work for --require?

@GeoffreyBooth
Copy link
Member

I believe #49440 discussed something similar

Yes, I think import.meta.main is the proper solution for this.

@timfish
Copy link
Contributor Author

timfish commented Jul 17, 2024

Yes, I think import.meta.main is the proper solution for this.

So if import.meta.main was added, this would be undefined in modules loaded via --import?

@RedYetiDev
Copy link
Member

Yes

@Flarna
Copy link
Member

Flarna commented Jul 17, 2024

for --require one can use module.isPreloading, see here

I think a similar API should be added for --import or the existing API should include it.

@GeoffreyBooth
Copy link
Member

I think a similar API should be added for --import or the existing API should include it.

That could also work. That's perhaps more appropriate for this particular case because there are questions about what import.meta.main should be for worker entry points and such.

I think we don't need to distinguish between modules loaded via --require or via --import, especially since the latter could load CommonJS or ESM.

@ljharb
Copy link
Member

ljharb commented Jul 17, 2024

I agree; does module.isPreloading work for --import also?

@Flarna
Copy link
Member

Flarna commented Jul 17, 2024

Not in my fast local tests in main thread only.

@timfish
Copy link
Contributor Author

timfish commented Jul 18, 2024

Yes, I think import.meta.main is the proper solution for this.

import.meta.main is only true in the entry module but not for any sub-modules. That means it's not suitable to solve the stated goal of this issue:

a library might instruct users to run the code via --import and might want to warn users if this is not the case

Unless the code is bundled, a library will always read import.meta.main as false.

module.isPreloading is exactly what was looking for. If we can have this to be true for --import too that would be great.

@GeoffreyBooth
Copy link
Member

If we can have this to be true for --import too that would be great.

Agreed. In the meantime, I think looking in process.argv and checking the values with import.meta.resolve might provide another solution, but I feel like it probably has holes and edge cases that it'll miss.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
esm Issues and PRs related to the ECMAScript Modules implementation. feature request Issues that request new features to be added to Node.js. loaders Issues and PRs related to ES module loaders
Projects
Development

No branches or pull requests

6 participants