Experimental port to Firebase hosting

This commit is contained in:
Michael Jackson
2019-01-05 16:50:05 -08:00
parent e4d6df255e
commit 31e7d3865a
300 changed files with 129300 additions and 5817 deletions

View File

@ -1,13 +0,0 @@
.about-logos {
margin: 2em 0;
display: flex;
justify-content: center;
}
.about-logo {
text-align: center;
flex: 1;
max-width: 80%;
}
.about-logo img {
max-width: 60%;
}

View File

@ -1,12 +1,95 @@
require('./About.css');
import React from 'react';
const React = require('react');
import Wrapper from './Wrapper';
const h = require('../utils/createHTML');
const markup = require('./About.md');
import cloudflareLogo from './CloudflareLogo.png';
import herokuLogo from './HerokuLogo.png';
function About() {
return <div className="wrapper" dangerouslySetInnerHTML={h(markup)} />;
const styles = {
logoList: {
margin: '2em 0',
display: 'flex',
justifyContent: 'center'
},
logo: {
textAlign: 'center',
flex: '1',
maxWidth: '80%'
},
logoImage: {
maxWidth: '60%'
}
};
function AboutLogo({ children }) {
return <div style={styles.logo}>{children}</div>;
}
module.exports = About;
function AboutLogoImage(props) {
return <img {...props} style={styles.logoImage} />;
}
export default function About() {
return (
<Wrapper>
<p>
unpkg is an <a href="https://github.com/unpkg">open source</a> project
built and maintained by{' '}
<a href="https://twitter.com/mjackson">Michael Jackson</a>.
</p>
<h3 id="sponsors">Sponsors</h3>
<p>
The fast, global infrastructure that powers unpkg is generously donated
by <a href="https://www.cloudflare.com">Cloudflare</a> and{' '}
<a href="https://www.heroku.com">Heroku</a>.
</p>
<div style={styles.logoList}>
<AboutLogo>
<a href="https://www.cloudflare.com">
<AboutLogoImage src={cloudflareLogo} />
</a>
</AboutLogo>
<AboutLogo>
<a href="https://www.heroku.com">
<AboutLogoImage src={herokuLogo} />
</a>
</AboutLogo>
</div>
<h3 id="cache-behavior">Cache Behavior</h3>
<p>
The CDN caches files based on their permanent URL, which includes the
npm package version. This works because npm does not allow package
authors to overwrite a package that has already been published with a
different one at the same version number.
</p>
<p>
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
specified, or the <code>maxSatisfying</code> version when a{' '}
<a href="https://github.com/npm/node-semver">semver version</a> is
given. Redirects are cached for 5 minutes.
</p>
<p>
Browsers are instructed (via the <code>Cache-Control</code> header) to
cache assets for 1 year.
</p>
<h3 id="abuse">Abuse</h3>
<p>
unpkg maintains a list of packages that are known to be malicious. If
you find such a package on npm, please let us know!
</p>
<h3 id="support">Support</h3>
<p>
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 href="https://twitter.com/unpkg">@unpkg</a> with any questions or
concerns.
</p>
</Wrapper>
);
}

View File

@ -1,30 +0,0 @@
unpkg is an [open source](https://github.com/unpkg) project built and maintained by [Michael Jackson](https://twitter.com/mjackson).
### Sponsors
The fast, global infrastructure that powers unpkg is generously donated by [Cloudflare](https://www.cloudflare.com) and [Heroku](https://www.heroku.com).
<div class="about-logos">
<div class="about-logo">
<a href="https://www.cloudflare.com"><img src="CloudflareLogo.png"></a>
</div>
<div class="about-logo">
<a href="https://www.heroku.com"><img src="HerokuLogo.png"></a>
</div>
</div>
### Cache Behavior
The CDN caches files based on their permanent URL, which includes the npm package version. This works because npm does not allow package authors to overwrite a package that has already been published with a different one at the same version number.
URLs that do not specify a package version number redirect to one that does. This is the `latest` version when no version is specified, or the `maxSatisfying` version when a [semver version](https://github.com/npm/node-semver) is given. Redirects are cached for 5 minutes.
Browsers are instructed (via the `Cache-Control` header) to cache assets for 1 year.
### Abuse
unpkg maintains a list of packages that are known to be malicious. If you find such a package on npm, please let us know!
### Support
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 [@unpkg](https://twitter.com/unpkg) with any questions or concerns.

View File

@ -1,7 +1,7 @@
const React = require('react');
const { HashRouter } = require('react-router-dom');
import React from 'react';
import { HashRouter } from 'react-router-dom';
const Layout = require('./Layout');
import Layout from './Layout';
function App() {
return (
@ -11,4 +11,4 @@ function App() {
);
}
module.exports = App;
export default App;

View File

@ -1,6 +0,0 @@
.home-example {
text-align: center;
background-color: #eee;
margin: 2em 0;
padding: 5px 0;
}

View File

@ -1,12 +1,159 @@
require('./Home.css');
import React from 'react';
const React = require('react');
import Wrapper from './Wrapper';
const h = require('../utils/createHTML');
const markup = require('./Home.md');
const styles = {
homeExample: {
textAlign: 'center',
backgroundColor: '#eee',
margin: '2em 0',
padding: '5px 0'
}
};
function Home() {
return <div className="wrapper" dangerouslySetInnerHTML={h(markup)} />;
export default function Home() {
return (
<Wrapper>
<p>
unpkg is a fast, global{' '}
<a href="https://en.wikipedia.org/wiki/Content_delivery_network">
content delivery network
</a>{' '}
for everything on <a href="https://www.npmjs.com/">npm</a>. Use it to
quickly and easily load any file from any package using a URL like:
</p>
<div style={styles.homeExample}>unpkg.com/:package@:version/:file</div>
<h3>Examples</h3>
<p>Using a fixed version:</p>
<ul>
<li>
<a href="/react@16.0.0/umd/react.production.min.js">
unpkg.com/react@16.0.0/umd/react.production.min.js
</a>
</li>
<li>
<a href="/react-dom@16.0.0/umd/react-dom.production.min.js">
unpkg.com/react-dom@16.0.0/umd/react-dom.production.min.js
</a>
</li>
</ul>
<p>
You may also use a{' '}
<a href="https://docs.npmjs.com/misc/semver">semver range</a> or a{' '}
<a href="https://docs.npmjs.com/cli/dist-tag">tag</a> instead of a fixed
version number, or omit the version/tag entirely to use the{' '}
<code>latest</code> tag.
</p>
<ul>
<li>
<a href="/react@^16/umd/react.production.min.js">
unpkg.com/react@^16/umd/react.production.min.js
</a>
</li>
<li>
<a href="/react/umd/react.production.min.js">
unpkg.com/react/umd/react.production.min.js
</a>
</li>
</ul>
<p>
If you omit the file path (i.e. use a &ldquo;bare&rdquo; URL), unpkg
will serve the file specified by the <code>unpkg</code> field in{' '}
<code>package.json</code>, or fall back to
<code>main</code>.
</p>
<ul>
<li>
<a href="/d3">unpkg.com/d3</a>
</li>
<li>
<a href="/jquery">unpkg.com/jquery</a>
</li>
<li>
<a href="/three">unpkg.com/three</a>
</li>
</ul>
<p>
Append a <code>/</code> at the end of a URL to view a listing of all the
files in a package.
</p>
<ul>
<li>
<a href="/react/">unpkg.com/react/</a>
</li>
<li>
<a href="/lodash/">unpkg.com/lodash/</a>
</li>
</ul>
<h3>Query Parameters</h3>
<dl>
<dt>
<code>?meta</code>
</dt>
<dd>
Return metadata about any file in a package as JSON (e.g.
<code>/any/file?meta</code>)
</dd>
<dt>
<code>?module</code>
</dt>
<dd>
Expands all{' '}
<a href="https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier">
&ldquo;bare&rdquo; <code>import</code> specifiers
</a>
in JavaScript modules to unpkg URLs. This feature is{' '}
<em>very experimental</em>
</dd>
</dl>
<h3>Workflow</h3>
<p>
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
include your <a href="https://github.com/umdjs/umd">UMD</a> build in
your npm package (not your repo, that&apos;s different!).
</p>
<p>You can do this easily using the following setup:</p>
<ul>
<li>
Add the <code>umd</code> (or <code>dist</code>) directory to your{' '}
<code>.gitignore</code> file
</li>
<li>
Add the <code>umd</code> directory to your{' '}
<a href="https://docs.npmjs.com/files/package.json#files">
files array
</a>{' '}
in
<code>package.json</code>
</li>
<li>
Use a build script to generate your UMD build in the <code>umd</code>{' '}
directory when you publish
</li>
</ul>
<p>
That&apos;s it! Now when you <code>npm publish</code> you&apos;ll have a
version available on unpkg as well.
</p>
</Wrapper>
);
}
module.exports = Home;

View File

@ -1,48 +0,0 @@
unpkg is a fast, global [content delivery network](https://en.wikipedia.org/wiki/Content_delivery_network) for everything on [npm](https://www.npmjs.com/). Use it to quickly and easily load any file from any package using a URL like:
<div class="home-example">unpkg.com/:package@:version/:file</div>
### Examples
Using a fixed version:
* [unpkg.com/react@16.0.0/umd/react.production.min.js](/react@16.0.0/umd/react.production.min.js)
* [unpkg.com/react-dom@16.0.0/umd/react-dom.production.min.js](/react-dom@16.0.0/umd/react-dom.production.min.js)
You may also use a [semver range](https://docs.npmjs.com/misc/semver) or a [tag](https://docs.npmjs.com/cli/dist-tag) instead of a fixed version number, or omit the version/tag entirely to use the `latest` tag.
* [unpkg.com/react@^16/umd/react.production.min.js](/react@^16/umd/react.production.min.js)
* [unpkg.com/react/umd/react.production.min.js](/react/umd/react.production.min.js)
If you omit the file path (i.e. use a "bare" URL), unpkg will serve the file specified by the `unpkg` field in `package.json`, or fall back to `main`.
* [unpkg.com/d3](/d3)
* [unpkg.com/jquery](/jquery)
* [unpkg.com/three](/three)
Append a `/` at the end of a URL to view a listing of all the files in a package.
* [unpkg.com/react/](/react/)
* [unpkg.com/lodash/](/lodash/)
### Query Parameters
<dl>
<dt>`?meta`</dt>
<dd>Return metadata about any file in a package as JSON (e.g. `/any/file?meta`)</dd>
<dt>`?module`</dt>
<dd>Expands all ["bare" `import` specifiers](https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier) in JavaScript modules to unpkg URLs. This feature is *very experimental*</dd>
</dl>
### Workflow
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 include your [UMD](https://github.com/umdjs/umd) build in your npm package (not your repo, that's different!).
You can do this easily using the following setup:
* Add the `umd` (or `dist`) directory to your `.gitignore` file
* Add the `umd` directory to your [files array](https://docs.npmjs.com/files/package.json#files) in `package.json`
* Use a build script to generate your UMD build in the `umd` directory when you publish
That's it! Now when you `npm publish` you'll have a version available on unpkg as well.

View File

@ -1,38 +0,0 @@
.layout-title {
margin: 0;
text-transform: uppercase;
text-align: center;
font-size: 5em;
}
.layout-nav {
margin: 0 0 3em;
}
.layout-navList {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
}
.layout-navList li {
flex-basis: auto;
list-style-type: none;
display: inline-block;
font-size: 1.1em;
margin: 0 10px;
}
.layout-navList li a:link {
text-decoration: none;
}
.layout-navList li a:link,
.layout-navList li a:visited {
color: black;
}
.layout-navUnderline {
height: 4px;
background-color: black;
position: absolute;
left: 0;
}

View File

@ -1,14 +1,47 @@
require('./Layout.css');
import React from 'react';
import PropTypes from 'prop-types';
import { Switch, Route, Link, withRouter } from 'react-router-dom';
import { Motion, spring } from 'react-motion';
const React = require('react');
const PropTypes = require('prop-types');
const { Switch, Route, Link, withRouter } = require('react-router-dom');
const { Motion, spring } = require('react-motion');
import WindowSize from './WindowSize';
import About from './About';
import Stats from './Stats';
import Home from './Home';
const WindowSize = require('./WindowSize');
const About = require('./About');
const Stats = require('./Stats');
const Home = require('./Home');
const styles = {
title: {
margin: 0,
textTransform: 'uppercase',
textAlign: 'center',
fontSize: '5em'
},
nav: {
margin: '0 0 3em'
},
navList: {
margin: 0,
padding: 0,
display: 'flex',
justifyContent: 'center'
},
navListItem: {
flexBasis: 'auto',
listStyleType: 'none',
display: 'inline-block',
fontSize: '1.1em',
margin: '0 10px'
},
navLink: {
textDecoration: 'none',
color: 'black'
},
navUnderline: {
height: 4,
backgroundColor: 'black',
position: 'absolute',
left: 0
}
};
class Layout extends React.Component {
static propTypes = {
@ -66,8 +99,9 @@ class Layout extends React.Component {
}
componentDidUpdate(prevProps) {
if (prevProps.location.pathname !== this.props.location.pathname)
if (prevProps.location.pathname !== this.props.location.pathname) {
this.adjustUnderline(true);
}
}
render() {
@ -85,20 +119,23 @@ class Layout extends React.Component {
<WindowSize onChange={this.adjustUnderline} />
<div className="wrapper">
<header>
<h1 className="layout-title">unpkg</h1>
<nav className="layout-nav">
<ol
className="layout-navList"
ref={node => (this.listNode = node)}
>
<li>
<Link to="/">Home</Link>
<h1 style={styles.title}>unpkg</h1>
<nav style={styles.nav}>
<ol style={styles.navList} ref={node => (this.listNode = node)}>
<li style={styles.navListItem}>
<Link to="/" style={styles.navLink}>
Home
</Link>
</li>
<li>
<Link to="/stats">Stats</Link>
<li style={styles.navListItem}>
<Link to="/stats" style={styles.navLink}>
Stats
</Link>
</li>
<li>
<Link to="/about">About</Link>
<li style={styles.navListItem}>
<Link to="/about" style={styles.navLink}>
About
</Link>
</li>
</ol>
<Motion
@ -106,8 +143,8 @@ class Layout extends React.Component {
style={style}
children={style => (
<div
className="layout-navUnderline"
style={{
...styles.navUnderline,
WebkitTransform: `translate3d(${style.left}px,0,0)`,
transform: `translate3d(${style.left}px,0,0)`,
width: style.width
@ -132,4 +169,4 @@ class Layout extends React.Component {
}
}
module.exports = withRouter(Layout);
export default withRouter(Layout);

View File

@ -1,8 +0,0 @@
.table-filter {
font-size: 0.8em;
text-align: right;
}
.regions-table .country-row td.country-name {
padding-left: 20px;
}

View File

@ -1,14 +1,24 @@
require('./Stats.css');
import React from 'react';
import PropTypes from 'prop-types';
import formatBytes from 'pretty-bytes';
import formatDate from 'date-fns/format';
import parseDate from 'date-fns/parse';
const React = require('react');
const PropTypes = require('prop-types');
const formatBytes = require('pretty-bytes');
const formatDate = require('date-fns/format');
const parseDate = require('date-fns/parse');
const { continents, countries } = require('countries-list');
import { continents, countries } from './countries.json';
const formatNumber = require('../utils/formatNumber');
const formatPercent = require('../utils/formatPercent');
import Wrapper from './Wrapper';
import formatNumber from '../utils/formatNumber';
import formatPercent from '../utils/formatPercent';
const styles = {
tableFilter: {
fontSize: '0.8em',
textAlign: 'right'
},
countryName: {
paddingLeft: 20
}
};
function getCountriesByContinent(continent) {
return Object.keys(countries).filter(
@ -20,17 +30,17 @@ function sumKeyValues(hash, keys) {
return keys.reduce((n, key) => n + (hash[key] || 0), 0);
}
function sumValues(hash) {
return Object.keys(hash).reduce((memo, key) => memo + hash[key], 0);
}
// function sumValues(hash) {
// return Object.keys(hash).reduce((memo, key) => memo + hash[key], 0);
// }
class Stats extends React.Component {
export default class Stats extends React.Component {
static propTypes = {
data: PropTypes.object
};
state = {
minPackageRequests: 1000000,
// minPackageRequests: 1000000,
minCountryRequests: 1000000
};
@ -46,45 +56,45 @@ class Stats extends React.Component {
const until = parseDate(totals.until);
// Packages
const packageRows = [];
// const packageRows = [];
Object.keys(totals.requests.package)
.sort((a, b) => {
return totals.requests.package[b] - totals.requests.package[a];
})
.forEach(packageName => {
const requests = totals.requests.package[packageName];
const bandwidth = totals.bandwidth.package[packageName];
// Object.keys(totals.requests.package)
// .sort((a, b) => {
// return totals.requests.package[b] - totals.requests.package[a];
// })
// .forEach(packageName => {
// const requests = totals.requests.package[packageName];
// const bandwidth = totals.bandwidth.package[packageName];
if (requests >= this.state.minPackageRequests) {
packageRows.push(
<tr key={packageName}>
<td>
<a
href={`https://npmjs.org/package/${packageName}`}
title={`${packageName} on npm`}
>
{packageName}
</a>
</td>
<td>
{formatNumber(requests)} (
{formatPercent(requests / totals.requests.all)}
%)
</td>
{bandwidth ? (
<td>
{formatBytes(bandwidth)} (
{formatPercent(bandwidth / totals.bandwidth.all)}
%)
</td>
) : (
<td>-</td>
)}
</tr>
);
}
});
// if (requests >= this.state.minPackageRequests) {
// packageRows.push(
// <tr key={packageName}>
// <td>
// <a
// href={`https://npmjs.org/package/${packageName}`}
// title={`${packageName} on npm`}
// >
// {packageName}
// </a>
// </td>
// <td>
// {formatNumber(requests)} (
// {formatPercent(requests / totals.requests.all)}
// %)
// </td>
// {bandwidth ? (
// <td>
// {formatBytes(bandwidth)} (
// {formatPercent(bandwidth / totals.bandwidth.all)}
// %)
// </td>
// ) : (
// <td>-</td>
// )}
// </tr>
// );
// }
// });
// Regions
const regionRows = [];
@ -114,7 +124,7 @@ class Stats extends React.Component {
continentData.bandwidth !== 0
) {
regionRows.push(
<tr key={continent} className="continent-row">
<tr key={continent}>
<td>
<strong>{continentName}</strong>
</td>
@ -145,8 +155,8 @@ class Stats extends React.Component {
if (countryRequests > this.state.minCountryRequests) {
regionRows.push(
<tr key={continent + country} className="country-row">
<td className="country-name">{countries[country].name}</td>
<tr key={continent + country}>
<td style={styles.countryName}>{countries[country].name}</td>
<td>
{formatNumber(countryRequests)} (
{formatPercent(countryRequests / totals.requests.all)}
@ -165,27 +175,27 @@ class Stats extends React.Component {
});
// Protocols
const protocolRows = Object.keys(totals.requests.protocol)
.sort((a, b) => {
return totals.requests.protocol[b] - totals.requests.protocol[a];
})
.map(protocol => {
const requests = totals.requests.protocol[protocol];
// const protocolRows = Object.keys(totals.requests.protocol)
// .sort((a, b) => {
// return totals.requests.protocol[b] - totals.requests.protocol[a];
// })
// .map(protocol => {
// const requests = totals.requests.protocol[protocol];
return (
<tr key={protocol}>
<td>{protocol}</td>
<td>
{formatNumber(requests)} (
{formatPercent(requests / sumValues(totals.requests.protocol))}
%)
</td>
</tr>
);
});
// return (
// <tr key={protocol}>
// <td>{protocol}</td>
// <td>
// {formatNumber(requests)} (
// {formatPercent(requests / sumValues(totals.requests.protocol))}
// %)
// </td>
// </tr>
// );
// });
return (
<div className="wrapper">
<Wrapper>
<p>
From <strong>{formatDate(since, 'MMM D')}</strong> to{' '}
<strong>{formatDate(until, 'MMM D')}</strong> unpkg served{' '}
@ -201,6 +211,12 @@ class Stats extends React.Component {
<h3>Packages</h3>
<p>
We recently migrated unpkg to a new backend and are working on getting
package-specific data back on the site.
</p>
{/*
<p>
The table below shows the most popular packages served by unpkg from{' '}
<strong>{formatDate(since, 'MMM D')}</strong> to{' '}
@ -208,7 +224,7 @@ class Stats extends React.Component {
{Object.keys(totals.requests.package).length} packages are shown.
</p>
<p className="table-filter">
<p style={styles.tableFilter}>
Include only packages that received at least{' '}
<select
value={this.state.minPackageRequests}
@ -244,6 +260,7 @@ class Stats extends React.Component {
</thead>
<tbody>{packageRows}</tbody>
</table>
*/}
<h3>Regions</h3>
@ -253,7 +270,7 @@ class Stats extends React.Component {
<strong>{formatDate(until, 'MMM D')}</strong> by geographic region.
</p>
<p className="table-filter">
<p style={styles.tableFilter}>
Include only countries that made at least{' '}
<select
value={this.state.minCountryRequests}
@ -272,12 +289,7 @@ class Stats extends React.Component {
requests.
</p>
<table
cellSpacing="0"
cellPadding="0"
style={{ width: '100%' }}
className="regions-table"
>
<table cellSpacing="0" cellPadding="0" style={{ width: '100%' }}>
<thead>
<tr>
<th>
@ -294,6 +306,7 @@ class Stats extends React.Component {
<tbody>{regionRows}</tbody>
</table>
{/*
<h3>Protocols</h3>
<p>
@ -315,9 +328,8 @@ class Stats extends React.Component {
</thead>
<tbody>{protocolRows}</tbody>
</table>
</div>
*/}
</Wrapper>
);
}
}
module.exports = Stats;

View File

@ -1,22 +1,22 @@
const React = require('react');
const PropTypes = require('prop-types');
import React from 'react';
import PropTypes from 'prop-types';
const addEvent = require('../utils/addEvent');
const removeEvent = require('../utils/removeEvent');
import { addEvent, removeEvent } from '../utils/dom';
const resizeEvent = 'resize';
class WindowSize extends React.Component {
export default class WindowSize extends React.Component {
static propTypes = {
onChange: PropTypes.func
};
handleWindowResize = () => {
if (this.props.onChange)
if (this.props.onChange) {
this.props.onChange({
width: window.innerWidth,
height: window.innerHeight
});
}
};
componentDidMount() {
@ -31,5 +31,3 @@ class WindowSize extends React.Component {
return null;
}
}
module.exports = WindowSize;

View File

@ -0,0 +1,5 @@
import React from 'react';
export default function Wrapper({ children }) {
return <div style={{ maxWidth: 700, margin: '0 auto' }}>{children}</div>;
}

File diff suppressed because one or more lines are too long