Inline scripts in HTML files

This commit is contained in:
Michael Jackson
2019-01-24 15:49:21 -08:00
parent e6134b0969
commit ea85062ff6
16 changed files with 350 additions and 136 deletions

View File

@ -1,13 +1,16 @@
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
import semver from 'semver';
import MainTemplate from '../client/MainTemplate';
import AutoIndexApp from '../client/autoIndex/App';
import createHTML from '../client/utils/createHTML';
import getScripts from '../utils/getScripts';
import renderTemplate from '../utils/renderTemplate';
import createElement from './utils/createElement';
import createHTML from './utils/createHTML';
import createScript from './utils/createScript';
import getEntryPoint from './utils/getEntryPoint';
import getGlobalScripts from './utils/getGlobalScripts';
import MainTemplate from './utils/MainTemplate';
const doctype = '<!DOCTYPE html>';
const globalURLs =
process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging'
? {
@ -34,24 +37,27 @@ export default function serveAutoIndexPage(req, res) {
entry: req.entry,
entries: req.entries
};
const content = createHTML(
ReactDOMServer.renderToString(React.createElement(AutoIndexApp, data))
const content = createHTML(renderToString(createElement(AutoIndexApp, data)));
const entryPoint = getEntryPoint('autoIndex', 'iife');
const elements = getGlobalScripts(entryPoint, globalURLs).concat(
createScript(entryPoint.code)
);
const scripts = getScripts('autoIndex', globalURLs);
const html = renderTemplate(MainTemplate, {
title: `UNPKG - ${req.packageName}`,
description: `The CDN for ${req.packageName}`,
data,
content,
scripts
});
const html =
doctype +
renderToStaticMarkup(
createElement(MainTemplate, {
title: `UNPKG - ${req.packageName}`,
description: `The CDN for ${req.packageName}`,
data,
content,
elements
})
);
res
.set({
'Cache-Control': 'no-cache, no-store, must-revalidate', // do not cache
'Cache-Control': 'public, max-age=14400', // 4 hours
'Cache-Tag': 'auto-index'
})
.send(html);

View File

@ -1,12 +1,15 @@
import React from 'react';
import { renderToString } from 'react-dom/server';
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
import MainTemplate from '../client/MainTemplate';
import MainApp from '../client/main/App';
import createHTML from '../client/utils/createHTML';
import getScripts from '../utils/getScripts';
import renderTemplate from '../utils/renderTemplate';
import createElement from './utils/createElement';
import createHTML from './utils/createHTML';
import createScript from './utils/createScript';
import getEntryPoint from './utils/getEntryPoint';
import getGlobalScripts from './utils/getGlobalScripts';
import MainTemplate from './utils/MainTemplate';
const doctype = '<!DOCTYPE html>';
const globalURLs =
process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging'
? {
@ -21,13 +24,19 @@ const globalURLs =
};
export default function serveMainPage(req, res) {
const content = createHTML(renderToString(React.createElement(MainApp)));
const scripts = getScripts('main', globalURLs);
const html = renderTemplate(MainTemplate, { content, scripts });
const content = createHTML(renderToString(createElement(MainApp)));
const entryPoint = getEntryPoint('main', 'iife');
const elements = getGlobalScripts(entryPoint, globalURLs).concat(
createScript(entryPoint.code)
);
const html =
doctype +
renderToStaticMarkup(createElement(MainTemplate, { content, elements }));
res
.set({
'Cache-Control': 'no-cache, no-store, must-revalidate', // do not cache
'Cache-Control': 'public, max-age=14400', // 4 hours
'Cache-Tag': 'main'
})
.send(html);

View File

@ -0,0 +1,71 @@
import React from 'react';
import PropTypes from 'prop-types';
import e from './createElement';
import h from './createHTML';
import x from './createScript';
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>\')';
const fetchShim =
'window.fetch || document.write(\'\\x3Cscript src="/whatwg-fetch@3.0.0/dist/fetch.umd.js">\\x3C/script>\')';
export default function MainTemplate({
title,
description,
favicon,
data,
content,
elements
}) {
return e(
'html',
{ lang: 'en' },
e(
'head',
null,
e('meta', { charSet: 'utf-8' }),
e('meta', { httpEquiv: 'X-UA-Compatible', content: 'IE=edge,chrome=1' }),
description && e('meta', { name: 'description', content: description }),
e('meta', {
name: 'viewport',
content: 'width=device-width,initial-scale=1,maximum-scale=1'
}),
e('meta', { name: 'timestamp', content: new Date().toISOString() }),
favicon && e('link', { rel: 'shortcut icon', href: favicon }),
e('title', null, title),
x(promiseShim),
x(fetchShim),
data && x(`window.__DATA__ = ${JSON.stringify(data)}`)
),
e(
'body',
null,
e('div', { id: 'root', dangerouslySetInnerHTML: content }),
...elements
)
);
}
MainTemplate.defaultProps = {
title: 'UNPKG',
description: 'The CDN for everything on npm',
favicon: '/favicon.ico',
content: h(''),
elements: []
};
if (process.env.NODE_ENV !== 'production') {
const htmlType = PropTypes.shape({
__html: PropTypes.string
});
MainTemplate.propTypes = {
title: PropTypes.string,
description: PropTypes.string,
favicon: PropTypes.string,
data: PropTypes.any,
content: htmlType,
elements: PropTypes.arrayOf(PropTypes.node)
};
}

View File

@ -0,0 +1 @@
export { createElement as default } from 'react';

View File

@ -0,0 +1,8 @@
import createElement from './createElement';
import createHTML from './createHTML';
export default function createScript(script) {
return createElement('script', {
dangerouslySetInnerHTML: createHTML(script)
});
}

View File

@ -0,0 +1,10 @@
import invariant from 'invariant';
import createElement from './createElement';
export default function getGlobalScripts(entryPoint, globalURLs) {
return entryPoint.globalImports.map(id => {
invariant(globalURLs[id], 'Missing global URL for id "%s"', id);
return createElement('script', { src: globalURLs[id] });
});
}

View File

@ -1,68 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import createHTML from './utils/createHTML';
import x from './utils/execScript';
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>\')';
const fetchShim =
'window.fetch || document.write(\'\\x3Cscript src="/whatwg-fetch@3.0.0/dist/fetch.umd.js">\\x3C/script>\')';
export default function MainTemplate({
title,
description,
favicon,
data,
content,
scripts
}) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge,chrome=1" />
{description && <meta name="description" content={description} />}
<meta
name="viewport"
content="width=device-width,initial-scale=1,maximum-scale=1"
/>
<meta name="timestamp" content={new Date().toISOString()} />
{favicon && <link rel="shortcut icon" href={favicon} />}
<title>{title}</title>
{x(promiseShim)}
{x(fetchShim)}
{data && x(`window.__DATA__ = ${JSON.stringify(data)}`)}
</head>
<body>
<div id="root" dangerouslySetInnerHTML={content} />
{scripts.map(src => (
<script key={src} src={src} />
))}
</body>
</html>
);
}
MainTemplate.defaultProps = {
title: 'UNPKG',
description: 'The CDN for everything on npm',
favicon: '/favicon.ico',
content: createHTML(''),
scripts: []
};
if (process.env.NODE_ENV !== 'production') {
const htmlType = PropTypes.shape({
__html: PropTypes.string
});
MainTemplate.propTypes = {
title: PropTypes.string,
description: PropTypes.string,
favicon: PropTypes.string,
data: PropTypes.any,
content: htmlType,
scripts: PropTypes.arrayOf(PropTypes.string)
};
}

View File

@ -1,7 +0,0 @@
import React from 'react';
import h from './createHTML';
export default function execScript(code) {
return <script dangerouslySetInnerHTML={h(code)} />;
}

View File

@ -1,16 +0,0 @@
import invariant from 'invariant';
import getEntryPoint from './getEntryPoint';
export default function getScripts(entryName, globalURLs) {
const entryPoint = getEntryPoint(entryName, 'iife');
invariant(entryPoint, 'Invalid entry name "%s"', entryName);
const globalScripts = entryPoint.globalImports.map(id => {
invariant(globalURLs[id], 'Missing global URL for id "%s"', id);
return globalURLs[id];
});
return globalScripts.concat(entryPoint.url);
}

View File

@ -1,11 +0,0 @@
import React from 'react';
import ReactDOMServer from 'react-dom/server';
const doctype = '<!DOCTYPE html>';
export default function renderTemplate(component, props) {
return (
doctype +
ReactDOMServer.renderToStaticMarkup(React.createElement(component, props))
);
}