Skip to content

Commit

Permalink
add full implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
MrSchneepflug committed May 21, 2019
0 parents commit 8c8dc07
Show file tree
Hide file tree
Showing 15 changed files with 2,908 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
.nyc_output/
dist/
7 changes: 7 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
language: node_js
node_js: node
after_success: yarn coverage
branches:
only:
- master
- /^greenkeeper/.*$/
6 changes: 6 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ISC License (ISC)
Copyright 2019-present Mr. Schneepflug

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
79 changes: 79 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# css-surgeon

Takes a single HTML document, removes all unused styles within style-tags and minifies the remains.
The initial intent of this library was to satisfy the size limits of CSS within AMP pages.
Therefore no CSS within an `<style amp-boilerplate>`-tag will be touched.

[![Build Status](https://travis-ci.com/MrSchneepflug/css-surgeon.svg?branch=master)](https://travis-ci.com/MrSchneepflug/css-surgeon)
[![Coverage Status](https://coveralls.io/repos/github/MrSchneepflug/css-surgeon/badge.svg?branch=master)](https://coveralls.io/github/MrSchneepflug/css-surgeon?branch=master)
[![Greenkeeper badge](https://badges.greenkeeper.io/MrSchneepflug/css-surgeon.svg)](https://greenkeeper.io/)

## Installation

```sh
yarn add css-surgeon
```

## Usage

Just pass the HTML document to the exposed `operate`-function.
The function returns a promise which resolves with the whole document.

```js
import {operate} from "css-surgeon";

const htmlWithMinifiedCss = await operate("<!doctype html><html>...");
```

## Example

Take this HTML document for example. There are several unused rules and selectors.

```html
<!doctype html>
<html lang="en">
<head>
<title>single style-tag</title>
<style>
.used-class, .unused-partial-class {color:#000000;}
#used-id, #unused-partial-id {color:#000000;}
h1, h3 {color:#000000;}
.unused-class {color:#000000;}
#unused-id {color:#000000;}
h2 {color:#000000;}
</style>
</head>
<body>
<div class="used-class"></div>
<div id="used-id"></div>
<h1>used element</h1>
</body>
</html>
```

The result of the `operate`-function will be:

```html
<!DOCTYPE html><html lang="en"><head>
<title>single style-tag</title>
<style>.used-class{color:#000}#used-id{color:#000}h1{color:#000}</style>
</head>
<body>
<div class="used-class"></div>
<div id="used-id"></div>
<h1>used element</h1>


</body></html>
```

> **Note:**
> The strange formatting is the result of `JSDOM.serialize()`.
## Tests

```sh
$ yarn install
$ yarn test
```
57 changes: 57 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"name": "css-surgeon",
"version": "1.0.0",
"description": "Removes unused styles within <style>-tags",
"author": "Mr. Schneepflug <mrschneepflug@not-my-problem.de>",
"keywords": [
"css",
"unused"
],
"license": "ISC",
"main": "dist/filter.js",
"types": "dist/filter.d.ts",
"files": [
"dist/operate.js",
"dist/operate.d.ts"
],
"scripts": {
"lint": "tslint --project .",
"test": "nyc mocha -r ts-node/register/transpile-only test/**/*.spec.ts",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"build": "yarn lint && yarn test && tsc",
"prepare": "yarn build",
"preversion": "yarn build"
},
"devDependencies": {
"@types/chai": "^4.1.7",
"@types/clean-css": "^4.2.1",
"@types/css": "^0.0.31",
"@types/jsdom": "^12.2.3",
"@types/mocha": "^5.2.6",
"@types/node": "^12.0.2",
"array-flatten": "^2.1.2",
"chai": "^4.2.0",
"coveralls": "^3.0.3",
"css": "^2.2.4",
"mocha": "^6.1.4",
"nyc": "^14.1.1",
"ts-node": "^8.1.0",
"tslint": "^5.16.0",
"typescript": "^3.4.5"
},
"dependencies": {
"clean-css": "^4.2.1",
"jsdom": "^15.1.0",
"uncss": "^0.16.2"
},
"nyc": {
"all": true,
"extension": [
".ts"
],
"exclude": [
"dist/",
"test/"
]
}
}
25 changes: 25 additions & 0 deletions src/operate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import util from "util";
import {JSDOM} from "jsdom";
import uncss from "uncss";
import CleanCss from "clean-css";

const filterUnusedCss = util.promisify(uncss);
const css = new CleanCss();

async function operate(input: string): Promise<string> {
const dom = new JSDOM(input);
const styleTags = Array.from(dom.window.document.querySelectorAll("style:not([amp-boilerplate])"));

if (styleTags.length === 0) {
return input;
}

await Promise.all(styleTags.map(async styleTag => {
const filteredCss = await filterUnusedCss(input, {raw: styleTag.textContent});
styleTag.textContent = css.minify(filteredCss).styles;
}));

return dom.serialize();
}

export {operate};
16 changes: 16 additions & 0 deletions test/fixtures/amp-style-tag.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<title>AMP style-tag</title>
<style>
.unused {
color: #000000;
}
</style>
<style amp-boilerplate>
.amp-styles-must-not-be-removed {
color: #000000;
}
</style>
</head>
</html>
32 changes: 32 additions & 0 deletions test/fixtures/multiple-style-tags.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!doctype html>
<html lang="en">
<head>
<title>multiple style-tags</title>
<style>
.used-class, .unused-partial-class {color:#000000;}
#used-id, #unused-partial-id {color:#000000;}
h1, h3 {color:#000000;}

.unused-class {color:#000000;}
#unused-id {color:#000000;}
h2 {color:#000000;}
</style>
<style>
.used-class-2, .unused-partial-class-2 {color:#000000;}
#used-id-2, #unused-partial-id-2 {color:#000000;}
h4, h6 {color:#000000;}

.unused-class-2 {color:#000000;}
#unused-id-2 {color:#000000;}
h5 {color:#000000;}
</style>
</head>
<body>
<div class="used-class"></div>
<div id="used-id"></div>
<h1>used element</h1>
<div class="used-class-2"></div>
<div id="used-id-2"></div>
<h4>used element</h4>
</body>
</html>
11 changes: 11 additions & 0 deletions test/fixtures/no-style-tag.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<title>No style-tag</title>
</head>
<body>
<div class="used-class"></div>
<div id="used-id"></div>
<h1>used element</h1>
</body>
</html>
20 changes: 20 additions & 0 deletions test/fixtures/single-style-tag.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<title>single style-tag</title>
<style>
.used-class, .unused-partial-class {color:#000000;}
#used-id, #unused-partial-id {color:#000000;}
h1, h3 {color:#000000;}

.unused-class {color:#000000;}
#unused-id {color:#000000;}
h2 {color:#000000;}
</style>
</head>
<body>
<div class="used-class"></div>
<div id="used-id"></div>
<h1>used element</h1>
</body>
</html>
Loading

0 comments on commit 8c8dc07

Please sign in to comment.