UNPKG

wix-style-react

Version:
211 lines (185 loc) • 5.98 kB
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); }