Vite項目自動添加eslint prettier源碼解讀
引言
vite-pretty-lint
庫是一個為Vite
創(chuàng)建的Vue
或React
項目初始化eslint
和prettier
的庫。
該庫的目的是為了讓開發(fā)者在創(chuàng)建項目時,不需要手動配置eslint
和prettier
,而是通過vite-pretty-lint
庫來自動配置。
源碼地址:
使用
根據(jù)vite-pretty-lint
庫的README.md
,使用該庫的只需要執(zhí)行一行命令即可:
// NPM npm init vite-pretty-lint // YARN yarn create vite-pretty-lint // PNPM pnpm init vite-pretty-lint
這里就涉及到了一個知識點,npm init <initializer>
,yarn create <initializer>
,pnpm init <initializer>
,這三個命令的作用是一樣的,都是用來初始化一個項目的。
<initializer>
是一個初始化項目的包名,比如vite-pretty-lint
就是一個初始化項目的包名;
執(zhí)行npm init vite-pretty-lint
命令后,相當于執(zhí)行npx create-vite-pretty-lint
命令;
這里不多講解,參考:npm init
源碼閱讀
打開lib/main.js
文件直接看,一開頭就看到了下面這段代碼:
const projectDirectory = process.cwd(); const eslintFile = path.join(projectDirectory, '.eslintrc.json'); const prettierFile = path.join(projectDirectory, '.prettierrc.json'); const eslintIgnoreFile = path.join(projectDirectory, '.eslintignore');
一看這些名字就知道,這里是用來創(chuàng)建eslint
和prettier
的配置文件的,這里的projectDirectory
就是當前項目的根目錄。
當然現(xiàn)在這些暫時還沒有用到,接著往下走:
async function run() { let projectType, packageManager; try { const answers = await askForProjectType(); projectType = answers.projectType; packageManager = answers.packageManager; } catch (error) { console.log(chalk.blue('\n?? Goodbye!')); return; } // 省略后面代碼 }
一個run
函數(shù),這個就是執(zhí)行命令的入口函數(shù),可以將代碼拉到最低下就知道了。
這里直接看askForProjectType
函數(shù),這個函數(shù)是通過./utils.js
文件來的,進去看看
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', 'pnpm'], }, ]); }
這里就是通過enquirer
庫來獲取用戶的輸入,enquirer
庫是一個命令行交互的庫,可以參考:enquirer
這里只有兩個問題,一個是項目類型,一個是包管理器,包管理器就是npm
,yarn
,pnpm
;
項目類型是用過getOptions
函數(shù)來獲取的:
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; }
getOptions
函數(shù)就是獲取templates
文件夾下的所有文件夾,然后將文件夾名作為選項返回。
回到main.js
文件,繼續(xù)往下看:
const {packages, eslintOverrides} = await import(`./templates/${projectType}.js`); const packageList = [...commonPackages, ...packages]; const eslintConfigOverrides = [...eslintConfig.overrides, ...eslintOverrides]; const eslint = {...eslintConfig, overrides: eslintConfigOverrides};
當用戶回答完問題后,就會根據(jù)用戶的選擇來導(dǎo)入對應(yīng)的模板文件,比如用戶選擇了react
,那么就會導(dǎo)入./templates/react.js
文件。
可以進去templates
文件夾里面的文件看看,這里是vue.js
文件:
export const packages = ['vue-eslint-parser', 'eslint-plugin-vue']; export const eslintOverrides = [ { files: ['*.js'], extends: ['eslint:recommended', 'plugin:prettier/recommended'], }, { files: ['*.vue'], parser: 'vue-eslint-parser', parserOptions: { ecmaVersion: 'latest', sourceType: 'module', }, extends: [ 'eslint:recommended', 'plugin:vue/vue3-recommended', 'plugin:prettier/recommended', ], rules: { 'vue/multi-word-component-names': 'off', }, }, ];
這里的packages
就是需要安裝的包,eslintOverrides
就是eslint
的配置,這里的配置就是vue
項目的eslint
配置。
然后下面就是一些合并的配置,都是通過shared.js
文件來的:
// shared.js export const commonPackages = [ 'eslint', 'prettier', 'eslint-plugin-prettier', 'eslint-config-prettier', 'vite-plugin-eslint', ]; export const eslintConfig = { env: { browser: true, es2021: true, node: true, }, overrides: [], };
繼續(xù)往下看:
const commandMap = { npm: `npm install --save-dev ${packageList.join(' ')}`, yarn: `yarn add --dev ${packageList.join(' ')}`, pnpm: `pnpm install --save-dev ${packageList.join(' ')}`, }; const viteConfigFiles = ['vite.config.js', 'vite.config.ts']; const [viteFile] = viteConfigFiles .map((file) => path.join(projectDirectory, file)) .filter((file) => fs.existsSync(file)); if (!viteFile) { console.log( chalk.red( '\n?? No vite config file found. Please run this command in a Vite project.\n' ) ); return; }
這里就是通過commandMap
來獲取對應(yīng)的安裝命令;
然后通過viteConfigFiles
來獲取vite
的配置文件;
這里是vite.config.js
或者vite.config.ts
,然后通過viteFile
來判斷是否存在vite
的配置文件;
沒有vite
的配置文件就證明不是vite
項目,然后程序結(jié)束。
繼續(xù)往下看:
const viteConfig = viteEslint(fs.readFileSync(viteFile, 'utf8')); const installCommand = commandMap[packageManager]; if (!installCommand) { console.log(chalk.red('\n? Sorry, we only support npm、yarn and pnpm!')); return; }
這里就是通過viteEslint
來獲取vite
的配置文件,然后通過installCommand
來獲取對應(yīng)的安裝命令。
vimEslint
也是在shared.js
文件里面的:
export function viteEslint(code) { const ast = babel.parseSync(code, { sourceType: 'module', comments: false, }); const { program } = ast; const importList = program.body .filter((body) => { return body.type === 'ImportDeclaration'; }) .map((body) => { delete body.trailingComments; return body; }); if (importList.find((body) => body.source.value === 'vite-plugin-eslint')) { return code; } const nonImportList = program.body.filter((body) => { return body.type !== 'ImportDeclaration'; }); const exportStatement = program.body.find( (body) => body.type === 'ExportDefaultDeclaration' ); if (exportStatement.declaration.type === 'CallExpression') { const [argument] = exportStatement.declaration.arguments; if (argument.type === 'ObjectExpression') { const plugin = argument.properties.find( ({ key }) => key.name === 'plugins' ); if (plugin) { plugin.value.elements.push(eslintPluginCall); } } } importList.push(eslintImport); importList.push(blankLine); program.body = importList.concat(nonImportList); ast.program = program; return babel.transformFromAstSync(ast, code, { sourceType: 'module' }).code; }
這里就是通過babel
來解析vite
的配置文件,然后通過importList
來獲取import
的配置,通過nonImportList
來獲取非import
的配置,通過exportStatement
來獲取export
的配置;
參考:babel
接著往下看:
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; } 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!') ); });
通過nanospinner
來創(chuàng)建一個spinner
,參考:nanospinner
通過child_process
的exec
來執(zhí)行安裝命令,參考:child_process
最后安裝完成,通過fs
來寫入對應(yīng)的配置文件;
總結(jié)
通過學習vite-plugin-eslint
,我們學習到了在vite
項目中如何配置eslint
和prettier
;
并且在這個過程中,我們學習到了如何通過vite
的plugin
來實現(xiàn)對vite
的擴展;
還有這個項目的對vite
的配置文件的解析,通過babel
來解析vite
的配置文件,然后通過importList
來獲取import
的配置,通過nonImportList
來獲取非import
的配置,通過exportStatement
來獲取export
的配置;
以上就是Vite項目自動添加eslint prettier源碼解讀的詳細內(nèi)容,更多關(guān)于Vite添加eslint prettier的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序報錯:this.setData is not a function的解決辦法
這篇文章主要介紹了微信小程序報錯:this.setData is not a function的解決辦法的相關(guān)資料,希望通過本文能幫助到大家解決這樣類似的問題,需要的朋友可以參考下2017-09-09腳本整合指定文件/文件夾執(zhí)行定制化ESLint命令使用實例
這篇文章主要為大家介紹了腳本整合指定文件/文件夾執(zhí)行定制化?ESLint命令使用實例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11lodash里to系列之將數(shù)據(jù)轉(zhuǎn)換成數(shù)字類型實現(xiàn)示例
這篇文章主要為大家介紹了lodash里to系列之將數(shù)據(jù)轉(zhuǎn)換成數(shù)字類型實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-08-08微信小程序 ecshop地址三級聯(lián)動實現(xiàn)實例代碼
這篇文章主要介紹了微信小程序 ecshop地址3級聯(lián)動實現(xiàn)實例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02