You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ra...@apache.org on 2020/01/09 22:23:49 UTC
[cordova-common] branch master updated: refactor(PluginInfo):
cleanup & simplify (#132)
This is an automated email from the ASF dual-hosted git repository.
raphinesse pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cordova-common.git
The following commit(s) were added to refs/heads/master by this push:
new b779a34 refactor(PluginInfo): cleanup & simplify (#132)
b779a34 is described below
commit b779a345e8dac4c658a1816ba595a6b95db47fa2
Author: Raphael von der Grün <ra...@gmail.com>
AuthorDate: Thu Jan 9 23:23:39 2020 +0100
refactor(PluginInfo): cleanup & simplify (#132)
* refactor: PluginInfo
* _getTags: simplify
* _getTagsInPlatform: support a list of platforms
* getPodSpecs: remove useless undefined value filtering
* getPodSpecs: further improve formatting
* getPreferences: simplify
* getHookScripts: simplify
* getFrameworks: only determine vars once
* getFrameworks: improve readability
* ctor: improve
* ctor: better var name for root elem
* destructure xml-helpers
* inline addCordova helper
* apply lebab.destruct-params
* convert comments to JSDocs
* remove outdated TODO; sync is faster
* map tags explicitly
* make _getTags* private methods
Co-authored-by: エリス <er...@users.noreply.github.com>
---
src/PluginInfo/PluginInfo.js | 485 +++++++++++++++++++++++--------------------
1 file changed, 261 insertions(+), 224 deletions(-)
diff --git a/src/PluginInfo/PluginInfo.js b/src/PluginInfo/PluginInfo.js
index f54509b..d360664 100644
--- a/src/PluginInfo/PluginInfo.js
+++ b/src/PluginInfo/PluginInfo.js
@@ -17,101 +17,112 @@
under the License.
*/
-/*
-A class for holidng the information currently stored in plugin.xml
-It should also be able to answer questions like whether the plugin
-is compatible with a given engine version.
-
-TODO (kamrik): refactor this to not use sync functions and return promises.
-*/
-
const path = require('path');
const fs = require('fs-extra');
-const xml_helpers = require('../util/xml-helpers');
+const { parseElementtreeSync } = require('../util/xml-helpers');
const CordovaError = require('../CordovaError');
+/**
+ * A class for holding the information currently stored in plugin.xml
+ *
+ * It should also be able to answer questions like whether the plugin
+ * is compatible with a given engine version.
+ */
class PluginInfo {
constructor (dirname) {
+ this.dir = dirname;
this.filepath = path.join(dirname, 'plugin.xml');
+
if (!fs.existsSync(this.filepath)) {
throw new CordovaError(`Cannot find plugin.xml for plugin "${path.basename(dirname)}". Please try adding it again.`);
}
- this.dir = dirname;
- const et = this._et = xml_helpers.parseElementtreeSync(this.filepath);
- const pelem = et.getroot();
- this.id = pelem.attrib.id;
- this.version = pelem.attrib.version;
+ this._et = parseElementtreeSync(this.filepath);
+ const root = this._et.getroot();
+
+ this.id = root.attrib.id;
+ this.version = root.attrib.version;
// Optional fields
- this.name = pelem.findtext('name');
- this.description = pelem.findtext('description');
- this.license = pelem.findtext('license');
- this.repo = pelem.findtext('repo');
- this.issue = pelem.findtext('issue');
- this.keywords = pelem.findtext('keywords');
- this.info = pelem.findtext('info');
- if (this.keywords) {
- this.keywords = this.keywords.split(',').map(s => s.trim());
+ const optTags = 'name description license repo issue info'.split(' ');
+ for (const tag of optTags) {
+ this[tag] = root.findtext(tag);
}
+
+ const keywordText = root.findtext('keywords');
+ this.keywords = keywordText && keywordText.split(',').map(s => s.trim());
}
- // <preference> tag
- // Example: <preference name="API_KEY" />
- // Used to require a variable to be specified via --variable when installing the plugin.
- // returns { key : default | null}
+ /**
+ * <preference> tag
+ *
+ * Used to require a variable to be specified via --variable when installing the plugin.
+ *
+ * @example <preference name="API_KEY" />
+ *
+ * @param {string} platform
+ * @return {Object} { key : default | null}
+ */
getPreferences (platform) {
- return _getTags(this._et, 'preference', platform, prefTag => {
- const name = prefTag.attrib.name.toUpperCase();
- const def = prefTag.attrib.default || null;
- return { preference: name, default: def };
- })
- .reduce((preferences, pref) => {
- preferences[pref.preference] = pref.default;
- return preferences;
- }, {});
+ return this._getTags('preference', platform).map(({ attrib }) => ({
+ [attrib.name.toUpperCase()]: attrib.default || null
+ }))
+ .reduce((acc, pref) => Object.assign(acc, pref), {});
}
- // <asset>
+ /**
+ * <asset>
+ *
+ * @param {string} platform
+ */
getAssets (platform) {
- return _getTags(this._et, 'asset', platform, tag => {
- const src = tag.attrib.src;
- const target = tag.attrib.target;
+ return this._getTags('asset', platform).map(({ attrib }) => {
+ const src = attrib.src;
+ const target = attrib.target;
if (!src || !target) {
- throw new Error(`Malformed <asset> tag. Both "src" and "target" attributes must be specified in\n${this.filepath}`);
+ throw new Error(`Malformed <asset> tag. Both "src" and "target" attributes must be specified in ${this.filepath}`);
}
return { itemType: 'asset', src, target };
});
}
- // <dependency>
- // Example:
- // <dependency id="com.plugin.id"
- // url="https://github.com/myuser/someplugin"
- // commit="428931ada3891801"
- // subdir="some/path/here" />
+ /**
+ * <dependency>
+ *
+ * @example
+ * <dependency id="com.plugin.id"
+ * url="https://github.com/myuser/someplugin"
+ * commit="428931ada3891801"
+ * subdir="some/path/here" />
+ *
+ * @param {string} platform
+ */
getDependencies (platform) {
- return _getTags(this._et, 'dependency', platform, tag => {
- if (!tag.attrib.id) {
+ return this._getTags('dependency', platform).map(({ attrib }) => {
+ if (!attrib.id) {
throw new CordovaError(`<dependency> tag is missing id attribute in ${this.filepath}`);
}
return {
- id: tag.attrib.id,
- version: tag.attrib.version || '',
- url: tag.attrib.url || '',
- subdir: tag.attrib.subdir || '',
- commit: tag.attrib.commit,
- git_ref: tag.attrib.commit
+ id: attrib.id,
+ version: attrib.version || '',
+ url: attrib.url || '',
+ subdir: attrib.subdir || '',
+ commit: attrib.commit,
+ git_ref: attrib.commit
};
});
}
- // <config-file> tag
+ /**
+ * <config-file> tag
+ *
+ * @param {string} platform
+ */
getConfigFiles (platform) {
- return _getTags(this._et, 'config-file', platform, tag => ({
+ return this._getTags('config-file', platform).map(tag => ({
target: tag.attrib.target,
parent: tag.attrib.parent,
after: tag.attrib.after,
@@ -122,8 +133,13 @@ class PluginInfo {
}));
}
+ /**
+ * <edit-config> tag
+ *
+ * @param {string} platform
+ */
getEditConfigs (platform) {
- return _getTags(this._et, 'edit-config', platform, tag => ({
+ return this._getTags('edit-config', platform).map(tag => ({
file: tag.attrib.file,
target: tag.attrib.target,
mode: tag.attrib.mode,
@@ -131,137 +147,163 @@ class PluginInfo {
}));
}
- // <info> tags, both global and within a <platform>
+ /**
+ * <info> tags, both global and within a <platform>
+ *
+ * @param {string} platform
+ */
// TODO (kamrik): Do we ever use <info> under <platform>? Example wanted.
getInfo (platform) {
- return _getTags(this._et, 'info', platform, elem => elem.text)
+ return this._getTags('info', platform).map(elem => elem.text)
// Filter out any undefined or empty strings.
.filter(Boolean);
}
- // <source-file>
- // Examples:
- // <source-file src="src/ios/someLib.a" framework="true" />
- // <source-file src="src/ios/someLib.a" compiler-flags="-fno-objc-arc" />
+ /**
+ * <source-file>
+ *
+ * @example
+ * <source-file src="src/ios/someLib.a" framework="true" />
+ * <source-file src="src/ios/someLib.a" compiler-flags="-fno-objc-arc" />
+ *
+ * @param {string} platform
+ */
getSourceFiles (platform) {
- return _getTagsInPlatform(this._et, 'source-file', platform, tag => ({
+ return this._getTagsInPlatform('source-file', platform).map(({ attrib }) => ({
itemType: 'source-file',
- src: tag.attrib.src,
- framework: isStrTrue(tag.attrib.framework),
- weak: isStrTrue(tag.attrib.weak),
- compilerFlags: tag.attrib['compiler-flags'],
- targetDir: tag.attrib['target-dir']
+ src: attrib.src,
+ framework: isStrTrue(attrib.framework),
+ weak: isStrTrue(attrib.weak),
+ compilerFlags: attrib['compiler-flags'],
+ targetDir: attrib['target-dir']
}));
}
- // <header-file>
- // Example:
- // <header-file src="CDVFoo.h" />
+ /**
+ * <header-file>
+ *
+ * @example <header-file src="CDVFoo.h" />
+ *
+ * @param {string} platform
+ */
getHeaderFiles (platform) {
- return _getTagsInPlatform(this._et, 'header-file', platform, tag => ({
+ return this._getTagsInPlatform('header-file', platform).map(({ attrib }) => ({
itemType: 'header-file',
- src: tag.attrib.src,
- targetDir: tag.attrib['target-dir'],
- type: tag.attrib.type
+ src: attrib.src,
+ targetDir: attrib['target-dir'],
+ type: attrib.type
}));
}
- // <resource-file>
- // Example:
- // <resource-file src="FooPluginStrings.xml" target="res/values/FooPluginStrings.xml" device-target="win" arch="x86" versions=">=8.1" />
+ /**
+ * <resource-file>
+ *
+ * @example
+ * <resource-file
+ * src="FooPluginStrings.xml"
+ * target="res/values/FooPluginStrings.xml"
+ * device-target="win"
+ * arch="x86"
+ * versions=">=8.1"
+ * />
+ *
+ * @param {string} platform
+ */
getResourceFiles (platform) {
- return _getTagsInPlatform(this._et, 'resource-file', platform, tag => ({
+ return this._getTagsInPlatform('resource-file', platform).map(({ attrib }) => ({
itemType: 'resource-file',
- src: tag.attrib.src,
- target: tag.attrib.target,
- versions: tag.attrib.versions,
- deviceTarget: tag.attrib['device-target'],
- arch: tag.attrib.arch,
- reference: tag.attrib.reference
+ src: attrib.src,
+ target: attrib.target,
+ versions: attrib.versions,
+ deviceTarget: attrib['device-target'],
+ arch: attrib.arch,
+ reference: attrib.reference
}));
}
- // <lib-file>
- // Example:
- // <lib-file src="src/BlackBerry10/native/device/libfoo.so" arch="device" />
+ /**
+ * <lib-file>
+ *
+ * @example
+ * <lib-file src="src/BlackBerry10/native/device/libfoo.so" arch="device" />
+ *
+ * @param {string} platform
+ */
getLibFiles (platform) {
- return _getTagsInPlatform(this._et, 'lib-file', platform, tag => ({
+ return this._getTagsInPlatform('lib-file', platform).map(({ attrib }) => ({
itemType: 'lib-file',
- src: tag.attrib.src,
- arch: tag.attrib.arch,
- Include: tag.attrib.Include,
- versions: tag.attrib.versions,
- deviceTarget: tag.attrib['device-target'] || tag.attrib.target
+ src: attrib.src,
+ arch: attrib.arch,
+ Include: attrib.Include,
+ versions: attrib.versions,
+ deviceTarget: attrib['device-target'] || attrib.target
}));
}
- // <podspec>
- // Example:
- // <podspec>
- // <config>
- // <source url="https://github.com/brightcove/BrightcoveSpecs.git" />
- // <source url="https://github.com/CocoaPods/Specs.git"/>
- // </config>
- // <pods use-frameworks="true" inhibit-all-warnings="true">
- // <pod name="PromiseKit" />
- // <pod name="Foobar1" spec="~> 2.0.0" />
- // <pod name="Foobar2" git="git@github.com:hoge/foobar1.git" />
- // <pod name="Foobar3" git="git@github.com:hoge/foobar2.git" branch="next" />
- // <pod name="Foobar4" swift-version="4.1" />
- // <pod name="Foobar5" swift-version="3.0" />
- // </pods>
- // </podspec>
+ /**
+ * <podspec>
+ *
+ * @example
+ * <podspec>
+ * <config>
+ * <source url="https://github.com/brightcove/BrightcoveSpecs.git" />
+ * <source url="https://github.com/CocoaPods/Specs.git"/>
+ * </config>
+ * <pods use-frameworks="true" inhibit-all-warnings="true">
+ * <pod name="PromiseKit" />
+ * <pod name="Foobar1" spec="~> 2.0.0" />
+ * <pod name="Foobar2" git="git@github.com:hoge/foobar1.git" />
+ * <pod name="Foobar3" git="git@github.com:hoge/foobar2.git" branch="next" />
+ * <pod name="Foobar4" swift-version="4.1" />
+ * <pod name="Foobar5" swift-version="3.0" />
+ * </pods>
+ * </podspec>
+ *
+ * @param {string} platform
+ */
getPodSpecs (platform) {
- return _getTagsInPlatform(this._et, 'podspec', platform, tag => {
- let declarations = null;
- let sources = null;
- let libraries = null;
+ return this._getTagsInPlatform('podspec', platform).map(tag => {
const config = tag.find('config');
const pods = tag.find('pods');
- if (config != null) {
- sources = config.findall('source').map(t => ({
- url: t.attrib.url
- })).reduce((acc, val) => {
- return Object.assign({}, acc, { [val.url]: { source: val.url } });
- }, {});
- }
- if (pods != null) {
- declarations = Object.keys(pods.attrib).reduce((acc, key) => {
- return pods.attrib[key] === undefined ? acc : Object.assign({}, acc, { [key]: pods.attrib[key] });
- }, {});
- libraries = pods.findall('pod').map(t => {
- return Object.keys(t.attrib).reduce((acc, key) => {
- return t.attrib[key] === undefined ? acc : Object.assign({}, acc, { [key]: t.attrib[key] });
- }, {});
- }).reduce((acc, val) => {
- return Object.assign({}, acc, { [val.name]: val });
- }, {});
- }
+
+ const sources = config && config.findall('source')
+ .map(el => ({ source: el.attrib.url }))
+ .reduce((acc, val) => Object.assign(acc, { [val.source]: val }), {});
+
+ const declarations = pods && pods.attrib;
+
+ const libraries = pods && pods.findall('pod')
+ .map(t => t.attrib)
+ .reduce((acc, val) => Object.assign(acc, { [val.name]: val }), {});
+
return { declarations, sources, libraries };
});
}
- // <hook>
- // Example:
- // <hook type="before_build" src="scripts/beforeBuild.js" />
+ /**
+ * <hook>
+ *
+ * @example
+ * <hook type="before_build" src="scripts/beforeBuild.js" />
+ *
+ * @param {string} hook
+ * @param {string} platforms
+ */
getHookScripts (hook, platforms) {
- let scriptElements = this._et.findall('./hook');
-
- if (platforms) {
- platforms.forEach(platform => {
- scriptElements = scriptElements.concat(this._et.findall(`./platform[@name="${platform}"]/hook`));
- });
- }
-
- function filterScriptByHookType (el) {
- return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
- }
-
- return scriptElements.filter(filterScriptByHookType);
+ return this._getTags('hook', platforms)
+ .filter(({ attrib }) =>
+ attrib.src && attrib.type &&
+ attrib.type.toLowerCase() === hook
+ );
}
+ /**
+ * <js-module>
+ *
+ * @param {string} platform
+ */
getJsModules (platform) {
- return _getTags(this._et, 'js-module', platform, tag => ({
+ return this._getTags('js-module', platform).map(tag => ({
itemType: 'js-module',
name: tag.attrib.name,
src: tag.attrib.src,
@@ -272,18 +314,16 @@ class PluginInfo {
}
getEngines () {
- return this._et.findall('engines/engine').map(n => ({
- name: n.attrib.name,
- version: n.attrib.version,
- platform: n.attrib.platform,
- scriptSrc: n.attrib.scriptSrc
+ return this._et.findall('engines/engine').map(({ attrib }) => ({
+ name: attrib.name,
+ version: attrib.version,
+ platform: attrib.platform,
+ scriptSrc: attrib.scriptSrc
}));
}
getPlatforms () {
- return this._et.findall('platform').map(n => ({
- name: n.attrib.name
- }));
+ return this._et.findall('platform').map(n => ({ name: n.attrib.name }));
}
getPlatformsArray () {
@@ -291,41 +331,36 @@ class PluginInfo {
}
getFrameworks (platform, options) {
- return _getTags(this._et, 'framework', platform, el => {
- let src = el.attrib.src;
- if (options) {
- let vars = options.cli_variables || {};
-
- if (Object.keys(vars).length === 0) {
- // get variable defaults from plugin.xml for removal
- vars = this.getPreferences(platform);
- }
- let regExp;
- // Iterate over plugin variables.
- // Replace them in framework src if they exist
- Object.keys(vars).forEach(name => {
- if (vars[name]) {
- regExp = new RegExp(`\\$${name}`, 'g');
- src = src.replace(regExp, vars[name]);
- }
- });
- }
- return {
- itemType: 'framework',
- type: el.attrib.type,
- parent: el.attrib.parent,
- custom: isStrTrue(el.attrib.custom),
- embed: isStrTrue(el.attrib.embed),
- src,
- spec: el.attrib.spec,
- weak: isStrTrue(el.attrib.weak),
- versions: el.attrib.versions,
- targetDir: el.attrib['target-dir'],
- deviceTarget: el.attrib['device-target'] || el.attrib.target,
- arch: el.attrib.arch,
- implementation: el.attrib.implementation
- };
- });
+ const { cli_variables = {} } = options || {};
+
+ const vars = Object.keys(cli_variables).length === 0
+ ? this.getPreferences(platform)
+ : cli_variables;
+
+ const varExpansions = Object.entries(vars)
+ .filter(([, value]) => value)
+ .map(([name, value]) =>
+ s => s.replace(new RegExp(`\\$${name}`, 'g'), value)
+ );
+
+ // Replaces plugin variables in s if they exist
+ const expandVars = s => varExpansions.reduce((acc, fn) => fn(acc), s);
+
+ return this._getTags('framework', platform).map(({ attrib }) => ({
+ itemType: 'framework',
+ type: attrib.type,
+ parent: attrib.parent,
+ custom: isStrTrue(attrib.custom),
+ embed: isStrTrue(attrib.embed),
+ src: expandVars(attrib.src),
+ spec: attrib.spec,
+ weak: isStrTrue(attrib.weak),
+ versions: attrib.versions,
+ targetDir: attrib['target-dir'],
+ deviceTarget: attrib['device-target'] || attrib.target,
+ arch: attrib.arch,
+ implementation: attrib.implementation
+ }));
}
getFilesAndFrameworks (platform, options) {
@@ -343,40 +378,41 @@ class PluginInfo {
getKeywordsAndPlatforms () {
return (this.keywords || [])
.concat('ecosystem:cordova')
- .concat(addCordova(this.getPlatformsArray()));
+ .concat(this.getPlatformsArray().map(p => `cordova-${p}`));
}
-}
-// Helper function used to prefix every element of an array with cordova-
-// Useful when we want to modify platforms to be cordova-platform
-function addCordova (someArray) {
- return someArray.map(element => `cordova-${element}`);
-}
-
-// Helper function used by most of the getSomething methods of PluginInfo.
-// Get all elements of a given name. Both in root and in platform sections
-// for the given platform. If transform is given and is a function, it is
-// applied to each element.
-function _getTags (pelem, tag, platform, transform) {
- const platformTag = pelem.find(`./platform[@name="${platform}"]`);
- let tagsInRoot = pelem.findall(tag);
- tagsInRoot = tagsInRoot || [];
- const tagsInPlatform = platformTag ? platformTag.findall(tag) : [];
- let tags = tagsInRoot.concat(tagsInPlatform);
- if (typeof transform === 'function') {
- tags = tags.map(transform);
+ /**
+ * Helper method used by most of the getSomething methods of PluginInfo.
+ *
+ * Get all elements of a given name. Both in root and in platform sections
+ * for the given platform.
+ *
+ * @private
+ *
+ * @param {string} tag
+ * @param {string|string[]} platform
+ */
+ _getTags (tag, platform) {
+ return this._et.findall(tag)
+ .concat(this._getTagsInPlatform(tag, platform));
}
- return tags;
-}
-// Same as _getTags() but only looks inside a platform section.
-function _getTagsInPlatform (pelem, tag, platform, transform) {
- const platformTag = pelem.find(`./platform[@name="${platform}"]`);
- let tags = platformTag ? platformTag.findall(tag) : [];
- if (typeof transform === 'function') {
- tags = tags.map(transform);
+ /**
+ * Same as _getTags() but only looks inside a platform section.
+ *
+ * @private
+ *
+ * @param {string} tag
+ * @param {string|string[]} platform
+ */
+ _getTagsInPlatform (tag, platform) {
+ const platforms = [].concat(platform);
+
+ return [].concat(...platforms.map(platform => {
+ const platformTag = this._et.find(`./platform[@name="${platform}"]`);
+ return platformTag ? platformTag.findall(tag) : [];
+ }));
}
- return tags;
}
// Check if x is a string 'true'.
@@ -385,6 +421,7 @@ function isStrTrue (x) {
}
module.exports = PluginInfo;
+
// Backwards compat:
PluginInfo.PluginInfo = PluginInfo;
PluginInfo.loadPluginsDir = dir => {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org