Skip to content
This repository has been archived by the owner on Mar 28, 2023. It is now read-only.

Commit

Permalink
feat: config file support extends (#11)
Browse files Browse the repository at this point in the history
* feat: config file support extends

* test: add cases for config extends

* test: correct config case
  • Loading branch information
PeachScript authored Jun 24, 2022
1 parent e89f575 commit edf71ee
Show file tree
Hide file tree
Showing 19 changed files with 194 additions and 3 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"@zerollup/ts-transform-paths": "1.7.18",
"husky": "^8.0.1",
"jest": "^27",
"jest-mock-process": "^1.5.1",
"lint-staged": "^13.0.1",
"prettier": "^2.6.2",
"prettier-plugin-packagejson": "^2.2.18",
Expand Down
10 changes: 10 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default (api: IApi) => {
description: 'build',
async fn() {
await builder({
userConfig: api.userConfig,
userConfig: api.config,
cwd: api.cwd,
pkg: api.pkg,
});
Expand Down
2 changes: 1 addition & 1 deletion src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default (api: IApi) => {
description: DEV_COMMAND,
async fn() {
const buildWatcher = await builder({
userConfig: api.userConfig,
userConfig: api.config,
cwd: api.cwd,
pkg: api.pkg,
watch: true,
Expand Down
2 changes: 1 addition & 1 deletion src/commands/prebundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default (api: IApi) => {
}: typeof import('../prebundler') = require('../prebundler');

await preBundle({
userConfig: api.userConfig.prebundle,
userConfig: api.config.prebundle,
cwd: api.cwd,
pkg: api.pkg,
});
Expand Down
66 changes: 66 additions & 0 deletions src/features/configPlugins/configPlugins.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,69 @@
import path from 'path';
import { deepmerge, resolve } from '@umijs/utils';
import { IApi } from '../../types';
import { getSchemas } from './schema';

/**
* parse extends option for config
*/
function parseExtendsConfig(opts: {
config: Record<string, any>;
resolvePaths?: string[];
api: IApi;
}) {
let { config } = opts;
const {
api,
resolvePaths = api.service.configManager!.files.map((f) => path.dirname(f)),
} = opts;

if (config.extends) {
let absExtendsPath = '';
const ConfigManager: any = api.service.configManager!.constructor;

// try to resolve extends path
resolvePaths.some((dir) => {
try {
absExtendsPath = resolve.sync(config.extends, {
basedir: dir,
extensions: ['.js', '.ts'],
});
return true;
} catch {}
});

if (!absExtendsPath) {
throw new Error(`Cannot find extends config file: ${config.extends}`);
} else if (api.service.configManager!.files.includes(absExtendsPath)) {
throw new Error(
`Cannot extends config circularly for file: ${absExtendsPath}`,
);
}

// load extends config
const { config: extendsConfig, files: extendsFiles } =
ConfigManager.getUserConfig({ configFiles: [absExtendsPath] });

ConfigManager.validateConfig({
config: extendsConfig,
schemas: api.service.configSchemas,
});

// try to parse nested extends config
const nestedConfig = parseExtendsConfig({
config: extendsConfig,
resolvePaths: [path.dirname(absExtendsPath)],
api,
});

// merge extends config & save related files
config = deepmerge(nestedConfig, config);
api.service.configManager!.files.push(...extendsFiles);
}

return config;
}

export default (api: IApi) => {
const configDefaults: Record<string, any> = {};

Expand All @@ -20,4 +83,7 @@ export default (api: IApi) => {
},
]);
}

// support extends config
api.modifyConfig((config) => parseExtendsConfig({ config, api }));
};
1 change: 1 addition & 0 deletions src/features/configPlugins/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ function getBundlessSchemas(Joi: Root) {
export function getSchemas(): Record<string, (Joi: Root) => any> {
return {
...getCommonSchemas(),
extends: (Joi) => Joi.string(),
esm: (Joi) => getBundlessSchemas(Joi),
cjs: (Joi) => getBundlessSchemas(Joi),
umd: (Joi) =>
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ export interface IFatherPreBundleConfig {
}

export interface IFatherConfig extends IFatherBaseConfig {
extends?: string;

/**
* bundler config (umd)
*/
Expand Down
77 changes: 77 additions & 0 deletions tests/config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import path from 'path';
import { mockProcessExit } from 'jest-mock-process';
import { distToMap } from './utils';
import * as cli from '../src/cli/cli';

jest.mock('@umijs/utils', () => {
const originalModule = jest.requireActual('@umijs/utils');

return {
__esModule: true,
...originalModule,

// workaround for watch config file change in jest
// ref:
// - https://github.com/facebook/jest/issues/6034
// - https://github.com/umijs/umi-next/blob/e46748deab90807c8504dfe11f3bb554f4f27ac3/packages/core/src/config/config.ts#L172
// TODO: remove this when umi ready
register: {
...originalModule.register,
getFiles: () => (global.TMP_CASE_CONFIG ? [global.TMP_CASE_CONFIG] : []),
},
};
});

const mockExit = mockProcessExit();
const CASES_DIR = path.join(__dirname, 'fixtures/config');

afterAll(() => {
delete process.env.APP_ROOT;
mockExit.mockRestore();
});
test('config: cyclic extends', async () => {
// execute build
process.env.APP_ROOT = path.join(CASES_DIR, 'config-cyclic-extends');

// workaround for get config file path
global.TMP_CASE_CONFIG = path.join(process.env.APP_ROOT, '.fatherrc.ts');

await cli.run({
args: { _: ['build'], $0: 'node' },
});

// expect process.exit(1) called
expect(mockExit).toHaveBeenCalledWith(1);

// restore mock
jest.unmock('@umijs/utils');
delete global.TMP_CASE_CONFIG;
});

test('config: nonexistent extends', async () => {
// execute build
process.env.APP_ROOT = path.join(CASES_DIR, 'config-nonexistent-extends');

await cli.run({
args: { _: ['build'], $0: 'node' },
});

// expect process.exit(1) called
expect(mockExit).toHaveBeenCalledWith(1);
});

test('config: nested extends', async () => {
// execute build
process.env.APP_ROOT = path.join(CASES_DIR, 'config-nested-extends');
await cli.run({
args: { _: ['build'], $0: 'node' },
});

// prepare file map
const fileMap = distToMap(
path.join(CASES_DIR, 'config-nested-extends', 'dist'),
);

// check result
require(`${process.env.APP_ROOT}/expect`).default(fileMap);
});
1 change: 1 addition & 0 deletions tests/dev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ jest.mock('@umijs/utils', () => {
// ref:
// - https://github.com/facebook/jest/issues/6034
// - https://github.com/umijs/umi-next/blob/e46748deab90807c8504dfe11f3bb554f4f27ac3/packages/core/src/config/config.ts#L172
// TODO: remove this when umi ready
register: {
...originalModule.register,
getFiles: () => [global.TMP_CASE_CONFIG],
Expand Down
4 changes: 4 additions & 0 deletions tests/fixtures/config/config-cyclic-extends/.fatherrc.base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
extends: './.fatherrc.ts',
esm: {},
};
4 changes: 4 additions & 0 deletions tests/fixtures/config/config-cyclic-extends/.fatherrc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
extends: './.fatherrc.base.ts',
esm: { transformer: 'babel' },
};
4 changes: 4 additions & 0 deletions tests/fixtures/config/config-nested-extends/.fatherrc.base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
extends: './.fatherrc.common.ts',
esm: {},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
platform: 'node',
};
4 changes: 4 additions & 0 deletions tests/fixtures/config/config-nested-extends/.fatherrc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
extends: './.fatherrc.base.ts',
esm: { transformer: 'babel' },
};
7 changes: 7 additions & 0 deletions tests/fixtures/config/config-nested-extends/expect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default (files: Record<string, string>) => {
// expect node platform
expect(files['esm/index.js']).toContain("from 'fs';");

// expect not esbuild
expect(files['esm/index.js']).not.toContain(' as default');
};
3 changes: 3 additions & 0 deletions tests/fixtures/config/config-nested-extends/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import * as fs from 'fs';

export default fs;
1 change: 1 addition & 0 deletions tests/fixtures/config/config-nested-extends/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
3 changes: 3 additions & 0 deletions tests/fixtures/config/config-nonexistent-extends/.fatherrc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
extends: './.fatherrc.nonexistent.ts',
};

0 comments on commit edf71ee

Please sign in to comment.