You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

521 lines
26KB

  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const chai_1 = require("chai");
  4. const child_process_1 = require("child_process");
  5. const path_1 = require("path");
  6. const semver = require("semver");
  7. const ts = require("typescript");
  8. const proxyquire = require("proxyquire");
  9. const index_1 = require("./index");
  10. const TEST_DIR = path_1.join(__dirname, '../tests');
  11. const PROJECT = path_1.join(TEST_DIR, 'tsconfig.json');
  12. const BIN_PATH = path_1.join(__dirname, '../dist/bin');
  13. const BIN_SCRIPT_PATH = path_1.join(__dirname, '../dist/bin-script');
  14. const SOURCE_MAP_REGEXP = /\/\/# sourceMappingURL=data:application\/json;charset=utf\-8;base64,[\w\+]+=*$/;
  15. describe('ts-node', function () {
  16. const cmd = `node "${BIN_PATH}" --project "${PROJECT}"`;
  17. this.timeout(10000);
  18. it('should export the correct version', function () {
  19. chai_1.expect(index_1.VERSION).to.equal(require('../package.json').version);
  20. });
  21. describe('cli', function () {
  22. this.slow(1000);
  23. it('should execute cli', function (done) {
  24. child_process_1.exec(`${cmd} tests/hello-world`, function (err, stdout) {
  25. chai_1.expect(err).to.equal(null);
  26. chai_1.expect(stdout).to.equal('Hello, world!\n');
  27. return done();
  28. });
  29. });
  30. it('should register via cli', function (done) {
  31. child_process_1.exec(`node -r ../register hello-world.ts`, {
  32. cwd: TEST_DIR
  33. }, function (err, stdout) {
  34. chai_1.expect(err).to.equal(null);
  35. chai_1.expect(stdout).to.equal('Hello, world!\n');
  36. return done();
  37. });
  38. });
  39. it('should execute cli with absolute path', function (done) {
  40. child_process_1.exec(`${cmd} "${path_1.join(TEST_DIR, 'hello-world')}"`, function (err, stdout) {
  41. chai_1.expect(err).to.equal(null);
  42. chai_1.expect(stdout).to.equal('Hello, world!\n');
  43. return done();
  44. });
  45. });
  46. it('should print scripts', function (done) {
  47. child_process_1.exec(`${cmd} -pe "import { example } from './tests/complex/index';example()"`, function (err, stdout) {
  48. chai_1.expect(err).to.equal(null);
  49. chai_1.expect(stdout).to.equal('example\n');
  50. return done();
  51. });
  52. });
  53. it('should provide registered information globally', function (done) {
  54. child_process_1.exec(`${cmd} tests/env`, function (err, stdout) {
  55. chai_1.expect(err).to.equal(null);
  56. chai_1.expect(stdout).to.equal('object\n');
  57. return done();
  58. });
  59. });
  60. it('should provide registered information on register', function (done) {
  61. child_process_1.exec(`node -r ../register env.ts`, {
  62. cwd: TEST_DIR
  63. }, function (err, stdout) {
  64. chai_1.expect(err).to.equal(null);
  65. chai_1.expect(stdout).to.equal('object\n');
  66. return done();
  67. });
  68. });
  69. if (semver.gte(ts.version, '1.8.0')) {
  70. it('should allow js', function (done) {
  71. child_process_1.exec([
  72. cmd,
  73. '-O "{\\\"allowJs\\\":true}"',
  74. '-pe "import { main } from \'./tests/allow-js/run\';main()"'
  75. ].join(' '), function (err, stdout) {
  76. chai_1.expect(err).to.equal(null);
  77. chai_1.expect(stdout).to.equal('hello world\n');
  78. return done();
  79. });
  80. });
  81. it('should include jsx when `allow-js` true', function (done) {
  82. child_process_1.exec([
  83. cmd,
  84. '-O "{\\\"allowJs\\\":true}"',
  85. '-pe "import { Foo2 } from \'./tests/allow-js/with-jsx\'; Foo2.sayHi()"'
  86. ].join(' '), function (err, stdout) {
  87. chai_1.expect(err).to.equal(null);
  88. chai_1.expect(stdout).to.equal('hello world\n');
  89. return done();
  90. });
  91. });
  92. }
  93. it('should eval code', function (done) {
  94. child_process_1.exec(`${cmd} -e "import * as m from './tests/module';console.log(m.example('test'))"`, function (err, stdout) {
  95. chai_1.expect(err).to.equal(null);
  96. chai_1.expect(stdout).to.equal('TEST\n');
  97. return done();
  98. });
  99. });
  100. it('should import empty files', function (done) {
  101. child_process_1.exec(`${cmd} -e "import './tests/empty'"`, function (err, stdout) {
  102. chai_1.expect(err).to.equal(null);
  103. chai_1.expect(stdout).to.equal('');
  104. return done();
  105. });
  106. });
  107. it('should throw errors', function (done) {
  108. child_process_1.exec(`${cmd} -e "import * as m from './tests/module';console.log(m.example(123))"`, function (err) {
  109. if (err === null) {
  110. return done('Command was expected to fail, but it succeeded.');
  111. }
  112. chai_1.expect(err.message).to.match(new RegExp('TS2345: Argument of type \'(?:number|123)\' ' +
  113. 'is not assignable to parameter of type \'string\'\\.'));
  114. return done();
  115. });
  116. });
  117. it('should be able to ignore diagnostic', function (done) {
  118. child_process_1.exec(`${cmd} --ignore-diagnostics 2345 -e "import * as m from './tests/module';console.log(m.example(123))"`, function (err) {
  119. if (err === null) {
  120. return done('Command was expected to fail, but it succeeded.');
  121. }
  122. chai_1.expect(err.message).to.match(/TypeError: (?:(?:undefined|foo\.toUpperCase) is not a function|.*has no method \'toUpperCase\')/);
  123. return done();
  124. });
  125. });
  126. it('should work with source maps', function (done) {
  127. child_process_1.exec(`${cmd} tests/throw`, function (err) {
  128. if (err === null) {
  129. return done('Command was expected to fail, but it succeeded.');
  130. }
  131. chai_1.expect(err.message).to.contain([
  132. `${path_1.join(__dirname, '../tests/throw.ts')}:3`,
  133. ' bar () { throw new Error(\'this is a demo\') }',
  134. ' ^',
  135. 'Error: this is a demo'
  136. ].join('\n'));
  137. return done();
  138. });
  139. });
  140. it('eval should work with source maps', function (done) {
  141. child_process_1.exec(`${cmd} -pe "import './tests/throw'"`, function (err) {
  142. if (err === null) {
  143. return done('Command was expected to fail, but it succeeded.');
  144. }
  145. chai_1.expect(err.message).to.contain([
  146. `${path_1.join(__dirname, '../tests/throw.ts')}:3`,
  147. ' bar () { throw new Error(\'this is a demo\') }',
  148. ' ^'
  149. ].join('\n'));
  150. return done();
  151. });
  152. });
  153. it('should support transpile only mode', function (done) {
  154. child_process_1.exec(`${cmd} --transpile-only -pe "x"`, function (err) {
  155. if (err === null) {
  156. return done('Command was expected to fail, but it succeeded.');
  157. }
  158. chai_1.expect(err.message).to.contain('ReferenceError: x is not defined');
  159. return done();
  160. });
  161. });
  162. it('should throw error even in transpileOnly mode', function (done) {
  163. child_process_1.exec(`${cmd} --transpile-only -pe "console."`, function (err) {
  164. if (err === null) {
  165. return done('Command was expected to fail, but it succeeded.');
  166. }
  167. chai_1.expect(err.message).to.contain('error TS1003: Identifier expected');
  168. return done();
  169. });
  170. });
  171. it('should pipe into `ts-node` and evaluate', function (done) {
  172. const cp = child_process_1.exec(cmd, function (err, stdout) {
  173. chai_1.expect(err).to.equal(null);
  174. chai_1.expect(stdout).to.equal('hello\n');
  175. return done();
  176. });
  177. cp.stdin.end("console.log('hello')");
  178. });
  179. it('should pipe into `ts-node`', function (done) {
  180. const cp = child_process_1.exec(`${cmd} -p`, function (err, stdout) {
  181. chai_1.expect(err).to.equal(null);
  182. chai_1.expect(stdout).to.equal('true\n');
  183. return done();
  184. });
  185. cp.stdin.end('true');
  186. });
  187. it('should pipe into an eval script', function (done) {
  188. const cp = child_process_1.exec(`${cmd} --transpile-only -pe 'process.stdin.isTTY'`, function (err, stdout) {
  189. chai_1.expect(err).to.equal(null);
  190. chai_1.expect(stdout).to.equal('undefined\n');
  191. return done();
  192. });
  193. cp.stdin.end('true');
  194. });
  195. it('should run REPL when --interactive passed and stdin is not a TTY', function (done) {
  196. const cp = child_process_1.exec(`${cmd} --interactive`, function (err, stdout) {
  197. chai_1.expect(err).to.equal(null);
  198. chai_1.expect(stdout).to.equal('> 123\n' +
  199. 'undefined\n' +
  200. '> ');
  201. return done();
  202. });
  203. cp.stdin.end('console.log("123")\n');
  204. });
  205. it('should support require flags', function (done) {
  206. child_process_1.exec(`${cmd} -r ./tests/hello-world -pe "console.log('success')"`, function (err, stdout) {
  207. chai_1.expect(err).to.equal(null);
  208. chai_1.expect(stdout).to.equal('Hello, world!\nsuccess\nundefined\n');
  209. return done();
  210. });
  211. });
  212. it('should support require from node modules', function (done) {
  213. child_process_1.exec(`${cmd} -r typescript -e "console.log('success')"`, function (err, stdout) {
  214. chai_1.expect(err).to.equal(null);
  215. chai_1.expect(stdout).to.equal('success\n');
  216. return done();
  217. });
  218. });
  219. it.skip('should use source maps with react tsx', function (done) {
  220. child_process_1.exec(`${cmd} -r ./tests/emit-compiled.ts tests/jsx-react.tsx`, function (err, stdout) {
  221. chai_1.expect(err).to.equal(null);
  222. chai_1.expect(stdout).to.equal('todo');
  223. return done();
  224. });
  225. });
  226. it('should allow custom typings', function (done) {
  227. child_process_1.exec(`${cmd} tests/custom-types`, function (err, stdout) {
  228. chai_1.expect(err).to.match(/Error: Cannot find module 'does-not-exist'/);
  229. return done();
  230. });
  231. });
  232. it('should preserve `ts-node` context with child process', function (done) {
  233. child_process_1.exec(`${cmd} tests/child-process`, function (err, stdout) {
  234. chai_1.expect(err).to.equal(null);
  235. chai_1.expect(stdout).to.equal('Hello, world!\n');
  236. return done();
  237. });
  238. });
  239. it('should import js before ts by default', function (done) {
  240. child_process_1.exec(`${cmd} tests/import-order/compiled`, function (err, stdout) {
  241. chai_1.expect(err).to.equal(null);
  242. chai_1.expect(stdout).to.equal('Hello, JavaScript!\n');
  243. return done();
  244. });
  245. });
  246. it('should import ts before js when --prefer-ts-exts flag is present', function (done) {
  247. child_process_1.exec(`${cmd} --prefer-ts-exts tests/import-order/compiled`, function (err, stdout) {
  248. chai_1.expect(err).to.equal(null);
  249. chai_1.expect(stdout).to.equal('Hello, TypeScript!\n');
  250. return done();
  251. });
  252. });
  253. it('should import ts before js when TS_NODE_PREFER_TS_EXTS env is present', function (done) {
  254. child_process_1.exec(`${cmd} tests/import-order/compiled`, { env: Object.assign(Object.assign({}, process.env), { TS_NODE_PREFER_TS_EXTS: 'true' }) }, function (err, stdout) {
  255. chai_1.expect(err).to.equal(null);
  256. chai_1.expect(stdout).to.equal('Hello, TypeScript!\n');
  257. return done();
  258. });
  259. });
  260. it('should ignore .d.ts files', function (done) {
  261. child_process_1.exec(`${cmd} tests/import-order/importer`, function (err, stdout) {
  262. chai_1.expect(err).to.equal(null);
  263. chai_1.expect(stdout).to.equal('Hello, World!\n');
  264. return done();
  265. });
  266. });
  267. describe('issue #884', function () {
  268. it('should compile', function (done) {
  269. child_process_1.exec(`node "${BIN_PATH}" --project tests/issue-884/tsconfig.json tests/issue-884`, function (err, stdout) {
  270. chai_1.expect(err).to.equal(null);
  271. chai_1.expect(stdout).to.equal('');
  272. return done();
  273. });
  274. });
  275. });
  276. describe('issue #986', function () {
  277. it('should not compile', function (done) {
  278. child_process_1.exec(`node "${BIN_PATH}" --project tests/issue-986/tsconfig.json tests/issue-986`, function (err, stdout, stderr) {
  279. chai_1.expect(err).not.to.equal(null);
  280. chai_1.expect(stderr).to.contain('Cannot find name \'TEST\''); // TypeScript error.
  281. chai_1.expect(stdout).to.equal('');
  282. return done();
  283. });
  284. });
  285. it('should compile with `--files`', function (done) {
  286. child_process_1.exec(`node "${BIN_PATH}" --files --project tests/issue-986/tsconfig.json tests/issue-986`, function (err, stdout, stderr) {
  287. chai_1.expect(err).not.to.equal(null);
  288. chai_1.expect(stderr).to.contain('ReferenceError: TEST is not defined'); // Runtime error.
  289. chai_1.expect(stdout).to.equal('');
  290. return done();
  291. });
  292. });
  293. });
  294. if (semver.gte(ts.version, '2.7.0')) {
  295. it('should support script mode', function (done) {
  296. child_process_1.exec(`node ${BIN_SCRIPT_PATH} tests/scope/a/log`, function (err, stdout) {
  297. chai_1.expect(err).to.equal(null);
  298. chai_1.expect(stdout).to.equal('.ts\n');
  299. return done();
  300. });
  301. });
  302. it('should read tsconfig relative to realpath, not symlink, in scriptMode', function (done) {
  303. child_process_1.exec(`node ${BIN_SCRIPT_PATH} tests/main-realpath/symlink/symlink.tsx`, function (err, stdout) {
  304. chai_1.expect(err).to.equal(null);
  305. chai_1.expect(stdout).to.equal('');
  306. return done();
  307. });
  308. });
  309. }
  310. describe('should read ts-node options from tsconfig.json', function () {
  311. const BIN_EXEC = `node "${path_1.join(__dirname, '../dist/bin')}" --project tests/tsconfig-options/tsconfig.json`;
  312. it('should override compiler options from env', function (done) {
  313. child_process_1.exec(`${BIN_EXEC} tests/tsconfig-options/log-options.js`, {
  314. env: Object.assign(Object.assign({}, process.env), { TS_NODE_COMPILER_OPTIONS: '{"typeRoots": ["env-typeroots"]}' })
  315. }, function (err, stdout) {
  316. chai_1.expect(err).to.equal(null);
  317. const { config } = JSON.parse(stdout);
  318. chai_1.expect(config.options.typeRoots).to.deep.equal([path_1.join(__dirname, '../tests/tsconfig-options/env-typeroots')]);
  319. return done();
  320. });
  321. });
  322. it('should use options from `tsconfig.json`', function (done) {
  323. child_process_1.exec(`${BIN_EXEC} tests/tsconfig-options/log-options.js`, function (err, stdout) {
  324. chai_1.expect(err).to.equal(null);
  325. const { options, config } = JSON.parse(stdout);
  326. chai_1.expect(config.options.typeRoots).to.deep.equal([path_1.join(__dirname, '../tests/tsconfig-options/tsconfig-typeroots')]);
  327. chai_1.expect(config.options.types).to.deep.equal(['tsconfig-tsnode-types']);
  328. chai_1.expect(options.pretty).to.equal(undefined);
  329. chai_1.expect(options.skipIgnore).to.equal(false);
  330. chai_1.expect(options.transpileOnly).to.equal(true);
  331. return done();
  332. });
  333. });
  334. it('should have flags override `tsconfig.json`', function (done) {
  335. child_process_1.exec(`${BIN_EXEC} --skip-ignore --compiler-options '{"types": ["flags-types"]}' tests/tsconfig-options/log-options.js`, function (err, stdout) {
  336. chai_1.expect(err).to.equal(null);
  337. const { options, config } = JSON.parse(stdout);
  338. chai_1.expect(config.options.typeRoots).to.deep.equal([path_1.join(__dirname, '../tests/tsconfig-options/tsconfig-typeroots')]);
  339. chai_1.expect(config.options.types).to.deep.equal(['flags-types']);
  340. chai_1.expect(options.pretty).to.equal(undefined);
  341. chai_1.expect(options.skipIgnore).to.equal(true);
  342. chai_1.expect(options.transpileOnly).to.equal(true);
  343. return done();
  344. });
  345. });
  346. it('should have `tsconfig.json` override environment', function (done) {
  347. child_process_1.exec(`${BIN_EXEC} tests/tsconfig-options/log-options.js`, {
  348. env: Object.assign(Object.assign({}, process.env), { TS_NODE_PRETTY: 'true', TS_NODE_SKIP_IGNORE: 'true' })
  349. }, function (err, stdout) {
  350. chai_1.expect(err).to.equal(null);
  351. const { options, config } = JSON.parse(stdout);
  352. chai_1.expect(config.options.typeRoots).to.deep.equal([path_1.join(__dirname, '../tests/tsconfig-options/tsconfig-typeroots')]);
  353. chai_1.expect(config.options.types).to.deep.equal(['tsconfig-tsnode-types']);
  354. chai_1.expect(options.pretty).to.equal(true);
  355. chai_1.expect(options.skipIgnore).to.equal(false);
  356. chai_1.expect(options.transpileOnly).to.equal(true);
  357. return done();
  358. });
  359. });
  360. });
  361. describe('compiler host', function () {
  362. it('should execute cli', function (done) {
  363. child_process_1.exec(`${cmd} --compiler-host tests/hello-world`, function (err, stdout) {
  364. chai_1.expect(err).to.equal(null);
  365. chai_1.expect(stdout).to.equal('Hello, world!\n');
  366. return done();
  367. });
  368. });
  369. it('should give ts error for invalid node_modules', function (done) {
  370. child_process_1.exec(`${cmd} --compiler-host --skip-ignore tests/from-node-modules`, function (err, stdout) {
  371. if (err === null)
  372. return done('Expected an error');
  373. chai_1.expect(err.message).to.contain('Unable to compile file from external library');
  374. return done();
  375. });
  376. });
  377. });
  378. });
  379. describe('register', function () {
  380. const registered = index_1.register({
  381. project: PROJECT,
  382. compilerOptions: {
  383. jsx: 'preserve'
  384. }
  385. });
  386. const moduleTestPath = require.resolve('../tests/module');
  387. afterEach(() => {
  388. // Re-enable project after every test.
  389. registered.enabled(true);
  390. });
  391. it('should be able to require typescript', function () {
  392. const m = require(moduleTestPath);
  393. chai_1.expect(m.example('foo')).to.equal('FOO');
  394. });
  395. it('should support dynamically disabling', function () {
  396. delete require.cache[moduleTestPath];
  397. chai_1.expect(registered.enabled(false)).to.equal(false);
  398. chai_1.expect(() => require(moduleTestPath)).to.throw(/Unexpected token/);
  399. delete require.cache[moduleTestPath];
  400. chai_1.expect(registered.enabled()).to.equal(false);
  401. chai_1.expect(() => require(moduleTestPath)).to.throw(/Unexpected token/);
  402. delete require.cache[moduleTestPath];
  403. chai_1.expect(registered.enabled(true)).to.equal(true);
  404. chai_1.expect(() => require(moduleTestPath)).to.not.throw();
  405. delete require.cache[moduleTestPath];
  406. chai_1.expect(registered.enabled()).to.equal(true);
  407. chai_1.expect(() => require(moduleTestPath)).to.not.throw();
  408. });
  409. if (semver.gte(ts.version, '2.7.0')) {
  410. it('should support compiler scopes', function () {
  411. const calls = [];
  412. registered.enabled(false);
  413. const compilers = [
  414. index_1.register({ dir: path_1.join(TEST_DIR, 'scope/a'), scope: true }),
  415. index_1.register({ dir: path_1.join(TEST_DIR, 'scope/b'), scope: true })
  416. ];
  417. compilers.forEach(c => {
  418. const old = c.compile;
  419. c.compile = (code, fileName, lineOffset) => {
  420. calls.push(fileName);
  421. return old(code, fileName, lineOffset);
  422. };
  423. });
  424. try {
  425. chai_1.expect(require('../tests/scope/a').ext).to.equal('.ts');
  426. chai_1.expect(require('../tests/scope/b').ext).to.equal('.ts');
  427. }
  428. finally {
  429. compilers.forEach(c => c.enabled(false));
  430. }
  431. chai_1.expect(calls).to.deep.equal([
  432. path_1.join(TEST_DIR, 'scope/a/index.ts'),
  433. path_1.join(TEST_DIR, 'scope/b/index.ts')
  434. ]);
  435. delete require.cache[moduleTestPath];
  436. chai_1.expect(() => require(moduleTestPath)).to.throw();
  437. });
  438. }
  439. it('should compile through js and ts', function () {
  440. const m = require('../tests/complex');
  441. chai_1.expect(m.example()).to.equal('example');
  442. });
  443. it('should work with proxyquire', function () {
  444. const m = proxyquire('../tests/complex', {
  445. './example': 'hello'
  446. });
  447. chai_1.expect(m.example()).to.equal('hello');
  448. });
  449. it('should work with `require.cache`', function () {
  450. const { example1, example2 } = require('../tests/require-cache');
  451. chai_1.expect(example1).to.not.equal(example2);
  452. });
  453. it('should use source maps', function (done) {
  454. try {
  455. require('../tests/throw');
  456. }
  457. catch (error) {
  458. chai_1.expect(error.stack).to.contain([
  459. 'Error: this is a demo',
  460. ` at Foo.bar (${path_1.join(__dirname, '../tests/throw.ts')}:3:18)`
  461. ].join('\n'));
  462. done();
  463. }
  464. });
  465. describe('JSX preserve', () => {
  466. let old = require.extensions['.tsx']; // tslint:disable-line
  467. let compiled;
  468. before(function () {
  469. require.extensions['.tsx'] = (m, fileName) => {
  470. const _compile = m._compile;
  471. m._compile = (code, fileName) => {
  472. compiled = code;
  473. return _compile.call(this, code, fileName);
  474. };
  475. return old(m, fileName);
  476. };
  477. });
  478. after(function () {
  479. require.extensions['.tsx'] = old; // tslint:disable-line
  480. });
  481. it('should use source maps', function (done) {
  482. try {
  483. require('../tests/with-jsx.tsx');
  484. }
  485. catch (error) {
  486. chai_1.expect(error.stack).to.contain('SyntaxError: Unexpected token');
  487. }
  488. chai_1.expect(compiled).to.match(SOURCE_MAP_REGEXP);
  489. done();
  490. });
  491. });
  492. });
  493. describe('create', () => {
  494. it('should create generic compiler instances', () => {
  495. const service = index_1.create({ compilerOptions: { target: 'es5' }, skipProject: true });
  496. const output = service.compile('const x = 10', 'test.ts');
  497. chai_1.expect(output).to.contain('var x = 10;');
  498. });
  499. });
  500. if (semver.gte(process.version, '13.0.0')) {
  501. describe('esm', () => {
  502. this.slow(1000);
  503. const cmd = `node --loader ../../esm.mjs`;
  504. it('should compile and execute as ESM', (done) => {
  505. child_process_1.exec(`${cmd} index.ts`, { cwd: path_1.join(__dirname, '../tests/esm') }, function (err, stdout) {
  506. chai_1.expect(err).to.equal(null);
  507. chai_1.expect(stdout).to.equal('foo bar baz biff\n');
  508. return done();
  509. });
  510. });
  511. it('supports --experimental-specifier-resolution=node', (done) => {
  512. child_process_1.exec(`${cmd} --experimental-specifier-resolution=node index.ts`, { cwd: path_1.join(__dirname, '../tests/esm-node-resolver') }, function (err, stdout) {
  513. chai_1.expect(err).to.equal(null);
  514. chai_1.expect(stdout).to.equal('foo bar baz biff\n');
  515. return done();
  516. });
  517. });
  518. });
  519. }
  520. });
  521. //# sourceMappingURL=index.spec.js.map