├── .gitignore ├── LICENSE ├── README.md ├── hooks ├── addTodayWidgetToProject.js ├── copyExtensionFolderToIosProjectFolder.js ├── fixAppEntitlements.js └── prerequisites.js ├── package-lock.json ├── package.json └── plugin.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.perspectivev3 3 | *.pbxuser 4 | .DS_Store 5 | build/ 6 | node_modules/ 7 | .idea/ 8 | .vscode/ 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 DavidStrausz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cordova Plugin for adding a Today Widget to an existing iOS Project dynamically 2 | 3 | This plugin extends your existing xcode project by parsing and modifying the project.pbxproj file using [cordova-node-xcode](https://github.com/apache/cordova-node-xcode). The today extension will be added to the XCode-Project everytime a `cordova platform add ios` is done. 4 | 5 | ## Usage 6 | 7 | ### 1. First of all you have to create a Today Widget yourself using XCode (`Editor > Add target > Today Extension`) 8 | 9 | * Fill in the fields, making note of the following: 10 | * Remember the name of the widget 11 | * Remember the last part of the bundle identifier (the suffix) 12 | * Enable the `App Groups` entitlement (`Targets > Select your widget > Capabilities`) and name your group: `group.` (you can use the group to share NSUserDefaults between the Widget and the main App). _Note that you have to add this to your provisioning profile_ 13 | * Implement your widget using `TodayViewController.swift` and `MainInterface.storyboard` (you can add additional source-files too). 14 | * When done implementing copy the `` folder from `` to anywhere tracked by your repository. 15 | * If your `MainInterface.storyboard` is listed in a sub-older named `Base.lproj`, pull it out of the folder and delete the folder. (there is no handling of variant-groups for different languages) 16 | * If you want to use an objective-c bridging header you can add it to the folder, just make sure it is named `Header.h` (`Bridging-Header.h` works too but the file won't be listed in XCode because the cordova bridging header has the same name and node-xcode thinks's it's the same file because it's checking the name and not the UUID) 17 | * If you need to add custom build settings you can use a xcconfig file, the script will add it to the project 18 | * Every file that is not a `.swift`, `.h`, `.m`, `.plist`, `.entitlements`, `.xcconfig` or `.storyboard` file will be added as a resource file to the project (images, fonts, etc.) 19 | 20 | ### 2. Install the plugin 21 | * `cordova plugin add https://github.com/DavidStrausz/cordova-plugin-today-widget.git --save` 22 | * This will not modify anything yet because the hooks only run `after_platform_add` 23 | * You can add variables to your `config.xml` in order to change some of the settings: 24 | 25 | | Variable | Default | Description | 26 | |-|-|-| 27 | |WIDGET_PATH| `/www` | Path to the folder that contains your widget folder relative to the project root | 28 | |WIDGET_NAME| Widget | Name of your widget | 29 | |WIDGET_BUNDLE_SUFFIX| widget | The last part of the widget bundle id | 30 | |ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES| YES | You might have to turn this off (change to NO) if you use other swift based plugins (such as cordova-plugin-geofence) | 31 | |SWIFT_VERSION| '3.0' | The version of Swift that your widget uses | 32 | 33 | This can be done either manually in the config.xml after installing the plugin, or be done through the CLI. 34 | 35 | #### Example: 36 | 37 | In the config.xml 38 | 39 | ``` 40 | 41 | 42 | 43 | 44 | ``` 45 | 46 | Directly through CLI: 47 | 48 | ``` 49 | cordova plugin add cordova-plugin-today-widget --variable WIDGET_NAME="NowWidget" --variable ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES="NO" 50 | ``` 51 | 52 | ### 3. Parametrization 53 | Especially for automated builds, parametrization is an important part. The following parameters are available: 54 | 55 | | Variable | Example | Description | 56 | |-|-|-| 57 | |\_\_DISPLAY_NAME__| AppName | Name of the original app | 58 | |\_\_APP_IDENTIFIER__| com.company.app | Bundle ID of the main app | 59 | |\_\_BUNDLE_SUFFIX__| widget | Bundle ID suffix for the widget | 60 | |\_\_BUNDLE_SHORT_VERSION_STRING__| 1.0.0 | The version of the main app in form MAJOR.MINOR.PATCH | 61 | |\_\_BUNDLE_VERSION__| 1234 | The build number of the main app 62 | 63 | These parameters are available in available in any `.plist` or `.entitlements` files. 64 | 65 | #### Examples for usage: 66 | To keep the app and widget in sync use the following settings 67 | 68 | `Widget-Info.plist`: 69 | * Bundle display name: \_\_DISPLAY_NAME__ 70 | * Bundle identifier: \_\_APP\_IDENTIFIER__.\_\_BUNDLE\_SUFFIX__ 71 | * Bundle version string, short: \_\_BUNDLE_SHORT_VERSION_STRING__ 72 | * Bundle version: \_\_BUNDLE_VERSION__ 73 | 74 | `Widget.entitlements`: 75 | * App Groups -> Item 0: group.\_\_APP_IDENTIFIER__ 76 | 77 | ### Infos 78 | * I only tested the plugin with cordova 7.0.1 and cordova-ios 4.4.0 up to now, but it should work with other versions too. 79 | * I used XCode 8.3.2 to create the widget alongside with the plugin. 80 | * You have to add the app group entitlement to your host app too and you have to recreate your provisioning profiles with the app-group entitlement added if you want to use shared user defaults. 81 | * Don't forget to copy the widgets folder from `platforms/ios` to your source folder every time you modify it, otherwise your changes will be lost after you remove the platform. 82 | 83 | ### Acknowledgements 84 | 85 | Thanks to [Remy Kabel](https://github.com/RomanovX) who parametrized the build and made it possible for it to be fully automated. 86 | Thanks to [Hernan Zhou](https://github.com/LuckyKat) whos [plugin](https://github.com/LuckyKat/cordova-sticker-pack-extension) was a great inspiration. 87 | -------------------------------------------------------------------------------- /hooks/addTodayWidgetToProject.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | var elementTree = require('elementtree'); 4 | var fs = require('fs'); 5 | var path = require('path'); 6 | var plist = require('plist'); 7 | var Q = require('q'); 8 | var xcode = require('xcode'); 9 | 10 | function log(logString, type) { 11 | var prefix; 12 | var postfix = ''; 13 | switch (type) { 14 | case 'error': 15 | prefix = '\x1b[1m' + '\x1b[31m' + '💥 😨 '; // bold, red 16 | throw new Error(prefix + logString + 'x1b[0m'); // reset 17 | case 'info': 18 | prefix = 19 | '\x1b[40m' + 20 | '\x1b[37m' + 21 | '\x1b[2m' + 22 | '☝️ [INFO] ' + 23 | '\x1b[0m\x1b[40m' + 24 | '\x1b[33m'; // fgWhite, dim, reset, bgBlack, fgYellow 25 | break; 26 | case 'start': 27 | prefix = '\x1b[40m' + '\x1b[36m'; // bgBlack, fgCyan 28 | break; 29 | case 'success': 30 | prefix = '\x1b[40m' + '\x1b[32m' + '✔ '; // bgBlack, fgGreen 31 | postfix = ' 🦄 🎉 🤘'; 32 | break; 33 | } 34 | 35 | console.log(prefix + logString + postfix); 36 | } 37 | 38 | function getPreferenceValue (config, name) { 39 | var value = config.match(new RegExp('name="' + name + '" value="(.*?)"', "i")); 40 | if(value && value[1]) { 41 | return value[1]; 42 | } else { 43 | return null; 44 | } 45 | } 46 | 47 | function replacePlaceholdersInPlist(plistPath, placeHolderValues) { 48 | var plistContents = fs.readFileSync(plistPath, 'utf8'); 49 | for (var i = 0; i < placeHolderValues.length; i++) { 50 | var placeHolderValue = placeHolderValues[i], 51 | regexp = new RegExp(placeHolderValue.placeHolder, "g"); 52 | plistContents = plistContents.replace(regexp, placeHolderValue.value); 53 | } 54 | fs.writeFileSync(plistPath, plistContents); 55 | } 56 | 57 | function getCordovaParameter(variableName, contents) { 58 | var variable; 59 | if(process.argv.join("|").indexOf(variableName + "=") > -1) { 60 | var re = new RegExp(variableName + '=(.*?)(\||$))', 'g'); 61 | variable = process.argv.join("|").match(re)[1]; 62 | } else { 63 | variable = getPreferenceValue(contents, variableName); 64 | } 65 | return variable; 66 | } 67 | 68 | console.log('\x1b[40m'); 69 | log( 70 | 'Running addTargetToXcodeProject hook, patching xcode project 🦄 ', 71 | 'start' 72 | ); 73 | 74 | module.exports = function (context) { 75 | var deferral = new Q.defer(); 76 | 77 | if (context.opts.cordova.platforms.indexOf('ios') < 0) { 78 | log('You have to add the ios platform before adding this plugin!', 'error'); 79 | } 80 | 81 | var contents = fs.readFileSync( 82 | path.join(context.opts.projectRoot, 'config.xml'), 83 | 'utf-8' 84 | ); 85 | 86 | // Get the plugin variables from the parameters or the config file 87 | var WIDGET_NAME = getCordovaParameter("WIDGET_NAME", contents); 88 | var WIDGET_BUNDLE_SUFFIX = getCordovaParameter("WIDGET_BUNDLE_SUFFIX", contents); 89 | var ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = getCordovaParameter("ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", contents); 90 | var SWIFT_VERSION = getCordovaParameter("SWIFT_VERSION", contents); 91 | 92 | if (contents) { 93 | contents = contents.substring(contents.indexOf('<')); 94 | } 95 | 96 | // Get the bundle-id from config.xml 97 | var etree = elementTree.parse(contents); 98 | var bundleId = etree.getroot().get('id'); 99 | log('Bundle id of your host app: ' + bundleId, 'info'); 100 | 101 | var iosFolder = context.opts.cordova.project 102 | ? context.opts.cordova.project.root 103 | : path.join(context.opts.projectRoot, 'platforms/ios/'); 104 | log('Folder containing your iOS project: ' + iosFolder, 'info'); 105 | 106 | fs.readdir(iosFolder, function (err, data) { 107 | var projectFolder; 108 | var projectName; 109 | var run = function () { 110 | var pbxProject; 111 | var projectPath; 112 | projectPath = path.join(projectFolder, 'project.pbxproj'); 113 | 114 | log( 115 | 'Parsing existing project at location: ' + projectPath + ' ...', 116 | 'info' 117 | ); 118 | if (context.opts.cordova.project) { 119 | pbxProject = context.opts.cordova.project.parseProjectFile( 120 | context.opts.projectRoot 121 | ).xcode; 122 | } else { 123 | pbxProject = xcode.project(projectPath); 124 | pbxProject.parseSync(); 125 | } 126 | 127 | var widgetName = WIDGET_NAME || projectName + ' Widget'; 128 | log('Your widget will be named: ' + widgetName, 'info'); 129 | 130 | var widgetBundleId = WIDGET_BUNDLE_SUFFIX || 'widget'; 131 | log('Your widget bundle id will be: ' + bundleId + '.' + widgetBundleId, 'info'); 132 | 133 | var widgetFolder = path.join(iosFolder, widgetName); 134 | var sourceFiles = []; 135 | var resourceFiles = []; 136 | var configFiles = []; 137 | var projectContainsSwiftFiles = false; 138 | var addBridgingHeader = false; 139 | var bridgingHeaderName; 140 | var addXcconfig = false; 141 | var xcconfigFileName; 142 | var xcconfigReference; 143 | var addEntitlementsFile = false; 144 | var entitlementsFileName; 145 | var projectPlistPath = path.join(iosFolder, projectName, projectName + '-Info.plist'); 146 | var projectPlistJson = plist.parse(fs.readFileSync(projectPlistPath, 'utf8')); 147 | var placeHolderValues = [ 148 | { 149 | placeHolder: '__DISPLAY_NAME__', 150 | value: projectPlistJson['CFBundleDisplayName'] 151 | }, 152 | { 153 | placeHolder: '__APP_IDENTIFIER__', 154 | value: projectPlistJson['CFBundleIdentifier'] 155 | }, 156 | { 157 | placeHolder: '__BUNDLE_SUFFIX__', 158 | value: widgetBundleId 159 | }, 160 | { 161 | placeHolder: '__BUNDLE_SHORT_VERSION_STRING__', 162 | value: projectPlistJson['CFBundleShortVersionString'] 163 | }, 164 | { 165 | placeHolder: '__BUNDLE_VERSION__', 166 | value: projectPlistJson['CFBundleVersion'] 167 | } 168 | ]; 169 | 170 | fs.readdirSync(widgetFolder).forEach(file => { 171 | if (!/^\..*/.test(file)) { 172 | // Ignore junk files like .DS_Store 173 | var fileExtension = path.extname(file); 174 | switch (fileExtension) { 175 | // Swift and Objective-C source files which need to be compiled 176 | case '.swift': 177 | projectContainsSwiftFiles = true; 178 | sourceFiles.push(file); 179 | break; 180 | case '.h': 181 | case '.m': 182 | if (file === 'Bridging-Header.h' || file === 'Header.h') { 183 | addBridgingHeader = true; 184 | bridgingHeaderName = file; 185 | } 186 | sourceFiles.push(file); 187 | break; 188 | // Configuration files 189 | case '.plist': 190 | case '.entitlements': 191 | case '.xcconfig': 192 | if (fileExtension === '.plist') { 193 | replacePlaceholdersInPlist(path.join(widgetFolder, file), placeHolderValues); 194 | } 195 | if (fileExtension === '.xcconfig') { 196 | addXcconfig = true; 197 | xcconfigFileName = file; 198 | } 199 | if (fileExtension === '.entitlements') { 200 | replacePlaceholdersInPlist(path.join(widgetFolder, file), placeHolderValues); 201 | addEntitlementsFile = true; 202 | entitlementsFileName = file; 203 | } 204 | configFiles.push(file); 205 | break; 206 | // Resources like storyboards, images, fonts, etc. 207 | default: 208 | resourceFiles.push(file); 209 | break; 210 | } 211 | } 212 | }); 213 | 214 | log('Found following files in your widget folder:', 'info'); 215 | console.log('Source-files: '); 216 | sourceFiles.forEach(file => { 217 | console.log(' - ', file); 218 | }); 219 | 220 | console.log('Config-files: '); 221 | configFiles.forEach(file => { 222 | console.log(' - ', file); 223 | }); 224 | 225 | console.log('Resource-files: '); 226 | resourceFiles.forEach(file => { 227 | console.log(' - ', file); 228 | }); 229 | 230 | // Add PBXNativeTarget to the project 231 | var target = pbxProject.addTarget( 232 | widgetName, 233 | 'app_extension', 234 | widgetName 235 | ); 236 | if (target) { 237 | log('Successfully added PBXNativeTarget!', 'info'); 238 | } 239 | 240 | // Create a separate PBXGroup for the widgets files, name has to be unique and path must be in quotation marks 241 | var pbxGroupKey = pbxProject.pbxCreateGroup( 242 | 'Widget', 243 | '"' + widgetName + '"' 244 | ); 245 | if (pbxGroupKey) { 246 | log( 247 | 'Successfully created empty PbxGroup for folder: ' + 248 | widgetName + 249 | ' with alias: Widget', 250 | 'info' 251 | ); 252 | } 253 | 254 | // Add the PbxGroup to cordovas "CustomTemplate"-group 255 | var customTemplateKey = pbxProject.findPBXGroupKey({ 256 | name: 'CustomTemplate', 257 | }); 258 | pbxProject.addToPbxGroup(pbxGroupKey, customTemplateKey); 259 | log( 260 | 'Successfully added the widgets PbxGroup to cordovas CustomTemplate!', 261 | 'info' 262 | ); 263 | 264 | // Add files which are not part of any build phase (config) 265 | configFiles.forEach(configFile => { 266 | var file = pbxProject.addFile(configFile, pbxGroupKey); 267 | // We need the reference to add the xcconfig to the XCBuildConfiguration as baseConfigurationReference 268 | if (path.extname(configFile) == '.xcconfig') { 269 | xcconfigReference = file.fileRef; 270 | } 271 | }); 272 | log( 273 | 'Successfully added ' + configFiles.length + ' configuration files!', 274 | 'info' 275 | ); 276 | 277 | // Add a new PBXSourcesBuildPhase for our TodayViewController (we can't add it to the existing one because a today extension is kind of an extra app) 278 | var sourcesBuildPhase = pbxProject.addBuildPhase( 279 | [], 280 | 'PBXSourcesBuildPhase', 281 | 'Sources', 282 | target.uuid 283 | ); 284 | if (sourcesBuildPhase) { 285 | log('Successfully added PBXSourcesBuildPhase!', 'info'); 286 | } 287 | 288 | // Add a new source file and add it to our PbxGroup and our newly created PBXSourcesBuildPhase 289 | sourceFiles.forEach(sourcefile => { 290 | pbxProject.addSourceFile( 291 | sourcefile, 292 | { target: target.uuid }, 293 | pbxGroupKey 294 | ); 295 | }); 296 | 297 | log( 298 | 'Successfully added ' + 299 | sourceFiles.length + 300 | ' source files to PbxGroup and PBXSourcesBuildPhase!', 301 | 'info' 302 | ); 303 | 304 | // Add a new PBXFrameworksBuildPhase for the Frameworks used by the widget (NotificationCenter.framework, libCordova.a) 305 | var frameworksBuildPhase = pbxProject.addBuildPhase( 306 | [], 307 | 'PBXFrameworksBuildPhase', 308 | 'Frameworks', 309 | target.uuid 310 | ); 311 | if (frameworksBuildPhase) { 312 | log('Successfully added PBXFrameworksBuildPhase!', 'info'); 313 | } 314 | 315 | // Add the frameworks needed by our widget, add them to the existing Frameworks PbxGroup and PBXFrameworksBuildPhase 316 | var frameworkFile1 = pbxProject.addFramework( 317 | 'NotificationCenter.framework', 318 | { target: target.uuid } 319 | ); 320 | var frameworkFile2 = pbxProject.addFramework('libCordova.a', { 321 | target: target.uuid, 322 | }); // seems to work because the first target is built before the second one 323 | if (frameworkFile1 && frameworkFile2) { 324 | log('Successfully added frameworks needed by the widget!', 'info'); 325 | } 326 | 327 | // Add a new PBXResourcesBuildPhase for the Resources used by the widget (MainInterface.storyboard) 328 | var resourcesBuildPhase = pbxProject.addBuildPhase( 329 | [], 330 | 'PBXResourcesBuildPhase', 331 | 'Resources', 332 | target.uuid 333 | ); 334 | if (resourcesBuildPhase) { 335 | log('Successfully added PBXResourcesBuildPhase!', 'info'); 336 | } 337 | 338 | // Add the resource file and include it into the targest PbxResourcesBuildPhase and PbxGroup 339 | resourceFiles.forEach(resourcefile => { 340 | pbxProject.addResourceFile( 341 | resourcefile, 342 | { target: target.uuid }, 343 | pbxGroupKey 344 | ); 345 | }); 346 | 347 | log( 348 | 'Successfully added ' + resourceFiles.length + ' resource files!', 349 | 'info' 350 | ); 351 | 352 | // Add build settings for Swift support, bridging header and xcconfig files 353 | var configurations = pbxProject.pbxXCBuildConfigurationSection(); 354 | for (var key in configurations) { 355 | if (typeof configurations[key].buildSettings !== 'undefined') { 356 | var buildSettingsObj = configurations[key].buildSettings; 357 | if (typeof buildSettingsObj['PRODUCT_NAME'] !== 'undefined') { 358 | var productName = buildSettingsObj['PRODUCT_NAME']; 359 | if (productName.indexOf('Widget') >= 0) { 360 | if (addXcconfig) { 361 | configurations[key].baseConfigurationReference = 362 | xcconfigReference + ' /* ' + xcconfigFileName + ' */'; 363 | log('Added xcconfig file reference to build settings!', 'info'); 364 | } 365 | if (addEntitlementsFile) { 366 | buildSettingsObj['CODE_SIGN_ENTITLEMENTS'] = '"' + widgetName + '/' + entitlementsFileName + '"'; 367 | log('Added entitlements file reference to build settings!', 'info'); 368 | } 369 | if (projectContainsSwiftFiles) { 370 | buildSettingsObj['SWIFT_VERSION'] = SWIFT_VERSION || '3.0'; 371 | buildSettingsObj['ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES'] = ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES || 'YES'; 372 | log('Added build settings for swift support!', 'info'); 373 | } 374 | if (addBridgingHeader) { 375 | buildSettingsObj['SWIFT_OBJC_BRIDGING_HEADER'] = 376 | '"$(PROJECT_DIR)/' + 377 | widgetName + 378 | '/' + 379 | bridgingHeaderName + 380 | '"'; 381 | log('Added bridging header reference to build settings!', 'info'); 382 | } 383 | } 384 | } 385 | } 386 | } 387 | 388 | // Write the modified project back to disc 389 | log('Writing the modified project back to disk ...', 'info'); 390 | fs.writeFileSync(projectPath, pbxProject.writeSync()); 391 | log( 392 | 'Added app extension to ' + projectName + ' xcode project', 393 | 'success' 394 | ); 395 | console.log('\x1b[0m'); // reset 396 | 397 | deferral.resolve(); 398 | }; 399 | 400 | if (err) { 401 | log(err, 'error'); 402 | } 403 | 404 | // Find the project folder by looking for *.xcodeproj 405 | if (data && data.length) { 406 | data.forEach(function (folder) { 407 | if (folder.match(/\.xcodeproj$/)) { 408 | projectFolder = path.join(iosFolder, folder); 409 | projectName = path.basename(folder, '.xcodeproj'); 410 | } 411 | }); 412 | } 413 | 414 | if (!projectFolder || !projectName) { 415 | log('Could not find an *.xcodeproj folder in: ' + iosFolder, 'error'); 416 | } 417 | 418 | run(); 419 | }); 420 | 421 | return deferral.promise; 422 | }; 423 | -------------------------------------------------------------------------------- /hooks/copyExtensionFolderToIosProjectFolder.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var Q = require('q'); 6 | 7 | function log(logString, type) { 8 | var prefix; 9 | var postfix = ''; 10 | switch (type) { 11 | case 'error': 12 | prefix = '\x1b[1m' + '\x1b[31m' + '💥 😨 '; // bold, red 13 | throw new Error(prefix + logString + 'x1b[0m'); // reset 14 | case 'info': 15 | prefix = 16 | '\x1b[40m' + 17 | '\x1b[37m' + 18 | '\x1b[2m' + 19 | '☝️ [INFO] ' + 20 | '\x1b[0m\x1b[40m' + 21 | '\x1b[33m'; // fgWhite, dim, reset, bgBlack, fgYellow 22 | break; 23 | case 'start': 24 | prefix = '\x1b[40m' + '\x1b[36m'; // bgBlack, fgCyan 25 | break; 26 | case 'success': 27 | prefix = '\x1b[40m' + '\x1b[32m' + '✔ '; // bgBlack, fgGreen 28 | postfix = ' 🦄 🎉 🤘'; 29 | break; 30 | } 31 | 32 | console.log(prefix + logString + postfix); 33 | } 34 | 35 | function getPreferenceValue (config, name) { 36 | var value = config.match(new RegExp('name="' + name + '" value="(.*?)"', "i")); 37 | if(value && value[1]) { 38 | return value[1]; 39 | } else { 40 | return null; 41 | } 42 | } 43 | 44 | console.log('\x1b[40m'); 45 | log( 46 | 'Running copyExtensionFolderToIosProject hook, copying widget folder ...', 47 | 'start' 48 | ); 49 | 50 | // http://stackoverflow.com/a/26038979/5930772 51 | var copyFileSync = function(source, target) { 52 | var targetFile = target; 53 | 54 | // If target is a directory a new file with the same name will be created 55 | if (fs.existsSync(target)) { 56 | if (fs.lstatSync(target).isDirectory()) { 57 | targetFile = path.join(target, path.basename(source)); 58 | } 59 | } 60 | 61 | fs.writeFileSync(targetFile, fs.readFileSync(source)); 62 | }; 63 | var copyFolderRecursiveSync = function(source, target) { 64 | var files = []; 65 | 66 | // Check if folder needs to be created or integrated 67 | var targetFolder = path.join(target, path.basename(source)); 68 | if (!fs.existsSync(targetFolder)) { 69 | fs.mkdirSync(targetFolder); 70 | } 71 | 72 | // Copy 73 | if (fs.lstatSync(source).isDirectory()) { 74 | files = fs.readdirSync(source); 75 | files.forEach(function(file) { 76 | var curSource = path.join(source, file); 77 | if (fs.lstatSync(curSource).isDirectory()) { 78 | copyFolderRecursiveSync(curSource, targetFolder); 79 | } else { 80 | copyFileSync(curSource, targetFolder); 81 | } 82 | }); 83 | } 84 | }; 85 | 86 | function getCordovaParameter(variableName, contents) { 87 | var variable; 88 | if(process.argv.join("|").indexOf(variableName + "=") > -1) { 89 | var re = new RegExp(variableName + '=(.*?)(\||$))', 'g'); 90 | variable = process.argv.join("|").match(re)[1]; 91 | } else { 92 | variable = getPreferenceValue(contents, variableName); 93 | } 94 | return variable; 95 | } 96 | 97 | module.exports = function(context) { 98 | var deferral = new Q.defer(); 99 | 100 | var contents = fs.readFileSync( 101 | path.join(context.opts.projectRoot, 'config.xml'), 102 | 'utf-8' 103 | ); 104 | 105 | var iosFolder = context.opts.cordova.project 106 | ? context.opts.cordova.project.root 107 | : path.join(context.opts.projectRoot, 'platforms/ios/'); 108 | fs.readdir(iosFolder, function(err, data) { 109 | var projectFolder; 110 | var projectName; 111 | var srcFolder; 112 | // Find the project folder by looking for *.xcodeproj 113 | if (data && data.length) { 114 | data.forEach(function(folder) { 115 | if (folder.match(/\.xcodeproj$/)) { 116 | projectFolder = path.join(iosFolder, folder); 117 | projectName = path.basename(folder, '.xcodeproj'); 118 | } 119 | }); 120 | } 121 | 122 | if (!projectFolder || !projectName) { 123 | log('Could not find an .xcodeproj folder in: ' + iosFolder, 'error'); 124 | } 125 | 126 | // Get the widget name and location from the parameters or the config file 127 | var WIDGET_NAME = getCordovaParameter("WIDGET_NAME", contents); 128 | var WIDGET_PATH = getCordovaParameter("WIDGET_PATH", contents); 129 | var widgetName = WIDGET_NAME || projectName + ' Widget'; 130 | 131 | if (WIDGET_PATH) { 132 | srcFolder = path.join( 133 | context.opts.projectRoot, 134 | WIDGET_PATH, 135 | widgetName + '/' 136 | ); 137 | } else { 138 | srcFolder = path.join( 139 | context.opts.projectRoot, 140 | 'www', 141 | widgetName + '/' 142 | ); 143 | } 144 | if (!fs.existsSync(srcFolder)) { 145 | log( 146 | 'Missing widget folder in ' + srcFolder + '. Should have the same name as your widget: ' + widgetName, 147 | 'error' 148 | ); 149 | } 150 | 151 | // Copy widget folder 152 | copyFolderRecursiveSync( 153 | srcFolder, 154 | path.join(context.opts.projectRoot, 'platforms', 'ios') 155 | ); 156 | log('Successfully copied Widget folder!', 'success'); 157 | console.log('\x1b[0m'); // reset 158 | 159 | deferral.resolve(); 160 | }); 161 | 162 | return deferral.promise; 163 | }; 164 | -------------------------------------------------------------------------------- /hooks/fixAppEntitlements.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | var elementTree = require('elementtree'); 4 | var fs = require('fs'); 5 | var path = require('path'); 6 | var plist = require('plist'); 7 | var Q = require('q'); 8 | var xcode = require('xcode'); 9 | 10 | function log(logString, type) { 11 | var prefix; 12 | var postfix = ''; 13 | switch (type) { 14 | case 'error': 15 | prefix = '\x1b[1m' + '\x1b[31m' + '💥 😨 '; // bold, red 16 | throw new Error(prefix + logString + 'x1b[0m'); // reset 17 | case 'info': 18 | prefix = 19 | '\x1b[40m' + 20 | '\x1b[37m' + 21 | '\x1b[2m' + 22 | '☝️ [INFO] ' + 23 | '\x1b[0m\x1b[40m' + 24 | '\x1b[33m'; // fgWhite, dim, reset, bgBlack, fgYellow 25 | break; 26 | case 'start': 27 | prefix = '\x1b[40m' + '\x1b[36m'; // bgBlack, fgCyan 28 | break; 29 | case 'success': 30 | prefix = '\x1b[40m' + '\x1b[32m' + '✔ '; // bgBlack, fgGreen 31 | postfix = ' 🦄 🎉 🤘'; 32 | break; 33 | } 34 | 35 | console.log(prefix + logString + postfix); 36 | } 37 | 38 | function removeDuplicateSubsequentLines(string) { 39 | var lineArray = string.split('\n'); 40 | return lineArray.filter((line, idx) => { 41 | return idx === 0 || ( line !== lineArray[idx - 1] ) 42 | }).join('\n'); 43 | } 44 | 45 | function replacePlaceholdersInPlist(plistPath, placeHolderValues) { 46 | var plistContents = fs.readFileSync(plistPath, 'utf8'); 47 | for (var i = 0; i < placeHolderValues.length; i++) { 48 | var placeHolderValue = placeHolderValues[i], 49 | regexp = new RegExp(placeHolderValue.placeHolder, "g"); 50 | plistContents = plistContents.replace(regexp, placeHolderValue.value); 51 | plistContents = removeDuplicateSubsequentLines(plistContents); 52 | } 53 | fs.writeFileSync(plistPath, plistContents); 54 | } 55 | 56 | console.log('\x1b[40m'); 57 | log( 58 | 'Running fixAppEntitlements hook, fixing the app entitlements 🦄 ', 59 | 'start' 60 | ); 61 | 62 | module.exports = function (context) { 63 | var deferral = new Q.defer(); 64 | 65 | if (context.opts.cordova.platforms.indexOf('ios') < 0) { 66 | log('You have to add the ios platform before adding this plugin!', 'error'); 67 | } 68 | 69 | var contents = fs.readFileSync( 70 | path.join(context.opts.projectRoot, 'config.xml'), 71 | 'utf-8' 72 | ); 73 | 74 | if (contents) { 75 | contents = contents.substring(contents.indexOf('<')); 76 | } 77 | 78 | // Get the bundle-id from config.xml 79 | var etree = elementTree.parse(contents); 80 | var bundleId = etree.getroot().get('id'); 81 | 82 | var iosFolder = context.opts.cordova.project 83 | ? context.opts.cordova.project.root 84 | : path.join(context.opts.projectRoot, 'platforms/ios/'); 85 | 86 | fs.readdir(iosFolder, function (err, data) { 87 | var projectFolder 88 | var projectName; 89 | var run = function () { 90 | var placeHolderValues = [ 91 | { 92 | placeHolder: '__APP_IDENTIFIER__', 93 | value: bundleId 94 | } 95 | ]; 96 | 97 | // Update app entitlements 98 | ['Debug', 'Release'].forEach(config => { 99 | var entitlementsPath = path.join(iosFolder, projectName, 'Entitlements-' + config + '.plist'); 100 | replacePlaceholdersInPlist(entitlementsPath, placeHolderValues); 101 | }); 102 | log('Successfully added app group information to the app entitlement files!', 'success'); 103 | 104 | console.log('\x1b[0m'); // reset 105 | 106 | deferral.resolve(); 107 | }; 108 | 109 | if (err) { 110 | log(err, 'error'); 111 | } 112 | 113 | // Find the project folder by looking for *.xcodeproj 114 | if (data && data.length) { 115 | data.forEach(function (folder) { 116 | if (folder.match(/\.xcodeproj$/)) { 117 | projectFolder = path.join(iosFolder, folder); 118 | projectName = path.basename(folder, '.xcodeproj'); 119 | } 120 | }); 121 | } 122 | 123 | if (!projectFolder || !projectName) { 124 | log('Could not find an *.xcodeproj folder in: ' + iosFolder, 'error'); 125 | } 126 | 127 | run(); 128 | }); 129 | 130 | return deferral.promise; 131 | }; 132 | -------------------------------------------------------------------------------- /hooks/prerequisites.js: -------------------------------------------------------------------------------- 1 | var child_process = require('child_process'); 2 | var Q = require('q'); 3 | 4 | module.exports = function (context) { 5 | var deferral = Q.defer(); 6 | 7 | child_process.exec('npm install', {cwd:__dirname}, 8 | function (error) { 9 | if (error !== null) { 10 | console.log('exec error: ' + error); 11 | deferral.reject('npm installation failed'); 12 | } 13 | deferral.resolve(); 14 | }); 15 | 16 | return deferral.promise; 17 | }; 18 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-today-widget", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-escapes": { 8 | "version": "1.4.0", 9 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", 10 | "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", 11 | "dev": true 12 | }, 13 | "ansi-regex": { 14 | "version": "2.1.1", 15 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 16 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 17 | "dev": true 18 | }, 19 | "ansi-styles": { 20 | "version": "2.2.1", 21 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 22 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 23 | "dev": true 24 | }, 25 | "app-root-path": { 26 | "version": "2.0.1", 27 | "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.0.1.tgz", 28 | "integrity": "sha1-zWLc+OT9WkF+/GZNLlsQZTxlG0Y=", 29 | "dev": true 30 | }, 31 | "argparse": { 32 | "version": "1.0.9", 33 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", 34 | "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", 35 | "dev": true, 36 | "requires": { 37 | "sprintf-js": "~1.0.2" 38 | } 39 | }, 40 | "balanced-match": { 41 | "version": "1.0.0", 42 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 43 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 44 | "dev": true 45 | }, 46 | "base64-js": { 47 | "version": "1.2.0", 48 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz", 49 | "integrity": "sha1-o5mS1yNYSBGYK+XikLtqU9hnAPE=" 50 | }, 51 | "big-integer": { 52 | "version": "1.6.48", 53 | "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", 54 | "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" 55 | }, 56 | "bplist-creator": { 57 | "version": "0.0.8", 58 | "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.8.tgz", 59 | "integrity": "sha512-Za9JKzD6fjLC16oX2wsXfc+qBEhJBJB1YPInoAQpMLhDuj5aVOv1baGeIQSq1Fr3OCqzvsoQcSBSwGId/Ja2PA==", 60 | "requires": { 61 | "stream-buffers": "~2.2.0" 62 | } 63 | }, 64 | "bplist-parser": { 65 | "version": "0.2.0", 66 | "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", 67 | "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", 68 | "requires": { 69 | "big-integer": "^1.6.44" 70 | } 71 | }, 72 | "brace-expansion": { 73 | "version": "1.1.8", 74 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 75 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 76 | "dev": true, 77 | "requires": { 78 | "balanced-match": "^1.0.0", 79 | "concat-map": "0.0.1" 80 | } 81 | }, 82 | "chalk": { 83 | "version": "1.1.3", 84 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 85 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 86 | "dev": true, 87 | "requires": { 88 | "ansi-styles": "^2.2.1", 89 | "escape-string-regexp": "^1.0.2", 90 | "has-ansi": "^2.0.0", 91 | "strip-ansi": "^3.0.0", 92 | "supports-color": "^2.0.0" 93 | } 94 | }, 95 | "ci-info": { 96 | "version": "1.0.0", 97 | "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.0.0.tgz", 98 | "integrity": "sha1-3FKF8rTiUYIWg2gcOBwziPRuxTQ=", 99 | "dev": true 100 | }, 101 | "cli-cursor": { 102 | "version": "1.0.2", 103 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", 104 | "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", 105 | "dev": true, 106 | "requires": { 107 | "restore-cursor": "^1.0.1" 108 | } 109 | }, 110 | "cli-spinners": { 111 | "version": "0.1.2", 112 | "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", 113 | "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=", 114 | "dev": true 115 | }, 116 | "cli-truncate": { 117 | "version": "0.2.1", 118 | "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", 119 | "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", 120 | "dev": true, 121 | "requires": { 122 | "slice-ansi": "0.0.4", 123 | "string-width": "^1.0.1" 124 | } 125 | }, 126 | "code-point-at": { 127 | "version": "1.1.0", 128 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 129 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", 130 | "dev": true 131 | }, 132 | "commander": { 133 | "version": "2.9.0", 134 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", 135 | "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", 136 | "dev": true, 137 | "requires": { 138 | "graceful-readlink": ">= 1.0.0" 139 | } 140 | }, 141 | "concat-map": { 142 | "version": "0.0.1", 143 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 144 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 145 | "dev": true 146 | }, 147 | "cosmiconfig": { 148 | "version": "1.1.0", 149 | "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-1.1.0.tgz", 150 | "integrity": "sha1-DeoPmATv37kp+7GxiOJVU+oFPTc=", 151 | "dev": true, 152 | "requires": { 153 | "graceful-fs": "^4.1.2", 154 | "js-yaml": "^3.4.3", 155 | "minimist": "^1.2.0", 156 | "object-assign": "^4.0.1", 157 | "os-homedir": "^1.0.1", 158 | "parse-json": "^2.2.0", 159 | "pinkie-promise": "^2.0.0", 160 | "require-from-string": "^1.1.0" 161 | } 162 | }, 163 | "cross-spawn": { 164 | "version": "5.1.0", 165 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 166 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 167 | "dev": true, 168 | "requires": { 169 | "lru-cache": "^4.0.1", 170 | "shebang-command": "^1.2.0", 171 | "which": "^1.2.9" 172 | } 173 | }, 174 | "date-fns": { 175 | "version": "1.28.5", 176 | "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.28.5.tgz", 177 | "integrity": "sha1-JXz8RdMi30XvVlhmWWfuhBzXP68=", 178 | "dev": true 179 | }, 180 | "elegant-spinner": { 181 | "version": "1.0.1", 182 | "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", 183 | "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", 184 | "dev": true 185 | }, 186 | "elementtree": { 187 | "version": "0.1.7", 188 | "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", 189 | "integrity": "sha1-mskb5uUvtuYkTE5UpKw+2K6OKcA=", 190 | "requires": { 191 | "sax": "1.1.4" 192 | } 193 | }, 194 | "error-ex": { 195 | "version": "1.3.1", 196 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", 197 | "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", 198 | "dev": true, 199 | "requires": { 200 | "is-arrayish": "^0.2.1" 201 | } 202 | }, 203 | "escape-string-regexp": { 204 | "version": "1.0.5", 205 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 206 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 207 | "dev": true 208 | }, 209 | "execa": { 210 | "version": "0.7.0", 211 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", 212 | "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", 213 | "dev": true, 214 | "requires": { 215 | "cross-spawn": "^5.0.1", 216 | "get-stream": "^3.0.0", 217 | "is-stream": "^1.1.0", 218 | "npm-run-path": "^2.0.0", 219 | "p-finally": "^1.0.0", 220 | "signal-exit": "^3.0.0", 221 | "strip-eof": "^1.0.0" 222 | } 223 | }, 224 | "exit-hook": { 225 | "version": "1.1.1", 226 | "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", 227 | "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", 228 | "dev": true 229 | }, 230 | "figures": { 231 | "version": "1.7.0", 232 | "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", 233 | "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", 234 | "dev": true, 235 | "requires": { 236 | "escape-string-regexp": "^1.0.5", 237 | "object-assign": "^4.1.0" 238 | } 239 | }, 240 | "file-match": { 241 | "version": "1.0.2", 242 | "resolved": "https://registry.npmjs.org/file-match/-/file-match-1.0.2.tgz", 243 | "integrity": "sha1-ycrSZdLIrfOoFHWw30dYWQafrvc=", 244 | "requires": { 245 | "utils-extend": "^1.0.6" 246 | } 247 | }, 248 | "file-system": { 249 | "version": "2.2.2", 250 | "resolved": "https://registry.npmjs.org/file-system/-/file-system-2.2.2.tgz", 251 | "integrity": "sha1-fWWDPjojR9zZVqgTxncVPtPt2Yc=", 252 | "requires": { 253 | "file-match": "^1.0.1", 254 | "utils-extend": "^1.0.4" 255 | } 256 | }, 257 | "find-parent-dir": { 258 | "version": "0.3.0", 259 | "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz", 260 | "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=", 261 | "dev": true 262 | }, 263 | "get-stream": { 264 | "version": "3.0.0", 265 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 266 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", 267 | "dev": true 268 | }, 269 | "graceful-fs": { 270 | "version": "4.1.11", 271 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 272 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 273 | "dev": true 274 | }, 275 | "graceful-readlink": { 276 | "version": "1.0.1", 277 | "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", 278 | "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", 279 | "dev": true 280 | }, 281 | "has-ansi": { 282 | "version": "2.0.0", 283 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 284 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 285 | "dev": true, 286 | "requires": { 287 | "ansi-regex": "^2.0.0" 288 | } 289 | }, 290 | "husky": { 291 | "version": "0.13.4", 292 | "resolved": "https://registry.npmjs.org/husky/-/husky-0.13.4.tgz", 293 | "integrity": "sha1-SHhcUCjeNFKlHEjBLE+UshJKFAc=", 294 | "dev": true, 295 | "requires": { 296 | "chalk": "^1.1.3", 297 | "find-parent-dir": "^0.3.0", 298 | "is-ci": "^1.0.9", 299 | "normalize-path": "^1.0.0" 300 | } 301 | }, 302 | "indent-string": { 303 | "version": "2.1.0", 304 | "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", 305 | "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", 306 | "dev": true, 307 | "requires": { 308 | "repeating": "^2.0.0" 309 | } 310 | }, 311 | "inherits": { 312 | "version": "2.0.1", 313 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", 314 | "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" 315 | }, 316 | "is-arrayish": { 317 | "version": "0.2.1", 318 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 319 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", 320 | "dev": true 321 | }, 322 | "is-ci": { 323 | "version": "1.0.10", 324 | "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.0.10.tgz", 325 | "integrity": "sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4=", 326 | "dev": true, 327 | "requires": { 328 | "ci-info": "^1.0.0" 329 | } 330 | }, 331 | "is-finite": { 332 | "version": "1.0.2", 333 | "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", 334 | "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", 335 | "dev": true, 336 | "requires": { 337 | "number-is-nan": "^1.0.0" 338 | } 339 | }, 340 | "is-fullwidth-code-point": { 341 | "version": "1.0.0", 342 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 343 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 344 | "dev": true, 345 | "requires": { 346 | "number-is-nan": "^1.0.0" 347 | } 348 | }, 349 | "is-promise": { 350 | "version": "2.1.0", 351 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", 352 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", 353 | "dev": true 354 | }, 355 | "is-stream": { 356 | "version": "1.1.0", 357 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 358 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", 359 | "dev": true 360 | }, 361 | "isexe": { 362 | "version": "2.0.0", 363 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 364 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 365 | "dev": true 366 | }, 367 | "js-yaml": { 368 | "version": "3.13.1", 369 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 370 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 371 | "dev": true, 372 | "requires": { 373 | "argparse": "^1.0.7", 374 | "esprima": "^4.0.0" 375 | }, 376 | "dependencies": { 377 | "esprima": { 378 | "version": "4.0.1", 379 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 380 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 381 | "dev": true 382 | } 383 | } 384 | }, 385 | "lint-staged": { 386 | "version": "3.6.1", 387 | "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-3.6.1.tgz", 388 | "integrity": "sha1-JEI8i3vZnZbhWs0ayMs5KnjlhYI=", 389 | "dev": true, 390 | "requires": { 391 | "app-root-path": "^2.0.0", 392 | "cosmiconfig": "^1.1.0", 393 | "execa": "^0.7.0", 394 | "listr": "^0.12.0", 395 | "lodash.chunk": "^4.2.0", 396 | "minimatch": "^3.0.0", 397 | "npm-which": "^3.0.1", 398 | "p-map": "^1.1.1", 399 | "staged-git-files": "0.0.4" 400 | } 401 | }, 402 | "listr": { 403 | "version": "0.12.0", 404 | "resolved": "https://registry.npmjs.org/listr/-/listr-0.12.0.tgz", 405 | "integrity": "sha1-a84sD1YD+klYDqF81qAMwOX6RRo=", 406 | "dev": true, 407 | "requires": { 408 | "chalk": "^1.1.3", 409 | "cli-truncate": "^0.2.1", 410 | "figures": "^1.7.0", 411 | "indent-string": "^2.1.0", 412 | "is-promise": "^2.1.0", 413 | "is-stream": "^1.1.0", 414 | "listr-silent-renderer": "^1.1.1", 415 | "listr-update-renderer": "^0.2.0", 416 | "listr-verbose-renderer": "^0.4.0", 417 | "log-symbols": "^1.0.2", 418 | "log-update": "^1.0.2", 419 | "ora": "^0.2.3", 420 | "p-map": "^1.1.1", 421 | "rxjs": "^5.0.0-beta.11", 422 | "stream-to-observable": "^0.1.0", 423 | "strip-ansi": "^3.0.1" 424 | } 425 | }, 426 | "listr-silent-renderer": { 427 | "version": "1.1.1", 428 | "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", 429 | "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", 430 | "dev": true 431 | }, 432 | "listr-update-renderer": { 433 | "version": "0.2.0", 434 | "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz", 435 | "integrity": "sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk=", 436 | "dev": true, 437 | "requires": { 438 | "chalk": "^1.1.3", 439 | "cli-truncate": "^0.2.1", 440 | "elegant-spinner": "^1.0.1", 441 | "figures": "^1.7.0", 442 | "indent-string": "^3.0.0", 443 | "log-symbols": "^1.0.2", 444 | "log-update": "^1.0.2", 445 | "strip-ansi": "^3.0.1" 446 | }, 447 | "dependencies": { 448 | "indent-string": { 449 | "version": "3.1.0", 450 | "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.1.0.tgz", 451 | "integrity": "sha1-CP9DNGAziDmbMp5rlTjcejz13n0=", 452 | "dev": true 453 | } 454 | } 455 | }, 456 | "listr-verbose-renderer": { 457 | "version": "0.4.0", 458 | "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.0.tgz", 459 | "integrity": "sha1-RNwBuww0oDxXIVTU0Izemx3FYg8=", 460 | "dev": true, 461 | "requires": { 462 | "chalk": "^1.1.3", 463 | "cli-cursor": "^1.0.2", 464 | "date-fns": "^1.27.2", 465 | "figures": "^1.7.0" 466 | } 467 | }, 468 | "lodash.chunk": { 469 | "version": "4.2.0", 470 | "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz", 471 | "integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw=", 472 | "dev": true 473 | }, 474 | "log-symbols": { 475 | "version": "1.0.2", 476 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", 477 | "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", 478 | "dev": true, 479 | "requires": { 480 | "chalk": "^1.0.0" 481 | } 482 | }, 483 | "log-update": { 484 | "version": "1.0.2", 485 | "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", 486 | "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", 487 | "dev": true, 488 | "requires": { 489 | "ansi-escapes": "^1.0.0", 490 | "cli-cursor": "^1.0.2" 491 | } 492 | }, 493 | "lru-cache": { 494 | "version": "4.1.1", 495 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", 496 | "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", 497 | "dev": true, 498 | "requires": { 499 | "pseudomap": "^1.0.2", 500 | "yallist": "^2.1.2" 501 | } 502 | }, 503 | "minimatch": { 504 | "version": "3.0.4", 505 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 506 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 507 | "dev": true, 508 | "requires": { 509 | "brace-expansion": "^1.1.7" 510 | } 511 | }, 512 | "minimist": { 513 | "version": "1.2.5", 514 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 515 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 516 | "dev": true 517 | }, 518 | "normalize-path": { 519 | "version": "1.0.0", 520 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-1.0.0.tgz", 521 | "integrity": "sha1-MtDkcvkf80VwHBWoMRAY07CpA3k=", 522 | "dev": true 523 | }, 524 | "npm-path": { 525 | "version": "2.0.3", 526 | "resolved": "https://registry.npmjs.org/npm-path/-/npm-path-2.0.3.tgz", 527 | "integrity": "sha1-Fc/04ciaONp39W9gVbJPl137K74=", 528 | "dev": true, 529 | "requires": { 530 | "which": "^1.2.10" 531 | } 532 | }, 533 | "npm-run-path": { 534 | "version": "2.0.2", 535 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 536 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 537 | "dev": true, 538 | "requires": { 539 | "path-key": "^2.0.0" 540 | } 541 | }, 542 | "npm-which": { 543 | "version": "3.0.1", 544 | "resolved": "https://registry.npmjs.org/npm-which/-/npm-which-3.0.1.tgz", 545 | "integrity": "sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo=", 546 | "dev": true, 547 | "requires": { 548 | "commander": "^2.9.0", 549 | "npm-path": "^2.0.2", 550 | "which": "^1.2.10" 551 | } 552 | }, 553 | "number-is-nan": { 554 | "version": "1.0.1", 555 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 556 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", 557 | "dev": true 558 | }, 559 | "object-assign": { 560 | "version": "4.1.1", 561 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 562 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 563 | "dev": true 564 | }, 565 | "onetime": { 566 | "version": "1.1.0", 567 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", 568 | "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", 569 | "dev": true 570 | }, 571 | "ora": { 572 | "version": "0.2.3", 573 | "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", 574 | "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", 575 | "dev": true, 576 | "requires": { 577 | "chalk": "^1.1.1", 578 | "cli-cursor": "^1.0.2", 579 | "cli-spinners": "^0.1.2", 580 | "object-assign": "^4.0.1" 581 | } 582 | }, 583 | "os-homedir": { 584 | "version": "1.0.2", 585 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 586 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", 587 | "dev": true 588 | }, 589 | "p-finally": { 590 | "version": "1.0.0", 591 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 592 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", 593 | "dev": true 594 | }, 595 | "p-map": { 596 | "version": "1.1.1", 597 | "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.1.1.tgz", 598 | "integrity": "sha1-BfXkrpegaDcbwqXMhr+9vBnErno=", 599 | "dev": true 600 | }, 601 | "parse-json": { 602 | "version": "2.2.0", 603 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 604 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 605 | "dev": true, 606 | "requires": { 607 | "error-ex": "^1.2.0" 608 | } 609 | }, 610 | "path": { 611 | "version": "0.12.7", 612 | "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", 613 | "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", 614 | "requires": { 615 | "process": "^0.11.1", 616 | "util": "^0.10.3" 617 | } 618 | }, 619 | "path-key": { 620 | "version": "2.0.1", 621 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 622 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 623 | "dev": true 624 | }, 625 | "pinkie": { 626 | "version": "2.0.4", 627 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 628 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", 629 | "dev": true 630 | }, 631 | "pinkie-promise": { 632 | "version": "2.0.1", 633 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 634 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 635 | "dev": true, 636 | "requires": { 637 | "pinkie": "^2.0.0" 638 | } 639 | }, 640 | "plist": { 641 | "version": "2.1.0", 642 | "resolved": "https://registry.npmjs.org/plist/-/plist-2.1.0.tgz", 643 | "integrity": "sha1-V8zbeggh3yGDEhejytVOPhRqECU=", 644 | "requires": { 645 | "base64-js": "1.2.0", 646 | "xmlbuilder": "8.2.2", 647 | "xmldom": "0.1.x" 648 | } 649 | }, 650 | "prettier": { 651 | "version": "1.4.4", 652 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.4.4.tgz", 653 | "integrity": "sha512-GuuPazIvjW1DG26yLQgO+nagmRF/h9M4RaCtZWqu/eFW7csdZkQEwPJUeXX10d+LzmCnR9DuIZndqIOn3p2YoA==", 654 | "dev": true 655 | }, 656 | "process": { 657 | "version": "0.11.10", 658 | "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 659 | "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" 660 | }, 661 | "pseudomap": { 662 | "version": "1.0.2", 663 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 664 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", 665 | "dev": true 666 | }, 667 | "q": { 668 | "version": "1.5.1", 669 | "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", 670 | "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" 671 | }, 672 | "repeating": { 673 | "version": "2.0.1", 674 | "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", 675 | "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", 676 | "dev": true, 677 | "requires": { 678 | "is-finite": "^1.0.0" 679 | } 680 | }, 681 | "require-from-string": { 682 | "version": "1.2.1", 683 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", 684 | "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", 685 | "dev": true 686 | }, 687 | "restore-cursor": { 688 | "version": "1.0.1", 689 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", 690 | "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", 691 | "dev": true, 692 | "requires": { 693 | "exit-hook": "^1.0.0", 694 | "onetime": "^1.0.0" 695 | } 696 | }, 697 | "rxjs": { 698 | "version": "5.4.1", 699 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.4.1.tgz", 700 | "integrity": "sha1-ti91fyeURdJloYpY+wpw3JDpFiY=", 701 | "dev": true, 702 | "requires": { 703 | "symbol-observable": "^1.0.1" 704 | } 705 | }, 706 | "sax": { 707 | "version": "1.1.4", 708 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", 709 | "integrity": "sha1-dLbTPJrh4AFRDxeakRaFiPGu2qk=" 710 | }, 711 | "shebang-command": { 712 | "version": "1.2.0", 713 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 714 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 715 | "dev": true, 716 | "requires": { 717 | "shebang-regex": "^1.0.0" 718 | } 719 | }, 720 | "shebang-regex": { 721 | "version": "1.0.0", 722 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 723 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 724 | "dev": true 725 | }, 726 | "signal-exit": { 727 | "version": "3.0.2", 728 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 729 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 730 | "dev": true 731 | }, 732 | "simple-plist": { 733 | "version": "1.1.0", 734 | "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.1.0.tgz", 735 | "integrity": "sha512-2i5Tc0BYAqppM7jVzmNrI+aEUntPolIq4fDgji6WuNNn1D/qYdn2KwoLhZdzQkE04lu9L5tUoeJsjuJAvd+lFg==", 736 | "requires": { 737 | "bplist-creator": "0.0.8", 738 | "bplist-parser": "0.2.0", 739 | "plist": "^3.0.1" 740 | }, 741 | "dependencies": { 742 | "base64-js": { 743 | "version": "1.3.1", 744 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", 745 | "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" 746 | }, 747 | "plist": { 748 | "version": "3.0.1", 749 | "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.1.tgz", 750 | "integrity": "sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==", 751 | "requires": { 752 | "base64-js": "^1.2.3", 753 | "xmlbuilder": "^9.0.7", 754 | "xmldom": "0.1.x" 755 | } 756 | }, 757 | "xmlbuilder": { 758 | "version": "9.0.7", 759 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", 760 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" 761 | } 762 | } 763 | }, 764 | "slice-ansi": { 765 | "version": "0.0.4", 766 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", 767 | "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", 768 | "dev": true 769 | }, 770 | "sprintf-js": { 771 | "version": "1.0.3", 772 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 773 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 774 | "dev": true 775 | }, 776 | "staged-git-files": { 777 | "version": "0.0.4", 778 | "resolved": "https://registry.npmjs.org/staged-git-files/-/staged-git-files-0.0.4.tgz", 779 | "integrity": "sha1-15fhtVHKemOd7AI33G60u5vhfTU=", 780 | "dev": true 781 | }, 782 | "stream-buffers": { 783 | "version": "2.2.0", 784 | "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", 785 | "integrity": "sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ=" 786 | }, 787 | "stream-to-observable": { 788 | "version": "0.1.0", 789 | "resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.1.0.tgz", 790 | "integrity": "sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4=", 791 | "dev": true 792 | }, 793 | "string-width": { 794 | "version": "1.0.2", 795 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 796 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 797 | "dev": true, 798 | "requires": { 799 | "code-point-at": "^1.0.0", 800 | "is-fullwidth-code-point": "^1.0.0", 801 | "strip-ansi": "^3.0.0" 802 | } 803 | }, 804 | "strip-ansi": { 805 | "version": "3.0.1", 806 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 807 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 808 | "dev": true, 809 | "requires": { 810 | "ansi-regex": "^2.0.0" 811 | } 812 | }, 813 | "strip-eof": { 814 | "version": "1.0.0", 815 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 816 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", 817 | "dev": true 818 | }, 819 | "supports-color": { 820 | "version": "2.0.0", 821 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 822 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 823 | "dev": true 824 | }, 825 | "symbol-observable": { 826 | "version": "1.0.4", 827 | "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz", 828 | "integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0=", 829 | "dev": true 830 | }, 831 | "util": { 832 | "version": "0.10.3", 833 | "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", 834 | "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", 835 | "requires": { 836 | "inherits": "2.0.1" 837 | } 838 | }, 839 | "utils-extend": { 840 | "version": "1.0.8", 841 | "resolved": "https://registry.npmjs.org/utils-extend/-/utils-extend-1.0.8.tgz", 842 | "integrity": "sha1-zP17ZFQPjpDuIe7Fd2nQZRyril8=" 843 | }, 844 | "uuid": { 845 | "version": "3.4.0", 846 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 847 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" 848 | }, 849 | "which": { 850 | "version": "1.2.14", 851 | "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", 852 | "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", 853 | "dev": true, 854 | "requires": { 855 | "isexe": "^2.0.0" 856 | } 857 | }, 858 | "xcode": { 859 | "version": "git+https://github.com/apache/cordova-node-xcode.git#b81970519188e4bac022f04c86a44f2437c4981e", 860 | "from": "git+https://github.com/apache/cordova-node-xcode.git", 861 | "requires": { 862 | "simple-plist": "^1.0.0", 863 | "uuid": "^3.3.2" 864 | } 865 | }, 866 | "xmlbuilder": { 867 | "version": "8.2.2", 868 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", 869 | "integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M=" 870 | }, 871 | "xmldom": { 872 | "version": "0.1.27", 873 | "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", 874 | "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=" 875 | }, 876 | "yallist": { 877 | "version": "2.1.2", 878 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 879 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", 880 | "dev": true 881 | } 882 | } 883 | } 884 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-today-widget", 3 | "version": "1.0.0", 4 | "description": "Add a toaday widget app extension target to your cordova project.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/DavidStrausz/cordova-plugin-today-widget" 8 | }, 9 | "keywords": [ 10 | "iOS", 11 | "App Extension", 12 | "Today Widget", 13 | "Cordova", 14 | "Ionic", 15 | "Plugin", 16 | "node-excode" 17 | ], 18 | "author": "David Strauß", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/DavidStrausz/cordova-plugin-today-widget/issues" 22 | }, 23 | "homepage": "https://github.com/DavidStrausz/cordova-plugin-today-widget", 24 | "dependencies": { 25 | "elementtree": "^0.1.7", 26 | "file-system": "^2.2.2", 27 | "path": "^0.12.7", 28 | "plist": "^2.1.0", 29 | "q": "^1.5.1", 30 | "xcode": "git+https://github.com/apache/cordova-node-xcode.git" 31 | }, 32 | "devDependencies": { 33 | "husky": "^0.13.4", 34 | "lint-staged": "^3.6.0", 35 | "prettier": "^1.4.2" 36 | }, 37 | "scripts": { 38 | "precommit": "lint-staged" 39 | }, 40 | "lint-staged": { 41 | "*.js": [ 42 | "prettier --parser javascript --single-quote --print-width 80 --tab-width 2 --trailing-comma es5 --list-different --write", 43 | "git add" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Today Widget App Extension 7 | David Strauß 8 | 9 | 10 | Add a today widget app extension target to your cordova project. 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | group.__APP_IDENTIFIER__ 23 | 24 | 25 | 26 | 27 | group.__APP_IDENTIFIER__ 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | --------------------------------------------------------------------------------