-
Notifications
You must be signed in to change notification settings - Fork 157
fix(tsconfig): parse extended tsconfigs when transpiling script blocks #502
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = () => false |
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 | ||
} | ||
} |
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" | ||
} |
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> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = () => false |
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 | ||
} | ||
} |
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" | ||
} |
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') | ||
} |
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 || '') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: we are still using the tsconfig package to resolve the path to the tsconfig file in the same way that |
||
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) | ||
} | ||
Comment on lines
+99
to
+106
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will provide us with an error message if typescript is unable to read the tsconfig file or one of the extended files because they don't exist or don't parse etc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is nice, I like good error messages. |
||
} | ||
) | ||
|
||
const compilerOptions = parsedConfig ? parsedConfig.options : {} | ||
|
||
const transpileConfig = { | ||
compilerOptions: { | ||
...compilerOptions, | ||
module: typescript.ModuleKind.CommonJS | ||
Comment on lines
+112
to
+115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: As in the old code, we override the module with |
||
} | ||
} | ||
|
||
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')) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
const throwError = require('./utils').throwError | ||
const throwError = require('./throw-error') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: In order to use ensure-require from within utils.js, I needed to break the circular dependency. This was done by moving throw-error out of utils and into its own module so that ensure-require doesn't depend on utils. |
||
|
||
module.exports = function(name, deps) { | ||
let i, len | ||
|
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') | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my project, we were setting esModuleInterop in a shared base config, and
Vue.extend(...)
was throwing a TypeError when running the test because it "could not read property 'extend' fromundefined
".