diff --git a/.changeset/smart-phones-exist.md b/.changeset/smart-phones-exist.md new file mode 100644 index 0000000000..eb1c3d041a --- /dev/null +++ b/.changeset/smart-phones-exist.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Added a new experimental `repo list-deprecations` command, which scans the entire project for usage of deprecated APIs. diff --git a/packages/cli/package.json b/packages/cli/package.json index f9147b924e..66b0456e32 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -68,15 +68,16 @@ "esbuild": "^0.14.10", "esbuild-loader": "^2.18.0", "eslint": "^8.6.0", - "eslint-webpack-plugin": "^2.6.0", "eslint-config-prettier": "^8.3.0", "eslint-formatter-friendly": "^7.0.0", + "eslint-plugin-deprecation": "^1.3.2", "eslint-plugin-import": "^2.25.4", "eslint-plugin-jest": "^25.3.4", "eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-monorepo": "^0.3.2", "eslint-plugin-react": "^7.28.0", "eslint-plugin-react-hooks": "^4.3.0", + "eslint-webpack-plugin": "^2.6.0", "express": "^4.17.1", "fork-ts-checker-webpack-plugin": "^7.0.0-alpha.8", "fs-extra": "10.0.1", @@ -86,13 +87,13 @@ "inquirer": "^8.2.0", "jest": "^26.0.1", "jest-css-modules": "^2.1.0", - "json-schema": "^0.4.0", "jest-transform-yaml": "^1.0.0", + "json-schema": "^0.4.0", "lodash": "^4.17.21", - "minimatch": "5.0.1", "mini-css-extract-plugin": "^2.4.2", - "npm-packlist": "^3.0.0", + "minimatch": "5.0.1", "node-libs-browser": "^2.2.1", + "npm-packlist": "^3.0.0", "ora": "^5.3.0", "postcss": "^8.1.0", "process": "^0.11.10", @@ -123,9 +124,9 @@ "devDependencies": { "@backstage/backend-common": "^0.13.1", "@backstage/config": "^1.0.0", + "@backstage/core-app-api": "^1.0.0", "@backstage/core-components": "^0.9.2", "@backstage/core-plugin-api": "^1.0.0", - "@backstage/core-app-api": "^1.0.0", "@backstage/dev-utils": "^1.0.0", "@backstage/test-utils": "^1.0.0", "@backstage/theme": "^0.2.15", @@ -147,9 +148,9 @@ "@types/yarnpkg__lockfile": "^1.1.4", "del": "^6.0.0", "mock-fs": "^5.1.0", + "msw": "^0.35.0", "nodemon": "^2.0.2", - "ts-node": "^10.0.0", - "msw": "^0.35.0" + "ts-node": "^10.0.0" }, "peerDependencies": { "@microsoft/api-extractor": "^7.19.2" diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index bc427bb19b..43cb557828 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -59,6 +59,14 @@ export function registerRepoCommand(program: CommanderStatic) { ) .option('--fix', 'Attempt to automatically fix violations') .action(lazy(() => import('./repo/lint').then(m => m.command))); + + command + .command('list-deprecations', { hidden: true }) + .description('List deprecations. [EXPERIMENTAL]') + .option('--json', 'Output as JSON') + .action( + lazy(() => import('./repo/list-deprecations').then(m => m.command)), + ); } export function registerScriptCommand(program: CommanderStatic) { diff --git a/packages/cli/src/commands/repo/list-deprecations.ts b/packages/cli/src/commands/repo/list-deprecations.ts new file mode 100644 index 0000000000..b53118cc40 --- /dev/null +++ b/packages/cli/src/commands/repo/list-deprecations.ts @@ -0,0 +1,90 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import chalk from 'chalk'; +import { ESLint } from 'eslint'; +import { Command } from 'commander'; +import { join as joinPath, relative as relativePath } from 'path'; +import { paths } from '../../lib/paths'; +import { PackageGraph } from '../../lib/monorepo'; + +export async function command(cmd: Command) { + const packages = await PackageGraph.listTargetPackages(); + + const eslint = new ESLint({ + cwd: paths.targetDir, + overrideConfig: { + plugins: ['deprecation'], + rules: { + 'deprecation/deprecation': 'error', + }, + parserOptions: { + project: [paths.resolveTargetRoot('tsconfig.json')], + }, + }, + extensions: ['jsx', 'ts', 'tsx', 'mjs', 'cjs'], + }); + + const { stderr } = process; + if (stderr.isTTY) { + stderr.write('Initializing TypeScript...'); + } + + const deprecations = []; + for (const [index, pkg] of packages.entries()) { + const results = await eslint.lintFiles(joinPath(pkg.dir, 'src')); + for (const result of results) { + for (const message of result.messages) { + if (message.ruleId !== 'deprecation/deprecation') { + continue; + } + + const path = relativePath(paths.targetRoot, result.filePath); + deprecations.push({ + path, + message: message.message, + line: message.line, + column: message.column, + }); + } + } + + if (stderr.isTTY) { + stderr.clearLine(0); + stderr.cursorTo(0); + stderr.write(`Scanning packages ${index + 1}/${packages.length}`); + } + } + + if (stderr.isTTY) { + stderr.clearLine(0); + stderr.cursorTo(0); + } + + if (cmd.json) { + console.log(JSON.stringify(deprecations, null, 2)); + } else { + for (const d of deprecations) { + const location = `${d.path}:${d.line}:${d.column}`; + const wrappedMessage = d.message.replace(/\r?\n\s*/g, ' '); + console.log(`${location} - ${chalk.yellow(wrappedMessage)}`); + } + } + + if (deprecations.length > 0) { + process.exit(1); + } +} diff --git a/yarn.lock b/yarn.lock index d6ca05e230..96d9301fae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11622,6 +11622,15 @@ eslint-plugin-cypress@^2.10.3: dependencies: globals "^11.12.0" +eslint-plugin-deprecation@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/eslint-plugin-deprecation/-/eslint-plugin-deprecation-1.3.2.tgz#a8125d28c56158cdfa1a685197e6be8ed86f189e" + integrity sha512-z93wbx9w7H/E3ogPw6AZMkkNJ6m51fTZRNZPNQqxQLmx+KKt7aLkMU9wN67s71i+VVHN4tLOZ3zT3QLbnlC0Mg== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + tslib "^2.3.1" + tsutils "^3.21.0" + eslint-plugin-graphql@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/eslint-plugin-graphql/-/eslint-plugin-graphql-4.0.0.tgz#d238ff2baee4d632cfcbe787a7a70a1f50428358" @@ -23973,7 +23982,7 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@~2.3.0: +tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@~2.3.0: version "2.3.1" resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==