UNPKG

9.74 kBJavaScriptView Raw
1/*
2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing,
9 * software distributed under the License is distributed on an
10 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the
12 * specific language governing permissions and limitations
13 * under the License.
14 *
15*/
16
17var fs = require('fs');
18var path = require('path');
19var shelljs = require('shelljs');
20var mungeutil = require('./ConfigChanges/munge-util');
21var pluginMappernto = require('cordova-registry-mapper').newToOld;
22var pluginMapperotn = require('cordova-registry-mapper').oldToNew;
23
24function PlatformJson (filePath, platform, root) {
25 this.filePath = filePath;
26 this.platform = platform;
27 this.root = fix_munge(root || {});
28}
29
30PlatformJson.load = function (plugins_dir, platform) {
31 var filePath = path.join(plugins_dir, platform + '.json');
32 var root = null;
33 if (fs.existsSync(filePath)) {
34 root = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
35 }
36 return new PlatformJson(filePath, platform, root);
37};
38
39PlatformJson.prototype.save = function () {
40 shelljs.mkdir('-p', path.dirname(this.filePath));
41 fs.writeFileSync(this.filePath, JSON.stringify(this.root, null, 2), 'utf-8');
42};
43
44/**
45 * Indicates whether the specified plugin is installed as a top-level (not as
46 * dependency to others)
47 * @method function
48 * @param {String} pluginId A plugin id to check for.
49 * @return {Boolean} true if plugin installed as top-level, otherwise false.
50 */
51PlatformJson.prototype.isPluginTopLevel = function (pluginId) {
52 var installedPlugins = this.root.installed_plugins;
53 return installedPlugins[pluginId] ||
54 installedPlugins[pluginMappernto[pluginId]] ||
55 installedPlugins[pluginMapperotn[pluginId]];
56};
57
58/**
59 * Indicates whether the specified plugin is installed as a dependency to other
60 * plugin.
61 * @method function
62 * @param {String} pluginId A plugin id to check for.
63 * @return {Boolean} true if plugin installed as a dependency, otherwise false.
64 */
65PlatformJson.prototype.isPluginDependent = function (pluginId) {
66 var dependentPlugins = this.root.dependent_plugins;
67 return dependentPlugins[pluginId] ||
68 dependentPlugins[pluginMappernto[pluginId]] ||
69 dependentPlugins[pluginMapperotn[pluginId]];
70};
71
72/**
73 * Indicates whether plugin is installed either as top-level or as dependency.
74 * @method function
75 * @param {String} pluginId A plugin id to check for.
76 * @return {Boolean} true if plugin installed, otherwise false.
77 */
78PlatformJson.prototype.isPluginInstalled = function (pluginId) {
79 return this.isPluginTopLevel(pluginId) ||
80 this.isPluginDependent(pluginId);
81};
82
83PlatformJson.prototype.addPlugin = function (pluginId, variables, isTopLevel) {
84 var pluginsList = isTopLevel ?
85 this.root.installed_plugins :
86 this.root.dependent_plugins;
87
88 pluginsList[pluginId] = variables;
89
90 return this;
91};
92
93/**
94 * @chaining
95 * Generates and adds metadata for provided plugin into associated <platform>.json file
96 *
97 * @param {PluginInfo} pluginInfo A pluginInfo instance to add metadata from
98 * @returns {this} Current PlatformJson instance to allow calls chaining
99 */
100PlatformJson.prototype.addPluginMetadata = function (pluginInfo) {
101
102 var installedModules = this.root.modules || [];
103
104 var installedPaths = installedModules.map(function (installedModule) {
105 return installedModule.file;
106 });
107
108 var modulesToInstall = pluginInfo.getJsModules(this.platform)
109 .map(function (module) {
110 return new ModuleMetadata(pluginInfo.id, module);
111 })
112 .filter(function (metadata) {
113 // Filter out modules which are already added to metadata
114 return installedPaths.indexOf(metadata.file) === -1;
115 });
116
117 this.root.modules = installedModules.concat(modulesToInstall);
118
119 this.root.plugin_metadata = this.root.plugin_metadata || {};
120 this.root.plugin_metadata[pluginInfo.id] = pluginInfo.version;
121
122 return this;
123};
124
125PlatformJson.prototype.removePlugin = function (pluginId, isTopLevel) {
126 var pluginsList = isTopLevel ?
127 this.root.installed_plugins :
128 this.root.dependent_plugins;
129
130 delete pluginsList[pluginId];
131
132 return this;
133};
134
135/**
136 * @chaining
137 * Removes metadata for provided plugin from associated file
138 *
139 * @param {PluginInfo} pluginInfo A PluginInfo instance to which modules' metadata
140 * we need to remove
141 *
142 * @returns {this} Current PlatformJson instance to allow calls chaining
143 */
144PlatformJson.prototype.removePluginMetadata = function (pluginInfo) {
145 var modulesToRemove = pluginInfo.getJsModules(this.platform)
146 .map(function (jsModule) {
147 return ['plugins', pluginInfo.id, jsModule.src].join('/');
148 });
149
150 var installedModules = this.root.modules || [];
151 this.root.modules = installedModules
152 .filter(function (installedModule) {
153 // Leave only those metadatas which 'file' is not in removed modules
154 return (modulesToRemove.indexOf(installedModule.file) === -1);
155 });
156
157 if (this.root.plugin_metadata) {
158 delete this.root.plugin_metadata[pluginInfo.id];
159 }
160
161 return this;
162};
163
164PlatformJson.prototype.addInstalledPluginToPrepareQueue = function (pluginDirName, vars, is_top_level, force) {
165 this.root.prepare_queue.installed.push({'plugin': pluginDirName, 'vars': vars, 'topLevel': is_top_level, 'force': force});
166};
167
168PlatformJson.prototype.addUninstalledPluginToPrepareQueue = function (pluginId, is_top_level) {
169 this.root.prepare_queue.uninstalled.push({'plugin': pluginId, 'id': pluginId, 'topLevel': is_top_level});
170};
171
172/**
173 * Moves plugin, specified by id to top-level plugins. If plugin is top-level
174 * already, then does nothing.
175 * @method function
176 * @param {String} pluginId A plugin id to make top-level.
177 * @return {PlatformJson} PlatformJson instance.
178 */
179PlatformJson.prototype.makeTopLevel = function (pluginId) {
180 var plugin = this.root.dependent_plugins[pluginId];
181 if (plugin) {
182 delete this.root.dependent_plugins[pluginId];
183 this.root.installed_plugins[pluginId] = plugin;
184 }
185 return this;
186};
187
188/**
189 * Generates a metadata for all installed plugins and js modules. The resultant
190 * string is ready to be written to 'cordova_plugins.js'
191 *
192 * @returns {String} cordova_plugins.js contents
193 */
194PlatformJson.prototype.generateMetadata = function () {
195 return [
196 'cordova.define(\'cordova/plugin_list\', function(require, exports, module) {',
197 'module.exports = ' + JSON.stringify(this.root.modules, null, 2) + ';',
198 'module.exports.metadata = ',
199 '// TOP OF METADATA',
200 JSON.stringify(this.root.plugin_metadata, null, 2) + ';',
201 '// BOTTOM OF METADATA',
202 '});' // Close cordova.define.
203 ].join('\n');
204};
205
206/**
207 * @chaining
208 * Generates and then saves metadata to specified file. Doesn't check if file exists.
209 *
210 * @param {String} destination File metadata will be written to
211 * @return {PlatformJson} PlatformJson instance
212 */
213PlatformJson.prototype.generateAndSaveMetadata = function (destination) {
214 var meta = this.generateMetadata();
215 shelljs.mkdir('-p', path.dirname(destination));
216 fs.writeFileSync(destination, meta, 'utf-8');
217
218 return this;
219};
220
221// convert a munge from the old format ([file][parent][xml] = count) to the current one
222function fix_munge (root) {
223 root.prepare_queue = root.prepare_queue || {installed: [], uninstalled: []};
224 root.config_munge = root.config_munge || {files: {}};
225 root.installed_plugins = root.installed_plugins || {};
226 root.dependent_plugins = root.dependent_plugins || {};
227
228 var munge = root.config_munge;
229 if (!munge.files) {
230 var new_munge = { files: {} };
231 for (var file in munge) {
232 for (var selector in munge[file]) {
233 for (var xml_child in munge[file][selector]) {
234 var val = parseInt(munge[file][selector][xml_child]);
235 for (var i = 0; i < val; i++) {
236 mungeutil.deep_add(new_munge, [file, selector, { xml: xml_child, count: val }]);
237 }
238 }
239 }
240 }
241 root.config_munge = new_munge;
242 }
243
244 return root;
245}
246
247/**
248 * @constructor
249 * @class ModuleMetadata
250 *
251 * Creates a ModuleMetadata object that represents module entry in 'cordova_plugins.js'
252 * file at run time
253 *
254 * @param {String} pluginId Plugin id where this module installed from
255 * @param (JsModule|Object) jsModule A js-module entry from PluginInfo class to generate metadata for
256 */
257function ModuleMetadata (pluginId, jsModule) {
258
259 if (!pluginId) throw new TypeError('pluginId argument must be a valid plugin id');
260 if (!jsModule.src && !jsModule.name) throw new TypeError('jsModule argument must contain src or/and name properties');
261
262 this.id = pluginId + '.' + (jsModule.name || jsModule.src.match(/([^\/]+)\.js/)[1]); /* eslint no-useless-escape: 0 */
263 this.file = ['plugins', pluginId, jsModule.src].join('/');
264 this.pluginId = pluginId;
265
266 if (jsModule.clobbers && jsModule.clobbers.length > 0) {
267 this.clobbers = jsModule.clobbers.map(function (o) { return o.target; });
268 }
269 if (jsModule.merges && jsModule.merges.length > 0) {
270 this.merges = jsModule.merges.map(function (o) { return o.target; });
271 }
272 if (jsModule.runs) {
273 this.runs = true;
274 }
275}
276
277module.exports = PlatformJson;