Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

more-css

Package Overview
Dependencies
Maintainers
2
Versions
73
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

more-css - npm Package Compare versions

Comparing version 0.6.2 to 0.7.0

.coveralls.yml

16

demo/jssc.js
define(function(require, exports) {
var factory = require('../web/lexer/factory'),
Token = require('../web/lexer/Token'),
render = require('../web/util/render'),
var factory = require('./factory'),
Token = require('./lexer/Token'),
render = require('./render'),
cacheLine = 0,

@@ -28,3 +28,3 @@ cacheTime = 0,

newClass = (array = /class-name\s*?\:\s*?(\w+)/i.exec(node.className)) === null ? null : array[1];
//����sh��first-line
//兼容sh的first-line
if(start < 1) {

@@ -46,3 +46,3 @@ start = (array = /first-line\s*?\:\s*?(\w+)/i.exec(node.className)) === null ? 0 : parseInt(array[1]);

function join(tokens) {
//�������¼���
//列数重新计算
if(lexer.col() > lastCol) {

@@ -69,3 +69,3 @@ lastCol = lexer.col();

}
//��Ⱦ�µĴ�����
//渲染新的代码行
if(!lexer.finish() && tokens[tokens.length - 1].type() == Token.LINE) {

@@ -76,3 +76,3 @@ tokens.pop();

ol.appendChild(df);
//��߾�
//左边距
var pLeft = (String(lexer.line()).length - 1) * 9 + 30 + 'px';

@@ -93,3 +93,3 @@ col.style.paddingLeft = pLeft;

node.style.display = 'none';
//������ɶ�ѡ������������dz�����������
//根据完成度选择继续分析还是持续分析缓存
function parseNext() {

@@ -96,0 +96,0 @@ if(lexer.finish()) {

{
"name": "more-css",
"version": "0.6.2",
"version": "0.7.0",
"description": "a css pre-compiler & agressive compressor",

@@ -8,7 +8,7 @@ "maintainers": [

"name": "army8735",
"email": "army8735@qq.com"
}
"email": "army8735@qq.com"
}
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "make test"
},

@@ -31,4 +31,27 @@ "repository": {

],
"dependencies": {},
"main": "./server/more",
"config": {
"blanket": {
"pattern": [
"more/build"
]
}
},
"dependencies": {
"clean-css": "^2.2.16",
"homunculus": "^0.5.6"
},
"devDependencies": {
"blanket": "^1.1.6",
"coveralls": "^2.10.0",
"expect.js": "^0.3.1",
"gulp": "^3.8.8",
"gulp-clean": "^0.3.1",
"gulp-jsdc": "^0.2.2",
"gulp-util": "^3.0.1",
"mocha": "^1.21.4",
"mocha-lcov-reporter": "0.0.1",
"through2": "^0.6.1",
"jsdc": "^0.4.14"
},
"main": "./index",
"engines": {

@@ -35,0 +58,0 @@ "node": ">= 0.10.0"

@@ -1,13 +0,20 @@

##A css pre-compiler by javascript
A css pre-compiler & radical-compressor
====
The css lexer bases on jssc: https://github.com/army8735/jssc
相对于less,more的命名意思是比原本的css多做了一点点
相对于less,more的命名意思是比原本的css多做了一点点,定位于普通产品简化开发位置,而非广义CSS下游框架重量级功能。
[![NPM version](https://badge.fury.io/js/more-css.png)](https://npmjs.org/package/more-css)
[![Build Status](https://travis-ci.org/army8735/more.svg?branch=master)](https://travis-ci.org/army8735/more)
[![Coverage Status](https://coveralls.io/repos/army8735/more/badge.png)](https://coveralls.io/r/army8735/more)
[![Dependency Status](https://david-dm.org/army8735/more.png)](https://david-dm.org/army8735/more)
目前提供层级功能、子文件拆分(实际上就是原标准的@import语法,并不擅自发明规范之外的新语法)、层级变量、自动拆分字符串、@extend继承;还有css激进压缩器。
详细见:https://github.com/army8735/more/wiki/%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3
more定位于普通产品简化开发位置,而非广义CSS下游框架重量级功能。more的设计严格遵循css标准规范并进行扩展。
web目录下为书写符合AMD/CMD规范的js文件;
server目录下为nodejs环境的module模块。
目前提供层级功能、子文件拆分(实际上就是原标准的@import语法)、层级变量、自动拆分字符串、@extend深继承;还有css激进压缩器。
详细见:https://github.com/army8735/more/wiki/document
build目录下为nodejs环境的module模块。
web目录下为书写符合AMD/CMD规范的js文件。
more的核心理念是:原有的浏览器标准不修改,编译后的代码严格保持调试一致,行数不变更,文件对应关系不改变,满足GoToDefine的先决条件。

@@ -21,65 +28,37 @@

more.parse(code:String, preVars:Object, preStyles:Object, preFns:Object):String
### More
* constructor(code:String = '') 传入需要预编译的code
* parse(code:String = null):String 预编译code,可以为空,否则会覆盖构造函数里传入的code
* parseFile(file:String, combo:Boolean = false):String 转换css文件,combo表明是否静态合并@import的文件
* combo详见[parseFile高级用法](https://github.com/army8735/more/wiki/document#parsefile高级用法)
* ast():Object 返回解析后的语法树
* tokens():Array<Object> 返回解析后的词法单元序列
* imports():Array<String> 返回解析后的@import文件列表
* vars(data:Object):Object 设置/读取变量哈希
* styles(data:Object):Object 设置/读取样式哈希
* fns(data:Object):Object 设置/读取方法哈希
* config(str:String):void 预编译一段css并将其结果作为此more对象之后预编译时的全局变量
* configFile(file:String):void 同上,传入一个文件的路径
* clean():void 清空设置
方法传入源代码,返回解析后的代码,如果出错,返回错误信息。
preVars为解析前预变量,功用在于实现层级变量作用域——即被import的文件可以访问父级变量。此接口可以自定义实现仅文件内作用域或全局作用域。
preStyles和preFns同理,为预被继承的样式集和方法集。
推荐使用层级作用域,demo文件夹中的import.html和node.js演示了这一特性。
### 静态属性
* parse(code:String = ''):String 快捷方式预编译,无需new步骤
* parseFile(code:String = '', combo:Boolean = false):String 快捷方式预编译文件,无需new步骤
* suffix(str:String):String 全局设置/读取文件后缀名,默认css
* root(str:String):String 全局设置/读取相对本地文件的根路径
* vars(data:Object):Object 全局设置/读取变量哈希
* styles(data:Object):Object 全局设置/读取样式哈希
* fns(data:Object):Object 全局设置/读取方法哈希
* config(str:String):void 预编译一段css并将其结果作为之后全局预编译时的全局变量
* configFile(file:String):void 同上,传入一个文件的路径
* clean():void 清空全局设置
* addKeyWord(kw:String/Array<String>):void 添加未知的css属性关键字以支持分析
* compress(code:String, radical:Boolean = false):String 压缩css代码,radical表明是否使用激进安全算法
more.tree():Node
## Demo
* demo目录下是一个web端的实时转换例子,本地浏览需要`npm install`安装依赖
* 依赖的语法解析器来自于`homunculus`:https://github.com/army8735/homunculus
* 在线地址:http://army8735.me/more/demo/
获取解析后的语法树。此为内部接口,一般用不到。
more.token():Array<Token>
获取解析后的词法单元列表。此为内部接口,一般用不到。
more.vars():Object<String, String>
获取解析后的变量声明哈希表。键为变量名,值为变量值。
more.global(global:Object<String, String/Number/Boolean>):Object<String, String/Number/Boolean>
设置/获取全局变量,可在所有文件中被访问。局部变量拥有更高优先级。
more.styles():Object<String, String>
获取样式集。键为标准选择器名,值为其对应的样式集和。
more.imports():Array
获取解析后的导入文件列表。
more.fns():Object<String, Object>
获取解析后的方法集。
more.compress(src:String, agressive:Boolean):String
压缩css文件。此方法基于clean-css,在不传入agressive参数或为false时即为clean-css的原有压缩功能;agressive为true时进行激进压缩,合并去重聚合以及择优提取公因子,也是安全无冲突的。
激进压缩5个步骤:合并相同选择器merge、去除同一选择器中重复样式声明duplicate、去除同一选择器中被覆盖的样式声明override、聚合相同样式的选择器union、提取公因子extract。
more.root(root:String):String
设置uri的根路径,此在@import解析中会用到,且不使用相对根路径的情况下无需设置。
more.localRoot(localRoot:String):String
设置本地根路径,此在build()方法中用到,且不使用相对根路径的情况下无需设置。
more.less(l:Boolean):Boolean
是否兼容less的相对路径。在uri标准中,'/uri'为相对根路径,'uri'和'./uri'均为相对当前路径,兼容less为仅后者为当前路径,前2为相对根。此设置会干扰@import和build()方法。
more.suffix(s:String):String
设置css文件的后缀名,默认为css,它影响构建@import的文件后缀名。
more.build(file:String, noImport:Boolean = false):String
构建css文件,将一个可能包含多个文件的文件合并为一个单独的字符串。noImport为true时不处理@import的文件,默认包括。
# License
[MIT License]
[MIT License]

@@ -1,1076 +0,375 @@

define(function(require, exports) {
var CssLexer = require('./lexer/CssLexer'),
CssRule = require('./lexer/rule/CssRule'),
Token = require('./lexer/Token'),
Parser = require('./parser/Parser'),
Node = require('./parser/Node'),
cleanCSS = require('clean-css'),
sort = require('./util/sort'),
index,
head,
body;
define(function(require, exports, module){var Clean=function(){var _17=require('clean-css');return _17.hasOwnProperty("Clean")?_17.Clean:_17.hasOwnProperty("default")?_17.default:_17}();
var sort=function(){var _18=require('./sort');return _18.hasOwnProperty("sort")?_18.sort:_18.hasOwnProperty("default")?_18.default:_18}();
var KEY_HASH=function(){var _19=require('./abbreviationKey.js');return _19.hasOwnProperty("KEY_HASH")?_19.KEY_HASH:_19.hasOwnProperty("default")?_19.default:_19}();
var impact=require('./impact');
function getHead(node, ignore) {
var leaves = node.leaves();
leaves.forEach(function(leaf) {
if([Node.FONTFACE, Node.MEDIA, Node.CHARSET, Node.IMPORT, Node.PAGE, Node.KEYFRAMES].indexOf(leaf.name()) > -1) {
joinHead(leaf, ignore);
}
});
}
function joinHead(node, ignore) {
var isToken = node.name() == Node.TOKEN;
if(isToken) {
var token = node.token();
if(token.type() != Token.VIRTUAL) {
head += token.content();
while(ignore[++index]) {
var ig = ignore[index];
head += ig.content();
delete ignore[index];
}
}
}
else {
node.leaves().forEach(function(leaf, i) {
joinHead(leaf, ignore);
});
}
}
var homunculus=require('homunculus');
var currentStyle;
var currentValue;
var currentHack;
var currentImpt;
function rebuild(node, ignore, arr) {
var leaves = node.leaves();
leaves.forEach(function(leaf) {
if([Node.FONTFACE, Node.MEDIA, Node.CHARSET, Node.IMPORT, Node.PAGE, Node.KEYFRAMES].indexOf(leaf.name()) == -1) {
rb(leaf, ignore, arr);
}
});
//��ÿһ��ѡ����˳�����У��Ƚ�ʱ����ֱ��==�Ƚ�
arr.forEach(function(o) {
sort(o.selectors);
o.s2s = o.selectors.join(',');
});
return arr;
}
function rb(node, ignore, arr, isSelector, isValue) {
var isToken = node.name() == Node.TOKEN;
if(isToken) {
var token = node.token();
if(token.type() != Token.VIRTUAL) {
if(isSelector) {
currentSelector += token.content();
}
else if(isValue) {
if(token.type() == Token.HACK) {
currentHack = token.content();
}
else if(token.type() == Token.IMPORTANT) {
currentImpt = true;
}
else {
currentValue += token.content();
}
}
while(ignore[++index]) {
var ig = ignore[index];
if(isSelector) {
currentSelector += ig.content();
}
else if(isValue) {
currentValue += ig.content();
}
}
}
}
else {
if(node.name() == Node.STYLESET) {
arr.push({
selectors: [],
block: []
});
}
else if(node.name() == Node.SELECTOR) {
currentSelector = '';
isSelector = true;
}
else if(node.name() == Node.KEY) {
currentStyle = {
key: node.leaves()[0].token().content()
};
}
else if(node.name() == Node.VALUE) {
currentValue = '';
isValue = true;
currentHack = null;
currentImpt = null;
}
node.leaves().forEach(function(leaf) {
rb(leaf, ignore, arr, isSelector, isValue);
});
if(node.name() == Node.SELECTOR) {
arr[arr.length - 1].selectors.push(currentSelector);
}
else if(node.name() == Node.VALUE) {
currentStyle.value = currentValue;
currentStyle.hack = currentHack;
currentStyle.impt = currentImpt;
arr[arr.length - 1].block.push(currentStyle);
}
}
}
var Token = homunculus.getClass('token');
var Node = homunculus.getClass('node', 'css');
function getK(s) {
if(s.indexOf('-webkit-') == 0) {
s = s.slice(8);
}
else if(s.indexOf('-moz-') == 0) {
s = s.slice(5);
}
else if(s.indexOf('-ms-') == 0) {
s = s.slice(4);
}
else if(s.indexOf('-o-') == 0) {
s = s.slice(3);
}
else if(/^[*_-]/.test(s)) {
s = s.slice(1);
}
return s;
}
var imCache = {};
function noImpact(node, first, other, child) {
var mode = false;
if(typeof child == 'number') {
mode = true;
}
//����::-ms-clear���ų�
for(var i = first; i <= other; i++) {
if(node[i].s2s.indexOf(':-ms-') > -1) {
return false;
}
}
//����ѡ���������ȼ�Ӱ��
if(first == other - 1) {
return true;
}
else if(!mode && typeof imCache[first + ',' + other] == 'boolean') {
return imCache[first + ',' + other];
}
//�ǽ�����������ͬ��ʽ���������important�������ȼ�����������м���ӵ�ֵ��ͬ����Ӱ��
else {
var hash = {};
var keys = {
background: true,
font: true,
margin: true,
padding: true,
'list-style': true,
overflow: true,
border: true,
'border-left': true,
'border-top': true,
'border-right': true,
'border-bottom': true,
'border-radius': true,
'background-position': true,
'background-color': true,
'background-repeat': true,
'background-attachment': true,
'background-image': true,
'font-style': true,
'line-height': true,
'font-family': true,
'font-variant': true,
'font-size': true,
'margin-left': true,
'margin-right': true,
'margin-bottom': true,
'margin-top': true,
'padding-left': true,
'padding-right': true,
'padding-bottom': true,
'padding-top': true,
'list-style-image': true,
'list-style-position': true,
'list-style-type': true,
'overlfow-x': true,
'overlfow-y': true,
'border-left-width': true,
'border-left-color': true,
'border-left-style': true,
'border-right-width': true,
'border-right-color': true,
'border-right-style': true,
'border-top-width': true,
'border-top-color': true,
'border-top-style': true,
'border-bottom-width': true,
'border-bottom-color': true,
'border-bottom-style': true,
'border-top-left-radius': true,
'border-top-right-radius': true,
'border-bottom-left-radius': true,
'border-bottom-right-radius': true
};
for(var i = first + 1; i < other; i++) {
//����
if(imCache[i + ',' + (i + 1)]) {
continue;
}
node[i].block.forEach(function(o) {
var k = getK(o.key);
if(hash[k]) {
hash[k].p = Math.max(hash[k].p, o.impt ? 2 : 1);
//��γ��ֲ�ֵͬ�����¼����Ϊ�������IJ�����ͬʱ��������ֵ��������true˵����ͻ
hash[k].v = hash[k].v == o.value ? o.value : true;
}
else {
hash[k] = {
p: o.impt ? 2 : 1,
v: o.value
};
}
});
}
var res = true;
var block = node[other].block;
//��child����ʱ�����other��ʽ��������ͻ������Ϊotherȫ��
if(mode) {
block = block.slice(child, child + 1);
}
block.forEach(function(o) {
if(res) {
var key = getK(o.key);
var n = hash[key];
if(n && n.p >= (o.impt ? 2 : 1)) {
if(n.v === true || n.v != o.value) {
res = false;
}
}
//����ʽ�ͷ���ʽ�г�ͻ
else if(keys[key]) {
switch(key) {
case 'background':
if( hash['background-position'] ||
hash['background-color'] ||
hash['background-repeat'] ||
hash['background-attachment'] ||
hash['background-image'] ) {
res = false;
}
else {
res = true;
}
break;
case 'background-position':
case 'background-color':
case 'background-repeat':
case 'background-attachment':
case 'background-image':
if(hash['background']) {
res = false;
}
else {
res = true;
}
break;
case 'font':
if( hash['font-style'] ||
hash['line-height'] ||
hash['font-family'] ||
hash['font-variant'] ||
hash['font-size'] ) {
res = false;
}
else {
res = true;
}
break;
case 'font-style':
case 'line-height':
case 'font-family':
case 'font-variant':
case 'font-size':
if(hash['font']) {
res = false;
}
else {
res = true;
}
break;
case 'margin':
if( hash['margin-top'] ||
hash['margin-right'] ||
hash['margin-bottom'] ||
hash['margin-left'] ) {
res = false;
}
else {
res = true;
}
break;
case 'margin-left':
case 'margin-right':
case 'margin-bottom':
case 'margin-top':
if(hash['margin']) {
res = false;
}
else {
res = true;
}
break;
case 'padding':
if( hash['padding-top'] ||
hash['padding-right'] ||
hash['padding-bottom'] ||
hash['padding-left'] ) {
res = false;
}
else {
res = true;
}
break;
case 'padding-left':
case 'padding-right':
case 'padding-bottom':
case 'padding-top':
if(hash['padding']) {
res = false;
}
else {
res = true;
}
break;
case 'list-style':
if( hash['list-style-image'] ||
hash['list-style-position'] ||
hash['list-style-type'] ) {
res = false;
}
else {
res = true;
}
break;
case 'list-style-image':
case 'list-style-position':
case 'list-style-type':
if(hash['list-style']) {
res = false;
}
else {
res = true;
}
break;
case 'overflow':
if( hash['overflow-x'] ||
hash['overflow-y'] ) {
res = false;
}
else {
res = true;
}
break;
case 'overlfow-x':
case 'overlfow-y':
if(hash['overlfow']) {
res = false;
}
else {
res = true;
}
break;
case 'border':
if( hash['border-left'] ||
hash['border-top'] ||
hash['border-right'] ||
hash['border-bottom'] ||
hash['border-width'] ||
hash['border-color'] ||
hash['border-style'] ||
hash['border-left-width'] ||
hash['border-left-color'] ||
hash['border-left-style'] ||
hash['border-top-width'] ||
hash['border-top-color'] ||
hash['border-top-style'] ||
hash['border-right-width'] ||
hash['border-right-color'] ||
hash['border-right-style'] ||
hash['border-bottom-width'] ||
hash['border-bottom-color'] ||
hash['border-bottom-style'] ) {
res = false;
}
else {
res = true;
}
break;
case 'border-left-width':
case 'border-left-color':
case 'border-left-style':
if( hash['border-left'] ||
hash['border'] ) {
res = false;
}
else {
res = true;
}
break;
case 'border-right-width':
case 'border-right-color':
case 'border-right-style':
if( hash['border-right'] ||
hash['border'] ) {
res = false;
}
else {
res = true;
}
break;
case 'border-top-width':
case 'border-top-color':
case 'border-top-style':
if( hash['border-top'] ||
hash['border'] ) {
res = false;
}
else {
res = true;
}
break;
case 'border-bottom-width':
case 'border-bottom-color':
case 'border-bottom-style':
if( hash['border-bottom'] ||
hash['border'] ) {
res = false;
}
else {
res = true;
}
break;
case 'border-left':
if( hash['border'] ||
hash['border-left-width'] ||
hash['border-left-color'] ||
hash['border-left-style'] ) {
res = false;
}
else {
res = true;
}
break;
case 'border-top':
if( hash['border'] ||
hash['border-top-width'] ||
hash['border-top-color'] ||
hash['border-top-style'] ) {
res = false;
}
else {
res = true;
}
break;
case 'border-right':
if( hash['border'] ||
hash['border-right-width'] ||
hash['border-right-color'] ||
hash['border-right-style'] ) {
res = false;
}
else {
res = true;
}
break;
case 'border-bottom':
if( hash['border'] ||
hash['border-bottom-width'] ||
hash['border-bottom-color'] ||
hash['border-bottom-style'] ) {
res = false;
}
else {
res = true;
}
break;
case 'border-radius':
if( hash['border-top-left-radius'] ||
hash['border-top-right-radius'] ||
hash['border-bottom-left-radius'] ||
hash['border-bottom-right-radius'] ) {
res = false;
}
else {
res = true;
}
break;
case 'border-top-left-radius':
case 'border-top-right-radius':
case 'border-bottom-left-radius':
case 'border-bottom-right-radius':
if(hash['border-radius']) {
res = false;
}
else {
res = true;
}
break;
}
}
}
});
//�������Ժ���
if(!mode) {
imCache[first + ',' + other] = res;
//���first��other֮�������ȼ���ͻ����first��other֮��Ҳ�����
if(res) {
for(var i = first + 1; i < other; i++) {
imCache[i + ',' + other] = res;
}
}
}
return res;
}
}
function clean(node) {
//���null
for(var i = node.length - 1; i >= 0; i--) {
var o = node[i];
for(var j = o.block.length - 1; j >= 0; j--) {
if(!o.block[j]) {
o.block.splice(j, 1);
}
}
if(!o.block.length) {
node.splice(i, 1);
}
}
//�ڵ�仯�������imcache
imCache = {};
}
exports.default=function(code, radical) {
return (new Compress(code, radical)).compress();
}
function merge(node) {
//ð�ݴ�������Ϊ���ܴ��������ж����ͬѡ�������������ѡ�����ɼ����ݹ����
for(var i = 0; i < node.length - 1; i++) {
var hash = {};
var index = {};
for(var j = i; j < node.length; j++) {
var o = node[j];
var s = o.s2s;
if(hash[s]) {
//�������ȼ���ͻʱ�ɺϲ��ֿ�����ͬѡ����
if(noImpact(node, index[s], j)) {
hash[s].block = hash[s].block.concat(o.block);
node.splice(j, 1);
j--;
}
}
else {
hash[s] = o;
index[s] = j;
}
}
}
}
var tempSelector;
var tempStyle;
var tempKey;
var tempValue;
function duplicate(node) {
var hash = {};
node.forEach(function(o) {
hash[o.s2s] = hash[o.s2s] || {};
for(var i = 0; i < o.block.length; i++) {
var style = o.block[i];
//����ʽ��+hackΪ����ȥ��hack��Ӱ��
var key = style.key;
if(style.hack) {
key += style.hack;
}
//���ȼ���ͨ����Ϊ1��!importantΪ2��ɾ�������ȼ����ȳ��ֵ�
var priority = style.impt ? 2 : 1;
if(hash[o.s2s][key]) {
if(priority >= hash[o.s2s][key].priority) {
//�ÿպ�ͳһɾ������ֹ����index
hash[o.s2s][key].parent.block[hash[o.s2s][key].index] = null;
hash[o.s2s][key] = {
index: i,
priority: priority,
parent: o
};
}
else {
o.block.splice(i, 1);
i--;
}
}
else {
hash[o.s2s][key] = {
index: i,
priority: priority,
parent: o
}
}
}
});
//���null
clean(node);
//����ֵ���ͨ��ʽ�Ḳ�ǵ�ǰ���hack
hash = {};
for(var i = node.length - 1; i >=0; i--) {
var o = node[i];
hash[o.s2s] = hash[o.s2s] || {};
for(var j = o.block.length - 1; j >= 0; j--) {
var style = o.block[j];
var key = getK(style.key);
if(key == style.key && !style.hack) {
hash[o.s2s][style.key] = style.impt ? 2 : 1;
}
else if(hash[o.s2s][key] && hash[o.s2s][key] >= (style.impt ? 2 : 1)) {
o.block.splice(j, 1);
}
}
}
}
function override(node) {
var hash = {};
var keys = {
background: true,
font: true,
margin: true,
padding: true,
'list-style': true,
overflow: true,
border: true,
'border-left': true,
'border-top': true,
'border-right': true,
'border-bottom': true,
'border-radius': true
};
for(var j = node.length - 1; j >= 0; j--) {
var o = node[j];
hash[o.s2s] = hash[o.s2s] || {};
//�Ӻ���ǰ������������ֵ�����ʽ�Ḳ�ǵ�ǰ��ķ���ʽ
for(var i = o.block.length - 1; i >= 0; i--) {
var style = o.block[i];
//hack�ķ���ʽҲ�ᱻ���ǣ���hahc������ʽû�и���Ȩ��
var k = getK(style.key);
if(k == style.key && keys[k] && !style.hack) {
hash[o.s2s][k] = style.impt ? 2 : 1;
//����4��������Ϊ����ʽҲ����Ϊ����ʽ
if(!{
'border-left': true,
'border-top': true,
'border-right': true,
'border-bottom': true
}[k]) {
continue;
}
}
switch(k) {
case 'background-position':
case 'background-color':
case 'background-repeat':
case 'background-attachment':
case 'background-image':
if(hash[o.s2s]['background'] == 2) {
o.block.splice(i, 1);
}
else if(hash[o.s2s]['background'] && !style.impt) {
o.block.splice(i, 1);
}
break;
case 'font-style':
case 'line-height':
case 'font-family':
case 'font-variant':
case 'font-size':
if(hash[o.s2s]['font'] == 2) {
o.block.splice(i, 1);
}
else if(hash[o.s2s]['font'] && !style.impt) {
o.block.splice(i, 1);
}
break;
case 'margin-top':
case 'margin-right':
case 'margin-bottom':
case 'margin-left':
if(hash[o.s2s]['margin'] == 2) {
o.block.splice(i, 1);
}
else if(hash[o.s2s]['margin'] && !style.impt) {
o.block.splice(i, 1);
}
break;
case 'padding-top':
case 'padding-right':
case 'padding-bottom':
case 'padding-left':
if(hash[o.s2s]['padding'] == 2) {
o.block.splice(i, 1);
}
else if(hash[o.s2s]['padding'] && !style.impt) {
o.block.splice(i, 1);
}
break;
case 'list-style-image':
case 'list-style-position':
case 'list-style-type':
if(hash[o.s2s]['list-style'] == 2) {
o.block.splice(i, 1);
}
else if(hash[o.s2s]['list-style'] && !style.impt) {
o.block.splice(i, 1);
}
break;
case 'overflow-x':
case 'overflow-y':
if(hash[o.s2s]['overflow'] == 2) {
o.block.splice(i, 1);
}
else if(hash[o.s2s]['overflow'] && !style.impt) {
o.block.splice(i, 1);
}
break;
case 'border-width':
case 'border-color':
case 'border-style':
case 'border-left':
case 'border-top':
case 'border-right':
case 'border-bottom':
if(hash[o.s2s]['border'] == 2) {
o.block.splice(i, 1);
}
else if(hash[o.s2s]['border'] && !style.impt) {
o.block.splice(i, 1);
}
break;
case 'border-left-width':
case 'border-left-color':
case 'border-left-style':
if(hash[o.s2s]['border-left'] == 2 || hash[o.s2s]['border'] == 2) {
o.block.splice(i, 1);
}
else if(hash[o.s2s]['border-left'] || hash[o.s2s]['border'] && !style.impt) {
o.block.splice(i, 1);
}
break;
case 'border-top-width':
case 'border-top-color':
case 'border-top-style':
if(hash[o.s2s]['border-top'] == 2 || hash[o.s2s]['border'] == 2) {
o.block.splice(i, 1);
}
else if(hash[o.s2s]['border-top'] || hash[o.s2s]['border'] && !style.impt) {
o.block.splice(i, 1);
}
break;
case 'border-right-width':
case 'border-right-color':
case 'border-right-style':
if(hash[o.s2s]['border-right'] == 2 || hash[o.s2s]['border'] == 2) {
o.block.splice(i, 1);
}
else if(hash[o.s2s]['border-right'] || hash[o.s2s]['border'] && !style.impt) {
o.block.splice(i, 1);
}
break;
case 'border-bottom-width':
case 'border-bottom-color':
case 'border-bottom-style':
if(hash[o.s2s]['border-bottom'] == 2 || hash[o.s2s]['border'] == 2) {
o.block.splice(i, 1);
}
else if(hash[o.s2s]['border-bottom'] || hash[o.s2s]['border'] && !style.impt) {
o.block.splice(i, 1);
}
break;
case 'border-top-left-radius':
case 'border-top-right-radius':
case 'border-bottom-left-radius':
case 'border-bottom-right-radius':
if(hash[o.s2s]['border-radius'] == 2) {
o.block.splice(i, 1);
}
else if(hash[o.s2s]['border-radius'] && !style.impt) {
o.block.splice(i, 1);
}
break;
}
}
}
}
function Compress(code, radical) {
this.code = code;
this.radical = radical;
this.head = '';
this.imCache = {};
}
Compress.prototype.compress = function() {
try {
this.code = (new Clean({
processImport: false
})).minify(this.code);
} catch(e) {
return e.toString();
}
if(!this.radical) {
return this.code;
}
var parser = homunculus.getParser('css');
try {
this.node = parser.parse(this.code);
this.ignores = parser.ignore();
this.index = 0;
}
catch(e) {
if(typeof console != 'undefined') {
console.error(e);
}
return e.toString();
}
var i = this.getHead();
var list = this.rebuild(i);
/* 获取新数据结构list后,处理以下5个步骤:
1合并相同选择器(检测是否冲突)、
2去除重复样式(合并后可能造成的重复,包括优先级重复)、
3去除被覆盖的样式(合并后可能造成的缩写覆盖)、
4聚合相同样式的选择器(样式确保唯一后排序,不冲突则合并相同的)、
5提取公因子(最大图形选择算法),
2和3已被clean-css实现,无需重复
*/
this.merge(list);
this.merge(list, true);
this.preJoin(list);
this.union(list);
this.preRelease(list);
this.extract(list);
return this.head + this.join(list);
}
Compress.prototype.getHead = function() {
var leaves = this.node.leaves();
for(var i = 0, len = leaves.length; i < len; i++) {
var leaf = leaves[i];
if(leaf.name() == Node.STYLESET) {
return i;
}
this.joinHead(leaf);
}
}
Compress.prototype.joinHead = function(node) {
var self = this;
var isToken = node.name() == Node.TOKEN;
if(isToken) {
var token = node.token();
if(token.type() != Token.VIRTUAL) {
self.head += token.content();
while(self.ignores[++self.index]) {
var ig = self.ignores[self.index];
self.head += ig.content();
}
}
}
else {
node.leaves().forEach(function(leaf) {
self.joinHead(leaf);
});
}
}
Compress.prototype.rebuild = function(i) {
var list = [];
var leaves = this.node.leaves();
for(var len = leaves.length; i < len; i++) {
var leaf = leaves[i];
var item = {
selectors: [],
styles: []
};
this.rb(leaf, item);
//将选择器排序,比较时可直接==比较
sort(item.selectors);
item.s2s = item.selectors.join(',');
list.push(item);
}
return list;
}
Compress.prototype.rb = function(node, item, isSelector, isStyle, isKey, isValue) {
var self = this;
var isToken = node.name() == Node.TOKEN;
if(isToken) {
var token = node.token();
if(token.type() != Token.VIRTUAL) {
var s = token.content();
if(isSelector) {
tempSelector += s;
}
else if(isStyle) {
if(isKey) {
tempKey += s;
}
else if(isValue) {
tempValue += s;
}
if(token.type() == Token.HACK) {
if(isKey) {
tempStyle.prefixHack = s;
}
else if(isValue) {
tempStyle.suffixHack = s;
}
}
else if(token.type() == Token.IMPORTANT) {
tempStyle.important = true;
}
}
while(self.ignores[++self.index]) {
var ig = self.ignores[self.index];
if(isSelector) {
tempSelector += ig.content();
}
else if(isStyle) {
if(isKey) {
tempKey += ig.content();
}
else if(isValue) {
tempValue += ig.content();
}
}
}
}
}
else {
if(node.name() == Node.SELECTOR) {
tempSelector = '';
isSelector = true;
}
else if(node.name() == Node.STYLE) {
tempStyle = {
key: '',
value: '',
content: '',
prefixHack: '',
suffixHack: '',
important: false
};
tempKey = '';
tempValue = '';
isStyle = true;
}
else if(node.name() == Node.KEY) {
isKey = true;
}
else if(node.name() == Node.VALUE) {
isValue = true;
}
node.leaves().forEach(function(leaf) {
self.rb(leaf, item, isSelector, isStyle, isKey, isValue);
});
if(node.name() == Node.SELECTOR) {
item.selectors.push(tempSelector);
}
else if(node.name() == Node.STYLE) {
tempStyle.key = tempKey;
tempStyle.content = tempValue;
tempStyle.value = tempValue.replace(/\s*!important\s*$/i, '')
.slice(0, tempValue.length - tempStyle.suffixHack.length)
.toLowerCase();
item.styles.push(tempStyle);
}
}
}
//合并相同选择器,向前向后两个方向
Compress.prototype.merge = function(list, direction) {
//冒泡处理,因为可能处理后留有多个相同选择器,但后面的选择器可继续递归过程
var res = false;
if(direction) {
outer:
for(var i = list.length - 1; i > 0; i--) {
for(var j = i - 1; j >= 0; j--) {
if(list[i].s2s == list[j].s2s) {
if(impact.noImpact(list, i, j)) {
list[i].styles = list[j].styles.concat(list[i].styles);
list.splice(j, 1);
impact.upImCache(j);
i--;
j--;
res = true;
}
else {
continue outer;
}
}
}
}
}
else {
outer:
for(var i = 0; i < list.length - 1; i++) {
for(var j = i + 1; j < list.length; j++) {
if(list[i].s2s == list[j].s2s) {
if(impact.noImpact(list, i, j)) {
list[i].styles = list[i].styles.concat(list[j].styles);
list.splice(j, 1);
impact.upImCache(j);
j--;
res = true;
}
else {
continue outer;
}
}
}
}
}
//递归处理,直到没有可合并的为止
if(res) {
this.merge(list, direction);
}
}
Compress.prototype.preJoin = function(list) {
//为union做准备,将选择器的样式拼接在一起存至value属性下,可直接==比较
list.forEach(function(item) {
sort(item.styles, function(a, b) {
return a.key > b.key;
});
item.value = '';
var len = item.styles.length;
item.styles.forEach(function(style, i) {
item.value += style.key;
item.value += ':';
item.value += style.content.toLowerCase();
if(i < len - 1) {
item.value += ';';
}
});
});
}
Compress.prototype.preRelease = function(list) {
//union完成后删除value属性
list.forEach(function(item) {
delete item.value;
});
}
//聚合相同样式的选择器
Compress.prototype.union = function(list) {
var res = false;
outer:
for(var i = 0; i < list.length - 1; i++) {
for(var j = i + 1; j < list.length; j++) {
if(list[i].value == list[j].value) {
if(impact.noImpact(list, i, j)) {
list[i].selectors = list[i].selectors.concat(list[j].selectors);
sort(list[i].selectors);
list[i].s2s = list[i].selectors.join(',');
list.splice(j, 1);
impact.upImCache(j);
j--;
res = true;
}
else {
continue outer;
}
}
}
}
if(res) {
this.union(list);
}
}
//提取公因子
Compress.prototype.extract = function(list) {
//统计单个样式的出现信息,以便后续操作
//keys按顺序保存键值,即以单个样式本身toString()
//hash将相同单个样式收集到一个数组里
var hash = {};
var keys = [];
list.forEach(function(item, i) {
item.styles.forEach(function(style, j) {
var key = style.key + ':' + style.content;
if(!hash.hasOwnProperty(key)) {
hash[key] = [];
keys.push(key);
}
hash[key].push({
parent: item,
i: i,
j: j
});
});
});
//index记录对应索引的选择器是否出现此样式
//比如index[0]记录keys[0]的样式出现在哪些位置上,位置为选择器索引
//max标明最大样式数
var index = [];
var max = 0;
keys.forEach(function(o) {
var same = hash[o];
var temp = {};
same.forEach(function(o2) {
temp[o2.i] = true;
max = Math.max(max, o2.i);
});
index.push(temp);
});
//以单个样式为横坐标,选择器顺序索引为纵坐标,组成一个二维数组
//索引和位置对应,空的地方填0
var map = [];
index.forEach(function(temp, idx) {
var arr = new Array(max);
for(var i = 0; i <= max; i++) {
arr[i] = 0;
}
Object.keys(temp).forEach(function(i) {
arr[parseInt(i)] = 1;
});
map.push(arr);
});console.log(keys,map)
//同列相同部分视为一个矩形面积,不同列拥有相同位置和高度可合并计算面积——即拥有相同样式的不同选择器以优先取最大面积合并
//当然至少要2列,因为1列为只出现在1个选择器中没必要提
//TODO:面积择优算法。目前想到的复杂度过高,无法用于实际场景
//舍弃采用单行合并,即拥有某个样式的所有选择器尝试合并
//当然因为优先级冲突不一定能够整行合并,应该递归其所有组合尝试,代价太大暂时忽略
var record = [];
var insert = [];
map.forEach(function(row, i) {
function union(node) {
var hash = {};
for(var i = 0; i < node.length; i++) {
var key = [];
node[i].block.forEach(function(style) {
var k = style.key + ':' + style.value;
if(style.hack) {
k += style.hack;
}
if(style.impt) {
k += '!important';
}
key.push(k);
});
sort(key);
key = key.join(';');
hash[key] = hash[key] || [];
hash[key].push({
n: node[i],
i: i
});
}
Object.keys(hash).forEach(function(o) {
if(hash[o].length > 1) {
var queue = hash[o];
//�����ѡ����ð�ݺϲ�����һ���ϣ����ÿ�
for(var i = 0; i < queue.length - 1; i++) {
for(var j = i + 1; j < queue.length; j++) {
if(queue[i].n.block.length && queue[j].n.block.length && noImpact(node, queue[i].i, queue[j].i)) {
queue[i].n.selectors = queue[i].n.selectors.concat(queue[j].n.selectors);
sort(queue[i].n.selectors);
queue[i].n.s2s = queue[i].n.selectors.join(',');
queue[j].n.block = [];
}
}
}
}
});
clean(node);
}
});
}
Compress.prototype.join = function(list) {
var body = '';
list.forEach(function(item) {
body += item.s2s;
body += '{';
var len = item.styles.length;
item.styles.forEach(function(style, i) {
body += style.key;
body += ':';
body += style.content;
if(i < len - 1) {
body += ';';
}
});
body += '}';
});
return body;
}
function extract(node) {
var hash = {};
node.forEach(function(o, i) {
o.block.forEach(function(style, j) {
var key = style.key + ':' + style.value;
if(style.hack) {
key += style.hack;
}
if(style.impt) {
key += '!important';
}
hash[key] = hash[key] || [];
hash[key].push({
parent: o,
i: i,
j: j
});
});
});
//��ֻ��1�γ��ֵ�ɾ������γ��ֵı����������µ����һ����ά����
var index = [];
var max = 0;
var keys = [];
Object.keys(hash).forEach(function(o) {
var same = hash[o];
if(same.length == 1) {
delete hash[o];
}
else {
keys.push(o);
var temp = {};
same.forEach(function(o2) {
temp[o2.i] = true;
max = Math.max(max, o2.i);
});
index.push(temp);
}
});
//���к�map��λ�ã�������λ�ö�Ӧ���յĵط���null
var map = [];
index.forEach(function(temp, idx) {
var arr = new Array(max);
for(var i = 0; i <= max; i++) {
arr[i] = 0;
}
Object.keys(temp).forEach(function(i) {
arr[parseInt(i)] = 1;
});
map.push(arr);
});
//ͬ����ͬ������Ϊһ������������ͬ��ӵ����ͬλ�ú͸߶ȿɺϲ��������������ӵ����ͬ��ʽ�IJ�ͬѡ����������ȡ�������ߺϲ�����Ȼ����Ҫ2�У���Ϊ1��Ϊֻ������һ��ѡ������û��Ҫ��
//to do ��������㷨��Ŀǰ�뵽�ĸ��Ӷȹ��ߣ��޷�����ʵ�ʳ���
//����֮���õ��кϲ�����ӵ��ijһ����ʽ������ѡ�������Ժϲ�����Ȼ��Ϊ���ȼ���ͻ��һ���ܹ����кϲ���Ӧ�õݹ���������ϳ��ԣ�����̫����ʱ����
var record = [];
var insert = [];
map.forEach(function(row, i) {
var start = row.indexOf(1);
var end = row.lastIndexOf(1);
//�в������ܽ������ij����գ��ж�֮
if(start == end) {
return;
}
var style = keys[i];
var same = hash[style];
//���ȱ��кϲ�������ͻ�����������кϲ�����Ϊ���ڳ���һ���޳�ͻ
if(noImpact(node, start, end, same[same.length - 1].j)) {
//û�г�ͻ��Ҫ���ϲ����Ƿ���С����������ٵ���ʽ����Ҫ��ѡ����ƴ�Ӵ�
var reduce = style.length * (same.length - 1);
var add = 0;
same.forEach(function(o, i) {
add += o.parent.s2s.length;
});
//��ȡ�ϲ�ÿ2��ѡ����֮����1��,����ʽ�����2���ַ�{}��������Ϊ����same.length
if(reduce > add + same.length) {
var ss = [];
same.forEach(function(o) {
ss = ss.concat(o.parent.selectors);
record.push(o);
});
sort(ss);
//������ȡ�ϲ������λ���ڵ�һ��֮��
insert.push({
i: same[0].i + 1,
selectors: ss,
block: [same[0].parent.block[same[0].j]],
s2s: ss.join(',')
});
}
//�������
for(var j = 0; j < row.length; j++) {
row[j] = 0;
}
}
else {
for(var m = 0; m < row.length - 1; m++) {
//���ڱ�����2�����ϲźϲ�
if(row[m] && row[m + 1]) {
for(var n = m + 2; n < row.length; n++) {
if(!row[n]) {
break;
}
}
//ͬʱ����������ͬ�ɺϲ�������������Щѡ����ӵ�е���ͬ��ʽ�ϲ�
var ri = [i];
var reduce = style.length * (n - m - 1);
for(var l = 0, compare = row.slice(m, n).join(''); l < map.length; l++) {
if(l != i && map[l].slice(m, n).join('') == compare) {
ri.push(l);
reduce += keys[l].length * (n - m - 1);
}
}
var add = 0;
var first;
same.forEach(function(o) {
if(o.i > m && o.i < n) {
if(!first) {
first = o;
}
add += o.parent.s2s.length;
}
});
if(reduce > add + n - m - 1) {
var ss = [];
same.forEach(function(o) {
if(o.i >= m && o.i < n) {
ss = ss.concat(o.parent.selectors);
record.push(o);
}
});
sort(ss);
var ins = {
i: first.i + 1,
selectors: ss,
block: [],
s2s: ss.join(',')
};
ri.forEach(function(l) {
var same = hash[keys[l]];
for(var k = m; k < n; k++) {
map[l][k] = 0;
}
ins.block.push(same[0].parent.block[same[0].j]);
});
insert.push(ins);
}
m = n;
}
}
}
});
//��ռ�¼����ȡ�������ȡ���
record.forEach(function(o) {
o.parent.block[o.j] = null;
});
sort(insert, function(a, b) {
return a.i < b.i;
});
insert.forEach(function(o) {
node.splice(o.i, 0, o);
});
clean(node);
//�ٴκϲ�����ȡ�����Ӳ����ľ�����ͬ��ʽ��ѡ����
if(insert.length) {
union(node);
}
}
function join(node) {
node.forEach(function(o) {
body += o.selectors.join(',');
body += '{';
o.block.forEach(function(style, i) {
body += style.key;
body += ':';
body += style.value;
if(style.impt) {
body += '!important';
}
if(style.hack) {
body += style.hack;
}
if(i < o.block.length - 1) {
body += ';';
}
});
body += '}'
});
}
function compress(src) {
var node,
ignore = {},
lexer = new CssLexer(new CssRule()),
parser = new Parser(lexer);
try {
lexer.parse(src);
node = parser.program();
ignore = parser.ignore();
} catch(e) {
if(window.console) {
console.error(e);
}
return e.toString();
}
index = 0;
head = '';
body = '';
getHead(node, ignore);
//��ast�ع��ɸ�ֱ�ӵ���ʽ�����Ӹ�����Ϣ
node = rebuild(node, ignore, []);
//�ϲ���ͬѡ����
merge(node);
//ȥ��ͬһѡ�������ظ���ʽ����
duplicate(node);
//ȥ��ͬһѡ�����б����ǵ���ʽ����
override(node);
//�ۺ���ͬ��ʽ��ѡ����
union(node);
//��ȡ������
extract(node);
//���
join(node);
return head + body;
}
exports.compress = compress;
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc