Skip to content

Commit

Permalink
refactor(compiler): expose utility for creating CSS selectors from AS…
Browse files Browse the repository at this point in the history
…T nodes (angular#52726)

When doing directive matching in the compiler, we need to be able to create a selector from an AST node. We already have the utility, but these changes simplify the public API and expose it so it can be used in `compiler-cli`.

PR Close angular#52726
  • Loading branch information
crisbeto authored and rlmestre committed Jan 26, 2024
1 parent b08cf2f commit 3f40d47
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 32 deletions.
1 change: 1 addition & 0 deletions packages/compiler/src/compiler.ts
Expand Up @@ -67,6 +67,7 @@ export * from './render3/view/api';
export {BoundAttribute as TmplAstBoundAttribute, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, Element as TmplAstElement, Icu as TmplAstIcu, Node as TmplAstNode, RecursiveVisitor as TmplAstRecursiveVisitor, Reference as TmplAstReference, Template as TmplAstTemplate, Text as TmplAstText, TextAttribute as TmplAstTextAttribute, Variable as TmplAstVariable, DeferredBlock as TmplAstDeferredBlock, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockError as TmplAstDeferredBlockError, DeferredTrigger as TmplAstDeferredTrigger, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, DeferredBlockTriggers as TmplAstDeferredBlockTriggers, UnknownBlock as TmplAstUnknownBlock} from './render3/r3_ast';
export * from './render3/view/t2_api';
export * from './render3/view/t2_binder';
export {createCssSelectorFromNode} from './render3/view/util';
export {Identifiers as R3Identifiers} from './render3/r3_identifiers';
export {R3ClassMetadata, CompileClassMetadataFn, compileClassMetadata, compileComponentClassMetadata} from './render3/r3_class_metadata_compiler';
export {compileClassDebugInfo, R3ClassDebugInfo} from './render3/r3_class_debug_info_compiler';
Expand Down
11 changes: 5 additions & 6 deletions packages/compiler/src/render3/view/t2_binder.ts
Expand Up @@ -11,8 +11,7 @@ import {SelectorMatcher} from '../../selector';
import {BoundAttribute, BoundEvent, BoundText, Comment, Content, DeferredBlock, DeferredBlockError, DeferredBlockLoading, DeferredBlockPlaceholder, DeferredTrigger, Element, ForLoopBlock, ForLoopBlockEmpty, HoverDeferredTrigger, Icu, IfBlock, IfBlockBranch, InteractionDeferredTrigger, Node, Reference, SwitchBlock, SwitchBlockCase, Template, Text, TextAttribute, UnknownBlock, Variable, ViewportDeferredTrigger, Visitor} from '../r3_ast';

import {BoundTarget, DirectiveMeta, ReferenceTarget, ScopedNode, Target, TargetBinder} from './t2_api';
import {createCssSelector} from './template';
import {getAttrsForDirectiveMatching} from './util';
import {createCssSelectorFromNode} from './util';

/**
* Processes `Target`s with a given set of directives and performs a binding operation, which
Expand Down Expand Up @@ -307,17 +306,17 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
}

visitElement(element: Element): void {
this.visitElementOrTemplate(element.name, element);
this.visitElementOrTemplate(element);
}

visitTemplate(template: Template): void {
this.visitElementOrTemplate('ng-template', template);
this.visitElementOrTemplate(template);
}

visitElementOrTemplate(elementName: string, node: Element|Template): void {
visitElementOrTemplate(node: Element|Template): void {
// First, determine the HTML shape of the node for the purpose of directive matching.
// Do this by building up a `CssSelector` for the node.
const cssSelector = createCssSelector(elementName, getAttrsForDirectiveMatching(node));
const cssSelector = createCssSelectorFromNode(node);

// Next, use the `SelectorMatcher` to get the list of directives on the node.
const directives: DirectiveT[] = [];
Expand Down
24 changes: 0 additions & 24 deletions packages/compiler/src/render3/view/template.ts
Expand Up @@ -2570,30 +2570,6 @@ class TrackByBindingScope extends BindingScope {
}
}

/**
* Creates a `CssSelector` given a tag name and a map of attributes
*/
export function createCssSelector(
elementName: string, attributes: {[name: string]: string}): CssSelector {
const cssSelector = new CssSelector();
const elementNameNoNs = splitNsName(elementName)[1];

cssSelector.setElement(elementNameNoNs);

Object.getOwnPropertyNames(attributes).forEach((name) => {
const nameNoNs = splitNsName(name)[1];
const value = attributes[name];

cssSelector.addAttribute(nameNoNs, value);
if (name.toLowerCase() === 'class') {
const classes = value.trim().split(/\s+/);
classes.forEach(className => cssSelector.addClassName(className));
}
});

return cssSelector;
}

/**
* Creates an array of expressions out of an `ngProjectAs` attributes
* which can be added to the instruction parameters.
Expand Down
30 changes: 28 additions & 2 deletions packages/compiler/src/render3/view/util.ts
Expand Up @@ -8,8 +8,10 @@

import {ConstantPool} from '../../constant_pool';
import {BindingType, Interpolation} from '../../expression_parser/ast';
import {splitNsName} from '../../ml_parser/tags';
import * as o from '../../output/output_ast';
import {ParseSourceSpan} from '../../parse_util';
import {CssSelector} from '../../selector';
import * as t from '../r3_ast';
import {Identifiers as R3} from '../r3_identifiers';
import {ForwardRefHandling} from '../util';
Expand Down Expand Up @@ -277,6 +279,31 @@ export class DefinitionMap<T = any> {
}
}

/**
* Creates a `CssSelector` from an AST node.
*/
export function createCssSelectorFromNode(node: t.Element|t.Template): CssSelector {
const elementName = node instanceof t.Element ? node.name : 'ng-template';
const attributes = getAttrsForDirectiveMatching(node);
const cssSelector = new CssSelector();
const elementNameNoNs = splitNsName(elementName)[1];

cssSelector.setElement(elementNameNoNs);

Object.getOwnPropertyNames(attributes).forEach((name) => {
const nameNoNs = splitNsName(name)[1];
const value = attributes[name];

cssSelector.addAttribute(nameNoNs, value);
if (name.toLowerCase() === 'class') {
const classes = value.trim().split(/\s+/);
classes.forEach(className => cssSelector.addClassName(className));
}
});

return cssSelector;
}

/**
* Extract a map of properties to values for a given element or template node, which can be used
* by the directive matching machinery.
Expand All @@ -286,8 +313,7 @@ export class DefinitionMap<T = any> {
* object maps a property name to its (static) value. For any bindings, this map simply maps the
* property name to an empty string.
*/
export function getAttrsForDirectiveMatching(elOrTpl: t.Element|
t.Template): {[name: string]: string} {
function getAttrsForDirectiveMatching(elOrTpl: t.Element|t.Template): {[name: string]: string} {
const attributesMap: {[name: string]: string} = {};


Expand Down

0 comments on commit 3f40d47

Please sign in to comment.