123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- 'use strict';
-
- const Literal = require('./literal');
- const postcssParse = require('postcss/lib/parse');
- const reNewLine = /(?:\r?\n|\r)/gm;
- const isLiteral = (token) => token[0] === 'word' && /^\$\{[\s\S]*\}$/.test(token[1]);
-
- function literal(start) {
- if (!isLiteral(start)) {
- return;
- }
-
- const tokens = [];
- let hasWord;
- let type;
- let token;
-
- while ((token = this.tokenizer.nextToken())) {
- tokens.push(token);
- type = token[0];
-
- if (type.length === 1) {
- break;
- } else if (type === 'word') {
- hasWord = true;
- }
- }
-
- while (tokens.length) {
- this.tokenizer.back(tokens.pop());
- }
-
- if (type === '{' || (type === ':' && !hasWord)) {
- return;
- }
-
- const node = new Literal({
- text: start[1],
- });
-
- this.init(node, start[2], start[3]);
-
- const input = this.input;
-
- if (input.templateLiteralStyles) {
- const offset = input.quasis[0].start;
- const nodeIndex = getNodeIndex(node, input);
- const start = offset + nodeIndex;
- const end = start + node.text.length;
- const templateLiteralStyles = input.templateLiteralStyles.filter(
- (style) => style.startIndex <= end && start < style.endIndex,
- );
-
- if (templateLiteralStyles.length) {
- const nodes = parseTemplateLiteralStyles(templateLiteralStyles, input, [
- nodeIndex,
- nodeIndex + node.text.length,
- ]);
-
- if (nodes.length) {
- node.nodes = nodes;
- nodes.forEach((n) => (n.parent = node));
- }
- }
- }
-
- return node;
- }
-
- function freeSemicolon(token) {
- this.spaces += token[1];
- const nodes = this.current.nodes;
- const prev = nodes && nodes[nodes.length - 1];
-
- if (prev && /^(rule|literal)$/.test(prev.type) && !prev.raws.ownSemicolon) {
- prev.raws.ownSemicolon = this.spaces;
- this.spaces = '';
- }
- }
-
- module.exports = {
- freeSemicolon,
- literal,
- };
-
- function parseTemplateLiteralStyles(styles, input, range) {
- const offset = input.quasis[0].start;
- const source = input.css;
-
- const opts = Object.assign({}, input.parseOptions);
-
- delete opts.templateLiteralStyles;
- delete opts.expressions;
- delete opts.quasis;
-
- const parseStyle = docFixer(offset, source, opts);
-
- const nodes = [];
- let index = range[0];
-
- styles
- .sort((a, b) => a.startIndex - b.startIndex)
- .forEach((style) => {
- const root = parseStyle(style);
-
- if (!root || !root.nodes.length) {
- return;
- }
-
- root.raws.beforeStart = source.slice(index, style.startIndex - offset);
- root.raws.afterEnd = '';
-
- if (style.endIndex) {
- index = style.endIndex - offset;
- } else {
- index = style.startIndex - offset + (style.content || root.source.input.css).length;
- }
-
- nodes.push(root);
- });
-
- if (nodes.length) {
- nodes[nodes.length - 1].raws.afterEnd = source.slice(index, range[1]);
- }
-
- return nodes;
- }
-
- class LocalFixer {
- constructor(offset, lines, style, templateParse) {
- const startIndex = style.startIndex - offset;
- let line = 0;
- let column = startIndex;
-
- lines.some((lineEndIndex, lineNumber) => {
- if (lineEndIndex >= startIndex) {
- line = lineNumber--;
-
- if (lineNumber in lines) {
- column = startIndex - lines[lineNumber] - 1;
- }
-
- return true;
- }
- });
-
- this.line = line;
- this.column = column;
- this.style = style;
- this.templateParse = templateParse;
- }
- object(object) {
- if (object) {
- if (object.line === 1) {
- object.column += this.column;
- }
-
- object.line += this.line;
- }
- }
- node(node) {
- this.object(node.source.start);
- this.object(node.source.end);
- }
- root(root) {
- this.node(root);
- root.walk((node) => {
- this.node(node);
- });
- }
- error(error) {
- if (error && error.name === 'CssSyntaxError') {
- this.object(error);
- this.object(error.input);
- error.message = error.message.replace(
- /:\d+:\d+:/,
- ':' + error.line + ':' + error.column + ':',
- );
- }
-
- return error;
- }
- parse(opts) {
- const style = this.style;
- const syntax = style.syntax;
- let root = style.root;
-
- try {
- root = this.templateParse(
- style.content,
- Object.assign(
- {},
- opts,
- {
- map: false,
- },
- style.opts,
- ),
- );
- } catch (error) {
- if (style.ignoreErrors) {
- return;
- } else if (!style.skipConvert) {
- this.error(error);
- }
-
- throw error;
- }
-
- if (!style.skipConvert) {
- this.root(root);
- }
-
- root.source.inline = Boolean(style.inline);
- root.source.lang = style.lang;
- root.source.syntax = syntax;
-
- return root;
- }
- }
-
- function docFixer(offset, source, opts) {
- let match;
- const lines = [];
-
- reNewLine.lastIndex = 0;
- while ((match = reNewLine.exec(source))) {
- lines.push(match.index);
- }
- lines.push(source.length);
-
- return function parseStyle(style) {
- const parse = style.syntax ? style.syntax.parse : postcssParse;
-
- return new LocalFixer(offset, lines, style, parse).parse(opts);
- };
- }
-
- function getNodeIndex(node, input) {
- const source = input.css;
- let match;
- let line = 1;
- let lastIndex = -1;
-
- reNewLine.lastIndex = 0;
- while ((match = reNewLine.exec(source))) {
- if (line === node.source.start.line) {
- return lastIndex + node.source.start.column;
- }
-
- lastIndex = match.index;
- line++;
- }
-
- if (line === node.source.start.line) {
- return lastIndex + node.source.start.column;
- }
-
- return source.length;
- }
|