diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a551bed2d27e9..d025994c2836c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -242,3 +242,17 @@ jobs: echo "Unused baselines:" git diff --exit-code --name-only fi + + check-project-references: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: "*" + check-latest: true + - run: npm ci + + - name: Check project references + run: node ./scripts/checkProjectReferences.mjs diff --git a/scripts/checkProjectReferences.mjs b/scripts/checkProjectReferences.mjs new file mode 100644 index 0000000000000..8d712fe4ce8d4 --- /dev/null +++ b/scripts/checkProjectReferences.mjs @@ -0,0 +1,84 @@ +import assert from "assert"; +import fs from "fs"; +import glob from "glob"; +import JSONC from "jsonc-parser"; +import path from "path"; +import url from "url"; + + +const __filename = url.fileURLToPath(new URL(import.meta.url)); +const __dirname = path.dirname(__filename); + + +// This script checks that we list all transitive references in all tsconfig.json files. +// See: https://github.com/microsoft/TypeScript/issues/30608 + + +/** + * @param {string} p + */ +function getTsconfigPath(p) { + if (fs.statSync(p).isDirectory()) { + p += "/tsconfig.json"; + } + else if (!p.endsWith(".json")) { + p += ".json"; + } + return p; +} + +/** + * @param {string} p + */ +function getReferences(p) { + const result = getReferencesWorker(p, new Set()); + assert(result); + return result; + + /** + * @param {string} p + * @param {Set} seen + */ + function getReferencesWorker(p, seen) { + const tsconfigPath = getTsconfigPath(p); + if (seen.has(tsconfigPath)) { + return undefined; + } + seen.add(tsconfigPath); + + const dir = path.dirname(tsconfigPath); + const contents = JSONC.parse(fs.readFileSync(tsconfigPath, "utf8")); + const references = new Set(); + for (const r of contents.references || []) { + references.add(path.resolve(dir, r.path)); + } + + const transitiveReferences = new Set(); + for (const r of references) { + const result = getReferencesWorker(r, seen); + if (!result) continue; + + const [otherReferences, otherTransitiveReferences] = result; + for (const r of otherReferences) { + transitiveReferences.add(r); + } + for (const r of otherTransitiveReferences) { + transitiveReferences.add(r); + } + } + + return [references, transitiveReferences]; + } +} + +const paths = glob.sync("src/**/*tsconfig*.json", { cwd: path.resolve(__dirname, "..") }); +for (const p of paths) { + const [references, transitiveReferences] = getReferences(p); + + for (const r of transitiveReferences) { + if (!references.has(r)) { + console.error(`${p} should reference ${path.relative(path.dirname(p), r)}`); + process.exitCode = 1; + } + } +} diff --git a/src/tsserver/tsconfig.json b/src/tsserver/tsconfig.json index f24abdaf6fc07..bf0ba2f9a7206 100644 --- a/src/tsserver/tsconfig.json +++ b/src/tsserver/tsconfig.json @@ -11,6 +11,8 @@ { "path": "../services" }, { "path": "../jsTyping" }, { "path": "../server" }, + { "path": "../typingsInstallerCore" }, + { "path": "../deprecatedCompat" }, ], "include": ["**/*"] }