From bddc8ca760fc2a293fde8d65579d896e5fd9b203 Mon Sep 17 00:00:00 2001 From: TZ Date: Sat, 31 Mar 2018 22:02:11 +0800 Subject: [PATCH 1/3] feat: provides source map support for stack traces --- .eslintignore | 2 + .gitignore | 4 +- README.md | 2 + lib/cmd/start.js | 4 +- lib/command.js | 38 ++++++ package.json | 2 + test/fixtures/ts-pkg/app/controller/home.ts | 15 +++ test/fixtures/ts-pkg/app/router.js | 6 + test/fixtures/ts-pkg/config/config.default.js | 3 + test/fixtures/ts-pkg/package.json | 13 ++ test/fixtures/ts-pkg/tsconfig.json | 29 +++++ test/fixtures/ts/app/controller/home.ts | 15 +++ test/fixtures/ts/app/router.js | 6 + test/fixtures/ts/config/config.default.js | 3 + test/fixtures/ts/package.json | 10 ++ test/fixtures/ts/tsconfig.json | 29 +++++ test/ts.test.js | 112 ++++++++++++++++++ 17 files changed, 291 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/ts-pkg/app/controller/home.ts create mode 100644 test/fixtures/ts-pkg/app/router.js create mode 100644 test/fixtures/ts-pkg/config/config.default.js create mode 100644 test/fixtures/ts-pkg/package.json create mode 100644 test/fixtures/ts-pkg/tsconfig.json create mode 100644 test/fixtures/ts/app/controller/home.ts create mode 100644 test/fixtures/ts/app/router.js create mode 100644 test/fixtures/ts/config/config.default.js create mode 100644 test/fixtures/ts/package.json create mode 100644 test/fixtures/ts/tsconfig.json create mode 100644 test/ts.test.js diff --git a/.eslintignore b/.eslintignore index 4ebc8ae..8cc505f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,3 @@ coverage +test/fixtures/ts/app/controller/home.js +test/fixtures/ts-pkg/app/controller/home.js \ No newline at end of file diff --git a/.gitignore b/.gitignore index 446bbe1..125fe6e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ logs/ npm-debug.log -/node_modules +node_modules coverage/ .idea/ run/ .DS_Store *.swp +test/fixtures/ts/app/controller/home.js +test/fixtures/ts-pkg/app/controller/home.js \ No newline at end of file diff --git a/README.md b/README.md index e2e9775..5bf8a17 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Add `eggctl` to `package.json` scripts: ``` Then run as: + - `npm start` - `npm stop` @@ -55,6 +56,7 @@ $ eggctl start [options] [baseDir] - `stderr` - customize stderr file, default to `$HOME/logs/master-stderr.log`. - `timeout` - the maximum timeout when app starts, default to 300s. - `ignore-stderr` - whether ignore stderr when app starts. + - `sourcemap` / `typescript` / `ts` - provides source map support for stack traces. ### stop diff --git a/lib/cmd/start.js b/lib/cmd/start.js index 6286043..5d562d6 100644 --- a/lib/cmd/start.js +++ b/lib/cmd/start.js @@ -133,7 +133,9 @@ class StartCommand extends Command { // remove unused properties from stringify, alias had been remove by `removeAlias` const ignoreKeys = [ '_', '$0', 'env', 'daemon', 'stdout', 'stderr', 'timeout', 'ignore-stderr' ]; - const eggArgs = [ this.serverBin, stringify(argv, ignoreKeys), `--title=${argv.title}` ]; + const clusterOptions = stringify(argv, ignoreKeys); + // Note: `spawn` is not like `fork`, had to pass `execArgv` youself + const eggArgs = [ ...(execArgv || []), this.serverBin, clusterOptions, `--title=${argv.title}` ]; this.logger.info('Run node %s', eggArgs.join(' ')); // whether run in the background. diff --git a/lib/command.js b/lib/command.js index 77f1a8e..c484252 100644 --- a/lib/command.js +++ b/lib/command.js @@ -1,5 +1,7 @@ 'use strict'; +const fs = require('fs'); +const path = require('path'); const BaseCommand = require('common-bin'); const Logger = require('zlogger'); const helper = require('./helper'); @@ -16,11 +18,47 @@ class Command extends BaseCommand { execArgv: 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 = { + sourcemap: { + description: 'whether enable sourcemap support, will load `source-map-support` etc', + type: 'boolean', + alias: [ 'ts', 'typescript' ], + }, + }; + this.logger = new Logger({ prefix: '[egg-scripts] ', time: false, }); } + + get context() { + const context = super.context; + const { argv, execArgvObj, cwd } = context; + + // read `egg.typescript` from package.json + let baseDir = argv._[0] || cwd; + if (!path.isAbsolute(baseDir)) baseDir = path.join(cwd, baseDir); + const pkgFile = path.join(baseDir, 'package.json'); + if (fs.existsSync(pkgFile)) { + const pkgInfo = require(pkgFile); + if (pkgInfo && pkgInfo.egg && pkgInfo.egg.typescript) { + argv.sourcemap = true; + } + } + + // execArgv + if (argv.sourcemap) { + execArgvObj.require = execArgvObj.require || []; + execArgvObj.require.push(require.resolve('source-map-support/register')); + } + + argv.sourcemap = argv.typescript = argv.ts = undefined; + + return context; + } } module.exports = Command; diff --git a/package.json b/package.json index cdd963c..f888b83 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "mz-modules": "^2.0.0", "node-homedir": "^1.1.0", "runscript": "^1.3.0", + "source-map-support": "^0.5.4", "zlogger": "^1.1.0" }, "devDependencies": { @@ -26,6 +27,7 @@ "eslint": "^4.11.0", "eslint-config-egg": "^5.1.1", "mm": "^2.2.0", + "typescript": "^2.8.1", "urllib": "^2.25.1", "webstorm-disable-index": "^1.2.0" }, diff --git a/test/fixtures/ts-pkg/app/controller/home.ts b/test/fixtures/ts-pkg/app/controller/home.ts new file mode 100644 index 0000000..8e631d9 --- /dev/null +++ b/test/fixtures/ts-pkg/app/controller/home.ts @@ -0,0 +1,15 @@ +import { Controller } from 'egg'; + +export default class AppController extends Controller { + public async index() { + try { + throw new Error('some err'); + } catch (err) { + this.ctx.logger.error(err); + this.ctx.body = { + msg: err.message, + stack: err.stack, + }; + } + } +}; diff --git a/test/fixtures/ts-pkg/app/router.js b/test/fixtures/ts-pkg/app/router.js new file mode 100644 index 0000000..bece6e7 --- /dev/null +++ b/test/fixtures/ts-pkg/app/router.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = app => { + const { router, controller } = app; + router.get('/', controller.home.index); +}; diff --git a/test/fixtures/ts-pkg/config/config.default.js b/test/fixtures/ts-pkg/config/config.default.js new file mode 100644 index 0000000..c997e00 --- /dev/null +++ b/test/fixtures/ts-pkg/config/config.default.js @@ -0,0 +1,3 @@ +'use strict'; + +exports.keys = '123456'; diff --git a/test/fixtures/ts-pkg/package.json b/test/fixtures/ts-pkg/package.json new file mode 100644 index 0000000..818511f --- /dev/null +++ b/test/fixtures/ts-pkg/package.json @@ -0,0 +1,13 @@ +{ + "name": "ts-pkg", + "version": "1.0.0", + "dependencies": { + "egg": "^1.0.0" + }, + "egg": { + "typescript": true + }, + "scripts": { + "build": "node ../../../node_modules/.bin/tsc" + } +} diff --git a/test/fixtures/ts-pkg/tsconfig.json b/test/fixtures/ts-pkg/tsconfig.json new file mode 100644 index 0000000..f5bd4ba --- /dev/null +++ b/test/fixtures/ts-pkg/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "target": "es2017", + "module": "commonjs", + "strict": true, + "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 + }, + "exclude": [ + "app/public", + "app/views" + ] +} \ No newline at end of file diff --git a/test/fixtures/ts/app/controller/home.ts b/test/fixtures/ts/app/controller/home.ts new file mode 100644 index 0000000..8e631d9 --- /dev/null +++ b/test/fixtures/ts/app/controller/home.ts @@ -0,0 +1,15 @@ +import { Controller } from 'egg'; + +export default class AppController extends Controller { + public async index() { + try { + throw new Error('some err'); + } catch (err) { + this.ctx.logger.error(err); + this.ctx.body = { + msg: err.message, + stack: err.stack, + }; + } + } +}; diff --git a/test/fixtures/ts/app/router.js b/test/fixtures/ts/app/router.js new file mode 100644 index 0000000..bece6e7 --- /dev/null +++ b/test/fixtures/ts/app/router.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = app => { + const { router, controller } = app; + router.get('/', controller.home.index); +}; diff --git a/test/fixtures/ts/config/config.default.js b/test/fixtures/ts/config/config.default.js new file mode 100644 index 0000000..c997e00 --- /dev/null +++ b/test/fixtures/ts/config/config.default.js @@ -0,0 +1,3 @@ +'use strict'; + +exports.keys = '123456'; diff --git a/test/fixtures/ts/package.json b/test/fixtures/ts/package.json new file mode 100644 index 0000000..5c422dd --- /dev/null +++ b/test/fixtures/ts/package.json @@ -0,0 +1,10 @@ +{ + "name": "ts", + "version": "1.0.0", + "dependencies": { + "egg": "^1.0.0" + }, + "scripts": { + "build": "node ../../../node_modules/.bin/tsc" + } +} diff --git a/test/fixtures/ts/tsconfig.json b/test/fixtures/ts/tsconfig.json new file mode 100644 index 0000000..f5bd4ba --- /dev/null +++ b/test/fixtures/ts/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "target": "es2017", + "module": "commonjs", + "strict": true, + "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 + }, + "exclude": [ + "app/public", + "app/views" + ] +} \ No newline at end of file diff --git a/test/ts.test.js b/test/ts.test.js new file mode 100644 index 0000000..1f8999b --- /dev/null +++ b/test/ts.test.js @@ -0,0 +1,112 @@ +'use strict'; + +const path = require('path'); +const assert = require('assert'); +const cp = require('child_process'); +const sleep = require('mz-modules/sleep'); +const rimraf = require('mz-modules/rimraf'); +const mkdirp = require('mz-modules/mkdirp'); +const coffee = require('coffee'); +const httpclient = require('urllib'); +const mm = require('mm'); +const utils = require('./utils'); + +describe('test/ts.test.js', () => { + const eggBin = require.resolve('../bin/egg-scripts.js'); + const homePath = path.join(__dirname, 'fixtures/home'); + const waitTime = '10s'; + let fixturePath; + + beforeEach(() => mm(process.env, 'MOCK_HOME_DIR', homePath)); + afterEach(mm.restore); + + before(() => mkdirp(homePath)); + after(() => rimraf(homePath)); + + describe('should display correct stack traces', () => { + let app; + beforeEach(function* () { + fixturePath = path.join(__dirname, 'fixtures/ts'); + yield utils.cleanup(fixturePath); + const result = cp.spawnSync('npm', [ 'run', 'build' ], { cwd: fixturePath }); + assert(!result.stderr.toString()); + }); + + afterEach(function* () { + app.proc.kill('SIGTERM'); + yield utils.cleanup(fixturePath); + }); + + it('--ts', function* () { + app = coffee.fork(eggBin, [ 'start', '--workers=1', '--ts', fixturePath ]); + // app.debug(); + app.expect('code', 0); + + yield sleep(waitTime); + + assert(app.stderr === ''); + assert(app.stdout.match(/egg started on http:\/\/127\.0\.0\.1:7001/)); + const result = yield httpclient.request('http://127.0.0.1:7001', { dataType: 'json' }); + // console.log(result.data); + assert(result.data.stack.includes('app/controller/home.ts:6:13')); + }); + + it('--typescript', function* () { + app = coffee.fork(eggBin, [ 'start', '--workers=1', '--typescript', fixturePath ]); + // app.debug(); + app.expect('code', 0); + + yield sleep(waitTime); + + assert(app.stderr === ''); + assert(app.stdout.match(/egg started on http:\/\/127\.0\.0\.1:7001/)); + const result = yield httpclient.request('http://127.0.0.1:7001', { dataType: 'json' }); + // console.log(result.data); + assert(result.data.stack.includes('app/controller/home.ts:6:13')); + }); + + it('--sourcemap', function* () { + app = coffee.fork(eggBin, [ 'start', '--workers=1', '--sourcemap', fixturePath ]); + // app.debug(); + app.expect('code', 0); + + yield sleep(waitTime); + + assert(app.stderr === ''); + assert(app.stdout.match(/egg started on http:\/\/127\.0\.0\.1:7001/)); + const result = yield httpclient.request('http://127.0.0.1:7001', { dataType: 'json' }); + // console.log(result.data); + assert(result.data.stack.includes('app/controller/home.ts:6:13')); + }); + }); + + describe('pkg.egg.typescript', () => { + let app; + beforeEach(function* () { + fixturePath = path.join(__dirname, 'fixtures/ts-pkg'); + yield utils.cleanup(fixturePath); + const result = cp.spawnSync('npm', [ 'run', 'build' ], { cwd: fixturePath }); + assert(!result.stderr.toString()); + }); + + afterEach(function* () { + app.proc.kill('SIGTERM'); + yield utils.cleanup(fixturePath); + }); + + it('should got correct stack', function* () { + app = coffee.fork(eggBin, [ 'start', '--workers=1', fixturePath ]); + // app.debug(); + app.expect('code', 0); + + yield sleep(waitTime); + + assert(app.stderr === ''); + assert(app.stdout.match(/egg started on http:\/\/127\.0\.0\.1:7001/)); + const result = yield httpclient.request('http://127.0.0.1:7001', { dataType: 'json' }); + // console.log(result.data); + assert(result.data.stack.includes('app/controller/home.ts:6:13')); + }); + }); +}); + From 7fc805d99679db95cfc8bf6af961147a0349a3a7 Mon Sep 17 00:00:00 2001 From: TZ Date: Sun, 1 Apr 2018 22:44:16 +0800 Subject: [PATCH 2/3] f --- test/fixtures/ts-pkg/app/controller/home.ts | 2 +- test/fixtures/ts/app/controller/home.ts | 2 +- test/ts.test.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/fixtures/ts-pkg/app/controller/home.ts b/test/fixtures/ts-pkg/app/controller/home.ts index 8e631d9..f6a07cd 100644 --- a/test/fixtures/ts-pkg/app/controller/home.ts +++ b/test/fixtures/ts-pkg/app/controller/home.ts @@ -1,7 +1,7 @@ import { Controller } from 'egg'; export default class AppController extends Controller { - public async index() { + public index() { try { throw new Error('some err'); } catch (err) { diff --git a/test/fixtures/ts/app/controller/home.ts b/test/fixtures/ts/app/controller/home.ts index 8e631d9..f6a07cd 100644 --- a/test/fixtures/ts/app/controller/home.ts +++ b/test/fixtures/ts/app/controller/home.ts @@ -1,7 +1,7 @@ import { Controller } from 'egg'; export default class AppController extends Controller { - public async index() { + public index() { try { throw new Error('some err'); } catch (err) { diff --git a/test/ts.test.js b/test/ts.test.js index 1f8999b..f2cf913 100644 --- a/test/ts.test.js +++ b/test/ts.test.js @@ -33,7 +33,7 @@ describe('test/ts.test.js', () => { }); afterEach(function* () { - app.proc.kill('SIGTERM'); + app && app.proc.kill('SIGTERM'); yield utils.cleanup(fixturePath); }); @@ -90,7 +90,7 @@ describe('test/ts.test.js', () => { }); afterEach(function* () { - app.proc.kill('SIGTERM'); + app && app.proc.kill('SIGTERM'); yield utils.cleanup(fixturePath); }); From 3571d586999577ff39eeff52de1ed1e6c80cd7b2 Mon Sep 17 00:00:00 2001 From: TZ Date: Mon, 2 Apr 2018 08:18:34 +0800 Subject: [PATCH 3/3] f --- test/start.test.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/start.test.js b/test/start.test.js index 9ef961e..42bc54a 100644 --- a/test/start.test.js +++ b/test/start.test.js @@ -310,9 +310,10 @@ describe('test/start.test.js', () => { describe('read cluster config', () => { let app; - const fixturePath = path.join(__dirname, 'fixtures/cluster-config'); + let fixturePath; before(function* () { + fixturePath = path.join(__dirname, 'fixtures/cluster-config'); yield utils.cleanup(fixturePath); }); @@ -366,9 +367,10 @@ describe('test/start.test.js', () => { describe('auto set custom node dir to PATH', () => { let app; - const fixturePath = path.join(__dirname, 'fixtures/custom-node-dir'); + let fixturePath; before(function* () { + fixturePath = path.join(__dirname, 'fixtures/custom-node-dir'); yield utils.cleanup(fixturePath); }); @@ -382,16 +384,16 @@ describe('test/start.test.js', () => { path.join(fixturePath, 'node_modules/.bin'), path.join(fixturePath, '.node/bin'), ].join(path.delimiter) + path.delimiter; - app = coffee.fork(eggBin, [ 'start', '--workers=2', fixturePath ]); - app.debug(); + app = coffee.fork(eggBin, [ 'start', '--workers=2', '--port=7002', fixturePath ]); + // app.debug(); app.expect('code', 0); yield sleep(waitTime); assert(app.stderr === ''); - assert(app.stdout.match(/egg started on http:\/\/127\.0\.0\.1:7001/)); + assert(app.stdout.match(/egg started on http:\/\/127\.0\.0\.1:7002/)); assert(!app.stdout.includes('app_worker#3:')); - const result = yield httpclient.request('http://127.0.0.1:7001'); + const result = yield httpclient.request('http://127.0.0.1:7002'); assert(result.data.toString().startsWith(`hi, ${expectPATH}`)); }); }); @@ -408,7 +410,7 @@ describe('test/start.test.js', () => { }); afterEach(function* () { yield coffee.fork(eggBin, [ 'stop', cwd ]) - .debug() + // .debug() .end(); yield utils.cleanup(cwd); }); @@ -440,7 +442,7 @@ describe('test/start.test.js', () => { it('should start default egg', function* () { cwd = path.join(__dirname, 'fixtures/egg-app'); yield coffee.fork(eggBin, [ 'start', '--daemon', '--workers=2', cwd ]) - .debug() + // .debug() .expect('stdout', /Starting egg application/) .expect('stdout', /egg started on http:\/\/127\.0\.0\.1:7001/) .expect('code', 0)