如何为前端项目一键自动添加eslint和prettier的支持

本文来自读者@那个曾经的少年回来了 写的源码共读35期笔记文章,授权投稿,写的真好。

本文参加了由公众号@若川视野 发起的每周源码共读活动,点此加我微信 ruochuan12 了解详情一起参与。

本文属于源码共读第35期 | 为 vite 项目自动添加 eslint 和 prettier点击了解本期详情一起参与 https://juejin.cn/post/7113563466211786783

前言

我之前好多次都是一步一步的安装eslint和prettier及相关依赖,一个配置文件一个配置文件的粘贴复制,并修改其中的相关配置。而且可能会在每个项目中都要去处理,如果项目工程规划化以后,eslint和prettier确实是项目少不了的配置。不知道你有没有像我一样操作过呢?

那么有没有一种更简单的方式去处理呢?答案是我终于遇到了。通过若川大佬的源码共读活动发现了,真的是太棒了。

本文以vite脚手架创建的项目为基础进行研究的,如果是其他脚手架创建的项目,那么就要自己去修改处理,但是原理是一样的。

那么接下来,我就要来一探究竟,先看看如何使用,然后查阅一下它的源码,看看它到底是如何实现的呢?

1、vite创建项目

  • 创建项目

yarn create vite

一顿操作以后项目就创建完毕了

c03962e28400387c46f04b3766320051.png
image.png
  • 2、安装依赖

yarn
  • 3、运行项目

yarn dev
  • 4、运行初始化eslint和prettier命令

yarn create vite-pretty-lint

先来看没有执行命令前的文件目录

3cf2ae21f2502d89f9f5b4e06b7b2535.png
image.png

再来看执行完命令后的文件目录

33d796876ac721ac2df35e508796fd56.png
image.png

可以发现文件目录中增加了eslint和prettier的相关配置,package.json中增加了相关的依赖、以及vite.config.xx文件也增加了相关配置,具体的文件变更可以查看https://github.com/lxchuan12/vite-project/commit/6cb274fded66634191532b2460dbde7e29836d2e。

一个命令干了这么多事情,真的太优秀了。接下来我们就去看看这如此优秀的源代码吧

2、整个过程的示意图

通过大致的查看源代码,简单总结出来的代码执行过程示意图,仅供参考

21dbba1e153b33abf5ae9dd739334212.png
未命名文件 (3).png

3、源码调试过程

3.1、找到调试代码的位置

通过package.json中的bin节点可以发现,yarn create vite-pretty-lint最终执行的便是lib/main.js中的代码

"bin": {"create-vite-pretty-lint": "lib/main.js"},

3.2、 开始调试的命令

因为我们现在只是要执行lib/main.js这个入口文件,通过package.jsonscripts 也没有发现执行命令,所以现在我们可以直接通过node来运行代码

node lib/main.js

调试成功的结果如下图所示

f540aadbfb0b750250caf2349f4c40fc.png
企业微信截图_16564645675849.png

3.3、 查看头部引入的模块

  • chalk终端多色彩输出

npm i chalkimport chalk from 'chalk'const log = console.log
// 字体背景颜色设置
log(chalk.bgGreen('chalk打印设置') )// 字体颜色设置
log(chalk.blue('Hello') + ' World' + chalk.red('!'))// 自定义颜色
const custom = chalk.hex('#F03A17')
const bgCustom = chalk.bgHex('#FFFFFF')
log(custom('customer'))
log(bgCustom('bgCustom'))

执行效果如下图所示

e9f96a108a6d8bbc71fa36851921fa36.png
image.png
  • gradient 文字颜色渐变

// 安装
npm i gradient-string
// 引入
import gradient  from 'gradient-string'// 使用
console.log(gradient('cyan', 'pink')('你好啊赛利亚欢迎来到编码世界'));
console.log(gradient('cyan', 'pink')('你好啊赛利亚欢迎来到编码世界'));
console.log(gradient('cyan', 'pink')('你好啊赛利亚欢迎来到编码世界'));
console.log(gradient('cyan', 'pink')('你好啊赛利亚欢迎来到编码世界'));
console.log(gradient('cyan', 'pink')('你好啊赛利亚欢迎来到编码世界'));

执行效果如下图所示

d2e6138a52d2da91f8d06128a9ff4653.png
image.png
  • child_process node.js中的子进程。

    在node.js中,只有一个线程执行所有的操作,如果某个操作需要大量消耗CPU资源的话,后续的操作就需要等待。后来node.js就提供了一个child_process模块,通过它可以开启多个子进程,在多个子进程之间可以共享内存空间,可以通过子进程的互相通信来实现信息的交换。

import { exec } from 'child_process';exec('ls',(error, stdout,stderr)=> {if(error) {console.log(error)return;}console.log('stdout: ' + stdout)console.log('执行其他操作')
})

执行效果如下图所示

b1832c9e216eb73383adf0db1c20e12d.png
image.png
  • fs fs用来操作文件的模块

import fs from 'fs'// 同步的读取方法,用来读取指定文件中的内容
fs.readFileSync() 
// 同步的写入方法,用来向指定文件中写内容
fs.writeFileSync()
  • path路径分类

import path from 'path';// 拼接路径
console.log(path.join('src', 'task.js'));  // src/task.js
  • nanospinner命令行中的加载动画

// 安装
npm i nanospinner// 引入模块
import { createSpinner } from 'nanospinner';const spinner = createSpinner('Run test').start()setTimeout(() => {spinner.success()
}, 1000)

执行效果如下图所示(Run test在加载的一个效果)

51ed08a085933103f10a0d2a21676894.gif
3.gif
  • enquirer (utils.js文件)

交互式询问CLI 简单说就是交互式询问用户输入

npm i enquirer import enquirer from 'enquirer' let tempArray = ['major(1.0.0)','minor(0.1.0)', 'patch(0.0.4)', "customer" ]
const { release } = await enquirer.prompt({type: 'select',name: 'release',message: 'Select release type',choices: tempArray
})if(release === 'customer') {console.log(release, 'customer')
} else {const targetVersion = release.match(/\((.*)\)/)[1]console.log(targetVersion, 'targetVersion')
}

执行效果如下图所示:先出来一个下拉选择,选择完后根据if判断进行输出

892cd71996558ba99c78aa0ae6a6a064.gif
4.gif

3.4、 调试具体代码

3.4.1、 main.js中的入口
async function run() {// 所有的逻辑代码
}run().catch((e) => {console.error(e);
});

通过run函数封装异步方法,这样最外面调用run函数时可以通过异步方法的catch捕获错误异常。

看一个小例子

const runTest = async () => {console.log('Running test')throw new Error('run test报错了')
}runTest().catch(err => {console.log('Error: ' + err)
})

执行后打印顺序如下

Running test
Error: Error: run test报错了

可以发现catch中截获了异常

接下来开始进入run函数了

3.4.2、 打印色彩字体
// 这个看上面的引入模块解析即可
console.log(chalk.bold(gradient.morning('\n🚀 Welcome to Eslint & Prettier Setup for Vite!\n'))
);
3.4.3、 交互式命令行
export function getOptions() {const OPTIONS = [];fs.readdirSync(path.join(__dirname, 'templates')).forEach((template) => {const { name } = path.parse(path.join(__dirname, 'templates', template));OPTIONS.push(name);});return OPTIONS;
}export function askForProjectType() {return enquirer.prompt([{type: 'select',name: 'projectType',message: 'What type of project do you have?',choices: getOptions(),},{type: 'select',name: 'packageManager',message: 'What package manager do you use?',choices: ['npm', 'yarn'],},]);
}try {const answers = await askForProjectType();projectType = answers.projectType;packageManager = answers.packageManager;} catch (error) {console.log(chalk.blue('\n👋 Goodbye!'));return;}

getOptions 函数根据fs.readdirSync读取项目工程template文件夹下的所有文件,并通过path.parse转换对象,来获取文件名称name

askForProjectType函数通过enquirer.prompt返回两个交互式命令行,供用户进行选择projectType选择项目类型:【react-ts】 【react】【vue-ts】 【vue】packageManager选择项目包管理方式:【npm】 【yarn】

3.4.4、根据交互命令行返回结果进行匹配模板

假如我们上面选择的是[vue-ts]

const { packages, eslintOverrides } = await import(`./templates/${projectType}.js`
);

/template/vue-ts.js模板中的代码(其中代码较多但一看就明白我就不贴了),就是export导出了两个固定的模板变量数组,packages则相当于要引入的npm模块列表,eslintOverrides这算是.eslintrc.json初始化模板。

3.4.5、拼接变量数组
const packageList = [...commonPackages, ...packages];
const eslintConfigOverrides = [...eslintConfig.overrides, ...eslintOverrides];
const eslint = { ...eslintConfig, overrides: eslintConfigOverrides };

commonPackagesshared.js中预定义的公共的npm 模块eslint则是通过公共npm模块中的eslintConfig和上面选择的template/xxxx.js中的进行拼接组成。

3.4.6、 生成安装依赖包的命令
const commandMap = {npm: `npm install --save-dev ${packageList.join(' ')}`,yarn: `yarn add --dev ${packageList.join(' ')}`,
};

packageList数组通过join转换为字符串,通过命令将所有拼接npm模块一起安装

058007c459991d312805f6178e1508ee.png
image.png
3.4.7、 读取项目的vite配置文件
const projectDirectory = process.cwd();const viteJs = path.join(projectDirectory, 'vite.config.js');const viteTs = path.join(projectDirectory, 'vite.config.ts');const viteMap = {vue: viteJs,react: viteJs,'vue-ts': viteTs,'react-ts': viteTs,};const viteFile = viteMap[projectType];const viteConfig = viteEslint(fs.readFileSync(viteFile, 'utf8'));const installCommand = commandMap[packageManager];if (!installCommand) {console.log(chalk.red('\n✖ Sorry, we only support npm and yarn!'));return;}

根据选择的项目类型,来拼接vite.config的路径,并读取项目中的vite.config配置文件

上面用到了一个函数viteEslint,这个具体的实现可以去看shared.js中,主要就是读取文件内容后,传入的参数code,就是vite.config.ts中的所有字符

6e446af53b8b683d3c39da2a45847c5a.png通过babel的parseSync转换为ast。ast对象如下图所示

41e19b28d740ff66e4a79bdc80acf64e.png
1656558646620.png

对ast数据进行了一系列的处理后,再通过babeltransformFromAstSync将ast转换为代码字符串。

对于babel处理这一块我也不太了解,有时间我得去加一下餐,具体的可以参考 https://juejin.cn/post/6844904008679686152

3.4.8 执行命令、执行完将eslint和prettier配置重写
const spinner = createSpinner('Installing packages...').start();exec(`${commandMap[packageManager]}`, { cwd: projectDirectory }, (error) => {if (error) {spinner.error({text: chalk.bold.red('Failed to install packages!'),mark: '✖',});console.error(error);return;}const eslintFile = path.join(projectDirectory, '.eslintrc.json');const prettierFile = path.join(projectDirectory, '.prettierrc.json');const eslintIgnoreFile = path.join(projectDirectory, '.eslintignore');fs.writeFileSync(eslintFile, JSON.stringify(eslint, null, 2));fs.writeFileSync(prettierFile, JSON.stringify(prettierConfig, null, 2));fs.writeFileSync(eslintIgnoreFile, eslintIgnore.join('\n'));fs.writeFileSync(viteFile, viteConfig);spinner.success({ text: chalk.bold.green('All done! 🎉'), mark: '✔' });console.log(chalk.bold.cyan('\n🔥 Reload your editor to activate the settings!'));});

首先通过createSpinner来创建一个命令行中的加载,然后通过child_process中的exec来执行[3.4.6]中生成的命令,去安装依赖并进行等待。

如果命令执行成功,则通过fs.writeFileSync将生成的数据写入到三个文件当中.eslintrc.json.prettierrc.json.eslintignorevite.config.xx

4、npm init、npx

印象里面大家可能对它的记忆可能都停留在,npm init之后是快速的初始化package.json,并通过交互式的命令行让我们输入需要的字段值,当然如果想直接使用默认值,也可以使用npm init -y

create-app-react创建项目命令,官网链接可以直接查看 https://create-react-app.dev/docs/getting-started

//官网的三种命令
npx create-react-app my-app
npm init react-app my-app
yarn create react-app my-app//我又发现npm create也是可以的
npm create react-app my-app

上述这些命令最终效果都是可以执行创建项目的

同样的vite创建项目的命令

//官网的命令
npm create vite@latest
yarn create vite
pnpm create vite// 指定具体模板的
// npm 6.x 
npm create vite@latest my-vue-app --template vue 
//npm 7+, extra double-dash is needed: 
npm create vite@latest my-vue-app -- --template vueyarn create vite my-vue-app --template vuepnpm create vite my-vue-app --template vue

可以发现vite官网没有使用npx命令,不过我在我自己电脑上尝试了另外几个命令确实也是可以的

npx create-vite my-app
npm init vite my-app
b2536e5aaabf11187f826307fe184b97.png
image.png

通过上面的对比可以一个小问题,yarn create去官网查了是存在这个指令的,官网地址可看 https://classic.yarnpkg.com/en/docs/cli/create#search

而对于npm create这个命令在npm官网是看不到的,但是在一篇博客中发现了更新日志

ceb6ebbc4b18d2b63b971f3737548d34.png
image.png

意思就是说npm create xxxnpm init xxx 以及yarn create xxx效果是一致的。那么我们来本文的命令行

// 我们是通过npm安装的,并且包名里是包含create的
npm i create-vite-pretty-lint// 那么以下几种方式都可以使用的
npm init vite-pretty-lint
npm create vite-pretty-lint
yarn create vite-pretty-lint
npx create-vite-pretty-lint

再来看一下npx

假如我们只在项目中安装了vite,那么node_modules.bin文件夹下是会存在vite指令的

d1926712c93afb1fc639b03496075aff.png
image.png

如果我们想在该项目下执行该命令第一种方式便是

90df14eda5afec9652312282c5605bd5.png
image.png

第二种方式就是直接在package.json的scripts属性下

932bcaf30626a0f17763f039d5c31b07.png
image.png

关于npx的详细说明可以看一下阮一峰大佬的精彩分享 http://www.ruanyifeng.com/blog/2019/02/npx.html

5、总结

  • npm init xxx的妙用,以及对npx的了解,感觉对package.json的每一个属性,可以专门去学习一下

  • 对于自动添加eslint和prettier配置的原理分析

  • .eslintrc.json、.eslintignore、.prettierrc.json算是直接新增文件,处理相对简单一些

  • 最重要的学习点:对vite.config文件在原有基础上的修改,这里就涉及到了AST抽象语法树

6、加餐 V8下的AST抽象语法树

有兴趣的话可以看看我前几天刚刚总结的关于V8引擎是如何运行JavaScript代码的,其中就涉及到关于AST的部分https://juejin.cn/post/7109410330295402509。

接下来有时间我会简单的把AST详细的学习一下,查了很多资料发现AST还是非常重要的,无论是babel、webpack、vite、vue、react、typescript等都使用到了AST。

c8917c257eeedc0945322f0fa8c056e9.gif

················· 若川简介 ·················

你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》20余篇,在知乎、掘金收获超百万阅读。
从2014年起,每年都会写一篇年度总结,已经坚持写了8年,点击查看年度总结。
同时,最近组织了源码共读活动,帮助4000+前端人学会看源码。公众号愿景:帮助5年内前端人走向前列。

fceedffdbba1ccb5b21d6126ef31b6e6.jpeg

扫码加我微信 ruochuan12、拉你进源码共读

今日话题

目前建有江西|湖南|湖北 籍 前端群,想进群的可以加我微信 ruochuan12 进群。分享、收藏、点赞、在看我的文章就是对我最大的支持

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/274607.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Server.Transfer方法在页面间传值

a.aspx页面代码: protected void Button5_Click(object sender, EventArgs e){Server.Transfer("b.aspx");}public string name{get{return this.TextBox1.Text;}} b.aspx页面代码: private void TheFour(){a newWebContext.Handler as a;//创…

极端原理_为极端而设计

极端原理*You can also read this article in German here.*您也可以 在此处 阅读 德语文章 。 In this article I’m going to draw on the Design thinking concept of designing for extreme users and I will argue that designing for extreme users can be considered a…

当CV工程师碰到了拷贝粘贴的需求——useClipboard的使用及源码解析

大家好,我是若川。持续组织了近一年的源码共读活动,感兴趣的可以 点此加我微信ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列。…

centos利用tar包安装phpmyadmin

我的网站根目录地址为/var/www/html,phpmyadmin安装包在/tmp下。 注意:php版本低于5.1的建议安装phpmyadmin2.11,因为phpmyadmin3.3对php版本的最低要求是php5.2 1 [rootCentOS ~]# tar -zxvf /tmp/phpMyAdmin-2.11.9-all-languages.tar.gz …

ux和ui_从UI切换到UX设计

ux和uiI still remember those days, when I was a soon-to-be graphic design graduate who started to question what my future will be. At that time, I realized that I loved graphic design, but I wasn’t sure if I enjoyed doing it. Creating logos, posters, broc…

春季招聘后前端工程师的就业指南

尽管疫情反复,大厂裁员,招聘季仍是在困难中有条不紊地落下了尾声。回顾今年的春季招聘,北京青年报记者发现,互联网“大厂”依然对“研发岗”需求最为旺盛。但许多企业最近都在围绕“降本提效”来进行业务调整,这对技术…

探索式测试的思维模型

上一章介绍了探索式测试的定义。在实际项目的测试执行过程中,读者是否曾遇到如下的几个现象: 测试人员按照一个测试用例来执行测试,得到的程序输出与预期输出不一致。 测试人员判断程序的行为并不是缺陷,但根据新的输出想到了新的…

图解Git分支和命令

大家好,我是若川。持续组织了近一年的源码共读活动,感兴趣的可以 点此加我微信ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列。…

vsco_VSCO重新设计:更直观,更简化的界面

vscoAmong the many photo-editing apps, VSCO has definitely become a popular favorite among both experienced photographers as well as “aesthetic” Instagram users. However, my interaction with the app starts and ends with using a few key filters and (maybe…

不同长度数据项的排序

注:本文改编自windmissing博客,感谢作者整理! 题目: a)给定一个整数数组,其中不同的整数中包含的数字个数可能不同,但是该数组中,所有整数中总的数字数为n。说明如何在O(n)时间内对该数组进行排…

浅谈前端埋点监控

大家好,我是若川。持续组织了近一年的源码共读活动,感兴趣的可以 加我微信lxchuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列。另外&…

css版式_第2部分:使版式具有响应能力,并为以后的版本奠定基础

css版式The feedback I’ve received over the past week has been amazing, and matches my own excitement about this project. I’ve spent a lot of time researching, writing, and teaching about creating better typography for reading on digital devices over the …

BBS项目--登录

BBS阶段性测试总要求 django登录报错 Error: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试。 原因分析:出现这种情况在Windows中很常见,就是端口被占用 解决措施:这时我们只需改一下端口便可以了 登录前端页面(HTML…

【声明】

我的公众号和朋友圈有时会有一些课程推广广告,微博的收入来源。我接的广告一般来说都是比自己去买会优惠不少,我也会想方设法争取到更多福利(优惠)。买过的都知道确实优惠。如果有人看到觉得不合适,不想看到&#xff0…

Win7 访问共享时输入正确密码仍然提示密码错误

1、直接按下winr键,输入secpol.msc,打开本地安全策略。 2、找到“安全设置”的“本地策略”的“安全选项” 3、在右边一栏找到“网络安全:LAN管理器身份验证级别”,双击进入 4、在默认状态选项下,英文版应该为"no…

怎么实现页面友好跳转_如何实现软,友好和一致的UI设计

怎么实现页面友好跳转重点 (Top highlight)Design trends are constantly changing, aren’t they? Each month there is a new visual effect or a trick that becomes “设计趋势在不断变化,不是吗? 每个月都有一个新的视觉效果或技巧,成为…

前端趋势 2022

大家好,我是若川。持续组织了近一年的源码共读活动,感兴趣的可以 加我微信lxchuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列。另外&…

MySQL Connector/ODBC 5.2.2 发布

MySQL Connector/ODBC 5.2.2 发布,这是一个稳定版本,下载地址: http://dev.mysql.com/downloads/connector/odbc/5.2.html MySQL Connector/ODBC 是 MySQL 官方发布的 ODBC 驱动程序包。转载于:https://www.cnblogs.com/shihao/archive/2012/…

优秀测试管理工具必备九大功能分析

摘要:测试管理工具对测试的重要性毋庸质疑,两位笔者有着多年的测试实战经验,对市面上的一些测试管理工具有过一定的研究,还根据目前比较流行的敏捷开发过程设计了一款测试管理工具。 这篇文章算是对这个设计过程的总结与分享&…

lightroom预设使用_在Lightroom中使用全景图增强照片游戏

lightroom预设使用Everyone here has taken a panorama with an iphone. We’ve spun around in a circle, trying to keep that arrow right on the line, and more than likely ended up with a strange, squiggly, horizontal photo. Every so often you might get lucky an…