Skip to content

Commit

Permalink
add support for fetching @import rules (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartveneman committed Feb 16, 2020
1 parent 8d70f62 commit 86dcd8c
Show file tree
Hide file tree
Showing 12 changed files with 156 additions and 51 deletions.
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

0 comments on commit 86dcd8c

Please sign in to comment.