From 0e23ea5284567050ee5a05d7888dd6e7398369b2 Mon Sep 17 00:00:00 2001 From: Jeremy Robertson Date: Tue, 25 Feb 2020 03:43:31 -0600 Subject: [PATCH] Desktop: Resolves #160: Add word counter feature to notes (#2444) * Add word counter logic Fix errant whitespace changes * update to using react hooks Use React hooks remove extra theme set Update styling function * correct linting and package lock issues * WIP: update button functionality * Add line count and update styling from feedback * corrected file location to fit new build --- .eslintignore | 1 + .gitignore | 1 + CliClient/package-lock.json | 14 +++- ElectronClient/gui/MainScreen.jsx | 17 ++++ .../gui/NoteContentPropertiesDialog.tsx | 83 +++++++++++++++++++ ElectronClient/gui/NotePropertiesDialog.jsx | 14 +--- ElectronClient/gui/NoteText.jsx | 12 +++ ElectronClient/package-lock.json | 46 +++++++--- ElectronClient/package.json | 1 + ElectronClient/theme.js | 19 +++++ ReactNativeClient/package-lock.json | 41 ++++++--- package-lock.json | 41 ++++++--- 12 files changed, 242 insertions(+), 48 deletions(-) create mode 100644 ElectronClient/gui/NoteContentPropertiesDialog.tsx diff --git a/.eslintignore b/.eslintignore index e88a06ff6..2de1de3a1 100644 --- a/.eslintignore +++ b/.eslintignore @@ -53,6 +53,7 @@ ReactNativeClient/lib/joplin-renderer/assets/ ReactNativeClient/lib/rnInjectedJs/ # AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD +ElectronClient/gui/NoteContentPropertiesDialog.js ElectronClient/gui/ResourceScreen.js ElectronClient/gui/ShareNoteDialog.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js diff --git a/.gitignore b/.gitignore index bf176a6b7..f028398c8 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ Tools/commit_hook.txt *.map # AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD +ElectronClient/gui/NoteContentPropertiesDialog.js ElectronClient/gui/ResourceScreen.js ElectronClient/gui/ShareNoteDialog.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js diff --git a/CliClient/package-lock.json b/CliClient/package-lock.json index 60479f2db..57c507c7a 100644 --- a/CliClient/package-lock.json +++ b/CliClient/package-lock.json @@ -2284,6 +2284,7 @@ "version": "2.9.0", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -2302,6 +2303,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -2404,6 +2406,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -2489,7 +2492,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -2589,12 +2593,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -6545,7 +6551,7 @@ "requires": { "chalk": "^2.1.0", "emphasize": "^1.5.0", - "node-emoji": "git+https://github.com/laurent22/node-emoji.git#9fa01eac463e94dde1316ef8c53089eeef4973b5", + "node-emoji": "git+https://github.com/laurent22/node-emoji.git", "slice-ansi": "^1.0.0", "string-width": "^2.1.1", "terminal-kit": "^1.13.11", diff --git a/ElectronClient/gui/MainScreen.jsx b/ElectronClient/gui/MainScreen.jsx index 809fdb402..54607e881 100644 --- a/ElectronClient/gui/MainScreen.jsx +++ b/ElectronClient/gui/MainScreen.jsx @@ -5,6 +5,7 @@ const { SideBar } = require('./SideBar.min.js'); const { NoteList } = require('./NoteList.min.js'); const { NoteText } = require('./NoteText.min.js'); const { PromptDialog } = require('./PromptDialog.min.js'); +const NoteContentPropertiesDialog = require('./NoteContentPropertiesDialog.js').default; const NotePropertiesDialog = require('./NotePropertiesDialog.min.js'); const ShareNoteDialog = require('./ShareNoteDialog.js').default; const Setting = require('lib/models/Setting.js'); @@ -25,6 +26,7 @@ class MainScreenComponent extends React.Component { super(); this.notePropertiesDialog_close = this.notePropertiesDialog_close.bind(this); + this.noteContentPropertiesDialog_close = this.noteContentPropertiesDialog_close.bind(this); this.shareNoteDialog_close = this.shareNoteDialog_close.bind(this); this.sidebar_onDrag = this.sidebar_onDrag.bind(this); this.noteList_onDrag = this.noteList_onDrag.bind(this); @@ -42,6 +44,10 @@ class MainScreenComponent extends React.Component { this.setState({ notePropertiesDialogOptions: {} }); } + noteContentPropertiesDialog_close() { + this.setState({ noteContentPropertiesDialogOptions: {} }); + } + shareNoteDialog_close() { this.setState({ shareNoteDialogOptions: {} }); } @@ -54,6 +60,7 @@ class MainScreenComponent extends React.Component { message: '', }, notePropertiesDialogOptions: {}, + noteContentPropertiesDialogOptions: {}, shareNoteDialogOptions: {}, }); } @@ -274,6 +281,14 @@ class MainScreenComponent extends React.Component { onRevisionLinkClick: command.onRevisionLinkClick, }, }); + } else if (command.name === 'commandContentProperties') { + this.setState({ + noteContentPropertiesDialogOptions: { + visible: true, + text: command.text, + lines: command.lines, + }, + }); } else if (command.name === 'commandShareNoteDialog') { this.setState({ shareNoteDialogOptions: { @@ -609,6 +624,7 @@ class MainScreenComponent extends React.Component { const modalLayerStyle = Object.assign({}, styles.modalLayer, { display: this.state.modalLayer.visible ? 'block' : 'none' }); const notePropertiesDialogOptions = this.state.notePropertiesDialogOptions; + const noteContentPropertiesDialogOptions = this.state.noteContentPropertiesDialogOptions; const shareNoteDialogOptions = this.state.shareNoteDialogOptions; const keyboardMode = Setting.value('editor.keyboardMode'); @@ -616,6 +632,7 @@ class MainScreenComponent extends React.Component {
{this.state.modalLayer.message}
+ {noteContentPropertiesDialogOptions.visible && } {notePropertiesDialogOptions.visible && } {shareNoteDialogOptions.visible && } diff --git a/ElectronClient/gui/NoteContentPropertiesDialog.tsx b/ElectronClient/gui/NoteContentPropertiesDialog.tsx new file mode 100644 index 000000000..a0cbb9b5a --- /dev/null +++ b/ElectronClient/gui/NoteContentPropertiesDialog.tsx @@ -0,0 +1,83 @@ +import * as React from 'react'; +import { useState, useEffect } from 'react'; +const { _ } = require('lib/locale.js'); +const { themeStyle } = require('../theme.js'); +const DialogButtonRow = require('./DialogButtonRow.min'); +const Countable = require('countable'); + +interface NoteContentPropertiesDialogProps { + theme: number, + text: string, + onClose: Function, +} + +interface TextPropertiesMap { + [key: string]: number; +} + +interface KeyToLabelMap { + [key: string]: string; +} + +export default function NoteContentPropertiesDialog(props:NoteContentPropertiesDialogProps) { + const theme = themeStyle(props.theme); + const textComps: JSX.Element[] = []; + const [lines, setLines] = useState(0); + const [words, setWords] = useState(0); + const [characters, setCharacters] = useState(0); + const [charactersNoSpace, setCharactersNoSpace] = useState(0); + + useEffect(() => { + Countable.count(props.text, (counter: { words: number; all: number; characters: number; }) => { + setWords(counter.words); + setCharacters(counter.all); + setCharactersNoSpace(counter.characters); + }); + setLines(props.text.split('\n').length); + }, []); + + const textProperties: TextPropertiesMap = { + lines: lines, + words: words, + characters: characters, + charactersNoSpace: charactersNoSpace, + }; + + const keyToLabel: KeyToLabelMap = { + words: _('Words'), + characters: _('Characters'), + charactersNoSpace: _('Characters excluding spaces'), + lines: _('Lines'), + }; + + const buttonRow_click = () => { + props.onClose(); + }; + + const createItemField = (key: string, value: number) => { + const labelComp = ; + const controlComp =
{value}
; + + return ( +
{labelComp}{controlComp}
+ ); + }; + + if (textProperties) { + for (let key in textProperties) { + if (!textProperties.hasOwnProperty(key)) continue; + const comp = createItemField(key, textProperties[key]); + textComps.push(comp); + } + } + + return ( +
+
+
{_('Content properties')}
+
{textComps}
+ +
+
+ ); +} diff --git a/ElectronClient/gui/NotePropertiesDialog.jsx b/ElectronClient/gui/NotePropertiesDialog.jsx index 7fd448ac6..7a82e8766 100644 --- a/ElectronClient/gui/NotePropertiesDialog.jsx +++ b/ElectronClient/gui/NotePropertiesDialog.jsx @@ -114,14 +114,6 @@ class NotePropertiesDialog extends React.Component { this.styles_ = {}; this.styleKey_ = styleKey; - this.styles_.controlBox = { - marginBottom: '1em', - color: 'black', // This will apply for the calendar - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - }; - this.styles_.button = { minWidth: theme.buttonMinWidth, minHeight: theme.buttonMinHeight, @@ -234,7 +226,7 @@ class NotePropertiesDialog extends React.Component { createNoteField(key, value) { const styles = this.styles(this.props.theme); const theme = themeStyle(this.props.theme); - const labelComp = ; + const labelComp = ; let controlComp = null; let editComp = null; let editCompHandler = null; @@ -315,7 +307,7 @@ class NotePropertiesDialog extends React.Component { ); } else { - controlComp =
{displayedValue}
; + controlComp =
{displayedValue}
; } if (['id', 'revisionsLink', 'markup_language'].indexOf(key) < 0) { @@ -335,7 +327,7 @@ class NotePropertiesDialog extends React.Component { } return ( -
+
{labelComp} {controlComp} {editComp} diff --git a/ElectronClient/gui/NoteText.jsx b/ElectronClient/gui/NoteText.jsx index 893ab07c4..320034632 100644 --- a/ElectronClient/gui/NoteText.jsx +++ b/ElectronClient/gui/NoteText.jsx @@ -1817,6 +1817,18 @@ class NoteTextComponent extends React.Component { }, }); + toolbarItems.push({ + tooltip: _('Content Properties'), + iconName: 'fa-sticky-note', + onClick: () => { + this.props.dispatch({ + type: 'WINDOW_COMMAND', + name: 'commandContentProperties', + text: this.state.note.body, + }); + }, + }); + return toolbarItems; } diff --git a/ElectronClient/package-lock.json b/ElectronClient/package-lock.json index 8692ff1a5..1c1939fbd 100644 --- a/ElectronClient/package-lock.json +++ b/ElectronClient/package-lock.json @@ -847,7 +847,8 @@ "version": "2.1.1", "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -896,7 +897,8 @@ "version": "1.1.0", "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", @@ -909,7 +911,8 @@ "version": "1.1.0", "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -1040,7 +1043,8 @@ "version": "2.0.3", "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -1054,6 +1058,7 @@ "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1087,6 +1092,7 @@ "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -1107,6 +1113,7 @@ "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -1210,6 +1217,7 @@ "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -1305,7 +1313,8 @@ "version": "5.1.2", "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -1347,6 +1356,7 @@ "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -1368,6 +1378,7 @@ "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1416,13 +1427,15 @@ "version": "1.0.2", "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "resolved": false, "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true + "dev": true, + "optional": true } } }, @@ -1440,13 +1453,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true + "dev": true, + "optional": true }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -1934,7 +1949,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz", "integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==", - "dev": true + "dev": true, + "optional": true }, "boxen": { "version": "4.2.0", @@ -2917,6 +2933,11 @@ } } }, + "countable": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/countable/-/countable-3.0.1.tgz", + "integrity": "sha512-dz/sdi+RFpHJKMen5RAB9EKpCKd/q5WMfxG3SbdbQ3Pak4+SEPmDhVWZF0GTTHwjqjR+7DmiZHzZiFJ4qeAzmg==" + }, "create-emotion": { "version": "9.2.12", "resolved": "https://registry.npmjs.org/create-emotion/-/create-emotion-9.2.12.tgz", @@ -4913,13 +4934,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true + "dev": true, + "optional": true }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -8501,7 +8524,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true + "dev": true, + "optional": true }, "is-glob": { "version": "2.0.1", diff --git a/ElectronClient/package.json b/ElectronClient/package.json index 65c1de2ce..fbfb629f7 100644 --- a/ElectronClient/package.json +++ b/ElectronClient/package.json @@ -90,6 +90,7 @@ "chokidar": "^3.0.0", "clean-html": "^1.5.0", "compare-versions": "^3.2.1", + "countable": "^3.0.1", "diacritics": "^1.3.0", "diff-match-patch": "^1.0.4", "electron-context-menu": "^0.15.0", diff --git a/ElectronClient/theme.js b/ElectronClient/theme.js index 9ac88c652..7b9be03ef 100644 --- a/ElectronClient/theme.js +++ b/ElectronClient/theme.js @@ -342,6 +342,25 @@ function addExtraStyles(style) { justifyContent: 'center', }; + style.controlBox = { + marginBottom: '1em', + color: 'black', // This will apply for the calendar + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }; + + style.controlBoxLabel = { + marginRight: '1em', + width: '10em', + display: 'inline-block', + fontWeight: 'bold', + }; + + style.controlBoxValue = { + display: 'inline-block', + }; + style.dialogBox = { backgroundColor: style.backgroundColor, padding: 16, diff --git a/ReactNativeClient/package-lock.json b/ReactNativeClient/package-lock.json index 0cb156a1c..b1d805b71 100644 --- a/ReactNativeClient/package-lock.json +++ b/ReactNativeClient/package-lock.json @@ -4552,7 +4552,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -4570,11 +4571,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4587,15 +4590,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -4698,7 +4704,8 @@ }, "inherits": { "version": "2.0.4", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -4708,6 +4715,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4720,17 +4728,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4747,6 +4758,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -4827,7 +4839,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -4837,6 +4850,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -4912,7 +4926,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -4942,6 +4957,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4959,6 +4975,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4997,11 +5014,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.1.1", - "bundled": true + "bundled": true, + "optional": true } } }, diff --git a/package-lock.json b/package-lock.json index 2624dfb41..cbb706b01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2301,7 +2301,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2322,12 +2323,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2342,17 +2345,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2469,7 +2475,8 @@ "inherits": { "version": "2.0.4", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2481,6 +2488,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2495,6 +2503,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2502,12 +2511,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -2526,6 +2537,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -2615,7 +2627,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -2627,6 +2640,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -2712,7 +2726,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -2748,6 +2763,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2767,6 +2783,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -2810,12 +2827,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true } } },