build:file
脚本的执行目的是生成包括 icon
, 入口文件
, i18n 国际化
, version
在内的文件, 内容为:css
node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js
复制代码
执行这个脚本的做用是:读取 packages/theme-chalk/src/icon.scss
文件, 对文件中的全部相似于 el-icon-close
这样的图标类名进行正则匹配,把全部符合正则的图标类名组成一个图标数组,最后把图标数组写入到 example/icon.json
。html
postcssvue
PostCSS 是一个容许使用 JS
插件转换样式的工具。 PostCSS
接受一个 CSS
文件并提供了一个 API 来分析、修改它的规则(经过把 CSS
转换成一个 AST
抽象语法树的方式。 PostCSS 更多说明, PostCSS API)。node
postcss.parsegit
解析一个 css
文件, 返回文件中所包含的 css
节点。github
<!-- icon.scss -->
.el-icon-info:before { content: "\e61a"; }
复制代码
const postcss = require('postcss')
const fs = require('fs')
const fontFile = fs.readFileSync('./testIcon.scss', 'utf-8')
const nodes = postcss.parse(fontFile).nodes
console.log(nodes)
复制代码
打印出来的结果为:正则表达式
[
Rule {
raws: {
before: '\r\n\r\n',
between: ' ',
semicolon: true,
after: '\r\n'
},
type: 'rule',
nodes:[...],
parent:
Root {
raws: [Object],
type: 'root',
nodes: [Circular],
source: [Object]
},
source: {
start: [Object],
input: [Object],
end: [Object]
},
selector: '[class^="el-icon-"], [class*=" el-icon-"]'
}
]
复制代码
对上面的前提知识了解后,开始看执行的脚本代码逻辑json
var postcss = require('postcss');
var fs = require('fs');
var path = require('path');
var fontFile = fs.readFileSync(path.resolve(__dirname, '../../packages/theme-chalk/src/icon.scss'), 'utf8');
var nodes = postcss.parse(fontFile).nodes;
var classList = [];
nodes.forEach((node) => {
var selector = node.selector || '';
var reg = new RegExp(/\.el-icon-([^:]+):before/);
var arr = selector.match(reg);
if (arr && arr[1]) {
classList.push(arr[1]);
}
});
fs.writeFile(path.resolve(__dirname, '../../examples/icon.json'), JSON.stringify(classList), () => {});
复制代码
循环中的逻辑:api
forEach
遍历取到的全部 css
文件节点。选择器
属性(包含 class
类、 id
) selector
。/\.el-icon-([^:]+):before/
匹配全部知足 Element iocn
命名规则的 selector
。 这里使用 字符串的 match
方法。它将返回全部匹配项。match
匹配不到结果时返回 null
),而且匹配的第一个结果也为真时。 为了不重复 只将第一个匹配结果放在 classList
数组中。最后将 生成的 classList
以字符串的形式写入到 examples/icon.js
数组
执行这个文件的目的是自动生成 整个 Elemnet
框架的入口文件。 这个入口文件须要暴露 一个 默认对象, 这个对象上包括 install
方法, install
方法中须要在传入的 Vue
参数上添加 所有的组件、指令、全局挂载的方法。除了 install
方法外,为了支持单组件的使用, 还须要在这个默认对象上面添加 所有的组件做为该对象的属性。( 关于 vue
插件的开发 , 和 Element
入口文件的分析)。
json-templater/String
一种模板语言实现。能够预先写一个字符串模板,在这个字符串模板中能够存在 以 {{var}}
包裹的变量, 使用该方法能够将字符串中的变量替换为其余值。
var render = require('json-templater/string');
let template = `A {{platform}} UI Library `
render(template, { platform: 'Desktop'});
复制代码
uppercamelcase
将给定字符串转换成 驼峰写法
os.EOL
一个字符串常量,定义操做系统相关的行末标志:
components.json
以每个组件名为对象的 key
, 以该组件的所在目录为 value
组成的对象。
"pagination": "./packages/pagination/index.js",
"dialog": "./packages/dialog/index.js",
"autocomplete": "./packages/autocomplete/index.js",
"dropdown": "./packages/dropdown/index.js",
"dropdown-menu": "./packages/dropdown-menu/index.js",
......
复制代码
// 组件-组件地址 json 对象
var Components = require('../../components.json');
var fs = require('fs');
// 替换 json 模板中的变量的方法
var render = require('json-templater/string');
// 字符串转换为 驼峰写法
var uppercamelcase = require('uppercamelcase');
var path = require('path');
// 当前操做系统的 换行符
var endOfLine = require('os').EOL;
// 将字符串文件模板输出的文件地址, 这个地址就是项目的入口文件的地址
var OUTPUT_PATH = path.join(__dirname, '../../src/index.js');
// 文件模板头部的 import 引用 字符串模板
var IMPORT_TEMPLATE = 'import {{name}} from \'../packages/{{package}}/index.js\';';
// 文件模板中的 component 的字符串模板
var INSTALL_COMPONENT_TEMPLATE = ' {{name}}';
// 整个文件模板中 主体字符串模板 里面有四个替代变量,分别是 include、 install、version、list
var MAIN_TEMPLATE = `......`
delete Components.font;
var ComponentNames = Object.keys(Components);
// 替换 include 的变量数组
var includeComponentTemplate = [];
// 替换 install 的变量数组
var installTemplate = [];
// 替换 list 的变量数组
var listTemplate = [];
// 循环 component.json 中的 key 组成的对象
ComponentNames.forEach(name => {
// 组件名 转换为 驼峰写法
var componentName = uppercamelcase(name);
// include 变量数组中添加 变量替换后的 IMPORT_TEMPLATE 字符串
includeComponentTemplate.push(render(IMPORT_TEMPLATE, {
name: componentName,
package: name
}));
// install 变量数组中添加 变量替换后的 INSTALL_COMPONENT_TEMPLATE 字符串 这里排除 'Loading', 'MessageBox', 'Notification', 'Message' 是由于这几个组件将会全局挂载到 Vue 的实例上。
if (['Loading', 'MessageBox', 'Notification', 'Message'].indexOf(componentName) === -1) {
installTemplate.push(render(INSTALL_COMPONENT_TEMPLATE, {
name: componentName,
component: name
}));
}
// list 的变量数组 中添加 组件的 key 的驼峰写法的字符串
if (componentName !== 'Loading') listTemplate.push(` ${componentName}`);
})
// 最终将 MAIN_TEMPLATE 模板中的变量进行替换,生成最终的 框架入口文件的 字符串模板
var template = render(MAIN_TEMPLATE, {
include: includeComponentTemplate.join(endOfLine),
install: installTemplate.join(',' + endOfLine),
version: process.env.VERSION || require('../../package.json').version,
list: listTemplate.join(',' + endOfLine)
});
// 将生成的 字符串模板写入到 框架入口文件
fs.writeFileSync(OUTPUT_PATH, template);
复制代码
关于
MAIN_TEMPLATE
字符串模板的写法分析,能够看 Element UI 项目分析
这个 文件中是经过循环已经配置好的 i18n
形式的数据字典, 对每个数据字典中语种对象都生成一个 语种目录,在每个目录中, 根据 模板引擎 生成 不一样语种对应的模板。 以后 根据数据字典中的 pages
属性里面的配置,替换掉模板中的变量。 最后写入到 example/pages/
。 这样每个语种都有一套对应的 .vue
代码。
page.json
这个文件里面配置了 i18n
的数据字典数组。
[
{
"lang": "zh-CN",
"pages": {
"index": { },
"component": {},
"changelog": {},
"design": {},
"guide": {},
"nav": {},
"resource": {}
}
}
...
]
复制代码
var fs = require('fs');
var path = require('path');
var langConfig = require('../../examples/i18n/page.json');
langConfig.forEach(lang => {
// 在../../examples/pages 文件夹下 读取 与 `lang.lang` (指 zh-CN、 en-US、 es ) 对应的文件信息, 若是抛出异常,说明该文件夹下没有改文件,就新建一个对应 `lang.lang` 的文件
try {
fs.statSync(path.resolve(__dirname, `../../examples/pages/${ lang.lang }`));
} catch (e) {
fs.mkdirSync(path.resolve(__dirname, `../../examples/pages/${ lang.lang }`));
}
// pages 里面包含 index component changelog design guide nav resource
Object.keys(lang.pages).forEach(page => {
// 模板地址
var templatePath = path.resolve(__dirname, `../../examples/pages/template/${ page }.tpl`);
// 输出地址
var outputPath = path.resolve(__dirname, `../../examples/pages/${ lang.lang }/${ page }.vue`);
var content = fs.readFileSync(templatePath, 'utf8');
var pairs = lang.pages[page]; // 就是本次循环里的 page
// 将 page 再次遍历, 并将 与 page 变量相对应的 模板中的 变量 替换成 page 对象中的 value
Object.keys(pairs).forEach(key => {
content = content.replace(new RegExp(`<%=\\s*${ key }\\s*>`, 'g'), pairs[key]);
});
// 最终将 替换后的 模板写入到 输入地址
fs.writeFileSync(outputPath, content);
});
});
复制代码
这个文件是根据 命令行参数 process.env.VERSION
或者 package.json
中的 version
的值, 来生成 框架的版本对象,并最终在 examples
下生成 version.json
文件
var fs = require('fs');
var path = require('path');
var version = process.env.VERSION || require('../../package.json').version;
var content = { '1.4.13': '1.4', '2.0.11': '2.0', '2.1.0': '2.1', '2.2.2': '2.2', '2.3.9': '2.3' };
if (!content[version]) content[version] = '2.4';
fs.writeFileSync(path.resolve(__dirname, '../../examples/versions.json'), JSON.stringify(content));
复制代码