mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
Improves the symbol linking logic to handle Angular component selectors (e.g., ngCombobox). It attempts to convert Angular selector patterns to their corresponding class names, improving navigation to Angular API documentation.
(cherry picked from commit 2d854e01bc)
70 lines
2.5 KiB
TypeScript
70 lines
2.5 KiB
TypeScript
/*!
|
|
* @license
|
|
* Copyright Google LLC 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.dev/license
|
|
*/
|
|
|
|
// In some case we know that we don't want to link a symbol
|
|
// Example when there is a conflict between API entries and compiler features.
|
|
// eg: "animate" is both an Animation API entry and an template instruction "animation.enter"
|
|
// or "style" is a generic term but also an Animation API entry.
|
|
const LINK_EXEMPT = new Set(['animate', 'animate.enter', 'animate.leave', 'style']);
|
|
|
|
export function shouldLinkSymbol(symbol: string): boolean {
|
|
return !LINK_EXEMPT.has(symbol);
|
|
}
|
|
|
|
// symbolName -> symbol info with moduleName and optional targetSymbol for aliases
|
|
export type ApiEntries = Record<string, {moduleName: string; targetSymbol?: string}>;
|
|
|
|
/**
|
|
* Extracts the symbol name and property name from a symbol string.
|
|
* eg:
|
|
* foobar() => {symbolName: 'foobar', propName: null}
|
|
* ApplicationRef.tick = > {symbolName: 'ApplicationRef', propName: 'tick'}
|
|
* ApplicationRef.tick() = > {symbolName: 'ApplicationRef', propName: 'tick'}
|
|
* @Component => {symbolName: 'Component', propName: null}
|
|
*/
|
|
export function extractFromSymbol(symbol: string): {propName: string | null; symbolName: string} {
|
|
let propName: string | undefined;
|
|
let symbolName = symbol;
|
|
// we want to match functions when they have parentheses
|
|
if (symbolName.endsWith('()')) {
|
|
symbolName = symbolName.slice(0, -2);
|
|
}
|
|
|
|
if (symbolName.startsWith('@')) {
|
|
symbolName = symbolName.slice(1);
|
|
}
|
|
if (symbolName.includes('#')) {
|
|
[symbolName, propName] = symbolName.split('#');
|
|
} else if (symbolName.includes('.')) {
|
|
[symbolName, propName] = symbolName.split('.');
|
|
}
|
|
|
|
return {propName: propName ?? null, symbolName: symbolName};
|
|
}
|
|
|
|
export function getSymbolUrl(symbol: string, apiEntries: ApiEntries): string | undefined {
|
|
if (hasMoreThanOneDot(symbol) || !shouldLinkSymbol(symbol)) {
|
|
return undefined;
|
|
}
|
|
|
|
const {symbolName, propName} = extractFromSymbol(symbol);
|
|
// We don't want to match entries like "constructor"
|
|
if (!Object.hasOwn(apiEntries, symbolName)) {
|
|
return undefined;
|
|
}
|
|
|
|
const apiEntry = apiEntries[symbolName];
|
|
const moduleName = apiEntry.moduleName;
|
|
const targetSymbol = apiEntry.targetSymbol ?? symbolName;
|
|
|
|
return `/api/${moduleName}/${targetSymbol}${propName ? `#${propName}` : ''}`;
|
|
}
|
|
|
|
function hasMoreThanOneDot(str: string) {
|
|
return str.split('.').length - 1 > 1;
|
|
}
|