
伴随框架升级而升级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
设置:
javascript
// @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 rules
eslint.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 files
processor: 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
javascript
// @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检查同步更新,避免本地与远程环境不一致。
参考: