Open timfish opened 3 months ago
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
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
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'
}
@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))
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])
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.
Does a similar approach work for --require?
I believe #49440 discussed something similar
Yes, I think import.meta.main
is the proper solution for this.
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
?
Yes
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.
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.
I agree; does module.isPreloading
work for --import
also?
Not in my fast local tests in main thread only.
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.
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.
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: https://github.com/nodejs/node/blob/aca49fc7d16ae87876fec6285b658868e04b1cf7/lib/internal/modules/esm/module_job.js#L255-L257What 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 toprocess.execArgv
. This is not trivial since you'd need to resolve bare specifiers to their actual source files.