@systemfsoftware/stryker-js-typescript-checker 1.2.2 → 1.2.3

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.
@@ -2,11 +2,11 @@ $ tsdown
2
2
  ℹ tsdown v0.22.9 powered by rolldown v1.2.0
3
3
  ℹ config file: /home/runner/work/systemfsoftware/systemfsoftware/packages/stryker-js/typescript-checker/tsdown.config.ts
4
4
  ℹ entry: ./src/index.ts
5
-
6
5
  ℹ tsconfig: tsconfig.json
7
- ℹ Build start
6
+
8
7
   WARN  `noExternal` is deprecated. Use `deps.alwaysBundle` instead.
9
8
 
9
+ ℹ Build start
10
10
 
11
11
   WARN  TypeScript 7.0 does not yet have a stable API and is experimental. Some options will be unavailable.
12
12
 
@@ -15,7 +15,7 @@ $ tsdown
15
15
  See more at https://tsdown.dev/options/dependencies#deps-onlybundle
16
16
  Detected dependencies in bundle:
17
17
  - @jsr/std__jsonc
18
- ℹ dist/index.mjs 31.56 kB │ gzip: 8.67 kB
18
+ ℹ dist/index.mjs 31.51 kB │ gzip: 8.65 kB
19
19
  ℹ dist/index.d.mts  4.66 kB │ gzip: 1.52 kB
20
- ℹ 2 files, total: 36.22 kB
21
- ✔ Build complete in 561ms
20
+ ℹ 2 files, total: 36.17 kB
21
+ ✔ Build complete in 635ms
package/dist/index.mjs CHANGED
@@ -311,9 +311,7 @@ function overrideOptions(config, useBuildMode) {
311
311
  const compilerOptions = {
312
312
  ...config.compilerOptions,
313
313
  ...COMPILER_OPTIONS_OVERRIDES,
314
- ...useBuildMode ? LOW_EMIT_OPTIONS_FOR_PROJECT_REFERENCES : NO_EMIT_OPTIONS_FOR_SINGLE_PROJECT,
315
- target: "es2022",
316
- moduleResolution: "bundler"
314
+ ...useBuildMode ? LOW_EMIT_OPTIONS_FOR_PROJECT_REFERENCES : NO_EMIT_OPTIONS_FOR_SINGLE_PROJECT
317
315
  };
318
316
  if (!useBuildMode && compilerOptions["declarationDir"] !== void 0 && compilerOptions["declarationDir"] !== null) delete compilerOptions["declarationDir"];
319
317
  if (useBuildMode) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@systemfsoftware/stryker-js-typescript-checker",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/systemfsoftware/systemfsoftware.git",
@@ -30,8 +30,8 @@
30
30
  "rimraf": "^6.1.3",
31
31
  "tsdown": "^0.22.9",
32
32
  "vitest": "^4",
33
- "@systemfsoftware/tsconfig": "^1.3.0",
34
- "@systemfsoftware/vitest-config": "^0.1.0"
33
+ "@systemfsoftware/vitest-config": "^0.1.0",
34
+ "@systemfsoftware/tsconfig": "^1.3.1"
35
35
  },
36
36
  "inlinedDependencies": {
37
37
  "@jsr/std__jsonc": "1.0.2"
@@ -117,13 +117,13 @@ export function determineBuildModeEnabled(tsconfigFileName: string): boolean {
117
117
  * @param useBuildMode whether or not `--build` mode is used
118
118
  */
119
119
  export function overrideOptions(config: TsConfig, useBuildMode: boolean): string {
120
+ // `target` and `moduleResolution` are deliberately absent: both belong to the consumer.
121
+ // Forcing `moduleResolution` contradicts `module: NodeNext`/`Node16` (TS5095 + TS5109),
122
+ // and forcing `target` hides lib features the consumer's own target allows (TS2550).
120
123
  const compilerOptions: Record<string, unknown> = {
121
124
  ...config.compilerOptions,
122
125
  ...COMPILER_OPTIONS_OVERRIDES,
123
126
  ...(useBuildMode ? LOW_EMIT_OPTIONS_FOR_PROJECT_REFERENCES : NO_EMIT_OPTIONS_FOR_SINGLE_PROJECT),
124
- // TypeScript 7 removed some legacy defaults that the upstream fixtures still use.
125
- target: 'es2022',
126
- moduleResolution: 'bundler',
127
127
  }
128
128
 
129
129
  if (
@@ -0,0 +1,101 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import { fileURLToPath } from 'url'
4
+
5
+ import { CheckStatus } from '@stryker-mutator/api/check'
6
+ import type { Location, Mutant, StrykerOptions } from '@stryker-mutator/api/core'
7
+ import type { Logger } from '@stryker-mutator/api/logging'
8
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
9
+
10
+ import { HybridFileSystem } from '../../src/fs/hybrid-file-system.js'
11
+ import { TypescriptChecker } from '../../src/typescript-checker.js'
12
+ import { TypescriptCompiler } from '../../src/typescript-compiler.js'
13
+
14
+ const resolveTestResource = path.resolve.bind(
15
+ path,
16
+ path.dirname(fileURLToPath(import.meta.url)),
17
+ '..',
18
+ '..',
19
+ 'testResources',
20
+ 'nodenext-project',
21
+ ) as unknown as typeof path.resolve
22
+
23
+ function createLogger(): Logger {
24
+ return {
25
+ isTraceEnabled: () => false,
26
+ isDebugEnabled: () => false,
27
+ isInfoEnabled: () => false,
28
+ isWarnEnabled: () => false,
29
+ isErrorEnabled: () => false,
30
+ isFatalEnabled: () => false,
31
+ trace: () => {},
32
+ debug: () => {},
33
+ info: () => {},
34
+ warn: () => {},
35
+ error: () => {},
36
+ fatal: () => {},
37
+ }
38
+ }
39
+
40
+ function createChecker(tsconfigFile: string): TypescriptChecker {
41
+ const options = {
42
+ tsconfigFile,
43
+ typescriptChecker: { prioritizePerformanceOverAccuracy: true },
44
+ } as unknown as StrykerOptions
45
+ const logger = createLogger()
46
+ const fileSystem = new HybridFileSystem()
47
+ const compiler = new TypescriptCompiler(logger, options, fileSystem)
48
+ return new TypescriptChecker(logger, options, compiler)
49
+ }
50
+
51
+ const utilSource = fs.readFileSync(resolveTestResource('src', 'util.ts'), 'utf8')
52
+
53
+ function createMutant(
54
+ findText: string,
55
+ replacement: string,
56
+ id: string,
57
+ ): Mutant {
58
+ const lines = utilSource.split('\n')
59
+ const lineNumber = lines.findIndex((line) => line.includes(findText))
60
+ if (lineNumber === -1) {
61
+ throw new Error(`Cannot find ${findText} in util.ts`)
62
+ }
63
+ const column = lines[lineNumber]!.indexOf(findText)
64
+ const location: Location = {
65
+ start: { line: lineNumber, column },
66
+ end: { line: lineNumber, column: column + findText.length },
67
+ }
68
+ return {
69
+ id,
70
+ fileName: resolveTestResource('src', 'util.ts'),
71
+ mutatorName: 'test-mutator',
72
+ location,
73
+ replacement,
74
+ }
75
+ }
76
+
77
+ describe('Typescript checker on a NodeNext project targeting es2024', () => {
78
+ let sut: TypescriptChecker
79
+
80
+ beforeEach(() => {
81
+ sut = createChecker(resolveTestResource('tsconfig.json'))
82
+ return sut.init()
83
+ })
84
+
85
+ afterEach(() => {
86
+ // @ts-expect-error private close method
87
+ sut.tsCompiler.close()
88
+ })
89
+
90
+ it('should validate a mutant that keeps the project compiling', async () => {
91
+ const mutant = createMutant('value % 2 === 0', 'value % 2 !== 0', 'passing')
92
+ const actual = await sut.check([mutant])
93
+ expect(actual).toEqual({ passing: { status: CheckStatus.Passed } })
94
+ })
95
+
96
+ it('should invalidate a mutant that violates the declared return type', async () => {
97
+ const mutant = createMutant("'even'", '42', 'breaking')
98
+ const actual = await sut.check([mutant])
99
+ expect(actual['breaking']!.status).toBe(CheckStatus.CompileError)
100
+ })
101
+ })
@@ -142,11 +142,30 @@ describe('overrideOptions', () => {
142
142
  expect(parsed.compilerOptions).toMatchObject({
143
143
  allowUnreachableCode: true,
144
144
  noEmit: true,
145
- target: 'es2022',
146
- moduleResolution: 'bundler',
147
145
  })
148
146
  })
149
147
 
148
+ it('should not inject a moduleResolution that contradicts the consumer module', () => {
149
+ const config = expectRight(
150
+ parseTsConfig('tsconfig.json', '{"compilerOptions":{"module":"NodeNext"}}'),
151
+ )
152
+ const output = JSON.parse(overrideOptions(config, false))
153
+ expect(output.compilerOptions.module).toBe('NodeNext')
154
+ expect(output.compilerOptions.moduleResolution).toBeUndefined()
155
+ })
156
+
157
+ it('should preserve the consumer target and moduleResolution verbatim', () => {
158
+ const config = expectRight(
159
+ parseTsConfig(
160
+ 'tsconfig.json',
161
+ '{"compilerOptions":{"target":"es2024","moduleResolution":"NodeNext"}}',
162
+ ),
163
+ )
164
+ const output = JSON.parse(overrideOptions(config, false))
165
+ expect(output.compilerOptions.target).toBe('es2024')
166
+ expect(output.compilerOptions.moduleResolution).toBe('NodeNext')
167
+ })
168
+
150
169
  it('should preserve unknown top-level keys and unknown compilerOptions through to the output', () => {
151
170
  const config = expectRight(
152
171
  parseTsConfig(
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ES5",
4
3
  "types": []
5
4
  }
6
5
  }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "nodenext-project",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module"
6
+ }
@@ -0,0 +1,5 @@
1
+ import { evenOdd } from './util.js'
2
+
3
+ const numbers = [1, 2, 3, 4]
4
+
5
+ export const groupedWithEs2024ObjectGroupBy = Object.groupBy(numbers, evenOdd)
@@ -0,0 +1,3 @@
1
+ export function evenOdd(value: number): string {
2
+ return value % 2 === 0 ? 'even' : 'odd'
3
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "module": "NodeNext",
6
+
7
+ // `moduleResolution` is deliberately absent and `target` is deliberately newer
8
+ // than es2022: the checker must not supply either. See nodenext-project.it.spec.ts.
9
+ "target": "es2024",
10
+
11
+ "verbatimModuleSyntax": true,
12
+ "outDir": "dist",
13
+ "types": []
14
+ },
15
+ "include": ["src/**/*.ts"]
16
+ }
@@ -1,8 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "strict": true,
4
- "target": "es5",
5
- "moduleResolution": "node",
6
4
  "module": "commonjs",
7
5
  "composite": true,
8
6
  "declaration": true,
@@ -1,8 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "strict": true,
4
- "target": "es5",
5
- "moduleResolution": "node",
6
4
  "module": "commonjs",
7
5
  "outDir": "dist",
8
6
 
@@ -2,8 +2,6 @@
2
2
  "$schema": "https://json.schemastore.org/tsconfig",
3
3
  "compilerOptions": {
4
4
  "strict": true,
5
- "target": "es5",
6
- "moduleResolution": "node",
7
5
  "module": "commonjs",
8
6
  "outDir": "dist",
9
7