Skip to content

Commit 99ba136

Browse files
test: api (#6)
1 parent 16e87a5 commit 99ba136

File tree

4 files changed

+147
-8
lines changed

4 files changed

+147
-8
lines changed

src/cjs/api/require.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,30 @@ import { register } from './global-require-patch.js';
44
import { resolveFilename } from './module-resolve-filename.js';
55

66
const getRequestContext = (
7-
filepath: string | URL,
7+
id: string,
8+
fromFile: string | URL,
89
) => {
10+
if (!fromFile) {
11+
throw new Error('The current file path (__filename or import.meta.url) must be provided in the second argument of tsx.require()');
12+
}
13+
914
if (
10-
(typeof filepath === 'string' && filepath.startsWith('file://'))
11-
|| filepath instanceof URL
15+
(typeof fromFile === 'string' && fromFile.startsWith('file://'))
16+
|| fromFile instanceof URL
1217
) {
13-
filepath = fileURLToPath(filepath);
18+
fromFile = fileURLToPath(fromFile);
1419
}
15-
return path.dirname(filepath);
20+
21+
return path.resolve(path.dirname(fromFile), id);
1622
};
1723

1824
const tsxRequire = (
1925
id: string,
2026
fromFile: string | URL,
2127
) => {
28+
const contextId = getRequestContext(id, fromFile);
2229
const unregister = register();
2330
try {
24-
const contextId = path.resolve(getRequestContext(fromFile), id);
25-
2631
// eslint-disable-next-line import-x/no-dynamic-require, n/global-require
2732
return require(contextId);
2833
} finally {
@@ -35,7 +40,7 @@ const resolve = (
3540
fromFile: string | URL,
3641
options?: { paths?: string[] | undefined },
3742
) => {
38-
const contextId = path.resolve(getRequestContext(fromFile), id);
43+
const contextId = getRequestContext(id, fromFile);
3944
return resolveFilename(contextId, module, false, options);
4045
};
4146
resolve.paths = require.resolve.paths;

tests/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { nodeVersions } from './utils/node-versions';
1111
const node = await createNode(nodeVersion);
1212
await describe(`Node ${node.version}`, async ({ runTestSuite }) => {
1313
await runTestSuite(import('./specs/cli'), node);
14+
await runTestSuite(import('./specs/api'), node);
1415
await runTestSuite(import('./specs/watch'), node);
1516
await runTestSuite(import('./specs/loaders'), node);
1617
await runTestSuite(

tests/specs/api.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import path from 'path';
2+
import { execaNode } from 'execa';
3+
import { testSuite, expect } from 'manten';
4+
import { createFixture } from 'fs-fixture';
5+
import { tsxEsmPath, cjsApiPath, type NodeApis } from '../utils/tsx.js';
6+
7+
const tsFiles = {
8+
'file.ts': `
9+
import { foo } from './foo'
10+
export const message = foo as string
11+
`,
12+
'foo.ts': `
13+
import { bar } from './bar.js'
14+
export const foo = \`foo \${bar}\` as string
15+
`,
16+
'bar.ts': 'export const bar = "bar" as string',
17+
};
18+
19+
export default testSuite(({ describe }, node: NodeApis) => {
20+
describe('API', ({ describe }) => {
21+
describe('CommonJS', ({ test }) => {
22+
test('register / unregister', async ({ onTestFinish }) => {
23+
const fixture = await createFixture({
24+
'register.cjs': `
25+
const { register } = require(${JSON.stringify(cjsApiPath)});
26+
try {
27+
require('./file');
28+
} catch {
29+
console.log('Fails as expected');
30+
}
31+
32+
const unregister = register();
33+
34+
const loaded = require('./file');
35+
console.log(loaded.message);
36+
37+
// Remove from cache
38+
const loadedPath = require.resolve('./file');
39+
delete require.cache[loadedPath];
40+
41+
unregister();
42+
43+
try {
44+
require('./file');
45+
} catch {
46+
console.log('Unregistered');
47+
}
48+
`,
49+
...tsFiles,
50+
});
51+
onTestFinish(async () => await fixture.rm());
52+
53+
const { stdout } = await execaNode(path.join(fixture.path, 'register.cjs'), [], {
54+
nodePath: node.path,
55+
nodeOptions: [],
56+
});
57+
58+
expect(stdout).toBe('Fails as expected\nfoo bar\nUnregistered');
59+
});
60+
61+
test('tsx.require()', async ({ onTestFinish }) => {
62+
const fixture = await createFixture({
63+
'require.cjs': `
64+
const tsx = require(${JSON.stringify(cjsApiPath)});
65+
try {
66+
require('./file');
67+
} catch {
68+
console.log('Fails as expected');
69+
}
70+
71+
const loaded = tsx.require('./file', __filename);
72+
console.log(loaded.message);
73+
74+
// Remove from cache
75+
const loadedPath = tsx.require.resolve('./file', __filename);
76+
delete require.cache[loadedPath];
77+
78+
try {
79+
require('./file');
80+
} catch {
81+
console.log('Unpolluted global require');
82+
}
83+
`,
84+
...tsFiles,
85+
});
86+
onTestFinish(async () => await fixture.rm());
87+
88+
const { stdout } = await execaNode(path.join(fixture.path, 'require.cjs'), [], {
89+
nodePath: node.path,
90+
nodeOptions: [],
91+
});
92+
93+
expect(stdout).toBe('Fails as expected\nfoo bar\nUnpolluted global require');
94+
});
95+
});
96+
97+
describe('Module', ({ test }) => {
98+
if (node.supports.moduleRegister) {
99+
test('module.register', async ({ onTestFinish }) => {
100+
const fixture = await createFixture({
101+
'package.json': JSON.stringify({ type: 'module' }),
102+
'module-register.mjs': `
103+
import { register } from 'node:module';
104+
105+
await import('./file.ts').catch((error) => {
106+
console.log('Fails as expected');
107+
});
108+
109+
register(${JSON.stringify(tsxEsmPath)}, {
110+
parentURL: import.meta.url,
111+
data: true,
112+
})
113+
114+
const { message } = await import('./file.ts?nocache')
115+
console.log(message)
116+
`,
117+
...tsFiles,
118+
});
119+
onTestFinish(async () => await fixture.rm());
120+
121+
const { stdout } = await execaNode(path.join(fixture.path, 'module-register.mjs'), [], {
122+
nodePath: node.path,
123+
nodeOptions: [],
124+
});
125+
126+
expect(stdout).toBe('Fails as expected\nfoo bar');
127+
});
128+
}
129+
});
130+
});
131+
});

tests/utils/tsx.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ type Options = {
1515
};
1616

1717
export const tsxPath = fileURLToPath(new URL('../../dist/cli.mjs', import.meta.url).toString());
18+
export const cjsApiPath = fileURLToPath(new URL('../../dist/cjs/api/index.cjs', import.meta.url).toString());
19+
export const tsxEsmPath = new URL('../../dist/esm/index.mjs', import.meta.url).toString();
1820

1921
const cjsPatchPath = fileURLToPath(new URL('../../dist/cjs/index.cjs', import.meta.url).toString());
2022
const hookPath = new URL('../../dist/esm/index.cjs', import.meta.url).toString();

0 commit comments

Comments
 (0)