Socket
Socket
Sign inDemoInstall

css-fruit

Package Overview
Dependencies
3
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.2 to 0.1.3

src/dataTypes/ImageSet.ts

6

package.json
{
"name": "css-fruit",
"description": "A Parser and Analysis of CSS Declaration",
"version": "0.1.2",
"version": "0.1.3",
"author": "Rainfore <rainforest92@126.com>",

@@ -13,3 +13,4 @@ "scripts": {

"test:one": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --require ts-node/register",
"test:integration": "mocha test/integration/css-fruit.js"
"test:integration": "mocha test/integration/css-fruit.js",
"docs": "typedoc --out docs ./src"
},

@@ -50,2 +51,3 @@ "main": "./dist/css-fruit.js",

"ts-node": "^7.0.1",
"typedoc": "^0.14.1",
"typescript": "^3.2.1",

@@ -52,0 +54,0 @@ "vusion-hooks": "^0.2.1",

@@ -171,2 +171,18 @@ # css-fruit

### 设计要点
- 每个节点类均继承`Fruit`
- 构造函数要实现以下内容:
- constructor() // 生成空对象,默认 invalid
- constructor(value: string) // 直接使用 this.parse 去解析
- constructor(...args) // 需要单独对每个参数解析
- constructor({}) ? 这种要不要实现得打个问号了。。。
- _absorb() 单独对每种属性做解析
- analyzeInLoop 中的流程控制
- return true; // 继续下一个 token
- return false; // 停留在当前 token,然后继续循环
- return undefined; // 结束当前循环,一般是遇到当前解析器不合法的字符,但可以让后续解析器继续处理
- throw Error; // 当前解析错误,直接停止解析
- 像`BackgroundPosition`这样的最长 hand 值,它的属性要么全没有,要么全都有
## 修改日志

@@ -173,0 +189,0 @@

@@ -1,2 +0,2 @@

import Fruit, { ValueNode, ValueNodeType, ParseDeepLevel, Stem } from '../Fruit';
import Fruit, { ValueNode, ValueNodeType, ParsedDepth, Stem } from '../Fruit';
const ValueParser = require('postcss-value-parser');

@@ -19,4 +19,2 @@

export default class Color extends Fruit {
protected _type: string = 'color';
protected _parseDeepLevelBoundary = ParseDeepLevel.dataTypes;
value: string;

@@ -33,2 +31,6 @@ r: number;

super();
this._type = 'color';
this._parseDepth = ParsedDepth.dataType;
this.init();
this.r = r;

@@ -62,3 +64,3 @@ this.g = g;

if (this.value)
throw new SyntaxError('Excessive value');
throw new SyntaxError(`Excessive value '${node.value}'`);
this.value = 'currentColor';

@@ -68,3 +70,3 @@ return this.valid = true;

if (this.value)
throw new SyntaxError('Excessive value');
throw new SyntaxError(`Excessive value '${node.value}'`);
this.value = node.value;

@@ -75,3 +77,3 @@ // @TODO parse named value;

if (this.value)
throw new SyntaxError('Excessive value');
throw new SyntaxError(`Excessive value '${node.value}'`);
this.value = node.value;

@@ -83,6 +85,6 @@ // @TODO parse named value;

if (node.unclosed)
throw new SyntaxError('Unclosed function: ' + node.value);
throw new SyntaxError(`Unclosed function '${node.value}'`);
if (node.value === 'rgb' || node.value === 'rgba' || node.value === 'hsl' || node.value === 'hsla') {
if (this.value)
throw new SyntaxError('Excessive value');
throw new SyntaxError(`Excessive value '${node.value}'`);
this.value = ValueParser.stringify(node);

@@ -162,3 +164,3 @@ return this.valid = true;

if (value.length !== 6 && value.length !== 3)
throw new SyntaxError('Unexpected length of hex number');
throw new SyntaxError(`Unexpected length of hex number '${value}'`);
else if (value.length === 3)

@@ -178,3 +180,3 @@ value = `${value[0]}${value[0]}${value[1]}${value[1]}${value[2]}${value[2]}`;

if (arr.length !== 4)
throw new SyntaxError('Unexpected params of rgba function');
throw new SyntaxError(`Unexpected params of rgba function '${value}'`);

@@ -188,3 +190,3 @@ return new Color(...arr);

if (arr.length !== 4)
throw new SyntaxError('Unexpected params of rgba function');
throw new SyntaxError(`Unexpected params of rgba function '${value}'`);

@@ -191,0 +193,0 @@ return new Color(...arr);

@@ -1,19 +0,32 @@

import Fruit, { ValueNode, ValueNodeType, ParseDeepLevel, Stem } from '../Fruit';
import Fruit, { ValueNode, ValueNodeType, ParsedDepth, Stem } from '../Fruit';
import URL from './URL';
import ImageSet from './ImageSet';
export default class Image extends Fruit {
protected _type: string = 'image';
protected _parseDeepLevelBoundary: ParseDeepLevel = ParseDeepLevel.dataTypes;
protected _state: { count: number };
value: URL | string;
value: URL | ImageSet | string;
// constructor(value?: string);
// constructor(width: string, height?: string) {
// super(width);
// if (arguments.length > 1) {
// this.width = width;
// this.height = height;
// }
// }
constructor();
constructor(value: string | URL | ImageSet);
constructor(value?: string | URL | ImageSet) {
super();
this._type = 'image';
this._parseDepth = ParsedDepth.dataType;
this.init();
const args = arguments;
this.tryCatch(() => {
if (args.length === 0)
return;
else if (typeof value === 'string')
this.parse(value);
else if (value instanceof URL || value instanceof ImageSet) {
// @矛盾: 赋值给`this.value`时,应不应该检查 URL 本身的合法性?
this.value = value.toResult() as URL | ImageSet | string;
this.valid = value.valid;
} else
throw new TypeError('Wrong type or excessive arguments');
});
}
protected init() {

@@ -39,13 +52,23 @@ super.init();

if (node.unclosed)
throw new SyntaxError('Unclosed function: ' + node.value);
throw new SyntaxError(`Unclosed function '${node.value}'`);
if (node.value === 'url') {
if (this.value)
throw new SyntaxError('Excessive values');
throw new SyntaxError(`Excessive value '${node.value}'`);
const url = new URL();
url.analyze(stem);
if (!url.valid)
throw new SyntaxError('Invalid url: ' + node.value);
throw new SyntaxError(`Invalid <url> '${node.value}'`);
this.value = url.toResult() as URL | string;
this.valid = true;
return false;
} else if (node.value === 'image-set' || node.value === '-webkit-image-set') {
if (this.value)
throw new SyntaxError(`Excessive value '${node.value}'`);
const imageSet = new ImageSet();
imageSet.analyze(stem);
if (!imageSet.valid)
throw new SyntaxError(`Invalid <image-set> '${node.value}'`);
this.value = imageSet.toResult() as ImageSet | string;
this.valid = true;
return false;
} // else

@@ -52,0 +75,0 @@ // cont gradient = new Gradient();

@@ -1,13 +0,43 @@

import Fruit, { ValueNode, ValueNodeType, ParseDeepLevel } from '../Fruit';
import Fruit, { ValueNode, ValueNodeType, ParsedDepth } from '../Fruit';
import { numberRE } from './Number';
const experimentalRE = new RegExp(`^(${String(numberRE).slice(2, -3)})(cap|ch|em|ex|ic|lh|rem|rlh|vh|vw|vi|vb|vmin|vmax|px|cm|mm|Q|in|pc|pt)?$`, 'i');
const partialRE = new RegExp(`^(${String(numberRE).slice(2, -3)})(ch|em|ex|rem|vh|vw|vmin|vmax|px|cm|mm|in|pc|pt)?$`, 'i');
const unitRE = /^ch|em|ex|rem|vh|vw|vmin|vmax|px|cm|mm|in|pc|pt$/i;
const experimentalUnitRE = /^cap|ch|em|ex|ic|lh|rem|rlh|vh|vw|vi|vb|vmin|vmax|px|cm|mm|Q|in|pc|pt$/i;
const partialRE = new RegExp(`^(${String(numberRE).slice(2, -3)})(${String(unitRE).slice(2, -3)})?$`, 'i');
export default class Length extends Fruit {
protected _type: string = 'length';
protected _parseDeepLevelBoundary = ParseDeepLevel.dataTypes;
number: number;
unit: string;
constructor();
constructor(value: string | number);
constructor(number: number, unit: string);
constructor(value?: string | number, unit?: string) {
super();
this._type = 'length';
this._parseDepth = ParsedDepth.dataType;
this.init();
const args = arguments;
this.tryCatch(() => {
if (args.length === 0)
return;
else if (typeof value === 'string' && args.length === 1)
this.parse(value);
else if (typeof value === 'number') {
if (!unit && value === 0) {
this.number = value;
this.unit = '';
this.valid = true;
} else if (unit && unitRE.test(unit)) {
this.number = value;
this.unit = unit;
this.valid = true;
} else
throw new SyntaxError(`Invalid unit '${unit}'`);
} else
throw new TypeError('Wrong type or excessive arguments');
});
}
init() {

@@ -21,13 +51,14 @@ super.init();

value = value.trim();
this.init();
const found = partialRE.exec(value);
if (!found)
throw new SyntaxError('Invalid length');
if (+found[1] !== 0 && !found[2])
throw new SyntaxError('A unit should be after the non-zero number');
this.tryCatch(() => {
const found = partialRE.exec(value);
if (!found)
throw new SyntaxError(`Invalid length '${value}'`);
if (+found[1] !== 0 && !found[2])
throw new SyntaxError('There must be a unit after the non-zero number');
this.number = +found[1];
this.unit = found[2] || '';
this.valid = true;
this.number = +found[1];
this.unit = found[2] || '';
this.valid = true;
});

@@ -34,0 +65,0 @@ return this.toResult();

@@ -1,2 +0,2 @@

import Fruit, { ValueNode, ValueNodeType, ParseDeepLevel } from '../Fruit';
import Fruit, { ValueNode, ValueNodeType, ParsedDepth } from '../Fruit';
import { numberRE } from './Number';

@@ -7,6 +7,26 @@

export default class Percentage extends Fruit {
protected _type: string = 'percentage';
protected _parseDeepLevelBoundary = ParseDeepLevel.dataTypes;
number: number;
constructor();
constructor(value: string | number);
constructor(value?: string | number) {
super();
this._type = 'percentage';
this._parseDepth = ParsedDepth.dataType;
this.init();
const args = arguments;
this.tryCatch(() => {
if (args.length === 0)
return;
else if (typeof value === 'string')
this.parse(value);
else if (typeof value === 'number') {
this.number = value;
this.valid = true;
} else
throw new TypeError('Wrong type or excessive arguments');
});
}
init() {

@@ -19,12 +39,13 @@ super.init();

value = value.trim();
this.init();
const found = partialRE.exec(value);
if (!found)
throw new SyntaxError('Invalid percentage');
// if (+found[1] !== 0 && !found[2])
// throw new SyntaxError('"%" should be after the non-zero number');
this.tryCatch(() => {
const found = partialRE.exec(value);
if (!found)
throw new SyntaxError(`Invalid percentage format of '${value}'`);
// if (+found[1] !== 0 && !found[2])
// throw new SyntaxError('"%" must be after the non-zero number');
this.number = +found[1];
this.valid = true;
this.number = +found[1];
this.valid = true;
});

@@ -31,0 +52,0 @@ return this.toResult();

@@ -1,2 +0,2 @@

import Fruit, { ValueNode, ValueNodeType, ParseDeepLevel } from '../Fruit';
import Fruit, { ValueNode, ValueNodeType, ParsedDepth } from '../Fruit';
import { parseQuery, stringifyQuery, Query } from '../utils';

@@ -18,4 +18,2 @@

export default class URL extends Fruit {
protected _type: string = 'url';
protected _parseDeepLevelBoundary: ParseDeepLevel = ParseDeepLevel.dataTypes;
// quote: string;

@@ -27,2 +25,21 @@ url: string;

constructor();
constructor(value: string);
constructor(value?: string) {
super();
this._type = 'url';
this._parseDepth = ParsedDepth.dataType;
this.init();
const args = arguments;
this.tryCatch(() => {
if (args.length === 0)
return;
else if (args.length === 1)
this.parse(value);
else
throw new TypeError('Wrong type or excessive arguments');
})
}
protected init() {

@@ -42,14 +59,14 @@ super.init();

if (node.unclosed)
throw new SyntaxError('Unclosed function: ' + node.value);
throw new SyntaxError(`Unclosed function '${node.value}'`);
if (node.value === 'url') {
if (this.url)
throw new SyntaxError('Duplicated url functions');
throw new SyntaxError(`Duplicated function 'url'`);
let url = '';
if (node.nodes.length > 1)
throw new SyntaxError('Invalid url');
throw new SyntaxError('Invalid url format');
else if (node.nodes.length === 1) {
const subNode = node.nodes[0];
if (subNode.unclosed)
throw new SyntaxError('Unclosed quote: ' + subNode.value);
throw new SyntaxError(`Unclosed quote '${subNode.value}'`);
else

@@ -61,6 +78,8 @@ url = subNode.value;

const found = urlRE.exec(url);
this.path = found[1] ? decodeURIComponent(found[1]) : '';
this.path = found[1] || '';
// this.path = found[1] ? decodeURIComponent(found[1]) : '';
if (found[2])
this.query = parseQuery(found[2]);
this.hash = found[3] ? decodeURIComponent(found[3].slice(1)) : '';
this.hash = found[3] ? found[3].slice(1) : '';
// this.hash = found[3] ? decodeURIComponent(found[3].slice(1)) : '';
return this.valid = true;

@@ -77,4 +96,5 @@ }

const queryString = this.query ? stringifyQuery(this.query) : '';
return `url(${quote}${encodeURIComponent(this.path)}${queryString}${this.hash ? '#' + encodeURIComponent(this.hash) : ''}${quote})`;
// return `url(${quote}${encodeURIComponent(this.path)}${queryString}${this.hash ? '#' + encodeURIComponent(this.hash) : ''}${quote})`;
return `url(${quote}${this.path}${queryString}${this.hash ? '#' + this.hash : ''}${quote})`;
}
}

@@ -31,4 +31,7 @@ const ValueParser = require('postcss-value-parser');

pos: number;
constructor(value: string) {
this.nodes = new ValueParser(value).nodes;
constructor(value: string | Array<ValueNode>) {
if (Array.isArray(value))
this.nodes = value;
else
this.nodes = new ValueParser(value).nodes;
this.pos = 0;

@@ -58,3 +61,16 @@ }

export const enum ParseDeepLevel {
/* @example:
background === '20px top'; // Keep shorthand
background.position === '20px top'; // Keep primaryLonghand | completeLonghand
background.position.x === '20px'; // Keep virtualLonghand
background.position.x.offset === '20px'; // Keep dataType
background.position.x.offset.number = 20; // Keep dataTypeProperty
border = '2px solid color'; // Keep shorthand
border.left = '20px solid color'; // Keep primaryLonghand
border.left.width = '20px'; // Keep completeLonghand | virtualLonghand | dataType
border.left.width.number = 20; // Keep dataTypeProperty
*/
export const enum ParsedDepth {
shorthand,

@@ -64,3 +80,4 @@ primaryLonghand,

virtualLonghand,
dataTypes,
dataType,
dataTypeProperty,
}

@@ -84,4 +101,5 @@

irrelevantProperty: IrrelevantProperty;
parseDeepLevel: ParseDeepLevel;
depthParseTo: ParsedDepth;
forceParsing: { [prop: string]: boolean };
throwErrors: boolean;
}

@@ -93,3 +111,3 @@

protected _inherited: boolean = false;
protected _parseDeepLevelBoundary = ParseDeepLevel.virtualLonghand;
protected _parseDepth = ParsedDepth.virtualLonghand;
raw: string;

@@ -99,8 +117,21 @@ valid: boolean = false;

constructor();
constructor(value?: string);
constructor(value?: string) {
if (arguments.length === 1 && value)
this.parse(value);
constructor(...args: any[]);
constructor(...args: any[]) {
if (args.length === 0)
return;
if (args.length === 1)
this.parse(args[0]);
else
this.parse(args.join(' '));
}
protected tryCatch(func: Function): void {
try {
func();
} catch (e) {
if (this.options.throwErrors)
throw e;
}
}
protected init(): void {

@@ -115,7 +146,9 @@ this.valid = false;

const stem = new Stem(value);
this.analyze(stem);
if (stem.head()) {
this.valid = false;
throw SyntaxError('Nodes of value cannot be fully analyzed: ' + value);
}
this.tryCatch(() => {
this.analyze(stem);
if (stem.head()) {
this.valid = false;
throw SyntaxError('Nodes of value cannot be fully analyzed: ' + value);
}
});
return this.toResult();

@@ -127,3 +160,3 @@ }

return undefined;
if (this.options.parseDeepLevel >= this._parseDeepLevelBoundary || this.options.forceParsing[this._type])
if (this.options.depthParseTo > this._parseDepth || this.options.forceParsing[this._type])
return this;

@@ -136,3 +169,2 @@ else

let node;
this.init();
while (node = stem.head()) {

@@ -144,3 +176,3 @@ let control: boolean;

this.valid = false;
throw e;
throw new Error(`When analyzing <${this._type}>\n\t` + e);
}

@@ -201,6 +233,4 @@

static parse(value: string): Fruit | string {
try {
const fruit = new this();
return fruit.parse(value);
} catch (e) {}
const fruit = new this();
return fruit.parse(value);
}

@@ -211,3 +241,7 @@

static validate(value: string): boolean {
return this.parse(value) !== undefined;
try {
return this.parse(value) !== undefined;
} catch (e) {
return false;
}
}

@@ -228,4 +262,5 @@

irrelevantProperty: IrrelevantProperty.ignore,
parseDeepLevel: ParseDeepLevel.virtualLonghand,
depthParseTo: ParsedDepth.dataType,
forceParsing: {},
throwErrors: false,
};

@@ -1,2 +0,2 @@

import Fruit, { ValueNode, ValueNodeType, Stem } from '../Fruit';
import Fruit, { ValueNode, ValueNodeType, Stem, ParsedDepth } from '../Fruit';
import Color from '../dataTypes/Color';

@@ -26,4 +26,2 @@ import Image from '../dataTypes/Image';

export default class Background extends Fruit {
protected _type: string = 'background';
protected _inherited: boolean = false;
protected _state: { boxCount: number };

@@ -39,2 +37,21 @@ attachment: string;

constructor();
constructor(value: string);
constructor(value?: string) {
super();
this._type = 'background';
this._parseDepth = ParsedDepth.shorthand;
this.init();
const args = arguments;
this.tryCatch(() => {
if (args.length === 0)
return;
else if (args.length === 1)
this.parse(value);
else
throw new TypeError('Wrong type or excessive arguments');
})
}
protected init() {

@@ -61,3 +78,3 @@ super.init();

if (this.attachment)
throw new SyntaxError('Excessive background-attachment');
throw new SyntaxError(`Excessive <background-attachment> '${node.value}'`);
this.attachment = node.value;

@@ -75,3 +92,3 @@ return this.valid = true;

} else
throw new SyntaxError('Excessive background-clip')
throw new SyntaxError(`Excessive <background-clip> '${node.value}'`);
} else {

@@ -104,3 +121,3 @@ let valid: boolean = false;

} else
throw new SyntaxError('Invalid background-size');
throw new SyntaxError(`Invalid <background-size> '${node.value}'`);
}

@@ -122,8 +139,8 @@ }

if (node.unclosed)
throw new SyntaxError('Unclosed function: ' + node.value);
if (node.value === 'url') {
throw new SyntaxError(`Unclosed function '${node.value}'`);
if (node.value === 'url' || node.value === 'image-set' || node.value === '-webkit-image-set') {
const image = new Image();
image.analyze(stem);
if (!image.valid)
throw new SyntaxError('Invalid image');
throw new SyntaxError(`Invalid <image> '${node.value}'`);
this.setImage(image.toResult() as Image | string);

@@ -145,3 +162,3 @@ this.valid = true;

if (this.color)
throw new SyntaxError('Excessive color');
throw new SyntaxError('Excessive <color>');
else

@@ -153,3 +170,3 @@ this.color = color;

if (this.image)
throw new SyntaxError('Excessive image');
throw new SyntaxError('Excessive <image>');
else

@@ -161,3 +178,3 @@ this.image = image;

if (this.position)
throw new SyntaxError('Excessive background-position');
throw new SyntaxError('Excessive <background-position>');
else

@@ -169,3 +186,3 @@ this.position = position;

if (this.repeat)
throw new SyntaxError('Excessive background-repeat');
throw new SyntaxError('Excessive <background-repeat>');
else

@@ -177,3 +194,3 @@ this.repeat = repeat;

if (this.size)
throw new SyntaxError('Excessive background-size');
throw new SyntaxError('Excessive <background-size>');
else

@@ -224,5 +241,5 @@ this.size = size;

} else
throw new Error('Incompatible property: ' + prop);
throw new TypeError(`Property '${prop}' is inconsistent with existing type '${this._type}'`);
return this;
}
}

@@ -1,2 +0,2 @@

import Fruit, { ValueNode, ValueNodeType } from '../Fruit';
import Fruit, { ValueNode, ValueNodeType, Stem } from '../Fruit';
import Length from '../dataTypes/Length';

@@ -19,3 +19,2 @@ import Percentage from '../dataTypes/Percentage';

export default class BackgroundPosition extends Fruit {
protected _type: string = 'background-position';
protected _state: { count: number, lastType: string };

@@ -25,2 +24,20 @@ x: BackgroundPositionValue;

constructor();
constructor(value: string);
constructor(value?: string) {
super();
this._type = 'background-position';
this.init();
const args = arguments;
this.tryCatch(() => {
if (args.length === 0)
return;
else if (args.length === 1)
this.parse(value);
else
throw new TypeError('Wrong type or excessive arguments');
});
}
protected init() {

@@ -33,2 +50,14 @@ super.init();

analyze(stem: Stem): void {
super.analyze(stem);
// Make sure each property has a value.
if (this.valid) {
if (!this.x.offset)
this.x.offset = new Length(0).toResult() as Length | Percentage | string;
if (!this.y.offset)
this.y.offset = new Length(0).toResult() as Length | Percentage | string;
}
}
protected analyzeInLoop(node: ValueNode): boolean {

@@ -43,3 +72,3 @@ if (node.type === ValueNodeType.space || node.type === ValueNodeType.comment)

if (this._state.count >= 2)
throw new SyntaxError('Excessive keyword');
throw new SyntaxError(`Excessive keyword '${node.value}'`);
else if (this._state.count === 0) {

@@ -64,3 +93,3 @@ this.x.origin = this.y.origin = node.value;

if (this._state.count >= 3)
throw new SyntaxError('Excessive keyword');
throw new SyntaxError(`Excessive keyword '${node.value}'`);
else if (this._state.count === 0) {

@@ -80,3 +109,3 @@ this.x.origin = node.value;

if (this.x.origin !== BackgroundPositionKeyword.center)
throw new SyntaxError('Duplicated keywords: ' + node.value);
throw new SyntaxError(`Duplicated keyword '${node.value}'`);
this.x.origin = node.value;

@@ -118,7 +147,7 @@ this._state.lastType = 'x';

} else
throw new SyntaxError('Excessive keyword: ' + node.value);
throw new SyntaxError(`Excessive keyword '${node.value}'`);
}
} else if (node.value === BackgroundPositionKeyword.top || node.value === BackgroundPositionKeyword.bottom) {
if (this._state.count >= 3)
throw new SyntaxError('Excessive keyword');
throw new SyntaxError(`Excessive keyword '${node.value}'`);
else if (this._state.count === 0) {

@@ -138,3 +167,3 @@ this.y.origin = node.value;

if (this.y.origin !== BackgroundPositionKeyword.center)
throw new SyntaxError('Duplicated keywords: ' + node.value);
throw new SyntaxError(`Duplicated keyword '${node.value}'`);
this.y.origin = node.value;

@@ -176,3 +205,3 @@ this._state.lastType = 'y';

} else
throw new SyntaxError('Excessive keyword: ' + node.value);
throw new SyntaxError(`Excessive keyword '${node.value}'`);
}

@@ -187,3 +216,3 @@ } else {

if (this._state.count >= 4)
throw new SyntaxError('Excessive <length-percentage> value: ' + lengthPercentage);
throw new SyntaxError(`Excessive <length-percentage> '${lengthPercentage}'`);
else if (this._state.count === 0) {

@@ -245,3 +274,3 @@ this.x.offset = lengthPercentage;

else
throw new Error('xxx');
throw new Error('Unexpected internal error');
this._state.lastType = 'length-percentage';

@@ -251,3 +280,3 @@ this._state.count++;

} else
throw new SyntaxError('Excessive <length-percentage> value: ' + lengthPercentage);
throw new SyntaxError(`Excessive <length-percentage> value '${lengthPercentage}'`);
} else if (this._state.count === 3) {

@@ -263,3 +292,3 @@ /**

if (this._state.lastType === 'length-percentage')
throw new Error('Excessive <length-percentage> value: ' + lengthPercentage);
throw new Error(`Excessive <length-percentage> value '${lengthPercentage}'`);
if (!this.x.offset)

@@ -283,5 +312,5 @@ this.x.offset = lengthPercentage;

if (this.x.offset)
if (this.x.offset.toString() !== '0')
x.push(this.x.offset.toString());
if (this.y.offset)
if (this.y.offset.toString() !== '0')
y.push(this.y.offset.toString());

@@ -288,0 +317,0 @@

@@ -13,3 +13,2 @@ import Fruit, { ValueNode, ValueNodeType } from '../Fruit';

export default class BackgroundRepeat extends Fruit {
protected _type: string = 'background-repeat';
protected _state: { count: number };

@@ -19,9 +18,23 @@ x: BackgroundRepeatKeyword;

constructor(value?: string);
constructor(x: BackgroundRepeatKeyword, y?: BackgroundRepeatKeyword) {
super(x);
if (arguments.length > 1) {
this.x = x;
this.y = y;
}
constructor();
constructor(value: string);
constructor(x: BackgroundRepeatKeyword, y: BackgroundRepeatKeyword);
constructor(x?: BackgroundRepeatKeyword | string, y?: BackgroundRepeatKeyword) {
super();
this._type = 'background-repeat';
this.init();
const args = arguments;
this.tryCatch(() => {
if (args.length === 0)
return;
else if (args.length === 1 && typeof x === 'string')
this.parse(x);
else if (args.length === 2) {
this.x = x as BackgroundRepeatKeyword;
this.y = y;
this.valid = true;
} else
throw new TypeError('Wrong type or excessive arguments');
})
}

@@ -42,3 +55,3 @@

if (this._state.count >= 1)
throw new SyntaxError('Excessive keyword');
throw new SyntaxError(`Excessive keyword '${node.value}'`);
else {

@@ -52,3 +65,3 @@ this.x = BackgroundRepeatKeyword.repeat;

if (this._state.count >= 1)
throw new SyntaxError('Excessive keyword');
throw new SyntaxError(`Excessive keyword '${node.value}'`);
else {

@@ -62,3 +75,3 @@ this.x = BackgroundRepeatKeyword['no-repeat'];

if (this._state.count >= 2)
throw new SyntaxError('Excessive keyword');
throw new SyntaxError(`Excessive keyword '${node.value}'`);
else if (this._state.count === 0) {

@@ -73,3 +86,3 @@ this.x = this.y = node.value as BackgroundRepeatKeyword;

} else
throw new Error('State inside problem!');
throw new Error('Unexpected internal error about _state.count');
}

@@ -76,0 +89,0 @@ }

@@ -11,9 +11,24 @@ import Fruit, { ValueNode, ValueNodeType } from '../Fruit';

constructor(value?: string);
constructor(width: string, height?: string) {
super(width);
if (arguments.length > 1) {
this.width = width;
this.height = height;
}
constructor();
constructor(value: string);
constructor(width: string, height: string);
constructor(width: Length | Percentage, height: Length | Percentage);
constructor(width?: Length | Percentage | string, height?: Length | Percentage | string) {
super();
this._type = 'background-size';
this.init();
const args = arguments;
this.tryCatch(() => {
if (args.length === 0)
return;
else if (args.length === 1 && typeof width === 'string')
this.parse(width);
else if (args.length === 2) {
this.width = width;
this.height = height;
this.valid = true;
} else
throw new TypeError('Wrong type or excessive arguments');
});
}

@@ -43,3 +58,3 @@

if (this._state.count >= 1)
throw new SyntaxError('Excessive keyword');
throw new SyntaxError(`Excessive keyword '${node.value}'`);
else {

@@ -60,3 +75,3 @@ this.width = this.height = node.value;

if (this._state.count >= 2)
throw new SyntaxError('Excessive <size> value: ' + size);
throw new SyntaxError(`Excessive value '${size}'`);
else if (this._state.count === 0) {

@@ -72,3 +87,3 @@ this.width = size;

} else
throw new Error('State Problem!');
throw new Error('Unexpected internal error about _state.count');
}

@@ -75,0 +90,0 @@ }

@@ -1,2 +0,2 @@

import Fruit, { ValueNode, ValueNodeType } from '../Fruit';
import Fruit, { ValueNode, ValueNodeType, ParsedDepth } from '../Fruit';
import Length from '../dataTypes/Length';

@@ -13,3 +13,2 @@ import Percentage from '../dataTypes/Percentage';

export default class Margin extends Fruit {
protected _type: string = 'background-repeat';
protected _state: { count: number };

@@ -22,2 +21,21 @@

constructor();
constructor(value: string);
constructor(value?: string) {
super();
this._type = 'margin';
this._parseDepth = ParsedDepth.shorthand;
this.init();
const args = arguments;
this.tryCatch(() => {
if (args.length === 0)
return;
else if (args.length === 1)
this.parse(value);
else
throw new TypeError('Wrong type or excessive arguments');
})
}
protected init() {

@@ -46,3 +64,3 @@ super.init();

if (this._state.count >= 4)
throw new SyntaxError('Excessive <margin> value: ' + value);
throw new SyntaxError(`Excessive value '${value}'`);
else if (this._state.count === 0)

@@ -49,0 +67,0 @@ this.top = this.right = this.bottom = this.left = value;

@@ -1,2 +0,2 @@

import Fruit, { ValueNode, ValueNodeType } from '../Fruit';
import Fruit, { ValueNode, ValueNodeType, ParsedDepth } from '../Fruit';
import Length from '../dataTypes/Length';

@@ -13,3 +13,2 @@ import Percentage from '../dataTypes/Percentage';

export default class Padding extends Fruit {
protected _type: string = 'background-repeat';
protected _state: { count: number };

@@ -22,2 +21,21 @@

constructor();
constructor(value: string);
constructor(value?: string) {
super();
this._type = 'padding';
this._parseDepth = ParsedDepth.shorthand;
this.init();
const args = arguments;
this.tryCatch(() => {
if (args.length === 0)
return;
else if (args.length === 1)
this.parse(value);
else
throw new TypeError('Wrong type or excessive arguments');
})
}
protected init() {

@@ -46,6 +64,6 @@ super.init();

if (String(value)[0] === '-')
throw new Error('Negative values are invalid');
throw new RangeError(`Negative value '${value}' is invalid`);
if (this._state.count >= 4)
throw new SyntaxError('Excessive <padding> value: ' + value);
throw new SyntaxError(`Excessive value '${value}'`);
else if (this._state.count === 0)

@@ -52,0 +70,0 @@ this.top = this.right = this.bottom = this.left = value;

@@ -13,3 +13,3 @@ import * as JSON5 from 'json5';

if (query[0] !== '?')
throw new Error("A valid query string passed to parseQuery should begin with '?'");
throw new SyntaxError("A valid query string passed to parseQuery should begin with '?'");

@@ -67,10 +67,18 @@ query = query.substr(1);

if (Array.isArray(value))
return value.map((subValue) => `${encodeURIComponent(key)}[]=${encodeURIComponent(subValue)}`).join('&');
return value.map((subValue) => `${key}[]=${subValue}`).join('&');
else if (value === true)
return `${encodeURIComponent(key)}`;
return `${key}`;
else if (value === false || value === null)
return `${encodeURIComponent(key)}=${value}`;
return `${key}=${value}`;
else
return `${encodeURIComponent(key)}=${encodeURIComponent(query[key] as string)}`;
return `${key}=${query[key] as string}`;
// if (Array.isArray(value))
// return value.map((subValue) => `${encodeURIComponent(key)}[]=${encodeURIComponent(subValue)}`).join('&');
// else if (value === true)
// return `${encodeURIComponent(key)}`;
// else if (value === false || value === null)
// return `${encodeURIComponent(key)}=${value}`;
// else
// return `${encodeURIComponent(key)}=${encodeURIComponent(query[key] as string)}`;
}).join('&');
}

@@ -35,2 +35,6 @@ import { expect } from 'chai';

it('parse(image-set) -> image-set', () => {
expect(Image.parse('image-set(url(bird.png) 1x, url(bird@2x.png) 2x)').toString()).to.equal("image-set(url('bird.png') 1x, url('bird@2x.png') 2x)");
});
it('parse(value) -> invalid', () => {

@@ -37,0 +41,0 @@ expect(Image.parse('abc')).to.be.undefined;

@@ -5,9 +5,29 @@ import { expect } from 'chai';

describe('Length', () => {
it('#constructor(value)', () => {
it('#constructor(value: string)', () => {
const length = new Length('4.01em');
expect(length.number).to.equal(4.01);
expect(length.unit).to.equal('em');
expect(length.valid).to.be.true;
});
it('#constructor(number: number)', () => {
const length = new Length(0);
expect(length.number).to.equal(0);
expect(length.unit).to.equal('');
expect(length.valid).to.be.true;
const length2 = new Length(3);
expect(length2.valid).to.be.false;
});
it('#constructor(number: number, unit: string)', () => {
const length = new Length(3, 'px');
expect(length.number).to.equal(3);
expect(length.unit).to.equal('px');
expect(length.valid).to.be.true;
const length2 = new Length(2.1, 'ab');
expect(length2.valid).to.be.false;
});
it('.validate(value) => true', () => {

@@ -14,0 +34,0 @@ expect(Length.validate('12px')).to.be.true;

@@ -5,3 +5,3 @@ import { expect } from 'chai';

describe('Percentage', () => {
it('#constructor(value)', () => {
it('#constructor(value: string)', () => {
const percentage = new Percentage('4.01%');

@@ -12,2 +12,8 @@

it('#constructor(value: number)', () => {
const percentage = new Percentage(12);
expect(percentage.number).to.equal(12);
});
it('.validate(value) => true', () => {

@@ -14,0 +20,0 @@ expect(Percentage.validate('12%')).to.be.true;

@@ -30,3 +30,3 @@ import { expect } from 'chai';

expect(new URL(`url('abc.png?xyz[]=a&xyz[]=b')`).toString()).to.equal(`url('abc.png?xyz[]=a&xyz[]=b')`);
expect(new URL(`url('abc.png?a%2C%26b=c%2C%26d')`).toString()).to.equal(`url('abc.png?a%2C%26b=c%2C%26d')`);
// expect(new URL(`url('abc.png?a%2C%26b=c%2C%26d')`).toString()).to.equal(`url('abc.png?a%2C%26b=c%2C%26d')`);
expect(new URL(`url('abc.png?{data:{a:1},isJSON5:true}')`).toString()).to.equal(`url('abc.png?{data:{a:1},isJSON5:true}')`);

@@ -46,3 +46,3 @@ })

expect(url.toString()).to.equal(`url('%E4%B8%AD%E6%96%87.png?%E5%8F%82%E6%95%B0=%E5%80%BC#%E5%93%88%E5%B8%8C')`);
expect(url.toString()).to.equal(`url('中文.png?参数=值#哈希')`);
});

@@ -49,0 +49,0 @@

@@ -14,2 +14,11 @@ import { expect } from 'chai';

it('#constructor(~image-set~)', () => {
const background = new Background(`red center top -webkit-image-set(url('./angry-birds.png') 1x, url('./angry-birds@2x.png') 2x) border-box no-repeat`);
expect(background.color.toString()).to.equal(`red`);
expect(background.image.toString()).to.equal(`-webkit-image-set(url('./angry-birds.png') 1x, url('./angry-birds@2x.png') 2x)`);
expect(background.position.toString()).to.equal('center top');
expect(background.repeat.toString()).to.equal('no-repeat');
});
it('(value) -> valid', () => {

@@ -16,0 +25,0 @@ expect(Background.validate('red url(abc.png) center top / auto 20px border-box no-repeat')).to.be.true;

@@ -9,3 +9,3 @@ import { expect } from 'chai';

expect(backgroundPosition.x.origin).to.equal('left');
expect(backgroundPosition.x.offset).to.be.undefined;
expect(backgroundPosition.x.offset.toString()).to.equal('0');
expect(backgroundPosition.y.origin).to.equal('top');

@@ -12,0 +12,0 @@ expect(backgroundPosition.y.offset.toString()).to.equal('40%');

@@ -6,2 +6,4 @@ import './dataTypes/Number';

import './dataTypes/Image';
import './dataTypes/ImageSet';
import './dataTypes/Resolution';
import './properties/Background';

@@ -11,1 +13,3 @@ import './properties/BackgroundPosition';

import './properties/BackgroundSize';
import './properties/Margin';
import './properties/Padding';

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

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc