create-react-app脚手架核心源码之/packages/create-react-app解读(五)

run函数

在create-react-app脚手架的目录为/packages/create-react-app/createReactApp.js

函数定义:执行app创建

源码如下:


function run(
  root,
  appName,
  version,
  verbose,
  originalDirectory,
  template,
  useYarn,
  usePnp
) {
    
    
  Promise.all([
    getInstallPackage(version, originalDirectory),
    getTemplateInstallPackage(template, originalDirectory),
  ]).then(([packageToInstall, templateToInstall]) => {
    
    
    const allDependencies = ['react', 'react-dom', packageToInstall];

    console.log('Installing packages. This might take a couple of minutes.');

    Promise.all([
      getPackageInfo(packageToInstall),
      getPackageInfo(templateToInstall),
    ])
      .then(([packageInfo, templateInfo]) =>
        checkIfOnline(useYarn).then(isOnline => ({
    
    
          isOnline,
          packageInfo,
          templateInfo,
        }))
      )
      .then(({
     
      isOnline, packageInfo, templateInfo }) => {
    
    
        let packageVersion = semver.coerce(packageInfo.version);

        const templatesVersionMinimum = '3.3.0';

        // Assume compatibility if we can't test the version.
        if (!semver.valid(packageVersion)) {
    
    
          packageVersion = templatesVersionMinimum;
        }

        // Only support templates when used alongside new react-scripts versions.
        const supportsTemplates = semver.gte(
          packageVersion,
          templatesVersionMinimum
        );
        if (supportsTemplates) {
    
    
          allDependencies.push(templateToInstall);
        } else if (template) {
    
    
          console.log('');
          console.log(
            `The ${
      
      chalk.cyan(packageInfo.name)} version you're using ${
      
      
              packageInfo.name === 'react-scripts' ? 'is not' : 'may not be'
            } compatible with the ${
      
      chalk.cyan('--template')} option.`
          );
          console.log('');
        }

        console.log(
          `Installing ${
      
      chalk.cyan('react')}, ${
      
      chalk.cyan(
            'react-dom'
          )}, and ${
      
      chalk.cyan(packageInfo.name)}${
      
      
            supportsTemplates ? ` with ${ 
        chalk.cyan(templateInfo.name)}` : ''
          }...`
        );
        console.log();

        return install(
          root,
          useYarn,
          usePnp,
          allDependencies,
          verbose,
          isOnline
        ).then(() => ({
    
    
          packageInfo,
          supportsTemplates,
          templateInfo,
        }));
      })
      .then(async ({
     
      packageInfo, supportsTemplates, templateInfo }) => {
    
    
        const packageName = packageInfo.name;
        const templateName = supportsTemplates ? templateInfo.name : undefined;
        checkNodeVersion(packageName);
        setCaretRangeForRuntimeDeps(packageName);

        const pnpPath = path.resolve(process.cwd(), '.pnp.js');

        const nodeArgs = fs.existsSync(pnpPath) ? ['--require', pnpPath] : [];

        await executeNodeScript(
          {
    
    
            cwd: process.cwd(),
            args: nodeArgs,
          },
          [root, appName, verbose, originalDirectory, templateName],
          `
        const init = require('${
      
      packageName}/scripts/init.js');
        init.apply(null, JSON.parse(process.argv[1]));
      `
        );

        if (version === '[email protected]') {
    
    
          console.log(
            chalk.yellow(
              `\nNote: the project was bootstrapped with an old unsupported version of tools.\n` +
                `Please update to Node >=14 and npm >=6 to get supported tools in new projects.\n`
            )
          );
        }
      })
      .catch(reason => {
    
    
        console.log();
        console.log('Aborting installation.');
        if (reason.command) {
    
    
          console.log(`  ${
      
      chalk.cyan(reason.command)} has failed.`);
        } else {
    
    
          console.log(
            chalk.red('Unexpected error. Please report it as a bug:')
          );
          console.log(reason);
        }
        console.log();

        // On 'exit' we will delete these files from target directory.
        const knownGeneratedFiles = ['package.json', 'node_modules'];
        const currentFiles = fs.readdirSync(path.join(root));
        currentFiles.forEach(file => {
    
    
          knownGeneratedFiles.forEach(fileToMatch => {
    
    
            // This removes all knownGeneratedFiles.
            if (file === fileToMatch) {
    
    
              console.log(`Deleting generated file... ${
      
      chalk.cyan(file)}`);
              fs.removeSync(path.join(root, file));
            }
          });
        });
        const remainingFiles = fs.readdirSync(path.join(root));
        if (!remainingFiles.length) {
    
    
          // Delete target folder if empty
          console.log(
            `Deleting ${
      
      chalk.cyan(`${ 
        appName}/`)} from ${
      
      chalk.cyan(
              path.resolve(root, '..')
            )}`
          );
          process.chdir(path.resolve(root, '..'));
          fs.removeSync(path.join(root));
        }
        console.log('Done.');
        process.exit(1);
      });
  });
}

函数解析

1. 主题结构

Promise.all([package, template])
.then(([p, t]) => {
    
    
  Promise.all([pi, ti])
  .then()
  .then()
  .then()
  .catch()
})

2. 第一个Promise.then执行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3bjtBZX2-1658402947951)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7a04bbe52f3f431a82e5a29fbdd9940b~tplv-k3u1fbpfcp-watermark.image?)]

如上所看到的,两个包react-scriptscra-template,这两个也是create-react-app脚手架packages中的两个主要的子项目

3. 执行checkOnline

两处关键的代码注释翻译

// 不要 ping Yarn 注册表。
// 我们只假设最好的情况。


// 如果定义了代理,我们可能无法解析外部主机名。
// 尝试将代理名称解析为连接的指示。

主要是判断yarn仓库连接检测

3. 第二个Promise.then执行结果

![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0BABvMhP-1658402947952)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/90382d22c3d9466a8cec497cde267f29~tplv-k3u1fbpfcp-watermark.image?)]](https://img-blog.csdnimg.cn/2ae5da50ee1d4244a704e43aa8e6d498.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LHJY8tRC-1658402947953)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e2f87d2e176c4cd9ae5d218d44619663~tplv-k3u1fbpfcp-watermark.image?)]

4. 继续执行,关键信息打印

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wGYwNb0U-1658402947953)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/eac2023f221e4827b2ee65c8d5b688f9~tplv-k3u1fbpfcp-watermark.image?)]

控制台信息打印:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kYxIJr2T-1658402947953)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4b7d27ac3e2a490ca47428b219721392~tplv-k3u1fbpfcp-watermark.image?)]

5. 项目执行install函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XmGVAHnK-1658402947954)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0d33a0ce5088466393e60238d895d6c4~tplv-k3u1fbpfcp-watermark.image?)]

commandargs debug数据:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9PJtzbjT-1658402947955)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a3d4c16c593349fa9596d4156ff6821e~tplv-k3u1fbpfcp-watermark.image?)]

控制台运行效果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-10wgBU4l-1658402947955)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/278364fc556f4bfa8e889dcc4c1e5b6f~tplv-k3u1fbpfcp-watermark.image?)]

运行完成:

  • 生成的项目目录新增了node_modulespackage-lock.json

在这里插入图片描述

  • 此时控制台打印如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qipnvxhk-1658402947956)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4aa71a31d5e44b7d84f58209f7d6cbd5~tplv-k3u1fbpfcp-watermark.image?)]

  • 项目的package.json内容如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BfQXpDZK-1658402947956)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/91febb8db9bb41c5960c35a320d17383~tplv-k3u1fbpfcp-watermark.image?)]

6. install返回数据如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ybWN1XCl-1658402947957)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9d050e32ad1a40fb872653907634dfdc~tplv-k3u1fbpfcp-watermark.image?)]

  • checkNodeVersion
    的packageJson运行后的数据'create-react-app/packages/my-app/node_modules/react-scripts/package.json'

  • setCaretRangeForRuntimeDeps

  const packagePath = path.join(process.cwd(), 'package.json');
  const packageJson = require(packagePath);

debug代码如下,其实就是生成的项目的pacakage.json里的内容

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NWY2q4FG-1658402947957)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e588fca3d80545589e156e7b05ab5e5b~tplv-k3u1fbpfcp-watermark.image?)]

  • makeCaretRange使插入符号范围
    举例:
    react: "18.2.0"返回react: "^18.2.0",具体差异,自行查阅其他资料

  • fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + os.EOL);

重新将更新后的内容异步写入到package.json,如下
在这里插入图片描述

对比上述,多了扩展符号^

7. pnpPath

debug

'/create-react-app/packages/my-app/.pnp.js'

.pnp.js是什么呢?

  • nodeArgs空数组

8. executeNodeScript

使用到了react-scripts,后续单独再补充
源代码如下:

function executeNodeScript({
     
      cwd, args }, data, source) {
    
    
  return new Promise((resolve, reject) => {
    
    
    const child = spawn(
      process.execPath,
      [...args, '-e', source, '--', JSON.stringify(data)],
      {
    
     cwd, stdio: 'inherit' }
    );

    child.on('close', code => {
    
    
      if (code !== 0) {
    
    
        reject({
    
    
          command: `node ${
      
      args.join(' ')}`,
        });
        return;
      }
      resolve();
    });
  });
}

debug代码参数情况:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EggxVDD6-1658402947959)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b042e39529354ea88439535232b4617b~tplv-k3u1fbpfcp-watermark.image?)]

开始运行如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TYumuGr6-1658402947960)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d67662aab8154cebac60402d04aa3b6c~tplv-k3u1fbpfcp-watermark.image?)]

到这里,新的项目基本就完成了,此时,去新建的项目my-app查看,如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9sIuc7mw-1658402947960)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e19e2cd5275d4b60a28264cd2d72466c~tplv-k3u1fbpfcp-watermark.image?)]

具体做了哪些事情,需要进一步去解读react-scripts,后续有时间再进一步分析;

猜你喜欢

转载自blog.csdn.net/gkf6104/article/details/125919398
今日推荐