| 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();
|
|
|