1 | var assertHtml = require('assert-html')
|
2 | var dedent = require('dedent')
|
3 | var mkdirp = require('mkdirp')
|
4 | var rimraf = require('rimraf')
|
5 | var path = require('path')
|
6 | var tape = require('tape')
|
7 | var fs = require('fs')
|
8 |
|
9 | var bankai = require('../')
|
10 |
|
11 | var tmpDirname
|
12 |
|
13 | function cleanup () {
|
14 | rimraf.sync(tmpDirname)
|
15 | }
|
16 |
|
17 | tape('renders some HTML', function (assert) {
|
18 | assert.on('end', cleanup)
|
19 |
|
20 | var expected = `
|
21 | <!DOCTYPE html>
|
22 | <html lang="en-US" dir="ltr">
|
23 | <head>
|
24 | <meta charset="utf-8">
|
25 | <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
26 | <script src="/__SCRIPTS_HASH__/bundle.js" integrity="sha512-__SCRIPTS_INTEGRITY__" defer></script>
|
27 | <script>;(function(a){"use strict";var b=function(b,c,d){function e(a){return h.body?a():void setTimeout(function(){e(a)})}function f(){i.addEventListener&&i.removeEventListener("load",f),i.media=d||"all"}var g,h=a.document,i=h.createElement("link");if(c)g=c;else{var j=(h.body||h.getElementsByTagName("head")[0]).childNodes;g=j[j.length-1]}var k=h.styleSheets;i.rel="stylesheet",i.href=b,i.media="only x",e(function(){g.parentNode.insertBefore(i,c?g:g.nextSibling)});var l=function(a){for(var b=i.href,c=k.length;c--;)if(k[c].href===b)return a();setTimeout(function(){l(a)})};return i.addEventListener&&i.addEventListener("load",f),i.onloadcssdefined=l,l(f),i};"undefined"!=typeof exports?exports.loadCSS=b:a.loadCSS=b})("undefined"!=typeof global?global:this);;(function(a){if(a.loadCSS){var b=loadCSS.relpreload={};if(b.support=function(){try{return a.document.createElement("link").relList.supports("preload")}catch(b){return!1}},b.poly=function(){for(var b=a.document.getElementsByTagName("link"),c=0;c<b.length;c++){var d=b[c];"preload"===d.rel&&"style"===d.getAttribute("as")&&(a.loadCSS(d.href,d,d.getAttribute("media")),d.rel=null)}},!b.support()){b.poly();var c=a.setInterval(b.poly,300);a.addEventListener&&a.addEventListener("load",function(){b.poly(),a.clearInterval(c)}),a.attachEvent&&a.attachEvent("onload",function(){a.clearInterval(c)})}}})(this);</script>
|
28 | <link rel="manifest" href="/manifest.json">
|
29 | <meta name="description" content=>
|
30 | <meta name="theme-color" content=#fff>
|
31 | <title></title>
|
32 | <link rel="preload" as="style" href="/__STYLE_HASH__/bundle.css" onload="this.rel='stylesheet'">
|
33 | </head>
|
34 | <body></body>
|
35 | </html>
|
36 | `.replace(/\n +/g, '')
|
37 |
|
38 | var script = dedent`
|
39 | 1 + 1
|
40 | `
|
41 |
|
42 | var dirname = 'document-pipeline-' + (Math.random() * 1e4).toFixed()
|
43 | tmpDirname = path.join(__dirname, '../tmp', dirname)
|
44 | var tmpScriptname = path.join(tmpDirname, 'index.js')
|
45 |
|
46 | mkdirp.sync(tmpDirname)
|
47 | fs.writeFileSync(tmpScriptname, script)
|
48 |
|
49 | var compiler = bankai(tmpScriptname, { watch: false })
|
50 | compiler.documents('/', function (err, res) {
|
51 | assert.error(err, 'no error writing document')
|
52 | assertHtml(assert, String(res.buffer), expected)
|
53 | })
|
54 |
|
55 | compiler.on('change', function (nodeName, second) {
|
56 | if (nodeName !== 'documents' || second !== 'list') return
|
57 | assert.end()
|
58 | })
|
59 |
|
60 | compiler.on('error', function () {
|
61 |
|
62 | })
|
63 |
|
64 | compiler.scripts('bundle.js', function (err, res) {
|
65 | assert.ifError(err, 'no err bundling scripts')
|
66 | expected = expected.replace('__SCRIPTS_HASH__', res.hash.toString('hex').slice(0, 16))
|
67 | expected = expected.replace('__SCRIPTS_INTEGRITY__', res.hash.toString('base64'))
|
68 |
|
69 | compiler.styles('bundle.css', function (err, res) {
|
70 | assert.ifError(err, 'no err bundling style')
|
71 | expected = expected.replace('__STYLE_HASH__', res.hash.toString('hex').slice(0, 16))
|
72 | expected = expected.replace('__STYLE_INTEGRITY__', res.hash.toString('base64'))
|
73 | })
|
74 | })
|
75 | })
|
76 |
|
77 | tape('server render choo apps', function (assert) {
|
78 | var expected = `
|
79 | <!DOCTYPE html>
|
80 | <html lang="en-US" dir="ltr">
|
81 | <head>
|
82 | <meta charset="utf-8">
|
83 | <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
84 | <script src="/__SCRIPTS_HASH__/bundle.js" integrity="sha512-__SCRIPTS_INTEGRITY__" defer></script>
|
85 | <script>;(function(a){"use strict";var b=function(b,c,d){function e(a){return h.body?a():void setTimeout(function(){e(a)})}function f(){i.addEventListener&&i.removeEventListener("load",f),i.media=d||"all"}var g,h=a.document,i=h.createElement("link");if(c)g=c;else{var j=(h.body||h.getElementsByTagName("head")[0]).childNodes;g=j[j.length-1]}var k=h.styleSheets;i.rel="stylesheet",i.href=b,i.media="only x",e(function(){g.parentNode.insertBefore(i,c?g:g.nextSibling)});var l=function(a){for(var b=i.href,c=k.length;c--;)if(k[c].href===b)return a();setTimeout(function(){l(a)})};return i.addEventListener&&i.addEventListener("load",f),i.onloadcssdefined=l,l(f),i};"undefined"!=typeof exports?exports.loadCSS=b:a.loadCSS=b})("undefined"!=typeof global?global:this);;(function(a){if(a.loadCSS){var b=loadCSS.relpreload={};if(b.support=function(){try{return a.document.createElement("link").relList.supports("preload")}catch(b){return!1}},b.poly=function(){for(var b=a.document.getElementsByTagName("link"),c=0;c<b.length;c++){var d=b[c];"preload"===d.rel&&"style"===d.getAttribute("as")&&(a.loadCSS(d.href,d,d.getAttribute("media")),d.rel=null)}},!b.support()){b.poly();var c=a.setInterval(b.poly,300);a.addEventListener&&a.addEventListener("load",function(){b.poly(),a.clearInterval(c)}),a.attachEvent&&a.attachEvent("onload",function(){a.clearInterval(c)})}}})(this);</script>
|
86 | <link rel="preload" as="font" crossorigin href="/assets/font.woff">
|
87 | <link rel="manifest" href="/manifest.json">
|
88 | <meta name="description" content=>
|
89 | <meta name="theme-color" content=#fff>
|
90 | <title></title>
|
91 | <link rel="preload" as="style" href="/__STYLE_HASH__/bundle.css" onload="this.rel='stylesheet'">
|
92 | </head>
|
93 | <body>
|
94 | meow
|
95 | </body>
|
96 | </html>
|
97 | `.replace(/\n +/g, '')
|
98 |
|
99 | var script = dedent`
|
100 | var html = require('choo/html')
|
101 | var choo = require('choo')
|
102 |
|
103 | var app = choo()
|
104 | app.route('/', function () {
|
105 | return html\`<body>meow</body>\`
|
106 | })
|
107 | if (module.parent) module.exports = app
|
108 | else app.mount('body')
|
109 | `
|
110 |
|
111 | var dirname = 'document-pipeline-' + (Math.random() * 1e4).toFixed()
|
112 | tmpDirname = path.join(__dirname, '../tmp', dirname)
|
113 | var tmpScriptname = path.join(tmpDirname, 'index.js')
|
114 | var assetDirname = path.join(tmpDirname, 'assets')
|
115 | var fontFilename = path.join(assetDirname, 'font.woff')
|
116 |
|
117 | mkdirp.sync(tmpDirname)
|
118 | mkdirp.sync(assetDirname)
|
119 |
|
120 | fs.writeFileSync(tmpScriptname, script)
|
121 | fs.writeFileSync(fontFilename, 'binary font data')
|
122 |
|
123 | var compiler = bankai(tmpScriptname, { watch: false })
|
124 | compiler.documents('/', function (err, res) {
|
125 | assert.error(err, 'no error writing document')
|
126 | assertHtml(assert, String(res.buffer), expected)
|
127 | })
|
128 |
|
129 | compiler.on('change', function (nodeName, second) {
|
130 | if (nodeName !== 'documents' || second !== 'list') return
|
131 | assert.end()
|
132 | })
|
133 |
|
134 | compiler.on('error', function () {
|
135 |
|
136 | })
|
137 |
|
138 | compiler.scripts('bundle.js', function (err, res) {
|
139 | assert.ifError(err, 'no err bundling scripts')
|
140 | expected = expected.replace('__SCRIPTS_HASH__', res.hash.toString('hex').slice(0, 16))
|
141 | expected = expected.replace('__SCRIPTS_INTEGRITY__', res.hash.toString('base64'))
|
142 | compiler.styles('bundle.css', function (err, res) {
|
143 | assert.ifError(err, 'no err bundling style')
|
144 | assert.ifError(err)
|
145 | expected = expected.replace('__STYLE_HASH__', res.hash.toString('hex').slice(0, 16))
|
146 | expected = expected.replace('__STYLE_INTEGRITY__', res.hash.toString('base64'))
|
147 | })
|
148 | })
|
149 | })
|
150 |
|
151 | tape('server render choo apps with root set', function (assert) {
|
152 | assert.on('end', cleanup)
|
153 |
|
154 | var expected = `
|
155 | <!DOCTYPE html>
|
156 | <html lang="en-US" dir="ltr">
|
157 | <head>
|
158 | <meta charset="utf-8">
|
159 | <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
160 | <script src="some-custom-root/__SCRIPTS_HASH__/bundle.js" integrity="sha512-__SCRIPTS_INTEGRITY__" defer></script>
|
161 | <script>;(function(a){"use strict";var b=function(b,c,d){function e(a){return h.body?a():void setTimeout(function(){e(a)})}function f(){i.addEventListener&&i.removeEventListener("load",f),i.media=d||"all"}var g,h=a.document,i=h.createElement("link");if(c)g=c;else{var j=(h.body||h.getElementsByTagName("head")[0]).childNodes;g=j[j.length-1]}var k=h.styleSheets;i.rel="stylesheet",i.href=b,i.media="only x",e(function(){g.parentNode.insertBefore(i,c?g:g.nextSibling)});var l=function(a){for(var b=i.href,c=k.length;c--;)if(k[c].href===b)return a();setTimeout(function(){l(a)})};return i.addEventListener&&i.addEventListener("load",f),i.onloadcssdefined=l,l(f),i};"undefined"!=typeof exports?exports.loadCSS=b:a.loadCSS=b})("undefined"!=typeof global?global:this);;(function(a){if(a.loadCSS){var b=loadCSS.relpreload={};if(b.support=function(){try{return a.document.createElement("link").relList.supports("preload")}catch(b){return!1}},b.poly=function(){for(var b=a.document.getElementsByTagName("link"),c=0;c<b.length;c++){var d=b[c];"preload"===d.rel&&"style"===d.getAttribute("as")&&(a.loadCSS(d.href,d,d.getAttribute("media")),d.rel=null)}},!b.support()){b.poly();var c=a.setInterval(b.poly,300);a.addEventListener&&a.addEventListener("load",function(){b.poly(),a.clearInterval(c)}),a.attachEvent&&a.attachEvent("onload",function(){a.clearInterval(c)})}}})(this);</script>
|
162 | <link rel="preload" as="font" crossorigin href="some-custom-root/assets/font.woff">
|
163 | <link rel="manifest" href="some-custom-root/manifest.json">
|
164 | <meta name="description" content=>
|
165 | <meta name="theme-color" content=#fff>
|
166 | <title></title>
|
167 | <link rel="preload" as="style" href="some-custom-root/__STYLE_HASH__/bundle.css" onload="this.rel='stylesheet'">
|
168 | </head>
|
169 | <body>
|
170 | meow
|
171 | </body>
|
172 | </html>
|
173 | `.replace(/\n +/g, '')
|
174 |
|
175 | var script = dedent`
|
176 | var html = require('choo/html')
|
177 | var choo = require('choo')
|
178 |
|
179 | var app = choo()
|
180 | app.route('/', function () {
|
181 | return html\`<body>meow</body>\`
|
182 | })
|
183 | if (module.parent) module.exports = app
|
184 | else app.mount('body')
|
185 | `
|
186 |
|
187 | var dirname = 'document-pipeline-' + (Math.random() * 1e4).toFixed()
|
188 | tmpDirname = path.join(__dirname, '../tmp', dirname)
|
189 | var tmpScriptname = path.join(tmpDirname, 'index.js')
|
190 | var assetDirname = path.join(tmpDirname, 'assets')
|
191 | var fontFilename = path.join(assetDirname, 'font.woff')
|
192 |
|
193 | mkdirp.sync(tmpDirname)
|
194 | mkdirp.sync(assetDirname)
|
195 |
|
196 | fs.writeFileSync(tmpScriptname, script)
|
197 | fs.writeFileSync(fontFilename, 'binary font data')
|
198 |
|
199 | var compiler = bankai(tmpScriptname, { watch: false, base: 'some-custom-root' })
|
200 | compiler.documents('/', function (err, res) {
|
201 | assert.error(err, 'no error writing document')
|
202 | assertHtml(assert, String(res.buffer), expected)
|
203 | })
|
204 |
|
205 | compiler.on('change', function (nodeName, second) {
|
206 | if (nodeName !== 'documents' || second !== 'list') return
|
207 | assert.end()
|
208 | })
|
209 |
|
210 | compiler.on('error', function () {
|
211 |
|
212 | })
|
213 |
|
214 | compiler.scripts('bundle.js', function (err, res) {
|
215 | assert.ifError(err, 'no err bundling scripts')
|
216 | expected = expected.replace('__SCRIPTS_HASH__', res.hash.toString('hex').slice(0, 16))
|
217 | expected = expected.replace('__SCRIPTS_INTEGRITY__', res.hash.toString('base64'))
|
218 | compiler.styles('bundle.css', function (err, res) {
|
219 | assert.ifError(err, 'no err bundling style')
|
220 | assert.ifError(err)
|
221 | expected = expected.replace('__STYLE_HASH__', res.hash.toString('hex').slice(0, 16))
|
222 | expected = expected.replace('__STYLE_INTEGRITY__', res.hash.toString('base64'))
|
223 | })
|
224 | })
|
225 | })
|
226 |
|
227 | tape('custom index.html template', function (assert) {
|
228 | assert.on('end', cleanup)
|
229 | assert.plan(3)
|
230 |
|
231 | var template = `
|
232 | <html>
|
233 | <head>
|
234 | <meta name="test" content="ok">
|
235 | </head>
|
236 | <body>
|
237 | </body>
|
238 | </html>
|
239 | `
|
240 | var file = `
|
241 | var html = require('choo/html')
|
242 | var choo = require('choo')
|
243 |
|
244 | var app = choo()
|
245 | app.route('/', function () {
|
246 | return html\`<body>meow</body>\`
|
247 | })
|
248 | module.exports = app.mount('body')
|
249 | `
|
250 |
|
251 | var dirname = 'document-pipeline-' + (Math.random() * 1e4).toFixed()
|
252 | tmpDirname = path.join(__dirname, '../tmp', dirname)
|
253 | mkdirp.sync(tmpDirname)
|
254 | fs.writeFileSync(path.join(tmpDirname, 'index.js'), file)
|
255 | fs.writeFileSync(path.join(tmpDirname, 'index.html'), template)
|
256 |
|
257 | var compiler = bankai(tmpDirname, { watch: false })
|
258 | compiler.documents('/', function (err, res) {
|
259 | assert.error(err, 'no error writing document')
|
260 | var body = res.buffer.toString('utf8')
|
261 | assert.notEqual(body.indexOf('<meta name="test" content="ok">'), -1, 'used the custom index.html')
|
262 | assert.notEqual(body.indexOf('meow'), -1, 'inserted the rendered app')
|
263 | })
|
264 | })
|
265 |
|
266 | tape('mount choo app into given selector', function (assert) {
|
267 | assert.on('end', cleanup)
|
268 | assert.plan(3)
|
269 |
|
270 | var template = `
|
271 | <html>
|
272 | <head></head>
|
273 | <body>
|
274 | <h1>Some Title!</h1>
|
275 | <div id="app"></div>
|
276 | </body>
|
277 | </html>
|
278 | `
|
279 | var file = `
|
280 | var html = require('choo/html')
|
281 | var choo = require('choo')
|
282 |
|
283 | var app = choo()
|
284 | app.route('/', function () {
|
285 | return html\`<div>meow</div>\`
|
286 | })
|
287 | module.exports = app.mount('#app')
|
288 | `
|
289 |
|
290 | var dirname = 'document-pipeline-' + (Math.random() * 1e4).toFixed()
|
291 | tmpDirname = path.join(__dirname, '../tmp', dirname)
|
292 | mkdirp.sync(tmpDirname)
|
293 | fs.writeFileSync(path.join(tmpDirname, 'index.js'), file)
|
294 | fs.writeFileSync(path.join(tmpDirname, 'index.html'), template)
|
295 |
|
296 | var compiler = bankai(tmpDirname, { watch: false })
|
297 | compiler.documents('/', function (err, res) {
|
298 | assert.error(err, 'no error writing document')
|
299 | var body = res.buffer.toString('utf8')
|
300 | assert.notEqual(body.indexOf('<h1>Some Title!</h1>'), -1, 'preserved body contents outside #app selector')
|
301 | assert.notEqual(body.indexOf('meow'), -1, 'inserted the rendered app')
|
302 | })
|
303 | })
|
304 |
|
305 | tape('inlines critical css', function (assert) {
|
306 | assert.on('end', cleanup)
|
307 | assert.plan(3)
|
308 |
|
309 | var file = `
|
310 | var css = require('sheetify')
|
311 | var html = require('choo/html')
|
312 | var choo = require('choo')
|
313 |
|
314 | css\`
|
315 | .classA { color: red }
|
316 | .classB { color: blue }
|
317 | \`
|
318 |
|
319 | var app = choo()
|
320 | app.route('/', function () {
|
321 | return html\`<body class="classA"></body>\`
|
322 | })
|
323 |
|
324 | // classB
|
325 |
|
326 | module.exports = app.mount('body')
|
327 | `
|
328 |
|
329 | var dirname = 'document-pipeline-' + (Math.random() * 1e4).toFixed()
|
330 | tmpDirname = path.join(__dirname, '../tmp', dirname)
|
331 | mkdirp.sync(tmpDirname)
|
332 | fs.writeFileSync(path.join(tmpDirname, 'index.js'), file)
|
333 |
|
334 | var compiler = bankai(tmpDirname, { watch: false })
|
335 | compiler.documents('/', function (err, res) {
|
336 | assert.error(err, 'no error writing document')
|
337 | var body = res.buffer.toString('utf8')
|
338 | assert.notEqual(body.indexOf('.classA{color:red;}'), -1, 'inlined the .classA selector')
|
339 | assert.equal(body.indexOf('.classB{color:blue;}'), -1, 'did not inline the .classB selector')
|
340 | })
|
341 | })
|