source: frontend/node_modules/css-tree/lib/lexer/Lexer.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 9.9 KB
Line 
1var SyntaxReferenceError = require('./error').SyntaxReferenceError;
2var MatchError = require('./error').MatchError;
3var names = require('../utils/names');
4var generic = require('./generic');
5var parse = require('../definition-syntax/parse');
6var generate = require('../definition-syntax/generate');
7var walk = require('../definition-syntax/walk');
8var prepareTokens = require('./prepare-tokens');
9var buildMatchGraph = require('./match-graph').buildMatchGraph;
10var matchAsTree = require('./match').matchAsTree;
11var trace = require('./trace');
12var search = require('./search');
13var getStructureFromConfig = require('./structure').getStructureFromConfig;
14var cssWideKeywords = buildMatchGraph('inherit | initial | unset');
15var cssWideKeywordsWithExpression = buildMatchGraph('inherit | initial | unset | <-ms-legacy-expression>');
16
17function dumpMapSyntax(map, compact, syntaxAsAst) {
18 var result = {};
19
20 for (var name in map) {
21 if (map[name].syntax) {
22 result[name] = syntaxAsAst
23 ? map[name].syntax
24 : generate(map[name].syntax, { compact: compact });
25 }
26 }
27
28 return result;
29}
30
31function valueHasVar(tokens) {
32 for (var i = 0; i < tokens.length; i++) {
33 if (tokens[i].value.toLowerCase() === 'var(') {
34 return true;
35 }
36 }
37
38 return false;
39}
40
41function buildMatchResult(match, error, iterations) {
42 return {
43 matched: match,
44 iterations: iterations,
45 error: error,
46 getTrace: trace.getTrace,
47 isType: trace.isType,
48 isProperty: trace.isProperty,
49 isKeyword: trace.isKeyword
50 };
51}
52
53function matchSyntax(lexer, syntax, value, useCommon) {
54 var tokens = prepareTokens(value, lexer.syntax);
55 var result;
56
57 if (valueHasVar(tokens)) {
58 return buildMatchResult(null, new Error('Matching for a tree with var() is not supported'));
59 }
60
61 if (useCommon) {
62 result = matchAsTree(tokens, lexer.valueCommonSyntax, lexer);
63 }
64
65 if (!useCommon || !result.match) {
66 result = matchAsTree(tokens, syntax.match, lexer);
67 if (!result.match) {
68 return buildMatchResult(
69 null,
70 new MatchError(result.reason, syntax.syntax, value, result),
71 result.iterations
72 );
73 }
74 }
75
76 return buildMatchResult(result.match, null, result.iterations);
77}
78
79var Lexer = function(config, syntax, structure) {
80 this.valueCommonSyntax = cssWideKeywords;
81 this.syntax = syntax;
82 this.generic = false;
83 this.properties = {};
84 this.types = {};
85 this.structure = structure || getStructureFromConfig(config);
86
87 if (config) {
88 if (config.types) {
89 for (var name in config.types) {
90 this.addType_(name, config.types[name]);
91 }
92 }
93
94 if (config.generic) {
95 this.generic = true;
96 for (var name in generic) {
97 this.addType_(name, generic[name]);
98 }
99 }
100
101 if (config.properties) {
102 for (var name in config.properties) {
103 this.addProperty_(name, config.properties[name]);
104 }
105 }
106 }
107};
108
109Lexer.prototype = {
110 structure: {},
111 checkStructure: function(ast) {
112 function collectWarning(node, message) {
113 warns.push({
114 node: node,
115 message: message
116 });
117 }
118
119 var structure = this.structure;
120 var warns = [];
121
122 this.syntax.walk(ast, function(node) {
123 if (structure.hasOwnProperty(node.type)) {
124 structure[node.type].check(node, collectWarning);
125 } else {
126 collectWarning(node, 'Unknown node type `' + node.type + '`');
127 }
128 });
129
130 return warns.length ? warns : false;
131 },
132
133 createDescriptor: function(syntax, type, name) {
134 var ref = {
135 type: type,
136 name: name
137 };
138 var descriptor = {
139 type: type,
140 name: name,
141 syntax: null,
142 match: null
143 };
144
145 if (typeof syntax === 'function') {
146 descriptor.match = buildMatchGraph(syntax, ref);
147 } else {
148 if (typeof syntax === 'string') {
149 // lazy parsing on first access
150 Object.defineProperty(descriptor, 'syntax', {
151 get: function() {
152 Object.defineProperty(descriptor, 'syntax', {
153 value: parse(syntax)
154 });
155
156 return descriptor.syntax;
157 }
158 });
159 } else {
160 descriptor.syntax = syntax;
161 }
162
163 // lazy graph build on first access
164 Object.defineProperty(descriptor, 'match', {
165 get: function() {
166 Object.defineProperty(descriptor, 'match', {
167 value: buildMatchGraph(descriptor.syntax, ref)
168 });
169
170 return descriptor.match;
171 }
172 });
173 }
174
175 return descriptor;
176 },
177 addProperty_: function(name, syntax) {
178 this.properties[name] = this.createDescriptor(syntax, 'Property', name);
179 },
180 addType_: function(name, syntax) {
181 this.types[name] = this.createDescriptor(syntax, 'Type', name);
182
183 if (syntax === generic['-ms-legacy-expression']) {
184 this.valueCommonSyntax = cssWideKeywordsWithExpression;
185 }
186 },
187
188 matchDeclaration: function(node) {
189 if (node.type !== 'Declaration') {
190 return buildMatchResult(null, new Error('Not a Declaration node'));
191 }
192
193 return this.matchProperty(node.property, node.value);
194 },
195 matchProperty: function(propertyName, value) {
196 var property = names.property(propertyName);
197
198 // don't match syntax for a custom property
199 if (property.custom) {
200 return buildMatchResult(null, new Error('Lexer matching doesn\'t applicable for custom properties'));
201 }
202
203 var propertySyntax = property.vendor
204 ? this.getProperty(property.name) || this.getProperty(property.basename)
205 : this.getProperty(property.name);
206
207 if (!propertySyntax) {
208 return buildMatchResult(null, new SyntaxReferenceError('Unknown property', propertyName));
209 }
210
211 return matchSyntax(this, propertySyntax, value, true);
212 },
213 matchType: function(typeName, value) {
214 var typeSyntax = this.getType(typeName);
215
216 if (!typeSyntax) {
217 return buildMatchResult(null, new SyntaxReferenceError('Unknown type', typeName));
218 }
219
220 return matchSyntax(this, typeSyntax, value, false);
221 },
222 match: function(syntax, value) {
223 if (typeof syntax !== 'string' && (!syntax || !syntax.type)) {
224 return buildMatchResult(null, new SyntaxReferenceError('Bad syntax'));
225 }
226
227 if (typeof syntax === 'string' || !syntax.match) {
228 syntax = this.createDescriptor(syntax, 'Type', 'anonymous');
229 }
230
231 return matchSyntax(this, syntax, value, false);
232 },
233
234 findValueFragments: function(propertyName, value, type, name) {
235 return search.matchFragments(this, value, this.matchProperty(propertyName, value), type, name);
236 },
237 findDeclarationValueFragments: function(declaration, type, name) {
238 return search.matchFragments(this, declaration.value, this.matchDeclaration(declaration), type, name);
239 },
240 findAllFragments: function(ast, type, name) {
241 var result = [];
242
243 this.syntax.walk(ast, {
244 visit: 'Declaration',
245 enter: function(declaration) {
246 result.push.apply(result, this.findDeclarationValueFragments(declaration, type, name));
247 }.bind(this)
248 });
249
250 return result;
251 },
252
253 getProperty: function(name) {
254 return this.properties.hasOwnProperty(name) ? this.properties[name] : null;
255 },
256 getType: function(name) {
257 return this.types.hasOwnProperty(name) ? this.types[name] : null;
258 },
259
260 validate: function() {
261 function validate(syntax, name, broken, descriptor) {
262 if (broken.hasOwnProperty(name)) {
263 return broken[name];
264 }
265
266 broken[name] = false;
267 if (descriptor.syntax !== null) {
268 walk(descriptor.syntax, function(node) {
269 if (node.type !== 'Type' && node.type !== 'Property') {
270 return;
271 }
272
273 var map = node.type === 'Type' ? syntax.types : syntax.properties;
274 var brokenMap = node.type === 'Type' ? brokenTypes : brokenProperties;
275
276 if (!map.hasOwnProperty(node.name) || validate(syntax, node.name, brokenMap, map[node.name])) {
277 broken[name] = true;
278 }
279 }, this);
280 }
281 }
282
283 var brokenTypes = {};
284 var brokenProperties = {};
285
286 for (var key in this.types) {
287 validate(this, key, brokenTypes, this.types[key]);
288 }
289
290 for (var key in this.properties) {
291 validate(this, key, brokenProperties, this.properties[key]);
292 }
293
294 brokenTypes = Object.keys(brokenTypes).filter(function(name) {
295 return brokenTypes[name];
296 });
297 brokenProperties = Object.keys(brokenProperties).filter(function(name) {
298 return brokenProperties[name];
299 });
300
301 if (brokenTypes.length || brokenProperties.length) {
302 return {
303 types: brokenTypes,
304 properties: brokenProperties
305 };
306 }
307
308 return null;
309 },
310 dump: function(syntaxAsAst, pretty) {
311 return {
312 generic: this.generic,
313 types: dumpMapSyntax(this.types, !pretty, syntaxAsAst),
314 properties: dumpMapSyntax(this.properties, !pretty, syntaxAsAst)
315 };
316 },
317 toString: function() {
318 return JSON.stringify(this.dump());
319 }
320};
321
322module.exports = Lexer;
Note: See TracBrowser for help on using the repository browser.