伴隨框架升級而升級ESLint遇到的問題與思考
對于eslint這個前端事實上的代碼檢查工具標準,大家可能是再熟悉不過了。幾乎是在編碼的時時刻刻都在和它接觸。在我們開發維護長達十年的項目中自然也是采用了ESLint
,在從 AngularJS
一路到今天現代化的 Angular V20
。 為了保持項目的活力和能夠利用現代化的框架特性,我們持續跟隨 Angular
的版本更新。在這么長時間的版本變動中,ESLint
作為項目必不可少的一部分自然也經歷了許多變動。我們也遇到過一些問題自然也有一些心得,今天就在這里和大家分享一下。
Lint 工具的變遷
- 在
AngularJS
時代
在這個古早的版本時期,如今大名鼎鼎甚至開始過氣的
Webpack
還只有個初級的版本。AngularJS
本身還沒有如今基于Webpack
的這一套打包工具,我們的項目中依賴的是gulp
,使用gulp-eslint
插件來做linter。
- Angular V4-V11
這一時期
AngularJS
被廢棄,谷歌發布了現代化標準的Angular
框架同時從JS轉向了類型安全的Typescript
,但是對于傳統的面向Javascript的ESLint此時被替換為了TSLint并成為了Angular
的默認linter。這一時期改變巨大并且是web技術高速發展的時期,所以Angular的發展有些混亂。它全面轉向TS,框架完全重做還要有很大的精力來開發讓開發者能順利從AngularJS
順利升級到新的Angular
的技術。我們從ESLint規則轉向了TSLint的規則,由于TS的語言特性我們還加了一些TS專有的規則。
- Angular v12-v18
在經過幾年的發展以后TSlint 團隊在2019年突然宣布廢棄項目,究其原因主要是TSLint 無法復用 JavaScript 生態系統中圍繞 ESLint 所做的任何工作。TSLint 不得不重新實現所有功能,從編輯器擴展到自動修復,再到規則。這個重大變化又導致Angular重新轉向了
angular-eslint
,在而它依賴的是typescript-eslint
.
- Angular V18 - 現在
從Angular V18 開始
angular-eslint
開始使用 ESLint V9,從v9開始ESLint開始使用所謂扁平化設置。
從原來的JSON配置,變成默認使用eslint.config.js
一個典型的eslint.config.js
設置:
// @ts-check// Allows us to bring in the recommended core rules from eslint itself
const eslint = require('@eslint/js');// Allows us to use the typed utility for our config, and to bring in the recommended rules for TypeScript projects from typescript-eslint
const tseslint = require('typescript-eslint');// Allows us to bring in the recommended rules for Angular projects from angular-eslint
const angular = require('angular-eslint');// Export our config array, which is composed together thanks to the typed utility function from typescript-eslint
module.exports = tseslint.config({// Everything in this config object targets our TypeScript files (Components, Directives, Pipes etc)files: ['**/*.ts'],extends: [// Apply the recommended core ruleseslint.configs.recommended,// Apply the recommended TypeScript rules...tseslint.configs.recommended,// Optionally apply stylistic rules from typescript-eslint that improve code consistency...tseslint.configs.stylistic,// Apply the recommended Angular rules...angular.configs.tsRecommended,],// IMPORTANT: Set the custom processor to enable inline template linting// This allows your inline Component templates to be extracted and linted with the same// rules as your external .html template filesprocessor: angular.processInlineTemplates,// Override specific rules for TypeScript files (these will take priority over the extended configs above)rules: {'@angular-eslint/directive-selector': ['error',{type: 'attribute',prefix: 'app',style: 'camelCase',},],'@angular-eslint/component-selector': ['error',{type: 'element',prefix: 'app',style: 'kebab-case',},],},},{// Everything in this config object targets our HTML files (both external template files,// AND inline templates thanks to the processor set in the TypeScript config above)files: ['**/*.html'],extends: [// Apply the recommended Angular template rules...angular.configs.templateRecommended,// Apply the Angular template rules which focus on accessibility of our apps...angular.configs.templateAccessibility,],rules: {},},
);
遇到的問題
- 從ESLint到TSLint
當時從ESLint規則到TSLint規則,由于命名上的不同需要一一對應的來遷移,耗費了一些時間。
- 從TSLint 遷移到 typescript-eslint
這一時期的問題主要是因為從TSLint
的rule重新遷移到eslint
的rule并不能實現完美的遷移,是因為TypeScript
添加了ESLint
不知道的新功能,ESLint
里沒有專門針對TS的特定規則。所以我們需要擴展ESLint
的規則來實現對Typescript
的支持,但是在我們當時遷移的時候,有些擴展還沒有完全支持,所以我們需要禁用掉一些lint規則來使遷移能夠順利完成。
- ESLint V9
當我們升級項目的框架到Angular V18版本以后,本地運行的lint-staged
命令失效了。這個命令是在每次commit本地代碼的時候對修改的文件做檢查。原因是隨著新版本的框架帶來了ESLint的V9版本,這個版本默認是需要采用上文所述的“扁平化”配置文件,但是Angular 本身做了處理繼續使用原來的JSON格式配置是可以的。但是問題在于我們的這個lint-staged
配置的是直接使用eslint
而不是采用Angular的linter命令,所以無法繼續兼容以前的eslintrc
配置文件。
最終的解決方案
由于歷史原因在我們的項目中server端和client的代碼在一個統一的代碼庫中,但是server的開發使用比較老的gulp-eslint
,而client使用Angular
的ng lint
。所以導致項目中其實是有兩個版本的配置文件,一個針對客戶端的Typescript
另一個是針對后端的Javascript
。所以我們使用了統一扁平化的配置文件利用ESLint的 override特性來覆蓋不同位置代碼。
簡化的配置文件如下:
eslint.config.js
// @ts-nocheck
const eslint = require("@eslint/js");
const tseslint = require("typescript-eslint");
const angular = require("angular-eslint");
const globals = require("globals");
const jsdoc = require('eslint-plugin-jsdoc');const typescriptEslintParser = require("@typescript-eslint/parser");
jsdoc.configs['flat/recommended'],module.exports = tseslint.config(/**** targeting all ts files in the client side*/{files: ["client/**/*.ts"],ignores: ["client/**/*.spec.ts","client/**/*.json.ts","client/**/*.d.ts","client/mockmodel/**/*.ts","client/**/polyfills.ts","client/unitTest.config.ts","client/environments/**/*.ts",],plugins: {jsdoc},extends: [...tseslint.configs.recommended,...angular.configs.tsRecommended],processor: angular.processInlineTemplates,rules: {// rules for the client side},},/*** targeting all html files, including inline templates*/{files: ["client/**/*.html"],extends: [...angular.configs.templateRecommended],rules: {},},/*** targeting all JS files in server folder*/{files: ["server/**/*.js"],extends: [eslint.configs.recommended],languageOptions: {globals: {...globals.browser,...globals.node,...globals.es2020,...globals.jasmine},parserOptions: {ecmaVersion: 2020,}},rules: {// rules for the server side},}
);
一些思考
在Angular和Typescript生態建設的這些年,我們在linter這個工具上遇到了一些麻煩。也感謝開源社區的貢獻讓我們也成功的解決了這些問題。
- 前端工具的快速迭代:前端生態變化迅速,選擇工具時需要評估其長期維護性,優先選擇有強大社區支持的項目。
- 配置標準化:ESLint V9的扁平化配置是未來趨勢,對于已有項目可以盡早適配以避免差異過大導致的后續問題。
- 混合項目管理:對于全棧項目,可以通過更好更清晰的ESLint多配置支持統一管理前后端代碼規范。
- 自動化測試:在工具鏈變更時,需要確保CI/CD流程中的lint檢查同步更新,避免本地與遠程環境不一致。
參考:
- https://en.wikipedia.org/wiki/Webpack?oldformat=true
- https://typescript-eslint.io/troubleshooting/faqs/eslint