SMELUKOV_DEV Telegram 121
Ну и, думаю, последний пост на тему css-парсинга в этом цикле )
Возможно, у вас в голове крутится что-то вроде:
"Сергей, в предыдущем посте ты плевался от работы с исходником через токены, а в генераторе сам этим грешишь!".
Все так, и в случае, когда нужно просто добавить пробелов - это ок. Более того, css-tree делает намного более детальную токенизацию, нежели postcss-value-parser
Но! Если все таки хочется заморочиться с контекстом и не вставлять пробелы только перед именем шрифта и только в свойстве font, то так тоже можно.
Решение делится на несколько этапов:
- найти все декларации у которых имя свойства равно font
- найти в них ноды со значением типа family-name
- вставить пробелы в генераторе только для этих нод

Этап 1: собираем все декларация типа font:

Для этого воспользуемся волкером и обойдем AST:


const {walk} = require('css-tree');

walk(compressedAST, {
enter(node) {
if (node.type === 'Declaration' && node.property === 'font') {
// нашли!
}
}
});


Этап 2: ищем в этих декларация ноды со значением типа family-name

Все не так просто. Так, например, здесь:


.foo {
color: red;
animation-name: blue;
}


только один цвет - red, а blue, хоть и валидное имя цвета, но используется как название анимации.
AST не знает какой тип значения (цвет, размер, имя шрифта и тп) хранится в ноде. Для этого, нам нужно подняться на уровень лексического разбора и найти нужные нам лексемы. Для этого у css-tree есть лексер. Вот его мы и используем чтобы в декларациях типа font найти все имена шрифтов:


const {walk, lexer} = require('css-tree');

const allFamilyNameNodes = new WeakSet();

walk(compressedAST, {
enter(node) {
if (node.type === 'Declaration' && node.property === 'font') {
const familyNames = lexer.findAllFragments(node, 'Type', 'family-name');

for (const item of familyNames) {
for (const node of item.nodes) {
targetNodes.add(node);
}
}
}
}
});


Теперь в allFamilyNameNodes хранятся все ноды, которые именно по смыслу содержат имя шрифта.

Этап 3: вставляем пробелы только перед собранными нодами

Здесь берем за основу уже знакомый нам код декоратора и чуть-чуть меняем его так, чтобы он срабатывал только для нод, которые мы собрали


const css = generate(compressedAST, {
decorator(handlers) {
return {
...handlers,
node(node) {
this.currentNode = node;
handlers.node(node);
},
tokenBefore(prev, current, value) {
if (
prev !== tokenTypes.WhiteSpace &&
current === tokenTypes.String &&
allFamilyNameNodes.has(this.currentNode)
) {
this.emit(' ');
return tokenTypes.WhiteSpace;
}
return handlers.tokenBefore(prev, current, value);
}
};
}
});


Всё.

Да, здесь можно было сразу найти все family-name, не обходя декларация типа font:

const familyNames = lexer.findAllFragments(compressedAST, 'Type', 'family-name');


Но в таком случае мы бы нашли вообще все family-name и в других свойствах. Тем не менее, вполне рабочий вариант, нечто среднее между первым и вторым, но мне захотелось показать более комплексный пример, да и такие вот комплексные штуки как раз используеются в разного рода плагинах к IDE, например.
👍62



tgoop.com/smelukov_dev/121
Create:
Last Update:

Ну и, думаю, последний пост на тему css-парсинга в этом цикле )
Возможно, у вас в голове крутится что-то вроде:
"Сергей, в предыдущем посте ты плевался от работы с исходником через токены, а в генераторе сам этим грешишь!".
Все так, и в случае, когда нужно просто добавить пробелов - это ок. Более того, css-tree делает намного более детальную токенизацию, нежели postcss-value-parser
Но! Если все таки хочется заморочиться с контекстом и не вставлять пробелы только перед именем шрифта и только в свойстве font, то так тоже можно.
Решение делится на несколько этапов:
- найти все декларации у которых имя свойства равно font
- найти в них ноды со значением типа family-name
- вставить пробелы в генераторе только для этих нод

Этап 1: собираем все декларация типа font:

Для этого воспользуемся волкером и обойдем AST:


const {walk} = require('css-tree');

walk(compressedAST, {
enter(node) {
if (node.type === 'Declaration' && node.property === 'font') {
// нашли!
}
}
});


Этап 2: ищем в этих декларация ноды со значением типа family-name

Все не так просто. Так, например, здесь:


.foo {
color: red;
animation-name: blue;
}


только один цвет - red, а blue, хоть и валидное имя цвета, но используется как название анимации.
AST не знает какой тип значения (цвет, размер, имя шрифта и тп) хранится в ноде. Для этого, нам нужно подняться на уровень лексического разбора и найти нужные нам лексемы. Для этого у css-tree есть лексер. Вот его мы и используем чтобы в декларациях типа font найти все имена шрифтов:


const {walk, lexer} = require('css-tree');

const allFamilyNameNodes = new WeakSet();

walk(compressedAST, {
enter(node) {
if (node.type === 'Declaration' && node.property === 'font') {
const familyNames = lexer.findAllFragments(node, 'Type', 'family-name');

for (const item of familyNames) {
for (const node of item.nodes) {
targetNodes.add(node);
}
}
}
}
});


Теперь в allFamilyNameNodes хранятся все ноды, которые именно по смыслу содержат имя шрифта.

Этап 3: вставляем пробелы только перед собранными нодами

Здесь берем за основу уже знакомый нам код декоратора и чуть-чуть меняем его так, чтобы он срабатывал только для нод, которые мы собрали


const css = generate(compressedAST, {
decorator(handlers) {
return {
...handlers,
node(node) {
this.currentNode = node;
handlers.node(node);
},
tokenBefore(prev, current, value) {
if (
prev !== tokenTypes.WhiteSpace &&
current === tokenTypes.String &&
allFamilyNameNodes.has(this.currentNode)
) {
this.emit(' ');
return tokenTypes.WhiteSpace;
}
return handlers.tokenBefore(prev, current, value);
}
};
}
});


Всё.

Да, здесь можно было сразу найти все family-name, не обходя декларация типа font:

const familyNames = lexer.findAllFragments(compressedAST, 'Type', 'family-name');


Но в таком случае мы бы нашли вообще все family-name и в других свойствах. Тем не менее, вполне рабочий вариант, нечто среднее между первым и вторым, но мне захотелось показать более комплексный пример, да и такие вот комплексные штуки как раз используеются в разного рода плагинах к IDE, например.

BY Сергей Мелюков


Share with your friend now:
tgoop.com/smelukov_dev/121

View MORE
Open in Telegram


Telegram News

Date: |

Find your optimal posting schedule and stick to it. The peak posting times include 8 am, 6 pm, and 8 pm on social media. Try to publish serious stuff in the morning and leave less demanding content later in the day. More>> Concise Those being doxxed include outgoing Chief Executive Carrie Lam Cheng Yuet-ngor, Chung and police assistant commissioner Joe Chan Tung, who heads police's cyber security and technology crime bureau. How to Create a Private or Public Channel on Telegram?
from us


Telegram Сергей Мелюков
FROM American