GitMaster里面展示项目结构时,同时也显示了对应的icon

看起来和Octotree是没什么区别,但其实在维护和更新上是有显著区别的。

Octotree是直接从file-icons/atom复制相关样式和字体文件到项目里,这样耦合的方式很不利于维护,所以我在处理文件图标时进行了额外的处理,把所有文件图标通过npm包的形式引入。

大家可能好奇为什么不直接用file-icons/atom,没有采用的原因有几个:

  • css样式经过Content Script方式注入会污染全局样式
  • 缺少Octicons图标
  • woff2文件指向不对

方案

经过考量,最终采用通过脚本处理文件,然后发布npm包: ineo6/file-icons

下载 file-icons/atom

使用download-git-repoGitHub下载代码。

还使用npm开发过程中常用的chalkora

ora是一个终端加载动画的库,有了它,你的终端不会再苍白。

image.png

chalk的作用是丰富终端文字样式。

image.png

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);
    }
  }
})

less处理

替换@font-face url

文件 styles/fonts.less 里面的内容是如下格式:

@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__'
              },
            },
          },

如何修改less文件

推荐使用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的定时任务实现。本文就暂不花费篇幅介绍了,感兴趣的可以摸索下。


ineo6
376 声望11 粉丝