Skip to content

Commit

Permalink
feat: support targets config (#549)
Browse files Browse the repository at this point in the history
* feat: ✨ 新增targets配置,用于修改构建产物的兼容性

* feat: ✨ 优化 targets 的类型判断, 区分不同编译模式的校验和ts类型; 新增 swc 支持

* docs: 📝 fix doc duplicate words

* chore: 🤖 将targets放到统一的utils中处理

* test: ✅ 添加 targets 配置的测试用例

* feat: ✨ 统一targets类型为 string | string[], 简化配置

* feat: ✨ 统一 targets 数据格式,与umi保持一致;修改swc传参方式

* docs: 📝 update config.md

* test: ✅ fix fatherrc in esbuild test

* test: ✅ esbuild 的esm测试用例中chrome版本号改为 54

* test: ✅ remove untouchable code, for test coverage

Co-authored-by: 墨焰 <huazhi.chz@alibaba-inc.com>
  • Loading branch information
huarse and 墨焰 committed Dec 7, 2022
1 parent a96c02a commit 3dbe137
Show file tree
Hide file tree
Showing 29 changed files with 174 additions and 25 deletions.
20 changes: 18 additions & 2 deletions docs/config.md
Expand Up @@ -48,9 +48,9 @@ father 支持以下配置项。
- 类型:`browser` | `node`
- 默认值:`<auto>`

指定构建产物的目标平台,其中 `esm``umd` 产物的默认 `platform``browser``cjs` 产物的默认 `platform``node`;指定为 `browser` 时产物默认兼容至 IE11,指定为 `node` 时产物默认兼容至 Node.js v14,兼容性不支持配置
指定构建产物的目标平台,其中 `esm``umd` 产物的默认 `platform``browser``cjs` 产物的默认 `platform``node`;指定为 `browser` 时产物默认兼容至 IE11,指定为 `node` 时产物默认兼容至 Node.js v14。

> 注:Bundless 模式下,如果手动将 `transformer` 指定为 `esbuild`,那么 `browser` 产物兼容性为 ES6 而不是 IE11。
> 注:Bundless 模式下,如果手动将 `transformer` 指定为 `esbuild`,那么 `browser` 产物默认兼容性为 Chrome65 而不是 IE11。
### sourcemap

Expand All @@ -61,6 +61,22 @@ father 支持以下配置项。

> 注:Bundless 模式下 map 对象的 file 字段为空
### targets

- 类型: `Record<string, number>`
- 默认值:`<auto>`

指定源码编译产物的兼容性,不同目标平台和编译模式下的默认值如下:

| `platform` | `transformer` | default value |
| ---------- | ------------- | ---------------- |
| `browser` | `babel` | `{ ie: 11 }` |
| `browser` | `esbuild` | `{ chrome: 65 }` |
| `browser` | `swc` | `{ chrome: 65 }` |
| `node` | `babel` | `{ node: 14 }` |
| `node` | `esbuild` | `{ node: 14 }` |
| `node` | `babel` | `{ node: 14 }` |

## 构建配置

father 以构建产物类型划分构建配置,其中 `esm``cjs` 产物为 Bundless 构建模式,`umd` 产物为 Bundle 构建模式,另外依赖预打包 `prebundle` 产物也为 Bundle 构建模式。
Expand Down
8 changes: 5 additions & 3 deletions src/builder/bundle/index.ts
Expand Up @@ -2,7 +2,7 @@ import { chalk, importLazy, logger } from '@umijs/utils';
import path from 'path';
import { CACHE_PATH } from '../../constants';
import type { BundleConfigProvider } from '../config';
import { getBabelPresetReactOpts } from '../utils';
import { getBabelPresetReactOpts, getBundleTargets } from '../utils';

const bundler: typeof import('@umijs/bundler-webpack') = importLazy(
path.dirname(require.resolve('@umijs/bundler-webpack/package.json')),
Expand Down Expand Up @@ -52,7 +52,7 @@ export default async (opts: {
theme: config.theme,

// compatible with IE11 by default
targets: { ie: 11 },
targets: getBundleTargets(config),
jsMinifier: JSMinifier.terser,
cssMinifier: CSSMinifier.cssnano,
extraBabelIncludes: [/node_modules/],
Expand All @@ -66,7 +66,9 @@ export default async (opts: {
babelPreset: [
require.resolve('@umijs/babel-preset-umi'),
{
presetEnv: {},
presetEnv: {
targets: getBundleTargets(config),
},
presetReact: getBabelPresetReactOpts(opts.configProvider.pkg),
presetTypeScript: {},
pluginTransformRuntime: {},
Expand Down
8 changes: 3 additions & 5 deletions src/builder/bundless/loaders/javascript/babel.ts
@@ -1,11 +1,12 @@
import { transform } from '@umijs/bundler-utils/compiled/babel/core';
import { winPath } from '@umijs/utils';
import path from 'path';
import { IFatherBundlessTypes, IFatherPlatformTypes } from '../../../../types';
import { IFatherBundlessTypes } from '../../../../types';
import {
addSourceMappingUrl,
ensureRelativePath,
getBabelPresetReactOpts,
getBundlessTargets,
} from '../../../utils';
import type { IJSTransformer } from '../types';

Expand Down Expand Up @@ -35,10 +36,7 @@ const babelTransformer: IJSTransformer = function (content) {
// TODO: correct optional in umi types and replace any here
const presetOpts: any = {
presetEnv: {
targets:
this.config.platform === IFatherPlatformTypes.BROWSER
? { ie: 11 }
: { node: 14 },
targets: getBundlessTargets(this.config),
modules: this.config.format === IFatherBundlessTypes.ESM ? false : 'auto',
},
presetReact: getBabelPresetReactOpts(this.pkg),
Expand Down
6 changes: 3 additions & 3 deletions src/builder/bundless/loaders/javascript/esbuild.ts
@@ -1,7 +1,8 @@
import { build } from '@umijs/bundler-utils/compiled/esbuild';
import { winPath } from '@umijs/utils';
import path from 'path';
import { IFatherBundlessConfig, IFatherPlatformTypes } from '../../../../types';
import { IFatherBundlessConfig } from '../../../../types';
import { getBundlessTargets } from '../../../utils';
import type { IJSTransformer } from '../types';

/**
Expand Down Expand Up @@ -59,8 +60,7 @@ const esbuildTransformer: IJSTransformer = async function () {
format: this.config.format,
define: this.config.define,
platform: this.config.platform,
target:
this.config.platform === IFatherPlatformTypes.NODE ? 'node14' : 'es6',
target: getBundlessTargets(this.config),
// esbuild need relative entry path
entryPoints: [path.relative(this.paths.cwd, this.paths.fileAbsPath)],
absWorkingDir: this.paths.cwd,
Expand Down
14 changes: 5 additions & 9 deletions src/builder/bundless/loaders/javascript/swc.ts
@@ -1,14 +1,11 @@
import { winPath } from '@umijs/utils';
import { lstatSync } from 'fs';
import path from 'path';
import {
IFatherBundlessConfig,
IFatherBundlessTypes,
IFatherPlatformTypes,
} from '../../../../types';
import { IFatherBundlessConfig, IFatherBundlessTypes } from '../../../../types';
import {
addSourceMappingUrl,
ensureRelativePath,
getBundlessTargets,
getSWCTransformReactOpts,
} from '../../../utils';
import { IJSTransformer } from '../types';
Expand Down Expand Up @@ -133,6 +130,9 @@ const swcTransformer: IJSTransformer = async function (content) {
)
: undefined,
sourceMaps: this.config.sourcemap,
env: {
targets: getBundlessTargets(this.config),
},

jsc: {
baseUrl: this.paths.cwd,
Expand All @@ -142,10 +142,6 @@ const swcTransformer: IJSTransformer = async function (content) {
...(isTSFile && isJSXFile ? { tsx: true } : {}),
...(!isTSFile && isJSXFile ? { jsx: true } : {}),
},
target:
this.config.platform === IFatherPlatformTypes.BROWSER
? 'es5'
: 'es2019',
transform: {
react: getSWCTransformReactOpts(this.pkg),
},
Expand Down
1 change: 1 addition & 0 deletions src/builder/config.ts
Expand Up @@ -334,6 +334,7 @@ export class BundlessConfigProvider extends ConfigProvider {
});
}

// TODO 这里匹配有问题,会先匹配到全局的,例如 src/async 会被 src/** 匹配到
getConfigForFile(filePath: string) {
return this.configs[this.matchers.findIndex((m) => m.match(filePath))];
}
Expand Down
49 changes: 48 additions & 1 deletion src/builder/utils.ts
@@ -1,6 +1,12 @@
import { semver } from '@umijs/utils';
import path from 'path';
import { IApi } from '../types';
import {
IApi,
IFatherBaseConfig,
IFatherJSTransformerTypes,
IFatherPlatformTypes,
} from '../types';
import type { IBundlessConfig } from './config';

export function addSourceMappingUrl(code: string, loc: string) {
return (
Expand Down Expand Up @@ -62,3 +68,44 @@ export function ensureRelativePath(relativePath: string) {
}
return relativePath;
}

const defaultCompileTarget: Record<
IFatherPlatformTypes,
Record<IFatherJSTransformerTypes, any>
> = {
[IFatherPlatformTypes.BROWSER]: {
[IFatherJSTransformerTypes.BABEL]: { ie: 11 },
[IFatherJSTransformerTypes.ESBUILD]: ['chrome65'],
[IFatherJSTransformerTypes.SWC]: { chrome: 65 },
},
[IFatherPlatformTypes.NODE]: {
[IFatherJSTransformerTypes.BABEL]: { node: 14 },
[IFatherJSTransformerTypes.ESBUILD]: ['node14'],
[IFatherJSTransformerTypes.SWC]: { node: 14 },
},
};

export function getBundleTargets({ targets }: IFatherBaseConfig) {
if (!targets || !Object.keys(targets).length) {
return defaultCompileTarget[IFatherPlatformTypes.BROWSER][
IFatherJSTransformerTypes.BABEL
];
}

return targets;
}

export function getBundlessTargets(config: IBundlessConfig) {
const { platform, transformer, targets } = config;

// targets is undefined or empty, fallback to default
if (!targets || !Object.keys(targets).length) {
return defaultCompileTarget[platform!][transformer!];
}
// esbuild accept string or string[]
if (transformer === IFatherJSTransformerTypes.ESBUILD) {
return Object.keys(targets).map((name) => `${name}${targets![name]}`);
}

return targets;
}
9 changes: 7 additions & 2 deletions src/features/configPlugins/schema.ts
@@ -1,5 +1,5 @@
import type { Root, SchemaLike } from '@umijs/utils/compiled/@hapi/joi';
import { IFatherPlatformTypes } from '../../types';
import { IFatherJSTransformerTypes, IFatherPlatformTypes } from '../../types';

function getCommonSchemas(): Record<string, (Joi: Root) => any> {
return {
Expand All @@ -16,6 +16,7 @@ function getCommonSchemas(): Record<string, (Joi: Root) => any> {
extraBabelPresets: (Joi) => Joi.array().optional(),
extraBabelPlugins: (Joi) => Joi.array().optional(),
sourcemap: (Joi) => Joi.boolean().optional(),
targets: (Joi) => Joi.object().optional(),
};
}

Expand All @@ -34,7 +35,11 @@ function getBundlessSchemas(Joi: Root) {
...getCommonSchemasJoi(Joi),
input: Joi.string(),
output: Joi.string(),
transformer: Joi.string(),
transformer: Joi.equal(
IFatherJSTransformerTypes.BABEL,
IFatherJSTransformerTypes.ESBUILD,
IFatherJSTransformerTypes.SWC,
).optional(),
overrides: Joi.object(),
ignores: Joi.array().items(Joi.string()),
});
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Expand Up @@ -117,6 +117,11 @@ export interface IFatherBaseConfig {
* output sourcemap
*/
sourcemap?: boolean;

/**
* compile targets
*/
targets?: Record<string, number>;
}

export interface IFatherBundlessConfig extends IFatherBaseConfig {
Expand Down
5 changes: 5 additions & 0 deletions tests/fixtures/build/bundle-targets/.fatherrc.ts
@@ -0,0 +1,5 @@
export default {
umd: {
targets: { chrome: 85 },
},
};
3 changes: 3 additions & 0 deletions tests/fixtures/build/bundle-targets/expect.ts
@@ -0,0 +1,3 @@
export default (files: Record<string, string>) => {
expect(files['umd/index.min.js']).toContain('await');
};
1 change: 1 addition & 0 deletions tests/fixtures/build/bundle-targets/package.json
@@ -0,0 +1 @@
{}
5 changes: 5 additions & 0 deletions tests/fixtures/build/bundle-targets/src/index.ts
@@ -0,0 +1,5 @@
async function hello() {
return await Promise.resolve('ok');
}

hello();
1 change: 1 addition & 0 deletions tests/fixtures/build/bundle-targets/tsconfig.json
@@ -0,0 +1 @@
{}
10 changes: 10 additions & 0 deletions tests/fixtures/build/bundless-babel-targets/.fatherrc.ts
@@ -0,0 +1,10 @@
export default {
cjs: {
transformer: 'babel',
targets: { chrome: 85 },
},
esm: {
transformer: 'babel',
targets: { ie: 11, chrome: 60 },
},
};
4 changes: 4 additions & 0 deletions tests/fixtures/build/bundless-babel-targets/expect.ts
@@ -0,0 +1,4 @@
export default (files: Record<string, string>) => {
expect(files['cjs/index.js']).toContain('await');
expect(files['esm/index.js']).toContain('_regeneratorRuntime');
};
1 change: 1 addition & 0 deletions tests/fixtures/build/bundless-babel-targets/package.json
@@ -0,0 +1 @@
{}
5 changes: 5 additions & 0 deletions tests/fixtures/build/bundless-babel-targets/src/index.ts
@@ -0,0 +1,5 @@
async function hello() {
return await Promise.resolve('ok');
}

hello();
1 change: 1 addition & 0 deletions tests/fixtures/build/bundless-babel-targets/tsconfig.json
@@ -0,0 +1 @@
{}
11 changes: 11 additions & 0 deletions tests/fixtures/build/bundless-esbuild-targets/.fatherrc.ts
@@ -0,0 +1,11 @@
export default {
targets: { chrome: 70 },
cjs: {
transformer: 'esbuild',
targets: { chrome: 85 },
},
esm: {
transformer: 'esbuild',
targets: { chrome: 54 },
},
};
4 changes: 4 additions & 0 deletions tests/fixtures/build/bundless-esbuild-targets/expect.ts
@@ -0,0 +1,4 @@
export default (files: Record<string, string>) => {
expect(files['cjs/index.js']).toContain('await');
expect(files['esm/index.js']).toContain('yield');
};
1 change: 1 addition & 0 deletions tests/fixtures/build/bundless-esbuild-targets/package.json
@@ -0,0 +1 @@
{}
5 changes: 5 additions & 0 deletions tests/fixtures/build/bundless-esbuild-targets/src/index.ts
@@ -0,0 +1,5 @@
async function hello() {
return await Promise.resolve('ok');
}

hello();
@@ -0,0 +1 @@
{}
10 changes: 10 additions & 0 deletions tests/fixtures/build/bundless-swc-targets/.fatherrc.ts
@@ -0,0 +1,10 @@
export default {
cjs: {
transformer: 'swc',
targets: { chrome: 85 },
},
esm: {
transformer: 'swc',
targets: { ie: 11 },
},
};
4 changes: 4 additions & 0 deletions tests/fixtures/build/bundless-swc-targets/expect.ts
@@ -0,0 +1,4 @@
export default (files: Record<string, string>) => {
expect(files['cjs/index.js']).toContain('await');
expect(files['esm/index.js']).toContain('_asyncToGenerator');
};
1 change: 1 addition & 0 deletions tests/fixtures/build/bundless-swc-targets/package.json
@@ -0,0 +1 @@
{}
5 changes: 5 additions & 0 deletions tests/fixtures/build/bundless-swc-targets/src/index.ts
@@ -0,0 +1,5 @@
async function hello() {
return await Promise.resolve('ok');
}

hello();
1 change: 1 addition & 0 deletions tests/fixtures/build/bundless-swc-targets/tsconfig.json
@@ -0,0 +1 @@
{}

0 comments on commit 3dbe137

Please sign in to comment.