- 本文参加了由公众号@若川视野发起的每周源码共读活动,点击了解详情一起参与。
- 这是源码共读的第34期
本文源码学习主要是了解 TDesign 中组件初始化命令,包括测试以下几个方面:
-
npm run init [component]
:生成初始化组件代码及文件 -
npm run init [component] del
:移除组件代码及文件
从这里我们可以了解单元测试中设计测试用例的依据是到:
- 怎么实现通过
模板 + 数据
的方式生成我们想要的基础代码 - 怎么实现通过命令删除对应基础代码及文件
- lodash.template 使用
流程
npm run init table
命令逻辑执行流程如下图:

npm run init tabl人体承受的最大电压e del
命令逻辑执行流程如下图:

组件模板
在了解其实现前,先看看其组件模板配置。
目录信息
script/i源码编程器nit
目录:

导出配置
外部通过调用getToBeCreatedFiles
来获取对应的配置目录信息

实现
npm run init
入口
const config = require('./config'); function init() { // 从参数中获取组件名称、del const [component, isDeleted] = process.argv.slice(2); if (!component) { console.error('[组件名]必填 - Please enter new component name'); process.exit(1); } // 获取组件入口路径 const indexPath = path.resolve(cwdPath, 'src/index.ts'); // 获取组件配置信息 const toBeCreatedFiles = config.getToBeCreatedFiles(component); if (isDeleted === 'del') { // 移除组件目录 deleteComponent(toBeCreatedFiles, component); // 从组件入口移除该组件引入 deleteComponentFromIndex(component, indexPath); } else { // 添加组价目录 addComponent(toBeCreatedFiles, component); // 在组件入口加入该组件引入 insertComponentToIndex(component, indexPath); } }
了解了入口逻辑,我们再来看看其内部实现。
新加组件
新加组件主要逻辑: addComponent
->outputFileWithTemplate
->insertComponentToIndex
。
addComponent
function addComponent(toBeCreatedFiles, component) { // 遍历配置信息 Object.keys(toBeCreatedFiles).forEach((dir) => { const _d = path.resolve(cwdPath, dir); // 生成对应目录 fs.mkdir(_d, { recursive: true }, (err) => { if (err) { utils.log(err, 'error'); return; } console.log(`${_d} directory has been created successfully!`); const contents = toBeCreatedFiles[dir]; // 获取配置信息中文件配置,即 files 字段 /* [`src/${component}`]: { desc: 'component source code', files: [ { file: 'index.ts', template: 'index.ts.tpl', }, { file: `${component}.tsx`, template: 'component.tsx.tpl', }, ], }, */ contents.files.forEach((item) => { if (typeof item === 'object') { if (item.template) { // 对象,内部通过 lodash 进行数据与模板结合,并生成相对应文件 outputFileWithTemplate(item, component, contents.desc, _d); } } else { // 非对象,直接生成文件 const _f = path.resolve(_d, item); createFile(_f, '', contents.desc); } }); }); }); }
outputFileWithTemplate
const _ = require('lodash'); function outputFileWithTemplate(item, component, desc, _d) { // 解析模板路径 const tplPath = path.resolve(__dirname, `./tpl/${item.template}`); // 获取模板文件内容 let data = fs.readFileSync(tplPath).toString(); // 通过 _.template 返回模板编译函数 const compiled = _.template(data); // 模板注入数据,返回注入数据后的模板内容 data = compiled({ component, upperComponent: getFirstLetterUpper(component), }); const _f = path.resolve(_d, item.file); // 生成文件 createFile(_f, data, desc); }
lodash.template的一个使用例子~
// 使用 "interpolate" 分隔符创建编译模板 var compiled = _.template('hello <%= user %>!'); compiled({ 'user': 'fred' }); // => 'hello fred!'
insertComponentToInd测试手机是否被监控ex
function insertComponentToIndex(component, indexPath) { // 组件名首字母大写:table -> Table const upper = getFirstLetterUpper(component); // last import line pattern const importPattern = /import.*?;(?=nn)/; // components pattern const cmpPattern = /(?<=const components = {n)[.|s|S]*?(?=};n)/g; const importPath = getImportStr(upper, component); const desc = '> insert component into index.ts'; let data = fs.readFileSync(indexPath).toString(); if (data.match(new RegExp(importPath))) { utils.log(`there is already ${component} in /src/index.ts`, 'notice'); return; } // insert component at last import and component lines. data = data.replace(importPattern, (a) => `${a}n${importPath}`).replace(cmpPattern, (a) => `${a} ${upper},n`); fs.writeFile(indexPath, data, (err) => { if (err) { utils.log(err, 'error'); } else { utils.log(`${desc}n${component} has been inserted into /src/index.ts`, 'success'); } }); }
删除组件
删除组件主要逻辑为:dele难破mg5日剧teComponent
-> deleteComponentFromIndex
deleteCompon单元测试家长评语ent
function deleteComponent(toBeCreatedFiles, component) { // 获取组件单元测试文件 const snapShotFiles = getSnapshotFiles(component); // 合并到配置信息中 const files = Object.assign(toBeCreatedFiles, snapShotFiles); // 遍历 Object.keys(files).forEach((dir) => { const item = files[dir]; if (item.deleteFiles && item.deleteFiles.length) { item.deleteFiles.forEach((f) => { // 移除文件 fs.existsSync(f) && fs.unlinkSync(f); }); } else { utils.deleteFolderRecursive(dir); } }); utils.log('All radio files have been removed.', 'success'); } // 单元测试文件 function getSnapshotFiles(component) { return { [`test/unit/${component}/__snapshots__/`]: { desc: 'snapshot test', files: ['index.test.js.snap', 'demo.test.js.snap'], }, }; }
deleteComponentFromIndex
function deleteComponentFromIndex(component, indexPath) { // 获取组件名称 const upper = getFirstLetterUpper(component); // 获取 import 字符串 const importStr = `${getImportStr(upper, component)}n`; // 获取入口文件内容 let data = fs.readFileSync(indexPath).toString(); // 匹配内容替换 data = data.replace(new RegExp(importStr), () => '').replace(new RegExp(` ${upper},n`), ''); // 重新写回到入口文件中 fs.writeFile(indexPath, data, (err) => { if (err) { utils.log(err, 'error'); } else { utils.log(`${component} has been removed from /src/index.ts`, 'success'); } }); } function getImportStr(upper, component) { return `import ${upper} from './${component}';`; }
总结
在了解了npm run init [component]
命令内部逻辑后,可以学习到一种模板思想,即通过输入命令,让模板与数据结合,生成组件代码目录,减少手动创建同类型源码的目录结构以及文件操作(类似于cli,即女配没有求生欲txt宝书网生成项目文件)源码之家~
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
评论(0)