From e314d0971f0f3031486cfa8a63487646a50bf094 Mon Sep 17 00:00:00 2001 From: Danny van Kooten Date: Sun, 4 Dec 2016 13:23:09 +0100 Subject: [PATCH] replace chartjs with d3 --- ROADMAP.md | 1 + api/api.go | 4 +- assets/js/components/Graph.js | 123 +++++++++------- assets/sass/_graphs.scss | 80 ++++++++++ assets/sass/styles.scss | 2 + gulpfile.js | 2 +- package.json | 10 +- yarn.lock | 266 +++++++++++++++++++++++++++++----- 8 files changed, 394 insertions(+), 94 deletions(-) create mode 100644 assets/sass/_graphs.scss diff --git a/ROADMAP.md b/ROADMAP.md index dc68df6..79e5ed9 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -5,6 +5,7 @@ This is a general draft document for thoughts and todo's, without any structure ### What's cooking? +- Replace chart with custom D3 chart. - Allow setting custom limit in table overviews. - Allow sorting in table overviews. - Choose a OS license & settle on name. diff --git a/api/api.go b/api/api.go index e0fe3a4..35cba25 100644 --- a/api/api.go +++ b/api/api.go @@ -13,8 +13,8 @@ type Datapoint struct { Percentage float32 `json:",omitempty"` } -var defaultPeriod = 7 -var defaultLimit = 10 +const defaultPeriod = 7 +const defaultLimit = 10 // log fatal errors func checkError(err error) { diff --git a/assets/js/components/Graph.js b/assets/js/components/Graph.js index 85eb332..139db18 100644 --- a/assets/js/components/Graph.js +++ b/assets/js/components/Graph.js @@ -1,12 +1,12 @@ 'use strict'; import { h, render, Component } from 'preact'; -import Chart from 'chart.js' -const dayInSeconds = 60 * 60 * 24; +import * as d3 from 'd3'; +import tip from 'd3-tip'; -Chart.defaults.global.tooltips.xPadding = 10; -Chart.defaults.global.tooltips.yPadding = 10; -Chart.defaults.global.layout = { padding: 10 } +d3.tip = tip; + +const dayInSeconds = 60 * 60 * 24; class Graph extends Component { constructor(props) { @@ -16,8 +16,12 @@ class Graph extends Component { visitorData: [], pageviewData: [] } + this.fetchData = this.fetchData.bind(this); - this.fetchData(props.period); + } + + componentDidMount() { + this.fetchData(this.props.period); } componentWillReceiveProps(newProps) { @@ -27,37 +31,58 @@ class Graph extends Component { } refreshChart() { - if( ! this.canvas ) { return; } + var w = 800, + h = 300, + padt = 20, padr = 20, padb = 60, padl = 30, + x = d3.scaleBand().rangeRound([0, w - padl - padr]).padding(0.1), + y = d3.scaleLinear().range([h, 0]), + yAxis = d3.axisLeft().scale(y).tickSize(-w + padl + padr), + xAxis = d3.axisBottom().scale(x); - // clear canvas - var newCanvas = document.createElement('canvas'); - this.canvas.parentNode.style.minHeight = this.canvas.parentNode.clientHeight + "px"; - this.canvas.parentNode.replaceChild(newCanvas, this.canvas); - this.canvas = newCanvas; + var tip = d3.tip() + .attr('class', 'd3-tip') + .html(function(d) { return '' + d.Count + '' + ' visitors' }) + .offset([-12, 0]); - if( this.chart ) { this.chart.clear(); } - this.chart = new Chart(this.canvas, { - type: 'line', - data: { - labels: this.state.visitorData.map((d) => d.Label), - datasets: [ - { - label: '# of Visitors', - data: this.state.visitorData.map((d) => d.Count), - backgroundColor: 'rgba(255, 155, 0, .6)', - pointStyle: 'rect', - pointBorderWidth: 0.1, - }, - { - label: '# of Pageviews', - data: this.state.pageviewData.map((d) => d.Count), - backgroundColor: 'rgba(0, 155, 255, .4)', - pointStyle: 'rect', - pointBorderWidth: 0.1, - } - ], - } - }); + var vis = d3.select('#graph') + .append('svg') + .attr('width', w) + .attr('height', h + padt + padb) + .append('g') + .attr('transform', 'translate(' + padl + ',' + padt + ')'); + + vis.call(tip); + + var max = d3.max(this.state.visitorData, function(d) { return d.Count }); + x.domain(d3.range(this.state.visitorData.length)) + y.domain([0, max]) + + // axes + vis.append("g") + .attr("class", "y axis") + .call(yAxis); + + vis.append("g") + .attr("class", "x axis") + .attr('transform', 'translate(0,' + h + ')') + .call(xAxis) + .selectAll('.x.axis g') + .style('display', function (d, i) { return i % 3 != 0 ? 'none' : 'block' }); + + // bars + var data = this.state.visitorData; + var bars = vis.selectAll('g.bar') + .data(data) + .enter().append('g') + .attr('class', 'bar') + .attr('transform', function (d, i) { return "translate(" + x(i) + ", 0)" }); + + bars.append('rect') + .attr('width', () => x.bandwidth()) + .attr('height', (d) => (h - y(d.Count)) ) + .attr('y', (d) => y(d.Count)) + .on('mouseover', tip.show) + .on('mouseout', tip.hide) } fetchData(period) { @@ -77,25 +102,25 @@ class Graph extends Component { window.requestAnimationFrame(this.refreshChart.bind(this)); }); - // fetch pageview data - fetch(`/api/pageviews/count/day?before=${before}&after=${after}`, { - credentials: 'include' - }).then((r) => { - if( r.ok ) { - return r.json(); - } - - throw new Error(); - }).then((data) => { - this.setState({ pageviewData: data }) - window.requestAnimationFrame(this.refreshChart.bind(this)); - }); + // // fetch pageview data + // fetch(`/api/pageviews/count/day?before=${before}&after=${after}`, { + // credentials: 'include' + // }).then((r) => { + // if( r.ok ) { + // return r.json(); + // } + // + // throw new Error(); + // }).then((data) => { + // this.setState({ pageviewData: data }) + // window.requestAnimationFrame(this.refreshChart.bind(this)); + // }); } render() { return (
- { this.canvas = el; }} /> +
) } diff --git a/assets/sass/_graphs.scss b/assets/sass/_graphs.scss new file mode 100644 index 0000000..aff6ca3 --- /dev/null +++ b/assets/sass/_graphs.scss @@ -0,0 +1,80 @@ +.d3-tip span { + color: #ff00c7; +} +.domain { + display: none; +} +.axis line { + stroke-width: 1px; + stroke: #eee; + shape-rendering: crispedges; +} +.axis text { + fill: #888; +} +rect { + fill: #339cff; + fill-opacity: 0.7; +} +rect:hover { + fill-opacity: 1; +} +.d3-tip { + line-height: 1; + font-weight: bold; + padding: 12px; + background: rgba(0, 0, 0, 0.8); + color: #fff; + border-radius: 2px; + pointer-events: none; +} + +/* Creates a small triangle extender for the tooltip */ +.d3-tip:after { + box-sizing: border-box; + display: inline; + font-size: 10px; + width: 100%; + line-height: 1; + color: rgba(0, 0, 0, 0.8); + position: absolute; + pointer-events: none; +} + +.d3-tip span { + color: lightgreen; +} + +/* Northward tooltips */ +.d3-tip.n:after { + content: "\25BC"; + margin: -1px 0 0 0; + top: 100%; + left: 0; + text-align: center; +} + +/* Eastward tooltips */ +.d3-tip.e:after { + content: "\25C0"; + margin: -4px 0 0 0; + top: 50%; + left: -8px; +} + +/* Southward tooltips */ +.d3-tip.s:after { + content: "\25B2"; + margin: 0 0 1px 0; + top: -8px; + left: 0; + text-align: center; +} + +/* Westward tooltips */ +.d3-tip.w:after { + content: "\25B6"; + margin: -4px 0 0 -1px; + top: 50%; + left: 100%; +} diff --git a/assets/sass/styles.scss b/assets/sass/styles.scss index da4b374..549094c 100644 --- a/assets/sass/styles.scss +++ b/assets/sass/styles.scss @@ -105,3 +105,5 @@ h4 { small { font-size: 70%; } + +@import "graphs" diff --git a/gulpfile.js b/gulpfile.js index 9cea5e0..075bab3 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -51,5 +51,5 @@ gulp.task('sass', function () { gulp.task('watch', ['default'], function() { gulp.watch(['./assets/js/**/*.js'], ['browserify', 'tracker'] ); - gulp.watch(['./assets/sass/**/*.scss'], ['sass'] ); + gulp.watch(['./assets/sass/**/**/*.scss'], ['sass'] ); }); diff --git a/package.json b/package.json index 42e68f9..7a67bf8 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,8 @@ { "devDependencies": { + "babel-plugin-transform-react-jsx": "^6.8.0", + "babel-preset-es2015": "^6.18.0", + "babelify": "^7.3.0", "browserify": "^13.1.1", "gulp": "^3.9.1", "gulp-rename": "^1.2.2", @@ -9,11 +12,8 @@ "vinyl-source-stream": "^1.1.0" }, "dependencies": { - "babel-plugin-transform-react-jsx": "^6.8.0", - "babel-preset-es2015": "^6.18.0", - "babelify": "^7.3.0", - "chart.js": "^2.4.0", - "mithril": "^0.2.5", + "d3": "^4.4.0", + "d3-tip": "^0.7.1", "preact": "^6.4.0" } } diff --git a/yarn.lock b/yarn.lock index a256fc2..b7f945e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -795,26 +795,6 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1: strip-ansi "^3.0.0" supports-color "^2.0.0" -chart.js: - version "2.4.0" - resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.4.0.tgz#44198073f0f43e5e16662e108420d92652a3c9a3" - dependencies: - chartjs-color "^2.0.0" - moment "^2.10.6" - -chartjs-color-string@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.4.0.tgz#57748d4530ae28d8db0a5492182ba06dfdf2f468" - dependencies: - color-name "^1.0.0" - -chartjs-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.0.0.tgz#7f60c7256589b24914814ece757659117381e35b" - dependencies: - chartjs-color-string "^0.4.0" - color-convert "^0.5.3" - cipher-base@^1.0.0, cipher-base@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" @@ -845,14 +825,6 @@ 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" -color-convert@^0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd" - -color-name@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" - combine-source-map@~0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e" @@ -868,7 +840,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@^2.9.0: +commander@^2.9.0, commander@2: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" dependencies: @@ -979,6 +951,222 @@ d@^0.1.1, d@~0.1.1: dependencies: es5-ext "~0.10.2" +d3, d3@^4.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/d3/-/d3-4.4.0.tgz#aec4ad0fd574304a909b012fac4783fbbcf9f096" + dependencies: + d3-array "1.0.2" + d3-axis "1.0.4" + d3-brush "1.0.3" + d3-chord "1.0.3" + d3-collection "1.0.2" + d3-color "1.0.2" + d3-dispatch "1.0.2" + d3-drag "1.0.2" + d3-dsv "1.0.3" + d3-ease "1.0.2" + d3-force "1.0.4" + d3-format "1.0.2" + d3-geo "1.4.0" + d3-hierarchy "1.0.3" + d3-interpolate "1.1.2" + d3-path "1.0.3" + d3-polygon "1.0.2" + d3-quadtree "1.0.2" + d3-queue "3.0.3" + d3-random "1.0.2" + d3-request "1.0.3" + d3-scale "1.0.4" + d3-selection "1.0.3" + d3-shape "1.0.4" + d3-time "1.0.4" + d3-time-format "2.0.3" + d3-timer "1.0.3" + d3-transition "1.0.3" + d3-voronoi "1.1.0" + d3-zoom "1.1.0" + +d3-array@1, d3-array@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.0.2.tgz#174237bf356a852fadd6af87743d928631de7655" + +d3-axis@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.4.tgz#bdfdcf5e859824062e0f17ad920f76236e72512c" + +d3-brush@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.0.3.tgz#4fa5374cc3b755d0990bf76b71b7a66417751c74" + dependencies: + d3-dispatch "1" + d3-drag "1" + d3-interpolate "1" + d3-selection "1" + d3-transition "1" + +d3-chord@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.3.tgz#a398bae7cb632a3c4ea687a555a6b9ee4609d990" + dependencies: + d3-array "1" + d3-path "1" + +d3-collection@1, d3-collection@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.2.tgz#df5acb5400443e9eabe9c1379896c67e52426b39" + +d3-color@1, d3-color@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.0.2.tgz#83cb4b3a9474e40795f009d97e97a15649830bbc" + +d3-dispatch@1, d3-dispatch@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.2.tgz#5b511e79a46a1f89492841c0a8f656687d5daa0a" + +d3-drag@1, d3-drag@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.0.2.tgz#d634cc3f7689f99dd03fd7eb1af2945c0f4339ad" + dependencies: + d3-dispatch "1" + d3-selection "1" + +d3-dsv@1, d3-dsv@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.0.3.tgz#049fe43c0f5f60c7ff7d376616bc76d6fc9d378f" + dependencies: + commander "2" + iconv-lite "0.4" + rw "1" + +d3-ease@1, d3-ease@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.2.tgz#b486f8f3ca308ca7be38197d65622b6e30983377" + +d3-force@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.0.4.tgz#f84dcbb3200be41de7bc30fa71923143156758bf" + dependencies: + d3-collection "1" + d3-dispatch "1" + d3-quadtree "1" + d3-timer "1" + +d3-format@1, d3-format@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.0.2.tgz#138618320b4bbeb43b5c0ff30519079fbbd7375e" + +d3-geo@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.4.0.tgz#15e58c414b5bafa1a960eeeb29059c94a60d8408" + dependencies: + d3-array "1" + +d3-hierarchy@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.0.3.tgz#986b4925e81f1e0b4087e9442850f950cf27d338" + +d3-interpolate@1, d3-interpolate@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.1.2.tgz#b52e6927a04fe1fe2a4cffc139e5389ed3e5e790" + dependencies: + d3-color "1" + +d3-path@1, d3-path@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.3.tgz#60103d0dea9a6cd6ca58de86c6d56724002d3fde" + +d3-polygon@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.2.tgz#6552c0fb03aa2d05023351da6e0e8adc4df0202b" + +d3-quadtree@1, d3-quadtree@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.2.tgz#e7e873af06aaa427eaa4af094cc4cbfb350b9e38" + +d3-queue@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/d3-queue/-/d3-queue-3.0.3.tgz#10ee4dd0574a1affaabfb931d0ba4f117926edc6" + +d3-random@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.0.2.tgz#83ff6a391206209c30565299e43c6549866db269" + +d3-request@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-request/-/d3-request-1.0.3.tgz#63fc7dfd784607db0df5d535d7cb898fceba755a" + dependencies: + d3-collection "1" + d3-dispatch "1" + d3-dsv "1" + xmlhttprequest "1" + +d3-scale@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-1.0.4.tgz#50e28bf6a193b706745528515ed9b3d44205a033" + dependencies: + d3-array "1" + d3-collection "1" + d3-color "1" + d3-format "1" + d3-interpolate "1" + d3-time "1" + d3-time-format "2" + +d3-selection@1, d3-selection@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.0.3.tgz#e63e51416172427854c1bcdfa066eb5fe872c108" + +d3-shape@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.0.4.tgz#145ee100ccbec42f8e3f1996cd05c786f79fe1c6" + dependencies: + d3-path "1" + +d3-time-format@2, d3-time-format@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.0.3.tgz#3241569b74ddc9c42e0689c0e8a903579fd6280a" + dependencies: + d3-time "1" + +d3-time@1, d3-time@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.4.tgz#2ceba09a76b7450c992a1ded4e10fc6195e69649" + +d3-timer@1, d3-timer@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.3.tgz#7a308a10c8524778e6b32d1d6c1c329209ae0ebf" + +d3-tip: + version "0.7.1" + resolved "https://registry.yarnpkg.com/d3-tip/-/d3-tip-0.7.1.tgz#78cbf554f67b720a70e3b0f191e14cffe68cdd79" + dependencies: + d3 "^4.2" + +d3-transition@1, d3-transition@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.0.3.tgz#91dc986bddb30973639320a85db72ce4ab1a27bb" + dependencies: + d3-color "1" + d3-dispatch "1" + d3-ease "1" + d3-interpolate "1" + d3-selection "1" + d3-timer "1" + +d3-voronoi@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.0.tgz#69ea59563cf1e75d1c6c45edc803c6583768f628" + +d3-zoom@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.1.0.tgz#29869cfd164928dd238aaa4365145741dcf1301f" + dependencies: + d3-dispatch "1" + d3-drag "1" + d3-interpolate "1" + d3-selection "1" + d3-transition "1" + dashdash@^1.12.0: version "1.14.0" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.0.tgz#29e486c5418bf0f356034a993d51686a33e84141" @@ -1630,6 +1818,10 @@ https-browserify@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" +iconv-lite@0.4: + version "0.4.15" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" + ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" @@ -2195,10 +2387,6 @@ minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -mithril@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/mithril/-/mithril-0.2.5.tgz#c1a50438a93ac23f11ada91188bb784c755404c2" - mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0": version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -2225,10 +2413,6 @@ module-deps@^4.0.8: through2 "^2.0.0" xtend "^4.0.0" -moment@^2.10.6: - version "2.17.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.0.tgz#a4c292e02aac5ddefb29a6eed24f51938dd3b74f" - ms@0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" @@ -2750,6 +2934,10 @@ ripemd160@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" +rw@1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.2.tgz#14ef5137ff7547c73ecf0e0af1f0aee07e5401ee" + sass-graph@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.1.2.tgz#965104be23e8103cb7e5f710df65935b317da57b" @@ -3165,6 +3353,10 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" +xmlhttprequest@1: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + xtend@^4.0.0, "xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"