Skip to content

Commit

Permalink
feat: support load module config with env (#151)
Browse files Browse the repository at this point in the history
<!--
Thank you for your pull request. Please review below requirements.
Bug fixes and new features should include tests and possibly benchmarks.
Contributors guide:
https://github.com/eggjs/egg/blob/master/CONTRIBUTING.md

感谢您贡献代码。请确认下列 checklist 的完成情况。
Bug 修复和新功能必须包含测试,必要时请附上性能测试。
Contributors guide:
https://github.com/eggjs/egg/blob/master/CONTRIBUTING.md
-->

##### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to
[x]. -->

- [x] `npm test` passes
- [x] tests and/or benchmarks are included
- [ ] documentation is changed or added
- [x] commit message follows commit guidelines

##### Affected core subsystem(s)
<!-- Provide affected core subsystem(s). -->


##### Description of change
<!-- Provide a description of the change below this comment. -->

<!--
- any feature?
- close https://github.com/eggjs/egg/ISSUE_URL
-->
  • Loading branch information
killagu authored Sep 7, 2023
1 parent 398549c commit c087226
Show file tree
Hide file tree
Showing 13 changed files with 150 additions and 45 deletions.
3 changes: 2 additions & 1 deletion core/common-util/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
},
"dependencies": {
"globby": "^11.1.0",
"js-yaml": "^3.14.0"
"js-yaml": "^3.14.0",
"extend2": "^1.0.0"
},
"publishConfig": {
"access": "public"
Expand Down
98 changes: 66 additions & 32 deletions core/common-util/src/ModuleConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import fs, { promises as fsPromise } from 'fs';
import path from 'path';
import globby from 'globby';
import { FSUtil } from './FSUtil';
import extend from 'extend2';

export interface ModuleReference {
name: string;
path: string;
}

Expand Down Expand Up @@ -45,11 +47,17 @@ const DEFAULT_READ_MODULE_REF_OPTS = {
};

export class ModuleConfigUtil {
public static moduleYamlPath(modulePath: string): string {
public static moduleYamlPath(modulePath: string, env?: string): string {
if (env) {
return path.join(modulePath, `module.${env}.yml`);
}
return path.join(modulePath, 'module.yml');
}

public static moduleJsonPath(modulePath: string): string {
public static moduleJsonPath(modulePath: string, env?: string): string {
if (env) {
return path.join(modulePath, `module.${env}.json`);
}
return path.join(modulePath, 'module.json');
}

Expand All @@ -75,12 +83,16 @@ export class ModuleConfigUtil {
// path.posix for windows keep path as foo/package.json
const pkgJson = path.posix.join(moduleReferenceConfig.package, 'package.json');
const file = require.resolve(pkgJson, options);
const modulePath = path.dirname(file);
moduleReference = {
path: path.dirname(file),
path: modulePath,
name: ModuleConfigUtil.readModuleNameSync(modulePath),
};
} else if (ModuleReferenceConfigHelp.isInlineModuleReference(moduleReferenceConfig)) {
const modulePath = path.join(configDir, moduleReferenceConfig.path);
moduleReference = {
path: path.join(configDir, moduleReferenceConfig.path),
path: modulePath,
name: ModuleConfigUtil.readModuleNameSync(modulePath),
};
} else {
throw new Error('unknown type of module reference config: ' + JSON.stringify(moduleReferenceConfig));
Expand Down Expand Up @@ -124,13 +136,15 @@ export class ModuleConfigUtil {
}
moduleDirSet.add(moduleDir);

let name: string;
try {
this.readModuleNameSync(moduleDir);
name = this.readModuleNameSync(moduleDir);
} catch (_) {
continue;
}
ref.push({
path: moduleDir,
name,
});
}
const moduleReferences = this.readModuleFromNodeModules(baseDir);
Expand All @@ -143,6 +157,7 @@ export class ModuleConfigUtil {
});
ref.push({
path: moduleReference.path,
name: moduleReference.name,
});
}
return ref;
Expand All @@ -169,11 +184,11 @@ export class ModuleConfigUtil {
const absolutePkgPath = path.dirname(packageJsonPath);
const realPkgPath = fs.realpathSync(absolutePkgPath);
try {
if (this.readModuleNameSync(realPkgPath)) {
ref.push({
path: realPkgPath,
});
}
const name = this.readModuleNameSync(realPkgPath);
ref.push({
path: realPkgPath,
name,
});
} catch (_) {
continue;
}
Expand All @@ -189,33 +204,44 @@ export class ModuleConfigUtil {
return path.join(baseDir, 'config', moduleDir);
}

private static getModuleName(pkg: any) {
assert(pkg.eggModule && pkg.eggModule.name, 'eggModule.name not found in package.json');
return pkg.eggModule.name;
}

public static async readModuleName(baseDir: string, moduleDir: string): Promise<string> {
moduleDir = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir);
const pkgContent = await fsPromise.readFile(path.join(moduleDir, 'package.json'), 'utf8');
const pkg = JSON.parse(pkgContent);
assert(pkg.eggModule && pkg.eggModule.name, 'eggModule.name not found in package.json');
return pkg.eggModule.name;
return ModuleConfigUtil.getModuleName(pkg);
}

public static readModuleNameSync(moduleDir: string, baseDir?: string): string {
moduleDir = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir);
const pkgContent = fs.readFileSync(path.join(moduleDir, 'package.json'), 'utf8');
const pkg = JSON.parse(pkgContent);
assert(pkg.eggModule && pkg.eggModule.name, 'eggModule.name not found in package.json');
return pkg.eggModule.name;
return ModuleConfigUtil.getModuleName(pkg);
}

public static async loadModuleConfig(moduleDir: string, baseDir?: string): Promise<ModuleConfig | undefined> {
public static async loadModuleConfig(moduleDir: string, baseDir?: string, env?: string): Promise<ModuleConfig | undefined> {
moduleDir = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir);
const yamlConfig = await ModuleConfigUtil.loadModuleYaml(moduleDir);
if (yamlConfig) {
return yamlConfig;
let defaultConfig = await ModuleConfigUtil.loadModuleYaml(moduleDir);
if (!defaultConfig) {
defaultConfig = await ModuleConfigUtil.loadModuleJson(moduleDir);
}
return await ModuleConfigUtil.loadModuleJson(moduleDir);
let envConfig: ModuleConfig | undefined;
if (env) {
envConfig = await ModuleConfigUtil.loadModuleYaml(moduleDir, env);
if (!envConfig) {
envConfig = await ModuleConfigUtil.loadModuleJson(moduleDir, env);
}
}
extend(true, defaultConfig, envConfig);
return defaultConfig;
}

private static async loadModuleJson(moduleDir: string): Promise<ModuleConfig | undefined> {
const moduleJsonPath = ModuleConfigUtil.moduleJsonPath(moduleDir);
private static async loadModuleJson(moduleDir: string, env?: string): Promise<ModuleConfig | undefined> {
const moduleJsonPath = ModuleConfigUtil.moduleJsonPath(moduleDir, env);
const moduleJsonPathExists = await FSUtil.fileExists(moduleJsonPath);
if (!moduleJsonPathExists) {
return;
Expand All @@ -225,8 +251,8 @@ export class ModuleConfigUtil {
return moduleJson.config;
}

private static async loadModuleYaml(moduleDir: string): Promise<ModuleConfig | undefined> {
const moduleYamlPath = ModuleConfigUtil.moduleYamlPath(moduleDir);
private static async loadModuleYaml(moduleDir: string, env?: string): Promise<ModuleConfig | undefined> {
const moduleYamlPath = ModuleConfigUtil.moduleYamlPath(moduleDir, env);
const moduleYamlPathExists = await FSUtil.fileExists(moduleYamlPath);
if (!moduleYamlPathExists) {
return;
Expand All @@ -235,17 +261,25 @@ export class ModuleConfigUtil {
return yaml.safeLoad(moduleYamlContent) as ModuleConfigUtil;
}

public static loadModuleConfigSync(moduleDir: string, baseDir?: string): ModuleConfig | undefined {
public static loadModuleConfigSync(moduleDir: string, baseDir?: string, env?: string): ModuleConfig | undefined {
moduleDir = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir);
const yamlConfig = ModuleConfigUtil.loadModuleYamlSync(moduleDir);
if (yamlConfig) {
return yamlConfig;
let defaultConfig = ModuleConfigUtil.loadModuleYamlSync(moduleDir);
if (!defaultConfig) {
defaultConfig = ModuleConfigUtil.loadModuleJsonSync(moduleDir);
}
let envConfig: ModuleConfig | undefined;
if (env) {
envConfig = ModuleConfigUtil.loadModuleYamlSync(moduleDir, env);
if (!envConfig) {
envConfig = ModuleConfigUtil.loadModuleJsonSync(moduleDir, env);
}
}
return ModuleConfigUtil.loadModuleJsonSync(moduleDir);
extend(true, defaultConfig, envConfig);
return defaultConfig;
}

private static loadModuleJsonSync(moduleDir: string): ModuleConfig | undefined {
const moduleJsonPath = ModuleConfigUtil.moduleJsonPath(moduleDir);
private static loadModuleJsonSync(moduleDir: string, env?: string): ModuleConfig | undefined {
const moduleJsonPath = ModuleConfigUtil.moduleJsonPath(moduleDir, env);
const moduleJsonPathExists = fs.existsSync(moduleJsonPath);
if (!moduleJsonPathExists) {
return;
Expand All @@ -255,8 +289,8 @@ export class ModuleConfigUtil {
return moduleJson.config;
}

private static loadModuleYamlSync(moduleDir: string): ModuleConfig | undefined {
const moduleYamlPath = ModuleConfigUtil.moduleYamlPath(moduleDir);
private static loadModuleYamlSync(moduleDir: string, env?: string): ModuleConfig | undefined {
const moduleYamlPath = ModuleConfigUtil.moduleYamlPath(moduleDir, env);
const moduleYamlPathExists = fs.existsSync(moduleYamlPath);
if (!moduleYamlPathExists) {
return;
Expand Down
26 changes: 17 additions & 9 deletions core/common-util/test/ModuleConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ describe('test/ModuleConfig.test.ts', () => {
const config = ModuleConfigUtil.loadModuleConfigSync(path.join(__dirname, './fixtures/modules/foo-yaml'));
assert.deepStrictEqual(config, { mysql: { host: '127.0.0.1' } });
});

it('should load env', () => {
const config = ModuleConfigUtil.loadModuleConfigSync(path.join(__dirname, './fixtures/modules/dev-module-config'), undefined, 'dev');
assert.deepStrictEqual(config, { mysql: { host: '127.0.0.1', port: 11306 } });
});
});

describe('load module reference', () => {
Expand All @@ -16,10 +21,10 @@ describe('test/ModuleConfig.test.ts', () => {
const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-no-module-json');
const ref = ModuleConfigUtil.readModuleReference(fixturesPath);
assert.deepStrictEqual(ref, [
{ path: path.join(fixturesPath, 'app/module-a') },
{ path: path.join(fixturesPath, 'app/module-b') },
{ path: path.join(fixturesPath, 'app/module-b/test/fixtures/module-e') },
{ path: path.join(fixturesPath, 'node_modules/module-c') },
{ path: path.join(fixturesPath, 'app/module-a'), name: 'moduleA' },
{ path: path.join(fixturesPath, 'app/module-b'), name: 'moduleB' },
{ path: path.join(fixturesPath, 'app/module-b/test/fixtures/module-e'), name: 'moduleE' },
{ path: path.join(fixturesPath, 'node_modules/module-c'), name: 'moduleC' },
]);
});

Expand All @@ -33,7 +38,7 @@ describe('test/ModuleConfig.test.ts', () => {
const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-symlink');
const ref = ModuleConfigUtil.readModuleReference(fixturesPath);
assert.deepStrictEqual(ref, [
{ path: path.join(fixturesPath, 'app/module-a') },
{ path: path.join(fixturesPath, 'app/module-a'), name: 'moduleA' },
]);
});
});
Expand All @@ -44,8 +49,8 @@ describe('test/ModuleConfig.test.ts', () => {
const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-module-json');
const ref = ModuleConfigUtil.readModuleReference(fixturesPath);
assert.deepStrictEqual(ref, [
{ path: path.join(fixturesPath, 'app/module-a') },
{ path: path.join(fixturesPath, 'app/module-b') },
{ path: path.join(fixturesPath, 'app/module-a'), name: 'moduleA' },
{ path: path.join(fixturesPath, 'app/module-b'), name: 'moduleB' },
]);
});
});
Expand All @@ -57,7 +62,7 @@ describe('test/ModuleConfig.test.ts', () => {
cwd: fixturesPath,
});
assert.deepStrictEqual(ref, [
{ path: path.join(fixturesPath, 'node_modules/module-a') },
{ path: path.join(fixturesPath, 'node_modules/module-a'), name: 'moduleA' },
]);
});
});
Expand All @@ -71,7 +76,7 @@ describe('test/ModuleConfig.test.ts', () => {
};
const ref = ModuleConfigUtil.readModuleReference(fixturesPath, readModuleOptions);
assert.deepStrictEqual(ref, [
{ path: path.join(fixturesPath, 'app/module-a') },
{ path: path.join(fixturesPath, 'app/module-a'), name: 'moduleA' },
]);
});
});
Expand All @@ -90,6 +95,7 @@ describe('test/ModuleConfig.test.ts', () => {
const ret = ModuleConfigUtil.readModuleFromNodeModules(dir);
assert.deepStrictEqual(ret, [{
path: path.resolve(__dirname, './fixtures/monorepo/packages/d/node_modules/e'),
name: 'e',
}]);
});

Expand All @@ -98,6 +104,7 @@ describe('test/ModuleConfig.test.ts', () => {
const ret = ModuleConfigUtil.readModuleFromNodeModules(dir);
assert.deepStrictEqual(ret, [{
path: path.resolve(__dirname, './fixtures/monorepo/packages/a/node_modules/c'),
name: 'c',
}]);
});

Expand All @@ -106,6 +113,7 @@ describe('test/ModuleConfig.test.ts', () => {
const ret = ModuleConfigUtil.readModuleFromNodeModules(dir);
assert.deepStrictEqual(ret, [{
path: path.resolve(__dirname, './fixtures/monorepo/packages/a'),
name: 'a',
}]);
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mysql:
port: 11306
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mysql:
host: 127.0.0.1
3 changes: 2 additions & 1 deletion plugin/config/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ export default class App {
for (const reference of this.app.moduleReferences) {
const absoluteRef = {
path: ModuleConfigUtil.resolveModuleDir(reference.path, this.app.baseDir),
name: reference.name,
};

const moduleName = ModuleConfigUtil.readModuleNameSync(absoluteRef.path);
this.app.moduleConfigs[moduleName] = {
name: moduleName,
reference: absoluteRef,
config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path) || {},
config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path, undefined, this.app.config.env) || {},
};
}
}
Expand Down
1 change: 1 addition & 0 deletions plugin/config/test/ReadModule.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ describe('test/ReadModule.test.ts', () => {
config: {},
name: 'moduleA',
reference: {
name: 'moduleA',
path: path.join(fixturesPath, 'app/module-a'),
},
},
Expand Down
7 changes: 5 additions & 2 deletions standalone/standalone/src/Runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ export interface RunnerOptions {
* use inner object handlers instead
*/
innerObjects?: Record<string, object>;

env?: string;
innerObjectHandlers?: Record<string, InnerObject[]>;
}

export class Runner {
readonly cwd: string;
readonly moduleReferences: readonly ModuleReference[];
readonly moduleConfigs: Record<string, ModuleConfigHolder>;
readonly env?: string;
private loadUnitLoader: EggModuleLoader;
private runnerProto: EggPrototype;
private configSourceEggPrototypeHook: ConfigSourceLoadUnitHook;
Expand All @@ -57,6 +58,7 @@ export class Runner {

constructor(cwd: string, options?: RunnerOptions) {
this.cwd = cwd;
this.env = options?.env;
this.moduleReferences = ModuleConfigUtil.readModuleReference(this.cwd);
this.moduleConfigs = {};
this.innerObjects = {
Expand All @@ -77,13 +79,14 @@ export class Runner {
for (const reference of this.moduleReferences) {
const absoluteRef = {
path: ModuleConfigUtil.resolveModuleDir(reference.path, this.cwd),
name: reference.name,
};

const moduleName = ModuleConfigUtil.readModuleNameSync(absoluteRef.path);
this.moduleConfigs[moduleName] = {
name: moduleName,
reference: absoluteRef,
config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path) || {},
config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path, undefined, this.env) || {},
};
}
for (const moduleConfig of Object.values(this.moduleConfigs)) {
Expand Down
Loading

0 comments on commit c087226

Please sign in to comment.