Skip to content

Commit 26277b1

Browse files
authored
Merge pull request #95 from Munter/feature/warnOnEntryPointRedirect
Warn if fetching an entry point results in an HTTP redirect
2 parents 85335ae + 2f1f76e commit 26277b1

File tree

2 files changed

+230
-1
lines changed

2 files changed

+230
-1
lines changed

lib/subfont.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,36 @@ module.exports = async function subfont(
136136

137137
await assetGraph.checkIncompatibleTypes();
138138

139+
const entrypointAssets = assetGraph.findAssets({ isInitial: true });
140+
const redirectOrigins = new Set();
141+
for (const relation of assetGraph
142+
.findRelations({ type: 'HttpRedirect' })
143+
.sort((a, b) => a.id - b.id)) {
144+
if (relation.from.isInitial) {
145+
assetGraph.info(
146+
new Error(`${relation.from.url} redirected to ${relation.to.url}`)
147+
);
148+
relation.to.isInitial = true;
149+
relation.from.isInitial = false;
150+
151+
redirectOrigins.add(relation.to.origin);
152+
}
153+
}
154+
if (
155+
entrypointAssets.length === redirectOrigins.size &&
156+
redirectOrigins.size === 1
157+
) {
158+
const newRoot = `${[...redirectOrigins][0]}/`;
159+
if (newRoot !== assetGraph.root) {
160+
assetGraph.info(
161+
new Error(
162+
`All entrypoints redirected, changing root from ${assetGraph.root} to ${newRoot}`
163+
)
164+
);
165+
assetGraph.root = newRoot;
166+
}
167+
}
168+
139169
let sumSizesBefore = 0;
140170
for (const asset of assetGraph.findAssets({
141171
isInline: false,
@@ -240,6 +270,7 @@ module.exports = async function subfont(
240270
await assetGraph.writeAssetsToDisc(
241271
{
242272
isLoaded: true,
273+
isRedirect: { $ne: true },
243274
url: (url) => url.startsWith(assetGraph.root),
244275
},
245276
outRoot,

test/subfont.js

Lines changed: 199 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const sinon = require('sinon');
33
const expect = require('unexpected').clone().use(require('unexpected-sinon'));
44
const subfont = require('../lib/subfont');
55
const httpception = require('httpception');
6+
const AssetGraph = require('assetgraph');
67
const proxyquire = require('proxyquire');
78
const pathModule = require('path');
89
const openSansBold = require('fs').readFileSync(
@@ -17,7 +18,11 @@ const openSansBold = require('fs').readFileSync(
1718
describe('subfont', function () {
1819
let mockConsole;
1920
beforeEach(async function () {
20-
mockConsole = { log: sinon.spy(), error: sinon.spy() };
21+
mockConsole = { log: sinon.spy(), warn: sinon.spy(), error: sinon.spy() };
22+
});
23+
24+
afterEach(function () {
25+
sinon.restore();
2126
});
2227

2328
describe('when a font is referenced by a stylesheet hosted outside the root', function () {
@@ -212,6 +217,199 @@ describe('subfont', function () {
212217
});
213218
});
214219

220+
describe('when fetching an entry point results in an HTTP redirect', function () {
221+
describe('with a single entry point', function () {
222+
beforeEach(function () {
223+
httpception([
224+
{
225+
request: 'GET http://example.com/',
226+
response: {
227+
statusCode: 301,
228+
headers: {
229+
Location: 'https://somewhereelse.com/',
230+
},
231+
},
232+
},
233+
{
234+
request: 'GET https://somewhereelse.com/',
235+
response: {
236+
headers: {
237+
'Content-Type': 'text/html; charset=utf-8',
238+
},
239+
body: `<!DOCTYPE html>
240+
<html>
241+
242+
<head>
243+
<style>
244+
@font-face {
245+
font-family: Open Sans;
246+
src: url(OpenSans.woff) format('woff');
247+
}
248+
249+
div {
250+
font-family: Open Sans;
251+
}
252+
</style>
253+
</head>
254+
<body>
255+
<div>Hello</div>
256+
</body>
257+
</html>
258+
`,
259+
},
260+
},
261+
{
262+
request: 'GET http://somewhereelse.com/OpenSans.woff',
263+
response: {
264+
headers: {
265+
'Content-Type': 'font/woff',
266+
},
267+
body: openSansBold,
268+
},
269+
},
270+
]);
271+
});
272+
273+
it('should issue a warning', async function () {
274+
const root = 'http://example.com/';
275+
sinon.stub(AssetGraph.prototype, 'info');
276+
277+
const assetGraph = await subfont(
278+
{
279+
root,
280+
inputFiles: [root],
281+
fallbacks: false,
282+
silent: true,
283+
dryRun: true,
284+
},
285+
mockConsole
286+
);
287+
288+
const htmlAssets = assetGraph.findAssets({
289+
isInitial: true,
290+
type: 'Html',
291+
});
292+
expect(htmlAssets, 'to have length', 1);
293+
expect(
294+
htmlAssets[0].url,
295+
'to equal',
296+
'https://somewhereelse.com/index.html'
297+
);
298+
expect(assetGraph.info, 'to have a call satisfying', () => {
299+
assetGraph.info(
300+
new Error(
301+
'http://example.com/ redirected to https://somewhereelse.com/'
302+
)
303+
);
304+
});
305+
});
306+
307+
it('should change the root of the graph so that files get written to disc', async function () {
308+
const root = 'http://example.com/';
309+
310+
sinon.stub(AssetGraph.prototype, 'info');
311+
const assetGraph = await subfont(
312+
{
313+
root,
314+
inputFiles: [root],
315+
fallbacks: false,
316+
silent: true,
317+
dryRun: true,
318+
},
319+
mockConsole
320+
);
321+
322+
expect(assetGraph.root, 'to equal', 'https://somewhereelse.com/');
323+
324+
expect(assetGraph.info, 'to have a call satisfying', () => {
325+
assetGraph.info(
326+
new Error(
327+
'All entrypoints redirected, changing root from http://example.com/ to https://somewhereelse.com/'
328+
)
329+
);
330+
});
331+
});
332+
});
333+
334+
describe('but other entry points do not get redirected', function () {
335+
beforeEach(function () {
336+
httpception([
337+
{
338+
request: 'GET http://example.com/',
339+
response: {
340+
statusCode: 301,
341+
headers: {
342+
Location: 'https://somewhereelse.com/',
343+
},
344+
},
345+
},
346+
{
347+
request: 'GET http://example.com/page2',
348+
response: {
349+
headers: {
350+
'Content-Type': 'text/html; charset=utf-8',
351+
},
352+
body: `<!DOCTYPE html><html></html>`,
353+
},
354+
},
355+
{
356+
request: 'GET https://somewhereelse.com/',
357+
response: {
358+
headers: {
359+
'Content-Type': 'text/html; charset=utf-8',
360+
},
361+
body: `<!DOCTYPE html>
362+
<html>
363+
<head>
364+
<style>
365+
@font-face {
366+
font-family: Open Sans;
367+
src: url(OpenSans.woff) format('woff');
368+
}
369+
370+
div {
371+
font-family: Open Sans;
372+
}
373+
</style>
374+
</head>
375+
<body>
376+
<div>Hello</div>
377+
</body>
378+
</html>
379+
`,
380+
},
381+
},
382+
{
383+
request: 'GET http://somewhereelse.com/OpenSans.woff',
384+
response: {
385+
headers: {
386+
'Content-Type': 'font/woff',
387+
},
388+
body: openSansBold,
389+
},
390+
},
391+
]);
392+
});
393+
394+
it('should not change the root', async function () {
395+
const root = 'http://example.com/';
396+
397+
const assetGraph = await subfont(
398+
{
399+
root,
400+
inputFiles: [root, `${root}page2`],
401+
fallbacks: false,
402+
silent: true,
403+
dryRun: true,
404+
},
405+
mockConsole
406+
);
407+
408+
expect(assetGraph.root, 'to equal', 'http://example.com/');
409+
});
410+
});
411+
});
412+
215413
it('should not dive into iframes', async function () {
216414
const root = encodeURI(
217415
`file://${pathModule.resolve(__dirname, '..', 'testdata', 'iframe')}`

0 commit comments

Comments
 (0)