Skip to content

feat(config): add TypeScript error logging during external file loading #110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
40 changes: 36 additions & 4 deletions packages/@intlayer/config/src/loadExternalFile.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import {
buildSync,
type BuildFailure,
type BuildOptions,
type BuildResult,
} from 'esbuild';
import { dirname } from 'path';
import { runInNewContext } from 'vm';
import { type BuildOptions, buildSync, type BuildResult } from 'esbuild';
import { LoadEnvFileOptions } from './envVariables/loadEnvFile';
import { getSandBoxContext } from './getSandboxContext';
import { ESMxCJSRequire } from './utils/ESMxCJSRequire';
import { logger } from './logger';
import { LoadEnvFileOptions } from './envVariables/loadEnvFile';
import { dirname } from 'path';
import { logTypeScriptErrors } from './logTypeScriptErrors';
import { ESMxCJSRequire } from './utils/ESMxCJSRequire';

const getTransformationOptions = (filePath: string): BuildOptions => ({
loader: {
Expand Down Expand Up @@ -55,11 +61,26 @@ export const loadExternalFile = (

// Rest is JS, MJS or TS

if (fileExtension === 'ts' || fileExtension === 'tsx') {
logTypeScriptErrors(filePath);
}

const moduleResult: BuildResult = buildSync({
entryPoints: [filePath],
...getTransformationOptions(filePath),
logLevel: 'silent', // prevent auto printing
});

// Log Warnings from esbuild
if (moduleResult.warnings?.length > 0) {
for (const warn of moduleResult.warnings) {
logger(
`[ESBUILD WARNING] ${warn.text} at ${warn.location?.file ?? filePath}:${warn.location?.line ?? '?'}:${warn.location?.column ?? '?'}`,
{ level: 'warn' }
);
}
}

const moduleResultString = moduleResult.outputFiles?.[0].text;

if (!moduleResultString) {
Expand Down Expand Up @@ -104,6 +125,17 @@ export const loadExternalFile = (

return fileContent;
} catch (error) {
// Specific handling for esbuild errors (i.e. build-time TypeScript errors)
if ((error as BuildFailure).errors) {
const esbuildErrors = (error as BuildFailure).errors;
for (const err of esbuildErrors) {
logger(
`${err.text} at ${err.location?.file}:${err.location?.line}:${err.location?.column}`,
{ level: 'error' }
);
}
}

logger(
`Error: ${error} ${JSON.stringify((error as Error).stack, null, 2)}`,
{
Expand Down
80 changes: 80 additions & 0 deletions packages/@intlayer/config/src/logTypeScriptErrors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { appLogger } from '@intlayer/config';
import {
createProgram,
findConfigFile,
flattenDiagnosticMessageText,
getPreEmitDiagnostics,
parseConfigFileTextToJson,
parseJsonConfigFileContent,
sys,
} from 'typescript';

export const logTypeScriptErrors = (filePath: string) => {
const configFileName = findConfigFile(
process.cwd(),
sys.fileExists,
'tsconfig.json'
);
if (!configFileName) {
appLogger("Could not find a 'tsconfig.json' file.", { level: 'error' });
return;
}

const configFileText = sys.readFile(configFileName);
if (!configFileText) {
appLogger(`Could not read the 'tsconfig.json' file at ${configFileName}.`, {
level: 'error',
});
return;
}

const configJson = parseConfigFileTextToJson(configFileName, configFileText);
if (configJson.error) {
appLogger(
`Error parsing 'tsconfig.json': ${flattenDiagnosticMessageText(configJson.error.messageText, '\n')}`,
{ level: 'error' }
);
return;
}

const parsedConfig = parseJsonConfigFileContent(
configJson.config,
sys,
process.cwd()
);
if (parsedConfig.errors.length > 0) {
parsedConfig.errors.forEach((error) => {
appLogger(
`Error in tsconfig.json: ${flattenDiagnosticMessageText(error.messageText, '\n')}`,
{ level: 'error' }
);
});
return;
}

// Remove incremental options to avoid the error
const { incremental, tsBuildInfoFile, ...compilerOptions } =
parsedConfig.options;

// Create a program with the modified compiler options
const program = createProgram([filePath], {
...compilerOptions,
noEmit: true,
});
const diagnostics = getPreEmitDiagnostics(program);

diagnostics.forEach((diagnostic) => {
const message = flattenDiagnosticMessageText(diagnostic.messageText, '\n');
if (diagnostic.file && diagnostic.start !== undefined) {
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(
diagnostic.start
);
appLogger(
`TS Error in ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`,
{ level: 'error' }
);
} else {
appLogger(`TS Error: ${message}`, { level: 'error' });
}
});
};
Loading