跳至主要內容

效能除錯

正如在 支援型別的 linting 文件 中提到的,如果你使用支援型別的 linting,你的 linting 時間理論上會和你的建置時間相去無幾。

如果你遇到的時間大幅慢於理論值,表示有幾個常見的罪魁禍首。

tsconfig 中的使用範例過廣

當使用支援型別的 linting 時,你需要提供一或多個 tsconfig。我們會先將所有檔案都進行預先解析,以便取得完整而充分的類型資訊。

如果在 include 中提供了範圍非常廣的 glob (像是 **/*),會導致比你預期還要多得多的檔案包含在這個預先解析中。此外,如果你沒有在 tsconfig 中提供 include,則等同於提供了範圍最廣的 glob。

廣泛 glob 會讓 TypeScript 分析類似建構程式的項目,這可能會對效能造成很大的影響。請務必提供目標為特別希望執行 linting 檢查的資料夾的 globs。

ESLint 選項中的廣泛 includes

在 ESLint 指令中指定 `tsconfig.json` 路徑也可能會產生比預期還多的磁碟 IO。請優先選用使用單一 `*` 且不使用 `**` 來遞迴檢查所有資料夾的路徑,而非具有 `**` 的 glob。

eslint.config.js
// @ts-check

import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';

export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommendedRequiringTypeChecking,
{
languageOptions: {
parserOptions: {
tsconfigRootDir: import.meta.dirname,
project: ['./**/tsconfig.json'],
project: ['./packages/*/tsconfig.json'],
},
},
},
);

請參閱 分析器選項「專案」中的 Glob 模式會讓 linting 檢查變慢 以取得更多詳細資料。

indent / @typescript-eslint/indent 規則

此規則協助確保您的程式碼庫遵循一致的縮排模式。不過,這會涉及檔案中每個單一標記的大量運算。在大型程式碼庫中,這些運算會累加起來並嚴重影響效能。

我們建議您不要使用此規則,而改用類似 prettier 的工具,強制套用標準化格式化。

請參閱我們的 格式化文件 以取得更多資訊。

eslint-plugin-prettier

此外掛元件會在 linting 檢查期間浮出 Prettier 格式化問題,協助確保您的程式碼隨時都有格式化。不過,這會產生相當大的成本 - 為了找出是否存在差異,它必須對每個在執行 linting 檢查的檔案進行一次 Prettier 格式化。這表示每個檔案會被分析兩次 - 一次由 ESLint 分析,另一次由 Prettier 分析。這會為大型程式碼庫累積起來。

我們建議您使用 Prettier 的 --check 旗標,而非使用此外掛元件,以偵測檔案是否未格式化正確。例如,我們的持續整合已設定為自動執行下列指令,這會阻擋未格式化的公關事項

npm run prettier --check .

請參閱 Prettier 的 --check 文件 以取得更多詳細資料。

eslint-plugin-import

這是我們在這個專案中自己使用的另一個偉大外掛程式。但是,有幾個規則可能會導致你的 Lints 非常慢,因為它們會導致外掛程式執行自己的剖析和檔案追蹤。對於大型程式庫而言,這個重複剖析會累積起來。

有許多規則會單獨分析檔案,但我們提供以下建議。

我們建議你不需要使用以下規則,因為 TypeScript 提供的檢查屬於標準類型檢查的一部份。

  • import/named
  • import/namespace
  • import/default
  • import/no-named-as-default-member
  • import/no-unresolved(只要你使用 import 而非 require

以下規則在 TypeScript 中沒有等效的檢查,因此我們建議你只在 CI/推送時執行這些規則,以減輕本地的效能負擔。

  • import/no-named-as-default
  • import/no-cycle
  • import/no-unused-modules
  • import/no-deprecated

import/extensions

強制使用副檔名

如果你想強制執行檔案副檔名的使用,且你沒有使用 moduleResolution node16nodenext,那對你而言其實沒有什麼好的替代方案,你應該繼續使用 import/extensions Lint 規則。

如果你想強制執行檔案副檔名的使用,且你使用 moduleResolution node16nodenext,那你就完全不需要使用 Lint 規則,因為 TypeScript 會自動強制你包含副檔名!

強制不使用副檔名

從表面上來看,import/extensions 似乎應該很快就能用於此用例,但這個規則不僅僅是純 AST 檢查,它必須解析磁碟中的模組,否則它不會對你輸入副檔名作為名稱的模組產生誤報(例如,foo.js 解析為 node_modules/foo.js/index.js,因此需要 .js)。這種磁碟查詢成本很高,因此會導致規則執行得較慢。

如果你的 project 沒有使用任何名稱中有副檔名的 npm 套件,你也不會使用兩個副檔名(例如 bar.js.ts)來命名檔案,那麼這樣的額外成本可能不值得花費,而你可以使用一個更簡單的檢查方式,透過使用 no-restricted-syntax Lint 規則。

以下設定的運算速度比 import/extensions 快了幾個數量級,因為它不會進行磁碟搜尋,但它會對前述 foo.js 模組等情況產生誤報。

function banImportExtension(extension) {
const message = `Unexpected use of file extension (.${extension}) in import`;
const literalAttributeMatcher = `Literal[value=/\\.${extension}$/]`;
return [
{
// import foo from 'bar.js';
selector: `ImportDeclaration > ${literalAttributeMatcher}.source`,
message,
},
{
// const foo = import('bar.js');
selector: `ImportExpression > ${literalAttributeMatcher}.source`,
message,
},
{
// type Foo = typeof import('bar.js');
selector: `TSImportType > TSLiteralType > ${literalAttributeMatcher}`,
message,
},
{
// const foo = require('foo.js');
selector: `CallExpression[callee.name = "require"] > ${literalAttributeMatcher}.arguments`,
message,
},
];
}

module.exports = {
// ... other config ...
rules: {
'no-restricted-syntax': [
'error',
...banImportExtension('js'),
...banImportExtension('jsx'),
...banImportExtension('ts'),
...banImportExtension('tsx'),
],
},
};