diff --git a/.prettierignore b/.prettierignore
index ed879107992e..f302899cc8f1 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -27,6 +27,9 @@ packages/components/css
packages/components/scss
packages/components/docs/js
+# Sketch
+**/*.sketchplugin/**
+
# Changelogs
**/CHANGELOG.md
diff --git a/.yarn-offline-mirror/@babel-polyfill-7.4.4.tgz b/.yarn-offline-mirror/@babel-polyfill-7.4.4.tgz
new file mode 100644
index 000000000000..f73d3a606c4b
Binary files /dev/null and b/.yarn-offline-mirror/@babel-polyfill-7.4.4.tgz differ
diff --git a/.yarn-offline-mirror/@skpm-babel-preset-0.2.1.tgz b/.yarn-offline-mirror/@skpm-babel-preset-0.2.1.tgz
new file mode 100644
index 000000000000..72d0f85db44a
Binary files /dev/null and b/.yarn-offline-mirror/@skpm-babel-preset-0.2.1.tgz differ
diff --git a/.yarn-offline-mirror/@skpm-builder-0.7.0.tgz b/.yarn-offline-mirror/@skpm-builder-0.7.0.tgz
new file mode 100644
index 000000000000..173e010a991d
Binary files /dev/null and b/.yarn-offline-mirror/@skpm-builder-0.7.0.tgz differ
diff --git a/.yarn-offline-mirror/@skpm-file-loader-2.0.1.tgz b/.yarn-offline-mirror/@skpm-file-loader-2.0.1.tgz
new file mode 100644
index 000000000000..c6f314ad9f53
Binary files /dev/null and b/.yarn-offline-mirror/@skpm-file-loader-2.0.1.tgz differ
diff --git a/.yarn-offline-mirror/@skpm-internal-utils-0.1.14.tgz b/.yarn-offline-mirror/@skpm-internal-utils-0.1.14.tgz
new file mode 100644
index 000000000000..c4bbe1de4175
Binary files /dev/null and b/.yarn-offline-mirror/@skpm-internal-utils-0.1.14.tgz differ
diff --git a/.yarn-offline-mirror/@skpm-nib-loader-0.1.1.tgz b/.yarn-offline-mirror/@skpm-nib-loader-0.1.1.tgz
new file mode 100644
index 000000000000..67e2b4463334
Binary files /dev/null and b/.yarn-offline-mirror/@skpm-nib-loader-0.1.1.tgz differ
diff --git a/.yarn-offline-mirror/author-regex-1.0.0.tgz b/.yarn-offline-mirror/author-regex-1.0.0.tgz
new file mode 100644
index 000000000000..e9cd8510f395
Binary files /dev/null and b/.yarn-offline-mirror/author-regex-1.0.0.tgz differ
diff --git a/.yarn-offline-mirror/cli-spinners-1.3.1.tgz b/.yarn-offline-mirror/cli-spinners-1.3.1.tgz
new file mode 100644
index 000000000000..c6abd39b3d30
Binary files /dev/null and b/.yarn-offline-mirror/cli-spinners-1.3.1.tgz differ
diff --git a/.yarn-offline-mirror/cocoascript-class-0.1.2.tgz b/.yarn-offline-mirror/cocoascript-class-0.1.2.tgz
new file mode 100644
index 000000000000..dc2e54d9fa6d
Binary files /dev/null and b/.yarn-offline-mirror/cocoascript-class-0.1.2.tgz differ
diff --git a/.yarn-offline-mirror/coscript-1.0.0.tgz b/.yarn-offline-mirror/coscript-1.0.0.tgz
new file mode 100644
index 000000000000..b2dcdc29a24d
Binary files /dev/null and b/.yarn-offline-mirror/coscript-1.0.0.tgz differ
diff --git a/.yarn-offline-mirror/cross-spawn-promise-0.10.1.tgz b/.yarn-offline-mirror/cross-spawn-promise-0.10.1.tgz
new file mode 100644
index 000000000000..b3ef70116b45
Binary files /dev/null and b/.yarn-offline-mirror/cross-spawn-promise-0.10.1.tgz differ
diff --git a/.yarn-offline-mirror/fs.promised-3.0.0.tgz b/.yarn-offline-mirror/fs.promised-3.0.0.tgz
new file mode 100644
index 000000000000..c2ee4458f8b6
Binary files /dev/null and b/.yarn-offline-mirror/fs.promised-3.0.0.tgz differ
diff --git a/.yarn-offline-mirror/gittar-0.1.1.tgz b/.yarn-offline-mirror/gittar-0.1.1.tgz
new file mode 100644
index 000000000000..584247d76835
Binary files /dev/null and b/.yarn-offline-mirror/gittar-0.1.1.tgz differ
diff --git a/.yarn-offline-mirror/globby-7.1.1.tgz b/.yarn-offline-mirror/globby-7.1.1.tgz
new file mode 100644
index 000000000000..e17f086356ef
Binary files /dev/null and b/.yarn-offline-mirror/globby-7.1.1.tgz differ
diff --git a/.yarn-offline-mirror/keychain-1.3.0.tgz b/.yarn-offline-mirror/keychain-1.3.0.tgz
new file mode 100644
index 000000000000..524b49a73485
Binary files /dev/null and b/.yarn-offline-mirror/keychain-1.3.0.tgz differ
diff --git a/.yarn-offline-mirror/ora-1.4.0.tgz b/.yarn-offline-mirror/ora-1.4.0.tgz
new file mode 100644
index 000000000000..2506c271864f
Binary files /dev/null and b/.yarn-offline-mirror/ora-1.4.0.tgz differ
diff --git a/.yarn-offline-mirror/parse-author-2.0.0.tgz b/.yarn-offline-mirror/parse-author-2.0.0.tgz
new file mode 100644
index 000000000000..147dd7749554
Binary files /dev/null and b/.yarn-offline-mirror/parse-author-2.0.0.tgz differ
diff --git a/.yarn-offline-mirror/promise-polyfill-8.1.0.tgz b/.yarn-offline-mirror/promise-polyfill-8.1.0.tgz
new file mode 100644
index 000000000000..89c3ab5756fc
Binary files /dev/null and b/.yarn-offline-mirror/promise-polyfill-8.1.0.tgz differ
diff --git a/.yarn-offline-mirror/run-sketch-plugin-1.0.3.tgz b/.yarn-offline-mirror/run-sketch-plugin-1.0.3.tgz
new file mode 100644
index 000000000000..a19c30426d38
Binary files /dev/null and b/.yarn-offline-mirror/run-sketch-plugin-1.0.3.tgz differ
diff --git a/.yarn-offline-mirror/sketch-polyfill-fetch-0.4.5.tgz b/.yarn-offline-mirror/sketch-polyfill-fetch-0.4.5.tgz
new file mode 100644
index 000000000000..86efb98dd1c6
Binary files /dev/null and b/.yarn-offline-mirror/sketch-polyfill-fetch-0.4.5.tgz differ
diff --git a/.yarn-offline-mirror/skpm-1.2.0.tgz b/.yarn-offline-mirror/skpm-1.2.0.tgz
new file mode 100644
index 000000000000..3094bc42a573
Binary files /dev/null and b/.yarn-offline-mirror/skpm-1.2.0.tgz differ
diff --git a/.yarn-offline-mirror/webpack-4.31.0.tgz b/.yarn-offline-mirror/webpack-4.31.0.tgz
new file mode 100644
index 000000000000..e23773bedfda
Binary files /dev/null and b/.yarn-offline-mirror/webpack-4.31.0.tgz differ
diff --git a/.yarn-offline-mirror/webpack-merge-4.2.1.tgz b/.yarn-offline-mirror/webpack-merge-4.2.1.tgz
new file mode 100644
index 000000000000..a7835e9a01eb
Binary files /dev/null and b/.yarn-offline-mirror/webpack-merge-4.2.1.tgz differ
diff --git a/.yarn-offline-mirror/yesno-0.2.0.tgz b/.yarn-offline-mirror/yesno-0.2.0.tgz
new file mode 100644
index 000000000000..14576677a4fd
Binary files /dev/null and b/.yarn-offline-mirror/yesno-0.2.0.tgz differ
diff --git a/package.json b/package.json
index fd24aa3f49b4..40a23220787d 100644
--- a/package.json
+++ b/package.json
@@ -94,6 +94,10 @@
"yarn lerna run lint:staged --scope carbon-components-react",
"git add"
],
+ "*.js,!packages/components/**/*.js,!packages/react/**/*.js": [
+ "yarn format:staged",
+ "git add"
+ ],
"*.md": [
"yarn format:staged",
"git add"
diff --git a/packages/icons/src/svg/16/checkmark--filled.svg b/packages/icons/src/svg/16/checkmark--filled.svg
index 86f22872e177..906de6f0ed10 100644
--- a/packages/icons/src/svg/16/checkmark--filled.svg
+++ b/packages/icons/src/svg/16/checkmark--filled.svg
@@ -8,5 +8,5 @@
-
+
diff --git a/packages/icons/src/svg/32/checkmark--filled.svg b/packages/icons/src/svg/32/checkmark--filled.svg
index 3af4edc9c2b8..858c59ffed0f 100644
--- a/packages/icons/src/svg/32/checkmark--filled.svg
+++ b/packages/icons/src/svg/32/checkmark--filled.svg
@@ -4,7 +4,7 @@
width="32px" height="32px" viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
diff --git a/packages/sketch/.gitignore b/packages/sketch/.gitignore
new file mode 100644
index 000000000000..6f0d0fa004ff
--- /dev/null
+++ b/packages/sketch/.gitignore
@@ -0,0 +1 @@
+*.sketchplugin
diff --git a/packages/sketch/README.md b/packages/sketch/README.md
new file mode 100644
index 000000000000..8374e383a613
--- /dev/null
+++ b/packages/sketch/README.md
@@ -0,0 +1,103 @@
+# @carbon/sketch
+
+
+
+
+## Table of Contents
+
+- [Getting started (developing)](#getting-started-developing)
+- [Overview](#overview)
+ - [Building sketch plugins](#building-sketch-plugins)
+ - [Project structure](#project-structure)
+- [Reference](#reference)
+- [Tips & Tricks](#tips--tricks)
+
+
+
+
+## Getting started (developing)
+
+When working with this package, you will need to follow a couple of steps to get
+started. It's important that you have Sketch installed on your system before
+attempting any of these steps.
+
+1. Run `cd packages/sketch` to go into the package folder
+2. Run `yarn build` to build out the initial plugin
+3. Run `yarn skpm:link` to link the plugin to your Sketch plugin folder
+
+Afterwards, you can continue development by running `yarn develop`.
+
+## Overview
+
+### Building sketch plugins
+
+The output of `@carbon/sketch` is a plugin at
+`packages/sketch/carbon-elements.sketchplugin`. This artifact is generated by
+running `yarn build`. Under the hood, we make use of a tool called
+[`skpm`](https://github.com/skpm/skpm) that allows us to write our Sketch
+plugins using modern JavaScript language features and modules.
+
+When using `skpm`, we add a field named `skpm` to our `package.json` so that it
+knows details about our plugin, most notably the name of the plugin and a
+reference to a `manifest.json` file. This file lists out the capabilities of the
+sketch plugin under the `commands` field, and also defines the menu structure th
+at should appear for this plugin under the Plugins menu in Sketch.
+
+Under the `commands` field, we list out commands and define a path to a `script`
+and name the `handler` that should be called when running the command. In other
+words, if we have a file like:
+
+```js
+// commands/greeting.js
+import sketch from 'sketch';
+
+export function greeting() {
+ sketch.UI.message('Hello world!');
+}
+```
+
+Then in our `manifest.json` we would create an entry under `commands` like:
+
+```json
+{
+ "commands": [
+ {
+ "name": "The name of the command"
+ "identifier": "the.command.identifier",
+ "script": "commands/greeting.",
+ "handler": "greeting"
+ }
+ ]
+}
+```
+
+_Note: We chose the `greeting` handler because that was what was exported in
+`export function greeting() {}`._
+
+We later use the `identifier` field in our menu to reference the command.
+
+### Project structure
+
+```bash
+packages/sketch
+├── package.json
+└── src
+ ├── commands # Implementation of commands in `manifest.json`
+ ├── manifest.json # Define capabilities of Sketch plugin
+ ├── sharedStyles # Methods for syncing shared layer and text styles
+ └── tools # Tools for interacting with the Sketch DOM and styles
+```
+
+## Reference
+
+- [API Reference](https://developer.sketch.com/reference/api) for the `sketch`
+ module
+- [`Sketch-Headers`](https://github.com/abynim/Sketch-Headers) to determine
+ methods on internal objects
+- [Plugin bundle guide](https://developer.sketch.com/guides/plugin-bundles/) for
+ structuring a plugin and its menus
+
+## Tips & Tricks
+
+- Use `yarn skpm log -f` to tail logs from the Sketch plugin. Useful if you want
+ to debug using `console.log` layer is removed
diff --git a/packages/sketch/package.json b/packages/sketch/package.json
new file mode 100644
index 000000000000..a35f23af7da7
--- /dev/null
+++ b/packages/sketch/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "@carbon/sketch",
+ "description": "Tooling for generating a sketch plugin to bring code to design",
+ "private": true,
+ "version": "10.2.0",
+ "license": "Apache-2.0",
+ "scripts": {
+ "build": "cross-env NODE_ENV=production skpm-build",
+ "clean": "rimraf carbon-elements.sketchplugin",
+ "develop": "cross-env NODE_ENV=development skpm-build --watch",
+ "skpm:link": "skpm-link"
+ },
+ "dependencies": {
+ "@babel/polyfill": "^7.4.4",
+ "@carbon/colors": "10.2.0",
+ "@carbon/icon-helpers": "10.2.0",
+ "@carbon/icons": "10.2.0",
+ "@carbon/themes": "10.2.0",
+ "@carbon/type": "10.2.0",
+ "@skpm/builder": "^0.7.0",
+ "color-string": "^1.5.3",
+ "skpm": "^1.2.0"
+ },
+ "devDependencies": {
+ "cross-env": "^5.2.0",
+ "rimraf": "^2.6.3"
+ },
+ "skpm": {
+ "name": "Carbon Elements",
+ "manifest": "src/manifest.json",
+ "main": "carbon-elements.sketchplugin",
+ "assets": []
+ }
+}
diff --git a/packages/sketch/src/commands/colors/generate.js b/packages/sketch/src/commands/colors/generate.js
new file mode 100644
index 000000000000..01668bb5da31
--- /dev/null
+++ b/packages/sketch/src/commands/colors/generate.js
@@ -0,0 +1,94 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { Document, Rectangle, ShapePath, SymbolMaster } from 'sketch/dom';
+import { command } from '../command';
+import { syncColorStyles } from '../../sharedStyles/colors';
+import { findOrCreatePage, selectPage } from '../../tools/page';
+import { groupByKey } from '../../tools/grouping';
+
+const ARTBOARD_WIDTH = 40;
+const ARTBOARD_HEIGHT = 40;
+const ARTBOARD_MARGIN = 8;
+
+export function generate() {
+ command('commands/colors/generate', () => {
+ const document = Document.getSelectedDocument();
+ const page = selectPage(findOrCreatePage(document, 'Color'));
+ const sharedStyles = syncColorStyles(document);
+ const { blackAndWhite, colors, support } = groupByKey(
+ sharedStyles,
+ sharedStyle => {
+ const { name } = sharedStyle;
+ const [category, swatch, grade] = name.split('/');
+ switch (swatch) {
+ case 'black':
+ case 'white':
+ return 'blackAndWhite';
+ case 'yellow':
+ case 'orange':
+ return 'support';
+ default:
+ return 'colors';
+ }
+ }
+ );
+
+ let X_OFFSET = 0;
+ let Y_OFFSET = 0;
+
+ for (const sharedStyle of blackAndWhite) {
+ createSymbolFromSharedStyle(sharedStyle, page, X_OFFSET, Y_OFFSET);
+ X_OFFSET = X_OFFSET + ARTBOARD_WIDTH + ARTBOARD_MARGIN;
+ }
+
+ X_OFFSET = 0;
+ Y_OFFSET = Y_OFFSET + ARTBOARD_HEIGHT + ARTBOARD_MARGIN;
+
+ const swatches = groupByKey(colors, sharedStyle => {
+ const [category, swatch, grade] = sharedStyle.name.split('/');
+ return swatch;
+ });
+
+ for (const swatch of Object.keys(swatches)) {
+ for (const sharedStyle of swatches[swatch]) {
+ createSymbolFromSharedStyle(sharedStyle, page, X_OFFSET, Y_OFFSET);
+ X_OFFSET = X_OFFSET + ARTBOARD_WIDTH + ARTBOARD_MARGIN;
+ }
+
+ X_OFFSET = 0;
+ Y_OFFSET = Y_OFFSET + ARTBOARD_HEIGHT + ARTBOARD_MARGIN;
+ }
+
+ for (const sharedStyle of support) {
+ createSymbolFromSharedStyle(sharedStyle, page, X_OFFSET, Y_OFFSET);
+ X_OFFSET = X_OFFSET + ARTBOARD_WIDTH + ARTBOARD_MARGIN;
+ }
+ });
+}
+
+function createSymbolFromSharedStyle(sharedStyle, parent, offsetX, offsetY) {
+ const [category, swatch, grade] = sharedStyle.name.split('/');
+
+ const colorName = grade ? `${swatch}/${swatch}-${grade}` : swatch;
+ const rectangle = new ShapePath({
+ name: 'Color',
+ frame: new Rectangle(0, 0, ARTBOARD_WIDTH, ARTBOARD_HEIGHT),
+ shapeType: ShapePath.ShapeType.Rectangle,
+ sharedStyleId: sharedStyle.id,
+ style: sharedStyle.style,
+ });
+
+ const artboard = new SymbolMaster({
+ parent,
+ name: `${category}/${colorName}`,
+ frame: new Rectangle(offsetX, offsetY, ARTBOARD_WIDTH, ARTBOARD_HEIGHT),
+ layers: [rectangle],
+ });
+
+ return artboard;
+}
diff --git a/packages/sketch/src/commands/colors/index.js b/packages/sketch/src/commands/colors/index.js
new file mode 100644
index 000000000000..8b8807840172
--- /dev/null
+++ b/packages/sketch/src/commands/colors/index.js
@@ -0,0 +1,9 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+export { sync } from './sync';
+export { generate } from './generate';
diff --git a/packages/sketch/src/commands/colors/sync.js b/packages/sketch/src/commands/colors/sync.js
new file mode 100644
index 000000000000..6a1ce0980934
--- /dev/null
+++ b/packages/sketch/src/commands/colors/sync.js
@@ -0,0 +1,17 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { Document } from 'sketch/dom';
+import { command } from '../command';
+import { syncColorStyles } from '../../sharedStyles/colors';
+
+export function sync() {
+ command('commands/colors/sync', () => {
+ const document = Document.getSelectedDocument();
+ syncColorStyles(document);
+ });
+}
diff --git a/packages/sketch/src/commands/command.js b/packages/sketch/src/commands/command.js
new file mode 100644
index 000000000000..bf0857ca1256
--- /dev/null
+++ b/packages/sketch/src/commands/command.js
@@ -0,0 +1,26 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import sketch from 'sketch';
+
+/**
+ * Wrap a given command function for consistent UI messages and development
+ * runtime logging.
+ * @param {string} name - the name of the command
+ * @param {Function} fn - the function to call to run the command
+ */
+export function command(name, fn) {
+ const start = Date.now();
+
+ sketch.UI.message('Hi 👋 We are still working on this! 🚧');
+ fn();
+ sketch.UI.message('Done! 🎉');
+
+ if (process.env.NODE_ENV === 'development') {
+ console.log(`[Carbon Elements] ${name}: Done in ${Date.now() - start}ms`);
+ }
+}
diff --git a/packages/sketch/src/commands/icons/generate.js b/packages/sketch/src/commands/icons/generate.js
new file mode 100644
index 000000000000..cd8b08d6e9d6
--- /dev/null
+++ b/packages/sketch/src/commands/icons/generate.js
@@ -0,0 +1,235 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { toString } from '@carbon/icon-helpers';
+import React from 'react';
+import {
+ Document,
+ Group,
+ Rectangle,
+ Shape,
+ Style,
+ SymbolMaster,
+} from 'sketch/dom';
+import { command } from '../command';
+import { syncColorStyles } from '../../sharedStyles/colors';
+import { groupByKey } from '../../tools/grouping';
+import { findOrCreatePage, selectPage } from '../../tools/page';
+
+const meta = require('@carbon/icons/meta.json');
+
+export function generate() {
+ command('commands/icons/generate', () => {
+ const document = Document.getSelectedDocument();
+ const page = selectPage(findOrCreatePage(document, 'Icons'));
+ const sharedStyles = syncColorStyles(document);
+ const [sharedStyle] = sharedStyles.filter(
+ ({ name }) => name === 'color/black'
+ );
+
+ if (!sharedStyle) {
+ throw new Error(
+ 'Unexpected error occurred, expected shared style but found none'
+ );
+ }
+
+ const icons = normalize(meta);
+ const iconNames = Object.keys(icons);
+
+ // To help with debugging, we have `start` and `end` values here to focus on
+ // specific icon ranges. You can also work on a specific icon by finding
+ // it's index and setting the value of start to the index and end to the
+ // index + 1.
+ //
+ // To find the index, you can use:
+ // console.log(iconNames.findIndex(name === 'name-to-find')); // 50
+ // And use that value below like:
+ // const start = 50;
+ // const end = 51;
+ // This will allow you to focus only on the icon named 'name-to-find'
+ const start = 0;
+ const end = iconNames.length;
+
+ // We keep track of the current X and Y offsets at the top-level, each
+ // iteration of an icon set should reset the X_OFFSET and update the
+ // Y_OFFSET with the maximum size in the icon set.
+ let X_OFFSET = 0;
+ let Y_OFFSET = 0;
+ let maxSize = -Infinity;
+
+ const artboards = iconNames.slice(start, end).flatMap((name, i) => {
+ const sizes = icons[name];
+
+ X_OFFSET = 0;
+ if (i !== 0) {
+ Y_OFFSET = Y_OFFSET + maxSize + 32;
+ }
+ maxSize = -Infinity;
+
+ return sizes.map(icon => {
+ // If our icon has an original size, we will need to render it in the
+ // original size and then resize it to the appropriate artboard size
+ const size = icon.original || icon.size;
+ const descriptor = Object.assign({}, icon.descriptor);
+
+ // We push a transparent rectangle to mirror the "bounding box" found in
+ // icon artboards that is stripped by our build process. Including this
+ // makes sure that our icon renders true to the path data
+ descriptor.content.push({
+ elem: 'rect',
+ attrs: {
+ width: size,
+ height: size,
+ fill: 'none',
+ },
+ });
+
+ const layer = createSVGLayer(icon.descriptor);
+
+ layer.name = icon.basename;
+ layer.rect = {
+ origin: {
+ x: 0,
+ y: 0,
+ },
+ size: {
+ width: icon.size,
+ height: icon.size,
+ },
+ };
+
+ let symbolName =
+ sizes.length !== 1
+ ? `category/${name}/${icon.size}`
+ : `category/${name}`;
+
+ if (icon.original) {
+ symbolName = `${symbolName}*`;
+ }
+
+ const artboard = new SymbolMaster({
+ name: symbolName,
+ frame: new Rectangle(X_OFFSET, Y_OFFSET, icon.size, icon.size),
+ layers: [layer],
+ });
+
+ if (size > maxSize) {
+ maxSize = size;
+ }
+
+ X_OFFSET = X_OFFSET + icon.size + 8;
+
+ const [group] = artboard.layers;
+
+ // Last layer will be the transparent rectangle we added above
+ const paths = group.layers.slice(0, -1).map(layer => layer.duplicate());
+
+ // We split things out into fillPaths and innerPaths, allowing us to
+ // style them independent of each other in the symbol. Useful for
+ // two-tone icons.
+ const { fillPaths = [], innerPaths = [] } = groupByKey(
+ paths,
+ (path, i) => {
+ const node = icon.descriptor.content[i];
+ if (node.attrs['data-icon-path'] === 'inner-path') {
+ return 'innerPaths';
+ }
+ return 'fillPaths';
+ }
+ );
+
+ let shape;
+ if (fillPaths.length === 1) {
+ shape = fillPaths[0];
+ shape.name = 'Fill';
+ shape.style = sharedStyle.style;
+ shape.sharedStyleId = sharedStyle.id;
+ } else {
+ // If we have multiple fill paths, we need to consolidate them into a
+ // single Shape so that we can style the icon with one override in the
+ // symbol
+ shape = new Shape({
+ name: 'Fill',
+ frame: new Rectangle(0, 0, icon.size, icon.size),
+ layers: fillPaths,
+ style: sharedStyle.style,
+ sharedStyleId: sharedStyle.id,
+ });
+ }
+
+ shape.style.borders = [];
+
+ for (const innerPath of innerPaths) {
+ innerPath.name = 'Inner Fill';
+ innerPath.style = sharedStyle.style;
+ innerPath.style.opacity = 0;
+ innerPath.sharedStyleId = sharedStyle.id;
+ }
+
+ artboard.layers.push(shape, ...innerPaths);
+ group.remove();
+
+ return artboard;
+ });
+ });
+
+ page.layers.push(...artboards);
+ });
+}
+
+/**
+ * Normalize a collection of icons by their basename
+ * @param {Array} icons
+ * @return {Object}
+ */
+function normalize(icons) {
+ // Collect all icons and group them by their base names. The value of the
+ // basename key is the array of all sizes for that icon
+ const iconsByBasename = icons.reduce((acc, icon) => {
+ // Ignore glyphs
+ if (!icon.size) {
+ return acc;
+ }
+ // Drop size from prefix
+ const name = [...icon.prefix.slice(1), icon.basename].join('/');
+ if (acc[name]) {
+ return {
+ ...acc,
+ [name]: acc[name].concat(icon).sort(sortBySize),
+ };
+ }
+ return {
+ ...acc,
+ [name]: [icon],
+ };
+ }, {});
+
+ return iconsByBasename;
+}
+
+function sortBySize(a, b) {
+ return b.size - a.size;
+}
+
+/**
+ * Create a layer from an SVG descriptor
+ *
+ * Reference:
+ * https://github.com/airbnb/react-sketchapp/blob/aa3070556c47883974edbc7f78978c421a8199f7/src/jsonUtils/sketchImpl/makeSvgLayer.js#L12
+ *
+ * @param {Object} svg
+ * @return {Layer}
+ */
+function createSVGLayer(svg) {
+ const svgString = NSString.stringWithString(toString(svg));
+ const svgData = svgString.dataUsingEncoding(NSUTF8StringEncoding);
+ const svgImporter = MSSVGImporter.svgImporter();
+ svgImporter.prepareToImportFromData(svgData);
+ const svgLayer = svgImporter.importAsLayer();
+
+ return svgLayer;
+}
diff --git a/packages/sketch/src/commands/icons/index.js b/packages/sketch/src/commands/icons/index.js
new file mode 100644
index 000000000000..864e1921d9f3
--- /dev/null
+++ b/packages/sketch/src/commands/icons/index.js
@@ -0,0 +1,8 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+export { generate } from './generate';
diff --git a/packages/sketch/src/commands/index.js b/packages/sketch/src/commands/index.js
new file mode 100644
index 000000000000..62ac3cc97bb3
--- /dev/null
+++ b/packages/sketch/src/commands/index.js
@@ -0,0 +1,17 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import '@babel/polyfill';
+
+// We export all commands through one entrypoint to prevent build errors
+// triggered by having separate entrypoints. Most notably we would encounter
+// parse errors because the bundlers were being generated incorrectly during
+// incremental rebuilds.
+export { sync as syncColors, generate as generateColors } from './colors';
+export { generate as generateIcons } from './icons';
+export { sync as syncThemes, generate as generateThemes } from './themes';
+export { sync as syncType, generate as generateType } from './type';
diff --git a/packages/sketch/src/commands/themes/generate.js b/packages/sketch/src/commands/themes/generate.js
new file mode 100644
index 000000000000..af39a312f2af
--- /dev/null
+++ b/packages/sketch/src/commands/themes/generate.js
@@ -0,0 +1,63 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { Document, Rectangle, ShapePath, SymbolMaster } from 'sketch/dom';
+import { command } from '../command';
+import { findOrCreatePage, selectPage } from '../../tools/page';
+import { groupByKey } from '../../tools/grouping';
+import { syncThemeColorStyles } from '../../sharedStyles/themes';
+
+const ARTBOARD_WIDTH = 40;
+const ARTBOARD_HEIGHT = 40;
+
+const ARTBOARD_MARGIN_VERTICAL = 8;
+const ARTBOARD_MARGIN_HORIZONTAL = 40;
+
+export function generate() {
+ command('commands/themes/generate', () => {
+ const document = Document.getSelectedDocument();
+ const page = selectPage(findOrCreatePage(document, 'Themes'));
+ const sharedStyles = syncThemeColorStyles(document);
+
+ const tokens = groupByKey(sharedStyles, sharedStyle => {
+ const [category, token] = sharedStyle.name.split('/');
+ return token.trim();
+ });
+
+ let X_OFFSET = 0;
+ let Y_OFFSET = 0;
+
+ for (const token of Object.keys(tokens)) {
+ for (const sharedStyle of tokens[token]) {
+ createSymbolFromSharedStyle(sharedStyle, page, X_OFFSET, Y_OFFSET);
+ X_OFFSET = X_OFFSET + ARTBOARD_WIDTH + ARTBOARD_MARGIN_HORIZONTAL;
+ }
+
+ X_OFFSET = 0;
+ Y_OFFSET = Y_OFFSET + ARTBOARD_HEIGHT + ARTBOARD_MARGIN_VERTICAL;
+ }
+ });
+}
+
+function createSymbolFromSharedStyle(sharedStyle, parent, offsetX, offsetY) {
+ const rectangle = new ShapePath({
+ name: 'Color',
+ frame: new Rectangle(0, 0, ARTBOARD_WIDTH, ARTBOARD_HEIGHT),
+ shapeType: ShapePath.ShapeType.Rectangle,
+ sharedStyleId: sharedStyle.id,
+ style: sharedStyle.style,
+ });
+
+ const artboard = new SymbolMaster({
+ parent,
+ name: sharedStyle.name,
+ frame: new Rectangle(offsetX, offsetY, ARTBOARD_WIDTH, ARTBOARD_HEIGHT),
+ layers: [rectangle],
+ });
+
+ return artboard;
+}
diff --git a/packages/sketch/src/commands/themes/index.js b/packages/sketch/src/commands/themes/index.js
new file mode 100644
index 000000000000..8b8807840172
--- /dev/null
+++ b/packages/sketch/src/commands/themes/index.js
@@ -0,0 +1,9 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+export { sync } from './sync';
+export { generate } from './generate';
diff --git a/packages/sketch/src/commands/themes/sync.js b/packages/sketch/src/commands/themes/sync.js
new file mode 100644
index 000000000000..2314f035b16d
--- /dev/null
+++ b/packages/sketch/src/commands/themes/sync.js
@@ -0,0 +1,17 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { Document } from 'sketch/dom';
+import { command } from '../command';
+import { syncThemeColorStyles } from '../../sharedStyles/themes';
+
+export function sync() {
+ command('commands/themes/sync', () => {
+ const document = Document.getSelectedDocument();
+ syncThemeColorStyles(document);
+ });
+}
diff --git a/packages/sketch/src/commands/type/generate.js b/packages/sketch/src/commands/type/generate.js
new file mode 100644
index 000000000000..781b89342de2
--- /dev/null
+++ b/packages/sketch/src/commands/type/generate.js
@@ -0,0 +1,39 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import sketch from 'sketch';
+import { Document, Rectangle, Text } from 'sketch/dom';
+import { command } from '../command';
+import { findOrCreatePage, selectPage } from '../../tools/page';
+import { syncTextStyles } from '../../sharedStyles/type';
+
+const TEXT_LAYER_WIDTH = 500;
+const TEXT_LAYER_HEIGHT = 75;
+const TEXT_MARGIN = 16;
+
+export function generate() {
+ command('commands/type/generate', () => {
+ const document = Document.getSelectedDocument();
+ const page = selectPage(findOrCreatePage(document, 'Type'));
+ const sharedStyles = syncTextStyles(document);
+
+ let Y_OFFSET = 0;
+
+ for (const sharedStyle of sharedStyles) {
+ const layer = new Text({
+ name: sharedStyle.name,
+ frame: new Rectangle(0, Y_OFFSET, TEXT_LAYER_WIDTH, TEXT_LAYER_HEIGHT),
+ style: sharedStyle.style,
+ sharedStyleId: sharedStyle.id,
+ parent: page,
+ text: sharedStyle.name,
+ });
+
+ Y_OFFSET = Y_OFFSET + TEXT_LAYER_HEIGHT + TEXT_MARGIN;
+ }
+ });
+}
diff --git a/packages/sketch/src/commands/type/index.js b/packages/sketch/src/commands/type/index.js
new file mode 100644
index 000000000000..8b8807840172
--- /dev/null
+++ b/packages/sketch/src/commands/type/index.js
@@ -0,0 +1,9 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+export { sync } from './sync';
+export { generate } from './generate';
diff --git a/packages/sketch/src/commands/type/sync.js b/packages/sketch/src/commands/type/sync.js
new file mode 100644
index 000000000000..98c996d0c934
--- /dev/null
+++ b/packages/sketch/src/commands/type/sync.js
@@ -0,0 +1,17 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { Document } from 'sketch/dom';
+import { command } from '../command';
+import { syncTextStyles } from '../../sharedStyles/type';
+
+export function sync() {
+ command('commands/type/sync', () => {
+ const document = Document.getSelectedDocument();
+ syncTextStyles(document);
+ });
+}
diff --git a/packages/sketch/src/manifest.json b/packages/sketch/src/manifest.json
new file mode 100644
index 000000000000..3eca1402fba9
--- /dev/null
+++ b/packages/sketch/src/manifest.json
@@ -0,0 +1,81 @@
+{
+ "identifier": "com.ibm.carbon.elements",
+ "compatibleVersion": "3",
+ "bundleVersion": 1,
+ "name": "Carbon Elements 🎨",
+ "commands": [
+ {
+ "name": "Sync shared layer styles",
+ "identifier": "carbon.elements.colors.sync",
+ "script": "commands/index.js",
+ "handler": "syncColors"
+ },
+ {
+ "name": "Generate color page",
+ "identifier": "carbon.elements.colors.generate",
+ "script": "commands/index.js",
+ "handler": "generateColors"
+ },
+ {
+ "name": "Sync shared layer styles",
+ "identifier": "carbon.elements.themes.sync",
+ "script": "commands/index.js",
+ "handler": "syncThemes"
+ },
+ {
+ "name": "Generate themes page",
+ "identifier": "carbon.elements.themes.generate",
+ "script": "commands/index.js",
+ "handler": "generateThemes"
+ },
+ {
+ "name": "Sync shared layer styles",
+ "identifier": "carbon.elements.type.sync",
+ "script": "commands/index.js",
+ "handler": "syncType"
+ },
+ {
+ "name": "Generate type page",
+ "identifier": "carbon.elements.type.generate",
+ "script": "commands/index.js",
+ "handler": "generateType"
+ },
+ {
+ "name": "Generate icon page and symbols",
+ "identifier": "carbon.elements.icons.generate",
+ "script": "commands/index.js",
+ "handler": "generateIcons"
+ }
+ ],
+ "menu": {
+ "items": [
+ {
+ "title": "Colors",
+ "items": [
+ "carbon.elements.colors.sync",
+ "carbon.elements.colors.generate"
+ ]
+ },
+ {
+ "title": "Themes",
+ "items": [
+ "carbon.elements.themes.sync",
+ "carbon.elements.themes.generate"
+ ]
+ },
+ {
+ "title": "Icons",
+ "items": [
+ "carbon.elements.icons.generate"
+ ]
+ },
+ {
+ "title": "Type",
+ "items": [
+ "carbon.elements.type.sync",
+ "carbon.elements.type.generate"
+ ]
+ }
+ ]
+ }
+}
diff --git a/packages/sketch/src/sharedStyles/colors.js b/packages/sketch/src/sharedStyles/colors.js
new file mode 100644
index 000000000000..e0c15f930630
--- /dev/null
+++ b/packages/sketch/src/sharedStyles/colors.js
@@ -0,0 +1,121 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { colors } from '@carbon/colors';
+import { formatTokenName } from '@carbon/themes';
+import { SharedStyle, Style } from 'sketch/dom';
+import { syncSharedStyle } from '../tools/sharedStyles';
+
+// We separate out certain colors that are not a part of the primary swatches
+// that we need to render
+const { black, white, orange, yellow, ...swatches } = colors;
+const colorNames = Object.keys(colors);
+
+// Color names in JavaScript will be camelCase, so we need to map them over to
+// the token name. For example: `warmGray` becomes `warm-gray`
+const formattedSwatchNames = colorNames.reduce(
+ (acc, key, i) => ({
+ ...acc,
+ [formatTokenName(key)]: colorNames[i],
+ }),
+ {}
+);
+
+// We need to build up our expected shared styles from code to diff with what
+// currently exists in the document. For this case, we'll use the shared style
+// name as the key and the value from the swatch as the expected value
+const expectedSharedStyles = Object.keys(swatches).reduce((acc, swatch) => {
+ const name = formatTokenName(swatch);
+ const swatchStyles = Object.keys(swatches[swatch]).reduce((acc, grade) => {
+ return {
+ ...acc,
+ [formatSharedStyleName(name, grade)]: swatches[swatch][grade],
+ };
+ }, {});
+
+ return {
+ ...acc,
+ ...swatchStyles,
+ };
+}, {});
+
+expectedSharedStyles[formatSharedStyleName('black')] = black['100'];
+expectedSharedStyles[formatSharedStyleName('white')] = white['0'];
+expectedSharedStyles[formatSharedStyleName('orange')] = orange['40'];
+expectedSharedStyles[formatSharedStyleName('yellow')] = yellow['20'];
+
+/**
+ * Sync color shared styles to the given document and return the result
+ * @param {Document} document
+ * @return {Array}
+ */
+export function syncColorStyles(document) {
+ const { sharedLayerStyles } = document;
+ const existingStyles = sharedLayerStyles.filter(({ name, style }) => {
+ const fill = style.fills[0];
+ // Colors in Sketch are #RRGGBB plus opacity, so typically they are
+ // #RRGGBBFF
+ const color = fill.color.slice(0, -2);
+ return expectedSharedStyles[name] && expectedSharedStyles[name] === color;
+ });
+
+ // Exit early if everything is the same
+ if (existingStyles.length === Object.keys(expectedSharedStyles).length) {
+ return existingStyles;
+ }
+
+ const sharedStyles = Object.keys(swatches).flatMap(swatchName => {
+ const name = formatTokenName(swatchName);
+ return Object.keys(swatches[swatchName]).map(grade => {
+ return syncColorStyle(
+ document,
+ formatSharedStyleName(name, grade),
+ swatches[swatchName][grade]
+ );
+ });
+ });
+
+ const singleColors = [
+ ['black', black['100']],
+ ['white', white['0']],
+ ['orange', orange['40']],
+ ['yellow', yellow['20']],
+ ].map(([name, value]) => {
+ return syncColorStyle(document, formatSharedStyleName(name), value);
+ });
+
+ return sharedStyles.concat(singleColors);
+}
+
+/**
+ * Our shared style name will need to have the `color` namespace alongside a
+ * name for the swatch and an optional grade.
+ * @param {string} name
+ * @param {string?} grade
+ * @return {string}
+ */
+function formatSharedStyleName(name, grade) {
+ return ['color', name, grade].filter(Boolean).join('/');
+}
+
+/**
+ * Sync the given color value as a shared style for the document
+ * @param {Document} document
+ * @param {string} name
+ * @param {string} value
+ * @return {SharedStyle}
+ */
+function syncColorStyle(document, name, value) {
+ return syncSharedStyle(document, name, {
+ fills: [
+ {
+ color: value,
+ fillType: Style.FillType.Color,
+ },
+ ],
+ });
+}
diff --git a/packages/sketch/src/sharedStyles/themes.js b/packages/sketch/src/sharedStyles/themes.js
new file mode 100644
index 000000000000..6f8bb2151e12
--- /dev/null
+++ b/packages/sketch/src/sharedStyles/themes.js
@@ -0,0 +1,118 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { white, g10, g90, g100, formatTokenName } from '@carbon/themes';
+import color from 'color-string';
+import { SharedStyle, Style } from 'sketch/dom';
+import { syncSharedStyle } from '../tools/sharedStyles';
+
+/**
+ * Sync theme color shared styles to the given document and return the result
+ * @param {Document} document
+ * @return {Array}
+ */
+export function syncThemeColorStyles(document) {
+ const themes = {
+ 'White theme': white,
+ 'Gray 10 theme': g10,
+ 'Gray 90 theme': g90,
+ 'Gray 100 theme': g100,
+ };
+
+ const { sharedLayerStyles } = document;
+ const existingStyles = sharedLayerStyles.filter(({ name, style }) => {
+ const [category, token] = name.split('/').map(value => value.trim());
+
+ if (!themes[category]) {
+ return false;
+ }
+
+ const expected = color.get.rgb(themes[category][formatSymbolName(token)]);
+ const actual = color.get.rgb(style.fills[0].color);
+
+ for (let i = 0; i < expected.length; i++) {
+ if (actual[i] !== expected[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ });
+ const expectedSharedStyles = Object.keys(themes).reduce((acc, theme) => {
+ const count = Object.keys(themes[theme]).length;
+ return acc + count;
+ }, 0);
+
+ // Exit early if everything is the same
+ if (existingStyles.length === expectedSharedStyles) {
+ return existingStyles;
+ }
+
+ const sharedStyles = Object.keys(themes).flatMap(theme => {
+ return Object.keys(themes[theme]).map(token => {
+ const name = `${theme} / ${formatTokenName(token)}`;
+ return syncColorStyle(document, name, themes[theme][token]);
+ });
+ });
+
+ return sharedStyles;
+}
+
+/**
+ * Sync the given color value as a shared style for the document
+ * @param {Document} document
+ * @param {string} name
+ * @param {string} value
+ * @return {SharedStyle}
+ */
+function syncColorStyle(document, name, value) {
+ return syncSharedStyle(document, name, {
+ fills: [
+ {
+ color: value,
+ fillType: Style.FillType.Color,
+ },
+ ],
+ });
+}
+
+const keywords = ['ui'];
+
+/**
+ * Transform a formatted token name back to its JavaScript value to look up the
+ * expected value for a token from code
+ * @param {string} token
+ * @return {string}
+ */
+function formatSymbolName(token) {
+ const parts = token.split('-');
+ let result = '';
+
+ for (let i = 0; i < parts.length; i++) {
+ const part = parts[i];
+
+ if (i === 0) {
+ result = part;
+ continue;
+ }
+
+ if (keywords.indexOf(part) !== -1) {
+ result += part.toUpperCase();
+ continue;
+ }
+
+ if (isNaN(part)) {
+ result += part[0].toUpperCase();
+ result += part.slice(1);
+ continue;
+ }
+
+ result += part;
+ }
+
+ return result;
+}
diff --git a/packages/sketch/src/sharedStyles/type.js b/packages/sketch/src/sharedStyles/type.js
new file mode 100644
index 000000000000..dc466f344fa0
--- /dev/null
+++ b/packages/sketch/src/sharedStyles/type.js
@@ -0,0 +1,97 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { formatTokenName } from '@carbon/themes';
+import { styles } from '@carbon/type';
+import { SharedStyle } from 'sketch/dom';
+import { syncSharedStyle } from '../tools/sharedStyles';
+
+const fontWeightTable = {
+ // Light
+ 300: 3,
+ // Regular
+ 400: 4,
+ // Medium
+ 500: 6,
+ // Semi-bold
+ 600: 8,
+ // Bold
+ 700: 9,
+};
+const expressiveTokens = new Set(['display', 'quotation', 'expressive']);
+
+/**
+ * Sync text shared styles to the given document and return the result
+ * @param {Document} document
+ * @return {Array}
+ */
+export function syncTextStyles(document) {
+ return Object.keys(styles)
+ .filter(token => {
+ for (const pattern of expressiveTokens) {
+ if (token.includes(pattern)) {
+ return false;
+ }
+ }
+ return true;
+ })
+ .map(token => {
+ const name = formatSharedStyleName(token);
+ const style = convertTypeStyle(token, styles[token]);
+ const sharedTextStyle = syncSharedStyle(
+ document,
+ name,
+ style,
+ SharedStyle.StyleType.Text
+ );
+
+ sharedTextStyle.style.textColor = '#000000ff';
+
+ return sharedTextStyle;
+ });
+}
+
+/**
+ * Format the given token to a value for a shared style name
+ * @param {string} token
+ * @return {string}
+ */
+function formatSharedStyleName(token) {
+ const parts = formatTokenName(token).split('-');
+ if (parts.length === 2) {
+ return parts.join('-');
+ }
+
+ const [category, name, grade] = parts;
+ if (category !== 'productive') {
+ return parts.join('-');
+ }
+
+ return `${category}/${name}-${grade}`;
+}
+
+/**
+ * Convert a given token and its style to a format used by Sketch
+ * @param {string} token
+ * @param {Object} style
+ * @return {Object}
+ */
+function convertTypeStyle(token, style) {
+ const fontSize = parseFloat(style.fontSize, 10) * 16;
+ const fontWeight = fontWeightTable[style.fontWeight];
+ const fontFamily = token.includes('code') ? 'IBM Plex Mono' : 'IBM Plex Sans';
+ const kerning = parseFloat(style.letterSpacing, 10);
+ const lineHeight = parseFloat(style.lineHeight, 10) * 16;
+
+ return {
+ fontFamily,
+ fontSize,
+ fontWeight,
+ kerning,
+ lineHeight,
+ };
+}
diff --git a/packages/sketch/src/tools/grouping.js b/packages/sketch/src/tools/grouping.js
new file mode 100644
index 000000000000..c606e730cfb8
--- /dev/null
+++ b/packages/sketch/src/tools/grouping.js
@@ -0,0 +1,42 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * Sort a collection by keys determined by a sorter. Useful for organizing a
+ * flat array by figuring out a key for each element and grouping that element
+ * along with others that have the same key.
+ *
+ * @param {Array} collection
+ * @param {Function} sorter
+ * @return {Array}
+ * @example
+ * const array = [1, 2, 3, 4];
+ * const { even, odd } = groupByKey(array, number => {
+ * if (number % 2 === 0) {
+ * return 'even';
+ * }
+ * return 'odd';
+ * });
+ * console.log(even); // [2, 4]
+ * console.log(odd); // [1, 3]
+ */
+export function groupByKey(collection, sorter) {
+ const result = {};
+
+ for (let i = 0; i < collection.length; i++) {
+ const element = collection[i];
+ const key = sorter(element, i);
+
+ if (!result[key]) {
+ result[key] = [];
+ }
+
+ result[key].push(element);
+ }
+
+ return result;
+}
diff --git a/packages/sketch/src/tools/page.js b/packages/sketch/src/tools/page.js
new file mode 100644
index 000000000000..17f902482b12
--- /dev/null
+++ b/packages/sketch/src/tools/page.js
@@ -0,0 +1,37 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { Document, Page } from 'sketch/dom';
+
+/**
+ * Find or create a page for the given document context and page name
+ * @param {Document} document
+ * @param {string} name
+ * @return {Page}
+ */
+export function findOrCreatePage(document, name) {
+ const [page] = document.pages.filter(page => page.name === name);
+
+ if (page) {
+ page.remove();
+ }
+
+ return new Page({
+ name,
+ parent: document,
+ });
+}
+
+/**
+ * Select the given page, making it the active page in the document
+ * @param {Page} page
+ * @return {Page}
+ */
+export function selectPage(page) {
+ page.selected = true;
+ return page;
+}
diff --git a/packages/sketch/src/tools/sharedStyles.js b/packages/sketch/src/tools/sharedStyles.js
new file mode 100644
index 000000000000..498ea6f96170
--- /dev/null
+++ b/packages/sketch/src/tools/sharedStyles.js
@@ -0,0 +1,53 @@
+/**
+ * Copyright IBM Corp. 2018, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { SharedStyle } from 'sketch/dom';
+
+/**
+ * Sync a shared style within a document.
+ * @param {Document} document
+ * @param {string} name
+ * @param {Object} style
+ * @param {StyleType?} styleType
+ * @return {SharedStyle}
+ */
+export function syncSharedStyle(
+ document,
+ name,
+ style,
+ styleType = SharedStyle.StyleType.Layer
+) {
+ // Figure out the type of shared style and try and find if we have already
+ // created a shared style with the given name
+ const documentSharedStyles =
+ styleType === SharedStyle.StyleType.Layer
+ ? document.sharedLayerStyles
+ : document.sharedTextStyles;
+ const [sharedStyle] = Array.from(documentSharedStyles).filter(sharedStyle => {
+ return sharedStyle.name === name;
+ });
+
+ // If none exists, we can create one from scratch
+ if (!sharedStyle) {
+ return SharedStyle.fromStyle({
+ name,
+ style,
+ styleType,
+ document,
+ });
+ }
+
+ // Otherwise, we'll go and update values of the sharedStyle with the given
+ // style if the values are different
+ Object.keys(style).forEach(key => {
+ if (sharedStyle.style[key] !== style[key]) {
+ sharedStyle.style[key] = style[key];
+ }
+ });
+
+ return sharedStyle;
+}
diff --git a/yarn.lock b/yarn.lock
index 39af50385c45..cdd6b26cc8df 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -333,7 +333,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
-"@babel/plugin-proposal-async-generator-functions@^7.1.0", "@babel/plugin-proposal-async-generator-functions@^7.2.0":
+"@babel/plugin-proposal-async-generator-functions@^7.0.0", "@babel/plugin-proposal-async-generator-functions@^7.1.0", "@babel/plugin-proposal-async-generator-functions@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e"
dependencies:
@@ -873,6 +873,14 @@
"@babel/helper-regex" "^7.4.4"
regexpu-core "^4.5.4"
+"@babel/polyfill@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.4.4.tgz#78801cf3dbe657844eeabf31c1cae3828051e893"
+ integrity sha512-WlthFLfhQQhh+A2Gn5NSFl0Huxz36x86Jn+E9OW7ibK8edKPq+KLy4apM1yDpQ8kJOVi1OVjpP4vSDLdrI04dg==
+ dependencies:
+ core-js "^2.6.5"
+ regenerator-runtime "^0.13.2"
+
"@babel/preset-env@7.1.0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.1.0.tgz#e67ea5b0441cfeab1d6f41e9b5c79798800e8d11"
@@ -2308,6 +2316,68 @@
dependencies:
any-observable "^0.3.0"
+"@skpm/babel-preset@0.2.1":
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/@skpm/babel-preset/-/babel-preset-0.2.1.tgz#1e0404e058514d72adebe98163b69b0bb1d737e4"
+ integrity sha512-bIG9JE0rdSuWh5QMyxIXKUPZUXI2BjXxmI1/dxcRNUK2IweZ9vvuuP7IMaplxPYqZgFqSuj1HAmX1uP/CG9YOw==
+ dependencies:
+ "@babel/plugin-proposal-async-generator-functions" "^7.0.0"
+ "@babel/plugin-proposal-object-rest-spread" "^7.0.0"
+ "@babel/preset-env" "^7.0.0"
+ "@babel/preset-react" "^7.0.0"
+
+"@skpm/builder@^0.7.0":
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/@skpm/builder/-/builder-0.7.0.tgz#483a871aa7c436c2a88e8e2069c81a0a74045d6e"
+ integrity sha512-dpYFQIGLKmSRFWBxmS2zItdFhb+b41Je+9YpyrJJTdyHDQLdSY284hinXESdqyuStUr3lqGXgZfZJpTF0hUw0Q==
+ dependencies:
+ "@babel/core" "^7.4.3"
+ "@skpm/babel-preset" "0.2.1"
+ "@skpm/file-loader" "^2.0.1"
+ "@skpm/internal-utils" "^0.1.14"
+ "@skpm/nib-loader" "^0.1.1"
+ babel-loader "^8.0.5"
+ chalk "^2.4.1"
+ globby "^9.2.0"
+ mkdirp "^0.5.1"
+ parse-author "2.0.0"
+ promise-polyfill "^8.1.0"
+ run-sketch-plugin "^1.0.0"
+ semver "^6.0.0"
+ sketch-polyfill-fetch "^0.4.3"
+ terser-webpack-plugin "^1.2.3"
+ webpack "^4.29.6"
+ webpack-merge "^4.2.1"
+ webpack-sources "^1.3.0"
+ yargs "^13.2.2"
+
+"@skpm/file-loader@^2.0.1":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@skpm/file-loader/-/file-loader-2.0.1.tgz#afd7624a77eb567550d7cb88f5a6e68a12785e3f"
+ integrity sha512-Xdv6dPqZu+fhNEZE4/uR9+97uqWfOiXt4E8fdvx2muWwhzs8jJHhc9/PO/9b/6eARbiS4A2W5s/FJC2bzgP0ag==
+ dependencies:
+ loader-utils "^1.0.2"
+ schema-utils "^0.4.5"
+
+"@skpm/internal-utils@^0.1.14":
+ version "0.1.14"
+ resolved "https://registry.yarnpkg.com/@skpm/internal-utils/-/internal-utils-0.1.14.tgz#e2a9c8fd8307c9b7ff3acd434377c18a828fb153"
+ integrity sha512-6++6bVgo5cc9E34AxZW7DytuNBgPhcn//HhiN0fKOvyvv06q4RqSAL6vun0hpdt96nPLtJe3/gF4G4lARQ/jPA==
+ dependencies:
+ chalk "^2.4.1"
+ js-yaml "^3.13.1"
+ object-assign "*"
+ yesno "0.2.0"
+
+"@skpm/nib-loader@^0.1.1":
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/@skpm/nib-loader/-/nib-loader-0.1.1.tgz#7cf31a0831d75d2ecf3832aea3c58cee43e41d82"
+ integrity sha512-EcrJk8i4DF/OzQx2P5Y8WC43eOL7jLElG6PkZ8lPLdfliMd9gehiLjfXBlDwEXj6pxH+zgydTB+9HEERQDZoRg==
+ dependencies:
+ cocoascript-class "^0.1.2"
+ loader-utils "^1.0.2"
+ schema-utils "^0.4.5"
+
"@storybook/addon-a11y@^4.1.18":
version "4.1.18"
resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-4.1.18.tgz#2194637735fe48fd5602790718193e5ac0e0c16d"
@@ -3978,6 +4048,11 @@ atob@^2.1.1:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+author-regex@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/author-regex/-/author-regex-1.0.0.tgz#d08885be6b9bbf9439fe087c76287245f0a81450"
+ integrity sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA=
+
autolinker@~0.15.0:
version "0.15.3"
resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.15.3.tgz#342417d8f2f3461b14cf09088d5edf8791dc9832"
@@ -4199,7 +4274,7 @@ babel-loader@8.0.4:
mkdirp "^0.5.1"
util.promisify "^1.0.0"
-babel-loader@^8.0.0, babel-loader@^8.0.4:
+babel-loader@^8.0.0, babel-loader@^8.0.4, babel-loader@^8.0.5:
version "8.0.6"
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb"
integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==
@@ -5710,6 +5785,11 @@ cli-cursor@^2.0.0, cli-cursor@^2.1.0:
dependencies:
restore-cursor "^2.0.0"
+cli-spinners@^1.0.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a"
+ integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==
+
cli-table2@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/cli-table2/-/cli-table2-0.2.0.tgz#2d1ef7f218a0e786e214540562d4bd177fe32d97"
@@ -5878,6 +5958,11 @@ coa@~1.0.1:
dependencies:
q "^1.1.2"
+cocoascript-class@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/cocoascript-class/-/cocoascript-class-0.1.2.tgz#dab25f20389946d9986c1812b88ac3783eec42d3"
+ integrity sha1-2rJfIDiZRtmYbBgSuIrDeD7sQtM=
+
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
@@ -5931,7 +6016,7 @@ color-string@^0.3.0:
dependencies:
color-name "^1.0.0"
-color-string@^1.5.2:
+color-string@^1.5.2, color-string@^1.5.3:
version "1.5.3"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc"
integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==
@@ -6370,7 +6455,7 @@ core-js@^1.0.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
-core-js@^2.2.0, core-js@^2.4.0, core-js@^2.5.0:
+core-js@^2.2.0, core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.5:
version "2.6.8"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.8.tgz#dc3a1e633a04267944e0cb850d3880f340248139"
integrity sha512-RWlREFU74TEkdXzyl1bka66O3kYp8jeTXrvJZDzVVMH8AiHUSOFpL1yfhQJ+wHocAm1m+4971W1PPzfLuCv1vg==
@@ -6390,6 +6475,11 @@ core-util-is@1.0.2, core-util-is@^1.0.0, core-util-is@~1.0.0:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+coscript@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/coscript/-/coscript-1.0.0.tgz#1a0ef8d5f8b4a67901b97ae59bd2f51e6cc6b0f1"
+ integrity sha1-Gg741fi0pnkBuXrlm9L1HmzGsPE=
+
cosmiconfig@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-4.0.0.tgz#760391549580bbd2df1e562bc177b13c290972dc"
@@ -6478,10 +6568,18 @@ create-react-class@^15.6.2:
cross-env@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.0.tgz#6ecd4c015d5773e614039ee529076669b9d126f2"
+ integrity sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==
dependencies:
cross-spawn "^6.0.5"
is-windows "^1.0.0"
+cross-spawn-promise@^0.10.1:
+ version "0.10.1"
+ resolved "https://registry.yarnpkg.com/cross-spawn-promise/-/cross-spawn-promise-0.10.1.tgz#db9cb4c50c60b72a15be049b78122ce382d87b10"
+ integrity sha1-25y0xQxgtyoVvgSbeBIs44LYexA=
+ dependencies:
+ cross-spawn "^5.1.0"
+
cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@@ -6509,7 +6607,7 @@ cross-spawn@^4.0.2:
lru-cache "^4.0.1"
which "^1.2.9"
-cross-spawn@^5.0.1:
+cross-spawn@^5.0.1, cross-spawn@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
@@ -9110,6 +9208,11 @@ fs-write-stream-atomic@^1.0.8:
imurmurhash "^0.1.4"
readable-stream "1 || 2"
+fs.promised@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/fs.promised/-/fs.promised-3.0.0.tgz#ab77379f7c1ad0939e1262a8c2ced93fa6c39d3b"
+ integrity sha1-q3c3n3wa0JOeEmKows7ZP6bDnTs=
+
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -9352,6 +9455,14 @@ github-from-package@0.0.0:
version "0.0.0"
resolved "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
+gittar@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/gittar/-/gittar-0.1.1.tgz#d6993ea6160a86c8b7f3de722a61f73bc99e14b4"
+ integrity sha1-1pk+phYKhsi3895yKmH3O8meFLQ=
+ dependencies:
+ mkdirp "^0.5.1"
+ tar "^4.4.1"
+
glob-base@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
@@ -9553,6 +9664,18 @@ globby@^6.0.0, globby@^6.1.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
+globby@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680"
+ integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA=
+ dependencies:
+ array-union "^1.0.1"
+ dir-glob "^2.0.0"
+ glob "^7.1.2"
+ ignore "^3.3.5"
+ pify "^3.0.0"
+ slash "^1.0.0"
+
globby@^8.0.1:
version "8.0.2"
resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d"
@@ -12431,7 +12554,7 @@ jsx-ast-utils@^2.0.1, jsx-ast-utils@^2.1.0:
dependencies:
array-includes "^3.0.3"
-jszip@^3.1.3:
+jszip@^3.1.3, jszip@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.2.1.tgz#c5d32df7274042282b157efb16e522b43435e01a"
integrity sha512-iCMBbo4eE5rb1VCpm5qXOAaUiRKRUKiItn8ah2YQQx9qymmSAY98eyQfioChEYcVQLh0zxJ3wS4A0mh90AVPvw==
@@ -12549,6 +12672,11 @@ kew@^0.7.0:
resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b"
integrity sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=
+keychain@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/keychain/-/keychain-1.3.0.tgz#ccb8ddc64a62f34d541ac25e612186442a432410"
+ integrity sha1-zLjdxkpi801UGsJeYSGGRCpDJBA=
+
keycode@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04"
@@ -14720,7 +14848,7 @@ oauth-sign@~0.9.0:
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
-object-assign@4.X, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
+object-assign@*, object-assign@4.X, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
@@ -14942,6 +15070,16 @@ optionator@^0.8.1, optionator@^0.8.2:
type-check "~0.3.2"
wordwrap "~1.0.0"
+ora@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/ora/-/ora-1.4.0.tgz#884458215b3a5d4097592285f93321bb7a79e2e5"
+ integrity sha512-iMK1DOQxzzh2MBlVsU42G80mnrvUhqsMh74phHtDlrcTZPK0pH6o7l7DRshK+0YsxDyEuaOkziVdvM3T0QTzpw==
+ dependencies:
+ chalk "^2.1.0"
+ cli-cursor "^2.1.0"
+ cli-spinners "^1.0.1"
+ log-symbols "^2.1.0"
+
ordered-read-streams@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz#7137e69b3298bb342247a1bbee3881c80e2fd78b"
@@ -15229,6 +15367,13 @@ parse-asn1@^5.0.0:
pbkdf2 "^3.0.3"
safe-buffer "^5.1.1"
+parse-author@2.0.0, parse-author@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/parse-author/-/parse-author-2.0.0.tgz#d3460bf1ddd0dfaeed42da754242e65fb684a81f"
+ integrity sha1-00YL8d3Q367tQtp1QkLmX7aEqB8=
+ dependencies:
+ author-regex "^1.0.0"
+
parse-entities@^1.0.2, parse-entities@^1.1.0, parse-entities@^1.1.2:
version "1.2.1"
resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.1.tgz#2c761ced065ba7dc68148580b5a225e4918cdd69"
@@ -16390,6 +16535,11 @@ promise-polyfill@^6.0.1:
resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-6.1.0.tgz#dfa96943ea9c121fca4de9b5868cb39d3472e057"
integrity sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=
+promise-polyfill@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.1.0.tgz#30059da54d1358ce905ac581f287e184aedf995d"
+ integrity sha512-OzSf6gcCUQ01byV4BgwyUCswlaQQ6gzXc23aLQWhicvfX9kfsUiUhgt3CCQej8jDnl8/PhGF31JdHX2/MzF3WA==
+
promise-retry@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-1.1.1.tgz#6739e968e3051da20ce6497fb2b50f6911df3d6d"
@@ -17974,6 +18124,13 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies:
aproba "^1.1.1"
+run-sketch-plugin@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/run-sketch-plugin/-/run-sketch-plugin-1.0.3.tgz#2eb6112d2b0870adfb03fce8310ade76d7fd8f52"
+ integrity sha1-LrYRLSsIcK37A/zoMQredtf9j1I=
+ dependencies:
+ coscript "^1.0.0"
+
rx-lite@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
@@ -18580,6 +18737,36 @@ sisteransi@^1.0.0:
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c"
integrity sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==
+sketch-polyfill-fetch@^0.4.3:
+ version "0.4.5"
+ resolved "https://registry.yarnpkg.com/sketch-polyfill-fetch/-/sketch-polyfill-fetch-0.4.5.tgz#04cc50943444e45343295204cd4b3b070d448256"
+ integrity sha512-5iTw1k8eu2BtiDVy8JMMR+uV8hxamb0Ib3Q1Ob9MbDnMm9RocyaDGNCaHs8loKnB69i9fCchMboO0pMR6QjGpQ==
+
+skpm@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/skpm/-/skpm-1.2.0.tgz#fa50465758c17533dc5c9a15be02e99584bc5b95"
+ integrity sha512-Y1b27m/jx6tDbwcx1Sr3slmYrP47fC8UnCdo4somdXid/fxHzLaoxpgSADgKNZ3emNUrsTvC/cV9oxORyhx26Q==
+ dependencies:
+ "@skpm/internal-utils" "^0.1.14"
+ chalk "^2.4.2"
+ cross-spawn-promise "^0.10.1"
+ fs.promised "^3.0.0"
+ gittar "^0.1.1"
+ globby "^7.1.1"
+ inquirer "^6.2.0"
+ is-ci "^2.0.0"
+ jszip "^3.2.1"
+ keychain "^1.3.0"
+ log-symbols "^2.2.0"
+ opn "^5.4.0"
+ ora "^1.4.0"
+ parse-author "^2.0.0"
+ request "^2.88.0"
+ update-notifier "^2.5.0"
+ which "^1.3.1"
+ xml2js "^0.4.17"
+ yargs "^13.2.2"
+
slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
@@ -19673,7 +19860,7 @@ tar@^2.0.0:
fstream "^1.0.12"
inherits "2"
-tar@^4, tar@^4.4.8:
+tar@^4, tar@^4.4.1, tar@^4.4.8:
version "4.4.8"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
dependencies:
@@ -19709,7 +19896,7 @@ term-size@^1.2.0:
dependencies:
execa "^0.7.0"
-terser-webpack-plugin@^1.1.0, terser-webpack-plugin@^1.2.0:
+terser-webpack-plugin@^1.1.0, terser-webpack-plugin@^1.2.0, terser-webpack-plugin@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz#3f98bc902fac3e5d0de730869f50668561262ec8"
dependencies:
@@ -21051,6 +21238,13 @@ webpack-log@^2.0.0:
ansi-colors "^3.0.0"
uuid "^3.3.2"
+webpack-merge@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.1.tgz#5e923cf802ea2ace4fd5af1d3247368a633489b4"
+ integrity sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw==
+ dependencies:
+ lodash "^4.17.5"
+
webpack-sources@^1.1.0, webpack-sources@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85"
@@ -21089,6 +21283,36 @@ webpack@^4.23.1, webpack@^4.25.1:
watchpack "^1.5.0"
webpack-sources "^1.3.0"
+webpack@^4.29.6:
+ version "4.31.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.31.0.tgz#ae201d45f0571336e42d1c2b5c8ab56c4d3b0c63"
+ integrity sha512-n6RVO3X0LbbipoE62akME9K/JI7qYrwwufs20VvgNNpqUoH4860KkaxJTbGq5bgkVZF9FqyyTG/0WPLH3PVNJA==
+ dependencies:
+ "@webassemblyjs/ast" "1.8.5"
+ "@webassemblyjs/helper-module-context" "1.8.5"
+ "@webassemblyjs/wasm-edit" "1.8.5"
+ "@webassemblyjs/wasm-parser" "1.8.5"
+ acorn "^6.0.5"
+ acorn-dynamic-import "^4.0.0"
+ ajv "^6.1.0"
+ ajv-keywords "^3.1.0"
+ chrome-trace-event "^1.0.0"
+ enhanced-resolve "^4.1.0"
+ eslint-scope "^4.0.0"
+ json-parse-better-errors "^1.0.2"
+ loader-runner "^2.3.0"
+ loader-utils "^1.1.0"
+ memory-fs "~0.4.1"
+ micromatch "^3.1.8"
+ mkdirp "~0.5.0"
+ neo-async "^2.5.0"
+ node-libs-browser "^2.0.0"
+ schema-utils "^1.0.0"
+ tapable "^1.1.0"
+ terser-webpack-plugin "^1.1.0"
+ watchpack "^1.5.0"
+ webpack-sources "^1.3.0"
+
websocket-driver@>=0.5.1:
version "0.7.0"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb"
@@ -21583,6 +21807,11 @@ yeast@0.1.2:
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk=
+yesno@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/yesno/-/yesno-0.2.0.tgz#acc6c6b57b5ef88279a83cd394be9dc5d2317b7a"
+ integrity sha512-A66RIAOz+nP2hFpXfULwr57yubsWjEXw7ay8XL5a1AA6FpxiEDllKCnx9vCFDhN5TviTrF9yIfBwPskW3x9jxw==
+
yup@^0.27.0:
version "0.27.0"
resolved "https://registry.yarnpkg.com/yup/-/yup-0.27.0.tgz#f8cb198c8e7dd2124beddc2457571329096b06e7"