-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathtsx.loader.mjs
100 lines (83 loc) · 2.51 KB
/
tsx.loader.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import path from 'node:path';
import { cwd } from 'node:process';
import { pathToFileURL } from 'node:url';
import { transform } from 'esbuild';
import { getFilenameExt } from '@nodejs-loaders/parse-filename';
import { findEsbuildConfig } from './find-esbuild-config.mjs';
/** @typedef {import('../types.d.ts').FileURL} FileURL */
/**
* The load hook needs to know the parent URL to find the esbuild config.
* But load hooks don't have access to the parent URL.
* If you try to pass it as return value from the resolve hook, it will be overwritten by node.
*
* @type {Map<FileURL, FileURL>}
*/
export const parentURLs = new Map();
/**
* @type {import('node:module').ResolveHook}
*/
async function resolveTSX(specifier, ctx, nextResolve) {
const nextResult = await nextResolve(specifier);
// Check against the fully resolved URL, not just the specifier, in case another loader has
// something to contribute to the resolution.
const ext = getFilenameExt(/** @type {FileURL} */ (nextResult.url));
parentURLs.set(
/** @type {FileURL} */ (nextResult.url),
/** @type {FileURL} */ (ctx.parentURL ?? pathToFileURL(path.join(cwd(), 'whatever.ext')).href),
);
if (ext === '.jsx') {
return {
...nextResult,
format: 'jsx',
};
}
if (ext === '.mts' || ext === '.ts' || ext === '.tsx') {
return {
...nextResult,
format: 'tsx',
};
}
return nextResult;
}
export { resolveTSX as resolve };
/**
* @type {import('node:module').LoadHook}
* @param {FileURL} url The fully resolved url.
*/
async function loadTSX(url, ctx, nextLoad) {
if (ctx.format !== 'jsx' && ctx.format !== 'tsx') return nextLoad(url); // not (j|t)sx
const format = 'module';
const esbuildConfig = findEsbuildConfig(url, parentURLs.get(url));
const nextResult = await nextLoad(url, {
format,
});
let rawSource = `${nextResult.source}`; // byte array → string
if (esbuildConfig.jsx === 'transform') {
rawSource = `import * as React from 'react';\n${rawSource}`;
}
const { code: source, warnings } = await transform(rawSource, {
sourcefile: url,
...esbuildConfig,
}).catch(({ errors }) => {
for (const {
location: { column, line, lineText },
text,
} of errors) {
// oxlint-disable-next-line no-console
console.error(
`TranspileError: ${text}\n at ${url}:${line}:${column}\n at: ${lineText}\n`,
);
}
return {
code: null,
warnings: [],
};
});
// oxlint-disable-next-line no-console
if (warnings?.length) console.warn(...warnings);
return {
format,
source,
};
}
export { loadTSX as load };