css-kits
Version:
Parse css to javascript object. Support change class and id
261 lines (260 loc) • 9.62 kB
JavaScript
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./helpful"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.splitAtRuleBlock = exports.splitSelectors = exports.splitDeclarationBlock = exports.splitRuleSetBlock = exports.splitIntoBlocks = void 0;
const helpful_1 = require("./helpful");
function findFirstIndexOfOpenBrace(subject) {
let result = null;
(0, helpful_1.styleForEach)({
subject,
callbackFn: (e) => {
if (e.value === '{') {
result = e.index;
e.breakForEach();
}
},
});
return result;
}
function splitIntoBlocks(subject) {
const result = [];
let deep = 0;
let lastIndex = 0;
let isInsideOfAtRule = false;
const resultPush = (index) => {
result.push({
start: lastIndex,
end: index + 1,
});
lastIndex = index + 1;
isInsideOfAtRule = false;
};
(0, helpful_1.styleForEach)({
subject,
callbackFn: ({ index: i, value: currentLetter }) => {
switch (currentLetter) {
case '{': {
deep++;
return;
}
case '}': {
deep--;
if (deep > 0)
return;
if (deep === 0) {
resultPush(i);
return;
}
throw new SyntaxError(subject.slice(i - 10, i + 10));
}
case '@': {
if (deep > 0)
return;
if (isInsideOfAtRule === false) {
isInsideOfAtRule = true;
return;
}
throw new SyntaxError(`Syntax is error at ${i}. "${subject.substring(i - 5, 10)}"`);
}
case ';': {
if (deep > 0)
return;
resultPush(i);
return;
}
}
},
});
return result;
}
exports.splitIntoBlocks = splitIntoBlocks;
/**
*
* @param subject look like ".header { display: block; background-color: blue; color: aqua; }"
* @returns [selectors, block];
*/
function splitRuleSetBlock(subject) {
const indexOfOpenBrace = findFirstIndexOfOpenBrace(subject);
if (!indexOfOpenBrace || indexOfOpenBrace === 0 || indexOfOpenBrace === subject.length - 1) {
throw new SyntaxError(subject);
}
const selectors = subject.slice(0, indexOfOpenBrace).trim();
if (selectors.length === 0) {
throw new SyntaxError(subject.slice(0, 30));
}
const block = subject.slice(indexOfOpenBrace).trim();
return [selectors, block];
}
exports.splitRuleSetBlock = splitRuleSetBlock;
function splitDeclarationBlock(subject) {
if (!subject.startsWith('{') || !subject.endsWith('}')) {
throw new SyntaxError(subject);
}
const standardSubject = subject.slice(1, -1).trim();
const results = [];
let hasColon = false;
let allOkays = true;
let lastIndex = 0;
(0, helpful_1.styleForEach)({
subject: standardSubject,
callbackFn: (e) => {
switch (e.value) {
case ':': {
hasColon = true;
return;
}
case ';': {
if (!hasColon) {
allOkays = false;
e.breakForEach();
return;
}
results.push(standardSubject.slice(lastIndex, e.index));
lastIndex = e.index + 1;
hasColon = false;
return;
}
}
},
});
if (!allOkays) {
throw new SyntaxError(subject);
}
if (lastIndex < standardSubject.length) {
if (!hasColon) {
allOkays = false;
throw new SyntaxError(subject);
}
results.push(standardSubject.slice(lastIndex, standardSubject.length));
}
return results.map((item) => item.trim());
}
exports.splitDeclarationBlock = splitDeclarationBlock;
function splitSelectors(subject) {
const ranges = [];
let lastIndex = 0;
let attributeDeep = 0;
let functionDeep = 0;
const pushRange = (index) => {
ranges.push({
start: lastIndex,
end: index,
});
lastIndex = index + 1;
};
(0, helpful_1.styleForEach)({
subject,
callbackFn: ({ value: currentLetter, index: i }) => {
switch (currentLetter) {
case ',': {
if (attributeDeep > 0 || functionDeep > 0)
return;
return pushRange(i);
}
case '(': {
if (attributeDeep > 0)
return;
functionDeep += 1;
return;
}
case ')': {
if (attributeDeep > 0)
return;
functionDeep -= 1;
if (functionDeep < 0) {
throw new SyntaxError(`Extra characters after close-brace "${subject.substring(i - 5, 20)}"`);
}
return;
}
case '[': {
attributeDeep += 1;
return;
}
case ']': {
attributeDeep -= 1;
if (attributeDeep < 0) {
throw new SyntaxError(`Extra characters after close-bracket "${subject.substring(i - 5, 20)}"`);
}
return;
}
}
},
});
if (lastIndex < subject.length) {
pushRange(subject.length);
}
const result = [];
ranges.forEach(({ start, end }) => {
if (end <= start)
return;
const tmp = subject.slice(start, end).trim();
if (tmp.length === 0)
return;
result.push(tmp);
});
return result;
}
exports.splitSelectors = splitSelectors;
/**
*
* @param subject It look like "@media screen and (max-width: 100px) { .test-media { display: block; background-color: blue; } }"
*/
function splitAtRuleBlock(subject) {
if (subject.length === 0) {
throw new SyntaxError('Empty string');
}
if (!subject.startsWith('@')) {
throw new SyntaxError(`${subject}`);
}
const lastCharacter = subject[subject.length - 1];
if (!(lastCharacter === ';' || lastCharacter === '}')) {
throw new SyntaxError(`${subject}`);
}
const firstIndexOfSpace = subject.indexOf(' ');
const identifier = subject.slice(1, firstIndexOfSpace).trim();
if (identifier.length === 0) {
throw new SyntaxError(`${subject}`);
}
const rest = subject.slice(firstIndexOfSpace).trim();
if (rest.length === 0) {
throw new SyntaxError(`${subject}`);
}
const result = {
identifier,
rule: '',
block: null,
};
switch (lastCharacter) {
case ';': {
result.rule = rest.slice(0, -1).trim();
result.block = null;
break;
}
case '}': {
const firstIndexOfOpenBrace = findFirstIndexOfOpenBrace(rest);
if (!firstIndexOfOpenBrace || firstIndexOfOpenBrace === 0) {
throw new SyntaxError(`${subject}`);
}
result.rule = rest.slice(0, firstIndexOfOpenBrace).trim();
result.block = rest.slice(firstIndexOfOpenBrace).trim();
break;
}
default: {
throw new SyntaxError(`${subject}`);
}
}
if (result.rule.length === 0) {
throw new SyntaxError(`${subject}`);
}
return result;
}
exports.splitAtRuleBlock = splitAtRuleBlock;
});