From 774c43fa0a607bdf45c8c2a60797f7eca3103502 Mon Sep 17 00:00:00 2001 From: Bob Ippolito Date: Thu, 18 Apr 2024 12:45:13 -0700 Subject: [PATCH] Add validate-tsc-types type validation step after build --- package.json | 5 ++- scripts/validate-tsc-types.js | 85 +++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 scripts/validate-tsc-types.js diff --git a/package.json b/package.json index 1ec7a0e3482f..851f1151620f 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,11 @@ "dev": "npm run dev --prefix packages/lexical-playground", "start-test-server": "npm run preview --prefix packages/lexical-playground -- --port 4000", "build": "node scripts/build.js", - "build-prod": "npm run clean && npm run build -- --prod", + "build-prod": "npm run clean && npm run build -- --prod && node ./scripts/validate-tsc-types.js", "build-playground-prod": "npm run build-prod && npm run build-prod --prefix packages/lexical-playground", - "build-release": "npm run build-prod -- --release", + "build-release": "npm run build-prod -- --release && node ./scripts/validate-tsc-types.js", "build-www": "npm run clean && npm run build -- --www && npm run build -- --www --prod && npm run prepare-www", + "build-types": "tsc -p ./tsconfig.build.json && node ./scripts/validate-tsc-types.js", "clean": "node scripts/clean.js", "extract-codes": "node scripts/build.js --codes", "flow": "node ./scripts/check-flow-types.js", diff --git a/scripts/validate-tsc-types.js b/scripts/validate-tsc-types.js new file mode 100644 index 000000000000..0216caba7cfc --- /dev/null +++ b/scripts/validate-tsc-types.js @@ -0,0 +1,85 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +// @ts-check +'use strict'; + +const fs = require('fs-extra'); +const glob = require('glob'); +const ts = require('typescript'); + +const pretty = process.env.CI !== 'true'; + +/** @type {ts.FormatDiagnosticsHost} */ +const diagnosticsHost = { + getCanonicalFileName: (fn) => fn, + getCurrentDirectory: () => './', + getNewLine: () => '\n', +}; + +/** + * @returns {number} error code for process.exit + */ +function validateTscTypes() { + const dtsFilesPattern = './.ts-temp/{lexical,lexical-*}/**/*.d.ts'; + const dtsFiles = glob.sync(dtsFilesPattern); + if (dtsFiles.length === 0) { + console.error( + `Missing ${dtsFilesPattern}, \`npm run build-prod\` or \`npm run build-release\` first`, + ); + process.exit(1); + } + /** @type {ts.Diagnostic[]} */ + const diagnostics = []; + for (const fn of dtsFiles) { + // console.log(fn); + const ast = ts.createSourceFile( + fn, + fs.readFileSync(fn, 'utf-8'), + ts.ScriptTarget.Latest, + ); + const checkSpecifier = (/** @type {ts.Node | undefined} */ node) => { + if (!node || node.kind !== ts.SyntaxKind.StringLiteral) { + return; + } + const specifier = /** @type {import('typescript').StringLiteral} */ ( + node + ); + if (/^shared(\/|$)/.test(specifier.text)) { + const start = specifier.getStart(ast); + diagnostics.push({ + category: ts.DiagnosticCategory.Error, + code: Infinity, + file: ast, + length: specifier.getEnd() - start, + messageText: `Published .d.ts files must not import private module '${specifier.text}'.`, + start, + }); + } + }; + ast.forEachChild((node) => { + if (node.kind === ts.SyntaxKind.ExportDeclaration) { + const exportNode = + /** @type {import('typescript').ExportDeclaration} */ (node); + checkSpecifier(exportNode.moduleSpecifier); + } else if (node.kind === ts.SyntaxKind.ImportDeclaration) { + const importNode = + /** @type {import('typescript').ImportDeclaration} */ (node); + checkSpecifier(importNode.moduleSpecifier); + } + }); + } + if (diagnostics.length > 0) { + const msg = ( + pretty ? ts.formatDiagnosticsWithColorAndContext : ts.formatDiagnostics + )(diagnostics, diagnosticsHost); + console.error(msg.replace(/ TSInfinity:/g, ':')); + process.exit(1); + } +} + +validateTscTypes();