Skip to content

Commit c3f2e94

Browse files
committed
Support Typescript
1 parent 7f047fc commit c3f2e94

9 files changed

+84
-49
lines changed

src/app.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ function updateFlowGraph (fg, exportFuncs, importFuncs,
147147
mod.rmFileFromImports(importFuncs, oldFname);
148148
}
149149
if (newFname) {
150-
const ast = astutil.buildSingleAST(newFname, newSrc);
150+
const ast = astutil.astFromSrc(newFname, newSrc);
151151
if (ast !== null) {
152152
bindings.addBindings(ast);
153153
mod.collectExportsImports(ast, exportFuncs, importFuncs);
@@ -209,14 +209,14 @@ function getChangeStats (oldFname, oldSrc, newFname, newSrc, patch) {
209209
let forwardAST = null, bckwardAST = null;
210210

211211
if (oldFname) {
212-
forwardAST = astutil.buildSingleAST(oldFname, oldSrc);
212+
forwardAST = astutil.astFromSrc(oldFname, oldSrc);
213213
if (forwardAST !== null) {
214214
forwardFuncs = astutil.getFunctions(forwardAST);
215215
forwardStats = detectChange(parser.parse(patch), forwardFuncs);
216216
}
217217
}
218218
if (newFname) {
219-
bckwardAST = astutil.buildSingleAST(newFname, newSrc);
219+
bckwardAST = astutil.astFromSrc(newFname, newSrc);
220220
if (bckwardAST !== null) {
221221
bckwardFuncs = astutil.getFunctions(bckwardAST);
222222
bckwardStats = detectChange(parser.invParse(patch), bckwardFuncs);

src/astutil.js

+53-40
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
const esprima = require('esprima');
1212
const fs = require('fs');
13-
const sloc = require('sloc');
1413
const escodegen = require('escodegen');
14+
const prep = require('./srcPreprocessor');
1515

1616
/* AST visitor */
1717
function visit(root, visitor) {
@@ -162,57 +162,70 @@ function ppPos(nd) {
162162
return basename(nd.attr.enclosingFile) + "@" + nd.loc.start.line + ":" + nd.range[0] + "-" + nd.range[1];
163163
}
164164

165-
/* Build an AST from a collection of source files. */
166-
function buildAST(files) {
167-
var sources = files.map(function (file) {
168-
return { filename: file,
169-
program: fs.readFileSync(file, 'utf-8') };
170-
});
171-
172-
var ast = {
165+
/* Build as AST from a collection of source files */
166+
function astFromFiles(files) {
167+
const ast = {
173168
type: 'ProgramCollection',
174169
programs: [],
175170
attr: {}
176-
};
177-
sources.forEach(function (source) {
178-
var prog = esprima.parseModule(source.program, { loc: true, range: true, tolerant: true});
179-
prog.attr = { filename: source.filename, sloc : sloc(source.program, "js").sloc};
180-
ast.programs.push(prog);
181-
});
171+
}
172+
173+
for (let file of files) {
174+
let src = fs.readFileSync(file, 'utf-8');
175+
ast.programs.push(buildProgram(file, src));
176+
}
182177
init(ast);
183-
ast.attr.sloc = ast.programs
184-
.map(function(program){
185-
return program.attr.sloc;
186-
}).reduce(function(previous, current) {
187-
return previous + current;
188-
});
189178
return ast;
190179
}
191180

192-
function buildSingleAST (fname, src) {
193-
let prog;
194-
try {
195-
prog = esprima.parseModule(src,
196-
{loc: true, range: true, tolerant: true, jsx: true});
197-
}
198-
catch(err) {
199-
console.log('-------------------------------------------');
200-
console.log('Warning: Esprima failed to parse ' + fname);
201-
console.log(err.stack);
202-
console.log('-------------------------------------------');
203-
return null;
204-
}
205-
prog.attr = {filename: fname, sloc: sloc(src, 'js').sloc };
206-
181+
/* Build an AST from file name and source code */
182+
function astFromSrc(fname, src) {
183+
const prog = buildProgram(fname, src);
207184
const ast = {
208185
'type': 'ProgramCollection',
209186
'programs': [prog],
210-
'attr': {sloc: prog.attr.sloc}
187+
'attr': {}
211188
}
212189
init(ast);
213190
return ast;
214191
}
215192

193+
function reportError(msg, err) {
194+
console.log('-------------------------------------------');
195+
console.log(msg);
196+
console.log(err.stack);
197+
console.log('-------------------------------------------');
198+
}
199+
200+
function buildProgram (fname, src) {
201+
// transpile typescript
202+
try {
203+
if (fname.endsWith('.ts'))
204+
src = prep.typescriptPrep(src);
205+
}
206+
catch (err) {
207+
reportError('WARNING: Transpiling typescript failed.', err);
208+
return null;
209+
}
210+
211+
// parse javascript
212+
let prog;
213+
try {
214+
prog = esprima.parseModule(src, {
215+
loc: true,
216+
range: true,
217+
tolerant: true,
218+
jsx: true
219+
});
220+
}
221+
catch(err) {
222+
reportError('Warning: Esprima failed to parse ' + fname, err);
223+
return null;
224+
}
225+
prog.attr = {filename: fname};
226+
return prog;
227+
}
228+
216229
// cf is used by getFunctions
217230
const cf = funcObj => {
218231
return funcObj.file + ':' +
@@ -233,7 +246,7 @@ const astToCode = astNode => {
233246
234247
Args:
235248
root - An ast node of type 'ProgramCollection',
236-
- the output of buildSingleAST function,
249+
- the output of astFromSrc function,
237250
- thus, root.programs.length is equal to 1
238251
239252
Returns:
@@ -415,8 +428,8 @@ module.exports.init = init;
415428
module.exports.ppPos = ppPos;
416429
module.exports.funcname = funcname;
417430
module.exports.encFuncName = encFuncName;
418-
module.exports.buildAST = buildAST;
419-
module.exports.buildSingleAST = buildSingleAST;
431+
module.exports.astFromFiles = astFromFiles;
432+
module.exports.astFromSrc = astFromSrc;
420433
module.exports.getFunctions = getFunctions;
421434
module.exports.isAnon = isAnon;
422435
module.exports.isModuleExports = isModuleExports;

src/callbackCounter.js

-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ define(function(require, exports) {
3838
var callbackPercentage = callbacks.length / totalParameters * 100;
3939
console.log("I found " + callbacks.length + " callbacks and " + callbackUses + " call back uses. In total we have " + functionDeclarationParameter + " function declaration parameters and " + functionExpressionParameter + " function expression parameters.");
4040
console.log("This makes a total of " + totalParameters + " parameters. Which means that (counting each function once as a callback) " + callbackPercentage + " percent of parameters are callbacks.");
41-
console.log("The total SLOC is " + ast.attr.sloc);
4241
};
4342

4443
function findEnclosingFunctionParameter(node, functionName) {

src/requireJsGraph.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ define(function(require, exports) {
4444
normOutgoingDep = normOutgoingDep.replace(/\//, "\\");
4545
var newStart = folder + normOutgoingDep;
4646
if (fs.existsSync(newStart)) {
47-
var referencedAST = astutil.buildAST([newStart]);
47+
var referencedAST = astutil.astFromFiles([newStart]);
4848
dependencyGraph = dependencyGraph.concat(makeRequireJsGraph(referencedAST))
4949
}
5050
});

src/runner.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ define(function (require, exports) {
138138
args.strategy = 'DEMAND';
139139
}
140140
if (args.time) console.time("parsing ");
141-
var ast = astutil.buildAST(files);
141+
var ast = astutil.astFromFiles(files);
142142
if (args.time) console.timeEnd("parsing ");
143143

144144
if (args.time) console.time("bindings ");
@@ -204,7 +204,7 @@ define(function (require, exports) {
204204
else if (fs.statSync(file).isDirectory()) {
205205
filelist = utils.collectFiles(file, filelist);
206206
}
207-
else if (file.endsWith(".js")) {
207+
else if (file.endsWith(".js") || file.endsWith(".ts")) {
208208
filelist.push(file);
209209
}
210210
});

src/srcPreprocessor.js

+15
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,22 @@ function trimHashbangPrep(src) {
6969
return src;
7070
}
7171

72+
/* Compile Typescript into plain Javascript
73+
Plugin @babel/typescript needs filename option to trigger,
74+
see the following issue for details:
75+
https://github.com/babel/babel/issues/8065
76+
*/
77+
function typescriptPrep(src) {
78+
return babel.transform(src, {
79+
presets: ["@babel/preset-typescript"],
80+
filename: '.ts',
81+
retainLines: true,
82+
parserOpts: {strictMode: false}
83+
}).code;
84+
}
85+
7286
module.exports.applyPreps = applyPreps;
7387
module.exports.stripFlowPrep = stripFlowPrep;
7488
module.exports.trimHashbangPrep = trimHashbangPrep;
89+
module.exports.typescriptPrep = typescriptPrep;
7590

tests/jest/trackFunctions.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ test('Test trackFunctions', () => {
4747
const newFname = 'tests/jest/WebPage.new';
4848
const oldSrc = fs.readFileSync(oldFname, 'utf-8');
4949
const newSrc = fs.readFileSync(newFname, 'utf-8');
50-
const oldAST = astutil.buildSingleAST('WebPage.js', oldSrc);
51-
const newAST = astutil.buildSingleAST('WebPage.js', newSrc);
50+
const oldAST = astutil.astFromSrc('WebPage.js', oldSrc);
51+
const newAST = astutil.astFromSrc('WebPage.js', newSrc);
5252
const oldFuncLst = astutil.getFunctions(oldAST);
5353
const newFuncLst = astutil.getFunctions(newAST);
5454
expect(trackFunctions(oldFuncLst, newFuncLst)).toEqual(truth);

tests/typescript/simple.truth

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
simple.ts:global:7 -> simple.ts:greeter:1

tests/typescript/simple.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
function greeter(person: string) {
2+
return "Hello, " + person;
3+
}
4+
5+
let user = "Jane User";
6+
7+
document.body.innerHTML = greeter(user);

0 commit comments

Comments
 (0)