Skip to content
This repository was archived by the owner on Dec 19, 2023. It is now read-only.

Commit 43f7735

Browse files
authored
Add htmlSource injection (#83)
1 parent 9d80013 commit 43f7735

File tree

7 files changed

+64
-8
lines changed

7 files changed

+64
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## HEAD
4+
5+
- Allow for base path injection in `htmlSource`.
6+
37
## 0.4.0
48

59
- **Bug:**: Fixes the way Underreact handles root relative urls.

README.md

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,26 +200,55 @@ Underreact is intended for single-page apps, so you only need one HTML page. If
200200

201201
You have 2 choices:
202202

203-
1. **Preferred:** Provide the [`htmlSource`] configuration option, which is an HTML string or a Promise that resolves to an HTML string.
203+
1. **Preferred:** Provide the [`htmlSource`] configuration option, which is an HTML string, a Promise or a Function returning HTML string or promise, that resolves to an HTML string.
204204
2. Provide no HTML-rendering function and let Underreact use the default, development-only HTML document. *You should only do this for prototyping and early development*: for production projects, you'll definitely want to define your own HTML, if only for the `<title>`.
205205

206206
If you provide a Promise for [`htmlSource`], you can use any async I/O you need to put together the page. For example, you could read JS files and inject their code directly into `<script>` tags, or inject CSS into `<style>` tags. Or you could make an HTTP call to fetch dynamic data and inject it into the page with a `<script>` tag, so it's available to your React app.
207207

208+
If you provide a Function for [`htmlSource`], Underreact would call it with the named parameter `basePath`. This gives you the flexibility to load assets with a root relative URL. The example below shows how to load a favicon from your `public` directory:
209+
210+
```js
211+
// underreact.config.js
212+
module.exports = {
213+
/**
214+
* @param {Object} opts
215+
* @param {Webpack} opts.basePath - the normalized value of your site's base path
216+
* @returns {Promise<string> | string}
217+
*/
218+
htmlSource: ({ basePath }) => `
219+
<!DOCTYPE html>
220+
<html lang="en">
221+
<head>
222+
<meta charset="utf-8">
223+
<title>Words that rhyme with fish</title>
224+
<meta name="description" content="A website about words that rhyme with fish, like plish">
225+
<link rel="shortcut icon" href="${basePath}/img/favicon.ico" type="image/x-icon" />
226+
</head>
227+
<body>
228+
<div id="app">
229+
<!-- React app will be rendered into this div -->
230+
</div>
231+
</body>
232+
</html>
233+
`
234+
};
235+
```
236+
208237
**Note: Underreact would automatically inject the relevant `script` and `link` tags to your HTML template.**
209238

210239
In the example below, we are defining our HTML in a separate file and requiring it in `underreact.config.js`:
211240

212241
```js
213242
// underreact.config.js
214-
const htmlSource = require('./html-source');
243+
const html = require('./html');
215244

216245
module.exports = function underreactConfig({ webpack, command, mode }) {
217246
return {
218-
htmlSource: htmlSource(mode)
247+
htmlSource: html(mode)
219248
};
220249
};
221250

222-
// html-source.js
251+
// html.js
223252
const fs = require('fs');
224253
const { promisify } = require('util');
225254
const minimizeJs = require('./minimize-js');
@@ -460,7 +489,7 @@ Enable hot module reloading of Underreact. Read ["How do I enable hot module rel
460489

461490
### htmlSource
462491

463-
Type: `string`\|`Promise<string>`. Default:[see the default HTML](https://github.com/mapbox/underreact/blob/next/lib/default-html.js).
492+
Type: `string`\|`Promise<string>`\|`Function<string | Promise<string>>`. Default:[see the default HTML](https://github.com/mapbox/underreact/blob/next/lib/default-html.js).
464493

465494
The HTML template for your app, or a Promise that resolves to it. Read ["Defining your HTML"](#defining-your-html) for more details.
466495

@@ -543,6 +572,8 @@ Path to the base directory on the domain where the site will be deployed. The de
543572

544573
This normalization behaviour comes in handy when writing statements like `process.env.BASE_PATH + '/my-path'`. Read ["How do I include SVGs, images, and videos?"](#how-do-i-include-svgs-images-and-videos).
545574

575+
Underreact also passes this as a named parameter to the [`htmlSource`] function. Read ["Defining your HTML"](#defining-your-html) for more details.
576+
546577
**Tip**: There's a good chance your app isn't at the root of your domain. So this option represents the path of your site *within* that domain. For example, if your app is at `https://www.special.com/ketchup/*`, you should set `siteBasePath: '/ketchup'`.
547578

548579
### stats

examples/fancy/public/blink.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.blink{
2+
animation:blinkText 1.5s infinite;
3+
}
4+
5+
@keyframes blinkText{
6+
0% { color: rgba(0,0,0,0); }
7+
49% { color: rgba(0,0,0,0.33); }
8+
50% { color: rgba(0,0,0,0.33); }
9+
99% { color: rgba(0,0,0,0.33); }
10+
100%{ color: rgba(0,0,0,0); }
11+
}

examples/fancy/src/app.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ class App extends React.Component {
3434
{typeof DEFINE_WORKED === 'undefined' ? 'No' : DEFINE_WORKED}
3535
</p>
3636
<p>Less-loading worked if this text is light blue.</p>
37+
<p className="blink">
38+
HTML base path injection works if this text blinks.
39+
</p>
3740
<p>
3841
Value of the env variable CLIENT_TOKEN:{' '}
3942
{process.env.CLIENT_TOKEN}

examples/fancy/underreact.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
44
const path = require('path');
55

6-
const htmlSource = `
6+
const htmlSource = ({basePath}) => `
77
<!DOCTYPE html>
88
<html lang="en">
99
<head>
1010
<meta charset="UTF-8">
1111
<meta name="viewport" content="width=device-width, initial-scale=1.0">
1212
<title>Fancy examples</title>
1313
<link href="https://api.mapbox.com/mapbox-assembly/v0.21.2/assembly.min.css" rel="stylesheet">
14+
<link href="${basePath}/blink.css" rel="stylesheet">
1415
<script async defer src="https://api.mapbox.com/mapbox-assembly/v0.21.2/assembly.js"></script>
1516
</head>
1617
<body>

lib/config/validate-config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function validateConfig(rawConfig = {}) {
1313
compileNodeModules: v.oneOfType(v.boolean, v.arrayOf(v.string)),
1414
devServerHistoryFallback: v.boolean,
1515
hot: v.boolean,
16-
htmlSource: v.oneOfType(validatePromise, v.string),
16+
htmlSource: v.oneOfType(validatePromise, v.string, v.func),
1717
jsEntry: validateAbsolutePaths,
1818
liveReload: v.boolean,
1919
environmentVariables: v.plainObject,

lib/webpack-config/generate-html-template.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ module.exports = function generateHtmlTemplate({
77
webpackCompilation,
88
publicPath
99
}) {
10-
return Promise.resolve(urc.htmlSource).then(html =>
10+
let htmlSource = urc.htmlSource;
11+
12+
if (typeof urc.htmlSource === 'function') {
13+
htmlSource = urc.htmlSource({ basePath: urc.getBasePathEnv() });
14+
}
15+
16+
return Promise.resolve(htmlSource).then(html =>
1117
html
1218
.replace(
1319
'<head>',

0 commit comments

Comments
 (0)