Skip to content

Commit c0c0836

Browse files
authored
Merge pull request #26 from kitesjs/api-discover
Api discover
2 parents bb7f52c + 146d2e4 commit c0c0836

File tree

12 files changed

+131
-57
lines changed

12 files changed

+131
-57
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ package-lock.json
7070
prod.config.json
7171
dev.config.json
7272
test*.js
73+
test*.ts
7374

7475
# bundle
7576
packages/**/*.d.ts

README.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,6 @@ Template-based Web Application Framework
99

1010
**Kites** is a framework providing `dynamic applications` assembling and `Template-based` extracting. Namely it contains a lot of templates and extensions to help building a new application quickly.
1111

12-
Take a look at this:
13-
14-
```ts
15-
@Launcher({
16-
imports?: [], // ... kites extensions manually imported here
17-
discover?: Array<string> | string | boolean; // autodiscover extensions in app's child directories or given path
18-
})
19-
class App {}
20-
```
21-
2212
Features
2313
=======
2414

kites.config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"logger": {
55
"console": {
66
"transport": "console",
7-
"level": "info"
7+
"level": "debug"
88
}
99

1010
},

packages/core/engine/kites-instance.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ describe('kites engine', () => {
7777
const app = await engine({
7878
discover: true,
7979
extensionsLocationCache: false,
80+
// discover extensions from appDirectory (by default)
81+
appDirectory: rootDirectory,
8082
rootDirectory: rootDirectory
8183
}).init();
8284

packages/core/engine/kites-instance.ts

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as path from 'path';
66
import { Logger, transports } from 'winston';
77

88
import { EventEmitter } from 'events';
9-
import { ExtensionsManager } from '../extensions/extensions-manager';
9+
import { DiscoverOptions, ExtensionsManager } from '../extensions/extensions-manager';
1010
import { createLogger } from '../logger';
1111
import { EventCollectionEmitter } from './event-collection';
1212

@@ -26,14 +26,13 @@ export type KitesReadyCallback = (kites: IKites) => void;
2626
export interface IKitesOptions {
2727
[key: string]: any;
2828
providers?: Array<Type<any>>;
29-
discover?: boolean | string; // string for path discovery
29+
discover?: DiscoverOptions; // options for discovery
3030
loadConfig?: boolean;
3131
rootDirectory?: string;
3232
appDirectory?: string;
3333
parentModuleDirectory?: string;
3434
env?: string;
3535
logger?: any;
36-
mode?: string;
3736
cacheAvailableExtensions?: any;
3837
tempDirectory?: string;
3938
extensionsLocationCache?: boolean;
@@ -52,7 +51,7 @@ export interface IKites {
5251
logger: Logger;
5352
container: Container;
5453
afterConfigLoaded(fn: KitesReadyCallback): IKites;
55-
discover(option?: string | boolean): IKites;
54+
discover(option: DiscoverOptions): IKites;
5655
use(extension: KitesExtension | ExtensionDefinition | ExtensionDefinition[]): IKites;
5756
init(): Promise<IKites>;
5857
}
@@ -111,6 +110,7 @@ export class KitesInstance extends EventEmitter implements IKites {
111110
// EXAMPLE 1: kites.discover(true)
112111
// EXAMPLE 2: kites.discover(false)
113112
// EXAMPLE 3: kites.discover('/path/to/discover')
113+
// EXAMPLE 4: kites.discover([true, 2, '/path/to/discover', '/path2'])
114114
discover: false,
115115
env: process.env.NODE_ENV || 'development',
116116
logger: {
@@ -195,15 +195,8 @@ export class KitesInstance extends EventEmitter implements IKites {
195195
/**
196196
* Enable auto discover extensions
197197
*/
198-
discover(option: string | boolean) {
199-
if (typeof option === 'string') {
200-
this.options.discover = true;
201-
this.options.rootDirectory = option;
202-
} else if (typeof option === 'boolean') {
203-
this.options.discover = option;
204-
} else {
205-
this.options.discover = true;
206-
}
198+
discover(option: DiscoverOptions) {
199+
this.options.discover = option;
207200
return this;
208201
}
209202

@@ -241,7 +234,7 @@ export class KitesInstance extends EventEmitter implements IKites {
241234
this._silentLogs(this.logger);
242235
}
243236

244-
this._initOptions();
237+
await this._initOptions();
245238
this.logger.info(`Initializing ${this.name}@${this.version} in mode "${this.options.env}"${this.options.loadConfig ? ', using configuration file ' + this.options.configFile : ''}`);
246239

247240
await this.extensionsManager.init();
@@ -254,10 +247,10 @@ export class KitesInstance extends EventEmitter implements IKites {
254247
return this;
255248
}
256249

257-
private _initOptions() {
250+
private async _initOptions() {
258251
if (this.options.loadConfig) {
259-
this._loadConfig();
260-
this.fnAfterConfigLoaded(this);
252+
await this._loadConfig();
253+
await this.fnAfterConfigLoaded(this);
261254
}
262255

263256
return this._configureWinstonTransports(this.options.logger);
@@ -269,16 +262,17 @@ export class KitesInstance extends EventEmitter implements IKites {
269262
});
270263
}
271264

272-
private _loadConfig() {
273-
var nconf = require('nconf');
265+
private async _loadConfig() {
266+
const config = await import('nconf');
267+
const nconf = new config.Provider();
268+
274269
let nfn = nconf.argv()
275270
.env({
276271
separator: ':'
277272
})
278273
.env({
279274
separator: '_'
280-
})
281-
.defaults(this.options);
275+
});
282276

283277
if (!this.options.configFile) {
284278

@@ -306,6 +300,8 @@ export class KitesInstance extends EventEmitter implements IKites {
306300
}
307301
}
308302

303+
// 'if nothing else': 'use this value'
304+
nconf.defaults(this.options);
309305
this.options = nconf.get();
310306
}
311307

packages/core/extensions/discover.spec.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import { discover } from './discover';
55

66
describe('Discover extensions', () => {
77
it('should load an extension', async () => {
8-
const rootDirectory = join(__dirname, '../test');
8+
const location = join(__dirname, '../test');
9+
const logger = createLogger('discover');
910
let extensions: any = await discover({
10-
logger: createLogger('discover'),
11-
rootDirectory: rootDirectory
11+
logger,
12+
rootDirectory: [location]
1213
});
13-
console.log('rootDirectory: ', rootDirectory);
14+
logger.info('Discovery location: ' + location);
1415
expect(extensions.length).eq(1);
1516
});
1617
});

packages/core/extensions/discover.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,22 @@ import * as path from 'path';
33
import { Logger } from 'winston';
44
import * as cache from './location-cache';
55

6+
/**
7+
* Discover options can be:
8+
* + boolean: true/false
9+
* + string: '/path/to/discover'
10+
* + array: [true, 2, '/path/to/discover', '/path2']
11+
*/
12+
export type DiscoverOptions = string | boolean | [boolean, number, ...string[]];
13+
14+
/**
15+
* Options to discover
16+
*/
617
export interface IDiscoverOptions {
718
readonly logger: Logger;
8-
readonly rootDirectory: any;
9-
readonly mode?: any;
19+
readonly depth?: number;
20+
readonly rootDirectory: string[];
21+
readonly env?: any;
1022
readonly cacheAvailableExtensions?: any;
1123
readonly tempDirectory?: any;
1224
readonly extensionsLocationCache?: any;

packages/core/extensions/extensions-manager.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import * as _ from 'lodash';
33
import * as os from 'os';
44
import * as path from 'path';
55
import { IKites } from '..';
6-
import { discover } from './discover';
6+
import { discover, DiscoverOptions } from './discover';
77
import { ExtensionDefinition, ExtensionOptions, KitesExtension } from './extensions';
88
import sorter from './sorter';
99

10-
export class ExtensionsManager extends EventEmitter {
10+
class ExtensionsManager extends EventEmitter {
1111
protected kites: IKites;
1212
protected availableExtensions: KitesExtension[];
1313
protected usedExtensions: KitesExtension[];
@@ -49,18 +49,37 @@ export class ExtensionsManager extends EventEmitter {
4949
*/
5050
async init() {
5151
this.availableExtensions = [];
52-
// auto discover extensions
53-
if (this.kites.options.discover || (this.kites.options.discover !== false && this.usedExtensions.length === 0)) {
52+
53+
let autodiscover = false;
54+
if (typeof this.kites.options.discover === 'undefined') {
55+
this.kites.options.discover = [false, 0];
56+
} else if (typeof this.kites.options.discover === 'boolean') {
57+
this.kites.options.discover = [this.kites.options.discover, 2, this.kites.options.appDirectory];
58+
} else if (typeof this.kites.options.discover === 'string') {
59+
this.kites.options.discover = [true, 2, this.kites.options.discover];
60+
} else if (this.kites.options.discover.length < 2) {
61+
throw new Error('Discover options as array requires at least 2 elements! Example: [true, 2]');
62+
}
63+
64+
// autodiscover extensions
65+
autodiscover = this.kites.options.discover.shift() as boolean;
66+
67+
if (autodiscover) {
68+
let depth = this.kites.options.discover.shift() as number;
69+
let directories = this.kites.options.discover as string[];
5470
let extensions = await discover({
5571
cacheAvailableExtensions: this.kites.options.cacheAvailableExtensions,
5672
extensionsLocationCache: this.kites.options.extensionsLocationCache,
5773
logger: this.kites.logger,
58-
mode: this.kites.options.mode,
59-
rootDirectory: this.kites.options.rootDirectory,
74+
env: this.kites.options.env,
75+
depth: depth,
76+
rootDirectory: directories,
6077
tempDirectory: this.kites.options.tempDirectory,
6178
});
62-
this.kites.logger.debug('Discovered ' + extensions.length + ' extensions');
79+
this.kites.logger.debug('Autodiscover ' + extensions.length + ' extensions!');
6380
this.availableExtensions = this.availableExtensions.concat(extensions);
81+
} else {
82+
this.kites.logger.debug('Autodiscover is not enabled!');
6483
}
6584
// filter extensions will be loaded?
6685
this.availableExtensions = this.availableExtensions.concat(this.usedExtensions);
@@ -71,7 +90,6 @@ export class ExtensionsManager extends EventEmitter {
7190

7291
this.availableExtensions.sort(sorter);
7392
return this.useMany(this.availableExtensions);
74-
7593
}
7694

7795
/**
@@ -130,7 +148,7 @@ export class ExtensionsManager extends EventEmitter {
130148
let errorMsg;
131149

132150
if (!extension.name) {
133-
errorMsg = `Error when loading anonymous extension${extension.directory != null ? ` at ${extension.directory}` : ''}${os.EOL}${e.stack}`;
151+
errorMsg = `Error when loading anonymous extension ${extension.directory != null ? ` at ${extension.directory}` : ''}${os.EOL}${e.stack}`;
134152
} else {
135153
errorMsg = `Error when loading extension ${extension.name}${os.EOL}${e.stack}`;
136154
}
@@ -141,3 +159,8 @@ export class ExtensionsManager extends EventEmitter {
141159
}
142160

143161
}
162+
163+
export {
164+
ExtensionsManager,
165+
DiscoverOptions
166+
};

packages/core/extensions/fs.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,51 @@ export function walkSync(rootPath: string, fileName: string, exclude?: string |
7676

7777
return results;
7878
}
79+
80+
/**
81+
* Discovery file list with depth level
82+
* @param dirname Folder to scan extensions
83+
* @param filename Pattern for searching
84+
* @param depth max level to discover
85+
* @param exclude pattern to exclude searching
86+
*/
87+
export function walkSyncLevel(dirname: string[], filename: string, depth: number = 2, exclude?: string) {
88+
// console.log('Start searching: ', dirname);
89+
90+
function readFiles(candidate: string, level: number): string[] {
91+
// console.log('Find in: ', candidate);
92+
93+
let results: string[] = [];
94+
let list: string[];
95+
try {
96+
list = fs.readdirSync(candidate);
97+
} catch (err) {
98+
// no permissions to read folder for example
99+
// just skip it
100+
list = [];
101+
}
102+
103+
for (const item of list) {
104+
let fullname = path.join(candidate, item);
105+
if (fullname.indexOf(exclude as string) < 0) {
106+
if (fs.statSync(fullname).isDirectory()) {
107+
if (level < depth) {
108+
const next = readFiles(fullname, level + 1);
109+
results = results.concat(next);
110+
}
111+
} else if (item === filename) {
112+
results.push(fullname);
113+
}
114+
}
115+
}
116+
117+
return results;
118+
}
119+
120+
let vResults = [];
121+
for (const item of dirname) {
122+
vResults = vResults.concat(readFiles(item, 0));
123+
}
124+
125+
return vResults;
126+
}

packages/core/extensions/location-cache.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ describe('Location cache', () => {
1414
const rootDirectory = join(__dirname, '../test');
1515
let extensions: any = await cache.get({
1616
logger: createLogger('location-cache'),
17-
rootDirectory: rootDirectory
17+
rootDirectory: [rootDirectory]
1818
});
1919
console.log('Found: ', extensions, rootDirectory);
2020
expect(extensions.length).eq(1);

0 commit comments

Comments
 (0)