diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..54e4a6d --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +LICENSE +node_modules +.* +*.gif +*.lock +*.log +*.snap diff --git a/.prettierrc b/.prettierrc index 76d6b11..c6a1376 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,4 @@ { "trailingComma": "es5", - "quotes": "single" + "singleQuote": true } diff --git a/.travis.yml b/.travis.yml index 158a12d..8ebd275 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ script: - node --version - yarn --version - yarn run test - - yarn run flow cache: yarn: true directories: diff --git a/README.md b/README.md index 5f8f71f..e01130d 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,17 @@ -draft-js-markdown-plugin -================================== +# draft-js-markdown-plugin [![Build Status](https://travis-ci.org/withspectrum/draft-js-markdown-plugin.svg?branch=master)](https://travis-ci.org/withspectrum/draft-js-markdown-plugin) [![npm](https://img.shields.io/npm/v/draft-js-markdown-plugin.svg)][npm] + +**IMPORTANT** The current version of `draft-js-markdown-plugin` is updated to work with DraftJS @ `^0.11.0`. Please ensure you've satisfied your peer dependencies in npm/yarn to avoid problems! + An opinionated [DraftJS] plugin for supporting Markdown syntax shortcuts in DraftJS. This plugin works with [DraftJS Plugins], and is a fork of the excellent [`draft-js-markdown-shortcuts-plugin`](https://github.com/ngs/draft-js-markdown-shortcuts-plugin) by [@ngs](https://github.com/ngs). (see [why fork that plugin](#why-fork-the-markdown-shortcuts-plugin) for more info) ![screen](screen.gif) -[View Demo][Demo] +[View Demo][demo] ## Installation @@ -26,13 +28,12 @@ import createMarkdownPlugin from 'draft-js-markdown-plugin'; import { EditorState } from 'draft-js'; export default class DemoEditor extends Component { - state = { editorState: EditorState.createEmpty(), - plugins: [createMarkdownPlugin()] + plugins: [createMarkdownPlugin()], }; - onChange = (editorState) => { + onChange = editorState => { this.setState({ editorState, }); @@ -62,7 +63,7 @@ import createPrismPlugin from 'draft-js-prism-plugin'; class Editor extends Component { state = { plugins: [ - // Add the Prism plugin to the plugins array + // Add the Prism plugin to the plugins array createPrismPlugin({ prism: Prism }), @@ -91,7 +92,7 @@ renderLanguageSelect = ({ }) => React.Node ``` -Code blocks render a select to switch syntax highlighting - `renderLanguageSelect` is a render function that lets you override how this is rendered. +Code blocks render a select to switch syntax highlighting - `renderLanguageSelect` is a render function that lets you override how this is rendered. #### Example: @@ -119,10 +120,10 @@ Dictionary for languages available to code block switcher ```js const languages = { - js: 'JavaScript' -} + js: 'JavaScript', +}; -const markdownPlugin = createMarkdownPlugin({ languages }) +const markdownPlugin = createMarkdownPlugin({ languages }); ``` ### `features` @@ -144,27 +145,20 @@ features = { const features = { inline: ['BOLD'], block: ['CODE', 'header-one'], -} -const plugin = createMarkdownPlugin({ features }) +}; +const plugin = createMarkdownPlugin({ features }); ``` -*Available Inline features*: +_Available Inline features_: ```js -[ - 'BOLD', - 'ITALIC', - 'CODE', - 'STRIKETHROUGH', - 'LINK', - 'IMAGE' -] +['BOLD', 'ITALIC', 'CODE', 'STRIKETHROUGH', 'LINK', 'IMAGE']; ``` -*Available Block features*: +_Available Block features_: ```js -import { CHECKABLE_LIST_ITEM } from "draft-js-checkable-list-item" +import { CHECKABLE_LIST_ITEM } from 'draft-js-checkable-list-item'; [ 'CODE', 'header-one', @@ -179,7 +173,7 @@ import { CHECKABLE_LIST_ITEM } from "draft-js-checkable-list-item" // see import statementabove CHECKABLE_LIST_ITEM, 'blockquote', -] +]; ``` ### `entityType` @@ -189,12 +183,12 @@ To interoperate this plugin with other DraftJS plugins, i.e. [`draft-js-plugins` #### Example: ```js -import createMarkdownPlugin from "draft-js-markdown-plugin"; -import createFocusPlugin from "draft-js-focus-plugin"; -import createImagePlugin from "draft-js-image-plugin"; +import createMarkdownPlugin from 'draft-js-markdown-plugin'; +import createFocusPlugin from 'draft-js-focus-plugin'; +import createImagePlugin from 'draft-js-image-plugin'; const entityType = { - IMAGE: "IMAGE", + IMAGE: 'IMAGE', }; const focusPlugin = createFocusPlugin(); @@ -209,7 +203,7 @@ const editorPlugins = [focusPlugin, imagePlugin, markdownPlugin]; ## Why fork the `markdown-shortcuts-plugin`? -Writing is a core part of our app, and while the `markdown-shortcuts-plugin` is awesome and battle-tested there are a few opinionated things we wanted to do differently. Rather than bother [@ngs](https://github.com/ngs) with tons of PRs, we figured it'd be better to own that core part of our experience fully. +Writing is a core part of our app, and while the `markdown-shortcuts-plugin` is awesome and battle-tested there are a few opinionated things we wanted to do differently. Rather than bother [@ngs](https://github.com/ngs) with tons of PRs, we figured it'd be better to own that core part of our experience fully. ## License @@ -217,8 +211,8 @@ Licensed under the MIT license, Copyright Ⓒ 2017 Space Program Inc. This plugi See [LICENSE] for the full license text. -[Demo]: https://markdown-plugin.spectrum.chat/ -[DraftJS]: https://facebook.github.io/draft-js/ -[DraftJS Plugins]: https://github.com/draft-js-plugins/draft-js-plugins -[LICENSE]: ./LICENSE +[demo]: https://markdown-plugin.spectrum.chat/ +[draftjs]: https://facebook.github.io/draft-js/ +[draftjs plugins]: https://github.com/draft-js-plugins/draft-js-plugins +[license]: ./LICENSE [npm]: https://www.npmjs.com/package/draft-js-markdown-plugin diff --git a/package.json b/package.json index b06a667..74f4f4b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "draft-js-markdown-plugin", - "version": "3.0.5", + "version": "4.0.0", "description": "A DraftJS plugin for supporting Markdown syntax shortcuts, fork of draft-js-markdown-shortcuts-plugin", "main": "lib/index.js", "scripts": { @@ -9,6 +9,7 @@ "build:js": "BABEL_DISABLE_CACHE=1 BABEL_ENV=production NODE_ENV=production node_modules/.bin/babel --out-dir='lib' --ignore='**/__test__/*' src", "clean": "node_modules/.bin/rimraf lib; node_modules/.bin/rimraf demo/public", "dev": "node_modules/.bin/babel-node ./demo/server.js", + "prettier": "prettier --write \"**/*\"", "test": "jest", "precommit": "jest && lint-staged" }, @@ -50,7 +51,8 @@ "coveralls": "^2.13.3", "css-loader": "^0.26.0", "css-modules-require-hook": "^4.2.3", - "draft-js-plugins-editor": "2.0.0-rc2", + "draft-js": "^0.11.0", + "draft-js-plugins-editor": "^3.0.0", "draft-js-prism": "ngs/draft-js-prism#6edb31c3805dd1de3fb897cc27fced6bac1bafbb", "enzyme": "^2.6.0", "eslint": "^3.11.1", @@ -94,16 +96,17 @@ "webpack-hot-middleware": "^2.24.3" }, "peerDependencies": { - "draft-js-plugins-editor": "~2.0.0-rc.1 || 2.0.0-rc2 || 2.0.0-rc1 || 2.0.0-beta12", + "draft-js-plugins-editor": "^3.0.0", "react": "^15.0.0 || ^16.0.0", - "react-dom": "^15.0.0 || ^16.0.0" + "react-dom": "^15.0.0 || ^16.0.0", + "draft-js": "^0.11.0", + "prismjs": "^1.0.0" }, "contributors": [ "Atsushi Nagase " ], "dependencies": { "decorate-component-with-props": "^1.1.0", - "draft-js": "^0.10.4", "draft-js-checkable-list-item": "^2.0.6", "draft-js-prism-plugin": "^0.1.3", "immutable": "~3.7.4", diff --git a/src/__test__/__snapshots__/plugin-new.test.js.snap b/src/__test__/__snapshots__/plugin-new.test.js.snap index ef6d926..6ee2a9b 100644 --- a/src/__test__/__snapshots__/plugin-new.test.js.snap +++ b/src/__test__/__snapshots__/plugin-new.test.js.snap @@ -76,4 +76,4 @@ Object { ], "entityMap": Object {}, } -` +`; diff --git a/src/__test__/plugin-new.test.js b/src/__test__/plugin-new.test.js index ac04fed..7ef8458 100644 --- a/src/__test__/plugin-new.test.js +++ b/src/__test__/plugin-new.test.js @@ -1,9 +1,9 @@ -import Draft, { EditorState, SelectionState, convertToRaw } from "draft-js"; -import createMarkdownPlugin from "../"; -import { applyMDtoInlineStyleChange } from "./utils"; +import Draft, { EditorState, SelectionState, convertToRaw } from 'draft-js'; +import createMarkdownPlugin from '../'; +import { applyMDtoInlineStyleChange } from './utils'; -describe("markdown", () => { - it("should convert asteriks to bold text", () => { +describe('markdown', () => { + it('should convert asteriks to bold text', () => { const { handleBeforeInput } = createMarkdownPlugin(); const setEditorState = jest.fn(); const before = EditorState.moveSelectionToEnd( @@ -12,9 +12,9 @@ describe("markdown", () => { entityMap: {}, blocks: [ { - key: "item1", - text: "Some *text", - type: "unstyled", + key: 'item1', + text: 'Some *text', + type: 'unstyled', depth: 0, inlineStyleRanges: [], entityRanges: [], @@ -24,22 +24,22 @@ describe("markdown", () => { }) ) ); - expect(handleBeforeInput("*", before, { setEditorState })).toEqual( - "handled" - ); + expect( + handleBeforeInput('*', before, new Date().getTime(), { setEditorState }) + ).toEqual('handled'); const raw = convertToRaw( setEditorState.mock.calls[0][0].getCurrentContent() ); expect(raw).toMatchSnapshot(); }); - it("should not do anything to existing inline styles when within them", () => { + it('should not do anything to existing inline styles when within them', () => { const { handleBeforeInput } = createMarkdownPlugin(); const setEditorState = jest.fn(); const boldInlineStyleRange = { length: 4, offset: 5, - style: "BOLD", + style: 'BOLD', }; const before = EditorState.forceSelection( EditorState.createWithContent( @@ -47,9 +47,9 @@ describe("markdown", () => { entityMap: {}, blocks: [ { - key: "item1", - text: "Some text", - type: "unstyled", + key: 'item1', + text: 'Some text', + type: 'unstyled', depth: 0, inlineStyleRanges: [boldInlineStyleRange], entityRanges: [], @@ -59,25 +59,27 @@ describe("markdown", () => { }) ), new SelectionState({ - anchorKey: "item1", + anchorKey: 'item1', anchorOffset: 6, - focusKey: "item1", + focusKey: 'item1', focusOffset: 6, isBackward: false, hasFocus: true, }) ); - expect(handleBeforeInput("a", before, { setEditorState })).toEqual( - "not-handled" - ); + expect( + handleBeforeInput('a', before, new Date().getTime(), { + setEditorState, + }) + ).toEqual('not-handled'); }); - it("should not unstick inline styles if they were not added by md-to-inline-style change", () => { + it('should not unstick inline styles if they were not added by md-to-inline-style change', () => { const { handleBeforeInput } = createMarkdownPlugin(); const boldInlineStyleRange = { length: 4, offset: 5, - style: "BOLD", + style: 'BOLD', }; const editorState = EditorState.moveSelectionToEnd( EditorState.createWithContent( @@ -85,9 +87,9 @@ describe("markdown", () => { entityMap: {}, blocks: [ { - key: "item1", - text: "Some text", - type: "unstyled", + key: 'item1', + text: 'Some text', + type: 'unstyled', depth: 0, inlineStyleRanges: [boldInlineStyleRange], entityRanges: [], @@ -97,16 +99,18 @@ describe("markdown", () => { }) ) ); - expect(handleBeforeInput("a", editorState, {})).toEqual("not-handled"); + expect( + handleBeforeInput('a', editorState, new Date().getTime(), {}) + ).toEqual('not-handled'); }); - it("should not have sticky inline styles", () => { + it('should not have sticky inline styles', () => { const { handleBeforeInput } = createMarkdownPlugin(); const setEditorState = jest.fn(); const boldInlineStyleRange = { length: 4, offset: 5, - style: "BOLD", + style: 'BOLD', }; const editorState = applyMDtoInlineStyleChange( EditorState.moveSelectionToEnd( @@ -115,9 +119,9 @@ describe("markdown", () => { entityMap: {}, blocks: [ { - key: "item1", - text: "Some text", - type: "unstyled", + key: 'item1', + text: 'Some text', + type: 'unstyled', depth: 0, inlineStyleRanges: [boldInlineStyleRange], entityRanges: [], @@ -129,9 +133,11 @@ describe("markdown", () => { ) ); - expect(handleBeforeInput("a", editorState, { setEditorState })).toEqual( - "handled" - ); + expect( + handleBeforeInput('a', editorState, new Date().getTime(), { + setEditorState, + }) + ).toEqual('handled'); const raw = convertToRaw( setEditorState.mock.calls[0][0].getCurrentContent() ); @@ -139,13 +145,13 @@ describe("markdown", () => { expect(raw).toMatchSnapshot(); }); - it("should not have sticky inline styles after the line ending with styles", () => { + it('should not have sticky inline styles after the line ending with styles', () => { const { handleBeforeInput } = createMarkdownPlugin(); const setEditorState = jest.fn(); const boldInlineStyleRange = { length: 4, offset: 5, - style: "BOLD", + style: 'BOLD', }; const editorState = applyMDtoInlineStyleChange( EditorState.moveSelectionToEnd( @@ -154,18 +160,18 @@ describe("markdown", () => { entityMap: {}, blocks: [ { - key: "item1", - text: "Some text", - type: "unstyled", + key: 'item1', + text: 'Some text', + type: 'unstyled', depth: 0, inlineStyleRanges: [boldInlineStyleRange], entityRanges: [], data: {}, }, { - key: "item2", - text: "", - type: "unstyled", + key: 'item2', + text: '', + type: 'unstyled', depth: 0, inlineStyleRanges: [], entityRanges: [], @@ -177,9 +183,11 @@ describe("markdown", () => { ) ); - expect(handleBeforeInput("a", editorState, { setEditorState })).toEqual( - "handled" - ); + expect( + handleBeforeInput('a', editorState, new Date().getTime(), { + setEditorState, + }) + ).toEqual('handled'); const raw = convertToRaw( setEditorState.mock.calls[0][0].getCurrentContent() ); diff --git a/src/__test__/plugin.test.js b/src/__test__/plugin.test.js index aa5f82b..2c287a6 100755 --- a/src/__test__/plugin.test.js +++ b/src/__test__/plugin.test.js @@ -479,6 +479,7 @@ describe("draft-js-markdown-plugin", () => { plugin.handleBeforeInput( character, editorState || store.getEditorState(), + new Date().getTime(), store ); currentRawContentState = { diff --git a/src/index.js b/src/index.js index 2324c92..83a085e 100755 --- a/src/index.js +++ b/src/index.js @@ -343,6 +343,7 @@ const createMarkdownPlugin = (_config = {}) => { } return "not-handled"; }, + // oddly handleReturn does not get the event timestamp handleReturn(ev, editorState, { setEditorState }) { if (inLink(editorState)) return "not-handled"; @@ -381,7 +382,12 @@ const createMarkdownPlugin = (_config = {}) => { return "not-handled"; }, - handleBeforeInput(character, editorState, { setEditorState }) { + handleBeforeInput( + character, + editorState, + eventTimestamp, + { setEditorState } + ) { // If we're in a code block - don't transform markdown if (inCodeBlock(editorState)) return "not-handled"; diff --git a/yarn.lock b/yarn.lock index 723b33c..5fe3135 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1529,6 +1529,11 @@ core-js@^2.4.0, core-js@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" +core-js@^2.4.1: + version "2.6.11" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" + integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -1793,10 +1798,6 @@ decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" -decorate-component-with-props@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/decorate-component-with-props/-/decorate-component-with-props-1.0.2.tgz#5764d3cf6a58685a522201bad31bff0cb531e5fe" - decorate-component-with-props@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decorate-component-with-props/-/decorate-component-with-props-1.1.0.tgz#b496c814c6a2aba0cf2ad26e44cbedb8ead42f15" @@ -1945,15 +1946,13 @@ draft-js-modifiers@^0.1.5: draft-js "~0.10.0" immutable "~3.7.4" -draft-js-plugins-editor@2.0.0-rc2: - version "2.0.0-rc2" - resolved "https://registry.yarnpkg.com/draft-js-plugins-editor/-/draft-js-plugins-editor-2.0.0-rc2.tgz#521eb415882a1f670c03a00581247e752291218c" +draft-js-plugins-editor@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/draft-js-plugins-editor/-/draft-js-plugins-editor-3.0.0.tgz#196d1e065e2c29faebaab4ec081b734fdef294a2" + integrity sha512-bFEL0FUIPg9VK3KSeBZ3D+uMqQEVe4Cv7++LWCMASRH02jy6x2f87NRxSZLzTQES5+oL6Qg+OEUlaTn409145A== dependencies: - decorate-component-with-props "^1.0.2" - find-with-regex "^1.0.2" immutable "~3.7.4" prop-types "^15.5.8" - union-class-names "^1.0.0" draft-js-prism-plugin@^0.1.3: version "0.1.3" @@ -1977,13 +1976,14 @@ draft-js-prism@ngs/draft-js-prism#6edb31c3805dd1de3fb897cc27fced6bac1bafbb: immutable "*" prismjs "^1.5.0" -draft-js@^0.10.4: - version "0.10.5" - resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.10.5.tgz#bfa9beb018fe0533dbb08d6675c371a6b08fa742" +draft-js@^0.11.0: + version "0.11.4" + resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.11.4.tgz#e48782b96b90a5c14f2cb34c164c2a1a359d8a48" + integrity sha512-BLZ59s0vkDj/zI8UPo9Nit/hPsl11ztDejxDCQlVbvEXJSWrTXqO6ZYgdw3hXLtuojq/URqq3wTrpnb3dvzvLA== dependencies: - fbjs "^0.8.15" + fbjs "^1.0.0" immutable "~3.7.4" - object-assign "^4.1.0" + object-assign "^4.1.1" draft-js@~0.10.0: version "0.10.2" @@ -2488,6 +2488,11 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" +fbjs-css-vars@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" + integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== + fbjs@^0.8.12: version "0.8.15" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.15.tgz#4f0695fdfcc16c37c0b07facec8cb4c4091685b9" @@ -2500,7 +2505,7 @@ fbjs@^0.8.12: setimmediate "^1.0.5" ua-parser-js "^0.7.9" -fbjs@^0.8.15, fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.9: +fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.9: version "0.8.16" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" dependencies: @@ -2512,6 +2517,20 @@ fbjs@^0.8.15, fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.9: setimmediate "^1.0.5" ua-parser-js "^0.7.9" +fbjs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-1.0.0.tgz#52c215e0883a3c86af2a7a776ed51525ae8e0a5a" + integrity sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA== + dependencies: + core-js "^2.4.1" + fbjs-css-vars "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + figures@^1.3.5, figures@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -2586,10 +2605,6 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" -find-with-regex@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/find-with-regex/-/find-with-regex-1.0.2.tgz#d3b36286539f14c527e31f194159c6d251651a45" - flat-cache@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" @@ -6362,6 +6377,11 @@ typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" +ua-parser-js@^0.7.18: + version "0.7.21" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" + integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== + ua-parser-js@^0.7.9: version "0.7.14" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.14.tgz#110d53fa4c3f326c121292bbeac904d2e03387ca" @@ -6387,10 +6407,6 @@ undefsafe@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-0.0.3.tgz#ecca3a03e56b9af17385baac812ac83b994a962f" -union-class-names@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/union-class-names/-/union-class-names-1.0.0.tgz#9259608adacc39094a2b0cfe16c78e6200617847" - uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"