Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@
[![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
[![Project: Wallace](https://img.shields.io/badge/Project-Wallace-29c87d.svg)](https://www.projectwallace.com/oss)

## Usage

```js
const extractCss = require('extract-css-core')

const css = await extractCss('http://www.projectwallace.com')
```

## Installation

```sh
npm install extract-css-core
# or
yarn add extract-css-core
```

## Problem, solution and shortcomings

### Problem
Expand All @@ -28,23 +44,7 @@ This module uses an instance of Chromium to render a page. This has the benefit
that most of the styles can be rendered, even when generated by JavaScript. The
[Puppeteer CSSCoverage API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#coveragestartcsscoverageoptions)
is the power behind finding most of the CSS. Additionally, the
`document.styleSheets` API is used to get CSS-inJS styling.

## Installation

```sh
npm install extract-css-core
# or
yarn add extract-css-core
```

## Usage

```js
const extractCss = require('extract-css-core')

const css = await extractCss('http://www.projectwallace.com')
```
`document.styleSheets` API is used to get CSS-in-JS styling.

## API

Expand All @@ -63,7 +63,7 @@ Default: `null`

Type: `String`

Default: `networkidle2`
Default: `networkidle0`

Can be any value as provided by the
[Puppeteer docs](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagegotourl-options).
Expand All @@ -72,4 +72,4 @@ Can be any value as provided by the

- [Wallace CLI](https://github.com/bartveneman/wallace-cli) - Pretty CSS analytics in your terminal
- [get-css](https://github.com/cssstats/cssstats/tree/master/packages/get-css) -
The original get-css
The original get-css from CSSStats
19 changes: 14 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ function InvalidUrlError({url, statusCode, statusText}) {

InvalidUrlError.prototype = Error.prototype

module.exports = async (url, {waitUntil = 'networkidle2'} = {}) => {
/**
* @param {string} url URL to get CSS from
* @param {string} waitUntil https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#pagegotourl-options
*/
module.exports = async (url, {waitUntil = 'networkidle0'} = {}) => {
// Setup a browser instance
const browser = await puppeteer.launch()

Expand Down Expand Up @@ -37,6 +41,7 @@ module.exports = async (url, {waitUntil = 'networkidle2'} = {}) => {
const coverage = await page.coverage.stopCSSCoverage()

// Get all CSS generated with the CSSStyleSheet API
// This is primarily for CSS-in-JS solutions
// See: https://developer.mozilla.org/en-US/docs/Web/API/CSSRule/cssText
const styleSheetsApiCss = await page.evaluate(() => {
/* global document */
Expand All @@ -45,9 +50,9 @@ module.exports = async (url, {waitUntil = 'networkidle2'} = {}) => {
.map(stylesheet =>
[...stylesheet.cssRules]
.map(cssStyleRule => cssStyleRule.cssText)
.join('')
.join('\n')
)
.join('')
.join('\n')
})

await browser.close()
Expand All @@ -61,7 +66,11 @@ module.exports = async (url, {waitUntil = 'networkidle2'} = {}) => {
.filter(styles => styles.url !== url)
// The `text` property contains the actual CSS
.map(({text}) => text)
.join('')
.join('\n')

return Promise.resolve(styleSheetsApiCss + coverageCss)
const css = [styleSheetsApiCss, coverageCss]
.filter(Boolean)
.join('\n')

return Promise.resolve(css)
}
3 changes: 2 additions & 1 deletion test/fixture.css
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.fixture { color: red; }
@import url("imported.css");
.fixture { color: red; }
1 change: 1 addition & 0 deletions test/imported.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.imported { color: blue; }
61 changes: 45 additions & 16 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,58 +6,87 @@ const {resolve} = require('path')
const extractCss = require('..')

let server
const fixture = readFileSync(resolve(__dirname, 'fixture.css'), 'utf8')

function staticFile(req, res) {
function serveStatic(req, res) {
const fileContents = readFileSync(resolve(__dirname, req.path.slice(1)), 'utf8')
res.send(fileContents)
}

test.before(async () => {
server = await createTestServer()

server.get('/fixture.css', staticFile)
server.get('/fixture.css', serveStatic)
server.get('/imported.css', serveStatic)
})

test.after(async () => {
await server.close()
})

test('it finds css in a <link> tag - HTML', async t => {
server.get('/link-tag-html.html', staticFile)
// @TODO: during tests, it doesn't find the imported CSS file contents
// but it does work outside of test scope
server.get('/link-tag-html.html', serveStatic)
const actual = await extractCss(server.url + '/link-tag-html.html')
const expected = fixture
t.is(actual, expected)

t.true(actual.includes('@import url("imported.css");'))
t.true(actual.includes('.fixture { color: red; }'))
t.snapshot(actual)
})

test('it finds css in a <link> tag - JS', async t => {
server.get('/link-tag-js.html', staticFile)
// @TODO: during tests, it doesn't find the imported CSS file contents
// but it does work outside of test scope
server.get('/link-tag-js.html', serveStatic)
const actual = await extractCss(server.url + '/link-tag-js.html')
const expected = fixture
t.is(actual, expected)

t.true(actual.includes('@import url("imported.css");'))
t.true(actual.includes('.fixture { color: red; }'))
t.snapshot(actual)
})

test('it finds css in a <style> tag - HTML', async t => {
server.get('/style-tag-html.html', staticFile)
server.get('/style-tag-html.html', serveStatic)
const actual = await extractCss(server.url + '/style-tag-html.html')
const expected = '.fixture { color: red; }'
t.is(actual, expected)

t.true(actual.includes('@import url("imported.css");'))
t.true(actual.includes('.fixture { color: red; }'))
t.true(actual.includes('.imported { color: blue; }'))
t.snapshot(actual)
})

test('it finds css in a <style> tag - JS', async t => {
server.get('/style-tag-js.html', staticFile)
server.get('/style-tag-js.html', serveStatic)
const actual = await extractCss(server.url + '/style-tag-js.html')
const expected = '.fixture { color: red; }'
t.is(actual, expected)

t.true(actual.includes('@import url("imported.css");'))
t.true(actual.includes('.fixture { color: red; }'))
t.true(actual.includes('.imported { color: blue; }'))
t.snapshot(actual)
})

test('it finds css-in-js', async t => {
server.get('/css-in-js.html', staticFile)
server.get('/css-in-js.html', serveStatic)
const actual = await extractCss(server.url + '/css-in-js.html')
const expected = '.bcMPWx { color: blue; }'

t.is(actual, expected)
})

test('it does not report the same CSS twice', async t => {
// @TODO: during tests, it doesn't find the imported CSS file contents
// but it does work outside of test scope
server.get('/kitchen-sink.html', serveStatic)
const actual = await extractCss(server.url + '/kitchen-sink.html')

t.true(actual.includes('@import url("imported.css");'))
t.true(actual.includes('.fixture { color: red; }'))
t.true(actual.includes('.style-tag-fixture-js { color: yellow; }'))
t.true(actual.includes('.style-tag-fixture-html { color: green; }'))

t.snapshot(actual)
})

test('it rejects if the url has an HTTP error status', async t => {
server.get('/404-page', (req, res) => {
res.status(404).send()
Expand Down
33 changes: 33 additions & 0 deletions test/kitchen-sink.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>

<!-- <link> in HTML -->
<link rel="stylesheet" href="/fixture.css">

<!-- <style> in HTML -->
<style>
.style-tag-fixture-html { color: green; }
</style>
</head>
<body>

<!-- <link> tag in JS -->
<script>
var style = document.createElement('link')
style.href = 'fixture.css'
style.rel = 'stylesheet'
document.head.appendChild(style)
</script>

<!-- <style> tag in JS -->
<script>
var style = document.createElement('style')
style.textContent = '.style-tag-fixture-js { color: yellow; }'
document.body.appendChild(style)
</script>
</body>
</html>
1 change: 1 addition & 0 deletions test/link-tag-html.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
</head>
<body>
<h1 class="fixture">&lt;link> tag in HTML</h1>
<div class="imported">imported</div>
</body>
</html>
1 change: 1 addition & 0 deletions test/link-tag-js.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

<body>
<h1 class="fixture">&lt;link> tag in JS</h1>
<div class="imported">imported</div>
<script>
var style = document.createElement('link')
style.href = 'fixture.css'
Expand Down
45 changes: 37 additions & 8 deletions test/snapshots/index.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,43 @@ The actual snapshot is saved in `index.js.snap`.

Generated by [AVA](https://ava.li).

## it combines server generated <link> and <style> tags with client side created <link> and <style> tags
## it does not report the same CSS twice

> Snapshot 1

`.server-style::after { content: "server-style"; }.js-style::after { content: "js-style"; }body {␊
color: teal;␊
}␊
body {␊
color: teal;␊
}␊
`
`.style-tag-fixture-html { color: green; }␊
.style-tag-fixture-js { color: yellow; }␊
@import url("imported.css");␊
.fixture { color: red; }␊
@import url("imported.css");␊
.fixture { color: red; }`

## it finds css in a <link> tag - HTML

> Snapshot 1

`@import url("imported.css");␊
.fixture { color: red; }`

## it finds css in a <link> tag - JS

> Snapshot 1

`@import url("imported.css");␊
.fixture { color: red; }`

## it finds css in a <style> tag - HTML

> Snapshot 1

`@import url("imported.css");␊
.fixture { color: red; }␊
.imported { color: blue; }`

## it finds css in a <style> tag - JS

> Snapshot 1

`@import url("imported.css");␊
.fixture { color: red; }␊
.imported { color: blue; }`
Binary file modified test/snapshots/index.js.snap
Binary file not shown.
3 changes: 2 additions & 1 deletion test/style-tag-html.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.fixture { color: red; }
@import url('imported.css');
.fixture { color: red; }
</style>
</head>
<body>
Expand Down
2 changes: 1 addition & 1 deletion test/style-tag-js.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<h1 class="fixture">&lt;style> tag in JS</h1>
<script>
var style = document.createElement('style')
style.textContent = '.fixture { color: red; }'
style.textContent = '@import url("imported.css");.fixture { color: red; }'
document.body.appendChild(style)
</script>
</body>
Expand Down