Skip to content

Commit cfb68ca

Browse files
authored
feat(node): Add a function to resolve entry point type (#14352)
1 parent 214305b commit cfb68ca

File tree

3 files changed

+169
-1
lines changed

3 files changed

+169
-1
lines changed
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { resolve } from 'node:path';
2+
import { defaultStackParser } from '../sdk/api';
3+
4+
export interface ProcessInterface {
5+
execArgv: string[];
6+
argv: string[];
7+
cwd: () => string;
8+
}
9+
10+
export interface ProcessArgs {
11+
appPath: string;
12+
importPaths: string[];
13+
requirePaths: string[];
14+
}
15+
16+
/**
17+
* Parses the process arguments to determine the app path, import paths, and require paths.
18+
*/
19+
export function parseProcessPaths(proc: ProcessInterface): ProcessArgs {
20+
const { execArgv, argv, cwd: getCwd } = proc;
21+
const cwd = getCwd();
22+
const appPath = resolve(cwd, argv[1] || '');
23+
24+
const joinedArgs = execArgv.join(' ');
25+
const importPaths = Array.from(joinedArgs.matchAll(/--import[ =](\S+)/g)).map(e => resolve(cwd, e[1] || ''));
26+
const requirePaths = Array.from(joinedArgs.matchAll(/--require[ =](\S+)/g)).map(e => resolve(cwd, e[1] || ''));
27+
28+
return { appPath, importPaths, requirePaths };
29+
}
30+
31+
/**
32+
* Gets the current entry point type.
33+
*
34+
* `app` means this function was most likely called via the app entry point.
35+
* `import` means this function was most likely called from an --import cli arg.
36+
* `require` means this function was most likely called from a --require cli arg.
37+
* `unknown` means we couldn't determine for sure.
38+
*/
39+
export function getEntryPointType(proc: ProcessInterface = process): 'import' | 'require' | 'app' | 'unknown' {
40+
const filenames = defaultStackParser(new Error().stack || '')
41+
.map(f => f.filename)
42+
.filter(Boolean) as string[];
43+
44+
const { appPath, importPaths, requirePaths } = parseProcessPaths(proc);
45+
46+
const output = [];
47+
48+
if (appPath && filenames.includes(appPath)) {
49+
output.push('app');
50+
}
51+
52+
if (importPaths.some(p => filenames.includes(p))) {
53+
output.push('import');
54+
}
55+
56+
if (requirePaths.some(p => filenames.includes(p))) {
57+
output.push('require');
58+
}
59+
60+
// We only only return anything other than 'unknown' if we only got one match.
61+
if (output.length === 1) {
62+
return output[0] as 'import' | 'require' | 'app';
63+
}
64+
65+
return 'unknown';
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import type { ProcessArgs, ProcessInterface } from '../../src/utils/entry-point';
2+
import { getEntryPointType, parseProcessPaths } from '../../src/utils/entry-point';
3+
4+
const PROCESS_ARG_TESTS: [ProcessInterface, ProcessArgs][] = [
5+
[
6+
{ cwd: () => '/user/tim/docs', argv: ['/bin/node', 'app.js'], execArgv: ['--import', './something.js'] },
7+
{ appPath: '/user/tim/docs/app.js', importPaths: ['/user/tim/docs/something.js'], requirePaths: [] },
8+
],
9+
[
10+
{
11+
cwd: () => '/user/tim/docs',
12+
argv: ['/bin/node', 'app.js'],
13+
execArgv: ['--import', './something.js', '--import=./else.js'],
14+
},
15+
{
16+
appPath: '/user/tim/docs/app.js',
17+
importPaths: ['/user/tim/docs/something.js', '/user/tim/docs/else.js'],
18+
requirePaths: [],
19+
},
20+
],
21+
[
22+
{
23+
cwd: () => '/user/tim/docs',
24+
argv: ['/bin/node', 'app.js'],
25+
execArgv: ['--require', './something.js', '--import=else.js'],
26+
},
27+
{
28+
appPath: '/user/tim/docs/app.js',
29+
importPaths: ['/user/tim/docs/else.js'],
30+
requirePaths: ['/user/tim/docs/something.js'],
31+
},
32+
],
33+
[
34+
{
35+
cwd: () => '/user/tim/docs',
36+
argv: ['/bin/node', 'app.js'],
37+
execArgv: ['--require=here/something.js'],
38+
},
39+
{
40+
appPath: '/user/tim/docs/app.js',
41+
importPaths: [],
42+
requirePaths: ['/user/tim/docs/here/something.js'],
43+
},
44+
],
45+
];
46+
47+
describe('getEntryPointType', () => {
48+
it.each(PROCESS_ARG_TESTS)('parseProcessArgs', (input, output) => {
49+
const result = parseProcessPaths(input);
50+
expect(result).toStrictEqual(output);
51+
});
52+
53+
it('app absolute', () => {
54+
const ctx = getEntryPointType({
55+
cwd: () => __dirname,
56+
argv: ['/bin/node', __filename],
57+
execArgv: [],
58+
});
59+
60+
expect(ctx).toEqual('app');
61+
});
62+
63+
it('app relative', () => {
64+
const ctx = getEntryPointType({
65+
cwd: () => __dirname,
66+
argv: ['/bin/node', 'entry-point.test.ts'],
67+
execArgv: [],
68+
});
69+
70+
expect(ctx).toEqual('app');
71+
});
72+
73+
it('import absolute', () => {
74+
const ctx = getEntryPointType({
75+
cwd: () => __dirname,
76+
argv: ['/bin/node', 'app.ts'],
77+
execArgv: ['--import', __filename],
78+
});
79+
80+
expect(ctx).toEqual('import');
81+
});
82+
83+
it('import relative', () => {
84+
const ctx = getEntryPointType({
85+
cwd: () => __dirname,
86+
argv: ['/bin/node', 'app.ts'],
87+
execArgv: ['--import', './entry-point.test.ts'],
88+
});
89+
90+
expect(ctx).toEqual('import');
91+
});
92+
93+
it('require relative', () => {
94+
const ctx = getEntryPointType({
95+
cwd: () => __dirname,
96+
argv: ['/bin/node', 'app.ts'],
97+
execArgv: ['--require', './entry-point.test.ts'],
98+
});
99+
100+
expect(ctx).toEqual('require');
101+
});
102+
});

packages/node/tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"include": ["src/**/*"],
55

66
"compilerOptions": {
7-
"lib": ["es2018"],
7+
"lib": ["es2018", "es2020.string"],
88
"module": "Node16"
99
}
1010
}

0 commit comments

Comments
 (0)