diff --git a/.autod.conf b/.autod.conf index d5d0bbd3..6ccc1a3d 100644 --- a/.autod.conf +++ b/.autod.conf @@ -13,6 +13,7 @@ module.exports = { 'co-mocha', 'intelli-espower-loader', 'power-assert', + 'espower-typescript', 'ypkgfiles', ], devdep: [ diff --git a/.gitignore b/.gitignore index 348042ed..f22ee7c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,18 @@ node_modules/ coverage/ -!test/fixtures/custom-framework-app/node_modules/ +test/fixtures/custom-framework-app/node_modules/ + +test/fixtures/demo-app/node_modules/aliyun-egg/ !test/fixtures/demo-app/node_modules/aliyun-egg/node_modules/ + +test/fixtures/ts/node_modules/aliyun-egg/ +!test/fixtures/ts/node_modules/aliyun-egg/node_modules/ + !test/fixtures/test-files-glob/** !test/fixtures/test-files-stack/node_modules/ !test/fixtures/example/node_modules/ + + **/run/*.json .tmp .vscode diff --git a/README.md b/README.md index 3e10ed6f..c7637d8d 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,8 @@ $ egg-bin dev - `--port` server port, default to `7001`. - `--cluster` worker process number, skip this argvs will start only `1` worker, provide this without value will start `cpu` count worker. - `--sticky` start a sticky cluster server, default to `false`. +- `--typescript` / `--ts` enable typescript support, default to `false`. +- `--require` will add to `execArgv`, support multiple. ### debug @@ -130,6 +132,7 @@ You can pass any mocha argv. - `--grep` only run tests matching - `--timeout` milliseconds, default to 30000 - `--full-trace` display the full stack trace, default to false. +- `--typescript` / `--ts` enable typescript support, default to `false`. - see more at https://mochajs.org/#usage #### environment diff --git a/lib/cmd/dev.js b/lib/cmd/dev.js index 1292475a..42643c09 100644 --- a/lib/cmd/dev.js +++ b/lib/cmd/dev.js @@ -33,6 +33,11 @@ class DevCommand extends Command { description: 'specify framework that can be absolute path or npm package', type: 'string', }, + require: { + description: 'will add to execArgv --require', + type: 'array', + alias: 'r', + }, }; } @@ -40,6 +45,18 @@ class DevCommand extends Command { return 'Start server at local dev mode'; } + get context() { + const context = super.context; + const { argv, execArgvObj } = context; + execArgvObj.require = execArgvObj.require || []; + // add require to execArgv + if (argv.require) { + execArgvObj.require.push(...argv.require); + argv.require = undefined; + } + return context; + } + * run(context) { const devArgs = yield this.formatArgs(context); const options = { diff --git a/lib/cmd/test.js b/lib/cmd/test.js index dd04ef6a..e2c6554a 100644 --- a/lib/cmd/test.js +++ b/lib/cmd/test.js @@ -85,13 +85,19 @@ class TestCommand extends Command { requireArr.push(require.resolve('intelli-espower-loader')); } + // for power-assert + if (testArgv.typescript) { + requireArr.push('espower-typescript/guess'); + } + testArgv.require = requireArr; // collect test files let files = testArgv._.slice(); if (!files.length) { - files = [ process.env.TESTS || 'test/**/*.test.js' ]; + files = [ process.env.TESTS || `test/**/*.test.${testArgv.typescript ? 'ts' : 'js'}` ]; } + // expand glob and skip node_modules and fixtures files = globby.sync(files.concat('!test/**/{fixtures, node_modules}/**/*.test.js')); files.sort(); @@ -108,6 +114,7 @@ class TestCommand extends Command { testArgv.r = undefined; testArgv.t = undefined; testArgv.g = undefined; + testArgv.typescript = undefined; return this.helper.unparseArgv(testArgv); } diff --git a/lib/command.js b/lib/command.js index 096a9966..a52b8758 100644 --- a/lib/command.js +++ b/lib/command.js @@ -1,5 +1,6 @@ 'use strict'; +const path = require('path'); const BaseCommand = require('common-bin'); class Command extends BaseCommand { @@ -9,16 +10,33 @@ class Command extends BaseCommand { execArgv: true, removeAlias: true, }; + + // common-bin setter, don't care about override at sub class + // https://github.com/node-modules/common-bin/blob/master/lib/command.js#L158 + this.options = { + typescript: { + description: 'whether enable typescript support, will load `ts-node/register` etc', + type: 'boolean', + alias: 'ts', + }, + }; } get context() { const context = super.context; + const { argv, debugPort, execArgvObj } = context; // compatible - if (context.debugPort) context.debug = context.debugPort; + if (debugPort) context.debug = debugPort; // remove unuse args - context.argv.$0 = undefined; + argv.$0 = undefined; + + // execArgv + if (argv.typescript) { + execArgvObj.require = execArgvObj.require || []; + execArgvObj.require.push(path.join(__dirname, './ts-helper.js')); + } return context; } diff --git a/lib/ts-helper.js b/lib/ts-helper.js new file mode 100644 index 00000000..8121e4cd --- /dev/null +++ b/lib/ts-helper.js @@ -0,0 +1,28 @@ +'use strict'; + +require('ts-node').register({ + typeCheck: true, + compilerOptions: { + target: 'es2017', + module: 'commonjs', + strict: true, + moduleResolution: 'node', + noImplicitAny: false, + experimentalDecorators: true, + emitDecoratorMetadata: true, + charset: 'utf8', + allowJs: false, + pretty: true, + noEmitOnError: false, + noUnusedLocals: true, + noUnusedParameters: true, + allowUnreachableCode: false, + allowUnusedLabels: false, + strictPropertyInitialization: false, + noFallthroughCasesInSwitch: true, + skipLibCheck: true, + skipDefaultLibCheck: true, + inlineSourceMap: true, + importHelpers: true, + }, +}); diff --git a/package.json b/package.json index 60159475..4c0b48c7 100644 --- a/package.json +++ b/package.json @@ -9,24 +9,27 @@ }, "dependencies": { "autod": "^3.0.1", - "chalk": "^2.3.1", - "co-mocha": "^1.2.1", - "common-bin": "^2.7.1", + "chalk": "^2.3.2", + "co-mocha": "^1.2.2", + "common-bin": "^2.7.2", "debug": "^3.1.0", "detect-port": "^1.2.2", "egg-utils": "^2.3.0", "globby": "^8.0.1", "inspector-proxy": "^1.2.1", "intelli-espower-loader": "^1.0.1", - "mocha": "^5.0.1", + "espower-typescript": "^8.1.3", + "mocha": "^5.0.5", "mz-modules": "^2.1.0", - "nyc": "^11.4.1", + "nyc": "^11.6.0", "power-assert": "^1.4.4", "semver": "^5.5.0", - "test-exclude": "^4.2.0", + "test-exclude": "^4.2.1", + "ts-node": "^5.0.1", "ypkgfiles": "^1.5.0" }, "devDependencies": { + "@types/mocha": "^5.0.0", "autod": "^3.0.1", "babel": "^6.3.26", "babel-preset-airbnb": "^1.0.1", @@ -35,7 +38,7 @@ "cross-env": "^3.1.3", "egg": "^1.8.0", "egg-ci": "^1.8.0", - "egg-mock": "^3.14.0", + "egg-mock": "^3.15.1", "enzyme": "^2.0.0", "eslint": "^4.12.1", "eslint-config-egg": "^7.0.0", diff --git a/test/fixtures/require-script.js b/test/fixtures/require-script.js new file mode 100644 index 00000000..00d46285 --- /dev/null +++ b/test/fixtures/require-script.js @@ -0,0 +1,3 @@ +'use strict'; + +console.log('hey, you require me by --require'); diff --git a/test/fixtures/ts/app.js b/test/fixtures/ts/app.js new file mode 100644 index 00000000..e46106f7 --- /dev/null +++ b/test/fixtures/ts/app.js @@ -0,0 +1,6 @@ +'use strict'; + + +module.exports = app => { + app.logger.info('###', require('./test.ts').default.name); +}; diff --git a/test/fixtures/ts/config/config.default.js b/test/fixtures/ts/config/config.default.js new file mode 100644 index 00000000..762c2647 --- /dev/null +++ b/test/fixtures/ts/config/config.default.js @@ -0,0 +1,3 @@ +'use strict'; + +exports.key = '12345'; diff --git a/test/fixtures/ts/node_modules/aliyun-egg/index.js b/test/fixtures/ts/node_modules/aliyun-egg/index.js new file mode 100644 index 00000000..c1500965 --- /dev/null +++ b/test/fixtures/ts/node_modules/aliyun-egg/index.js @@ -0,0 +1,20 @@ +'use strict'; + +const egg = require('../../../../../node_modules/egg'); + +module.exports = Object.assign({}, egg); + +module.exports.startCluster = options => { + console.log('options.typescript=%s', options.typescript); + console.log('options: %j', options); + if (process.execArgv.length) { + console.log('process.execArgv:', process.execArgv); + } + + // make sure exit + setTimeout(() => { + console.log('exit by master test end') + process.exit(0); + }, 5000); + return egg.startCluster(options); +}; diff --git a/test/fixtures/ts/node_modules/aliyun-egg/package.json b/test/fixtures/ts/node_modules/aliyun-egg/package.json new file mode 100644 index 00000000..4e36e661 --- /dev/null +++ b/test/fixtures/ts/node_modules/aliyun-egg/package.json @@ -0,0 +1,6 @@ +{ + "name": "aliyun-egg", + "dependencies": { + "egg": "*" + } +} diff --git a/test/fixtures/ts/package.json b/test/fixtures/ts/package.json new file mode 100644 index 00000000..3cb71fd5 --- /dev/null +++ b/test/fixtures/ts/package.json @@ -0,0 +1,6 @@ +{ + "name": "ts", + "egg": { + "framework": "aliyun-egg" + } +} \ No newline at end of file diff --git a/test/fixtures/ts/test.ts b/test/fixtures/ts/test.ts new file mode 100644 index 00000000..61519692 --- /dev/null +++ b/test/fixtures/ts/test.ts @@ -0,0 +1,3 @@ +'use strict'; + +export default { name: 'egg from ts' }; diff --git a/test/fixtures/ts/test/a.test.js b/test/fixtures/ts/test/a.test.js new file mode 100644 index 00000000..32c1eddc --- /dev/null +++ b/test/fixtures/ts/test/a.test.js @@ -0,0 +1,7 @@ +'use strict'; + +describe('a.test.js', () => { + it('should success', () => { + throw 'should not load js files'; + }); +}); diff --git a/test/fixtures/ts/test/sub.ts b/test/fixtures/ts/test/sub.ts new file mode 100644 index 00000000..61519692 --- /dev/null +++ b/test/fixtures/ts/test/sub.ts @@ -0,0 +1,3 @@ +'use strict'; + +export default { name: 'egg from ts' }; diff --git a/test/fixtures/ts/test/typescript.test.ts b/test/fixtures/ts/test/typescript.test.ts new file mode 100644 index 00000000..afe066d7 --- /dev/null +++ b/test/fixtures/ts/test/typescript.test.ts @@ -0,0 +1,17 @@ +'use strict'; + +const assert = require('assert'); + +describe('typescript.test.ts', () => { + it('should success', () => { + const obj = require('./sub'); + console.log('###', obj.default.name); + assert(obj.default.name === 'egg from ts'); + }); + + it('should fail', () => { + const obj = require('./sub'); + console.log('###', obj.default.name); + assert(obj.default.name === 'wrong assert ts'); + }); +}); diff --git a/test/lib/cmd/dev.test.js b/test/lib/cmd/dev.test.js index cdaf7c13..230d5dca 100644 --- a/test/lib/cmd/dev.test.js +++ b/test/lib/cmd/dev.test.js @@ -137,4 +137,14 @@ describe('test/lib/cmd/dev.test.js', () => { .expect('code', 0) .end(done); }); + + it('should support --require', () => { + const script = path.join(__dirname, '../../fixtures/require-script'); + mm(process.env, 'NODE_ENV', 'development'); + return coffee.fork(eggBin, [ 'dev', '--require', script ], { cwd }) + // .debug() + .expect('stdout', /hey, you require me by --require/) + .expect('code', 0) + .end(); + }); }); diff --git a/test/ts.test.js b/test/ts.test.js new file mode 100644 index 00000000..8d2a3460 --- /dev/null +++ b/test/ts.test.js @@ -0,0 +1,34 @@ +'use strict'; + +const path = require('path'); +const coffee = require('coffee'); +const mm = require('mm'); + +describe('test/ts.test.js', () => { + const eggBin = require.resolve('../bin/egg-bin'); + const cwd = path.join(__dirname, './fixtures/ts'); + + afterEach(mm.restore); + + it('should support ts', () => { + mm(process.env, 'NODE_ENV', 'development'); + return coffee.fork(eggBin, [ 'dev', '--typescript' ], { cwd }) + .debug() + .expect('stdout', /### egg from ts/) + .expect('stdout', /options.typescript=true/) + .expect('stdout', /egg started/) + .expect('code', 0) + .end(); + }); + + it('should support ts test', () => { + mm(process.env, 'NODE_ENV', 'development'); + return coffee.fork(eggBin, [ 'test', '--typescript' ], { cwd }) + .debug() + .notExpect('stdout', /false == true/) + .notExpect('stdout', /should not load js files/) + .expect('stdout', /--- \[string\] 'wrong assert ts'/) + .expect('code', 1) + .end(); + }); +});