diff --git a/lib/Api.js b/lib/Api.js index ac9f915..db3e97b 100644 --- a/lib/Api.js +++ b/lib/Api.js @@ -25,178 +25,181 @@ const path = require('node:path'); const fs = require('node:fs'); -const cdvcmn = require('cordova-common'); -const CordovaLogger = cdvcmn.CordovaLogger; -const ConfigParser = cdvcmn.ConfigParser; -const ActionStack = cdvcmn.ActionStack; -const selfEvents = cdvcmn.events; -const xmlHelpers = cdvcmn.xmlHelpers; -const PlatformJson = cdvcmn.PlatformJson; -const PlatformMunger = cdvcmn.ConfigChanges.PlatformMunger; -const PluginInfoProvider = cdvcmn.PluginInfoProvider; -const CordovaError = cdvcmn.CordovaError; -const FileUpdater = cdvcmn.FileUpdater; +const { + ActionStack, + ConfigChanges: { PlatformMunger }, + ConfigParser, + CordovaLogger, + CordovaError, + events, + FileUpdater, + PlatformJson, + PluginInfoProvider, + xmlHelpers +} = require('cordova-common'); const { createProject } = require('./create'); +const VERSION = require('../package').version; -const PLATFORM_NAME = 'browser'; +const PLATFORM = 'browser'; function setupEvents (externalEventEmitter) { if (externalEventEmitter) { // This will make the platform internal events visible outside - selfEvents.forwardEventsTo(externalEventEmitter); + events.forwardEventsTo(externalEventEmitter); return externalEventEmitter; } // There is no logger if external emitter is not present, // so attach a console logger - CordovaLogger.get().subscribe(selfEvents); - return selfEvents; + CordovaLogger.get().subscribe(events); + return events; } /** * Logs all file operations via the verbose event stream, indented. */ function logFileOp (message) { - selfEvents.emit('verbose', ' ' + message); + events.emit('verbose', ` ${message}`); } function dirExists (dir) { return fs.existsSync(dir) && fs.statSync(dir).isDirectory(); } -function Api (platform, platformRootDir, events) { - this.platform = platform || PLATFORM_NAME; - this.root = platformRootDir; - this.events = setupEvents(events); - if (!dirExists(this.root) || !dirExists(path.join(this.root, 'cordova'))) { - throw new CordovaError('The provided path "' + this.root + '" is not a valid browser project.'); - } - this._handler = require('./browser_handler'); - - this.locations = { - platformRootDir, - root: this.root, - www: path.join(this.root, 'www'), - res: path.join(this.root, 'res'), - platformWww: path.join(this.root, 'platform_www'), - configXml: path.join(this.root, 'config.xml'), - defaultConfigXml: path.join(this.root, 'cordova/defaults.xml'), - build: path.join(this.root, 'build'), - // NOTE: Due to platformApi spec we need to return relative paths here - cordovaJs: 'templates/project/assets/www/cordova.js', - cordovaJsSrc: 'cordova-js-src' - }; - - this._platformJson = PlatformJson.load(this.root, platform); - this._pluginInfoProvider = new PluginInfoProvider(); - this._munger = new PlatformMunger(platform, this.root, this._platformJson, this._pluginInfoProvider); -} - -Api.createPlatform = function (dest, config, options, events) { - events = setupEvents(events); +class Api { + constructor (platform, platformRootDir, externalEventEmitter) { + this.platform = PLATFORM; + this.root = platformRootDir; + this.events = setupEvents(externalEventEmitter); + if (!dirExists(this.root) || !dirExists(path.join(this.root, 'cordova'))) { + throw new CordovaError(`The provided path "${this.root}" is not a valid browser project.`); + } + this._handler = require('./browser_handler'); + + this.locations = { + platformRootDir, + root: this.root, + www: path.join(this.root, 'www'), + res: path.join(this.root, 'res'), + platformWww: path.join(this.root, 'platform_www'), + configXml: path.join(this.root, 'config.xml'), + defaultConfigXml: path.join(this.root, 'cordova/defaults.xml'), + build: path.join(this.root, 'build'), + // NOTE: Due to platformApi spec we need to return relative paths here + cordovaJs: 'templates/project/assets/www/cordova.js', + cordovaJsSrc: 'cordova-js-src' + }; - const name = config.name().normalize('NFD'); + this._platformJson = PlatformJson.load(this.root, this.platform); + this._pluginInfoProvider = new PluginInfoProvider(); + this._munger = new PlatformMunger(this.platform, this.root, this._platformJson, this._pluginInfoProvider); + } - try { - // we create the project using our scripts in this platform - return createProject(dest, config.packageName(), name, options, config) - .then(function () { - // after platform is created we return Api instance based on new Api.js location - // Api.js has been copied to the new project - // This is required to correctly resolve paths in the future api calls - const PlatformApi = require(path.resolve(dest, 'cordova/Api')); - return new PlatformApi('browser', dest, events); - }); - } catch (e) { - events.emit('error', 'createPlatform is not callable from the browser project API.'); - throw (e); + static async createPlatform (dest, config, options, externalEventEmitter) { + externalEventEmitter = setupEvents(externalEventEmitter); + + const name = config.name().normalize('NFD'); + + try { + // we create the project using our scripts in this platform + return createProject(dest, config.packageName(), name, options, config) + .then(function () { + // after platform is created we return Api instance based on new Api.js location + // Api.js has been copied to the new project + // This is required to correctly resolve paths in the future api calls + const PlatformApi = require(path.resolve(dest, 'cordova/Api')); + return new PlatformApi('browser', dest, externalEventEmitter); + }); + } catch (e) { + externalEventEmitter.emit('error', 'createPlatform is not callable from the browser project API.'); + throw (e); + } } -}; - -Api.updatePlatform = function (dest, options, events) { - // console.log("test-platform:Api:updatePlatform"); - // todo?: create projectInstance and fulfill promise with it. - return Promise.resolve(); -}; - -Api.prototype.getPlatformInfo = function () { - // console.log("browser-platform:Api:getPlatformInfo"); - // return PlatformInfo object - return { - locations: this.locations, - root: this.root, - name: this.platform, - version: { version: '1.0.0' }, // um, todo! - projectConfig: this.config - }; -}; - -Api.prototype.prepare = async function (cordovaProject, options) { - // First cleanup current config and merge project's one into own - const defaultConfigPath = path.join(this.locations.platformRootDir, 'cordova', - 'defaults.xml'); - const ownConfigPath = this.locations.configXml; - const sourceCfg = cordovaProject.projectConfig; - - // If defaults.xml is present, overwrite platform config.xml with it. - // Otherwise save whatever is there as defaults so it can be - // restored or copy project config into platform if none exists. - if (fs.existsSync(defaultConfigPath)) { - this.events.emit('verbose', 'Generating config.xml from defaults for platform "' + this.platform + '"'); - fs.cpSync(defaultConfigPath, ownConfigPath, { force: true }); - } else if (fs.existsSync(ownConfigPath)) { - this.events.emit('verbose', 'Generating defaults.xml from own config.xml for platform "' + this.platform + '"'); - fs.cpSync(ownConfigPath, defaultConfigPath, { force: true }); - } else { - this.events.emit('verbose', 'case 3"' + this.platform + '"'); - fs.cpSync(sourceCfg.path, ownConfigPath, { force: true }); + + static async updatePlatform (dest, options, events) { + // console.log("test-platform:Api:updatePlatform"); + // todo?: create projectInstance and fulfill promise with it. + return Promise.resolve(); } - // merge our configs - this.config = new ConfigParser(ownConfigPath); - xmlHelpers.mergeXml(cordovaProject.projectConfig.doc.getroot(), - this.config.doc.getroot(), - this.platform, true); - this.config.write(); - - // Update own www dir with project's www assets and plugins' assets and js-files - this._updateWww(cordovaProject); - - // Copy or Create manifest.json - // todo: move this to a manifest helper module - // output path - const manifestPath = path.join(this.locations.www, 'manifest.json'); - const srcManifestPath = path.join(cordovaProject.locations.www, 'manifest.json'); - if (fs.existsSync(srcManifestPath)) { - // just blindly copy it to our output/www - // todo: validate it? ensure all properties we expect exist? - this.events.emit('verbose', 'copying ' + srcManifestPath + ' => ' + manifestPath); - fs.cpSync(srcManifestPath, manifestPath, { force: true }); - } else { - const manifestJson = { - background_color: '#FFF', - display: 'standalone' + getPlatformInfo () { + // console.log("browser-platform:Api:getPlatformInfo"); + // return PlatformInfo object + return { + locations: this.locations, + root: this.root, + name: this.platform, + version: Api.version(), + projectConfig: this.config }; - if (this.config) { - if (this.config.name()) { - manifestJson.name = this.config.name(); - } - if (this.config.shortName()) { - manifestJson.short_name = this.config.shortName(); - } - if (this.config.packageName()) { - manifestJson.version = this.config.packageName(); - } - if (this.config.description()) { - manifestJson.description = this.config.description(); - } - if (this.config.author()) { - manifestJson.author = this.config.author(); - } - // icons - const icons = this.config.getStaticResources('browser', 'icon'); - const manifestIcons = icons.map(function (icon) { + } + + async prepare (cordovaProject, options) { + // First cleanup current config and merge project's one into own + const defaultConfigPath = path.join(this.locations.platformRootDir, 'cordova', + 'defaults.xml'); + const ownConfigPath = this.locations.configXml; + const sourceCfg = cordovaProject.projectConfig; + + // If defaults.xml is present, overwrite platform config.xml with it. + // Otherwise save whatever is there as defaults so it can be + // restored or copy project config into platform if none exists. + if (fs.existsSync(defaultConfigPath)) { + this.events.emit('verbose', `Generating config.xml from defaults for platform "${this.platform}"`); + fs.cpSync(defaultConfigPath, ownConfigPath, { force: true }); + } else if (fs.existsSync(ownConfigPath)) { + this.events.emit('verbose', `Generating defaults.xml from own config.xml for platform "${this.platform}"`); + fs.cpSync(ownConfigPath, defaultConfigPath, { force: true }); + } else { + this.events.emit('verbose', `case 3"${this.platform}"`); + fs.cpSync(sourceCfg.path, ownConfigPath, { force: true }); + } + + // merge our configs + this.config = new ConfigParser(ownConfigPath); + xmlHelpers.mergeXml(cordovaProject.projectConfig.doc.getroot(), + this.config.doc.getroot(), + this.platform, true); + this.config.write(); + + // Update own www dir with project's www assets and plugins' assets and js-files + this.#updateWww(cordovaProject); + + // Copy or Create manifest.json + // todo: move this to a manifest helper module + // output path + const manifestPath = path.join(this.locations.www, 'manifest.json'); + const srcManifestPath = path.join(cordovaProject.locations.www, 'manifest.json'); + if (fs.existsSync(srcManifestPath)) { + // just blindly copy it to our output/www + // todo: validate it? ensure all properties we expect exist? + this.events.emit('verbose', `copying ${srcManifestPath} => ${manifestPath}`); + fs.cpSync(srcManifestPath, manifestPath, { force: true }); + } else { + const manifestJson = { + background_color: '#FFF', + display: 'standalone' + }; + if (this.config) { + if (this.config.name()) { + manifestJson.name = this.config.name(); + } + if (this.config.shortName()) { + manifestJson.short_name = this.config.shortName(); + } + if (this.config.packageName()) { + manifestJson.version = this.config.packageName(); + } + if (this.config.description()) { + manifestJson.description = this.config.description(); + } + if (this.config.author()) { + manifestJson.author = this.config.author(); + } + // icons + const icons = this.config.getStaticResources('browser', 'icon'); + // given a tag like this : // /* configParser returns icons that look like this : @@ -213,348 +216,336 @@ Api.prototype.prepare = async function (cordovaProject, options) { "sizes": "128x128" } ******/ // ?Is it worth looking at file extentions? - return { + const manifestIcons = icons.map(icon => ({ src: icon.src, type: 'image/png', - sizes: (icon.width + 'x' + icon.height) - }; - }); - manifestJson.icons = manifestIcons; - - // orientation - // - const oriPref = this.config.getGlobalPreference('Orientation'); - if (oriPref) { - // if it's a supported value, use it - if (['landscape', 'portrait'].indexOf(oriPref) > -1) { - manifestJson.orientation = oriPref; - } else { // anything else maps to 'any' - manifestJson.orientation = 'any'; + sizes: `${icon.width}x${icon.height}` + })); + manifestJson.icons = manifestIcons; + + // orientation + // + const oriPref = this.config.getGlobalPreference('Orientation'); + if (oriPref) { + // if it's a supported value, use it + if (['landscape', 'portrait'].indexOf(oriPref) > -1) { + manifestJson.orientation = oriPref; + } else { // anything else maps to 'any' + manifestJson.orientation = 'any'; + } } - } - // get start_url - const contentNode = this.config.doc.find('content') || { attrib: { src: 'index.html' } }; // sensible default - manifestJson.start_url = contentNode.attrib.src; - - // now we get some values from start_url page ... - const startUrlPath = path.join(cordovaProject.locations.www, manifestJson.start_url); - if (fs.existsSync(startUrlPath)) { - const contents = fs.readFileSync(startUrlPath, 'utf-8'); - // matches - const themeColorRegex = /]*name="theme-color")\s[^>]*content="([^>]*)"/i; - const result = themeColorRegex.exec(contents); - let themeColor; - if (result && result.length >= 2) { - themeColor = result[1]; - } else { // see if there is a preference in config.xml - // - themeColor = this.config.getGlobalPreference('StatusBarBackgroundColor'); - } - if (themeColor) { - manifestJson.theme_color = themeColor; + // get start_url + const contentNode = this.config.doc.find('content') || { attrib: { src: 'index.html' } }; // sensible default + manifestJson.start_url = contentNode.attrib.src; + + // now we get some values from start_url page ... + const startUrlPath = path.join(cordovaProject.locations.www, manifestJson.start_url); + if (fs.existsSync(startUrlPath)) { + const contents = fs.readFileSync(startUrlPath, 'utf-8'); + // matches + const themeColorRegex = /]*name="theme-color")\s[^>]*content="([^>]*)"/i; + const result = themeColorRegex.exec(contents); + let themeColor; + if (result && result.length >= 2) { + themeColor = result[1]; + } else { // see if there is a preference in config.xml + // + themeColor = this.config.getGlobalPreference('StatusBarBackgroundColor'); + } + if (themeColor) { + manifestJson.theme_color = themeColor; + } } } + fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 2), 'utf8'); } - fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 2), 'utf8'); - } - // Copy munged config.xml to platform www dir - fs.cpSync( - this.locations.configXml, - path.join(this.locations.www, 'config.xml'), - { force: true, recursive: true } - ); -}; - -// Replace the www dir with contents of platform_www and app www. -Api.prototype._updateWww = function (cordovaProject) { - // add cordova www and platform_www to sourceDirs - const sourceDirs = [ - path.relative(cordovaProject.root, cordovaProject.locations.www), - path.relative(cordovaProject.root, this.locations.platformWww) - ]; - - // If project contains 'merges' for our platform, use them as another overrides - const merges_path = path.join(cordovaProject.root, 'merges', 'browser'); - if (fs.existsSync(merges_path)) { - selfEvents.emit('verbose', 'Found "merges/browser" folder. Copying its contents into the browser project.'); - // add merges/browser to sourceDirs - sourceDirs.push(path.join('merges', 'browser')); + // Copy munged config.xml to platform www dir + fs.cpSync( + this.locations.configXml, + path.join(this.locations.www, 'config.xml'), + { force: true, recursive: true } + ); } - // targetDir points to browser/www - const targetDir = path.relative(cordovaProject.root, this.locations.www); - selfEvents.emit('verbose', 'Merging and updating files from [' + sourceDirs.join(', ') + '] to ' + targetDir); - FileUpdater.mergeAndUpdateDir(sourceDirs, targetDir, { rootDir: cordovaProject.root }, logFileOp); -}; - -Api.prototype.addPlugin = function (pluginInfo, installOptions) { - // console.log(new Error().stack); - if (!pluginInfo) { - return Promise.reject(new Error('The parameter is incorrect. The first parameter ' + - 'should be valid PluginInfo instance')); + // Replace the www dir with contents of platform_www and app www. + #updateWww (cordovaProject) { + // add cordova www and platform_www to sourceDirs + const sourceDirs = [ + path.relative(cordovaProject.root, cordovaProject.locations.www), + path.relative(cordovaProject.root, this.locations.platformWww) + ]; + + // If project contains 'merges' for our platform, use them as another overrides + const merges_path = path.join(cordovaProject.root, 'merges', 'browser'); + if (fs.existsSync(merges_path)) { + events.emit('verbose', 'Found "merges/browser" folder. Copying its contents into the browser project.'); + // add merges/browser to sourceDirs + sourceDirs.push(path.join('merges', 'browser')); + } + + // targetDir points to browser/www + const targetDir = path.relative(cordovaProject.root, this.locations.www); + events.emit('verbose', `Merging and updating files from [${sourceDirs.join(', ')}] to ${targetDir}`); + FileUpdater.mergeAndUpdateDir(sourceDirs, targetDir, { rootDir: cordovaProject.root }, logFileOp); } - installOptions = installOptions || {}; - installOptions.variables = installOptions.variables || {}; - // CB-10108 platformVersion option is required for proper plugin installation - installOptions.platformVersion = installOptions.platformVersion || - this.getPlatformInfo().version; - - const self = this; - const actions = new ActionStack(); - const projectFile = this._handler.parseProjectFile && this._handler.parseProjectFile(this.root); - - // gather all files needs to be handled during install - pluginInfo.getFilesAndFrameworks(this.platform) - .concat(pluginInfo.getAssets(this.platform)) - .concat(pluginInfo.getJsModules(this.platform)) - .forEach(function (item) { - actions.push(actions.createAction( - self._getInstaller(item.itemType), - [item, pluginInfo.dir, pluginInfo.id, installOptions, projectFile], - self._getUninstaller(item.itemType), - [item, pluginInfo.dir, pluginInfo.id, installOptions, projectFile])); - }); - - // run through the action stack - return actions.process(this.platform, this.root) - .then(function () { - if (projectFile) { - projectFile.write(); - } + async addPlugin (pluginInfo, installOptions) { + // console.log(new Error().stack); + if (!pluginInfo) { + return Promise.reject( + new Error('The parameter is incorrect. The first parameter should be valid PluginInfo instance') + ); + } - // Add PACKAGE_NAME variable into vars - if (!installOptions.variables.PACKAGE_NAME) { - installOptions.variables.PACKAGE_NAME = self._handler.package_name(self.root); - } + installOptions = installOptions || {}; + installOptions.variables = installOptions.variables || {}; + // CB-10108 platformVersion option is required for proper plugin installation + installOptions.platformVersion = installOptions.platformVersion || + this.getPlatformInfo().version; + + const actions = new ActionStack(); + const projectFile = this._handler.parseProjectFile && this._handler.parseProjectFile(this.root); + + // gather all files needs to be handled during install + pluginInfo.getFilesAndFrameworks(this.platform) + .concat(pluginInfo.getAssets(this.platform)) + .concat(pluginInfo.getJsModules(this.platform)) + .forEach(item => { + actions.push(actions.createAction( + this._getInstaller(item.itemType, item, pluginInfo.dir, pluginInfo.id, installOptions, projectFile), + this._getUninstaller(item.itemType, item, pluginInfo.dir, pluginInfo.id, installOptions, projectFile) + )); + }); - self._munger - // Ignore passed `is_top_level` option since platform itself doesn't know - // anything about managing dependencies - it's responsibility of caller. - .add_plugin_changes(pluginInfo, installOptions.variables, /* is_top_level= */true, /* should_increment= */true) - .save_all(); - - const targetDir = installOptions.usePlatformWww - ? self.getPlatformInfo().locations.platformWww - : self.getPlatformInfo().locations.www; - - self._addModulesInfo(pluginInfo, targetDir); - }); -}; - -Api.prototype.removePlugin = function (plugin, uninstallOptions) { - // console.log("NotImplemented :: browser-platform:Api:removePlugin ",plugin, uninstallOptions); - - uninstallOptions = uninstallOptions || {}; - // CB-10108 platformVersion option is required for proper plugin installation - uninstallOptions.platformVersion = uninstallOptions.platformVersion || - this.getPlatformInfo().version; - - const self = this; - const actions = new ActionStack(); - const projectFile = this._handler.parseProjectFile && this._handler.parseProjectFile(this.root); - - // queue up plugin files - plugin.getFilesAndFrameworks(this.platform) - .concat(plugin.getAssets(this.platform)) - .concat(plugin.getJsModules(this.platform)) - .forEach(function (item) { - actions.push(actions.createAction( - self._getUninstaller(item.itemType), [item, plugin.dir, plugin.id, uninstallOptions, projectFile], - self._getInstaller(item.itemType), [item, plugin.dir, plugin.id, uninstallOptions, projectFile])); - }); - - // run through the action stack - return actions.process(this.platform, this.root) - .then(function () { - if (projectFile) { - projectFile.write(); - } + // run through the action stack + return actions.process(this.platform, this.root) + .then(() => { + if (projectFile) { + projectFile.write(); + } - self._munger - // Ignore passed `is_top_level` option since platform itself doesn't know - // anything about managing dependencies - it's responsibility of caller. - .remove_plugin_changes(plugin, /* is_top_level= */true) - .save_all(); - - const targetDir = uninstallOptions.usePlatformWww - ? self.getPlatformInfo().locations.platformWww - : self.getPlatformInfo().locations.www; - - self._removeModulesInfo(plugin, targetDir); - // Remove stale plugin directory - // TODO: this should be done by plugin files uninstaller - fs.rmSync( - path.resolve(self.root, 'Plugins', plugin.id), - { force: true, recursive: true } - ); - }); -}; + // Add PACKAGE_NAME variable into vars + if (!installOptions.variables.PACKAGE_NAME) { + installOptions.variables.PACKAGE_NAME = this._handler.package_name(this.root); + } + + this._munger + // Ignore passed `is_top_level` option since platform itself doesn't know + // anything about managing dependencies - it's responsibility of caller. + .add_plugin_changes(pluginInfo, installOptions.variables, /* is_top_level= */true, /* should_increment= */true) + .save_all(); -Api.prototype._getInstaller = function (type) { - const self = this; - return function (item, plugin_dir, plugin_id, options, project) { - const installer = self._handler[type]; + const targetDir = installOptions.usePlatformWww + ? this.getPlatformInfo().locations.platformWww + : this.getPlatformInfo().locations.www; + + this.#addModulesInfo(pluginInfo, targetDir); + }); + } + + async removePlugin (plugin, uninstallOptions) { + // console.log("NotImplemented :: browser-platform:Api:removePlugin ",plugin, uninstallOptions); + + uninstallOptions = uninstallOptions || {}; + // CB-10108 platformVersion option is required for proper plugin installation + uninstallOptions.platformVersion = uninstallOptions.platformVersion || + this.getPlatformInfo().version; + + const actions = new ActionStack(); + const projectFile = this._handler.parseProjectFile && this._handler.parseProjectFile(this.root); + + // queue up plugin files + plugin.getFilesAndFrameworks(this.platform) + .concat(plugin.getAssets(this.platform)) + .concat(plugin.getJsModules(this.platform)) + .forEach(item => { + actions.push(actions.createAction( + this._getUninstaller(item.itemType, item, plugin.dir, plugin.id, uninstallOptions, projectFile), + this._getInstaller(item.itemType, item, plugin.dir, plugin.id, uninstallOptions, projectFile) + )); + }); + + // run through the action stack + return actions.process(this.platform, this.root) + .then(() => { + if (projectFile) { + projectFile.write(); + } + + this._munger + // Ignore passed `is_top_level` option since platform itself doesn't know + // anything about managing dependencies - it's responsibility of caller. + .remove_plugin_changes(plugin, /* is_top_level= */true) + .save_all(); + + const targetDir = uninstallOptions.usePlatformWww + ? this.getPlatformInfo().locations.platformWww + : this.getPlatformInfo().locations.www; + + this.#removeModulesInfo(plugin, targetDir); + // Remove stale plugin directory + // TODO: this should be done by plugin files uninstaller + fs.rmSync( + path.resolve(this.root, 'Plugins', plugin.id), + { force: true, recursive: true } + ); + }); + } + + _getInstaller (type, item, plugin_dir, plugin_id, options, project) { + const installer = this._handler[type]; if (!installer) { - console.log('unrecognized type ' + type); + console.log(`unrecognized type ${type}`); } else { const wwwDest = options.usePlatformWww - ? self.getPlatformInfo().locations.platformWww - : self._handler.www_dir(self.root); + ? this.getPlatformInfo().locations.platformWww + : this._handler.www_dir(this.root); if (type === 'asset') { installer.install(item, plugin_dir, wwwDest); } else if (type === 'js-module') { installer.install(item, plugin_dir, plugin_id, wwwDest); } else { - installer.install(item, plugin_dir, self.root, plugin_id, options, project); + installer.install(item, plugin_dir, this.root, plugin_id, options, project); } } - }; -}; + } -Api.prototype._getUninstaller = function (type) { - const self = this; - return function (item, plugin_dir, plugin_id, options, project) { - const installer = self._handler[type]; + _getUninstaller (type, item, plugin_dir, plugin_id, options, project) { + const installer = this._handler[type]; if (!installer) { - console.log('browser plugin uninstall: unrecognized type, skipping : ' + type); + console.log(`browser plugin uninstall: unrecognized type, skipping : ${type}`); } else { const wwwDest = options.usePlatformWww - ? self.getPlatformInfo().locations.platformWww - : self._handler.www_dir(self.root); + ? this.getPlatformInfo().locations.platformWww + : this._handler.www_dir(this.root); - if (['asset', 'js-module'].indexOf(type) > -1) { + if (['asset', 'js-module'].includes(type)) { return installer.uninstall(item, wwwDest, plugin_id); } else { - return installer.uninstall(item, self.root, plugin_id, options, project); + return installer.uninstall(item, this.root, plugin_id, options, project); } } - }; -}; + } -/** - * Removes the specified modules from list of installed modules and updates - * platform_json and cordova_plugins.js on disk. - * - * @param {PluginInfo} plugin PluginInfo instance for plugin, which modules - * needs to be added. - * @param {String} targetDir The directory, where updated cordova_plugins.js - * should be written to. - */ -Api.prototype._addModulesInfo = function (plugin, targetDir) { - const installedModules = this._platformJson.root.modules || []; - - const installedPaths = installedModules.map(function (installedModule) { - return installedModule.file; - }); - - const modulesToInstall = plugin.getJsModules(this.platform) - .filter(function (moduleToInstall) { - return installedPaths.indexOf(moduleToInstall.file) === -1; - }).map(function (moduleToInstall) { - const moduleName = plugin.id + '.' + (moduleToInstall.name || moduleToInstall.src.match(/([^/]+)\.js/)[1]); - const obj = { - file: ['plugins', plugin.id, moduleToInstall.src].join('/'), - id: moduleName, - pluginId: plugin.id - }; - if (moduleToInstall.clobbers.length > 0) { - obj.clobbers = moduleToInstall.clobbers.map(function (o) { return o.target; }); - } - if (moduleToInstall.merges.length > 0) { - obj.merges = moduleToInstall.merges.map(function (o) { return o.target; }); - } - if (moduleToInstall.runs) { - obj.runs = true; - } + /** + * Removes the specified modules from list of installed modules and updates + * platform_json and cordova_plugins.js on disk. + * + * @param {PluginInfo} plugin PluginInfo instance for plugin, which modules + * needs to be added. + * @param {String} targetDir The directory, where updated cordova_plugins.js + * should be written to. + */ + #addModulesInfo (plugin, targetDir) { + const installedModules = this._platformJson.root.modules || []; + const installedPaths = installedModules.map(installedModule => installedModule.file); + + const modulesToInstall = plugin.getJsModules(this.platform) + .filter(moduleToInstall => !installedPaths.includes(moduleToInstall.file)) + .map(moduleToInstall => { + const moduleName = plugin.id + '.' + (moduleToInstall.name || moduleToInstall.src.match(/([^/]+)\.js/)[1]); + const obj = { + file: ['plugins', plugin.id, moduleToInstall.src].join('/'), + id: moduleName, + pluginId: plugin.id + }; + if (moduleToInstall.clobbers.length > 0) { + obj.clobbers = moduleToInstall.clobbers.map(o => o.target); + } + if (moduleToInstall.merges.length > 0) { + obj.merges = moduleToInstall.merges.map(o => o.target); + } + if (moduleToInstall.runs) { + obj.runs = true; + } + + return obj; + }); - return obj; - }); + this._platformJson.root.modules = installedModules.concat(modulesToInstall); + if (!this._platformJson.root.plugin_metadata) { + this._platformJson.root.plugin_metadata = {}; + } + this._platformJson.root.plugin_metadata[plugin.id] = plugin.version; - this._platformJson.root.modules = installedModules.concat(modulesToInstall); - if (!this._platformJson.root.plugin_metadata) { - this._platformJson.root.plugin_metadata = {}; + this.#writePluginModules(targetDir); + this._platformJson.save(); } - this._platformJson.root.plugin_metadata[plugin.id] = plugin.version; - this._writePluginModules(targetDir); - this._platformJson.save(); -}; -/** - * Fetches all installed modules, generates cordova_plugins contents and writes - * it to file. - * - * @param {String} targetDir Directory, where write cordova_plugins.js to. - * Ususally it is either /www or /platform_www - * directories. - */ -Api.prototype._writePluginModules = function (targetDir) { - // Write out moduleObjects as JSON wrapped in a cordova module to cordova_plugins.js - let final_contents = 'cordova.define(\'cordova/plugin_list\', function(require, exports, module) {\n'; - final_contents += 'module.exports = ' + JSON.stringify(this._platformJson.root.modules, null, ' ') + ';\n'; - final_contents += 'module.exports.metadata = \n'; - final_contents += '// TOP OF METADATA\n'; - final_contents += JSON.stringify(this._platformJson.root.plugin_metadata || {}, null, ' ') + '\n'; - final_contents += '// BOTTOM OF METADATA\n'; - final_contents += '});'; // Close cordova.define. - - fs.mkdirSync(targetDir, { recursive: true }); - fs.writeFileSync(path.join(targetDir, 'cordova_plugins.js'), final_contents, 'utf-8'); -}; + /** + * Fetches all installed modules, generates cordova_plugins contents and writes + * it to file. + * + * @param {String} targetDir Directory, where write cordova_plugins.js to. + * Ususally it is either /www or /platform_www + * directories. + */ + #writePluginModules (targetDir) { + // Write out moduleObjects as JSON wrapped in a cordova module to cordova_plugins.js + let final_contents = 'cordova.define(\'cordova/plugin_list\', function(require, exports, module) {\n'; + final_contents += `module.exports = ${JSON.stringify(this._platformJson.root.modules, null, ' ')};\n`; + final_contents += 'module.exports.metadata = \n'; + final_contents += '// TOP OF METADATA\n'; + final_contents += JSON.stringify(this._platformJson.root.plugin_metadata || {}, null, ' ') + '\n'; + final_contents += '// BOTTOM OF METADATA\n'; + final_contents += '});'; // Close cordova.define. + + fs.mkdirSync(targetDir, { recursive: true }); + fs.writeFileSync(path.join(targetDir, 'cordova_plugins.js'), final_contents, 'utf-8'); + } -/** - * Removes the specified modules from list of installed modules and updates - * platform_json and cordova_plugins.js on disk. - * - * @param {PluginInfo} plugin PluginInfo instance for plugin, which modules - * needs to be removed. - * @param {String} targetDir The directory, where updated cordova_plugins.js - * should be written to. - */ -Api.prototype._removeModulesInfo = function (plugin, targetDir) { - const installedModules = this._platformJson.root.modules || []; - const modulesToRemove = plugin.getJsModules(this.platform) - .map(function (jsModule) { - return ['plugins', plugin.id, jsModule.src].join('/'); - }); - - const updatedModules = installedModules - .filter(function (installedModule) { - return (modulesToRemove.indexOf(installedModule.file) === -1); - }); - - this._platformJson.root.modules = updatedModules; - if (this._platformJson.root.plugin_metadata) { - delete this._platformJson.root.plugin_metadata[plugin.id]; + /** + * Removes the specified modules from list of installed modules and updates + * platform_json and cordova_plugins.js on disk. + * + * @param {PluginInfo} plugin PluginInfo instance for plugin, which modules + * needs to be removed. + * @param {String} targetDir The directory, where updated cordova_plugins.js + * should be written to. + */ + #removeModulesInfo (plugin, targetDir) { + const installedModules = this._platformJson.root.modules || []; + const modulesToRemove = plugin.getJsModules(this.platform) + .map(jsModule => ['plugins', plugin.id, jsModule.src].join('/')); + + const updatedModules = installedModules.filter( + installedModule => !modulesToRemove.includes(installedModule.file) + ); + + this._platformJson.root.modules = updatedModules; + if (this._platformJson.root.plugin_metadata) { + delete this._platformJson.root.plugin_metadata[plugin.id]; + } + + this.#writePluginModules(targetDir); + this._platformJson.save(); } - this._writePluginModules(targetDir); - this._platformJson.save(); -}; - -Api.prototype.build = function (buildOptions) { - const self = this; - return require('./check_reqs').run() - .then(function () { - return require('./build').run.call(self, buildOptions); - }); -}; - -Api.prototype.run = function (runOptions) { - return require('./run').run(runOptions); -}; - -Api.prototype.clean = function (cleanOptions) { - return require('./clean').run(cleanOptions); -}; - -Api.prototype.requirements = function () { - return require('./check_reqs').run(); -}; + async build (buildOptions) { + return require('./check_reqs').run() + .then(() => require('./build').run(buildOptions)); + } + + async run (runOptions) { + return require('./run').run(runOptions); + } + + async clean (cleanOptions) { + return require('./clean').run(cleanOptions); + } + + async requirements () { + return require('./check_reqs').run(); + } + + static version () { + return VERSION; + } +} module.exports = Api; diff --git a/lib/browser_handler.js b/lib/browser_handler.js index 94da890..fb786bb 100644 --- a/lib/browser_handler.js +++ b/lib/browser_handler.js @@ -19,7 +19,8 @@ const path = require('node:path'); const fs = require('node:fs'); -const events = require('cordova-common').events; + +const { events } = require('cordova-common'); module.exports = { www_dir: function (project_dir) { @@ -53,9 +54,9 @@ module.exports = { // Read in the file, prepend the cordova.define, and write it back out. let scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM if (moduleSource.match(/.*\.json$/)) { - scriptContent = 'module.exports = ' + scriptContent; + scriptContent = `module.exports = ${scriptContent}`; } - scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) { ' + scriptContent + '\n});\n'; + scriptContent = `cordova.define("${moduleName}", function(require, exports, module) { ${scriptContent}\n});\n`; const moduleDestination = path.resolve(www_dir, 'plugins', plugin_id, jsModule.src); fs.mkdirSync(path.dirname(moduleDestination), { recursive: true }); @@ -64,7 +65,7 @@ module.exports = { uninstall: function (jsModule, www_dir, plugin_id) { const pluginRelativePath = path.join('plugins', plugin_id, jsModule.src); // common.removeFileAndParents(www_dir, pluginRelativePath); - console.log('js-module uninstall called : ' + pluginRelativePath); + console.log(`js-module uninstall called : ${pluginRelativePath}`); } }, 'source-file': { diff --git a/lib/browser_parser.js b/lib/browser_parser.js index 50a1822..4f9fd01 100644 --- a/lib/browser_parser.js +++ b/lib/browser_parser.js @@ -19,9 +19,12 @@ const fs = require('node:fs'); const path = require('node:path'); -const CordovaError = require('cordova-common').CordovaError; -const events = require('cordova-common').events; -const FileUpdater = require('cordova-common').FileUpdater; + +const { + CordovaError, + FileUpdater, + events +} = require('cordova-common'); function dirExists (dir) { return fs.existsSync(dir) && fs.statSync(dir).isDirectory(); @@ -29,7 +32,7 @@ function dirExists (dir) { function browser_parser (project) { if (!dirExists(project) || !dirExists(path.join(project, 'cordova'))) { - throw new CordovaError('The provided path "' + project + '" is not a valid browser project.'); + throw new CordovaError(`The provided path "${project}" is not a valid browser project.`); } this.path = project; } @@ -44,7 +47,7 @@ browser_parser.prototype.www_dir = function () { * Logs all file operations via the verbose event stream, indented. */ function logFileOp (message) { - events.emit('verbose', ' ' + message); + events.emit('verbose', ` ${message}`); } // Replace the www dir with contents of platform_www and app www. @@ -67,7 +70,7 @@ browser_parser.prototype.update_www = function (cordovaProject, opts) { // targetDir points to browser/www const targetDir = path.relative(cordovaProject.root, my_www); - events.emit('verbose', 'Merging and updating files from [' + sourceDirs.join(', ') + '] to ' + targetDir); + events.emit('verbose', `Merging and updating files from [${sourceDirs.join(', ')}] to ${targetDir}`); FileUpdater.mergeAndUpdateDir(sourceDirs, targetDir, { rootDir: cordovaProject.root }, logFileOp); }; diff --git a/lib/build.js b/lib/build.js index b3ce5e1..32c1dbc 100644 --- a/lib/build.js +++ b/lib/build.js @@ -1,5 +1,3 @@ -#!/usr/bin/env node - /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file @@ -20,6 +18,7 @@ */ const path = require('node:path'); + const check_reqs = require('./check_reqs'); /** @@ -33,5 +32,5 @@ module.exports.run = function () { module.exports.help = function () { console.log('Usage: cordova build browser'); const wwwPath = path.resolve(path.join(__dirname, '../../www')); - console.log("Build will create the packaged app in '" + wwwPath + "'."); + console.log(`Build will create the packaged app in '${wwwPath}'.`); }; diff --git a/lib/check_reqs.js b/lib/check_reqs.js index fc13f04..32feeea 100644 --- a/lib/check_reqs.js +++ b/lib/check_reqs.js @@ -1,5 +1,3 @@ -#!/usr/bin/env node - /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file diff --git a/lib/clean.js b/lib/clean.js index c8f18ac..7ae5a4b 100644 --- a/lib/clean.js +++ b/lib/clean.js @@ -1,5 +1,3 @@ -#!/usr/bin/env node - /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file @@ -21,7 +19,9 @@ const fs = require('node:fs'); const path = require('node:path'); + const check_reqs = require('./check_reqs'); + const platformBuildDir = path.join('platforms', 'browser', 'www'); const run = function () { @@ -37,7 +37,7 @@ const run = function () { fs.rmSync(platformBuildDir, { recursive: true }); } } catch (err) { - console.log('could not remove ' + platformBuildDir + ' : ' + err.message); + console.log(`could not remove ${platformBuildDir} : ${err.message}`); } }; diff --git a/lib/create.js b/lib/create.js index f8ef75e..1b729b7 100644 --- a/lib/create.js +++ b/lib/create.js @@ -19,10 +19,15 @@ const fs = require('node:fs'); const path = require('node:path'); -const ROOT = path.join(__dirname, '..'); -const { CordovaError, events } = require('cordova-common'); +const { + CordovaError, + events +} = require('cordova-common'); + const check_reqs = require('./check_reqs'); +const ROOT = path.join(__dirname, '..'); + // exported method to create a project, returns a promise that resolves with null module.exports.createProject = function (project_path, package_name, project_name, opts = {}, root_config) { project_path = path.relative(process.cwd(), project_path); diff --git a/lib/run.js b/lib/run.js index 748523d..d3c8da0 100644 --- a/lib/run.js +++ b/lib/run.js @@ -1,5 +1,3 @@ -#!/usr/bin/env node - /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file @@ -22,6 +20,7 @@ const fs = require('node:fs'); const path = require('node:path'); const url = require('node:url'); + const cordovaServe = require('cordova-serve'); module.exports.run = function (args) { @@ -41,7 +40,7 @@ module.exports.run = function (args) { const manifest = require(manifestFilePath); startPage = manifest.start_url; } catch (err) { - console.log('failed to require manifest ... ' + err); + console.log(`failed to require manifest ... ${err}`); } } @@ -55,8 +54,8 @@ module.exports.run = function (args) { const projectUrl = (new url.URL(`http://localhost:${server.port}/${startPage}`)).href; - console.log('startPage = ' + startPage); - console.log('Static file server running @ ' + projectUrl + '\nCTRL + C to shut down'); + console.log(`startPage = ${startPage}`); + console.log(`Static file server running @ ${projectUrl}\nCTRL + C to shut down`); return server.launchBrowser({ target: args.target, url: projectUrl }); }) .catch(function (error) { diff --git a/lib/update.js b/lib/update.js index db712bf..7e62184 100644 --- a/lib/update.js +++ b/lib/update.js @@ -17,10 +17,12 @@ under the License. */ -const create = require('./create'); const fs = require('node:fs'); + const { CordovaError } = require('cordova-common'); +const create = require('./create'); + module.exports.help = function () { console.log('WARNING : Make sure to back up your project before updating!'); console.log('Usage: update PathToProject '); diff --git a/spec/browser_handler.spec.js b/spec/browser_handler.spec.js index f63c115..e3cc39f 100644 --- a/spec/browser_handler.spec.js +++ b/spec/browser_handler.spec.js @@ -17,10 +17,11 @@ under the License. */ -const browser_handler = require('../lib/browser_handler'); const fs = require('node:fs'); const path = require('node:path'); +const browser_handler = require('../lib/browser_handler'); + describe('Asset install tests', function () { let fsstatMock; const asset = { diff --git a/spec/projectApi.spec.js b/spec/projectApi.spec.js index 3342a71..bc18cb4 100644 --- a/spec/projectApi.spec.js +++ b/spec/projectApi.spec.js @@ -21,13 +21,12 @@ const path = require('node:path'); const { Module } = require('node:module'); const EventEmitter = require('node:events'); const tmp = require('tmp'); +const { ConfigParser } = require('cordova-common'); +const Api = require('../lib/Api'); process.env.NODE_PATH = path.resolve(__dirname, '../../'); Module._initPaths(); -const { ConfigParser } = require('cordova-common'); -const Api = require('../lib/Api'); - tmp.setGracefulCleanup(); function makeTempDir () { diff --git a/templates/cordova/version b/templates/cordova/version index 7b0de4c..43e2e9f 100755 --- a/templates/cordova/version +++ b/templates/cordova/version @@ -19,7 +19,6 @@ under the License. */ -// Coho updates this line: -const VERSION = '8.0.0-dev'; +const Api = require('./Api'); -console.log(VERSION); +console.log(Api.version());