GitMaster
里面展现项目结构时,同时也显示了对应的icon
。javascript
看起来和Octotree
是没什么区别,但其实在维护和更新上是有显著区别的。css
Octotree
是直接从file-icons/atom
复制相关样式和字体文件到项目里,这样耦合的方式很不利于维护,因此我在处理文件图标时进行了额外的处理,把全部文件图标经过npm
包的形式引入。前端
你们可能好奇为何不直接用file-icons/atom
,没有采用的缘由有几个:java
css
样式通过Content Script
方式注入会污染全局样式Octicons
图标woff2
文件指向不对通过考量,最终采用经过脚本处理文件,而后发布npm
包: ineo6/file-icons。node
file-icons/atom
使用download-git-repo
从GitHub
下载代码。webpack
还使用npm
开发过程当中经常使用的chalk
和ora
。git
ora
是一个终端加载动画的库,有了它,你的终端不会再苍白。github
chalk
的做用是丰富终端文字样式。web
const path = require('path'); const chalk = require('chalk'); const fs = require('fs'); const os = require('os'); const ora = require('ora'); const download = require('download-git-repo'); const cwd = process.cwd(); const origin = 'file-icons/atom'; const branch = "#master"; const tmpDirPrefix = path.join(os.tmpdir(), '.tmp'); const tmpDir = fs.mkdtempSync(tmpDirPrefix); const spinner = ora(`downloading ${origin}...`); spinner.start(); download(`${origin}${branch}`, tmpDir, { clone: false }, function (err) { spinner.stop(); if (err) { console.log(chalk.red(`Failed to download repo https://github.com/${origin}${branch}`, err)); } else { console.log(chalk.green(`Success to download repo https://github.com/${origin}${branch}`)); const spinnerExtract = ora('Extract Data...'); spinnerExtract.start(); try { // 处理代码的逻辑 spinnerExtract.stop(); console.log(chalk.green('Done!')); } catch (e) { spinnerExtract.stop(); console.log(e.message); } } })
文件 styles/fonts.less 里面的内容是以下格式:chrome
@font-face { font-family: FontAwesome; font-weight: normal; font-style: normal; src: url("atom://file-icons/fonts/fontawesome.woff2"); }
这个显然没法在前端项目甚至Chrome
扩展里正确引用woff2
字体。
由于在Chrome
扩展里没法引入远程的woff2
,因此改成引入扩展目录中的字体,即改为以下格式:
@font-face { font-family: FontAwesome; font-weight: normal; font-style: normal; src: url("@{ICON_PATH}/fonts/fontawesome.woff2"); }
而后在webpack
里设置less
变量ICON_PATH
'less-loader', { loader: 'less-loader', options: { javascriptEnabled: true, modifyVars: { ICON_PATH: 'chrome-extension://__MSG_@@extension_id__' }, }, },
推荐使用gonzales-pe
,它可以解析SCSS
, Sass
, LESS
,并转为AST
抽象语法树。
而后咱们根据须要修改AST
的结构,最终调用astNode.tostring()
转换获得代码。
const { parse } = require('gonzales-pe'); const fs = require('fs'); const chalk = require('chalk'); function replaceAtomHost(content) { if (content.includes('atom://file-icons/')) { content = content.replace('atom://file-icons/', '@{ICON_PATH}/'); } return content; } function replaceUrlHost(ast) { ast.traverseByType('uri', (node) => { node.traverse(item => { if (item.is('string')) { item.content = replaceAtomHost(item.content) } }); }); return ast; } function replaceDeclaration(ast) { ast.traverseByType('declaration', (decl) => { let isVariable = false; decl.traverse((item) => { if (item.type === 'property') { item.traverse((childNode) => { if (childNode.content === 'custom-font-path') { isVariable = true; } }); } if (isVariable) { if (item.type === 'value') { const node = item.content[0]; node.content = replaceAtomHost(node.content) } } return item; }); }); return ast; } function processFonts(lessFile) { const content = fs.readFileSync(lessFile).toString(); if (content && content.length > 0) { let astTree; try { astTree = parse(content, { syntax: 'less' }) } catch (e) { console.log(chalk.red(`parse error: ${e}`)); return; } try { astTree = replaceUrlHost(astTree); astTree = replaceDeclaration(astTree); return astTree; } catch (e) { console.log(chalk.red(`transform error: ${e}`)); } } } module.exports = function (file) { const ast = processFonts(file); if (ast) { fs.writeFileSync(file, ast.toString()); } }
. ├── bin ├── index.js ├── index.less // 入口样式 ├── lib // 完成的样式,字体 └── resource // 待合并资源
从file-icons/atom
复制如下文件到lib
:
fonts
styles
lib/icons
lib/utils.js
从resource
里面内容复制到lib
。
在index.less
里面内容以下:
@import "lib/styles/colours.less"; @import "lib/styles/fonts.less"; @import "lib/styles/octicons.less"; .file-icons-wrapper { @import "lib/styles/icons.less"; @import "lib/styles/fix.less"; }
这里经过添加父级file-icons-wrapper
来控制样式影响范围。
至此,大体完成了针对file-icons/atom
的定制工做。
最终咱们经过npm run build
命令完成拉取代码,处理文件的。
对应的脚本在bin/update.js
固然最后能够优化的是让任务自动执行,这点能够结合GitHub Actions
的定时任务实现。本文就暂不花费篇幅介绍了,感兴趣的能够摸索下。