Skip to content

Commit

Permalink
fix(typescript): erase type exports (babel#9944)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
airato authored and nicolo-ribaudo committed May 17, 2019
1 parent 3f0590d commit 2080042
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 0 deletions.
61 changes: 61 additions & 0 deletions packages/babel-plugin-transform-typescript/src/index.js
Expand Up @@ -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<string>;
}

const PARSED_PARAMS = new WeakSet();
Expand All @@ -40,6 +54,7 @@ export default declare((api, { jsxPragma = "React" }) => {

Program(path, state: State) {
state.programPath = path;
state.exportableTSNames = new Set();

const { file } = state;

Expand All @@ -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)) {
Expand Down Expand Up @@ -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();
},
Expand Down
Expand Up @@ -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 {}
@@ -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

0 comments on commit 2080042

Please sign in to comment.