@d-es-ign/stryker-js-instrumenter 9.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/LICENSE +201 -0
  2. package/dist/src/create-instrumenter.d.ts +16 -0
  3. package/dist/src/create-instrumenter.d.ts.map +1 -0
  4. package/dist/src/create-instrumenter.js +15 -0
  5. package/dist/src/create-instrumenter.js.map +1 -0
  6. package/dist/src/disable-type-checks.d.ts +11 -0
  7. package/dist/src/disable-type-checks.d.ts.map +1 -0
  8. package/dist/src/disable-type-checks.js +125 -0
  9. package/dist/src/disable-type-checks.js.map +1 -0
  10. package/dist/src/file.d.ts +6 -0
  11. package/dist/src/file.d.ts.map +1 -0
  12. package/dist/src/file.js +2 -0
  13. package/dist/src/file.js.map +1 -0
  14. package/dist/src/frameworks/angular-ignorer.d.ts +16 -0
  15. package/dist/src/frameworks/angular-ignorer.d.ts.map +1 -0
  16. package/dist/src/frameworks/angular-ignorer.js +85 -0
  17. package/dist/src/frameworks/angular-ignorer.js.map +1 -0
  18. package/dist/src/frameworks/index.d.ts +4 -0
  19. package/dist/src/frameworks/index.d.ts.map +1 -0
  20. package/dist/src/frameworks/index.js +7 -0
  21. package/dist/src/frameworks/index.js.map +1 -0
  22. package/dist/src/index.d.ts +9 -0
  23. package/dist/src/index.d.ts.map +1 -0
  24. package/dist/src/index.js +8 -0
  25. package/dist/src/index.js.map +1 -0
  26. package/dist/src/instrument-result.d.ts +7 -0
  27. package/dist/src/instrument-result.d.ts.map +1 -0
  28. package/dist/src/instrument-result.js +2 -0
  29. package/dist/src/instrument-result.js.map +1 -0
  30. package/dist/src/instrumenter-options.d.ts +5 -0
  31. package/dist/src/instrumenter-options.d.ts.map +1 -0
  32. package/dist/src/instrumenter-options.js +2 -0
  33. package/dist/src/instrumenter-options.js.map +1 -0
  34. package/dist/src/instrumenter-tokens.d.ts +6 -0
  35. package/dist/src/instrumenter-tokens.d.ts.map +1 -0
  36. package/dist/src/instrumenter-tokens.js +6 -0
  37. package/dist/src/instrumenter-tokens.js.map +1 -0
  38. package/dist/src/instrumenter.d.ts +24 -0
  39. package/dist/src/instrumenter.d.ts.map +1 -0
  40. package/dist/src/instrumenter.js +76 -0
  41. package/dist/src/instrumenter.js.map +1 -0
  42. package/dist/src/mutant-placers/expression-mutant-placer.d.ts +10 -0
  43. package/dist/src/mutant-placers/expression-mutant-placer.d.ts.map +1 -0
  44. package/dist/src/mutant-placers/expression-mutant-placer.js +144 -0
  45. package/dist/src/mutant-placers/expression-mutant-placer.js.map +1 -0
  46. package/dist/src/mutant-placers/index.d.ts +5 -0
  47. package/dist/src/mutant-placers/index.d.ts.map +1 -0
  48. package/dist/src/mutant-placers/index.js +11 -0
  49. package/dist/src/mutant-placers/index.js.map +1 -0
  50. package/dist/src/mutant-placers/mutant-placer.d.ts +8 -0
  51. package/dist/src/mutant-placers/mutant-placer.d.ts.map +1 -0
  52. package/dist/src/mutant-placers/mutant-placer.js +2 -0
  53. package/dist/src/mutant-placers/mutant-placer.js.map +1 -0
  54. package/dist/src/mutant-placers/statement-mutant-placer.d.ts +8 -0
  55. package/dist/src/mutant-placers/statement-mutant-placer.d.ts.map +1 -0
  56. package/dist/src/mutant-placers/statement-mutant-placer.js +24 -0
  57. package/dist/src/mutant-placers/statement-mutant-placer.js.map +1 -0
  58. package/dist/src/mutant-placers/switch-case-mutant-placer.d.ts +14 -0
  59. package/dist/src/mutant-placers/switch-case-mutant-placer.d.ts.map +1 -0
  60. package/dist/src/mutant-placers/switch-case-mutant-placer.js +29 -0
  61. package/dist/src/mutant-placers/switch-case-mutant-placer.js.map +1 -0
  62. package/dist/src/mutant-placers/throw-placement-error.d.ts +5 -0
  63. package/dist/src/mutant-placers/throw-placement-error.d.ts.map +1 -0
  64. package/dist/src/mutant-placers/throw-placement-error.js +17 -0
  65. package/dist/src/mutant-placers/throw-placement-error.js.map +1 -0
  66. package/dist/src/mutant.d.ts +27 -0
  67. package/dist/src/mutant.d.ts.map +1 -0
  68. package/dist/src/mutant.js +80 -0
  69. package/dist/src/mutant.js.map +1 -0
  70. package/dist/src/mutators/arithmetic-operator-mutator.d.ts +3 -0
  71. package/dist/src/mutators/arithmetic-operator-mutator.d.ts.map +1 -0
  72. package/dist/src/mutators/arithmetic-operator-mutator.js +34 -0
  73. package/dist/src/mutators/arithmetic-operator-mutator.js.map +1 -0
  74. package/dist/src/mutators/array-declaration-mutator.d.ts +3 -0
  75. package/dist/src/mutators/array-declaration-mutator.d.ts.map +1 -0
  76. package/dist/src/mutators/array-declaration-mutator.js +26 -0
  77. package/dist/src/mutators/array-declaration-mutator.js.map +1 -0
  78. package/dist/src/mutators/arrow-function-mutator.d.ts +3 -0
  79. package/dist/src/mutators/arrow-function-mutator.d.ts.map +1 -0
  80. package/dist/src/mutators/arrow-function-mutator.js +14 -0
  81. package/dist/src/mutators/arrow-function-mutator.js.map +1 -0
  82. package/dist/src/mutators/assignment-operator-mutator.d.ts +3 -0
  83. package/dist/src/mutators/assignment-operator-mutator.d.ts.map +1 -0
  84. package/dist/src/mutators/assignment-operator-mutator.js +42 -0
  85. package/dist/src/mutators/assignment-operator-mutator.js.map +1 -0
  86. package/dist/src/mutators/block-statement-mutator.d.ts +3 -0
  87. package/dist/src/mutators/block-statement-mutator.d.ts.map +1 -0
  88. package/dist/src/mutators/block-statement-mutator.js +63 -0
  89. package/dist/src/mutators/block-statement-mutator.js.map +1 -0
  90. package/dist/src/mutators/boolean-literal-mutator.d.ts +3 -0
  91. package/dist/src/mutators/boolean-literal-mutator.d.ts.map +1 -0
  92. package/dist/src/mutators/boolean-literal-mutator.js +17 -0
  93. package/dist/src/mutators/boolean-literal-mutator.js.map +1 -0
  94. package/dist/src/mutators/conditional-expression-mutator.d.ts +3 -0
  95. package/dist/src/mutators/conditional-expression-mutator.d.ts.map +1 -0
  96. package/dist/src/mutators/conditional-expression-mutator.js +81 -0
  97. package/dist/src/mutators/conditional-expression-mutator.js.map +1 -0
  98. package/dist/src/mutators/equality-operator-mutator.d.ts +3 -0
  99. package/dist/src/mutators/equality-operator-mutator.d.ts.map +1 -0
  100. package/dist/src/mutators/equality-operator-mutator.js +28 -0
  101. package/dist/src/mutators/equality-operator-mutator.js.map +1 -0
  102. package/dist/src/mutators/index.d.ts +4 -0
  103. package/dist/src/mutators/index.d.ts.map +1 -0
  104. package/dist/src/mutators/index.js +4 -0
  105. package/dist/src/mutators/index.js.map +1 -0
  106. package/dist/src/mutators/logical-operator-mutator.d.ts +3 -0
  107. package/dist/src/mutators/logical-operator-mutator.d.ts.map +1 -0
  108. package/dist/src/mutators/logical-operator-mutator.js +21 -0
  109. package/dist/src/mutators/logical-operator-mutator.js.map +1 -0
  110. package/dist/src/mutators/method-expression-mutator.d.ts +3 -0
  111. package/dist/src/mutators/method-expression-mutator.d.ts.map +1 -0
  112. package/dist/src/mutators/method-expression-mutator.js +65 -0
  113. package/dist/src/mutators/method-expression-mutator.js.map +1 -0
  114. package/dist/src/mutators/mutate.d.ts +3 -0
  115. package/dist/src/mutators/mutate.d.ts.map +1 -0
  116. package/dist/src/mutators/mutate.js +35 -0
  117. package/dist/src/mutators/mutate.js.map +1 -0
  118. package/dist/src/mutators/mutator-options.d.ts +5 -0
  119. package/dist/src/mutators/mutator-options.d.ts.map +1 -0
  120. package/dist/src/mutators/mutator-options.js +2 -0
  121. package/dist/src/mutators/mutator-options.js.map +1 -0
  122. package/dist/src/mutators/node-mutator.d.ts +6 -0
  123. package/dist/src/mutators/node-mutator.d.ts.map +1 -0
  124. package/dist/src/mutators/node-mutator.js +2 -0
  125. package/dist/src/mutators/node-mutator.js.map +1 -0
  126. package/dist/src/mutators/object-literal-mutator.d.ts +3 -0
  127. package/dist/src/mutators/object-literal-mutator.d.ts.map +1 -0
  128. package/dist/src/mutators/object-literal-mutator.js +11 -0
  129. package/dist/src/mutators/object-literal-mutator.js.map +1 -0
  130. package/dist/src/mutators/optional-chaining-mutator.d.ts +15 -0
  131. package/dist/src/mutators/optional-chaining-mutator.d.ts.map +1 -0
  132. package/dist/src/mutators/optional-chaining-mutator.js +28 -0
  133. package/dist/src/mutators/optional-chaining-mutator.js.map +1 -0
  134. package/dist/src/mutators/regex-mutator.d.ts +3 -0
  135. package/dist/src/mutators/regex-mutator.d.ts.map +1 -0
  136. package/dist/src/mutators/regex-mutator.js +53 -0
  137. package/dist/src/mutators/regex-mutator.js.map +1 -0
  138. package/dist/src/mutators/string-literal-mutator.d.ts +3 -0
  139. package/dist/src/mutators/string-literal-mutator.d.ts.map +1 -0
  140. package/dist/src/mutators/string-literal-mutator.js +36 -0
  141. package/dist/src/mutators/string-literal-mutator.js.map +1 -0
  142. package/dist/src/mutators/unary-operator-mutator.d.ts +3 -0
  143. package/dist/src/mutators/unary-operator-mutator.d.ts.map +1 -0
  144. package/dist/src/mutators/unary-operator-mutator.js +27 -0
  145. package/dist/src/mutators/unary-operator-mutator.js.map +1 -0
  146. package/dist/src/mutators/update-operator-mutator.d.ts +3 -0
  147. package/dist/src/mutators/update-operator-mutator.d.ts.map +1 -0
  148. package/dist/src/mutators/update-operator-mutator.js +17 -0
  149. package/dist/src/mutators/update-operator-mutator.js.map +1 -0
  150. package/dist/src/parsers/create-parser.d.ts +5 -0
  151. package/dist/src/parsers/create-parser.d.ts.map +1 -0
  152. package/dist/src/parsers/create-parser.js +58 -0
  153. package/dist/src/parsers/create-parser.js.map +1 -0
  154. package/dist/src/parsers/html-parser.d.ts +4 -0
  155. package/dist/src/parsers/html-parser.d.ts.map +1 -0
  156. package/dist/src/parsers/html-parser.js +96 -0
  157. package/dist/src/parsers/html-parser.js.map +1 -0
  158. package/dist/src/parsers/index.d.ts +5 -0
  159. package/dist/src/parsers/index.d.ts.map +1 -0
  160. package/dist/src/parsers/index.js +3 -0
  161. package/dist/src/parsers/index.js.map +1 -0
  162. package/dist/src/parsers/js-parser.d.ts +4 -0
  163. package/dist/src/parsers/js-parser.d.ts.map +1 -0
  164. package/dist/src/parsers/js-parser.js +50 -0
  165. package/dist/src/parsers/js-parser.js.map +1 -0
  166. package/dist/src/parsers/parse-error.d.ts +5 -0
  167. package/dist/src/parsers/parse-error.d.ts.map +1 -0
  168. package/dist/src/parsers/parse-error.js +6 -0
  169. package/dist/src/parsers/parse-error.js.map +1 -0
  170. package/dist/src/parsers/parser-context.d.ts +6 -0
  171. package/dist/src/parsers/parser-context.d.ts.map +1 -0
  172. package/dist/src/parsers/parser-context.js +2 -0
  173. package/dist/src/parsers/parser-context.js.map +1 -0
  174. package/dist/src/parsers/parser-options.d.ts +4 -0
  175. package/dist/src/parsers/parser-options.d.ts.map +1 -0
  176. package/dist/src/parsers/parser-options.js +2 -0
  177. package/dist/src/parsers/parser-options.js.map +1 -0
  178. package/dist/src/parsers/svelte-parser.d.ts +4 -0
  179. package/dist/src/parsers/svelte-parser.d.ts.map +1 -0
  180. package/dist/src/parsers/svelte-parser.js +169 -0
  181. package/dist/src/parsers/svelte-parser.js.map +1 -0
  182. package/dist/src/parsers/ts-parser.d.ts +9 -0
  183. package/dist/src/parsers/ts-parser.d.ts.map +1 -0
  184. package/dist/src/parsers/ts-parser.js +54 -0
  185. package/dist/src/parsers/ts-parser.js.map +1 -0
  186. package/dist/src/printers/html-printer.d.ts +4 -0
  187. package/dist/src/printers/html-printer.d.ts.map +1 -0
  188. package/dist/src/printers/html-printer.js +15 -0
  189. package/dist/src/printers/html-printer.js.map +1 -0
  190. package/dist/src/printers/index.d.ts +7 -0
  191. package/dist/src/printers/index.d.ts.map +1 -0
  192. package/dist/src/printers/index.js +23 -0
  193. package/dist/src/printers/index.js.map +1 -0
  194. package/dist/src/printers/js-printer.d.ts +4 -0
  195. package/dist/src/printers/js-printer.d.ts.map +1 -0
  196. package/dist/src/printers/js-printer.js +6 -0
  197. package/dist/src/printers/js-printer.js.map +1 -0
  198. package/dist/src/printers/svelte-printer.d.ts +4 -0
  199. package/dist/src/printers/svelte-printer.d.ts.map +1 -0
  200. package/dist/src/printers/svelte-printer.js +28 -0
  201. package/dist/src/printers/svelte-printer.js.map +1 -0
  202. package/dist/src/printers/ts-printer.d.ts +4 -0
  203. package/dist/src/printers/ts-printer.d.ts.map +1 -0
  204. package/dist/src/printers/ts-printer.js +9 -0
  205. package/dist/src/printers/ts-printer.js.map +1 -0
  206. package/dist/src/syntax/index.d.ts +86 -0
  207. package/dist/src/syntax/index.d.ts.map +1 -0
  208. package/dist/src/syntax/index.js +9 -0
  209. package/dist/src/syntax/index.js.map +1 -0
  210. package/dist/src/transformers/babel-transformer.d.ts +4 -0
  211. package/dist/src/transformers/babel-transformer.d.ts.map +1 -0
  212. package/dist/src/transformers/babel-transformer.js +142 -0
  213. package/dist/src/transformers/babel-transformer.js.map +1 -0
  214. package/dist/src/transformers/directive-bookkeeper.d.ts +19 -0
  215. package/dist/src/transformers/directive-bookkeeper.d.ts.map +1 -0
  216. package/dist/src/transformers/directive-bookkeeper.js +108 -0
  217. package/dist/src/transformers/directive-bookkeeper.js.map +1 -0
  218. package/dist/src/transformers/html-transformer.d.ts +4 -0
  219. package/dist/src/transformers/html-transformer.d.ts.map +1 -0
  220. package/dist/src/transformers/html-transformer.js +6 -0
  221. package/dist/src/transformers/html-transformer.js.map +1 -0
  222. package/dist/src/transformers/ignorer-bookkeeper.d.ts +18 -0
  223. package/dist/src/transformers/ignorer-bookkeeper.d.ts.map +1 -0
  224. package/dist/src/transformers/ignorer-bookkeeper.js +29 -0
  225. package/dist/src/transformers/ignorer-bookkeeper.js.map +1 -0
  226. package/dist/src/transformers/index.d.ts +6 -0
  227. package/dist/src/transformers/index.d.ts.map +1 -0
  228. package/dist/src/transformers/index.js +4 -0
  229. package/dist/src/transformers/index.js.map +1 -0
  230. package/dist/src/transformers/mutant-collector.d.ts +19 -0
  231. package/dist/src/transformers/mutant-collector.d.ts.map +1 -0
  232. package/dist/src/transformers/mutant-collector.js +25 -0
  233. package/dist/src/transformers/mutant-collector.js.map +1 -0
  234. package/dist/src/transformers/svelte-transformer.d.ts +4 -0
  235. package/dist/src/transformers/svelte-transformer.d.ts.map +1 -0
  236. package/dist/src/transformers/svelte-transformer.js +46 -0
  237. package/dist/src/transformers/svelte-transformer.js.map +1 -0
  238. package/dist/src/transformers/transformer-options.d.ts +6 -0
  239. package/dist/src/transformers/transformer-options.d.ts.map +1 -0
  240. package/dist/src/transformers/transformer-options.js +2 -0
  241. package/dist/src/transformers/transformer-options.js.map +1 -0
  242. package/dist/src/transformers/transformer.d.ts +22 -0
  243. package/dist/src/transformers/transformer.d.ts.map +1 -0
  244. package/dist/src/transformers/transformer.js +30 -0
  245. package/dist/src/transformers/transformer.js.map +1 -0
  246. package/dist/src/util/binary-operator.d.ts +2 -0
  247. package/dist/src/util/binary-operator.d.ts.map +1 -0
  248. package/dist/src/util/binary-operator.js +2 -0
  249. package/dist/src/util/binary-operator.js.map +1 -0
  250. package/dist/src/util/index.d.ts +4 -0
  251. package/dist/src/util/index.d.ts.map +1 -0
  252. package/dist/src/util/index.js +4 -0
  253. package/dist/src/util/index.js.map +1 -0
  254. package/dist/src/util/position-converter.d.ts +16 -0
  255. package/dist/src/util/position-converter.d.ts.map +1 -0
  256. package/dist/src/util/position-converter.js +126 -0
  257. package/dist/src/util/position-converter.js.map +1 -0
  258. package/dist/src/util/syntax-helpers.d.ts +60 -0
  259. package/dist/src/util/syntax-helpers.d.ts.map +1 -0
  260. package/dist/src/util/syntax-helpers.js +217 -0
  261. package/dist/src/util/syntax-helpers.js.map +1 -0
  262. package/package.json +64 -0
  263. package/src/create-instrumenter.ts +30 -0
  264. package/src/disable-type-checks.ts +153 -0
  265. package/src/file.ts +6 -0
  266. package/src/frameworks/angular-ignorer.ts +129 -0
  267. package/src/frameworks/index.ts +9 -0
  268. package/src/index.ts +8 -0
  269. package/src/instrument-result.ts +8 -0
  270. package/src/instrumenter-options.ts +5 -0
  271. package/src/instrumenter-tokens.ts +5 -0
  272. package/src/instrumenter.ts +100 -0
  273. package/src/mutant-placers/expression-mutant-placer.ts +210 -0
  274. package/src/mutant-placers/index.ts +12 -0
  275. package/src/mutant-placers/mutant-placer.ts +9 -0
  276. package/src/mutant-placers/statement-mutant-placer.ts +39 -0
  277. package/src/mutant-placers/switch-case-mutant-placer.ts +41 -0
  278. package/src/mutant-placers/throw-placement-error.ts +34 -0
  279. package/src/mutant.ts +101 -0
  280. package/src/mutators/arithmetic-operator-mutator.ts +51 -0
  281. package/src/mutators/array-declaration-mutator.ts +36 -0
  282. package/src/mutators/arrow-function-mutator.ts +22 -0
  283. package/src/mutators/assignment-operator-mutator.ts +59 -0
  284. package/src/mutators/block-statement-mutator.ts +89 -0
  285. package/src/mutators/boolean-literal-mutator.ts +24 -0
  286. package/src/mutators/conditional-expression-mutator.ts +92 -0
  287. package/src/mutators/equality-operator-mutator.ts +35 -0
  288. package/src/mutators/index.ts +3 -0
  289. package/src/mutators/logical-operator-mutator.ts +29 -0
  290. package/src/mutators/method-expression-mutator.ts +99 -0
  291. package/src/mutators/mutate.ts +36 -0
  292. package/src/mutators/mutator-options.ts +4 -0
  293. package/src/mutators/node-mutator.ts +6 -0
  294. package/src/mutators/object-literal-mutator.ts +15 -0
  295. package/src/mutators/optional-chaining-mutator.ts +39 -0
  296. package/src/mutators/regex-mutator.ts +69 -0
  297. package/src/mutators/string-literal-mutator.ts +49 -0
  298. package/src/mutators/unary-operator-mutator.ts +39 -0
  299. package/src/mutators/update-operator-mutator.ts +26 -0
  300. package/src/parsers/create-parser.ts +78 -0
  301. package/src/parsers/html-parser.ts +138 -0
  302. package/src/parsers/index.ts +5 -0
  303. package/src/parsers/js-parser.ts +58 -0
  304. package/src/parsers/parse-error.ts +9 -0
  305. package/src/parsers/parser-context.ts +15 -0
  306. package/src/parsers/parser-options.ts +3 -0
  307. package/src/parsers/svelte-parser.ts +255 -0
  308. package/src/parsers/ts-parser.ts +69 -0
  309. package/src/printers/html-printer.ts +20 -0
  310. package/src/printers/index.ts +33 -0
  311. package/src/printers/js-printer.ts +11 -0
  312. package/src/printers/svelte-printer.ts +34 -0
  313. package/src/printers/ts-printer.ts +14 -0
  314. package/src/syntax/index.ts +97 -0
  315. package/src/transformers/babel-transformer.ts +217 -0
  316. package/src/transformers/directive-bookkeeper.ts +173 -0
  317. package/src/transformers/html-transformer.ts +13 -0
  318. package/src/transformers/ignorer-bookkeeper.ts +41 -0
  319. package/src/transformers/index.ts +6 -0
  320. package/src/transformers/mutant-collector.ts +44 -0
  321. package/src/transformers/svelte-transformer.ts +57 -0
  322. package/src/transformers/transformer-options.ts +7 -0
  323. package/src/transformers/transformer.ts +54 -0
  324. package/src/util/binary-operator.ts +23 -0
  325. package/src/util/index.ts +3 -0
  326. package/src/util/position-converter.ts +145 -0
  327. package/src/util/syntax-helpers.ts +301 -0
@@ -0,0 +1,153 @@
1
+ import type { types } from '@babel/core';
2
+ import { notEmpty } from '@stryker-mutator/util';
3
+
4
+ import { File } from './file.js';
5
+
6
+ import { createParser, getFormat, ParserOptions } from './parsers/index.js';
7
+ import { AstFormat, HtmlAst, ScriptAst, SvelteAst } from './syntax/index.js';
8
+
9
+ const commentDirectiveRegEx = /^(\s*)@(ts-[a-z-]+).*$/;
10
+ const tsDirectiveLikeRegEx = /@(ts-[a-z-]+)/;
11
+ const startingCommentRegex = /(^\s*\/\*.*?\*\/)/gs;
12
+
13
+ /**
14
+ * Disables TypeScript type checking for a single file by inserting `// @ts-nocheck` commands.
15
+ * It also does this for *.js files, as they can be type checked by typescript as well.
16
+ * Other file types are silently ignored
17
+ *
18
+ * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#-ts-nocheck-in-typescript-files
19
+ */
20
+ export async function disableTypeChecks(
21
+ file: File,
22
+ options: ParserOptions,
23
+ ): Promise<File> {
24
+ const format = getFormat(file.name);
25
+ if (!format) {
26
+ // Readme files and stuff don't need disabling.
27
+ return file;
28
+ }
29
+ if (isJSFileWithoutTSDirectives(file, format)) {
30
+ // Performance optimization. Only parse the file when it has a chance of containing a `// @ts-` directive
31
+ return {
32
+ ...file,
33
+ content: prefixWithNoCheck(file.content),
34
+ };
35
+ }
36
+
37
+ const parse = createParser(options);
38
+ const ast = await parse(file.content, file.name);
39
+ switch (ast.format) {
40
+ case AstFormat.JS:
41
+ case AstFormat.TS:
42
+ case AstFormat.Tsx:
43
+ return { ...file, content: disableTypeCheckingInBabelAst(ast) };
44
+ case AstFormat.Html:
45
+ return { ...file, content: disableTypeCheckingInHtml(ast) };
46
+ case AstFormat.Svelte:
47
+ return { ...file, content: disableTypeCheckingInSvelte(ast) };
48
+ }
49
+ }
50
+
51
+ function isJSFileWithoutTSDirectives(file: File, format: AstFormat) {
52
+ return (
53
+ (format === AstFormat.TS || format === AstFormat.JS) &&
54
+ !tsDirectiveLikeRegEx.test(file.content)
55
+ );
56
+ }
57
+
58
+ function disableTypeCheckingInBabelAst(ast: ScriptAst): string {
59
+ return prefixWithNoCheck(
60
+ removeTSDirectives(ast.rawContent, ast.root.comments),
61
+ );
62
+ }
63
+
64
+ function prefixWithNoCheck(code: string): string {
65
+ if (code.startsWith('#')) {
66
+ // first line has a shebang (#!/usr/bin/env node)
67
+ const newLineIndex = code.indexOf('\n');
68
+ if (newLineIndex > 0) {
69
+ return `${code.substring(0, newLineIndex)}\n// @ts-nocheck\n${code.substring(newLineIndex + 1)}`;
70
+ } else {
71
+ return code;
72
+ }
73
+ } else {
74
+ // We should leave comments, like `/** @jest-env jsdom */ at the top of the file, see #2569
75
+ startingCommentRegex.lastIndex = 0;
76
+ const commentMatch = startingCommentRegex.exec(code);
77
+ return `${commentMatch?.[1].concat('\n') ?? ''}// @ts-nocheck\n${code.substring(commentMatch?.[1].length ?? 0)}`;
78
+ }
79
+ }
80
+
81
+ function disableTypeCheckingInHtml(ast: HtmlAst): string {
82
+ const sortedScripts = [...ast.root.scripts].sort(
83
+ (a, b) => a.root.start! - b.root.start!,
84
+ );
85
+ let currentIndex = 0;
86
+ let html = '';
87
+ for (const script of sortedScripts) {
88
+ html += ast.rawContent.substring(currentIndex, script.root.start!);
89
+ html += '\n';
90
+ html += prefixWithNoCheck(
91
+ removeTSDirectives(script.rawContent, script.root.comments),
92
+ );
93
+ html += '\n';
94
+ currentIndex = script.root.end!;
95
+ }
96
+ html += ast.rawContent.substring(currentIndex);
97
+ return html;
98
+ }
99
+
100
+ function disableTypeCheckingInSvelte(ast: SvelteAst): string {
101
+ const sortedScripts = [ast.root.moduleScript, ...ast.root.additionalScripts]
102
+ .filter(notEmpty)
103
+ .sort((a, b) => a.range.start - b.range.start);
104
+ let currentIndex = 0;
105
+ let html = '';
106
+ for (const script of sortedScripts) {
107
+ html += ast.rawContent.substring(currentIndex, script.range.start);
108
+ html += '\n';
109
+ html += prefixWithNoCheck(
110
+ removeTSDirectives(script.ast.rawContent, script.ast.root.comments),
111
+ );
112
+ html += '\n';
113
+ currentIndex = script.range.end;
114
+ }
115
+ html += ast.rawContent.substring(currentIndex);
116
+ return html;
117
+ }
118
+
119
+ function removeTSDirectives(
120
+ text: string,
121
+ comments: Array<types.CommentBlock | types.CommentLine> | null | undefined,
122
+ ): string {
123
+ const directiveRanges = comments
124
+ ?.map(tryParseTSDirective)
125
+ .filter(notEmpty)
126
+ .sort((a, b) => a.startPos - b.startPos);
127
+ if (directiveRanges) {
128
+ let currentIndex = 0;
129
+ let pruned = '';
130
+ for (const directiveRange of directiveRanges) {
131
+ pruned += text.substring(currentIndex, directiveRange.startPos);
132
+ currentIndex = directiveRange.endPos;
133
+ }
134
+ pruned += text.substring(currentIndex);
135
+ return pruned;
136
+ } else {
137
+ return text;
138
+ }
139
+ }
140
+
141
+ function tryParseTSDirective(
142
+ comment: types.CommentBlock | types.CommentLine,
143
+ ): { startPos: number; endPos: number } | undefined {
144
+ const match = commentDirectiveRegEx.exec(comment.value);
145
+ if (match) {
146
+ const directiveStartPos = comment.start! + match[1].length + 2; // +2 to account for the `//` or `/*` start character
147
+ return {
148
+ startPos: directiveStartPos,
149
+ endPos: directiveStartPos + match[2].length + 1,
150
+ };
151
+ }
152
+ return undefined;
153
+ }
package/src/file.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { FileDescription } from '@stryker-mutator/api/core';
2
+
3
+ export interface File extends FileDescription {
4
+ name: string;
5
+ content: string;
6
+ }
@@ -0,0 +1,129 @@
1
+ import { Ignorer, NodePath } from '@stryker-mutator/api/ignore';
2
+
3
+ const ANGULAR_SIGNAL_IO_FUNCTIONS = Object.freeze(['input', 'model', 'output']);
4
+
5
+ const ANGULAR_SIGNAL_QUERY_FUNCTIONS = Object.freeze([
6
+ 'contentChild',
7
+ 'contentChildren',
8
+ 'viewChild',
9
+ 'viewChildren',
10
+ ]);
11
+
12
+ const INPUT_MODEL_OUTPUT_CONFIG_MSG =
13
+ 'Angular signal based input, model and output functions configuration object cannot be mutated as that causes issues with the Angular compiler.';
14
+
15
+ const SIGNAL_QUERY_OPTIONS_MSG =
16
+ 'Angular signal query options object cannot be mutated as that causes issues with the Angular compiler.';
17
+
18
+ export class AngularIgnorer implements Ignorer {
19
+ public shouldIgnore(path: NodePath): string | undefined {
20
+ if (this.isInputModelOrOutputConfigurationObject(path)) {
21
+ return INPUT_MODEL_OUTPUT_CONFIG_MSG;
22
+ }
23
+
24
+ if (this.isSignalQueryOptionsObject(path)) {
25
+ return SIGNAL_QUERY_OPTIONS_MSG;
26
+ }
27
+
28
+ return undefined;
29
+ }
30
+
31
+ #isClassFieldLike(path: NodePath): boolean {
32
+ return (
33
+ path.isClassProperty() ||
34
+ path.isClassPrivateProperty() ||
35
+ path.isClassAccessorProperty()
36
+ );
37
+ }
38
+
39
+ /**
40
+ * Determines if the given path is a configuration object for an Angular input, model or output function.
41
+ * This solves the "Argument needs to be statically analyzable." error
42
+ */
43
+ private isInputModelOrOutputConfigurationObject(path: NodePath): boolean {
44
+ if (
45
+ !path.isObjectExpression() ||
46
+ !path.parentPath.isCallExpression() ||
47
+ !path.parentPath.parentPath.isClassProperty()
48
+ ) {
49
+ return false;
50
+ }
51
+
52
+ const callExpression = path.parentPath;
53
+ const objectExpression = path;
54
+
55
+ const isRequiredSignalIOFunction =
56
+ callExpression.node.callee.type === 'MemberExpression' &&
57
+ callExpression.node.callee.object.type === 'Identifier' &&
58
+ ANGULAR_SIGNAL_IO_FUNCTIONS.includes(
59
+ callExpression.node.callee.object.name,
60
+ ) &&
61
+ callExpression.node.callee.property.type === 'Identifier' &&
62
+ callExpression.node.callee.property.name === 'required';
63
+
64
+ const isSignalIOFunction =
65
+ callExpression.node.callee.type === 'Identifier' &&
66
+ ANGULAR_SIGNAL_IO_FUNCTIONS.includes(callExpression.node.callee.name);
67
+
68
+ const isOutput =
69
+ callExpression.node.callee.type === 'Identifier' &&
70
+ callExpression.node.callee.name === 'output';
71
+
72
+ if (isRequiredSignalIOFunction || isOutput) {
73
+ // The { alias: '...' } should be the first argument of the call expression
74
+ return (
75
+ callExpression.node.arguments.length >= 1 &&
76
+ callExpression.node.arguments[0] === objectExpression.node
77
+ );
78
+ }
79
+
80
+ if (isSignalIOFunction) {
81
+ // The { alias: '...' } should be the second argument of the call expression
82
+ return (
83
+ callExpression.node.arguments.length >= 2 &&
84
+ callExpression.node.arguments[1] === objectExpression.node
85
+ );
86
+ }
87
+
88
+ return false;
89
+ }
90
+
91
+ /**
92
+ * Determines if the given path is a configuration object for an Angular signal query function.
93
+ * This solves the "Argument needs to be statically analyzable." error
94
+ */
95
+ private isSignalQueryOptionsObject(path: NodePath): boolean {
96
+ if (
97
+ !path.isObjectExpression() ||
98
+ !path.parentPath.isCallExpression() ||
99
+ !this.#isClassFieldLike(path.parentPath.parentPath)
100
+ ) {
101
+ return false;
102
+ }
103
+
104
+ const callExpression = path.parentPath;
105
+ const objectExpression = path;
106
+
107
+ const callee = callExpression.node.callee;
108
+
109
+ const isQueryFn =
110
+ callee.type === 'Identifier' &&
111
+ ANGULAR_SIGNAL_QUERY_FUNCTIONS.includes(callee.name);
112
+
113
+ const isRequiredQueryFn =
114
+ callee.type === 'MemberExpression' &&
115
+ callee.object.type === 'Identifier' &&
116
+ ANGULAR_SIGNAL_QUERY_FUNCTIONS.includes(callee.object.name) &&
117
+ callee.property.type === 'Identifier' &&
118
+ callee.property.name === 'required';
119
+
120
+ if (!isQueryFn && !isRequiredQueryFn) {
121
+ return false;
122
+ }
123
+
124
+ return (
125
+ callExpression.node.arguments.length >= 2 &&
126
+ callExpression.node.arguments[1] === objectExpression.node
127
+ );
128
+ }
129
+ }
@@ -0,0 +1,9 @@
1
+ import { PluginKind, declareClassPlugin } from '@stryker-mutator/api/plugin';
2
+
3
+ import { AngularIgnorer } from './angular-ignorer.js';
4
+
5
+ export const strykerPlugins = [
6
+ declareClassPlugin(PluginKind.Ignore, 'angular', AngularIgnorer),
7
+ ];
8
+
9
+ export const frameworkPluginsFileUrl = import.meta.url;
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * from './frameworks/index.js';
2
+ export * from './create-instrumenter.js';
3
+ export * from './instrumenter.js';
4
+ export * from './instrument-result.js';
5
+ export * from './instrumenter-options.js';
6
+ export * from './disable-type-checks.js';
7
+ export type { ParserOptions } from './parsers/index.js';
8
+ export * from './file.js';
@@ -0,0 +1,8 @@
1
+ import { Mutant } from '@stryker-mutator/api/core';
2
+
3
+ import { File } from './file.js';
4
+
5
+ export interface InstrumentResult {
6
+ files: readonly File[];
7
+ mutants: readonly Mutant[];
8
+ }
@@ -0,0 +1,5 @@
1
+ import { ParserOptions } from './parsers/index.js';
2
+ import { TransformerOptions } from './transformers/index.js';
3
+
4
+ export interface InstrumenterOptions
5
+ extends ParserOptions, TransformerOptions {}
@@ -0,0 +1,5 @@
1
+ export const instrumenterTokens = Object.freeze({
2
+ createParser: 'instrumenterCreateParser',
3
+ print: 'instrumenterPrint',
4
+ transform: 'instrumenterTransform',
5
+ } as const);
@@ -0,0 +1,100 @@
1
+ import path from 'path';
2
+
3
+ import { tokens, commonTokens } from '@stryker-mutator/api/plugin';
4
+ import { Logger } from '@stryker-mutator/api/logging';
5
+ import { MutateDescription } from '@stryker-mutator/api/core';
6
+
7
+ import { createParser } from './parsers/index.js';
8
+ import { transform, MutantCollector } from './transformers/index.js';
9
+ import { print } from './printers/index.js';
10
+ import { InstrumentResult } from './instrument-result.js';
11
+ import { InstrumenterOptions } from './instrumenter-options.js';
12
+ import { instrumenterTokens } from './instrumenter-tokens.js';
13
+ import { File } from './file.js';
14
+
15
+ /**
16
+ * The instrumenter is responsible for
17
+ * * Generating mutants based on source files
18
+ * * Instrumenting the source code with the mutants placed in `mutant switches`.
19
+ * * Adding mutant coverage expressions in the source code.
20
+ * @see https://github.com/stryker-mutator/stryker-js/issues/1514
21
+ */
22
+ export class Instrumenter {
23
+ public static inject = tokens(
24
+ commonTokens.logger,
25
+ instrumenterTokens.createParser,
26
+ instrumenterTokens.print,
27
+ instrumenterTokens.transform,
28
+ );
29
+
30
+ constructor(
31
+ private readonly logger: Logger,
32
+ private readonly _createParser = createParser,
33
+ private readonly _print = print,
34
+ private readonly _transform = transform,
35
+ ) {}
36
+
37
+ public async instrument(
38
+ files: readonly File[],
39
+ options: InstrumenterOptions,
40
+ ): Promise<InstrumentResult> {
41
+ this.logger.debug(
42
+ 'Instrumenting %d source files with mutants',
43
+ files.length,
44
+ );
45
+ const mutantCollector = new MutantCollector();
46
+ const outFiles: File[] = [];
47
+ let mutantCount = 0;
48
+ const parse = this._createParser(options);
49
+ for (const { name, mutate, content } of files) {
50
+ const ast = await parse(content, name);
51
+ this._transform(ast, mutantCollector, {
52
+ options,
53
+ mutateDescription: toBabelLineNumber(mutate),
54
+ logger: this.logger,
55
+ });
56
+ const mutatedContent = this._print(ast);
57
+ outFiles.push({
58
+ name,
59
+ mutate,
60
+ content: mutatedContent,
61
+ });
62
+ if (this.logger.isDebugEnabled()) {
63
+ const nrOfMutantsInFile = mutantCollector.mutants.length - mutantCount;
64
+ mutantCount = mutantCollector.mutants.length;
65
+ this.logger.debug(
66
+ `Instrumented ${path.relative(process.cwd(), name)} (${nrOfMutantsInFile} mutant(s))`,
67
+ );
68
+ }
69
+ }
70
+ const mutants = mutantCollector.mutants.map((mutant) =>
71
+ mutant.toApiMutant(),
72
+ );
73
+ this.logger.info(
74
+ 'Instrumented %d source file(s) with %d mutant(s)',
75
+ files.length,
76
+ mutants.length,
77
+ );
78
+ return {
79
+ files: outFiles,
80
+ mutants,
81
+ };
82
+ }
83
+ }
84
+
85
+ function toBabelLineNumber(range: MutateDescription): MutateDescription {
86
+ if (typeof range === 'boolean') {
87
+ return range;
88
+ } else {
89
+ return range.map(({ start, end }) => ({
90
+ start: {
91
+ column: start.column,
92
+ line: start.line + 1,
93
+ },
94
+ end: {
95
+ column: end.column,
96
+ line: end.line + 1,
97
+ },
98
+ }));
99
+ }
100
+ }
@@ -0,0 +1,210 @@
1
+ import babel, { type NodePath } from '@babel/core';
2
+
3
+ import {
4
+ mutantTestExpression,
5
+ mutationCoverageSequenceExpression,
6
+ } from '../util/syntax-helpers.js';
7
+
8
+ import { MutantPlacer } from './mutant-placer.js';
9
+
10
+ const { types } = babel;
11
+
12
+ /**
13
+ * Will set the identifier of anonymous function expressions if is located in a variable declaration.
14
+ * Will treat input as readonly. Returns undefined if not needed.
15
+ * @example
16
+ * const a = function() { }
17
+ * becomes
18
+ * const a = function a() {}
19
+ */
20
+ function classOrFunctionExpressionNamedIfNeeded(
21
+ path: NodePath<babel.types.Expression>,
22
+ ): babel.types.Expression | undefined {
23
+ if (
24
+ (path.isFunctionExpression() || path.isClassExpression()) &&
25
+ !path.node.id
26
+ ) {
27
+ if (
28
+ path.parentPath.isVariableDeclarator() &&
29
+ types.isIdentifier(path.parentPath.node.id)
30
+ ) {
31
+ path.node.id = path.parentPath.node.id;
32
+ return path.node;
33
+ } else if (
34
+ path.parentPath.isObjectProperty() &&
35
+ types.isIdentifier(path.parentPath.node.key) &&
36
+ path.getStatementParent()?.isVariableDeclaration()
37
+ ) {
38
+ path.node.id = path.parentPath.node.key;
39
+ return path.node;
40
+ }
41
+ }
42
+ return;
43
+ }
44
+
45
+ /**
46
+ * Will set the identifier of anonymous arrow function expressions if is located in a variable declaration.
47
+ * Will treat input as readonly. Returns undefined if not needed.
48
+ * @example
49
+ * const a = () => { }
50
+ * becomes
51
+ * const a = (() => { const a = () => {}; return a; })()
52
+ */
53
+ function arrowFunctionExpressionNamedIfNeeded(
54
+ path: NodePath<babel.types.Expression>,
55
+ ): babel.types.Expression | undefined {
56
+ if (
57
+ path.isArrowFunctionExpression() &&
58
+ path.parentPath.isVariableDeclarator() &&
59
+ types.isIdentifier(path.parentPath.node.id)
60
+ ) {
61
+ return types.callExpression(
62
+ types.arrowFunctionExpression(
63
+ [],
64
+ types.blockStatement([
65
+ types.variableDeclaration('const', [
66
+ types.variableDeclarator(path.parentPath.node.id, path.node),
67
+ ]),
68
+ types.returnStatement(path.parentPath.node.id),
69
+ ]),
70
+ ),
71
+ [],
72
+ );
73
+ }
74
+ return;
75
+ }
76
+
77
+ function nameIfAnonymous(
78
+ path: NodePath<babel.types.Expression>,
79
+ ): babel.types.Expression {
80
+ return (
81
+ classOrFunctionExpressionNamedIfNeeded(path) ??
82
+ arrowFunctionExpressionNamedIfNeeded(path) ??
83
+ path.node
84
+ );
85
+ }
86
+
87
+ function isMemberOrCallOrNonNullExpression(path: NodePath) {
88
+ return isCallExpression(path) || isMemberOrNonNullExpression(path);
89
+ }
90
+
91
+ function isMemberOrNonNullExpression(
92
+ path: NodePath,
93
+ ): path is NodePath<
94
+ | babel.types.MemberExpression
95
+ | babel.types.OptionalMemberExpression
96
+ | babel.types.TSNonNullExpression
97
+ > {
98
+ return isMemberExpression(path) || path.isTSNonNullExpression();
99
+ }
100
+ function isMemberExpression(
101
+ path: NodePath,
102
+ ): path is NodePath<
103
+ babel.types.MemberExpression | babel.types.OptionalMemberExpression
104
+ > {
105
+ return path.isMemberExpression() || path.isOptionalMemberExpression();
106
+ }
107
+
108
+ function isCallExpression(
109
+ path: NodePath,
110
+ ): path is NodePath<
111
+ babel.types.CallExpression | babel.types.OptionalCallExpression
112
+ > {
113
+ return path.isCallExpression() || path.isOptionalCallExpression();
114
+ }
115
+
116
+ function isValidExpression(path: NodePath<babel.types.Expression>) {
117
+ const parent = path.parentPath;
118
+ return (
119
+ !isObjectPropertyKey() &&
120
+ !isPartOfChain() &&
121
+ !parent.isTaggedTemplateExpression() &&
122
+ !isPartOfDeleteExpression() &&
123
+ !isPartOfAssignmentExpression()
124
+ );
125
+
126
+ /**
127
+ * Determines if the expression is property of an object.
128
+ * @example
129
+ * const a = {
130
+ * 'foo': 'bar' // 'foo' here is an object property
131
+ * };
132
+ */
133
+ function isObjectPropertyKey() {
134
+ return parent.isObjectProperty() && parent.node.key === path.node;
135
+ }
136
+
137
+ /**
138
+ * Determines if the expression is part of a call/member chain.
139
+ * @example
140
+ * // bar is part of chain, foo is NOT part of the chain:
141
+ * foo.bar.baz();
142
+ * foo.bar?.baz()
143
+ * foo.bar;
144
+ * foo.bar!;
145
+ * foo.bar();
146
+ * foo?.bar();
147
+ * baz[foo.bar()]
148
+ * bar?.baz[0]
149
+ */
150
+ function isPartOfChain() {
151
+ return (
152
+ isMemberOrCallOrNonNullExpression(path) &&
153
+ ((isMemberExpression(parent) &&
154
+ !(parent.node.computed && parent.node.property === path.node)) ||
155
+ parent.isTSNonNullExpression() ||
156
+ (isCallExpression(parent) && parent.node.callee === path.node))
157
+ );
158
+ }
159
+
160
+ /**
161
+ * Determines if the expression is part of a delete expression.
162
+ * @returns true if the expression is part of a delete expression
163
+ * @example
164
+ * delete foo.bar;
165
+ */
166
+ function isPartOfDeleteExpression() {
167
+ return parent.isUnaryExpression() && parent.node.operator === 'delete';
168
+ }
169
+
170
+ /**
171
+ * Determines if the expression is part of an assignment expression.
172
+ * @returns true if the expression is part of an assignment expression
173
+ * @example
174
+ * foo.bar = 42;
175
+ * initialNodes.filter((n) => n.id === 'tiptilt')[0].className = tiptiltState;
176
+ */
177
+ function isPartOfAssignmentExpression() {
178
+ return parent.isAssignmentExpression() && parent.node.left === path.node;
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Places the mutants with a conditional expression: `global.activeMutant === 1? mutatedCode : originalCode`;
184
+ */
185
+ export const expressionMutantPlacer = {
186
+ name: 'expressionMutantPlacer',
187
+ canPlace(path) {
188
+ return path.isExpression() && isValidExpression(path);
189
+ },
190
+ place(path, appliedMutants) {
191
+ // Make sure anonymous functions and classes keep their 'name' property
192
+ let expression = nameIfAnonymous(path);
193
+
194
+ // Add the mutation coverage expression
195
+ expression = mutationCoverageSequenceExpression(
196
+ appliedMutants.keys(),
197
+ expression,
198
+ );
199
+
200
+ // Now apply the mutants
201
+ for (const [mutant, appliedMutant] of appliedMutants) {
202
+ expression = types.conditionalExpression(
203
+ mutantTestExpression(mutant.id),
204
+ appliedMutant,
205
+ expression,
206
+ );
207
+ }
208
+ path.replaceWith(expression);
209
+ },
210
+ } satisfies MutantPlacer<babel.types.Expression>;
@@ -0,0 +1,12 @@
1
+ import { expressionMutantPlacer } from './expression-mutant-placer.js';
2
+ import { MutantPlacer } from './mutant-placer.js';
3
+ import { statementMutantPlacer } from './statement-mutant-placer.js';
4
+ import { switchCaseMutantPlacer } from './switch-case-mutant-placer.js';
5
+
6
+ export * from './mutant-placer.js';
7
+ export * from './throw-placement-error.js';
8
+ export const allMutantPlacers: readonly MutantPlacer[] = Object.freeze([
9
+ expressionMutantPlacer,
10
+ statementMutantPlacer,
11
+ switchCaseMutantPlacer,
12
+ ]);
@@ -0,0 +1,9 @@
1
+ import { NodePath, types } from '@babel/core';
2
+
3
+ import { Mutant } from '../mutant.js';
4
+
5
+ export interface MutantPlacer<TNode extends types.Node = types.Node> {
6
+ name: string;
7
+ canPlace(path: NodePath): boolean;
8
+ place(path: NodePath<TNode>, appliedMutants: Map<Mutant, TNode>): void;
9
+ }