UNPKG

@carlosjeurissen/stylelint-csstree-validator

Version:

Stylelint plugin to validate CSS syntax

137 lines (119 loc) 3.95 kB
'use strict'; const cssTree = require('css-tree'); const SassVariable = require('./SassVariable.cjs'); const SassInterpolation = require('./SassInterpolation.cjs'); const SassNamespace = require('./SassNamespace.cjs'); const NUMBERSIGN = 0x0023; // U+0023 NUMBER SIGN (#) const DOLLARSIGN = 0x0024; // U+0024 DOLLAR SIGN ($) const PERCENTAGESIGN = 0x0025; // U+0025 PERCENTAGE SIGN (%) const FULLSTOP = 0x002E; // U+002E FULL STOP (.) // custom error class PreprocessorExtensionError { constructor() { this.type = 'PreprocessorExtensionError'; } } function throwOnSyntaxExtension() { let node = null; switch (this.tokenType) { case cssTree.tokenTypes.Ident: if (this.isDelim(FULLSTOP, 1)) { node = this.SassNamespace(); } break; case cssTree.tokenTypes.Delim: switch (this.source.charCodeAt(this.tokenStart)) { case DOLLARSIGN: // sass: $var node = this.SassVariable(); break; case NUMBERSIGN: // sass: #{ } if (this.lookupType(1) === cssTree.tokenTypes.LeftCurlyBracket) { node = this.SassInterpolation(this.scope.Value, this.readSequence); } break; case PERCENTAGESIGN: // sass: 5 % 4 node = this.Operator(); break; } break; } // currently we can't validate values that contain less/sass extensions if (node !== null) { throw new PreprocessorExtensionError(); } } function sass(syntaxConfig) { // new node types syntaxConfig.node.SassVariable = SassVariable; syntaxConfig.node.SassInterpolation = SassInterpolation; syntaxConfig.node.SassNamespace = SassNamespace; // custom at-rules syntaxConfig.atrules['at-root'] = { prelude: '<any-value>' }; syntaxConfig.atrules.content = { prelude: '<any-value>' }; syntaxConfig.atrules.debug = { prelude: '<any-value>' }; syntaxConfig.atrules.each = { prelude: '<any-value>' }; syntaxConfig.atrules.else = { prelude: '<any-value>?' }; syntaxConfig.atrules.error = { prelude: '<any-value>' }; syntaxConfig.atrules.extend = { prelude: '<any-value>' }; syntaxConfig.atrules.for = { prelude: '<any-value>' }; syntaxConfig.atrules.forward = { prelude: '<any-value>' }; syntaxConfig.atrules.function = { prelude: '<any-value>' }; syntaxConfig.atrules.if = { prelude: '<any-value>' }; syntaxConfig.atrules.import = { prelude: syntaxConfig.atrules.import.prelude + '| <string>#' // FIXME: fix prelude extension in css-tree }; syntaxConfig.atrules.include = { prelude: '<any-value>' }; syntaxConfig.atrules.mixin = { prelude: '<any-value>' }; syntaxConfig.atrules.return = { prelude: '<any-value>' }; syntaxConfig.atrules.use = { prelude: '<any-value>' }; syntaxConfig.atrules.warn = { prelude: '<any-value>' }; syntaxConfig.atrules.while = { prelude: '<any-value>' }; // extend parser's at-rule preluder parser const originalAttrulePreludeGetNode = syntaxConfig.scope.AtrulePrelude.getNode; syntaxConfig.scope.AtrulePrelude.getNode = function(context) { throwOnSyntaxExtension.call(this); return originalAttrulePreludeGetNode.call(this, context); }; // extend parser's value parser const originalValueGetNode = syntaxConfig.scope.Value.getNode; syntaxConfig.scope.Value.getNode = function(context) { throwOnSyntaxExtension.call(this); return originalValueGetNode.call(this, context); }; return syntaxConfig; } module.exports = sass;