| OLD | NEW |
| (Empty) | |
| 1 const fs = require('fs'); |
| 2 var escodegen = require('escodegen'); |
| 3 var esprima = require('esprima'); |
| 4 |
| 5 String.prototype.rtrim = function() { |
| 6 return this.replace(/\s+$/,""); |
| 7 } |
| 8 |
| 9 String.prototype.replaceLineWithin = function(from, to, a, b) { |
| 10 --from; |
| 11 ++to; |
| 12 var chunk = this.substring(from, to); |
| 13 var newChunk = []; |
| 14 for (var line of chunk.split("\n")) { |
| 15 if (line.trim() === a) |
| 16 newChunk.push(line.replace(a, b)); |
| 17 else |
| 18 newChunk.push(line); |
| 19 } |
| 20 return this.substring(0, from) + newChunk.join("\n") + this.substring(to); |
| 21 } |
| 22 |
| 23 String.prototype.replaceRange = function(range, replacement) { |
| 24 return this.substring(0, range[0]) + replacement + this.substring(range[1]); |
| 25 } |
| 26 |
| 27 class FileProcessor { |
| 28 |
| 29 constructor(filePath) |
| 30 { |
| 31 this._filePath = filePath; |
| 32 this._classInfos = new Map(); |
| 33 this._content = fs.readFileSync(filePath).toString(); |
| 34 |
| 35 var program; |
| 36 try { |
| 37 program = esprima.parse(this._content, {attachComment:true, range: t
rue, loc: true}); |
| 38 } catch(e) { |
| 39 console.log("FAILED TO PARSE: " + filePath); |
| 40 return; |
| 41 } |
| 42 |
| 43 this._definitions = new Map(); |
| 44 |
| 45 this._walkAddParent(program, node => this._visit(node)); |
| 46 var newContent = this._content; |
| 47 |
| 48 for (var name of this._classInfos.keys()) { |
| 49 var newDefinition = []; |
| 50 var commentNode = this._classInfos.get(name).constructorCommentNode; |
| 51 var constructorNode = this._classInfos.get(name).constructorNode; |
| 52 var prototypeNode = this._classInfos.get(name).prototypeNode; |
| 53 var comment = commentNode ? this._text(commentNode) : ""; |
| 54 if (!comment || !comment.includes("@constructor")) |
| 55 continue; |
| 56 var moduleName = filePath.replace(/front_end\/([^/]+)\/.*/, "$1"); |
| 57 if (moduleName.endsWith("_lazy")) |
| 58 moduleName = moduleName.substring(0, moduleName.length - "_lazy"
.length); |
| 59 |
| 60 if (moduleName === "accessibility") |
| 61 moduleName = "a11y"; |
| 62 if (moduleName === "resources") |
| 63 moduleName = "storage"; |
| 64 if (moduleName === "heap_snapshot_worker") |
| 65 moduleName = "heap_worker"; |
| 66 if (moduleName === "timeline") |
| 67 moduleName = "timeline_ui"; |
| 68 if (moduleName === "timeline_module") |
| 69 moduleName = "timeline"; |
| 70 |
| 71 var newName = name.replace("WebInspector.", moduleName + "."); |
| 72 var capModule = moduleName.substring(0, 1).toUpperCase() + moduleNam
e.substring(1); |
| 73 var prefix = moduleName + "." + capModule; |
| 74 if (newName.startsWith(prefix) && newName.length > prefix.length) |
| 75 newName = newName.replace(prefix, moduleName + "."); |
| 76 |
| 77 console.log(newName); |
| 78 |
| 79 var commentLines = comment.split("\n"); |
| 80 var implementsLines = []; |
| 81 for (var commentLine of commentLines) { |
| 82 var implementsMatch = commentLine.match(/@implements {(.*)}/); |
| 83 if (implementsMatch) |
| 84 implementsLines.push(` * @implements {${implementsMatch[1]}}
`); |
| 85 var templateMatch = commentLine.match(/@template/); |
| 86 if (templateMatch) |
| 87 implementsLines.push(commentLine); |
| 88 } |
| 89 implementsLines.push(" * @unrestricted"); |
| 90 newDefinition.push(`/**\n${implementsLines.join("\n")}\n*/\n`); |
| 91 |
| 92 if (constructorNode.type === "FunctionDeclaration") |
| 93 newDefinition.push(`var ${name} = class `); |
| 94 else |
| 95 newDefinition.push(`${name} = class `); |
| 96 |
| 97 var extendsMatch = comment.match(/@extends {(.*)}/); |
| 98 let superName; |
| 99 if (extendsMatch) |
| 100 superName = extendsMatch[1]; |
| 101 |
| 102 if (superName) |
| 103 newDefinition.push(`extends ${superName} {`); |
| 104 else |
| 105 newDefinition.push(`{`); |
| 106 |
| 107 if (comment) { |
| 108 comment = comment.replace(/^/gm, " ").substring(2); |
| 109 comment = comment.replace(/\s*@implements.*/g, ""); |
| 110 comment = comment.replace(/\s*@extends.*/, ""); |
| 111 comment = comment.replace(/\s*@constructor/, ""); |
| 112 comment = comment.replace(/\s*@template.*/g, ""); |
| 113 comment = comment.split("\n").filter(line => line.trim() !== "*"
).join("\n"); |
| 114 if (comment.replace(/[*/]/g, "").trim()) |
| 115 newDefinition.push(`\n ${comment}`); |
| 116 } |
| 117 |
| 118 var constructorText = this._text(constructorNode).trim(); |
| 119 constructorText = constructorText.replace(/^/gm, " "); |
| 120 var lines = constructorText.split("\n"); |
| 121 lines[0] = lines[0].replace(`function ${name}`, " constructor"); |
| 122 lines[0] = lines[0].replace(`function(`, " constructor("); |
| 123 constructorText = lines.join("\n"); |
| 124 if (constructorText.includes(`${superName}.call`)) { |
| 125 constructorText = constructorText.replace(`${superName}.call(thi
s, `, "super("); |
| 126 constructorText = constructorText.replace(`${superName}.call(thi
s)`, "super()"); |
| 127 } else if (superName) { |
| 128 constructorText = constructorText.replace("{", "{\n super
();"); |
| 129 } |
| 130 |
| 131 if (constructorText.replace(/constructor[^{]+{/m, "").replace(/.*}/,
"").trim()) { |
| 132 newDefinition.push("\n"); |
| 133 newDefinition.push(constructorText); |
| 134 } |
| 135 |
| 136 if (prototypeNode) { |
| 137 // Remove block comma |
| 138 var prototypeText = this._text(prototypeNode); |
| 139 for (var i = 0; i < prototypeNode.properties.length; ++i) { |
| 140 var previous = prototypeNode.properties[i - 1]; |
| 141 var property = prototypeNode.properties[i]; |
| 142 prototypeText = prototypeText.replaceLineWithin(previous ? p
revious.range[1] - prototypeNode.range[0] : 0, property.range[0] - prototypeNode
.range[0], "},", "} "); |
| 143 } |
| 144 prototypeText = prototypeText.replaceLineWithin(property ? prope
rty.range[1] - prototypeNode.range[0] : prototypeNode.range[1] - prototypeNode.r
ange[0], prototypeNode.range[1] - prototypeNode.range[0], "},", "} "); |
| 145 |
| 146 prototypeText = prototypeText.replace(/(^\s+[\w]+)\:\s?function/
mg, "$1"); |
| 147 prototypeText = prototypeText.replace(new RegExp(`${superName}\.
prototype\.([^.]+)\.call\\(this, `, 'g'), "super.$1("); |
| 148 prototypeText = prototypeText.replace(new RegExp(`${superName}\.
prototype\.([^.]+)\.call\\(this\\)`, 'g'), "super.$1()"); |
| 149 prototypeText = prototypeText.replace(new RegExp(`^\\s+__proto__
\\s*:\\s*${superName}.*`, "m"), ""); |
| 150 |
| 151 newDefinition.push("\n"); |
| 152 newDefinition.push(prototypeText.substring(1, prototypeText.leng
th - 2)); |
| 153 } |
| 154 newDefinition.push("\n}\n"); |
| 155 this._definitions.set(name, newDefinition.join(""));; |
| 156 } |
| 157 |
| 158 var newContent = this._content; |
| 159 for (var name of this._definitions.keys()) { |
| 160 var classInfo = this._classInfos.get(name); |
| 161 |
| 162 newContent = this._replaceRange(newContent, classInfo.constructorCom
mentNode.range, ""); |
| 163 newContent = this._replaceRange(newContent, classInfo.constructorNod
e.range, this._definitions.get(name)); |
| 164 |
| 165 |
| 166 if (classInfo.prototypeNode) |
| 167 newContent = this._replaceRange(newContent, classInfo.prototypeN
ode.range, ""); |
| 168 } |
| 169 |
| 170 for (var name of this._definitions.keys()) { |
| 171 newContent = newContent.replace(`${name} = ${name} =`, `${name} =`); |
| 172 newContent = newContent.replace(`${name} = /**`, `/**`); |
| 173 newContent = newContent.replace(`${name}.prototype = ;`, ""); |
| 174 newContent = newContent.replace(`${name}.prototype =\n;`, ""); |
| 175 } |
| 176 newContent = newContent.trim(); |
| 177 newContent = newContent.replace(/\n\n\n/g, "\n\n"); |
| 178 newContent = newContent.replace(/}\n;/g, "};"); |
| 179 newContent = newContent.split("\n").map(line => line.rtrim()).join("\n")
; |
| 180 newContent = newContent.replace("\n\n", "\n") + "\n"; |
| 181 fs.writeFileSync(filePath, newContent); |
| 182 } |
| 183 |
| 184 _replaceRange(text, range, newContent) |
| 185 { |
| 186 range = range.slice(); |
| 187 var result = text.substring(0, range[0]) + newContent + text.substring(r
ange[1]); |
| 188 var newLength = newContent.length; |
| 189 for (var name of this._definitions.keys()) { |
| 190 var classInfo = this._classInfos.get(name); |
| 191 this._patchRange(classInfo.constructorCommentNode.range, range, newL
ength); |
| 192 this._patchRange(classInfo.constructorNode.range, range, newLength); |
| 193 if (classInfo.prototypeNode) |
| 194 this._patchRange(classInfo.prototypeNode.range, range, newLength
); |
| 195 } |
| 196 return result; |
| 197 } |
| 198 |
| 199 _patchRange(range, shift, newLength) |
| 200 { |
| 201 var oldLength = shift[1] - shift[0]; |
| 202 if (shift[0] > range[1]) |
| 203 return; |
| 204 if (shift[1] < range[0]) { |
| 205 range[0] += newLength - oldLength; |
| 206 range[1] += newLength - oldLength; |
| 207 return; |
| 208 } |
| 209 if (shift[0] == range[0] && shift[1] == range[1]) { |
| 210 range[1] = range[0]; |
| 211 return; |
| 212 } |
| 213 console.error("POOR RANGE", this._filePath, range, shift); |
| 214 } |
| 215 |
| 216 _classInfo(name) { |
| 217 var result = this._classInfos.get(name); |
| 218 if (!result) { |
| 219 result = {}; |
| 220 this._classInfos.set(name, result); |
| 221 } |
| 222 return result; |
| 223 } |
| 224 |
| 225 _log(title, node) { |
| 226 if (node) |
| 227 console.log(title + " : " + node.range[0] + ":" + node.range[1] + "
" + this._text(node)); |
| 228 } |
| 229 |
| 230 _text(node) { |
| 231 return this._content.substring(node.range[0], node.range[1]); |
| 232 } |
| 233 |
| 234 _visit(node) { |
| 235 var leadingComments = node.leadingComments || (node.parent && node.paren
t.leadingComments) || (node.parent && node.parent.parent && node.parent.parent.l
eadingComments); |
| 236 var comment = null; |
| 237 if (leadingComments) { |
| 238 comment = leadingComments[leadingComments.length - 1]; |
| 239 var validComment = node.range[0] > comment.range[1] && this._content
.substring(comment.range[1], node.range[0]).replace(/[^\n]/g, "").length < 2; |
| 240 if (!validComment) |
| 241 comment = null; |
| 242 } |
| 243 |
| 244 if (node.type === "FunctionDeclaration") { |
| 245 var classInfo = this._classInfo(node.id.name); |
| 246 classInfo.constructorCommentNode = comment; |
| 247 classInfo.constructorNode = node; |
| 248 return; |
| 249 } |
| 250 |
| 251 if (node.type === "FunctionExpression") { |
| 252 if (!node.parent || !node.parent.left) |
| 253 return; |
| 254 var name = this._text(node.parent.left); |
| 255 var classInfo = this._classInfo(name); |
| 256 classInfo.constructorCommentNode = comment; |
| 257 classInfo.constructorNode = node.parent.right; |
| 258 return; |
| 259 } |
| 260 |
| 261 if (node.type === "AssignmentExpression" && node.left.property && node.l
eft.property.name === "prototype") { |
| 262 var protoName = this._text(node.left); |
| 263 var name = protoName.substring(0, protoName.length - ".prototype".le
ngth); |
| 264 this._classInfo(name).prototypeNode = node.right; |
| 265 return; |
| 266 } |
| 267 } |
| 268 |
| 269 _walkAddParent(ast, fn) { |
| 270 var stack = [ast]; |
| 271 for (var i = 0; i < stack.length; i += 1) { |
| 272 var node = stack[i]; |
| 273 fn(node); |
| 274 for (var key in node) { |
| 275 if (key !== 'parent') { |
| 276 var child = node[key]; |
| 277 if (child instanceof Array) { |
| 278 if (typeof child[0] === "number") |
| 279 continue; |
| 280 var len = child.length; |
| 281 for (var j = 0; j < len; j += 1) { |
| 282 var subchild = child[j]; |
| 283 subchild.parent = node; |
| 284 stack.push(subchild); |
| 285 } |
| 286 } else if (child != void 0 && typeof child.type === 'string'
) { |
| 287 child.parent = node; |
| 288 stack.push(child); |
| 289 } |
| 290 } |
| 291 } |
| 292 } |
| 293 } |
| 294 } |
| 295 |
| 296 function walkSync(currentDirPath) { |
| 297 var fs = require('fs'), |
| 298 path = require('path'); |
| 299 fs.readdirSync(currentDirPath).forEach(function (name) { |
| 300 var filePath = path.join(currentDirPath, name); |
| 301 var stat = fs.statSync(filePath); |
| 302 if (stat.isFile() && filePath.endsWith(".js")) { |
| 303 if (filePath.includes("ExtensionAPI.js")) |
| 304 return; |
| 305 if (filePath.includes("externs.js")) |
| 306 return; |
| 307 if (filePath.includes("eslint") || filePath.includes("lighthouse-bac
kground.js") || filePath.includes("/cm/") || filePath.includes("/cm_modes/") ||
filePath.includes("/xterm.js/") || filePath.includes("/acorn/") || filePath.incl
udes("/gonzales/")) |
| 308 return; |
| 309 new FileProcessor(filePath); |
| 310 } else if (stat.isDirectory()) { |
| 311 walkSync(filePath); |
| 312 } |
| 313 }); |
| 314 } |
| 315 |
| 316 walkSync('front_end'); |
| OLD | NEW |