Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: vuejs/vue-jest
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v29.1.1
Choose a base ref
...
head repository: vuejs/vue-jest
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v29.2.0
Choose a head ref
  • 1 commit
  • 16 files changed
  • 2 contributors

Commits on Nov 3, 2022

  1. fix: parse extended tsconfigs when transpiling script blocks (#502)

    * fix(tsconfig): parse extended tsconfigs when transpiling script blocks
    
    A change introduced in v28.1.0 in PR #471 unintentionally changed the behavior of the tsconfig parsing such that configs using "extends" were no longer being considered.
    
    Fixes: #495
    
    * chore(cache): cache tsconfig parsing to avoid the cost per vue file / interpolated string
    
    Co-authored-by: Adam Hines <ahines@factset.com>
    thebanjomatic and Adam Hines authored Nov 3, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    bb516da View commit details
34 changes: 34 additions & 0 deletions e2e/2.x/basic/components/ExtendedTsConfig.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<div>
{{ exclamationMarks }}
<type-script-child />
</div>
</template>

<script lang="ts">
import TypeScriptChild from './TypeScriptChild.vue'
import moduleRequiringEsModuleInterop from './ModuleRequiringEsModuleInterop'
// The default import above relies on esModuleInterop being set to true in order to use it from
// an import statement instead of require. This option is configured in the tsconfig.base.json,
// so if we are no longer fully processing the tsconfig options (extended from a base config)
// this test should fail. This was one of the only reliable ways I could get a test to fail if
// these conditions are not being met and happen to be the use-case which was triggering errors
// in my config setup.
if (moduleRequiringEsModuleInterop()) {
throw new Error('Should never hit this')
}
export default {
computed: {
exclamationMarks(): string {
return 'string'
}
},
components: {
TypeScriptChild
}
}
</script>
1 change: 1 addition & 0 deletions e2e/2.x/basic/components/ModuleRequiringEsModuleInterop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = () => false
7 changes: 7 additions & 0 deletions e2e/2.x/basic/test.js
Original file line number Diff line number Diff line change
@@ -21,6 +21,8 @@ import Jsx from './components/Jsx.vue'
import Constructor from './components/Constructor.vue'
import { compileStyle } from '@vue/component-compiler-utils'
import ScriptSetup from './components/ScriptSetup'
import ExtendedTsConfig from './components/ExtendedTsConfig.vue'

jest.mock('@vue/component-compiler-utils', () => ({
...jest.requireActual('@vue/component-compiler-utils'),
compileStyle: jest.fn(() => ({ errors: [], code: '' }))
@@ -163,6 +165,11 @@ test('processes SFC with <script setup>', () => {
expect(wrapper.html()).toContain('Welcome to Your Vue.js App')
})

test('handles extended tsconfig.json files', () => {
const wrapper = mount(ExtendedTsConfig)
expect(wrapper.element.tagName).toBe('DIV')
})

test('should pass properly "styleOptions" into "preprocessOptions"', () => {
const filePath = resolve(__dirname, './components/Basic.vue')
const fileString = readFileSync(filePath, { encoding: 'utf8' })
21 changes: 21 additions & 0 deletions e2e/2.x/basic/tsconfig.base.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "es6"],
"module": "es2015",
"moduleResolution": "node",
"types": ["vue-typescript-import-dts", "node"],
"isolatedModules": false,
"experimentalDecorators": true,
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"suppressImplicitAnyIndexErrors": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"esModuleInterop": true,
"allowJs": true
}
}
19 changes: 1 addition & 18 deletions e2e/2.x/basic/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,3 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "es6"],
"module": "es2015",
"moduleResolution": "node",
"types": ["vue-typescript-import-dts", "node"],
"isolatedModules": false,
"experimentalDecorators": true,
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"suppressImplicitAnyIndexErrors": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"allowJs": true
}
"extends": "./tsconfig.base.json"
}
34 changes: 34 additions & 0 deletions e2e/3.x/basic/components/ExtendedTsConfig.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<div>
{{ exclamationMarks }}
<type-script-child />
</div>
</template>

<script lang="ts">
import TypeScriptChild from './TypeScriptChild.vue'
import moduleRequiringEsModuleInterop from './ModuleRequiringEsModuleInterop'
// The default import above relies on esModuleInterop being set to true in order to use it from
// an import statement instead of require. This option is configured in the tsconfig.base.json,
// so if we are no longer fully processing the tsconfig options (extended from a base config)
// this test should fail. This was one of the only reliable ways I could get a test to fail if
// these conditions are not being met and happen to be the use-case which was triggering errors
// in my config setup.
if (moduleRequiringEsModuleInterop()) {
throw new Error('Should never hit this')
}
export default {
computed: {
exclamationMarks(): string {
return 'string'
}
},
components: {
TypeScriptChild
}
}
</script>
1 change: 1 addition & 0 deletions e2e/3.x/basic/components/ModuleRequiringEsModuleInterop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = () => false
7 changes: 7 additions & 0 deletions e2e/3.x/basic/test.js
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ import ScriptSetup from './components/ScriptSetup.vue'
import ScriptSetupSugarRef from './components/ScriptSetupSugarRef.vue'
import FunctionalRenderFn from './components/FunctionalRenderFn.vue'
import CompilerDirective from './components/CompilerDirective.vue'
import ExtendedTsConfig from './components/ExtendedTsConfig.vue'

// TODO: JSX for Vue 3? TSX?
import Jsx from './components/Jsx.vue'
@@ -207,3 +208,9 @@ test('ensure compilerOptions is passed down', () => {
const elm = document.querySelector('h1')
expect(elm.hasAttribute('data-test')).toBe(false)
})

test('handles extended tsconfig.json files', () => {
mount(ExtendedTsConfig)
const elm = document.querySelector('div')
expect(elm).toBeDefined()
})
21 changes: 21 additions & 0 deletions e2e/3.x/basic/tsconfig.base.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "es6"],
"module": "es2015",
"moduleResolution": "node",
"types": ["vue-typescript-import-dts", "node"],
"isolatedModules": false,
"experimentalDecorators": true,
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"suppressImplicitAnyIndexErrors": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"esModuleInterop": true,
"allowJs": true
}
}
19 changes: 1 addition & 18 deletions e2e/3.x/basic/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,3 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "es6"],
"module": "es2015",
"moduleResolution": "node",
"types": ["vue-typescript-import-dts", "node"],
"isolatedModules": false,
"experimentalDecorators": true,
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"suppressImplicitAnyIndexErrors": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"allowJs": true
}
"extends": "./tsconfig.base.json"
}
2 changes: 1 addition & 1 deletion packages/vue2-jest/lib/ensure-require.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const throwError = require('./utils').throwError
const throwError = require('./throw-error')

module.exports = function(name, deps) {
let i, len
3 changes: 3 additions & 0 deletions packages/vue2-jest/lib/throw-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = function throwError(msg) {
throw new Error('\n[vue-jest] Error: ' + msg + '\n')
}
52 changes: 41 additions & 11 deletions packages/vue2-jest/lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const ensureRequire = require('./ensure-require')
const throwError = require('./throw-error')
const constants = require('./constants')
const loadPartialConfig = require('@babel/core').loadPartialConfig
const { loadSync: loadTsConfigSync } = require('tsconfig')
const { resolveSync: resolveTsConfigSync } = require('tsconfig')
const chalk = require('chalk')
const path = require('path')
const fs = require('fs')
@@ -68,23 +70,55 @@ const getBabelOptions = function loadBabelOptions(filename, options = {}) {
return loadPartialConfig(opts).options
}

const tsConfigCache = new Map()

/**
* Load TypeScript config from tsconfig.json.
* @param {string | undefined} path tsconfig.json file path (default: root)
* @returns {import('typescript').TranspileOptions | null} TypeScript compilerOptions or null
*/
const getTypeScriptConfig = function getTypeScriptConfig(path) {
const tsconfig = loadTsConfigSync(process.cwd(), path || '')
if (!tsconfig.path) {
if (tsConfigCache.has(path)) {
return tsConfigCache.get(path)
}

ensureRequire('typescript', ['typescript'])
const typescript = require('typescript')

const tsconfigPath = resolveTsConfigSync(process.cwd(), path || '')
if (!tsconfigPath) {
warn(`Not found tsconfig.json.`)
return null
}
const compilerOptions =
(tsconfig.config && tsconfig.config.compilerOptions) || {}

return {
compilerOptions: { ...compilerOptions, module: 'commonjs' }
const parsedConfig = typescript.getParsedCommandLineOfConfigFile(
tsconfigPath,
{},
{
...typescript.sys,
onUnRecoverableConfigFileDiagnostic: e => {
const errorMessage = typescript.formatDiagnostic(e, {
getCurrentDirectory: () => process.cwd(),
getNewLine: () => `\n`,
getCanonicalFileName: file => file.replace(/\\/g, '/')
})
warn(errorMessage)
}
}
)

const compilerOptions = parsedConfig ? parsedConfig.options : {}

const transpileConfig = {
compilerOptions: {
...compilerOptions,
module: typescript.ModuleKind.CommonJS
}
}

tsConfigCache.set(path, transpileConfig)

return transpileConfig
}

function isValidTransformer(transformer) {
@@ -131,10 +165,6 @@ const getCustomTransformer = function getCustomTransformer(
: transformer
}

const throwError = function error(msg) {
throw new Error('\n[vue-jest] Error: ' + msg + '\n')
}

const stripInlineSourceMap = function(str) {
return str.slice(0, str.indexOf('//# sourceMappingURL'))
}
2 changes: 1 addition & 1 deletion packages/vue3-jest/lib/ensure-require.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const throwError = require('./utils').throwError
const throwError = require('./throw-error')

module.exports = function(name, deps) {
let i, len
3 changes: 3 additions & 0 deletions packages/vue3-jest/lib/throw-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = function throwError(msg) {
throw new Error('\n[vue-jest] Error: ' + msg + '\n')
}
55 changes: 43 additions & 12 deletions packages/vue3-jest/lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const ensureRequire = require('./ensure-require')
const throwError = require('./throw-error')
const constants = require('./constants')
const loadPartialConfig = require('@babel/core').loadPartialConfig
const { loadSync: loadTsConfigSync } = require('tsconfig')
const { resolveSync: resolveTsConfigSync } = require('tsconfig')
const chalk = require('chalk')
const path = require('path')
const fs = require('fs')
@@ -68,24 +70,57 @@ const getBabelOptions = function loadBabelOptions(filename, options = {}) {
return loadPartialConfig(opts).options
}

const tsConfigCache = new Map()

/**
* Load TypeScript config from tsconfig.json.
* @param {string | undefined} path tsconfig.json file path (default: root)
* @returns {import('typescript').TranspileOptions | null} TypeScript compilerOptions or null
*/
const getTypeScriptConfig = function getTypeScriptConfig(path) {
const tsconfig = loadTsConfigSync(process.cwd(), path || '')
if (!tsconfig.path) {
if (tsConfigCache.has(path)) {
return tsConfigCache.get(path)
}

ensureRequire('typescript', ['typescript'])
const typescript = require('typescript')

const tsconfigPath = resolveTsConfigSync(process.cwd(), path || '')
if (!tsconfigPath) {
warn(`Not found tsconfig.json.`)
return null
}
const compilerOptions =
(tsconfig.config && tsconfig.config.compilerOptions) || {}

// Force es5 to prevent const vue_1 = require('vue') from conflicting
return {
compilerOptions: { ...compilerOptions, target: 'es5', module: 'commonjs' }
const parsedConfig = typescript.getParsedCommandLineOfConfigFile(
tsconfigPath,
{},
{
...typescript.sys,
onUnRecoverableConfigFileDiagnostic: e => {
const errorMessage = typescript.formatDiagnostic(e, {
getCurrentDirectory: () => process.cwd(),
getNewLine: () => `\n`,
getCanonicalFileName: file => file.replace(/\\/g, '/')
})
warn(errorMessage)
}
}
)

const compilerOptions = parsedConfig ? parsedConfig.options : {}

const transpileConfig = {
compilerOptions: {
...compilerOptions,
// Force es5 to prevent const vue_1 = require('vue') from conflicting
target: typescript.ScriptTarget.ES5,
module: typescript.ModuleKind.CommonJS
}
}

tsConfigCache.set(path, transpileConfig)

return transpileConfig
}

function isValidTransformer(transformer) {
@@ -133,10 +168,6 @@ const getCustomTransformer = function getCustomTransformer(
: transformer
}

const throwError = function error(msg) {
throw new Error('\n[vue-jest] Error: ' + msg + '\n')
}

const stripInlineSourceMap = function(str) {
return str.slice(0, str.indexOf('//# sourceMappingURL'))
}