Coder Social home page Coder Social logo

cordova-common's Introduction

cordova-common

NPM

Node CI

Exposes shared functionality used by cordova-lib and Cordova platforms.

Exposed APIs

events

Represents special instance of NodeJS EventEmitter which is intended to be used to post events to cordova-lib and cordova-cli

Usage:

const { events } = require('cordova-common');
events.emit('warn', 'Some warning message')

Here are the following supported events by cordova-cli:

  • verbose
  • log
  • info
  • warn
  • error

CordovaError

An error class used by Cordova to throw cordova-specific errors. The CordovaError class is inherited from Error, so it is a valid instance of Error. (instanceof check succeeds).

Usage:

const { CordovaError } = require('cordova-common');
throw new CordovaError('Some error message', SOME_ERR_CODE);

See CordovaError for supported error codes.

ConfigParser

Exposes functionality to deal with cordova project config.xml files. For ConfigParser API reference check ConfigParser Readme.

Usage:

const { ConfigParser } = require('cordova-common');
const appConfig = new ConfigParser('path/to/cordova-app/config.xml');
console.log(`${appconfig.name()}:${appConfig.version()}`);

PluginInfoProvider and PluginInfo

PluginInfo is a wrapper for cordova plugins' plugin.xml files. This class may be instantiated directly or via PluginInfoProvider. The difference is that PluginInfoProvider caches PluginInfo instances based on plugin source directory.

Usage:

const { PluginInfo, PluginInfoProvider }  = require('cordova-common');

// The following instances are equal
const plugin1 = new PluginInfo('path/to/plugin_directory');
const plugin2 = new PluginInfoProvider().get('path/to/plugin_directory');

console.log(`The plugin ${plugin1.id} has version ${plugin1.version}`)

ActionStack

Utility module for dealing with sequential tasks. Provides a set of tasks that are needed to be done and reverts all tasks that are already completed if one of those tasks fail to complete. Used internally by cordova-lib and platform's plugin installation routines.

Usage:

const { ActionStack } = require('cordova-common');

const stack = new ActionStack();
const action1 = stack.createAction(task1, [<task parameters>], task1_reverter, [<reverter_parameters>]);
const action2 = stack.createAction(task2, [<task parameters>], task2_reverter, [<reverter_parameters>]);

stack.push(action1);
stack.push(action2);

stack.process()
    .then(() => {
        // all actions succeded
    })
    .catch(error => {
        // One of actions failed with error
    });

superspawn

Module for spawning child processes with some advanced logic.

Usage:

const { superspawn } = require('cordova-common');

superspawn.spawn('adb', ['devices'])
    .progress(data => {
        if (data.stderr) console.error(`"adb devices" raised an error: ${data.stderr}`);
    })
    .then(devices => {
        // Do something...
    });

xmlHelpers

A set of utility methods for dealing with XML files.

Usage:

const { xmlHelpers } = require('cordova-common');

const doc1 = xmlHelpers.parseElementtreeSync('some/xml/file');
const doc2 = xmlHelpers.parseElementtreeSync('another/xml/file');

xmlHelpers.mergeXml(doc1, doc2); // doc2 now contains all the nodes from doc1

Other APIs

The APIs listed below are also exposed but are intended to be only used internally by cordova plugin installation routines.

  • PlatformJson
  • ConfigChanges
  • ConfigKeeper
  • ConfigFile

Setup

  • Clone this repository onto your local machine

    git clone https://github.com/apache/cordova-common.git
  • Navigate to cordova-common directory, install dependencies and npm-link

    cd cordova-common && npm install && npm link
  • Navigate to cordova-lib directory and link cordova-common

    cd <cordova-lib directory> && npm link cordova-common && npm install

cordova-common's People

Contributors

audreyso avatar breautek avatar brodybits avatar carynbear avatar dependabot[bot] avatar dpa99c avatar dpogue avatar erisu avatar filmaj avatar holyhoehle avatar infil00p avatar jcaron23 avatar juliascript avatar knaito-asial avatar ktop avatar libremente avatar matrosov-nikita avatar nikhilkh avatar niklasmerz avatar oliversalzburg avatar pbakondy avatar piotr-cz avatar purplecabbage avatar raphinesse avatar remcohaszing avatar riknoll avatar shazron avatar stevengill avatar tylerbreau avatar vladimir-kotikov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cordova-common's Issues

PluginInfo getPreferences is designed in a way that prevents safely adding new attributes

Bug Report

Problem

PluginInfo getPreferences is designed in a way that prevents safely adding new attributes.

While I am not aware of a need to add new attributes to the preference tag, if a need comes up in the future it does not appear to be supported at all.

What is expected to happen?

Adding new attributes should probably be possible.

What does actually happen?

Preferences are aggregated into a single object and the value in the name attribute is used to create new attributes on this object.
Common attributes are overwritten. For example if you have 2 preferences with the names pref1 and pref2.
The object will contain 1 name attribute with either the value pref1 or pref2.

If you wanted to add a new attribute, difference preferences would override each other's new attribute.

Information

I noticed this while preparing unit tests for the XML Passthrough update, #181.

    Seems like it is not possible to support additional attributes on preferences.
describe('Preference', () => {
    it('Test 007: Preference supports xml passthrough', function () {
        const preferences = pluginPassthrough.getPreferences('android');
        console.log(preferences);
        expect(preferences.passthroughpref.anattrib).toBe('value');
    });
});


<preference name="passthroughpref" anattrib="value" />
<!-- <preference name="passthroughpref2" anattrib="value2" /> -->

{ name: 'passthroughpref', anattrib: 'value', PASSTHROUGHPREF: null }



<preference name="passthroughpref" anattrib="value" />
<preference name="passthroughpref2" anattrib="value2" />

{
  name: 'passthroughpref2',
  anattrib: 'value2',
  PASSTHROUGHPREF: null,
  PASSTHROUGHPREF2: null
}

Originally posted by @TylerBreau in #181 (comment)

Command or Code

XML file with 2 or more preferences.
You will see attributes like name will overwrite eachother.

Environment, Platform, Device

N/A

Version information

cordova-common master, commit fc966d1

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

Remove Platform Specific Code

Overview & Issue

Currently, cordova-common has platform-specific code which should reside in each platform repo.

For example, when implementing the new API to support Android's Adaptive Icons, adding the necessary attributes to the icon element in config.xml is currently handled in common.

In a perfect world, common should only control the basic elements and attributes that are shared among all platforms.

Goal

The goal of this ticket is to remove the platform-specific code from cordova-common and move them to their respective repo.

  • This will involve converting the code into a base structure where each platform can extend each class. The platform will then inject its additional or modified logic.

  • The platform-specific unit testing code will need to migrate to each respective repo.

  • Remove the need to pass in platform type when the platform type is already known.

Work in Progress

Not possible to disable backup, because of config conflicts are handled

Bug Report

I would like to set the following setting in my config.xml , to disable backups as it can result in major problems with state.
However, that's not possible if you have plugins that also alter AndroidManifest.xml application, ie requestLegacyExternalStorage as has also been introduced in the master branch of cordova-file.

Config.xml

<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application" xmlns:android="http://schemas.android.com/apk/res/android">
  <application android:allowBackup="false"/>
</edit-config>

plugin.xml

<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application">
  <application android:requestLegacyExternalStorage="true" />
</edit-config>
Failed to install 'cordova-plugin-file': Error: cordova-plugin-file cannot be added. <edit-config> changes in this plugin conflicts with <edit-config> changes in config.xml. Conflicts must be resolved before plugin can be added.

Problem

I can't disable android backup, and use plugins like cordova-file from master branch, or telerik-image-picker because they have the same "target", not because they actually conflict.
This is also a problem because many others target the same xml element, because it hosts a lot of application settings as per the Android Documentation: https://developer.android.com/guide/topics/manifest/application-element

What is expected to happen?

Android backup is disabled, plugins are useable

What does actually happen?

Conflict checker cancels plugin install on Android platform.

Information

Command or Code

with the following added to config.xml

<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application" xmlns:android="http://schemas.android.com/apk/res/android">
  <application android:allowBackup="false"/>
</edit-config>

Cordova Plugin add cordova-plugin-telerik-imagepicker@latest

or
cordova plugin add https://github.com/apache/cordova-plugin-file/

Environment, Platform, Device

Cordova-Android 9.0

Version information

Cordova CLI 10.0.0
Cordova Android 9.0.0
Cordova Plugin Telerik ImagePicker 2.3.5
Cordova Plugin File@Master

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

Error: Cannot find module '@netflix/nerror'

Bug Report

Problem

The exception below is being triggered reproducible with the most recent versions of Cordova.

What is expected to happen?

Adding the iOS platform should work seamlessly for all plugins.

What does actually happen?

Error: Cannot find module '@netflix/nerror'
Require stack:
- [...]/platforms/ios/cordova/node_modules/cordova-common/src/CordovaError.js
- [...]/platforms/ios/cordova/node_modules/cordova-common/cordova-common.js
- [...]/platforms/ios/cordova/lib/plugman/pluginHandlers.js
- [...]/platforms/ios/cordova/lib/projectFile.js
- [...]/platforms/ios/cordova/Api.js
- [...]/platforms/ios/cordova/version
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:1029:15)
    at Function.Module._load (internal/modules/cjs/loader.js:898:27)
    at Module.require (internal/modules/cjs/loader.js:1089:19)
    at require (internal/modules/cjs/helpers.js:73:18)
    at Object.<anonymous> ([...]/platforms/ios/cordova/node_modules/cordova-common/src/CordovaError.js:22:20)
    at Module._compile (internal/modules/cjs/loader.js:1200:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1220:10)
    at Module.load (internal/modules/cjs/loader.js:1049:32)
    at Function.Module._load (internal/modules/cjs/loader.js:937:14)
    at Module.require (internal/modules/cjs/loader.js:1089:19) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '[...]/platforms/ios/cordova/node_modules/cordova-common/src/CordovaError.js',
    '[...]/platforms/ios/cordova/node_modules/cordova-common/cordova-common.js',
    '[...]/platforms/ios/cordova/lib/plugman/pluginHandlers.js',
    '[...]/platforms/ios/cordova/lib/projectFile.js',
    '[...]/platforms/ios/cordova/Api.js',
    '[...]/platforms/ios/cordova/version'
  ]
}

Command or Code

cordova platform add ios

Versions & Environment

  • macOS 10.14.6
  • Node v14.4.0
  • npm 6.14.5
  • Cordova 9.0.0 ([email protected])
  • cordova-ios@^6.0.0

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

doc.find is not a function

Bug Report

Problem

What is expected to happen?

cordova prepare should be finished successful

What does actually happen?

ios prepare finished with
doc.find is not a function

doc.find is not a function
TypeError: doc.find is not a function
    at Object.resolveParent (/Users/.../.npm-global/lib/node_modules/cordova/node_modules/cordova-common/src/util/xml-helpers.js:131:51)

the problem is on this code block
image

Information

The /cordova_v10_staging/platforms/ios/ios.json file contain only two permission descriptions in config_munge.files.*-Info.plist section:

image

should contain all permissions

my ios config:

<platform name="ios">
    <icon src="res/ios/icon/icon-20.png" width="20" height="20"/>
    <icon src="res/ios/icon/[email protected]" width="40" height="40"/>
    <icon src="res/ios/icon/[email protected]" width="60" height="60"/>
    <icon src="res/ios/icon/[email protected]" width="48" height="48"/>
    <icon src="res/ios/icon/[email protected]" width="55" height="55"/>
    <icon src="res/ios/icon/icon-29.png" width="29" height="29"/>
    <icon src="res/ios/icon/[email protected]" width="58" height="58"/>
    <icon src="res/ios/icon/[email protected]" width="87" height="87"/>
    <icon src="res/ios/icon/icon-40.png" width="40" height="40"/>
    <icon src="res/ios/icon/[email protected]" width="80" height="80"/>
    <icon src="res/ios/icon/[email protected]" width="120" height="120"/>
    <icon src="res/ios/icon/[email protected]" width="88" height="88"/>
    <icon src="res/ios/icon/icon-50.png" width="50" height="50"/>
    <icon src="res/ios/icon/[email protected]" width="100" height="100"/>
    <icon src="res/ios/icon/icon-60.png" width="60" height="60"/>
    <icon src="res/ios/icon/[email protected]" width="120" height="120"/>
    <icon src="res/ios/icon/[email protected]" width="180" height="180"/>
    <icon src="res/ios/icon/icon-72.png" width="72" height="72"/>
    <icon src="res/ios/icon/[email protected]" width="144" height="144"/>
    <icon src="res/ios/icon/icon-76.png" width="76" height="76"/>
    <icon src="res/ios/icon/[email protected]" width="152" height="152"/>
    <icon src="res/ios/icon/[email protected]" width="167" height="167"/>
    <icon src="res/ios/icon/[email protected]" width="172" height="172"/>
    <icon src="res/ios/icon/[email protected]" width="196" height="196"/>
    <icon src="res/ios/icon/[email protected]" width="216" height="216"/>
    <icon src="res/ios/icon/icon-1024.png" width="1024" height="1024"/>
    <icon src="res/ios/icon/icon.png" width="57" height="57"/>
    <icon src="res/ios/icon/[email protected]" width="114" height="114"/>
    <!-- Default image to be used for all modes -->
    <splash src="res/ios/splash/Default@2x~universal~anyany.png"/>
    <allow-intent href="itms:*"/>
    <allow-intent href="itms-apps:*"/>
    <preference name="deployment-target" value="11.0"/>
    <preference name="iosPersistentFileLocation" value="Library"/>
    <preference name="BackupWebStorage" value="none"/>
    <edit-config file="*-Info.plist" mode="merge" target="NSContactsUsageDescription">
      <string>Der Zugriff wird verwendet, um Kontaktdaten zur Auswahl zu stellen</string>
    </edit-config>
    <edit-config file="*-Info.plist" mode="merge" target="NSPhotoLibraryUsageDescription">
      <string>Der Zugriff wird verwendet, um Fotos zur Auswahl zu stellen</string>
    </edit-config>
    <edit-config file="*-Info.plist" mode="merge" target="NSCameraUsageDescription">
      <string>Der Zugriff wird verwendet, um die Aufnahme von Fotos zur Auswahl zu stellen</string>
    </edit-config>
    <edit-config file="*-Info.plist" mode="merge" target="NSMicrophoneUsageDescription">
      <string>Der Zugriff wird verwendet, um für ein Video die Tonaufnahme zu aktivieren</string>
    </edit-config>
    <edit-config file="*-Info.plist" mode="merge" target="ITSAppUsesNonExemptEncryption">
      <false/>
    </edit-config>
    <edit-config file="*-Info.plist" mode="merge" target="CFBundleDevelopmentRegion">
      <string>de_DE</string>
    </edit-config>
    <edit-config file="*-Info.plist" target="LSApplicationQueriesSchemes" mode="merge">
      <array>
        <string>cydia</string>
      </array>
    </edit-config>
    <edit-config file="*-Info.plist" target="UISupportedInterfaceOrientations" mode="overwrite">
      <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationPortraitUpsideDown</string>
      </array>
    </edit-config>
    <!-- Set orientation on iPad -->
    <edit-config file="*-Info.plist" target="UISupportedInterfaceOrientations~ipad" mode="overwrite">
      <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationPortraitUpsideDown</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
      </array>
    </edit-config>
    <preference name="WKWebViewOnly" value="true"/>
    <preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine"/>
    <feature name="CDVWKWebViewEngine">
      <param name="ios-package" value="CDVWKWebViewEngine"/>
    </feature>
  </platform>

Command or Code

I use simple bash script for cleaning, install and upgrade plugins and platforms. In this case I just use clean function

function clean_cordova_project_folder() {
    cd "$1" || exit

    echo "clean_cordova_project_folder -> $1"

   #  rm -rf node_modules
    rm -rf platforms
    rm -rf plugins
    rm -rf ul_web_hooks
    rm -rf package-lock.json

    npm install

    cordova prepare --verbose --debug

    cd ..
}


APP_FOLDER_STAGE="<yout_cordova_project_folder>" 
# 1. clean folders and install packages
clean_cordova_project_folder "$APP_FOLDER_STAGE"

Environment, Platform, Device

  • macos Catalina (10.15.7 (19H512))

Version information

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

Fix license spec of transitive xmldom dependency

ref: https://github.com/apache/cordova-coho/blob/master/docs/tools-release-process.md

When we do coho check-license to check the dependency licenses, we get the following message on xmldom at the end:

└─ known-issues
   ├─ manually-verified-versions
   │  └─ 0: [email protected]
   ├─ license-key: not a compatible SPDX expression, and also used the plural 'licenses' as a key.
   ├─ pull-request-to-fix: https://github.com/jindw/xmldom/pull/178
   ├─ verified-license: (LGPL or MIT)
   └─ verified-license-notes: https://github.com/jindw/xmldom/blob/master/LICENSE . Note that the authors have omitted tags in their repo for their npm releases, but the hashes can be grabbed from 'npm info xmldom'

@shazron already got it fixed in jindw/xmldom#178 but they never did npm publish with the fix.

This xmldom issue affects all platform releases as well.

I hope we can get this fixed on xmldom in the near future.

Problem with edit-config attribute in plugin.xml

Bug Report

Problem

A specific configuration done using edit-config element in plugin.xml, applies properly when plugin is added through cordova plugin add. But the same edit-config results in a bug if cordova prepare command is used whenever after adding the plugin.
The config that i used is as follows,

<edit-config file="AndroidManifest.xml" target="/manifest/application/activity[@android:name='MainActivity']" mode="merge">
      <activity android:name="org.apache.cordova.log.MultiViewActivity" />
</edit-config>

I tried to use my custom activity in place of MainActivity. Default AndroidManifest.xml activity tag is as follows,

<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" android:label="@string/activity_name"  android:name="MainActivity">
            <intent-filter android:label="@string/launcher_name">
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

What is expected to happen?

activity tag in the AndroidManifest.xml should be updated with android:name="MultiViewActivity" instead of "MainActivity"

<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" android:label="@string/activity_name"  android:name="MultiViewActivity">
            <intent-filter android:label="@string/launcher_name">
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

What does actually happen?

First time when i add plugin, it works fine. The MainActivity name is replaced with MultiViewActivity.
But after i run cordova prepare, the extra activity tag for same className get appened which causes two activity element with same name error and build fails. Even console error is thrown for cordova prepare command

<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" android:label="@string/activity_name"  android:name="MultiViewActivity">
            <intent-filter android:label="@string/launcher_name">
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

<activity android:name="org.apache.cordova.plugin_name.MultiViewActivity" />

Information

Command or Code

Environment, Platform, Device

I am using cordova 10.0.0 and cordova-android version 9.0.0

Version information

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

config-file is merged into wrong *-Info.plist when other plugin has podspec.

Bug Report

The config-file in plug.xml is merged into wrong *-info.plist when it has installed Pods.

Here is the step to reproduce

npx cordova create SampleApp com.sample.app SampleApp
cd SampleApp/
npx cordova platform add ios
npx cordova plugin add cordova-plugin-facebook4
npx cordova plugin add cordova-plugin-background-mode

cordova-plugin-facebook4 installs cocoaPods Facebook SDK.

cordova-plugin-background-mode has UIBackgroundMode config-file:

<config-file target="*-Info.plist" parent="UIBackgroundModes">
            <array>
                <string>audio</string>
            </array>
</config-file>

It should have merged into SampleApp-Info.plist but it merged into Pods/Target Support Files/Pods-SampleApp/Pods-SampleApp-Info.plist.

Screen Shot 2020-05-13 at 3 10 13 PM

However, if the project name is lower than 'Pods' (ex 'MyApp'), the issue does not happen.

ConfigFIle.js for iOS Info.plist when the CocoaPods version is 1.6.* or later

Bug Report

Image the config.xml or plugin.xml includes config-file tag like

      <config-file target="*-Info.plist" parent="SampleTest">
        <array>
          <string>hello</string>
        </array>
      </config-file> 

Then the cordova-common searches [Application Name]-Info.plist file under the platform/ios in order to update [Application Name]-Info.plist file in cordova prepare process.

This logic can be found in src/ConfigChanges/ConfigFile.js in cordova-common as

       // [CB-5989] multiple Info.plist files may exist. default to $PROJECT_NAME-Info.plist
        if (matches.length > 1 && file.includes('-Info.plist')) {
            var plistName = getIOSProjectname(project_dir) + '-Info.plist';
            for (var i = 0; i < matches.length; i++) {
                if (matches[i].includes(plistName)) {
                    filepath = matches[i];
                    break;
                }
            }
        }

This works well if the cocoapods version is 1.5.* or earlier.

However if the cocoapods version is 1.6.* or later, the project includes plugins which include cocoapods libraries with subdirectory path like <pod name="Firebase/Core" spec="6.3.0"/> , the file

  platforms/ios/Pods/Target Support Files/Pods-[Application Name]/Pods-[Application Name]-Info.plist

is created and this may lead wrong behavior in config-file tag with target *-Info.plist in cordova prepare ios.

This created file satisfies the above logic (i.e. finding *-Info.plist file) condition.

(Rematk that If the cocoapods version is 1.5.* or earlier, that file name is different. Actually the created file in this case is

  platforms/ios/Pods/Target Support Files/Pods-[Application Name]/Info.plist

. Therefore no problem happens)

Problem

If the build environment uses CocoaPods 1.6.* or later and the project uses CocoaPods Library with subdirectory path structure like <pod name="Firebase/Core" spec="6.3.0"/>,
There are two files which satisfies the above logic condition (i.e. *-Info.plist file finding condition) in config-file. That two files' path are

platforms/ios/[Application Name]-Info.plist
platforms/ios/Pods/Target Support Files/Pods-[Application Name]/Info.plist

If the first letter of [Application Name] is Q-Z (i.e. the letter after P of Pods) , then the above logic detect the wrong file

 platforms/ios/Pods/Target Support Files/Pods-[Application Name]/Info.plist

instead of the correct file

platforms/ios/[Application Name]-Info.plist

as its target file.

What is expected to happen?

Plugin's config-file with the target *-Info.plist must be applied to the correct one

platforms/ios/[Application Name]-Info.plist

What does actually happen?

Plugin's config-file with the target *-Info.plist is applied to

platforms/ios/Pods/Target Support Files/Pods-[Application Name]/Info.plist

If all followings are satisfied,

i) cocoapods version is 1.6.* or later,
ii) some cocoapods libraries use subdirectory path structure. (like cordova-plugin-firebasex plugin. This plugin is different from cordova-plugin-firebase. )
iii) config-file tag in config.xml or plugin.xml changes *-Info.plist file.

Information

Command or Code

How to reproduce this iusse,

  1. create cordova project
$ cordova create sampleProject
  1. replace package.json
{
  "name": "helloworld",
  "displayName": "HelloCordova",
  "version": "1.0.0",
  "description": "A sample Apache Cordova application that responds to the deviceready event.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "ecosystem:cordova"
  ],
  "author": "Apache Cordova Team",
  "license": "Apache-2.0",
  "dependencies": {
    "cordova-ios": "^5.0.1",
    "cordova-plugin-androidx": "^1.0.2",
    "cordova-plugin-androidx-adapter": "^1.1.0",
    "cordova-plugin-firebasex": "^6.0.4"
  },
  "devDependencies": {
    "cordova-plugin-whitelist": "^1.3.4"
  },
  "cordova": {
    "plugins": {
      "cordova-plugin-whitelist": {},
      "cordova-plugin-firebasex": {}
    },
    "platforms": [
      "ios"
    ]
  }
}
  1. update config.xml
<widget ...>
    <name>Z-HelloCordova</name>
   ...
    <platform name="ios">
      <config-file target="*-Info.plist" parent="SampleTest">
        <array>
          <string>hello</string>
        </array>
      </config-file> 
      <allow-intent href="itms:*" />
      <allow-intent href="itms-apps:*" />
    </platform>     
  1. do cordova prepare
$ cordova prepare ios

Then thare is no SampleTest in platforms/ios/Z-HelloCordova/Z-HelloCordova-Info.plist

However if we use the application name A-HelloCordova

    <name>A-HelloCordova</name>

in step2
, there is SampleTest in platforms/ios/A-HelloCordova/A-HelloCordova-Info.plist as

	<key>SampleTest</key>
	<array>
		<string>hello</string>
	</array>

Environment, Platform, Device

Mac
ruby 2.3.7p456
CocoaPods 1.7.5
xcodebuild 10.3 (Build Version 10G8)
(Remark that this issue happens in cordova prepare ios)

Version information

cordova-cli 9.0.0 ([email protected])
cordova-ios (in project node_modules) 5.0.1
cordova-common (in project node_modules) 3.2.0

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

Failed to install '<plugin>': TypeError: Cannot read properties of undefined (reading 'id')

Bug Report

Problem

I have created an internal cordova plugin at our company which adds the android:networkSecurityConfig attribute to the /manifest/application/ tag in AndroidManifest.xml via the following <edit-config:

<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application">
   <application android:networkSecurityConfig="@xml/uq_network_security_config" />
</edit-config>

This works flawlessly if the plugin is added the first time. But if the plugin is removed and then re-added again an error occurrs.

What is expected to happen?

The plugin should be added again without any issues.

What does actually happen?

If the plugin is re-added, the following error is produced:

Failed to install 'uq-cordova-plugin-settings': TypeError: Cannot read properties of undefined (reading 'id')
    at registerConflict (D:\CordovaApp\node_modules\cordova-common\src\ConfigChanges\ConfigChanges.js:255:43)
    at D:\CordovaApp\node_modules\cordova-common\src\ConfigChanges\ConfigChanges.js:283:34
    at Array.forEach (<anonymous>)
    at PlatformMunger._is_conflicting (D:\CordovaApp\node_modules\cordova-common\src\ConfigChanges\ConfigChanges.js:259:21)
    at PlatformMunger.add_plugin_changes (D:\CordovaApp\node_modules\cordova-common\src\ConfigChanges\ConfigChanges.js:106:59)
    at D:\CordovaApp\node_modules\cordova-common\src\PluginManager.js:120:33
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Cannot read properties of undefined (reading 'id')

Information

The problem is that, upon removal, the platforms/android/android.json looks like the following:

"AndroidManifest.xml": {
  "parents": {
    "/*": [
      {
        "xml": "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />",
        "count": 1
      },
      {
        "xml": "<queries><intent><action android:name=\"android.media.action.IMAGE_CAPTURE\" /></intent><intent><action android:name=\"android.intent.action.GET_CONTENT\" /></intent><intent><action android:name=\"android.intent.action.PICK\" /></intent><intent><action android:name=\"com.android.camera.action.CROP\" /><data android:mimeType=\"image/*\" android:scheme=\"content\" /></intent></queries>",
        "count": 1
      },
      ...
    ],
    "application": [],
    "/manifest/application": []
  }
}

Notice the empty array for /manifest/application.

The code in ConfigChanges.js registerConflict accesses the first entry in the array, which does not exist in this case. Therefore, the above error is being thrown.
image

With a slight adjustment to the code the above error could be avoided, but it leads to another error ("There was a conflict trying to modify attributes with") which is a different issue, I guess.
image

Command or Code

Create a new cordova project, create a new plugin directory with the <edit-config lines from the beginning of the issue.

cordova create hello com.example.hello HelloWorld
mkdir cordova-plugin-test
# add plugin.xml and package.json to cordova-plugin-test
cd hello
cordova platform add [email protected]
cordova plugin add ..\cordova-plugin-test
cordova plugin remove cordova-plugin-test
cordova plugin add ..\cordova-plugin-test
# => produces the error

A minimum viable plugin.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
        id="cordova-plugin-test" version="0.0.1">
    <name>Test</name>
    <description>Cordova Device Test</description>
    <license>Apache 2.0</license>
    <keywords>cordova,test</keywords>

    <platform name="android">
      <edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application">
            <application android:networkSecurityConfig="@xml/uq_network_security_config" />
        </edit-config>
    </platform>
</plugin>

The package.json looks like this:

{
  "name": "cordova-plugin-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Environment, Platform, Device

Environment: Windows 10 with PowerShell

Version information

Cordova-Cli: 12.0.0 ([email protected])
Cordova Platform Android: 12.0.0
Windows 10
PowerShell 7

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

Config changes not properly applied if paths differ but resolve to same file

Bug Report

Problem

config-file and edit-config options in config.xml do not work properly.

I am trying to make 3 modifications to AndroidManifest.xml:

  • Add WAKE_LOCK permission (and add uses-feature for touchscreen)
<config-file parent="/manifest" target="AndroidManifest.xml">
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-feature android:name="android.hardware.touchscreen" android:required="true" />
</config-file>
  • Modify activity tag's theme property
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application/activity[@android:label='@string/activity_name']">
    <activity android:theme="@style/Theme.FullScreen.Splash" />
</edit-config>
  • Modify application tag's usesCleartextTraffic property
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
    <application android:usesCleartextTraffic="true" />
</edit-config>

Depending on the order these config-file and edit-config tags appear in config.xml the result is different.

What is expected to happen?

Expected AndroidManifest.xml to have all 3 updates:

<application android:hardwareAccelerated="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:usesCleartextTraffic="true">
    <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" android:label="@string/activity_name" android:launchMode="singleTop" android:name="MainActivity" android:theme="@style/Theme.FullScreen.Splash" android:windowSoftInputMode="adjustResize">
        <intent-filter android:label="@string/launcher_name">
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-feature android:name="android.hardware.touchscreen" android:required="true" />

What does actually happen?

Depending on the order these config-file and edit-config tags appear in config.xml the result is different.
I can only get either: [WAKE_LOCK and Theme], or [clearText].

[WAKE_LOCK and Theme]:

    <application android:hardwareAccelerated="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true">
        <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" android:label="@string/activity_name" android:launchMode="singleTop" android:name="MainActivity" android:theme="@style/Theme.FullScreen.Splash" android:windowSoftInputMode="adjustResize">
            <intent-filter android:label="@string/launcher_name">
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-feature android:name="android.hardware.touchscreen" android:required="true" />

[clearText]:

    <application android:hardwareAccelerated="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:usesCleartextTraffic="true">
        <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" android:label="@string/activity_name" android:launchMode="singleTop" android:name="MainActivity" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:windowSoftInputMode="adjustResize">
            <intent-filter android:label="@string/launcher_name">
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

Summary of the different order combinations > results:
image

Information

Command or Code

I have tried refreshing AndroidManifest.xml with:

  • cordova platform rm android && cordova platfrom add android
  • Deleting /platforms, /node_modules, and /plugins, and running cordova prepare android

Environment, Platform, Device

Windows 10

Version information

cordova -v
10.0.0-dev ([email protected])

In particular, cordova-cli commit 11ce340a47719060b57bcfe18852b20f21a69c65
[email protected]

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

Fluent interface for ConfigParser

Implement a Fluent interface for ConfigParser.

As a developer, I want to do

new ConfigParser('...')
    .addEngine(...)
    .addPlugin(...)
    .write();

Should be possible as a non-breaking change. If in doubt, we could export something like the following as a first step:

class FluentConfigParser extends ConfigParser {
    addPlugin (...args) {
        return (super.addPlugin(...args), this);
    }
    addEngine (...args) {
        return (super.addEngine(...args), this);
    }
}

CordovaError test fails

On my local machine (Ubuntu 18.10, Node 10.10.0, npm 6.4.1) one test fails with latest master:

Failures:
1) CordovaError class Test 003 : toString works
  Message:
    Expected 'error
        at' to equal 'CordovaError'.
  Stack:
    Error: Expected 'error
        at <Jasmine>
        at UserContext.<anonymous> (/home/raphinesse/cordova/code/cordova-common/spec/CordovaError/CordovaError.spec.js:37:60)
        at <Jasmine>
        at runCallback (timers.js:694:18)
        at tryOnImmediate (timers.js:665:5)
        at processImmediate (timers.js:647:5)

The test was introduced in ce3801a as part of #24. That is also the first commit where the test fails for me. Curiously, the CI run of that commit is green.

Do you have any idea what might be going on here @knight9999?

Unable to graft xml at selector "manifest/uses-sdk"

Bug Report

Problem

What is expected to happen?

<edit-config> to apply changes to AndroidManifest.xml

What does actually happen?

An error occurs and the build fails.

Information

Error stacktrace:

Unable to graft xml at selector "/manifest/uses-sdk" from "C:\Users\norman\development\gradletest\platforms\android\app\src\main\AndroidManifest.xml" during config install
Error: Unable to graft xml at selector "/manifest/uses-sdk" from "C:\Users\norman\development\gradletest\platforms\android\app\src\main\AndroidManifest.xml" during config install
    at ConfigFile_graft_child [as graft_child] (C:\Users\norman\AppData\Roaming\npm\node_modules\cordova\node_modules\cordova-common\src\ConfigChanges\ConfigFile.js:120:19)
    at PlatformMunger_apply_file_munge [as apply_file_munge] (C:\Users\norman\AppData\Roaming\npm\node_modules\cordova\node_modules\cordova-common\src\ConfigChanges\ConfigChanges.js:81:34)
    at munge_helper (C:\Users\norman\AppData\Roaming\npm\node_modules\cordova\node_modules\cordova-common\src\ConfigChanges\ConfigChanges.js:238:14)
    at PlatformMunger.add_config_changes (C:\Users\norman\AppData\Roaming\npm\node_modules\cordova\node_modules\cordova-common\src\ConfigChanges\ConfigChanges.js:216:12)
    at C:\Users\norman\AppData\Roaming\npm\node_modules\cordova\node_modules\cordova-lib\src\cordova\prepare.js:112:32
    at async Promise.all (index 0)

Command or Code

Use cordova create to build a simple hello world project.

Inside the config.xml add:

<edit-config file="AndroidManifest.xml" mode="overwrite" target="/manifest/uses-sdk">
    <uses-sdk android:minSdkVersion="16" android:maxSdkVersion="23" />
</edit-config>

This is a copied and paste example from the docs. But this appears to occur when having target="/manifest/uses-sdk", regardless of the body of <edit-config> block. This also fails if mode="merge" is set.

Do note that the following does not fail:

<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest">
    <manifest xmlns:tools="http://schemas.android.com/tools" />
</edit-config>

This leads me to believe perhaps this occurs if the AndroidManifest.xml does not have the target node.

Finally run: cordova build android to observe the failure.

Environment, Platform, Device

Windows 10
Cordova-Android 9

Version information

Windows 10
cordova: 9.0.0 ([email protected])

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

config-file conflicts between plugins and config.xml

Bug Report

Problem

I am using https://github.com/QuentinFarizon/cordova-plugin-ble-central/tree/dfu in which plugin.xml adds two tags inside AndroidManufest application tag : https://github.com/QuentinFarizon/cordova-plugin-ble-central/blob/dfu/plugin.xml#L76-L79

I am also using https://github.com/dpa99c/cordova-plugin-firebasex that documents (https://github.com/dpa99c/cordova-plugin-firebasex/#android-default-notification-icon) adding this to my config.xml :

<platform name="android">
    <config-file target="AndroidManifest.xml" parent="/manifest/application">
        <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon" />
    </config-file>
</platform>

What is expected to happen?

No conflict should occur, it should result in :

<application (....)>
   <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon" />
    <activity android:name="com.megster.cordova.ble.central.NotificationActivity" />
    <service android:name="com.megster.cordova.ble.central.DfuService" />
    (...)
</application>

What does actually happen?

During platform/plugin add, everythings goes fine.

Juste before build, AndroidManifest is correct (all lines are present).

During build, this message : "Conflict found, edit-config changes from config.xml will overwrite plugin.xml changes" (I do not have an edit-config in my config.xml).

Issue : activity and service tags from com.megster.cordova.ble.central are NOT added to AndroidManifest (they have been removed).

While debugging inside cordova-common, I can see here the details of conflict :

{
   "conflictFound":true,
   "conflictingMunge":{
      "files":{
         "AndroidManifest.xml":{
            "parents":{
               "/manifest/application":[
                  {
                     "xml":"<activity android:name=\"com.megster.cordova.ble.central.NotificationActivity\" />",
                     "count":1
                  },
                  {
                     "xml":"<service android:name=\"com.megster.cordova.ble.central.DfuService\" />",
                     "count":1
                  },
                  {
                     "xml":"<meta-data android:name=\"com.google.firebase.messaging.default_notification_icon\" android:resource=\"@drawable/notification_icon\" />",
                     "count":0,
                     "id":"config.xml"
                  }
               ]
            }
         }
      }
   },
   "configxmlMunge":{
      "files":{
         
      }
   },
   "conflictWithConfigxml":false,
   "noChanges":[
      
   ],
   "unusedConfigMunge":{
      "files":{
         "AndroidManifest.xml":{
            "parents":{
               "/manifest/application":[
                  {
                     "xml":"<meta-data android:name=\"com.google.firebase.messaging.default_notification_icon\" android:resource=\"@drawable/notification_icon\" />",
                     "count":1,
                     "id":"config.xml"
                  }
               ]
            }
         }
      }
   }
}

Information

Apart from the conflict issue, I think the build should definitely fail if some configurations lines from plugins are removed without more than a warning console line.

Command or Code

cordova build

Environment, Platform, Device

Reproduced on ubuntu and macOS

Version information

cordova 9.0.0
cordova-android 9.0.0
(tested with cordova 10 also, no luck)

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

Mark plugin-/engine-Tags as deprecated

Bug Report

On a conversation on slack it turned out, that plugin-Tags and engine-Tags are deprecated to use in config.xml. It should be clear for the user, that these tags are deprecated and they should be only maintained by the package.json-File.

Problem

There is no hint, that these tags are deprecated. The documentation don't say anything, nor the cli.
Some old users could still use the plugin- and engine-Tags in the config.xml and could be wondered what is the right way.

What is expected to happen?

There should be somewhere a hint, that these tags are deprecated.

What does actually happen?

There is nowhere a hint, that these tags are deprecated.

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

FileUpdater fails to copy symlinked files

Bug Report

Problem

cordova prepare errors out when it has to copy files that are symlinks.

What is expected to happen?

Not not error out and copy files successfully.

What does actually happen?

Produces stacktrace, as shown below.

Information

copy  www/node_modules/@totalpave/cordova-plugin-date/node_modules/.bin/atob platforms/ios/www/node_modules/@totalpave/cordova-plugin-date/node_modules/.bin/atob (updated file)
Cannot copy '../atob/bin/atob.js' to a subdirectory of itself, '../atob/bin/atob.js'.
Error: Cannot copy '../atob/bin/atob.js' to a subdirectory of itself, '../atob/bin/atob.js'.
    at onLink (/Users/normanbreau/development/tp-pci-client/node_modules/fs-extra/lib/copy-sync/copy-sync.js:146:13)
    at getStats (/Users/normanbreau/development/tp-pci-client/node_modules/fs-extra/lib/copy-sync/copy-sync.js:49:45)
    at startCopy (/Users/normanbreau/development/tp-pci-client/node_modules/fs-extra/lib/copy-sync/copy-sync.js:38:10)
    at handleFilterAndCopy (/Users/normanbreau/development/tp-pci-client/node_modules/fs-extra/lib/copy-sync/copy-sync.js:33:10)
    at Object.copySync (/Users/normanbreau/development/tp-pci-client/node_modules/fs-extra/lib/copy-sync/copy-sync.js:26:10)
    at updatePathWithStats (/Users/normanbreau/development/tp-pci-client/node_modules/cordova-common/src/FileUpdater.js:103:24)
    at /Users/normanbreau/development/tp-pci-client/node_modules/cordova-common/src/FileUpdater.js:298:19
    at Array.forEach (<anonymous>)
    at Object.mergeAndUpdateDir (/Users/normanbreau/development/tp-pci-client/node_modules/cordova-common/src/FileUpdater.js:296:33)
    at updateWww (/Users/normanbreau/development/tp-pci-client/platforms/ios/cordova/lib/prepare.js:162:17)

The file www/node_modules/@totalpave/cordova-plugin-date/node_modules/.bin/atob is a symlink to ../atob/bin/atob.js, which fs-extra seems to not handle properly without the dereference option enabled.

Also probably related to an issue reported in cordova-android platform:
apache/cordova-android#693

Command or Code

cordova plugin add @totalpave/cordova-plugin-date
cordova prepare ios

Environment, Platform, Device

Mac OS X Mojave

Version information

Cordova 9

Other useful information

Modifying the following line:

fs.copySync(sourceFullPath, targetFullPath);

To include the deference option fixed the issue for me.
https://github.com/jprichardson/node-fs-extra/blob/HEAD/docs/copy-sync.md

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

Security Vulnerability in underscore <= 1.12.0 CVE-2021-23358

A security vulnerability was detected for underscore <= 1.12.0 according to my code scanner.

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-23358

My applications do not use underscore and seems this only came as a potential risk because I'm using this library via cordova.

Any suggestions are appreciated, Thanks!

Apache Cordova uses GitHub Issues as a feature request and bug tracker only.
For usage and support questions, please check out the resources below. Thanks!


You can get answers to your usage and support questions about Apache Cordova on:


If you are using a tool that uses Cordova internally, like e.g. Ionic, check their support channels:

iOS: plist entitlements are overwritten when multiple plugins are installed (4.5.4 iOS engine)

Related to https://issues.apache.org/jira/browse/CB-13496

Description

We updated our cordova ios engine version in our app from 4.3.1 -> 4.5.4 so we could support dynamic frameworks for a new plugin i.e. use the embed attribute [https://cordova.apache.org/docs/en/8.x/plugin_ref/spec.html#framework].

After updating I noticed that the ipa didn’t have the expected keychain-access-groups entitlements and the SSO login in our app failed (which relies on these entitlements), and it had been working previously. The expected keychain entry was being added to the plist when plugin A was added to the app but then deleted when plugin B was added.

Reproduction steps

Pre-requisite: a Cordova app with plugins A and B that both modify the keychain-access-groups entitlements

  1. remove all plugins from app
  2. add plugin A and confirm the entitlements has the expected keychain-access-groups entry
  3. add plugin B

Expected: entitlements plist contains both plugin A's and B's keychain-access-groups entries

Actual: entitlements plist contains only plugin B's entry

Possible fix

CB-13496 looks to be related and applying the fix from there resolves my issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.