From 2080042808b18b005fb54aeaf9ab708f64cb1005 Mon Sep 17 00:00:00 2001 From: Airat Aminev Date: Thu, 16 May 2019 22:22:52 -0700 Subject: [PATCH] fix(typescript): erase type exports (#9944) * fix(typescript): erase type exports * use Set instead of array for tracking and checking TS type declarations * add a test for an interface exported before its declaration --- .../src/index.js | 61 +++++++++++++++++++ .../fixtures/declarations/erased/input.mjs | 37 +++++++++++ .../fixtures/declarations/erased/output.mjs | 31 ++++++++++ 3 files changed, 129 insertions(+) diff --git a/packages/babel-plugin-transform-typescript/src/index.js b/packages/babel-plugin-transform-typescript/src/index.js index 2a78a433ea1b..a0b52505363c 100644 --- a/packages/babel-plugin-transform-typescript/src/index.js +++ b/packages/babel-plugin-transform-typescript/src/index.js @@ -16,8 +16,22 @@ function isInType(path) { } } +function isTSExportableDeclaration(node) { + // all kinds of type exports that transpile to nothing + // exception is enums, since they transpile to JS values + return ( + t.isTSInterfaceDeclaration(node) || + t.isTSTypeAliasDeclaration(node) || + t.isTSModuleDeclaration(node) || + (t.isVariableDeclaration(node) && node.declare) || + (t.isClassDeclaration(node) && node.declare) || + t.isTSDeclareFunction(node) + ); +} + interface State { programPath: any; + exportableTSNames: Set; } const PARSED_PARAMS = new WeakSet(); @@ -40,6 +54,7 @@ export default declare((api, { jsxPragma = "React" }) => { Program(path, state: State) { state.programPath = path; + state.exportableTSNames = new Set(); const { file } = state; @@ -52,6 +67,32 @@ export default declare((api, { jsxPragma = "React" }) => { } } + // find exportable top level type declarations + for (const stmt of path.get("body")) { + if (isTSExportableDeclaration(stmt.node)) { + if (stmt.node.id && stmt.node.id.name) { + state.exportableTSNames.add(stmt.node.id.name); + } else if ( + stmt.node.declarations && + stmt.node.declarations.length > 0 + ) { + for (const declaration of stmt.node.declarations) { + if (declaration.id && declaration.id.name) { + state.exportableTSNames.add(declaration.id.name); + } + } + } + } else if ( + t.isExportNamedDeclaration(stmt.node) && + stmt.node.specifiers.length === 0 && + isTSExportableDeclaration(stmt.node.declaration) && + stmt.node.declaration.id && + stmt.node.declaration.id.name + ) { + state.exportableTSNames.add(stmt.node.declaration.id.name); + } + } + // remove type imports for (const stmt of path.get("body")) { if (t.isImportDeclaration(stmt)) { @@ -94,6 +135,26 @@ export default declare((api, { jsxPragma = "React" }) => { } }, + ExportNamedDeclaration(path, { exportableTSNames }) { + // remove export declaration if it's exporting only types + if ( + path.node.specifiers.length > 0 && + !path.node.specifiers.find( + exportSpecifier => + !exportableTSNames.has(exportSpecifier.local.name), + ) + ) { + path.remove(); + } + }, + + ExportSpecifier(path, { exportableTSNames }) { + // remove type exports + if (exportableTSNames.has(path.node.local.name)) { + path.remove(); + } + }, + TSDeclareFunction(path) { path.remove(); }, diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/declarations/erased/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/declarations/erased/input.mjs index a8fbeee28ef8..b54970d889a5 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/declarations/erased/input.mjs +++ b/packages/babel-plugin-transform-typescript/test/fixtures/declarations/erased/input.mjs @@ -8,3 +8,40 @@ declare namespace N {} export interface I {} export type T = number; export class C2 {} + +export { x, f, E, C }; // only E +export { M, N, I as I1, T as T1 }; // everything removed +export { + x as x2, + f as f2, + C as CC2, + E as E2, + M as M2, + N as N2, + I as I2, + T as T2, + C2 as C3 +}; // only E and C2 + +interface II2 {} +type AA = {}; +enum BB { + K +} +enum BB { + L = "LL" +} +export { II2, AA, BB }; // only BB +export { II2 as II3, AA as AA2 }; // everything removed +export { BB as BB1 }; // as-is + +interface II3 {} +type AA2 = {}; +enum BB2 {} +function foo() {} +export { II3 as default, AA2 as A, BB2 as BB3, foo }; // only BB2 and foo + +// export an interface before declaration +export { Bar } // everything removed +export { Bar as Bar2, C2 as C4 } // only C4 +interface Bar {} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/declarations/erased/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/declarations/erased/output.mjs index efd6dcc73555..f6d9a1893f18 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/declarations/erased/output.mjs +++ b/packages/babel-plugin-transform-typescript/test/fixtures/declarations/erased/output.mjs @@ -1 +1,32 @@ export class C2 {} +export { E }; // only E + +// everything removed +export { E as E2, C2 as C3 }; // only E and C2 + +var BB; + +(function (BB) { + BB[BB["K"] = 0] = "K"; +})(BB || (BB = {})); + +(function (BB) { + BB["L"] = "LL"; +})(BB || (BB = {})); + +export { BB }; // only BB + +// everything removed +export { BB as BB1 }; // as-is + +var BB2; + +(function (BB2) {})(BB2 || (BB2 = {})); + +function foo() {} + +export { BB2 as BB3, foo }; // only BB2 and foo +// export an interface before declaration + +// everything removed +export { C2 as C4 }; // only C4