From 91512a94776efdab07961a61cb3365ce8e9bbdc4 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 11 Aug 2023 13:17:48 -0700 Subject: [PATCH 1/3] Ensure our project references include transitive references --- .github/workflows/ci.yml | 14 +++++++ scripts/checkProjectReferences.mjs | 65 ++++++++++++++++++++++++++++++ src/tsserver/tsconfig.json | 2 + src/tsserverlibrary/tsconfig.json | 4 +- 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 scripts/checkProjectReferences.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a551bed2d27e9..e99c91c5e196c 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 src/**/*tsconfig*.json diff --git a/scripts/checkProjectReferences.mjs b/scripts/checkProjectReferences.mjs new file mode 100644 index 0000000000000..05aa60c002d54 --- /dev/null +++ b/scripts/checkProjectReferences.mjs @@ -0,0 +1,65 @@ +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 tsconfigPath = getTsconfigPath(p); + 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 [references, parentTransitiveReferences] = getReferences(r); + for (const r of references) { + transitiveReferences.add(r); + } + for (const r of parentTransitiveReferences) { + 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": ["**/*"] } diff --git a/src/tsserverlibrary/tsconfig.json b/src/tsserverlibrary/tsconfig.json index 20b8306af53eb..28bab0196aa36 100644 --- a/src/tsserverlibrary/tsconfig.json +++ b/src/tsserverlibrary/tsconfig.json @@ -6,7 +6,9 @@ { "path": "../compiler" }, { "path": "../jsTyping" }, { "path": "../services" }, - { "path": "../server" } + { "path": "../server" }, + { "path": "../typingsInstallerCore"}, + { "path": "../deprecatedCompat" }, ], "include": ["**/*"] } From a8e9714a253dd015cb02b0f4ae9af781e1b1ea86 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 14 Aug 2023 16:03:19 -0700 Subject: [PATCH 2/3] Update .github/workflows/ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e99c91c5e196c..d025994c2836c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -255,4 +255,4 @@ jobs: - run: npm ci - name: Check project references - run: node ./scripts/checkProjectReferences.mjs src/**/*tsconfig*.json + run: node ./scripts/checkProjectReferences.mjs From 3b82b6340182337210b528f5546767d4cd04423e Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 14 Aug 2023 16:12:17 -0700 Subject: [PATCH 3/3] Guard against cycles --- scripts/checkProjectReferences.mjs | 51 ++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/scripts/checkProjectReferences.mjs b/scripts/checkProjectReferences.mjs index 05aa60c002d54..8d712fe4ce8d4 100644 --- a/scripts/checkProjectReferences.mjs +++ b/scripts/checkProjectReferences.mjs @@ -1,3 +1,4 @@ +import assert from "assert"; import fs from "fs"; import glob from "glob"; import JSONC from "jsonc-parser"; @@ -30,26 +31,44 @@ function getTsconfigPath(p) { * @param {string} p */ function getReferences(p) { - const tsconfigPath = getTsconfigPath(p); - 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 result = getReferencesWorker(p, new Set()); + assert(result); + return result; - const transitiveReferences = new Set(); - for (const r of references) { - const [references, parentTransitiveReferences] = getReferences(r); - for (const r of references) { - transitiveReferences.add(r); + /** + * @param {string} p + * @param {Set} seen + */ + function getReferencesWorker(p, seen) { + const tsconfigPath = getTsconfigPath(p); + if (seen.has(tsconfigPath)) { + return undefined; } - for (const r of parentTransitiveReferences) { - transitiveReferences.add(r); + 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)); } - } - return [references, transitiveReferences]; + 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, "..") });