Skip to content

Commit 41306ae

Browse files
committed
feat(config): add TypeScript error logging during external file loading
1 parent 36a5302 commit 41306ae

File tree

2 files changed

+116
-4
lines changed

2 files changed

+116
-4
lines changed

packages/@intlayer/config/src/loadExternalFile.ts

+36-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
import {
2+
buildSync,
3+
type BuildFailure,
4+
type BuildOptions,
5+
type BuildResult,
6+
} from 'esbuild';
7+
import { dirname } from 'path';
18
import { runInNewContext } from 'vm';
2-
import { type BuildOptions, buildSync, type BuildResult } from 'esbuild';
9+
import { LoadEnvFileOptions } from './envVariables/loadEnvFile';
310
import { getSandBoxContext } from './getSandboxContext';
4-
import { ESMxCJSRequire } from './utils/ESMxCJSRequire';
511
import { logger } from './logger';
6-
import { LoadEnvFileOptions } from './envVariables/loadEnvFile';
7-
import { dirname } from 'path';
12+
import { logTypeScriptErrors } from './logTypeScriptErrors';
13+
import { ESMxCJSRequire } from './utils/ESMxCJSRequire';
814

915
const getTransformationOptions = (filePath: string): BuildOptions => ({
1016
loader: {
@@ -55,11 +61,26 @@ export const loadExternalFile = (
5561

5662
// Rest is JS, MJS or TS
5763

64+
if (fileExtension === 'ts' || fileExtension === 'tsx') {
65+
logTypeScriptErrors(filePath);
66+
}
67+
5868
const moduleResult: BuildResult = buildSync({
5969
entryPoints: [filePath],
6070
...getTransformationOptions(filePath),
71+
logLevel: 'silent', // prevent auto printing
6172
});
6273

74+
// Log Warnings from esbuild
75+
if (moduleResult.warnings?.length > 0) {
76+
for (const warn of moduleResult.warnings) {
77+
logger(
78+
`[ESBUILD WARNING] ${warn.text} at ${warn.location?.file ?? filePath}:${warn.location?.line ?? '?'}:${warn.location?.column ?? '?'}`,
79+
{ level: 'warn' }
80+
);
81+
}
82+
}
83+
6384
const moduleResultString = moduleResult.outputFiles?.[0].text;
6485

6586
if (!moduleResultString) {
@@ -104,6 +125,17 @@ export const loadExternalFile = (
104125

105126
return fileContent;
106127
} catch (error) {
128+
// Specific handling for esbuild errors (i.e. build-time TypeScript errors)
129+
if ((error as BuildFailure).errors) {
130+
const esbuildErrors = (error as BuildFailure).errors;
131+
for (const err of esbuildErrors) {
132+
logger(
133+
`${err.text} at ${err.location?.file}:${err.location?.line}:${err.location?.column}`,
134+
{ level: 'error' }
135+
);
136+
}
137+
}
138+
107139
logger(
108140
`Error: ${error} ${JSON.stringify((error as Error).stack, null, 2)}`,
109141
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { appLogger } from '@intlayer/config';
2+
import {
3+
createProgram,
4+
findConfigFile,
5+
flattenDiagnosticMessageText,
6+
getPreEmitDiagnostics,
7+
parseConfigFileTextToJson,
8+
parseJsonConfigFileContent,
9+
sys,
10+
} from 'typescript';
11+
12+
export const logTypeScriptErrors = (filePath: string) => {
13+
const configFileName = findConfigFile(
14+
process.cwd(),
15+
sys.fileExists,
16+
'tsconfig.json'
17+
);
18+
if (!configFileName) {
19+
appLogger("Could not find a 'tsconfig.json' file.", { level: 'error' });
20+
return;
21+
}
22+
23+
const configFileText = sys.readFile(configFileName);
24+
if (!configFileText) {
25+
appLogger(`Could not read the 'tsconfig.json' file at ${configFileName}.`, {
26+
level: 'error',
27+
});
28+
return;
29+
}
30+
31+
const configJson = parseConfigFileTextToJson(configFileName, configFileText);
32+
if (configJson.error) {
33+
appLogger(
34+
`Error parsing 'tsconfig.json': ${flattenDiagnosticMessageText(configJson.error.messageText, '\n')}`,
35+
{ level: 'error' }
36+
);
37+
return;
38+
}
39+
40+
const parsedConfig = parseJsonConfigFileContent(
41+
configJson.config,
42+
sys,
43+
process.cwd()
44+
);
45+
if (parsedConfig.errors.length > 0) {
46+
parsedConfig.errors.forEach((error) => {
47+
appLogger(
48+
`Error in tsconfig.json: ${flattenDiagnosticMessageText(error.messageText, '\n')}`,
49+
{ level: 'error' }
50+
);
51+
});
52+
return;
53+
}
54+
55+
// Remove incremental options to avoid the error
56+
const { incremental, tsBuildInfoFile, ...compilerOptions } =
57+
parsedConfig.options;
58+
59+
// Create a program with the modified compiler options
60+
const program = createProgram([filePath], {
61+
...compilerOptions,
62+
noEmit: true,
63+
});
64+
const diagnostics = getPreEmitDiagnostics(program);
65+
66+
diagnostics.forEach((diagnostic) => {
67+
const message = flattenDiagnosticMessageText(diagnostic.messageText, '\n');
68+
if (diagnostic.file && diagnostic.start !== undefined) {
69+
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(
70+
diagnostic.start
71+
);
72+
appLogger(
73+
`TS Error in ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`,
74+
{ level: 'error' }
75+
);
76+
} else {
77+
appLogger(`TS Error: ${message}`, { level: 'error' });
78+
}
79+
});
80+
};

0 commit comments

Comments
 (0)