• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kulshekhar / ts-jest / 9804725795

05 Jul 2024 07:30AM UTC coverage: 96.474%. Remained the same
9804725795

push

github

ahnpnl
build(deps): Update dependency eslint-plugin-jsdoc to ^48.5.1

788 of 885 branches covered (89.04%)

Branch coverage included in aggregate %.

4712 of 4816 relevant lines covered (97.84%)

1248.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.02
/src/cli/config/migrate.ts
1
import { existsSync } from 'fs'
6✔
2
import { basename, resolve } from 'path'
6✔
3

6✔
4
import type { Config } from '@jest/types'
6✔
5
import { createLogger } from 'bs-logger'
6✔
6
import stableStringify from 'fast-json-stable-stringify'
6✔
7
import { stringify as stringifyJson5 } from 'json5'
6✔
8

6✔
9
import type { CliCommand, CliCommandArgs } from '..'
6✔
10
import { backportJestConfig } from '../../utils/backports'
6✔
11
import { JestPresetNames, TsJestPresetDescriptor, allPresets, defaults } from '../helpers/presets'
6✔
12

6✔
13
/**
6✔
14
 * @internal
6✔
15
 */
6✔
16
export const run: CliCommand = async (args: CliCommandArgs /* , logger: Logger*/) => {
6✔
17
  const nullLogger = createLogger({ targets: [] })
120✔
18
  const file = args._[0]?.toString()
120!
19
  const filePath = resolve(process.cwd(), file)
120✔
20
  const footNotes: string[] = []
120✔
21
  if (!existsSync(filePath)) {
120✔
22
    throw new Error(`Configuration file ${file} does not exists.`)
6✔
23
  }
6✔
24
  const name = basename(file)
114✔
25
  const isPackage = name === 'package.json'
114✔
26
  if (!/\.(js|json)$/.test(name)) {
120✔
27
    throw new TypeError(`Configuration file ${file} must be a JavaScript or JSON file.`)
6✔
28
  }
6✔
29
  let actualConfig: Config.InitialOptions = require(filePath)
108✔
30
  if (isPackage) {
120✔
31
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
78✔
32
    actualConfig = (actualConfig as any).jest
78✔
33
  }
78✔
34
  if (!actualConfig) actualConfig = {}
120!
35

108✔
36
  // migrate
108✔
37
  // first we backport our options
108✔
38
  const migratedConfig = backportJestConfig(nullLogger, actualConfig)
108✔
39
  let presetName: JestPresetNames | undefined
108✔
40
  let preset: TsJestPresetDescriptor | undefined
108✔
41
  // then we check if we can use `preset`
108✔
42
  if (!migratedConfig.preset && args.jestPreset) {
120✔
43
    // find the best preset
84✔
44
    if (args.js) {
84✔
45
      presetName = args.js === 'babel' ? JestPresetNames.jsWIthBabel : JestPresetNames.jsWithTs
6!
46
    } else {
84✔
47
      // try to detect what transformer the js extensions would target
78✔
48
      const jsTransformers = Object.keys(migratedConfig.transform || {}).reduce((list, pattern) => {
78✔
49
        if (RegExp(pattern.replace(/^<rootDir>\/?/, '/dummy-project/')).test('/dummy-project/src/foo.js')) {
60✔
50
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
24✔
51
          let transformer: string = (migratedConfig.transform as any)[pattern]
24✔
52
          if (/\bbabel-jest\b/.test(transformer)) transformer = 'babel-jest'
24✔
53
          else if (/\ts-jest\b/.test(transformer)) transformer = 'ts-jest'
12!
54

24✔
55
          return [...list, transformer]
24✔
56
        }
24✔
57

36✔
58
        return list
36✔
59
      }, [] as string[])
78✔
60
      // depending on the transformer found, we use one or the other preset
78✔
61
      const jsWithTs = jsTransformers.includes('ts-jest')
78✔
62
      const jsWithBabel = jsTransformers.includes('babel-jest')
78✔
63
      if (jsWithBabel && !jsWithTs) {
78✔
64
        presetName = JestPresetNames.jsWIthBabel
12✔
65
      } else if (jsWithTs && !jsWithBabel) {
78✔
66
        presetName = JestPresetNames.jsWithTs
6✔
67
      } else {
66✔
68
        // sounds like js files are NOT handled, or handled with a unknown transformer, so we do not need to handle it
60✔
69
        presetName = JestPresetNames.default
60✔
70
      }
60✔
71
    }
78✔
72
    // ensure we are using a preset
84✔
73
    presetName = presetName ?? JestPresetNames.default
84!
74
    preset = allPresets[presetName]
84✔
75
    footNotes.push(
84✔
76
      `Detected preset '${preset.label}' as the best matching preset for your configuration.
84✔
77
Visit https://kulshekhar.github.io/ts-jest/user/config/#jest-preset for more information about presets.
84✔
78
`,
84✔
79
    )
84✔
80
  } else if (migratedConfig.preset?.startsWith('ts-jest')) {
120✔
81
    if (args.jestPreset === false) {
12!
82
      delete migratedConfig.preset
×
83
    } else {
12✔
84
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
12✔
85
      preset = (allPresets as any)[migratedConfig.preset] ?? defaults
12!
86
    }
12✔
87
  }
12✔
88

108✔
89
  // enforce the correct name
108✔
90
  if (preset) migratedConfig.preset = preset.name
120✔
91

108✔
92
  // check the extensions
108✔
93
  if (migratedConfig.moduleFileExtensions?.length && preset) {
120✔
94
    const presetValue = dedupSort(preset.value.moduleFileExtensions ?? []).join('::')
6!
95
    const migratedValue = dedupSort(migratedConfig.moduleFileExtensions).join('::')
6✔
96
    if (presetValue === migratedValue) {
6!
97
      delete migratedConfig.moduleFileExtensions
×
98
    }
×
99
  }
6✔
100
  // there is a testRegex, remove our testMatch
108✔
101
  if ((typeof migratedConfig.testRegex === 'string' || migratedConfig.testRegex?.length) && preset) {
120✔
102
    delete migratedConfig.testMatch
18✔
103
  }
18✔
104
  // check the testMatch
90✔
105
  else if (migratedConfig.testMatch?.length && preset) {
90✔
106
    const presetValue = dedupSort(preset.value.testMatch ?? []).join('::')
18!
107
    const migratedValue = dedupSort(migratedConfig.testMatch).join('::')
18✔
108
    if (presetValue === migratedValue) {
18!
109
      delete migratedConfig.testMatch
×
110
    }
×
111
  }
18✔
112

108✔
113
  // migrate the transform
108✔
114
  if (migratedConfig.transform) {
120✔
115
    Object.keys(migratedConfig.transform).forEach((key) => {
30✔
116
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
60✔
117
      const val = (migratedConfig.transform as any)[key]
60✔
118
      if (typeof val === 'string' && /\/?ts-jest?(?:\/preprocessor\.js)?$/.test(val)) {
60✔
119
        // eslint-disable-next-line
42✔
120
        ;(migratedConfig.transform as any)[key] = 'ts-jest'
42✔
121
      }
42✔
122
    })
30✔
123
  }
30✔
124

108✔
125
  // migrate globals config to transformer config
108✔
126
  const globalsTsJestConfig = migratedConfig.globals?.['ts-jest']
120!
127
  if (globalsTsJestConfig && migratedConfig.transform) {
120✔
128
    Object.keys(migratedConfig.transform).forEach((key) => {
30✔
129
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
60✔
130
      const val = (migratedConfig.transform as any)[key]
60✔
131
      if (typeof val === 'string' && val.includes('ts-jest')) {
60✔
132
        // eslint-disable-next-line
42✔
133
        ;(migratedConfig.transform as any)[key] = Object.keys(globalsTsJestConfig).length ? [val, globalsTsJestConfig] : val
42✔
134
      }
42✔
135
    })
30✔
136
    delete (migratedConfig.globals ?? Object.create(null))['ts-jest']
30!
137
  }
30✔
138

108✔
139
  // check if it's the same as the preset's one
108✔
140
  if (preset && migratedConfig.transform) {
120✔
141
    if (stableStringify(migratedConfig.transform) === stableStringify(preset.value.transform)) {
30!
142
      delete migratedConfig.transform
×
143
    } else {
30✔
144
      const migratedConfigTransform = migratedConfig.transform
30✔
145
      const presetValueTransform = preset.value.transform
30✔
146
      if (migratedConfigTransform && presetValueTransform) {
30✔
147
        migratedConfig.transform = Object.entries(migratedConfigTransform).reduce(
30✔
148
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
30✔
149
          (acc: undefined | Record<string, any>, [fileRegex, transformerConfig]) => {
30✔
150
            const presetValueTransformerConfig = presetValueTransform[fileRegex]
60✔
151
            const shouldRemoveDuplicatedConfig =
60✔
152
              (presetValueTransformerConfig &&
60✔
153
                Array.isArray(presetValueTransformerConfig) &&
60✔
154
                transformerConfig === presetValueTransformerConfig[0] &&
60✔
155
                !Object.keys(presetValueTransformerConfig[1]).length) ||
60✔
156
              transformerConfig === presetValueTransformerConfig
48✔
157

60✔
158
            return shouldRemoveDuplicatedConfig
60✔
159
              ? acc
60✔
160
              : acc
60✔
161
              ? { ...acc, [fileRegex]: transformerConfig }
36✔
162
              : { [fileRegex]: transformerConfig }
36✔
163
          },
30✔
164
          undefined,
30✔
165
        )
30✔
166
      }
30✔
167
    }
30✔
168
  }
30✔
169

108✔
170
  // cleanup
108✔
171
  cleanupConfig(actualConfig)
108✔
172
  cleanupConfig(migratedConfig)
108✔
173
  const before = stableStringify(actualConfig)
108✔
174
  const after = stableStringify(migratedConfig)
108✔
175
  if (after === before) {
120✔
176
    process.stderr.write(`
12✔
177
No migration needed for given Jest configuration
12✔
178
    `)
12✔
179

12✔
180
    return
12✔
181
  }
12✔
182

96✔
183
  const stringify = file.endsWith('.json') ? JSON.stringify : stringifyJson5
120✔
184
  const prefix = file.endsWith('.json') ? '"jest": ' : 'module.exports = '
120✔
185

120✔
186
  // if we are using preset, inform the user that he might be able to remove some section(s)
120✔
187
  // we couldn't check for equality
120✔
188
  //   if (usesPreset && migratedConfig.testMatch) {
120✔
189
  //     footNotes.push(`
120✔
190
  // I couldn't check if your "testMatch" value is the same as mine which is: ${stringify(
120✔
191
  //       presets.testMatch,
120✔
192
  //       undefined,
120✔
193
  //       '  ',
120✔
194
  //     )}
120✔
195
  // If it is the case, you can safely remove the "testMatch" from what I've migrated.
120✔
196
  // `)
120✔
197
  //   }
120✔
198
  if (preset && migratedConfig.transform) {
120✔
199
    footNotes.push(`
18✔
200
I couldn't check if your "transform" value is the same as mine which is: ${stringify(
18✔
201
      preset.value.transform,
18✔
202
      undefined,
18✔
203
      '  ',
18✔
204
    )}
18✔
205
If it is the case, you can safely remove the "transform" from what I've migrated.
18✔
206
`)
18✔
207
  }
18✔
208

96✔
209
  // output new config
96✔
210
  process.stderr.write(`
96✔
211
Migrated Jest configuration:
96✔
212
`)
96✔
213
  process.stdout.write(`${prefix}${stringify(migratedConfig, undefined, '  ')}\n`)
96✔
214
  if (footNotes.length) {
120✔
215
    process.stderr.write(`
84✔
216
${footNotes.join('\n')}
84✔
217
`)
84✔
218
  }
84✔
219
}
120✔
220

6✔
221
function cleanupConfig(config: Config.InitialOptions): void {
216✔
222
  if (config.globals) {
216✔
223
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
132✔
224
    if ((config as any).globals['ts-jest'] && Object.keys((config as any).globals['ts-jest']).length === 0) {
132✔
225
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
60✔
226
      delete (config as any).globals['ts-jest']
60✔
227
    }
60✔
228
    if (!Object.keys(config.globals).length) {
132✔
229
      delete config.globals
108✔
230
    }
108✔
231
  }
132✔
232
  if (config.transform && !Object.keys(config.transform).length) {
216!
233
    delete config.transform
×
234
  }
×
235
  if (config.moduleFileExtensions) {
216✔
236
    config.moduleFileExtensions = dedupSort(config.moduleFileExtensions)
12✔
237
    if (!config.moduleFileExtensions.length) delete config.moduleFileExtensions
12!
238
  }
12✔
239
  if (config.testMatch) {
216✔
240
    config.testMatch = dedupSort(config.testMatch)
48✔
241
    if (!config.testMatch.length) delete config.testMatch
48!
242
  }
48✔
243
  if (config.preset === JestPresetNames.default) config.preset = defaults.name
216!
244
}
216✔
245

6✔
246
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6✔
247
function dedupSort(arr: any[]) {
108✔
248
  return arr
108✔
249
    .filter((s, i, a) => a.findIndex((e) => s.toString() === e.toString()) === i)
108✔
250
    .sort((a, b) => (a.toString() > b.toString() ? 1 : a.toString() < b.toString() ? -1 : 0))
108!
251
}
108✔
252

6✔
253
/**
6✔
254
 * @internal
6✔
255
 */
6✔
256
export const help: CliCommand = async () => {
6✔
257
  process.stdout.write(`
6✔
258
Usage:
6✔
259
  ts-jest config:migrate [options] <config-file>
6✔
260

6✔
261
Arguments:
6✔
262
  <config-file>         Can be a js or json Jest config file. If it is a
6✔
263
                        package.json file, the configuration will be read from
6✔
264
                        the "jest" property.
6✔
265

6✔
266
Options:
6✔
267
  --js ts|babel         Process .js files with ts-jest if 'ts' or with
6✔
268
                        babel-jest if 'babel'
6✔
269
  --no-jest-preset      Disable the use of Jest presets
6✔
270
`)
6✔
271
}
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc