Repository URL to install this package:
|
Version:
1.2.8 ▾
|
"use strict";
// const { get } = require('lodash')
// const log = require('fliplog')
const _require = require("./modules/chain-able"),
toUniverseView = _require.toUniverseView,
dot = _require.dot;
const set = dot.set;
/* eslint-disable brace-style */
// https://mdn.mozillademos.org/files/3665/css%20syntax%20-%20declaration.png
let Selector = class Selector {} // & ~ + * [=""] : ,
// lowercase-or-numbers
;
Selector.className = /^([a-z][a-z0-9]*)(-[a-z0-9]+)*$/; // same as above, but starts with `.` ends with `{`
Selector.match = /^.([a-z][a-z0-9]*)(-[a-z0-9]+)*/;
const isStarOrDoubleSlash = line => line.startsWith('/*') || line.startsWith('//'); // const isStarOrDoubleSlash = char => char === '*' || char === '//'
// const isComment = line => line.charAt(0) === '/' && isStarOrDoubleSlash(line.charAt(1))
const isComment = line => line.startsWith('/') && isStarOrDoubleSlash(line);
const not = fn => x => !fn(x);
const isNotComment = not(isComment);
const trim = x => x.trim();
const isNotEmpty = line => line !== '';
const removeFirst = current => {
current = current.split('');
current.shift();
current = current.join('');
return current;
};
const removeLast = (current, num = 1) => {
current = current.split('');
while (num > 0) {
num -= 1;
current.pop();
}
current = current.join('');
return current;
};
const reverse = x => x.split('').reverse().join('');
const removeIndentWhile = str => {
let current = str.replace(/\t\r\n/gmi, '');
while (current.charAt(0) === ' ') {
current = removeFirst(current);
}
return current;
};
const removeIndents = str => {
str = removeIndentWhile(str);
str = reverse(str);
str = removeIndentWhile(str);
str = reverse(str);
return str;
};
const isSelector = line => Selector.match.test(line); // isSelector(line) &&
const isMultiLineSelector = line => line.endsWith(','); // isSelector(line) &&
const isSelectorEnd = line => line.endsWith('{');
const isRuleEnd = line => line.endsWith('}');
function toPath(line) {
line = line // esc
// .replace(/\./gmi, '\\.')
// .split(' ').join('.')
.split('.').join('\\.') // .split('\\.\\.').join('\\.')
// .map(l => l === '.')
// ---
// .replace(/\.\s/gim, '')
// .replace(/\./gim, ' ')
// .replace(/\s+/gim, '.')
// .replace(/ /gmi, '')
// .replace(/(\s|\t|\n|\r)+/gmi, '')
.trim(); // return removeLast(line)
if (line.endsWith('\\.')) {
return removeLast(line, 2);
} // replace first dot if it exists
return line.charAt(0) === '.' ? line.replace('.', '') : line;
}
let Declaration = class Declaration {
constructor(property, value) {
this.property = property;
this.value = value;
}
};
let Parser = class Parser {
constructor(css) {
this.css = css; // this.level = 0
this.store = new Map();
this.obj = {};
this.selectorPath = [];
this.selectorGroupStack = false; // this.history = []
// this.stack = []
// this.path = []
this.mapLine = this.mapLine.bind(this);
}
get selector() {
return this.selectorPath.join(' ');
}
get(selector) {
return this.store.get(selector);
}
set(selector) {
if (this.store.has(selector)) {
return;
}
const line = {
selectors: [selector],
rules: []
};
this.store.set(selector, line); // this.selectorPath.push(selector)
}
last() {
return this.selectorPath[this.selectorPath.length - 1];
}
lastWasMultiLineSelector() {
let last = this.last();
if (!last) return false;
return isMultiLineSelector(last);
}
add(line) {
line = line.replace(/\&/gmi, '').trim();
this.selectorPath.push(line);
this.set(this.selector);
const path = toPath(this.selector);
set(this.obj, path, this.get(this.selector).rules);
toUniverseView(this.obj);
}
pop(line) {
this.selectorPath.pop();
}
mapLine(line) {
if (isMultiLineSelector(line)) {
line = removeLast(line);
if (this.selectorGroupStack) {
this.selectorGroupStack.push(line);
} else {
this.selectorGroupStack = [line];
}
}
if (isSelectorEnd(line)) {
line = removeLast(line); // line = line.trim()
// this is the annoying part...
if (this.selectorGroupStack) {
const selector = this.selector;
const selectorGroupStack = this.selectorGroupStack.concat([line]);
this.selectorGroupStack = false;
selectorGroupStack.forEach((nestedSelector, index) => {
const isLast = index === selectorGroupStack.length - 1;
let nested = nestedSelector;
if (isLast) {
nested = selector + nestedSelector;
nested += ',';
} else {
nested = selector + nestedSelector;
}
this.add(nested);
});
} else {
this.add(line);
}
} else if (isRuleEnd(line)) {
this.pop();
} else {
// console.log(this.selector, line)
const group = this.get(this.selector); // console.log(group, this.selector)
group.rules.push(line);
} // console.log(this.selectorPath)
}
parse(css) {
const mapped = css.split('\n').map(removeIndents).filter(isNotComment) // .filter(x => isComment(x))
.filter(isNotEmpty).map(this.mapLine);
Array.from(this.store.keys()).forEach(name => {
console.log('\n');
const value = this.get(name); // log.fmtobj({ [name]: value.rules }).echo()
// log.bold(name).echo()
// log.blue('selectors').data(value.selectors).echo()
// log.yellow('rules').data(value.rules).echo()
// console.log({ [name]: value })
console.log('\n');
}); // log.fmtobj(this.obj.toFlat(false)).echo()
// log.quick((this.obj))
// log.quick(toUniverseView(this.obj, true))
// console.log(this.selector)
return css;
}
};
function parse(css) {
return new Parser(css).parse(css);
}
module.exports = parse;