249 lines
8.5 KiB
TypeScript
249 lines
8.5 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import {ParseLocation, ParseSourceSpan} from '../parse_util';
|
|
|
|
import {CssToken, CssTokenType} from './css_lexer';
|
|
|
|
export enum BlockType {
|
|
Import,
|
|
Charset,
|
|
Namespace,
|
|
Supports,
|
|
Keyframes,
|
|
MediaQuery,
|
|
Selector,
|
|
FontFace,
|
|
Page,
|
|
Document,
|
|
Viewport,
|
|
Unsupported
|
|
}
|
|
|
|
export interface CssAstVisitor {
|
|
visitCssValue(ast: CssStyleValueAst, context?: any): any;
|
|
visitCssInlineRule(ast: CssInlineRuleAst, context?: any): any;
|
|
visitCssAtRulePredicate(ast: CssAtRulePredicateAst, context?: any): any;
|
|
visitCssKeyframeRule(ast: CssKeyframeRuleAst, context?: any): any;
|
|
visitCssKeyframeDefinition(ast: CssKeyframeDefinitionAst, context?: any): any;
|
|
visitCssMediaQueryRule(ast: CssMediaQueryRuleAst, context?: any): any;
|
|
visitCssSelectorRule(ast: CssSelectorRuleAst, context?: any): any;
|
|
visitCssSelector(ast: CssSelectorAst, context?: any): any;
|
|
visitCssSimpleSelector(ast: CssSimpleSelectorAst, context?: any): any;
|
|
visitCssPseudoSelector(ast: CssPseudoSelectorAst, context?: any): any;
|
|
visitCssDefinition(ast: CssDefinitionAst, context?: any): any;
|
|
visitCssBlock(ast: CssBlockAst, context?: any): any;
|
|
visitCssStylesBlock(ast: CssStylesBlockAst, context?: any): any;
|
|
visitCssStyleSheet(ast: CssStyleSheetAst, context?: any): any;
|
|
visitCssUnknownRule(ast: CssUnknownRuleAst, context?: any): any;
|
|
visitCssUnknownTokenList(ast: CssUnknownTokenListAst, context?: any): any;
|
|
}
|
|
|
|
export abstract class CssAst {
|
|
constructor(public location: ParseSourceSpan) {}
|
|
get start(): ParseLocation { return this.location.start; }
|
|
get end(): ParseLocation { return this.location.end; }
|
|
abstract visit(visitor: CssAstVisitor, context?: any): any;
|
|
}
|
|
|
|
export class CssStyleValueAst extends CssAst {
|
|
constructor(location: ParseSourceSpan, public tokens: CssToken[], public strValue: string) {
|
|
super(location);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any { return visitor.visitCssValue(this); }
|
|
}
|
|
|
|
export abstract class CssRuleAst extends CssAst {
|
|
constructor(location: ParseSourceSpan) { super(location); }
|
|
}
|
|
|
|
export class CssBlockRuleAst extends CssRuleAst {
|
|
constructor(
|
|
public location: ParseSourceSpan, public type: BlockType, public block: CssBlockAst,
|
|
public name: CssToken|null = null) {
|
|
super(location);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssBlock(this.block, context);
|
|
}
|
|
}
|
|
|
|
export class CssKeyframeRuleAst extends CssBlockRuleAst {
|
|
constructor(location: ParseSourceSpan, name: CssToken, block: CssBlockAst) {
|
|
super(location, BlockType.Keyframes, block, name);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssKeyframeRule(this, context);
|
|
}
|
|
}
|
|
|
|
export class CssKeyframeDefinitionAst extends CssBlockRuleAst {
|
|
constructor(location: ParseSourceSpan, public steps: CssToken[], block: CssBlockAst) {
|
|
super(location, BlockType.Keyframes, block, mergeTokens(steps, ','));
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssKeyframeDefinition(this, context);
|
|
}
|
|
}
|
|
|
|
export class CssBlockDefinitionRuleAst extends CssBlockRuleAst {
|
|
constructor(
|
|
location: ParseSourceSpan, public strValue: string, type: BlockType,
|
|
public query: CssAtRulePredicateAst, block: CssBlockAst) {
|
|
super(location, type, block);
|
|
const firstCssToken: CssToken = query.tokens[0];
|
|
this.name = new CssToken(
|
|
firstCssToken.index, firstCssToken.column, firstCssToken.line, CssTokenType.Identifier,
|
|
this.strValue);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssBlock(this.block, context);
|
|
}
|
|
}
|
|
|
|
export class CssMediaQueryRuleAst extends CssBlockDefinitionRuleAst {
|
|
constructor(
|
|
location: ParseSourceSpan, strValue: string, query: CssAtRulePredicateAst,
|
|
block: CssBlockAst) {
|
|
super(location, strValue, BlockType.MediaQuery, query, block);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssMediaQueryRule(this, context);
|
|
}
|
|
}
|
|
|
|
export class CssAtRulePredicateAst extends CssAst {
|
|
constructor(location: ParseSourceSpan, public strValue: string, public tokens: CssToken[]) {
|
|
super(location);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssAtRulePredicate(this, context);
|
|
}
|
|
}
|
|
|
|
export class CssInlineRuleAst extends CssRuleAst {
|
|
constructor(location: ParseSourceSpan, public type: BlockType, public value: CssStyleValueAst) {
|
|
super(location);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssInlineRule(this, context);
|
|
}
|
|
}
|
|
|
|
export class CssSelectorRuleAst extends CssBlockRuleAst {
|
|
public strValue: string;
|
|
|
|
constructor(location: ParseSourceSpan, public selectors: CssSelectorAst[], block: CssBlockAst) {
|
|
super(location, BlockType.Selector, block);
|
|
this.strValue = selectors.map(selector => selector.strValue).join(',');
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssSelectorRule(this, context);
|
|
}
|
|
}
|
|
|
|
export class CssDefinitionAst extends CssAst {
|
|
constructor(
|
|
location: ParseSourceSpan, public property: CssToken, public value: CssStyleValueAst) {
|
|
super(location);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssDefinition(this, context);
|
|
}
|
|
}
|
|
|
|
export abstract class CssSelectorPartAst extends CssAst {
|
|
constructor(location: ParseSourceSpan) { super(location); }
|
|
}
|
|
|
|
export class CssSelectorAst extends CssSelectorPartAst {
|
|
public strValue: string;
|
|
constructor(location: ParseSourceSpan, public selectorParts: CssSimpleSelectorAst[]) {
|
|
super(location);
|
|
this.strValue = selectorParts.map(part => part.strValue).join('');
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssSelector(this, context);
|
|
}
|
|
}
|
|
|
|
export class CssSimpleSelectorAst extends CssSelectorPartAst {
|
|
constructor(
|
|
location: ParseSourceSpan, public tokens: CssToken[], public strValue: string,
|
|
public pseudoSelectors: CssPseudoSelectorAst[], public operator: CssToken) {
|
|
super(location);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssSimpleSelector(this, context);
|
|
}
|
|
}
|
|
|
|
export class CssPseudoSelectorAst extends CssSelectorPartAst {
|
|
constructor(
|
|
location: ParseSourceSpan, public strValue: string, public name: string,
|
|
public tokens: CssToken[], public innerSelectors: CssSelectorAst[]) {
|
|
super(location);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssPseudoSelector(this, context);
|
|
}
|
|
}
|
|
|
|
export class CssBlockAst extends CssAst {
|
|
constructor(location: ParseSourceSpan, public entries: CssAst[]) { super(location); }
|
|
visit(visitor: CssAstVisitor, context?: any): any { return visitor.visitCssBlock(this, context); }
|
|
}
|
|
|
|
/*
|
|
a style block is different from a standard block because it contains
|
|
css prop:value definitions. A regular block can contain a list of Ast entries.
|
|
*/
|
|
export class CssStylesBlockAst extends CssBlockAst {
|
|
constructor(location: ParseSourceSpan, public definitions: CssDefinitionAst[]) {
|
|
super(location, definitions);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssStylesBlock(this, context);
|
|
}
|
|
}
|
|
|
|
export class CssStyleSheetAst extends CssAst {
|
|
constructor(location: ParseSourceSpan, public rules: CssAst[]) { super(location); }
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssStyleSheet(this, context);
|
|
}
|
|
}
|
|
|
|
export class CssUnknownRuleAst extends CssRuleAst {
|
|
constructor(location: ParseSourceSpan, public ruleName: string, public tokens: CssToken[]) {
|
|
super(location);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssUnknownRule(this, context);
|
|
}
|
|
}
|
|
|
|
export class CssUnknownTokenListAst extends CssRuleAst {
|
|
constructor(location: ParseSourceSpan, public name: string, public tokens: CssToken[]) {
|
|
super(location);
|
|
}
|
|
visit(visitor: CssAstVisitor, context?: any): any {
|
|
return visitor.visitCssUnknownTokenList(this, context);
|
|
}
|
|
}
|
|
|
|
export function mergeTokens(tokens: CssToken[], separator: string = ''): CssToken {
|
|
const mainToken = tokens[0];
|
|
let str = mainToken.strValue;
|
|
for (let i = 1; i < tokens.length; i++) {
|
|
str += separator + tokens[i].strValue;
|
|
}
|
|
|
|
return new CssToken(mainToken.index, mainToken.column, mainToken.line, mainToken.type, str);
|
|
}
|