'use strict' ; import { trim } from "../utils/strings" ; export enum NodeType { Undefined , Identifier , Stylesheet , Ruleset , Selector , SimpleSelector , SelectorInterpolation , SelectorCombinator , SelectorCombinatorParent , SelectorCombinatorSibling , SelectorCombinatorAllSiblings , SelectorCombinatorShadowPiercingDescendant , Page , PageBoxMarginBox , ClassSelector , IdentifierSelector , ElementNameSelector , PseudoSelector , AttributeSelector , Declaration , Declarations , Property , Expression , BinaryExpression , Term , Operator , Value , StringLiteral , URILiteral , EscapedValue , Function , NumericValue , HexColorValue , RatioValue , MixinDeclaration , MixinReference , VariableName , VariableDeclaration , Prio , Interpolation , NestedProperties , ExtendsReference , SelectorPlaceholder , Debug , If , Else , For , Each , While , MixinContentReference , MixinContentDeclaration , Media , Keyframe , FontFace , Import , Namespace , Invocation , FunctionDeclaration , ReturnStatement , MediaQuery , MediaCondition , MediaFeature , FunctionParameter , FunctionArgument , KeyframeSelector , ViewPort , Document , AtApplyRule , CustomPropertyDeclaration , CustomPropertySet , ListEntry , Supports , SupportsCondition , NamespacePrefix , GridLine , Plugin , UnknownAtRule , Use , ModuleConfiguration , Forward , ForwardVisibility , Module } export enum ReferenceType { Mixin , Rule , Variable , Function , Keyframe , Unknown , Module , Forward , ForwardVisibility } export function getNodeAtOffset ( node , offset ) { let candidate = null ; if ( ! node || offset < node . offset || offset > node . end ) { return null ; } node . accept ( ( node ) => { if ( node . offset === - 1 && node . length === - 1 ) { return true ; } if ( node . offset <= offset && node . end >= offset ) { if ( ! candidate ) { candidate = node ; } else if ( node . length <= candidate . length ) { candidate = node ; } return true ; } return false ; } ) ; return candidate ; } export function getNodePath ( node , offset ) { let candidate = getNodeAtOffset ( node , offset ) ; const path = [ ] ; while ( candidate ) { path . unshift ( candidate ) ; candidate = candidate . parent ; } return path ; } export function getParentDeclaration ( node ) { const decl = < Declaration > node . findParent ( NodeType . Declaration ) ; const value = decl && decl . getValue ( ) ; if ( value && value . encloses ( node ) ) { return decl ; } return null ; } export interface ITextProvider { ( offset , length ) : string ; } export class Node { public parent : Node | null ; public offset : number ; public length : number ; public get end ( ) { return this . offset + this . length ; } public options : { [ name ] : any ; } | undefined ; public textProvider : ITextProvider | undefined ; private children : Node [ ] | undefined ; private issues : IMarker [ ] | undefined ; private nodeType : NodeType | undefined ; constructor ( offset = - 1 , len = - 1 , nodeType ? ) { this . parent = null ; this . offset = offset ; this . length = len ; if ( nodeType ) { this . nodeType = nodeType ; } } public set type ( type ) { this . nodeType = type ; } public get type ( ) : NodeType { return this . nodeType || NodeType . Undefined ; } private getTextProvider ( ) { let node = this ; while ( node && ! node . textProvider ) { node = node . parent ; } if ( node ) { return node . textProvider ! ; } return ( ) => { return 'unknown' ; } ; } public getText ( ) { return this . getTextProvider ( ) ( this . offset , this . length ) ; } public matches ( str ) { return this . length === str . length && this . getTextProvider ( ) ( this . offset , this . length ) === str ; } public startsWith ( str ) { return this . length >= str . length && this . getTextProvider ( ) ( this . offset , str . length ) === str ; } public endsWith ( str ) { return this . length >= str . length && this . getTextProvider ( ) ( this . end - str . length , str . length ) === str ; } public accept ( visitor ) { if ( visitor ( this ) && this . children ) { for ( const child of this . children ) { child . accept ( visitor ) ; } } } public acceptVisitor ( visitor ) { this . accept ( visitor . visitNode . bind ( visitor ) ) ; } public adoptChild ( node , index = - 1 ) { if ( node . parent && node . parent . children ) { const idx = node . parent . children . indexOf ( node ) ; if ( idx >= 0 ) { node . parent . children . splice ( idx , 1 ) ; } } node . parent = this ; let children = this . children ; if ( ! children ) { children = this . children = [ ] ; } if ( index !== - 1 ) { children . splice ( index , 0 , node ) ; } else { children . push ( node ) ; } return node ; } public attachTo ( parent , index = - 1 ) { if ( parent ) { parent . adoptChild ( this , index ) ; } return this ; } public collectIssues ( results ) { if ( this . issues ) { results . push . apply ( results , this . issues ) ; } } public addIssue ( issue ) { if ( ! this . issues ) { this . issues = [ ] ; } this . issues . push ( issue ) ; } public hasIssue ( rule ) { return Array . isArray ( this . issues ) && this . issues . some ( i => i . getRule ( ) === rule ) ; } public isErroneous ( recursive = false ) { if ( this . issues && this . issues . length > 0 ) { return true ; } return recursive && Array . isArray ( this . children ) && this . children . some ( c => c . isErroneous ( true ) ) ; } public setNode ( field , node , index = - 1 ) { if ( node ) { node . attachTo ( this , index ) ; ( < any > this ) [ field ] = node ; return true ; } return false ; } public addChild ( node ) { if ( node ) { if ( ! this . children ) { this . children = [ ] ; } node . attachTo ( this ) ; this . updateOffsetAndLength ( node ) ; return true ; } return false ; } private updateOffsetAndLength ( node ) { if ( node . offset < this . offset || this . offset === - 1 ) { this . offset = node . offset ; } const nodeEnd = node . end ; if ( ( nodeEnd > this . end ) || this . length === - 1 ) { this . length = nodeEnd - this . offset ; } } public hasChildren ( ) { return ! ! this . children && this . children . length > 0 ; } public getChildren ( ) { return this . children ? this . children . slice ( 0 ) : [ ] ; } public getChild ( index ) { if ( this . children && index < this . children . length ) { return this . children [ index ] ; } return null ; } public addChildren ( nodes ) { for ( const node of nodes ) { this . addChild ( node ) ; } } public findFirstChildBeforeOffset ( offset ) { if ( this . children ) { let current = null ; for ( let i = this . children . length - 1 ; i >= 0 ; i -- ) { current = this . children [ i ] ; if ( current . offset <= offset ) { return current ; } } } return null ; } public findChildAtOffset ( offset , goDeep ) { const current = this . findFirstChildBeforeOffset ( offset ) ; if ( current && current . end >= offset ) { if ( goDeep ) { return current . findChildAtOffset ( offset , true ) || current ; } return current ; } return null ; } public encloses ( candidate ) { return this . offset <= candidate . offset && this . offset + this . length >= candidate . offset + candidate . length ; } public getParent ( ) { let result = this . parent ; while ( result instanceof Nodelist ) { result = result . parent ; } return result ; } public findParent ( type ) { let result = this ; while ( result && result . type !== type ) { result = result . parent ; } return result ; } public findAParent ( ... types ) { let result = this ; while ( result && ! types . some ( t => result ! . type === t ) ) { result = result . parent ; } return result ; } public setData ( key , value ) { if ( ! this . options ) { this . options = { } ; } this . options [ key ] = value ; } public getData ( key ) { if ( ! this . options || ! this . options . hasOwnProperty ( key ) ) { return null ; } return this . options [ key ] ; } } export interface NodeConstructor < T > { new ( offset , len ) : T ; } export class Nodelist extends Node { private _nodeList ! : void ; constructor ( parent , index = - 1 ) { super ( - 1 , - 1 ) ; this . attachTo ( parent , index ) ; this . offset = - 1 ; this . length = - 1 ; } } export class Identifier extends Node { public referenceTypes ? : ReferenceType [ ] ; public isCustomProperty = false ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Identifier ; } public containsInterpolation ( ) { return this . hasChildren ( ) ; } } export class Stylesheet extends Node { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Stylesheet ; } } export class Declarations extends Node { private _declarations ! : void ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Declarations ; } } export class BodyDeclaration extends Node { public declarations ? : Declarations ; constructor ( offset , length ) { super ( offset , length ) ; } public getDeclarations ( ) { return this . declarations ; } public setDeclarations ( decls ) { return this . setNode ( 'declarations' , decls ) ; } } export class RuleSet extends BodyDeclaration { private selectors ? : Nodelist ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Ruleset ; } public getSelectors ( ) { if ( ! this . selectors ) { this . selectors = new Nodelist ( this ) ; } return this . selectors ; } public isNested ( ) { return ! ! this . parent && this . parent . findParent ( NodeType . Declarations ) !== null ; } } export class Selector extends Node { private _selector ! : void ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Selector ; } } export class SimpleSelector extends Node { private _simpleSelector ! : void ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . SimpleSelector ; } } export class AtApplyRule extends Node { public identifier ? : Identifier ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . AtApplyRule ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } public getName ( ) { return this . identifier ? this . identifier . getText ( ) : '' ; } } export abstract class AbstractDeclaration extends Node { public colonPosition : number | undefined ; public semicolonPosition : number | undefined ; constructor ( offset , length ) { super ( offset , length ) ; } } export class CustomPropertySet extends BodyDeclaration { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . CustomPropertySet ; } } export class Declaration extends AbstractDeclaration { public property : Property | null = null ; public value ? : Expression ; public nestedProperties ? : NestedProperties ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Declaration ; } public setProperty ( node ) { return this . setNode ( 'property' , node ) ; } public getProperty ( ) { return this . property ; } public getFullPropertyName ( ) { const propertyName = this . property ? this . property . getName ( ) : 'unknown' ; if ( this . parent instanceof Declarations && this . parent . getParent ( ) instanceof NestedProperties ) { const parentDecl = this . parent . getParent ( ) ! . getParent ( ) ; if ( parentDecl instanceof Declaration ) { return ( < Declaration > parentDecl ) . getFullPropertyName ( ) + propertyName ; } } return propertyName ; } public getNonPrefixedPropertyName ( ) { const propertyName = this . getFullPropertyName ( ) ; if ( propertyName && propertyName . charAt ( 0 ) === '-' ) { const vendorPrefixEnd = propertyName . indexOf ( '-' , 1 ) ; if ( vendorPrefixEnd !== - 1 ) { return propertyName . substring ( vendorPrefixEnd + 1 ) ; } } return propertyName ; } public setValue ( value ) { return this . setNode ( 'value' , value ) ; } public getValue ( ) { return this . value ; } public setNestedProperties ( value ) { return this . setNode ( 'nestedProperties' , value ) ; } public getNestedProperties ( ) { return this . nestedProperties ; } } export class CustomPropertyDeclaration extends Declaration { public propertySet ? : CustomPropertySet ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . CustomPropertyDeclaration ; } public setPropertySet ( value ) { return this . setNode ( 'propertySet' , value ) ; } public getPropertySet ( ) { return this . propertySet ; } } export class Property extends Node { public identifier ? : Identifier ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Property ; } public setIdentifier ( value ) { return this . setNode ( 'identifier' , value ) ; } public getIdentifier ( ) { return this . identifier ; } public getName ( ) { return trim ( this . getText ( ) , /[_\+]+$/ ) ; } public isCustomProperty ( ) { return ! ! this . identifier && this . identifier . isCustomProperty ; } } export class Invocation extends Node { private arguments ? : Nodelist ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Invocation ; } public getArguments ( ) { if ( ! this . arguments ) { this . arguments = new Nodelist ( this ) ; } return this . arguments ; } } export class Function extends Invocation { public identifier ? : Identifier ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Function ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } public getName ( ) { return this . identifier ? this . identifier . getText ( ) : '' ; } } export class FunctionParameter extends Node { public identifier ? : Node ; public defaultValue ? : Node ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . FunctionParameter ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } public getName ( ) { return this . identifier ? this . identifier . getText ( ) : '' ; } public setDefaultValue ( node ) { return this . setNode ( 'defaultValue' , node , 0 ) ; } public getDefaultValue ( ) { return this . defaultValue ; } } export class FunctionArgument extends Node { public identifier ? : Node ; public value ? : Node ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . FunctionArgument ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } public getName ( ) { return this . identifier ? this . identifier . getText ( ) : '' ; } public setValue ( node ) { return this . setNode ( 'value' , node , 0 ) ; } public getValue ( ) { return this . value ; } } export class IfStatement extends BodyDeclaration { public expression ? : Expression ; public elseClause ? : BodyDeclaration ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . If ; } public setExpression ( node ) { return this . setNode ( 'expression' , node , 0 ) ; } public setElseClause ( elseClause ) { return this . setNode ( 'elseClause' , elseClause ) ; } } export class ForStatement extends BodyDeclaration { public variable ? : Variable ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . For ; } public setVariable ( node ) { return this . setNode ( 'variable' , node , 0 ) ; } } export class EachStatement extends BodyDeclaration { public variables ? : Nodelist ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Each ; } public getVariables ( ) { if ( ! this . variables ) { this . variables = new Nodelist ( this ) ; } return this . variables ; } } export class WhileStatement extends BodyDeclaration { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . While ; } } export class ElseStatement extends BodyDeclaration { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Else ; } } export class FunctionDeclaration extends BodyDeclaration { public identifier ? : Identifier ; public parameters ? : Nodelist ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . FunctionDeclaration ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } public getName ( ) { return this . identifier ? this . identifier . getText ( ) : '' ; } public getParameters ( ) { if ( ! this . parameters ) { this . parameters = new Nodelist ( this ) ; } return this . parameters ; } } export class ViewPort extends BodyDeclaration { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . ViewPort ; } } export class FontFace extends BodyDeclaration { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . FontFace ; } } export class NestedProperties extends BodyDeclaration { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . NestedProperties ; } } export class Keyframe extends BodyDeclaration { public keyword ? : Node ; public identifier ? : Identifier ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Keyframe ; } public setKeyword ( keyword ) { return this . setNode ( 'keyword' , keyword , 0 ) ; } public getKeyword ( ) { return this . keyword ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } public getName ( ) { return this . identifier ? this . identifier . getText ( ) : '' ; } } export class KeyframeSelector extends BodyDeclaration { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . KeyframeSelector ; } } export class Import extends Node { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Import ; } public setMedialist ( node ) { if ( node ) { node . attachTo ( this ) ; return true ; } return false ; } } export class Use extends Node { public identifier ? : Identifier ; public parameters ? : Nodelist ; public get type ( ) : NodeType { return NodeType . Use ; } public getParameters ( ) { if ( ! this . parameters ) { this . parameters = new Nodelist ( this ) ; } return this . parameters ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } } export class ModuleConfiguration extends Node { public identifier ? : Node ; public value ? : Node ; public get type ( ) : NodeType { return NodeType . ModuleConfiguration ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } public getName ( ) { return this . identifier ? this . identifier . getText ( ) : '' ; } public setValue ( node ) { return this . setNode ( 'value' , node , 0 ) ; } public getValue ( ) { return this . value ; } } export class Forward extends Node { public identifier ? : Node ; public members ? : Nodelist ; public parameters ? : Nodelist ; public get type ( ) : NodeType { return NodeType . Forward ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } public getMembers ( ) { if ( ! this . members ) { this . members = new Nodelist ( this ) ; } return this . members ; } public getParameters ( ) { if ( ! this . parameters ) { this . parameters = new Nodelist ( this ) ; } return this . parameters ; } } export class ForwardVisibility extends Node { public identifier ? : Node ; public get type ( ) : NodeType { return NodeType . ForwardVisibility ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } } export class Namespace extends Node { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Namespace ; } } export class Media extends BodyDeclaration { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Media ; } } export class Supports extends BodyDeclaration { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Supports ; } } export class Document extends BodyDeclaration { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Document ; } } export class Medialist extends Node { private mediums ? : Nodelist ; constructor ( offset , length ) { super ( offset , length ) ; } public getMediums ( ) { if ( ! this . mediums ) { this . mediums = new Nodelist ( this ) ; } return this . mediums ; } } export class MediaQuery extends Node { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . MediaQuery ; } } export class MediaCondition extends Node { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . MediaCondition ; } } export class MediaFeature extends Node { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . MediaFeature ; } } export class SupportsCondition extends Node { public lParent ? : number ; public rParent ? : number ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . SupportsCondition ; } } export class Page extends BodyDeclaration { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Page ; } } export class PageBoxMarginBox extends BodyDeclaration { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . PageBoxMarginBox ; } } export class Expression extends Node { private _expression ! : void ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Expression ; } } export class BinaryExpression extends Node { public left ? : Node ; public right ? : Node ; public operator ? : Node ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . BinaryExpression ; } public setLeft ( left ) { return this . setNode ( 'left' , left ) ; } public getLeft ( ) { return this . left ; } public setRight ( right ) { return this . setNode ( 'right' , right ) ; } public getRight ( ) { return this . right ; } public setOperator ( value ) { return this . setNode ( 'operator' , value ) ; } public getOperator ( ) { return this . operator ; } } export class Term extends Node { public operator ? : Node ; public expression ? : Node ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Term ; } public setOperator ( value ) { return this . setNode ( 'operator' , value ) ; } public getOperator ( ) { return this . operator ; } public setExpression ( value ) { return this . setNode ( 'expression' , value ) ; } public getExpression ( ) { return this . expression ; } } export class AttributeSelector extends Node { public namespacePrefix ? : Node ; public identifier ? : Identifier ; public operator ? : Operator ; public value ? : BinaryExpression ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . AttributeSelector ; } public setNamespacePrefix ( value ) { return this . setNode ( 'namespacePrefix' , value ) ; } public getNamespacePrefix ( ) { return this . namespacePrefix ; } public setIdentifier ( value ) { return this . setNode ( 'identifier' , value ) ; } public getIdentifier ( ) { return this . identifier ; } public setOperator ( operator ) { return this . setNode ( 'operator' , operator ) ; } public getOperator ( ) { return this . operator ; } public setValue ( value ) { return this . setNode ( 'value' , value ) ; } public getValue ( ) { return this . value ; } } export class Operator extends Node { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Operator ; } } export class HexColorValue extends Node { private _hexColorValue ! : void ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . HexColorValue ; } } export class RatioValue extends Node { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . RatioValue ; } } const _dot = '.' . charCodeAt ( 0 ) , _0 = '0' . charCodeAt ( 0 ) , _9 = '9' . charCodeAt ( 0 ) ; export class NumericValue extends Node { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . NumericValue ; } public getValue ( ) { const raw = this . getText ( ) ; let unitIdx = 0 ; let code ; for ( let i = 0 , len = raw . length ; i < len ; i ++ ) { code = raw . charCodeAt ( i ) ; if ( ! ( _0 <= code && code <= _9 || code === _dot ) ) { break ; } unitIdx += 1 ; } return { value : raw . substring ( 0 , unitIdx ) , unit : unitIdx < raw . length ? raw . substring ( unitIdx ) : undefined } ; } } export class VariableDeclaration extends AbstractDeclaration { private variable : Variable | null = null ; private value : Node | null = null ; public needsSemicolon : boolean = true ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . VariableDeclaration ; } public setVariable ( node ) { if ( node ) { node . attachTo ( this ) ; this . variable = node ; return true ; } return false ; } public getVariable ( ) { return this . variable ; } public getName ( ) { return this . variable ? this . variable . getName ( ) : '' ; } public setValue ( node ) { if ( node ) { node . attachTo ( this ) ; this . value = node ; return true ; } return false ; } public getValue ( ) { return this . value ; } } export class Interpolation extends Node { constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . Interpolation ; } } export class Variable extends Node { public module ? : Module ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . VariableName ; } public getName ( ) { return this . getText ( ) ; } } export class ExtendsReference extends Node { private selectors ? : Nodelist ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . ExtendsReference ; } public getSelectors ( ) { if ( ! this . selectors ) { this . selectors = new Nodelist ( this ) ; } return this . selectors ; } } export class MixinContentReference extends Node { private arguments ? : Nodelist ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . MixinContentReference ; } public getArguments ( ) { if ( ! this . arguments ) { this . arguments = new Nodelist ( this ) ; } return this . arguments ; } } export class MixinContentDeclaration extends BodyDeclaration { private parameters ? : Nodelist ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . MixinContentReference ; } public getParameters ( ) { if ( ! this . parameters ) { this . parameters = new Nodelist ( this ) ; } return this . parameters ; } } export class MixinReference extends Node { public namespaces ? : Nodelist ; public identifier ? : Identifier ; private arguments ? : Nodelist ; public content ? : MixinContentDeclaration ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . MixinReference ; } public getNamespaces ( ) { if ( ! this . namespaces ) { this . namespaces = new Nodelist ( this ) ; } return this . namespaces ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } public getName ( ) { return this . identifier ? this . identifier . getText ( ) : '' ; } public getArguments ( ) { if ( ! this . arguments ) { this . arguments = new Nodelist ( this ) ; } return this . arguments ; } public setContent ( node ) { return this . setNode ( 'content' , node ) ; } public getContent ( ) { return this . content ; } } export class MixinDeclaration extends BodyDeclaration { public identifier ? : Identifier ; private parameters ? : Nodelist ; private guard ? : LessGuard ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . MixinDeclaration ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } public getName ( ) { return this . identifier ? this . identifier . getText ( ) : '' ; } public getParameters ( ) { if ( ! this . parameters ) { this . parameters = new Nodelist ( this ) ; } return this . parameters ; } public setGuard ( node ) { if ( node ) { node . attachTo ( this ) ; this . guard = node ; } return false ; } } export class UnknownAtRule extends BodyDeclaration { public atRuleName ? : string ; constructor ( offset , length ) { super ( offset , length ) ; } public get type ( ) : NodeType { return NodeType . UnknownAtRule ; } public setAtRuleName ( atRuleName ) { this . atRuleName = atRuleName ; } public getAtRuleName ( ) { return this . atRuleName ; } } export class ListEntry extends Node { public key ? : Node ; public value ? : Node ; public get type ( ) : NodeType { return NodeType . ListEntry ; } public setKey ( node ) { return this . setNode ( 'key' , node , 0 ) ; } public setValue ( node ) { return this . setNode ( 'value' , node , 1 ) ; } } export class LessGuard extends Node { public isNegated ? : boolean ; private conditions ? : Nodelist ; public getConditions ( ) { if ( ! this . conditions ) { this . conditions = new Nodelist ( this ) ; } return this . conditions ; } } export class GuardCondition extends Node { public variable ? : Node ; public isEquals ? : boolean ; public isGreater ? : boolean ; public isEqualsGreater ? : boolean ; public isLess ? : boolean ; public isEqualsLess ? : boolean ; public setVariable ( node ) { return this . setNode ( 'variable' , node ) ; } } export class Module extends Node { public identifier ? : Identifier ; public get type ( ) : NodeType { return NodeType . Module ; } public setIdentifier ( node ) { return this . setNode ( 'identifier' , node , 0 ) ; } public getIdentifier ( ) { return this . identifier ; } } export interface IRule { id : string ; message : string ; } export enum Level { Ignore = 1 , Warning = 2 , Error = 4 } export interface IMarker { getNode ( ) : Node ; getMessage ( ) : string ; getOffset ( ) : number ; getLength ( ) : number ; getRule ( ) : IRule ; getLevel ( ) : Level ; } export class Marker implements IMarker { private node : Node ; private rule : IRule ; private level : Level ; private message : string ; private offset : number ; private length : number ; constructor ( node , rule , level , message ? , offset = node . offset , length = node . length ) { this . node = node ; this . rule = rule ; this . level = level ; this . message = message || rule . message ; this . offset = offset ; this . length = length ; } public getRule ( ) { return this . rule ; } public getLevel ( ) { return this . level ; } public getOffset ( ) { return this . offset ; } public getLength ( ) { return this . length ; } public getNode ( ) { return this . node ; } public getMessage ( ) { return this . message ; } } export interface IVisitor { visitNode : ( node : Node ) => boolean ; } export interface IVisitorFunction { ( node ) : boolean ; } export class ParseErrorCollector implements IVisitor { static entries ( node ) { const visitor = new ParseErrorCollector ( ) ; node . acceptVisitor ( visitor ) ; return visitor . entries ; } public entries : IMarker [ ] ; constructor ( ) { this . entries = [ ] ; } public visitNode ( node ) { if ( node . isErroneous ( ) ) { node . collectIssues ( this . entries ) ; } return true ; } }