From 66be4aa5e3c2cd4c56c924d60eb0a74548b3af86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 15 Apr 2019 22:46:19 +0200 Subject: [PATCH] Prevent JSX pragma detection in transform-typescript from leaking between files (#9798) This PR fixes a bug in support of JSX pragma in Typescript (introduced in https://github.com/babel/babel/pull/9095). We've noticed this bug while building a large codebase with webpack+babel+typescript. Some files from our projects are using `/** @jsx h*/` to use a custom function for JSX syntax; and some others are using the React syntax. We've noticed that our react imports were being removed by babel because the plugin instance was being reused by `babel-loader` and webpack; and it was causing an error : ``` ReferenceError: React is not defined ``` This PR resets the `jsxPragma` being used to the initial value for each new file being processed. I can't add a unit test for this bug because it requires processing 2 files (one with `/** @jsx something */` then one with the react syntax). I've tested a build of this PR and it correctly fixes the issue for us. --- .../babel-plugin-transform-typescript/src/index.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/babel-plugin-transform-typescript/src/index.js b/packages/babel-plugin-transform-typescript/src/index.js index b558447733..2a78a433ea 100644 --- a/packages/babel-plugin-transform-typescript/src/index.js +++ b/packages/babel-plugin-transform-typescript/src/index.js @@ -21,6 +21,7 @@ interface State { } const PARSED_PARAMS = new WeakSet(); +const PRAGMA_KEY = "@babel/plugin-transform-typescript/jsxPragma"; export default declare((api, { jsxPragma = "React" }) => { api.assertVersion(7); @@ -46,7 +47,7 @@ export default declare((api, { jsxPragma = "React" }) => { for (const comment of (file.ast.comments: Array)) { const jsxMatches = JSX_ANNOTATION_REGEX.exec(comment.value); if (jsxMatches) { - jsxPragma = jsxMatches[1]; + file.set(PRAGMA_KEY, jsxMatches[1]); } } } @@ -72,7 +73,10 @@ export default declare((api, { jsxPragma = "React" }) => { // just bail if there is no binding, since chances are good that if // the import statement was injected then it wasn't a typescript type // import anyway. - if (binding && isImportTypeOnly(binding, state.programPath)) { + if ( + binding && + isImportTypeOnly(file, binding, state.programPath) + ) { importsToRemove.push(binding.path); } else { allElided = false; @@ -313,14 +317,15 @@ export default declare((api, { jsxPragma = "React" }) => { // 'access' and 'readonly' are only for parameter properties, so constructor visitor will handle them. } - function isImportTypeOnly(binding, programPath) { + function isImportTypeOnly(file, binding, programPath) { for (const path of binding.referencePaths) { if (!isInType(path)) { return false; } } - if (binding.identifier.name !== jsxPragma) { + const fileJsxPragma = file.get(PRAGMA_KEY) || jsxPragma; + if (binding.identifier.name !== fileJsxPragma) { return true; }