@angular/language-service
Version:
Angular - language services
1,291 lines (1,274 loc) • 12.6 MB
JavaScript
/**
* @license Angular v20.0.4
* Copyright Google LLC All Rights Reserved.
* License: MIT
*/
let $deferred;
function define(modules, callback) {
$deferred = {modules, callback};
}
module.exports = function(provided) {
const ts = provided['typescript'];
if (!ts) {
throw new Error('Caller does not provide typescript module');
}
const results = {};
const resolvedModules = $deferred.modules.map(m => {
if (m === 'exports') {
return results;
}
if (m === 'typescript') {
return ts;
}
return require(m);
});
$deferred.callback(...resolvedModules);
return results;
};
define(['module', 'exports', 'typescript', 'os', 'fs', 'module', 'path', 'url', 'assert'], (function (module, exports, ts, os, fs$1, module$1, p, url, assert) { 'use strict';
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var os__namespace = /*#__PURE__*/_interopNamespaceDefault(os);
var p__namespace = /*#__PURE__*/_interopNamespaceDefault(p);
/**
* @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
*/
function isNgLanguageService(ls) {
return 'getTcb' in ls;
}
/**
* The default `FileSystem` that will always fail.
*
* This is a way of ensuring that the developer consciously chooses and
* configures the `FileSystem` before using it; particularly important when
* considering static functions like `absoluteFrom()` which rely on
* the `FileSystem` under the hood.
*/
class InvalidFileSystem {
exists(path) {
throw makeError();
}
readFile(path) {
throw makeError();
}
readFileBuffer(path) {
throw makeError();
}
writeFile(path, data, exclusive) {
throw makeError();
}
removeFile(path) {
throw makeError();
}
symlink(target, path) {
throw makeError();
}
readdir(path) {
throw makeError();
}
lstat(path) {
throw makeError();
}
stat(path) {
throw makeError();
}
pwd() {
throw makeError();
}
chdir(path) {
throw makeError();
}
extname(path) {
throw makeError();
}
copyFile(from, to) {
throw makeError();
}
moveFile(from, to) {
throw makeError();
}
ensureDir(path) {
throw makeError();
}
removeDeep(path) {
throw makeError();
}
isCaseSensitive() {
throw makeError();
}
resolve(...paths) {
throw makeError();
}
dirname(file) {
throw makeError();
}
join(basePath, ...paths) {
throw makeError();
}
isRoot(path) {
throw makeError();
}
isRooted(path) {
throw makeError();
}
relative(from, to) {
throw makeError();
}
basename(filePath, extension) {
throw makeError();
}
realpath(filePath) {
throw makeError();
}
getDefaultLibLocation() {
throw makeError();
}
normalize(path) {
throw makeError();
}
}
function makeError() {
return new Error('FileSystem has not been configured. Please call `setFileSystem()` before calling this method.');
}
const TS_DTS_JS_EXTENSION = /(?:\.d)?\.ts$|\.js$/;
/**
* Remove a .ts, .d.ts, or .js extension from a file name.
*/
function stripExtension(path) {
return path.replace(TS_DTS_JS_EXTENSION, '');
}
function getSourceFileOrError(program, fileName) {
const sf = program.getSourceFile(fileName);
if (sf === undefined) {
throw new Error(`Program does not contain "${fileName}" - available files are ${program
.getSourceFiles()
.map((sf) => sf.fileName)
.join(', ')}`);
}
return sf;
}
let fs = new InvalidFileSystem();
function getFileSystem() {
return fs;
}
function setFileSystem(fileSystem) {
fs = fileSystem;
}
/**
* Convert the path `path` to an `AbsoluteFsPath`, throwing an error if it's not an absolute path.
*/
function absoluteFrom(path) {
if (!fs.isRooted(path)) {
throw new Error(`Internal Error: absoluteFrom(${path}): path is not absolute`);
}
return fs.resolve(path);
}
const ABSOLUTE_PATH = Symbol('AbsolutePath');
/**
* Extract an `AbsoluteFsPath` from a `ts.SourceFile`-like object.
*/
function absoluteFromSourceFile(sf) {
const sfWithPatch = sf;
if (sfWithPatch[ABSOLUTE_PATH] === undefined) {
sfWithPatch[ABSOLUTE_PATH] = fs.resolve(sfWithPatch.fileName);
}
// Non-null assertion needed since TS doesn't narrow the type of fields that use a symbol as a key
// apparently.
return sfWithPatch[ABSOLUTE_PATH];
}
/**
* Static access to `dirname`.
*/
function dirname(file) {
return fs.dirname(file);
}
/**
* Static access to `join`.
*/
function join(basePath, ...paths) {
return fs.join(basePath, ...paths);
}
/**
* Static access to `resolve`s.
*/
function resolve(basePath, ...paths) {
return fs.resolve(basePath, ...paths);
}
/**
* Static access to `isRooted`.
*/
function isRooted(path) {
return fs.isRooted(path);
}
/**
* Static access to `relative`.
*/
function relative(from, to) {
return fs.relative(from, to);
}
/**
* Returns true if the given path is locally relative.
*
* This is used to work out if the given path is relative (i.e. not absolute) but also is not
* escaping the current directory.
*/
function isLocalRelativePath(relativePath) {
return !isRooted(relativePath) && !relativePath.startsWith('..');
}
/**
* Converts a path to a form suitable for use as a relative module import specifier.
*
* In other words it adds the `./` to the path if it is locally relative.
*/
function toRelativeImport(relativePath) {
return isLocalRelativePath(relativePath) ? `./${relativePath}` : relativePath;
}
/**
* @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
*/
/// <reference types="node" />
class NgtscCompilerHost {
fs;
options;
constructor(fs, options = {}) {
this.fs = fs;
this.options = options;
}
getSourceFile(fileName, languageVersion) {
const text = this.readFile(fileName);
return text !== undefined
? ts.createSourceFile(fileName, text, languageVersion, true)
: undefined;
}
getDefaultLibFileName(options) {
return this.fs.join(this.getDefaultLibLocation(), ts.getDefaultLibFileName(options));
}
getDefaultLibLocation() {
return this.fs.getDefaultLibLocation();
}
writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles) {
const path = absoluteFrom(fileName);
this.fs.ensureDir(this.fs.dirname(path));
this.fs.writeFile(path, data);
}
getCurrentDirectory() {
return this.fs.pwd();
}
getCanonicalFileName(fileName) {
return this.useCaseSensitiveFileNames() ? fileName : fileName.toLowerCase();
}
useCaseSensitiveFileNames() {
return this.fs.isCaseSensitive();
}
getNewLine() {
switch (this.options.newLine) {
case ts.NewLineKind.CarriageReturnLineFeed:
return '\r\n';
case ts.NewLineKind.LineFeed:
return '\n';
default:
return os__namespace.EOL;
}
}
fileExists(fileName) {
const absPath = this.fs.resolve(fileName);
return this.fs.exists(absPath) && this.fs.stat(absPath).isFile();
}
readFile(fileName) {
const absPath = this.fs.resolve(fileName);
if (!this.fileExists(absPath)) {
return undefined;
}
return this.fs.readFile(absPath);
}
realpath(path) {
return this.fs.realpath(this.fs.resolve(path));
}
}
const LogicalProjectPath = {
/**
* Get the relative path between two `LogicalProjectPath`s.
*
* This will return a `PathSegment` which would be a valid module specifier to use in `from` when
* importing from `to`.
*/
relativePathBetween: function (from, to) {
const relativePath = relative(dirname(resolve(from)), resolve(to));
return toRelativeImport(relativePath);
},
};
/**
* A utility class which can translate absolute paths to source files into logical paths in
* TypeScript's logical file system, based on the root directories of the project.
*/
class LogicalFileSystem {
compilerHost;
/**
* The root directories of the project, sorted with the longest path first.
*/
rootDirs;
/**
* The same root directories as `rootDirs` but with each one converted to its
* canonical form for matching in case-insensitive file-systems.
*/
canonicalRootDirs;
/**
* A cache of file paths to project paths, because computation of these paths is slightly
* expensive.
*/
cache = new Map();
constructor(rootDirs, compilerHost) {
this.compilerHost = compilerHost;
// Make a copy and sort it by length in reverse order (longest first). This speeds up lookups,
// since there's no need to keep going through the array once a match is found.
this.rootDirs = rootDirs.concat([]).sort((a, b) => b.length - a.length);
this.canonicalRootDirs = this.rootDirs.map((dir) => this.compilerHost.getCanonicalFileName(dir));
}
/**
* Get the logical path in the project of a `ts.SourceFile`.
*
* This method is provided as a convenient alternative to calling
* `logicalPathOfFile(absoluteFromSourceFile(sf))`.
*/
logicalPathOfSf(sf) {
return this.logicalPathOfFile(absoluteFromSourceFile(sf));
}
/**
* Get the logical path in the project of a source file.
*
* @returns A `LogicalProjectPath` to the source file, or `null` if the source file is not in any
* of the TS project's root directories.
*/
logicalPathOfFile(physicalFile) {
if (!this.cache.has(physicalFile)) {
const canonicalFilePath = this.compilerHost.getCanonicalFileName(physicalFile);
let logicalFile = null;
for (let i = 0; i < this.rootDirs.length; i++) {
const rootDir = this.rootDirs[i];
const canonicalRootDir = this.canonicalRootDirs[i];
if (isWithinBasePath$2(canonicalRootDir, canonicalFilePath)) {
// Note that we match against canonical paths but then create the logical path from
// original paths.
logicalFile = this.createLogicalProjectPath(physicalFile, rootDir);
// The logical project does not include any special "node_modules" nested directories.
if (logicalFile.indexOf('/node_modules/') !== -1) {
logicalFile = null;
}
else {
break;
}
}
}
this.cache.set(physicalFile, logicalFile);
}
return this.cache.get(physicalFile);
}
createLogicalProjectPath(file, rootDir) {
const logicalPath = stripExtension(file.slice(rootDir.length));
return (logicalPath.startsWith('/') ? logicalPath : '/' + logicalPath);
}
}
/**
* Is the `path` a descendant of the `base`?
* E.g. `foo/bar/zee` is within `foo/bar` but not within `foo/car`.
*/
function isWithinBasePath$2(base, path) {
return isLocalRelativePath(relative(base, path));
}
/**
* @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
*/
/// <reference types="node" />
/**
* A wrapper around the Node.js file-system that supports path manipulation.
*/
class NodeJSPathManipulation {
pwd() {
return this.normalize(process.cwd());
}
chdir(dir) {
process.chdir(dir);
}
resolve(...paths) {
return this.normalize(p__namespace.resolve(...paths));
}
dirname(file) {
return this.normalize(p__namespace.dirname(file));
}
join(basePath, ...paths) {
return this.normalize(p__namespace.join(basePath, ...paths));
}
isRoot(path) {
return this.dirname(path) === this.normalize(path);
}
isRooted(path) {
return p__namespace.isAbsolute(path);
}
relative(from, to) {
return this.normalize(p__namespace.relative(from, to));
}
basename(filePath, extension) {
return p__namespace.basename(filePath, extension);
}
extname(path) {
return p__namespace.extname(path);
}
normalize(path) {
// Convert backslashes to forward slashes
return path.replace(/\\/g, '/');
}
}
// G3-ESM-MARKER: G3 uses CommonJS, but externally everything in ESM.
// CommonJS/ESM interop for determining the current file name and containing dir.
const isCommonJS = typeof __filename !== 'undefined';
const currentFileUrl = isCommonJS ? null : new URL(module.uri, document.baseURI).href;
const currentFileName = isCommonJS ? __filename : url.fileURLToPath(currentFileUrl);
/**
* A wrapper around the Node.js file-system that supports readonly operations and path manipulation.
*/
class NodeJSReadonlyFileSystem extends NodeJSPathManipulation {
_caseSensitive = undefined;
isCaseSensitive() {
if (this._caseSensitive === undefined) {
// Note the use of the real file-system is intentional:
// `this.exists()` relies upon `isCaseSensitive()` so that would cause an infinite recursion.
this._caseSensitive = !fs$1.existsSync(this.normalize(toggleCase(currentFileName)));
}
return this._caseSensitive;
}
exists(path) {
return fs$1.existsSync(path);
}
readFile(path) {
return fs$1.readFileSync(path, 'utf8');
}
readFileBuffer(path) {
return fs$1.readFileSync(path);
}
readdir(path) {
return fs$1.readdirSync(path);
}
lstat(path) {
return fs$1.lstatSync(path);
}
stat(path) {
return fs$1.statSync(path);
}
realpath(path) {
return this.resolve(fs$1.realpathSync(path));
}
getDefaultLibLocation() {
// G3-ESM-MARKER: G3 uses CommonJS, but externally everything in ESM.
const requireFn = isCommonJS ? require : module$1.createRequire(currentFileUrl);
return this.resolve(requireFn.resolve('typescript'), '..');
}
}
/**
* A wrapper around the Node.js file-system (i.e. the `fs` package).
*/
class NodeJSFileSystem extends NodeJSReadonlyFileSystem {
writeFile(path, data, exclusive = false) {
fs$1.writeFileSync(path, data, exclusive ? { flag: 'wx' } : undefined);
}
removeFile(path) {
fs$1.unlinkSync(path);
}
symlink(target, path) {
fs$1.symlinkSync(target, path);
}
copyFile(from, to) {
fs$1.copyFileSync(from, to);
}
moveFile(from, to) {
fs$1.renameSync(from, to);
}
ensureDir(path) {
fs$1.mkdirSync(path, { recursive: true });
}
removeDeep(path) {
fs$1.rmdirSync(path, { recursive: true });
}
}
/**
* Toggle the case of each character in a string.
*/
function toggleCase(str) {
return str.replace(/\w/g, (ch) => ch.toUpperCase() === ch ? ch.toLowerCase() : ch.toUpperCase());
}
/**
* @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
*/
// We use TypeScript's native `ts.matchFiles` utility for the virtual file systems
// and their TypeScript compiler host `readDirectory` implementation. TypeScript's
// function implements complex logic for matching files with respect to root
// directory, extensions, excludes, includes etc. The function is currently
// internal but we can use it as the API most likely will not change any time soon,
// nor does it seem like this is being made public any time soon.
// Related issue for tracking: https://github.com/microsoft/TypeScript/issues/13793.
/**
* Creates a {@link ts.CompilerHost#readDirectory} implementation function,
* that leverages the specified file system (that may be e.g. virtual).
*/
function createFileSystemTsReadDirectoryFn(fs) {
if (ts.matchFiles === undefined) {
throw Error('Unable to read directory in configured file system. This means that ' +
'TypeScript changed its file matching internals.\n\nPlease consider downgrading your ' +
'TypeScript version, and report an issue in the Angular framework repository.');
}
const matchFilesFn = ts.matchFiles.bind(ts);
return (rootDir, extensions, excludes, includes, depth) => {
const directoryExists = (p) => {
const resolvedPath = fs.resolve(p);
return fs.exists(resolvedPath) && fs.stat(resolvedPath).isDirectory();
};
return matchFilesFn(rootDir, extensions, excludes, includes, fs.isCaseSensitive(), fs.pwd(), depth, (p) => {
const resolvedPath = fs.resolve(p);
// TS also gracefully returns an empty file set.
if (!directoryExists(resolvedPath)) {
return { directories: [], files: [] };
}
const children = fs.readdir(resolvedPath);
const files = [];
const directories = [];
for (const child of children) {
if (fs.stat(fs.join(resolvedPath, child))?.isDirectory()) {
directories.push(child);
}
else {
files.push(child);
}
}
return { files, directories };
}, (p) => fs.resolve(p), (p) => directoryExists(p));
};
}
/**
* @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
*/
const _SELECTOR_REGEXP = new RegExp('(\\:not\\()|' + // 1: ":not("
'(([\\.\\#]?)[-\\w]+)|' + // 2: "tag"; 3: "."/"#";
// "-" should appear first in the regexp below as FF31 parses "[.-\w]" as a range
// 4: attribute; 5: attribute_string; 6: attribute_value
'(?:\\[([-.\\w*\\\\$]+)(?:=(["\']?)([^\\]"\']*)\\5)?\\])|' + // "[name]", "[name=value]",
// "[name="value"]",
// "[name='value']"
'(\\))|' + // 7: ")"
'(\\s*,\\s*)', // 8: ","
'g');
/**
* A css selector contains an element name,
* css classes and attribute/value pairs with the purpose
* of selecting subsets out of them.
*/
class CssSelector {
element = null;
classNames = [];
/**
* The selectors are encoded in pairs where:
* - even locations are attribute names
* - odd locations are attribute values.
*
* Example:
* Selector: `[key1=value1][key2]` would parse to:
* ```
* ['key1', 'value1', 'key2', '']
* ```
*/
attrs = [];
notSelectors = [];
static parse(selector) {
const results = [];
const _addResult = (res, cssSel) => {
if (cssSel.notSelectors.length > 0 &&
!cssSel.element &&
cssSel.classNames.length == 0 &&
cssSel.attrs.length == 0) {
cssSel.element = '*';
}
res.push(cssSel);
};
let cssSelector = new CssSelector();
let match;
let current = cssSelector;
let inNot = false;
_SELECTOR_REGEXP.lastIndex = 0;
while ((match = _SELECTOR_REGEXP.exec(selector))) {
if (match[1 /* SelectorRegexp.NOT */]) {
if (inNot) {
throw new Error('Nesting :not in a selector is not allowed');
}
inNot = true;
current = new CssSelector();
cssSelector.notSelectors.push(current);
}
const tag = match[2 /* SelectorRegexp.TAG */];
if (tag) {
const prefix = match[3 /* SelectorRegexp.PREFIX */];
if (prefix === '#') {
// #hash
current.addAttribute('id', tag.slice(1));
}
else if (prefix === '.') {
// Class
current.addClassName(tag.slice(1));
}
else {
// Element
current.setElement(tag);
}
}
const attribute = match[4 /* SelectorRegexp.ATTRIBUTE */];
if (attribute) {
current.addAttribute(current.unescapeAttribute(attribute), match[6 /* SelectorRegexp.ATTRIBUTE_VALUE */]);
}
if (match[7 /* SelectorRegexp.NOT_END */]) {
inNot = false;
current = cssSelector;
}
if (match[8 /* SelectorRegexp.SEPARATOR */]) {
if (inNot) {
throw new Error('Multiple selectors in :not are not supported');
}
_addResult(results, cssSelector);
cssSelector = current = new CssSelector();
}
}
_addResult(results, cssSelector);
return results;
}
/**
* Unescape `\$` sequences from the CSS attribute selector.
*
* This is needed because `$` can have a special meaning in CSS selectors,
* but we might want to match an attribute that contains `$`.
* [MDN web link for more
* info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors).
* @param attr the attribute to unescape.
* @returns the unescaped string.
*/
unescapeAttribute(attr) {
let result = '';
let escaping = false;
for (let i = 0; i < attr.length; i++) {
const char = attr.charAt(i);
if (char === '\\') {
escaping = true;
continue;
}
if (char === '$' && !escaping) {
throw new Error(`Error in attribute selector "${attr}". ` +
`Unescaped "$" is not supported. Please escape with "\\$".`);
}
escaping = false;
result += char;
}
return result;
}
/**
* Escape `$` sequences from the CSS attribute selector.
*
* This is needed because `$` can have a special meaning in CSS selectors,
* with this method we are escaping `$` with `\$'.
* [MDN web link for more
* info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors).
* @param attr the attribute to escape.
* @returns the escaped string.
*/
escapeAttribute(attr) {
return attr.replace(/\\/g, '\\\\').replace(/\$/g, '\\$');
}
isElementSelector() {
return (this.hasElementSelector() &&
this.classNames.length == 0 &&
this.attrs.length == 0 &&
this.notSelectors.length === 0);
}
hasElementSelector() {
return !!this.element;
}
setElement(element = null) {
this.element = element;
}
getAttrs() {
const result = [];
if (this.classNames.length > 0) {
result.push('class', this.classNames.join(' '));
}
return result.concat(this.attrs);
}
addAttribute(name, value = '') {
this.attrs.push(name, (value && value.toLowerCase()) || '');
}
addClassName(name) {
this.classNames.push(name.toLowerCase());
}
toString() {
let res = this.element || '';
if (this.classNames) {
this.classNames.forEach((klass) => (res += `.${klass}`));
}
if (this.attrs) {
for (let i = 0; i < this.attrs.length; i += 2) {
const name = this.escapeAttribute(this.attrs[i]);
const value = this.attrs[i + 1];
res += `[${name}${value ? '=' + value : ''}]`;
}
}
this.notSelectors.forEach((notSelector) => (res += `:not(${notSelector})`));
return res;
}
}
/**
* Reads a list of CssSelectors and allows to calculate which ones
* are contained in a given CssSelector.
*/
class SelectorMatcher {
static createNotMatcher(notSelectors) {
const notMatcher = new SelectorMatcher();
notMatcher.addSelectables(notSelectors, null);
return notMatcher;
}
_elementMap = new Map();
_elementPartialMap = new Map();
_classMap = new Map();
_classPartialMap = new Map();
_attrValueMap = new Map();
_attrValuePartialMap = new Map();
_listContexts = [];
addSelectables(cssSelectors, callbackCtxt) {
let listContext = null;
if (cssSelectors.length > 1) {
listContext = new SelectorListContext(cssSelectors);
this._listContexts.push(listContext);
}
for (let i = 0; i < cssSelectors.length; i++) {
this._addSelectable(cssSelectors[i], callbackCtxt, listContext);
}
}
/**
* Add an object that can be found later on by calling `match`.
* @param cssSelector A css selector
* @param callbackCtxt An opaque object that will be given to the callback of the `match` function
*/
_addSelectable(cssSelector, callbackCtxt, listContext) {
let matcher = this;
const element = cssSelector.element;
const classNames = cssSelector.classNames;
const attrs = cssSelector.attrs;
const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext);
if (element) {
const isTerminal = attrs.length === 0 && classNames.length === 0;
if (isTerminal) {
this._addTerminal(matcher._elementMap, element, selectable);
}
else {
matcher = this._addPartial(matcher._elementPartialMap, element);
}
}
if (classNames) {
for (let i = 0; i < classNames.length; i++) {
const isTerminal = attrs.length === 0 && i === classNames.length - 1;
const className = classNames[i];
if (isTerminal) {
this._addTerminal(matcher._classMap, className, selectable);
}
else {
matcher = this._addPartial(matcher._classPartialMap, className);
}
}
}
if (attrs) {
for (let i = 0; i < attrs.length; i += 2) {
const isTerminal = i === attrs.length - 2;
const name = attrs[i];
const value = attrs[i + 1];
if (isTerminal) {
const terminalMap = matcher._attrValueMap;
let terminalValuesMap = terminalMap.get(name);
if (!terminalValuesMap) {
terminalValuesMap = new Map();
terminalMap.set(name, terminalValuesMap);
}
this._addTerminal(terminalValuesMap, value, selectable);
}
else {
const partialMap = matcher._attrValuePartialMap;
let partialValuesMap = partialMap.get(name);
if (!partialValuesMap) {
partialValuesMap = new Map();
partialMap.set(name, partialValuesMap);
}
matcher = this._addPartial(partialValuesMap, value);
}
}
}
}
_addTerminal(map, name, selectable) {
let terminalList = map.get(name);
if (!terminalList) {
terminalList = [];
map.set(name, terminalList);
}
terminalList.push(selectable);
}
_addPartial(map, name) {
let matcher = map.get(name);
if (!matcher) {
matcher = new SelectorMatcher();
map.set(name, matcher);
}
return matcher;
}
/**
* Find the objects that have been added via `addSelectable`
* whose css selector is contained in the given css selector.
* @param cssSelector A css selector
* @param matchedCallback This callback will be called with the object handed into `addSelectable`
* @return boolean true if a match was found
*/
match(cssSelector, matchedCallback) {
let result = false;
const element = cssSelector.element;
const classNames = cssSelector.classNames;
const attrs = cssSelector.attrs;
for (let i = 0; i < this._listContexts.length; i++) {
this._listContexts[i].alreadyMatched = false;
}
result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result;
result =
this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) || result;
if (classNames) {
for (let i = 0; i < classNames.length; i++) {
const className = classNames[i];
result =
this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
result =
this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) ||
result;
}
}
if (attrs) {
for (let i = 0; i < attrs.length; i += 2) {
const name = attrs[i];
const value = attrs[i + 1];
const terminalValuesMap = this._attrValueMap.get(name);
if (value) {
result =
this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
}
result =
this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
const partialValuesMap = this._attrValuePartialMap.get(name);
if (value) {
result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
}
result =
this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result;
}
}
return result;
}
/** @internal */
_matchTerminal(map, name, cssSelector, matchedCallback) {
if (!map || typeof name !== 'string') {
return false;
}
let selectables = map.get(name) || [];
const starSelectables = map.get('*');
if (starSelectables) {
selectables = selectables.concat(starSelectables);
}
if (selectables.length === 0) {
return false;
}
let selectable;
let result = false;
for (let i = 0; i < selectables.length; i++) {
selectable = selectables[i];
result = selectable.finalize(cssSelector, matchedCallback) || result;
}
return result;
}
/** @internal */
_matchPartial(map, name, cssSelector, matchedCallback) {
if (!map || typeof name !== 'string') {
return false;
}
const nestedSelector = map.get(name);
if (!nestedSelector) {
return false;
}
// TODO(perf): get rid of recursion and measure again
// TODO(perf): don't pass the whole selector into the recursion,
// but only the not processed parts
return nestedSelector.match(cssSelector, matchedCallback);
}
}
class SelectorListContext {
selectors;
alreadyMatched = false;
constructor(selectors) {
this.selectors = selectors;
}
}
// Store context to pass back selector and context when a selector is matched
class SelectorContext {
selector;
cbContext;
listContext;
notSelectors;
constructor(selector, cbContext, listContext) {
this.selector = selector;
this.cbContext = cbContext;
this.listContext = listContext;
this.notSelectors = selector.notSelectors;
}
finalize(cssSelector, callback) {
let result = true;
if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) {
const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
result = !notMatcher.match(cssSelector, null);
}
if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) {
if (this.listContext) {
this.listContext.alreadyMatched = true;
}
callback(this.selector, this.cbContext);
}
return result;
}
}
class SelectorlessMatcher {
registry;
constructor(registry) {
this.registry = registry;
}
match(name) {
return this.registry.has(name) ? this.registry.get(name) : [];
}
}
/**
* @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
*/
// Attention:
// This file duplicates types and values from @angular/core
// so that we are able to make @angular/compiler independent of @angular/core.
// This is important to prevent a build cycle, as @angular/core needs to
// be compiled with the compiler.
// Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not
// explicitly set.
const emitDistinctChangesOnlyDefaultValue = true;
var ViewEncapsulation$1;
(function (ViewEncapsulation) {
ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
// Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
})(ViewEncapsulation$1 || (ViewEncapsulation$1 = {}));
var ChangeDetectionStrategy;
(function (ChangeDetectionStrategy) {
ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
})(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
/** Flags describing an input for a directive. */
var InputFlags;
(function (InputFlags) {
InputFlags[InputFlags["None"] = 0] = "None";
InputFlags[InputFlags["SignalBased"] = 1] = "SignalBased";
InputFlags[InputFlags["HasDecoratorInputTransform"] = 2] = "HasDecoratorInputTransform";
})(InputFlags || (InputFlags = {}));
const CUSTOM_ELEMENTS_SCHEMA = {
name: 'custom-elements',
};
const NO_ERRORS_SCHEMA = {
name: 'no-errors-schema',
};
var SecurityContext;
(function (SecurityContext) {
SecurityContext[SecurityContext["NONE"] = 0] = "NONE";
SecurityContext[SecurityContext["HTML"] = 1] = "HTML";
SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE";
SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT";
SecurityContext[SecurityContext["URL"] = 4] = "URL";
SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL";
})(SecurityContext || (SecurityContext = {}));
var MissingTranslationStrategy;
(function (MissingTranslationStrategy) {
MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error";
MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning";
MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore";
})(MissingTranslationStrategy || (MissingTranslationStrategy = {}));
function parserSelectorToSimpleSelector(selector) {
const classes = selector.classNames && selector.classNames.length
? [8 /* SelectorFlags.CLASS */, ...selector.classNames]
: [];
const elementName = selector.element && selector.element !== '*' ? selector.element : '';
return [elementName, ...selector.attrs, ...classes];
}
function parserSelectorToNegativeSelector(selector) {
const classes = selector.classNames && selector.classNames.length
? [8 /* SelectorFlags.CLASS */, ...selector.classNames]
: [];
if (selector.element) {
return [
1 /* SelectorFlags.NOT */ | 4 /* SelectorFlags.ELEMENT */,
selector.element,
...selector.attrs,
...classes,
];
}
else if (selector.attrs.length) {
return [1 /* SelectorFlags.NOT */ | 2 /* SelectorFlags.ATTRIBUTE */, ...selector.attrs, ...classes];
}
else {
return selector.classNames && selector.classNames.length
? [1 /* SelectorFlags.NOT */ | 8 /* SelectorFlags.CLASS */, ...selector.classNames]
: [];
}
}
function parserSelectorToR3Selector(selector) {
const positive = parserSelectorToSimpleSelector(selector);
const negative = selector.notSelectors && selector.notSelectors.length
? selector.notSelectors.map((notSelector) => parserSelectorToNegativeSelector(notSelector))
: [];
return positive.concat(...negative);
}
function parseSelectorToR3Selector(selector) {
return selector ? CssSelector.parse(selector).map(parserSelectorToR3Selector) : [];
}
/**
* @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
*/
var FactoryTarget;
(function (FactoryTarget) {
FactoryTarget[FactoryTarget["Directive"] = 0] = "Directive";
FactoryTarget[FactoryTarget["Component"] = 1] = "Component";
FactoryTarget[FactoryTarget["Injectable"] = 2] = "Injectable";
FactoryTarget[FactoryTarget["Pipe"] = 3] = "Pipe";
FactoryTarget[FactoryTarget["NgModule"] = 4] = "NgModule";
})(FactoryTarget || (FactoryTarget = {}));
var R3TemplateDependencyKind$1;
(function (R3TemplateDependencyKind) {
R3TemplateDependencyKind[R3TemplateDependencyKind["Directive"] = 0] = "Directive";
R3TemplateDependencyKind[R3TemplateDependencyKind["Pipe"] = 1] = "Pipe";
R3TemplateDependencyKind[R3TemplateDependencyKind["NgModule"] = 2] = "NgModule";
})(R3TemplateDependencyKind$1 || (R3TemplateDependencyKind$1 = {}));
var ViewEncapsulation;
(function (ViewEncapsulation) {
ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
// Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
})(ViewEncapsulation || (ViewEncapsulation = {}));
/**
* @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
*/
/**
* A lazily created TextEncoder instance for converting strings into UTF-8 bytes
*/
let textEncoder;
/**
* Return the message id or compute it using the XLIFF1 digest.
*/
function digest$1(message) {
return message.id || computeDigest(message);
}
/**
* Compute the message id using the XLIFF1 digest.
*/
function computeDigest(message) {
return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
}
/**
* Return the message id or compute it using the XLIFF2/XMB/$localize digest.
*/
function decimalDigest(message) {
return message.id || computeDecimalDigest(message);
}
/**
* Compute the message id using the XLIFF2/XMB/$localize digest.
*/
function computeDecimalDigest(message) {
const visitor = new _SerializerIgnoreIcuExpVisitor();
const parts = message.nodes.map((a) => a.visit(visitor, null));
return computeMsgId(parts.join(''), message.meaning);
}
/**
* Serialize the i18n ast to something xml-like in order to generate an UID.
*
* The visitor is also used in the i18n parser tests
*
* @internal
*/
class _SerializerVisitor {
visitText(text, context) {
return text.value;
}
visitContainer(container, context) {
return `[${container.children.map((child) => child.visit(this)).join(', ')}]`;
}
visitIcu(icu, context) {
const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`;
}
visitTagPlaceholder(ph, context) {
return ph.isVoid
? `<ph tag name="${ph.startName}"/>`
: `<ph tag name="${ph.startName}">${ph.children
.map((child) => child.visit(this))
.join(', ')}</ph name="${ph.closeName}">`;
}
visitPlaceholder(ph, context) {
return ph.value ? `<ph name="${ph.name}">${ph.value}</ph>` : `<ph name="${ph.name}"/>`;
}
visitIcuPlaceholder(ph, context) {
return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`;
}
visitBlockPlaceholder(ph, context) {
return `<ph block name="${ph.startName}">${ph.children
.map((child) => child.visit(this))
.join(', ')}</ph name="${ph.closeName}">`;
}
}
const serializerVisitor$1 = new _SerializerVisitor();
function serializeNodes(nodes) {
return nodes.map((a) => a.visit(serializerVisitor$1, null));
}
/**
* Serialize the i18n ast to something xml-like in order to generate an UID.
*
* Ignore the ICU expressions so that message IDs stays identical if only the expression changes.
*
* @internal
*/
class _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor {
visitIcu(icu) {
let strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
// Do not take the expression into account
return `{${icu.type}, ${strCases.join(', ')}}`;
}
}
/**
* Compute the SHA1 of the given string
*
* see https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
*
* WARNING: this function has not been designed not tested with security in mind.
* DO NOT USE IT IN A SECURITY SENSITIVE CONTEXT.
*/
function sha1(str) {
textEncoder ??= new TextEncoder();
const utf8 = [...textEncoder.encode(str)];
const words32 = bytesToWords32(utf8, Endian.Big);
const len = utf8.length * 8;
const w = new Uint32Array(80);
let a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476, e = 0xc3d2e1f0;
words32[len >> 5] |= 0x80 << (24 - (len % 32));
words32[(((len + 64) >> 9) << 4) + 15] = len;
for (let i = 0; i < words32.length; i += 16) {
const h0 = a, h1 = b, h2 = c, h3 = d, h4 = e;
for (let j = 0; j < 80; j++) {
if (j < 16) {
w[j] = words32[i + j];
}
else {
w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
}
const fkVal = fk(j, b, c, d);
const f = fkVal[0];
const k = fkVal[1];
const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32);
e = d;
d = c;
c = rol32(b, 30);
b = a;
a = temp;
}
a = add32(a, h0);
b = add32(b, h1);
c = add32(c, h2);
d = add32(d, h3);
e = add32(e, h4);
}
// Convert the output parts to a 160-bit hexadecimal string
return toHexU32(a) + toHexU32(b) + toHexU32(c) + toHexU32(d) + toHexU32(e);
}
/**
* Convert and format a number as a string representing a 32-bit unsigned hexadecimal number.
* @param value The value to format as a string.
* @returns A hexadecimal string representing the value.
*/
function toHexU32(value) {
// unsigned right shift of zero ensures an unsigned 32-bit number
return (value >>> 0).toString(16).padStart(8, '0');
}
function fk(index, b, c, d) {
if (index < 20) {
return [(b & c) | (~b & d), 0x5a827999];
}
if (index < 40) {
return [b ^ c ^ d, 0x6ed9eba1];
}
if (index < 60) {
return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];
}
return [b ^ c ^ d, 0xca62c1d6];
}
/**
* Compute the fingerprint of the given string
*
* The output is 64 bit number encoded as a decimal string
*
* based on:
* https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java
*/
function fingerprint(str) {
textEncoder ??= new TextEncoder();
const utf8 = textEncoder.encode(str);
const view = new DataView(utf8.buffer, utf8.b