webpack 之 构建包设计

将构建配置抽离成 npm 包的意义

  • 通用性
    • 业务开发这无需关注构建配置
    • 统一团队构建脚本
  • 可维护性
    • 构建配置合理的拆分(开发、生产、ssr环境)
    • README文档 changelog 文档等
  • 质量
    • 冒烟测试、单元测试、测试覆盖等
    • 持续集成

可选方案

  • 通过多个配置文件管理不同环境的配置
    • 通过webpack --config参数来控制
  • 将构建配置设计成一个库
  • 抽成一个工具进行管理
  • 将所有配置都放在一个文件,通过 --env 参数控制分支选择

构建配置包设计

通过多个配置文件管理不同环境的 webpack 配置

  • 基础环境 webpack.base.js
  • 开发环境 webpack.dev.js
  • 生产环境 webpack.prod.js
  • ssr 环境 webpack.ssr.js

抽离成一个 npm 包统一管理

  • 规范
    • git commit 日志
    • README
    • ESlint规范
    • Semver 规范
  • 质量
    • 冒烟测试
    • 单元测试
    • 测试覆盖率
    • CI

操作方法:通过 webpack-merge 组合配置

merge = require('webpack-merge')
...
merge(
	{ a[1], b: 5, c: 20 },
    { a[2], b: 10, d: 421 }
)
{ a: [1, 2], b: 10, c: 20, d: 421 }

合并配置

module.exports = merge( baseConfig, devConfig )

功能模块设计与目录结构

目录结构

  • lib 放置源代码
  • test 放置测试代码

请添加图片描述

新建项目

  1. 新建 builder-webpack 目录
  2. cd builder-webpack 进入目录
  3. yarn init 初始化项目
  4. 新建 lib 目录,其下新建四个文件
    • webpack.base.js
    • webpack.dev.js
    • webpack.prod.js
    • webpack.ssr.js

配置 webpack.base.js 文件

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');

const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const setMPA = () => {
  let entry = {};
  let htmlWebpackPlugins = [];
  let HtmlWebpackExternalsPlugins = [];
  let url = path.join(__dirname, '/src/*/index.jsx').replaceAll('\\', '/');
  const entryFiles = glob.sync(url);
  Object.keys(entryFiles).map(index => {
    let entryFile = entryFiles[index];
    let match = entryFile.match(/src\/(.*)\/index.jsx/);
    const pageName = match && match[1];
    if (pageName) {
      entry[pageName] = entryFile;
      // 每个入口文件设置 基础库的cdn
      HtmlWebpackExternalsPlugins.push(new HtmlWebpackExternalsPlugin({
        externals: [
          {
            module: 'react',
            entry: 'https://unpkg.com/react@18/umd/react.development.js',
            global: 'React'
          },
          {
            module: 'react-dom',
            entry: 'https://unpkg.com/react-dom@18/umd/react-dom.development.js',
            global: 'ReactDOM'
          }
        ],
        files: [`${pageName}.html`]
      }));
      // 入口文件生成模板
      htmlWebpackPlugins.push(
        new HtmlWebpackPlugin({
          template: path.join(__dirname, `/src/${pageName}/index.html`),
          filename: `${pageName}.html`,
          chunks: [pageName],
          inject: true,
          minify: {
            html5: true,
            collapseWhitespace: true,
            preserveLineBreaks: false,
            minifyCSS: true,
            minifyJS: true,
            removeComments: false
          }
        })
      );
    }
  });

  return { entry, htmlWebpackPlugins, HtmlWebpackExternalsPlugins };
};

const { entry, htmlWebpackPlugins, HtmlWebpackExternalsPlugins } = setMPA();

module.exports = {
  entry: entry,
  output:{
    filename: '[name].js',
    path: path.join(__dirname, 'dist')
  }
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ['babel-loader']
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: ['autoprefixer']
              }
            }
          },
          'less-loader'
        ]
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: ['autoprefixer']
              }
            }
          }
        ]
      },
      {
        test: /\.(png|svg|jpeg|jpg|gif|ico)$/i,
        type: 'asset',
        generator: {
          filename: "static/img/[name].[hash:7][ext]"
        }
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    /* 命令行信息显示优化 */
    new FriendlyErrorsWebpackPlugin(),
    /* 打包捕获 error */
    function () {
      this.hooks.done.tap('done', stats => {
        if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf('--watch') == -1) {
          console.log('build error');
          process.exit(1);
        }
      });
    },
    /* CSS 提取成一个单独的文件 */
    new MiniCssExtractPlugin({
      filename: '[name]_[hash:8].css'
    }),
  ]
    .concat(htmlWebpackPlugins)
    .concat(HtmlWebpackExternalsPlugins),
  stats: 'errors-only'
};

安装 webpack-merge,配置 webpack.dev.js

yarn add webpack-merge -D

在 webpack.dev.js 中引入

const {merge} = requrie('webpack-merge')
const baseConfig = require('./webpack.base')
const devConfig = {}
module.exports = merge(baseConfig, devConfig)
const {merge} = requrie('webpack-merge');
const webpack = require('webpack');
const baseConfig = require('./webpack.base');

const devConfig = {
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ],
  devServer: {
    port: 3000,
    compress: false,
    static: {
      directory: path.join(process.cwd(), 'dist'),
      publicPath: '/'
    },
    client: {
      overlay: {
        errors: true,
        warnings: false
      }
    },
    stats: 'errors-only'
  },
  devtools: 'cheap-source-map'
};
module.exports = merge(baseConfig, devConfig);

配置 webpack.prod.js

同样是使用 webpack-merge

const merge = require('webpack-merge');
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin');
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
const baseConfig = require('./webpack.base');

const prodConfig = {
  mode: 'production',
  /* 设置提取的公共文件包的大小 */
  optimization: {
  	 /* 压缩 css */
    minimize: true,
    minimizer: [
      new CssMinimizerWebpackPlugin()
    ],
    splitChunks: {
      minSize: 0,
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks: 'all',
          minChunks: 2
        }
      }
    }
  },
  plugins: [
    /* 速度优化: 引入基础包的 cdn */
    new HtmlWebpackExternalsPlugin({
      externals: [
        {
          module: 'react',
          entry: 'https://unpkg.com/react@18/umd/react.development.js',
          global: 'React'
        },
        {
          module: 'react-dom',
          entry: 'https://unpkg.com/react-dom@18/umd/react-dom.development.js',
          global: 'ReactDOM'
        }
      ]
    })
  ]
};

module.exports = merge(baseConfig, prodConfig);

optimize-css-assets-webpack-plugin 插件配合 cssnano css 处理器来压缩处理配置中 ExtractTextPlugin 实例导出的文件的文件名运行,而不是 源css文件的文件名,默认为 /\.css$/g

  • assetNameRegExp 一个正则表达式,指示应优化的最小化的资源的名称
    • 提供的正则表达式针对配置中 ExtractTextPlugin 实例导出的文件的文件名运行,而不是源css 文件的文件名,默认是 /\.css$/g
  • cssProcessor 用于优化最小化 css 的 css 处理器,默认是 cssnano
    • 这应该是一个跟随 cssnano.processor 接口的函数(接收 css 和 选项参数并返回一个 Promise)
  • cssProcessorOptions 传递给 cssProcessor 的选项,默认为 {}
  • cssProcessorPluginOptions 传递给 CSSProcessor 的插件选项,默认为 {}
  • canPrint 一个布尔值,指示插件是否可以将信息打印到控制台,默认为 true

配置 webpack.ssr.js

拷贝一份 webpack.prod.js,需要设置忽略解析 css

{
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    'ignore-loader'
                ]
            },
            {
                test:/\.less$/,
                use:[
                    'ignore-loader'
                ]
            }
        ]
    }
}
const merge = require('webpack-merge');
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin');
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
const baseConfig = require('./webpack.base');

const prodConfig = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['ignore-loader']
      },
      {
        test: /\.less$/,
        use: ['ignore-loader']
      }
    ]
  },
  mode: 'production',
  /* 设置提取的公共文件包的大小 */
  optimization: {
  	 /* 压缩 css */
    minimize: true,
    minimizer: [
      new CssMinimizerWebpackPlugin()
    ],
    splitChunks: {
      minSize: 0,
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks: 'all',
          minChunks: 2
        }
      }
    }
  },
  plugins: [
    /* 速度优化: 引入基础包的 cdn */
    new HtmlWebpackExternalsPlugin({
      externals: [
        {
          module: 'react',
          entry: 'https://unpkg.com/react@18/umd/react.development.js',
          global: 'React'
        },
        {
          module: 'react-dom',
          entry: 'https://unpkg.com/react-dom@18/umd/react-dom.development.js',
          global: 'ReactDOM'
        }
      ]
    })
  ]
};

module.exports = merge(baseConfig, prodConfig);

其他文件

  • 根目录新建 README.md 文件
  • .gitignore 设置 git 忽略文件
/node_modules
/logs

通过 ESLint 规范构建脚本

使用 eslint-config-airbnb-base

eslint --fix 可自动处理空格

module.exports = {
    "parser": "babal-eslint",
    "extends": "airbnb-base",
    "env": {
        "browser": true,
        "node": true
    }
}
  • 安装 eslint@babel/eslint-parser@babel/coreeslint-plugin-importeslint-config-airbnb-base
yarn add eslint @babel/eslint-parser @babel/core eslint-plugin-import eslint-config-airbnb-base -D 
  • 配置 eslint 配置文件 .eslintrc.js
module.exports = {
  "parser": "@babel/eslint-parser",
  "parserOptions": {
    "requireConfigFile": false
  },
  "extends": "airbnb-base",
  "env": {
    "browser": true,
    "node": true
  }
};
  • package.json 设置 scripts 脚本
{
    "scripts": {
        "fix": "eslint ./lib --fix",
        "eslint": "eslint ./lib"
    },
}
  • 使用(根据 eslint 提示对代码进行调整)
yarn run eslint 

yarn run fix 可自动更正一些格式问题

冒烟测试(预测试)

对提交测试的软件在进行详细深入的测试之前进行的预测试

主要目的暴露导致软件需要重新发布的基本功能失效等严重问题

关注问题

  • 构建是否成功

  • 每次构件完成 build 目录是否有内容输出

    • 是否有 JS、CSS 等静态资源文件
    • 是否有 HTML 文件

每次都手动执行,比较繁琐,可通过一些工具(mocha)来完成这一步骤

检测构建(清空dist、判断是否构建成功)

在示例项目中原型构建,看是否有报错

  • 根目录下创建 test 目录

  • test/smoke/index.js 再次书写冒烟测试相关的代码

  • 需要判断否写构建是否正常运作,需要有一个模板项目

  • 新建 test/smoke/template 目录下创建模板项目

    • 拷贝一个项目到 template 目录下,删除 该项目相关的webpack 配置(webpack.prod.js 等文件)
  • 需要 rimraf 这个库来处理删除 dist 目录这个操作

    • 每次构建之前否需要将 dist 目录清空
    • 执行删除操作之后,会执行一个回调函数
    yarn add rimraf -D
    
  • test/smoke/index.js

    • 首先删除 dist 目录,清除上次构建内容
    • 引入配置文件 webpack.prod.js 文件
    • webpack 方法接收一个配置文件,后执行回调函数,在回调函数中捕获错误信息,若构建有问题,打印错误信息
const path = require('path');
const webpack = require('webpack');
const rimraf = requir('rimraf');

/* 需要现将目录切换到 template  */
process.chdir(path.join(__dirname, 'template'));

rimraf('./dist', () => {
  const prodConfig = require('../../lib/webpack.prod');
  webpack(prodConfig, (err, stats) => {
    if (err) {
      console.log(err);
      process.exit(2);
    }
    console.log(stats.toString({
      colors: true,
      modules: false,
      children: false
    }));
  });
});
  • 执行 node index.js 文件
node ./test/smoke/index.js
  • 模板项目中路径均使用 __dirname(以template为根) 来配置路径,需要使用 process.cwd() 来获取整个项目的根路径
    • webpack.base.js 中定义一个全局变量 projectRoot 来替换 __dirname

webpack.prod.js

const { merge } = require('webpack-merge');
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin');
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
const baseConfig = require('./webpack.base');


const prodConfig = {
  mode: 'production',
  /* 设置提取的公共文件包的大小 */
  optimization: {
    /* 压缩 css */
    minimize: true,
    minimizer: [
      new CssMinimizerWebpackPlugin()
    ],
    splitChunks: {
      minSize: 0,
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks: 'all',
          minChunks: 2,
        },
      },
    },
  },
  plugins: [

    /* 速度优化: 引入基础包的 cdn */
    new HtmlWebpackExternalsPlugin({
      externals: [
        {
          module: 'react',
          entry: 'https://unpkg.com/react@18/umd/react.development.js',
          global: 'React',
        },
        {
          module: 'react-dom',
          entry: 'https://unpkg.com/react-dom@18/umd/react-dom.development.js',
          global: 'ReactDOM',
        },
      ],
    }),
  ],
};

module.exports = merge(baseConfig, prodConfig);

webpack.base.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');
const glob = require('glob');

const projectRoot = process.cwd();
/* /builder-webpack/test/smoke/template/ */

const setMPA = () => {
  const entry = {};
  const htmlWebpackPlugins = [];
  const url = path.join(projectRoot, '/src/*/index.jsx').replaceAll('\\', '/');
  const entryFiles = glob.sync(url);
  Object.keys(entryFiles).map((index) => {
    const entryFile = entryFiles[index];
    const match = entryFile.match(/src\/(.*)\/index.jsx/);
    const pageName = match && match[1];
    if (pageName) {
      entry[pageName] = entryFile;
      // 入口文件生成模板
      htmlWebpackPlugins.push(
        new HtmlWebpackPlugin({
          template: path.join(projectRoot, `/src/${pageName}/index.html`),
          filename: `${pageName}.html`,
          chunks: [pageName],
          inject: true,
          minify: {
            html5: true,
            collapseWhitespace: true,
            preserveLineBreaks: false,
            minifyCSS: true,
            minifyJS: true,
            removeComments: false,
          },
        }),
      );
    }
    return htmlWebpackPlugins;
  });

  return { entry, htmlWebpackPlugins };
};

const { entry, htmlWebpackPlugins } = setMPA();
console.log(entry);
module.exports = {
  entry: entry,
  output: {
    filename: '[name].js',
    path: path.join(projectRoot, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ['babel-loader'],
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: ['autoprefixer'],
              },
            },
          },
          'less-loader',
        ],
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: ['autoprefixer'],
              },
            },
          },
        ],
      },
      {
        test: /\.(png|svg|jpeg|jpg|gif|ico)$/i,
        type: 'asset',
        generator: {
          filename: 'static/img/[name].[hash:7][ext]',
        },
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    /* 命令行信息显示优化 */
    new FriendlyErrorsWebpackPlugin(),
    /* 打包捕获 error */
    function doneErrorPlugin() {
      this.hooks.done.tap('done', (stats) => {
        if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf('--watch') === -1) {
          process.exit(1);
        }
      });
    },
    /* CSS 提取成一个单独的文件 */
    new MiniCssExtractPlugin({
      filename: '[name]_[hash:8].css',
    }),
  ]
    .concat(htmlWebpackPlugins),
  resolve: {
    alias: {
      '@': path.join(projectRoot, '/'),
      '@server': path.join(projectRoot, '/server'),
      '@src': path.join(projectRoot, '/src'),
      '@comp': path.join(projectRoot, '/src/components'),
      '@images': path.join(projectRoot, '/src/assets/images'),
    }
  },
  stats: 'errors-only',
};

创建测试文件

  • /test/smoke/目录下新建两个测试文件

    • html-test.js 检测是否生成 HTML 文件
    • css-js-test.js 检测是否生成 css、js 文件
  • 安装 mocha

yarn add mocha -D
  • 安装 glob-all
yarn add glob-all -D 
  • html-test.js
const glob = require('glob-all');

describe('Checking generated html files', () => {
  it('should generate html files', done => {
    const files = glob.sync([
      './dist/index.html',
      './dist/search.html',
    ]);

    if (files.length > 0) {
      done();
    } else {
      throw new Error('no html files generate');
    }
  });
});
  • css-js-test.js
const glob = require('glob-all');

describe('Checking generated css js files', () => {
  it('should generate css js files', done => {
    const files = glob.sync([
      './dist/index_*.js',
      './dist/index_*.css',
      './dist/search_*.js',
      './dist/News_*.js',
    ]);

    if (files.length > 0) {
      done();
    } else {
      throw new Error('no css js files generate');
    }
  });
});
  • 在 index.js 中
    • 新建 mocha 实例
    • 使用 实例方法 addFile() 引入以上两个测试文件
    • 使用 实例方法 run() 运行 mocha
const path = require('path');
const webpack = require('webpack');
const rimraf = require('rimraf');
const Mocha = require('mocha');

/* 设置过期时间 */
const mocha = new Mocha({
  timeout: '10000ms'
});

/* 需要现将目录切换到 template  */
process.chdir(path.join(__dirname, 'template'));

rimraf('./dist', () => {
  const prodConfig = require('../../lib/webpack.prod');
  webpack(prodConfig, (err, stats) => {
    if (err) {
      console.log(err);
      process.exit(2);
    }
    console.log(stats.toString({
      colors: true,
      modules: false,
      children: false
    }));

+    console.log('Webpack build success, begin run test');

+    mocha.addFile(path.join(__dirname, 'html-test.js'));
+    mocha.addFile(path.join(__dirname, 'css-js-test.js'));

+    mocha.run();
  });
});

在这里插入图片描述

单元测试与测试覆盖率

冒烟测试保证了 构建包的基本功能可用

细节部分的把控需要单元测试来完成

可选方案

  • 单纯测试框架,需要断言库(chai / should.js / expect / better-assert)

    • mocha 框架
    • ava 框架
  • 集成框架,开箱即用

    • Jasmine 框架
    • Jest 框架
  • 极简 API

编写单元测试用例

  • 技术选型: Mocha + Chai
  • 测试代码: describe, it, expect
  • 测试命令: mocha add.test.js

  1. test 目录下新建 unit 目录用来编写测试用例

    • 创建对应的测试文件 webpack-base-test.js
    
    describe('webpack.base.js test case', () => {
      const baseConfig = require('../../lib/webpack.base');
      console.log(baseConfig);
      it('entry', () => {
    
      });
    });
    
  2. test 目录下新建 index.js 作为单元测试的入口文件(主要用来引入对应的单元测试用例

    • 每次执行测试用例之前需要进入到模板项目 template 中去
    • 因此使用 process.chdir() 切换路径到 template 目录
    const path = require('path');
    
    process.chdir(path.join(__dirname, 'smoke/template'));
    
    describe('builder-webpack test case', () => {
      require('./unit/webpack-base-test');
    });
    
  3. package.json 新建 scripts

    {
        "scripts": {
        	"test": "./node_modules/.bin/_mocha"
        }
    }
    

    执行 yarn run test 时就会自动访问 test 目录下 index.js 入口文件

    yarn run test
    

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  1. 为判断 每次构建是否影响 entry 使用断言库 assert

  2. 安装 assert

    yarn add assert -D
    
    const assert = require('assert');
    
    describe('webpack.base.js test case', () => {
      const baseConfig = require('../../lib/webpack.base');
      // console.log(baseConfig);
      it('entry', () => {
        assert.equal(baseConfig.entry.index, 'D:/Z-workSpace/React/ssr-react/builder-webpack/test/smoke/template/src/index/index.jsx');
        assert.equal(baseConfig.entry.search, 'D:/Z-workSpace/React/ssr-react/builder-webpack/test/smoke/template/src/search/index.jsx');
      });
    });
    

在这里插入图片描述

此时测试用例已经跑通了

测试覆盖率

推荐使用 nyc

  • 安装
yarn add nyc -D
  • 修改 scripts 脚本
{
    "scripts": {
        "test": "mocha",
        "coverage": "npx nyc mocha"
    }
}

在这里插入图片描述

单元测试与冒烟测试

单元测试和测试覆盖率一般是在构建前还是构建后完成?

  • 针对基础组件或者构建包,通过单元测试和测试覆盖率 保证组件质量,这需要在发版之前严格遵守
  • 对业务而言,每次 commit 就会进行业务代码的构建。同时异步触发单元测试和测试覆盖率检查,无先后顺序

持续集成

在这里插入图片描述

优点

  • 快速发现错误
  • 防止分支大幅偏离主干

核心措施

代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成

接入 github action

  • 首先创建一个 github 项目,命名为 builder-webpack
    在这里插入图片描述

  • 项目根目录下创建 .github/workflow/test.yml 编写 yml 脚本设置自动化测试即可

  • git 链接远程仓库

    # 克隆远程仓库到一个干净的目录
    git clone <远程仓库地址>
    
    # 进入项目
    cd builder-webpack[项目名称]
    
    # 查看当前目录绝对路径
    pwd
    
    # 将源码复制到该目录下(-r 表示递归,对多个文件操作)
    cp -r [被复制目录 ./***/ ] [当前目录 ./]
    
    cd ../../
    cp -r ../ssr-react/builder-webpack/ ./
    
    
  • 编写 github action 自动化测试脚本 test.yml

    # This is a basic workflow to help you get started with Actions
    
    name: CI
    
    # Controls when the workflow will run
    on:
      # Triggers the workflow on push or pull request events but only for the "main" branch
      push:
        branches: [ "main" ]
      pull_request:
        branches: [ "main" ]
    
      # Allows you to run this workflow manually from the Actions tab
      workflow_dispatch:
    
    # A workflow run is made up of one or more jobs that can run sequentially or in parallel
    jobs:
      # This workflow contains a single job called "build"
      build:
        # The type of runner that the job will run on
        runs-on: ubuntu-latest
    
        # Steps represent a sequence of tasks that will be executed as part of the job
        steps:
          # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
          - uses: actions/checkout@v3
                
          - name: setup node.js environment
            uses: actions/setup-node@v3.3.0
            with:
              node-version: "18.X"
    
          - name: install dep
            run: yarn install -D
            
          - name: switch to template project
            run: cd ./test/smoke/template
            
          - name: install template project dep
            run: yarn install -D
            
          - name: run test scripts
            run: yarn run test
    
  • 提交代码到远程仓库

    git add .
    git commit -m 'feat: builder github action ci'
    git push origin [branch_name]
    
  • 由于之前是在本地测试,entry 入口是本地目录,推送到 github 之后需要修改 webpack-base-test.js 单元测试脚本

    assert.equal(baseConfig.entry.index, '/home/runner/work/builder-webpack/builder-webpack/test/smoke/template/src/index/index.jsx');
        assert.equal(baseConfig.entry.search, '/home/runner/work/builder-webpack/builder-webpack/test/smoke/template/src/search/index.jsx');
    

    具体构建信息可在 github 项目 action 中查看

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • test.yml 设置依赖缓存,减少每次构建安装依赖的时间

    
    name: CI
    
    on:
    
      push:
        branches: [ "main" ]
      pull_request:
        branches: [ "main" ]
    
    
      workflow_dispatch:
    
    
    jobs:
     
      test:
     
        runs-on: ubuntu-latest
    
       
        steps:
          # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
          - uses: actions/checkout@v3
          
    
              
              
          - name: setup node.js environment
            uses: actions/setup-node@v3.3.0
            with:
              node-version: "18.X"
              
          - name: Cache node_modules
            id: cache-node-modules
            uses: actions/cache@v1
            with:
              path: node_modules
              key: ${{ runner.os }}-${{ matrix.node-version }}-nodeModules-${{ hashFiles('package.json') }}
              restore-keys: |
                ${{ runner.os }}-${{ matrix.node-version }}-nodeModules-
    
          - name: install dep
            if: steps.cache-node-modules.outputs.cache-hit != 'true'
            run: yarn install -D
            
          - name: switch to template project
            run: cd ./test/smoke/template
            
            
          - name: Cache node_modules
            id: cache-node-modules-template
            uses: actions/cache@v2
            with:
              path: node_modules
              key: ${{ runner.os }}-${{ matrix.node-version }}-nodeModules-${{ hashFiles('package.json') }}
              restore-keys: |
                ${{ runner.os }}-${{ matrix.node-version }}-nodeModules-
            
          - name: install template project dep
            if: steps.cache-node-modules-template.outputs.cache-hit != 'true'
            run: yarn install -D
            
          - name: run test scripts
            run: yarn run test
            
    

在这里插入图片描述

发布构建包到 npm 社区

  • 修改 package.json 中 name 字节(确保 npm 社区 中该包名没有被使用过)
  • 升级版本
    • 补丁版本号 npm version patch
    • 小版本号 npm version minor
    • 大版本号 npm version major
# 登录 npm
npm login

# 升级版本
npm version minor

# 发布构建包
npm publish

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值