diff --git a/client/About.js b/client/About.js index 9b8e883..ce0eea6 100644 --- a/client/About.js +++ b/client/About.js @@ -1,7 +1,8 @@ import React from 'react' import contentHTML from './About.md' -const About = () => -
+const About = () => ( +
+) export default About diff --git a/client/App.js b/client/App.js index 5b283af..982d6c3 100644 --- a/client/App.js +++ b/client/App.js @@ -2,9 +2,10 @@ import React from 'react' import { HashRouter } from 'react-router-dom' import Layout from './Layout' -const App = () => +const App = () => ( - + +) export default App diff --git a/client/Home.js b/client/Home.js index f99e466..3ecff7b 100644 --- a/client/Home.js +++ b/client/Home.js @@ -1,7 +1,8 @@ import React from 'react' import contentHTML from './Home.md' -const Home = () => -
+const Home = () => ( +
+) export default Home diff --git a/client/Layout.js b/client/Layout.js index 8a26a9c..455eb93 100644 --- a/client/Layout.js +++ b/client/Layout.js @@ -54,8 +54,7 @@ class Layout extends React.Component { if (window.localStorage) { const savedStats = window.localStorage.savedStats - if (savedStats) - this.setState({ stats: JSON.parse(savedStats) }) + if (savedStats) this.setState({ stats: JSON.parse(savedStats) }) window.onbeforeunload = () => { localStorage.savedStats = JSON.stringify(this.state.stats) @@ -72,21 +71,32 @@ class Layout extends React.Component { const { underlineLeft, underlineWidth, useSpring } = this.state const style = { - left: useSpring ? spring(underlineLeft, { stiffness: 220 }) : underlineLeft, + left: useSpring + ? spring(underlineLeft, { stiffness: 220 }) + : underlineLeft, width: useSpring ? spring(underlineWidth) : underlineWidth } return (
- +

unpkg

) diff --git a/client/NumberTextInput.js b/client/NumberTextInput.js index 9541d0e..8d63a46 100644 --- a/client/NumberTextInput.js +++ b/client/NumberTextInput.js @@ -24,12 +24,11 @@ class NumberTextInput extends React.Component { this.setState({ value: this.props.value }) } - handleChange = (event) => { + handleChange = event => { const value = this.props.parseNumber(event.target.value) this.setState({ value }, () => { - if (this.props.onChange) - this.props.onChange(value) + if (this.props.onChange) this.props.onChange(value) }) } @@ -39,7 +38,12 @@ class NumberTextInput extends React.Component { const displayValue = formatNumber(value) return ( - + ) } } diff --git a/client/Stats.js b/client/Stats.js index ad8c0f1..80f8ff9 100644 --- a/client/Stats.js +++ b/client/Stats.js @@ -8,13 +8,15 @@ import formatPercent from './utils/formatPercent' import { continents, countries } from 'countries-list' -const getCountriesByContinent = (continent) => - Object.keys(countries).filter(country => countries[country].continent === continent) +const getCountriesByContinent = continent => + Object.keys(countries).filter( + country => countries[country].continent === continent + ) const sumKeyValues = (hash, keys) => keys.reduce((n, key) => n + (hash[key] || 0), 0) -const sumValues = (hash) => +const sumValues = hash => Object.keys(hash).reduce((memo, key) => memo + hash[key], 0) class Stats extends React.Component { @@ -30,8 +32,7 @@ class Stats extends React.Component { render() { const { data } = this.props - if (data == null) - return null + if (data == null) return null const totals = data.totals @@ -42,25 +43,43 @@ class Stats extends React.Component { // Packages const packageRows = [] - Object.keys(totals.requests.package).sort((a, b) => { - return totals.requests.package[b] - totals.requests.package[a] - }).forEach(packageName => { - const requests = totals.requests.package[packageName] - const bandwidth = totals.bandwidth.package[packageName] + Object.keys(totals.requests.package) + .sort((a, b) => { + return totals.requests.package[b] - totals.requests.package[a] + }) + .forEach(packageName => { + const requests = totals.requests.package[packageName] + const bandwidth = totals.bandwidth.package[packageName] - if (requests >= this.state.minPackageRequests) { - packageRows.push( - - {packageName} - {formatNumber(requests)} ({formatPercent(requests / totals.requests.all)}%) - {bandwidth - ? {formatBytes(bandwidth)} ({formatPercent(bandwidth / totals.bandwidth.all)}%) - : - - } - - ) - } - }) + if (requests >= this.state.minPackageRequests) { + packageRows.push( + + + + {packageName} + + + + {formatNumber(requests)} ({formatPercent( + requests / totals.requests.all + )}%) + + {bandwidth ? ( + + {formatBytes(bandwidth)} ({formatPercent( + bandwidth / totals.bandwidth.all + )}%) + + ) : ( + - + )} + + ) + } + }) // Regions const regionRows = [] @@ -85,12 +104,23 @@ class Stats extends React.Component { const continentName = continents[continent] const continentData = continentsData[continent] - if (continentData.requests > this.state.minCountryRequests && continentData.bandwidth !== 0) { + if ( + continentData.requests > this.state.minCountryRequests && + continentData.bandwidth !== 0 + ) { regionRows.push( {continentName} - {formatNumber(continentData.requests)} ({formatPercent(continentData.requests / totals.requests.all)}%) - {formatBytes(continentData.bandwidth)} ({formatPercent(continentData.bandwidth / totals.bandwidth.all)}%) + + {formatNumber(continentData.requests)} ({formatPercent( + continentData.requests / totals.requests.all + )}%) + + + {formatBytes(continentData.bandwidth)} ({formatPercent( + continentData.bandwidth / totals.bandwidth.all + )}%) + ) @@ -106,8 +136,16 @@ class Stats extends React.Component { regionRows.push( {countries[country].name} - {formatNumber(countryRequests)} ({formatPercent(countryRequests / totals.requests.all)}%) - {formatBytes(countryBandwidth)} ({formatPercent(countryBandwidth / totals.bandwidth.all)}%) + + {formatNumber(countryRequests)} ({formatPercent( + countryRequests / totals.requests.all + )}%) + + + {formatBytes(countryBandwidth)} ({formatPercent( + countryBandwidth / totals.bandwidth.all + )}%) + ) } @@ -116,30 +154,58 @@ class Stats extends React.Component { }) // Protocols - const protocolRows = Object.keys(totals.requests.protocol).sort((a, b) => { - return totals.requests.protocol[b] - totals.requests.protocol[a] - }).map(protocol => { - const requests = totals.requests.protocol[protocol] + const protocolRows = Object.keys(totals.requests.protocol) + .sort((a, b) => { + return totals.requests.protocol[b] - totals.requests.protocol[a] + }) + .map(protocol => { + const requests = totals.requests.protocol[protocol] - return ( - - {protocol} - {formatNumber(requests)} ({formatPercent(requests / sumValues(totals.requests.protocol))}%) - - ) - }) + return ( + + {protocol} + + {formatNumber(requests)} ({formatPercent( + requests / sumValues(totals.requests.protocol) + )}%) + + + ) + }) return (
-

From {formatDate(since, 'MMM D')} to {formatDate(until, 'MMM D')} unpkg served {formatNumber(totals.requests.all)} requests and a total of {formatBytes(totals.bandwidth.all)} of data to {formatNumber(totals.uniques.all)} unique visitors, {formatPercent(totals.requests.cached / totals.requests.all, 0)}% of which were served from the cache.

+

+ From {formatDate(since, 'MMM D')} to{' '} + {formatDate(until, 'MMM D')} unpkg served{' '} + {formatNumber(totals.requests.all)} requests and a + total of {formatBytes(totals.bandwidth.all)} of data + to {formatNumber(totals.uniques.all)} unique + visitors,{' '} + + {formatPercent(totals.requests.cached / totals.requests.all, 0)}% + {' '} + of which were served from the cache. +

Packages

-

The table below shows the most popular packages served by unpkg from {formatDate(since, 'MMM D')} to {formatDate(until, 'MMM D')}. Only the top {Object.keys(totals.requests.package).length} packages are shown.

+

+ The table below shows the most popular packages served by unpkg from{' '} + {formatDate(since, 'MMM D')} to{' '} + {formatDate(until, 'MMM D')}. Only the top{' '} + {Object.keys(totals.requests.package).length} packages are shown. +

-

Include only packages that received at least this.setState({ minPackageRequests: parseInt(event.target.value, 10) })} + onChange={event => + this.setState({ + minPackageRequests: parseInt(event.target.value, 10) + }) + } > @@ -147,7 +213,8 @@ class Stats extends React.Component { - requests. + {' '} + requests.

@@ -158,28 +225,42 @@ class Stats extends React.Component { - - {packageRows} - + {packageRows}
Bandwidth (% of total)

Regions

-

The table below breaks down requests to unpkg from {formatDate(since, 'MMM D')} to {formatDate(until, 'MMM D')} by geographic region.

+

+ The table below breaks down requests to unpkg from{' '} + {formatDate(since, 'MMM D')} to{' '} + {formatDate(until, 'MMM D')} by geographic region. +

-

Include only countries that made at least this.setState({ minCountryRequests: parseInt(event.target.value, 10) })} + onChange={event => + this.setState({ + minCountryRequests: parseInt(event.target.value, 10) + }) + } > - requests. + {' '} + requests.

- +
@@ -187,14 +268,16 @@ class Stats extends React.Component { - - {regionRows} - + {regionRows}
RegionBandwidth (% of total)

Protocols

-

The table below breaks down requests to unpkg from {formatDate(since, 'MMM D')} to {formatDate(until, 'MMM D')} by HTTP protocol.

+

+ The table below breaks down requests to unpkg from{' '} + {formatDate(since, 'MMM D')} to{' '} + {formatDate(until, 'MMM D')} by HTTP protocol. +

@@ -203,11 +286,8 @@ class Stats extends React.Component { - - {protocolRows} - + {protocolRows}
Requests (% of total)
-
) } diff --git a/client/index.js b/client/index.js index f66d1fc..fde0d67 100644 --- a/client/index.js +++ b/client/index.js @@ -3,7 +3,4 @@ import ReactDOM from 'react-dom' import App from './App' import './index.css' -ReactDOM.render( - , - document.getElementById('root') -) +ReactDOM.render(, document.getElementById('root')) diff --git a/client/utils/formatNumber.js b/client/utils/formatNumber.js index 6c69ad3..9b20345 100644 --- a/client/utils/formatNumber.js +++ b/client/utils/formatNumber.js @@ -1,9 +1,8 @@ -const formatNumber = (n) => { +const formatNumber = n => { const digits = String(n).split('') const groups = [] - while (digits.length) - groups.unshift(digits.splice(-3).join('')) + while (digits.length) groups.unshift(digits.splice(-3).join('')) return groups.join(',') } diff --git a/client/utils/parseNumber.js b/client/utils/parseNumber.js index 089bda7..fb0a9ea 100644 --- a/client/utils/parseNumber.js +++ b/client/utils/parseNumber.js @@ -1,4 +1,3 @@ -const parseNumber = (s) => - parseInt(s.replace(/,/g, ''), 10) || 0 +const parseNumber = s => parseInt(s.replace(/,/g, ''), 10) || 0 export default parseNumber diff --git a/package.json b/package.json index 5802734..e7e271b 100644 --- a/package.json +++ b/package.json @@ -114,5 +114,9 @@ }, "eslintConfig": { "extends": "react-app" + }, + "prettier": { + "semi": false, + "single-quote": true } } diff --git a/server/CloudflareAPI.js b/server/CloudflareAPI.js index d011922..8da56f9 100644 --- a/server/CloudflareAPI.js +++ b/server/CloudflareAPI.js @@ -7,15 +7,9 @@ const CloudflareAPIURL = 'https://api.cloudflare.com' const CloudflareEmail = process.env.CLOUDFLARE_EMAIL const CloudflareKey = process.env.CLOUDFLARE_KEY -invariant( - CloudflareEmail, - 'Missing the $CLOUDFLARE_EMAIL environment variable' -) +invariant(CloudflareEmail, 'Missing the $CLOUDFLARE_EMAIL environment variable') -invariant( - CloudflareKey, - 'Missing the $CLOUDFLARE_KEY environment variable' -) +invariant(CloudflareKey, 'Missing the $CLOUDFLARE_KEY environment variable') function get(path, headers) { return fetch(`${CloudflareAPIURL}/client/v4${path}`, { @@ -27,26 +21,28 @@ function get(path, headers) { } function getJSON(path, headers) { - return get(path, headers).then(function (res) { - return res.json() - }).then(function (data) { - if (!data.success) { - console.error(`CloudflareAPI.getJSON failed at ${path}`) - console.error(data) - throw new Error('Failed to getJSON from Cloudflare') - } + return get(path, headers) + .then(function(res) { + return res.json() + }) + .then(function(data) { + if (!data.success) { + console.error(`CloudflareAPI.getJSON failed at ${path}`) + console.error(data) + throw new Error('Failed to getJSON from Cloudflare') + } - return data.result - }) + return data.result + }) } function getZones(domains) { return Promise.all( - (Array.isArray(domains) ? domains : [ domains ]).map(function (domain) { + (Array.isArray(domains) ? domains : [domains]).map(function(domain) { return getJSON(`/zones?name=${domain}`) }) - ).then(function (results) { - return results.reduce(function (memo, zones) { + ).then(function(results) { + return results.reduce(function(memo, zones) { return memo.concat(zones) }) }) @@ -68,26 +64,36 @@ function reduceResults(target, values) { function getZoneAnalyticsDashboard(zones, since, until) { return Promise.all( - (Array.isArray(zones) ? zones : [ zones ]).map(function (zone) { - return getJSON(`/zones/${zone.id}/analytics/dashboard?since=${since.toISOString()}&until=${until.toISOString()}`) + (Array.isArray(zones) ? zones : [zones]).map(function(zone) { + return getJSON( + `/zones/${ + zone.id + }/analytics/dashboard?since=${since.toISOString()}&until=${until.toISOString()}` + ) }) - ).then(function (results) { + ).then(function(results) { return results.reduce(reduceResults) }) } function getJSONStream(path, headers) { - const acceptGzipHeaders = Object.assign({}, headers, { 'Accept-Encoding': 'gzip' }) - - return get(path, acceptGzipHeaders).then(function (res) { - return res.body.pipe(gunzip()) - }).then(function (stream) { - return stream.pipe(ndjson.parse()) + const acceptGzipHeaders = Object.assign({}, headers, { + 'Accept-Encoding': 'gzip' }) + + return get(path, acceptGzipHeaders) + .then(function(res) { + return res.body.pipe(gunzip()) + }) + .then(function(stream) { + return stream.pipe(ndjson.parse()) + }) } function getLogs(zoneId, startTime, endTime) { - return getJSONStream(`/zones/${zoneId}/logs/requests?start=${startTime}&end=${endTime}`) + return getJSONStream( + `/zones/${zoneId}/logs/requests?start=${startTime}&end=${endTime}` + ) } module.exports = { diff --git a/server/RedisClient.js b/server/RedisClient.js index d725bd2..36a4d13 100644 --- a/server/RedisClient.js +++ b/server/RedisClient.js @@ -2,7 +2,8 @@ const redis = require('redis') redis.debug_mode = process.env.DEBUG_REDIS != null -const RedisURL = process.env.OPENREDIS_URL || process.env.REDIS_URL || 'redis://localhost:6379' +const RedisURL = + process.env.OPENREDIS_URL || process.env.REDIS_URL || 'redis://localhost:6379' const client = redis.createClient(RedisURL) diff --git a/server/StatsServer.js b/server/StatsServer.js index fbfb35d..2712a2c 100644 --- a/server/StatsServer.js +++ b/server/StatsServer.js @@ -4,7 +4,7 @@ const db = require('./RedisClient') const PackageBlacklist = require('./PackageBlacklist').blacklist function prunePackages(packagesMap) { - PackageBlacklist.forEach(function (packageName) { + PackageBlacklist.forEach(function(packageName) { delete packagesMap[packageName] }) @@ -33,8 +33,8 @@ function createScoresMap(array) { } function getScoresMap(key, n = 100) { - return new Promise(function (resolve, reject) { - db.zrevrange(key, 0, n, 'withscores', function (error, value) { + return new Promise(function(resolve, reject) { + db.zrevrange(key, 0, n, 'withscores', function(error, value) { if (error) { reject(error) } else { @@ -45,11 +45,15 @@ function getScoresMap(key, n = 100) { } function getPackageRequests(date, n = 100) { - return getScoresMap(`stats-packageRequests-${createDayKey(date)}`, n).then(prunePackages) + return getScoresMap(`stats-packageRequests-${createDayKey(date)}`, n).then( + prunePackages + ) } function getPackageBandwidth(date, n = 100) { - return getScoresMap(`stats-packageBytes-${createDayKey(date)}`, n).then(prunePackages) + return getScoresMap(`stats-packageBytes-${createDayKey(date)}`, n).then( + prunePackages + ) } function getProtocolRequests(date) { @@ -63,7 +67,7 @@ function addDailyMetricsToTimeseries(timeseries) { getPackageRequests(since), getPackageBandwidth(since), getProtocolRequests(since) - ]).then(function (results) { + ]).then(function(results) { timeseries.requests.package = results[0] timeseries.bandwidth.package = results[1] timeseries.requests.protocol = results[2] @@ -72,8 +76,8 @@ function addDailyMetricsToTimeseries(timeseries) { } function sumMaps(maps) { - return maps.reduce(function (memo, map) { - Object.keys(map).forEach(function (key) { + return maps.reduce(function(memo, map) { + Object.keys(map).forEach(function(key) { memo[key] = (memo[key] || 0) + map[key] }) @@ -82,29 +86,29 @@ function sumMaps(maps) { } function addDailyMetrics(result) { - return Promise.all( - result.timeseries.map(addDailyMetricsToTimeseries) - ).then(function () { - result.totals.requests.package = sumMaps( - result.timeseries.map(function (timeseries) { - return timeseries.requests.package - }) - ) + return Promise.all(result.timeseries.map(addDailyMetricsToTimeseries)).then( + function() { + result.totals.requests.package = sumMaps( + result.timeseries.map(function(timeseries) { + return timeseries.requests.package + }) + ) - result.totals.bandwidth.package = sumMaps( - result.timeseries.map(function (timeseries) { - return timeseries.bandwidth.package - }) - ) + result.totals.bandwidth.package = sumMaps( + result.timeseries.map(function(timeseries) { + return timeseries.bandwidth.package + }) + ) - result.totals.requests.protocol = sumMaps( - result.timeseries.map(function (timeseries) { - return timeseries.requests.protocol - }) - ) + result.totals.requests.protocol = sumMaps( + result.timeseries.map(function(timeseries) { + return timeseries.requests.protocol + }) + ) - return result - }) + return result + } + ) } function extractPublicInfo(data) { @@ -136,19 +140,18 @@ function extractPublicInfo(data) { } } -const DomainNames = [ - 'unpkg.com', - 'npmcdn.com' -] +const DomainNames = ['unpkg.com', 'npmcdn.com'] function fetchStats(since, until) { - return cf.getZones(DomainNames).then(function (zones) { - return cf.getZoneAnalyticsDashboard(zones, since, until).then(function (dashboard) { - return { - timeseries: dashboard.timeseries.map(extractPublicInfo), - totals: extractPublicInfo(dashboard.totals) - } - }) + return cf.getZones(DomainNames).then(function(zones) { + return cf + .getZoneAnalyticsDashboard(zones, since, until) + .then(function(dashboard) { + return { + timeseries: dashboard.timeseries.map(extractPublicInfo), + totals: extractPublicInfo(dashboard.totals) + } + }) }) } @@ -159,10 +162,9 @@ const oneDay = oneHour * 24 function getStats(since, until, callback) { let promise = fetchStats(since, until) - if ((until - since) > oneDay) - promise = promise.then(addDailyMetrics) + if (until - since > oneDay) promise = promise.then(addDailyMetrics) - promise.then(function (value) { + promise.then(function(value) { callback(null, value) }, callback) } diff --git a/server/createSearchServer.js b/server/createSearchServer.js index 96edf66..458704f 100644 --- a/server/createSearchServer.js +++ b/server/createSearchServer.js @@ -4,18 +4,23 @@ const npmSearch = require('./npm/search') function createSearchServer() { const app = express() - app.get('/', function (req, res) { + app.get('/', function(req, res) { const { query, page = 0 } = req.query if (!query) return res.status(403).send({ error: 'Missing ?query parameter' }) - npmSearch(query, page).then(function (result) { - res.send(result) - }, function (error) { - console.error(error) - res.status(500).send({ error: 'There was an error executing the search' }) - }) + npmSearch(query, page).then( + function(result) { + res.send(result) + }, + function(error) { + console.error(error) + res + .status(500) + .send({ error: 'There was an error executing the search' }) + } + ) }) return app diff --git a/server/createServer.test.js b/server/createServer.test.js index f565b85..bc25306 100644 --- a/server/createServer.test.js +++ b/server/createServer.test.js @@ -1,32 +1,38 @@ const request = require('supertest') const createServer = require('./createServer') -describe('The server app', function () { +describe('The server app', function() { let app - beforeEach(function () { + beforeEach(function() { app = createServer() }) - it('rejects invalid package names', function (done) { - request(app).get('/_invalid/index.js').then(function (res) { - expect(res.statusCode).toBe(403) - done() - }) + it('rejects invalid package names', function(done) { + request(app) + .get('/_invalid/index.js') + .then(function(res) { + expect(res.statusCode).toBe(403) + done() + }) }) - it('redirects invalid query params', function (done) { - request(app).get('/react?main=index&invalid').then(function (res) { - expect(res.statusCode).toBe(302) - expect(res.headers.location).toBe('/react?main=index') - done() - }) + it('redirects invalid query params', function(done) { + request(app) + .get('/react?main=index&invalid') + .then(function(res) { + expect(res.statusCode).toBe(302) + expect(res.headers.location).toBe('/react?main=index') + done() + }) }) - it('redirects /_meta to ?meta', function (done) { - request(app).get('/_meta/react?main=index').then(function (res) { - expect(res.statusCode).toBe(302) - expect(res.headers.location).toBe('/react?main=index&meta') - done() - }) + it('redirects /_meta to ?meta', function(done) { + request(app) + .get('/_meta/react?main=index') + .then(function(res) { + expect(res.statusCode).toBe(302) + expect(res.headers.location).toBe('/react?main=index&meta') + done() + }) }) }) diff --git a/server/createStatsServer.js b/server/createStatsServer.js index c4a6df8..dc00a0e 100644 --- a/server/createStatsServer.js +++ b/server/createStatsServer.js @@ -5,7 +5,7 @@ const startOfSecond = require('date-fns/start_of_second') const StatsServer = require('./StatsServer') function serveArbitraryStats(req, res) { - const now = startOfSecond(new Date) + const now = startOfSecond(new Date()) const since = req.query.since ? new Date(req.query.since) : subDays(now, 30) const until = req.query.until ? new Date(req.query.until) : now @@ -16,37 +16,43 @@ function serveArbitraryStats(req, res) { return res.status(403).send({ error: '?until is not a valid date' }) if (until <= since) - return res.status(403).send({ error: '?until date must come after ?since date' }) + return res + .status(403) + .send({ error: '?until date must come after ?since date' }) if (until > now) return res.status(403).send({ error: '?until must be a date in the past' }) - StatsServer.getStats(since, until, function (error, stats) { + StatsServer.getStats(since, until, function(error, stats) { if (error) { console.error(error) res.status(500).send({ error: 'Unable to fetch stats' }) } else { - res.set({ - 'Cache-Control': 'public, max-age=60', - 'Cache-Tag': 'stats' - }).send(stats) + res + .set({ + 'Cache-Control': 'public, max-age=60', + 'Cache-Tag': 'stats' + }) + .send(stats) } }) } function servePastDaysStats(days, req, res) { - const until = startOfDay(new Date) + const until = startOfDay(new Date()) const since = subDays(until, days) - StatsServer.getStats(since, until, function (error, stats) { + StatsServer.getStats(since, until, function(error, stats) { if (error) { console.error(error) res.status(500).send({ error: 'Unable to fetch stats' }) } else { - res.set({ - 'Cache-Control': 'public, max-age=60', - 'Cache-Tag': 'stats' - }).send(stats) + res + .set({ + 'Cache-Control': 'public, max-age=60', + 'Cache-Tag': 'stats' + }) + .send(stats) } }) } diff --git a/server/ingestLogs.js b/server/ingestLogs.js index 600c5be..1697230 100644 --- a/server/ingestLogs.js +++ b/server/ingestLogs.js @@ -37,7 +37,7 @@ const oneMinute = oneSecond * 60 const oneHour = oneMinute * 60 function computeCounters(stream) { - return new Promise(function (resolve, reject) { + return new Promise(function(resolve, reject) { const counters = {} const expireat = {} @@ -49,7 +49,7 @@ function computeCounters(stream) { stream .on('error', reject) - .on('data', function (entry) { + .on('data', function(entry) { const date = new Date(Math.round(entry.timestamp / 1000000)) const nextDay = startOfDay(addDays(date, 1)) @@ -66,9 +66,22 @@ function computeCounters(stream) { const url = parsePackageURL(parseURL(clientRequest.uri).pathname) const packageName = url && url.packageName - if (packageName && validateNPMPackageName(packageName).errors == null) { - incr(`stats-packageRequests-${dayKey}`, packageName, 1, thirtyDaysLater) - incr(`stats-packageBytes-${dayKey}`, packageName, edgeResponse.bytes, thirtyDaysLater) + if ( + packageName && + validateNPMPackageName(packageName).errors == null + ) { + incr( + `stats-packageRequests-${dayKey}`, + packageName, + 1, + thirtyDaysLater + ) + incr( + `stats-packageBytes-${dayKey}`, + packageName, + edgeResponse.bytes, + thirtyDaysLater + ) } } @@ -85,32 +98,36 @@ function computeCounters(stream) { if (hostname) { incr(`stats-hostnameRequests-${dayKey}`, hostname, 1, sevenDaysLater) - incr(`stats-hostnameBytes-${dayKey}`, hostname, edgeResponse.bytes, sevenDaysLater) + incr( + `stats-hostnameBytes-${dayKey}`, + hostname, + edgeResponse.bytes, + sevenDaysLater + ) } }) - .on('end', function () { + .on('end', function() { resolve({ counters, expireat }) }) }) } function processLogs(stream) { - return computeCounters(stream).then(function ({ counters, expireat }) { - Object.keys(counters).forEach(function (key) { + return computeCounters(stream).then(function({ counters, expireat }) { + Object.keys(counters).forEach(function(key) { const values = counters[key] - Object.keys(values).forEach(function (member) { + Object.keys(values).forEach(function(member) { db.zincrby(key, values[member], member) }) - if (expireat[key]) - db.expireat(key, expireat[key]) + if (expireat[key]) db.expireat(key, expireat[key]) }) }) } function ingestLogs(zone, startSeconds, endSeconds) { - return new Promise(function (resolve) { + return new Promise(function(resolve) { console.log( 'info: Started ingesting logs for %s from %s to %s', zone.name, @@ -121,7 +138,7 @@ function ingestLogs(zone, startSeconds, endSeconds) { const startFetchTime = Date.now() resolve( - cf.getLogs(zone.id, startSeconds, endSeconds).then(function (stream) { + cf.getLogs(zone.id, startSeconds, endSeconds).then(function(stream) { const endFetchTime = Date.now() console.log( @@ -133,7 +150,7 @@ function ingestLogs(zone, startSeconds, endSeconds) { const startProcessTime = Date.now() - return processLogs(stream).then(function () { + return processLogs(stream).then(function() { const endProcessTime = Date.now() console.log( @@ -149,10 +166,13 @@ function ingestLogs(zone, startSeconds, endSeconds) { } function startZone(zone) { - const startSecondsKey = `ingestLogsWorker-nextStartSeconds-${zone.name.replace('.', '-')}` + const startSecondsKey = `ingestLogsWorker-nextStartSeconds-${zone.name.replace( + '.', + '-' + )}` function takeATurn() { - db.get(startSecondsKey, function (error, value) { + db.get(startSecondsKey, function(error, value) { let startSeconds = value && parseInt(value, 10) const now = Date.now() @@ -182,18 +202,21 @@ function startZone(zone) { // set of logs. This will help ensure that any congestion in the log // pipeline has passed and a full set of logs can be ingested. // https://support.cloudflare.com/hc/en-us/articles/216672448-Enterprise-Log-Share-REST-API - const maxSeconds = toSeconds(now - (oneMinute * 30)) + const maxSeconds = toSeconds(now - oneMinute * 30) if (startSeconds < maxSeconds) { const endSeconds = startSeconds + LogWindowSeconds - ingestLogs(zone, startSeconds, endSeconds).then(function () { - db.set(startSecondsKey, endSeconds) - setTimeout(takeATurn) - }, function (error) { - console.error(error.stack) - process.exit(1) - }) + ingestLogs(zone, startSeconds, endSeconds).then( + function() { + db.set(startSecondsKey, endSeconds) + setTimeout(takeATurn) + }, + function(error) { + console.error(error.stack) + process.exit(1) + } + ) } else { setTimeout(takeATurn, (startSeconds - maxSeconds) * 1000) } @@ -203,8 +226,8 @@ function startZone(zone) { takeATurn() } -Promise.all(DomainNames.map(cf.getZones)).then(function (results) { - const zones = results.reduce(function (memo, zones) { +Promise.all(DomainNames.map(cf.getZones)).then(function(results) { + const zones = results.reduce(function(memo, zones) { return memo.concat(zones) }) diff --git a/server/middleware/checkBlacklist.js b/server/middleware/checkBlacklist.js index 07b09a5..0f652ec 100644 --- a/server/middleware/checkBlacklist.js +++ b/server/middleware/checkBlacklist.js @@ -1,8 +1,11 @@ function checkBlacklist(blacklist) { - return function (req, res, next) { + return function(req, res, next) { // Do not allow packages that have been blacklisted. if (blacklist.includes(req.packageName)) { - res.status(403).type('text').send(`Package "${req.packageName}" is blacklisted`) + res + .status(403) + .type('text') + .send(`Package "${req.packageName}" is blacklisted`) } else { next() } diff --git a/server/middleware/components/DirectoryListing.js b/server/middleware/components/DirectoryListing.js index 3e8be5f..6aa0bcc 100644 --- a/server/middleware/components/DirectoryListing.js +++ b/server/middleware/components/DirectoryListing.js @@ -4,27 +4,28 @@ const getFileContentType = require('../utils/getFileContentType') const e = React.createElement -const formatTime = (time) => - new Date(time).toISOString() +const formatTime = time => new Date(time).toISOString() const DirectoryListing = ({ dir, entries }) => { const rows = entries.map(({ file, stats }, index) => { const isDir = stats.isDirectory() const href = file + (isDir ? '/' : '') - return ( - e('tr', { key: file, className: index % 2 ? 'odd' : 'even' }, - e('td', null, e('a', { title: file, href }, file)), - e('td', null, isDir ? '-' : getFileContentType(file)), - e('td', null, isDir ? '-' : prettyBytes(stats.size)), - e('td', null, isDir ? '-' : formatTime(stats.mtime)) - ) + return e( + 'tr', + { key: file, className: index % 2 ? 'odd' : 'even' }, + e('td', null, e('a', { title: file, href }, file)), + e('td', null, isDir ? '-' : getFileContentType(file)), + e('td', null, isDir ? '-' : prettyBytes(stats.size)), + e('td', null, isDir ? '-' : formatTime(stats.mtime)) ) }) if (dir !== '/') rows.unshift( - e('tr', { key: '..', className: 'odd' }, + e( + 'tr', + { key: '..', className: 'odd' }, e('td', null, e('a', { title: 'Parent directory', href: '../' }, '..')), e('td', null, '-'), e('td', null, '-'), @@ -32,18 +33,22 @@ const DirectoryListing = ({ dir, entries }) => { ) ) - return ( - e('table', null, - e('thead', null, - e('tr', null, - e('th', null, 'Name'), - e('th', null, 'Type'), - e('th', null, 'Size'), - e('th', null, 'Last Modified') - ) - ), - e('tbody', null, rows) - ) + return e( + 'table', + null, + e( + 'thead', + null, + e( + 'tr', + null, + e('th', null, 'Name'), + e('th', null, 'Type'), + e('th', null, 'Size'), + e('th', null, 'Last Modified') + ) + ), + e('tbody', null, rows) ) } diff --git a/server/middleware/components/IndexPage.js b/server/middleware/components/IndexPage.js index b4e830e..4f5654b 100644 --- a/server/middleware/components/IndexPage.js +++ b/server/middleware/components/IndexPage.js @@ -13,34 +13,41 @@ s.onchange = function () { } ` -const byVersion = (a, b) => - semver.lt(a, b) ? -1 : (semver.gt(a, b) ? 1 : 0) +const byVersion = (a, b) => (semver.lt(a, b) ? -1 : semver.gt(a, b) ? 1 : 0) const IndexPage = ({ packageInfo, version, dir, entries }) => { const versions = Object.keys(packageInfo.versions).sort(byVersion) - const options = versions.map(v => ( + const options = versions.map(v => e('option', { key: v, value: v }, `${packageInfo.name}@${v}`) - )) + ) - return ( - e('html', null, - e('head', null, - e('meta', { charSet: 'utf-8' }), - e('title', null, `Index of ${dir}`), - e('style', { dangerouslySetInnerHTML: { __html: IndexPageStyle } }) - ), - e('body', null, - e('div', { className: 'content-wrapper' }, - e('div', { className: 'version-wrapper' }, - e('select', { id: 'version', defaultValue: version }, options) - ), - e('h1', null, `Index of ${dir}`), - e('script', { dangerouslySetInnerHTML: { __html: IndexPageScript } }), - e('hr'), - e(DirectoryListing, { dir, entries }), - e('hr'), - e('address', null, `${packageInfo.name}@${version}`) - ) + return e( + 'html', + null, + e( + 'head', + null, + e('meta', { charSet: 'utf-8' }), + e('title', null, `Index of ${dir}`), + e('style', { dangerouslySetInnerHTML: { __html: IndexPageStyle } }) + ), + e( + 'body', + null, + e( + 'div', + { className: 'content-wrapper' }, + e( + 'div', + { className: 'version-wrapper' }, + e('select', { id: 'version', defaultValue: version }, options) + ), + e('h1', null, `Index of ${dir}`), + e('script', { dangerouslySetInnerHTML: { __html: IndexPageScript } }), + e('hr'), + e(DirectoryListing, { dir, entries }), + e('hr'), + e('address', null, `${packageInfo.name}@${version}`) ) ) ) diff --git a/server/middleware/fetchFile.js b/server/middleware/fetchFile.js index 14e30f9..8077bff 100644 --- a/server/middleware/fetchFile.js +++ b/server/middleware/fetchFile.js @@ -13,18 +13,18 @@ function getBasename(file) { /** * File extensions to look for when automatically resolving. */ -const FindExtensions = [ '', '.js', '.json' ] +const FindExtensions = ['', '.js', '.json'] /** * Resolves a path like "lib/file" into "lib/file.js" or "lib/file.json" * depending on which one is available, similar to require('lib/file'). */ function findFile(base, useIndex, callback) { - FindExtensions.reduceRight(function (next, ext) { + FindExtensions.reduceRight(function(next, ext) { const file = base + ext - return function () { - fs.stat(file, function (error, stats) { + return function() { + fs.stat(file, function(error, stats) { if (error) { if (error.code === 'ENOENT' || error.code === 'ENOTDIR') { next() @@ -32,7 +32,11 @@ function findFile(base, useIndex, callback) { callback(error) } } else if (useIndex && stats.isDirectory()) { - findFile(path.join(file, 'index'), false, function (error, indexFile, indexStats) { + findFile(path.join(file, 'index'), false, function( + error, + indexFile, + indexStats + ) { if (error) { callback(error) } else if (indexFile) { @@ -55,14 +59,20 @@ function findFile(base, useIndex, callback) { * trailing slash. */ function fetchFile(req, res, next) { - getPackageInfo(req.packageName, function (error, packageInfo) { + getPackageInfo(req.packageName, function(error, packageInfo) { if (error) { console.error(error) - return res.status(500).type('text').send(`Cannot get info for package "${req.packageName}"`) + return res + .status(500) + .type('text') + .send(`Cannot get info for package "${req.packageName}"`) } if (packageInfo == null || packageInfo.versions == null) - return res.status(404).type('text').send(`Cannot find package "${req.packageName}"`) + return res + .status(404) + .type('text') + .send(`Cannot find package "${req.packageName}"`) req.packageInfo = packageInfo @@ -70,10 +80,13 @@ function fetchFile(req, res, next) { // A valid request for a package we haven't downloaded yet. req.packageConfig = req.packageInfo.versions[req.packageVersion] - getPackage(req.packageConfig, function (error, outputDir) { + getPackage(req.packageConfig, function(error, outputDir) { if (error) { console.error(error) - res.status(500).type('text').send(`Cannot fetch package ${req.packageSpec}`) + res + .status(500) + .type('text') + .send(`Cannot fetch package ${req.packageSpec}`) } else { req.packageDir = outputDir @@ -84,12 +97,18 @@ function fetchFile(req, res, next) { // They want an ES module. Try "module", "jsnext:main", and "/" // https://github.com/rollup/rollup/wiki/pkg.module if (!filename) - filename = req.packageConfig.module || req.packageConfig['jsnext:main'] || '/' + filename = + req.packageConfig.module || + req.packageConfig['jsnext:main'] || + '/' } else if (filename) { // They are requesting an explicit filename. Only try to find an // index file if they are NOT requesting an HTML directory listing. useIndex = filename[filename.length - 1] !== '/' - } else if (req.query.main && typeof req.packageConfig[req.query.main] === 'string') { + } else if ( + req.query.main && + typeof req.packageConfig[req.query.main] === 'string' + ) { // They specified a custom ?main field. filename = req.packageConfig[req.query.main] } else if (typeof req.packageConfig.unpkg === 'string') { @@ -104,23 +123,46 @@ function fetchFile(req, res, next) { filename = req.packageConfig.main || '/' } - findFile(path.join(req.packageDir, filename), useIndex, function (error, file, stats) { - if (error) - console.error(error) + findFile(path.join(req.packageDir, filename), useIndex, function( + error, + file, + stats + ) { + if (error) console.error(error) if (file == null) - return res.status(404).type('text').send(`Cannot find module "${filename}" in package ${req.packageSpec}`) + return res + .status(404) + .type('text') + .send( + `Cannot find module "${filename}" in package ${ + req.packageSpec + }` + ) filename = file.replace(req.packageDir, '') - if (req.query.main != null || getBasename(req.filename) !== getBasename(filename)) { + if ( + req.query.main != null || + getBasename(req.filename) !== getBasename(filename) + ) { // Need to redirect to the module file so relative imports resolve // correctly. Cache module redirects for 1 minute. delete req.query.main - res.set({ - 'Cache-Control': 'public, max-age=60', - 'Cache-Tag': 'redirect,module-redirect' - }).redirect(302, createPackageURL(req.packageName, req.packageVersion, filename, createSearch(req.query))) + res + .set({ + 'Cache-Control': 'public, max-age=60', + 'Cache-Tag': 'redirect,module-redirect' + }) + .redirect( + 302, + createPackageURL( + req.packageName, + req.packageVersion, + filename, + createSearch(req.query) + ) + ) } else { req.filename = filename req.stats = stats @@ -131,21 +173,47 @@ function fetchFile(req, res, next) { }) } else if (req.packageVersion in req.packageInfo['dist-tags']) { // Cache tag redirects for 1 minute. - res.set({ - 'Cache-Control': 'public, max-age=60', - 'Cache-Tag': 'redirect,tag-redirect' - }).redirect(302, createPackageURL(req.packageName, req.packageInfo['dist-tags'][req.packageVersion], req.filename, req.search)) + res + .set({ + 'Cache-Control': 'public, max-age=60', + 'Cache-Tag': 'redirect,tag-redirect' + }) + .redirect( + 302, + createPackageURL( + req.packageName, + req.packageInfo['dist-tags'][req.packageVersion], + req.filename, + req.search + ) + ) } else { - const maxVersion = semver.maxSatisfying(Object.keys(req.packageInfo.versions), req.packageVersion) + const maxVersion = semver.maxSatisfying( + Object.keys(req.packageInfo.versions), + req.packageVersion + ) if (maxVersion) { // Cache semver redirects for 1 minute. - res.set({ - 'Cache-Control': 'public, max-age=60', - 'Cache-Tag': 'redirect,semver-redirect' - }).redirect(302, createPackageURL(req.packageName, maxVersion, req.filename, req.search)) + res + .set({ + 'Cache-Control': 'public, max-age=60', + 'Cache-Tag': 'redirect,semver-redirect' + }) + .redirect( + 302, + createPackageURL( + req.packageName, + maxVersion, + req.filename, + req.search + ) + ) } else { - res.status(404).type('text').send(`Cannot find package ${req.packageSpec}`) + res + .status(404) + .type('text') + .send(`Cannot find package ${req.packageSpec}`) } } }) diff --git a/server/middleware/packageURL.js b/server/middleware/packageURL.js index ae8f191..1ee7e2f 100644 --- a/server/middleware/packageURL.js +++ b/server/middleware/packageURL.js @@ -19,9 +19,8 @@ function queryIsKnown(query) { function sanitizeQuery(query) { const saneQuery = {} - Object.keys(query).forEach(function (param) { - if (isKnownQueryParam(param)) - saneQuery[param] = query[param] + Object.keys(query).forEach(function(param) { + if (isKnownQueryParam(param)) saneQuery[param] = query[param] }) return saneQuery @@ -54,13 +53,21 @@ function packageURL(req, res, next) { // Do not allow invalid URLs. if (url == null) - return res.status(403).type('text').send(`Invalid URL: ${req.url}`) + return res + .status(403) + .type('text') + .send(`Invalid URL: ${req.url}`) const nameErrors = validateNPMPackageName(url.packageName).errors // Do not allow invalid package names. if (nameErrors) - return res.status(403).type('text').send(`Invalid package name: ${url.packageName} (${nameErrors.join(', ')})`) + return res + .status(403) + .type('text') + .send( + `Invalid package name: ${url.packageName} (${nameErrors.join(', ')})` + ) req.packageName = url.packageName req.packageVersion = url.packageVersion diff --git a/server/middleware/serveFile.js b/server/middleware/serveFile.js index 5333f59..39e476e 100644 --- a/server/middleware/serveFile.js +++ b/server/middleware/serveFile.js @@ -18,36 +18,43 @@ const AutoIndex = !process.env.DISABLE_INDEX const MaximumDepth = 128 const FileTransforms = { - expand: function (file, dependencies, callback) { + expand: function(file, dependencies, callback) { const options = { - plugins: [ - unpkgRewrite(dependencies) - ] + plugins: [unpkgRewrite(dependencies)] } - babel.transformFile(file, options, function (error, result) { + babel.transformFile(file, options, function(error, result) { callback(error, result && result.code) }) } } - /** * Send the file, JSON metadata, or HTML directory listing. */ function serveFile(req, res, next) { if (req.query.meta != null) { // Serve JSON metadata. - getMetadata(req.packageDir, req.filename, req.stats, MaximumDepth, function (error, metadata) { + getMetadata(req.packageDir, req.filename, req.stats, MaximumDepth, function( + error, + metadata + ) { if (error) { console.error(error) - res.status(500).type('text').send(`Cannot generate metadata for ${req.packageSpec}${req.filename}`) + res + .status(500) + .type('text') + .send( + `Cannot generate metadata for ${req.packageSpec}${req.filename}` + ) } else { // Cache metadata for 1 year. - res.set({ - 'Cache-Control': 'public, max-age=31536000', - 'Cache-Tag': 'meta' - }).send(metadata) + res + .set({ + 'Cache-Control': 'public, max-age=31536000', + 'Cache-Tag': 'meta' + }) + .send(metadata) } }) } else if (req.stats.isFile()) { @@ -56,39 +63,52 @@ function serveFile(req, res, next) { let contentType = getFileContentType(file) - if (contentType === 'text/html') - contentType = 'text/plain' // We can't serve HTML because bad people :( + if (contentType === 'text/html') contentType = 'text/plain' // We can't serve HTML because bad people :( if (contentType === 'application/javascript' && req.query.module != null) { // Serve a JavaScript module. - const dependencies = Object.assign({}, + const dependencies = Object.assign( + {}, req.packageConfig.peerDependencies, req.packageConfig.dependencies ) - FileTransforms.expand(file, dependencies, function (error, code) { + FileTransforms.expand(file, dependencies, function(error, code) { if (error) { console.error(error) - const debugInfo = error.constructor.name + ': ' + error.message.replace(/^.*?\/unpkg-.+?\//, `/${req.packageSpec}/`) + '\n\n' + error.codeFrame - res.status(500).type('text').send(`Cannot generate module for ${req.packageSpec}${req.filename}\n\n${debugInfo}`) + const debugInfo = + error.constructor.name + + ': ' + + error.message.replace(/^.*?\/unpkg-.+?\//, `/${req.packageSpec}/`) + + '\n\n' + + error.codeFrame + res + .status(500) + .type('text') + .send( + `Cannot generate module for ${req.packageSpec}${ + req.filename + }\n\n${debugInfo}` + ) } else { // Cache modules for 1 year. - res.set({ - 'Content-Type': contentType, - 'Content-Length': Buffer.byteLength(code), - 'Cache-Control': 'public, max-age=31536000', - 'Cache-Tag': 'file,js-file,js-module' - }).send(code) + res + .set({ + 'Content-Type': contentType, + 'Content-Length': Buffer.byteLength(code), + 'Cache-Control': 'public, max-age=31536000', + 'Cache-Tag': 'file,js-file,js-module' + }) + .send(code) } }) } else { // Serve some other static file. - const tags = [ 'file' ] + const tags = ['file'] const ext = path.extname(req.filename).substr(1) - if (ext) - tags.push(`${ext}-file`) + if (ext) tags.push(`${ext}-file`) // Cache files for 1 year. res.set({ @@ -96,13 +116,13 @@ function serveFile(req, res, next) { 'Content-Length': req.stats.size, 'Cache-Control': 'public, max-age=31536000', 'Last-Modified': req.stats.mtime.toUTCString(), - 'ETag': etag(req.stats), + ETag: etag(req.stats), 'Cache-Tag': tags.join(',') }) const stream = fs.createReadStream(file) - stream.on('error', function (error) { + stream.on('error', function(error) { console.error(`Cannot send file ${req.packageSpec}${req.filename}`) console.error(error) res.sendStatus(500) @@ -112,20 +132,36 @@ function serveFile(req, res, next) { } } else if (AutoIndex && req.stats.isDirectory()) { // Serve an HTML directory listing. - getIndexHTML(req.packageInfo, req.packageVersion, req.packageDir, req.filename, function (error, html) { - if (error) { - console.error(error) - res.status(500).type('text').send(`Cannot generate index page for ${req.packageSpec}${req.filename}`) - } else { - // Cache HTML directory listings for 1 minute. - res.set({ - 'Cache-Control': 'public, max-age=60', - 'Cache-Tag': 'index' - }).send(html) + getIndexHTML( + req.packageInfo, + req.packageVersion, + req.packageDir, + req.filename, + function(error, html) { + if (error) { + console.error(error) + res + .status(500) + .type('text') + .send( + `Cannot generate index page for ${req.packageSpec}${req.filename}` + ) + } else { + // Cache HTML directory listings for 1 minute. + res + .set({ + 'Cache-Control': 'public, max-age=60', + 'Cache-Tag': 'index' + }) + .send(html) + } } - }) + ) } else { - res.status(403).type('text').send(`Cannot serve ${req.packageSpec}${req.filename}; it's not a file`) + res + .status(403) + .type('text') + .send(`Cannot serve ${req.packageSpec}${req.filename}; it's not a file`) } } diff --git a/server/middleware/utils/createCache.js b/server/middleware/utils/createCache.js index e0106ae..73fab02 100644 --- a/server/middleware/utils/createCache.js +++ b/server/middleware/utils/createCache.js @@ -10,7 +10,7 @@ function createCache(keyPrefix) { } function get(key, callback) { - db.get(createKey(key), function (error, value) { + db.get(createKey(key), function(error, value) { callback(error, value && JSON.parse(value)) }) } diff --git a/server/middleware/utils/createMutex.js b/server/middleware/utils/createMutex.js index d635e4e..da5f666 100644 --- a/server/middleware/utils/createMutex.js +++ b/server/middleware/utils/createMutex.js @@ -1,16 +1,19 @@ function createMutex(doWork) { const mutex = {} - return function (key, payload, callback) { + return function(key, payload, callback) { if (mutex[key]) { mutex[key].push(callback) } else { - mutex[key] = [ function () { - delete mutex[key] - }, callback ] + mutex[key] = [ + function() { + delete mutex[key] + }, + callback + ] - doWork(payload, function (error, value) { - mutex[key].forEach(function (callback) { + doWork(payload, function(error, value) { + mutex[key].forEach(function(callback) { callback(error, value) }) }) diff --git a/server/middleware/utils/createSearch.js b/server/middleware/utils/createSearch.js index a4fd805..65a4b17 100644 --- a/server/middleware/utils/createSearch.js +++ b/server/middleware/utils/createSearch.js @@ -1,7 +1,7 @@ function createSearch(query) { const params = [] - Object.keys(query).forEach(function (param) { + Object.keys(query).forEach(function(param) { if (query[param] === '') { params.push(param) // Omit the trailing "=" from param= } else { diff --git a/server/middleware/utils/getIndexHTML.js b/server/middleware/utils/getIndexHTML.js index c5d874e..651d1b1 100644 --- a/server/middleware/utils/getIndexHTML.js +++ b/server/middleware/utils/getIndexHTML.js @@ -8,16 +8,16 @@ const IndexPage = require('../components/IndexPage') const e = React.createElement function getEntries(dir) { - return new Promise(function (resolve, reject) { - fs.readdir(dir, function (error, files) { + return new Promise(function(resolve, reject) { + fs.readdir(dir, function(error, files) { if (error) { reject(error) } else { resolve( Promise.all( files.map(file => getFileStats(path.join(dir, file))) - ).then(function (statsArray) { - return statsArray.map(function (stats, index) { + ).then(function(statsArray) { + return statsArray.map(function(stats, index) { return { file: files[index], stats } }) }) diff --git a/server/middleware/utils/getMetadata.js b/server/middleware/utils/getMetadata.js index 37fa390..7768e19 100644 --- a/server/middleware/utils/getMetadata.js +++ b/server/middleware/utils/getMetadata.js @@ -6,20 +6,27 @@ const getFileStats = require('./getFileStats') const getFileType = require('./getFileType') function getEntries(dir, file, maximumDepth) { - return new Promise(function (resolve, reject) { - fs.readdir(path.join(dir, file), function (error, files) { + return new Promise(function(resolve, reject) { + fs.readdir(path.join(dir, file), function(error, files) { if (error) { reject(error) } else { resolve( Promise.all( - files.map(function (f) { + files.map(function(f) { return getFileStats(path.join(dir, file, f)) }) - ).then(function (statsArray) { - return Promise.all(statsArray.map(function (stats, index) { - return getMetadataRecursive(dir, path.join(file, files[index]), stats, maximumDepth - 1) - })) + ).then(function(statsArray) { + return Promise.all( + statsArray.map(function(stats, index) { + return getMetadataRecursive( + dir, + path.join(file, files[index]), + stats, + maximumDepth - 1 + ) + }) + ) }) ) } @@ -32,12 +39,12 @@ function formatTime(time) { } function getIntegrity(file) { - return new Promise(function (resolve, reject) { - fs.readFile(file, function (error, data) { + return new Promise(function(resolve, reject) { + fs.readFile(file, function(error, data) { if (error) { reject(error) } else { - resolve(SRIToolbox.generate({ algorithms: [ 'sha384' ] }, data)) + resolve(SRIToolbox.generate({ algorithms: ['sha384'] }, data)) } }) }) @@ -53,7 +60,7 @@ function getMetadataRecursive(dir, file, stats, maximumDepth) { } if (stats.isFile()) { - return getIntegrity(path.join(dir, file)).then(function (integrity) { + return getIntegrity(path.join(dir, file)).then(function(integrity) { metadata.integrity = integrity return metadata }) @@ -62,16 +69,19 @@ function getMetadataRecursive(dir, file, stats, maximumDepth) { if (!stats.isDirectory() || maximumDepth === 0) return Promise.resolve(metadata) - return getEntries(dir, file, maximumDepth).then(function (files) { + return getEntries(dir, file, maximumDepth).then(function(files) { metadata.files = files return metadata }) } function getMetadata(baseDir, path, stats, maximumDepth, callback) { - getMetadataRecursive(baseDir, path, stats, maximumDepth).then(function (metadata) { + getMetadataRecursive(baseDir, path, stats, maximumDepth).then(function( + metadata + ) { callback(null, metadata) - }, callback) + }, + callback) } module.exports = getMetadata diff --git a/server/middleware/utils/getPackage.js b/server/middleware/utils/getPackage.js index 5be1e76..85fc1fa 100644 --- a/server/middleware/utils/getPackage.js +++ b/server/middleware/utils/getPackage.js @@ -26,7 +26,7 @@ function ignoreSymlinks(file, headers) { } function extractResponse(response, outputDir) { - return new Promise(function (resolve, reject) { + return new Promise(function(resolve, reject) { const extract = tar.extract(outputDir, { readable: true, // All dirs/files should be readable. map: stripNamePrefix, @@ -44,24 +44,24 @@ function extractResponse(response, outputDir) { function fetchAndExtract(tarballURL, outputDir) { console.log(`info: Fetching ${tarballURL} and extracting to ${outputDir}`) - return fetch(tarballURL).then(function (response) { + return fetch(tarballURL).then(function(response) { return extractResponse(response, outputDir) }) } -const fetchMutex = createMutex(function (payload, callback) { +const fetchMutex = createMutex(function(payload, callback) { const { tarballURL, outputDir } = payload - fs.access(outputDir, function (error) { + fs.access(outputDir, function(error) { if (error) { if (error.code === 'ENOENT' || error.code === 'ENOTDIR') { // ENOENT or ENOTDIR are to be expected when we haven't yet // fetched a package for the first time. Carry on! - mkdirp(outputDir, function (error) { + mkdirp(outputDir, function(error) { if (error) { callback(error) } else { - fetchAndExtract(tarballURL, outputDir).then(function () { + fetchAndExtract(tarballURL, outputDir).then(function() { callback() }, callback) } @@ -80,7 +80,7 @@ function getPackage(packageConfig, callback) { const tarballURL = packageConfig.dist.tarball const outputDir = createTempPath(packageConfig.name, packageConfig.version) - fetchMutex(tarballURL, { tarballURL, outputDir }, function (error) { + fetchMutex(tarballURL, { tarballURL, outputDir }, function(error) { callback(error, outputDir) }) } diff --git a/server/middleware/utils/getPackageInfo.js b/server/middleware/utils/getPackageInfo.js index 34fd4a2..3cac869 100644 --- a/server/middleware/utils/getPackageInfo.js +++ b/server/middleware/utils/getPackageInfo.js @@ -20,9 +20,9 @@ function fetchPackageInfo(packageName) { return fetch(url, { headers: { - 'Accept': 'application/json' + Accept: 'application/json' } - }).then(function (res) { + }).then(function(res) { return res.status === 404 ? null : res.json() }) } @@ -31,32 +31,35 @@ const PackageNotFound = 'PackageNotFound' // This mutex prevents multiple concurrent requests to // the registry for the same package info. -const fetchMutex = createMutex(function (packageName, callback) { - fetchPackageInfo(packageName).then(function (value) { - if (value == null) { - // Cache 404s for 5 minutes. This prevents us from making - // unnecessary requests to the registry for bad package names. - // In the worst case, a brand new package's info will be - // available within 5 minutes. - PackageInfoCache.set(packageName, PackageNotFound, 300, function () { - callback(null, value) - }) - } else { - // Cache valid package info for 1 minute. - PackageInfoCache.set(packageName, value, 60, function () { - callback(null, value) +const fetchMutex = createMutex(function(packageName, callback) { + fetchPackageInfo(packageName).then( + function(value) { + if (value == null) { + // Cache 404s for 5 minutes. This prevents us from making + // unnecessary requests to the registry for bad package names. + // In the worst case, a brand new package's info will be + // available within 5 minutes. + PackageInfoCache.set(packageName, PackageNotFound, 300, function() { + callback(null, value) + }) + } else { + // Cache valid package info for 1 minute. + PackageInfoCache.set(packageName, value, 60, function() { + callback(null, value) + }) + } + }, + function(error) { + // Do not cache errors. + PackageInfoCache.del(packageName, function() { + callback(error) }) } - }, function (error) { - // Do not cache errors. - PackageInfoCache.del(packageName, function () { - callback(error) - }) - }) + ) }) function getPackageInfo(packageName, callback) { - PackageInfoCache.get(packageName, function (error, value) { + PackageInfoCache.get(packageName, function(error, value) { if (error || value != null) { callback(error, value === PackageNotFound ? null : value) } else { diff --git a/server/npm/assetPathsIndex.js b/server/npm/assetPathsIndex.js index 2400d68..ba5fe65 100644 --- a/server/npm/assetPathsIndex.js +++ b/server/npm/assetPathsIndex.js @@ -10,169 +10,96 @@ * The range `null` is a catch-all. */ module.exports = { - 'angular': [ - [ '>=1.2.27', '/angular.min.js' ], - [ null, '/lib/angular.min.js' ] + angular: [['>=1.2.27', '/angular.min.js'], [null, '/lib/angular.min.js']], + + 'angular-animate': [[null, '/angular-animate.min.js']], + + 'angular-cookies': [[null, '/angular-cookies.min.js']], + + 'angular-resource': [[null, '/angular-resource.min.js']], + + 'angular-sanitize': [[null, '/angular-sanitize.min.js']], + + 'angular-ui-bootstrap': [[null, '/dist/ui-bootstrap.js']], + + 'animate.css': [[null, '/animate.min.css']], + + 'babel-standalone': [[null, '/babel.min.js']], + + backbone: [[null, '/backbone-min.js']], + + bootstrap: [ + [null, '/dist/css/bootstrap.min.css', '/dist/js/bootstrap.min.js'] ], - 'angular-animate': [ - [ null, '/angular-animate.min.js' ] - ], + 'bootstrap-sass': [[null, '/assets/javascripts/bootstrap.min.js']], - 'angular-cookies': [ - [ null, '/angular-cookies.min.js' ] - ], + bulma: [[null, '/css/bulma.css']], - 'angular-resource': [ - [ null, '/angular-resource.min.js' ] - ], + 'core.js': [[null, '/dist/core.min.js']], - 'angular-sanitize': [ - [ null, '/angular-sanitize.min.js' ] - ], + 'create-react-class': [[null, '/create-react-class.min.js']], - 'angular-ui-bootstrap': [ - [ null, '/dist/ui-bootstrap.js' ] - ], + d3: [[null, '/build/d3.min.js']], - 'animate.css': [ - [ null, '/animate.min.css' ] - ], - - 'babel-standalone': [ - [ null, '/babel.min.js' ] - ], - - 'backbone': [ - [ null, '/backbone-min.js' ] - ], - - 'bootstrap': [ - [ null, '/dist/css/bootstrap.min.css', '/dist/js/bootstrap.min.js' ] - ], - - 'bootstrap-sass': [ - [ null, '/assets/javascripts/bootstrap.min.js' ] - ], - - 'bulma': [ - [ null, '/css/bulma.css' ] - ], - - 'core.js': [ - [ null, '/dist/core.min.js' ] - ], - - 'create-react-class': [ - [ null, '/create-react-class.min.js' ] - ], - - 'd3': [ - [ null, '/build/d3.min.js' ] - ], - - 'ember-source': [ - [ null, '/dist/ember.min.js' ] - ], + 'ember-source': [[null, '/dist/ember.min.js']], 'foundation-sites': [ - [ null, '/dist/css/foundation.min.css', '/dist/js/foundation.min.js' ] + [null, '/dist/css/foundation.min.css', '/dist/js/foundation.min.js'] ], - 'gsap': [ - [ null, '/TweenMax.js' ] + gsap: [[null, '/TweenMax.js']], + + handlebars: [[null, '/dist/handlebars.min.js']], + + jquery: [[null, '/dist/jquery.min.js']], + + fastclick: [[null, '/lib/fastclick.js']], + + lodash: [['<3', '/dist/lodash.min.js'], [null, '/lodash.min.js']], + + 'masonry-layout': [[null, '/dist/masonry.pkgd.min.js']], + + 'materialize-css': [[null, '/dist/css/materialize.min.css']], + + 'ngx-bootstrap': [[null, '/bundles/ngx-bootstrap.umd.js']], + + react: [ + ['>=16.0.0-alpha.7', '/umd/react.production.min.js'], + [null, '/dist/react.min.js'] ], - 'handlebars': [ - [ null, '/dist/handlebars.min.js' ] - ], - - 'jquery': [ - [ null, '/dist/jquery.min.js' ] - ], - - 'fastclick': [ - [ null, '/lib/fastclick.js' ] - ], - - 'lodash': [ - [ '<3', '/dist/lodash.min.js' ], - [ null, '/lodash.min.js' ] - ], - - 'masonry-layout': [ - [ null, '/dist/masonry.pkgd.min.js' ] - ], - - 'materialize-css': [ - [ null, '/dist/css/materialize.min.css' ] - ], - - 'ngx-bootstrap': [ - [ null, '/bundles/ngx-bootstrap.umd.js' ] - ], - - 'react': [ - [ '>=16.0.0-alpha.7', '/umd/react.production.min.js' ], - [ null, '/dist/react.min.js' ] - ], - - 'react-bootstrap': [ - [ null, '/dist/react-bootstrap.min.js' ] - ], + 'react-bootstrap': [[null, '/dist/react-bootstrap.min.js']], 'react-dom': [ - [ '>=16.0.0-alpha.7', '/umd/react-dom.production.min.js' ], - [ null, '/dist/react-dom.min.js' ] + ['>=16.0.0-alpha.7', '/umd/react-dom.production.min.js'], + [null, '/dist/react-dom.min.js'] ], 'react-router': [ - [ '>=4.0.0', '/umd/react-router.min.js' ], - [ null, '/umd/ReactRouter.min.js' ] + ['>=4.0.0', '/umd/react-router.min.js'], + [null, '/umd/ReactRouter.min.js'] ], - 'redux': [ - [ null, '/dist/redux.min.js' ] - ], + redux: [[null, '/dist/redux.min.js']], - 'redux-saga': [ - [ null, '/dist/redux-saga.min.js' ] - ], + 'redux-saga': [[null, '/dist/redux-saga.min.js']], - 'redux-thunk': [ - [ null, '/dist/redux-thunk.min.js' ] - ], + 'redux-thunk': [[null, '/dist/redux-thunk.min.js']], - 'snapsvg': [ - [ null, '/snap.svg-min.js' ] - ], + snapsvg: [[null, '/snap.svg-min.js']], - 'systemjs': [ - [ null, '/dist/system.js' ] - ], + systemjs: [[null, '/dist/system.js']], - 'three': [ - [ '<=0.77.0', '/three.min.js' ], - [ null, '/build/three.min.js' ] - ], + three: [['<=0.77.0', '/three.min.js'], [null, '/build/three.min.js']], - 'underscore': [ - [ null, '/underscore-min.js' ] - ], + underscore: [[null, '/underscore-min.js']], - 'vue': [ - [ null, '/dist/vue.min.js' ] - ], + vue: [[null, '/dist/vue.min.js']], - 'zepto': [ - [ null, '/dist/zepto.min.js' ] - ], + zepto: [[null, '/dist/zepto.min.js']], - 'zingchart': [ - [ null, '/client/zingchart.min.js' ] - ], + zingchart: [[null, '/client/zingchart.min.js']], - 'zone.js': [ - [ null, '/dist/zone.js' ] - ] + 'zone.js': [[null, '/dist/zone.js']] } diff --git a/server/npm/getAssetPaths.js b/server/npm/getAssetPaths.js index befa831..0ae9535 100644 --- a/server/npm/getAssetPaths.js +++ b/server/npm/getAssetPaths.js @@ -5,11 +5,10 @@ function getAssetPaths(packageName, version) { const entries = assetPathsIndex[packageName] if (entries) { - const matchingEntry = entries.find(function (entry) { + const matchingEntry = entries.find(function(entry) { const range = entry[0] - if (range == null || semver.satisfies(version, range)) - return entry + if (range == null || semver.satisfies(version, range)) return entry }) return matchingEntry.slice(1) diff --git a/server/npm/search.js b/server/npm/search.js index 35de1ae..1220d50 100644 --- a/server/npm/search.js +++ b/server/npm/search.js @@ -2,13 +2,13 @@ const searchIndex = require('./searchIndex') const getAssetPaths = require('./getAssetPaths') function enhanceHit(hit) { - return new Promise(function (resolve, reject) { + return new Promise(function(resolve, reject) { const assetPaths = getAssetPaths(hit.name, hit.version) if (assetPaths) { // TODO: Double check the package metadata to ensure the files // haven't moved from the paths in the index? - hit.assets = assetPaths.map(function (path) { + hit.assets = assetPaths.map(function(path) { return `https://unpkg.com/${hit.name}@${hit.version}${path}` }) @@ -16,9 +16,7 @@ function enhanceHit(hit) { } else { // We don't have any global paths for this package yet. Try // using the "bare" URL. - hit.assets = [ - `https://unpkg.com/${hit.name}@${hit.version}` - ] + hit.assets = [`https://unpkg.com/${hit.name}@${hit.version}`] resolve(hit) } @@ -32,13 +30,13 @@ function concat(string) { } function search(query, page) { - return new Promise(function (resolve, reject) { + return new Promise(function(resolve, reject) { const hitsPerPage = 10 const params = { // typoTolerance: 'min', // optionalFacetFilters: `concatenatedName:${concat(query)}`, - facets: [ 'keywords' ], + facets: ['keywords'], attributesToHighlight: null, attributesToRetrieve: [ 'description', @@ -58,14 +56,12 @@ function search(query, page) { page } - searchIndex.search(query, params, function (error, value) { + searchIndex.search(query, params, function(error, value) { if (error) { reject(error) } else { resolve( - Promise.all( - value.hits.map(enhanceHit) - ).then(function (hits) { + Promise.all(value.hits.map(enhanceHit)).then(function(hits) { const totalHits = value.nbHits const totalPages = value.nbPages diff --git a/server/npm/searchIndex.js b/server/npm/searchIndex.js index 6278fc6..a80c872 100644 --- a/server/npm/searchIndex.js +++ b/server/npm/searchIndex.js @@ -14,9 +14,8 @@ invariant( 'Missing $ALGOLIA_NPM_SEARCH_API_KEY environment variable' ) -const index = algolia( - AlgoliaNpmSearchAppId, - AlgoliaNpmSearchApiKey -).initIndex('npm-search') +const index = algolia(AlgoliaNpmSearchAppId, AlgoliaNpmSearchApiKey).initIndex( + 'npm-search' +) module.exports = index diff --git a/server/utils/createPackageURL.js b/server/utils/createPackageURL.js index 8456579..dbeef43 100644 --- a/server/utils/createPackageURL.js +++ b/server/utils/createPackageURL.js @@ -1,14 +1,11 @@ function createPackageURL(packageName, version, filename, search) { let pathname = `/${packageName}` - if (version != null) - pathname += `@${version}` + if (version != null) pathname += `@${version}` - if (filename) - pathname += filename + if (filename) pathname += filename - if (search) - pathname += search + if (search) pathname += search return pathname } diff --git a/server/utils/parsePackageURL.js b/server/utils/parsePackageURL.js index 34324ea..f61c726 100644 --- a/server/utils/parsePackageURL.js +++ b/server/utils/parsePackageURL.js @@ -19,20 +19,20 @@ function parsePackageURL(packageURL) { const match = URLFormat.exec(pathname) - if (match == null) - return null + if (match == null) return null const packageName = match[1] const packageVersion = decodeParam(match[2]) || 'latest' const filename = decodeParam(match[3]) - return { // If the URL is /@scope/name@version/file.js?main=browser: - pathname, // /@scope/name@version/path.js - search, // ?main=browser - query, // { main: 'browser' } - packageName, // @scope/name + return { + // If the URL is /@scope/name@version/file.js?main=browser: + pathname, // /@scope/name@version/path.js + search, // ?main=browser + query, // { main: 'browser' } + packageName, // @scope/name packageVersion, // version - filename // /file.js + filename // /file.js } }