Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/api/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,18 @@ emitMyWarning();
// Emits nothing
```

## `process.entrypoint`

<!-- YAML
added: REPLACEME
-->

* Type: {string | undefined}

The URL of the entry point that Node.js was started with, such as
`'file:///path/to/app.js'`. Worker threads inherit this value. If Node.js was
started without an entry point, such as in the REPL, the value is {undefined}.

## `process.env`

<!-- YAML
Expand Down
4 changes: 4 additions & 0 deletions lib/internal/main/worker_thread.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const {
} = primordials;

const {
defineEntrypoint,
prepareWorkerThreadExecution,
initializeModuleLoaders,
markBootstrapComplete,
Expand Down Expand Up @@ -95,6 +96,7 @@ port.on('message', (message) => {
argv,
cwdCounter,
doEval,
entrypoint,
environmentData,
filename,
hasStdin,
Expand All @@ -103,6 +105,8 @@ port.on('message', (message) => {
mainThreadPort,
} = message;

defineEntrypoint(() => entrypoint);

if (doEval !== 'internal') {
if (argv !== undefined) {
ArrayPrototypePushApply(process.argv, argv);
Expand Down
34 changes: 34 additions & 0 deletions lib/internal/process/pre_execution.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const {
setupCoverageHooks,
emitExperimentalWarning,
deprecate,
getCWDURL,
} = require('internal/util');

const {
Expand Down Expand Up @@ -254,6 +255,25 @@ function refreshRuntimeOptions() {
refreshOptions();
}

function defineEntrypoint(computeEntrypoint) {
let entrypoint;
let computed = false;

ObjectDefineProperty(process, 'entrypoint', {
__proto__: null,
enumerable: true,
configurable: true,
get() {
if (!computed) {
entrypoint = computeEntrypoint();
computed = true;
}
return entrypoint;
},
set: undefined,
});
}

/**
* Patch the process object with legacy properties and normalizations.
* Replace `process.argv[0]` with `process.execPath`, preserving the original `argv[0]` value as `process.argv0`.
Expand Down Expand Up @@ -294,6 +314,19 @@ function patchProcessObject(expandArgv1) {
}
}

const entryArg = process.argv[1];
defineEntrypoint(() => {
if (mainEntry !== undefined) {
const { pathToFileURL } = require('internal/url');
return pathToFileURL(mainEntry).href;
}
if (entryArg && getOptionValue('--entry-url')) {
const { URL } = require('internal/url');
return new URL(entryArg, getCWDURL()).href;
}
return undefined;
});

Comment thread
avivkeller marked this conversation as resolved.
// We need to initialize the global console here again with process.stdout
// and friends for snapshot deserialization.
const globalConsole = require('internal/console/global');
Expand Down Expand Up @@ -824,6 +857,7 @@ function getHeapSnapshotFilename(diagnosticDir) {
}

module.exports = {
defineEntrypoint,
initializeModuleLoaders,
prepareMainThreadExecution,
prepareWorkerThreadExecution,
Expand Down
1 change: 1 addition & 0 deletions lib/internal/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ class Worker extends EventEmitter {

this[kPort].postMessage({
argv,
entrypoint: process.entrypoint,
type: messageTypes.LOAD_SCRIPT,
filename,
doEval,
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/entrypoint/check-commonjs.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

const assert = require('node:assert');

assert.strictEqual(process.entrypoint, process.env.NODE_TEST_ENTRYPOINT);
4 changes: 4 additions & 0 deletions test/fixtures/entrypoint/check-module.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import assert from 'node:assert';
import { entrypoint } from 'node:process';

assert.strictEqual(entrypoint, process.env.NODE_TEST_ENTRYPOINT);
1 change: 1 addition & 0 deletions test/fixtures/entrypoint/commonjs.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log(process.entrypoint);
4 changes: 4 additions & 0 deletions test/fixtures/entrypoint/loader.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import assert from 'node:assert';
import { entrypoint } from 'node:process';

assert.strictEqual(entrypoint, process.env.NODE_TEST_ENTRYPOINT);
1 change: 1 addition & 0 deletions test/fixtures/entrypoint/module.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log(process.entrypoint);
9 changes: 9 additions & 0 deletions test/fixtures/entrypoint/worker.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

const { isMainThread, workerData, Worker } = require('node:worker_threads');

if (isMainThread || workerData === 'nested') {
new Worker(__filename, { workerData: isMainThread ? 'nested' : 'leaf' });
} else {
console.log(process.entrypoint);
}
118 changes: 118 additions & 0 deletions test/parallel/test-process-entrypoint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
'use strict';

const { spawnPromisified } = require('../common');
const fixtures = require('../common/fixtures');

const assert = require('node:assert');
const { execPath } = require('node:process');
const { describe, it } = require('node:test');

const cjsFixture = fixtures.path('entrypoint', 'commonjs.cjs');
const cjsFixtureURL = fixtures.fileURL('entrypoint', 'commonjs.cjs').href;

const mjsFixture = fixtures.path('entrypoint', 'module.mjs');
const mjsFixtureURL = fixtures.fileURL('entrypoint', 'module.mjs').href;

const workerFixture = fixtures.path('entrypoint', 'worker.cjs');
const workerFixtureURL = fixtures.fileURL('entrypoint', 'worker.cjs').href;

const cjsPreload = fixtures.path('entrypoint', 'check-commonjs.cjs');
const esmPreload = fixtures.fileURL('entrypoint', 'check-module.mjs').href;
const loader = fixtures.fileURL('entrypoint', 'loader.mjs').href;

const printEntrypoint = 'console.log(process.entrypoint)';

async function getEntrypoint(args, options, expected) {
const { code, signal, stderr, stdout } = await spawnPromisified(
execPath,
args,
{
...options,
env: {
...process.env,
NODE_TEST_ENTRYPOINT: expected,
},
},
);
assert.strictEqual(code, 0, stderr);
assert.strictEqual(signal, null);

return stdout.trim();
}

const testCases = [
{
name: 'is the file URL of a CommonJS entry point',
args: [cjsFixture],
expected: cjsFixtureURL,
},
{
name: 'is the file URL of an ES module entry point',
args: [mjsFixture],
expected: mjsFixtureURL,
},
{
name: 'is absolute when passed a relative path',
args: ['./commonjs.cjs'],
options: { cwd: fixtures.path('entrypoint') },
expected: cjsFixtureURL,
},
{
name: 'stays correct when using --require',
args: ['--require', cjsPreload, cjsFixture],
expected: cjsFixtureURL,
},
{
name: 'stays correct when using --import',
args: ['--import', esmPreload, cjsFixture],
expected: cjsFixtureURL,
},
{
name: 'stays correct when using --loader',
args: ['--loader', loader, cjsFixture],
expected: cjsFixtureURL,
},
{
name: 'stays correct when combining preloads and loaders',
args: [
'--require', cjsPreload,
'--import', esmPreload,
'--loader', loader,
mjsFixture,
],
expected: mjsFixtureURL,
},
{
name: 'is inherited by worker threads and their preloads and loaders',
args: [
'--require', cjsPreload,
'--import', esmPreload,
'--loader', loader,
workerFixture,
],
expected: workerFixtureURL,
},
{
name: 'supports non-file --entry-url entry points',
args: ['--entry-url', `data:text/javascript,${printEntrypoint}`],
expected: `data:text/javascript,${printEntrypoint}`,
},
{
name: 'is undefined when evaluating CommonJS without an entry point',
args: ['--eval', printEntrypoint],
expected: 'undefined',
},
{
name: 'is undefined when evaluating an ES module without an entry point',
args: ['--input-type=module', '--eval', printEntrypoint],
expected: 'undefined',
},
];

describe('process.entrypoint', { concurrency: true }, () => {
for (const { name, args, options, expected } of testCases) {
it(name, async () => {
assert.strictEqual(await getEntrypoint(args, options, expected), expected);
});
}
});
Loading