Skip to content

Commit 776e315

Browse files
authored
Improve tests and coverage of extracting (#25)
* rewrite extracting logic to ditch coverage API * oopsie * oopsie * omg * improve cssfinding * fix tests Co-authored-by: Bart Veneman <bartveneman@users.noreply.github.com>
1 parent 796c88a commit 776e315

22 files changed

+174
-153
lines changed

package-lock.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
"@bartveneman/eslint-config-bv": "0.0.2",
2828
"ava": "^2.4.0",
2929
"create-test-server": "^3.0.1",
30-
"eslint": "^6.7.2"
30+
"eslint": "^6.7.2",
31+
"sirv": "^0.4.2"
3132
},
3233
"dependencies": {
3334
"puppeteer": "^2.0.0"

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ String.
5757

5858
Type: `Object`
5959

60-
Default: `null`
60+
Default: `{}`
6161

6262
#### waitUntil
6363

src/index.js

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
/* global document */
2-
32
const puppeteer = require('puppeteer')
4-
const crypto = require('crypto')
5-
6-
function hashString(str) {
7-
return crypto.createHash('md5').update(str, 'utf8').digest('hex')
8-
}
93

104
function InvalidUrlError({url, statusCode, statusText}) {
115
this.name = 'InvalidUrlError'
@@ -17,15 +11,14 @@ InvalidUrlError.prototype = Error.prototype
1711
/**
1812
* @param {string} url URL to get CSS from
1913
* @param {string} waitUntil https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#pagegotourl-options
14+
* @returns {string} All CSS that was found
2015
*/
2116
module.exports = async (url, {waitUntil = 'networkidle0'} = {}) => {
2217
// Setup a browser instance
2318
const browser = await puppeteer.launch()
2419

2520
// Create a new page and navigate to it
2621
const page = await browser.newPage()
27-
28-
// Start CSS coverage. This is the meat and bones of this module
2922
await page.coverage.startCSSCoverage()
3023
const response = await page.goto(url, {waitUntil})
3124

@@ -43,22 +36,22 @@ module.exports = async (url, {waitUntil = 'networkidle0'} = {}) => {
4336
)
4437
}
4538

46-
// Coverage contains a lot of <style> and <link> CSS,
47-
// but not all...
4839
const coverage = await page.coverage.stopCSSCoverage()
4940

5041
// Get all CSS generated with the CSSStyleSheet API
5142
// This is primarily for CSS-in-JS solutions
5243
// See: https://developer.mozilla.org/en-US/docs/Web/API/CSSRule/cssText
5344
const styleSheetsApiCss = await page.evaluate(() => {
5445
return [...document.styleSheets]
46+
// Only take the stylesheets without href (BUT WHY)
5547
.filter(stylesheet => stylesheet.href === null)
56-
.map(stylesheet =>
57-
[...stylesheet.cssRules]
58-
.map(cssStyleRule => cssStyleRule.cssText)
59-
.join('\n')
60-
)
61-
.join('\n')
48+
.map(stylesheet => {
49+
return {
50+
type: stylesheet.ownerNode.tagName.toLowerCase(),
51+
href: stylesheet.href || document.location.href,
52+
css: [...stylesheet.cssRules].map(({cssText}) => cssText).join('\n')
53+
}
54+
})
6255
})
6356

6457
// Get all inline styles: <element style="">
@@ -71,38 +64,36 @@ module.exports = async (url, {waitUntil = 'networkidle0'} = {}) => {
7164
// <h1 style="color: red;">Text</h1>
7265
//
7366
// CSSRule:
74-
// [x-inline-style-237a7d] { color: red; }
75-
// ^^^^^^
67+
// [x-extract-css-inline-style] { color: red; }
7668
//
77-
// The 6-digit hash is based on the actual CSS, so it's not
78-
// necessarily unique!
7969
const inlineCssRules = await page.evaluate(() => {
8070
return [...document.querySelectorAll('[style]')]
8171
.map(element => element.getAttribute('style'))
72+
// Filter out empty style="" attributes
8273
.filter(Boolean)
8374
})
8475
const inlineCss = inlineCssRules
85-
.map(rule => {
86-
const hash = hashString(rule).slice(-6)
87-
return `[x-inline-style-${hash}] { ${rule} }`
88-
})
89-
.join('\n')
76+
.map(rule => `[x-extract-css-inline-style] { ${rule} }`)
77+
.map(css => ({type: 'inline', href: url, css}))
9078

91-
await browser.close()
92-
93-
// Turn the coverage Array into a single string of CSS
94-
const coverageCss = coverage
79+
const links = coverage
9580
// Filter out the <style> tags that were found in the coverage
9681
// report since we've conducted our own search for them.
9782
// A coverage CSS item with the same url as the url of the page
9883
// we requested is an indication that this was a <style> tag
99-
.filter(styles => styles.url !== url)
100-
// The `text` property contains the actual CSS
101-
.map(({text}) => text)
102-
.join('\n')
84+
.filter(entry => entry.url !== url)
85+
.map(entry => ({
86+
href: entry.url,
87+
css: entry.text,
88+
type: 'link-or-import'
89+
}))
90+
91+
await browser.close()
10392

104-
const css = [styleSheetsApiCss, coverageCss, inlineCss]
105-
.filter(Boolean)
93+
const css = links
94+
.concat(styleSheetsApiCss)
95+
.concat(inlineCss)
96+
.map(({css}) => css)
10697
.join('\n')
10798

10899
return Promise.resolve(css)

test/fixture.css

Lines changed: 0 additions & 2 deletions
This file was deleted.
File renamed without changes.

test/fixtures/import-in-css.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.css-imported-with-css {}

test/fixtures/import-in-js.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.css-imported-with-js {}
File renamed without changes.
File renamed without changes.

test/fixtures/kitchen-sink.html

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Document</title>
7+
8+
<!-- <link> in HTML -->
9+
<link rel="stylesheet" href="/link-in-html.css">
10+
11+
<!-- <style> in HTML -->
12+
<style>
13+
.style-tag-in-html { color: green; }
14+
</style>
15+
16+
<style id="js-insertRule"></style>
17+
</head>
18+
<body>
19+
20+
<div style="background-image: url('background-image-inline-style-attribute-in-html');"></div>
21+
<div id="inline-csstext"></div>
22+
<div id="inline-prop"></div>
23+
24+
<!-- <link> tag in JS -->
25+
<script>
26+
var style = document.createElement('link')
27+
style.href = 'link-tag-js.css'
28+
style.rel = 'stylesheet'
29+
document.head.appendChild(style)
30+
</script>
31+
32+
<!-- <style> tag in JS -->
33+
<script>
34+
var style = document.createElement('style')
35+
style.textContent = '.style-tag-js { }'
36+
document.body.appendChild(style)
37+
</script>
38+
39+
<!-- inline styles via JS -->
40+
<script>
41+
var element = document.querySelector('#inline-prop')
42+
element.style.backgroundImage = 'url(background-image-inline-style-js-with-prop)';
43+
</script>
44+
<script>
45+
var element = document.querySelector('#inline-csstext')
46+
element.style.cssText = 'background-image: url(background-image-inline-style-js-cssText)';
47+
</script>
48+
49+
<!-- Add styles via CSSStyleSheet.insertRule() -->
50+
<script>
51+
var sheet = document.querySelector('#js-insertRule').sheet
52+
sheet.insertRule('.js-insertRule { color: red; }')
53+
</script>
54+
</body>
55+
</html>

test/fixtures/link-in-html.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@import url("import-in-css.css");
2+
3+
.link-in-html { }

test/link-tag-html.html renamed to test/fixtures/link-tag-html.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>Document</title>
7-
<link rel="stylesheet" href="/fixture.css">
7+
<link rel="stylesheet" href="/link-in-html.css">
88
</head>
99
<body>
1010
<h1 class="fixture">&lt;link> tag in HTML</h1>

test/fixtures/link-tag-js.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@import url("import-in-css.css");
2+
3+
.link-tag-created-with-js {}

test/link-tag-js.html renamed to test/fixtures/link-tag-js.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ <h1 class="fixture">&lt;link> tag in JS</h1>
1212
<div class="imported">imported</div>
1313
<script>
1414
var style = document.createElement('link')
15-
style.href = 'fixture.css'
15+
style.href = 'link-tag-js.css'
1616
style.rel = 'stylesheet'
1717
document.head.appendChild(style)
1818
</script>

test/style-tag-html.html renamed to test/fixtures/style-tag-html.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>Document</title>
77
<style>
8-
@import url('imported.css');
8+
@import url('import-in-css.css');
99
.fixture { color: red; }
1010
</style>
1111
</head>

test/style-tag-js.html renamed to test/fixtures/style-tag-js.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<h1 class="fixture">&lt;style> tag in JS</h1>
1010
<script>
1111
var style = document.createElement('style')
12-
style.textContent = '@import url("imported.css");.fixture { color: red; }'
12+
style.textContent = '@import url("import-in-js.css");.fixture { color: red; }'
1313
document.body.appendChild(style)
1414
</script>
1515
</body>

test/imported.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)