diff --git a/README.md b/README.md index 782312e..17b3969 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ Look up flags and options can be used [in ts-node's docs](https://github.com/Typ * `--rs` - Allow to restart with "rs" line entered in stdio, disabled by default. * `--notify` - to display desktop-notifications (Notifications are only displayed if `node-notifier` is installed). * `--cache-directory` - tmp dir which is used to keep the compiled sources (by default os tmp directory is used) +* `--file-change-hook` - Bind a file watch hook. If you need to detect that you are running with `ts-node-dev`, check if `process.env.TS_NODE_DEV` is set. diff --git a/package.json b/package.json index c5e6e8b..4278884 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "dependencies": { "chokidar": "^3.5.1", "dynamic-dedupe": "^0.3.0", + "esm": "^3.2.25", "minimist": "^1.2.5", "mkdirp": "^1.0.4", "resolve": "^1.0.0", @@ -70,7 +71,6 @@ "chalk": "^4.1.0", "coffee-script": "^1.8.0", "eslint": "^7.7.0", - "esm": "^3.2.22", "fs-extra": "^9.0.1", "mocha": "^8.1.1", "np": "^6.5.0", @@ -97,4 +97,4 @@ "publishConfig": { "registry": "https://registry.npmjs.org" } -} +} \ No newline at end of file diff --git a/src/bin.ts b/src/bin.ts index abe42cc..d3f1c78 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -98,6 +98,7 @@ const devFlags = { 'debounce', 'watch', 'cache-directory', + 'file-change-hook' ], } @@ -121,6 +122,7 @@ type DevOptions = { 'exec-check': boolean 'exit-child': boolean 'cache-directory': string + 'file-change-hook': string 'error-recompile': boolean quiet: boolean 'tree-kill': boolean diff --git a/src/index.ts b/src/index.ts index ed1be9c..bc6d2dd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,8 @@ import { fork, ChildProcess } from 'child_process' import chokidar from 'chokidar' import fs from 'fs' +import path from 'path' +import vm from 'vm' import readline from 'readline' const kill = require('tree-kill') @@ -38,9 +40,9 @@ export const runDev = ( // The child_process let child: | (ChildProcess & { - stopping?: boolean - respawn?: boolean - }) + stopping?: boolean + respawn?: boolean + }) | undefined const wrapper = resolveMain(__dirname + '/wrap.js') @@ -90,12 +92,12 @@ export const runDev = ( log.info( 'ts-node-dev ver. ' + - version + - ' (using ts-node ver. ' + - tsNodeVersion + - ', typescript ver. ' + - tsVersion + - ')' + version + + ' (using ts-node ver. ' + + tsNodeVersion + + ', typescript ver. ' + + tsVersion + + ')' ) /** @@ -240,6 +242,26 @@ export const runDev = ( } else { notify('Restarting', file + ' has been modified') } + + if (opts['file-change-hook']) { + try { + const scriptPath = path.resolve(process.cwd(), opts['file-change-hook']) + const code = `require("${scriptPath}").default("${file}");` + const result = vm.runInNewContext(code, { + require: require("esm")(module), + module, + console, + process + }); + if (!result) { + notify('FileChangeHook', 'Hook exit code: 0.') + } + } catch (err) { + notify('FileChangeHook', err) + console.log(err) + } + } + compiler.compileChanged(file) if (starting) { log.debug('Already starting') diff --git a/test/fixture/fileChangeHook.js b/test/fixture/fileChangeHook.js new file mode 100644 index 0000000..1734308 --- /dev/null +++ b/test/fixture/fileChangeHook.js @@ -0,0 +1,4 @@ +export default function (file) { + console.log('change file:' + file); + return 0 +} \ No newline at end of file diff --git a/test/tsnd.test.ts b/test/tsnd.test.ts index 991a9f2..d11ca7b 100644 --- a/test/tsnd.test.ts +++ b/test/tsnd.test.ts @@ -66,7 +66,7 @@ describe('ts-node-dev', function () { await ps.waitForLine(/\[ERROR\]/) const out = ps.getStdout() const err = ps.getStderr() - + t.ok(/Compilation error in/.test(err), 'Reports error file') t.ok(/[ERROR].*Unable to compile TypeScript/.test(out), 'Report TS error') t.ok(/Argument of type/.test(out), 'Report TS error diagnostics') @@ -159,7 +159,7 @@ describe('ts-node-dev', function () { await ps.waitForLine(/JS MODULE/) t.ok(true, 'ok') await ps.exit() - }) + }) it('should handle -r esm option and load JS modules', async () => { const ps = spawnTsNodeDev([`--respawn`, `-r esm`, `js-module.js`].join(' ')) @@ -361,4 +361,15 @@ describe('ts-node-dev', function () { const list = fs.readdirSync(cacheDir) t.ok(list[0] === 'compiled', '"compiled" dir is there') }) + + it('should handle --file-change-hook flag', async () => { + writeFile('test.ts', 'a') + const ps = spawnTsNodeDev([`--file-change-hook`, `fileChangeHook.js`, `--respawn`, `--watch`, `test.ts`, `simple.ts`].join(' ')) + await ps.waitForLine(/v1/) + writeFile('test.ts', 'b') + await ps.waitForLine(/Restarting.*test.ts/) + t.ok(true, 'works') + await ps.exit() + await removeFile('test.ts') + }) }) diff --git a/yarn.lock b/yarn.lock index 6ab0ef4..5db21cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1476,10 +1476,10 @@ eslint@^7.7.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -esm@^3.2.22: - version "3.2.22" - resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.22.tgz#5062c2e22fee3ccfee4e8f20da768330da90d6e3" - integrity sha512-z8YG7U44L82j1XrdEJcqZOLUnjxco8pO453gKOlaMD1/md1n/5QrscAmYG+oKUspsmDLuBFZrpbxI6aQ67yRxA== +esm@^3.2.25: + version "3.2.25" + resolved "https://registry.npm.taobao.org/esm/download/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" + integrity sha1-NCwYwp1WFXaIulzjH4Qx+7eVzBA= espree@^7.2.0: version "7.2.0"