Skip to content

Commit

Permalink
add web export | add stats data to transform,, parse and render funct…
Browse files Browse the repository at this point in the history
…ions | fix escape sequence parsing bug #4
  • Loading branch information
tbela99 committed Aug 12, 2023
1 parent 6c27892 commit 1c22625
Show file tree
Hide file tree
Showing 20 changed files with 393 additions and 222 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![cov](https://tbela99.github.io/css-parser/badges/coverage.svg)](https://github.com/tbela99/css-parser/actions)
[![npm](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Ftbela99%2Fcss-parser%2Fmaster%2Fpackage.json&query=version&logo=npm&label=npm&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F%40tbela99%2Fcss-parser)](https://www.npmjs.com/package/@tbela99/css-parser) [![cov](https://tbela99.github.io/css-parser/badges/coverage.svg)](https://github.com/tbela99/css-parser/actions)

# css-parser

Expand All @@ -12,15 +12,15 @@ $ npm install @tbela99/css-parser

### Features

- [x] fault tolerant parser, will try to fix invalid tokens according to the CSS syntax module 3 recommendations.
- [x] efficient minification, see benchmark
- [x] replace @import at-rules with actual css content of the imported rule
- [x] automatically create nested css rules
- [x] works the same way in node and web browser
- fault tolerant parser, will try to fix invalid tokens according to the CSS syntax module 3 recommendations.
- efficient minification, see [benchmark](https://tbela99.github.io/css-parser/benchmark/index.html)
- replace @import at-rules with actual css content of the imported rule
- automatically create nested css rules
- works the same way in node and web browser

### Performance

- [x] flatten @import
- flatten @import

## Transform

Expand Down Expand Up @@ -50,7 +50,7 @@ Include ParseOptions and RenderOptions

- src: string, optional. css file location to be used with sourcemap.
- minify: boolean, optional. default to _true_. optimize ast.
- nestingRules: boolean, optional. automatically nest rules.
- nestingRules: boolean, optional. automatically generated nested rules.
- removeEmpty: boolean, remove empty nodes from the ast.
- location: boolean, optional. includes node location in the ast, required for sourcemap generation.
- cwd: string, optional. the current working directory. when specified url() are resolved using this value
Expand Down
115 changes: 68 additions & 47 deletions dist/index-umd-web.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,7 @@
if (name.charAt(0) != '#') {
return false;
}
if (isIdent(name.charAt(1))) {
return true;
}
return true;
return isIdent(name.charAt(1));
}
function isNumber(name) {
if (name.length == 0) {
Expand Down Expand Up @@ -1566,6 +1563,7 @@
}

function render(data, opt = {}) {
const startTime = performance.now();
const options = Object.assign(opt.minify ?? true ? {
indent: '',
newLine: '',
Expand All @@ -1584,7 +1582,9 @@
}
return acc + renderToken(curr, options);
}
return { code: doRender(data, options, reducer, 0) };
return { code: doRender(data, options, reducer, 0), stats: {
total: `${(performance.now() - startTime).toFixed(2)}ms`
} };
}
// @ts-ignore
function doRender(data, options, reducer, level = 0, indents = []) {
Expand Down Expand Up @@ -1613,7 +1613,7 @@
case 'AtRule':
case 'Rule':
if (data.typ == 'AtRule' && !('chi' in data)) {
return `${indent}@${data.nam} ${data.val};`;
return `${indent}@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val};`;
}
// @ts-ignore
let children = data.chi.reduce((css, node) => {
Expand All @@ -1625,7 +1625,7 @@
str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`;
}
else if (node.typ == 'AtRule' && !('chi' in node)) {
str = `@${node.nam} ${node.val};`;
str = `${data.val === '' ? '' : options.indent || ' '}${data.val};`;
}
else {
str = doRender(node, options, reducer, level + 1, indents);
Expand All @@ -1642,7 +1642,7 @@
children = children.slice(0, -1);
}
if (data.typ == 'AtRule') {
return `@${data.nam}${data.val ? ' ' + data.val + options.indent : ''}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
return `@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val}${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
}
return data.sel + `${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
}
Expand Down Expand Up @@ -1792,7 +1792,9 @@
case 'Delim':
return /* options.minify && 'Pseudo-class' == token.typ && '::' == token.val.slice(0, 2) ? token.val.slice(1) : */ token.val;
}
throw new Error(`unexpected token ${JSON.stringify(token, null, 1)}`);
console.error(`unexpected token ${JSON.stringify(token, null, 1)}`);
// throw new Error(`unexpected token ${JSON.stringify(token, null, 1)}`);
return '';
}

function eq(a, b) {
Expand Down Expand Up @@ -2581,7 +2583,7 @@
// @ts-ignore
return null;
}
return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node2 : node2 };
return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node1 : node2 };
}
function matchSelectors(selector1, selector2, parentType) {
let match = [[]];
Expand Down Expand Up @@ -2769,11 +2771,27 @@
if (node.typ == 'AtRule' && node.nam == 'font-face') {
continue;
}
if (node.typ == 'AtRule' && node.val == 'all') {
if (node.typ == 'AtRule') {
if (node.nam == 'media' && node.val == 'all') {
// @ts-ignore
ast.chi?.splice(i, 1, ...node.chi);
i--;
continue;
}
// console.debug({previous, node});
// @ts-ignore
ast.chi?.splice(i, 1, ...node.chi);
i--;
continue;
if (previous?.typ == 'AtRule' &&
previous.nam == node.nam &&
previous.val == node.val) {
if ('chi' in node) {
// @ts-ignore
previous.chi.push(...node.chi);
}
// else {
ast?.chi?.splice(i--, 1);
continue;
// }
}
}
// @ts-ignore
if (node.typ == 'Rule') {
Expand Down Expand Up @@ -3288,10 +3306,11 @@
}
buffer += quoteStr;
while (value = peek()) {
if (ind >= iterator.length) {
yield pushToken(buffer, hasNewLine ? 'Bad-string' : 'Unclosed-string');
break;
}
// if (ind >= iterator.length) {
//
// yield pushToken(buffer, hasNewLine ? 'Bad-string' : 'Unclosed-string');
// break;
// }
if (value == '\\') {
const sequence = peek(6);
let escapeSequence = '';
Expand All @@ -3314,7 +3333,7 @@
// not hex or new line
// @ts-ignore
if (i == 1 && !isNewLine(codepoint)) {
buffer += sequence[i];
buffer += value + sequence[i];
next(2);
continue;
}
Expand All @@ -3334,11 +3353,12 @@
continue;
}
// buffer += value;
if (ind >= iterator.length) {
// drop '\\' at the end
yield pushToken(buffer);
break;
}
// if (ind >= iterator.length) {
//
// // drop '\\' at the end
// yield pushToken(buffer);
// break;
// }
buffer += next(2);
continue;
}
Expand Down Expand Up @@ -3507,7 +3527,7 @@
buffer = '';
break;
}
buffer += value;
buffer += prev() + value;
break;
case '"':
case "'":
Expand Down Expand Up @@ -3709,6 +3729,7 @@
* @param opt
*/
async function parse$1(iterator, opt = {}) {
const startTime = performance.now();
const errors = [];
const options = {
src: '',
Expand Down Expand Up @@ -3847,7 +3868,7 @@
src: options.resolve(url, options.src).absolute
}));
});
bytesIn += root.bytesIn;
bytesIn += root.stats.bytesIn;
if (root.ast.chi.length > 0) {
context.chi.push(...root.ast.chi);
}
Expand Down Expand Up @@ -3893,13 +3914,6 @@
// rule
if (delim.typ == 'Block-start') {
const position = map.get(tokens[0]);
// if (context.typ == 'Rule') {
//
// if (tokens[0]?.typ == 'Iden') {
// errors.push({action: 'drop', message: 'invalid nesting rule', location: {src, ...position}});
// return null;
// }
// }
const uniq = new Map;
parseTokens(tokens, { minify: options.minify }).reduce((acc, curr, index, array) => {
if (curr.typ == 'Whitespace') {
Expand Down Expand Up @@ -4075,12 +4089,21 @@
if (tokens.length > 0) {
await parseNode(tokens);
}
const endParseTime = performance.now();
if (options.minify) {
if (ast.chi.length > 0) {
minify(ast, options, true);
}
}
return { ast, errors, bytesIn };
const endTime = performance.now();
return {
ast, errors, stats: {
bytesIn,
parse: `${(endParseTime - startTime).toFixed(2)}ms`,
minify: `${(endTime - endParseTime).toFixed(2)}ms`,
total: `${(endTime - startTime).toFixed(2)}ms`
}
};
}
function parseString(src, options = { location: false }) {
return [...tokenize(src)].map(t => {
Expand Down Expand Up @@ -4431,19 +4454,17 @@
async function transform$1(css, options = {}) {
options = { minify: true, removeEmpty: true, ...options };
const startTime = performance.now();
const parseResult = await parse$1(css, options);
const renderTime = performance.now();
const rendered = render(parseResult.ast, options);
const endTime = performance.now();
return {
...parseResult, ...rendered, stats: {
bytesIn: parseResult.bytesIn,
bytesOut: rendered.code.length,
parse: `${(renderTime - startTime).toFixed(2)}ms`,
render: `${(endTime - renderTime).toFixed(2)}ms`,
total: `${(endTime - startTime).toFixed(2)}ms`
}
};
return parse$1(css, options).then((parseResult) => {
const rendered = render(parseResult.ast, options);
return {
...parseResult, ...rendered, stats: {
bytesOut: rendered.code.length,
...parseResult.stats,
render: rendered.stats.total,
total: `${(performance.now() - startTime).toFixed(2)}ms`
}
};
});
}

const matchUrl = /^(https?:)?\/\//;
Expand Down
Loading

0 comments on commit 1c22625

Please sign in to comment.