wix-style-react
Version:
wix-style-react
211 lines (185 loc) • 5.98 kB
JavaScript
import ts from 'typescript';
import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
import boxen from 'boxen';
function checkTypeScriptErrors(fileNames, options) {
const program = ts.createProgram(fileNames, options);
const emitResult = program.emit();
const allDiagnostics = ts
.getPreEmitDiagnostics(program)
.concat(emitResult.diagnostics);
if (allDiagnostics.length > 0) {
const errors = allDiagnostics.map(diagnostic => {
if (diagnostic.file) {
const { line, character } =
diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
const message = ts.flattenDiagnosticMessageText(
diagnostic.messageText,
'\n',
);
return {
fileName: diagnostic.file.fileName,
message: `${diagnostic.file.fileName} (${line + 1},${
character + 1
}): ${message}`,
};
} else {
return {
fileName: 'unknown',
message: ts.flattenDiagnosticMessageText(
diagnostic.messageText,
'\n',
),
};
}
});
throw errors;
}
}
function getExportsFromFile(fileName, visit) {
const program = ts.createProgram([fileName], { allowJs: true });
const checker = program.getTypeChecker();
const sourceFile = program.getSourceFile(fileName);
const exports = [];
ts.forEachChild(sourceFile, node => visit(node, checker, exports));
return exports;
}
function visitJS(node, checker, exports) {
if (ts.isVariableStatement(node)) {
node.declarationList.declarations.forEach(declaration => {
let identifier;
declaration.forEachChild(dec => {
if (ts.isIdentifier(dec)) {
identifier = dec.getText();
}
if (ts.isBinaryExpression(dec) && dec?.left?.expression?.getText() === 'exports') {
exports.push(identifier);
}
});
});
}
ts.forEachChild(node, node => visitJS(node, checker, exports));
}
function visitTS(node, checker, exports) {
if (
ts.isExpressionStatement(node) &&
ts.isBinaryExpression(node.expression) &&
ts.isPropertyAccessExpression(node.expression.left) &&
node.expression.left.expression.getText() === 'exports'
) {
const symbol = checker.getSymbolAtLocation(node.expression.left.name);
if (symbol) {
exports.push(symbol.getName());
}
} else if (
ts.isVariableStatement(node) &&
node.modifiers?.some(
modifier => modifier.kind === ts.SyntaxKind.ExportKeyword,
)
) {
node.declarationList.declarations.forEach(declaration => {
const symbol = checker.getSymbolAtLocation(declaration.name);
if (symbol) {
exports.push(symbol.getName());
}
});
}
ts.forEachChild(node, node => visitTS(node, checker, exports));
}
function validateMatchingExportsInJsAndDts(dtsFiles) {
const exportErrors = dtsFiles.reduce((errors, dtsFile) => {
const jsFile = dtsFile.replace('.d.ts', '.js');
if (fs.existsSync(jsFile)) {
const jsExports = getExportsFromFile(jsFile, visitJS);
const dtsExports = getExportsFromFile(dtsFile, visitTS);
const missingExportsInDts = jsExports.filter(
jsExport => !dtsExports.includes(jsExport),
);
const missingExportsInJs = dtsExports.filter(
dtsExport => !jsExports.includes(dtsExport),
);
if (missingExportsInDts.length > 0) {
missingExportsInDts.forEach(missingExport => {
errors.push({
fileName: dtsFile,
message: `The following export exist in js but missing in the .d.ts file: ${missingExport}`,
});
});
}
if (missingExportsInJs.length > 0) {
missingExportsInJs.forEach(missingExport => {
errors.push({
fileName: jsFile,
message: `The following export exist in d.ts but missing in the js file: ${missingExport}`,
});
});
}
if (errors.length === 0) {
console.log(`Successfully exported ${jsExports.length} for ${dtsFile.substring(dtsFile.lastIndexOf('/') + 1)}`);
}
return errors;
}
}, []);
if (exportErrors.length > 0) {
throw exportErrors;
}
}
async function checkValidImportsInJs(dtsFiles) {
const errors = [];
for (const dtsFile of dtsFiles) {
const jsFile = dtsFile.replace('.d.ts', '.js');
try {
await import(jsFile);
} catch (error) {
errors.push({
fileName: jsFile,
message: `The failed to compile: ${error} `,
});
}
}
if (errors.length > 0) {
throw errors;
}
}
const directoryPath = path.join(process.cwd(), '/dist/testkit');
const filesInDirectory = fs.readdirSync(directoryPath);
const dtsFiles = filesInDirectory
.filter(file => file.includes('.d.ts'))
.map(file => path.join(directoryPath, file));
if (dtsFiles.length > 0) {
try {
checkTypeScriptErrors(dtsFiles, {
noEmit: true,
target: ts.ScriptTarget.ES2017,
module: ts.ModuleKind.CommonJS,
include: [directoryPath],
types: [],
allowSyntheticDefaultImports: true,
});
validateMatchingExportsInJsAndDts(dtsFiles);
await checkValidImportsInJs(dtsFiles);
} catch (errors) {
if (!errors.reduce) {
console.log(errors);
} else {
const groupedErrors = errors.reduce((groups, error) => {
if (!groups[error.fileName]) {
groups[error.fileName] = [];
}
groups[error.fileName].push(error.message);
return groups;
}, {});
for (const fileName in groupedErrors) {
console.error(chalk.red(boxen(`Errors in ${fileName}:`, { padding: 1 })));
groupedErrors[fileName].forEach((errorMessage, index) => {
console.error(`${index + 1}: ${chalk.red(errorMessage)}`);
});
}
}
process.exit(1);
}
} else {
console.error('No .d.ts files found in the dist/testkits directory.');
process.exit(1);
}