OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 'use strict'; |
| 5 const fs = require('fs'); |
| 6 const path = require('path'); |
| 7 |
| 8 const utils = require('../utils'); |
| 9 |
| 10 const FRONTEND_PATH = path.resolve(__dirname, '..', '..', 'front_end'); |
| 11 const BUILD_GN_PATH = path.resolve(__dirname, '..', '..', 'BUILD.gn'); |
| 12 const SPECIAL_CASE_NAMESPACES_PATH = path.resolve(__dirname, '..', 'special_case
_namespaces.json'); |
| 13 |
| 14 const APPLICATION_DESCRIPTORS = [ |
| 15 'inspector.json', |
| 16 'unit_test_runner.json', |
| 17 ]; |
| 18 |
| 19 // Replace based on specified transformation |
| 20 |
| 21 const MODULES_TO_REMOVE = []; // ['components_lazy', 'ui_lazy']; |
| 22 |
| 23 // NOTE: Extensions-only change |
| 24 const FILES_AFFECTING_EXTENSIONS = [ |
| 25 'components_lazy/LineLevelProfile.js', |
| 26 'components_lazy/GCActionDelegate.js', |
| 27 'components/RequestAppBannerActionDelegate.js', |
| 28 'ui_lazy/CommandMenu.js', |
| 29 ]; |
| 30 |
| 31 const JS_FILES_MAPPING = [ |
| 32 // {file: 'components_lazy/CookiesTable.js', new: 'cookie_table'}, |
| 33 // {file: 'ui/BezierEditor.js', new: 'inline_editor'}, |
| 34 // {file: 'ui/BezierUI.js', new: 'inline_editor'}, |
| 35 // {file: 'ui/ColorSwatch.js', new: 'inline_editor'}, |
| 36 // {file: 'ui/CSSShadowEditor.js', new: 'inline_editor'}, |
| 37 // {file: 'ui/SwatchPopoverHelper.js', new: 'inline_editor'}, |
| 38 // {file: 'components/Spectrum.js', new: 'color_picker'}, |
| 39 // |
| 40 // // Cannot extract dom_ui because of cyclic dependency with components |
| 41 // // {file: 'components/DOMPresentationUtils.js', new: 'dom_ui'}, |
| 42 // {file: 'components/ExecutionContextSelector.js', existing: 'main'}, |
| 43 // {file: 'components_lazy/FilmStripModel.js', existing: 'sdk'}, |
| 44 // |
| 45 // {file: 'components_lazy/FilmStripView.js', new: 'perf_ui'}, |
| 46 {file: 'components_lazy/GCActionDelegate.js', existing: 'main'}, |
| 47 {file: 'components_lazy/LineLevelProfile.js', new: 'perf_ui'}, |
| 48 {file: 'components/RequestAppBannerActionDelegate.js', existing: 'main'}, |
| 49 // {file: 'components/ShortcutsScreen.js', existing: 'ui'}, |
| 50 {file: 'ui_lazy/FilteredListWidget.js', new: 'quick_open'}, |
| 51 {file: 'ui_lazy/CommandMenu.js', new: 'quick_open'}, |
| 52 // {file: 'ui_lazy/DataGrid.js', new: 'data_grid'}, |
| 53 // {file: 'ui_lazy/ViewportDataGrid.js', new: 'data_grid'}, |
| 54 // {file: 'ui_lazy/SortableDataGrid.js', new: 'data_grid'}, |
| 55 // {file: 'ui_lazy/ShowMoreDataGridNode.js', new: 'data_grid'}, |
| 56 // {file: 'ui_lazy/ChartViewport.js', new: 'perf_ui'}, |
| 57 // {file: 'ui_lazy/FlameChart.js', new: 'perf_ui'}, |
| 58 // {file: 'ui_lazy/OverviewGrid.js', new: 'perf_ui'}, |
| 59 // {file: 'ui_lazy/PieChart.js', new: 'perf_ui'}, |
| 60 // {file: 'ui_lazy/TimelineGrid.js', new: 'perf_ui'}, |
| 61 // {file: 'ui_lazy/TimelineOverviewPane.js', new: 'perf_ui'}, |
| 62 {file: 'sources/UISourceCodeFrame.js', existing: 'source_frame'}, |
| 63 {file: 'sources/SourceCodeDiff.js', existing: 'source_frame'}, |
| 64 ]; |
| 65 |
| 66 const MODULE_MAPPING = { |
| 67 // cookie_table: { |
| 68 // dependencies: ['ui', 'sdk', 'data_grid'], |
| 69 // dependents: ['resources', 'network'], |
| 70 // applications: ['inspector.json'], |
| 71 // autostart: false, |
| 72 // }, |
| 73 // inline_editor: { |
| 74 // dependencies: ['ui'], |
| 75 // dependents: ['sources', 'elements', 'resources'], |
| 76 // applications: ['inspector.json', 'unit_test_runner.json'], |
| 77 // autostart: false, |
| 78 // }, |
| 79 // color_picker: { |
| 80 // dependencies: ['ui', 'sdk'], |
| 81 // dependents: ['sources', 'elements'], |
| 82 // applications: ['inspector.json'], |
| 83 // autostart: false, |
| 84 // }, |
| 85 perf_ui: { |
| 86 dependencies: ['ui', 'sdk', 'bindings', 'source_frame', 'text_editor'], |
| 87 dependents: ['network', 'timeline', 'profiler', 'layer_viewer'], |
| 88 applications: ['inspector.json'], |
| 89 autostart: false, |
| 90 }, |
| 91 quick_open: { |
| 92 dependencies: ['ui', 'diff'], |
| 93 dependents: ['sources'], |
| 94 applications: ['inspector.json', 'unit_test_runner.json'], |
| 95 autostart: false, |
| 96 }, |
| 97 // data_grid: { |
| 98 // dependencies: ['ui'], |
| 99 // dependents: ['network', 'profiler', 'resources', 'console', 'timeline'], |
| 100 // applications: ['inspector.json', 'unit_test_runner.json'], |
| 101 // autostart: false, |
| 102 // }, |
| 103 }; |
| 104 |
| 105 const NEW_DEPENDENCIES_BY_EXISTING_MODULES = { |
| 106 // resources: ['components'], |
| 107 source_frame: ['persistence', 'diff'], |
| 108 timeline: ['extensions'], |
| 109 css_tracker: ['source_frame'], |
| 110 }; |
| 111 |
| 112 const REMOVE_DEPENDENCIES_BY_EXISTING_MODULES = { |
| 113 css_tracker: ['sources'], |
| 114 }; |
| 115 |
| 116 const DEPENDENCIES_BY_MODULE = Object.keys(MODULE_MAPPING).reduce((acc, module)
=> { |
| 117 acc[module] = MODULE_MAPPING[module].dependencies; |
| 118 return acc; |
| 119 }, {}); |
| 120 |
| 121 const APPLICATIONS_BY_MODULE = Object.keys(MODULE_MAPPING).reduce((acc, module)
=> { |
| 122 acc[module] = MODULE_MAPPING[module].applications; |
| 123 return acc; |
| 124 }, {}); |
| 125 |
| 126 const DEPENDENTS_BY_MODULE = Object.keys(MODULE_MAPPING).reduce((acc, module) =>
{ |
| 127 acc[module] = MODULE_MAPPING[module].dependents; |
| 128 return acc; |
| 129 }, {}); |
| 130 |
| 131 function extractModule() { |
| 132 const modules = new Set(); |
| 133 for (let fileObj of JS_FILES_MAPPING) { |
| 134 let moduleName = fileObj.file.split('/')[0]; |
| 135 modules.add(moduleName); |
| 136 } |
| 137 const newModuleSet = JS_FILES_MAPPING.reduce((acc, file) => file.new ? acc.add
(file.new) : acc, new Set()); |
| 138 const targetToOriginalFilesMap = JS_FILES_MAPPING.reduce((acc, f) => { |
| 139 let components = f.file.split('/'); |
| 140 components[0] = f.new || f.existing; |
| 141 acc.set(components.join('/'), f.file); |
| 142 return acc; |
| 143 }, new Map()); |
| 144 |
| 145 const cssFilesMapping = findCSSFiles(); |
| 146 const identifiersByFile = calculateIdentifiers(); |
| 147 const identifierMap = mapIdentifiers(identifiersByFile, cssFilesMapping); |
| 148 const extensionMap = removeFromExistingModuleDescriptors(modules, identifierMa
p, cssFilesMapping); |
| 149 |
| 150 // Find out which files are moving extensions |
| 151 for (let e of extensionMap.keys()) { |
| 152 for (let [f, identifiers] of identifiersByFile) { |
| 153 if (identifiers.includes(e)) |
| 154 console.log(`extension: ${e} in file: ${f}`); |
| 155 } |
| 156 } |
| 157 |
| 158 moveFiles(cssFilesMapping); |
| 159 createNewModuleDescriptors(extensionMap, cssFilesMapping, identifiersByFile, t
argetToOriginalFilesMap); |
| 160 updateExistingModuleDescriptors(extensionMap, cssFilesMapping, identifiersByFi
le, targetToOriginalFilesMap); |
| 161 addDependenciesToDescriptors(); |
| 162 renameIdentifiers(identifierMap); |
| 163 updateBuildGNFile(cssFilesMapping, newModuleSet); |
| 164 for (let descriptor of APPLICATION_DESCRIPTORS) |
| 165 updateApplicationDescriptor(descriptor, newModuleSet); |
| 166 |
| 167 for (let m of MODULES_TO_REMOVE) { |
| 168 utils.removeRecursive(path.resolve(FRONTEND_PATH, m)); |
| 169 } |
| 170 } |
| 171 |
| 172 String.prototype.replaceAll = function(search, replacement) { |
| 173 let target = this; |
| 174 return target.replace(new RegExp('\\b' + search + '\\b', 'g'), replacement); |
| 175 }; |
| 176 |
| 177 Set.prototype.union = function(setB) { |
| 178 let union = new Set(this); |
| 179 for (let elem of setB) |
| 180 union.add(elem); |
| 181 |
| 182 return union; |
| 183 }; |
| 184 |
| 185 function mapModuleToNamespace(module) { |
| 186 const specialCases = require(SPECIAL_CASE_NAMESPACES_PATH); |
| 187 return specialCases[module] || toCamelCase(module); |
| 188 |
| 189 function toCamelCase(module) { |
| 190 return module.split('_').map(a => a.substring(0, 1).toUpperCase() + a.substr
ing(1)).join(''); |
| 191 } |
| 192 } |
| 193 |
| 194 function findCSSFiles() { |
| 195 let cssFilesMapping = new Map(); |
| 196 for (let fileObj of JS_FILES_MAPPING) |
| 197 cssFilesMapping.set(fileObj.file, scrapeCSSFile(fileObj.file)); |
| 198 |
| 199 |
| 200 function scrapeCSSFile(filePath) { |
| 201 let cssFiles = new Set(); |
| 202 const fullPath = path.resolve(FRONTEND_PATH, filePath); |
| 203 let content = fs.readFileSync(fullPath).toString(); |
| 204 let lines = content.split('\n'); |
| 205 for (let line of lines) { |
| 206 let match = line.match(/'(.+\.css)'/); |
| 207 if (!match) |
| 208 continue; |
| 209 let matchPath = match[1]; |
| 210 cssFiles.add(path.basename(path.resolve(FRONTEND_PATH, matchPath))); |
| 211 } |
| 212 return cssFiles; |
| 213 } |
| 214 |
| 215 return cssFilesMapping; |
| 216 } |
| 217 |
| 218 function calculateIdentifiers() { |
| 219 const identifiersByFile = new Map(); |
| 220 for (let fileObj of JS_FILES_MAPPING) { |
| 221 const fullPath = path.resolve(FRONTEND_PATH, fileObj.file); |
| 222 let content = fs.readFileSync(fullPath).toString(); |
| 223 identifiersByFile.set(fileObj.file, scrapeIdentifiers(content, fileObj)); |
| 224 } |
| 225 return identifiersByFile; |
| 226 |
| 227 function scrapeIdentifiers(content, fileObj) { |
| 228 let identifiers = []; |
| 229 let lines = content.split('\n'); |
| 230 for (let line of lines) { |
| 231 let match = line.match(new RegExp(`^([a-z_A-Z0-9\.]+)\\s=`)) || line.match
(new RegExp(`^([a-z_A-Z0-9\.]+);`)); |
| 232 if (!match) |
| 233 continue; |
| 234 let name = match[1]; |
| 235 |
| 236 var currentModule = fileObj.file.split('/')[0]; |
| 237 if (name.split('.')[0] !== mapModuleToNamespace(currentModule)) |
| 238 console.log(`POSSIBLE ISSUE: identifier: ${name} found in ${currentModul
e}`); |
| 239 else |
| 240 identifiers.push(name); |
| 241 } |
| 242 return identifiers; |
| 243 } |
| 244 } |
| 245 |
| 246 function moveFiles(cssFilesMapping) { |
| 247 for (let fileObj of JS_FILES_MAPPING) { |
| 248 let sourceFilePath = path.resolve(FRONTEND_PATH, fileObj.file); |
| 249 let targetFilePath = getMappedFilePath(fileObj); |
| 250 let moduleDir = path.resolve(targetFilePath, '..'); |
| 251 if (!fs.existsSync(moduleDir)) |
| 252 fs.mkdirSync(moduleDir); |
| 253 |
| 254 move(sourceFilePath, targetFilePath); |
| 255 if (cssFilesMapping.has(fileObj.file)) { |
| 256 cssFilesMapping.get(fileObj.file).forEach((file) => { |
| 257 let module = fileObj.new || fileObj.existing; |
| 258 move(path.resolve(FRONTEND_PATH, fileObj.file.split('/')[0], file), path
.resolve(FRONTEND_PATH, module, file)); |
| 259 }); |
| 260 } |
| 261 } |
| 262 |
| 263 function move(sourceFilePath, targetFilePath) { |
| 264 try { |
| 265 fs.writeFileSync(targetFilePath, fs.readFileSync(sourceFilePath)); |
| 266 fs.unlinkSync(sourceFilePath); |
| 267 } catch (err) { |
| 268 console.log(`error moving ${sourceFilePath} -> ${targetFilePath}`); |
| 269 } |
| 270 } |
| 271 |
| 272 function getMappedFilePath(fileObj) { |
| 273 let components = fileObj.file.split('/'); |
| 274 components[0] = fileObj.existing || fileObj.new; |
| 275 return path.resolve(FRONTEND_PATH, components.join('/')); |
| 276 } |
| 277 } |
| 278 |
| 279 function updateBuildGNFile(cssFilesMapping, newModuleSet) { |
| 280 let content = fs.readFileSync(BUILD_GN_PATH).toString(); |
| 281 let newSourcesToAdd = []; |
| 282 let partialPathMapping = calculatePartialPathMapping(); |
| 283 for (let module of MODULES_TO_REMOVE) { |
| 284 partialPathMapping.set(`"front_end/${module}/module.json",\n`, ''); |
| 285 partialPathMapping.set(`"$resources_out_dir/${module}/${module}_module.js",\
n`, ''); |
| 286 } |
| 287 const newNonAutostartModules = [...newModuleSet] |
| 288 .filter(module => !MODULE_MAPPING[module].a
utostart) |
| 289 .map(module => `"$resources_out_dir/${modul
e}/${module}_module.js",`); |
| 290 |
| 291 let newContent = addContentToLinesInSortedOrder({ |
| 292 content, |
| 293 startLine: '# this contains non-autostart non-remote modules only.', |
| 294 endLine: ']', |
| 295 linesToInsert: newNonAutostartModules, |
| 296 }); |
| 297 |
| 298 for (let pair of partialPathMapping.entries()) |
| 299 newContent = newContent.replace(pair[0], pair[1]); |
| 300 |
| 301 newContent = addContentToLinesInSortedOrder({ |
| 302 content: newContent, |
| 303 startLine: 'all_devtools_files = [', |
| 304 endLine: ']', |
| 305 linesToInsert: newSourcesToAdd, |
| 306 }); |
| 307 |
| 308 fs.writeFileSync(BUILD_GN_PATH, newContent); |
| 309 |
| 310 function calculatePartialPathMapping() { |
| 311 let partialPathMapping = new Map(); |
| 312 for (let fileObj of JS_FILES_MAPPING) { |
| 313 let components = fileObj.file.split('/'); |
| 314 let sourceModule = components[0]; |
| 315 let targetModule = fileObj.existing || fileObj.new; |
| 316 components[0] = targetModule; |
| 317 partialPathMapping.set(`"front_end/${fileObj.file}",\n`, ''); |
| 318 newSourcesToAdd.push(`"front_end/${components.join('/')}",`); |
| 319 if (cssFilesMapping.has(fileObj.file)) { |
| 320 for (let cssFile of cssFilesMapping.get(fileObj.file)) { |
| 321 partialPathMapping.set(`"front_end/${sourceModule}/${cssFile}",\n`, ''
); |
| 322 newSourcesToAdd.push(`"front_end/${targetModule}/${cssFile}",`); |
| 323 } |
| 324 } |
| 325 } |
| 326 return partialPathMapping; |
| 327 } |
| 328 |
| 329 function top(array) { |
| 330 return array[array.length - 1]; |
| 331 } |
| 332 |
| 333 function addContentToLinesInSortedOrder({content, startLine, endLine, linesToI
nsert}) { |
| 334 let lines = content.split('\n'); |
| 335 let seenStartLine = false; |
| 336 let contentStack = linesToInsert.sort((a, b) => a.toLowerCase().localeCompar
e(b.toLowerCase())).reverse(); |
| 337 for (var i = 0; i < lines.length; i++) { |
| 338 let line = lines[i].trim(); |
| 339 let nextLine = lines[i + 1].trim(); |
| 340 if (line === startLine) |
| 341 seenStartLine = true; |
| 342 |
| 343 if (line === endLine && seenStartLine) |
| 344 break; |
| 345 |
| 346 if (!seenStartLine) |
| 347 continue; |
| 348 |
| 349 const nextContent = top(contentStack) ? top(contentStack).toLowerCase() :
''; |
| 350 if ((line === startLine || nextContent > line.toLowerCase()) && |
| 351 (nextLine === endLine || nextContent < nextLine.toLowerCase())) |
| 352 lines.splice(i + 1, 0, contentStack.pop()); |
| 353 } |
| 354 if (contentStack.length) |
| 355 lines.splice(i, 0, ...contentStack); |
| 356 return lines.join('\n'); |
| 357 } |
| 358 } |
| 359 |
| 360 function mapIdentifiers(identifiersByFile, cssFilesMapping) { |
| 361 const filesToTargetModule = new Map(); |
| 362 for (let fileObj of JS_FILES_MAPPING) |
| 363 filesToTargetModule.set(fileObj.file, fileObj.existing || fileObj.new); |
| 364 |
| 365 |
| 366 const map = new Map(); |
| 367 for (let [file, identifiers] of identifiersByFile) { |
| 368 let targetModule = filesToTargetModule.get(file); |
| 369 for (let identifier of identifiers) { |
| 370 let components = identifier.split('.'); |
| 371 components[0] = mapModuleToNamespace(targetModule); |
| 372 let newIdentifier = components.join('.'); |
| 373 map.set(identifier, newIdentifier); |
| 374 } |
| 375 } |
| 376 for (let [jsFile, cssFiles] of cssFilesMapping) { |
| 377 let fileObj = JS_FILES_MAPPING.filter(f => f.file === jsFile)[0]; |
| 378 let sourceModule = fileObj.file.split('/')[0]; |
| 379 let targetModule = fileObj.existing || fileObj.new; |
| 380 for (let cssFile of cssFiles) { |
| 381 let key = `${sourceModule}/${cssFile}`; |
| 382 let value = `${targetModule}/${cssFile}`; |
| 383 map.set(key, value); |
| 384 } |
| 385 } |
| 386 return map; |
| 387 } |
| 388 |
| 389 function renameIdentifiers(identifierMap) { |
| 390 walkSync('front_end', write, true); |
| 391 |
| 392 walkSync('../../LayoutTests/http/tests/inspector', write, false); |
| 393 walkSync('../../LayoutTests/http/tests/inspector-enabled', write, false); |
| 394 walkSync('../../LayoutTests/http/tests/inspector-protocol', write, false); |
| 395 walkSync('../../LayoutTests/http/tests/inspector-unit', write, false); |
| 396 walkSync('../../LayoutTests/inspector', write, false); |
| 397 walkSync('../../LayoutTests/inspector-enabled', write, false); |
| 398 walkSync('../../LayoutTests/inspector-protocol', write, false); |
| 399 |
| 400 function walkSync(currentDirPath, process, json) { |
| 401 fs.readdirSync(currentDirPath).forEach(function(name) { |
| 402 let filePath = path.join(currentDirPath, name); |
| 403 let stat = fs.statSync(filePath); |
| 404 if (stat.isFile() && (filePath.endsWith('.js') || filePath.endsWith('.html
') || filePath.endsWith('.xhtml') || |
| 405 filePath.endsWith('-expected.txt') || (json && fileP
ath.endsWith('.json')))) { |
| 406 if (filePath.includes('ExtensionAPI.js')) |
| 407 return; |
| 408 if (filePath.includes('externs.js')) |
| 409 return; |
| 410 if (filePath.includes('eslint') || filePath.includes('lighthouse-backgro
und.js') || filePath.includes('/cm/') || |
| 411 filePath.includes('/xterm.js/') || filePath.includes('/acorn/') || f
ilePath.includes('/gonzales-scss')) |
| 412 return; |
| 413 if (filePath.includes('/cm_modes/') && !filePath.includes('DefaultCodeMi
rror') && |
| 414 !filePath.includes('module.json')) |
| 415 return; |
| 416 process(filePath); |
| 417 } else if (stat.isDirectory()) { |
| 418 walkSync(filePath, process, json); |
| 419 } |
| 420 }); |
| 421 } |
| 422 |
| 423 function write(filePath) { |
| 424 let content = fs.readFileSync(filePath).toString(); |
| 425 let newContent = content; |
| 426 for (let key of identifierMap.keys()) { |
| 427 let originalIdentifier = key; |
| 428 let newIdentifier = identifierMap.get(key); |
| 429 newContent = newContent.replaceAll(originalIdentifier, newIdentifier); |
| 430 } |
| 431 // one-off |
| 432 if (filePath.includes('LayoutTests/http/tests/inspector-unit/filtered-item-s
election-dialog-filtering.js')) |
| 433 newContent = newContent.replaceAll('ui_lazy', 'quick_open'); |
| 434 // if (filePath.includes('LayoutTests/inspector/components/cookies-table.htm
l')) |
| 435 // newContent = newContent.replaceAll('components_lazy', 'cookie_table'); |
| 436 // if (filePath.includes('LayoutTests/inspector/components/datagrid-autosize
.html')) |
| 437 // newContent = newContent.replaceAll('ui_lazy', 'data_grid'); |
| 438 // if (filePath.includes('LayoutTests/inspector/components/datagrid-test.js'
)) |
| 439 // newContent = newContent.replaceAll('ui_lazy', 'data_grid'); |
| 440 |
| 441 if (content !== newContent) |
| 442 fs.writeFileSync(filePath, newContent); |
| 443 } |
| 444 } |
| 445 |
| 446 function removeFromExistingModuleDescriptors(modules, identifierMap, cssFilesMap
ping) { |
| 447 let extensionMap = new Map(); |
| 448 let moduleFileMap = new Map(); |
| 449 |
| 450 for (let fileObj of JS_FILES_MAPPING) { |
| 451 let components = fileObj.file.split('/'); |
| 452 let module = components[0]; |
| 453 let fileName = components[1]; |
| 454 |
| 455 if (!moduleFileMap.get(module)) |
| 456 moduleFileMap.set(module, []); |
| 457 |
| 458 moduleFileMap.set(module, moduleFileMap.get(module).concat(fileName)); |
| 459 } |
| 460 |
| 461 for (let module of modules) { |
| 462 let moduleJSONPath = path.resolve(FRONTEND_PATH, module, 'module.json'); |
| 463 let content = fs.readFileSync(moduleJSONPath).toString(); |
| 464 let moduleObj = parseJSON(content); |
| 465 let removedScripts = removeScripts(moduleObj, module); |
| 466 removeResources(moduleObj, removedScripts); |
| 467 removeExtensions(moduleObj); |
| 468 fs.writeFileSync(moduleJSONPath, stringifyJSON(moduleObj)); |
| 469 } |
| 470 |
| 471 return extensionMap; |
| 472 |
| 473 function removeScripts(moduleObj, module) { |
| 474 let remainingScripts = []; |
| 475 let removedScripts = []; |
| 476 let moduleFiles = moduleFileMap.get(module); |
| 477 for (let script of moduleObj.scripts) { |
| 478 if (!moduleFiles.includes(script)) |
| 479 remainingScripts.push(script); |
| 480 else |
| 481 removedScripts.push(module + '/' + script); |
| 482 } |
| 483 moduleObj.scripts = remainingScripts; |
| 484 return removedScripts; |
| 485 } |
| 486 |
| 487 function removeResources(moduleObj, removedScripts) { |
| 488 if (!moduleObj.resources) |
| 489 return; |
| 490 let remainingResources = []; |
| 491 let removedResources = new Set(); |
| 492 for (let script of removedScripts) |
| 493 removedResources = removedResources.union(cssFilesMapping.get(script)); |
| 494 |
| 495 |
| 496 for (let resource of moduleObj.resources) { |
| 497 if (!removedResources.has(resource)) |
| 498 remainingResources.push(resource); |
| 499 } |
| 500 moduleObj.resources = remainingResources; |
| 501 } |
| 502 |
| 503 function removeExtensions(moduleObj) { |
| 504 if (!moduleObj.extensions) |
| 505 return; |
| 506 let remainingExtensions = []; |
| 507 for (let extension of moduleObj.extensions) { |
| 508 if (!objectIncludesIdentifier(extension)) |
| 509 remainingExtensions.push(extension); |
| 510 else |
| 511 extensionMap.set(objectIncludesIdentifier(extension), extension); |
| 512 } |
| 513 moduleObj.extensions = remainingExtensions; |
| 514 } |
| 515 |
| 516 function objectIncludesIdentifier(object) { |
| 517 for (let key in object) { |
| 518 let value = object[key]; |
| 519 if (identifierMap.has(value)) |
| 520 return value; |
| 521 } |
| 522 return false; |
| 523 } |
| 524 } |
| 525 |
| 526 function createNewModuleDescriptors(extensionMap, cssFilesMapping, identifiersBy
File, targetToOriginalFilesMap) { |
| 527 let filesByNewModule = getFilesByNewModule(); |
| 528 |
| 529 for (let module of filesByNewModule.keys()) { |
| 530 let moduleObj = {}; |
| 531 |
| 532 let scripts = getModuleScripts(module); |
| 533 let extensions = getModuleExtensions(scripts, module); |
| 534 if (extensions.length) |
| 535 moduleObj.extensions = extensions; |
| 536 |
| 537 moduleObj.dependencies = DEPENDENCIES_BY_MODULE[module]; |
| 538 |
| 539 moduleObj.scripts = scripts; |
| 540 |
| 541 let resources = getModuleResources(moduleObj.scripts, module); |
| 542 if (resources.length) |
| 543 moduleObj.resources = resources; |
| 544 |
| 545 let moduleJSONPath = path.resolve(FRONTEND_PATH, module, 'module.json'); |
| 546 fs.writeFileSync(moduleJSONPath, stringifyJSON(moduleObj)); |
| 547 } |
| 548 |
| 549 function getFilesByNewModule() { |
| 550 let filesByNewModule = new Map(); |
| 551 for (let fileObj of JS_FILES_MAPPING) { |
| 552 if (!fileObj.new) |
| 553 continue; |
| 554 if (!filesByNewModule.has(fileObj.new)) |
| 555 filesByNewModule.set(fileObj.new, []); |
| 556 |
| 557 filesByNewModule.set(fileObj.new, filesByNewModule.get(fileObj.new).concat
([fileObj.file])); |
| 558 } |
| 559 return filesByNewModule; |
| 560 } |
| 561 |
| 562 function getModuleScripts(module) { |
| 563 return filesByNewModule.get(module).map((file) => file.split('/')[1]); |
| 564 } |
| 565 |
| 566 function getModuleResources(scripts, module) { |
| 567 let resources = []; |
| 568 scripts.map(script => module + '/' + script).forEach((script) => { |
| 569 script = targetToOriginalFilesMap.get(script); |
| 570 if (!cssFilesMapping.has(script)) |
| 571 return; |
| 572 |
| 573 resources = resources.concat([...cssFilesMapping.get(script)]); |
| 574 }); |
| 575 return resources; |
| 576 } |
| 577 |
| 578 function getModuleExtensions(scripts, module) { |
| 579 let extensions = []; |
| 580 let identifiers = |
| 581 scripts.map(script => module + '/' + script) |
| 582 .reduce((acc, file) => acc.concat(identifiersByFile.get(targetToOrig
inalFilesMap.get(file))), []); |
| 583 for (let identifier of identifiers) { |
| 584 if (extensionMap.has(identifier)) |
| 585 extensions.push(extensionMap.get(identifier)); |
| 586 } |
| 587 return extensions; |
| 588 } |
| 589 } |
| 590 |
| 591 function calculateFilesByModuleType(type) { |
| 592 let filesByNewModule = new Map(); |
| 593 for (let fileObj of JS_FILES_MAPPING) { |
| 594 if (!fileObj[type]) |
| 595 continue; |
| 596 if (!filesByNewModule.has(fileObj[type])) |
| 597 filesByNewModule.set(fileObj[type], []); |
| 598 |
| 599 filesByNewModule.set(fileObj[type], filesByNewModule.get(fileObj[type]).conc
at([fileObj.file])); |
| 600 } |
| 601 return filesByNewModule; |
| 602 } |
| 603 |
| 604 function updateExistingModuleDescriptors(extensionMap, cssFilesMapping, identifi
ersByFile, targetToOriginalFilesMap) { |
| 605 let filesByExistingModule = calculateFilesByModuleType('existing'); |
| 606 for (let module of filesByExistingModule.keys()) { |
| 607 let moduleJSONPath = path.resolve(FRONTEND_PATH, module, 'module.json'); |
| 608 let content = fs.readFileSync(moduleJSONPath).toString(); |
| 609 let moduleObj = parseJSON(content); |
| 610 |
| 611 let scripts = getModuleScripts(module); |
| 612 let existingExtensions = moduleObj.extensions || []; |
| 613 let extensions = existingExtensions.concat(getModuleExtensions(scripts, modu
le)); |
| 614 if (extensions.length) |
| 615 moduleObj.extensions = extensions; |
| 616 |
| 617 moduleObj.scripts = moduleObj.scripts.concat(scripts); |
| 618 |
| 619 let existingResources = moduleObj.resources || []; |
| 620 let resources = existingResources.concat(getModuleResources(scripts, module)
); |
| 621 if (resources.length) |
| 622 moduleObj.resources = resources; |
| 623 |
| 624 fs.writeFileSync(moduleJSONPath, stringifyJSON(moduleObj)); |
| 625 } |
| 626 |
| 627 |
| 628 function getModuleScripts(module) { |
| 629 return filesByExistingModule.get(module).map((file) => file.split('/')[1]); |
| 630 } |
| 631 |
| 632 function getModuleResources(scripts, module) { |
| 633 let resources = []; |
| 634 scripts.map(script => module + '/' + script).forEach((script) => { |
| 635 script = targetToOriginalFilesMap.get(script); |
| 636 if (!cssFilesMapping.has(script)) |
| 637 return; |
| 638 |
| 639 resources = resources.concat([...cssFilesMapping.get(script)]); |
| 640 }); |
| 641 return resources; |
| 642 } |
| 643 |
| 644 function getModuleExtensions(scripts, module) { |
| 645 let extensions = []; |
| 646 let identifiers = |
| 647 scripts.map(script => module + '/' + script) |
| 648 .reduce((acc, file) => acc.concat(identifiersByFile.get(targetToOrig
inalFilesMap.get(file))), []); |
| 649 for (let identifier of identifiers) { |
| 650 if (extensionMap.has(identifier)) |
| 651 extensions.push(extensionMap.get(identifier)); |
| 652 } |
| 653 return extensions; |
| 654 } |
| 655 } |
| 656 |
| 657 function addDependenciesToDescriptors() { |
| 658 for (let module of getModules()) { |
| 659 let moduleJSONPath = path.resolve(FRONTEND_PATH, module, 'module.json'); |
| 660 let content = fs.readFileSync(moduleJSONPath).toString(); |
| 661 let moduleObj = parseJSON(content); |
| 662 |
| 663 let existingDependencies = moduleObj.dependencies || []; |
| 664 let dependencies = existingDependencies.concat(getModuleDependencies(module)
) |
| 665 .filter((depModule) => !MODULES_TO_REMOVE.includes(de
pModule)) |
| 666 .filter((depModule) => !(REMOVE_DEPENDENCIES_BY_EXIST
ING_MODULES[module] || []).includes(depModule)); |
| 667 let newDependenciesForExistingModule = NEW_DEPENDENCIES_BY_EXISTING_MODULES[
module]; |
| 668 if (newDependenciesForExistingModule) |
| 669 dependencies = dependencies.concat(newDependenciesForExistingModule); |
| 670 if (dependencies.length) |
| 671 moduleObj.dependencies = dependencies; |
| 672 let newStringified = stringifyJSON(moduleObj); |
| 673 if (stringifyJSON(moduleObj) !== stringifyJSON(parseJSON(content))) |
| 674 fs.writeFileSync(moduleJSONPath, newStringified); |
| 675 } |
| 676 |
| 677 function getModuleDependencies(existingModule) { |
| 678 let newDeps = []; |
| 679 for (let newModule in DEPENDENTS_BY_MODULE) { |
| 680 let dependents = DEPENDENTS_BY_MODULE[newModule]; |
| 681 if (dependents.includes(existingModule)) |
| 682 newDeps.push(newModule); |
| 683 } |
| 684 return newDeps; |
| 685 } |
| 686 } |
| 687 |
| 688 function updateApplicationDescriptor(descriptorFileName, newModuleSet) { |
| 689 let descriptorPath = path.join(FRONTEND_PATH, descriptorFileName); |
| 690 let newModules = [...newModuleSet].filter(m => APPLICATIONS_BY_MODULE[m].inclu
des(descriptorFileName)); |
| 691 let includeNewModules = (acc, line) => { |
| 692 if (line.includes('{') && line.endsWith('}')) { |
| 693 line += ','; |
| 694 acc.push(line); |
| 695 return acc.concat(newModules.map((m, i) => { |
| 696 // Need spacing to preserve indentation |
| 697 let string; |
| 698 if (MODULE_MAPPING[m].autostart) |
| 699 string = ` { "name": "${m}", "type": "autostart"}`; |
| 700 else |
| 701 string = ` { "name": "${m}" }`; |
| 702 if (i !== newModules.length - 1) |
| 703 string += ','; |
| 704 return string; |
| 705 })); |
| 706 } |
| 707 return acc.concat([line]); |
| 708 }; |
| 709 let removeModules = (acc, line) => MODULES_TO_REMOVE.every(m => !line.includes
(m)) ? acc.concat([line]) : acc; |
| 710 let lines = |
| 711 fs.readFileSync(descriptorPath).toString().split('\n').reduce(includeNewMo
dules, []).reduce(removeModules, []); |
| 712 fs.writeFileSync(descriptorPath, lines.join('\n')); |
| 713 } |
| 714 |
| 715 function getModules() { |
| 716 return fs.readdirSync(FRONTEND_PATH).filter(function(file) { |
| 717 return fs.statSync(path.join(FRONTEND_PATH, file)).isDirectory() && |
| 718 utils.isFile(path.join(FRONTEND_PATH, file, 'module.json')); |
| 719 }); |
| 720 } |
| 721 |
| 722 function parseJSON(string) { |
| 723 return JSON.parse(string); |
| 724 } |
| 725 |
| 726 function stringifyJSON(obj) { |
| 727 return unicodeEscape(JSON.stringify(obj, null, 4) + '\n'); |
| 728 } |
| 729 |
| 730 // http://stackoverflow.com/questions/7499473/need-to-escape-non-ascii-character
s-in-javascript |
| 731 function unicodeEscape(string) { |
| 732 function padWithLeadingZeros(string) { |
| 733 return new Array(5 - string.length).join("0") + string; |
| 734 } |
| 735 |
| 736 function unicodeCharEscape(charCode) { |
| 737 return "\\u" + padWithLeadingZeros(charCode.toString(16)); |
| 738 } |
| 739 |
| 740 return string.split("") |
| 741 .map(function (char) { |
| 742 var charCode = char.charCodeAt(0); |
| 743 return charCode > 127 ? unicodeCharEscape(charCode) : char; |
| 744 }) |
| 745 .join(""); |
| 746 } |
| 747 |
| 748 if (require.main === module) |
| 749 extractModule(); |
OLD | NEW |