umi源码-08 umi scripts 脚本

523 阅读1分钟

umi的脚本太多了,也都很有意思,因此有必要单独拎出来了解一下。 先来说几个包

chalk 日志加颜色

给命令行的日志加上颜色显示出来

import chalk from 'chalk';

const log = console.log;

// Combine styled and normal strings
log(chalk.blue('Hello') + ' World' + chalk.red('!'));

git-repo-info 仓库信息

获取根目录中 .git 文件的描述信息比如分支等

var getRepoInfo = require('git-repo-info');
 
var info = getRepoInfo();
// info.branch               // current branch
// info.sha                  // current sha
// info.abbreviatedSha       // first 10 chars of the current sha
// info.tag                  // tag for the current sha (or `null` if no tag exists)
// info.lastTag              // tag for the closest tagged ancestor
//                           //   (or `null` if no ancestor is tagged)
// info.commitsSinceLastTag  // number of commits since the closest tagged ancestor
//                           //   (`0` if this commit is tagged, or `Infinity` if no ancestor is tagged)
// info.committer            // committer for the current sha
// info.committerDate        // commit date for the current sha
// info.author               // author for the current sha
// info.authorDate           // authored date for the current sha
// info.commitMessage        // commit message for the current sha
// info.root                 // root directory for the Git repo or submodule
//                           //   (if in a worktree, this is the directory containing the original copy)
// info.commonGitDir         // directory containing Git metadata for this repo or submodule
//                           //   (if in a worktree, this is the primary Git directory for the repo)
// info.worktreeGitDir       // if in a worktree, the directory containing Git metadata specific to
                          //   this worktree; otherwise, this is the same as `commonGitDir`.

zx 高效写shell脚本

Google的zx库 可以帮助我们更加高效地用Nodejs写shell 脚本。

import 'zx/globals' // 会自动加载 $ 命令

let flags = [
  '--oneline',
  '--decorate',
  '--color',
]
await $`git log ${flags}`

cross-spawn 跨平台命令行

编写跨平台的命令

// 原生 node调用时,需要区分 Windows、OSX 和 Linux 环境,要不然会报错
const process = require('process');
const { spawn } = require('child_process');

spawn('npm', {
  stdio: 'inherit',
  // 仅在当前运行环境为 Windows 时,才使用 shell
  shell: process.platform === 'win32'
});


cross-spawn 可以根据平台自动补全对应配置,解决了跨平台不兼容的问题

const spawn = require('cross-spawn');

// Spawn NPM asynchronously
const child = spawn('npm', ['list''-g''-depth''0'], { stdio'inherit' });

umi-scripts bootstrap

monorepo时,在packages/ 文件夹下只需要新建一个文件名,再执行这个命令,就可以新建一个代码仓库,这个代码仓库包括 package.json, README.md, tsconfig.json 等描述信息。下面分步骤对代码进行说明 执行命令 umi-scripts bootstrap someNewRepo。 源码中只用了 force 参数,用于更新所以代码仓库,比如 umi-scripts bootstrap --force 这里有个小疑问,为啥不获取参数 argv: { _: [ 'someNewRepo' ] },当和 packages中的文件名称不匹配时,生成一个新的代码仓库。

import 'zx/globals';
import { PATHS, SCRIPTS } from './.internal/constants';
import { setExcludeFolder } from './.internal/utils';


(async () => {
  const root = PATHS.ROOT;
  // 获取根目录下的 packages 文件夹
  const pkgDir = path.join(root, 'packages');
  // 获取所有的文件夹名称
  // '["ast","babel-preset-umi","bundler-esbuild",
  // "bundler-utils","bundler-vite","bundler-webpack",
  // "core","create-umi","demo","lint","max","mfsu",
  // "plugin-docs","plugin-run","plugins","preset-umi",
  // "preset-vue","renderer-react","renderer-vue",
  // "server","testing","umi","utils"]'
  const pkgs = await fs.readdir(pkgDir);


  // 遍历 packages 下的所有文件夹名称
  for (const pkg of pkgs) {
    // console.log(pkg, 'pppp')
    if (pkg.charAt(0) === '.') continue;
    if (!(await fs.stat(path.join(pkgDir, pkg))).isDirectory()) continue;
    console.log(argv, 'argv====') // { _: [ 'someNewRepo' ] }
    await bootstrapPkg({
      pkgDir,
      pkg,
      force: argv.force,
    });
  }



  function getName(pkgName: string) {
    if (['umi'].includes(pkgName)) {
      return pkgName;
    } else {
      return `@umijs/${pkgName}`;
    }
  }

  function getVersion() {
    return require(PATHS.LERNA_CONFIG).version;
  }

  async function bootstrapPkg(opts: any) {
    const pkgDir = path.join(opts.pkgDir, opts.pkg);
    if (!opts.force && fs.existsSync(path.join(pkgDir, 'package.json'))) {
      console.log(`${opts.pkg} exists`);
    } else {
    // 当某个文件夹不下存在 package.json,生成一个子代码仓库
    // 包括: package.json、 README.md、tsconfig.json、.fatherrc.ts、
      const name = getName(opts.pkg);


      // package.json
      const pkgPkgJSONPath = path.join(pkgDir, 'package.json');
      const hasPkgJSON = fs.existsSync(pkgPkgJSONPath);
      const pkgPkgJSON = hasPkgJSON ? require(pkgPkgJSONPath) : {};
      fs.writeJSONSync(
        pkgPkgJSONPath,
        Object.assign(
          {
            name,
            version: getVersion(),
            description: name,
            main: 'dist/index.js',
            types: 'dist/index.d.ts',
            files: ['dist'],
            scripts: {
              build: SCRIPTS.BUILD,
              'build:deps': SCRIPTS.BUNDLE_DEPS,
              dev: SCRIPTS.DEV,
            },
            repository: {
              type: 'git',
              url: 'https://github.com/umijs/umi',
            },
            authors: [
              'chencheng <sorrycc@gmail.com> (https://github.com/sorrycc)',
            ],
            license: 'MIT',
            bugs: 'https://github.com/umijs/umi/issues',
            homepage: `https://github.com/umijs/umi/tree/master/packages/${opts.pkg}#readme`,
            publishConfig: {
              access: 'public',
            },
          },
          {
            ...(hasPkgJSON
              ? {
                  authors: pkgPkgJSON.authors,
                  bin: pkgPkgJSON.bin,
                  files: pkgPkgJSON.files,
                  scripts: pkgPkgJSON.scripts,
                  description: pkgPkgJSON.description,
                  dependencies: pkgPkgJSON.dependencies,
                  devDependencies: pkgPkgJSON.devDependencies,
                  compiledConfig: pkgPkgJSON.compiledConfig,
                }
              : {}),
          },
        ),
        { spaces: '  ' },
      );


      // README.md
      await fs.writeFile(
        path.join(pkgDir, 'README.md'),
        `# ${name}\n\nSee our website [umijs](https://umijs.org) for more information.`,
        'utf-8',
      );


      // tsconfig.json
      await fs.writeFile(
        path.join(pkgDir, 'tsconfig.json'),
        `{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src"]
}\n`,
        'utf-8',
      );


      // .fatherrc.ts
      await fs.writeFile(
        path.join(pkgDir, '.fatherrc.ts'),
        `import { defineConfig } from 'father';


export default defineConfig({
  extends: '../../.fatherrc.base.ts',
});\n`,
        'utf-8',
      );


      // src/index.ts
      const srcDir = path.join(pkgDir, 'src');
      if (!fs.existsSync(srcDir)) {
        await $`mkdir ${srcDir}`;
      }
      if (!fs.existsSync(path.join(pkgDir, 'src', 'index.ts'))) {
        await fs.writeFile(
          path.join(pkgDir, 'src', 'index.ts'),
          `
export default () => {
  return '${name}';
};\n`.trimLeft(),
          'utf-8',
        );
        await fs.writeFile(
          path.join(pkgDir, 'src', 'index.test.ts'),
          `
import index from './index';


test('normal', () => {
  expect(index()).toEqual('${name}');
});\n`.trimLeft(),
          'utf-8',
        );
      }


      // set excludeFolder for webstorm
      setExcludeFolder({ pkg: opts.pkg, cwd: root });


      console.log(chalk.green(`${opts.pkg} bootstrapped`));
    }
  }
})();