diff --git a/README.md b/README.md index 1c36c3a..97ce78c 100644 --- a/README.md +++ b/README.md @@ -215,14 +215,14 @@ for (const token of tokenize('a { color: red }')) { ### Node types, type guards and `clone()` -Node type constants (`STYLE_RULE`, `DECLARATION`, `DIMENSION`, …), `TYPE_NAMES` and `NODE_TYPES` are exported from the root, as are type guards such as `is_declaration()` and `is_dimension()` that narrow a node to its specific type. `node.clone({ deep?, locations? })` returns a mutable plain-object copy of a node. +Node type constants (`STYLE_RULE`, `DECLARATION`, `DIMENSION`, …) and `NODE_TYPES` are exported from the root, as are type guards such as `is_declaration()` and `is_dimension()` that narrow a node to its specific type. `node.clone({ deep?, locations? })` returns a mutable plain-object copy of a node. ```typescript -import { parse, is_declaration, TYPE_NAMES, DECLARATION } from '@projectwallace/css-parser' +import { parse, is_declaration } from '@projectwallace/css-parser' const decl = parse('a { color: red }').first_child.block.first_child is_declaration(decl) // true -TYPE_NAMES[DECLARATION] // "Declaration" +decl.type_name // "Declaration" decl.clone({ deep: false }).property // "color" ``` diff --git a/src/css-node.ts b/src/css-node.ts index 6bb5a17..4f634c3 100644 --- a/src/css-node.ts +++ b/src/css-node.ts @@ -72,57 +72,54 @@ import { } from './string-utils' import { dimension_number_end } from './parse-dimension' -// Type name lookup table - maps numeric type to CSSTree-compatible strings -export const TYPE_NAMES = { - [STYLESHEET]: 'StyleSheet', - [STYLE_RULE]: 'Rule', - [AT_RULE]: 'Atrule', - [DECLARATION]: 'Declaration', - [SELECTOR]: 'Selector', - [COMMENT]: 'Comment', - [BLOCK]: 'Block', - [RAW]: 'Raw', - [IDENTIFIER]: 'Identifier', - [NUMBER]: 'Number', - [DIMENSION]: 'Dimension', - [STRING]: 'String', - [HASH]: 'Hash', - [FUNCTION]: 'Function', - [OPERATOR]: 'Operator', - [PARENTHESIS]: 'Parentheses', - [URL]: 'Url', - [UNICODE_RANGE]: 'UnicodeRange', - [VALUE]: 'Value', - [SELECTOR_LIST]: 'SelectorList', - [TYPE_SELECTOR]: 'TypeSelector', - [CLASS_SELECTOR]: 'ClassSelector', - [ID_SELECTOR]: 'IdSelector', - [ATTRIBUTE_SELECTOR]: 'AttributeSelector', - [PSEUDO_CLASS_SELECTOR]: 'PseudoClassSelector', - [PSEUDO_ELEMENT_SELECTOR]: 'PseudoElementSelector', - [COMBINATOR]: 'Combinator', - [UNIVERSAL_SELECTOR]: 'UniversalSelector', - [NESTING_SELECTOR]: 'NestingSelector', - [NTH_SELECTOR]: 'Nth', - [NTH_OF_SELECTOR]: 'NthOf', - [LANG_SELECTOR]: 'Lang', - [MEDIA_QUERY]: 'MediaQuery', - [MEDIA_FEATURE]: 'Feature', - [MEDIA_TYPE]: 'MediaType', - [CONTAINER_QUERY]: 'ContainerQuery', - [SUPPORTS_QUERY]: 'SupportsQuery', - [SUPPORTS_DECLARATION]: 'SupportsDeclaration', - [LAYER_NAME]: 'Layer', - [PRELUDE_OPERATOR]: 'Operator', - [FEATURE_RANGE]: 'MediaFeatureRange', - [AT_RULE_PRELUDE]: 'AtrulePrelude', - [PRELUDE_SELECTORLIST]: 'PreludeSelectorList', - [RATIO]: 'Ratio', - [IF_BRANCH]: 'IfBranch', - [IF_CONDITION]: 'IfCondition', -} as const - -export type TypeName = (typeof TYPE_NAMES)[keyof typeof TYPE_NAMES] | 'unknown' +// CSSTree-compatible node type names +export type TypeName = + | 'StyleSheet' + | 'Rule' + | 'Atrule' + | 'Declaration' + | 'Selector' + | 'Comment' + | 'Block' + | 'Raw' + | 'Identifier' + | 'Number' + | 'Dimension' + | 'String' + | 'Hash' + | 'Function' + | 'Operator' + | 'Parentheses' + | 'Url' + | 'UnicodeRange' + | 'Value' + | 'SelectorList' + | 'TypeSelector' + | 'ClassSelector' + | 'IdSelector' + | 'AttributeSelector' + | 'PseudoClassSelector' + | 'PseudoElementSelector' + | 'Combinator' + | 'UniversalSelector' + | 'NestingSelector' + | 'Nth' + | 'NthOf' + | 'Lang' + | 'MediaQuery' + | 'Feature' + | 'MediaType' + | 'ContainerQuery' + | 'SupportsQuery' + | 'SupportsDeclaration' + | 'Layer' + | 'MediaFeatureRange' + | 'AtrulePrelude' + | 'PreludeSelectorList' + | 'Ratio' + | 'IfBranch' + | 'IfCondition' + | 'unknown' // Node type constants (numeric for performance) export type CSSNodeType = @@ -327,7 +324,103 @@ export class CSSNode { /** Get node type as human-readable string */ get type_name(): TypeName { - return TYPE_NAMES[this.type] || 'unknown' + // A switch compiles to a jump table, which is faster than an object lookup + switch (this.type) { + case STYLESHEET: + return 'StyleSheet' + case STYLE_RULE: + return 'Rule' + case AT_RULE: + return 'Atrule' + case DECLARATION: + return 'Declaration' + case SELECTOR: + return 'Selector' + case COMMENT: + return 'Comment' + case BLOCK: + return 'Block' + case RAW: + return 'Raw' + case IDENTIFIER: + return 'Identifier' + case NUMBER: + return 'Number' + case DIMENSION: + return 'Dimension' + case STRING: + return 'String' + case HASH: + return 'Hash' + case FUNCTION: + return 'Function' + case OPERATOR: + return 'Operator' + case PARENTHESIS: + return 'Parentheses' + case URL: + return 'Url' + case UNICODE_RANGE: + return 'UnicodeRange' + case VALUE: + return 'Value' + case SELECTOR_LIST: + return 'SelectorList' + case TYPE_SELECTOR: + return 'TypeSelector' + case CLASS_SELECTOR: + return 'ClassSelector' + case ID_SELECTOR: + return 'IdSelector' + case ATTRIBUTE_SELECTOR: + return 'AttributeSelector' + case PSEUDO_CLASS_SELECTOR: + return 'PseudoClassSelector' + case PSEUDO_ELEMENT_SELECTOR: + return 'PseudoElementSelector' + case COMBINATOR: + return 'Combinator' + case UNIVERSAL_SELECTOR: + return 'UniversalSelector' + case NESTING_SELECTOR: + return 'NestingSelector' + case NTH_SELECTOR: + return 'Nth' + case NTH_OF_SELECTOR: + return 'NthOf' + case LANG_SELECTOR: + return 'Lang' + case MEDIA_QUERY: + return 'MediaQuery' + case MEDIA_FEATURE: + return 'Feature' + case MEDIA_TYPE: + return 'MediaType' + case CONTAINER_QUERY: + return 'ContainerQuery' + case SUPPORTS_QUERY: + return 'SupportsQuery' + case SUPPORTS_DECLARATION: + return 'SupportsDeclaration' + case LAYER_NAME: + return 'Layer' + case PRELUDE_OPERATOR: + return 'Operator' + case FEATURE_RANGE: + return 'MediaFeatureRange' + case AT_RULE_PRELUDE: + return 'AtrulePrelude' + case PRELUDE_SELECTORLIST: + return 'PreludeSelectorList' + case RATIO: + return 'Ratio' + case IF_BRANCH: + return 'IfBranch' + case IF_CONDITION: + return 'IfCondition' + default: + return 'unknown' + } } /** Get the full text of this node from source */ diff --git a/src/index.ts b/src/index.ts index 015420f..190bc6c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,7 +21,7 @@ export { export { type ParserOptions } from './parse' // Types -export { TYPE_NAMES, type CloneOptions, type PlainCSSNode } from './css-node' +export { type CloneOptions, type PlainCSSNode } from './css-node' export type { LexerPosition, CommentInfo } from './tokenize' // Constants - imported from dedicated constants file