Skip to content

Commit bb269b5

Browse files
anion155danielpza
authored andcommitted
fix: not transforming declaration files
Used replica of typescript compiler visitor - fix export * syntax closes #13, #14
1 parent 247bad6 commit bb269b5

File tree

8 files changed

+191
-49
lines changed

8 files changed

+191
-49
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
lib
22
node_modules
3-
tests/**/*.js
3+
tests/out/

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ node_js:
33
- stable
44
script:
55
- npm test
6-
- npx prettier "{src,tests}/**/*.ts" -c
6+
- npx prettier "src/**/*.ts" -c
77
deploy:
88
provider: npm
99

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"build": "tsc",
3838
"prepare": "npm run build",
3939
"release": "standard-version",
40-
"test": "npm run build && ttsc -p tests/tsconfig.json && node tests/out/core/index.js"
40+
"test": "./test.sh"
4141
},
4242
"dependencies": {
4343
"slash": "^3.0.0"

src/index.ts

Lines changed: 170 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,182 @@ import { dirname, relative, resolve } from "path";
22
import ts from "typescript";
33
import slash from "slash";
44

5-
const transformer = <T extends ts.Node>(_: ts.Program) => {
6-
return (context: ts.TransformationContext) => (rootNode: T) => {
7-
const compilerOptions = context.getCompilerOptions();
5+
const transformer = (_: ts.Program) => (context: ts.TransformationContext) => (
6+
sourceFile: ts.SourceFile
7+
) => {
8+
const resolver =
9+
typeof (context as any).getEmitResolver === "function"
10+
? (context as any).getEmitResolver()
11+
: undefined;
12+
const compilerOptions = context.getCompilerOptions();
13+
const sourceDir = dirname(sourceFile.fileName);
14+
15+
const { baseUrl = "", paths = {} } = compilerOptions;
16+
17+
const binds = Object.keys(paths)
18+
.filter(key => paths[key].length)
19+
.map(key => ({
20+
regexp: new RegExp("^" + key.replace("*", "(.*)") + "$"),
21+
path: paths[key][0]
22+
}));
23+
24+
if (!baseUrl || binds.length === 0) {
25+
// There is nothing we can do without baseUrl and paths specified.
26+
return sourceFile;
27+
}
28+
29+
function bindModuleToFile(moduleName: string) {
30+
for (const { regexp, path } of binds) {
31+
const match = regexp.exec(moduleName);
32+
if (match) {
33+
const out = path.replace(/\*/g, match[1]);
34+
const file = slash(relative(sourceDir, resolve(baseUrl, out)));
35+
return file[0] === "." ? file : `./${file}`;
36+
}
37+
}
38+
}
39+
40+
function visit(node: ts.Node): ts.VisitResult<ts.Node> {
841
if (
9-
compilerOptions.baseUrl === undefined ||
10-
compilerOptions.paths === undefined
42+
resolver &&
43+
ts.isExportDeclaration(node) &&
44+
!node.exportClause &&
45+
!compilerOptions.isolatedModules &&
46+
!resolver.moduleExportsSomeValue(node.moduleSpecifier)
1147
) {
12-
throw new Error(
13-
"Should define baseUrl and paths properties in the tsconfig"
48+
return undefined;
49+
}
50+
if (ts.isImportDeclaration(node)) {
51+
return unpathImportDeclaration(node);
52+
}
53+
if (ts.isExportDeclaration(node)) {
54+
return unpathExportDeclaration(node);
55+
}
56+
57+
return ts.visitEachChild(node, visit, context);
58+
}
59+
60+
function unpathImportDeclaration(
61+
node: ts.ImportDeclaration
62+
): ts.VisitResult<ts.Statement> {
63+
if (!ts.isStringLiteral(node.moduleSpecifier)) {
64+
return node;
65+
}
66+
const file = bindModuleToFile(node.moduleSpecifier.text);
67+
if (!file) {
68+
return node;
69+
}
70+
const fileLiteral = ts.createLiteral(file);
71+
72+
const importClause = ts.visitNode(
73+
node.importClause,
74+
visitImportClause as any,
75+
ts.isImportClause
76+
);
77+
return node.importClause === importClause || importClause
78+
? ts.updateImportDeclaration(
79+
node,
80+
node.decorators,
81+
node.modifiers,
82+
node.importClause,
83+
fileLiteral
84+
)
85+
: undefined;
86+
}
87+
function visitImportClause(
88+
node: ts.ImportClause
89+
): ts.VisitResult<ts.ImportClause> {
90+
const name = resolver.isReferencedAliasDeclaration(node)
91+
? node.name
92+
: undefined;
93+
const namedBindings = ts.visitNode(
94+
node.namedBindings,
95+
visitNamedImportBindings as any,
96+
ts.isNamedImports
97+
);
98+
return name || namedBindings
99+
? ts.updateImportClause(node, name, namedBindings)
100+
: undefined;
101+
}
102+
function visitNamedImportBindings(
103+
node: ts.NamedImportBindings
104+
): ts.VisitResult<ts.NamedImportBindings> {
105+
if (node.kind === ts.SyntaxKind.NamespaceImport) {
106+
return resolver.isReferencedAliasDeclaration(node) ? node : undefined;
107+
} else {
108+
const elements = ts.visitNodes(
109+
node.elements,
110+
visitImportSpecifier as any,
111+
ts.isImportSpecifier
14112
);
113+
return elements.some(e => e)
114+
? ts.updateNamedImports(node, elements)
115+
: undefined;
15116
}
16-
const baseUrl = compilerOptions.baseUrl;
17-
const paths = compilerOptions.paths;
18-
const regPaths = Object.keys(paths).map(key => ({
19-
regexp: new RegExp("^" + key.replace("*", "(.*)") + "$"),
20-
resolve: paths[key][0]
21-
}));
22-
let fileDir = "";
23-
function findFileInPaths(text: string) {
24-
for (const path of regPaths) {
25-
const match = text.match(path.regexp);
26-
if (match) {
27-
const out = path.resolve.replace(/\*/g, match[1]);
28-
const file = slash(relative(fileDir, resolve(baseUrl, out)));
29-
return file[0] === "." ? file : `./${file}`;
30-
}
31-
}
32-
return null;
117+
}
118+
function visitImportSpecifier(
119+
node: ts.ImportSpecifier
120+
): ts.VisitResult<ts.ImportSpecifier> {
121+
return resolver.isReferencedAliasDeclaration(node) ? node : undefined;
122+
}
123+
124+
function unpathExportDeclaration(
125+
node: ts.ExportDeclaration
126+
): ts.VisitResult<ts.Statement> {
127+
if (!node.moduleSpecifier || !ts.isStringLiteral(node.moduleSpecifier)) {
128+
return node;
33129
}
34-
function visit(node: ts.Node): ts.Node {
35-
if (ts.isSourceFile(node)) {
36-
fileDir = dirname(node.fileName);
37-
return ts.visitEachChild(node, visit, context);
38-
}
39-
if (
40-
(ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&
41-
node.moduleSpecifier &&
42-
ts.isStringLiteral(node.moduleSpecifier)
43-
) {
44-
const file = findFileInPaths(node.moduleSpecifier.text);
45-
if (file) {
46-
node.moduleSpecifier.text = file;
47-
return node;
48-
}
49-
}
50-
return ts.visitEachChild(node, visit, context);
130+
if (
131+
!node.exportClause &&
132+
!compilerOptions.isolatedModules &&
133+
!resolver.moduleExportsSomeValue(node.moduleSpecifier)
134+
) {
135+
return node;
51136
}
52-
return ts.visitNode(rootNode, visit);
53-
};
137+
if (node.exportClause && resolver.isValueAliasDeclaration(node)) {
138+
return node;
139+
}
140+
141+
const file = bindModuleToFile(node.moduleSpecifier.text);
142+
if (!file) {
143+
return node;
144+
}
145+
const fileLiteral = ts.createLiteral(file);
146+
147+
const exportClause = ts.visitNode(
148+
node.exportClause,
149+
visitNamedExports as any,
150+
ts.isNamedExports
151+
);
152+
return node.exportClause === exportClause || exportClause
153+
? ts.updateExportDeclaration(
154+
node,
155+
node.decorators,
156+
node.modifiers,
157+
node.exportClause,
158+
fileLiteral
159+
)
160+
: undefined;
161+
}
162+
function visitNamedExports(
163+
node: ts.NamedExports
164+
): ts.VisitResult<ts.NamedExports> {
165+
const elements = ts.visitNodes(
166+
node.elements,
167+
visitExportSpecifier as any,
168+
ts.isExportSpecifier
169+
);
170+
return elements.some(e => e)
171+
? ts.updateNamedExports(node, elements)
172+
: undefined;
173+
}
174+
function visitExportSpecifier(
175+
node: ts.ExportSpecifier
176+
): ts.VisitResult<ts.ExportSpecifier> {
177+
return resolver.isValueAliasDeclaration(node) ? node : undefined;
178+
}
179+
180+
return ts.visitNode(sourceFile, visit);
54181
};
55182

56183
export default transformer;

test.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
5+
npm run build
6+
npx ttsc -p tests/tsconfig.json
7+
node tests/out/core/index.js
8+
if fgrep '@utils' tests/out/*/* ;
9+
then
10+
false
11+
fi

tests/tsconfig.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
"strict": true,
77

8+
"declaration": true,
89
"outDir": "out/",
910
"baseUrl": "./",
1011
"paths": {
@@ -13,6 +14,9 @@
1314

1415
"esModuleInterop": true,
1516

16-
"plugins": [{ "transform": "../" }]
17+
"plugins": [
18+
{ "transform": "../" },
19+
{ "transform": "../", "afterDeclarations": true }
20+
]
1721
}
1822
}

tests/utils/types-only.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export type NoRuntimecodeHere = "never gonna give you up!";
22

3-
// throw new Error('Not supposed to be!');
3+
throw new Error("Not supposed to be!");

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212

1313
"esModuleInterop": true
1414
},
15-
"include": ["src/**/*"]
15+
"exclude": ["tests", "lib"]
1616
}

0 commit comments

Comments
 (0)