├── .gitignore ├── .prettierrc ├── LICENSE.md ├── README.md ├── package.json ├── src ├── index.ts ├── offset.ts ├── paperClipperSimplify.ts └── unite.ts ├── tsconfig.json ├── tslint.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /lib 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "trailingComma": "all", 4 | "singleQuote": true 5 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Chris Bitsakis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # paper-clipper 2 | 3 | Use Clipper's boolean and offsetting operations in Paper.js 4 | 5 | [Paper.js](https://github.com/paperjs/paper.js) is a vector graphics scripting library. 6 | [js-angusj-clipper](https://github.com/xaviergonz/js-angusj-clipper) is a library with polygon offsetting and boolean operations. For optimal performance, it uses the original C++ version of [Clipper](https://sourceforge.net/projects/polyclipping/) via WebAssembly. 7 | 8 | ## Usage 9 | 10 | ### Install `paper-clipper` in your project: 11 | 12 | ``` 13 | yarn add paper-clipper 14 | ``` 15 | 16 | or 17 | 18 | ``` 19 | npm install --save paper-clipper 20 | ``` 21 | 22 | ### Initialize paper-clipper 23 | 24 | Include methods from paper-clipper 25 | 26 | ```js 27 | import { clipperLib, clipperOffset, clipperUnite, paperClipperSimplify } from 'paper-clipper'; 28 | ``` 29 | 30 | In an `async` function, create an instance of the Clipper library (usually only do this once in your app): 31 | 32 | ```js 33 | const clipper = await clipperLib.loadNativeClipperLibInstanceAsync( 34 | clipperLib.NativeClipperLibRequestedFormat.WasmWithAsmJsFallback, 35 | ); 36 | ``` 37 | 38 | ### clipperOffset 39 | 40 | As Clipper supports polygons only (ie. paths without bezier curves), paper-clipper's `clipperOffset` method will [`Path#flatten`](http://paperjs.org/reference/path/#flatten) the Paper.js Path, offset it, then apply simplification to the resulting path. 41 | 42 | Path simplification and smoothing is performed by the built-in `paperClipperSimplify` function which uses Paper's [`Path#simplify`](http://paperjs.org/reference/path/#simplify) method, modified to better conserve edges of the input path. 43 | 44 | ```js 45 | const path = new paper.Path(..) 46 | 47 | // Set strokeJoin on path for styling of expanded path edges 48 | path.strokeJoin = 'round' // or 'miter' or 'bevel' 49 | // Set strokeCap for styling of the ends of open paths 50 | path.strokeCap = 'round' // or 'square' or 'butt' 51 | 52 | // Offset a Paper.js Path 53 | // By 10 pixels 54 | const offsetPaths = await clipperOffset(clipper)(path, 10) 55 | 56 | // With simplification disabled 57 | // This returns an offset path with many segments, all without handles 58 | const offsetPaths = await clipperOffset(clipper)(path, { offset: 10, simplify: false }) 59 | 60 | // With a custom simplify method applied to the offset path 61 | // mySimplifyFn = (path) => output path 62 | const offsetPaths = await clipperOffset(clipper)(path, { offset: 10, simplify: mySimplifyFn }) 63 | 64 | // With tolerance applied to the built-in paperClipperSimplify method. (default: 0.25) 65 | const offsetPaths = await clipperOffset(clipper)(path, { offset: 10, tolerance: 2 }) 66 | ``` 67 | 68 | ### clipperUnite 69 | 70 | The `clipperUnite` method accepts a Paper.js Path but will discard handles, treating the path as a polygon. It offers better performance than Paper.js [`Path#unite`](http://paperjs.org/reference/path/#unite-path) when this boolean operation is needed on Paths with no segment handles. 71 | 72 | ```js 73 | // Unite two Paper.js Paths 74 | const paths = [new paper.Path(..), new paper.Path(..)] 75 | const unitedPaths = await clipperUnite(clipper)(paperPaths) 76 | ``` 77 | 78 | Refer to [js-angusj-clipper](https://github.com/xaviergonz/js-angusj-clipper) and its [API reference](https://github.com/xaviergonz/js-angusj-clipper/blob/master/docs/apiReference/index.md) for documentation. 79 | 80 | ### Further development 81 | 82 | - Clipper's other boolean operations (Intersection, Difference, Xor) could be included in the development of this library. 83 | 84 | [![npm version](https://badge.fury.io/js/paper-clipper.svg)](https://badge.fury.io/js/paper-clipper) 85 | 86 | ![paper-clipper strands](https://i.imgur.com/ZajvDJx.png) 87 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paper-clipper", 3 | "version": "1.1.1", 4 | "description": "Use Clipper's boolean and offsetting operations in paper.js", 5 | "main": "lib/index.js", 6 | "types": "lib/index.d.ts", 7 | "scripts": { 8 | "build": "tsc", 9 | "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"", 10 | "lint": "tslint -p tsconfig.json" 11 | }, 12 | "files": [ 13 | "lib/**/*" 14 | ], 15 | "repository": "https://gitlab.com/northamerican/paper-clipper", 16 | "author": "Chris Bitsakis", 17 | "license": "MIT", 18 | "private": false, 19 | "keywords": [ 20 | "paperjs", 21 | "paper", 22 | "offset", 23 | "offsetting", 24 | "clipper", 25 | "boolean", 26 | "operations" 27 | ], 28 | "dependencies": { 29 | "js-angusj-clipper": "^1.1.0", 30 | "paper": "^0.12.11", 31 | "simplify-js": "^1.2.4" 32 | }, 33 | "devDependencies": { 34 | "prettier": "^2.1.1", 35 | "tslint": "^6.1.3", 36 | "tslint-config-prettier": "^1.18.0", 37 | "typescript": "^4.0.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as clipperLib from 'js-angusj-clipper' 2 | import * as clipperLibWeb from 'js-angusj-clipper/web' 3 | import clipperOffset from './offset' 4 | import clipperUnite from './unite' 5 | import paperClipperSimplify from './paperClipperSimplify' 6 | 7 | export { 8 | clipperLib, 9 | clipperLibWeb, 10 | clipperOffset, 11 | clipperUnite, 12 | paperClipperSimplify 13 | } -------------------------------------------------------------------------------- /src/offset.ts: -------------------------------------------------------------------------------- 1 | import * as clipperLib from 'js-angusj-clipper' 2 | import paper from 'paper' 3 | import simplify from 'simplify-js' 4 | import paperClipperSimplify, { paperClipperSimplifyTolerance } from './paperClipperSimplify' 5 | 6 | // @ts-ignore 7 | paper.setup() 8 | 9 | enum EndTypes { 10 | round = clipperLib.EndType.OpenRound, 11 | square = clipperLib.EndType.OpenSquare, 12 | butt = clipperLib.EndType.OpenButt, 13 | closed = clipperLib.EndType.ClosedPolygon // clipperLib.EndType.ClosedLine 14 | } 15 | 16 | enum JoinTypes { 17 | miter = clipperLib.JoinType.Miter, 18 | round = clipperLib.JoinType.Round, 19 | bevel = clipperLib.JoinType.Square, 20 | } 21 | 22 | const scale = 1000 23 | const simplifyJsTolerance = 0.5 24 | 25 | type ClipperOffsetOptions = { 26 | offset: number 27 | tolerance?: number 28 | simplify?: ((targetPath: paper.Path) => paper.Path) | Boolean 29 | } 30 | 31 | type ClipperOffsetCallback = { 32 | (path: paper.Path, options: ClipperOffsetOptions, tolerance?: number): Promise, 33 | (path: paper.Path, offset: number, tolerance?: number): Promise 34 | } 35 | 36 | function clipperOffset (clipper: clipperLib.ClipperLibWrapper): ClipperOffsetCallback 37 | function clipperOffset (clipper: clipperLib.ClipperLibWrapper) { 38 | return async (path: paper.Path, options: any, tolerance: number = paperClipperSimplifyTolerance): Promise => { 39 | const suppliedOffset = !isNaN(options) 40 | const suppliedOptions = typeof options === 'object' && !isNaN(options.offset) 41 | 42 | if (!suppliedOffset && !suppliedOptions) { 43 | throw (new Error(`clipperOffset callback expects an options object or offset number as second argument. 44 | ex: await clipperOffset(clipper)(path, 10) 45 | or await clipperOffset(clipper)(path, { offset: 10, simplify: false })`)) 46 | } 47 | 48 | const offsetOptions: ClipperOffsetOptions = suppliedOptions ? options : { 49 | offset: options 50 | } 51 | const { closed, strokeJoin, strokeCap, miterLimit } = path 52 | const pathCopy = path.clone() as paper.Path 53 | pathCopy.flatten(1) 54 | 55 | const data = pathCopy.segments.map(({ point }) => 56 | ({ 57 | x: Math.round(point.x * scale), 58 | y: Math.round(point.y * scale) 59 | }) 60 | ) 61 | 62 | const offsetPaths = clipper.offsetToPaths({ 63 | delta: offsetOptions.offset * scale, 64 | miterLimit: miterLimit * scale, 65 | arcTolerance: 0.25 * scale, 66 | offsetInputs: [{ 67 | // @ts-ignore 68 | joinType: JoinTypes[strokeJoin], 69 | // @ts-ignore 70 | endType: closed ? EndTypes.closed : EndTypes[strokeCap], 71 | data 72 | }] 73 | }) 74 | 75 | if (!offsetPaths) return [] 76 | 77 | const isfunction = (fn: any): fn is Function => typeof fn === 'function' 78 | const isUndefinedOrTrue = (opt: any) => typeof opt === "undefined" || opt === true 79 | 80 | // If simplify option is a function, then use the function provided 81 | const simplifyFn = isfunction(offsetOptions.simplify) ? offsetOptions.simplify 82 | // If simplify option is underfined or true, use built-in simplify function 83 | : isUndefinedOrTrue(offsetOptions.simplify) ? paperClipperSimplify(tolerance) 84 | // Otherwise perform no-op when processing path 85 | : (path: paper.Path) => path 86 | 87 | return offsetPaths 88 | .map(offsetPath => 89 | new paper.Path({ 90 | closed: true, 91 | // The simplify-js library performs simplifications on polygons. This improves performance before simplifying into curves with Paper. 92 | segments: simplify(offsetPath.map(point => ({ 93 | x: point.x / scale, 94 | y: point.y / scale 95 | })), simplifyJsTolerance) 96 | }) 97 | ) 98 | .map(simplifyFn) 99 | .filter(offsetPath => offsetPath.length) 100 | } 101 | } 102 | 103 | export default clipperOffset 104 | -------------------------------------------------------------------------------- /src/paperClipperSimplify.ts: -------------------------------------------------------------------------------- 1 | import paper from 'paper' 2 | 3 | export const paperClipperSimplifyTolerance = 2.5 4 | const geomEpsilon = 1e-4 5 | 6 | // Get part of a path from offsets 7 | // Returns new path 8 | const getPathPart = (targetPath: paper.Path, from: number, distance = Infinity) => { 9 | const reverse = distance < 0 10 | const path = targetPath.clone({ insert: false }) 11 | const pathPart = path.splitAt(from) || path 12 | 13 | if (reverse) { 14 | const pathLength = path.length 15 | const reverseOffset = pathLength - Math.abs(distance) 16 | const withinPath = reverseOffset > 0 17 | 18 | if (withinPath) { 19 | const pathPartReverse = path.splitAt(reverseOffset) 20 | 21 | pathPartReverse.reverse() 22 | 23 | return pathPartReverse 24 | } 25 | 26 | path.reverse() 27 | 28 | return path 29 | } else { 30 | const withinPath = distance < pathPart.length 31 | 32 | if (withinPath) { 33 | pathPart.splitAt(distance) 34 | } 35 | 36 | return pathPart 37 | } 38 | } 39 | 40 | // Must be a segment with no handles 41 | const getSegmentAngle = (segment: paper.Segment) => { 42 | if (!segment.path.closed && (segment.isFirst() || segment.isLast())) return null 43 | 44 | const { handleIn, handleOut, point, path } = segment 45 | 46 | const hasHandleIn = handleIn.length > geomEpsilon 47 | const hasHandleOut = handleOut.length > geomEpsilon 48 | 49 | const inPointAngleLocation = path.getLocationAt(segment.isFirst() ? path.length - 1 : segment.previous.location.offset) 50 | const outPointAngleLocation = path.getLocationAt(segment.isLast() ? 1 : segment.next.location.offset) 51 | 52 | if (!inPointAngleLocation || !outPointAngleLocation) return null 53 | 54 | const inPointAngle = inPointAngleLocation.point.subtract(point).angle 55 | const outPointAngle = outPointAngleLocation.point.subtract(point).angle 56 | 57 | const inAngle = hasHandleIn ? handleIn.angle : inPointAngle 58 | const outAngle = hasHandleOut ? handleOut.angle : outPointAngle 59 | 60 | const angle = 180 - Math.abs(Math.abs(inAngle - outAngle) - 180) 61 | 62 | return angle 63 | } 64 | 65 | const segmentIsAngled = (threshold = 1) => (segment: paper.Segment) => { 66 | const angle = getSegmentAngle(segment) as number 67 | const isAngled = angle > geomEpsilon && angle < (180 - threshold) 68 | 69 | return isAngled 70 | } 71 | 72 | const splitAtOffsets = (path: paper.Path) => (offsets: number[]) => { 73 | if (offsets.length === 0) return [path] 74 | if (offsets.length === 1 && path.closed) return [path] 75 | 76 | return offsets.reduce((pathParts: paper.Path[], offset, i, offsetsArr) => { 77 | const prevOffset = offsetsArr[i - 1] || 0 78 | const pathPart = getPathPart(path, prevOffset, offset - prevOffset) 79 | const isLast = i === offsetsArr.length - 1 80 | 81 | pathParts = pathParts.concat(pathPart) 82 | 83 | if (isLast && !path.closed) { 84 | const lastPathPart = getPathPart(path, offset, Infinity) 85 | 86 | pathParts = pathParts.concat(lastPathPart) 87 | } 88 | 89 | return pathParts 90 | }, []) 91 | } 92 | 93 | const joinPaths = (paths: paper.Path[]) => { 94 | if (paths.length === 0) return null 95 | 96 | return paths.reduce((path, pathPart, i) => { 97 | if (i === 0) return pathPart 98 | 99 | path.join(pathPart, geomEpsilon) 100 | return path 101 | }) 102 | } 103 | 104 | const recursiveSimplify = (tolerance: number) => (targetPathPart: paper.Path) => { 105 | const pathPart = targetPathPart.clone({ insert: false }) as paper.Path 106 | 107 | pathPart.simplify(tolerance) 108 | 109 | const hasMoreSegments = pathPart.segments.length >= targetPathPart.segments.length 110 | 111 | return hasMoreSegments ? targetPathPart : pathPart 112 | } 113 | 114 | const removeDuplicateAdjacentSegments = (path: paper.Path): paper.Path => { 115 | const { segments } = path 116 | const segmentsBefore = segments.length 117 | 118 | segments.forEach(segment => { 119 | const { next } = segment 120 | 121 | if (!next) return 122 | 123 | const duplicateSegment = segment.point.isClose(next.point, geomEpsilon) 124 | 125 | if (duplicateSegment) { 126 | next.handleIn = segment.handleIn.clone() 127 | 128 | segment.remove() 129 | } 130 | }) 131 | 132 | return segmentsBefore > segments.length ? removeDuplicateAdjacentSegments(path) : path 133 | } 134 | 135 | const paperClipperSimplify = (tolerance: number = paperClipperSimplifyTolerance) => (targetPath: paper.Path): paper.Path => { 136 | const path = removeDuplicateAdjacentSegments(targetPath) 137 | const { closed } = path 138 | 139 | if (path.length === 0) return targetPath 140 | 141 | if (closed) { 142 | path.closed = false 143 | } 144 | 145 | const angledSegments = path.segments.filter(segmentIsAngled(45)) 146 | const angledSegmentOffsets = angledSegments.map( 147 | segment => segment.location.offset 148 | ) 149 | 150 | const pathParts = splitAtOffsets(path)(angledSegmentOffsets) 151 | .map(removeDuplicateAdjacentSegments) 152 | .map(recursiveSimplify(tolerance)) 153 | 154 | const joinedPath = joinPaths(pathParts) 155 | 156 | if (!joinedPath) return targetPath 157 | 158 | if (closed) { 159 | joinedPath.join(joinedPath) 160 | joinedPath.closed = true 161 | } 162 | 163 | return joinedPath 164 | } 165 | 166 | export default paperClipperSimplify 167 | -------------------------------------------------------------------------------- /src/unite.ts: -------------------------------------------------------------------------------- 1 | import * as clipperLib from 'js-angusj-clipper' 2 | import paper from 'paper' 3 | 4 | // @ts-ignore 5 | paper.setup() 6 | 7 | enum FillTypes { 8 | evenodd = clipperLib.PolyFillType.EvenOdd, 9 | nonzero = clipperLib.PolyFillType.NonZero 10 | } 11 | 12 | const clipperOffset = (clipper: clipperLib.ClipperLibWrapper) => async (paths: paper.Path[]): Promise => { 13 | const scale = 1000 14 | const data = paths.map(path => 15 | path.segments.map(({ point }) => ({ x: Math.round(point.x * scale), y: Math.round(point.y * scale) })) 16 | ) 17 | 18 | const { closed, fillRule } = paths[0] 19 | 20 | const unitedPaths = clipper.clipToPaths({ 21 | clipType: clipperLib.ClipType.Union, 22 | // @ts-ignore 23 | subjectFillType: FillTypes[fillRule], 24 | subjectInputs: [{ 25 | closed, 26 | data 27 | }] 28 | }) 29 | 30 | if (!unitedPaths) return [] 31 | 32 | return unitedPaths 33 | .map(path => 34 | new paper.Path({ 35 | closed, 36 | segments: path.map(point => ({ x: point.x / scale, y: point.y / scale })) 37 | }) 38 | ) 39 | } 40 | 41 | export default clipperOffset -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "./lib", 7 | "strict": true, 8 | "esModuleInterop": true 9 | }, 10 | "include": ["src"], 11 | "exclude": ["node_modules", "**/__tests__/*"] 12 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:recommended", "tslint-config-prettier"] 3 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@^7.0.0": 6 | version "7.10.4" 7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" 8 | integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== 9 | dependencies: 10 | "@babel/highlight" "^7.10.4" 11 | 12 | "@babel/helper-validator-identifier@^7.10.4": 13 | version "7.10.4" 14 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" 15 | integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== 16 | 17 | "@babel/highlight@^7.10.4": 18 | version "7.10.4" 19 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" 20 | integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== 21 | dependencies: 22 | "@babel/helper-validator-identifier" "^7.10.4" 23 | chalk "^2.0.0" 24 | js-tokens "^4.0.0" 25 | 26 | ansi-styles@^3.2.1: 27 | version "3.2.1" 28 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 29 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 30 | dependencies: 31 | color-convert "^1.9.0" 32 | 33 | argparse@^1.0.7: 34 | version "1.0.10" 35 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 36 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 37 | dependencies: 38 | sprintf-js "~1.0.2" 39 | 40 | balanced-match@^1.0.0: 41 | version "1.0.0" 42 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 43 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 44 | 45 | brace-expansion@^1.1.7: 46 | version "1.1.11" 47 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 48 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 49 | dependencies: 50 | balanced-match "^1.0.0" 51 | concat-map "0.0.1" 52 | 53 | builtin-modules@^1.1.1: 54 | version "1.1.1" 55 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" 56 | integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= 57 | 58 | chalk@^2.0.0, chalk@^2.3.0: 59 | version "2.4.2" 60 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 61 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 62 | dependencies: 63 | ansi-styles "^3.2.1" 64 | escape-string-regexp "^1.0.5" 65 | supports-color "^5.3.0" 66 | 67 | color-convert@^1.9.0: 68 | version "1.9.3" 69 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 70 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 71 | dependencies: 72 | color-name "1.1.3" 73 | 74 | color-name@1.1.3: 75 | version "1.1.3" 76 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 77 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 78 | 79 | commander@^2.12.1: 80 | version "2.20.3" 81 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" 82 | integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== 83 | 84 | concat-map@0.0.1: 85 | version "0.0.1" 86 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 87 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 88 | 89 | diff@^4.0.1: 90 | version "4.0.2" 91 | resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" 92 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== 93 | 94 | escape-string-regexp@^1.0.5: 95 | version "1.0.5" 96 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 97 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 98 | 99 | esprima@^4.0.0: 100 | version "4.0.1" 101 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 102 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 103 | 104 | fs.realpath@^1.0.0: 105 | version "1.0.0" 106 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 107 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 108 | 109 | glob@^7.1.1: 110 | version "7.1.6" 111 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 112 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 113 | dependencies: 114 | fs.realpath "^1.0.0" 115 | inflight "^1.0.4" 116 | inherits "2" 117 | minimatch "^3.0.4" 118 | once "^1.3.0" 119 | path-is-absolute "^1.0.0" 120 | 121 | has-flag@^3.0.0: 122 | version "3.0.0" 123 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 124 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 125 | 126 | inflight@^1.0.4: 127 | version "1.0.6" 128 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 129 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 130 | dependencies: 131 | once "^1.3.0" 132 | wrappy "1" 133 | 134 | inherits@2: 135 | version "2.0.4" 136 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 137 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 138 | 139 | js-angusj-clipper@^1.1.0: 140 | version "1.1.1" 141 | resolved "https://registry.yarnpkg.com/js-angusj-clipper/-/js-angusj-clipper-1.1.1.tgz#37a5f509e798d9cc41220eb6bfc0165d9c41c6f6" 142 | integrity sha512-gXCBJPQM7dVbz36ipzM5IOvmsxK2RUsN5o9xIuhvztiULGZbtqHBb0AMZu2J9BYn54nubrY0pVhVMwWLcQ+Vhw== 143 | 144 | js-tokens@^4.0.0: 145 | version "4.0.0" 146 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 147 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 148 | 149 | js-yaml@^3.13.1: 150 | version "3.14.0" 151 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" 152 | integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== 153 | dependencies: 154 | argparse "^1.0.7" 155 | esprima "^4.0.0" 156 | 157 | minimatch@^3.0.4: 158 | version "3.0.4" 159 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 160 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 161 | dependencies: 162 | brace-expansion "^1.1.7" 163 | 164 | minimist@^1.2.5: 165 | version "1.2.5" 166 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" 167 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== 168 | 169 | mkdirp@^0.5.3: 170 | version "0.5.5" 171 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" 172 | integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== 173 | dependencies: 174 | minimist "^1.2.5" 175 | 176 | once@^1.3.0: 177 | version "1.4.0" 178 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 179 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 180 | dependencies: 181 | wrappy "1" 182 | 183 | paper@^0.12.11: 184 | version "0.12.15" 185 | resolved "https://registry.yarnpkg.com/paper/-/paper-0.12.15.tgz#28b5e5ce0f7a99a851e980f6715f376881dfbc84" 186 | integrity sha512-9qqtcY+V0Sd2RTLZKh7rTfWDbhfitOrtq3kzgOufhGpuYpCUXM+3UIK61au14tI0F9NA4xSttDYfk376O50aPA== 187 | 188 | path-is-absolute@^1.0.0: 189 | version "1.0.1" 190 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 191 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 192 | 193 | path-parse@^1.0.6: 194 | version "1.0.6" 195 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" 196 | integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== 197 | 198 | prettier@^2.1.1: 199 | version "2.3.1" 200 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" 201 | integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA== 202 | 203 | resolve@^1.3.2: 204 | version "1.17.0" 205 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" 206 | integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== 207 | dependencies: 208 | path-parse "^1.0.6" 209 | 210 | semver@^5.3.0: 211 | version "5.7.1" 212 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 213 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 214 | 215 | simplify-js@^1.2.4: 216 | version "1.2.4" 217 | resolved "https://registry.yarnpkg.com/simplify-js/-/simplify-js-1.2.4.tgz#7aab22d6df547ffd40ef0761ccd82b75287d45c7" 218 | integrity sha512-vITfSlwt7h/oyrU42R83mtzFpwYk3+mkH9bOHqq/Qw6n8rtR7aE3NZQ5fbcyCUVVmuMJR6ynsAhOfK2qoah8Jg== 219 | 220 | sprintf-js@~1.0.2: 221 | version "1.0.3" 222 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 223 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 224 | 225 | supports-color@^5.3.0: 226 | version "5.5.0" 227 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 228 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 229 | dependencies: 230 | has-flag "^3.0.0" 231 | 232 | tslib@^1.13.0, tslib@^1.8.1: 233 | version "1.13.0" 234 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" 235 | integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== 236 | 237 | tslint-config-prettier@^1.18.0: 238 | version "1.18.0" 239 | resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37" 240 | integrity sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg== 241 | 242 | tslint@^6.1.3: 243 | version "6.1.3" 244 | resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904" 245 | integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg== 246 | dependencies: 247 | "@babel/code-frame" "^7.0.0" 248 | builtin-modules "^1.1.1" 249 | chalk "^2.3.0" 250 | commander "^2.12.1" 251 | diff "^4.0.1" 252 | glob "^7.1.1" 253 | js-yaml "^3.13.1" 254 | minimatch "^3.0.4" 255 | mkdirp "^0.5.3" 256 | resolve "^1.3.2" 257 | semver "^5.3.0" 258 | tslib "^1.13.0" 259 | tsutils "^2.29.0" 260 | 261 | tsutils@^2.29.0: 262 | version "2.29.0" 263 | resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" 264 | integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== 265 | dependencies: 266 | tslib "^1.8.1" 267 | 268 | typescript@^4.0.2: 269 | version "4.3.4" 270 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.4.tgz#3f85b986945bcf31071decdd96cf8bfa65f9dcbc" 271 | integrity sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew== 272 | 273 | wrappy@1: 274 | version "1.0.2" 275 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 276 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 277 | --------------------------------------------------------------------------------