# amos-build

## descr

* **supoport webpack@1: 小于 v1.2.68**
* **supoport webpack@2+: 大于 v2.0.0**
* **update `babel@7.x` since v3.1.0**
* **`amos-build@5.0.3` 以上版本，去掉默认 `module.exports` 支持，混用 `export` 和 `export default` 的模块会存在部分bug，需要自行修改**

## node-sass install wizard

[分享](http://note.youdao.com/noteshare?id=7566ba03f46929e07e7fd5d91bb53013&sub=A5C0195506294B6091D2C1666ECD17D6)

基于 webpack 的构建封装.

----

## 特性

* 基于 [webpack](http://webpack.github.io/docs/) 实现
* 支持通过 `webpack.config.js` 进行扩展 webpack 的配置项
* 支持导出`amosConfig.js、dll/*`进行自定义扩充
* @deprecated 支持 [stage-0](https://babeljs.io/docs/plugins/preset-stage-0), [es2015](https://babeljs.io/docs/plugins/preset-es2015), [react](https://facebook.github.io/react/),[sass](http://sass-lang.com/) 和 [less](http://lesscss.org/)
* 支持 [env](http://babeljs.io/docs/plugins/preset-env/)
* 支持dll,通过与amos-dll配合,快速进行构建,注意配置context为build目录
* 支持发布模式,并进行zip压缩,通过`.amosrun.config.js`进行自定义配置
* 支持 hash 模式的构建, 并生成映射表 `map.json`
* 插件: `plugins/*`,集成SimpleProgress、WebpackMd5Hash、AddAssetHtmlPlugin等
* 添加 worker-loader 支持 @since v2.0.19

## 安装

```bash
npm i amos-build --save--dev
```

## 使用

```bash
amos-build [options]
```

### 命令行参数

```bash
$ amos-build -h

  Usage: amos-build [options]

  Options:

    -h, --help                output usage information
    -v, --version             output the version number
    -o, --output-path <path>  output path
    -w, --watch [delay]       watch file changes and rebuild
    --hash                    build with hash and output map.json
    --publicPath <publicPath> webpack publicPath
    --devtool <devtool>       sourcemap generate method, default is null
    --config <path>           custom config path, default is webpack.config.js
    --no-compress             build without compress
```

### 配置扩展

如果需要对内置的 webpack 配置进行修改, 可在项目根目录新建 `webpack.config.js` 进行扩展.

让 `webpack.config.js` 输出 `Function`, 比如:

```js
const path = require("path");

module.exports = function(webpackConfig) {
  webpackConfig.output.path = path.join(__dirname, './public');
  return webpackConfig;
};
```

参数:

- `webpackConfig` -- 默认配置, 修改后返回新的配置

详细内容：[amos-build 基本使用](http://ilex.amos.build.com/index.html)

* `amos-run`
  * **common**: scss/js src/** to lib,retain original scss
  * **singlejs**: build for react components js，width js/jsx, output lib
  * **assetscss**: build for assetscss，with less、sass/scss
  * **amosantd**: for amos-antd
  * **amosantdCustom**: build for amosantdCustom，with less、css js/jsx
  * **static**
  * **assets2dist**
  * **scss**
  * **amoscoreJs**: build for amos-core js，`width js/jsx, output min.js/.css`
  * **amoscore**: for amos-core，按顺序执行 ['scss', 'static', 'assets2dist', 'amoscoreJs']。
  * **amoscoreCustom**: for amos-core
  * **security**: for amos-security，`build for amos-security，width scss/sass js/jsx, output min.js/.css`
  * **amd**: for amd,only copy file to `released`
  * **themes**: build all themes. you should copy this files to prod dir
  * **themes-watch**: build themes and watch themes dir
  * **commonExtra**: 公共Extra，将额外文件打包进released下
  * **src-static-all**: static all
  * **third-js**: third js, 将根目录下 `third、extra、plugin、plugins` 文件夹之下的所有js、jsx拷贝至 lib 目录下，保留文件目录结构
  * **released**: for all released, copy `dist、src/assets/**、extra/**、browser/**、*.html、amos.config.js、favicon.ico` to released
  * **dllreleased**: for all dllreleased dev
  * **dllreleased-test**: for all dllreleased test
  * **dllreleased-pre**: for all dllreleased pre
  * **dllreleased-prod**: for all dllreleased prod
  * **extraReleased**: for all extraReleased released
  * **extraReleased-prod**: for all extraReleased released
  * **extraReleased-test**: for all extraReleased released
  * **extraReleased-pre**: for all extraReleased released
  * **extraReleased-dev**: for all extraReleased released
  * **released2server**: for all released and copy to remote server
  * **dorobots**: create robots
  * **released2zip**: zip released

* `amos-auto`
  * **amd**: for amd,only copy file to `released`
  * **released**: for all released
  * **dllreleased**: for all dllreleased
  * **released2server**: for all released and copy to remote server
  * **released2zip**: zip released

* `ab-tools`
  * **webpack**: entry is `src/*.js`
  * **build**: build `src/**/*.js、src/**/*.scss`, with react
  * **ab-babel**: `src/**/*.js` non react
  * **ab-js**: `src/**/*.js` non babel

* amosConfig

  for amos team

* commonConfig

  common webpack config

* simpleConfig

  build js with webpack

* docitConfig

  same as simpleConfig, and add parse md doc loader

* dist

  can make your single components with one bundle, example `amos-core/amos-antd`

* **extra config**

  .amosrun.config.js

  such as

  ```js
    module.exports = {
      // 指定zip文件名称
      zip: 'released.rar', // default
      // 自定义配置 zip  v5.0.5 版本新增
      zip: {
        prefix: 'released', // 前缀，默认 released
        format: 'rar', // 文件格式，默认 rar，可选 zip
        date: true // 使用 date 作为后缀, false、true、datefmt [YYYYmmdd、YYYY、mmdd、HH:mm:SS] 等 年Y 月m 日d 时H 分M秒S
      },
      // 同级目录静态文件的拷贝 默认: ['*.html', 'amos.config.js', 'favicon.ico']
      extra: ['loadBMapLib.js', 'defaultSetting.js', '*.html', 'amos.config.js', 'favicon.ico']
    };
  ```

## simpleConfig/docitConfig/polyfillConfig

### usage

* simpleConfig

```js
/**
 * webpack 打包配置文件
 * @author ilex
 */
const simpleConfig = require('amos-build/lib/simpleConfig');

// ------------------------------------------------------
// 添加webpack加载别名,用于导包重定向，优化打包以及代码
// 此处需要自己进行定义
// 👻可修改
// ------------------------------------------------------
const alias = {
  _MOCK: __dirname + '/src/_mock'
};
// ------------------------------------
// 入口点
// ------------------------------------
const entry = {
  app: './src/entry/index.js'
};

const config = {
  tpl: './tpl.html',
  toFile: 'index.html',
  // 设置自定义的网站 ico，默认为 amos.ico， 设置为 null 则不设置 ico
  favicon: './my.ico',
  entry: entry,
  port: 3000,
  useHot: true,
  // 启用 worker `your.worker.js`,独立打包worker
  enableWorker: true,
  alias: alias,
  sourceMap: true,
  // 设置忽略压缩，默认在 prod 模式下，会自动加载 ugly 插件，该参数设置为 true时，将不再执行压缩
  ignoreUgly: false,
  excludeUgly: [/common\.js/, /app\.\w+\.js/], // 注意，excludeUgly 代表的是编译后生成的js文件
  // Boolean|null|String， null|false|true。
  // 设置为 null 则不启用 cache。
  // 设置为 false 则采用系统 temp 路径。C:\\Users\\SmartRay\\AppData\\Local\\Temp\\.babel-cache
  // 设置为 true 则采用当前项目目录。 -> node_modules/.cache/...
  // 设置为 string 则在temp目录下新建目录。 '.cache' -> C:\\Users\\SmartRay\\AppData\\Local\\Temp\\.cache
  cache: false
};

// 如果需要将指定的的 node_modules 下的包不进行 Ugly 处理，可以如下设置
// 以下设置，将会把 指定的 vender 包，打进 common.js 中
/*
const entry = {
  app: './src/entry/index.js'
  vender: ['react', 'react-dom', ...]
};

const config = {
  ...
  excludeUgly: [/common\.js/, /vender\.\w+\.js/], // 注意，excludeUgly 代表的是编译后生成的js文件
};

*/

const defaultConfig = simpleConfig(config);
defaultConfig.name = 'amos-init';

module.exports = defaultConfig;
```

* docitConfig

同上，修改为
`const docitConfig = require('amos-build/lib/docitConfig');`

* polyfillConfig

同上，修改为
`const polyfillConfig = require('amos-build/lib/polyfillConfig');` or `const polyfillConfig = require('amos-build/lib/polyfillConfig/index.js');`

* umdConfig

生成 `umd` 模式的模块。支持传入 `cssModules` 用于设置 `css-loader` 的 `modules` 属性。

webpack.config.js

```js
const HtmlWebpackPlugin = require('amos-build/lib/plugins/HtmlWebpackPlugin');
const HtmlInlineCodePlugin = require('html-inline-code-plugin');
const resolveCwd = require('amos-build/lib/tools/resolveCwd');
const umdConfig = require('amos-build/lib/umdConfig');
const ENV = require('amos-build/lib/tools/__ENV__');

const alias = {
};

const entry = {
  demo: './example/entry.umd.js'
};

const sourceMap = ENV === 'production' ? false : true

const config = {
  entry,
  alias,
  // 仅 umdConfig 支持，表现为 output.pasth
  outputPath: resolveCwd('docsite'), // 默认 为 dist 目录,注意 绝对路径
  // 仅 umdConfig 支持，表现为 output.filename
  filename: 'mydemo.js', // '[name].js'
  library: 'mydemo',
  libraryTarget: 'umd',
  // 开启 css modules
  // cssModules: true,
  sourceMap
};

const defaultConfig = umdConfig(config);

if (ENV === 'development'){
  defaultConfig.plugins.push(
    // index tpl
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: './tpl.html',
      inject: true,
      minify: {
        removeComments: true,
        collapseWhitespace: false,
        removeAttributeQuotes: false
      },
      chunksSortMode: 'dependency'
    }),
    new HtmlInlineCodePlugin({
      inject: 'body',
      begin: false,
      tag: 'script',
      code: `
        console.log(mydemo);
        var createUI = window.mydemo;
        createUI();
      `
    })
  );
}

module.exports = defaultConfig;
```

entry.umd.js

```js
import React from 'react';
import { render } from 'react-dom';
import App from './App';
import './index.scss';

export default function createUI({ container = 'app' } = {}) {
  render(<App />, document.getElementById(container));
}
```

umd 模式新增 code-split since 5.0.1

使用默认 `scripts.json` 动态加入umd chunk 脚本

```js
// 界面直接加载
fetch('/scripts.json').then(function(res){
  return res.json();
}).then(function(data){
  data.head.scripts.forEach(function(js){
    // document.write(js);
  });
  data.head.urls.forEach(function(url){
    var scri = document.createElement('script');
    scri.src = url;
    document.body.appendChild(scri);
  });
  data.head.tags.forEach(function(tag){
    var t = document.createElement(tag.tagName);
    if (tagName === 'script'){
      t.src = tag.attributes.src;
    }
    document.body.appendChild(t);
  });
  data.body.scripts.forEach(function(js){
    document.write(js);
  });
});

// 通过代码引入

import scripts from './scripts.json';
```

* cjsLibConfig

默认启用使用 `NodeModuleExternals` 插件，生成 externals 配置。

同上，修改为
`const cjsLibConfig = require('amos-build/lib/cjsLibConfig');`

```js
const cjsLibConfig = require('amos-build/lib/cjsLibConfig');

const config = {
  external
};
```

设置 `whitelist` 将 `node_modules` 中的 required assets (i.e css files) 加入。

```js
nodeExternals({
  // load non-javascript files with extensions, presumably via loaders
  whitelist: [/\.(?!(?:jsx?|json)$).{1,5}$/i],
})
```

## plugins

### ProgressWithMsg

#### options

|property|type|description|
| -------- | ---- | ----------- |
| messageTemplate | string | A template of progress and message shown while bundling modules. You can also use [node-progress token](https://github.com/visionmedia/node-progress#tokens). ```:msg``` shows current message of [webpack ProgressPlugin](https://webpack.github.io/docs/list-of-plugins.html#progressplugin). |
| progressOptions | object | [node-progress options](https://github.com/visionmedia/node-progress#options) to draw progress bar. defaults are shown below.

```js

  import ProgressWithMsg from 'amos-build/lib/plugins/ProgressWithMsg';

  // basic use
  const webpackConfig = {
    entry: 'index.js',
    output: {
      path: 'dist',
      filename: 'bundle.js'
    },
    plugins: [
      new SimpleProgressPlugin()
    ]
  };

  // options

  -- defaults
  {
    messageTemplate: [':bar', chalk.green(':percent'), ':msg'].join(' ')
    progressOptions: {
      complete: chalk.bgGreen(' '),
      incomplete: chalk.bgWhite(' '),
      width: 40,
      total: 100,
      clear: false
    }
  }

```

## use dll

### 生成dll依赖

> 生成dll  `webpack.dll.js` 配置, 默认生成为 `Amoslib`

```js
const generateDll = require('amos-build/lib/dll/generateDll');
const path = require('path');

const config = {
  amosLib: ['react', 'react-dom'],
  // webpack.output.path  dll文件生成目录
  outputPath: 'dll',
  // webpack.output.filename
  fileName: '[name].js',
  manifestPath: path.resolve(__dirname, 'dll/manifest.json'),
  sourceMap: true;
};

module.exports = generateDll(config);
```

### 使用 dll

> dll config （采用 amos-dll为例）,注意目录为 `根目录/build/amosbuildConf`， 由于 `amos-dll`执行时 `context` 为 `build`目录

```js

//-----------------------------------------------------------
// notice: do not modify this file content except entry/alias
// you can modify some config such as
// `showProgress/tpl/productionSourceMap/productionGzip`
// and so on
//-----------------------------------------------------------

const path = require('path');
const distPath = path.resolve(__dirname, '..', 'dist');

// const projectRoot = path.resolve(__dirname, '../');

const assetsPath = function(_path) {
  return path.posix.join('static', _path);
};

// ------------------------------------
// 入口点
// 👻 可修改
// ------------------------------------
const entry = {
  // app: './src/entry/index.js'
  app: './src/entry/demo.js'
};

// ------------------------------------
// 添加webpack加载别名,用于导包重定向，优化打包以及代码
// 此处需要自己进行定义
// 👻可修改
// ------------------------------------
const alias = {
  // MODEL: projectRoot + '/src/model',
  // UTILS: projectRoot + '/src/utils',
  // CONSTS: projectRoot + '/src/consts'
};

//-----------------------------------------------------------
// 👻  可修改，除非你知道该怎么修改
///-----------------------------------------------------------
module.exports = {
  webpackConf: {
    name: 'fireiot',
    entry: entry,
    alias: alias
  },
  showProgress: true,
  context: __dirname,
  tpl: 'tpl.html',
  extractTextPath: assetsPath('css/[name].css?v=[contenthash:8]'),
  visualizer: path.resolve(distPath, 'visualizer.html'),
  prod: {
    dll: {
      fileName: './node_modules/amos-dll/common/prod/Amoslib.js', // 完整路径
      manifest: 'amos-dll/common/prod/manifest.json',
      outputPath: assetsPath('common/js'), // 生成目录
      publicPath: '/static/common/js' // 注入地址
    },
    toFile: path.resolve(distPath, 'index.html'),
    assetsRoot: distPath,
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    productionSourceMap: false,
    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],
    output: {
      path: distPath,
      filename: assetsPath('js/[name].js?v=[chunkhash:8]'),
      chunkFilename: assetsPath('js/chunk.[id].js?v=[chunkhash:8]')
    }
  },
  dev: {
    dll: {
      fileName: './node_modules/amos-dll/common/dev/Amoslib.js', // 完整路径
      manifest: 'amos-dll/common/dev/manifest.json',
      outputPath: assetsPath('common/js'), // 生成目录
      publicPath: '/static/common/js' // 注入地址
    },
    toFile: 'index.html',
    port: 9000,
    assetsRoot: distPath,
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    staticPath: '/common',
    proxyTable: {},
    // CSS Sourcemaps off by default because relative paths are "buggy"
    // with this option, according to the CSS-Loader README
    // (https://github.com/webpack/css-loader#sourcemaps)
    // In our experience, they generally work as expected,
    // just be aware of this issue when enabling this option.
    cssSourceMap: false,
    output: {
      path: distPath,
      filename: assetsPath('js/[name].js'),
      chunkFilename: assetsPath('js/chunk.[id].js')
    }
  },
  test: {
    dll: {
      fileName: './node_modules/amos-dll/common/prod/Amoslib.js', // 完整路径
      // fileName: require.resolve('amos-dll/common/prod/Amoslib.js'),
      manifest: 'amos-dll/common/prod/manifest.json',
      outputPath: assetsPath('common/js'), // 生成目录
      publicPath: '/static/common/js' // 注入地址
    },
    index: path.resolve(distPath, 'index.html'),
    assetsRoot: distPath,
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    productionSourceMap: true,
    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ['js', 'css']
  }
};
```

> webpack.config.dll.prod.js (根目录)

```js
const dllConfig = require('amos-build/lib/dll/prod');

const config = require('./build/amosbuildConf');

module.exports = dllConfig(config);
```

## react-native to web

### useage

1. install

安装 `react-native-web`

```bash
$ npm install --save-dev react-native-web
```

2. setup webpack.config.js

```js
// webpack.config.js

const rn2webConfig = require('amos-build/lib/reactNativeToWeb');

module.exports = rn2webConfig();

// custom options
const options = {
  port: 3003,
  alias: alias,
  sourceMap: true
};

module.exports = rn2webConfig(options);

```

3. add index.html

```html
<!DOCTYPE html>
<html lang="en">

<head>
  <title>ilex.h</title>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
  <div id="react-root"></div>
  <script src="/dist/index.web.js"></script>
</body>

</html>
```

## 配置第三方模块按需加载 babel 加载时

since@3.1.2 版本及之后版本支持。仅支持：`umdConfig、simpleConfig、cjsLibConfig` 三种模式

```js
const afModuleMapping = require('amos-framework/lib/moduleMapping');
const adModuleMapping = require('amos-antd/lib/moduleMapping');

const config = {
  entry,
  outputPath: resolveApp('docsite'),
  filename: '[name].js',
  library: 'BabelDemo',
  libraryTarget: 'umd',
  // 配置自定义 babel 插件
  babelPlugin: [
    [
      require.resolve('ray-pack-toolkit/lib/babel/SplitImportPlugin'),
      {
        libraryName: ['amos-framework', 'amos-antd'],
        customMapping: {
          // 特殊处理
          'amos-framework': afModuleMapping,
          'amos-antd': adModuleMapping
        }
      }
    ]
  ],
  sourceMap: false
};
```

## 注意事项

`simpleConfig/docitConfig/polyfillConfig` 等，均设置 `publicPath` 为服务器根路径，如果需要使用相对路径，或其他路径，可以自定义 output 或者，删掉改属性。主要表现在，webpack 打包时，注入的文件路径。

```html
<script type="text/javascript" src="/common.js">
```

如果去掉 `publicPath`,则最终的 html 如下：

```html
<script type="text/javascript" src="common.js">
```

实现方式：

```js
var docitConfig = require('amos-build/lib/docitConfig');

// ... 配置信息

var defaultConfig = docitConfig(config);

// 删除 publicPath
delete defaultConfig.output.publicPath;

// 修改 publicPath
defaultConfig.output.publicPath = '.'; // <script type="text/javascript" src="./common.js">
```

## 常见错误剖析

### 在开发模式下，开启split，部分chunk file hash值并未改变，但是内容却变了

剖析：该问题，是由webpack进行模块分割时，经常会出现的bug

解决：

```js
const webpack = require('webpack');

// 方式一：将系统中默认采用 id 命名方式，改为 NamedModulesPlugin
defaultConfig.plugins.push(
  // new webpack.HashedModuleIdsPlugin();
  // 非必须
  // new webpack.NamedModulesPlugin();
  new webpack.NamedChunksPlugin((chunk) => {
    if (chunk.name) {
      return chunk.name;
    }
    return chunk.mapModules(m => m.id).join('_');
  }),
);
// 方式二：采用 hash 方式, 不推荐，因为每次都会更改 hash 值。原始采用的是 chunkhash
defaultConfig.output.filename = '[name].[hash].js';
defaultConfig.output.chunkFilename = 'chunk/[name].[hash].js';

```

在 3.0.12 及其之后版本，新增 `useHash` 来默认启用 hash 的方式创建文件名,仅在 `production` 模式下起效。

```js
const simpleConfig = require('amos-build/lib/simpleConfig');

const config = {
  ...
  useHash: true
};
const defaultConfig = simpleConfig(config);
```

### externals 说明

#### use a regex in the Webpack config

Webpack allows inserting [regex](https://webpack.js.org/configuration/externals/#regex) in the *externals* array, to capture non-relative modules:

```js
{
  externals: [
    // Every non-relative module is external
    // abc -> require("abc")
    /^[a-z\-0-9]+$/
  ]
}
// 自定义
{
  externals: [
    function(context, request, callback) {
      if (/^(amos-|ray-)/i.test(request)){
        // return callback(null, 'umd ' + request);
        return callback(null, 'commonjs ' + request);
      }
      callback();
    },
    /^(react|react-dom|prop-types|dt2react|classnames|moment)$/i,
    /^(amos-|ray-)/i,
  ]
}
```

> 这种方式，会强制将所有的 **non-relative requires** 剥离，如果使用了 `alias` 配置，将不会进行 `bundled`。

采用 `NodeMoudleExternals` 实现仅仅剥离 `node_modules` 中的模块。

## webpack 5.x 之后版本

手动添加 node 依赖支持:

`mqtt-packet/writeToStream.js` 依赖 `safe-buffer 和 process-nextick-args`，将 `buffer 和 process` 设置为 amos-mqtt 的强依赖。

* mqtt (readable-stream)
* `jszip/lib/readable-stream-browser.js` (stream)

* Buffer: `buffer`  mqtt `amos-mqtt` 中将其声明为强依赖
* process: `process/browser`   mqtt `amos-mqtt` 中将其声明为强依赖
* stream: `stream-browserify`  jszip `amos-viz` 中将其声明为强依赖

> `v5.0.3` 及之前版本，多次通过 `@import` 引入重复的 css 或者 scss 将会导致最终打包生成多份样式。
> `v5.0.4` 解决重复引入样式问题，及工程中无 `.stylelintrc.json` 文件问题。

## 新增 zipConfig

since 5.0.3 版本之后，新增 zipConfig。

支持 `simpleConfig、umdConfig、docitConfig、cjsLibConfig、themeConfig、MFConfig`

```js
const config = {
  zipConfig: {
    // 复制
    copy: [
      { source: 'dist/**', destination: 'released' },
      { source: 'amos.config.js', destination: 'released/amos.config.js' },
      { source: 'src/assets/**', destination: 'released/src/assets' }
    ],
    // 删除文件夹或者文件
    delete: ['zip/**'],
    // 创建文件夹
    mkdir: ['zip/'],
    // 压缩
    // options 配置 https://www.archiverjs.com/archiver
    archive: [
      { source: 'released/**', destination: 'zip/released.zip' },
      { source: 'amos.config.js', destination: 'zip/conf.zip' },
      { source: 'dist/robot.txt', destination: 'zip/robot.zip' },
      { source: 'dist/**', destination: 'zip/dist.zip', format: 'tar' },
      // 开启gzip
      { source: 'dist/**', destination: 'zip/dist.zip', format: 'tar', options: {
        gzip: true,
        gzipOptions: {
          level: 1,
        },
        globOptions: {
          nomount: true,
        }
      } }
    ]
  }
};
```

案例：

组态工程中，将sdkroot 目录进行打包，同时将 `scripts.json` 文件独立拷贝

> 注意，打包之前，需要手动先删除 `external/morphic/` 目录，可直接在组态 npm scripts 中新增 `"cleanSdk": "rimraf sdkroot external/morphic"`

```js
const config = {
  ...
  zipConfig: {
    copy: [
      { source: 'sdkroot/scripts.json', destination: 'external/morphic/scripts.json' }
    ],
    archive: [
      { source: 'sdkroot', destination: 'external/morphic/sdk.zip' }
    ]
  }
  ...
};
```

## 新增 gzip

`since v5.1.1` 版本之后，新增 `gzip` 配置项，可以在打包时开启 gzip 特性。

`gzip` 也可以直接使用 `nginx` 中的 `gzip` 特性，无需打包处理。

```js
const config = {
  ...
  gzip: true // 简单使用，采用默认 `test: /\.js(\?.*)?$/i`
  gzip: {
    test: /\.js(\?.*)?$/i,
    include: /\/includes/,
    exclude: /\/excludes/,
    algorithm: "gzip",
    compressionOptions: {
      level: 9
    },
    threshold: 0,
    minRatio: 0.8,
    filename: '[path][base].gz',
    deleteOriginalAssets: false
  }

};
```

nginx 开启 gzip

```conf
#开启和关闭gzip模式
gzip on|off;
#gizp压缩起点，文件大于1k才进行压缩
gzip_min_length 1k;
# gzip 压缩级别，1-9，数字越大压缩的越好，也越占用CPU时间
gzip_comp_level 5;
# 进行压缩的文件类型。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript;
#nginx对于静态文件的处理模块，开启后会寻找以.gz结尾的文件，直接返回，不会占用cpu进行压缩，如果找不到则不进行压缩
gzip_static on|off
# 是否在http header中添加Vary: Accept-Encoding，建议开启
gzip_vary on;
# 设置压缩所需要的缓冲区大小，以4k为单位，如果文件为7k则申请2*4k的缓冲区
gzip_buffers 2 4k;
# 设置gzip压缩针对的HTTP协议版本
gzip_http_version 1.1;
```

[compression-webpack-plugin 文档](https://webpack.docschina.org/plugins/compression-webpack-plugin/)

## MFConfig

采用 ModuleFederation 模式，创建 config 配置脚本。MfConfig 是 simpleConfig 的产物扩展功能而来

新增配置参数: mfConfig、mfMode

```js
const MFConfig = require('amos-build/lib/MFConfig');
const { getReactMf, getShared } = require('amos-build/lib/tools/pluginConfig');
const { dependencies: deps, devDependencies: devps } = require('./package.json');

// both 模式
const mfOption = {
  name: 'amosasp',
  // 默认 library，可以不传入
  library: { type: 'var', name: 'amosasp' },
  filename: 'remoteEntry.js',
  remotes: __ENV__ === 'production' ? mfInfo.remotes.prod : mfInfo.remotes.dev,
  exposes: {
    './amos-xxx': './src/xxx.js'
  },
  shared: {
    ...getReactMf(deps.react, deps['react-dom'])
  }
};

// host 模式
const mfOption = {
  remotes: __ENV__ === 'production' ? mfInfo.remotes.prod : mfInfo.remotes.dev,
  shared: {
    ...getReactMf(deps.react, deps['react-dom'])
  }
};

// remote 模式
const mfOption = {
  name: 'amosaf',
  // 默认 library，可以不传入
  library: { type: 'var', name: 'amosaf' },
  filename: 'remoteEntry.js',
  exposes: {
  './amos-xxx': './src/xxx.js'
  },
  shared: {
    ...getReactMf(devps.react, devps['react-dom']),
    ...getShared([
    //  'amos-tool', 'crispfit-ui', 'domtool', 'dt2react'
      'amos-tool', 'dt2react'
    ], deps)
  }
};

const config = {
  ...
  mfConfig: mfOption,
  mfMode: 'both', // remote、host
  ...,
  replacer: {
    pkgs: [],
    rules: [
      { src: 'amos-xxx', dest: 'amosaf/amos-xxx' },
    ]
  }
};

// 搭配 replacer 进行使用，可以编译时替换目录
```

`umdConfig、cjsLibConfig` 想直接使用 remotes 模块，采用如下方式即可:

```js
const umdConfig = require('amos-build/lib/umdConfig');
const { getReactMf, getShared, invokeMFRemotes } = require('amos-build/lib/tools/pluginConfig');
const { dependencies: deps, devDependencies: devps } = require('./package.json');

// host 模式
const mfOption = {
  remotes: __ENV__ === 'production' ? mfInfo.remotes.prod : mfInfo.remotes.dev,
  shared: {
    ...getReactMf(deps.react, deps['react-dom'])
  }
};

const config = {
  ...,
  replacer: {
    pkgs: [],
    rules: [
      { src: 'amos-xxx', dest: 'amosaf/amos-xxx' },
    ]
  }
};

const defaultConfig = umdConfig(config);

invokeMFRemotes(defaultConfig, mfOption);

module.exports = defaultConfig;
```

### 教程系列

#### 项目中使用 .umdrc.js 配置文件

`.umdrc.js` 位于项目根目录

```js
const { commonExternals } = require('amos-build/lib/tools/pluginConfig');

// 通过 commonExternals 快速创建 umdConfig
const umdConfig = commonExternals([
  'react', 'react-dom', 'amos-react-router', 'prop-types', 'amos-antd', 'amos-framework', 'xgplayer', 'xgplayer-flv', 'xgplayer-hls', 'amos-designer',

  // studio asp remote 需要
  'base-r3d', 'amos-gojs'
]);

module.exports = {
  // dir 和 list 用于从 pkg 中拷贝 mf 包
  dir: 'website/umd_lib',
  list: [
    ...umdConfig.list,

    // codemirror、ace
    { pkg: 'amos-code-actuator', umd: 'dist' },
    // tinymce quill
    { pkg: 'amos-richtext', umd: 'umd' },
    // echarts all
    { pkg: 'amos-viz', umd: 'umd' }
  ],

  externals: {
    ...umdConfig.externals,

    'amos-code-actuator/lib/codemirror/umd': 'ActuatorCM',
    // ace editor
    'amos-code-actuator/lib/ace/umd': 'ActuatorACE',
    'amos-viz/lib/echarts/umd': 'VizEcharts',
    // rich text
    'amos-richtext/lib/quill/umd': 'RichTextQuill',
    'amos-richtext/lib/tinymce/umd': 'RichTextMCE'
  },

  scripts: {
    // 注意，数组中，靠前的后加载
    head: [
      ...umdConfig.scripts.head
    ],
    body: [
      ...umdConfig.scripts.body,

      "amos-code-actuator/codemirror.umd.js",
      "amos-code-actuator/ace.umd.js",
      "amos-viz/viz-echarts.umd.js",
      "amos-richtext/tinymce.umd.js",
      "amos-richtext/quill.umd.js",
    ]
  },
  // link 样式可以, 均放在 head
  links: [
    ...umdConfig.links
  ]
};
```

根目录的 `webpack.config.js`

`umd_lib` 内容可采取线上模块，也可采用 `amos-plugin --umd --umd-r` 命令动态生成

```js
/**
 * webpack 打包配置文件
 * @author ilex
 */
const MFConfig = require('amos-build/lib/MFConfig');
const { getRCConfigInfo, getOuterScripts, getDynamicTags } = require('amos-build/lib/tools/pluginConfig');
const HtmlInlineCodePlugin = require('html-inline-code-plugin');

const umdRc = getRCConfigInfo('umd', '');

const defaultConfig = MFConfig({ ... });

// 设置 外部脚本配置
defaultConfig.externals = umdRc.externals;

// prefix 可以填写线上已发布目录，如 `http://172.16.10.72/umd_lib/`，此时无需执行相关 umd copy

// 给 html 加入 externals 脚本
const headLibs = getOuterScripts(umdRc.scripts.head, {
  inject: 'head',
  prefix: '/umd_lib/'
});

const bodyLibs = getOuterScripts(umdRc.scripts.body, {
  inject: 'body',
  prefix: '/umd_lib/'
});

const headLinks = getDynamicTags(umdRc.links, {
  inject: 'head',
  prefix: '/umd_lib/'
});

defaultConfig.plugins.push(
  new HtmlInlineCodePlugin([
    ...headLibs,
    ...bodyLibs,
    ...headLinks
  ])
);
```

#### 项目中将多个lib挂在同一个 lib name 下

```js
/**
 * webpack 打包配置文件
 * 测试 mods 分离
 * @author ilex
 */
const umdConfig = require('amos-build/lib/umdConfig');
const { getRCConfigInfo } = require('amos-build/lib/tools/pluginConfig');
const resolveCwd = require('amos-build/lib/tools/resolveCwd');
const __ENV__ = require('amos-build/lib/tools/__ENV__');

const coms = require('./test/mfcard/entry.js');

const umdRc = getRCConfigInfo('umd', '.');

function genConfig(entry, libname, analyzer) {
  const config = {
    entry,
    outputPath: resolveCwd('mfcarddist'),
    filename: '[name].js',
    //
    library: libname,
    libraryTarget: 'umd',
    libraryExport: 'default',
    // 启动 WebWorker
    enableWorker: true,
    // ignoreUgly: true,
    sourceMap: __ENV__ === 'production' ? false : true
  };

  // 分析 module 模块
  if (analyzer) {
    config.analyzer = {
      // umd 模式下，使用 static 模式
      analyzerMode: 'static'
    };
  }

  const ucg = umdConfig(config);

  ucg.name = 'amos-ag-mfcard';

  // 设置 外部脚本配置
  ucg.externals = umdRc.externals;

  return ucg;
}

const comsConfig = (Object.keys(coms) || []).reduce((prev, curr) => {

  prev.push(genConfig({
    [curr]: coms[curr]
  }, ['___$agModules', '[name]']));

  return prev;
}, []);


module.exports = [
  ...comsConfig
];

```

#### 项目中将自动扫描并添加entry

* 将当前目录下的所有 `*.js` 添加到 entry 中

entry.js 目录： `mfcard/entry.js`

```js
// 自动添加 entry，必须要保证 key 和 文件名一样才可以

const glob = require('glob');
const path = require('path');

const ignore = [
  '**/node_modules/**',
  './mfcard/_globalParams.js',
  './mfcard/entry.js',
  './mfcard/myentry.js'
];

// 使用glob模块来匹配文件
const files = glob.sync('./mfcard/*.js', { ignore });

// 创建一个entry对象
const entries = {};

// 遍历所有匹配到的文件，将它们设置为entry
files.forEach(file => {
  const f = path.parse(file);
  // const fname = path.basename(file);
  // const name = fname.replace('.js', '');

  // 直接使用 path.resolve 获取全路径
  // const allpath = path.resolve(file);
  // 如果使用 require.resolve 获取全路径，需要定位到 sync 中指定的根目录下。
  const entryPath = require.resolve(file.replace('./mfcard', '.'));
  // 全小写 (需要与 mfcard组件定义的 key 保持一致)
  entries[f.name.toLowerCase()] = entryPath;
});


module.exports = entries;
```

* 将 mfcard 目录中的所有满足条件的 `index.js` 添加到 `entry` 中

```js
const glob = require('glob');

// 使用glob模块来匹配文件
const files = glob.sync('./mfcard/**/index.js', { ignore: ['**/node_modules/**', './mfcard/__test__/**', './mfcard/index.js'] });

const reg = /^\.\/([^_][\w-]+\/)+index\.(js|jsx)?$/;

// 创建一个entry对象
const entries = {};

// 遍历所有匹配到的文件，将它们设置为 entry （必须要约定entry name 为 index.js 父级文件夹名）
files.forEach(file => {
  const match = file.match(reg);

  const name = match[1].replace('/', '');
  // 采用 `require.resolve` 处理全路径时，根据脚本运行位置，有可能需要去除部分前缀
  const fp = file.replace('./mfcard', '.');
  const entryPath = require.resolve(fp);

  // 采用 path.resolve 直接可以获取到全路径
  // const entryPath = path.resolve(file);
  entries[name] = entryPath;
});

module.exports = entries;
```

## 全局字符串替换

```js
// build 时间
_AMOS_BUILD_EXECUTE_TIME_
// 全局统一版本好
__GLOBAL_VERSION_

// 使用方式
console.log('amos build time:', _AMOS_BUILD_EXECUTE_TIME_)
window.__global_version = __GLOBAL_VERSION_;
```

## BUGS

### 启用 ModuleFederationPlugin 时，babel-loader 设置 presets 存在的bug

`Uncaught (in promise) Error: Shared module is not available for eager consumption: webpack/sharing/consume/default/react/react`

```js
[
  resolve('@babel/preset-react'),
  [
    resolve('@babel/preset-env'),
    {
      // https://babeljs.io/docs/en/babel-preset-env#modules
      // 设置为 false 将会保留 es 模块。将imports和exports让webpack去处理
      modules: 'amd', // 该值仅当设置为 `amd umd auto false` 时，才能使用 ModuleFederationPlugin
      targets: {
        browsers: envBrowsers
      }
    }
  ]
];
```

## changelog
