From 958b1172c09a59eb0122bfa5e8afe9b296325faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=96=E7=8C=A9?= Date: Mon, 25 Mar 2019 15:36:48 +0800 Subject: [PATCH] feat: config improvement (#45) --- README.md | 21 +- package.json | 4 +- src/generators/auto.ts | 5 +- src/generators/custom.ts | 40 +-- src/index.ts | 237 +++++++++++------- src/register.ts | 3 +- src/scripts/eggInfo.ts | 19 +- src/utils.ts | 35 +-- src/watcher.ts | 3 + test/fixtures/app-multi/app/abc/test.ts | 3 + test/fixtures/app-multi/app/bbc/test2.ts | 3 + .../app-multi/config/config.default.ts | 9 + test/fixtures/app-multi/jsconfig.json | 6 + test/fixtures/app-multi/package.json | 3 + test/fixtures/app12/package.json | 12 + test/fixtures/app12/tsHelper.json | 7 + test/fixtures/app13/package.json | 12 + test/fixtures/app13/tsCustom.json | 7 + test/generators/utils.ts | 2 +- test/index.test.ts | 58 ++++- test/utils.test.ts | 7 - 21 files changed, 345 insertions(+), 151 deletions(-) create mode 100644 test/fixtures/app-multi/app/abc/test.ts create mode 100644 test/fixtures/app-multi/app/bbc/test2.ts create mode 100644 test/fixtures/app-multi/config/config.default.ts create mode 100644 test/fixtures/app-multi/jsconfig.json create mode 100644 test/fixtures/app-multi/package.json create mode 100644 test/fixtures/app12/package.json create mode 100644 test/fixtures/app12/tsHelper.json create mode 100644 test/fixtures/app13/package.json create mode 100644 test/fixtures/app13/tsCustom.json diff --git a/README.md b/README.md index 7a2503d..31bdc38 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,26 @@ In `package.json` "enabled": true, "generator": "function", "interfaceHandle": "InstanceType<{{ 0 }}>" - }, + } + } + } + } +} +``` + +or use `dot-prop` + +```json +// {cwd}/package.json + +{ + "egg": { + "framework": "egg", + "tsHelper": { + "watchDirs.model": { + "enabled": true, + "generator": "function", + "interfaceHandle": "InstanceType<{{ 0 }}>" } } } diff --git a/package.json b/package.json index cc15938..d6e9689 100644 --- a/package.json +++ b/package.json @@ -34,13 +34,15 @@ "license": "MIT", "dependencies": { "cache-require-paths": "^0.3.0", + "chalk": "^2.4.2", "chokidar": "^2.0.1", "commander": "^2.15.1", "debug": "^3.1.0", + "dot-prop": "^4.2.0", "enquirer": "^2.3.0", "globby": "^8.0.1", "mkdirp": "^0.5.1", - "ts-node": "^7.0.0", + "ts-node": "^8.0.0", "tslib": "^1.9.3", "typescript": "^3.0.0", "yn": "^3.0.0" diff --git a/src/generators/auto.ts b/src/generators/auto.ts index 1f9cebd..59aca15 100644 --- a/src/generators/auto.ts +++ b/src/generators/auto.ts @@ -11,7 +11,10 @@ export default function(config: TsGenConfig, baseConfig: TsHelperConfig) { /* istanbul ignore else */ if (result.content) { result.content = [ - 'type AutoInstanceType any ? ReturnType : T> = U extends { new (...args: any[]): any } ? InstanceType : U;', + 'type AnyClass = new (...args: any[]) => any;', + 'type AnyFunc = (...args: any[]) => T;', + 'type CanExportFunc = AnyFunc> | AnyFunc>;', + 'type AutoInstanceType : T> = U extends AnyClass ? InstanceType : U;', result.content, ].join('\n'); } diff --git a/src/generators/custom.ts b/src/generators/custom.ts index ef46dad..2b0ce01 100644 --- a/src/generators/custom.ts +++ b/src/generators/custom.ts @@ -1,7 +1,6 @@ // generator for custom loader import { default as TsHelper, TsGenConfig, TsHelperConfig } from '..'; import { declMapping } from '../config'; -import Watcher from '../watcher'; import * as utils from '../utils'; import path from 'path'; @@ -16,7 +15,8 @@ export const defaultConfig = { ], }; -const customWatcherPrefix = 'custom-'; +const customWatcherName = 'custom'; +const customSpecRef = `${customWatcherName}_spec_ref`; const DeclareMapping = utils.pickFields(declMapping, [ 'ctx', 'app' ]); export default function(config: TsGenConfig, baseConfig: TsHelperConfig, tsHelper: TsHelper) { @@ -27,23 +27,26 @@ export default function(config: TsGenConfig, baseConfig: TsHelperConfig, tsHelpe if (eggConfig.customLoader) { Object.keys(eggConfig.customLoader).forEach(key => { const loaderConfig = eggConfig.customLoader[key]; - if ( - !loaderConfig || - !loaderConfig.directory || - !DeclareMapping[loaderConfig.inject] || - loaderConfig.tsd === false - ) return; + if (!loaderConfig || !loaderConfig.directory) { + return; + } + + loaderConfig.inject = loaderConfig.inject || 'app'; + if (!DeclareMapping[loaderConfig.inject] || loaderConfig.tsd === false) return; // custom d.ts name - const name = `${customWatcherPrefix}${key}`; + const name = `${customWatcherName}-${key}`; newCustomWatcherList.push(name); // create a custom watcher tsHelper.registerWatcher(name, { + ref: customSpecRef, distName: `${name}.d.ts`, directory: loaderConfig.directory, + pattern: loaderConfig.match, + ignore: loaderConfig.ignore, caseStyle: loaderConfig.caseStyle || 'lower', - interface: declMapping[key], + interface: loaderConfig.interface || declMapping[key], declareTo: `${DeclareMapping[loaderConfig.inject]}.${key}`, generator: 'auto', execAtInit: true, @@ -52,18 +55,15 @@ export default function(config: TsGenConfig, baseConfig: TsHelperConfig, tsHelpe } // collect watcher which is need to remove. - const removeList: Watcher[] = []; - tsHelper.watcherList.forEach(w => { - if (w.name.startsWith(customWatcherPrefix) && !newCustomWatcherList.includes(w.name)) { - removeList.push(w); - } - }); + const removeList = tsHelper.watcherList.filter(w => ( + w.ref === customSpecRef && !newCustomWatcherList.includes(w.name) + )); // remove watcher and old d.ts - return removeList.map(w => { - tsHelper.destroyWatcher(w.name); - return { dist: path.resolve(w.dtsDir, `${w.name}.d.ts`) }; - }); + tsHelper.destroyWatcher.apply(tsHelper, removeList.map(w => w.name)); + return removeList.map(w => ({ + dist: path.resolve(w.dtsDir, `${w.name}.d.ts`), + })); }; return utils.getEggInfo(baseConfig.cwd, { diff --git a/src/index.ts b/src/index.ts index da493d6..6754c77 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,15 @@ import chokidar from 'chokidar'; -import d from 'debug'; import assert from 'assert'; import { EventEmitter } from 'events'; import fs from 'fs'; +import chalk from 'chalk'; import path from 'path'; +import { get as deepGet, set as deepSet } from 'dot-prop'; import { declMapping, dtsComment, dtsCommentRE } from './config'; import Watcher, { WatchItem } from './watcher'; import * as utils from './utils'; import glob from 'globby'; -const debug = d('egg-ts-helper#index'); +const isInUnitTest = process.env.NODE_ENV === 'test'; declare global { interface PlainObject { @@ -27,7 +28,7 @@ export interface TsHelperOption { autoRemoveJs?: boolean; throttle?: number; execAtInit?: boolean; - configFile?: string; + configFile?: string | string[]; silent?: boolean; } @@ -65,9 +66,9 @@ export const defaultConfig = { watch: utils.convertString(process.env.ETS_WATCH, false), watchOptions: undefined, execAtInit: utils.convertString(process.env.ETS_EXEC_AT_INIT, false), - silent: utils.convertString(process.env.ETS_SILENT, process.env.NODE_ENV === 'test'), - watchDirs: {}, - configFile: utils.convertString(process.env.ETS_CONFIG_FILE, './tshelper'), + silent: utils.convertString(process.env.ETS_SILENT, isInUnitTest), + watchDirs: {} as PlainObject, + configFile: utils.convertString(process.env.ETS_CONFIG_FILE, '') || [ './tshelper', './tsHelper' ], }; // default watch dir @@ -104,8 +105,8 @@ export function getDefaultWatchDirs(opt: TsHelperOption = {}) { // model const eggInfo = (opt && opt.cwd) ? utils.getEggInfo(opt.cwd) : undefined; - const hasModelInCustomLoader = !!utils.deepGet(eggInfo, 'config.customLoader.model'); - const sequelizeInfo = utils.deepGet(eggInfo, 'plugins.sequelize') || {}; + const hasModelInCustomLoader = !!deepGet(eggInfo, 'config.customLoader.model'); + const sequelizeInfo = deepGet(eggInfo, 'plugins.sequelize', {}); const isUsingSequelize = sequelizeInfo.package === 'egg-sequelize' && sequelizeInfo.enable; baseConfig.model = { directory: 'app/model', @@ -114,8 +115,8 @@ export function getDefaultWatchDirs(opt: TsHelperOption = {}) { caseStyle: 'upper', enabled: !hasModelInCustomLoader, ...(isUsingSequelize ? { - interface: 'Sequelize', framework: 'sequelize', + interface: 'Sequelize', } : {}), }; @@ -157,7 +158,7 @@ export function getDefaultWatchDirs(opt: TsHelperOption = {}) { } export default class TsHelper extends EventEmitter { config: TsHelperConfig; - watcherList: Map = new Map(); + watcherList: Watcher[] = []; private cacheDist: PlainObject = {}; private dtsFileList: string[] = []; @@ -170,15 +171,14 @@ export default class TsHelper extends EventEmitter { // configure ets this.configure(options); - // clean files - this.cleanFiles(); - // init watcher this.initWatcher(); } // build all watcher build() { + // clean old files + this.cleanFiles(); this.watcherList.forEach(watcher => watcher.execute()); return this; } @@ -187,18 +187,22 @@ export default class TsHelper extends EventEmitter { destroy() { this.removeAllListeners(); this.watcherList.forEach(item => item.destroy()); - this.watcherList.clear(); + this.watcherList.length = 0; } // log - log(info) { - if (this.config.silent) { + log(info: string, ignoreSilent?: boolean) { + if (!ignoreSilent && this.config.silent) { return; } utils.log(info); } + warn(info: string) { + this.log(chalk.yellow(info), !isInUnitTest); + } + // create oneForAll file createOneForAll(dist?: string) { const config = this.config; @@ -223,16 +227,19 @@ export default class TsHelper extends EventEmitter { // init watcher private initWatcher() { Object.keys(this.config.watchDirs).forEach(key => { - this.registerWatcher(key, this.config.watchDirs[key]); + this.registerWatcher(key, this.config.watchDirs[key], false); }); } // destroy watcher - destroyWatcher(name: string) { - if (this.watcherList.has(name)) { - this.watcherList.get(name)!.destroy(); - this.watcherList.delete(name); - } + destroyWatcher(...refs: string[]) { + this.watcherList = this.watcherList.filter(w => { + if (refs.includes(w.ref)) { + w.destroy(); + return false; + } + return true; + }); } // clean old files in startup @@ -241,34 +248,84 @@ export default class TsHelper extends EventEmitter { glob.sync([ '**/*.d.ts', '!**/node_modules' ], { cwd }) .forEach(file => { const fileUrl = path.resolve(cwd, file); - const content = fs.readFileSync(fileUrl, { encoding: 'utf-8' }); + const content = fs.readFileSync(fileUrl, 'utf-8'); const isGeneratedByEts = content.match(dtsCommentRE); if (isGeneratedByEts) fs.unlinkSync(fileUrl); }); } // register watcher - registerWatcher(name: string, watchConfig: WatchItem) { - this.destroyWatcher(name); + registerWatcher(name: string, watchConfig: WatchItem & { directory: string | string[]; }, removeDuplicate: boolean = true) { + if (removeDuplicate) { + this.destroyWatcher(name); + } + if (watchConfig.hasOwnProperty('enabled') && !watchConfig.enabled) { return; } - const options = { - name, - execAtInit: this.config.execAtInit, - ...watchConfig, - }; + const directories = Array.isArray(watchConfig.directory) + ? watchConfig.directory + : [ watchConfig.directory ]; + + // support array directory. + return directories.map(dir => { + const options = { + name, + ref: name, + execAtInit: this.config.execAtInit, + ...watchConfig, + }; + + if (dir) { + options.directory = dir; + } - if (!this.config.watch) { - options.watch = false; + if (!this.config.watch) { + options.watch = false; + } + + const watcher = new Watcher(this); + watcher.on('update', this.generateTs.bind(this)); + watcher.init(options); + this.watcherList.push(watcher); + return watcher; + }); + } + + private loadWatcherConfig(config: TsHelperConfig, options: TsHelperOption) { + const configFile = options.configFile || config.configFile; + const eggInfo = utils.getEggInfo(config.cwd); + const getConfigFromPkg = pkg => (pkg.egg || {}).tsHelper; + + // read from enabled plugins + if (eggInfo.plugins) { + Object.keys(eggInfo.plugins) + .forEach(k => { + const pluginInfo = eggInfo.plugins![k]; + if (pluginInfo.enable && pluginInfo.path) { + this.mergeConfig(config, getConfigFromPkg(utils.getPkgInfo(pluginInfo.path))); + } + }); } - const watcher = new Watcher(this); - watcher.on('update', this.generateTs.bind(this)); - watcher.init(options); - this.watcherList.set(name, watcher); - return watcher; + // read from eggPaths + if (eggInfo.eggPaths) { + eggInfo.eggPaths.forEach(p => { + this.mergeConfig(config, getConfigFromPkg(utils.getPkgInfo(p))); + }); + } + + // read from package.json + this.mergeConfig(config, getConfigFromPkg(utils.getPkgInfo(config.cwd))); + + // read from local file( default to tshelper | tsHelper ) + (Array.isArray(configFile) ? configFile : [ configFile ]).forEach(f => { + this.mergeConfig(config, utils.requireFile(utils.getAbsoluteUrlByCwd(f, config.cwd))); + }); + + // merge local config and options to config + this.mergeConfig(config, options); } // configure @@ -280,29 +337,19 @@ export default class TsHelper extends EventEmitter { // base config const config = { ...defaultConfig }; - const configFile = options.configFile || config.configFile; config.cwd = options.cwd || config.cwd; - const pkgInfo = utils.getPkgInfo(config.cwd); config.framework = options.framework || defaultConfig.framework; config.watchDirs = getDefaultWatchDirs(config); + config.typings = utils.getAbsoluteUrlByCwd(config.typings, config.cwd); + this.config = config as TsHelperConfig; - // read from package.json - if (pkgInfo.egg) { - mergeConfig(config, pkgInfo.egg.tsHelper); + // deprecated framework + if (options.framework !== defaultConfig.framework) { + this.warn(`options.framework are deprecated, using default value(${defaultConfig.framework}) instead`); } - // read from local file - mergeConfig(config, utils.requireFile(utils.getAbsoluteUrlByCwd(configFile, config.cwd))); - debug('%o', config); - - // merge local config and options to config - mergeConfig(config, options); - debug('%o', options); - - // resolve config.typings to absolute url - config.typings = utils.getAbsoluteUrlByCwd(config.typings, config.cwd); - - this.config = config as TsHelperConfig; + // load watcher config + this.loadWatcherConfig(this.config, options); } private generateTs(result: GeneratorCbResult, file: string | undefined, startTime: number) { @@ -368,48 +415,56 @@ export default class TsHelper extends EventEmitter { this.cacheDist[fileUrl] = content; return false; } -} - -export function createTsHelperInstance(options: TsHelperOption) { - return new TsHelper(options); -} -// merge ts helper options -function mergeConfig(base: TsHelperConfig, ...args: TsHelperOption[]) { - args.forEach(opt => { - if (!opt) { - return; - } - - Object.keys(opt).forEach(key => { - if (key !== 'watchDirs') { - base[key] = opt[key] === undefined ? base[key] : opt[key]; - return; - } + // support dot prop config + private formatConfig(config) { + const newConfig: any = {}; + Object.keys(config).forEach(key => deepSet(newConfig, key, config[key])); + return newConfig; + } - const watchDirs = opt.watchDirs || {}; - Object.keys(watchDirs).forEach(k => { - const item = watchDirs[k]; - if (typeof item === 'boolean') { - if (base.watchDirs[k]) { - base.watchDirs[k].enabled = item; - } - } else if (item) { - // check private generator - assert(!Watcher.isPrivateGenerator(item.generator), `${item.generator} is a private generator, can not configure in config file`); + // merge ts helper options + private mergeConfig(base: TsHelperConfig, ...args: Array) { + args.forEach(opt => { + if (!opt) return; - // compatible for deprecated field - if (item.path) { - item.directory = item.path; - } + const config = this.formatConfig(opt); + Object.keys(config).forEach(key => { + if (key !== 'watchDirs') { + base[key] = config[key] === undefined ? base[key] : config[key]; + return; + } - if (base.watchDirs[k]) { - Object.assign(base.watchDirs[k], item); - } else { - base.watchDirs[k] = item; + const watchDirs = config.watchDirs || {}; + Object.keys(watchDirs).forEach(k => { + const item = watchDirs[k]; + if (typeof item === 'boolean') { + if (base.watchDirs[k]) base.watchDirs[k].enabled = item; + } else if (item) { + // check private generator + assert(!Watcher.isPrivateGenerator(item.generator), `${item.generator} is a private generator, can not configure in config file`); + + // compatible for deprecated fields + [ + [ 'path', 'directory' ], + ].forEach(([ oldValue, newValue ]) => { + if (item[oldValue]) { + item[newValue] = item[oldValue]; + } + }); + + if (base.watchDirs[k]) { + Object.assign(base.watchDirs[k], item); + } else { + base.watchDirs[k] = item; + } } - } + }); }); }); - }); + } +} + +export function createTsHelperInstance(options: TsHelperOption) { + return new TsHelper(options); } diff --git a/src/register.ts b/src/register.ts index 6d672b8..1418bb6 100644 --- a/src/register.ts +++ b/src/register.ts @@ -1,5 +1,6 @@ import cluster from 'cluster'; import d from 'debug'; +import { get as deepGet } from 'dot-prop'; import { createTsHelperInstance } from './'; import * as util from './utils'; const debug = d('egg-ts-helper#register'); @@ -22,7 +23,7 @@ function register(watch: boolean) { // write jsconfig if the project is wrote by js util.writeJsConfig(cwd); } else { - const tsNodeMode = util.deepGet(util.getPkgInfo(cwd), 'egg.typescript') || + const tsNodeMode = deepGet(util.getPkgInfo(cwd), 'egg.typescript') || process.argv.includes('--ts') || process.argv.includes('--typescript') || process.env.EGG_TYPESCRIPT === 'true'; diff --git a/src/scripts/eggInfo.ts b/src/scripts/eggInfo.ts index 0383604..d3662f5 100644 --- a/src/scripts/eggInfo.ts +++ b/src/scripts/eggInfo.ts @@ -6,16 +6,16 @@ import 'cache-require-paths'; import fs from 'fs'; import path from 'path'; import { eggInfoPath } from '../config'; -import { requireFile, getPkgInfo, writeFileSync, EggInfoResult, checkMaybeIsTsProj } from '../utils'; +import * as utils from '../utils'; const cwd = process.cwd(); -const eggInfo: EggInfoResult = {}; +const eggInfo: utils.EggInfoResult = {}; const startTime = Date.now(); -if (checkMaybeIsTsProj(cwd)) { +if (utils.checkMaybeIsTsProj(cwd)) { // only require ts-node in ts project require('ts-node/register'); } -const framework = (getPkgInfo(cwd).egg || {}).framework || 'egg'; +const framework = (utils.getPkgInfo(cwd).egg || {}).framework || 'egg'; const loader = getLoader(cwd, framework); if (loader) { try { @@ -27,7 +27,7 @@ if (loader) { // hack loadFile, ignore config file without customLoader for faster booting mockFn(loader, 'loadFile', filepath => { if (filepath && filepath.substring(filepath.lastIndexOf(path.sep) + 1).startsWith('config.')) { - const fileContent = fs.readFileSync(filepath, { encoding: 'utf-8' }); + const fileContent = fs.readFileSync(filepath, 'utf-8'); if (!fileContent.includes('customLoader')) return; } return true; @@ -41,10 +41,11 @@ if (loader) { eggInfo.plugins = loader.allPlugins; eggInfo.config = loader.config; + eggInfo.eggPaths = loader.eggPaths; eggInfo.timing = Date.now() - startTime; } -writeFileSync(eggInfoPath, JSON.stringify(eggInfo)); +utils.writeFileSync(eggInfoPath, JSON.stringify(eggInfo)); /* istanbul ignore next */ function noop() {} @@ -63,7 +64,7 @@ function getLoader(baseDir: string, framework: string) { /* istanbul ignore if */ if (!eggCore) return; const EggLoader = eggCore.EggLoader; - const egg = requireFile(frameworkPath) || requireFile(framework); + const egg = utils.requireFile(frameworkPath) || utils.requireFile(framework); /* istanbul ignore if */ if (!egg || !EggLoader) return; process.env.EGG_SERVER_ENV = 'local'; @@ -85,10 +86,10 @@ function findEggCore(baseDir: string, frameworkPath: string) { eggCorePath = path.join(frameworkPath, 'node_modules/egg-core'); } // try to load egg-core in cwd - const eggCore = requireFile(eggCorePath); + const eggCore = utils.requireFile(eggCorePath); if (!eggCore) { // try to resolve egg-core - return requireFile('egg-core'); + return utils.requireFile('egg-core'); } return eggCore; } diff --git a/src/utils.ts b/src/utils.ts index a9b9069..317016c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -37,24 +37,23 @@ export const TS_CONFIG = { }, }; -export function deepGet(obj, props: string) { - if (!obj) return; - const propList = props.split('.'); - while (propList.length) { - obj = obj[propList.shift()!]; - if (!obj) return; - } - return obj; -} - export interface GetEggInfoOpt { async?: boolean; env?: PlainObject; callback?: (result: EggInfoResult) => any; } +export interface EggPluginInfo { + from: string; + enable: boolean; + package?: string; + path: string; + etsConfig?: PlainObject; +} + export interface EggInfoResult { - plugins?: PlainObject<{ from: string; enable: boolean; package?: string; }>; + eggPaths?: string[]; + plugins?: PlainObject; config?: PlainObject; timing?: number; } @@ -99,14 +98,14 @@ export function getEggInfo(cwd: string, opt exec(cmd, opt, err => { caches.runningPromise = null; if (err) reject(err); - resolve(end(getJson(fs.readFileSync(eggInfoPath).toString()))); + resolve(end(getJson(fs.readFileSync(eggInfoPath, 'utf-8')))); }); }); return caches.runningPromise; } else { try { execSync(cmd, opt); - return end(getJson(fs.readFileSync(eggInfoPath).toString())); + return end(getJson(fs.readFileSync(eggInfoPath, 'utf-8'))); } catch (e) { return end({}); } @@ -358,9 +357,15 @@ export function extend(obj, ...args: Array>): T { return obj; } -// require package.json +// load package.json export function getPkgInfo(cwd: string) { - return requireFile(path.resolve(cwd, './package.json')) || {}; + const pkgPath = path.resolve(cwd, './package.json'); + if (!fs.existsSync(pkgPath)) return {}; + try { + return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); + } catch (e) { + return {}; + } } // format property diff --git a/src/watcher.ts b/src/watcher.ts index 02dfd9f..aadb127 100644 --- a/src/watcher.ts +++ b/src/watcher.ts @@ -13,6 +13,7 @@ const generators = utils.loadModules( ); export interface BaseWatchItem { + ref: string; directory: string; generator: string; enabled?: boolean; @@ -40,6 +41,7 @@ function formatGenerator(generator) { } export default class Watcher extends EventEmitter { + ref: string; name: string; dir: string; options: WatcherOptions; @@ -60,6 +62,7 @@ export default class Watcher extends EventEmitter { const generatorName = options.generator || 'class'; this.config = this.helper.config; this.name = options.name; + this.ref = options.ref; this.generator = this.getGenerator(generatorName); options = this.options = { trigger: [ 'add', 'unlink' ], diff --git a/test/fixtures/app-multi/app/abc/test.ts b/test/fixtures/app-multi/app/abc/test.ts new file mode 100644 index 0000000..cff0e9b --- /dev/null +++ b/test/fixtures/app-multi/app/abc/test.ts @@ -0,0 +1,3 @@ +export default () => { + return { c: 123 }; +}; diff --git a/test/fixtures/app-multi/app/bbc/test2.ts b/test/fixtures/app-multi/app/bbc/test2.ts new file mode 100644 index 0000000..cff0e9b --- /dev/null +++ b/test/fixtures/app-multi/app/bbc/test2.ts @@ -0,0 +1,3 @@ +export default () => { + return { c: 123 }; +}; diff --git a/test/fixtures/app-multi/config/config.default.ts b/test/fixtures/app-multi/config/config.default.ts new file mode 100644 index 0000000..b47a25d --- /dev/null +++ b/test/fixtures/app-multi/config/config.default.ts @@ -0,0 +1,9 @@ +export const key = '123'; +export const customLoader = { + abc: { + directory: [ + 'app/abc', + 'app/bbc', + ], + }, +}; diff --git a/test/fixtures/app-multi/jsconfig.json b/test/fixtures/app-multi/jsconfig.json new file mode 100644 index 0000000..fac5417 --- /dev/null +++ b/test/fixtures/app-multi/jsconfig.json @@ -0,0 +1,6 @@ +{ + "include": [ + "**/*" + ], + "mySpecConfig": true +} \ No newline at end of file diff --git a/test/fixtures/app-multi/package.json b/test/fixtures/app-multi/package.json new file mode 100644 index 0000000..fd1f8f6 --- /dev/null +++ b/test/fixtures/app-multi/package.json @@ -0,0 +1,3 @@ +{ + "name": "app" +} diff --git a/test/fixtures/app12/package.json b/test/fixtures/app12/package.json new file mode 100644 index 0000000..c16a5e9 --- /dev/null +++ b/test/fixtures/app12/package.json @@ -0,0 +1,12 @@ +{ + "name": "app", + "egg": { + "tsHelper": { + "watchDirs.model.enabled": false, + "watchDirs.service": false, + "watchDirs.dal": { + "interface": "IDAL2" + } + } + } +} diff --git a/test/fixtures/app12/tsHelper.json b/test/fixtures/app12/tsHelper.json new file mode 100644 index 0000000..1d454b8 --- /dev/null +++ b/test/fixtures/app12/tsHelper.json @@ -0,0 +1,7 @@ +{ + "watchDirs.dal": { + "directory": "app/dal/dao", + "caseStyle": "upper", + "watch": false + } +} \ No newline at end of file diff --git a/test/fixtures/app13/package.json b/test/fixtures/app13/package.json new file mode 100644 index 0000000..c16a5e9 --- /dev/null +++ b/test/fixtures/app13/package.json @@ -0,0 +1,12 @@ +{ + "name": "app", + "egg": { + "tsHelper": { + "watchDirs.model.enabled": false, + "watchDirs.service": false, + "watchDirs.dal": { + "interface": "IDAL2" + } + } + } +} diff --git a/test/fixtures/app13/tsCustom.json b/test/fixtures/app13/tsCustom.json new file mode 100644 index 0000000..1d454b8 --- /dev/null +++ b/test/fixtures/app13/tsCustom.json @@ -0,0 +1,7 @@ +{ + "watchDirs.dal": { + "directory": "app/dal/dao", + "caseStyle": "upper", + "watch": false + } +} \ No newline at end of file diff --git a/test/generators/utils.ts b/test/generators/utils.ts index 81357e8..adcea8b 100644 --- a/test/generators/utils.ts +++ b/test/generators/utils.ts @@ -15,7 +15,7 @@ export function triggerGenerator w.name === name)!; assert(watcher, 'watcher is not exist'); const dir = path.resolve(appDir, watcher.options.directory); watcher.init({ diff --git a/test/index.test.ts b/test/index.test.ts index 01d7803..d9aee16 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -3,6 +3,7 @@ import del from 'del'; import fs from 'fs'; import mkdirp from 'mkdirp'; import path from 'path'; +import mm from 'egg-mock'; import { sleep, spawn, getStd, eggBin, timeoutPromise, mockFile, createTsHelper, createNodeModuleSym } from './utils'; import assert = require('assert'); import TsHelper, { getDefaultWatchDirs } from '../dist/'; @@ -13,6 +14,8 @@ describe('index.test.ts', () => { del.sync(path.resolve(__dirname, './fixtures/*/typings'), { force: true }); }); + afterEach(mm.restore); + it('should works without error', async () => { const dir = path.resolve(__dirname, './fixtures/app/app/service/test'); mkdirp.sync(dir); @@ -253,8 +256,8 @@ describe('index.test.ts', () => { watchDirs, }); - assert(tsHelper.watcherList.size === 1); - assert(!!tsHelper.watcherList.has('proxy')); + assert(tsHelper.watcherList.length === 1); + assert(!!tsHelper.watcherList.find(w => w.name === 'proxy')); }); it('should auto create customLoader with config', () => { @@ -282,6 +285,22 @@ describe('index.test.ts', () => { assert(!fs.existsSync(path.resolve(customPath, './typings/app/custom6/custom-custom6.d.ts'))); }); + it('should support multiple directories', () => { + const multiPath = path.resolve(__dirname, './fixtures/app-multi'); + createTsHelper({ + cwd: multiPath, + watch: false, + execAtInit: true, + }); + + const dts1 = path.resolve(multiPath, './typings/app/abc/custom-abc.d.ts'); + const dts2 = path.resolve(multiPath, './typings/app/bbc/custom-abc.d.ts'); + assert(fs.existsSync(dts1)); + assert(fs.existsSync(dts2)); + assert(fs.readFileSync(dts1, 'utf-8').match(/interface T_custom_abc {\s+test: AutoInstanceType;/)); + assert(fs.readFileSync(dts2, 'utf-8').match(/interface T_custom_abc {\s+test2: AutoInstanceType;/)); + }); + it('should support read framework by package.json', () => { tsHelper = createTsHelper({ cwd: path.resolve(__dirname, './fixtures/app3'), @@ -309,8 +328,8 @@ describe('index.test.ts', () => { const item = (watchDirs[k] as any); return !item.hasOwnProperty('enabled') || item.enabled; }).length; - assert(tsHelper.watcherList.size === len - 2); - assert(!!tsHelper.watcherList.has('controller')); + assert(tsHelper.watcherList.length === len - 2); + assert(!!tsHelper.watcherList.find(w => w.name === 'controller')); }); it('should works without error in real app', async () => { @@ -386,4 +405,35 @@ describe('index.test.ts', () => { assert(fs.existsSync(path.resolve(baseDir, './typings/app/middleware/index.d.ts'))); assert(fs.existsSync(path.resolve(baseDir, './typings/config/index.d.ts'))); }); + + it('should support tsHelper.json and dot-prop', async () => { + const baseDir = path.resolve(__dirname, './fixtures/app12/'); + tsHelper = createTsHelper({ + cwd: baseDir, + execAtInit: false, + autoRemoveJs: false, + }); + + assert(tsHelper.config.watchDirs.dal); + assert(tsHelper.config.watchDirs.dal.interface === 'IDAL2'); + assert(tsHelper.config.watchDirs.dal.directory === 'app/dal/dao'); + assert(tsHelper.config.watchDirs.model.enabled === false); + assert(tsHelper.config.watchDirs.service.enabled === false); + }); + + it('should support custom config file', async () => { + const baseDir = path.resolve(__dirname, './fixtures/app12/'); + mm(process.env, 'ETS_CONFIG_FILE', path.resolve(baseDir, 'tsCustom.json')); + tsHelper = createTsHelper({ + cwd: baseDir, + execAtInit: false, + autoRemoveJs: false, + }); + + assert(tsHelper.config.watchDirs.dal); + assert(tsHelper.config.watchDirs.dal.interface === 'IDAL2'); + assert(tsHelper.config.watchDirs.dal.directory === 'app/dal/dao'); + assert(tsHelper.config.watchDirs.model.enabled === false); + assert(tsHelper.config.watchDirs.service.enabled === false); + }); }); diff --git a/test/utils.test.ts b/test/utils.test.ts index 5badb29..5e53a0c 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -201,11 +201,4 @@ describe('utils.test.ts', () => { json = utils.getJson('{ "abc: 123 }'); assert(json); }); - - it('should deepGet without error', async () => { - assert(utils.deepGet(undefined, 'abc') === undefined); - assert(utils.deepGet({ abc: 1 }, 'abc') === 1); - assert(utils.deepGet({ abc: { bbc: 1 } }, 'abc.bbc') === 1); - assert(utils.deepGet({ abc: undefined }, 'abc.bbc') === undefined); - }); });