Skip to content

Commit

Permalink
feat: scan framework dependencies as optional module (#184)
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 Jan 17, 2024
1 parent 3379384 commit a4908c6
Show file tree
Hide file tree
Showing 27 changed files with 289 additions and 156 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ plugin/tegg/test/fixtures/apps/**/*.js
!benchmark/**/*.js
!standalone/standalone/test/fixtures/**/node_modules
!standalone/standalone/test/fixtures/**/node_modules/**/*.js
!plugin/tegg/test/fixtures/**/node_modules
11 changes: 11 additions & 0 deletions core/common-util/src/Graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface GraphNodeObj {
export class GraphNode<T extends GraphNodeObj> {
val: T;
toNodeMap: Map<string, GraphNode<T>> = new Map();
fromNodeMap: Map<string, GraphNode<T>> = new Map();

constructor(val: T) {
this.val = val;
Expand All @@ -24,6 +25,14 @@ export class GraphNode<T extends GraphNodeObj> {
return true;
}

addFromVertex(node: GraphNode<T>) {
if (this.fromNodeMap.has(node.id)) {
return false;
}
this.fromNodeMap.set(node.id, node);
return true;
}

[inspect]() {
return this.toJSON();
}
Expand All @@ -32,6 +41,7 @@ export class GraphNode<T extends GraphNodeObj> {
return {
val: this.val,
toNodes: Array.from(this.toNodeMap.values()),
fromNodes: Array.from(this.fromNodeMap.values()),
};
}

Expand Down Expand Up @@ -84,6 +94,7 @@ export class Graph<T extends GraphNodeObj> {
}

addEdge(from: GraphNode<T>, to: GraphNode<T>): boolean {
to.addFromVertex(from);
return from.addToVertex(to);
}

Expand Down
3 changes: 3 additions & 0 deletions core/common-util/src/ModuleConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ import extend from 'extend2';
export interface ModuleReference {
name: string;
path: string;
optional?: boolean;
}

export interface InlineModuleReferenceConfig {
path: string;
optional?: boolean;
}

export interface NpmModuleReferenceConfig {
package: string;
optional?: boolean;
}

export type ModuleReferenceConfig = InlineModuleReferenceConfig | NpmModuleReferenceConfig;
Expand Down
3 changes: 3 additions & 0 deletions core/metadata/src/model/AppGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ export class AppGraph {
throw new Error('module has recursive deps: ' + loopPath);
}
this.moduleConfigList = this.graph.sort()
.filter(t => {
return t.val.moduleConfig.optional !== true || t.fromNodeMap.size > 0;
})
.map(t => t.val.moduleConfig);
}
}
35 changes: 35 additions & 0 deletions core/metadata/test/AppGraph.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import assert from 'assert';
import path from 'path';
import { AppGraph, ModuleNode } from '../src/model/AppGraph';
import { RootProto } from './fixtures/modules/app-graph-modules/root/Root';
import { UsedProto } from './fixtures/modules/app-graph-modules/used/Used';
import { UnusedProto } from './fixtures/modules/app-graph-modules/unused/Unused';

describe('test/LoadUnit/AppGraph.test.ts', () => {
it('optional module dep should work', () => {
const graph = new AppGraph();
const rootModuleNode = new ModuleNode({
name: 'foo',
path: path.join(__dirname, './fixtures/modules/app-graph-modules/root'),
});
rootModuleNode.addClazz(RootProto);
graph.addNode(rootModuleNode);
const usedOptionalModuleNode = new ModuleNode({
name: 'usedOptionalModuleNode',
path: path.join(__dirname, './fixtures/modules/app-graph-modules/used'),
optional: true,
});
usedOptionalModuleNode.addClazz(UsedProto);
graph.addNode(usedOptionalModuleNode);
const unusedOptionalModuleNode = new ModuleNode({
name: 'unusedOptionalModuleNode',
path: path.join(__dirname, './fixtures/modules/app-graph-modules/unused'),
optional: true,
});
unusedOptionalModuleNode.addClazz(UnusedProto);
graph.addNode(unusedOptionalModuleNode);
graph.build();
graph.sort();
assert(graph.moduleConfigList.length === 2);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { SingletonProto, Inject } from '@eggjs/core-decorator';
import { UsedProto } from '../used/Used';

@SingletonProto()
export class RootProto {
@Inject() usedProto: UsedProto;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "root",
"eggModule": {
"name": "root"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { SingletonProto } from '@eggjs/core-decorator';

@SingletonProto()
export class UnusedProto {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "unused",
"eggModule": {
"name": "unused"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { AccessLevel, SingletonProto } from '@eggjs/core-decorator';

@SingletonProto({
accessLevel: AccessLevel.PUBLIC,
})
export class UsedProto {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "used",
"eggModule": {
"name": "used"
}
}
10 changes: 7 additions & 3 deletions plugin/config/app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Application } from 'egg';
import { ModuleConfigUtil } from '@eggjs/tegg-common-util';
import { ModuleConfigUtil, ModuleReference } from '@eggjs/tegg-common-util';
import { ModuleScanner } from './lib/ModuleScanner';

export default class App {
private readonly app: Application;
Expand All @@ -10,12 +11,15 @@ export default class App {

configWillLoad() {
const { readModuleOptions } = this.app.config.tegg || {};
this.app.moduleReferences = ModuleConfigUtil.readModuleReference(this.app.baseDir, readModuleOptions || {});
const moduleScanner = new ModuleScanner(this.app.baseDir, readModuleOptions);
this.app.moduleReferences = moduleScanner.loadModuleReferences();

this.app.moduleConfigs = {};
for (const reference of this.app.moduleReferences) {
const absoluteRef = {
const absoluteRef: ModuleReference = {
path: ModuleConfigUtil.resolveModuleDir(reference.path, this.app.baseDir),
name: reference.name,
optional: reference.optional,
};

const moduleName = ModuleConfigUtil.readModuleNameSync(absoluteRef.path);
Expand Down
41 changes: 41 additions & 0 deletions plugin/config/lib/ModuleScanner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ModuleConfigUtil, ModuleReference, ReadModuleReferenceOptions } from '@eggjs/tegg-common-util';
import path from 'path';

export class ModuleScanner {
private readonly baseDir: string;
private readonly readModuleOptions: ReadModuleReferenceOptions;

constructor(baseDir: string, readModuleOptions: ReadModuleReferenceOptions) {
this.baseDir = baseDir;
this.readModuleOptions = readModuleOptions;
}

/**
* - load module references from config or scan from baseDir
* - load framework module as optional module reference
*/
loadModuleReferences(): readonly ModuleReference[] {
const moduleReferences = ModuleConfigUtil.readModuleReference(this.baseDir, this.readModuleOptions || {});
// eslint-disable-next-line @typescript-eslint/no-var-requires
const appPkg = require(path.join(this.baseDir, 'package.json'));
const framework = appPkg.egg?.framework;
if (!framework) {
return moduleReferences;
}
// eslint-disable-next-line @typescript-eslint/no-var-requires
const frameworkPkg = require.resolve(`${framework}/package.json`, {
paths: [ this.baseDir ],
});
const frameworkDir = path.dirname(frameworkPkg);
const optionalModuleReferences = ModuleConfigUtil.readModuleReference(frameworkDir, this.readModuleOptions || {});
return [
...moduleReferences,
...optionalModuleReferences.map(t => {
return {
...t,
optional: true,
};
}),
];
}
}
2 changes: 2 additions & 0 deletions plugin/config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
"app.js",
"app.d.ts",
"typings/index.d.ts",
"lib/**/*.js",
"lib/**/*.d.ts",
"agent.js",
"agent.d.ts"
],
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: {
optional: undefined,
name: 'moduleA',
path: path.join(fixturesPath, 'app/module-a'),
},
Expand Down
153 changes: 0 additions & 153 deletions plugin/tegg/lib/AppGraph.ts

This file was deleted.

Loading

0 comments on commit a4908c6

Please sign in to comment.