New "browse" UI
Also, separated out browse, ?meta, and ?module request handlers. Fixes #82
|
@ -2,7 +2,7 @@ import request from 'supertest';
|
||||||
|
|
||||||
import createServer from '../createServer.js';
|
import createServer from '../createServer.js';
|
||||||
|
|
||||||
describe('A request that targets a directory with a trailing slash', () => {
|
describe('A request to browse a directory', () => {
|
||||||
let server;
|
let server;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
server = createServer();
|
server = createServer();
|
||||||
|
@ -11,7 +11,7 @@ describe('A request that targets a directory with a trailing slash', () => {
|
||||||
describe('when the directory exists', () => {
|
describe('when the directory exists', () => {
|
||||||
it('returns an HTML page', done => {
|
it('returns an HTML page', done => {
|
||||||
request(server)
|
request(server)
|
||||||
.get('/react@16.8.0/umd/')
|
.get('/browse/react@16.8.0/umd/')
|
||||||
.end((err, res) => {
|
.end((err, res) => {
|
||||||
expect(res.statusCode).toBe(200);
|
expect(res.statusCode).toBe(200);
|
||||||
expect(res.headers['content-type']).toMatch(/\btext\/html\b/);
|
expect(res.headers['content-type']).toMatch(/\btext\/html\b/);
|
||||||
|
@ -21,12 +21,12 @@ describe('A request that targets a directory with a trailing slash', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when the directory does not exist', () => {
|
describe('when the directory does not exist', () => {
|
||||||
it('returns a 404 text error', done => {
|
it('returns a 404 HTML page', done => {
|
||||||
request(server)
|
request(server)
|
||||||
.get('/react@16.8.0/not-here/')
|
.get('/browse/react@16.8.0/not-here/')
|
||||||
.end((err, res) => {
|
.end((err, res) => {
|
||||||
expect(res.statusCode).toBe(404);
|
expect(res.statusCode).toBe(404);
|
||||||
expect(res.headers['content-type']).toMatch(/\btext\/plain\b/);
|
expect(res.headers['content-type']).toMatch(/\btext\/html\b/);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -0,0 +1,34 @@
|
||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
import createServer from '../createServer.js';
|
||||||
|
|
||||||
|
describe('A request to browse a file', () => {
|
||||||
|
let server;
|
||||||
|
beforeEach(() => {
|
||||||
|
server = createServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the file exists', () => {
|
||||||
|
it('returns an HTML page', done => {
|
||||||
|
request(server)
|
||||||
|
.get('/browse/react@16.8.0/umd/react.production.min.js')
|
||||||
|
.end((err, res) => {
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.headers['content-type']).toMatch(/\btext\/html\b/);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the file does not exist', () => {
|
||||||
|
it('returns a 404 HTML page', done => {
|
||||||
|
request(server)
|
||||||
|
.get('/browse/react@16.8.0/not-here.js')
|
||||||
|
.end((err, res) => {
|
||||||
|
expect(res.statusCode).toBe(404);
|
||||||
|
expect(res.headers['content-type']).toMatch(/\btext\/html\b/);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -27,4 +27,14 @@ describe('Legacy URLs', () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('redirect */ to /browse/*/', done => {
|
||||||
|
request(server)
|
||||||
|
.get('/react@16.8.0/umd/')
|
||||||
|
.end((err, res) => {
|
||||||
|
expect(res.statusCode).toBe(302);
|
||||||
|
expect(res.headers.location).toEqual('/browse/react@16.8.0/umd/');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,38 +1,36 @@
|
||||||
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
|
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
|
||||||
|
|
||||||
import AutoIndexApp from '../client/autoIndex/App.js';
|
import BrowseApp from '../client/browse/App.js';
|
||||||
|
import MainTemplate from '../templates/MainTemplate.js';
|
||||||
import MainTemplate from './utils/MainTemplate.js';
|
|
||||||
import getScripts from './utils/getScripts.js';
|
|
||||||
import { createElement, createHTML } from './utils/markupHelpers.js';
|
|
||||||
import { getAvailableVersions } from '../utils/npm.js';
|
import { getAvailableVersions } from '../utils/npm.js';
|
||||||
|
import getScripts from '../utils/getScripts.js';
|
||||||
|
import { createElement, createHTML } from '../utils/markup.js';
|
||||||
|
|
||||||
const doctype = '<!DOCTYPE html>';
|
const doctype = '<!DOCTYPE html>';
|
||||||
const globalURLs =
|
const globalURLs =
|
||||||
process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging'
|
process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging'
|
||||||
? {
|
? {
|
||||||
'@emotion/core': '/@emotion/core@10.0.6/dist/core.umd.min.js',
|
'@emotion/core': '/@emotion/core@10.0.6/dist/core.umd.min.js',
|
||||||
react: '/react@16.7.0/umd/react.production.min.js',
|
react: '/react@16.8.6/umd/react.production.min.js',
|
||||||
'react-dom': '/react-dom@16.7.0/umd/react-dom.production.min.js'
|
'react-dom': '/react-dom@16.8.6/umd/react-dom.production.min.js'
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
'@emotion/core': '/@emotion/core@10.0.6/dist/core.umd.min.js',
|
'@emotion/core': '/@emotion/core@10.0.6/dist/core.umd.min.js',
|
||||||
react: '/react@16.7.0/umd/react.development.js',
|
react: '/react@16.8.6/umd/react.development.js',
|
||||||
'react-dom': '/react-dom@16.7.0/umd/react-dom.development.js'
|
'react-dom': '/react-dom@16.8.6/umd/react-dom.development.js'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function serveAutoIndexPage(req, res) {
|
export default async function serveBrowsePage(req, res) {
|
||||||
const availableVersions = await getAvailableVersions(req.packageName);
|
const availableVersions = await getAvailableVersions(req.packageName);
|
||||||
const data = {
|
const data = {
|
||||||
packageName: req.packageName,
|
packageName: req.packageName,
|
||||||
packageVersion: req.packageVersion,
|
packageVersion: req.packageVersion,
|
||||||
availableVersions: availableVersions,
|
availableVersions: availableVersions,
|
||||||
filename: req.filename,
|
filename: req.filename,
|
||||||
entry: req.entry,
|
target: req.browseTarget
|
||||||
entries: req.entries
|
|
||||||
};
|
};
|
||||||
const content = createHTML(renderToString(createElement(AutoIndexApp, data)));
|
const content = createHTML(renderToString(createElement(BrowseApp, data)));
|
||||||
const elements = getScripts('autoIndex', 'iife', globalURLs);
|
const elements = getScripts('browse', 'iife', globalURLs);
|
||||||
|
|
||||||
const html =
|
const html =
|
||||||
doctype +
|
doctype +
|
||||||
|
@ -49,7 +47,7 @@ export default async function serveAutoIndexPage(req, res) {
|
||||||
res
|
res
|
||||||
.set({
|
.set({
|
||||||
'Cache-Control': 'public, max-age=14400', // 4 hours
|
'Cache-Control': 'public, max-age=14400', // 4 hours
|
||||||
'Cache-Tag': 'auto-index'
|
'Cache-Tag': 'browse'
|
||||||
})
|
})
|
||||||
.send(html);
|
.send(html);
|
||||||
}
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
import path from 'path';
|
||||||
|
import gunzip from 'gunzip-maybe';
|
||||||
|
import tar from 'tar-stream';
|
||||||
|
|
||||||
|
import bufferStream from '../utils/bufferStream.js';
|
||||||
|
import getContentType from '../utils/getContentType.js';
|
||||||
|
import getIntegrity from '../utils/getIntegrity.js';
|
||||||
|
import { getPackage } from '../utils/npm.js';
|
||||||
|
import serveBrowsePage from './serveBrowsePage.js';
|
||||||
|
|
||||||
|
async function findMatchingEntries(stream, filename) {
|
||||||
|
// filename = /some/dir/name
|
||||||
|
return new Promise((accept, reject) => {
|
||||||
|
const entries = {};
|
||||||
|
|
||||||
|
stream
|
||||||
|
.pipe(gunzip())
|
||||||
|
.pipe(tar.extract())
|
||||||
|
.on('error', reject)
|
||||||
|
.on('entry', async (header, stream, next) => {
|
||||||
|
const entry = {
|
||||||
|
// Most packages have header names that look like `package/index.js`
|
||||||
|
// so we shorten that to just `/index.js` here. A few packages use a
|
||||||
|
// prefix other than `package/`. e.g. the firebase package uses the
|
||||||
|
// `firebase_npm/` prefix. So we just strip the first dir name.
|
||||||
|
path: header.name.replace(/^[^/]+/, ''),
|
||||||
|
type: header.type
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dynamically create "directory" entries for all subdirectories
|
||||||
|
// in this entry's path. Some tarballs omit directory entries for
|
||||||
|
// some reason, so this is the "brute force" method.
|
||||||
|
let dir = path.dirname(entry.path);
|
||||||
|
while (dir !== '/') {
|
||||||
|
if (!entries[dir] && path.dirname(dir) === filename) {
|
||||||
|
entries[dir] = { path: dir, type: 'directory' };
|
||||||
|
}
|
||||||
|
dir = path.dirname(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore non-files and files that aren't in this directory.
|
||||||
|
if (entry.type !== 'file' || path.dirname(entry.path) !== filename) {
|
||||||
|
stream.resume();
|
||||||
|
stream.on('end', next);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = await bufferStream(stream);
|
||||||
|
|
||||||
|
entry.contentType = getContentType(entry.path);
|
||||||
|
entry.integrity = getIntegrity(content);
|
||||||
|
entry.size = content.length;
|
||||||
|
|
||||||
|
entries[entry.path] = entry;
|
||||||
|
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
.on('finish', () => {
|
||||||
|
accept(entries);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function serveDirectoryBrowser(req, res) {
|
||||||
|
const stream = await getPackage(req.packageName, req.packageVersion);
|
||||||
|
|
||||||
|
const filename = req.filename.slice(0, -1) || '/';
|
||||||
|
const entries = await findMatchingEntries(stream, filename);
|
||||||
|
|
||||||
|
if (Object.keys(entries).length === 0) {
|
||||||
|
return res.status(404).send(`Not found: ${req.packageSpec}${req.filename}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.browseTarget = {
|
||||||
|
path: filename,
|
||||||
|
type: 'directory',
|
||||||
|
details: entries
|
||||||
|
};
|
||||||
|
|
||||||
|
serveBrowsePage(req, res);
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
import path from 'path';
|
||||||
|
import gunzip from 'gunzip-maybe';
|
||||||
|
import tar from 'tar-stream';
|
||||||
|
|
||||||
|
import bufferStream from '../utils/bufferStream.js';
|
||||||
|
import getContentType from '../utils/getContentType.js';
|
||||||
|
import getIntegrity from '../utils/getIntegrity.js';
|
||||||
|
import { getPackage } from '../utils/npm.js';
|
||||||
|
|
||||||
|
async function findMatchingEntries(stream, filename) {
|
||||||
|
// filename = /some/dir/name
|
||||||
|
return new Promise((accept, reject) => {
|
||||||
|
const entries = {};
|
||||||
|
|
||||||
|
entries[filename] = { path: filename, type: 'directory' };
|
||||||
|
|
||||||
|
stream
|
||||||
|
.pipe(gunzip())
|
||||||
|
.pipe(tar.extract())
|
||||||
|
.on('error', reject)
|
||||||
|
.on('entry', async (header, stream, next) => {
|
||||||
|
const entry = {
|
||||||
|
// Most packages have header names that look like `package/index.js`
|
||||||
|
// so we shorten that to just `/index.js` here. A few packages use a
|
||||||
|
// prefix other than `package/`. e.g. the firebase package uses the
|
||||||
|
// `firebase_npm/` prefix. So we just strip the first dir name.
|
||||||
|
path: header.name.replace(/^[^/]+/, ''),
|
||||||
|
type: header.type
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dynamically create "directory" entries for all subdirectories
|
||||||
|
// in this entry's path. Some tarballs omit directory entries for
|
||||||
|
// some reason, so this is the "brute force" method.
|
||||||
|
let dir = path.dirname(entry.path);
|
||||||
|
while (dir !== '/') {
|
||||||
|
if (!entries[dir] && dir.startsWith(filename)) {
|
||||||
|
entries[dir] = { path: dir, type: 'directory' };
|
||||||
|
}
|
||||||
|
dir = path.dirname(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore non-files and files that don't match the prefix.
|
||||||
|
if (entry.type !== 'file' || !entry.path.startsWith(filename)) {
|
||||||
|
stream.resume();
|
||||||
|
stream.on('end', next);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = await bufferStream(stream);
|
||||||
|
|
||||||
|
entry.contentType = getContentType(entry.path);
|
||||||
|
entry.integrity = getIntegrity(content);
|
||||||
|
entry.lastModified = header.mtime.toUTCString();
|
||||||
|
entry.size = content.length;
|
||||||
|
|
||||||
|
entries[entry.path] = entry;
|
||||||
|
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
.on('finish', () => {
|
||||||
|
accept(entries);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMatchingEntries(entry, entries) {
|
||||||
|
return Object.keys(entries)
|
||||||
|
.filter(key => entry.path !== key && path.dirname(key) === entry.path)
|
||||||
|
.map(key => entries[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMetadata(entry, entries) {
|
||||||
|
const metadata = { path: entry.path, type: entry.type };
|
||||||
|
|
||||||
|
if (entry.type === 'file') {
|
||||||
|
metadata.contentType = entry.contentType;
|
||||||
|
metadata.integrity = entry.integrity;
|
||||||
|
metadata.lastModified = entry.lastModified;
|
||||||
|
metadata.size = entry.size;
|
||||||
|
} else if (entry.type === 'directory') {
|
||||||
|
metadata.files = getMatchingEntries(entry, entries).map(e =>
|
||||||
|
getMetadata(e, entries)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function serveDirectoryMetadata(req, res) {
|
||||||
|
const stream = await getPackage(req.packageName, req.packageVersion);
|
||||||
|
|
||||||
|
const filename = req.filename.slice(0, -1) || '/';
|
||||||
|
const entries = await findMatchingEntries(stream, filename);
|
||||||
|
const metadata = getMetadata(entries[filename], entries);
|
||||||
|
|
||||||
|
res.send(metadata);
|
||||||
|
}
|
|
@ -1,23 +1,24 @@
|
||||||
import serveAutoIndexPage from './serveAutoIndexPage.js';
|
import path from 'path';
|
||||||
import serveMetadata from './serveMetadata.js';
|
import etag from 'etag';
|
||||||
import serveModule from './serveModule.js';
|
|
||||||
import serveStaticFile from './serveStaticFile.js';
|
import getContentTypeHeader from '../utils/getContentTypeHeader.js';
|
||||||
|
|
||||||
/**
|
|
||||||
* Send the file, JSON metadata, or HTML directory listing.
|
|
||||||
*/
|
|
||||||
export default function serveFile(req, res) {
|
export default function serveFile(req, res) {
|
||||||
if (req.query.meta != null) {
|
const tags = ['file'];
|
||||||
return serveMetadata(req, res);
|
|
||||||
|
const ext = path.extname(req.entry.path).substr(1);
|
||||||
|
if (ext) {
|
||||||
|
tags.push(`${ext}-file`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.entry.type === 'directory') {
|
res
|
||||||
return serveAutoIndexPage(req, res);
|
.set({
|
||||||
}
|
'Content-Type': getContentTypeHeader(req.entry.contentType),
|
||||||
|
'Content-Length': req.entry.size,
|
||||||
if (req.query.module != null) {
|
'Cache-Control': 'public, max-age=31536000', // 1 year
|
||||||
return serveModule(req, res);
|
'Last-Modified': req.entry.lastModified,
|
||||||
}
|
ETag: etag(req.entry.content),
|
||||||
|
'Cache-Tag': tags.join(', ')
|
||||||
serveStaticFile(req, res);
|
})
|
||||||
|
.send(req.entry.content);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
import gunzip from 'gunzip-maybe';
|
||||||
|
import tar from 'tar-stream';
|
||||||
|
|
||||||
|
import bufferStream from '../utils/bufferStream.js';
|
||||||
|
import createDataURI from '../utils/createDataURI.js';
|
||||||
|
import getContentType from '../utils/getContentType.js';
|
||||||
|
import getIntegrity from '../utils/getIntegrity.js';
|
||||||
|
import { getPackage } from '../utils/npm.js';
|
||||||
|
import getHighlights from '../utils/getHighlights.js';
|
||||||
|
import getLanguageName from '../utils/getLanguageName.js';
|
||||||
|
|
||||||
|
import serveBrowsePage from './serveBrowsePage.js';
|
||||||
|
|
||||||
|
async function findEntry(stream, filename) {
|
||||||
|
// filename = /some/file/name.js
|
||||||
|
return new Promise((accept, reject) => {
|
||||||
|
let foundEntry = null;
|
||||||
|
|
||||||
|
stream
|
||||||
|
.pipe(gunzip())
|
||||||
|
.pipe(tar.extract())
|
||||||
|
.on('error', reject)
|
||||||
|
.on('entry', async (header, stream, next) => {
|
||||||
|
const entry = {
|
||||||
|
// Most packages have header names that look like `package/index.js`
|
||||||
|
// so we shorten that to just `/index.js` here. A few packages use a
|
||||||
|
// prefix other than `package/`. e.g. the firebase package uses the
|
||||||
|
// `firebase_npm/` prefix. So we just strip the first dir name.
|
||||||
|
path: header.name.replace(/^[^/]+/, ''),
|
||||||
|
type: header.type
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ignore non-files and files that don't match the name.
|
||||||
|
if (entry.type !== 'file' || entry.path !== filename) {
|
||||||
|
stream.resume();
|
||||||
|
stream.on('end', next);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.content = await bufferStream(stream);
|
||||||
|
foundEntry = entry;
|
||||||
|
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
.on('finish', () => {
|
||||||
|
accept(foundEntry);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function serveFileBrowser(req, res) {
|
||||||
|
const stream = await getPackage(req.packageName, req.packageVersion);
|
||||||
|
const entry = await findEntry(stream, req.filename);
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
return res.status(404).send(`Not found: ${req.packageSpec}${req.filename}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const details = {
|
||||||
|
contentType: getContentType(entry.path),
|
||||||
|
integrity: getIntegrity(entry.content),
|
||||||
|
language: getLanguageName(entry.path),
|
||||||
|
size: entry.content.length
|
||||||
|
};
|
||||||
|
|
||||||
|
if (/^image\//.test(details.contentType)) {
|
||||||
|
details.uri = createDataURI(details.contentType, entry.content);
|
||||||
|
details.highlights = null;
|
||||||
|
} else {
|
||||||
|
details.uri = null;
|
||||||
|
details.highlights = getHighlights(
|
||||||
|
entry.content.toString('utf8'),
|
||||||
|
entry.path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.browseTarget = {
|
||||||
|
path: req.filename,
|
||||||
|
type: 'file',
|
||||||
|
details
|
||||||
|
};
|
||||||
|
|
||||||
|
serveBrowsePage(req, res);
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
import gunzip from 'gunzip-maybe';
|
||||||
|
import tar from 'tar-stream';
|
||||||
|
|
||||||
|
import bufferStream from '../utils/bufferStream.js';
|
||||||
|
import getContentType from '../utils/getContentType.js';
|
||||||
|
import getIntegrity from '../utils/getIntegrity.js';
|
||||||
|
import { getPackage } from '../utils/npm.js';
|
||||||
|
|
||||||
|
async function findEntry(stream, filename) {
|
||||||
|
// filename = /some/file/name.js
|
||||||
|
return new Promise((accept, reject) => {
|
||||||
|
let foundEntry = null;
|
||||||
|
|
||||||
|
stream
|
||||||
|
.pipe(gunzip())
|
||||||
|
.pipe(tar.extract())
|
||||||
|
.on('error', reject)
|
||||||
|
.on('entry', async (header, stream, next) => {
|
||||||
|
const entry = {
|
||||||
|
// Most packages have header names that look like `package/index.js`
|
||||||
|
// so we shorten that to just `/index.js` here. A few packages use a
|
||||||
|
// prefix other than `package/`. e.g. the firebase package uses the
|
||||||
|
// `firebase_npm/` prefix. So we just strip the first dir name.
|
||||||
|
path: header.name.replace(/^[^/]+/, ''),
|
||||||
|
type: header.type
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ignore non-files and files that don't match the name.
|
||||||
|
if (entry.type !== 'file' || entry.path !== filename) {
|
||||||
|
stream.resume();
|
||||||
|
stream.on('end', next);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = await bufferStream(stream);
|
||||||
|
|
||||||
|
entry.contentType = getContentType(entry.path);
|
||||||
|
entry.integrity = getIntegrity(content);
|
||||||
|
entry.lastModified = header.mtime.toUTCString();
|
||||||
|
entry.size = content.length;
|
||||||
|
|
||||||
|
foundEntry = entry;
|
||||||
|
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
.on('finish', () => {
|
||||||
|
accept(foundEntry);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function serveFileMetadata(req, res) {
|
||||||
|
const stream = await getPackage(req.packageName, req.packageVersion);
|
||||||
|
const entry = await findEntry(stream, req.filename);
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
// TODO: 404
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(entry);
|
||||||
|
}
|
|
@ -1,23 +1,22 @@
|
||||||
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
|
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
|
||||||
|
|
||||||
import MainApp from '../client/main/App.js';
|
import MainApp from '../client/main/App.js';
|
||||||
|
import MainTemplate from '../templates/MainTemplate.js';
|
||||||
import MainTemplate from './utils/MainTemplate.js';
|
import getScripts from '../utils/getScripts.js';
|
||||||
import getScripts from './utils/getScripts.js';
|
import { createElement, createHTML } from '../utils/markup.js';
|
||||||
import { createElement, createHTML } from './utils/markupHelpers.js';
|
|
||||||
|
|
||||||
const doctype = '<!DOCTYPE html>';
|
const doctype = '<!DOCTYPE html>';
|
||||||
const globalURLs =
|
const globalURLs =
|
||||||
process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging'
|
process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging'
|
||||||
? {
|
? {
|
||||||
'@emotion/core': '/@emotion/core@10.0.6/dist/core.umd.min.js',
|
'@emotion/core': '/@emotion/core@10.0.6/dist/core.umd.min.js',
|
||||||
react: '/react@16.7.0/umd/react.production.min.js',
|
react: '/react@16.8.6/umd/react.production.min.js',
|
||||||
'react-dom': '/react-dom@16.7.0/umd/react-dom.production.min.js'
|
'react-dom': '/react-dom@16.8.6/umd/react-dom.production.min.js'
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
'@emotion/core': '/@emotion/core@10.0.6/dist/core.umd.min.js',
|
'@emotion/core': '/@emotion/core@10.0.6/dist/core.umd.min.js',
|
||||||
react: '/react@16.7.0/umd/react.development.js',
|
react: '/react@16.8.6/umd/react.development.js',
|
||||||
'react-dom': '/react-dom@16.7.0/umd/react-dom.development.js'
|
'react-dom': '/react-dom@16.8.6/umd/react-dom.development.js'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function serveMainPage(req, res) {
|
export default function serveMainPage(req, res) {
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
function getMatchingEntries(entry, entries) {
|
|
||||||
const dirname = entry.name || '.';
|
|
||||||
|
|
||||||
return Object.keys(entries)
|
|
||||||
.filter(name => entry.name !== name && path.dirname(name) === dirname)
|
|
||||||
.map(name => entries[name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const leadingSlashes = /^\/*/;
|
|
||||||
|
|
||||||
function getMetadata(entry, entries) {
|
|
||||||
const metadata = {
|
|
||||||
path: entry.name.replace(leadingSlashes, '/'),
|
|
||||||
type: entry.type
|
|
||||||
};
|
|
||||||
|
|
||||||
if (entry.type === 'file') {
|
|
||||||
metadata.contentType = entry.contentType;
|
|
||||||
metadata.integrity = entry.integrity;
|
|
||||||
metadata.lastModified = entry.lastModified;
|
|
||||||
metadata.size = entry.size;
|
|
||||||
} else if (entry.type === 'directory') {
|
|
||||||
metadata.files = getMatchingEntries(entry, entries).map(e =>
|
|
||||||
getMetadata(e, entries)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function serveMetadata(req, res) {
|
|
||||||
const metadata = getMetadata(req.entry, req.entries);
|
|
||||||
|
|
||||||
res
|
|
||||||
.set({
|
|
||||||
'Cache-Control': 'public, max-age=31536000', // 1 year
|
|
||||||
'Cache-Tag': 'meta'
|
|
||||||
})
|
|
||||||
.send(metadata);
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
import serveHTMLModule from './serveHTMLModule.js';
|
import serveHTMLModule from './serveHTMLModule.js';
|
||||||
import serveJavaScriptModule from './serveJavaScriptModule.js';
|
import serveJavaScriptModule from './serveJavaScriptModule.js';
|
||||||
|
|
||||||
export default function serveModule(req, res) {
|
export default async function serveModule(req, res) {
|
||||||
if (req.entry.contentType === 'application/javascript') {
|
if (req.entry.contentType === 'application/javascript') {
|
||||||
return serveJavaScriptModule(req, res);
|
return serveJavaScriptModule(req, res);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import path from 'path';
|
|
||||||
import etag from 'etag';
|
|
||||||
|
|
||||||
import getContentTypeHeader from '../utils/getContentTypeHeader.js';
|
|
||||||
|
|
||||||
export default function serveStaticFile(req, res) {
|
|
||||||
const tags = ['file'];
|
|
||||||
|
|
||||||
const ext = path.extname(req.entry.name).substr(1);
|
|
||||||
if (ext) {
|
|
||||||
tags.push(`${ext}-file`);
|
|
||||||
}
|
|
||||||
|
|
||||||
res
|
|
||||||
.set({
|
|
||||||
'Content-Length': req.entry.size,
|
|
||||||
'Content-Type': getContentTypeHeader(req.entry.contentType),
|
|
||||||
'Cache-Control': 'public, max-age=31536000', // 1 year
|
|
||||||
'Last-Modified': req.entry.lastModified,
|
|
||||||
ETag: etag(req.entry.content),
|
|
||||||
'Cache-Tag': tags.join(', ')
|
|
||||||
})
|
|
||||||
.send(req.entry.content);
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { subDays, startOfDay } from 'date-fns';
|
import { subDays, startOfDay } from 'date-fns';
|
||||||
|
|
||||||
import getStats from './utils/getStats.js';
|
import getStats from '../utils/getStats.js';
|
||||||
|
|
||||||
export default function serveStats(req, res) {
|
export default function serveStats(req, res) {
|
||||||
let since, until;
|
let since, until;
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true
|
"browser": true
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": ["react", "react-hooks"],
|
||||||
"react"
|
|
||||||
],
|
|
||||||
"rules": {
|
"rules": {
|
||||||
"react/jsx-uses-react": "error",
|
"react/jsx-uses-react": "error",
|
||||||
"react/jsx-uses-vars": "error"
|
"react/jsx-uses-vars": "error",
|
||||||
|
"react-hooks/rules-of-hooks": "error",
|
||||||
|
"react-hooks/exhaustive-deps": "warn"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"react": {
|
"react": {
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
/** @jsx jsx */
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Global, css, jsx } from '@emotion/core';
|
|
||||||
|
|
||||||
import DirectoryListing from './DirectoryListing.js';
|
|
||||||
|
|
||||||
const globalStyles = css`
|
|
||||||
body {
|
|
||||||
font-size: 14px;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
|
||||||
Helvetica, Arial, sans-serif;
|
|
||||||
line-height: 1.7;
|
|
||||||
padding: 0px 10px 5px;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default function App({
|
|
||||||
packageName,
|
|
||||||
packageVersion,
|
|
||||||
availableVersions = [],
|
|
||||||
filename,
|
|
||||||
entry,
|
|
||||||
entries
|
|
||||||
}) {
|
|
||||||
function handleChange(event) {
|
|
||||||
window.location.href = window.location.href.replace(
|
|
||||||
'@' + packageVersion,
|
|
||||||
'@' + event.target.value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div css={{ maxWidth: 900, margin: '0 auto' }}>
|
|
||||||
<Global styles={globalStyles} />
|
|
||||||
|
|
||||||
<header
|
|
||||||
css={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<h1>
|
|
||||||
Index of /{packageName}@{packageVersion}
|
|
||||||
{filename}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<div css={{ float: 'right', lineHeight: '2.25em' }}>
|
|
||||||
Version:{' '}
|
|
||||||
<select
|
|
||||||
id="version"
|
|
||||||
defaultValue={packageVersion}
|
|
||||||
onChange={handleChange}
|
|
||||||
css={{ fontSize: '1em' }}
|
|
||||||
>
|
|
||||||
{availableVersions.map(v => (
|
|
||||||
<option key={v} value={v}>
|
|
||||||
{v}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<DirectoryListing filename={filename} entry={entry} entries={entries} />
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<address css={{ textAlign: 'right' }}>
|
|
||||||
{packageName}@{packageVersion}
|
|
||||||
</address>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
|
||||||
const entryType = PropTypes.object;
|
|
||||||
|
|
||||||
App.propTypes = {
|
|
||||||
packageName: PropTypes.string.isRequired,
|
|
||||||
packageVersion: PropTypes.string.isRequired,
|
|
||||||
availableVersions: PropTypes.arrayOf(PropTypes.string),
|
|
||||||
filename: PropTypes.string.isRequired,
|
|
||||||
entry: entryType.isRequired,
|
|
||||||
entries: PropTypes.objectOf(entryType).isRequired
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,142 +0,0 @@
|
||||||
/** @jsx jsx */
|
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { jsx } from '@emotion/core';
|
|
||||||
import formatBytes from 'pretty-bytes';
|
|
||||||
import sortBy from 'sort-by';
|
|
||||||
|
|
||||||
function getDirname(name) {
|
|
||||||
return (
|
|
||||||
name
|
|
||||||
.split('/')
|
|
||||||
.slice(0, -1)
|
|
||||||
.join('/') || '.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMatchingEntries(entry, entries) {
|
|
||||||
const dirname = entry.name || '.';
|
|
||||||
|
|
||||||
return Object.keys(entries)
|
|
||||||
.filter(name => entry.name !== name && getDirname(name) === dirname)
|
|
||||||
.map(name => entries[name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRelativeName(base, name) {
|
|
||||||
return base.length ? name.substr(base.length + 1) : name;
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = {
|
|
||||||
tableHead: {
|
|
||||||
textAlign: 'left',
|
|
||||||
padding: '0.5em 1em'
|
|
||||||
},
|
|
||||||
tableCell: {
|
|
||||||
padding: '0.5em 1em'
|
|
||||||
},
|
|
||||||
evenRow: {
|
|
||||||
backgroundColor: '#eee'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function DirectoryListing({ filename, entry, entries }) {
|
|
||||||
const rows = [];
|
|
||||||
|
|
||||||
if (filename !== '/') {
|
|
||||||
rows.push(
|
|
||||||
<tr key="..">
|
|
||||||
<td css={styles.tableCell}>
|
|
||||||
<a title="Parent directory" href="../">
|
|
||||||
..
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td css={styles.tableCell}>-</td>
|
|
||||||
<td css={styles.tableCell}>-</td>
|
|
||||||
<td css={styles.tableCell}>-</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const matchingEntries = getMatchingEntries(entry, entries);
|
|
||||||
|
|
||||||
matchingEntries
|
|
||||||
.filter(({ type }) => type === 'directory')
|
|
||||||
.sort(sortBy('name'))
|
|
||||||
.forEach(({ name }) => {
|
|
||||||
const relName = getRelativeName(entry.name, name);
|
|
||||||
const href = relName + '/';
|
|
||||||
|
|
||||||
rows.push(
|
|
||||||
<tr key={name}>
|
|
||||||
<td css={styles.tableCell}>
|
|
||||||
<a title={relName} href={href}>
|
|
||||||
{href}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td css={styles.tableCell}>-</td>
|
|
||||||
<td css={styles.tableCell}>-</td>
|
|
||||||
<td css={styles.tableCell}>-</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
matchingEntries
|
|
||||||
.filter(({ type }) => type === 'file')
|
|
||||||
.sort(sortBy('name'))
|
|
||||||
.forEach(({ name, size, contentType, lastModified }) => {
|
|
||||||
const relName = getRelativeName(entry.name, name);
|
|
||||||
|
|
||||||
rows.push(
|
|
||||||
<tr key={name}>
|
|
||||||
<td css={styles.tableCell}>
|
|
||||||
<a title={relName} href={relName}>
|
|
||||||
{relName}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td css={styles.tableCell}>{contentType}</td>
|
|
||||||
<td css={styles.tableCell}>{formatBytes(size)}</td>
|
|
||||||
<td css={styles.tableCell}>{lastModified}</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<table
|
|
||||||
css={{
|
|
||||||
width: '100%',
|
|
||||||
borderCollapse: 'collapse',
|
|
||||||
font: '0.85em Monaco, monospace'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th css={styles.tableHead}>Name</th>
|
|
||||||
<th css={styles.tableHead}>Type</th>
|
|
||||||
<th css={styles.tableHead}>Size</th>
|
|
||||||
<th css={styles.tableHead}>Last Modified</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{rows.map((row, index) =>
|
|
||||||
React.cloneElement(row, {
|
|
||||||
style: index % 2 ? undefined : styles.evenRow
|
|
||||||
})
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
|
||||||
const entryType = PropTypes.shape({
|
|
||||||
name: PropTypes.string.isRequired
|
|
||||||
});
|
|
||||||
|
|
||||||
DirectoryListing.propTypes = {
|
|
||||||
filename: PropTypes.string.isRequired,
|
|
||||||
entry: entryType.isRequired,
|
|
||||||
entries: PropTypes.objectOf(entryType).isRequired
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import App from './autoIndex/App.js';
|
import App from './browse/App.js';
|
||||||
|
|
||||||
const props = window.__DATA__ || {};
|
const props = window.__DATA__ || {};
|
||||||
|
|
|
@ -0,0 +1,356 @@
|
||||||
|
/** @jsx jsx */
|
||||||
|
import { Global, css, jsx } from '@emotion/core';
|
||||||
|
import { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { fontSans, fontMono } from '../utils/style.js';
|
||||||
|
|
||||||
|
import { PackageInfoProvider } from './PackageInfo.js';
|
||||||
|
import DirectoryViewer from './DirectoryViewer.js';
|
||||||
|
import FileViewer from './FileViewer.js';
|
||||||
|
import { TwitterIcon, GitHubIcon } from './Icons.js';
|
||||||
|
|
||||||
|
import SelectDownArrow from './images/SelectDownArrow.png';
|
||||||
|
|
||||||
|
const globalStyles = css`
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#root {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
${fontSans}
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
background: white;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
${fontMono}
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Adapted from https://github.com/highlightjs/highlight.js/blob/master/src/styles/atom-one-light.css
|
||||||
|
const lightCodeStyles = css`
|
||||||
|
.code-listing {
|
||||||
|
background: #fbfdff;
|
||||||
|
color: #383a42;
|
||||||
|
}
|
||||||
|
.code-comment,
|
||||||
|
.code-quote {
|
||||||
|
color: #a0a1a7;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.code-doctag,
|
||||||
|
.code-keyword,
|
||||||
|
.code-link,
|
||||||
|
.code-formula {
|
||||||
|
color: #a626a4;
|
||||||
|
}
|
||||||
|
.code-section,
|
||||||
|
.code-name,
|
||||||
|
.code-selector-tag,
|
||||||
|
.code-deletion,
|
||||||
|
.code-subst {
|
||||||
|
color: #e45649;
|
||||||
|
}
|
||||||
|
.code-literal {
|
||||||
|
color: #0184bb;
|
||||||
|
}
|
||||||
|
.code-string,
|
||||||
|
.code-regexp,
|
||||||
|
.code-addition,
|
||||||
|
.code-attribute,
|
||||||
|
.code-meta-string {
|
||||||
|
color: #50a14f;
|
||||||
|
}
|
||||||
|
.code-built_in,
|
||||||
|
.code-class .code-title {
|
||||||
|
color: #c18401;
|
||||||
|
}
|
||||||
|
.code-attr,
|
||||||
|
.code-variable,
|
||||||
|
.code-template-variable,
|
||||||
|
.code-type,
|
||||||
|
.code-selector-class,
|
||||||
|
.code-selector-attr,
|
||||||
|
.code-selector-pseudo,
|
||||||
|
.code-number {
|
||||||
|
color: #986801;
|
||||||
|
}
|
||||||
|
.code-symbol,
|
||||||
|
.code-bullet,
|
||||||
|
.code-meta,
|
||||||
|
.code-selector-id,
|
||||||
|
.code-title {
|
||||||
|
color: #4078f2;
|
||||||
|
}
|
||||||
|
.code-emphasis {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.code-strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const linkStyle = {
|
||||||
|
color: '#0076ff',
|
||||||
|
textDecoration: 'none',
|
||||||
|
':hover': {
|
||||||
|
textDecoration: 'underline'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function App({
|
||||||
|
packageName,
|
||||||
|
packageVersion,
|
||||||
|
availableVersions = [],
|
||||||
|
filename,
|
||||||
|
target
|
||||||
|
}) {
|
||||||
|
function handleChange(event) {
|
||||||
|
window.location.href = window.location.href.replace(
|
||||||
|
'@' + packageVersion,
|
||||||
|
'@' + event.target.value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const breadcrumbs = [];
|
||||||
|
|
||||||
|
if (filename === '/') {
|
||||||
|
breadcrumbs.push(packageName);
|
||||||
|
} else {
|
||||||
|
let url = `/browse/${packageName}@${packageVersion}`;
|
||||||
|
|
||||||
|
breadcrumbs.push(
|
||||||
|
<a href={`${url}/`} css={linkStyle}>
|
||||||
|
{packageName}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
|
||||||
|
const segments = filename
|
||||||
|
.replace(/^\/+/, '')
|
||||||
|
.replace(/\/+$/, '')
|
||||||
|
.split('/');
|
||||||
|
|
||||||
|
const lastSegment = segments.pop();
|
||||||
|
|
||||||
|
segments.forEach(segment => {
|
||||||
|
url += `/${segment}`;
|
||||||
|
breadcrumbs.push(
|
||||||
|
<a href={`${url}/`} css={linkStyle}>
|
||||||
|
{segment}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
breadcrumbs.push(lastSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Provide a user pref to go full width?
|
||||||
|
const maxContentWidth = 940;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PackageInfoProvider
|
||||||
|
packageName={packageName}
|
||||||
|
packageVersion={packageVersion}
|
||||||
|
>
|
||||||
|
<Fragment>
|
||||||
|
<Global styles={globalStyles} />
|
||||||
|
<Global styles={lightCodeStyles} />
|
||||||
|
|
||||||
|
<div css={{ flex: '1 0 auto' }}>
|
||||||
|
<div
|
||||||
|
css={{
|
||||||
|
maxWidth: maxContentWidth,
|
||||||
|
padding: '0 20px',
|
||||||
|
margin: '0 auto'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<header css={{ textAlign: 'center' }}>
|
||||||
|
<h1 css={{ fontSize: '3rem', marginTop: '2rem' }}>
|
||||||
|
<a href="/" css={{ color: '#000', textDecoration: 'none' }}>
|
||||||
|
UNPKG
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
{/*
|
||||||
|
<nav>
|
||||||
|
<a href="#" css={{ ...linkStyle, color: '#c400ff' }}>
|
||||||
|
Become a Sponsor
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
*/}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<header
|
||||||
|
css={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h1 css={{ fontSize: '1.5rem' }}>
|
||||||
|
<nav>
|
||||||
|
{breadcrumbs.map((link, index) => (
|
||||||
|
<span key={index}>
|
||||||
|
{index !== 0 && (
|
||||||
|
<span css={{ paddingLeft: 5, paddingRight: 5 }}>/</span>
|
||||||
|
)}
|
||||||
|
{link}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
</h1>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="version">Version:</label>{' '}
|
||||||
|
<select
|
||||||
|
name="version"
|
||||||
|
defaultValue={packageVersion}
|
||||||
|
onChange={handleChange}
|
||||||
|
css={{
|
||||||
|
appearance: 'none',
|
||||||
|
cursor: 'pointer',
|
||||||
|
padding: '4px 24px 4px 8px',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: '0.9em',
|
||||||
|
color: '#24292e',
|
||||||
|
border: '1px solid rgba(27,31,35,.2)',
|
||||||
|
borderRadius: 3,
|
||||||
|
backgroundColor: '#eff3f6',
|
||||||
|
backgroundImage: `url(${SelectDownArrow})`,
|
||||||
|
backgroundPosition: 'right 8px center',
|
||||||
|
backgroundRepeat: 'no-repeat',
|
||||||
|
backgroundSize: 'auto 25%',
|
||||||
|
':hover': {
|
||||||
|
backgroundColor: '#e6ebf1',
|
||||||
|
borderColor: 'rgba(27,31,35,.35)'
|
||||||
|
},
|
||||||
|
':active': {
|
||||||
|
backgroundColor: '#e9ecef',
|
||||||
|
borderColor: 'rgba(27,31,35,.35)',
|
||||||
|
boxShadow: 'inset 0 0.15em 0.3em rgba(27,31,35,.15)'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{availableVersions.map(v => (
|
||||||
|
<option key={v} value={v}>
|
||||||
|
{v}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
css={{
|
||||||
|
maxWidth: maxContentWidth,
|
||||||
|
padding: '0 20px',
|
||||||
|
margin: '0 auto',
|
||||||
|
'@media (max-width: 700px)': {
|
||||||
|
padding: 0,
|
||||||
|
margin: 0
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{target.type === 'directory' ? (
|
||||||
|
<DirectoryViewer path={target.path} details={target.details} />
|
||||||
|
) : target.type === 'file' ? (
|
||||||
|
<FileViewer path={target.path} details={target.details} />
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer
|
||||||
|
css={{
|
||||||
|
marginTop: '5rem',
|
||||||
|
background: 'black',
|
||||||
|
color: '#aaa'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
css={{
|
||||||
|
maxWidth: maxContentWidth,
|
||||||
|
padding: '10px 20px',
|
||||||
|
margin: '0 auto',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>© {new Date().getFullYear()} UNPKG</p>
|
||||||
|
<p css={{ fontSize: '1.5rem' }}>
|
||||||
|
<a
|
||||||
|
title="Twitter"
|
||||||
|
href="https://twitter.com/unpkg"
|
||||||
|
css={{
|
||||||
|
color: '#aaa',
|
||||||
|
display: 'inline-block',
|
||||||
|
':hover': { color: 'white' }
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TwitterIcon />
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
title="GitHub"
|
||||||
|
href="https://github.com/mjackson/unpkg"
|
||||||
|
css={{
|
||||||
|
color: '#aaa',
|
||||||
|
display: 'inline-block',
|
||||||
|
marginLeft: '1rem',
|
||||||
|
':hover': { color: 'white' }
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GitHubIcon />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</Fragment>
|
||||||
|
</PackageInfoProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
const targetType = PropTypes.shape({
|
||||||
|
path: PropTypes.string.isRequired,
|
||||||
|
type: PropTypes.oneOf(['directory', 'file']).isRequired,
|
||||||
|
details: PropTypes.object.isRequired
|
||||||
|
});
|
||||||
|
|
||||||
|
App.propTypes = {
|
||||||
|
packageName: PropTypes.string.isRequired,
|
||||||
|
packageVersion: PropTypes.string.isRequired,
|
||||||
|
availableVersions: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
filename: PropTypes.string.isRequired,
|
||||||
|
target: targetType.isRequired
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
/** @jsx jsx */
|
||||||
|
import { jsx } from '@emotion/core';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import VisuallyHidden from '@reach/visually-hidden';
|
||||||
|
import sortBy from 'sort-by';
|
||||||
|
|
||||||
|
import { formatBytes } from '../utils/format.js';
|
||||||
|
|
||||||
|
import { DirectoryIcon, CodeFileIcon } from './Icons.js';
|
||||||
|
|
||||||
|
const linkStyle = {
|
||||||
|
color: '#0076ff',
|
||||||
|
textDecoration: 'none',
|
||||||
|
':hover': {
|
||||||
|
textDecoration: 'underline'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableCellStyle = {
|
||||||
|
paddingTop: 6,
|
||||||
|
paddingRight: 3,
|
||||||
|
paddingBottom: 6,
|
||||||
|
paddingLeft: 3,
|
||||||
|
borderTop: '1px solid #eaecef'
|
||||||
|
};
|
||||||
|
|
||||||
|
const iconCellStyle = {
|
||||||
|
...tableCellStyle,
|
||||||
|
color: '#424242',
|
||||||
|
width: 17,
|
||||||
|
paddingRight: 2,
|
||||||
|
paddingLeft: 10,
|
||||||
|
'@media (max-width: 700px)': {
|
||||||
|
paddingLeft: 20
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const typeCellStyle = {
|
||||||
|
...tableCellStyle,
|
||||||
|
textAlign: 'right',
|
||||||
|
paddingRight: 10,
|
||||||
|
'@media (max-width: 700px)': {
|
||||||
|
paddingRight: 20
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getRelName(path, base) {
|
||||||
|
return path.substr(base.length > 1 ? base.length + 1 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DirectoryViewer({ path, details: entries }) {
|
||||||
|
const rows = [];
|
||||||
|
|
||||||
|
if (path !== '/') {
|
||||||
|
rows.push(
|
||||||
|
<tr key="..">
|
||||||
|
<td css={iconCellStyle} />
|
||||||
|
<td css={tableCellStyle}>
|
||||||
|
<a title="Parent directory" href="../" css={linkStyle}>
|
||||||
|
..
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td css={tableCellStyle}></td>
|
||||||
|
<td css={typeCellStyle}></td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { subdirs, files } = Object.keys(entries).reduce(
|
||||||
|
(memo, key) => {
|
||||||
|
const { subdirs, files } = memo;
|
||||||
|
const entry = entries[key];
|
||||||
|
|
||||||
|
if (entry.type === 'directory') {
|
||||||
|
subdirs.push(entry);
|
||||||
|
} else if (entry.type === 'file') {
|
||||||
|
files.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return memo;
|
||||||
|
},
|
||||||
|
{ subdirs: [], files: [] }
|
||||||
|
);
|
||||||
|
|
||||||
|
subdirs.sort(sortBy('path')).forEach(({ path: dirname }) => {
|
||||||
|
const relName = getRelName(dirname, path);
|
||||||
|
const href = relName + '/';
|
||||||
|
|
||||||
|
rows.push(
|
||||||
|
<tr key={relName}>
|
||||||
|
<td css={iconCellStyle}>
|
||||||
|
<DirectoryIcon />
|
||||||
|
</td>
|
||||||
|
<td css={tableCellStyle}>
|
||||||
|
<a title={relName} href={href} css={linkStyle}>
|
||||||
|
{relName}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td css={tableCellStyle}>-</td>
|
||||||
|
<td css={typeCellStyle}>-</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
files
|
||||||
|
.sort(sortBy('path'))
|
||||||
|
.forEach(({ path: filename, size, contentType }) => {
|
||||||
|
const relName = getRelName(filename, path);
|
||||||
|
const href = relName;
|
||||||
|
|
||||||
|
rows.push(
|
||||||
|
<tr key={relName}>
|
||||||
|
<td css={iconCellStyle}>
|
||||||
|
<CodeFileIcon />
|
||||||
|
</td>
|
||||||
|
<td css={tableCellStyle}>
|
||||||
|
<a title={relName} href={href} css={linkStyle}>
|
||||||
|
{relName}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td css={tableCellStyle}>{formatBytes(size)}</td>
|
||||||
|
<td css={typeCellStyle}>{contentType}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
css={{
|
||||||
|
border: '1px solid #dfe2e5',
|
||||||
|
borderRadius: 3,
|
||||||
|
borderTopWidth: 0,
|
||||||
|
'@media (max-width: 700px)': {
|
||||||
|
borderRightWidth: 0,
|
||||||
|
borderLeftWidth: 0
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<table
|
||||||
|
css={{
|
||||||
|
width: '100%',
|
||||||
|
borderCollapse: 'collapse',
|
||||||
|
borderRadius: 2,
|
||||||
|
background: '#fff'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<VisuallyHidden>Icon</VisuallyHidden>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<VisuallyHidden>Name</VisuallyHidden>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<VisuallyHidden>Size</VisuallyHidden>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<VisuallyHidden>Content Type</VisuallyHidden>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{rows}</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
DirectoryViewer.propTypes = {
|
||||||
|
path: PropTypes.string.isRequired,
|
||||||
|
details: PropTypes.objectOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
path: PropTypes.string.isRequired,
|
||||||
|
type: PropTypes.oneOf(['directory', 'file']).isRequired,
|
||||||
|
contentType: PropTypes.string, // file only
|
||||||
|
integrity: PropTypes.string, // file only
|
||||||
|
size: PropTypes.number // file only
|
||||||
|
})
|
||||||
|
).isRequired
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
/** @jsx jsx */
|
||||||
|
import { jsx } from '@emotion/core';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { formatBytes } from '../utils/format.js';
|
||||||
|
import { createHTML } from '../utils/markup.js';
|
||||||
|
|
||||||
|
import { usePackageInfo } from './PackageInfo.js';
|
||||||
|
|
||||||
|
function getBasename(path) {
|
||||||
|
const segments = path.split('/');
|
||||||
|
return segments[segments.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function ImageViewer({ path, uri }) {
|
||||||
|
return (
|
||||||
|
<div css={{ padding: 20, textAlign: 'center' }}>
|
||||||
|
<img title={getBasename(path)} src={uri} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CodeListing({ highlights }) {
|
||||||
|
const lines = highlights.slice(0);
|
||||||
|
const hasTrailingNewline = lines.length && lines[lines.length - 1] === '';
|
||||||
|
if (hasTrailingNewline) {
|
||||||
|
lines.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="code-listing"
|
||||||
|
css={{
|
||||||
|
overflowX: 'auto',
|
||||||
|
overflowY: 'hidden',
|
||||||
|
paddingTop: 5,
|
||||||
|
paddingBottom: 5
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<table
|
||||||
|
css={{
|
||||||
|
border: 'none',
|
||||||
|
borderCollapse: 'collapse',
|
||||||
|
borderSpacing: 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<tbody>
|
||||||
|
{lines.map((line, index) => {
|
||||||
|
const lineNumber = index + 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr key={index}>
|
||||||
|
<td
|
||||||
|
id={`L${lineNumber}`}
|
||||||
|
css={{
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 10,
|
||||||
|
color: 'rgba(27,31,35,.3)',
|
||||||
|
textAlign: 'right',
|
||||||
|
verticalAlign: 'top',
|
||||||
|
width: '1%',
|
||||||
|
minWidth: 50,
|
||||||
|
userSelect: 'none'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{lineNumber}</span>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
id={`LC${lineNumber}`}
|
||||||
|
css={{
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 10,
|
||||||
|
color: '#24292e',
|
||||||
|
whiteSpace: 'pre'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<code dangerouslySetInnerHTML={createHTML(line)} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{!hasTrailingNewline && (
|
||||||
|
<tr key="no-newline">
|
||||||
|
<td
|
||||||
|
css={{
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 10,
|
||||||
|
color: 'rgba(27,31,35,.3)',
|
||||||
|
textAlign: 'right',
|
||||||
|
verticalAlign: 'top',
|
||||||
|
width: '1%',
|
||||||
|
minWidth: 50,
|
||||||
|
userSelect: 'none'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
\
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
css={{
|
||||||
|
paddingLeft: 10,
|
||||||
|
color: 'rgba(27,31,35,.3)',
|
||||||
|
userSelect: 'none'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
No newline at end of file
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function BinaryViewer() {
|
||||||
|
return (
|
||||||
|
<div css={{ padding: 20 }}>
|
||||||
|
<p css={{ textAlign: 'center' }}>No preview available.</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FileViewer({ path, details }) {
|
||||||
|
const { packageName, packageVersion } = usePackageInfo();
|
||||||
|
const { highlights, uri, language, size } = details;
|
||||||
|
|
||||||
|
const segments = path.split('/');
|
||||||
|
const filename = segments[segments.length - 1];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
css={{
|
||||||
|
border: '1px solid #dfe2e5',
|
||||||
|
borderRadius: 3,
|
||||||
|
'@media (max-width: 700px)': {
|
||||||
|
borderRightWidth: 0,
|
||||||
|
borderLeftWidth: 0
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
css={{
|
||||||
|
padding: 10,
|
||||||
|
background: '#f6f8fa',
|
||||||
|
color: '#424242',
|
||||||
|
border: '1px solid #d1d5da',
|
||||||
|
borderTopLeftRadius: 3,
|
||||||
|
borderTopRightRadius: 3,
|
||||||
|
margin: '-1px -1px 0',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
'@media (max-width: 700px)': {
|
||||||
|
paddingRight: 20,
|
||||||
|
paddingLeft: 20
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{formatBytes(size)}</span> <span>{language}</span>{' '}
|
||||||
|
<a
|
||||||
|
title={filename}
|
||||||
|
href={`/${packageName}@${packageVersion}${path}`}
|
||||||
|
css={{
|
||||||
|
display: 'inline-block',
|
||||||
|
textDecoration: 'none',
|
||||||
|
padding: '2px 8px',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: '0.9rem',
|
||||||
|
color: '#24292e',
|
||||||
|
backgroundColor: '#eff3f6',
|
||||||
|
border: '1px solid rgba(27,31,35,.2)',
|
||||||
|
borderRadius: 3,
|
||||||
|
':hover': {
|
||||||
|
backgroundColor: '#e6ebf1',
|
||||||
|
borderColor: 'rgba(27,31,35,.35)'
|
||||||
|
},
|
||||||
|
':active': {
|
||||||
|
backgroundColor: '#e9ecef',
|
||||||
|
borderColor: 'rgba(27,31,35,.35)',
|
||||||
|
boxShadow: 'inset 0 0.15em 0.3em rgba(27,31,35,.15)'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
View Raw
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{highlights ? (
|
||||||
|
<CodeListing highlights={highlights} />
|
||||||
|
) : uri ? (
|
||||||
|
<ImageViewer path={path} uri={uri} />
|
||||||
|
) : (
|
||||||
|
<BinaryViewer />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
FileViewer.propTypes = {
|
||||||
|
path: PropTypes.string.isRequired,
|
||||||
|
details: PropTypes.shape({
|
||||||
|
contentType: PropTypes.string.isRequired,
|
||||||
|
highlights: PropTypes.arrayOf(PropTypes.string), // code
|
||||||
|
uri: PropTypes.string, // images
|
||||||
|
integrity: PropTypes.string.isRequired,
|
||||||
|
language: PropTypes.string.isRequired,
|
||||||
|
size: PropTypes.number.isRequired
|
||||||
|
}).isRequired
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/** @jsx jsx */
|
||||||
|
import { jsx } from '@emotion/core';
|
||||||
|
import { GoFileDirectory, GoFile } from 'react-icons/go';
|
||||||
|
import { FaTwitter, FaGithub } from 'react-icons/fa';
|
||||||
|
|
||||||
|
function createIcon(Type, { css, ...rest }) {
|
||||||
|
return <Type css={{ ...css, verticalAlign: 'text-bottom' }} {...rest} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DirectoryIcon(props) {
|
||||||
|
return createIcon(GoFileDirectory, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CodeFileIcon(props) {
|
||||||
|
return createIcon(GoFile, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TwitterIcon(props) {
|
||||||
|
return createIcon(FaTwitter, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GitHubIcon(props) {
|
||||||
|
return createIcon(FaGithub, props);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import React, { createContext, useContext } from 'react';
|
||||||
|
|
||||||
|
const Context = createContext();
|
||||||
|
|
||||||
|
export function PackageInfoProvider({ children, ...rest }) {
|
||||||
|
return <Context.Provider children={children} value={rest} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usePackageInfo() {
|
||||||
|
return useContext(Context);
|
||||||
|
}
|
After Width: | Height: | Size: 307 B |
After Width: | Height: | Size: 343 B |
|
@ -1,40 +1,45 @@
|
||||||
/** @jsx jsx */
|
/** @jsx jsx */
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Global, css, jsx } from '@emotion/core';
|
import { Global, css, jsx } from '@emotion/core';
|
||||||
|
import { Fragment, useEffect, useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import formatBytes from 'pretty-bytes';
|
import formatBytes from 'pretty-bytes';
|
||||||
import formatDate from 'date-fns/format';
|
import formatDate from 'date-fns/format';
|
||||||
import parseDate from 'date-fns/parse';
|
import parseDate from 'date-fns/parse';
|
||||||
|
|
||||||
import formatNumber from '../utils/formatNumber.js';
|
import { formatNumber, formatPercent } from '../utils/format.js';
|
||||||
import formatPercent from '../utils/formatPercent.js';
|
import { fontSans, fontMono } from '../utils/style.js';
|
||||||
|
|
||||||
import cloudflareLogo from './CloudflareLogo.png';
|
import { TwitterIcon, GitHubIcon } from './Icons.js';
|
||||||
import angularLogo from './AngularLogo.png';
|
import CloudflareLogo from './images/CloudflareLogo.png';
|
||||||
import googleCloudLogo from './GoogleCloudLogo.png';
|
import AngularLogo from './images/AngularLogo.png';
|
||||||
|
|
||||||
const globalStyles = css`
|
const globalStyles = css`
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#root {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-size: 14px;
|
${fontSans}
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
font-size: 16px;
|
||||||
Helvetica, Arial, sans-serif;
|
line-height: 1.5;
|
||||||
line-height: 1.7;
|
background: white;
|
||||||
padding: 5px 20px;
|
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 800px) {
|
code {
|
||||||
body {
|
${fontMono}
|
||||||
padding: 40px 20px 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a:link {
|
|
||||||
color: blue;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
a:visited {
|
|
||||||
color: rebeccapurple;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dd,
|
dd,
|
||||||
|
@ -42,23 +47,18 @@ const globalStyles = css`
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
padding-left: 25px;
|
padding-left: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const styles = {
|
const linkStyle = {
|
||||||
heading: {
|
color: '#0076ff',
|
||||||
margin: '0.8em 0',
|
textDecoration: 'none',
|
||||||
textTransform: 'uppercase',
|
':hover': {
|
||||||
textAlign: 'center',
|
textDecoration: 'underline'
|
||||||
fontSize: '5em'
|
|
||||||
},
|
|
||||||
subheading: {
|
|
||||||
fontSize: '1.6em'
|
|
||||||
},
|
|
||||||
example: {
|
|
||||||
textAlign: 'center',
|
|
||||||
backgroundColor: '#eee',
|
|
||||||
margin: '2em 0',
|
|
||||||
padding: '5px 0'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -90,59 +90,73 @@ function Stats({ data }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class App extends React.Component {
|
export default function App() {
|
||||||
constructor(props) {
|
const [stats, setStats] = useState(
|
||||||
super(props);
|
typeof window === 'object' &&
|
||||||
|
window.localStorage &&
|
||||||
|
window.localStorage.savedStats
|
||||||
|
? JSON.parse(window.localStorage.savedStats)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
const hasStats = !!(stats && !stats.error);
|
||||||
|
const stringStats = JSON.stringify(stats);
|
||||||
|
|
||||||
this.state = { stats: null };
|
useEffect(() => {
|
||||||
|
window.localStorage.savedStats = stringStats;
|
||||||
|
}, [stringStats]);
|
||||||
|
|
||||||
if (typeof window === 'object' && window.localStorage) {
|
useEffect(() => {
|
||||||
const savedStats = window.localStorage.savedStats;
|
|
||||||
|
|
||||||
if (savedStats) {
|
|
||||||
this.state.stats = JSON.parse(savedStats);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onbeforeunload = () => {
|
|
||||||
window.localStorage.savedStats = JSON.stringify(this.state.stats);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
// Refresh latest stats.
|
|
||||||
fetch('/api/stats?period=last-month')
|
fetch('/api/stats?period=last-month')
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(stats => this.setState({ stats }));
|
.then(setStats);
|
||||||
}
|
}, []);
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const { stats } = this.state;
|
<Fragment>
|
||||||
const hasStats = !!(stats && !stats.error);
|
<div
|
||||||
|
css={{
|
||||||
return (
|
maxWidth: 740,
|
||||||
<div css={{ maxWidth: 700, margin: '0 auto' }}>
|
margin: '0 auto',
|
||||||
|
padding: '0 20px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Global styles={globalStyles} />
|
<Global styles={globalStyles} />
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<h1 css={styles.heading}>unpkg</h1>
|
<h1
|
||||||
|
css={{
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
textAlign: 'center',
|
||||||
|
fontSize: '5em'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
unpkg
|
||||||
|
</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
unpkg is a fast, global{' '}
|
unpkg is a fast, global content delivery network for everything on{' '}
|
||||||
<a href="https://en.wikipedia.org/wiki/Content_delivery_network">
|
<a href="https://www.npmjs.com/" css={linkStyle}>
|
||||||
content delivery network
|
npm
|
||||||
</a>{' '}
|
</a>
|
||||||
for everything on <a href="https://www.npmjs.com/">npm</a>. Use it
|
. Use it to quickly and easily load any file from any package using
|
||||||
to quickly and easily load any file from any package using a URL
|
a URL like:
|
||||||
like:
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div css={styles.example}>unpkg.com/:package@:version/:file</div>
|
<div
|
||||||
|
css={{
|
||||||
|
textAlign: 'center',
|
||||||
|
backgroundColor: '#eee',
|
||||||
|
margin: '2em 0',
|
||||||
|
padding: '5px 0'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
unpkg.com/:package@:version/:file
|
||||||
|
</div>
|
||||||
|
|
||||||
{hasStats && <Stats data={stats} />}
|
{hasStats && <Stats data={stats} />}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<h3 css={styles.subheading} id="examples">
|
<h3 css={{ fontSize: '1.6em' }} id="examples">
|
||||||
Examples
|
Examples
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
@ -150,12 +164,20 @@ export default class App extends React.Component {
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/react@16.7.0/umd/react.production.min.js">
|
<a
|
||||||
|
title="react.production.min.js"
|
||||||
|
href="/react@16.7.0/umd/react.production.min.js"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
unpkg.com/react@16.7.0/umd/react.production.min.js
|
unpkg.com/react@16.7.0/umd/react.production.min.js
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/react-dom@16.7.0/umd/react-dom.production.min.js">
|
<a
|
||||||
|
title="react-dom.production.min.js"
|
||||||
|
href="/react-dom@16.7.0/umd/react-dom.production.min.js"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
unpkg.com/react-dom@16.7.0/umd/react-dom.production.min.js
|
unpkg.com/react-dom@16.7.0/umd/react-dom.production.min.js
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -163,20 +185,41 @@ export default class App extends React.Component {
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You may also use a{' '}
|
You may also use a{' '}
|
||||||
<a href="https://docs.npmjs.com/misc/semver">semver range</a> or a{' '}
|
<a
|
||||||
<a href="https://docs.npmjs.com/cli/dist-tag">tag</a> instead of a
|
title="semver"
|
||||||
fixed version number, or omit the version/tag entirely to use the{' '}
|
href="https://docs.npmjs.com/misc/semver"
|
||||||
<code>latest</code> tag.
|
css={linkStyle}
|
||||||
|
>
|
||||||
|
semver range
|
||||||
|
</a>{' '}
|
||||||
|
or a{' '}
|
||||||
|
<a
|
||||||
|
title="tags"
|
||||||
|
href="https://docs.npmjs.com/cli/dist-tag"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
|
tag
|
||||||
|
</a>{' '}
|
||||||
|
instead of a fixed version number, or omit the version/tag entirely to
|
||||||
|
use the <code>latest</code> tag.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/react@^16/umd/react.production.min.js">
|
<a
|
||||||
|
title="react.production.min.js"
|
||||||
|
href="/react@^16/umd/react.production.min.js"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
unpkg.com/react@^16/umd/react.production.min.js
|
unpkg.com/react@^16/umd/react.production.min.js
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/react/umd/react.production.min.js">
|
<a
|
||||||
|
title="react.production.min.js"
|
||||||
|
href="/react/umd/react.production.min.js"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
unpkg.com/react/umd/react.production.min.js
|
unpkg.com/react/umd/react.production.min.js
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -190,10 +233,14 @@ export default class App extends React.Component {
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/jquery">unpkg.com/jquery</a>
|
<a title="jQuery" href="/jquery" css={linkStyle}>
|
||||||
|
unpkg.com/jquery
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/three">unpkg.com/three</a>
|
<a title="Three.js" href="/three" css={linkStyle}>
|
||||||
|
unpkg.com/three
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@ -204,14 +251,26 @@ export default class App extends React.Component {
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/react/">unpkg.com/react/</a>
|
<a
|
||||||
|
title="Index of the react package"
|
||||||
|
href="/react/"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
|
unpkg.com/react/
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/lodash/">unpkg.com/lodash/</a>
|
<a
|
||||||
|
title="Index of the react-router package"
|
||||||
|
href="/react-router/"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
|
unpkg.com/react-router/
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3 css={styles.subheading} id="query-params">
|
<h3 css={{ fontSize: '1.6em' }} id="query-params">
|
||||||
Query Parameters
|
Query Parameters
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
@ -229,7 +288,11 @@ export default class App extends React.Component {
|
||||||
</dt>
|
</dt>
|
||||||
<dd>
|
<dd>
|
||||||
Expands all{' '}
|
Expands all{' '}
|
||||||
<a href="https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier">
|
<a
|
||||||
|
title="bare import specifiers"
|
||||||
|
href="https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
“bare” <code>import</code> specifiers
|
“bare” <code>import</code> specifiers
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
in JavaScript modules to unpkg URLs. This feature is{' '}
|
in JavaScript modules to unpkg URLs. This feature is{' '}
|
||||||
|
@ -237,7 +300,7 @@ export default class App extends React.Component {
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<h3 css={styles.subheading} id="cache-behavior">
|
<h3 css={{ fontSize: '1.6em' }} id="cache-behavior">
|
||||||
Cache Behavior
|
Cache Behavior
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
@ -255,8 +318,14 @@ export default class App extends React.Component {
|
||||||
URLs that do not specify a package version number redirect to one that
|
URLs that do not specify a package version number redirect to one that
|
||||||
does. This is the <code>latest</code> version when no version is
|
does. This is the <code>latest</code> version when no version is
|
||||||
specified, or the <code>maxSatisfying</code> version when a{' '}
|
specified, or the <code>maxSatisfying</code> version when a{' '}
|
||||||
<a href="https://github.com/npm/node-semver">semver version</a> is
|
<a
|
||||||
given. Redirects are cached for 10 minutes at the CDN, 1 minute in
|
title="semver"
|
||||||
|
href="https://github.com/npm/node-semver"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
|
semver version
|
||||||
|
</a>{' '}
|
||||||
|
is given. Redirects are cached for 10 minutes at the CDN, 1 minute in
|
||||||
browsers.
|
browsers.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
@ -267,15 +336,18 @@ export default class App extends React.Component {
|
||||||
latest version and redirect them.
|
latest version and redirect them.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 css={styles.subheading} id="workflow">
|
<h3 css={{ fontSize: '1.6em' }} id="workflow">
|
||||||
Workflow
|
Workflow
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
For npm package authors, unpkg relieves the burden of publishing your
|
For npm package authors, unpkg relieves the burden of publishing your
|
||||||
code to a CDN in addition to the npm registry. All you need to do is
|
code to a CDN in addition to the npm registry. All you need to do is
|
||||||
include your <a href="https://github.com/umdjs/umd">UMD</a> build in
|
include your{' '}
|
||||||
your npm package (not your repo, that's different!).
|
<a title="UMD" href="https://github.com/umdjs/umd" css={linkStyle}>
|
||||||
|
UMD
|
||||||
|
</a>{' '}
|
||||||
|
build in your npm package (not your repo, that's different!).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>You can do this easily using the following setup:</p>
|
<p>You can do this easily using the following setup:</p>
|
||||||
|
@ -287,7 +359,11 @@ export default class App extends React.Component {
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Add the <code>umd</code> directory to your{' '}
|
Add the <code>umd</code> directory to your{' '}
|
||||||
<a href="https://docs.npmjs.com/files/package.json#files">
|
<a
|
||||||
|
title="package.json files array"
|
||||||
|
href="https://docs.npmjs.com/files/package.json#files"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
files array
|
files array
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
in <code>package.json</code>
|
in <code>package.json</code>
|
||||||
|
@ -303,24 +379,50 @@ export default class App extends React.Component {
|
||||||
a version available on unpkg as well.
|
a version available on unpkg as well.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 css={styles.subheading} id="about">
|
<h3 css={{ fontSize: '1.6em' }} id="about">
|
||||||
About
|
About
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
unpkg is an <a href="https://github.com/unpkg">open source</a> project
|
unpkg is an{' '}
|
||||||
built and maintained by{' '}
|
<a
|
||||||
<a href="https://twitter.com/mjackson">Michael Jackson</a>. unpkg is
|
title="unpkg on GitHub"
|
||||||
not affiliated with or supported by npm, Inc. in any way. Please do
|
href="https://github.com/unpkg"
|
||||||
not contact npm for help with unpkg. Instead, please reach out to{' '}
|
css={linkStyle}
|
||||||
<a href="https://twitter.com/unpkg">@unpkg</a> with any questions or
|
>
|
||||||
concerns.
|
open source
|
||||||
|
</a>{' '}
|
||||||
|
project built and maintained by{' '}
|
||||||
|
<a
|
||||||
|
title="mjackson on Twitter"
|
||||||
|
href="https://twitter.com/mjackson"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
|
Michael Jackson
|
||||||
|
</a>
|
||||||
|
. unpkg is not affiliated with or supported by npm, Inc. in any way.
|
||||||
|
Please do not contact npm for help with unpkg. Instead, please reach
|
||||||
|
out to{' '}
|
||||||
|
<a
|
||||||
|
title="unpkg on Twitter"
|
||||||
|
href="https://twitter.com/unpkg"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
|
@unpkg
|
||||||
|
</a>{' '}
|
||||||
|
with any questions or concerns.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The unpkg CDN is powered by{' '}
|
The unpkg CDN is powered by{' '}
|
||||||
<a href="https://www.cloudflare.com">Cloudflare</a>, one of the
|
<a
|
||||||
world's largest and fastest cloud network platforms.{' '}
|
title="Cloudflare"
|
||||||
|
href="https://www.cloudflare.com"
|
||||||
|
css={linkStyle}
|
||||||
|
>
|
||||||
|
Cloudflare
|
||||||
|
</a>
|
||||||
|
, one of the world's largest and fastest cloud network platforms.{' '}
|
||||||
{hasStats && (
|
{hasStats && (
|
||||||
<span>
|
<span>
|
||||||
In the past month, Cloudflare served over{' '}
|
In the past month, Cloudflare served over{' '}
|
||||||
|
@ -339,19 +441,27 @@ export default class App extends React.Component {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AboutLogo>
|
<AboutLogo>
|
||||||
<a href="https://www.cloudflare.com">
|
<a title="Cloudflare" href="https://www.cloudflare.com">
|
||||||
<AboutLogoImage src={cloudflareLogo} height="100" />
|
<AboutLogoImage src={CloudflareLogo} height="100" />
|
||||||
</a>
|
</a>
|
||||||
</AboutLogo>
|
</AboutLogo>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The origin servers for unpkg are powered by{' '}
|
The origin servers for unpkg are powered by{' '}
|
||||||
<a href="https://cloud.google.com/">Google Cloud</a> and made possible
|
<a
|
||||||
by a generous donation from the{' '}
|
title="Google Cloud"
|
||||||
<a href="https://angular.io">Angular web framework</a>, one of the
|
href="https://cloud.google.com/"
|
||||||
world's most popular libraries for building incredible user
|
css={linkStyle}
|
||||||
experiences on both desktop and mobile.
|
>
|
||||||
|
Google Cloud
|
||||||
|
</a>{' '}
|
||||||
|
and made possible by a generous donation from the{' '}
|
||||||
|
<a title="Angular" href="https://angular.io" css={linkStyle}>
|
||||||
|
Angular web framework
|
||||||
|
</a>
|
||||||
|
, one of the world's most popular libraries for building
|
||||||
|
incredible user experiences on both desktop and mobile.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -362,37 +472,61 @@ export default class App extends React.Component {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AboutLogo>
|
<AboutLogo>
|
||||||
<a href="https://angular.io">
|
<a title="Angular" href="https://angular.io">
|
||||||
<AboutLogoImage src={angularLogo} width="200" />
|
<AboutLogoImage src={AngularLogo} width="200" />
|
||||||
</a>
|
</a>
|
||||||
</AboutLogo>
|
</AboutLogo>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<footer
|
<footer
|
||||||
|
css={{
|
||||||
|
marginTop: '5rem',
|
||||||
|
background: 'black',
|
||||||
|
color: '#aaa'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
css={{
|
css={{
|
||||||
marginTop: '10em',
|
maxWidth: 740,
|
||||||
color: '#aaa'
|
padding: '10px 20px',
|
||||||
|
margin: '0 auto',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<p css={{ textAlign: 'center' }}>
|
<p>© {new Date().getFullYear()} UNPKG</p>
|
||||||
© {new Date().getFullYear()} unpkg — powered
|
<p css={{ fontSize: '1.5rem' }}>
|
||||||
by{' '}
|
<a
|
||||||
<a href="https://cloud.google.com/">
|
title="Twitter"
|
||||||
<img
|
href="https://twitter.com/unpkg"
|
||||||
src={googleCloudLogo}
|
css={{
|
||||||
height="32"
|
color: '#aaa',
|
||||||
css={{
|
display: 'inline-block',
|
||||||
verticalAlign: 'middle',
|
':hover': { color: 'white' }
|
||||||
marginTop: -2,
|
}}
|
||||||
marginLeft: -10
|
>
|
||||||
}}
|
<TwitterIcon />
|
||||||
/>
|
</a>
|
||||||
|
<a
|
||||||
|
title="GitHub"
|
||||||
|
href="https://github.com/mjackson/unpkg"
|
||||||
|
css={{
|
||||||
|
color: '#aaa',
|
||||||
|
display: 'inline-block',
|
||||||
|
marginLeft: '1rem',
|
||||||
|
':hover': { color: 'white' }
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GitHubIcon />
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</div>
|
||||||
</div>
|
</footer>
|
||||||
);
|
</Fragment>
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
|
Before Width: | Height: | Size: 25 KiB |
|
@ -0,0 +1,15 @@
|
||||||
|
/** @jsx jsx */
|
||||||
|
import { jsx } from '@emotion/core';
|
||||||
|
import { FaTwitter, FaGithub } from 'react-icons/fa';
|
||||||
|
|
||||||
|
function createIcon(Type, { css, ...rest }) {
|
||||||
|
return <Type css={{ ...css, verticalAlign: 'text-bottom' }} {...rest} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TwitterIcon(props) {
|
||||||
|
return createIcon(FaTwitter, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GitHubIcon(props) {
|
||||||
|
return createIcon(FaGithub, props);
|
||||||
|
}
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
@ -0,0 +1,18 @@
|
||||||
|
import formatBytes from 'pretty-bytes';
|
||||||
|
|
||||||
|
export { formatBytes };
|
||||||
|
|
||||||
|
export function formatNumber(n) {
|
||||||
|
const digits = String(n).split('');
|
||||||
|
const groups = [];
|
||||||
|
|
||||||
|
while (digits.length) {
|
||||||
|
groups.unshift(digits.splice(-3).join(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatPercent(n, decimals = 1) {
|
||||||
|
return (n * 100).toPrecision(decimals + 2);
|
||||||
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
export default function formatNumber(n) {
|
|
||||||
const digits = String(n).split('');
|
|
||||||
const groups = [];
|
|
||||||
|
|
||||||
while (digits.length) {
|
|
||||||
groups.unshift(digits.splice(-3).join(''));
|
|
||||||
}
|
|
||||||
|
|
||||||
return groups.join(',');
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
export default function formatPercent(n, decimals = 1) {
|
|
||||||
return (n * 100).toPrecision(decimals + 2);
|
|
||||||
}
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function createHTML(content) {
|
||||||
|
return { __html: content };
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
export const fontSans = `
|
||||||
|
font-family: -apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
"Segoe UI",
|
||||||
|
"Roboto",
|
||||||
|
"Oxygen",
|
||||||
|
"Ubuntu",
|
||||||
|
"Cantarell",
|
||||||
|
"Fira Sans",
|
||||||
|
"Droid Sans",
|
||||||
|
"Helvetica Neue",
|
||||||
|
sans-serif;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const fontMono = `
|
||||||
|
font-family: Menlo,
|
||||||
|
Monaco,
|
||||||
|
Lucida Console,
|
||||||
|
Liberation Mono,
|
||||||
|
DejaVu Sans Mono,
|
||||||
|
Bitstream Vera Sans Mono,
|
||||||
|
Courier New,
|
||||||
|
monospace;
|
||||||
|
`;
|
|
@ -1,49 +1,148 @@
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
|
||||||
|
import serveDirectoryBrowser from './actions/serveDirectoryBrowser.js';
|
||||||
|
import serveDirectoryMetadata from './actions/serveDirectoryMetadata.js';
|
||||||
|
import serveFileBrowser from './actions/serveFileBrowser.js';
|
||||||
|
import serveFileMetadata from './actions/serveFileMetadata.js';
|
||||||
import serveFile from './actions/serveFile.js';
|
import serveFile from './actions/serveFile.js';
|
||||||
import serveMainPage from './actions/serveMainPage.js';
|
import serveMainPage from './actions/serveMainPage.js';
|
||||||
|
import serveModule from './actions/serveModule.js';
|
||||||
import serveStats from './actions/serveStats.js';
|
import serveStats from './actions/serveStats.js';
|
||||||
|
|
||||||
import cors from './middleware/cors.js';
|
import cors from './middleware/cors.js';
|
||||||
import fetchPackage from './middleware/fetchPackage.js';
|
import findEntry from './middleware/findEntry.js';
|
||||||
import findFile from './middleware/findFile.js';
|
|
||||||
import logger from './middleware/logger.js';
|
import logger from './middleware/logger.js';
|
||||||
import redirectLegacyURLs from './middleware/redirectLegacyURLs.js';
|
import redirectLegacyURLs from './middleware/redirectLegacyURLs.js';
|
||||||
import staticFiles from './middleware/staticFiles.js';
|
import staticFiles from './middleware/staticFiles.js';
|
||||||
|
import validateFilename from './middleware/validateFilename.js';
|
||||||
import validatePackageURL from './middleware/validatePackageURL.js';
|
import validatePackageURL from './middleware/validatePackageURL.js';
|
||||||
import validatePackageName from './middleware/validatePackageName.js';
|
import validatePackageName from './middleware/validatePackageName.js';
|
||||||
import validateQuery from './middleware/validateQuery.js';
|
import validateQuery from './middleware/validateQuery.js';
|
||||||
|
import validateVersion from './middleware/validateVersion.js';
|
||||||
|
|
||||||
export default function createServer() {
|
function createApp(callback) {
|
||||||
const app = express();
|
const app = express();
|
||||||
|
callback(app);
|
||||||
app.disable('x-powered-by');
|
|
||||||
app.enable('trust proxy');
|
|
||||||
|
|
||||||
app.use(logger);
|
|
||||||
app.use(cors);
|
|
||||||
app.use(staticFiles);
|
|
||||||
|
|
||||||
// Special startup request from App Engine
|
|
||||||
// https://cloud.google.com/appengine/docs/standard/nodejs/how-instances-are-managed
|
|
||||||
app.get('/_ah/start', (req, res) => {
|
|
||||||
res.status(200).end();
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/', serveMainPage);
|
|
||||||
app.get('/api/stats', serveStats);
|
|
||||||
|
|
||||||
app.use(redirectLegacyURLs);
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
'*',
|
|
||||||
validatePackageURL,
|
|
||||||
validatePackageName,
|
|
||||||
validateQuery,
|
|
||||||
fetchPackage,
|
|
||||||
findFile,
|
|
||||||
serveFile
|
|
||||||
);
|
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function createServer() {
|
||||||
|
return createApp(app => {
|
||||||
|
app.disable('x-powered-by');
|
||||||
|
app.enable('trust proxy');
|
||||||
|
app.enable('strict routing');
|
||||||
|
|
||||||
|
app.use(logger);
|
||||||
|
app.use(cors);
|
||||||
|
app.use(staticFiles);
|
||||||
|
|
||||||
|
// Special startup request from App Engine
|
||||||
|
// https://cloud.google.com/appengine/docs/standard/nodejs/how-instances-are-managed
|
||||||
|
app.get('/_ah/start', (req, res) => {
|
||||||
|
res.status(200).end();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/', serveMainPage);
|
||||||
|
app.get('/api/stats', serveStats);
|
||||||
|
|
||||||
|
app.use(redirectLegacyURLs);
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
'/browse',
|
||||||
|
createApp(app => {
|
||||||
|
app.enable('strict routing');
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
'*/',
|
||||||
|
validatePackageURL,
|
||||||
|
validatePackageName,
|
||||||
|
validateQuery,
|
||||||
|
validateVersion,
|
||||||
|
serveDirectoryBrowser
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
'*',
|
||||||
|
validatePackageURL,
|
||||||
|
validatePackageName,
|
||||||
|
validateQuery,
|
||||||
|
validateVersion,
|
||||||
|
serveFileBrowser
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// We need to route in this weird way because Express
|
||||||
|
// doesn't have a way to route based on query params.
|
||||||
|
const metadataApp = createApp(app => {
|
||||||
|
app.enable('strict routing');
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
'*/',
|
||||||
|
validatePackageURL,
|
||||||
|
validatePackageName,
|
||||||
|
validateQuery,
|
||||||
|
validateVersion,
|
||||||
|
validateFilename,
|
||||||
|
serveDirectoryMetadata
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
'*',
|
||||||
|
validatePackageURL,
|
||||||
|
validatePackageName,
|
||||||
|
validateQuery,
|
||||||
|
validateVersion,
|
||||||
|
validateFilename,
|
||||||
|
serveFileMetadata
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
if (req.query.meta != null) {
|
||||||
|
metadataApp(req, res);
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const moduleApp = createApp(app => {
|
||||||
|
app.enable('strict routing');
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
'*',
|
||||||
|
validatePackageURL,
|
||||||
|
validatePackageName,
|
||||||
|
validateQuery,
|
||||||
|
validateVersion,
|
||||||
|
validateFilename,
|
||||||
|
findEntry,
|
||||||
|
serveModule
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
if (req.query.module != null) {
|
||||||
|
moduleApp(req, res);
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send old */ requests to the new /browse UI.
|
||||||
|
app.get('*/', (req, res) => {
|
||||||
|
res.redirect(302, '/browse' + req.url);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
'*',
|
||||||
|
validatePackageURL,
|
||||||
|
validatePackageName,
|
||||||
|
validateQuery,
|
||||||
|
validateVersion,
|
||||||
|
validateFilename,
|
||||||
|
findEntry,
|
||||||
|
serveFile
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -9,13 +9,8 @@ import getIntegrity from '../utils/getIntegrity.js';
|
||||||
import getContentType from '../utils/getContentType.js';
|
import getContentType from '../utils/getContentType.js';
|
||||||
import bufferStream from '../utils/bufferStream.js';
|
import bufferStream from '../utils/bufferStream.js';
|
||||||
|
|
||||||
const leadingSlashes = /^\/*/;
|
|
||||||
const multipleSlashes = /\/*/;
|
|
||||||
const trailingSlashes = /\/*$/;
|
|
||||||
const leadingSegment = /^[^/]+\/?/;
|
|
||||||
|
|
||||||
function fileRedirect(req, res, entry) {
|
function fileRedirect(req, res, entry) {
|
||||||
// Redirect to the file with the extension so it's more
|
// Redirect to the file with the extension so it's
|
||||||
// clear which file is being served.
|
// clear which file is being served.
|
||||||
res
|
res
|
||||||
.set({
|
.set({
|
||||||
|
@ -27,7 +22,7 @@ function fileRedirect(req, res, entry) {
|
||||||
createPackageURL(
|
createPackageURL(
|
||||||
req.packageName,
|
req.packageName,
|
||||||
req.packageVersion,
|
req.packageVersion,
|
||||||
entry.name.replace(leadingSlashes, '/'),
|
entry.path,
|
||||||
createSearch(req.query)
|
createSearch(req.query)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -46,7 +41,7 @@ function indexRedirect(req, res, entry) {
|
||||||
createPackageURL(
|
createPackageURL(
|
||||||
req.packageName,
|
req.packageName,
|
||||||
req.packageVersion,
|
req.packageVersion,
|
||||||
entry.name.replace(leadingSlashes, '/'),
|
entry.path,
|
||||||
createSearch(req.query)
|
createSearch(req.query)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -57,16 +52,17 @@ function indexRedirect(req, res, entry) {
|
||||||
* Follows node's resolution algorithm.
|
* Follows node's resolution algorithm.
|
||||||
* https://nodejs.org/api/modules.html#modules_all_together
|
* https://nodejs.org/api/modules.html#modules_all_together
|
||||||
*/
|
*/
|
||||||
function searchEntries(stream, entryName, wantsIndex) {
|
function searchEntries(stream, filename) {
|
||||||
|
// filename = /some/file/name.js or /some/dir/name
|
||||||
return new Promise((accept, reject) => {
|
return new Promise((accept, reject) => {
|
||||||
const jsEntryName = `${entryName}.js`;
|
const jsEntryFilename = `${filename}.js`;
|
||||||
const jsonEntryName = `${entryName}.json`;
|
const jsonEntryFilename = `${filename}.json`;
|
||||||
const entries = {};
|
|
||||||
|
|
||||||
|
const matchingEntries = {};
|
||||||
let foundEntry;
|
let foundEntry;
|
||||||
|
|
||||||
if (entryName === '') {
|
if (filename === '/') {
|
||||||
foundEntry = entries[''] = { name: '', type: 'directory' };
|
foundEntry = matchingEntries['/'] = { name: '/', type: 'directory' };
|
||||||
}
|
}
|
||||||
|
|
||||||
stream
|
stream
|
||||||
|
@ -79,41 +75,43 @@ function searchEntries(stream, entryName, wantsIndex) {
|
||||||
// so we shorten that to just `index.js` here. A few packages use a
|
// so we shorten that to just `index.js` here. A few packages use a
|
||||||
// prefix other than `package/`. e.g. the firebase package uses the
|
// prefix other than `package/`. e.g. the firebase package uses the
|
||||||
// `firebase_npm/` prefix. So we just strip the first dir name.
|
// `firebase_npm/` prefix. So we just strip the first dir name.
|
||||||
name: header.name.replace(leadingSegment, ''),
|
path: header.name.replace(/^[^/]+/g, ''),
|
||||||
type: header.type
|
type: header.type
|
||||||
};
|
};
|
||||||
|
|
||||||
// Skip non-files and files that don't match the entryName.
|
// Skip non-files and files that don't match the entryName.
|
||||||
if (entry.type !== 'file' || entry.name.indexOf(entryName) !== 0) {
|
if (entry.type !== 'file' || !entry.path.startsWith(filename)) {
|
||||||
stream.resume();
|
stream.resume();
|
||||||
stream.on('end', next);
|
stream.on('end', next);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
entries[entry.name] = entry;
|
matchingEntries[entry.path] = entry;
|
||||||
|
|
||||||
// Dynamically create "directory" entries for all directories
|
// Dynamically create "directory" entries for all directories
|
||||||
// that are in this file's path. Some tarballs omit these entries
|
// that are in this file's path. Some tarballs omit these entries
|
||||||
// for some reason, so this is the "brute force" method.
|
// for some reason, so this is the "brute force" method.
|
||||||
let dir = path.dirname(entry.name);
|
let dir = path.dirname(entry.path);
|
||||||
while (dir !== '.') {
|
while (dir !== '/') {
|
||||||
entries[dir] = entries[dir] || { name: dir, type: 'directory' };
|
if (!matchingEntries[dir]) {
|
||||||
|
matchingEntries[dir] = { name: dir, type: 'directory' };
|
||||||
|
}
|
||||||
dir = path.dirname(dir);
|
dir = path.dirname(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
entry.name === entryName ||
|
entry.path === filename ||
|
||||||
// Allow accessing e.g. `/index.js` or `/index.json`
|
// Allow accessing e.g. `/index.js` or `/index.json`
|
||||||
// using `/index` for compatibility with npm
|
// using `/index` for compatibility with npm
|
||||||
(!wantsIndex && entry.name === jsEntryName) ||
|
entry.path === jsEntryFilename ||
|
||||||
(!wantsIndex && entry.name === jsonEntryName)
|
entry.path === jsonEntryFilename
|
||||||
) {
|
) {
|
||||||
if (foundEntry) {
|
if (foundEntry) {
|
||||||
if (
|
if (
|
||||||
foundEntry.name !== entryName &&
|
foundEntry.path !== filename &&
|
||||||
(entry.name === entryName ||
|
(entry.path === filename ||
|
||||||
(entry.name === jsEntryName &&
|
(entry.path === jsEntryFilename &&
|
||||||
foundEntry.name === jsonEntryName))
|
foundEntry.path === jsonEntryFilename))
|
||||||
) {
|
) {
|
||||||
// This entry is higher priority than the one
|
// This entry is higher priority than the one
|
||||||
// we already found. Replace it.
|
// we already found. Replace it.
|
||||||
|
@ -127,9 +125,7 @@ function searchEntries(stream, entryName, wantsIndex) {
|
||||||
|
|
||||||
const content = await bufferStream(stream);
|
const content = await bufferStream(stream);
|
||||||
|
|
||||||
// Set some extra properties for files that we will
|
entry.contentType = getContentType(entry.path);
|
||||||
// need to serve them and for ?meta listings.
|
|
||||||
entry.contentType = getContentType(entry.name);
|
|
||||||
entry.integrity = getIntegrity(content);
|
entry.integrity = getIntegrity(content);
|
||||||
entry.lastModified = header.mtime.toUTCString();
|
entry.lastModified = header.mtime.toUTCString();
|
||||||
entry.size = content.length;
|
entry.size = content.length;
|
||||||
|
@ -144,10 +140,10 @@ function searchEntries(stream, entryName, wantsIndex) {
|
||||||
})
|
})
|
||||||
.on('finish', () => {
|
.on('finish', () => {
|
||||||
accept({
|
accept({
|
||||||
entries,
|
|
||||||
// If we didn't find a matching file entry,
|
// If we didn't find a matching file entry,
|
||||||
// try a directory entry with the same name.
|
// try a directory entry with the same name.
|
||||||
foundEntry: foundEntry || entries[entryName] || null
|
foundEntry: foundEntry || matchingEntries[filename] || null,
|
||||||
|
matchingEntries: matchingEntries
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -157,23 +153,14 @@ function searchEntries(stream, entryName, wantsIndex) {
|
||||||
* Fetch and search the archive to try and find the requested file.
|
* Fetch and search the archive to try and find the requested file.
|
||||||
* Redirect to the "index" file if a directory was requested.
|
* Redirect to the "index" file if a directory was requested.
|
||||||
*/
|
*/
|
||||||
export default async function findFile(req, res, next) {
|
export default async function findEntry(req, res, next) {
|
||||||
const wantsIndex = req.filename.endsWith('/');
|
|
||||||
|
|
||||||
// The name of the file/directory we're looking for.
|
|
||||||
const entryName = req.filename
|
|
||||||
.replace(multipleSlashes, '/')
|
|
||||||
.replace(trailingSlashes, '')
|
|
||||||
.replace(leadingSlashes, '');
|
|
||||||
|
|
||||||
const stream = await getPackage(req.packageName, req.packageVersion);
|
const stream = await getPackage(req.packageName, req.packageVersion);
|
||||||
const { entries, foundEntry } = await searchEntries(
|
const { foundEntry: entry, matchingEntries: entries } = await searchEntries(
|
||||||
stream,
|
stream,
|
||||||
entryName,
|
req.filename
|
||||||
wantsIndex
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!foundEntry) {
|
if (!entry) {
|
||||||
return res
|
return res
|
||||||
.status(404)
|
.status(404)
|
||||||
.set({
|
.set({
|
||||||
|
@ -184,18 +171,17 @@ export default async function findFile(req, res, next) {
|
||||||
.send(`Cannot find "${req.filename}" in ${req.packageSpec}`);
|
.send(`Cannot find "${req.filename}" in ${req.packageSpec}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundEntry.type === 'file' && foundEntry.name !== entryName) {
|
if (entry.type === 'file' && entry.path !== req.filename) {
|
||||||
return fileRedirect(req, res, foundEntry);
|
return fileRedirect(req, res, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the foundEntry is a directory and there is no trailing slash
|
if (entry.type === 'directory') {
|
||||||
// on the request path, we need to redirect to some "index" file
|
// We need to redirect to some "index" file inside the directory so
|
||||||
// inside that directory. This is so our URLs work in a similar way
|
// our URLs work in a similar way to require("lib") in node where it
|
||||||
// to require("lib") in node where it searches for `lib/index.js`
|
// uses `lib/index.js` when `lib` is a directory.
|
||||||
// and `lib/index.json` when `lib` is a directory.
|
|
||||||
if (foundEntry.type === 'directory' && !wantsIndex) {
|
|
||||||
const indexEntry =
|
const indexEntry =
|
||||||
entries[`${entryName}/index.js`] || entries[`${entryName}/index.json`];
|
entries[`${req.filename}/index.js`] ||
|
||||||
|
entries[`${req.filename}/index.json`];
|
||||||
|
|
||||||
if (indexEntry && indexEntry.type === 'file') {
|
if (indexEntry && indexEntry.type === 'file') {
|
||||||
return indexRedirect(req, res, indexEntry);
|
return indexRedirect(req, res, indexEntry);
|
||||||
|
@ -211,8 +197,7 @@ export default async function findFile(req, res, next) {
|
||||||
.send(`Cannot find an index in "${req.filename}" in ${req.packageSpec}`);
|
.send(`Cannot find an index in "${req.filename}" in ${req.packageSpec}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
req.entries = entries;
|
req.entry = entry;
|
||||||
req.entry = foundEntry;
|
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
|
@ -1,18 +1,5 @@
|
||||||
import createPackageURL from '../utils/createPackageURL.js';
|
import createPackageURL from '../utils/createPackageURL.js';
|
||||||
import createSearch from '../utils/createSearch.js';
|
import createSearch from '../utils/createSearch.js';
|
||||||
import { getPackageConfig, resolveVersion } from '../utils/npm.js';
|
|
||||||
|
|
||||||
function semverRedirect(req, res, newVersion) {
|
|
||||||
res
|
|
||||||
.set({
|
|
||||||
'Cache-Control': 'public, s-maxage=600, max-age=60', // 10 mins on CDN, 1 min on clients
|
|
||||||
'Cache-Tag': 'redirect, semver-redirect'
|
|
||||||
})
|
|
||||||
.redirect(
|
|
||||||
302,
|
|
||||||
createPackageURL(req.packageName, newVersion, req.filename, req.search)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const leadingSlashes = /^\/*/;
|
const leadingSlashes = /^\/*/;
|
||||||
|
|
||||||
|
@ -83,37 +70,9 @@ function filenameRedirect(req, res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch the package config. Redirect to the exact version if the request
|
* Redirect to the exact filename if the request omits one.
|
||||||
* targets a tag or uses semver, or to the exact filename if the request
|
|
||||||
* omits the filename.
|
|
||||||
*/
|
*/
|
||||||
export default async function fetchPackage(req, res, next) {
|
export default async function validateFilename(req, res, next) {
|
||||||
const version = await resolveVersion(req.packageName, req.packageVersion);
|
|
||||||
|
|
||||||
if (!version) {
|
|
||||||
return res
|
|
||||||
.status(404)
|
|
||||||
.type('text')
|
|
||||||
.send(`Cannot find package ${req.packageSpec}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version !== req.packageVersion) {
|
|
||||||
return semverRedirect(req, res, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
req.packageConfig = await getPackageConfig(
|
|
||||||
req.packageName,
|
|
||||||
req.packageVersion
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!req.packageConfig) {
|
|
||||||
// TODO: Log why.
|
|
||||||
return res
|
|
||||||
.status(500)
|
|
||||||
.type('text')
|
|
||||||
.send(`Cannot get config for package ${req.packageSpec}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!req.filename) {
|
if (!req.filename) {
|
||||||
return filenameRedirect(req, res);
|
return filenameRedirect(req, res);
|
||||||
}
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import createPackageURL from '../utils/createPackageURL.js';
|
||||||
|
import { getPackageConfig, resolveVersion } from '../utils/npm.js';
|
||||||
|
|
||||||
|
function semverRedirect(req, res, newVersion) {
|
||||||
|
res
|
||||||
|
.set({
|
||||||
|
'Cache-Control': 'public, s-maxage=600, max-age=60', // 10 mins on CDN, 1 min on clients
|
||||||
|
'Cache-Tag': 'redirect, semver-redirect'
|
||||||
|
})
|
||||||
|
.redirect(
|
||||||
|
302,
|
||||||
|
createPackageURL(req.packageName, newVersion, req.filename, req.search)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the package version/tag in the URL and make sure it's good. Also
|
||||||
|
* fetch the package config and add it to req.packageConfig. Redirect to
|
||||||
|
* the resolved version number if necessary.
|
||||||
|
*/
|
||||||
|
export default async function validateVersion(req, res, next) {
|
||||||
|
const version = await resolveVersion(req.packageName, req.packageVersion);
|
||||||
|
|
||||||
|
if (!version) {
|
||||||
|
return res
|
||||||
|
.status(404)
|
||||||
|
.type('text')
|
||||||
|
.send(`Cannot find package ${req.packageSpec}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version !== req.packageVersion) {
|
||||||
|
return semverRedirect(req, res, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.packageConfig = await getPackageConfig(
|
||||||
|
req.packageName,
|
||||||
|
req.packageVersion
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!req.packageConfig) {
|
||||||
|
// TODO: Log why.
|
||||||
|
return res
|
||||||
|
.status(500)
|
||||||
|
.type('text')
|
||||||
|
.send(`Cannot get config for package ${req.packageSpec}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import encodeJSONForScript from '../utils/encodeJSONForScript.js';
|
||||||
import {
|
import {
|
||||||
createElement as e,
|
createElement as e,
|
||||||
createHTML as h,
|
createHTML as h,
|
||||||
createScript as x
|
createScript as x
|
||||||
} from './markupHelpers.js';
|
} from '../utils/markup.js';
|
||||||
|
|
||||||
const promiseShim =
|
const promiseShim =
|
||||||
'window.Promise || document.write(\'\\x3Cscript src="/es6-promise@4.2.5/dist/es6-promise.min.js">\\x3C/script>\\x3Cscript>ES6Promise.polyfill()\\x3C/script>\')';
|
'window.Promise || document.write(\'\\x3Cscript src="/es6-promise@4.2.5/dist/es6-promise.min.js">\\x3C/script>\\x3Cscript>ES6Promise.polyfill()\\x3C/script>\')';
|
||||||
|
@ -47,7 +48,7 @@ gtag('config', 'UA-140352188-1');`),
|
||||||
e('title', null, title),
|
e('title', null, title),
|
||||||
x(promiseShim),
|
x(promiseShim),
|
||||||
x(fetchShim),
|
x(fetchShim),
|
||||||
data && x(`window.__DATA__ = ${JSON.stringify(data)}`)
|
data && x(`window.__DATA__ = ${encodeJSONForScript(data)}`)
|
||||||
),
|
),
|
||||||
e(
|
e(
|
||||||
'body',
|
'body',
|
|
@ -1,39 +1,47 @@
|
||||||
import getContentType from '../getContentType.js';
|
import getContentType from '../getContentType.js';
|
||||||
|
|
||||||
it('gets a content type of text/plain for LICENSE|README|CHANGES|AUTHORS|Makefile', () => {
|
describe('getContentType', () => {
|
||||||
expect(getContentType('AUTHORS')).toBe('text/plain');
|
it('returns text/plain for LICENSE|README|CHANGES|AUTHORS|Makefile', () => {
|
||||||
expect(getContentType('CHANGES')).toBe('text/plain');
|
expect(getContentType('AUTHORS')).toBe('text/plain');
|
||||||
expect(getContentType('LICENSE')).toBe('text/plain');
|
expect(getContentType('CHANGES')).toBe('text/plain');
|
||||||
expect(getContentType('Makefile')).toBe('text/plain');
|
expect(getContentType('LICENSE')).toBe('text/plain');
|
||||||
expect(getContentType('PATENTS')).toBe('text/plain');
|
expect(getContentType('Makefile')).toBe('text/plain');
|
||||||
expect(getContentType('README')).toBe('text/plain');
|
expect(getContentType('PATENTS')).toBe('text/plain');
|
||||||
});
|
expect(getContentType('README')).toBe('text/plain');
|
||||||
|
});
|
||||||
|
|
||||||
it('gets a content type of text/plain for .*rc files', () => {
|
it('returns text/plain for .*rc files', () => {
|
||||||
expect(getContentType('.eslintrc')).toBe('text/plain');
|
expect(getContentType('.eslintrc')).toBe('text/plain');
|
||||||
expect(getContentType('.babelrc')).toBe('text/plain');
|
expect(getContentType('.babelrc')).toBe('text/plain');
|
||||||
expect(getContentType('.anythingrc')).toBe('text/plain');
|
expect(getContentType('.anythingrc')).toBe('text/plain');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets a content type of text/plain for .git* files', () => {
|
it('returns text/plain for .git* files', () => {
|
||||||
expect(getContentType('.gitignore')).toBe('text/plain');
|
expect(getContentType('.gitignore')).toBe('text/plain');
|
||||||
expect(getContentType('.gitanything')).toBe('text/plain');
|
expect(getContentType('.gitanything')).toBe('text/plain');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets a content type of text/plain for .*ignore files', () => {
|
it('returns text/plain for .*ignore files', () => {
|
||||||
expect(getContentType('.eslintignore')).toBe('text/plain');
|
expect(getContentType('.eslintignore')).toBe('text/plain');
|
||||||
expect(getContentType('.anythingignore')).toBe('text/plain');
|
expect(getContentType('.anythingignore')).toBe('text/plain');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets a content type of text/plain for .ts files', () => {
|
it('returns text/plain for .ts(x) files', () => {
|
||||||
expect(getContentType('app.ts')).toBe('text/plain');
|
expect(getContentType('app.ts')).toBe('text/plain');
|
||||||
expect(getContentType('app.d.ts')).toBe('text/plain');
|
expect(getContentType('app.d.ts')).toBe('text/plain');
|
||||||
});
|
expect(getContentType('app.tsx')).toBe('text/plain');
|
||||||
|
});
|
||||||
|
|
||||||
it('gets a content type of text/plain for .flow files', () => {
|
it('returns text/plain for .flow files', () => {
|
||||||
expect(getContentType('app.js.flow')).toBe('text/plain');
|
expect(getContentType('app.js.flow')).toBe('text/plain');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets a content type of text/plain for .lock files', () => {
|
it('returns text/plain for .lock files', () => {
|
||||||
expect(getContentType('yarn.lock')).toBe('text/plain');
|
expect(getContentType('yarn.lock')).toBe('text/plain');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns application/json for .map files', () => {
|
||||||
|
expect(getContentType('react.js.map')).toBe('application/json');
|
||||||
|
expect(getContentType('react.json.map')).toBe('application/json');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
import getLanguageName from '../getLanguageName.js';
|
||||||
|
|
||||||
|
describe('getLanguageName', () => {
|
||||||
|
// Hard-coded overrides
|
||||||
|
|
||||||
|
it('detects Flow files', () => {
|
||||||
|
expect(getLanguageName('react.flow')).toBe('Flow');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects source maps', () => {
|
||||||
|
expect(getLanguageName('react.map')).toBe('Source Map (JSON)');
|
||||||
|
expect(getLanguageName('react.js.map')).toBe('Source Map (JSON)');
|
||||||
|
expect(getLanguageName('react.json.map')).toBe('Source Map (JSON)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects TypeScript files', () => {
|
||||||
|
expect(getLanguageName('react.d.ts')).toBe('TypeScript');
|
||||||
|
expect(getLanguageName('react.tsx')).toBe('TypeScript');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Content-Type lookups
|
||||||
|
|
||||||
|
it('detects JavaScript files', () => {
|
||||||
|
expect(getLanguageName('react.js')).toBe('JavaScript');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects JSON files', () => {
|
||||||
|
expect(getLanguageName('react.json')).toBe('JSON');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects binary files', () => {
|
||||||
|
expect(getLanguageName('ionicons.bin')).toBe('Binary');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects EOT files', () => {
|
||||||
|
expect(getLanguageName('ionicons.eot')).toBe('Embedded OpenType');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects SVG files', () => {
|
||||||
|
expect(getLanguageName('react.svg')).toBe('SVG');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects TTF files', () => {
|
||||||
|
expect(getLanguageName('ionicons.ttf')).toBe('TrueType Font');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects WOFF files', () => {
|
||||||
|
expect(getLanguageName('ionicons.woff')).toBe('WOFF');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects WOFF2 files', () => {
|
||||||
|
expect(getLanguageName('ionicons.woff2')).toBe('WOFF2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects CSS files', () => {
|
||||||
|
expect(getLanguageName('react.css')).toBe('CSS');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects HTML files', () => {
|
||||||
|
expect(getLanguageName('react.html')).toBe('HTML');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects JSX files', () => {
|
||||||
|
expect(getLanguageName('react.jsx')).toBe('JSX');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects Markdown files', () => {
|
||||||
|
expect(getLanguageName('README.md')).toBe('Markdown');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects plain text files', () => {
|
||||||
|
expect(getLanguageName('README')).toBe('Plain Text');
|
||||||
|
expect(getLanguageName('LICENSE')).toBe('Plain Text');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects SCSS files', () => {
|
||||||
|
expect(getLanguageName('some.scss')).toBe('SCSS');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects YAML files', () => {
|
||||||
|
expect(getLanguageName('config.yml')).toBe('YAML');
|
||||||
|
expect(getLanguageName('config.yaml')).toBe('YAML');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function createDataURI(contentType, content) {
|
||||||
|
return `data:${contentType};base64,${content.toString('base64')}`;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
import jsesc from 'jsesc';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes some data as JSON that may safely be included in HTML.
|
||||||
|
*/
|
||||||
|
export default function encodeJSONForScript(data) {
|
||||||
|
return jsesc(data, { json: true, isScriptContext: true });
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import path from 'path';
|
||||||
import mime from 'mime';
|
import mime from 'mime';
|
||||||
|
|
||||||
mime.define(
|
mime.define(
|
||||||
|
@ -19,5 +20,9 @@ mime.define(
|
||||||
const textFiles = /\/?(\.[a-z]*rc|\.git[a-z]*|\.[a-z]*ignore|\.lock)$/i;
|
const textFiles = /\/?(\.[a-z]*rc|\.git[a-z]*|\.[a-z]*ignore|\.lock)$/i;
|
||||||
|
|
||||||
export default function getContentType(file) {
|
export default function getContentType(file) {
|
||||||
return textFiles.test(file) ? 'text/plain' : mime.getType(file);
|
const name = path.basename(file);
|
||||||
|
|
||||||
|
return textFiles.test(name)
|
||||||
|
? 'text/plain'
|
||||||
|
: mime.getType(name) || 'text/plain';
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
import path from 'path';
|
||||||
|
import hljs from 'highlight.js';
|
||||||
|
|
||||||
|
import getContentType from './getContentType.js';
|
||||||
|
|
||||||
|
function escapeHTML(code) {
|
||||||
|
return code
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
|
||||||
|
// These should probably be added to highlight.js auto-detection.
|
||||||
|
const extLanguages = {
|
||||||
|
map: 'json',
|
||||||
|
mjs: 'javascript',
|
||||||
|
tsbuildinfo: 'json',
|
||||||
|
tsx: 'typescript',
|
||||||
|
vue: 'html'
|
||||||
|
};
|
||||||
|
|
||||||
|
function getLanguage(file) {
|
||||||
|
// Try to guess the language based on the file extension.
|
||||||
|
const ext = path.extname(file).substr(1);
|
||||||
|
|
||||||
|
if (ext) {
|
||||||
|
return extLanguages[ext] || ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentType = getContentType(file);
|
||||||
|
|
||||||
|
if (contentType === 'text/plain') {
|
||||||
|
return 'text';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLines(code) {
|
||||||
|
return code
|
||||||
|
.split('\n')
|
||||||
|
.map((line, index, array) =>
|
||||||
|
index === array.length - 1 ? line : line + '\n'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of HTML strings that highlight the given source code.
|
||||||
|
*/
|
||||||
|
export default function getHighlights(code, file) {
|
||||||
|
const language = getLanguage(file);
|
||||||
|
|
||||||
|
if (!language) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (language === 'text') {
|
||||||
|
return getLines(code).map(escapeHTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let continuation = false;
|
||||||
|
const hi = getLines(code).map(line => {
|
||||||
|
const result = hljs.highlight(language, line, false, continuation);
|
||||||
|
continuation = result.top;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
return hi.map(result =>
|
||||||
|
result.value.replace(
|
||||||
|
/<span class="hljs-(\w+)">/g,
|
||||||
|
'<span class="code-$1">'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
// Probably an "unknown language" error.
|
||||||
|
// console.error(error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import getContentType from './getContentType.js';
|
||||||
|
|
||||||
|
const contentTypeNames = {
|
||||||
|
'application/javascript': 'JavaScript',
|
||||||
|
'application/json': 'JSON',
|
||||||
|
'application/octet-stream': 'Binary',
|
||||||
|
'application/vnd.ms-fontobject': 'Embedded OpenType',
|
||||||
|
'application/xml': 'XML',
|
||||||
|
'image/svg+xml': 'SVG',
|
||||||
|
'font/ttf': 'TrueType Font',
|
||||||
|
'font/woff': 'WOFF',
|
||||||
|
'font/woff2': 'WOFF2',
|
||||||
|
'text/css': 'CSS',
|
||||||
|
'text/html': 'HTML',
|
||||||
|
'text/jsx': 'JSX',
|
||||||
|
'text/markdown': 'Markdown',
|
||||||
|
'text/plain': 'Plain Text',
|
||||||
|
'text/x-scss': 'SCSS',
|
||||||
|
'text/yaml': 'YAML'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a human-friendly name for whatever is in the given file.
|
||||||
|
*/
|
||||||
|
export default function getLanguageName(file) {
|
||||||
|
// Content-Type is text/plain, but we can be more descriptive.
|
||||||
|
if (/\.flow$/.test(file)) return 'Flow';
|
||||||
|
if (/\.(d\.ts|tsx)$/.test(file)) return 'TypeScript';
|
||||||
|
|
||||||
|
// Content-Type is application/json, but we can be more descriptive.
|
||||||
|
if (/\.map$/.test(file)) return 'Source Map (JSON)';
|
||||||
|
|
||||||
|
const contentType = getContentType(file);
|
||||||
|
|
||||||
|
return contentTypeNames[contentType] || contentType;
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
// eslint-disable-next-line import/no-unresolved
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import entryManifest from 'entry-manifest';
|
import entryManifest from 'entry-manifest';
|
||||||
|
|
||||||
import { createElement, createScript } from './markupHelpers.js';
|
import { createElement, createScript } from './markup.js';
|
||||||
|
|
||||||
function getEntryPoint(name, format) {
|
function getEntryPoint(name, format) {
|
||||||
let entryPoints;
|
let entryPoints;
|
|
@ -19,7 +19,7 @@ export default function parsePackageURL(originalURL) {
|
||||||
|
|
||||||
const packageName = match[1];
|
const packageName = match[1];
|
||||||
const packageVersion = match[2] || 'latest';
|
const packageVersion = match[2] || 'latest';
|
||||||
const filename = match[3] || '';
|
const filename = (match[3] || '').replace(/\/\/+/g, '/');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// If the URL is /@scope/name@version/file.js?main=browser:
|
// If the URL is /@scope/name@version/file.js?main=browser:
|
||||||
|
|
11
package.json
|
@ -6,6 +6,7 @@
|
||||||
"build": "rollup -c",
|
"build": "rollup -c",
|
||||||
"clean": "git clean -e '!/.env' -fdX .",
|
"clean": "git clean -e '!/.env' -fdX .",
|
||||||
"lint": "eslint modules",
|
"lint": "eslint modules",
|
||||||
|
"serve": "nodemon -w server.js server.js",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"watch": "rollup -c -w"
|
"watch": "rollup -c -w"
|
||||||
},
|
},
|
||||||
|
@ -17,21 +18,25 @@
|
||||||
"@babel/plugin-syntax-export-namespace-from": "^7.2.0",
|
"@babel/plugin-syntax-export-namespace-from": "^7.2.0",
|
||||||
"@babel/plugin-syntax-import-meta": "^7.2.0",
|
"@babel/plugin-syntax-import-meta": "^7.2.0",
|
||||||
"@emotion/core": "^10.0.6",
|
"@emotion/core": "^10.0.6",
|
||||||
|
"@reach/visually-hidden": "^0.1.4",
|
||||||
"cheerio": "^1.0.0-rc.2",
|
"cheerio": "^1.0.0-rc.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"date-fns": "^1.30.1",
|
"date-fns": "^1.30.1",
|
||||||
"etag": "^1.8.1",
|
"etag": "^1.8.1",
|
||||||
"express": "^4.16.4",
|
"express": "^4.16.4",
|
||||||
"gunzip-maybe": "^1.4.1",
|
"gunzip-maybe": "^1.4.1",
|
||||||
|
"highlight.js": "^9.15.8",
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^2.2.1",
|
||||||
|
"jsesc": "^2.5.2",
|
||||||
"lru-cache": "^5.1.1",
|
"lru-cache": "^5.1.1",
|
||||||
"mime": "^2.4.0",
|
"mime": "^2.4.0",
|
||||||
"morgan": "^1.9.1",
|
"morgan": "^1.9.1",
|
||||||
"ndjson": "^1.5.0",
|
"ndjson": "^1.5.0",
|
||||||
"pretty-bytes": "^5.1.0",
|
"pretty-bytes": "^5.1.0",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.7.0",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.7.0",
|
"react-dom": "^16.8.6",
|
||||||
|
"react-icons": "^3.7.0",
|
||||||
"semver": "^5.6.0",
|
"semver": "^5.6.0",
|
||||||
"sort-by": "^1.2.0",
|
"sort-by": "^1.2.0",
|
||||||
"sri-toolbox": "^0.2.0",
|
"sri-toolbox": "^0.2.0",
|
||||||
|
@ -54,7 +59,9 @@
|
||||||
"eslint-import-resolver-node": "^0.3.2",
|
"eslint-import-resolver-node": "^0.3.2",
|
||||||
"eslint-plugin-import": "^2.8.0",
|
"eslint-plugin-import": "^2.8.0",
|
||||||
"eslint-plugin-react": "^7.14.2",
|
"eslint-plugin-react": "^7.14.2",
|
||||||
|
"eslint-plugin-react-hooks": "^1.6.1",
|
||||||
"jest": "^22.4.4",
|
"jest": "^22.4.4",
|
||||||
|
"nodemon": "^1.19.1",
|
||||||
"rollup": "^1.1.0",
|
"rollup": "^1.1.0",
|
||||||
"rollup-plugin-babel": "^4.2.0",
|
"rollup-plugin-babel": "^4.2.0",
|
||||||
"rollup-plugin-commonjs": "^9.2.0",
|
"rollup-plugin-commonjs": "^9.2.0",
|
||||||
|
|
|
@ -33,7 +33,7 @@ function entryManifest() {
|
||||||
Object.keys(bundle).forEach(fileName => {
|
Object.keys(bundle).forEach(fileName => {
|
||||||
const info = bundle[fileName];
|
const info = bundle[fileName];
|
||||||
|
|
||||||
// We're only interested in entry points.
|
// We're interested only in entry points.
|
||||||
if (!info.isEntry) return;
|
if (!info.isEntry) return;
|
||||||
|
|
||||||
const globalImports = info.imports.filter(
|
const globalImports = info.imports.filter(
|
||||||
|
|
|
@ -18,9 +18,9 @@ const dev = env === 'development';
|
||||||
|
|
||||||
const manifest = entryManifest();
|
const manifest = entryManifest();
|
||||||
|
|
||||||
const client = ['main', 'autoIndex'].map(entryName => {
|
const client = ['browse', 'main'].map(entryName => {
|
||||||
return {
|
return {
|
||||||
external: ['react', 'react-dom', '@emotion/core'],
|
external: ['@emotion/core', 'react', 'react-dom', 'highlight.js'],
|
||||||
input: `modules/client/${entryName}.js`,
|
input: `modules/client/${entryName}.js`,
|
||||||
output: {
|
output: {
|
||||||
format: 'iife',
|
format: 'iife',
|
||||||
|
|
415
yarn.lock
|
@ -904,6 +904,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.2.tgz#63985d3d8b02530e0869962f4da09142ee8e200e"
|
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.2.tgz#63985d3d8b02530e0869962f4da09142ee8e200e"
|
||||||
integrity sha512-n/VQ4mbfr81aqkx/XmVicOLjviMuy02eenSdJY33SVA7S2J42EU0P1H0mOogfYedb3wXA0d/LVtBrgTSm04WEA==
|
integrity sha512-n/VQ4mbfr81aqkx/XmVicOLjviMuy02eenSdJY33SVA7S2J42EU0P1H0mOogfYedb3wXA0d/LVtBrgTSm04WEA==
|
||||||
|
|
||||||
|
"@reach/visually-hidden@^0.1.4":
|
||||||
|
version "0.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@reach/visually-hidden/-/visually-hidden-0.1.4.tgz#0dc4ecedf523004337214187db70a46183bd945b"
|
||||||
|
integrity sha512-QHbzXjflSlCvDd6vJwdwx16mSB+vUCCQMiU/wK/CgVNPibtpEiIbisyxkpZc55DyDFNUIqP91rSUsNae+ogGDQ==
|
||||||
|
|
||||||
"@types/estree@0.0.39":
|
"@types/estree@0.0.39":
|
||||||
version "0.0.39"
|
version "0.0.39"
|
||||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
||||||
|
@ -1009,6 +1014,13 @@ ajv@^6.5.5:
|
||||||
json-schema-traverse "^0.4.1"
|
json-schema-traverse "^0.4.1"
|
||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
|
ansi-align@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
|
||||||
|
integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=
|
||||||
|
dependencies:
|
||||||
|
string-width "^2.0.0"
|
||||||
|
|
||||||
ansi-escapes@^3.0.0:
|
ansi-escapes@^3.0.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
|
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
|
||||||
|
@ -1148,6 +1160,11 @@ astral-regex@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
|
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
|
||||||
integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
|
integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
|
||||||
|
|
||||||
|
async-each@^1.0.1:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
|
||||||
|
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
|
||||||
|
|
||||||
async-limiter@~1.0.0:
|
async-limiter@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
|
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
|
||||||
|
@ -1446,6 +1463,11 @@ bcrypt-pbkdf@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
tweetnacl "^0.14.3"
|
tweetnacl "^0.14.3"
|
||||||
|
|
||||||
|
binary-extensions@^1.0.0:
|
||||||
|
version "1.13.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
|
||||||
|
integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
|
||||||
|
|
||||||
bl@^1.0.0:
|
bl@^1.0.0:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
|
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
|
||||||
|
@ -1475,6 +1497,19 @@ boolbase@~1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||||
|
|
||||||
|
boxen@^1.2.1:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b"
|
||||||
|
integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==
|
||||||
|
dependencies:
|
||||||
|
ansi-align "^2.0.0"
|
||||||
|
camelcase "^4.0.0"
|
||||||
|
chalk "^2.0.1"
|
||||||
|
cli-boxes "^1.0.0"
|
||||||
|
string-width "^2.0.0"
|
||||||
|
term-size "^1.2.0"
|
||||||
|
widest-line "^2.0.0"
|
||||||
|
|
||||||
brace-expansion@^1.1.7:
|
brace-expansion@^1.1.7:
|
||||||
version "1.1.11"
|
version "1.1.11"
|
||||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||||
|
@ -1492,7 +1527,7 @@ braces@^1.8.2:
|
||||||
preserve "^0.2.0"
|
preserve "^0.2.0"
|
||||||
repeat-element "^1.1.2"
|
repeat-element "^1.1.2"
|
||||||
|
|
||||||
braces@^2.3.1:
|
braces@^2.3.1, braces@^2.3.2:
|
||||||
version "2.3.2"
|
version "2.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
|
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
|
||||||
integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
|
integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
|
||||||
|
@ -1627,11 +1662,16 @@ callsites@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
|
resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
|
||||||
integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
|
integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
|
||||||
|
|
||||||
camelcase@^4.1.0:
|
camelcase@^4.0.0, camelcase@^4.1.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
|
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
|
||||||
integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
|
integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
|
||||||
|
|
||||||
|
camelcase@^5.0.0:
|
||||||
|
version "5.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||||
|
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||||
|
|
||||||
caniuse-lite@^1.0.30000967:
|
caniuse-lite@^1.0.30000967:
|
||||||
version "1.0.30000969"
|
version "1.0.30000969"
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000969.tgz#7664f571f2072657bde70b00a1fc1ba41f1942a9"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000969.tgz#7664f571f2072657bde70b00a1fc1ba41f1942a9"
|
||||||
|
@ -1644,6 +1684,11 @@ capture-exit@^1.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
rsvp "^3.3.3"
|
rsvp "^3.3.3"
|
||||||
|
|
||||||
|
capture-stack-trace@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d"
|
||||||
|
integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==
|
||||||
|
|
||||||
caseless@~0.12.0:
|
caseless@~0.12.0:
|
||||||
version "0.12.0"
|
version "0.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||||
|
@ -1686,6 +1731,25 @@ cheerio@^1.0.0-rc.2:
|
||||||
lodash "^4.15.0"
|
lodash "^4.15.0"
|
||||||
parse5 "^3.0.1"
|
parse5 "^3.0.1"
|
||||||
|
|
||||||
|
chokidar@^2.1.5:
|
||||||
|
version "2.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5"
|
||||||
|
integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==
|
||||||
|
dependencies:
|
||||||
|
anymatch "^2.0.0"
|
||||||
|
async-each "^1.0.1"
|
||||||
|
braces "^2.3.2"
|
||||||
|
glob-parent "^3.1.0"
|
||||||
|
inherits "^2.0.3"
|
||||||
|
is-binary-path "^1.0.0"
|
||||||
|
is-glob "^4.0.0"
|
||||||
|
normalize-path "^3.0.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
readdirp "^2.2.1"
|
||||||
|
upath "^1.1.1"
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents "^1.2.7"
|
||||||
|
|
||||||
chownr@^1.1.1:
|
chownr@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494"
|
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494"
|
||||||
|
@ -1711,6 +1775,11 @@ class-utils@^0.3.5:
|
||||||
isobject "^3.0.0"
|
isobject "^3.0.0"
|
||||||
static-extend "^0.1.1"
|
static-extend "^0.1.1"
|
||||||
|
|
||||||
|
cli-boxes@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
|
||||||
|
integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM=
|
||||||
|
|
||||||
cli-cursor@^2.1.0:
|
cli-cursor@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
|
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
|
||||||
|
@ -1818,6 +1887,18 @@ concat-stream@^1.6.0:
|
||||||
readable-stream "^2.2.2"
|
readable-stream "^2.2.2"
|
||||||
typedarray "^0.0.6"
|
typedarray "^0.0.6"
|
||||||
|
|
||||||
|
configstore@^3.0.0:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f"
|
||||||
|
integrity sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==
|
||||||
|
dependencies:
|
||||||
|
dot-prop "^4.1.0"
|
||||||
|
graceful-fs "^4.1.2"
|
||||||
|
make-dir "^1.0.0"
|
||||||
|
unique-string "^1.0.0"
|
||||||
|
write-file-atomic "^2.0.0"
|
||||||
|
xdg-basedir "^3.0.0"
|
||||||
|
|
||||||
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
||||||
|
@ -1915,6 +1996,13 @@ cosmiconfig@^5.2.0:
|
||||||
js-yaml "^3.13.1"
|
js-yaml "^3.13.1"
|
||||||
parse-json "^4.0.0"
|
parse-json "^4.0.0"
|
||||||
|
|
||||||
|
create-error-class@^3.0.0:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
|
||||||
|
integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=
|
||||||
|
dependencies:
|
||||||
|
capture-stack-trace "^1.0.0"
|
||||||
|
|
||||||
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
|
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
|
||||||
|
@ -2159,11 +2247,23 @@ domutils@^1.5.1:
|
||||||
dom-serializer "0"
|
dom-serializer "0"
|
||||||
domelementtype "1"
|
domelementtype "1"
|
||||||
|
|
||||||
|
dot-prop@^4.1.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
|
||||||
|
integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==
|
||||||
|
dependencies:
|
||||||
|
is-obj "^1.0.0"
|
||||||
|
|
||||||
dotenv@^6.2.0:
|
dotenv@^6.2.0:
|
||||||
version "6.2.0"
|
version "6.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
|
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
|
||||||
integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
|
integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
|
||||||
|
|
||||||
|
duplexer3@^0.1.4:
|
||||||
|
version "0.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
||||||
|
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
|
||||||
|
|
||||||
duplexify@^3.5.0, duplexify@^3.6.0:
|
duplexify@^3.5.0, duplexify@^3.6.0:
|
||||||
version "3.7.1"
|
version "3.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
|
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
|
||||||
|
@ -2299,6 +2399,11 @@ eslint-plugin-import@^2.8.0:
|
||||||
read-pkg-up "^2.0.0"
|
read-pkg-up "^2.0.0"
|
||||||
resolve "^1.10.0"
|
resolve "^1.10.0"
|
||||||
|
|
||||||
|
eslint-plugin-react-hooks@^1.6.1:
|
||||||
|
version "1.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.6.1.tgz#3c66a5515ea3e0a221ffc5d4e75c971c217b1a4c"
|
||||||
|
integrity sha512-wHhmGJyVuijnYIJXZJHDUF2WM+rJYTjulUTqF9k61d3BTk8etydz+M4dXUVH7M76ZRS85rqBTCx0Es/lLsrjnA==
|
||||||
|
|
||||||
eslint-plugin-react@^7.14.2:
|
eslint-plugin-react@^7.14.2:
|
||||||
version "7.14.2"
|
version "7.14.2"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.14.2.tgz#94c193cc77a899ac0ecbb2766fbef88685b7ecc1"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.14.2.tgz#94c193cc77a899ac0ecbb2766fbef88685b7ecc1"
|
||||||
|
@ -2775,7 +2880,7 @@ fs.realpath@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||||
|
|
||||||
fsevents@^1.2.3:
|
fsevents@^1.2.3, fsevents@^1.2.7:
|
||||||
version "1.2.9"
|
version "1.2.9"
|
||||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f"
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f"
|
||||||
integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==
|
integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==
|
||||||
|
@ -2844,6 +2949,14 @@ glob-parent@^2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^2.0.0"
|
is-glob "^2.0.0"
|
||||||
|
|
||||||
|
glob-parent@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
|
||||||
|
integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=
|
||||||
|
dependencies:
|
||||||
|
is-glob "^3.1.0"
|
||||||
|
path-dirname "^1.0.0"
|
||||||
|
|
||||||
glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3:
|
glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3:
|
||||||
version "7.1.4"
|
version "7.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
|
||||||
|
@ -2856,6 +2969,13 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3:
|
||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
path-is-absolute "^1.0.0"
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
|
global-dirs@^0.1.0:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445"
|
||||||
|
integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=
|
||||||
|
dependencies:
|
||||||
|
ini "^1.3.4"
|
||||||
|
|
||||||
globals@^11.0.1, globals@^11.1.0:
|
globals@^11.0.1, globals@^11.1.0:
|
||||||
version "11.12.0"
|
version "11.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||||
|
@ -2901,6 +3021,23 @@ google-closure-compiler@20181125.0.1:
|
||||||
google-closure-compiler-linux "^20181125.0.1"
|
google-closure-compiler-linux "^20181125.0.1"
|
||||||
google-closure-compiler-osx "^20181125.0.1"
|
google-closure-compiler-osx "^20181125.0.1"
|
||||||
|
|
||||||
|
got@^6.7.1:
|
||||||
|
version "6.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
|
||||||
|
integrity sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=
|
||||||
|
dependencies:
|
||||||
|
create-error-class "^3.0.0"
|
||||||
|
duplexer3 "^0.1.4"
|
||||||
|
get-stream "^3.0.0"
|
||||||
|
is-redirect "^1.0.0"
|
||||||
|
is-retry-allowed "^1.0.0"
|
||||||
|
is-stream "^1.0.0"
|
||||||
|
lowercase-keys "^1.0.0"
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
|
timed-out "^4.0.0"
|
||||||
|
unzip-response "^2.0.1"
|
||||||
|
url-parse-lax "^1.0.0"
|
||||||
|
|
||||||
graceful-fs@^4.1.11, graceful-fs@^4.1.2:
|
graceful-fs@^4.1.11, graceful-fs@^4.1.2:
|
||||||
version "4.1.15"
|
version "4.1.15"
|
||||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||||
|
@ -3012,6 +3149,11 @@ has@^1.0.1, has@^1.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind "^1.1.1"
|
function-bind "^1.1.1"
|
||||||
|
|
||||||
|
highlight.js@^9.15.8:
|
||||||
|
version "9.15.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.8.tgz#f344fda123f36f1a65490e932cf90569e4999971"
|
||||||
|
integrity sha512-RrapkKQWwE+wKdF73VsOa2RQdIoO3mxwJ4P8mhbI6KYJUraUHRKM5w5zQQKXNk0xNL4UVRdulV9SBJcmzJNzVA==
|
||||||
|
|
||||||
home-or-tmp@^2.0.0:
|
home-or-tmp@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
|
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
|
||||||
|
@ -3071,6 +3213,11 @@ iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
|
||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer ">= 2.1.2 < 3"
|
safer-buffer ">= 2.1.2 < 3"
|
||||||
|
|
||||||
|
ignore-by-default@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
|
||||||
|
integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk=
|
||||||
|
|
||||||
ignore-walk@^3.0.1:
|
ignore-walk@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8"
|
resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8"
|
||||||
|
@ -3091,6 +3238,11 @@ import-fresh@^2.0.0:
|
||||||
caller-path "^2.0.0"
|
caller-path "^2.0.0"
|
||||||
resolve-from "^3.0.0"
|
resolve-from "^3.0.0"
|
||||||
|
|
||||||
|
import-lazy@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
|
||||||
|
integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=
|
||||||
|
|
||||||
import-local@^1.0.0:
|
import-local@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"
|
resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"
|
||||||
|
@ -3117,7 +3269,7 @@ inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||||
|
|
||||||
ini@~1.3.0:
|
ini@^1.3.4, ini@~1.3.0:
|
||||||
version "1.3.5"
|
version "1.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
||||||
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
|
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
|
||||||
|
@ -3178,6 +3330,13 @@ is-arrayish@^0.2.1:
|
||||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
||||||
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
|
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
|
||||||
|
|
||||||
|
is-binary-path@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
|
||||||
|
integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=
|
||||||
|
dependencies:
|
||||||
|
binary-extensions "^1.0.0"
|
||||||
|
|
||||||
is-buffer@^1.1.5:
|
is-buffer@^1.1.5:
|
||||||
version "1.1.6"
|
version "1.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||||
|
@ -3271,6 +3430,11 @@ is-extglob@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
|
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
|
||||||
integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=
|
integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=
|
||||||
|
|
||||||
|
is-extglob@^2.1.0, is-extglob@^2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||||
|
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
|
||||||
|
|
||||||
is-finite@^1.0.0:
|
is-finite@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
|
resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
|
||||||
|
@ -3302,16 +3466,43 @@ is-glob@^2.0.0, is-glob@^2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-extglob "^1.0.0"
|
is-extglob "^1.0.0"
|
||||||
|
|
||||||
|
is-glob@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
|
||||||
|
integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=
|
||||||
|
dependencies:
|
||||||
|
is-extglob "^2.1.0"
|
||||||
|
|
||||||
|
is-glob@^4.0.0:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
|
||||||
|
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
|
||||||
|
dependencies:
|
||||||
|
is-extglob "^2.1.1"
|
||||||
|
|
||||||
is-gzip@^1.0.0:
|
is-gzip@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83"
|
resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83"
|
||||||
integrity sha1-bKiwe5nHeZgCWQDlVc7Y7YCHmoM=
|
integrity sha1-bKiwe5nHeZgCWQDlVc7Y7YCHmoM=
|
||||||
|
|
||||||
|
is-installed-globally@^0.1.0:
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
|
||||||
|
integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=
|
||||||
|
dependencies:
|
||||||
|
global-dirs "^0.1.0"
|
||||||
|
is-path-inside "^1.0.0"
|
||||||
|
|
||||||
is-module@^1.0.0:
|
is-module@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||||
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
|
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
|
||||||
|
|
||||||
|
is-npm@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4"
|
||||||
|
integrity sha1-8vtjpl5JBbQGyGBydloaTceTufQ=
|
||||||
|
|
||||||
is-number@^2.1.0:
|
is-number@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
|
resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
|
||||||
|
@ -3331,6 +3522,18 @@ is-number@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
|
resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
|
||||||
integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
|
integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
|
||||||
|
|
||||||
|
is-obj@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
|
||||||
|
integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
|
||||||
|
|
||||||
|
is-path-inside@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
|
||||||
|
integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
|
||||||
|
dependencies:
|
||||||
|
path-is-inside "^1.0.1"
|
||||||
|
|
||||||
is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
|
is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
||||||
|
@ -3353,6 +3556,11 @@ is-promise@^2.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
|
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
|
||||||
integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
|
integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
|
||||||
|
|
||||||
|
is-redirect@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24"
|
||||||
|
integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=
|
||||||
|
|
||||||
is-regex@^1.0.4:
|
is-regex@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
|
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
|
||||||
|
@ -3365,7 +3573,12 @@ is-resolvable@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
|
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
|
||||||
integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
|
integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
|
||||||
|
|
||||||
is-stream@^1.0.1, is-stream@^1.1.0:
|
is-retry-allowed@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
|
||||||
|
integrity sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=
|
||||||
|
|
||||||
|
is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||||
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
|
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
|
||||||
|
@ -3851,7 +4064,7 @@ jsesc@^1.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
|
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
|
||||||
integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s=
|
integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s=
|
||||||
|
|
||||||
jsesc@^2.5.1:
|
jsesc@^2.5.1, jsesc@^2.5.2:
|
||||||
version "2.5.2"
|
version "2.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
|
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
|
||||||
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
|
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
|
||||||
|
@ -3957,6 +4170,13 @@ kind-of@^6.0.0, kind-of@^6.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
|
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
|
||||||
integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
|
integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
|
||||||
|
|
||||||
|
latest-version@^3.0.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15"
|
||||||
|
integrity sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=
|
||||||
|
dependencies:
|
||||||
|
package-json "^4.0.0"
|
||||||
|
|
||||||
lcid@^1.0.0:
|
lcid@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
|
resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
|
||||||
|
@ -4028,6 +4248,11 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
js-tokens "^3.0.0 || ^4.0.0"
|
js-tokens "^3.0.0 || ^4.0.0"
|
||||||
|
|
||||||
|
lowercase-keys@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
||||||
|
integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
|
||||||
|
|
||||||
lru-cache@^4.0.1:
|
lru-cache@^4.0.1:
|
||||||
version "4.1.5"
|
version "4.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
|
||||||
|
@ -4373,6 +4598,22 @@ node-releases@^1.1.19:
|
||||||
dependencies:
|
dependencies:
|
||||||
semver "^5.3.0"
|
semver "^5.3.0"
|
||||||
|
|
||||||
|
nodemon@^1.19.1:
|
||||||
|
version "1.19.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.19.1.tgz#576f0aad0f863aabf8c48517f6192ff987cd5071"
|
||||||
|
integrity sha512-/DXLzd/GhiaDXXbGId5BzxP1GlsqtMGM9zTmkWrgXtSqjKmGSbLicM/oAy4FR0YWm14jCHRwnR31AHS2dYFHrg==
|
||||||
|
dependencies:
|
||||||
|
chokidar "^2.1.5"
|
||||||
|
debug "^3.1.0"
|
||||||
|
ignore-by-default "^1.0.1"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
pstree.remy "^1.1.6"
|
||||||
|
semver "^5.5.0"
|
||||||
|
supports-color "^5.2.0"
|
||||||
|
touch "^3.1.0"
|
||||||
|
undefsafe "^2.0.2"
|
||||||
|
update-notifier "^2.5.0"
|
||||||
|
|
||||||
nopt@^4.0.1:
|
nopt@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
|
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
|
||||||
|
@ -4381,6 +4622,13 @@ nopt@^4.0.1:
|
||||||
abbrev "1"
|
abbrev "1"
|
||||||
osenv "^0.1.4"
|
osenv "^0.1.4"
|
||||||
|
|
||||||
|
nopt@~1.0.10:
|
||||||
|
version "1.0.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
|
||||||
|
integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=
|
||||||
|
dependencies:
|
||||||
|
abbrev "1"
|
||||||
|
|
||||||
normalize-package-data@^2.3.2:
|
normalize-package-data@^2.3.2:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
||||||
|
@ -4398,6 +4646,11 @@ normalize-path@^2.0.1, normalize-path@^2.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
remove-trailing-separator "^1.0.1"
|
remove-trailing-separator "^1.0.1"
|
||||||
|
|
||||||
|
normalize-path@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||||
|
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||||
|
|
||||||
npm-bundled@^1.0.1:
|
npm-bundled@^1.0.1:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd"
|
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd"
|
||||||
|
@ -4641,6 +4894,16 @@ p-try@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
|
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
|
||||||
integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
|
integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
|
||||||
|
|
||||||
|
package-json@^4.0.0:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed"
|
||||||
|
integrity sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=
|
||||||
|
dependencies:
|
||||||
|
got "^6.7.1"
|
||||||
|
registry-auth-token "^3.0.1"
|
||||||
|
registry-url "^3.0.3"
|
||||||
|
semver "^5.1.0"
|
||||||
|
|
||||||
pako@~0.2.0:
|
pako@~0.2.0:
|
||||||
version "0.2.9"
|
version "0.2.9"
|
||||||
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
|
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
|
||||||
|
@ -4693,6 +4956,11 @@ pascalcase@^0.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
|
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
|
||||||
integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
|
integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
|
||||||
|
|
||||||
|
path-dirname@^1.0.0:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
|
||||||
|
integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=
|
||||||
|
|
||||||
path-exists@^2.0.0:
|
path-exists@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
|
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
|
||||||
|
@ -4710,7 +4978,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||||
|
|
||||||
path-is-inside@^1.0.2:
|
path-is-inside@^1.0.1, path-is-inside@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
|
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
|
||||||
integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
|
integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
|
||||||
|
@ -4809,6 +5077,11 @@ prelude-ls@~1.1.2:
|
||||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
||||||
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
|
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
|
||||||
|
|
||||||
|
prepend-http@^1.0.1:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
||||||
|
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
|
||||||
|
|
||||||
preserve@^0.2.0:
|
preserve@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
||||||
|
@ -4869,6 +5142,11 @@ psl@^1.1.24, psl@^1.1.28:
|
||||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184"
|
resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184"
|
||||||
integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==
|
integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==
|
||||||
|
|
||||||
|
pstree.remy@^1.1.6:
|
||||||
|
version "1.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.7.tgz#c76963a28047ed61542dc361aa26ee55a7fa15f3"
|
||||||
|
integrity sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==
|
||||||
|
|
||||||
pump@^2.0.0:
|
pump@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
|
resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
|
||||||
|
@ -4930,7 +5208,7 @@ raw-body@2.4.0:
|
||||||
iconv-lite "0.4.24"
|
iconv-lite "0.4.24"
|
||||||
unpipe "1.0.0"
|
unpipe "1.0.0"
|
||||||
|
|
||||||
rc@^1.2.7:
|
rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
|
||||||
version "1.2.8"
|
version "1.2.8"
|
||||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
||||||
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
|
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
|
||||||
|
@ -4940,7 +5218,7 @@ rc@^1.2.7:
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
react-dom@^16.7.0:
|
react-dom@^16.8.6:
|
||||||
version "16.8.6"
|
version "16.8.6"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"
|
||||||
integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==
|
integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==
|
||||||
|
@ -4950,12 +5228,19 @@ react-dom@^16.7.0:
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
scheduler "^0.13.6"
|
scheduler "^0.13.6"
|
||||||
|
|
||||||
|
react-icons@^3.7.0:
|
||||||
|
version "3.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-3.7.0.tgz#64fe46231fabfeea27895edeae6c3b78114b8c8f"
|
||||||
|
integrity sha512-7MyPwjIhuyW0D2N3s4DEd0hGPGFf0sK+IIRKhc1FvSpZNVmnUoGvHbmAwzGJU+3my+fvihVWgwU5SDtlAri56Q==
|
||||||
|
dependencies:
|
||||||
|
camelcase "^5.0.0"
|
||||||
|
|
||||||
react-is@^16.8.1:
|
react-is@^16.8.1:
|
||||||
version "16.8.6"
|
version "16.8.6"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
|
||||||
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
|
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
|
||||||
|
|
||||||
react@^16.7.0:
|
react@^16.8.6:
|
||||||
version "16.8.6"
|
version "16.8.6"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"
|
resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"
|
||||||
integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==
|
integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==
|
||||||
|
@ -4999,7 +5284,7 @@ read-pkg@^2.0.0:
|
||||||
normalize-package-data "^2.3.2"
|
normalize-package-data "^2.3.2"
|
||||||
path-type "^2.0.0"
|
path-type "^2.0.0"
|
||||||
|
|
||||||
readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6:
|
readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6:
|
||||||
version "2.3.6"
|
version "2.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||||
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
|
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
|
||||||
|
@ -5021,6 +5306,15 @@ readable-stream@^3.1.1:
|
||||||
string_decoder "^1.1.1"
|
string_decoder "^1.1.1"
|
||||||
util-deprecate "^1.0.1"
|
util-deprecate "^1.0.1"
|
||||||
|
|
||||||
|
readdirp@^2.2.1:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
|
||||||
|
integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.1.11"
|
||||||
|
micromatch "^3.1.10"
|
||||||
|
readable-stream "^2.0.2"
|
||||||
|
|
||||||
realpath-native@^1.0.0:
|
realpath-native@^1.0.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c"
|
resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c"
|
||||||
|
@ -5094,6 +5388,21 @@ regexpu-core@^4.5.4:
|
||||||
unicode-match-property-ecmascript "^1.0.4"
|
unicode-match-property-ecmascript "^1.0.4"
|
||||||
unicode-match-property-value-ecmascript "^1.1.0"
|
unicode-match-property-value-ecmascript "^1.1.0"
|
||||||
|
|
||||||
|
registry-auth-token@^3.0.1:
|
||||||
|
version "3.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e"
|
||||||
|
integrity sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==
|
||||||
|
dependencies:
|
||||||
|
rc "^1.1.6"
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
|
registry-url@^3.0.3:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
|
||||||
|
integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI=
|
||||||
|
dependencies:
|
||||||
|
rc "^1.0.1"
|
||||||
|
|
||||||
regjsgen@^0.5.0:
|
regjsgen@^0.5.0:
|
||||||
version "0.5.0"
|
version "0.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd"
|
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd"
|
||||||
|
@ -5393,7 +5702,14 @@ scheduler@^0.13.6:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
|
semver-diff@^2.0.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
|
||||||
|
integrity sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=
|
||||||
|
dependencies:
|
||||||
|
semver "^5.0.3"
|
||||||
|
|
||||||
|
"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
|
||||||
version "5.7.0"
|
version "5.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
|
||||||
integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
|
integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
|
||||||
|
@ -5783,7 +6099,7 @@ supports-color@^3.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^1.0.0"
|
has-flag "^1.0.0"
|
||||||
|
|
||||||
supports-color@^5.3.0:
|
supports-color@^5.2.0, supports-color@^5.3.0:
|
||||||
version "5.5.0"
|
version "5.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||||
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
||||||
|
@ -5859,6 +6175,13 @@ tempy@^0.3.0:
|
||||||
type-fest "^0.3.1"
|
type-fest "^0.3.1"
|
||||||
unique-string "^1.0.0"
|
unique-string "^1.0.0"
|
||||||
|
|
||||||
|
term-size@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69"
|
||||||
|
integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=
|
||||||
|
dependencies:
|
||||||
|
execa "^0.7.0"
|
||||||
|
|
||||||
test-exclude@^4.2.1:
|
test-exclude@^4.2.1:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.3.tgz#a9a5e64474e4398339245a0a769ad7c2f4a97c20"
|
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.3.tgz#a9a5e64474e4398339245a0a769ad7c2f4a97c20"
|
||||||
|
@ -5893,6 +6216,11 @@ through@^2.3.6:
|
||||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||||
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
||||||
|
|
||||||
|
timed-out@^4.0.0:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
|
||||||
|
integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=
|
||||||
|
|
||||||
tmp@^0.0.33:
|
tmp@^0.0.33:
|
||||||
version "0.0.33"
|
version "0.0.33"
|
||||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||||
|
@ -5950,6 +6278,13 @@ toidentifier@1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
||||||
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
||||||
|
|
||||||
|
touch@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"
|
||||||
|
integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==
|
||||||
|
dependencies:
|
||||||
|
nopt "~1.0.10"
|
||||||
|
|
||||||
tough-cookie@^2.3.3, tough-cookie@^2.3.4:
|
tough-cookie@^2.3.3, tough-cookie@^2.3.4:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
||||||
|
@ -6023,6 +6358,13 @@ uglify-js@^3.1.4:
|
||||||
commander "~2.20.0"
|
commander "~2.20.0"
|
||||||
source-map "~0.6.1"
|
source-map "~0.6.1"
|
||||||
|
|
||||||
|
undefsafe@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.2.tgz#225f6b9e0337663e0d8e7cfd686fc2836ccace76"
|
||||||
|
integrity sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=
|
||||||
|
dependencies:
|
||||||
|
debug "^2.2.0"
|
||||||
|
|
||||||
unicode-canonical-property-names-ecmascript@^1.0.4:
|
unicode-canonical-property-names-ecmascript@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
|
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
|
||||||
|
@ -6076,6 +6418,32 @@ unset-value@^1.0.0:
|
||||||
has-value "^0.3.1"
|
has-value "^0.3.1"
|
||||||
isobject "^3.0.0"
|
isobject "^3.0.0"
|
||||||
|
|
||||||
|
unzip-response@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97"
|
||||||
|
integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=
|
||||||
|
|
||||||
|
upath@^1.1.1:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068"
|
||||||
|
integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==
|
||||||
|
|
||||||
|
update-notifier@^2.5.0:
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6"
|
||||||
|
integrity sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==
|
||||||
|
dependencies:
|
||||||
|
boxen "^1.2.1"
|
||||||
|
chalk "^2.0.1"
|
||||||
|
configstore "^3.0.0"
|
||||||
|
import-lazy "^2.1.0"
|
||||||
|
is-ci "^1.0.10"
|
||||||
|
is-installed-globally "^0.1.0"
|
||||||
|
is-npm "^1.0.0"
|
||||||
|
latest-version "^3.0.0"
|
||||||
|
semver-diff "^2.0.0"
|
||||||
|
xdg-basedir "^3.0.0"
|
||||||
|
|
||||||
uri-js@^4.2.2:
|
uri-js@^4.2.2:
|
||||||
version "4.2.2"
|
version "4.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||||
|
@ -6088,6 +6456,13 @@ urix@^0.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
|
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
|
||||||
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
|
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
|
||||||
|
|
||||||
|
url-parse-lax@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73"
|
||||||
|
integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=
|
||||||
|
dependencies:
|
||||||
|
prepend-http "^1.0.1"
|
||||||
|
|
||||||
use@^3.1.0:
|
use@^3.1.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||||
|
@ -6252,6 +6627,13 @@ wide-align@^1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
string-width "^1.0.2 || 2"
|
string-width "^1.0.2 || 2"
|
||||||
|
|
||||||
|
widest-line@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc"
|
||||||
|
integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==
|
||||||
|
dependencies:
|
||||||
|
string-width "^2.1.1"
|
||||||
|
|
||||||
wordwrap@~0.0.2:
|
wordwrap@~0.0.2:
|
||||||
version "0.0.3"
|
version "0.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
|
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
|
||||||
|
@ -6275,7 +6657,7 @@ wrappy@1:
|
||||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||||
|
|
||||||
write-file-atomic@^2.1.0:
|
write-file-atomic@^2.0.0, write-file-atomic@^2.1.0:
|
||||||
version "2.4.3"
|
version "2.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481"
|
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481"
|
||||||
integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==
|
integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==
|
||||||
|
@ -6298,6 +6680,11 @@ ws@^5.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
async-limiter "~1.0.0"
|
async-limiter "~1.0.0"
|
||||||
|
|
||||||
|
xdg-basedir@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
||||||
|
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
|
||||||
|
|
||||||
xml-name-validator@^3.0.0:
|
xml-name-validator@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||||
|
|