1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | const Parser = require('postcss/lib/parser');
|
12 |
|
13 | const AtWord = require('./nodes/AtWord');
|
14 | const Comment = require('./nodes/Comment');
|
15 | const Func = require('./nodes/Func');
|
16 | const Interpolation = require('./nodes/Interpolation');
|
17 | const Numeric = require('./nodes/Numeric');
|
18 | const Operator = require('./nodes/Operator');
|
19 | const Punctuation = require('./nodes/Punctuation');
|
20 | const Quoted = require('./nodes/Quoted');
|
21 | const UnicodeRange = require('./nodes/UnicodeRange');
|
22 | const Word = require('./nodes/Word');
|
23 |
|
24 | const defaults = {
|
25 | ignoreUnknownWords: false,
|
26 |
|
27 | interpolation: false,
|
28 | parentNode: null,
|
29 | variables: {
|
30 | prefixes: ['--']
|
31 | }
|
32 | };
|
33 |
|
34 | module.exports = class ValuesParser extends Parser {
|
35 | constructor(input, opts = {}) {
|
36 | super(input);
|
37 |
|
38 | this.lastNode = null;
|
39 | this.options = Object.assign({}, defaults, opts);
|
40 | this.parentNode = this.options.parentNode;
|
41 | }
|
42 |
|
43 | back(tokens) {
|
44 | for (const token of tokens.reverse()) {
|
45 | this.tokenizer.back(token);
|
46 | }
|
47 | }
|
48 |
|
49 | comment(token) {
|
50 | super.comment(token);
|
51 |
|
52 | const inline = Comment.testInline(token);
|
53 | const node = this.lastNode;
|
54 | node.inline = inline;
|
55 | Object.setPrototypeOf(node, Comment.prototype);
|
56 | }
|
57 |
|
58 | fromFirst(tokens, Constructor) {
|
59 | const [first] = tokens;
|
60 | const [, value, startLine, startChar] = first;
|
61 | const node = new Constructor({ value });
|
62 |
|
63 | this.init(node, startLine, startChar);
|
64 | this.current = node;
|
65 | this.end(first);
|
66 | this.back(tokens.slice(1));
|
67 | }
|
68 |
|
69 | init(node, line, column) {
|
70 | super.init(node, line, column);
|
71 |
|
72 |
|
73 |
|
74 | this.lastNode = node;
|
75 | }
|
76 |
|
77 | other(start) {
|
78 |
|
79 |
|
80 | const brackets = [];
|
81 | const tokens = [];
|
82 | let token = start;
|
83 | let type = null;
|
84 | let bracket = null;
|
85 |
|
86 | while (token) {
|
87 | [type] = token;
|
88 | tokens.push(token);
|
89 |
|
90 | if (type === '(' || type === '[') {
|
91 | if (!bracket) {
|
92 | bracket = token;
|
93 | }
|
94 |
|
95 | brackets.push(type === '(' ? ')' : ']');
|
96 | } else if (type === brackets[brackets.length - 1]) {
|
97 | brackets.pop();
|
98 | if (brackets.length === 0) {
|
99 | bracket = null;
|
100 | }
|
101 | }
|
102 |
|
103 | token = this.tokenizer.nextToken();
|
104 | }
|
105 |
|
106 | if (brackets.length > 0) {
|
107 | this.unclosedBracket(bracket);
|
108 | }
|
109 |
|
110 | this.unknownWord(tokens);
|
111 | }
|
112 |
|
113 |
|
114 | parse() {
|
115 | let token;
|
116 | while (!this.tokenizer.endOfFile()) {
|
117 | token = this.tokenizer.nextToken();
|
118 |
|
119 | switch (token[0]) {
|
120 | case 'space':
|
121 | this.spaces += token[1];
|
122 | break;
|
123 |
|
124 | case 'comment':
|
125 | this.comment(token);
|
126 | break;
|
127 |
|
128 | case 'at-word':
|
129 | this.atrule(token);
|
130 | Object.setPrototypeOf(this.lastNode, AtWord.prototype);
|
131 | this.lastNode.type = 'atword';
|
132 | break;
|
133 |
|
134 | default:
|
135 | this.other(token);
|
136 | break;
|
137 | }
|
138 | }
|
139 | this.endFile();
|
140 | }
|
141 |
|
142 | unknownWord(tokens) {
|
143 |
|
144 |
|
145 |
|
146 | const [first] = tokens;
|
147 | const [type, value] = first;
|
148 |
|
149 | if (Punctuation.chars.includes(type)) {
|
150 | Punctuation.fromTokens(tokens, this);
|
151 | } else if (Func.test(tokens)) {
|
152 | Func.fromTokens(tokens, this);
|
153 | } else if (this.options.interpolation && Interpolation.test(tokens, this)) {
|
154 | Interpolation.fromTokens(tokens, this);
|
155 | } else if (type === 'brackets') {
|
156 | Punctuation.tokenizeBrackets(tokens, this);
|
157 | } else if (type === 'comma') {
|
158 | Punctuation.fromTokens(tokens, this);
|
159 | } else if (type === 'operator') {
|
160 | Operator.fromTokens(tokens, this);
|
161 | } else if (type === 'string') {
|
162 | Quoted.fromTokens(tokens, this);
|
163 | } else if (type === 'word') {
|
164 | if (value === ',') {
|
165 | Punctuation.fromTokens(tokens, this);
|
166 | } else if (value === '//') {
|
167 | Comment.tokenizeNext(tokens, this);
|
168 | } else if (Comment.testInline(first)) {
|
169 |
|
170 |
|
171 | const { parentNode } = this;
|
172 | if (parentNode && parentNode.type === 'func' && parentNode.name === 'url') {
|
173 | Word.fromTokens(tokens, this);
|
174 | } else {
|
175 | Comment.tokenizeInline(tokens, this);
|
176 | }
|
177 | } else if (value.includes(',')) {
|
178 | Punctuation.tokenizeCommas(tokens, this);
|
179 | } else if (Word.testWord(tokens, this)) {
|
180 |
|
181 | Word.fromTokens(tokens, this);
|
182 | } else if (Numeric.test(value)) {
|
183 | Numeric.fromTokens(tokens, this);
|
184 | } else if (UnicodeRange.test(value)) {
|
185 | UnicodeRange.fromTokens(tokens, this);
|
186 | } else if (Operator.chars.includes(value)) {
|
187 | Operator.fromTokens(tokens, this);
|
188 | } else if (/^[\w-]+$/.test(value)) {
|
189 | Word.fromTokens(tokens, this);
|
190 | } else if (Operator.regex.test(value)) {
|
191 | Operator.tokenize(tokens, this);
|
192 | } else if (this.options.ignoreUnknownWords) {
|
193 | Word.fromTokens(tokens, this);
|
194 | } else {
|
195 | super.unknownWord(tokens);
|
196 | }
|
197 | } else {
|
198 |
|
199 | super.unknownWord(tokens);
|
200 | }
|
201 | }
|
202 | };
|