Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(101)

Unified Diff: third_party/WebKit/Source/devtools/scripts/extract_module/extract_module.js

Issue 2608043002: DevTools: extract modules (with extensions) (Closed)
Patch Set: fix externs (PerfUI) Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/devtools/scripts/extract_module/extract_module.js
diff --git a/third_party/WebKit/Source/devtools/scripts/extract_module/extract_module.js b/third_party/WebKit/Source/devtools/scripts/extract_module/extract_module.js
new file mode 100644
index 0000000000000000000000000000000000000000..888edbcb1c8f13f882cff0e3c9e158b11df8b6e0
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/scripts/extract_module/extract_module.js
@@ -0,0 +1,749 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+'use strict';
+const fs = require('fs');
+const path = require('path');
+
+const utils = require('../utils');
+
+const FRONTEND_PATH = path.resolve(__dirname, '..', '..', 'front_end');
+const BUILD_GN_PATH = path.resolve(__dirname, '..', '..', 'BUILD.gn');
+const SPECIAL_CASE_NAMESPACES_PATH = path.resolve(__dirname, '..', 'special_case_namespaces.json');
+
+const APPLICATION_DESCRIPTORS = [
+ 'inspector.json',
+ 'unit_test_runner.json',
+];
+
+// Replace based on specified transformation
+
+const MODULES_TO_REMOVE = []; // ['components_lazy', 'ui_lazy'];
+
+// NOTE: Extensions-only change
+const FILES_AFFECTING_EXTENSIONS = [
+ 'components_lazy/LineLevelProfile.js',
+ 'components_lazy/GCActionDelegate.js',
+ 'components/RequestAppBannerActionDelegate.js',
+ 'ui_lazy/CommandMenu.js',
+];
+
+const JS_FILES_MAPPING = [
+ // {file: 'components_lazy/CookiesTable.js', new: 'cookie_table'},
+ // {file: 'ui/BezierEditor.js', new: 'inline_editor'},
+ // {file: 'ui/BezierUI.js', new: 'inline_editor'},
+ // {file: 'ui/ColorSwatch.js', new: 'inline_editor'},
+ // {file: 'ui/CSSShadowEditor.js', new: 'inline_editor'},
+ // {file: 'ui/SwatchPopoverHelper.js', new: 'inline_editor'},
+ // {file: 'components/Spectrum.js', new: 'color_picker'},
+ //
+ // // Cannot extract dom_ui because of cyclic dependency with components
+ // // {file: 'components/DOMPresentationUtils.js', new: 'dom_ui'},
+ // {file: 'components/ExecutionContextSelector.js', existing: 'main'},
+ // {file: 'components_lazy/FilmStripModel.js', existing: 'sdk'},
+ //
+ // {file: 'components_lazy/FilmStripView.js', new: 'perf_ui'},
+ {file: 'components_lazy/GCActionDelegate.js', existing: 'main'},
+ {file: 'components_lazy/LineLevelProfile.js', new: 'perf_ui'},
+ {file: 'components/RequestAppBannerActionDelegate.js', existing: 'main'},
+ // {file: 'components/ShortcutsScreen.js', existing: 'ui'},
+ {file: 'ui_lazy/FilteredListWidget.js', new: 'quick_open'},
+ {file: 'ui_lazy/CommandMenu.js', new: 'quick_open'},
+ // {file: 'ui_lazy/DataGrid.js', new: 'data_grid'},
+ // {file: 'ui_lazy/ViewportDataGrid.js', new: 'data_grid'},
+ // {file: 'ui_lazy/SortableDataGrid.js', new: 'data_grid'},
+ // {file: 'ui_lazy/ShowMoreDataGridNode.js', new: 'data_grid'},
+ // {file: 'ui_lazy/ChartViewport.js', new: 'perf_ui'},
+ // {file: 'ui_lazy/FlameChart.js', new: 'perf_ui'},
+ // {file: 'ui_lazy/OverviewGrid.js', new: 'perf_ui'},
+ // {file: 'ui_lazy/PieChart.js', new: 'perf_ui'},
+ // {file: 'ui_lazy/TimelineGrid.js', new: 'perf_ui'},
+ // {file: 'ui_lazy/TimelineOverviewPane.js', new: 'perf_ui'},
+ {file: 'sources/UISourceCodeFrame.js', existing: 'source_frame'},
+ {file: 'sources/SourceCodeDiff.js', existing: 'source_frame'},
+];
+
+const MODULE_MAPPING = {
+ // cookie_table: {
+ // dependencies: ['ui', 'sdk', 'data_grid'],
+ // dependents: ['resources', 'network'],
+ // applications: ['inspector.json'],
+ // autostart: false,
+ // },
+ // inline_editor: {
+ // dependencies: ['ui'],
+ // dependents: ['sources', 'elements', 'resources'],
+ // applications: ['inspector.json', 'unit_test_runner.json'],
+ // autostart: false,
+ // },
+ // color_picker: {
+ // dependencies: ['ui', 'sdk'],
+ // dependents: ['sources', 'elements'],
+ // applications: ['inspector.json'],
+ // autostart: false,
+ // },
+ perf_ui: {
+ dependencies: ['ui', 'sdk', 'bindings', 'source_frame', 'text_editor'],
+ dependents: ['network', 'timeline', 'profiler', 'layer_viewer'],
+ applications: ['inspector.json'],
+ autostart: false,
+ },
+ quick_open: {
+ dependencies: ['ui', 'diff'],
+ dependents: ['sources'],
+ applications: ['inspector.json', 'unit_test_runner.json'],
+ autostart: false,
+ },
+ // data_grid: {
+ // dependencies: ['ui'],
+ // dependents: ['network', 'profiler', 'resources', 'console', 'timeline'],
+ // applications: ['inspector.json', 'unit_test_runner.json'],
+ // autostart: false,
+ // },
+};
+
+const NEW_DEPENDENCIES_BY_EXISTING_MODULES = {
+ // resources: ['components'],
+ source_frame: ['persistence', 'diff'],
+ timeline: ['extensions'],
+ css_tracker: ['source_frame'],
+};
+
+const REMOVE_DEPENDENCIES_BY_EXISTING_MODULES = {
+ css_tracker: ['sources'],
+};
+
+const DEPENDENCIES_BY_MODULE = Object.keys(MODULE_MAPPING).reduce((acc, module) => {
+ acc[module] = MODULE_MAPPING[module].dependencies;
+ return acc;
+}, {});
+
+const APPLICATIONS_BY_MODULE = Object.keys(MODULE_MAPPING).reduce((acc, module) => {
+ acc[module] = MODULE_MAPPING[module].applications;
+ return acc;
+}, {});
+
+const DEPENDENTS_BY_MODULE = Object.keys(MODULE_MAPPING).reduce((acc, module) => {
+ acc[module] = MODULE_MAPPING[module].dependents;
+ return acc;
+}, {});
+
+function extractModule() {
+ const modules = new Set();
+ for (let fileObj of JS_FILES_MAPPING) {
+ let moduleName = fileObj.file.split('/')[0];
+ modules.add(moduleName);
+ }
+ const newModuleSet = JS_FILES_MAPPING.reduce((acc, file) => file.new ? acc.add(file.new) : acc, new Set());
+ const targetToOriginalFilesMap = JS_FILES_MAPPING.reduce((acc, f) => {
+ let components = f.file.split('/');
+ components[0] = f.new || f.existing;
+ acc.set(components.join('/'), f.file);
+ return acc;
+ }, new Map());
+
+ const cssFilesMapping = findCSSFiles();
+ const identifiersByFile = calculateIdentifiers();
+ const identifierMap = mapIdentifiers(identifiersByFile, cssFilesMapping);
+ const extensionMap = removeFromExistingModuleDescriptors(modules, identifierMap, cssFilesMapping);
+
+ // Find out which files are moving extensions
+ for (let e of extensionMap.keys()) {
+ for (let [f, identifiers] of identifiersByFile) {
+ if (identifiers.includes(e))
+ console.log(`extension: ${e} in file: ${f}`);
+ }
+ }
+
+ moveFiles(cssFilesMapping);
+ createNewModuleDescriptors(extensionMap, cssFilesMapping, identifiersByFile, targetToOriginalFilesMap);
+ updateExistingModuleDescriptors(extensionMap, cssFilesMapping, identifiersByFile, targetToOriginalFilesMap);
+ addDependenciesToDescriptors();
+ renameIdentifiers(identifierMap);
+ updateBuildGNFile(cssFilesMapping, newModuleSet);
+ for (let descriptor of APPLICATION_DESCRIPTORS)
+ updateApplicationDescriptor(descriptor, newModuleSet);
+
+ for (let m of MODULES_TO_REMOVE) {
+ utils.removeRecursive(path.resolve(FRONTEND_PATH, m));
+ }
+}
+
+String.prototype.replaceAll = function(search, replacement) {
+ let target = this;
+ return target.replace(new RegExp('\\b' + search + '\\b', 'g'), replacement);
+};
+
+Set.prototype.union = function(setB) {
+ let union = new Set(this);
+ for (let elem of setB)
+ union.add(elem);
+
+ return union;
+};
+
+function mapModuleToNamespace(module) {
+ const specialCases = require(SPECIAL_CASE_NAMESPACES_PATH);
+ return specialCases[module] || toCamelCase(module);
+
+ function toCamelCase(module) {
+ return module.split('_').map(a => a.substring(0, 1).toUpperCase() + a.substring(1)).join('');
+ }
+}
+
+function findCSSFiles() {
+ let cssFilesMapping = new Map();
+ for (let fileObj of JS_FILES_MAPPING)
+ cssFilesMapping.set(fileObj.file, scrapeCSSFile(fileObj.file));
+
+
+ function scrapeCSSFile(filePath) {
+ let cssFiles = new Set();
+ const fullPath = path.resolve(FRONTEND_PATH, filePath);
+ let content = fs.readFileSync(fullPath).toString();
+ let lines = content.split('\n');
+ for (let line of lines) {
+ let match = line.match(/'(.+\.css)'/);
+ if (!match)
+ continue;
+ let matchPath = match[1];
+ cssFiles.add(path.basename(path.resolve(FRONTEND_PATH, matchPath)));
+ }
+ return cssFiles;
+ }
+
+ return cssFilesMapping;
+}
+
+function calculateIdentifiers() {
+ const identifiersByFile = new Map();
+ for (let fileObj of JS_FILES_MAPPING) {
+ const fullPath = path.resolve(FRONTEND_PATH, fileObj.file);
+ let content = fs.readFileSync(fullPath).toString();
+ identifiersByFile.set(fileObj.file, scrapeIdentifiers(content, fileObj));
+ }
+ return identifiersByFile;
+
+ function scrapeIdentifiers(content, fileObj) {
+ let identifiers = [];
+ let lines = content.split('\n');
+ for (let line of lines) {
+ let match = line.match(new RegExp(`^([a-z_A-Z0-9\.]+)\\s=`)) || line.match(new RegExp(`^([a-z_A-Z0-9\.]+);`));
+ if (!match)
+ continue;
+ let name = match[1];
+
+ var currentModule = fileObj.file.split('/')[0];
+ if (name.split('.')[0] !== mapModuleToNamespace(currentModule))
+ console.log(`POSSIBLE ISSUE: identifier: ${name} found in ${currentModule}`);
+ else
+ identifiers.push(name);
+ }
+ return identifiers;
+ }
+}
+
+function moveFiles(cssFilesMapping) {
+ for (let fileObj of JS_FILES_MAPPING) {
+ let sourceFilePath = path.resolve(FRONTEND_PATH, fileObj.file);
+ let targetFilePath = getMappedFilePath(fileObj);
+ let moduleDir = path.resolve(targetFilePath, '..');
+ if (!fs.existsSync(moduleDir))
+ fs.mkdirSync(moduleDir);
+
+ move(sourceFilePath, targetFilePath);
+ if (cssFilesMapping.has(fileObj.file)) {
+ cssFilesMapping.get(fileObj.file).forEach((file) => {
+ let module = fileObj.new || fileObj.existing;
+ move(path.resolve(FRONTEND_PATH, fileObj.file.split('/')[0], file), path.resolve(FRONTEND_PATH, module, file));
+ });
+ }
+ }
+
+ function move(sourceFilePath, targetFilePath) {
+ try {
+ fs.writeFileSync(targetFilePath, fs.readFileSync(sourceFilePath));
+ fs.unlinkSync(sourceFilePath);
+ } catch (err) {
+ console.log(`error moving ${sourceFilePath} -> ${targetFilePath}`);
+ }
+ }
+
+ function getMappedFilePath(fileObj) {
+ let components = fileObj.file.split('/');
+ components[0] = fileObj.existing || fileObj.new;
+ return path.resolve(FRONTEND_PATH, components.join('/'));
+ }
+}
+
+function updateBuildGNFile(cssFilesMapping, newModuleSet) {
+ let content = fs.readFileSync(BUILD_GN_PATH).toString();
+ let newSourcesToAdd = [];
+ let partialPathMapping = calculatePartialPathMapping();
+ for (let module of MODULES_TO_REMOVE) {
+ partialPathMapping.set(`"front_end/${module}/module.json",\n`, '');
+ partialPathMapping.set(`"$resources_out_dir/${module}/${module}_module.js",\n`, '');
+ }
+ const newNonAutostartModules = [...newModuleSet]
+ .filter(module => !MODULE_MAPPING[module].autostart)
+ .map(module => `"$resources_out_dir/${module}/${module}_module.js",`);
+
+ let newContent = addContentToLinesInSortedOrder({
+ content,
+ startLine: '# this contains non-autostart non-remote modules only.',
+ endLine: ']',
+ linesToInsert: newNonAutostartModules,
+ });
+
+ for (let pair of partialPathMapping.entries())
+ newContent = newContent.replace(pair[0], pair[1]);
+
+ newContent = addContentToLinesInSortedOrder({
+ content: newContent,
+ startLine: 'all_devtools_files = [',
+ endLine: ']',
+ linesToInsert: newSourcesToAdd,
+ });
+
+ fs.writeFileSync(BUILD_GN_PATH, newContent);
+
+ function calculatePartialPathMapping() {
+ let partialPathMapping = new Map();
+ for (let fileObj of JS_FILES_MAPPING) {
+ let components = fileObj.file.split('/');
+ let sourceModule = components[0];
+ let targetModule = fileObj.existing || fileObj.new;
+ components[0] = targetModule;
+ partialPathMapping.set(`"front_end/${fileObj.file}",\n`, '');
+ newSourcesToAdd.push(`"front_end/${components.join('/')}",`);
+ if (cssFilesMapping.has(fileObj.file)) {
+ for (let cssFile of cssFilesMapping.get(fileObj.file)) {
+ partialPathMapping.set(`"front_end/${sourceModule}/${cssFile}",\n`, '');
+ newSourcesToAdd.push(`"front_end/${targetModule}/${cssFile}",`);
+ }
+ }
+ }
+ return partialPathMapping;
+ }
+
+ function top(array) {
+ return array[array.length - 1];
+ }
+
+ function addContentToLinesInSortedOrder({content, startLine, endLine, linesToInsert}) {
+ let lines = content.split('\n');
+ let seenStartLine = false;
+ let contentStack = linesToInsert.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())).reverse();
+ for (var i = 0; i < lines.length; i++) {
+ let line = lines[i].trim();
+ let nextLine = lines[i + 1].trim();
+ if (line === startLine)
+ seenStartLine = true;
+
+ if (line === endLine && seenStartLine)
+ break;
+
+ if (!seenStartLine)
+ continue;
+
+ const nextContent = top(contentStack) ? top(contentStack).toLowerCase() : '';
+ if ((line === startLine || nextContent > line.toLowerCase()) &&
+ (nextLine === endLine || nextContent < nextLine.toLowerCase()))
+ lines.splice(i + 1, 0, contentStack.pop());
+ }
+ if (contentStack.length)
+ lines.splice(i, 0, ...contentStack);
+ return lines.join('\n');
+ }
+}
+
+function mapIdentifiers(identifiersByFile, cssFilesMapping) {
+ const filesToTargetModule = new Map();
+ for (let fileObj of JS_FILES_MAPPING)
+ filesToTargetModule.set(fileObj.file, fileObj.existing || fileObj.new);
+
+
+ const map = new Map();
+ for (let [file, identifiers] of identifiersByFile) {
+ let targetModule = filesToTargetModule.get(file);
+ for (let identifier of identifiers) {
+ let components = identifier.split('.');
+ components[0] = mapModuleToNamespace(targetModule);
+ let newIdentifier = components.join('.');
+ map.set(identifier, newIdentifier);
+ }
+ }
+ for (let [jsFile, cssFiles] of cssFilesMapping) {
+ let fileObj = JS_FILES_MAPPING.filter(f => f.file === jsFile)[0];
+ let sourceModule = fileObj.file.split('/')[0];
+ let targetModule = fileObj.existing || fileObj.new;
+ for (let cssFile of cssFiles) {
+ let key = `${sourceModule}/${cssFile}`;
+ let value = `${targetModule}/${cssFile}`;
+ map.set(key, value);
+ }
+ }
+ return map;
+}
+
+function renameIdentifiers(identifierMap) {
+ walkSync('front_end', write, true);
+
+ walkSync('../../LayoutTests/http/tests/inspector', write, false);
+ walkSync('../../LayoutTests/http/tests/inspector-enabled', write, false);
+ walkSync('../../LayoutTests/http/tests/inspector-protocol', write, false);
+ walkSync('../../LayoutTests/http/tests/inspector-unit', write, false);
+ walkSync('../../LayoutTests/inspector', write, false);
+ walkSync('../../LayoutTests/inspector-enabled', write, false);
+ walkSync('../../LayoutTests/inspector-protocol', write, false);
+
+ function walkSync(currentDirPath, process, json) {
+ fs.readdirSync(currentDirPath).forEach(function(name) {
+ let filePath = path.join(currentDirPath, name);
+ let stat = fs.statSync(filePath);
+ if (stat.isFile() && (filePath.endsWith('.js') || filePath.endsWith('.html') || filePath.endsWith('.xhtml') ||
+ filePath.endsWith('-expected.txt') || (json && filePath.endsWith('.json')))) {
+ if (filePath.includes('ExtensionAPI.js'))
+ return;
+ if (filePath.includes('externs.js'))
+ return;
+ if (filePath.includes('eslint') || filePath.includes('lighthouse-background.js') || filePath.includes('/cm/') ||
+ filePath.includes('/xterm.js/') || filePath.includes('/acorn/') || filePath.includes('/gonzales-scss'))
+ return;
+ if (filePath.includes('/cm_modes/') && !filePath.includes('DefaultCodeMirror') &&
+ !filePath.includes('module.json'))
+ return;
+ process(filePath);
+ } else if (stat.isDirectory()) {
+ walkSync(filePath, process, json);
+ }
+ });
+ }
+
+ function write(filePath) {
+ let content = fs.readFileSync(filePath).toString();
+ let newContent = content;
+ for (let key of identifierMap.keys()) {
+ let originalIdentifier = key;
+ let newIdentifier = identifierMap.get(key);
+ newContent = newContent.replaceAll(originalIdentifier, newIdentifier);
+ }
+ // one-off
+ if (filePath.includes('LayoutTests/http/tests/inspector-unit/filtered-item-selection-dialog-filtering.js'))
+ newContent = newContent.replaceAll('ui_lazy', 'quick_open');
+ // if (filePath.includes('LayoutTests/inspector/components/cookies-table.html'))
+ // newContent = newContent.replaceAll('components_lazy', 'cookie_table');
+ // if (filePath.includes('LayoutTests/inspector/components/datagrid-autosize.html'))
+ // newContent = newContent.replaceAll('ui_lazy', 'data_grid');
+ // if (filePath.includes('LayoutTests/inspector/components/datagrid-test.js'))
+ // newContent = newContent.replaceAll('ui_lazy', 'data_grid');
+
+ if (content !== newContent)
+ fs.writeFileSync(filePath, newContent);
+ }
+}
+
+function removeFromExistingModuleDescriptors(modules, identifierMap, cssFilesMapping) {
+ let extensionMap = new Map();
+ let moduleFileMap = new Map();
+
+ for (let fileObj of JS_FILES_MAPPING) {
+ let components = fileObj.file.split('/');
+ let module = components[0];
+ let fileName = components[1];
+
+ if (!moduleFileMap.get(module))
+ moduleFileMap.set(module, []);
+
+ moduleFileMap.set(module, moduleFileMap.get(module).concat(fileName));
+ }
+
+ for (let module of modules) {
+ let moduleJSONPath = path.resolve(FRONTEND_PATH, module, 'module.json');
+ let content = fs.readFileSync(moduleJSONPath).toString();
+ let moduleObj = parseJSON(content);
+ let removedScripts = removeScripts(moduleObj, module);
+ removeResources(moduleObj, removedScripts);
+ removeExtensions(moduleObj);
+ fs.writeFileSync(moduleJSONPath, stringifyJSON(moduleObj));
+ }
+
+ return extensionMap;
+
+ function removeScripts(moduleObj, module) {
+ let remainingScripts = [];
+ let removedScripts = [];
+ let moduleFiles = moduleFileMap.get(module);
+ for (let script of moduleObj.scripts) {
+ if (!moduleFiles.includes(script))
+ remainingScripts.push(script);
+ else
+ removedScripts.push(module + '/' + script);
+ }
+ moduleObj.scripts = remainingScripts;
+ return removedScripts;
+ }
+
+ function removeResources(moduleObj, removedScripts) {
+ if (!moduleObj.resources)
+ return;
+ let remainingResources = [];
+ let removedResources = new Set();
+ for (let script of removedScripts)
+ removedResources = removedResources.union(cssFilesMapping.get(script));
+
+
+ for (let resource of moduleObj.resources) {
+ if (!removedResources.has(resource))
+ remainingResources.push(resource);
+ }
+ moduleObj.resources = remainingResources;
+ }
+
+ function removeExtensions(moduleObj) {
+ if (!moduleObj.extensions)
+ return;
+ let remainingExtensions = [];
+ for (let extension of moduleObj.extensions) {
+ if (!objectIncludesIdentifier(extension))
+ remainingExtensions.push(extension);
+ else
+ extensionMap.set(objectIncludesIdentifier(extension), extension);
+ }
+ moduleObj.extensions = remainingExtensions;
+ }
+
+ function objectIncludesIdentifier(object) {
+ for (let key in object) {
+ let value = object[key];
+ if (identifierMap.has(value))
+ return value;
+ }
+ return false;
+ }
+}
+
+function createNewModuleDescriptors(extensionMap, cssFilesMapping, identifiersByFile, targetToOriginalFilesMap) {
+ let filesByNewModule = getFilesByNewModule();
+
+ for (let module of filesByNewModule.keys()) {
+ let moduleObj = {};
+
+ let scripts = getModuleScripts(module);
+ let extensions = getModuleExtensions(scripts, module);
+ if (extensions.length)
+ moduleObj.extensions = extensions;
+
+ moduleObj.dependencies = DEPENDENCIES_BY_MODULE[module];
+
+ moduleObj.scripts = scripts;
+
+ let resources = getModuleResources(moduleObj.scripts, module);
+ if (resources.length)
+ moduleObj.resources = resources;
+
+ let moduleJSONPath = path.resolve(FRONTEND_PATH, module, 'module.json');
+ fs.writeFileSync(moduleJSONPath, stringifyJSON(moduleObj));
+ }
+
+ function getFilesByNewModule() {
+ let filesByNewModule = new Map();
+ for (let fileObj of JS_FILES_MAPPING) {
+ if (!fileObj.new)
+ continue;
+ if (!filesByNewModule.has(fileObj.new))
+ filesByNewModule.set(fileObj.new, []);
+
+ filesByNewModule.set(fileObj.new, filesByNewModule.get(fileObj.new).concat([fileObj.file]));
+ }
+ return filesByNewModule;
+ }
+
+ function getModuleScripts(module) {
+ return filesByNewModule.get(module).map((file) => file.split('/')[1]);
+ }
+
+ function getModuleResources(scripts, module) {
+ let resources = [];
+ scripts.map(script => module + '/' + script).forEach((script) => {
+ script = targetToOriginalFilesMap.get(script);
+ if (!cssFilesMapping.has(script))
+ return;
+
+ resources = resources.concat([...cssFilesMapping.get(script)]);
+ });
+ return resources;
+ }
+
+ function getModuleExtensions(scripts, module) {
+ let extensions = [];
+ let identifiers =
+ scripts.map(script => module + '/' + script)
+ .reduce((acc, file) => acc.concat(identifiersByFile.get(targetToOriginalFilesMap.get(file))), []);
+ for (let identifier of identifiers) {
+ if (extensionMap.has(identifier))
+ extensions.push(extensionMap.get(identifier));
+ }
+ return extensions;
+ }
+}
+
+function calculateFilesByModuleType(type) {
+ let filesByNewModule = new Map();
+ for (let fileObj of JS_FILES_MAPPING) {
+ if (!fileObj[type])
+ continue;
+ if (!filesByNewModule.has(fileObj[type]))
+ filesByNewModule.set(fileObj[type], []);
+
+ filesByNewModule.set(fileObj[type], filesByNewModule.get(fileObj[type]).concat([fileObj.file]));
+ }
+ return filesByNewModule;
+}
+
+function updateExistingModuleDescriptors(extensionMap, cssFilesMapping, identifiersByFile, targetToOriginalFilesMap) {
+ let filesByExistingModule = calculateFilesByModuleType('existing');
+ for (let module of filesByExistingModule.keys()) {
+ let moduleJSONPath = path.resolve(FRONTEND_PATH, module, 'module.json');
+ let content = fs.readFileSync(moduleJSONPath).toString();
+ let moduleObj = parseJSON(content);
+
+ let scripts = getModuleScripts(module);
+ let existingExtensions = moduleObj.extensions || [];
+ let extensions = existingExtensions.concat(getModuleExtensions(scripts, module));
+ if (extensions.length)
+ moduleObj.extensions = extensions;
+
+ moduleObj.scripts = moduleObj.scripts.concat(scripts);
+
+ let existingResources = moduleObj.resources || [];
+ let resources = existingResources.concat(getModuleResources(scripts, module));
+ if (resources.length)
+ moduleObj.resources = resources;
+
+ fs.writeFileSync(moduleJSONPath, stringifyJSON(moduleObj));
+ }
+
+
+ function getModuleScripts(module) {
+ return filesByExistingModule.get(module).map((file) => file.split('/')[1]);
+ }
+
+ function getModuleResources(scripts, module) {
+ let resources = [];
+ scripts.map(script => module + '/' + script).forEach((script) => {
+ script = targetToOriginalFilesMap.get(script);
+ if (!cssFilesMapping.has(script))
+ return;
+
+ resources = resources.concat([...cssFilesMapping.get(script)]);
+ });
+ return resources;
+ }
+
+ function getModuleExtensions(scripts, module) {
+ let extensions = [];
+ let identifiers =
+ scripts.map(script => module + '/' + script)
+ .reduce((acc, file) => acc.concat(identifiersByFile.get(targetToOriginalFilesMap.get(file))), []);
+ for (let identifier of identifiers) {
+ if (extensionMap.has(identifier))
+ extensions.push(extensionMap.get(identifier));
+ }
+ return extensions;
+ }
+}
+
+function addDependenciesToDescriptors() {
+ for (let module of getModules()) {
+ let moduleJSONPath = path.resolve(FRONTEND_PATH, module, 'module.json');
+ let content = fs.readFileSync(moduleJSONPath).toString();
+ let moduleObj = parseJSON(content);
+
+ let existingDependencies = moduleObj.dependencies || [];
+ let dependencies = existingDependencies.concat(getModuleDependencies(module))
+ .filter((depModule) => !MODULES_TO_REMOVE.includes(depModule))
+ .filter((depModule) => !(REMOVE_DEPENDENCIES_BY_EXISTING_MODULES[module] || []).includes(depModule));
+ let newDependenciesForExistingModule = NEW_DEPENDENCIES_BY_EXISTING_MODULES[module];
+ if (newDependenciesForExistingModule)
+ dependencies = dependencies.concat(newDependenciesForExistingModule);
+ if (dependencies.length)
+ moduleObj.dependencies = dependencies;
+ let newStringified = stringifyJSON(moduleObj);
+ if (stringifyJSON(moduleObj) !== stringifyJSON(parseJSON(content)))
+ fs.writeFileSync(moduleJSONPath, newStringified);
+ }
+
+ function getModuleDependencies(existingModule) {
+ let newDeps = [];
+ for (let newModule in DEPENDENTS_BY_MODULE) {
+ let dependents = DEPENDENTS_BY_MODULE[newModule];
+ if (dependents.includes(existingModule))
+ newDeps.push(newModule);
+ }
+ return newDeps;
+ }
+}
+
+function updateApplicationDescriptor(descriptorFileName, newModuleSet) {
+ let descriptorPath = path.join(FRONTEND_PATH, descriptorFileName);
+ let newModules = [...newModuleSet].filter(m => APPLICATIONS_BY_MODULE[m].includes(descriptorFileName));
+ let includeNewModules = (acc, line) => {
+ if (line.includes('{') && line.endsWith('}')) {
+ line += ',';
+ acc.push(line);
+ return acc.concat(newModules.map((m, i) => {
+ // Need spacing to preserve indentation
+ let string;
+ if (MODULE_MAPPING[m].autostart)
+ string = ` { "name": "${m}", "type": "autostart"}`;
+ else
+ string = ` { "name": "${m}" }`;
+ if (i !== newModules.length - 1)
+ string += ',';
+ return string;
+ }));
+ }
+ return acc.concat([line]);
+ };
+ let removeModules = (acc, line) => MODULES_TO_REMOVE.every(m => !line.includes(m)) ? acc.concat([line]) : acc;
+ let lines =
+ fs.readFileSync(descriptorPath).toString().split('\n').reduce(includeNewModules, []).reduce(removeModules, []);
+ fs.writeFileSync(descriptorPath, lines.join('\n'));
+}
+
+function getModules() {
+ return fs.readdirSync(FRONTEND_PATH).filter(function(file) {
+ return fs.statSync(path.join(FRONTEND_PATH, file)).isDirectory() &&
+ utils.isFile(path.join(FRONTEND_PATH, file, 'module.json'));
+ });
+}
+
+function parseJSON(string) {
+ return JSON.parse(string);
+}
+
+function stringifyJSON(obj) {
+ return unicodeEscape(JSON.stringify(obj, null, 4) + '\n');
+}
+
+// http://stackoverflow.com/questions/7499473/need-to-escape-non-ascii-characters-in-javascript
+function unicodeEscape(string) {
+ function padWithLeadingZeros(string) {
+ return new Array(5 - string.length).join("0") + string;
+ }
+
+ function unicodeCharEscape(charCode) {
+ return "\\u" + padWithLeadingZeros(charCode.toString(16));
+ }
+
+ return string.split("")
+ .map(function (char) {
+ var charCode = char.charCodeAt(0);
+ return charCode > 127 ? unicodeCharEscape(charCode) : char;
+ })
+ .join("");
+}
+
+if (require.main === module)
+ extractModule();

Powered by Google App Engine
This is Rietveld 408576698