Skip to content

Commit 14247e3

Browse files
committed
Add typescript validation script
1 parent 5fe20c2 commit 14247e3

File tree

2 files changed

+273
-2
lines changed

2 files changed

+273
-2
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@
4040
"react": "^16.8.6",
4141
"react-native": "^0.59.8",
4242
"sinon": "^7.3.2",
43-
"wait-for-expect": "^1.2.0"
43+
"wait-for-expect": "^1.2.0",
44+
"esprima": "^4.0.1",
45+
"typescript": "^4.0.3"
4446
},
4547
"jest": {
4648
"preset": "react-native",
@@ -53,4 +55,4 @@
5355
"scripts": {
5456
"test": "jest && ./node_modules/.bin/codecov"
5557
}
56-
}
58+
}

typescript_validator.js

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
'use strict';
2+
var fs = require('fs');
3+
const esprima = require("esprima");
4+
const ts = require('typescript');
5+
6+
const LOG_LEVEL_SUCCESS = 0;
7+
const LOG_LEVEL_FAIL = 1;
8+
9+
const INDEX_FILE = 'index.js';
10+
const TS_DEF_FILE_PATH = 'index.d.ts';
11+
const MODULE_DIR = 'modules/';
12+
13+
const JS_FILES = 'Javascript Files';
14+
const TS_DEF_FILE = 'Typescript Definition File';
15+
16+
function parseModule(block) {
17+
var functions = [];
18+
var enums = [];
19+
let tree = esprima.parseModule(block);
20+
tree.body.forEach((el) => {
21+
if ((el.type == "VariableDeclaration" && el.declarations[0].init.type == "ObjectExpression") || (el.type == "ExportDefaultDeclaration" && el.declaration.type == "ObjectExpression")) {
22+
var props = [];
23+
if (el.type == "ExportDefaultDeclaration" && el.declaration.type == "ObjectExpression") {
24+
props = el.declaration.properties;
25+
} else {
26+
props = el.declarations[0].init.properties;
27+
}
28+
props.forEach((prop) => {
29+
if (prop.value.type == "FunctionExpression") {
30+
var params = [];
31+
prop.value.params.forEach((param) => {
32+
if (param.type == "ObjectPattern") { // flatten composite object params
33+
param.properties.forEach((param) => {
34+
params.push(param.key.name)
35+
});
36+
} else {
37+
params.push(param.name)
38+
}
39+
});
40+
functions.push({ name: prop.key.name, params: params });
41+
} else {
42+
var values = [];
43+
prop.value.properties.forEach((value) => {
44+
values.push(value.key.name)
45+
});
46+
enums.push({ name: prop.key.name, values: values });
47+
}
48+
});
49+
}
50+
});
51+
return { functions: functions, enums: enums };
52+
}
53+
54+
function parseDefinition() {
55+
const node = ts.createSourceFile(
56+
TS_DEF_FILE_PATH,
57+
fs.readFileSync(TS_DEF_FILE_PATH, 'utf8'),
58+
ts.ScriptTarget.Latest
59+
);
60+
var allDef = [];
61+
var indexFunctions = [];
62+
var indexEnums = [];
63+
node.statements.forEach((statement) => {
64+
if (statement.body) {
65+
// this is a module
66+
var functions = [];
67+
var enums = [];
68+
statement.body.statements.forEach((moduleStatement) => {
69+
var params = [];
70+
if (moduleStatement.parameters) {
71+
moduleStatement.parameters.forEach((param) => {
72+
if (param.type.members) {
73+
param.type.members.forEach((member) => {
74+
params.push(member.name.escapedText);
75+
});
76+
} else {
77+
params.push(param.name.escapedText);
78+
}
79+
});
80+
functions.push({ name: moduleStatement.name.escapedText, params: params });
81+
} else {
82+
var enumValues = [];
83+
moduleStatement.members.forEach((member) => {
84+
enumValues.push(member.name.escapedText);
85+
});
86+
enums.push({ name: moduleStatement.name.escapedText, values: enumValues });
87+
}
88+
});
89+
allDef.push({ moduleName: statement.name.escapedText, data: { functions: functions, enums: enums } });
90+
} else {
91+
var params = [];
92+
var enumValues = [];
93+
if (statement.parameters) {
94+
statement.parameters.forEach((param) => { // flatten composite object params
95+
if (param.type.members) {
96+
param.type.members.forEach((member) => {
97+
params.push(member.name.escapedText);
98+
});
99+
} else {
100+
params.push(param.name.escapedText);
101+
}
102+
});
103+
indexFunctions.push({ name: statement.name.escapedText, params: params });
104+
} else {
105+
if (statement.kind == 252) { // statement kind 252 indicates an enum declare statement
106+
var enumValues = [];
107+
statement.members.forEach((member) => {
108+
enumValues.push(member.name.escapedText);
109+
});
110+
indexEnums.push({ name: statement.name.escapedText, values: enumValues });
111+
}
112+
}
113+
}
114+
});
115+
allDef.push({ moduleName: "index", data: { functions: indexFunctions, enums: indexEnums } });
116+
return allDef;
117+
}
118+
119+
function compareModuleDefinition(module, definition, baseFile) {
120+
var flag = true;
121+
module.data.functions.forEach((func) => {
122+
var funcFound = false;
123+
for (var i = 0; i < definition.data.functions.length; i++) {
124+
if (func.name === definition.data.functions[i].name) {
125+
func.params.forEach((param) => {
126+
var paramFound = false;
127+
for (var j = 0; j < definition.data.functions[i].params.length; j++) {
128+
if (param === definition.data.functions[i].params[j]) {
129+
paramFound = true;
130+
break;
131+
}
132+
}
133+
if (!paramFound) {
134+
logParamDiscrepency(param, func.name, module.moduleName, baseFile);
135+
flag = false;
136+
}
137+
});
138+
var funcFound = true;
139+
break;
140+
}
141+
}
142+
if (!funcFound) {
143+
logFunctionDiscrepency(module.moduleName, func.name, baseFile);
144+
flag = false;
145+
}
146+
});
147+
148+
module.data.enums.forEach((en) => {
149+
var enumFound = false;
150+
for (var i = 0; i < definition.data.enums.length; i++) {
151+
if (en.name === definition.data.enums[i].name) {
152+
en.values.forEach((value) => {
153+
var valueFound = false;
154+
for (var j = 0; j < definition.data.enums[i].values.length; j++) {
155+
if (value === definition.data.enums[i].values[j]) {
156+
valueFound = true;
157+
break;
158+
}
159+
}
160+
if (!valueFound) {
161+
logEnumValueDiscrepency(value, en.name, module.moduleName, baseFile);
162+
flag = false;
163+
}
164+
});
165+
var enumFound = true;
166+
break;
167+
}
168+
}
169+
if (!enumFound) {
170+
logEnumDiscrepency(en.name, module.moduleName, fileName);
171+
flag = false;
172+
}
173+
});
174+
175+
return flag;
176+
}
177+
178+
function logModuleDiscrepency(module, fileName) {
179+
console.error(" Module: " + module + " was not found in" + fileName);
180+
}
181+
182+
function logFunctionDiscrepency(module, func, fileName) {
183+
console.error("Function: " + func + " in Module: " + module + " was not found in " + fileName);
184+
185+
}
186+
187+
function logParamDiscrepency(param, func, module, fileName) {
188+
console.error("Param: " + param + " for the function: " + func + " in Module: " + module + " was not found in " + fileName);
189+
}
190+
191+
function logEnumDiscrepency(en, module, fileName) {
192+
console.error("Enum: " + en + " in Module: " + module + " was not found in " + fileName)
193+
}
194+
195+
196+
function logEnumValueDiscrepency(value, en, module, fileName) {
197+
console.error("Value: " + value + " for the Enum: " + en + " in Module: " + module + " was not found in " + fileName);
198+
}
199+
200+
201+
function finish(logLevel, message) {
202+
if (logLevel === LOG_LEVEL_SUCCESS) {
203+
console.info(message);
204+
} else {
205+
console.error(message);
206+
process.exit(1);
207+
}
208+
}
209+
210+
var allMatch = true;
211+
var all = [];
212+
var allDef = [];
213+
var modules = fs.readdirSync(MODULE_DIR);
214+
for (var i = 0; i < modules.length; i++) {
215+
modules[i] = MODULE_DIR + modules[i];
216+
}
217+
modules.push(INDEX_FILE);
218+
modules.forEach((module) => {
219+
var data = fs.readFileSync(module);
220+
all.push({ moduleName: module.replace('.js', '').replace(MODULE_DIR, ''), data: parseModule(data.toString()) });
221+
});
222+
223+
allDef = parseDefinition();
224+
225+
console.log(JS_FILES + ' <-- ' + TS_DEF_FILE);
226+
for (var i = 0; i < allDef.length; i++) {
227+
var moduleFound = false;
228+
for (var j = 0; j < all.length; j++) {
229+
if (allDef[i].moduleName === all[j].moduleName) {
230+
if (!compareModuleDefinition(allDef[i], all[j], JS_FILES)) {
231+
allMatch = false;
232+
}
233+
moduleFound = true;
234+
break;
235+
}
236+
}
237+
if (!moduleFound) {
238+
logModuleDiscrepency(allDef[i].moduleName, JS_FILES);
239+
allMatch = false;
240+
}
241+
}
242+
if (allMatch) {
243+
console.log('✅');
244+
}
245+
console.log('\n');
246+
console.log(TS_DEF_FILE + ' <-- ' + JS_FILES);
247+
for (var i = 0; i < all.length; i++) {
248+
var moduleFound = false;
249+
for (var j = 0; j < allDef.length; j++) {
250+
if (all[i].moduleName === allDef[j].moduleName) {
251+
if (!compareModuleDefinition(all[i], allDef[j], TS_DEF_FILE)) {
252+
allMatch = false;
253+
}
254+
moduleFound = true;
255+
break;
256+
}
257+
}
258+
if (!moduleFound) {
259+
logModuleDiscrepency(all[i].moduleName, TS_DEF_FILE);
260+
allMatch = false;
261+
}
262+
}
263+
if (allMatch) {
264+
console.log('✅');
265+
finish(LOG_LEVEL_SUCCESS, "Javascript and Typescript Definition match!")
266+
} else {
267+
finish(LOG_LEVEL_FAIL, "\n ❌ Javascript and Typescript Definition do not match!")
268+
269+
}

0 commit comments

Comments
 (0)