From 7d2dd80f2c7a89f31c8f96c2e911a6f9beaf7cbc Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Mon, 28 Sep 2020 16:09:35 +0200 Subject: [PATCH] fix(shebang): support shebang in in files (#2510) Support shebang (i.e. `#!/usr/bin/env node`) in javascript files. --- e2e/test/mocha-javascript/src/Add.js | 1 + .../instrumenter/src/disable-type-checks.ts | 12 +++- .../disable-type-checks.it.spec.ts | 2 +- .../test/integration/instrumenter.it.spec.ts | 3 + .../test/unit/disable-type-checks.spec.ts | 13 ++++ .../app.component.ts.out.snap | 33 ++++++++++ .../testResources/instrumenter/shebang.js | 3 + .../instrumenter/shebang.js.out.snap | 61 +++++++++++++++++++ 8 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 packages/instrumenter/testResources/instrumenter/shebang.js create mode 100644 packages/instrumenter/testResources/instrumenter/shebang.js.out.snap diff --git a/e2e/test/mocha-javascript/src/Add.js b/e2e/test/mocha-javascript/src/Add.js index 0d6e64385c..580a6015b2 100644 --- a/e2e/test/mocha-javascript/src/Add.js +++ b/e2e/test/mocha-javascript/src/Add.js @@ -1,3 +1,4 @@ +#! /usr/bin/env node module.exports.add = function(num1, num2) { return num1 + num2; }; diff --git a/packages/instrumenter/src/disable-type-checks.ts b/packages/instrumenter/src/disable-type-checks.ts index c131c96a1f..d01dd3e5b4 100644 --- a/packages/instrumenter/src/disable-type-checks.ts +++ b/packages/instrumenter/src/disable-type-checks.ts @@ -35,7 +35,17 @@ function disableTypeCheckingInBabelAst(ast: JSAst | TSAst): string { } function prefixWithNoCheck(code: string): string { - return `// @ts-nocheck\n${code}`; + if (code.startsWith('#')) { + // first line has a shebang (#!/usr/bin/env node) + const newLineIndex = code.indexOf('\n'); + if (newLineIndex > 0) { + return `${code.substr(0, newLineIndex)}\n// @ts-nocheck\n${code.substr(newLineIndex + 1)}`; + } else { + return code; + } + } else { + return `// @ts-nocheck\n${code}`; + } } function disableTypeCheckingInHtml(ast: HtmlAst): string { diff --git a/packages/instrumenter/test/integration/disable-type-checks.it.spec.ts b/packages/instrumenter/test/integration/disable-type-checks.it.spec.ts index 6ea4077485..3bb4c19c28 100644 --- a/packages/instrumenter/test/integration/disable-type-checks.it.spec.ts +++ b/packages/instrumenter/test/integration/disable-type-checks.it.spec.ts @@ -20,7 +20,7 @@ const resolveTestResource = path.resolve.bind( ); describe(`${disableTypeChecks.name} integration`, () => { - it('should be able disable type checks of a type script file', async () => { + it('should be able disable type checks of a typescript file', async () => { await arrangeAndActAssert('app.component.ts'); }); it('should be able disable type checks of an html file', async () => { diff --git a/packages/instrumenter/test/integration/instrumenter.it.spec.ts b/packages/instrumenter/test/integration/instrumenter.it.spec.ts index 27715a07b2..95125954dc 100644 --- a/packages/instrumenter/test/integration/instrumenter.it.spec.ts +++ b/packages/instrumenter/test/integration/instrumenter.it.spec.ts @@ -46,6 +46,9 @@ describe('instrumenter integration', () => { it('should be able to instrument super calls', async () => { await arrangeAndActAssert('super-call.ts'); }); + it('should be able to instrument js files with a shebang in them', async () => { + await arrangeAndActAssert('shebang.js'); + }); describe('type declarations', () => { it('should not produce mutants for TS type definitions', async () => { diff --git a/packages/instrumenter/test/unit/disable-type-checks.spec.ts b/packages/instrumenter/test/unit/disable-type-checks.spec.ts index 1c61cd3006..9396d1224a 100644 --- a/packages/instrumenter/test/unit/disable-type-checks.spec.ts +++ b/packages/instrumenter/test/unit/disable-type-checks.spec.ts @@ -14,6 +14,19 @@ describe(disableTypeChecks.name, () => { assertions.expectTextFileEqual(actual, new File('foo.js', '// @ts-nocheck\nfoo.bar();')); }); + describe('with shebang (`#!/usr/bin/env node`)', () => { + it('should insert `// @ts-nocheck` after the new line', async () => { + const inputFile = new File('foo.js', '#!/usr/bin/env node\nfoo.bar();'); + const actual = await disableTypeChecks(inputFile, { plugins: null }); + assertions.expectTextFileEqual(actual, new File('foo.js', '#!/usr/bin/env node\n// @ts-nocheck\nfoo.bar();')); + }); + it('should not insert if there is no code', async () => { + const inputFile = new File('foo.js', '#!/usr/bin/env node'); + const actual = await disableTypeChecks(inputFile, { plugins: null }); + assertions.expectTextFileEqual(actual, new File('foo.js', '#!/usr/bin/env node')); + }); + }); + it('should not even parse the file if "@ts-" can\'t be found anywhere in the file (performance optimization)', async () => { const createParserSpy = sinon.spy(parsers, 'createParser'); const inputFile = new File('foo.js', 'foo.bar();'); diff --git a/packages/instrumenter/testResources/disable-type-checks/app.component.ts.out.snap b/packages/instrumenter/testResources/disable-type-checks/app.component.ts.out.snap index 0891124a73..c95c1d7ad5 100644 --- a/packages/instrumenter/testResources/disable-type-checks/app.component.ts.out.snap +++ b/packages/instrumenter/testResources/disable-type-checks/app.component.ts.out.snap @@ -65,3 +65,36 @@ export class AppComponent { } " `; + +exports[`disableTypeChecks integration should be able disable type checks of a typescript file 1`] = ` +"// @ts-nocheck +import {Component, HostListener, Inject} from '@angular/core'; +import {DOCUMENT} from '@angular/common'; + +@Component({ + selector: 'ksw-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'] +}) +export class AppComponent { + title = 'Kantishop'; + + constructor(@Inject(DOCUMENT) document) { + } + + @HostListener('window:scroll', ['$event']) + onWindowScroll(e) { + // + if (window.pageYOffset > document.getElementById('banner').offsetHeight) { + const element = document.getElementById('kanti-menu'); + element.classList.add('kanti-sticky'); + document.getElementsByTagName('main').item(0).setAttribute('style', 'margin-top: 50px'); + } else { + const element = document.getElementById('kanti-menu'); + element.classList.remove('kanti-sticky'); + document.getElementsByTagName('main').item(0).removeAttribute('style'); + } + } +} +" +`; diff --git a/packages/instrumenter/testResources/instrumenter/shebang.js b/packages/instrumenter/testResources/instrumenter/shebang.js new file mode 100644 index 0000000000..b691b76619 --- /dev/null +++ b/packages/instrumenter/testResources/instrumenter/shebang.js @@ -0,0 +1,3 @@ +#! /usr/bin/env node + +console.log('test'); diff --git a/packages/instrumenter/testResources/instrumenter/shebang.js.out.snap b/packages/instrumenter/testResources/instrumenter/shebang.js.out.snap new file mode 100644 index 0000000000..239391de3d --- /dev/null +++ b/packages/instrumenter/testResources/instrumenter/shebang.js.out.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`instrumenter integration should be able to instrument js files with a shebang in them 1`] = ` +"#! /usr/bin/env node + +function stryNS_9fa48() { + var g = new Function(\\"return this\\")(); + var ns = g.__stryker__ || (g.__stryker__ = {}); + + if (ns.activeMutant === undefined && g.process && g.process.env && g.process.env.__STRYKER_ACTIVE_MUTANT__) { + ns.activeMutant = Number(g.process.env.__STRYKER_ACTIVE_MUTANT__); + } + + function retrieveNS() { + return ns; + } + + stryNS_9fa48 = retrieveNS; + return retrieveNS(); +} + +stryNS_9fa48(); + +function stryCov_9fa48() { + var ns = stryNS_9fa48(); + var cov = ns.mutantCoverage || (ns.mutantCoverage = { + static: {}, + perTest: {} + }); + + function cover() { + var c = cov.static; + + if (ns.currentTestId) { + c = cov.perTest[ns.currentTestId] = cov.perTest[ns.currentTestId] || {}; + } + + var a = arguments; + + for (var i = 0; i < a.length; i++) { + c[a[i]] = (c[a[i]] || 0) + 1; + } + } + + stryCov_9fa48 = cover; + cover.apply(null, arguments); +} + +function stryMutAct_9fa48(id) { + var ns = stryNS_9fa48(); + + function isActive(id) { + return ns.activeMutant === id; + } + + stryMutAct_9fa48 = isActive; + return isActive(id); +} + +console.log(stryMutAct_9fa48(0) ? \\"\\" : (stryCov_9fa48(0), 'test'));" +`;