Giter VIP home page Giter VIP logo

build-scripts's Introduction

2.x | 1.x | 0.x

build-scripts

NPM version NPM downloads

基于 Webpack 的插件化工程构建工具,支持快速建设一套开箱即用的工程方案。

目录

特性

  • 完善灵活的插件能力,帮助扩展不同工程构建的场景
  • 提供多构建任务机制,支持同时构建多份产物
  • 标准化构建&调试的完整流程,同时提供 Hook 能力进行定制
  • 已支持多种场景:
    • React 项目开发
    • Rax 项目开发
    • NPM 包开发
    • 天马模块开发

常见问题

NPM 包名是 build-scripts 还是 @alib/build-scripts

1.x 以及未来都以 build-scripts 为准,0.x 版本当时因为命名被占用只能使用 @alib/build-scripts 这个包名。

2.x 相比 1.x 有什么变化?

参考 版本升级 章节。

何时使用 build-scripts?

多个项目共享配置以及其他工程能力,同时支持插件扩展&修改配置。

使用 build-scripts 的项目如何修改工程配置?

build-scripts 核心是提供一套完善的工程插件设计,本身不耦合任何工程配置,也不具备任何工程调试构建能力,具体的工程配置都是由插件提供的,因此此类问题建议查阅下方对应场景的文档。

使用场景

基于 build-scripts 目前已支持多种场景,覆盖大多数的研发场景,当然你可以完全自定义一套工程能力。

React 项目开发

天马模块

私有方案

NPM 包开发

自定义工程

如果不想使用上述官方提供的解决方案,也可以基于 build-scripts 自定义完整的工程能力,具体请参考 example

方案设计

image

  • build-scripts 提供核心配置、插件机制、构建生命周期等能力
  • build-scripts 本身不在耦合具体构建工具的设计,具体实现由上层工具决定

能力介绍

build-scripts 2.0 本身不耦合任何构建工具,以下能力均以官方实现的 webpack-service 为基础

webpack-service 是集合 build-scripts 和 webpack 提供的基础构建服务,能力上对标 build-scripts 1.x 版本

配置文件

build-scripts 默认将 build.json 作为工程配置文件,运行 build-scripts 命令时会默认读取当前目录的 build.json 文件。

配置方式:

{
  "externals": {
    "react": "React"
  },
  "plugins": [
    "build-plugin-component",
    "./build.plugin.js"
  ]
}

build.json 中核心包括两部分内容:

  • 基础配置:比如示例的 externals 字段,默认情况下不支持任何字段,由基础插件通过 registerUserConfig API 注册扩展
  • 插件配置:二三方插件,以及针对单个项目通过本地插件实现 webpack config 的更改

除了 json 类型以外,build-scripts 也支持 ts 类型的配置文件:

// build.plugin.ts

export default {
  plugins: [],
}

配置插件

通过 build.json 中提供的 plugins 字段可配置插件列表,插件数组项每一项代表一个插件,build-scripts 将按顺序执行插件列表,插件配置形式如下:

{
  "plugins": [
    // 数组第一项为插件名,第二项为插件参数
    ["build-plugin-fusion", {
      "themePackage": "@icedesign/theme"
    }]
  ]
}

本地自定义配置

如果基础配置和已有插件都无法支持业务需求,可以通过本地插件自定义配置来实现,新建 build.plugin.js 文件作为一个自定义插件,然后写入以下代码:

module.exports = ({ context, onGetConfig }) => {
  // 这里面可以写哪些,具体请查看插件开发章节
  onGetConfig((config) => {
  });
}

最后在 build.json 里引入自定义插件即可:

{
  "plugins": [
    "build-plugin-app",
    "./build.plugin.js"
  ]
}

插件开发

通过命令创建一个插件 npm 包:

$ npm init npm-template <pluginName> build-plugin-template
$ cd <pluginName>

插件本质上是一个 Node.js 模块,入口如下:

module.exports = ({ context, onGetConfig, onHook, ...rest }, options) => {
  // 第一项参数为插件 API 提供的能力
  // options:插件自定义参数
};

插件方法会收到两个参数,第一个参数是插件提供的 API 接口和能力,推荐解构方式按需使用 API,第二个参数 options 是插件自定义的参数,由插件开发者决定提供哪些选项给用户自定义。

插件 API

插件可以方便扩展和自定义工程能力,这一切都基于 build-scripts 提供的插件 API。

context

context 参数包含运行时的各种环境信息:

  • command 当前运行命令 start|build|test
  • commandArgs script 命令执行时接受到的参数
  • rootDir 项目根目录
  • originalUserConfig 用户在 build.json 中配置的原始内容
  • userConfig 用户配置,包含被 modifyUserConfig 修改后的结果
  • pkg 项目 package.json 的内容
module.exports = ({ context }) => {
  const { userConfig, command, webpack } = context;
  console.log('userConfig', userConfig);
  console.log('command', command);
};

onGetConfig

通过 onGetConfig 获取通过 registerTask 注册的配置,并对配置进行自定义修改:

// 场景一:修改所有 webpack 配置
module.exports = ({ onGetWebpackConfig }) => {
  onGetWebpackConfig((config) => {
    config.entry('src/index');
  });
}

// 场景二:多 webpack 任务情况下,修改指定任务配置
module.exports = ({onGetConfig, registerTask}) => {
  registerTask('web', webpackConfigWeb);
  registerTask('weex', webpackConfigWeex);

  onGetConfig('web'(config) => {
    config.entry('src/index');
  });

  onGetConfig('weex'(config) => {
    config.entry('src/app');
  });
}

onHook

通过 onHook 监听命令运行时事件,onHook 注册的函数执行完成后才会执行后续操作,可以用于在命令运行中途插入插件想做的操作:

module.exports = ({ onHook }) => {
  onHook('before.start.load', () => {
    // do something before dev
  });
  onHook('after.build.compile', stats => {
    // do something after build
  });
};

目前的命令执行生命周期如下:

start 命令:

生命周期 参数 调用时机
before.start.load { args: CommandArgs; webpackConfig: WebpackConfig[] } 获取 webpack 配置之前
before.start.run { args: CommandArgs; webpackConfig: WebpackConfig[] } webpack 执行构建之前
after.start.compile { url: string; stats: WebpackAssets; isFirstCompile: boolean } 编译结束,每次重新编译都会执行
before.start.devServer { url: string; devServer: WebpackDevServer } server 中间件加载后,webpack devServer 启动前
after.start.devServer { url: string; devServer: WebpackDevServer } webpack devServer 启动后

build 命令:

生命周期 参数 调用时机
before.build.load { args: CommandArgs; webpackConfig: WebpackConfig[] } 获取 webpack 配置之前
before.build.run { args: CommandArgs; webpackConfig: WebpackConfig[] } webpack 执行构建之前
after.build.compile { url: string; stats: WebpackAssets; isFirstCompile } 编译结束

test 命令:

生命周期 参数 调用时机
before.test.load { args: CommandArgs; webpackConfig: WebpackConfig[] } 获取 jest 配置之前
before.test.run { args: CommandArgs; config: JestConfig } jest 执行构建之前
after.test { result: JestResult } 测试结束

registerUserConfig

通过 registerUserConfig 注册 build.json 中的顶层配置字段,注册是可以进行用户字段校验,支持传入单个配置对象或者包含多个配置对象的数组。

方法生效的生命周期,在 registerTask 和 onGetConfig 之间。

配置对象字段如下:

  • name (string)

字段名称,唯一标识,多个插件无法注册相同的字段 保留字段:plugins

  • validation(string|function)

字段校验,支持 string 快速校验,string|boolean|number,也可以自定义函数,根据 return 值判断校验结果

  • ignoreTasks(string[])

配置忽略指定 webpack 任务

  • setConfig(function)

字段效果,具体作用到 webpack 配置上,接收参数:

  • config:通过 registerTask 注册的配置
  • value: build.json 中的字段值
  • context:与外部 context 相同,新增字段 taskName 表现当前正在修改的 task
module.exports = ({ registerUserConfig }) => {
  registerUserConfig({
    name: 'entry',
    // validation: 'string',
    validation: value => {
      return typeof value === 'string';
    },
    config: (config, value, context) => {
      config.mode(value);
    },
  });
};

registerTask

用于注册多 webpack 任务,比如 build-plugin-react-app 上已完整支持 React 链路开发,大部分情况下在默认 webpack 任务上拓展即可,无需额外注册.

// 注册的 config 必须是以 webpack-chain 形式组织
module.exports = ({ registerTask }) => {
  registerTask('web', webpackConfigWeb);
  registerTask('component', webpackConfigComponent);
};

cancelTask

用于取消已注册任务

module.exports = ({ cancelTask }) => {
  cancelTask('web');
};

hasRegistration

判断 build.json 中的顶层配置字段或者 cli 参数是否已经注册:

module.exports = ({ hasRegistration }) => {
  // 判断 build.json 顶层配置字段 entry 是否已配置
  const hasEntryRegistered = hasRegistration('entry');

  // 判断 cli --https 参数是否已被注册
  const hasHttpsRegistered = hasRegistration('https' 'cliOption');
  ...
}

modifyConfigRegistration

用于修改已注册用户配置的行为:

module.exports = ({ modifyConfigRegistration }) => {
  modifyConfigRegistration('name', configRegistration => {
    return {
      ...configRegistration,
      // 修正验证字段
      validation: 'string',
    };
  });
};

modifyUserConfig

通过 modifyUserConfig 可以修改通过 registerUserConfig 注册的基础配置,在插件中快速复用基础配置的处理逻辑:

module.exports = ({ modifyUserConfig }) => {
  modifyUserConfig(originConfig => {
    // 通过函数返回批量修改
    return { ...originConfig, define: { target: 'xxxx' } };
  });
};

通过指定具体修改的基础配置,快速完成配置的修改:

module.exports = ({ modifyUserConfig }) => {
  modifyUserConfig('entry', 'src/app');

  // 通过对象路径修改,比如修改对象 { outputAssetsPath: { js: 'js-dist'} } 可通过以下方式
  modifyUserConfig('outputAssetsPath.js', 'js');

  // 支持深合并,默认情况下 modifyUserConfig 将覆盖原有配置,通过配置参数支持配置的合并
  modifyUserConfig('outputAssetsPath', {
    js: 'js-output'
  }, { deepmerge: true });
};

API 执行的生命周期:所有插件对于修改配置函数将保存至 modifyConfigRegistration 中,在 runUserConfig 执行前完成对当前 userConfig 内容的修改

registerCliOption

注册各命令上支持的 cli 参数,比如 npm start --https 来开启 https:

module.exports = ({ registerCliOption }) => {
  registerCliOption({
    name: 'https', // 注册的 cli 参数名称,
    commands: ['start'], // 支持的命令,如果为空默认任何命令都将执行注册方法
    config: (config, value, context) => {
      // 对应命令链路上的需要执行的相关操作
    },
  });
};

注册函数执行周期,在 userConfig 相关注册函数执行之后。

modifyCliRegistration

用于修改已注册 cli 配置的行为:

module.exports = ({ modifyConfigRegistration }) => {
  modifyCliRegistration('https', cliRegistration => {
    return {
      ...cliRegistration,
      // 修正 commands 字段
      commands: ['start'],
    };
  });
};

getAllTask

用于获取所有注入任务的名称:

module.exports = ({ getAllTask }) => {
  const taskNames = getAllTask();
  // ['web', 'miniapp']
};

插件间通信

在一些业务场景下,插件间需要进行通信:

  1. 不同插件之间需要知道彼此的存在来确定是否执行相应的逻辑
  2. 多个插件共有的配置信息可以抽出来,在某个插件中进行配置
  3. 上层插件的执行,需要依赖基础插件提供的方法

基于上述的诉求,API 层面提供 setValuegetValue 来用于数据的存取,registerMethodapplyMethod 来解决方法的复用。

setValue

用来在 context 中注册变量,以供插件之间的通信。

module.exports = ({ setValue }) => {
  setValue('key', 123);
};

getValue

用来获取 context 中注册的变量。

module.exports = ({ getValue }) => {
  const value = getValue('key'); // 123
};

registerMethod

向工程核心注册相关方法,方便其他插件进行复用:

module.exports = ({ registerMethod }) => {
  // 注册方法
  registerMethod('pipeAppRouterBefore', content => {
    // 执行相关注册逻辑,可以返回相应的值
    return true;
  });
};

registerMethod 注册方式时,通过参数指定可以获取调用该方法的具体插件名:

module.exports = ({ registerMethod }) => {
  // 注册方法
  registerMethod('pipeAppRouterBefore', (pluginName) => (content) => {
    console.log('plugin name', pluginName);
    console.log('content', content);
    // 执行相关注册逻辑,可以返回相应的值
    return true;
  }, { pluginName: true });
};

applyMethod

调用其他插件的注册方法

module.exports = ({ applyMethod }) => {
  // 使用其他差价注册方法的方式,如果插件未注册,将返回一个 error 类型的错误
  // 类似 new Error(`apply unkown method ${name}`)
  const result = applyMethod('pipeAppRouterBefore', 'content');
};

版本升级

1.x -> 2.x

2.x 的核心变化:

  • 与 webpack 整体解耦,沉淀为插件开发服务
  • 修改与 webpack 耦合的相关 API

具体的 API 变化如下:

onGetWebpackConfig 移除

onGetWebpackConfig 变更为 onGetConfig,使用方式保持与之前不变,对于调用 onGetConfig 获取的 config 配置内容由具体的框架决定。 比如在 ICE PKG 下使用 API onGetConfig 获取的配置内容为基于 rollup 配置抽象的配置对象

registerTask 变化

registerTask 原先要求注册的任务配置必须是 webpack-chain 形式的配置,基于 build-scripts 2.0,其注册的内容由上层框架决定。 比如在 ICE PKG 下,任务配置为一个对象,详解具体配置项

registerUserConfig 变化

registerUserConfig 的参数 configWebpack 变更为 setConfigsetConfig 具体配置由上层框架决定:

module.exports = ({ registerUserConfig }) => {
  registerUserConfig({
    name: 'custom-key',
    validation: 'boolean' // 可选,支持类型有 string, number, array, object, boolean
    setConfig: (config) => {
      // config 内容由具体框架决定
    },
  });
};

registerCliOption 变化

registerCliOption 变化同 registerUserConfig

module.exports = ({ registerCliOption }) => {
  registerCliOption({
    name: 'custom-options',
    setConfig: (config) => {
      // config 内容由具体框架决定
    },
  });
};

0.x -> 1.x

1.x 核心变化:

  • 包名由 @alib/build-scripts 切换为 build-scripts
  • 不再依赖 webpack&jest&webpack-dev-server,建议由基础插件或项目自身依赖
  • 插件上下文 context 增加 originalUserConfig 字段,用于读取用户原始配置
  • userConfig 类型校验增强,支持 string | object | array 校验

除了前两点属于不兼容改动,其他能力都保持向前兼容。

自定义工程

在 package.json 中增加依赖:

{
  "devDependencies": {
+    "jest": "^26.4.2",
+    "webpack": "^4.27.1",
+    "webpack-dev-server": "^4.0.0",
-    "@alib/build-scripts": "^0.1.0",
+    "build-scripts": "^1.0.0",
  }
}

其中 jest 可按需判断是否需要安装,webpack 版本按需选择。修改完成后重装依赖然后重启即可。

build-scripts 暂时不支持直接从 1.x 升级为 2.x,2.x 的升级必须搭配上层 service 实现,比如 build-scripts 1.x + build-plugin-component 的组件开发模式将会由 ICE PKG 支持,ICE PKG 即是一个基于 build-scripts 2.x 实现的包开发方案

React 项目(icejs)

升级 icejs 2.0 即可。

Rax 项目(rax-app)

rax-app 3.8.0 以上已升级。

业务组件(build-plugin-component)

在 package.json 中升级依赖:

{
  "devDependencies": {
-    "@alib/build-scripts": "^0.1.0",
+    "build-scripts": "^1.0.0",
-    "build-plugin-component": "^1.0.0",
+    "build-plugin-component": "^1.6.5",
  }
}

build-plugin-component 从 1.6.5 开始同时兼容 build-scripts 0.x 和 1.x 两个版本

天马模块(@ali/build-plugin-pegasus-base)

待支持

License

MIT

build-scripts's People

Contributors

chenks12138 avatar clarkxia avatar guoyunhe avatar imsobear avatar luhc228 avatar maoxiaoke avatar npmmirror avatar screetbloom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

build-scripts's Issues

webpack 配置修改能力增强

webpack-chain 的部分能力受限,是否考虑开放部分直接修改 webpack config 的能力?

遇到几个 case 无法支持:

  1. 接入这个插件 speed-measure-webpack-plugin
  2. 没有 copy config 的能力
  3. webpack-chain 无法支持 use function 的形式,比如
{
  rules: [{
    test: /\.js$/,
    use: (options) => {
      return [
        'babel-loader',
      ]
    }
  }]
}

修改 React-单组件 后没有自动更新 meta.ts

  • **系统: ** macOS 12.6
  • **Node.js: ** v14.19.1

依次执行

  • npm init @alilc/element lowcode-material-demo 创建 React-单组件 物料项目
  • yarn install
  • yarn lowcode:dev

src/index.ts

import * as React from 'react';
import { forwardRef, ForwardRefRenderFunction } from 'react';

interface ComponentProps {
  title: string;
  content: string;
  hhhh: string;
}

const ExampleComponent = (props: ComponentProps, ref: any) => {
  const { title, content, ...others } = props;

  return (
    <div ref={ref} className="ExampleComponent" {...others}>
      <h1>{title}</h1>
      <p>test test</p>
      <p>{others.hhhh}</p>
      {content || 'Hello ExampleComponent'}
    </div>
  );
};

const RefExampleComponent = forwardRef(ExampleComponent as ForwardRefRenderFunction<any, ComponentProps>);
RefExampleComponent.displayName = 'ExampleComponent';

export default RefExampleComponent;

meta.ts

import { ComponentMetadata, Snippet } from '@alilc/lowcode-types';

const LowcodeMaterialDemoMeta: ComponentMetadata = {
  "componentName": "LowcodeMaterialDemo",
  "title": "LowcodeMaterialDemo",
  "docUrl": "",
  "screenshot": "",
  "devMode": "proCode",
  "npm": {
    "package": "lowcode-material-demo",
    "version": "0.1.0",
    "exportName": "default",
    "main": "src/index.tsx",
    "destructuring": false,
    "subName": ""
  },
  "configure": {
    "props": [
      {
        "title": {
          "label": {
            "type": "i18n",
            "en-US": "title",
            "zh-CN": "title"
          }
        },
        "name": "title",
        "setter": {
          "componentName": "StringSetter",
          "isRequired": true,
          "initialValue": ""
        }
      },
      {
        "title": {
          "label": {
            "type": "i18n",
            "en-US": "content",
            "zh-CN": "content"
          }
        },
        "name": "content",
        "setter": {
          "componentName": "StringSetter",
          "isRequired": true,
          "initialValue": ""
        }
      },
      {
        "title": {
          "label": {
            "type": "i18n",
            "en-US": "hhhh",
            "zh-CN": "hhhh"
          }
        },
        "name": "hhhh",
        "setter": {
          "componentName": "StringSetter",
          "isRequired": true,
          "initialValue": ""
        }
      },
      {
        "title": {
          "label": {
            "type": "i18n",
            "en-US": "cbb",
            "zh-CN": "cbb"
          }
        },
        "name": "cbb",
        "setter": {
          "componentName": "StringSetter",
          "isRequired": true,
          "initialValue": ""
        }
      },
      {
        "title": {
          "label": {
            "type": "i18n",
            "en-US": "ref",
            "zh-CN": "ref"
          }
        },
        "name": "ref",
        "setter": {
          "componentName": "MixedSetter",
          "props": {
            "setters": [
              {
                "componentName": "FunctionSetter"
              },
              {
                "componentName": "ObjectSetter",
                "props": {
                  "config": {
                    "extraSetter": {
                      "componentName": "MixedSetter",
                      "isRequired": false,
                      "props": {}
                    }
                  }
                },
                "isRequired": false,
                "initialValue": {}
              }
            ]
          }
        }
      },
      {
        "title": {
          "label": {
            "type": "i18n",
            "en-US": "key",
            "zh-CN": "key"
          }
        },
        "name": "key",
        "setter": {
          "componentName": "MixedSetter",
          "props": {
            "setters": [
              {
                "componentName": "StringSetter",
                "isRequired": false,
                "initialValue": ""
              },
              {
                "componentName": "NumberSetter",
                "isRequired": false,
                "initialValue": 0
              }
            ]
          }
        }
      }
    ],
    "supports": {
      "style": true
    },
    "component": {}
  }
};
const snippets: Snippet[] = [
  {
    "title": "LowcodeMaterialDemo",
    "screenshot": "",
    "schema": {
      "componentName": "LowcodeMaterialDemo",
      "props": {}
    }
  }
];

export default {
  ...LowcodeMaterialDemoMeta,
  snippets
};

meta.ts 中的内容是因为没有自动更新,然后手动修改的

image

test命令支持 node 调试

目前针对测试用例的代码调试,只能通过 console 等手段来进行,希望 test 命令支持跟 chrome 或者 ide 结合的断点调试能力

build-scripts 2.x 的配置解析策略

背景

目前支持的配置文件有:

  • build.json
  • build.config.js
  • build.config.ts
  • build.config.mjs
  • build.config.mts

问题:

  1. 解析顺序问题
  2. 当前,所有问题都会转换 .mjs 进行代码解析,会导致配置代码无法使用 cjs 语法(比如 config.ts 中使用 require 语法)

方案一

区分仓库为 es module (使用 import 加载)和 commonjs (使用 require 加载)。以下会识别为 es module 仓库:

  • .mjs.mts 结尾的配置文件
  • 配置 type: module.js.ts 的文件

方案二

方案一存在的问题在于社区仍存在大量生态不支持项目配置 type: module,现阶段下,在非 type: module 的仓库中无法使用 import 语法。因此方案二不以 type: module 来区分 es module。策略如下:

  1. .cjs.cts 结尾的文件以 commonjs 方式加载
  2. 其他均以 es module 方式加载

[RFC] build-scripts 2.0 版本设计

背景

  • build-scripts 与 webpack 体系解耦,沉淀为底层插件开发服务。

方案

build-scripts 分层设计

image

Service 规范

Service 确定如何实现诸如 startbuild 的命令,可以是由函数或类实现,比如(以 webpack 为例):

import { Service } from 'build-scripts';
import start from './start';
import build from './build';
import WebpackChain from 'webpack-chain';
import webpack from 'webpack';

const webpackService = new Service<WebpackChain, Record<'webpack', typeof webpack>>({
	name: 'webpack',
	command: {
		start,
		build
	},
	bundlers: {
		wepback
	}
})

export default webpackService;

// start.ts 实现 npm run start
// build.ts 实现 npm run build
// 参考实现如下
export default start = (context: Context<WebpackChain>) => {
	const { getConfig, applyHook } = context;
	const configArr = context.getConfig();

	await applyHook(`before.${command}.load`, { xxx })

	try {
		compiler = webpackInstance(webpackConfig);
	} catch (e) {
		await applyHook('error', { err });
	}

	compiler.run((err, stats) => {
		// 
	})

	await applyHook(`after.${command}.compile`, result)
}

Service API

Service 可消费 Context 的所有 public API:

interface Context {
	command: 'build' | 'start' | 'test',
	commandArgs: object;
	rootDir: string;
	pkg: Json;
	applyHook: (key: string, opts?: {}) => Promise<void>;
	logger: Logger;
	...
}

插件 API

插件 API 与现有插件 api 基本保持一致(除部分存在命名上变更)。主要变更有:

  • onGetWebpackConfig → 变更为 onGetConfig,使用方式保持与之前不变
// 入参 config 与 registerTask 配置 对应
interface onGetConfig<T> {
	(name: string, fn: (config: T)): void;
	(fn: (config: T)): void;
}
  • registerTask 入参变更
    • 对于 webpack,注册的是 wepback-chain
    • 对于 vite,注册的是 vite config (目前对于 ice 来说,通过单一插件支持,因此注册的仍是 webpack-chian,通过一层 webpack2vite 进行了磨平)
    • 对于 rollup,注册的是 rollup 配置
  • ctx 不再默认导出 webpack

之前存在从 ctx 下导出 wepback 的场景,目的是保证使用的是统一 webpack 实例。新模式下,需要 Service 注入给 Plugin 消费。

const ctx = createContext({
	command: 'start',
	rootDir: this.options.rootDir,
	bundlers: {
		webpack
	}
})
  • configWebpack 字段修改为 setConfig,入参为 Task Config。

注册 Task

Task 通过插件注册,注册方式如下:

const plugin = ({
	registerTask
}) => {
	// 以 webpack 为例,注册 webpack-chain 配置
	registerTask('taskName', webpackConfig);
}

具体改动

  • Context API 变更

    • 移除 publicAPI registerCommandModules
    • 移除 publicAPI getCommandModule
    • 移除 pulicAPI getWebpackConfig
    • 移除 privateAPI getProjectFile - 移进 utils 文件夹,修改名为 loadPkg
    • 修改 onHook 方法为 privateAPI
  • 依赖包升级

  • Service 可消费的 api

    • command
    • commandArgs
    • rootDir
    • pkg
    • userConfig

[RFC] build-scripts 移除对 webpack 的依赖

背景

build-scripts 内置依赖 webpack 4.x,让具体的项目依赖中无需关心 webpack 版本。
而随着 webpack 5 的发布,越来越多工程配置开始希望使用 webpack 5 的新能力,目前在 build-scripts 工程体系下需要进行如下的配置开启 webpack 5 的使用:

  1. 项目 package.json 中添加 webpack@5 的依赖
  2. build.json 工程配置中,通过开启 "customWebpack": true 的方式指定使用项目依赖中的 webpack

上述的配置方式相对比较繁琐,对于开发者并不友好,希望能够移除底层对于 webpack 的依赖。

实现方案

webpack 从 dependencies 中移除,具体的 webpack 版本 由具体的基础插件对 webpack 版本进行管控

plugin 类型统一

在 build-scripts 2.0 设计中支持的 plugin 设置包括以下两种:

  • PluginOption { name, plugin, runtime}
  • Plugin

PluginOption 的类型名称定位上不够清晰,跟其内部的 plugin 关系有重叠

备选方案:

统一导出 plugins 配置类型: type Plugin = PluginOption | PluginLegacy | [string, object]
PluginOption 包含 { name, setup, runtime } / { name, config, runtime }

添加插件报错

const MomentLocalesPlugin = require('@ant-design/moment-webpack-plugin');
module.exports = ({ context, onGetWebpackConfig }) => {
  // 这里面可以写哪些,具体请查看插件开发章节
  onGetWebpackConfig((config) => {
    config.plugin('moment').use(MomentLocalesPlugin);
  });
}

抛出:
Screenshot 2023-05-08 at 11 06 18

如何生成type文件

如题。以及生成type文件的位置如何配置。
看了下readme中并没有对应的说明,还是只能自己配置webpack?

build命令保留旧文件夹

主要问题

使用rax构建微信小程序,使用build/wechat-miniprogram作为微信开发者工具的项目根目录,执行rax-app build命令后,文件夹build/wechat-miniprogram在构建过程中被删除并重新生成,旧文件夹的删除导致了微信开发者工具不能读取项目的project.config.json,不得不在微信开发者工具中重新打开项目。

我认为在执行build命令时,不删除文件夹build/wechat-miniprogram,而是仅清空build/wechat-miniprogram下的内容可能会更合理

复现步骤

环境

macOS 10.15.7
node v14.15.4
npm v7.4.3
rax-app v3.3.3
@alib/build-scripts v0.1.30

步骤

  1. 执行 npm init rax rax-example创建项目
  2. 下载依赖,使用rax-app build编译项目后,将build/wechat-miniprogram作为项目根目录使用微信开发者工具打开
  3. 再次执行rax-app build,构建完成后,微信开发者工具不能正常读取项目内容
    image

尝试的解决方案

根据rax文档的小程序配置将项目根目录作为微信开发者工具的项目根目录,但是这需要在项目根目录创建project.config.json。个人认为project.config.json应该作为rax根据build.config.js构建的产物出现在build/wechat-miniprogram文件夹下更合理,同时,希望project.config.json中的appid是在构建过程中build.config.js读取环境变量生成的,因此这个方案不能满足我的要求。

缺少依赖 inquirer ~

Node: 14.17.4
npm: 6.14.14

> build-scripts start

build-scripts 1.1.1
internal/modules/cjs/loader.js:892
  throw err;
  ^

Error: Cannot find module 'inquirer'
Require stack:
- ..../node_modules/build-scripts/bin/child-process-start.js

[RFC]  增强注册配置类型检验的能力

现状

build-scripts 支持了 registerUserConfigregisterCliOption 来扩展工程配置,注册的时候会支持检验配置的类型,比如:

registerUserConfig({
  name: 'target',
  validation: 'object'
});

而在日常实践中,大多数配置可能需要支持多种数据类型,比如既可以是字符串也可以是数组(string|string[])。对于这种形式的配置项只能通过函数的形式支持,不方便开发进行快捷定义多类型的配置。

期望形式

期望 build-scripts 在 validation 配置上支持以 string|array|object 的形式,快捷定义多类型的工程配置:

registerUserConfig({
  name: 'target',
  validation: 'string|object'
});

实现方式

validation 配置为 字符串的形式下,默认以 | 字符解析多种数据类型,可选类型为:string | number | array | object | boolean

对于更加严格的配置控制,依旧推荐通过函数的方式传入

build 阶段构建目录清空时机

configArr.forEach(v => {
try {
const userBuildPath = v.chainConfig.output.get('path');
const buildPath = path.resolve(rootDir, userBuildPath);
fs.emptyDirSync(buildPath);
} catch (e) {
if (fs.existsSync(defaultPath)) {
fs.emptyDirSync(defaultPath);
}
}
});
const webpackConfig = configArr.map(v => v.chainConfig.toConfig());
await applyHook(`before.${command}.run`, {

目前会在 before.build.load 之后,before.build.run 之前删除,对于开发者通过自定义钩子往构建目录添加内容会有一定的认知成本,希望优化成执行 build 整体逻辑之前进行清空

webpack-dev-server 4 和 build-scripts+build-plugin-component 不兼容

在组件开发项目中:

{
    "build-plugin-component": "^1.9.2",
    "build-scripts": "^1.2.1",
    "webpack": "^5.64.4",
    "webpack-dev-server": "^4.6.0"
}

运行 build-scripts start 报错

ERR! Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'overlay'. These properties are valid:
   object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, server?, setupExitSignals?, static?, watchFiles?, webSocketServer? }

运行 build-scripts build 正常

如何添加自定义 module.rules ? ERR! [Config File] Config key 'module' is not supported

ERR! [Config File] Config key 'module' is not supported

my build.json

{
  "entry": {
    "preview": "./src/preview.tsx",
    "app": "./src/index.js"
  },
  "vendor": false,
  "devServer": {
    "hot": false
  },
  "publicPath": "/",
  "externals": {
    "react": "var window.React",
    "react-dom": "var window.ReactDOM",
    "prop-types": "var window.PropTypes",
    "@alifd/next": "var window.Next",
    "@alilc/lowcode-engine": "var window.AliLowCodeEngine",
    "@alilc/lowcode-editor-core": "var window.AliLowCodeEngine.common.editorCabin",
    "@alilc/lowcode-editor-skeleton": "var window.AliLowCodeEngine.common.skeletonCabin",
    "@alilc/lowcode-designer": "var window.AliLowCodeEngine.common.designerCabin",
    "@alilc/lowcode-engine-ext": "var window.AliLowCodeEngineExt",
    "@ali/lowcode-engine": "var window.AliLowCodeEngine",
    "moment": "var window.moment",
    "lodash": "var window._"
  },
  "plugins": [
    [
      "build-plugin-react-app"
    ],
    [
      "build-plugin-moment-locales",
      {
        "locales": [
          "zh-cn"
        ]
      }
    ],
    "./build.plugin.js"
  ],
  "module": {
    "rules": [
      {
        "use": "babel-loader"
      },
    ]
  }
}

then when i run "npm run start "

> @alilc/[email protected] start /Users/limi/Downloads/lowcode-demo
> build-scripts start --disable-reload --port 5556

@alib/build-scripts 0.1.32
ERR! CONFIG Failed to get config.
ERR! [Config File] Config key 'module' is not supported
Error: [Config File] Config key 'module' is not supported
    at Context.<anonymous> (/Users/limi/Downloads/lowcode-demo/node_modules/@alib/build-scripts/lib/core/Context.js:340:31)
    at Generator.next (<anonymous>)
    at /Users/limi/Downloads/lowcode-demo/node_modules/@alib/build-scripts/lib/core/Context.js:8:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/limi/Downloads/lowcode-demo/node_modules/@alib/build-scripts/lib/core/Context.js:4:12)
    at Context.runUserConfig (/Users/limi/Downloads/lowcode-demo/node_modules/@alib/build-scripts/lib/core/Context.js:334:36)
    at Context.<anonymous> (/Users/limi/Downloads/lowcode-demo/node_modules/@alib/build-scripts/lib/core/Context.js:406:24)
    at Generator.next (<anonymous>)
    at fulfilled (/Users/limi/Downloads/lowcode-demo/node_modules/@alib/build-scripts/lib/core/Context.js:5:58)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @alilc/[email protected] start: `build-scripts start --disable-reload --port 5556`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @alilc/[email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/limi/.npm/_logs/2022-04-12T02_08_33_543Z-debug.log

希望支持vue工程的启动和构建

我们工程是用vue写的,正在使用飞冰开发自己的物料库,在开发页面级物料时发现build-scripts只支持react工程的调试和构建,希望build-scripts能支持vue工程的调试和构建

希望增加中断进程钩子

希望增加SIGINT信号钩子

build-plugin-cone-split-schema
该插件会在编译时生成大量临时文件,
进程完成前中断,则临时文件会保留,
再次启动,这些临时文件会影响第二次编译,
且这些临时文件会污染git changes

[Question] 0.1.x版本怎么设置proxy

我在开发低代码引擎的设置器,官方的脚手架工具是这个,它用的就是@alib/build-scripts的版本是0.1.32。
目前我找不到build-scripts的文档,build.json如下
image
执行build-scripts start会报错:
image
现在开发要调用接口,怎么办

# 请教下 onGetWebpackConfig 不执行,提示 No webpack config found

请教下,为什么 onGetWebpackConfig 不执行?
提示 WARN CONFIG No webpack config found.

    "build-plugin-component": "^1.11.0",
    "build-scripts": "^1.3.0",
    "jest": "^26.4.2",
    "webpack": "^4.43.0",
    "webpack-dev-server": "^3.11.3"
// build.json 文件内容

{
  "externals": {
    "react": "var window.React",
    "react-dom": "var window.ReactDOM",
  },
  "plugins": [
    "build-plugin-component",
    "./build.plugin.js"
  ]
}

// build.plugin.js 文件内容
module.exports = ({ onGetWebpackConfig }) => {
  onGetWebpackConfig((config) => {
   // 这里为啥不执行?
    console.log('---------');
    console.log('---------');
    console.log('---------');
  });
};

[RFC] 动态设置插件执行顺序

背景

目前 build-scripts 工程工具应用于 icejs、rax-app 等框架体系,框架中内置了大量的插件及其功能。而对于三方插件其执行顺序永远在内置插件之后,对于一些强制依赖所有配置完成修改后的能力(如 rax-app wirteToDisk 配置依赖 outputDir 的设置)将会出现失效的情况。因此希望能提供一些机制动态设置插件执行顺序

技术方案

目前插件的规范是以默认导出的 JavaScript 模块的方式导出:

// esm
export default () => {}

// cjs
module.exports = () => {}

通过导出规范的约束,增加执行顺序的设置

// esm
export default {
  plugin: () => {},
  enforce: 'pre'
}

// cjs
module.exports = {
  plugin: () => {},
  enfore: 'pre'
}

enforce 可选值为 pre(前置执行)、normal(默认)、post(后置执行)

执行顺序如下:

  • 执行设置 enforce 为 pre 的插件
  • 执行设置 enforce 为 normal 的插件和现有默认导出的插件
  • 执行设置 enforce 为 post 的插件

[RFC] 自定义工程配置能力增强

背景

build-scripts 提供了多任务的能力,开发者可以针对同一工程构建添加多个 webpack 任务。而所有的用户配置对 webpack 任务的修改默认对所有的任务生效。面向一些多任务的场景,存在定制的配置需求,并不希望基础的用户配置对当前任务照成影响(目前出现影响后通过自定义 webpack 配置重新覆盖,成本较高)

onGetWebpackConfig 已支持指定 task 生效的方式,而 registerUserConfig 和 registerCliOption 默认均未提供

期望方式

为了方便不同插件间对于已添加配置有更强的定制能力,期望通过一下方式增强自定义工程配置的能力:

// registerUserConfig / registerCliOptions 支持 ignoreTask
registerUserConfig({
  name: 'target',
  validation: 'object',
  ignoreTasks: ['ssr'],
  configWebpack: () => {}
});

// 新增 modifyRegisterConfig 获取修改配置,理论上应该支持除 name 外,所有配置属性修改
modifyConfigRegistration('configName', (config) => {
  return {
    ignoreTasks: ['ssr', 'web']
  }
});

如何修改babel配置?

发现引入es模块文件超过500kb就构建不起来?需要在.babelrc 中开启 compact: false
在项目根目录或者packages/* 子包下面添加发现不生效,如何才能生效呢?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.