diff --git a/Cargo.lock b/Cargo.lock index 61dcc1313..a4a7d25eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1030,6 +1030,7 @@ dependencies = [ "pathdiff", "rustc-hash", "semver", + "serde_json", "tempfile", ] diff --git a/crates/oxc_angular_compiler/Cargo.toml b/crates/oxc_angular_compiler/Cargo.toml index 9ac749661..9ac25ef88 100644 --- a/crates/oxc_angular_compiler/Cargo.toml +++ b/crates/oxc_angular_compiler/Cargo.toml @@ -42,6 +42,7 @@ cross_file_elision = ["oxc_resolver", "pathdiff"] [dev-dependencies] insta = { workspace = true, features = ["glob"] } +serde_json = { workspace = true } tempfile = "3" [lints] diff --git a/crates/oxc_angular_compiler/src/component/decorator.rs b/crates/oxc_angular_compiler/src/component/decorator.rs index 3b1f93a9a..604dad1c9 100644 --- a/crates/oxc_angular_compiler/src/component/decorator.rs +++ b/crates/oxc_angular_compiler/src/component/decorator.rs @@ -19,7 +19,7 @@ use super::metadata::{ use super::transform::ImportMap; use crate::directive::{ StringConsts, extract_host_bindings, extract_host_listeners, extract_input_metadata, - extract_output_metadata, + extract_output_metadata, merge_by_class_property, parse_decorator_io, }; use crate::output::oxc_converter::convert_oxc_expression; @@ -261,11 +261,18 @@ pub fn extract_component_metadata<'a>( metadata.constructor_deps = extract_constructor_deps(allocator, class, import_map, has_superclass); - // Extract inputs from @Input decorators on class members - metadata.inputs = extract_input_metadata(allocator, class, source_text); - - // Extract outputs from @Output decorators on class members - metadata.outputs = extract_output_metadata(allocator, class); + // Inputs/outputs from the `inputs:`/`outputs:` metadata, overridden by + // @Input/@Output/signal members ({...fromMeta, ...fromFields} in ngtsc). + let io = parse_decorator_io(allocator, config_obj, source_text, consts); + metadata.inputs = merge_by_class_property( + io.inputs, + extract_input_metadata(allocator, class, source_text), + |i| i.class_property_name.as_str(), + ); + metadata.outputs = + merge_by_class_property(io.outputs, extract_output_metadata(allocator, class), |o| { + o.0.as_str() + }); // Detect if ngOnChanges lifecycle hook is implemented // Similar to Angular's: const usesOnChanges = members.some(member => ...) diff --git a/crates/oxc_angular_compiler/src/component/mod.rs b/crates/oxc_angular_compiler/src/component/mod.rs index 0eed37813..0a699d38e 100644 --- a/crates/oxc_angular_compiler/src/component/mod.rs +++ b/crates/oxc_angular_compiler/src/component/mod.rs @@ -21,6 +21,7 @@ mod transform; #[cfg(feature = "cross_file_elision")] pub use cross_file_elision::CrossFileAnalyzer; pub use decorator::extract_component_metadata; +pub(crate) use decorator::find_component_decorator; pub use definition::{ ComponentDefinitions, const_value_to_expression, generate_component_definitions, }; diff --git a/crates/oxc_angular_compiler/src/component/transform.rs b/crates/oxc_angular_compiler/src/component/transform.rs index 981a40095..7ae089c32 100644 --- a/crates/oxc_angular_compiler/src/component/transform.rs +++ b/crates/oxc_angular_compiler/src/component/transform.rs @@ -41,7 +41,7 @@ use crate::class_metadata::{ use crate::directive::collect_string_consts; use crate::directive::{ R3QueryMetadata, create_content_queries_function, create_view_queries_function, - extract_content_queries, extract_directive_metadata, extract_view_queries, + decorator_io_errors, extract_content_queries, extract_directive_metadata, extract_view_queries, find_directive_decorator, find_directive_decorator_span, generate_directive_definitions, }; use crate::dts; @@ -2578,6 +2578,9 @@ pub fn transform_angular_file( // Compute implicit_standalone based on Angular version let implicit_standalone = options.implicit_standalone(); + // `inputs:`/`outputs:` forms ngtsc rejects, rather than dropping them silently. + result.diagnostics.extend(decorator_io_errors(allocator, class, &string_consts)); + if let Some(mut metadata) = extract_component_metadata( &allocator, class, diff --git a/crates/oxc_angular_compiler/src/directive/decorator.rs b/crates/oxc_angular_compiler/src/directive/decorator.rs index b45fda650..75fe699da 100644 --- a/crates/oxc_angular_compiler/src/directive/decorator.rs +++ b/crates/oxc_angular_compiler/src/directive/decorator.rs @@ -8,14 +8,17 @@ use std::collections::HashMap; use oxc_allocator::{Allocator, Box, Vec}; use oxc_ast::ast::{ Argument, ArrayExpressionElement, BindingPattern, Class, ClassElement, Declaration, Decorator, - Expression, MethodDefinitionKind, ObjectPropertyKind, Program, PropertyKey, Statement, - TemplateLiteral, VariableDeclarationKind, + Expression, MethodDefinitionKind, ObjectExpression, ObjectPropertyKind, Program, PropertyKey, + Statement, TemplateLiteral, VariableDeclarationKind, }; -use oxc_span::Span; +use oxc_diagnostics::OxcDiagnostic; +use oxc_span::{GetSpan, Span}; use oxc_str::Ident; +use super::evaluator::{Evaluator, FileScope, Prop, Value}; use super::metadata::{ R3DirectiveMetadata, R3DirectiveMetadataBuilder, R3HostDirectiveMetadata, R3HostMetadata, + R3InputMetadata, }; use crate::factory::R3DependencyMetadata; use crate::output::ast::{OutputAstBuilder, OutputExpression, ReadVarExpr}; @@ -117,6 +120,7 @@ pub fn extract_directive_metadata<'a>( // Track host metadata from the decorator let mut host_from_decorator: Option> = None; + let io = config_obj.map(|obj| parse_decorator_io(allocator, obj, source_text, consts)); // Parse each property in the config object (if present) if let Some(config_obj) = config_obj { @@ -203,6 +207,14 @@ pub fn extract_directive_metadata<'a>( // The builder already has host data from extract_from_class, we need to merge the decorator host let mut metadata = builder.build()?; + if let Some(io) = io { + let fields = std::mem::replace(&mut metadata.inputs, Vec::new_in(&allocator)); + metadata.inputs = + merge_by_class_property(io.inputs, fields, |i| i.class_property_name.as_str()); + let fields = std::mem::replace(&mut metadata.outputs, Vec::new_in(&allocator)); + metadata.outputs = merge_by_class_property(io.outputs, fields, |o| o.0.as_str()); + } + // Merge host metadata from decorator into the existing host metadata if let Some(decorator_host) = host_from_decorator { // Merge properties @@ -466,8 +478,28 @@ fn has_ng_on_changes_method(class: &Class<'_>) -> bool { /// the official Angular compiler's compile-time constant folding. /// /// Only literal string values (string literals and single-quasi template literals) -/// are captured; computed initializers and cross-file imports are out of scope. -pub type StringConsts<'a> = HashMap<&'a str, Ident<'a>>; +/// are folded; computed initializers and cross-file imports are out of scope. +/// It also carries the file's top-level declarations, for evaluating +/// `inputs:`, `outputs:` and `queries:` the way ngtsc's partial evaluator does. +#[derive(Default)] +pub struct StringConsts<'a> { + strings: HashMap<&'a str, Ident<'a>>, + program: Option<&'a Program<'a>>, + /// Built on first use: most files have no decorator metadata to evaluate. + scope: std::cell::OnceCell>, +} + +impl<'a> StringConsts<'a> { + /// The folded string value of a same-file `const`. + pub fn get(&self, name: &str) -> Option<&Ident<'a>> { + self.strings.get(name) + } + + /// The file's top-level declarations, for the partial evaluator. + pub(crate) fn scope(&self) -> &FileScope<'a> { + self.scope.get_or_init(|| self.program.map(FileScope::collect).unwrap_or_default()) + } +} /// Walk the top-level statements of a program and collect string-valued `const` /// declarations. @@ -480,12 +512,12 @@ pub type StringConsts<'a> = HashMap<&'a str, Ident<'a>>; /// matching the official Angular compiler's partial evaluator. pub fn collect_string_consts<'a>( allocator: &'a Allocator, - program: &Program<'a>, + program: &'a Program<'a>, ) -> StringConsts<'a> { // Collect every top-level `const` binding's name + initializer up front so // we can iterate to a fixed point. Each pending entry is dropped from the // worklist as soon as its initializer folds successfully. - let mut pending: std::vec::Vec<(&'a str, &Expression<'a>)> = std::vec::Vec::new(); + let mut pending: std::vec::Vec<(&'a str, &'a Expression<'a>)> = std::vec::Vec::new(); for stmt in &program.body { let decl = match stmt { Statement::VariableDeclaration(d) => d.as_ref(), @@ -507,21 +539,25 @@ pub fn collect_string_consts<'a>( } } - let mut map = StringConsts::default(); + let mut map = StringConsts { + strings: HashMap::default(), + program: Some(program), + scope: std::cell::OnceCell::new(), + }; loop { - let before = map.len(); + let before = map.strings.len(); pending.retain(|(name, init)| { - if map.contains_key(name) { + if map.strings.contains_key(name) { return false; } if let Some(value) = extract_string_value(allocator, init, &map) { - map.insert(name, value); + map.strings.insert(name, value); false } else { true } }); - if map.len() == before { + if map.strings.len() == before { break; } } @@ -609,6 +645,409 @@ fn extract_boolean_value(expr: &Expression<'_>) -> Option { } } +/// `inputs:` / `outputs:` declared in a `@Directive` / `@Component` metadata object. +pub(crate) struct DecoratorIo<'a> { + pub inputs: Vec<'a, R3InputMetadata<'a>>, + /// (class property name, binding property name) + pub outputs: Vec<'a, (Ident<'a>, Ident<'a>)>, + /// The first error ngtsc reports for `inputs:`, then for `outputs:`, and + /// the node it reports it on (the `inputs:` / `outputs:` value). + pub input_error: Option<(String, Span)>, + pub output_error: Option<(String, Span)>, +} + +/// The last property called `name` in a decorator metadata object. Like +/// ngtsc's `reflectObjectLiteral`, computed keys (`[K]: ...`) are skipped. +pub(super) fn config_property<'a>( + config: &'a ObjectExpression<'a>, + name: &str, + consts: &StringConsts<'a>, +) -> Option<&'a Expression<'a>> { + config.properties.iter().rev().find_map(|prop| match prop { + ObjectPropertyKind::ObjectProperty(prop) + if !prop.computed + && get_property_key_name(&prop.key, consts).is_some_and(|k| k == name) => + { + Some(&prop.value) + } + _ => None, + }) +} + +/// ngtsc's error for `value` in `@Directive.{field}`, or, when the value comes +/// from another module, why oxc can't tell (ngtsc would read that file). +fn io_error(field: &str, message: impl FnOnce() -> String, value: &Value<'_>) -> String { + match value { + Value::Reference { name, .. } if value.is_import() => format!( + "@Directive.{field} depends on '{name}', which is imported from another module. \ + OXC compiles one file at a time and cannot evaluate values from other files." + ), + _ => format!("{}{}", message(), value.wrong_type_suffix()), + } +} + +/// oxc's error for a transform written inside a function that the metadata +/// calls, using that function's parameters (`transform: (v) => v + name`), so +/// it would mean something else, or nothing, where the metadata is compiled. +/// ngtsc copies it there anyway. +/// +/// The same for a transform written in a namespace that uses the namespace's +/// declarations (`transform: fn`, with `fn` declared in the namespace), for +/// which ngtsc emits the bare names. +fn scoped_transform_error(input: &str, expr: &Expression<'_>, consts: &StringConsts<'_>) -> String { + match consts.scope().namespace_used_by(expr) { + Some(namespace) => format!( + "@Directive.inputs: the transform of \"{input}\" uses a declaration of namespace \ + {namespace}, which isn't in scope outside it. OXC can't emit it there." + ), + None => format!( + "@Directive.inputs: the transform of \"{input}\" uses a parameter of the function \ + it's written in. OXC can't emit it outside that function." + ), + } +} + +/// Parse `inputs:` / `outputs:` from a decorator metadata object. +/// +/// Reference: `parseInputsArray` / `parseOutputsArray` in +/// packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts +pub(crate) fn parse_decorator_io<'a>( + allocator: &'a Allocator, + config: &'a ObjectExpression<'a>, + source_text: Option<&'a str>, + consts: &StringConsts<'a>, +) -> DecoratorIo<'a> { + let evaluator = Evaluator::new(consts); + let alloc = |s: &str| Ident::from(allocator.alloc_str(s)); + let mut io = DecoratorIo { + inputs: Vec::new_in(&allocator), + outputs: Vec::new_in(&allocator), + input_error: None, + output_error: None, + }; + + if let Some(expr) = config_property(config, "inputs", consts) { + let span = expr.span(); + match evaluator.evaluate(expr) { + Value::Array(items) => { + for (i, item) in items.iter().enumerate() { + let error = match item { + Value::String(s) => { + let (class_name, binding_name) = parse_mapping_string(s); + upsert_input( + &mut io.inputs, + R3InputMetadata { + binding_property_name: alloc(binding_name), + ..R3InputMetadata::simple(alloc(class_name)) + }, + ); + None + } + Value::Object(_) => { + parse_input_object(allocator, &mut io, item, i, source_text, consts) + } + other => Some(io_error( + "inputs", + || { + "@Directive.inputs array can only contain strings or object literals" + .into() + }, + other, + )), + }; + io.input_error = io.input_error.take().or(error.map(|e| (e, span))); + } + } + other => { + let error = io_error( + "inputs", + || "Failed to resolve @Directive.inputs to an array".into(), + &other, + ); + io.input_error = Some((error, span)); + } + } + } + + if let Some(expr) = config_property(config, "outputs", consts) { + let span = expr.span(); + match evaluator.evaluate(expr) { + Value::Array(items) => { + for (i, item) in items.iter().enumerate() { + match item { + Value::String(s) => { + let (class_name, binding_name) = parse_mapping_string(s); + upsert_meta( + &mut io.outputs, + (alloc(class_name), alloc(binding_name)), + |o| o.0.as_str(), + ); + } + other => { + io.output_error = io.output_error.take().or_else(|| { + let message = || { + format!("Failed to resolve outputs at position {i} to a string") + }; + Some((io_error("outputs", message, other), span)) + }); + } + } + } + } + other => { + let error = io_error( + "outputs", + || "Failed to resolve @Directive.outputs to a string array".into(), + &other, + ); + io.output_error = Some((error, span)); + } + } + } + io +} + +/// `'field'` or `'field: binding'` -> (class property name, binding property name). +/// Like ngtsc's `value.split(':', 2)`, anything after a second colon is ignored. +fn parse_mapping_string(value: &str) -> (&str, &str) { + let mut parts = value.split(':').map(str::trim); + let field = parts.next().unwrap_or_default(); + (field, parts.next().unwrap_or(field)) +} + +/// `{ name, alias?, required?, transform? }` at `position` in the `inputs:` array. +/// Returns ngtsc's error, if any. +fn parse_input_object<'a>( + allocator: &'a Allocator, + io: &mut DecoratorIo<'a>, + item: &Value<'a>, + position: usize, + source_text: Option<&'a str>, + consts: &StringConsts<'a>, +) -> Option { + let name = match item.prop("name").map(|p| &p.value) { + Some(Value::String(name)) => name.as_str(), + other => { + let message = || { + format!( + "Value at position {position} of @Directive.inputs array must have a \"name\" property" + ) + }; + return Some(io_error("inputs", message, other.unwrap_or(&Value::Undefined))); + } + }; + // ngtsc would read an imported alias or `required` flag from its file; oxc + // can't, and guessing would compile the wrong binding. + for key in ["alias", "required"] { + if let Some(value) = item.prop(key).map(|p| &p.value).filter(|v| v.is_import()) { + return Some(io_error("inputs", String::new, value)); + } + } + let alias = item.prop("alias").and_then(|p| p.value.as_str()).unwrap_or(name); + let required = matches!(item.prop("required").map(|p| &p.value), Some(Value::Bool(true))); + let transform_function = match item.prop("transform") { + Some(Prop { expr: Some(expr), origin: None, .. }) => { + return Some(scoped_transform_error(name, expr, consts)); + } + Some(transform) => { + transform.origin.and_then(|e| convert_oxc_expression(allocator, e, source_text)) + } + None => None, + }; + upsert_input( + &mut io.inputs, + R3InputMetadata { + class_property_name: Ident::from(allocator.alloc_str(name)), + binding_property_name: Ident::from(allocator.alloc_str(alias)), + required, + is_signal: false, + transform_function, + }, + ); + None +} + +/// ngtsc's `{...fromMeta, ...fromFields}` keyed by class property name: a member +/// declaration replaces the metadata entry in place, new members are appended. +/// The result is ordered like the keys of a JavaScript object: integer-like +/// keys first, in ascending order. +pub(crate) fn merge_by_class_property( + mut from_meta: Vec<'_, T>, + from_fields: impl IntoIterator, + key: impl Fn(&T) -> &str, +) -> Vec<'_, T> { + for field in from_fields { + upsert(&mut from_meta, field, &key); + } + // A stable sort: the other keys keep their insertion order. + from_meta.sort_by_key(|item| array_index(key(item)).unwrap_or(u32::MAX)); + from_meta +} + +/// The array index a JavaScript object key stands for (`"0"`, `"42"`, but not +/// `"01"` or `"4294967295"`), which orders it before all other keys. +fn array_index(key: &str) -> Option { + let index: u32 = key.parse().ok()?; + (index != u32::MAX && index.to_string() == key).then_some(index) +} + +/// Insert keyed by class property name, like assigning to a JS object: a +/// repeated key keeps its first position and takes the later value. +fn upsert(list: &mut Vec<'_, T>, item: T, key: impl Fn(&T) -> &str) { + match list.iter().position(|existing| key(existing) == key(&item)) { + Some(i) => list[i] = item, + None => list.push(item), + } +} + +/// [`upsert`] for a metadata entry. ngtsc assigns those to a plain object, where +/// `__proto__` sets the prototype instead, so that entry is dropped. +fn upsert_meta(list: &mut Vec<'_, T>, item: T, key: impl Fn(&T) -> &str) { + if key(&item) != "__proto__" { + upsert(list, item, key); + } +} + +fn upsert_input<'a>(inputs: &mut Vec<'a, R3InputMetadata<'a>>, input: R3InputMetadata<'a>) { + upsert_meta(inputs, input, |i| i.class_property_name.as_str()); +} + +/// The `@Component` / `@Directive` decorator on `class`, its metadata object +/// (if any) and its name. +pub(crate) fn angular_decorator_config<'a>( + class: &'a Class<'a>, +) -> Option<(Option<&'a ObjectExpression<'a>>, &'static str)> { + let (decorator, name) = crate::component::find_component_decorator(&class.decorators) + .map(|d| (d, "Component")) + .or_else(|| find_directive_decorator(&class.decorators).map(|d| (d, "Directive")))?; + let config = match &decorator.expression { + Expression::CallExpression(call) => match call.arguments.first() { + Some(Argument::ObjectExpression(config)) => Some(&**config), + _ => None, + }, + _ => None, + }; + Some((config, name)) +} + +/// The first error ngtsc raises for the inputs and outputs of a `@Component` / +/// `@Directive` on `class`, in the order it checks them +/// (`extractDirectiveMetadata`): `inputs:`, input members, `outputs:`, then +/// output members. ngtsc stops at the first one. +/// +/// Each error points where ngtsc's does: the `inputs:` / `outputs:` value, or +/// the member. +pub fn decorator_io_errors<'a>( + allocator: &'a Allocator, + class: &'a Class<'a>, + consts: &StringConsts<'a>, +) -> std::vec::Vec { + let Some((config, decorator_name)) = angular_decorator_config(class) else { + return std::vec::Vec::new(); + }; + let io = config.map(|config| parse_decorator_io(allocator, config, None, consts)); + let (meta_inputs, meta_outputs): (std::vec::Vec<&str>, std::vec::Vec<&str>) = match &io { + Some(io) => ( + io.inputs.iter().map(|i| i.class_property_name.as_str()).collect(), + io.outputs.iter().map(|o| o.0.as_str()).collect(), + ), + None => Default::default(), + }; + let input_members = || { + class.body.body.iter().find_map(|element| { + let ClassElement::PropertyDefinition(prop) = element else { return None }; + let name = prop.key.static_name()?; + // A signal input only collides with a metadata entry of the same name. + let value = prop.value.as_ref().filter(|_| meta_inputs.contains(&name.as_ref()))?; + let is_input = is_initializer_api_call(value, consts, &[INPUT_API, MODEL_API]); + is_input.then(|| { + let message = format!( + "Input \"{name}\" is also declared as non-signal in @{decorator_name}." + ); + (message, prop.span) + }) + }) + }; + let output_members = || { + class.body.body.iter().find_map(|element| { + let ClassElement::PropertyDefinition(prop) = element else { return None }; + let (value, name) = (prop.value.as_ref()?, prop.key.static_name()?); + if !meta_outputs.contains(&name.as_ref()) { + return None; + } + let is_output = is_initializer_api_call( + value, + consts, + &[OUTPUT_API, OUTPUT_FROM_OBSERVABLE_API, MODEL_API], + ); + is_output.then(|| { + let message = format!( + "Output \"{name}\" is unexpectedly declared in @{decorator_name} as well." + ); + (message, prop.span) + }) + }) + }; + + io.as_ref() + .and_then(|io| io.input_error.clone()) + .or_else(input_members) + .or_else(|| io.as_ref().and_then(|io| io.output_error.clone())) + .or_else(output_members) + .map(|(message, span)| OxcDiagnostic::error(message).with_label(span)) + .into_iter() + .collect() +} + +/// An initializer API: its function name and the module exporting it. +pub(crate) type InitializerApi = (&'static str, &'static str); +pub(crate) const INPUT_API: InitializerApi = ("input", "@angular/core"); +pub(crate) const MODEL_API: InitializerApi = ("model", "@angular/core"); +pub(crate) const OUTPUT_API: InitializerApi = ("output", "@angular/core"); +pub(crate) const OUTPUT_FROM_OBSERVABLE_API: InitializerApi = + ("outputFromObservable", "@angular/core/rxjs-interop"); + +/// Whether `value` calls one of `apis`, as ngtsc's `tryParseInitializerApi` +/// recognises them: `f()` or `f.required()` with `f` imported by name from the +/// API's module (under any alias), or `ns.f()` or `ns.f.required()` with `ns` +/// a namespace import of it, looking through `as` and parentheses. A function +/// only named like an API (a local one, or one from another module) isn't one. +pub(crate) fn is_initializer_api_call( + value: &Expression<'_>, + consts: &StringConsts<'_>, + apis: &[InitializerApi], +) -> bool { + let Expression::CallExpression(call) = super::unwrap_initializer_api_expr(value) else { + return false; + }; + let scope = consts.scope(); + // `f`, imported by name. + let named = |f: &Expression<'_>| { + let Expression::Identifier(id) = f else { return false }; + scope.import(&id.name).is_some_and(|import| { + apis.iter() + .any(|&(name, module)| import.imported == Some(name) && import.module == module) + }) + }; + // `ns.f`, through a namespace import. + let namespaced = |f: &Expression<'_>| { + let Expression::StaticMemberExpression(member) = f else { return false }; + let Expression::Identifier(ns) = &member.object else { return false }; + scope.import(&ns.name).is_some_and(|import| { + import.imported.is_none() + && apis + .iter() + .any(|&(name, module)| member.property.name == name && import.module == module) + }) + }; + let callee = &call.callee; + named(callee) + || namespaced(callee) + || matches!(callee, Expression::StaticMemberExpression(required) + if required.property.name == "required" + && (named(&required.object) || namespaced(&required.object))) +} + /// Extract host metadata from a host object expression. /// /// Reference: packages/compiler/src/render3/view/compiler.ts:560-604 diff --git a/crates/oxc_angular_compiler/src/directive/evaluator.rs b/crates/oxc_angular_compiler/src/directive/evaluator.rs new file mode 100644 index 000000000..112404cbe --- /dev/null +++ b/crates/oxc_angular_compiler/src/directive/evaluator.rs @@ -0,0 +1,2352 @@ +//! A single-file model of ngtsc's partial evaluator. +//! +//! Decorator metadata such as `inputs:`, `outputs:` and `queries:` is read by +//! ngtsc through its partial evaluator +//! (packages/compiler-cli/src/ngtsc/partial_evaluator), and its diagnostics +//! describe values the way that evaluator resolves them. This module reproduces +//! the parts of it that can be answered from the current file, so the compiled +//! metadata and the diagnostics match ngtsc word for word. Imported bindings +//! can't be seen into and are treated as opaque references; anything computed +//! from one stays a reference to that import (see [`Value::is_import`]). + +use std::cell::{Cell, RefCell}; +use std::collections::{HashMap, HashSet}; + +use oxc_ast::ast::{ + Argument, ArrayExpression, ArrayExpressionElement, ArrowFunctionBody, ArrowFunctionExpression, + BinaryExpression, BindingPattern, BlockStatement, CallExpression, CatchClause, ChainElement, + Class, ClassElement, ComputedMemberExpression, ConditionalExpression, Declaration, + ExportDefaultDeclarationKind, Expression, ForInStatement, ForOfStatement, ForStatement, + ForStatementInit, ForStatementLeft, FormalParameters, Function, FunctionBody, + IdentifierReference, ImportDeclarationSpecifier, LogicalExpression, MethodDefinitionKind, + ModuleExportName, ObjectExpression, ObjectPropertyKind, Program, PropertyKey, PropertyKind, + Statement, StaticBlock, StaticMemberExpression, Super, SwitchStatement, TSEnumDeclaration, + TSEnumMemberName, TSInterfaceDeclaration, TSLiteral, TSModuleReference, TSNamespaceDeclaration, + TSNamespaceDeclarationBody, TSQualifiedName, TSTupleElement, TSType, TSTypeAliasDeclaration, + TSTypeName, TSTypeOperatorOperator, TSTypeParameterDeclaration, TSTypeParameterInstantiation, + TSTypeQueryExprName, TemplateLiteral, ThisExpression, UnaryExpression, VariableDeclaration, + VariableDeclarationKind, +}; +use oxc_ast_visit::{Visit, walk}; +use oxc_span::{GetSpan, Span}; +use oxc_syntax::operator::{BinaryOperator, LogicalOperator, UnaryOperator}; +use oxc_syntax::scope::ScopeFlags; + +use crate::output::emitter::format_number_like_js; + +/// Everything declared at the top level of a file that the evaluator can resolve. +#[derive(Default)] +pub(crate) struct FileScope<'a> { + /// `const`/`let`/`var` bindings. The first declaration of a name wins, as + /// it's TypeScript's value declaration (`var X = ['a']; var X = ['b'];`). + variables: HashMap<&'a str, Variable<'a>>, + /// Function declarations: the implementation (the first declaration when + /// there's none) and how many body-less declarations (overloads, + /// `declare function`) come with it. + functions: HashMap<&'a str, (&'a Function<'a>, usize)>, + enums: HashMap<&'a str, &'a TSEnumDeclaration<'a>>, + classes: HashMap<&'a str, &'a Class<'a>>, + imports: HashMap<&'a str, Import<'a>>, + /// Names exported from the file (`export ...` and `export { ... }`). + exported: HashSet<&'a str>, + /// Interfaces, type aliases, classes and enums declared in the file. + types: HashSet<&'a str>, + /// Import-equals aliases (`import A = NS.T`, `export import A = NS`, + /// `import A = require('m')`): what each stands for. + aliases: HashMap<&'a str, &'a TSModuleReference<'a>>, + /// The bodies of the file's namespaces (`namespace NS { ... }`, nested + /// ones included), which ngtsc reads through `typeof NS.X`. + blocks: std::vec::Vec>, + /// The id of each top-level namespace (see [`NamespaceBlock::namespace`]). + namespace_ids: HashMap<&'a str, usize>, + /// The id of the namespace exported as `name` from namespace `n`, by `(n, name)`. + member_namespaces: HashMap<(usize, &'a str), usize>, + /// The block each function, class and static method declared in a + /// namespace is written in, by the address of its node. + declared_in: HashMap, + /// The name of each namespace, by id. + namespace_names: std::vec::Vec<&'a str>, + /// The ids of the instantiated namespaces: those that declare a value + /// (a variable, function, class or enum, or an instantiated namespace), + /// which makes the namespace a value too. TypeScript gives the others no + /// value (`M` in `namespace M { type T = 1; }` isn't one). + instantiated: HashSet, +} + +/// The body of a namespace declaration (`namespace NS { ... }`). +#[derive(Default)] +struct NamespaceBlock<'a> { + /// The namespace it belongs to. TypeScript merges namespaces declared with + /// the same name in the same place (`namespace NS {}` twice, or + /// `namespace A.B {}` and `namespace A { export namespace B {} }`), so + /// their bodies share the id. + namespace: usize, + /// The block it's written in, or `None` at the top level. + parent: Option, + /// Its declarations, collected like the top level's (with `export`ed + /// names in `exported`). + scope: FileScope<'a>, + /// The namespaces declared directly in it, with their ids. + namespaces: HashMap<&'a str, usize>, + /// The namespace's name, qualified from the top level (`A.B`). + name: String, + span: Span, +} + +/// What `typeof A.B.X` names, for `A.B` a namespace the file declares. +enum Qualified<'a> { + /// The value `X` declared (and exported) in a namespace block. + Declared(usize, &'a str), + /// The (instantiated) namespace `X`. + Namespace(&'a str), + /// Nothing TypeScript can resolve: `X` isn't exported, or doesn't exist. + Missing, +} + +/// A top-level variable binding. +enum Variable<'a> { + /// A name bound by a declaration with an initializer, and the path to it + /// when it's destructured (`const { a: [x] } = init` binds `x` at `a`, `0`). + Init(&'a Expression<'a>, std::vec::Vec>), + /// `declare const X: T`: evaluated from its type when that's a literal. + Declared(Option<&'a TSType<'a>>), + /// `let x;`: `undefined`. + Uninitialized, +} + +/// A step into a destructured initializer, as ngtsc's `visitBindingElement` reads it. +#[derive(Clone, Copy)] +enum PathKey<'a> { + Index(usize), + Key(&'a str), + /// A key ngtsc doesn't follow (a string literal or computed property name). + Unknown, +} + +/// An import binding: the module it's imported from and, unless it's a +/// namespace import, the name it's exported under. +#[derive(Clone, Copy)] +pub(crate) struct Import<'a> { + pub module: &'a str, + pub imported: Option<&'a str>, +} + +impl<'a> FileScope<'a> { + pub(crate) fn collect(program: &'a Program<'a>) -> Self { + let mut scope = Self::default(); + for stmt in &program.body { + match stmt { + Statement::ImportDeclaration(import) => { + let module = import.source.value.as_str(); + for spec in import.specifiers.iter().flatten() { + let (local, imported) = match spec { + ImportDeclarationSpecifier::ImportSpecifier(s) => { + let imported = match &s.imported { + ModuleExportName::IdentifierName(n) => n.name.as_str(), + ModuleExportName::IdentifierReference(n) => n.name.as_str(), + ModuleExportName::StringLiteral(n) => n.value.as_str(), + }; + (s.local.name.as_str(), Some(imported)) + } + ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => { + (s.local.name.as_str(), Some("default")) + } + ImportDeclarationSpecifier::ImportNamespaceSpecifier(s) => { + (s.local.name.as_str(), None) + } + }; + scope.imports.insert(local, Import { module, imported }); + } + } + Statement::ExportDeclaration(export) => { + scope.declaration(&export.declaration, true); + } + Statement::ExportNamedDeclaration(export) => { + for spec in &export.specifiers { + if let ModuleExportName::IdentifierReference(local) = &spec.local { + scope.exported.insert(local.name.as_str()); + } + } + } + Statement::ExportDefaultDeclaration(export) => match &export.declaration { + ExportDefaultDeclarationKind::ClassDeclaration(class) => { + scope.class(class, true); + } + ExportDefaultDeclarationKind::FunctionDeclaration(function) => { + scope.function(function, true); + } + _ => {} + }, + _ => { + if let Some(decl) = stmt.as_declaration() { + scope.declaration(decl, false); + } + } + } + } + for stmt in &program.body { + let (decl, exported) = match stmt { + Statement::ExportDeclaration(export) => (&export.declaration, true), + _ => match stmt.as_declaration() { + Some(decl) => (decl, false), + None => continue, + }, + }; + if let Declaration::TSNamespaceDeclaration(ns) = decl { + scope.namespace(ns, None, exported, false); + } + } + scope.mark_instantiated(); + scope + } + + /// Collects the namespace `ns`, declared in `parent` (`None`: at the top + /// level), and in an ambient context (`declare namespace`) if `ambient`. + fn namespace( + &mut self, + ns: &'a TSNamespaceDeclaration<'a>, + parent: Option, + exported: bool, + ambient: bool, + ) { + let name = ns.id.name.as_str(); + // Every declaration in an ambient namespace is exported, with or + // without `export` (TypeScript's `ExportContext`). + let ambient = ambient || ns.declare; + // Top-level namespaces merge by name, exported ones by the namespace + // they're a member of, and others only within their block. + let next = self.namespace_names.len(); + let id = match parent { + None => *self.namespace_ids.entry(name).or_insert(next), + Some(p) if exported => { + let key = (self.blocks[p].namespace, name); + *self.member_namespaces.entry(key).or_insert(next) + } + Some(p) => self.blocks[p].namespaces.get(name).copied().unwrap_or(next), + }; + if id == next { + self.namespace_names.push(name); + } + let qualified = match parent { + Some(p) => format!("{}.{name}", self.blocks[p].name), + None => name.to_string(), + }; + if let Some(p) = parent { + self.blocks[p].namespaces.insert(name, id); + if exported { + self.blocks[p].scope.exported.insert(name); + } + } + let index = self.blocks.len(); + self.blocks.push(NamespaceBlock { + namespace: id, + parent, + name: qualified, + span: ns.span, + ..NamespaceBlock::default() + }); + let statements = match &ns.body { + // `namespace A.B {}`: `B` is an exported member of `A`. + TSNamespaceDeclarationBody::TSNamespaceDeclaration(inner) => { + self.namespace(inner, Some(index), true, ambient); + return; + } + TSNamespaceDeclarationBody::TSModuleBlock(block) => &block.body, + }; + let mut scope = FileScope::default(); + let mut nested = std::vec::Vec::new(); + for stmt in statements { + let (decl, exported) = match stmt { + Statement::ExportDeclaration(export) => (&export.declaration, true), + _ => match stmt.as_declaration() { + Some(decl) => (decl, ambient), + None => continue, + }, + }; + scope.declaration(decl, exported); + match decl { + Declaration::FunctionDeclaration(f) => { + self.declared_in.insert(std::ptr::from_ref::(f) as usize, index); + } + Declaration::ClassDeclaration(class) => { + self.declared_in.insert(std::ptr::from_ref::(class) as usize, index); + for element in &class.body.body { + if let ClassElement::MethodDefinition(m) = element { + let method = std::ptr::from_ref::(&m.value) as usize; + self.declared_in.insert(method, index); + } + } + } + Declaration::TSNamespaceDeclaration(inner) => nested.push((&**inner, exported)), + _ => {} + } + } + self.blocks[index].scope = scope; + for (inner, exported) in nested { + self.namespace(inner, Some(index), exported, ambient); + } + } + + /// Marks the namespaces that declare a value as instantiated (see + /// [`Self::instantiated`]), once they're all collected. + fn mark_instantiated(&mut self) { + loop { + let before = self.instantiated.len(); + for block in &self.blocks { + let scope = &block.scope; + let values = !scope.variables.is_empty() + || !scope.functions.is_empty() + || !scope.classes.is_empty() + || !scope.enums.is_empty() + || block.namespaces.values().any(|id| self.instantiated.contains(id)); + if values && !self.instantiated.contains(&block.namespace) { + self.instantiated.insert(block.namespace); + } + } + if self.instantiated.len() == before { + break; + } + } + } + + /// Whether the namespace block `b` itself declares the value `name`: a + /// variable, function, class, enum, import-equals alias or instantiated + /// namespace. + fn block_declares(&self, b: usize, name: &str) -> bool { + let block = &self.blocks[b]; + block.namespaces.get(name).is_some_and(|id| self.instantiated.contains(id)) + || block.scope.aliases.contains_key(name) + || block.scope.variables.contains_key(name) + || block.scope.functions.contains_key(name) + || block.scope.enums.contains_key(name) + || block.scope.classes.contains_key(name) + } + + /// A block of the namespace `namespace` that exports the value or namespace `name`. + fn exporting_block(&self, namespace: usize, name: &str) -> Option { + (0..self.blocks.len()).find(|&b| { + self.blocks[b].namespace == namespace + && self.blocks[b].scope.exported.contains(name) + && self.block_declares(b, name) + }) + } + + /// The namespace block whose declaration `name` refers to inside block + /// `from`: the block's own, one its namespace exports from another of its + /// bodies, or the same for each enclosing block. `None` when the name is + /// resolved at the top level. + fn declaring_block(&self, from: usize, name: &str) -> Option { + let mut current = Some(from); + while let Some(b) = current { + if self.block_declares(b, name) { + return Some(b); + } + if let Some(other) = self.exporting_block(self.blocks[b].namespace, name) { + return Some(other); + } + current = self.blocks[b].parent; + } + None + } + + /// The id of the namespace `name` names in `from` (`None`: the top level), + /// as the head of a qualified name: a namespace, or an import-equals alias + /// of one, declared there or in an enclosing block. `None` when `name` is + /// something else. + fn namespace_of(&self, from: Option, name: &str, depth: u16) -> Option { + if depth > MAX_ALIASES { + return None; + } + let mut current = from; + while let Some(b) = current { + let block = &self.blocks[b]; + if let Some(id) = block.namespaces.get(name) { + return Some(*id); + } + if let Some(reference) = block.scope.aliases.get(name) { + return self.alias_namespace(Some(b), reference, depth + 1); + } + if self.block_declares(b, name) { + return None; + } + if let Some(id) = self.member_namespaces.get(&(block.namespace, name)) { + return Some(*id); + } + if let Some(other) = self.exporting_block(block.namespace, name) { + let reference = self.blocks[other].scope.aliases.get(name)?; + return self.alias_namespace(Some(other), reference, depth + 1); + } + current = block.parent; + } + if let Some(id) = self.namespace_ids.get(name) { + return Some(*id); + } + let reference = self.aliases.get(name)?; + self.alias_namespace(None, reference, depth + 1) + } + + /// The id of the namespace an import-equals alias declared in `block` + /// stands for, if it's one. + fn alias_namespace( + &self, + block: Option, + reference: &TSModuleReference<'_>, + depth: u16, + ) -> Option { + let parts = match reference { + TSModuleReference::IdentifierReference(id) => std::vec![id.name.as_str()], + TSModuleReference::QualifiedName(q) => qualified_parts(q)?, + TSModuleReference::ExternalModuleReference(_) => return None, + }; + let mut id = self.namespace_of(block, parts[0], depth)?; + for part in &parts[1..] { + id = *self.member_namespaces.get(&(id, *part))?; + } + Some(id) + } + + /// The declarations of the block `block`, or of the top level for `None`. + fn declarations(&self, block: Option) -> &FileScope<'a> { + block.map_or(self, |b| &self.blocks[b].scope) + } + + /// The block a function, static method or class declared in a namespace is + /// written in, or `None` for one declared at the top level. + fn block_of(&self, node: &T) -> Option { + self.declared_in.get(&(std::ptr::from_ref(node) as usize)).copied() + } + + /// What the qualified name `parts` (`["A", "B", "X"]` for `A.B.X`), + /// written in `from`, names when its head is a namespace the file + /// declares, as TypeScript resolves it: each part after the head is an + /// exported member of the namespace before it. `None` when the head, or a + /// part before the last, isn't a namespace. + fn qualified(&self, from: Option, parts: &[&'a str]) -> Option> { + let (head, rest) = parts.split_first()?; + let (last, middle) = rest.split_last()?; + let mut namespace = self.namespace_of(from, head, 0)?; + for part in middle { + match self.member_namespaces.get(&(namespace, *part)) { + Some(id) => namespace = *id, + None if self.exporting_block(namespace, part).is_some() => return None, + None => return Some(Qualified::Missing), + } + } + Some(match self.exporting_block(namespace, last) { + Some(b) if self.blocks[b].namespaces.contains_key(last) => Qualified::Namespace(last), + Some(b) => Qualified::Declared(b, last), + None => Qualified::Missing, + }) + } + + /// The qualified name of the namespace `expr` is written in, when it uses + /// that namespace's declarations (or an enclosing one's), which don't + /// resolve at the top level. + pub(crate) fn namespace_used_by(&self, expr: &Expression<'a>) -> Option<&str> { + let span = expr.span(); + let block = (0..self.blocks.len()) + .filter(|&b| self.blocks[b].span.contains_inclusive(span)) + .min_by_key(|&b| self.blocks[b].span.size())?; + let frame = Frame::at(Some(block)); + let mut uses = UsesFrame::new(&frame, self); + uses.visit_expression(expr); + uses.found.then(|| self.blocks[block].name.as_str()) + } + + fn declaration(&mut self, decl: &'a Declaration<'a>, exported: bool) { + let name = |scope: &mut Self, name: &'a str| { + if exported { + scope.exported.insert(name); + } + }; + match decl { + Declaration::VariableDeclaration(vars) => { + for var in &vars.declarations { + let mut bindings = std::vec::Vec::new(); + collect_bindings(&var.id, &mut std::vec::Vec::new(), &mut bindings); + for (id, path) in bindings { + let variable = match &var.init { + Some(init) => Variable::Init(init, path), + None if vars.declare => Variable::Declared( + var.type_annotation.as_ref().map(|t| &t.type_annotation), + ), + None => Variable::Uninitialized, + }; + self.variables.entry(id).or_insert(variable); + name(self, id); + } + } + } + Declaration::FunctionDeclaration(function) => self.function(function, exported), + Declaration::ClassDeclaration(class) => self.class(class, exported), + Declaration::TSEnumDeclaration(e) => { + let id = e.id.name.as_str(); + self.enums.entry(id).or_insert(e); + self.types.insert(id); + name(self, id); + } + Declaration::TSInterfaceDeclaration(i) => { + self.types.insert(i.id.name.as_str()); + name(self, i.id.name.as_str()); + } + Declaration::TSTypeAliasDeclaration(t) => { + self.types.insert(t.id.name.as_str()); + name(self, t.id.name.as_str()); + } + Declaration::TSImportEqualsDeclaration(alias) => { + self.aliases.entry(alias.id.name.as_str()).or_insert(&alias.module_reference); + name(self, alias.id.name.as_str()); + } + _ => {} + } + } + + fn function(&mut self, function: &'a Function<'a>, exported: bool) { + let Some(id) = &function.id else { return }; + let id = id.name.as_str(); + if exported { + self.exported.insert(id); + } + // Overloads are body-less declarations followed by the implementation. + let entry = self.functions.entry(id).or_insert((function, 0)); + if function.body.is_none() { + entry.1 += 1; + } + if function.body.is_some() || entry.0.body.is_none() { + entry.0 = function; + } + } + + fn class(&mut self, class: &'a Class<'a>, exported: bool) { + let Some(id) = &class.id else { return }; + let id = id.name.as_str(); + self.classes.insert(id, class); + self.types.insert(id); + if exported { + self.exported.insert(id); + } + } + + pub(crate) fn import(&self, name: &str) -> Option> { + self.imports.get(name).copied() + } +} + +/// The names a binding pattern declares, each with its path into the initializer. +fn collect_bindings<'a>( + pattern: &'a BindingPattern<'a>, + path: &mut std::vec::Vec>, + out: &mut std::vec::Vec<(&'a str, std::vec::Vec>)>, +) { + match pattern { + BindingPattern::BindingIdentifier(id) => out.push((id.name.as_str(), path.clone())), + // A default value isn't evaluated (`const { a = 1 } = {}` is `undefined`). + BindingPattern::AssignmentPattern(p) => collect_bindings(&p.left, path, out), + BindingPattern::ObjectPattern(p) => { + for prop in &p.properties { + path.push(match &prop.key { + PropertyKey::StaticIdentifier(id) if !prop.computed => { + PathKey::Key(id.name.as_str()) + } + _ => PathKey::Unknown, + }); + collect_bindings(&prop.value, path, out); + path.pop(); + } + // ngtsc reads `{ ...rest }` as the property named after the binding. + if let Some(rest) = &p.rest { + path.push(match &rest.argument { + BindingPattern::BindingIdentifier(id) => PathKey::Key(id.name.as_str()), + _ => PathKey::Unknown, + }); + collect_bindings(&rest.argument, path, out); + path.pop(); + } + } + BindingPattern::ArrayPattern(p) => { + for (i, element) in p.elements.iter().enumerate() { + if let Some(element) = element { + path.push(PathKey::Index(i)); + collect_bindings(element, path, out); + path.pop(); + } + } + // ... and `[a, ...rest]` as the element at the rest element's position. + if let Some(rest) = &p.rest { + path.push(PathKey::Index(p.elements.len())); + collect_bindings(&rest.argument, path, out); + path.pop(); + } + } + } +} + +/// The parts of a qualified name, head first (`["A", "B", "X"]` for `A.B.X`). +fn qualified_parts_of<'a>(name: &TSTypeName<'a>) -> Option> { + fn parts<'a>(name: &TSTypeName<'a>, out: &mut std::vec::Vec<&'a str>) -> Option<()> { + match name { + TSTypeName::IdentifierReference(id) => out.push(id.name.as_str()), + TSTypeName::QualifiedName(q) => { + parts(&q.left, out)?; + out.push(q.right.name.as_str()); + } + TSTypeName::ThisExpression(_) => return None, + } + Some(()) + } + let mut out = std::vec::Vec::new(); + parts(name, &mut out)?; + Some(out) +} + +/// [`qualified_parts_of`] for the name in `typeof A.B.X`. +fn qualified_parts<'a>(name: &TSQualifiedName<'a>) -> Option> { + let mut out = qualified_parts_of(&name.left)?; + out.push(name.right.name.as_str()); + Some(out) +} + +/// What a declaration reference resolves to. +#[derive(Clone)] +pub(crate) enum RefKind<'a> { + /// A function declaration or static method in this file, with the number of + /// body-less declarations (overloads) that come with it. + Function(&'a Function<'a>, usize), + Class(&'a Class<'a>), + /// An imported binding, `ns.x` through `import * as ns`, or a value + /// computed from one: its value is in another file. + Import, + /// An identifier with no declaration in this file that names a standard + /// ECMAScript global (see [`ES_GLOBALS`]). + Global, + /// Any other declaration (a `declare`d variable, an enum member, ...). + Other, +} + +/// A value as ngtsc's partial evaluator resolves it. +#[derive(Clone)] +pub(crate) enum Value<'a> { + Null, + Undefined, + Bool(bool), + Number(f64), + String(String), + Array(Vec>), + Object(Vec>), + /// `import * as ns`. + Module, + Reference { + name: String, + kind: RefKind<'a>, + }, + /// A member of an enum declared in this file (ngtsc's `EnumValue`): the + /// enum's name and the member's value. + Enum { + name: String, + value: Box>, + }, + /// `array.slice`, `array.concat` or `string.concat`, which ngtsc can call. + Builtin(Builtin<'a>), + Dynamic, +} + +/// ngtsc's `KnownFn`s (partial_evaluator/src/builtin.ts), bound to their receiver. +#[derive(Clone)] +pub(crate) enum Builtin<'a> { + ArraySlice(Vec>), + ArrayConcat(Vec>), + StringConcat(String), +} + +/// An object literal property: its key, value, and the source expression it came from. +#[derive(Clone)] +pub(crate) struct Prop<'a> { + pub key: String, + pub value: Value<'a>, + pub expr: Option<&'a Expression<'a>>, + /// `expr` as it can be written where the metadata is compiled, if it can + /// (see [`Evaluator::origin`]): the same expression outside a function, + /// the argument a parameter was passed, or `None` for an expression that + /// uses the parameters of the function it's written in. + pub origin: Option<&'a Expression<'a>>, +} + +impl<'a> Value<'a> { + pub(crate) fn prop(&self, key: &str) -> Option<&Prop<'a>> { + match self { + Value::Object(props) => props.iter().rev().find(|p| p.key == key), + _ => None, + } + } + + pub(crate) fn as_str(&self) -> Option<&str> { + match self { + Value::String(s) => Some(s), + _ => None, + } + } + + /// Whether this is an imported binding (or a value computed from one), which + /// can't be evaluated without reading another file. + pub(crate) fn is_import(&self) -> bool { + matches!(self, Value::Reference { kind: RefKind::Import, .. }) + } + + /// ngtsc's `describeResolvedType`, one level deep like its diagnostics. + pub(crate) fn describe(&self) -> String { + self.describe_to(1) + } + + fn describe_to(&self, depth: u16) -> String { + match self { + Value::Null => "null".into(), + Value::Undefined => "undefined".into(), + Value::Bool(_) => "boolean".into(), + Value::Number(_) => "number".into(), + Value::String(_) => "string".into(), + Value::Object(_) if depth == 0 => "object".into(), + Value::Object(props) if props.is_empty() => "{}".into(), + Value::Object(props) => { + let mut seen = HashSet::new(); + // Later duplicates win but keep the first key's position, like a Map. + let entries: std::vec::Vec = props + .iter() + .filter(|p| seen.insert(p.key.as_str())) + .map(|p| { + let value = self.prop(&p.key).map_or(&p.value, |p| &p.value); + format!("{}: {}", quote_key(&p.key), value.describe_to(depth - 1)) + }) + .collect(); + format!("{{ {} }}", entries.join("; ")) + } + Value::Array(_) if depth == 0 => "Array".into(), + Value::Array(items) => format!( + "[{}]", + items + .iter() + .map(|v| v.describe_to(depth - 1)) + .collect::>() + .join(", ") + ), + Value::Module => "(module)".into(), + Value::Reference { name, .. } | Value::Enum { name, .. } => name.clone(), + Value::Builtin(_) => "Function".into(), + Value::Dynamic => "(not statically analyzable)".into(), + } + } + + /// The chained line ngtsc's `createValueHasWrongTypeError` adds after a message. + pub(crate) fn wrong_type_suffix(&self) -> String { + match self { + Value::Dynamic => " Value could not be determined statically.".into(), + Value::Reference { name, .. } => format!(" Value is a reference to '{name}'."), + _ => format!(" Value is of type '{}'.", self.describe()), + } + } + + /// JavaScript truthiness. Arrays, objects and references are objects. + fn truthy(&self) -> bool { + match self { + Value::Null | Value::Undefined => false, + Value::Bool(b) => *b, + Value::Number(n) => *n != 0.0 && !n.is_nan(), + Value::String(s) => !s.is_empty(), + _ => true, + } + } + + /// A rough size, to charge for copying the value. + fn weight(&self) -> u32 { + match self { + Value::Array(items) => { + items.iter().fold(1u32, |sum, item| sum.saturating_add(item.weight())) + } + Value::Object(props) => { + props.iter().fold(1u32, |sum, p| sum.saturating_add(p.value.weight())) + } + Value::Enum { value, .. } => value.weight().saturating_add(1), + Value::Builtin(Builtin::ArraySlice(items) | Builtin::ArrayConcat(items)) => { + items.iter().fold(1u32, |sum, item| sum.saturating_add(item.weight())) + } + _ => 1, + } + } +} + +fn quote_key(key: &str) -> String { + if !key.is_empty() && key.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { + key.to_string() + } else { + format!("'{}'", key.replace('\'', "\\'")) + } +} + +/// The `declare var` / `declare function` globals of TypeScript's ES2022 +/// library (lib.es5.d.ts ... lib.es2022.*.d.ts). ngtsc resolves these to a +/// reference to their declaration; any other undeclared name is unknown to it. +/// DOM globals depend on the `lib` option and are treated as unknown. +const ES_GLOBALS: &[&str] = &[ + "AggregateError", + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "DataView", + "Date", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "Error", + "escape", + "eval", + "EvalError", + "FinalizationRegistry", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "isFinite", + "isNaN", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "parseFloat", + "parseInt", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "RegExp", + "Set", + "SharedArrayBuffer", + "String", + "Symbol", + "SyntaxError", + "TypeError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "unescape", + "URIError", + "WeakMap", + "WeakRef", + "WeakSet", +]; + +/// Bounds a chain of import-equals aliases (`import A = B; import B = A;`). +const MAX_ALIASES: u16 = 64; + +/// Bounds expression nesting (including calls), to keep deeply nested values +/// off the end of the stack; a debug build uses about 2 KiB per level. Chains of +/// consts don't nest (see [`Evaluator::evaluate_dependencies`]). +const MAX_DEPTH: u16 = 500; + +/// Bounds the total work of one evaluator: values that grow exponentially +/// (`const B = [...A, ...A]`, recursive calls) become dynamic instead of hanging. +const FUEL: u32 = 1 << 22; + +/// Parameter bindings of the function being evaluated, or the members of the +/// enum whose initializers are being evaluated. +#[derive(Default)] +struct Frame<'a> { + bindings: HashMap<&'a str, Binding<'a>>, + /// Evaluating the body of a called function, whose parameters, `this` and + /// `arguments` don't exist where the metadata is compiled. + in_call: bool, + /// The namespace block the code being evaluated is written in, whose + /// names resolve before the top level's (`None`: the top level). + block: Option, +} + +impl Frame<'_> { + /// The frame for code written directly in `block`. + fn at(block: Option) -> Self { + Self { block, ..Self::default() } + } +} + +/// A variable or enum the evaluator caches: the namespace block that declares +/// it (`None`: the top level) and its name. +type Slot<'a> = (Option, &'a str); + +/// A name bound in a [`Frame`]. +#[derive(Clone)] +struct Binding<'a> { + value: Value<'a>, + /// For a parameter, the argument it was passed, when that can be written + /// where the metadata is compiled (see [`Evaluator::origin`]). + origin: Option<&'a Expression<'a>>, + /// For a rest parameter, where each of its elements was written, like + /// `origin` (see [`Evaluator::element_origins`]). + elements: Option>>>, +} + +impl<'a> Binding<'a> { + fn new(value: Value<'a>, origin: Option<&'a Expression<'a>>) -> Self { + Self { value, origin, elements: None } + } +} + +/// A property key or index, as ngtsc's `accessHelper` receives it. +#[derive(Clone, Copy)] +enum Key<'k> { + Str(&'k str), + Num(f64), +} + +pub(crate) struct Evaluator<'s, 'a> { + /// The file's declarations are only looked at when an identifier is resolved. + consts: &'s super::StringConsts<'a>, + /// Variables and enums already evaluated (with their weight), so a chain + /// of consts that each reference the previous one several times stays + /// linear. `None` while one is being evaluated, which makes a circular + /// reference dynamic (ngtsc overflows its stack on those). + variables: RefCell, Option<(Value<'a>, u32)>>>, + fuel: Cell, +} + +impl<'s, 'a> Evaluator<'s, 'a> { + pub(crate) fn new(consts: &'s super::StringConsts<'a>) -> Self { + Self { consts, variables: RefCell::default(), fuel: Cell::new(FUEL) } + } + + pub(crate) fn evaluate(&self, expr: &'a Expression<'a>) -> Value<'a> { + self.eval(expr, 0, &Frame::default()) + } + + /// `expr` (evaluated in `frame`) as it can be written where the metadata is + /// compiled. Outside a called function that's `expr` itself. In one, a + /// parameter stands for the argument it was passed (ngtsc emits the + /// identifier the argument's reference was first named by, or the argument + /// itself); any other expression is kept only if it doesn't use the + /// function's parameters, `this` or `arguments`, which don't exist there. + /// + /// The same goes for an expression written in a namespace: it's kept only + /// if it doesn't use the namespace's declarations, which aren't in scope + /// at the top level either. + fn origin(&self, expr: &'a Expression<'a>, frame: &Frame<'a>) -> Option<&'a Expression<'a>> { + if !frame.in_call && frame.block.is_none() { + return Some(expr); + } + if let Expression::Identifier(id) = expr + && let Some(binding) = frame.bindings.get(id.name.as_str()) + { + return binding.origin; + } + // `args[1]`, with `args` a parameter: the element's own origin. + if let Expression::ComputedMemberExpression(m) = expr + && let Expression::Identifier(id) = &m.object + && frame.bindings.contains_key(id.name.as_str()) + { + return match self.eval(&m.expression, 0, frame) { + Value::Number(i) if i >= 0.0 && i.fract() == 0.0 => self + .element_origins(&m.object, frame) + .and_then(|elements| elements.get(i as usize).copied().flatten()), + _ => None, + }; + } + let mut uses = UsesFrame::new(frame, self.consts.scope()); + uses.visit_expression(expr); + (!uses.found).then_some(expr) + } + + /// Where each element of the array `expr` (evaluated in `frame`) was + /// written, as [`Self::origin`] gives it, when that's known: for an array + /// literal, a top-level variable initialized with one, or a parameter + /// passed one (a rest parameter gets its arguments'). One entry per value + /// [`Self::spread`] gives for `expr`. + fn element_origins( + &self, + expr: &'a Expression<'a>, + frame: &Frame<'a>, + ) -> Option>>> { + self.element_origins_at(expr, frame, 0) + } + + fn element_origins_at( + &self, + expr: &'a Expression<'a>, + frame: &Frame<'a>, + depth: u16, + ) -> Option>>> { + // Bounded like `eval`: cycles (`const A = [...A]`) and arrays that grow + // exponentially give up instead of overflowing or hanging. + if depth > MAX_DEPTH || !self.spend(1) { + return None; + } + let depth = depth + 1; + match expr { + Expression::ParenthesizedExpression(e) => { + self.element_origins_at(&e.expression, frame, depth) + } + Expression::TSAsExpression(e) => self.element_origins_at(&e.expression, frame, depth), + Expression::TSNonNullExpression(e) => { + self.element_origins_at(&e.expression, frame, depth) + } + Expression::ArrayExpression(arr) => { + let mut origins = std::vec::Vec::new(); + for el in &arr.elements { + match el { + ArrayExpressionElement::SpreadElement(spread) => { + let inner = self.element_origins_at(&spread.argument, frame, depth)?; + if !self.spend(inner.len() as u32) { + return None; + } + origins.extend(inner); + } + ArrayExpressionElement::Elision(_) => origins.push(None), + _ => origins.push(self.origin(el.to_expression(), frame)), + } + } + Some(origins) + } + Expression::Identifier(id) => { + let name = id.name.as_str(); + if let Some(binding) = frame.bindings.get(name) { + return match &binding.elements { + Some(elements) => Some(elements.clone()), + // An origin is written where the metadata is compiled. + None => self.element_origins_at(binding.origin?, &Frame::default(), depth), + }; + } + let scope = self.consts.scope(); + // A namespace's declarations are written where they don't + // resolve at the top level. + if frame.block.and_then(|from| scope.declaring_block(from, name)).is_some() { + return None; + } + match scope.variables.get(name)? { + // Top-level declarations don't see the caller's parameters. + Variable::Init(init, path) if path.is_empty() => { + self.element_origins_at(init, &Frame::default(), depth) + } + _ => None, + } + } + _ => None, + } + } + + /// Use up `amount` of the fuel; `false` once it has run out. + fn spend(&self, amount: u32) -> bool { + let left = self.fuel.get(); + self.fuel.set(left.saturating_sub(amount)); + left >= amount + } + + /// Evaluates `expr`. Each kind of expression has its own function, so this + /// one's stack frame (paid on every level of nesting) stays small. + fn eval(&self, expr: &'a Expression<'a>, depth: u16, frame: &Frame<'a>) -> Value<'a> { + if depth > MAX_DEPTH || !self.spend(1) { + return Value::Dynamic; + } + let depth = depth + 1; + match expr { + Expression::NullLiteral(_) => Value::Null, + Expression::BooleanLiteral(b) => Value::Bool(b.value), + Expression::NumericLiteral(n) => Value::Number(n.value), + Expression::StringLiteral(s) => Value::String(s.value.to_string()), + Expression::TemplateLiteral(tpl) => self.template(tpl, depth, frame), + Expression::Identifier(id) => self.identifier(id.name.as_str(), depth, frame), + Expression::ParenthesizedExpression(e) => self.eval(&e.expression, depth, frame), + Expression::TSAsExpression(e) => self.eval(&e.expression, depth, frame), + Expression::TSNonNullExpression(e) => self.eval(&e.expression, depth, frame), + Expression::ArrayExpression(arr) => self.array(arr, depth, frame), + Expression::ObjectExpression(obj) => self.object(obj, depth, frame), + Expression::StaticMemberExpression(m) => self.static_member_expr(m, depth, frame), + Expression::ComputedMemberExpression(m) => self.computed_member_expr(m, depth, frame), + // `a?.b` and `f?.()` evaluate like `a.b` and `f()`. + Expression::ChainExpression(chain) => match &chain.expression { + ChainElement::CallExpression(call) => self.call(call, depth, frame), + ChainElement::TSNonNullExpression(e) => self.eval(&e.expression, depth, frame), + ChainElement::StaticMemberExpression(m) => self.static_member_expr(m, depth, frame), + ChainElement::ComputedMemberExpression(m) => { + self.computed_member_expr(m, depth, frame) + } + ChainElement::PrivateFieldExpression(_) => Value::Dynamic, + }, + Expression::CallExpression(call) => self.call(call, depth, frame), + Expression::ConditionalExpression(c) => self.conditional(c, depth, frame), + Expression::UnaryExpression(u) => self.unary_expr(u, depth, frame), + Expression::BinaryExpression(b) => self.binary_expr(b, depth, frame), + Expression::LogicalExpression(l) => self.logical_expr(l, depth, frame), + // Everything else is syntax ngtsc doesn't evaluate: `satisfies`, + // `x`, functions, `typeof`, `new`, ... + _ => Value::Dynamic, + } + } + + #[inline(never)] + fn template(&self, tpl: &'a TemplateLiteral<'a>, depth: u16, frame: &Frame<'a>) -> Value<'a> { + let mut out = String::new(); + for (i, quasi) in tpl.quasis.iter().enumerate() { + let Some(cooked) = &quasi.value.cooked else { return Value::Dynamic }; + out.push_str(cooked); + if let Some(e) = tpl.expressions.get(i) { + match literal(self.eval(e, depth, frame)) { + Value::Dynamic => return Value::Dynamic, + value if value.is_import() => return value, + value => out.push_str(&to_js_string(&value)), + } + } + } + Value::String(out) + } + + #[inline(never)] + fn array(&self, arr: &'a ArrayExpression<'a>, depth: u16, frame: &Frame<'a>) -> Value<'a> { + let mut items = std::vec::Vec::new(); + for el in &arr.elements { + match el { + ArrayExpressionElement::SpreadElement(spread) => { + items.extend(self.spread(&spread.argument, depth, frame)); + } + ArrayExpressionElement::Elision(_) => items.push(Value::Dynamic), + _ => items.push(self.eval(el.to_expression(), depth, frame)), + } + } + Value::Array(items) + } + + #[inline(never)] + fn object(&self, obj: &'a ObjectExpression<'a>, depth: u16, frame: &Frame<'a>) -> Value<'a> { + let mut props = std::vec::Vec::new(); + for prop in &obj.properties { + match prop { + ObjectPropertyKind::ObjectProperty(p) => { + if p.method || !matches!(p.kind, PropertyKind::Init) { + return Value::Dynamic; + } + let Some(key) = self.property_key(&p.key, p.computed, depth, frame) else { + return Value::Dynamic; + }; + let value = self.eval(&p.value, depth, frame); + let origin = self.origin(&p.value, frame); + props.push(Prop { key, value, expr: Some(&p.value), origin }); + } + ObjectPropertyKind::SpreadProperty(spread) => { + match self.eval(&spread.argument, depth, frame) { + Value::Object(inner) if self.spend(inner.len() as u32) => { + props.extend(inner); + } + value if value.is_import() => return value, + _ => return Value::Dynamic, + } + } + } + } + Value::Object(props) + } + + #[inline(never)] + fn static_member_expr( + &self, + m: &'a StaticMemberExpression<'a>, + depth: u16, + frame: &Frame<'a>, + ) -> Value<'a> { + let object = self.eval(&m.object, depth, frame); + self.member(object, Key::Str(m.property.name.as_str()), depth) + } + + #[inline(never)] + fn computed_member_expr( + &self, + m: &'a ComputedMemberExpression<'a>, + depth: u16, + frame: &Frame<'a>, + ) -> Value<'a> { + let object = self.eval(&m.object, depth, frame); + if matches!(object, Value::Dynamic) { + return Value::Dynamic; + } + match self.eval(&m.expression, depth, frame) { + Value::String(key) => self.member(object, Key::Str(&key), depth), + Value::Number(n) => self.member(object, Key::Num(n), depth), + key if key.is_import() => key, + _ => Value::Dynamic, + } + } + + #[inline(never)] + fn conditional( + &self, + c: &'a ConditionalExpression<'a>, + depth: u16, + frame: &Frame<'a>, + ) -> Value<'a> { + match self.eval(&c.test, depth, frame) { + Value::Dynamic => Value::Dynamic, + test if test.is_import() => test, + test if test.truthy() => self.eval(&c.consequent, depth, frame), + _ => self.eval(&c.alternate, depth, frame), + } + } + + #[inline(never)] + fn unary_expr(&self, u: &'a UnaryExpression<'a>, depth: u16, frame: &Frame<'a>) -> Value<'a> { + if !matches!( + u.operator, + UnaryOperator::UnaryNegation + | UnaryOperator::UnaryPlus + | UnaryOperator::LogicalNot + | UnaryOperator::BitwiseNot + ) { + return Value::Dynamic; + } + match self.eval(&u.argument, depth, frame) { + Value::Dynamic => Value::Dynamic, + value if value.is_import() => value, + value => unary(u.operator, &value), + } + } + + #[inline(never)] + fn binary_expr(&self, b: &'a BinaryExpression<'a>, depth: u16, frame: &Frame<'a>) -> Value<'a> { + if matches!(b.operator, BinaryOperator::In | BinaryOperator::Instanceof) { + return Value::Dynamic; + } + // Operands must be primitives (an enum member counts as its value). + let left = literal(self.eval(&b.left, depth, frame)); + let right = literal(self.eval(&b.right, depth, frame)); + match (left, right) { + (Value::Dynamic, _) | (_, Value::Dynamic) => Value::Dynamic, + (value, _) | (_, value) if value.is_import() => value, + (left, right) => binary(b.operator, &left, &right), + } + } + + #[inline(never)] + fn logical_expr( + &self, + l: &'a LogicalExpression<'a>, + depth: u16, + frame: &Frame<'a>, + ) -> Value<'a> { + // `??` isn't one of ngtsc's operators. + if l.operator == LogicalOperator::Coalesce { + return Value::Dynamic; + } + // ngtsc evaluates both operands, and either being dynamic makes the + // result dynamic. An import only matters when it's the left operand + // (which picks the result) or the operand picked: `'a' || NAME` is + // `'a'` whatever `NAME` is. + let left = self.eval(&l.left, depth, frame); + let right = self.eval(&l.right, depth, frame); + match (left, right) { + (Value::Dynamic, _) | (_, Value::Dynamic) => Value::Dynamic, + (left, _) if left.is_import() => left, + (left, right) => match (l.operator, left.truthy()) { + (LogicalOperator::And, true) | (LogicalOperator::Or, false) => right, + _ => left, + }, + } + } + + /// The elements `...expr` adds to an array or an argument list. + fn spread( + &self, + expr: &'a Expression<'a>, + depth: u16, + frame: &Frame<'a>, + ) -> std::vec::Vec> { + match self.eval(expr, depth, frame) { + Value::Array(inner) if self.spend(inner.len() as u32) => inner, + value if value.is_import() => vec![value], + // ngtsc marks only this element as dynamic, not the whole array. + _ => vec![Value::Dynamic], + } + } + + fn property_key( + &self, + key: &'a PropertyKey<'a>, + computed: bool, + depth: u16, + frame: &Frame<'a>, + ) -> Option { + if computed { + // A computed key has to evaluate to a string (`{ [1]: x }` doesn't). + return match self.eval(key.to_expression(), depth, frame) { + Value::String(s) => Some(s), + _ => None, + }; + } + match key { + PropertyKey::StaticIdentifier(id) => Some(id.name.to_string()), + PropertyKey::StringLiteral(s) => Some(s.value.to_string()), + PropertyKey::NumericLiteral(n) => Some(format_number_like_js(n.value)), + _ => None, + } + } + + fn identifier(&self, name: &'a str, depth: u16, frame: &Frame<'a>) -> Value<'a> { + if let Some(binding) = frame.bindings.get(name) { + return binding.value.clone(); + } + let scope = self.consts.scope(); + // Inside a namespace, its declarations (and its enclosing ones') come first. + if let Some(block) = frame.block.and_then(|from| scope.declaring_block(from, name)) { + return self.declared(Some(block), name, depth).unwrap_or(Value::Dynamic); + } + if let Some(value) = self.declared(None, name, depth) { + return value; + } + if let Some(import) = scope.imports.get(name) { + return match import.imported { + Some(imported) => { + let name = if imported == "default" { name } else { imported }; + Value::Reference { name: name.into(), kind: RefKind::Import } + } + None => Value::Module, + }; + } + match name { + "undefined" => Value::Undefined, + _ if ES_GLOBALS.contains(&name) => { + Value::Reference { name: name.into(), kind: RefKind::Global } + } + // ngtsc can't find a declaration for it. + _ => Value::Dynamic, + } + } + + /// The value of `name` when `block` (`None`: the top level) declares it + /// as a variable, enum, function, class or namespace. A namespace is a + /// reference to it, and an `import x = ...` alias in a namespace is dynamic. + fn declared(&self, block: Option, name: &'a str, depth: u16) -> Option> { + let file = self.consts.scope(); + let scope = file.declarations(block); + if scope.variables.contains_key(name) || scope.enums.contains_key(name) { + return Some(self.stored((block, name), depth)); + } + if let Some((function, overloads)) = scope.functions.get(name) { + let kind = RefKind::Function(function, *overloads); + return Some(Value::Reference { name: name.into(), kind }); + } + if let Some(class) = scope.classes.get(name) { + return Some(Value::Reference { name: name.into(), kind: RefKind::Class(class) }); + } + if let Some(reference) = scope.aliases.get(name) { + return Some(self.alias(block, reference, depth)); + } + let namespace = match block { + Some(b) => file.blocks[b] + .namespaces + .get(name) + .or_else(|| file.member_namespaces.get(&(file.blocks[b].namespace, name))), + None => file.namespace_ids.get(name), + }; + namespace + .filter(|id| file.instantiated.contains(id)) + .map(|_| Value::Reference { name: name.into(), kind: RefKind::Other }) + } + + /// The value of an import-equals alias declared in `block`: what its + /// entity names (`import f = NS.f` is `NS.f`), resolved where the alias + /// is written. Another module's (`import m = require('m')`) is dynamic. + fn alias( + &self, + block: Option, + reference: &'a TSModuleReference<'a>, + depth: u16, + ) -> Value<'a> { + if depth > MAX_DEPTH || !self.spend(1) { + return Value::Dynamic; + } + let depth = depth + 1; + let scope = self.consts.scope(); + let parts = match reference { + // TypeScript resolves `import A = B` only as a namespace. + TSModuleReference::IdentifierReference(id) => { + return match scope.namespace_of(block, id.name.as_str(), 0) { + Some(ns) if scope.instantiated.contains(&ns) => { + let name = scope.namespace_names[ns]; + Value::Reference { name: name.into(), kind: RefKind::Other } + } + _ => Value::Dynamic, + }; + } + TSModuleReference::QualifiedName(q) => qualified_parts(q), + TSModuleReference::ExternalModuleReference(_) => None, + }; + match parts.and_then(|parts| scope.qualified(block, &parts)) { + Some(Qualified::Declared(b, member)) => { + self.declared(Some(b), member, depth).unwrap_or(Value::Dynamic) + } + Some(Qualified::Namespace(name)) => { + Value::Reference { name: name.into(), kind: RefKind::Other } + } + _ => Value::Dynamic, + } + } + + /// A variable's or enum's value, evaluated once. + fn stored(&self, slot: Slot<'a>, depth: u16) -> Value<'a> { + if let Some(cached) = self.variables.borrow().get(&slot) { + return match cached { + Some((value, weight)) if self.spend(*weight) => value.clone(), + _ => Value::Dynamic, + }; + } + self.evaluate_dependencies(slot, depth); + self.evaluate_top_level(slot, depth) + } + + /// Where the variable or enum `name`, mentioned in code written in + /// `block`, is declared, if it's one. + fn slot(&self, block: Option, name: &'a str) -> Option> { + let scope = self.consts.scope(); + let block = block.and_then(|from| scope.declaring_block(from, name)); + let declarations = scope.declarations(block); + (declarations.variables.contains_key(name) || declarations.enums.contains_key(name)) + .then_some((block, name)) + } + + /// Evaluates the top-level bindings `root`'s initializer mentions (and theirs) + /// before `root`, deepest first, so a long chain of consts + /// (`const X2 = [...X1]`) is evaluated one link at a time instead of + /// recursively through the whole chain. + fn evaluate_dependencies(&self, root: Slot<'a>, depth: u16) { + let scope = self.consts.scope(); + let pending = |slot: &Slot<'a>| !self.variables.borrow().contains_key(slot); + let mut seen = HashSet::from([root]); + let mut stack = vec![(root, false)]; + while let Some((slot, ready)) = stack.pop() { + if ready { + if slot != root && pending(&slot) { + self.evaluate_top_level(slot, depth); + } + continue; + } + stack.push((slot, true)); + let mut mentions = Mentions(std::vec::Vec::new()); + let declarations = scope.declarations(slot.0); + match (declarations.variables.get(slot.1), declarations.enums.get(slot.1)) { + (Some(Variable::Init(init, _)), _) => mentions.visit_expression(init), + (_, Some(e)) => mentions.visit_ts_enum_declaration(e), + _ => {} + } + for dep in mentions.0 { + if let Some(dep) = self.slot(slot.0, dep) + && pending(&dep) + && seen.insert(dep) + { + stack.push((dep, false)); + } + } + } + } + + fn evaluate_top_level(&self, slot: Slot<'a>, depth: u16) -> Value<'a> { + let scope = self.consts.scope().declarations(slot.0); + self.variables.borrow_mut().insert(slot, None); + let value = match (scope.variables.get(slot.1), scope.enums.get(slot.1)) { + (Some(variable), _) => self.variable(slot, variable, depth), + (_, Some(e)) => self.enumeration(e, depth, slot.0), + _ => Value::Dynamic, + }; + let weight = value.weight(); + self.variables.borrow_mut().insert(slot, Some((value.clone(), weight))); + value + } + + fn variable(&self, slot: Slot<'a>, variable: &Variable<'a>, depth: u16) -> Value<'a> { + let (block, name) = slot; + match variable { + // Declarations don't see the caller's parameters. + Variable::Init(init, path) => { + let mut value = self.eval(init, depth, &Frame::at(block)); + for key in path { + value = match key { + PathKey::Index(i) => self.member(value, Key::Num(*i as f64), depth), + PathKey::Key(k) => self.member(value, Key::Str(k), depth), + PathKey::Unknown => Value::Dynamic, + }; + if matches!(value, Value::Dynamic) { + break; + } + } + value + } + // A literal type is its value; otherwise it's a reference to the variable. + Variable::Declared(ty) => match ty.map(|ty| self.eval_type(ty, depth, block)) { + Some(value) if !matches!(value, Value::Dynamic) => value, + _ => Value::Reference { name: name.into(), kind: RefKind::Other }, + }, + Variable::Uninitialized => Value::Undefined, + } + } + + /// ngtsc's `visitEnumDeclaration`: a map of members to enum values. A member + /// without an initializer is its index (not the previous member plus one). + fn enumeration( + &self, + e: &'a TSEnumDeclaration<'a>, + depth: u16, + block: Option, + ) -> Value<'a> { + let name_of = |member: &'a TSEnumMemberName<'a>| match member { + TSEnumMemberName::Identifier(id) => Some(id.name.as_str()), + TSEnumMemberName::String(s) | TSEnumMemberName::ComputedString(s) => { + Some(s.value.as_str()) + } + TSEnumMemberName::ComputedTemplateString(t) => { + t.quasis.first().and_then(|q| q.value.cooked.as_ref()).map(oxc_str::Str::as_str) + } + }; + // Inside the initializers, a member's name refers to the member. + let bindings = e + .body + .members + .iter() + .filter_map(|m| name_of(&m.id)) + .map(|n| { + let value = Value::Reference { name: n.into(), kind: RefKind::Other }; + (n, Binding::new(value, None)) + }) + .collect(); + let frame = Frame { bindings, in_call: false, block }; + let props = e + .body + .members + .iter() + .enumerate() + .filter_map(|(index, member)| { + let key = name_of(&member.id)?; + let value = match &member.initializer { + Some(init) => self.eval(init, depth, &frame), + None => Value::Number(index as f64), + }; + let value = Value::Enum { name: e.id.name.to_string(), value: Box::new(value) }; + Some(Prop { key: key.to_string(), value, expr: None, origin: None }) + }) + .collect(); + Value::Object(props) + } + + /// ngtsc's `visitType`, for `declare const X: T` written in `block` + /// (`None`: at the top level). + fn eval_type(&self, ty: &'a TSType<'a>, depth: u16, block: Option) -> Value<'a> { + if depth > MAX_DEPTH || !self.spend(1) { + return Value::Dynamic; + } + let depth = depth + 1; + match ty { + TSType::TSNullKeyword(_) => Value::Null, + TSType::TSLiteralType(lit) => match &lit.literal { + TSLiteral::BooleanLiteral(b) => Value::Bool(b.value), + TSLiteral::NumericLiteral(n) => Value::Number(n.value), + TSLiteral::StringLiteral(s) => Value::String(s.value.to_string()), + TSLiteral::TemplateLiteral(t) if t.expressions.is_empty() => t + .quasis + .first() + .and_then(|q| q.value.cooked.as_ref()) + .map_or(Value::Dynamic, |c| Value::String(c.to_string())), + TSLiteral::UnaryExpression(u) => { + match self.eval(&u.argument, depth, &Frame::at(block)) { + Value::Dynamic => Value::Dynamic, + value => unary(u.operator, &value), + } + } + _ => Value::Dynamic, + }, + TSType::TSTupleType(tuple) => Value::Array( + tuple + .element_types + .iter() + .map(|el| self.eval_tuple_element(el, depth, block)) + .collect(), + ), + TSType::TSNamedTupleMember(member) => { + self.eval_tuple_element(&member.element_type, depth, block) + } + TSType::TSTypeOperatorType(op) if op.operator == TSTypeOperatorOperator::Readonly => { + self.eval_type(&op.type_annotation, depth, block) + } + TSType::TSTypeQuery(query) => match &query.expr_name { + TSTypeQueryExprName::IdentifierReference(id) => { + self.identifier(id.name.as_str(), depth, &Frame::at(block)) + } + TSTypeQueryExprName::QualifiedName(name) => { + // `typeof NS.X`, for a namespace `NS` the file declares, + // is the value `X` the namespace exports. + let scope = self.consts.scope(); + match qualified_parts(name).and_then(|parts| scope.qualified(block, &parts)) { + Some(Qualified::Declared(b, member)) => { + return self.declared(Some(b), member, depth).unwrap_or(Value::Dynamic); + } + Some(Qualified::Namespace(name)) => { + return Value::Reference { name: name.into(), kind: RefKind::Other }; + } + Some(Qualified::Missing) => return Value::Dynamic, + None => {} + } + // Otherwise, `typeof E.A` is a reference to the declaration of `A`. + let mut left = &name.left; + while let TSTypeName::QualifiedName(q) = left { + left = &q.left; + } + let imported = matches!(left, TSTypeName::IdentifierReference(id) + if self.consts.scope().imports.contains_key(id.name.as_str())); + let kind = if imported { RefKind::Import } else { RefKind::Other }; + Value::Reference { name: name.right.name.to_string(), kind } + } + _ => Value::Dynamic, + }, + // A type naming a class is a reference to it. + TSType::TSTypeReference(reference) => { + let scope = self.consts.scope(); + let (block, name) = match &reference.type_name { + TSTypeName::IdentifierReference(id) => { + let name = id.name.as_str(); + (block.and_then(|from| scope.declaring_block(from, name)), name) + } + TSTypeName::QualifiedName(q) => { + match qualified_parts_of(&reference.type_name) + .and_then(|parts| scope.qualified(block, &parts)) + { + Some(Qualified::Declared(b, name)) => (Some(b), name), + Some(_) => return Value::Dynamic, + None => (None, q.right.name.as_str()), + } + } + TSTypeName::ThisExpression(_) => return Value::Dynamic, + }; + match scope.declarations(block).classes.get(name) { + Some(class) => { + Value::Reference { name: name.into(), kind: RefKind::Class(class) } + } + None => Value::Dynamic, + } + } + _ => Value::Dynamic, + } + } + + fn eval_tuple_element( + &self, + element: &'a TSTupleElement<'a>, + depth: u16, + block: Option, + ) -> Value<'a> { + element.as_ts_type().map_or(Value::Dynamic, |ty| self.eval_type(ty, depth, block)) + } + + /// ngtsc's `visitCallExpression`: builtins, and same-file functions whose + /// body is a single `return`. + fn call(&self, call: &'a CallExpression<'a>, depth: u16, frame: &Frame<'a>) -> Value<'a> { + let (function, overloads) = match self.eval(&call.callee, depth, frame) { + Value::Builtin(builtin) => { + let args = self.arguments(call, depth, frame); + return self.call_builtin(builtin, args); + } + Value::Reference { kind: RefKind::Function(function, overloads), .. } => { + (function, overloads) + } + // An imported function runs in another file. + callee if callee.is_import() => return callee, + _ => return Value::Dynamic, + }; + // A body-less first declaration (an overload, `declare function`) can't be evaluated. + let Some(body) = function.body.as_ref().filter(|_| overloads == 0) else { + return Value::Dynamic; + }; + let [Statement::ReturnStatement(ret)] = body.statements.as_slice() else { + return Value::Dynamic; + }; + if !body.directives.is_empty() { + return Value::Dynamic; + } + // Each argument's value, and where it was written. + let mut args = std::vec::Vec::new(); + let mut origins = std::vec::Vec::new(); + for arg in &call.arguments { + if let Argument::SpreadElement(spread) = arg { + let values = self.spread(&spread.argument, depth, frame); + let known = self + .element_origins(&spread.argument, frame) + .filter(|known| known.len() == values.len()); + origins.extend(known.unwrap_or_else(|| vec![None; values.len()])); + args.extend(values); + } else { + let expr = arg.to_expression(); + args.push(self.eval(expr, depth, frame)); + origins.push(self.origin(expr, frame)); + } + } + // ngtsc counts a `this` parameter as the first one. + let offset = usize::from(function.this_param.is_some()); + // Its body sees the declarations where it's written. + let block = self.consts.scope().block_of(function); + let mut scope = Frame { bindings: HashMap::new(), in_call: true, block }; + for (i, param) in function.params.items.iter().enumerate() { + let binding = match args.get(i + offset) { + None | Some(Value::Undefined) if param.initializer.is_some() => { + param.initializer.as_ref().map(|init| { + Binding::new(self.eval(init, depth, &scope), self.origin(init, &scope)) + }) + } + arg => arg.map(|value| { + Binding::new(value.clone(), origins.get(i + offset).copied().flatten()) + }), + }; + let binding = binding.unwrap_or(Binding::new(Value::Undefined, None)); + bind(&mut scope, ¶m.pattern, binding); + } + if let Some(rest) = &function.params.rest { + let start = function.params.items.len() + offset; + let rest_args = args.get(start..).map_or_else(std::vec::Vec::new, <[_]>::to_vec); + let elements = origins.get(start..).map_or_else(std::vec::Vec::new, <[_]>::to_vec); + let binding = + Binding { value: Value::Array(rest_args), origin: None, elements: Some(elements) }; + bind(&mut scope, &rest.rest.argument, binding); + } + ret.argument.as_ref().map_or(Value::Undefined, |e| self.eval(e, depth, &scope)) + } + + fn arguments( + &self, + call: &'a CallExpression<'a>, + depth: u16, + frame: &Frame<'a>, + ) -> std::vec::Vec> { + let mut args = std::vec::Vec::new(); + for arg in &call.arguments { + match arg { + Argument::SpreadElement(spread) => { + args.extend(self.spread(&spread.argument, depth, frame)); + } + _ => args.push(self.eval(arg.to_expression(), depth, frame)), + } + } + args + } + + fn call_builtin(&self, builtin: Builtin<'a>, args: std::vec::Vec>) -> Value<'a> { + match builtin { + Builtin::ArraySlice(items) if args.is_empty() => Value::Array(items), + Builtin::ArraySlice(_) => Value::Dynamic, + Builtin::ArrayConcat(mut items) => { + for arg in args { + match arg { + Value::Array(inner) if self.spend(inner.len() as u32) => { + items.extend(inner); + } + Value::Array(_) => return Value::Dynamic, + arg => items.push(arg), + } + } + Value::Array(items) + } + Builtin::StringConcat(mut s) => { + for arg in args { + let arg = match arg { + Value::Enum { value, .. } => *value, + arg => arg, + }; + match arg { + Value::Null + | Value::Undefined + | Value::Bool(_) + | Value::Number(_) + | Value::String(_) => s.push_str(&to_js_string(&arg)), + arg if arg.is_import() => return arg, + _ => return Value::Dynamic, + } + } + Value::String(s) + } + } + } + + /// ngtsc's `accessHelper`: `object.key` / `object[key]`. + fn member(&self, object: Value<'a>, key: Key<'_>, depth: u16) -> Value<'a> { + let key_str = || match key { + Key::Str(s) => s.to_string(), + Key::Num(n) => format_number_like_js(n), + }; + match object { + Value::Object(props) => { + let key = key_str(); + props.into_iter().rev().find(|p| p.key == key).map_or(Value::Undefined, |p| p.value) + } + Value::Array(items) => match key { + Key::Str("length") => Value::Number(items.len() as f64), + Key::Str("slice") => Value::Builtin(Builtin::ArraySlice(items)), + Key::Str("concat") => Value::Builtin(Builtin::ArrayConcat(items)), + // Only an integer indexes an array; `X['0']` doesn't. + Key::Num(n) if n.fract() == 0.0 => { + if n >= 0.0 && n < items.len() as f64 { + items.into_iter().nth(n as usize).unwrap_or(Value::Undefined) + } else { + Value::Undefined + } + } + _ => Value::Dynamic, + }, + Value::String(s) if matches!(key, Key::Str("concat")) => { + Value::Builtin(Builtin::StringConcat(s)) + } + Value::Module => Value::Reference { name: key_str(), kind: RefKind::Import }, + Value::Reference { kind: RefKind::Class(class), .. } => { + self.static_member(class, &key_str(), depth) + } + // The object is in another file, and so is its member. + object @ Value::Reference { kind: RefKind::Import, .. } => object, + Value::Reference { kind: RefKind::Global, .. } => { + Value::Reference { name: key_str(), kind: RefKind::Global } + } + _ => Value::Dynamic, + } + } + + fn static_member(&self, class: &'a Class<'a>, key: &str, depth: u16) -> Value<'a> { + let mut overloads = 0; + let mut method = None; + for element in &class.body.body { + match element { + ClassElement::MethodDefinition(m) + if m.r#static && m.key.static_name().is_some_and(|n| n == key) => + { + if m.kind != MethodDefinitionKind::Method { + return Value::Reference { name: key.into(), kind: RefKind::Other }; + } + if m.value.body.is_none() { + overloads += 1; + } + if m.value.body.is_some() || method.is_none() { + method = Some(&*m.value); + } + } + ClassElement::PropertyDefinition(p) + if method.is_none() + && p.r#static + && p.key.static_name().is_some_and(|n| n == key) => + { + return match &p.value { + Some(value) => { + let block = self.consts.scope().block_of(class); + self.eval(value, depth, &Frame::at(block)) + } + None => Value::Reference { name: key.into(), kind: RefKind::Other }, + }; + } + _ => {} + } + } + match method { + Some(method) => { + Value::Reference { name: key.into(), kind: RefKind::Function(method, overloads) } + } + None => Value::Undefined, + } + } +} + +/// The identifiers an expression mentions. +struct Mentions<'a>(std::vec::Vec<&'a str>); + +impl<'a> Visit<'a> for Mentions<'a> { + fn visit_identifier_reference(&mut self, id: &IdentifierReference<'a>) { + self.0.push(id.name.as_str()); + } +} + +/// Bind a parameter: a destructured parameter's names aren't evaluated. +fn bind<'a>(frame: &mut Frame<'a>, pattern: &'a BindingPattern<'a>, binding: Binding<'a>) { + if let BindingPattern::BindingIdentifier(id) = pattern { + frame.bindings.insert(id.name.as_str(), binding); + return; + } + let mut names = std::vec::Vec::new(); + collect_bindings(pattern, &mut std::vec::Vec::new(), &mut names); + for (name, _) in names { + frame.bindings.insert(name, Binding::new(Value::Dynamic, None)); + } +} + +/// Whether an expression uses a name bound in a called function's frame, or +/// that function's `this` or `arguments`. Names are scoped like JavaScript +/// does: a parameter, variable, function or class the expression declares +/// itself (in a nested function, block, loop or `catch`) hides the frame's, +/// and a nested non-arrow function has its own `this` and `arguments`. +struct UsesFrame<'f, 'a> { + frame: &'f Frame<'a>, + /// Resolves the names of the namespace block the frame is in, if any. + scope: &'f FileScope<'a>, + found: bool, + /// The names each scope entered inside the expression declares, innermost last. + scopes: std::vec::Vec>, + /// How many functions entered inside the expression have their own + /// `arguments` (non-arrow functions) ... + own_arguments: usize, + /// ... and their own `this` (those, and class bodies). + own_this: usize, +} + +impl<'f, 'a> UsesFrame<'f, 'a> { + fn new(frame: &'f Frame<'a>, scope: &'f FileScope<'a>) -> Self { + Self { + frame, + scope, + found: false, + scopes: std::vec::Vec::new(), + own_arguments: 0, + own_this: 0, + } + } + + fn declared(&self, name: &str) -> bool { + self.scopes.iter().any(|scope| scope.iter().any(|n| n == name)) + } + + /// Visits `f` with `names` in scope. + fn scoped(&mut self, names: std::vec::Vec, f: impl FnOnce(&mut Self)) { + self.scopes.push(names); + f(self); + self.scopes.pop(); + } +} + +/// The names a binding pattern declares. +fn bound_names(pattern: &BindingPattern<'_>, out: &mut std::vec::Vec) { + match pattern { + BindingPattern::BindingIdentifier(id) => out.push(id.name.to_string()), + BindingPattern::AssignmentPattern(p) => bound_names(&p.left, out), + BindingPattern::ObjectPattern(p) => { + for prop in &p.properties { + bound_names(&prop.value, out); + } + if let Some(rest) = &p.rest { + bound_names(&rest.argument, out); + } + } + BindingPattern::ArrayPattern(p) => { + for element in p.elements.iter().flatten() { + bound_names(element, out); + } + if let Some(rest) = &p.rest { + bound_names(&rest.argument, out); + } + } + } +} + +/// The names a function's parameters declare. +fn parameter_names(params: &FormalParameters<'_>, out: &mut std::vec::Vec) { + for param in ¶ms.items { + bound_names(¶m.pattern, out); + } + if let Some(rest) = ¶ms.rest { + bound_names(&rest.rest.argument, out); + } +} + +/// The names `let`, `const`, `class` and `function` declarations directly in +/// a block declare, for the whole block. +fn lexical_names(statements: &[Statement<'_>], out: &mut std::vec::Vec) { + for statement in statements { + match statement { + Statement::VariableDeclaration(decl) if decl.kind != VariableDeclarationKind::Var => { + for d in &decl.declarations { + bound_names(&d.id, out); + } + } + Statement::FunctionDeclaration(f) => { + out.extend(f.id.as_ref().map(|id| id.name.to_string())); + } + Statement::ClassDeclaration(c) => { + out.extend(c.id.as_ref().map(|id| id.name.to_string())); + } + _ => {} + } + } +} + +/// The names `var` declarations in a function body declare (in nested blocks +/// too, but not in nested functions or classes), for the whole function. +struct VarNames<'n>(&'n mut std::vec::Vec); + +impl<'a> Visit<'a> for VarNames<'_> { + fn visit_variable_declaration(&mut self, decl: &VariableDeclaration<'a>) { + if decl.kind == VariableDeclarationKind::Var { + for d in &decl.declarations { + bound_names(&d.id, self.0); + } + } + } + + fn visit_function(&mut self, _: &Function<'a>, _: ScopeFlags) {} + + fn visit_arrow_function_expression(&mut self, _: &ArrowFunctionExpression<'a>) {} + + fn visit_class(&mut self, _: &Class<'a>) {} +} + +/// The names a function body declares: its `var`s and its lexical declarations. +fn body_names(body: &FunctionBody<'_>, out: &mut std::vec::Vec) { + let mut vars = VarNames(out); + for statement in &body.statements { + vars.visit_statement(statement); + } + lexical_names(&body.statements, out); +} + +impl<'a> Visit<'a> for UsesFrame<'_, 'a> { + fn visit_identifier_reference(&mut self, id: &IdentifierReference<'a>) { + let name = id.name.as_str(); + let in_namespace = + || self.frame.block.and_then(|from| self.scope.declaring_block(from, name)).is_some(); + self.found |= if name == "arguments" && self.frame.in_call { + self.own_arguments == 0 && !self.declared(name) + } else { + (self.frame.bindings.contains_key(name) || in_namespace()) && !self.declared(name) + }; + } + + fn visit_this_expression(&mut self, _: &ThisExpression) { + self.found |= self.frame.in_call && self.own_this == 0; + } + + fn visit_super(&mut self, _: &Super) { + self.found |= self.frame.in_call && self.own_this == 0; + } + + // Types are erased from the emitted code, so a name used only in one + // (`(v: string): typeof name => v`, `v as typeof name`, `id(v)`) + // doesn't need to be in scope where the transform is emitted. The runtime + // parts around them (a parameter's default, the expression inside `as`, + // `satisfies`, `x` and `x!`) are still visited. + fn visit_ts_type(&mut self, _: &TSType<'a>) {} + + fn visit_ts_type_parameter_declaration(&mut self, _: &TSTypeParameterDeclaration<'a>) {} + + fn visit_ts_type_parameter_instantiation(&mut self, _: &TSTypeParameterInstantiation<'a>) {} + + fn visit_ts_type_alias_declaration(&mut self, _: &TSTypeAliasDeclaration<'a>) {} + + fn visit_ts_interface_declaration(&mut self, _: &TSInterfaceDeclaration<'a>) {} + + fn visit_function(&mut self, f: &Function<'a>, flags: ScopeFlags) { + let mut names = std::vec::Vec::new(); + // A function expression's own name is in scope in its body; a + // declaration's is declared in the enclosing block. + names.extend(f.id.as_ref().map(|id| id.name.to_string())); + parameter_names(&f.params, &mut names); + if let Some(body) = &f.body { + body_names(body, &mut names); + } + self.own_arguments += 1; + self.own_this += 1; + self.scoped(names, |v| walk::walk_function(v, f, flags)); + self.own_arguments -= 1; + self.own_this -= 1; + } + + fn visit_arrow_function_expression(&mut self, f: &ArrowFunctionExpression<'a>) { + let mut names = std::vec::Vec::new(); + parameter_names(&f.params, &mut names); + if let ArrowFunctionBody::FunctionBody(body) = &f.body { + body_names(body, &mut names); + } + self.scoped(names, |v| walk::walk_arrow_function_expression(v, f)); + } + + fn visit_class(&mut self, class: &Class<'a>) { + // A class expression's own name is in scope in its body. + let names = class.id.iter().map(|id| id.name.to_string()).collect(); + self.scoped(names, |v| { + if let Some(heritage) = &class.heritage { + v.visit_expression(&heritage.expression); + } + v.own_this += 1; + v.visit_class_body(&class.body); + v.own_this -= 1; + }); + } + + fn visit_static_block(&mut self, block: &StaticBlock<'a>) { + let mut names = std::vec::Vec::new(); + let mut vars = VarNames(&mut names); + for statement in &block.body { + vars.visit_statement(statement); + } + lexical_names(&block.body, &mut names); + self.scoped(names, |v| walk::walk_static_block(v, block)); + } + + fn visit_block_statement(&mut self, block: &BlockStatement<'a>) { + let mut names = std::vec::Vec::new(); + lexical_names(&block.body, &mut names); + self.scoped(names, |v| walk::walk_block_statement(v, block)); + } + + fn visit_for_statement(&mut self, stmt: &ForStatement<'a>) { + let mut names = std::vec::Vec::new(); + if let Some(ForStatementInit::VariableDeclaration(decl)) = &stmt.init + && decl.kind != VariableDeclarationKind::Var + { + for d in &decl.declarations { + bound_names(&d.id, &mut names); + } + } + self.scoped(names, |v| walk::walk_for_statement(v, stmt)); + } + + fn visit_for_in_statement(&mut self, stmt: &ForInStatement<'a>) { + let names = loop_names(&stmt.left); + self.scoped(names, |v| walk::walk_for_in_statement(v, stmt)); + } + + fn visit_for_of_statement(&mut self, stmt: &ForOfStatement<'a>) { + let names = loop_names(&stmt.left); + self.scoped(names, |v| walk::walk_for_of_statement(v, stmt)); + } + + fn visit_switch_statement(&mut self, stmt: &SwitchStatement<'a>) { + // The discriminant is outside the cases' block. + self.visit_expression(&stmt.discriminant); + let mut names = std::vec::Vec::new(); + for case in &stmt.cases { + lexical_names(&case.consequent, &mut names); + } + self.scoped(names, |v| { + for case in &stmt.cases { + v.visit_switch_case(case); + } + }); + } + + fn visit_catch_clause(&mut self, clause: &CatchClause<'a>) { + let mut names = std::vec::Vec::new(); + if let Some(param) = &clause.param { + bound_names(¶m.pattern, &mut names); + } + self.scoped(names, |v| walk::walk_catch_clause(v, clause)); + } +} + +/// The names a `for (let ... of/in ...)` head declares for the loop. +fn loop_names(left: &ForStatementLeft<'_>) -> std::vec::Vec { + let mut names = std::vec::Vec::new(); + if let ForStatementLeft::VariableDeclaration(decl) = left + && decl.kind != VariableDeclarationKind::Var + { + for d in &decl.declarations { + bound_names(&d.id, &mut names); + } + } + names +} + +/// ngtsc's `literal()`: an operand of a binary operator or a template literal +/// must be a primitive, and an enum member counts as its value. +fn literal(value: Value<'_>) -> Value<'_> { + let value = match value { + Value::Enum { value, .. } => *value, + value => value, + }; + match value { + Value::Dynamic + | Value::Null + | Value::Undefined + | Value::Bool(_) + | Value::Number(_) + | Value::String(_) => value, + value if value.is_import() => value, + _ => Value::Dynamic, + } +} + +/// `String(value)` for a primitive. +fn to_js_string(value: &Value<'_>) -> String { + match value { + Value::Null => "null".into(), + Value::Undefined => "undefined".into(), + Value::Bool(b) => b.to_string(), + Value::Number(n) => format_number_like_js(*n), + Value::String(s) => s.clone(), + Value::Array(items) => items + .iter() + .map(|item| match item { + Value::Null | Value::Undefined => String::new(), + item => to_js_string(item), + }) + .collect::>() + .join(","), + _ => "[object Object]".into(), + } +} + +/// `Number(value)`. Arrays convert through their string form, like in JavaScript. +fn to_number(value: &Value<'_>) -> f64 { + match value { + Value::Null => 0.0, + Value::Bool(b) => f64::from(u8::from(*b)), + Value::Number(n) => *n, + Value::String(s) => string_to_number(s), + Value::Array(_) => string_to_number(&to_js_string(value)), + _ => f64::NAN, + } +} + +/// JavaScript's `StringToNumber`. +fn string_to_number(s: &str) -> f64 { + let is_js_space = |c: char| { + matches!( + c, + '\t' | '\n' | '\u{b}' | '\u{c}' | '\r' | ' ' | '\u{a0}' | '\u{1680}' | '\u{2000}' + ..='\u{200a}' + | '\u{2028}' + | '\u{2029}' + | '\u{202f}' + | '\u{205f}' + | '\u{3000}' + | '\u{feff}' + ) + }; + let s = s.trim_matches(is_js_space); + if s.is_empty() { + return 0.0; + } + let radix = |digits: &str, radix: u32| { + if digits.is_empty() { + return f64::NAN; + } + digits + .chars() + .try_fold(0.0, |acc: f64, c| { + c.to_digit(radix).map(|d| acc * f64::from(radix) + f64::from(d)) + }) + .unwrap_or(f64::NAN) + }; + match s.get(..2) { + Some("0x" | "0X") => return radix(&s[2..], 16), + Some("0o" | "0O") => return radix(&s[2..], 8), + Some("0b" | "0B") => return radix(&s[2..], 2), + _ => {} + } + let unsigned = s.strip_prefix(['+', '-']).unwrap_or(s); + if unsigned == "Infinity" { + return if s.starts_with('-') { f64::NEG_INFINITY } else { f64::INFINITY }; + } + // Rust also parses `inf`, `nan`, ...: only accept JavaScript's decimal syntax. + let (mantissa, exponent) = match unsigned.find(['e', 'E']) { + Some(at) => (&unsigned[..at], Some(&unsigned[at + 1..])), + None => (unsigned, None), + }; + let (int, frac) = mantissa.split_once('.').unwrap_or((mantissa, "")); + let digits = |s: &str| s.bytes().all(|b| b.is_ascii_digit()); + let valid_mantissa = digits(int) && digits(frac) && !(int.is_empty() && frac.is_empty()); + let valid_exponent = exponent.is_none_or(|e| { + let e = e.strip_prefix(['+', '-']).unwrap_or(e); + !e.is_empty() && digits(e) + }); + if valid_mantissa && valid_exponent { s.parse().unwrap_or(f64::NAN) } else { f64::NAN } +} + +/// JavaScript's `ToInt32`. +fn to_int32(n: f64) -> i32 { + if !n.is_finite() { + return 0; + } + (n.trunc().rem_euclid(4_294_967_296.0) as u32) as i32 +} + +fn unary<'a>(operator: UnaryOperator, value: &Value<'a>) -> Value<'a> { + let value = match value { + // An enum member is an object at runtime. + Value::Enum { .. } => &Value::Dynamic, + value => value, + }; + let number = || if matches!(value, Value::Dynamic) { f64::NAN } else { to_number(value) }; + match operator { + UnaryOperator::UnaryNegation => Value::Number(-number()), + UnaryOperator::UnaryPlus => Value::Number(number()), + UnaryOperator::LogicalNot => Value::Bool(!value.truthy()), + UnaryOperator::BitwiseNot => Value::Number(f64::from(!to_int32(number()))), + _ => Value::Dynamic, + } +} + +/// A binary operator on two primitives. +fn binary<'a>(operator: BinaryOperator, left: &Value<'a>, right: &Value<'a>) -> Value<'a> { + let (l, r) = (to_number(left), to_number(right)); + let shift = || to_int32(r) as u32 & 31; + match operator { + BinaryOperator::Addition => match (left, right) { + (Value::String(_), _) | (_, Value::String(_)) => { + Value::String(to_js_string(left) + &to_js_string(right)) + } + _ => Value::Number(l + r), + }, + BinaryOperator::Subtraction => Value::Number(l - r), + BinaryOperator::Multiplication => Value::Number(l * r), + BinaryOperator::Division => Value::Number(l / r), + BinaryOperator::Remainder => Value::Number(l % r), + BinaryOperator::Exponential => { + Value::Number(if r.is_nan() || (l.abs() == 1.0 && r.is_infinite()) { + f64::NAN + } else { + l.powf(r) + }) + } + BinaryOperator::BitwiseAnd => Value::Number(f64::from(to_int32(l) & to_int32(r))), + BinaryOperator::BitwiseOR => Value::Number(f64::from(to_int32(l) | to_int32(r))), + BinaryOperator::BitwiseXOR => Value::Number(f64::from(to_int32(l) ^ to_int32(r))), + BinaryOperator::ShiftLeft => Value::Number(f64::from(to_int32(l).wrapping_shl(shift()))), + BinaryOperator::ShiftRight => Value::Number(f64::from(to_int32(l) >> shift())), + BinaryOperator::ShiftRightZeroFill => { + Value::Number(f64::from((to_int32(l) as u32) >> shift())) + } + BinaryOperator::LessThan => Value::Bool(compare(left, right, false)), + BinaryOperator::GreaterThan => Value::Bool(compare(right, left, false)), + BinaryOperator::LessEqualThan => Value::Bool(compare(left, right, true)), + BinaryOperator::GreaterEqualThan => Value::Bool(compare(right, left, true)), + BinaryOperator::Equality => Value::Bool(loose_equals(left, right)), + BinaryOperator::Inequality => Value::Bool(!loose_equals(left, right)), + BinaryOperator::StrictEquality => Value::Bool(strict_equals(left, right)), + BinaryOperator::StrictInequality => Value::Bool(!strict_equals(left, right)), + BinaryOperator::In | BinaryOperator::Instanceof => Value::Dynamic, + } +} + +/// `a < b` (or `a <= b`): strings compare by UTF-16 code units, anything else as numbers. +fn compare(a: &Value<'_>, b: &Value<'_>, or_equal: bool) -> bool { + let ordering = match (a, b) { + (Value::String(a), Value::String(b)) => Some(a.encode_utf16().cmp(b.encode_utf16())), + _ => to_number(a).partial_cmp(&to_number(b)), + }; + ordering.is_some_and(|o| o.is_lt() || (or_equal && o.is_eq())) +} + +fn strict_equals(a: &Value<'_>, b: &Value<'_>) -> bool { + match (a, b) { + (Value::Null, Value::Null) | (Value::Undefined, Value::Undefined) => true, + (Value::Bool(a), Value::Bool(b)) => a == b, + (Value::Number(a), Value::Number(b)) => a == b, + (Value::String(a), Value::String(b)) => a == b, + _ => false, + } +} + +fn loose_equals(a: &Value<'_>, b: &Value<'_>) -> bool { + match (a, b) { + (Value::Null | Value::Undefined, Value::Null | Value::Undefined) => true, + (Value::Null | Value::Undefined, _) | (_, Value::Null | Value::Undefined) => false, + (Value::String(_), Value::String(_)) => strict_equals(a, b), + _ => to_number(a) == to_number(b), + } +} diff --git a/crates/oxc_angular_compiler/src/directive/mod.rs b/crates/oxc_angular_compiler/src/directive/mod.rs index 279208720..71aab3981 100644 --- a/crates/oxc_angular_compiler/src/directive/mod.rs +++ b/crates/oxc_angular_compiler/src/directive/mod.rs @@ -15,6 +15,7 @@ mod compiler; mod decorator; mod definition; +mod evaluator; mod metadata; mod property_decorators; mod query; @@ -26,9 +27,11 @@ pub use compiler::{ }; pub(crate) use decorator::find_directive_decorator; pub use decorator::{ - StringConsts, collect_string_consts, extract_directive_metadata, find_directive_decorator_span, + StringConsts, collect_string_consts, decorator_io_errors, extract_directive_metadata, + find_directive_decorator_span, }; pub(crate) use decorator::{extract_string_value, resolve_template_literal}; +pub(crate) use decorator::{merge_by_class_property, parse_decorator_io}; pub use definition::{DirectiveDefinitions, generate_directive_definitions}; pub use metadata::{ QueryPredicate, R3DirectiveMetadata, R3DirectiveMetadataBuilder, R3HostDirectiveMetadata, diff --git a/crates/oxc_angular_compiler/src/output/emitter.rs b/crates/oxc_angular_compiler/src/output/emitter.rs index 545731069..8dd572d16 100644 --- a/crates/oxc_angular_compiler/src/output/emitter.rs +++ b/crates/oxc_angular_compiler/src/output/emitter.rs @@ -1322,7 +1322,7 @@ fn escape_identifier(input: &Ident<'_>, escape_dollar: bool, always_quote: bool) /// This function matches the ECMAScript specification for `Number::toString()` /// (ECMA-262, 7.1.12.1) to ensure the emitted code matches Angular's TypeScript compiler /// which uses JavaScript's template literal coercion (`${value}`). -fn format_number_like_js(value: f64) -> String { +pub(crate) fn format_number_like_js(value: f64) -> String { // 1. NaN if value.is_nan() { return "NaN".to_string(); diff --git a/crates/oxc_angular_compiler/tests/decorator_inputs_outputs_test.rs b/crates/oxc_angular_compiler/tests/decorator_inputs_outputs_test.rs new file mode 100644 index 000000000..61b62cff8 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/decorator_inputs_outputs_test.rs @@ -0,0 +1,61 @@ +//! `inputs:` declared in `@Directive` metadata were silently dropped from the +//! compiled definition. This is the original report; exhaustive parity with +//! ngtsc for `inputs:` / `outputs:` / `queries:` metadata is checked by +//! `decorator_metadata_ngtsc_test.rs`. + +use oxc_allocator::Allocator; +use oxc_angular_compiler::{TransformOptions, transform_angular_file}; + +/// The whitespace-free `ɵɵdefineDirective` call for `class_name`. +fn define_call(code: &str, class_name: &str) -> String { + let compact: String = code.chars().filter(|c| !c.is_whitespace()).collect(); + let start = compact.find(&format!("({{type:{class_name},")).expect("define call"); + let end = compact[start..].find("});").map_or(compact.len(), |i| start + i); + compact[start..end].to_string() +} + +#[test] +fn inputs_declared_in_directive_metadata_are_compiled() { + // The report used `const bool = (v) => ...`; ngtsc (and oxc) reject a + // function expression assigned to a variable as a transform, so this uses a + // function declaration. + let source = "import { Directive, Input } from '@angular/core'; + +export function bool(v: unknown): boolean { return v === '' || v === true; } + +@Directive({ + selector: 'a', + inputs: ['x', { name: 'y', alias: 'why', transform: bool }], +}) +class A { + x?: string; + y?: boolean; +} + +@Directive() +abstract class B { + @Input() p?: string; + @Input({ alias: 'q-q', transform: bool }) q?: boolean; +} + +@Directive({ selector: 'c' }) +class C extends B {} +"; + let allocator = Allocator::default(); + let result = transform_angular_file( + &allocator, + "test.ts", + source, + Some(&TransformOptions::default()), + None, + ); + assert!(!result.has_errors(), "{:?}", result.diagnostics); + // ngtsc: `inputs: { x: "x", y: [2, "why", "y", bool] }` + assert!( + define_call(&result.code, "A").contains(r#"inputs:{x:"x",y:[2,"why","y",bool]}"#), + "{}", + define_call(&result.code, "A") + ); + assert!(define_call(&result.code, "B").contains(r#"inputs:{p:"p",q:[2,"q-q","q",bool]}"#)); + assert!(define_call(&result.code, "C").contains("features:[i0.ɵɵInheritDefinitionFeature]")); +} diff --git a/crates/oxc_angular_compiler/tests/decorator_metadata_ngtsc_test.rs b/crates/oxc_angular_compiler/tests/decorator_metadata_ngtsc_test.rs new file mode 100644 index 000000000..0b2fafc95 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/decorator_metadata_ngtsc_test.rs @@ -0,0 +1,680 @@ +//! Parity with ngtsc for `inputs:` / `outputs:` / `queries:` decorator metadata +//! and input transforms. +//! +//! `fixtures/decorator_metadata_ngtsc.json` is a snapshot of ngtsc's output +//! (`@angular/compiler-cli` 22.1.7) for each case. Each case's `origin` says +//! where it comes from: an Angular spec (sources taken verbatim from +//! angular/angular) or a probe of ngtsc's partial evaluator and `.d.ts` +//! printer. Cases ngtsc and oxc can't agree on by design carry a `skip` +//! reason (mostly: they need declarations from another file). For every case this +//! compares the diagnostics word for word and, when ngtsc accepts the file, +//! each class's `inputs`/`outputs` maps and query functions (ignoring +//! whitespace and parentheses, which the two emitters place differently) and +//! its `.d.ts` members (ignoring whitespace, whose layout TypeScript's printer +//! derives from source positions). +//! +//! One deliberate difference: when ngtsc types an `ngAcceptInputType_*` with a +//! type from another module (`i1.Foo`, plus an `import * as i1` it adds to the +//! `.d.ts`), oxc writes `unknown`. Aliases numbered per source file can't be +//! merged safely into bundled declaration files; `i0` (`@angular/core`) is +//! always imported, so those types are kept. + +use std::collections::BTreeMap; + +use oxc_allocator::Allocator; +use oxc_angular_compiler::{TransformOptions, TransformResult, transform_angular_file}; +use serde_json::Value; + +const FIXTURES: &str = include_str!("fixtures/decorator_metadata_ngtsc.json"); + +/// `s` without the characters `drop` matches, except inside string literals: +/// `"a b"` stays `"a b"`, so a stray space in an emitted name is a mismatch. +fn strip_outside_strings(s: &str, drop: impl Fn(char) -> bool) -> String { + let mut out = String::with_capacity(s.len()); + let mut chars = s.chars(); + while let Some(c) = chars.next() { + if matches!(c, '"' | '\'' | '`') { + out.push(c); + while let Some(inner) = chars.next() { + out.push(inner); + if inner == '\\' { + out.extend(chars.next()); + } else if inner == c { + break; + } + } + } else if !drop(c) { + out.push(c); + } + } + out +} + +fn strip(s: &str) -> String { + strip_outside_strings(s, char::is_whitespace) +} + +fn strip_parens(s: &str) -> String { + strip_outside_strings(s, |c| c == '(' || c == ')') +} + +/// A UTF-16 offset (what ngtsc reports) as a byte offset into `source`. +fn byte_offset(source: &str, utf16: u64) -> usize { + let mut units = 0; + for (at, c) in source.char_indices() { + if units >= utf16 { + return at; + } + units += c.len_utf16() as u64; + } + source.len() +} + +/// The balanced `{...}` / `(...)` / `[...]` starting at byte `open`. +fn balanced(text: &str, open: usize) -> &str { + let bytes = text.as_bytes(); + let mut stack = Vec::new(); + let mut i = open; + while i < bytes.len() { + match bytes[i] { + q @ (b'"' | b'\'' | b'`') => { + i += 1; + while i < bytes.len() && bytes[i] != q { + i += if bytes[i] == b'\\' { 2 } else { 1 }; + } + } + b'{' => stack.push(b'}'), + b'(' => stack.push(b')'), + b'[' => stack.push(b']'), + c if stack.last() == Some(&c) => { + stack.pop(); + if stack.is_empty() { + return &text[open..=i]; + } + } + _ => {} + } + i += 1; + } + &text[open..] +} + +/// ngtsc adds a docs link to a few diagnostics when it formats them; the link +/// depends on the Angular release, so it isn't part of the comparison. +fn without_docs_link(message: &str) -> &str { + let Some(at) = message.find(" Find more at https://") else { return message }; + message[..at].strip_suffix('.').unwrap_or(&message[..at]) +} + +/// Per class, what the fixture records: `inputs`/`outputs`/`contentQueries`/ +/// `viewQuery` from the JS (whitespace removed, `_cN` constants inlined) and +/// `dts:` from the `.d.ts`. +fn oxc_classes( + js: &str, + dts: &[oxc_angular_compiler::dts::DtsDeclaration], +) -> BTreeMap> { + let mut consts = BTreeMap::new(); + for (at, _) in js.match_indices("const _c") { + let rest = &js[at + 6..]; + let Some(eq) = rest.find(" = ") else { continue }; + let name = rest[..eq].to_string(); + consts.insert(name, strip(balanced(rest, eq + 3))); + } + let inline = |s: String| { + let mut out = s; + // Longest names first so `_c10` isn't replaced as `_c1` + `0`. + let mut names: Vec<&String> = consts.keys().collect(); + names.sort_by_key(|n| std::cmp::Reverse(n.len())); + for name in names { + out = out.replace(name.as_str(), &consts[name]); + } + out + }; + + let mut classes: BTreeMap> = BTreeMap::new(); + for marker in ["ɵɵdefineDirective(", "ɵɵdefineComponent("] { + for (at, _) in js.match_indices(marker) { + let def = balanced(js, at + marker.len() - 1); + let compact = strip(def); + let Some(name) = compact + .strip_prefix("({type:") + .map(|r| r.split(|c: char| !c.is_alphanumeric() && c != '_').next().unwrap()) + else { + continue; + }; + let entry = classes.entry(name.to_string()).or_default(); + for key in ["inputs", "outputs"] { + if let Some(pos) = compact.find(&format!(",{key}:{{")) { + let open = pos + key.len() + 2; + entry.insert(key.into(), balanced(&compact, open).to_string()); + } + } + for key in ["contentQueries", "viewQuery"] { + if let Some(pos) = def + .find(&format!("{key}: function")) + .or_else(|| def.find(&format!("{key}:function"))) + { + let params_end = pos + def[pos..].find(')').unwrap(); + let open = params_end + def[params_end..].find('{').unwrap(); + entry.insert(key.into(), inline(strip(balanced(def, open)))); + } + } + } + } + for decl in dts { + let entry = classes.entry(decl.class_name.clone()).or_default(); + for line in decl.members.lines() { + let line = line.trim(); + let Some(rest) = line.strip_prefix("static ") else { continue }; + let member = rest.split(':').next().unwrap().trim_matches('"'); + if member == "ɵdir" || member == "ɵcmp" || member.starts_with("ngAcceptInputType_") { + entry.insert(format!("dts:{member}"), strip(line)); + } + } + } + classes +} + +/// `staticngAcceptInputType_x:;` becomes `...:unknown;` when `` +/// references a module other than `@angular/core` (see the module docs). +fn accept_type_without_other_modules(key: &str, expected: &str) -> String { + let other_module = expected.match_indices('i').any(|(at, _)| { + let before = expected[..at].chars().last(); + let digits: String = expected[at + 1..].chars().take_while(char::is_ascii_digit).collect(); + !before.is_some_and(|c| c.is_alphanumeric() || c == '_' || c == '$') + && !digits.is_empty() + && digits != "0" + && expected[at + 1 + digits.len()..].starts_with('.') + }); + if key.starts_with("dts:ngAcceptInputType_") && other_module { + let colon = expected.find(':').unwrap(); + format!("{}:unknown;", &expected[..colon]) + } else { + expected.to_string() + } +} + +#[test] +fn decorator_metadata_matches_ngtsc() { + let fixtures: Value = serde_json::from_str(FIXTURES).unwrap(); + let mut failures = Vec::new(); + let mut compared = 0; + + for fixture in fixtures["fixtures"].as_array().unwrap() { + if fixture.get("skip").is_some() { + continue; + } + compared += 1; + let name = fixture["name"].as_str().unwrap(); + let source = fixture["files"]["test.ts"].as_str().unwrap(); + let allocator = Allocator::default(); + let result = transform_angular_file( + &allocator, + "test.ts", + source, + Some(&TransformOptions::default()), + None, + ); + + let expected: Vec<&str> = fixture["diagnostics"] + .as_array() + .unwrap() + .iter() + .map(|d| without_docs_link(d.as_str().unwrap())) + .collect(); + let actual: Vec = result + .diagnostics + .iter() + .filter(|d| d.severity == oxc_diagnostics::Severity::Error) + .map(|d| d.message.to_string()) + .collect(); + if actual != expected { + failures.push(format!( + "{name}\n diagnostics\n ngtsc: {expected:?}\n oxc: {actual:?}" + )); + } + // Where each diagnostic points, when the fixture records it. + if let Some(spans) = fixture.get("diagnosticSpans").and_then(Value::as_array) { + let expected: Vec> = spans + .iter() + .map(|span| { + let span = span.as_array()?; + let offset = |i: usize| byte_offset(source, span[i].as_u64().unwrap()); + Some((offset(0), offset(1))) + }) + .collect(); + let actual: Vec> = result + .diagnostics + .iter() + .filter(|d| d.severity == oxc_diagnostics::Severity::Error) + .map(|d| { + let label = d.labels.first()?; + let (offset, len) = (label.offset() as usize, label.len() as usize); + Some((offset, offset + len)) + }) + .collect(); + if actual != expected { + let text = |spans: &[Option<(usize, usize)>]| { + spans + .iter() + .map(|s| s.map(|(start, end)| &source[start..end])) + .collect::>() + }; + failures.push(format!( + "{name}\n diagnostic spans\n ngtsc: {:?}\n oxc: {:?}", + text(&expected), + text(&actual) + )); + } + } + + // ngtsc emits nothing for a class it rejects. + if !expected.is_empty() { + continue; + } + let oxc = oxc_classes(&result.code, &result.dts_declarations); + for (class, members) in fixture["classes"].as_object().unwrap() { + for (key, value) in members.as_object().unwrap() { + let expected = accept_type_without_other_modules(key, value.as_str().unwrap()); + let expected = expected.as_str(); + let actual = + oxc.get(class).and_then(|m| m.get(key)).map_or("", String::as_str); + let same = if key.starts_with("dts:") { + actual == expected + } else { + strip_parens(actual) == strip_parens(expected) + }; + if !same { + failures.push(format!( + "{name}\n {class}.{key}\n ngtsc: {expected}\n oxc: {actual}" + )); + } + } + } + } + + assert!( + failures.is_empty(), + "{} mismatches with ngtsc:\n\n{}", + failures.len(), + failures.join("\n\n") + ); + assert_eq!(compared, 380, "fixtures compared"); +} + +fn transform(source: &str) -> TransformResult { + let allocator = Allocator::default(); + transform_angular_file(&allocator, "test.ts", source, Some(&TransformOptions::default()), None) +} + +/// Each error's message and the source text it points at. +fn errors(result: &TransformResult, source: &str) -> Vec<(String, String)> { + result + .diagnostics + .iter() + .filter(|d| d.severity == oxc_diagnostics::Severity::Error) + .map(|d| { + let label = d + .labels + .first() + .map_or("", |l| &source[l.offset() as usize..(l.offset() + l.len()) as usize]); + (d.message.to_string(), label.to_string()) + }) + .collect() +} + +/// ngtsc reads a value imported from another file and compiles these (checked +/// with @angular/compiler-cli 22.1.7 and the export in a second file). oxc sees +/// one file, so it can't, and says so rather than guessing: a dropped alias or +/// `required` flag would compile a different binding. +#[test] +fn values_imported_from_another_module_are_reported_as_unreadable() { + let cases = [ + ("inputs: SHARED", "SHARED", "SHARED", "inputs"), + ("inputs: [...SHARED, 'x']", "SHARED", "[...SHARED, 'x']", "inputs"), + ("inputs: [NAME]", "NAME", "[NAME]", "inputs"), + ("inputs: [ns.NAME]", "NAME", "[ns.NAME]", "inputs"), + ("inputs: [{name: 'x', alias: ALIAS}]", "ALIAS", "[{name: 'x', alias: ALIAS}]", "inputs"), + ("inputs: [{name: 'x', required: REQ}]", "REQ", "[{name: 'x', required: REQ}]", "inputs"), + ("inputs: SHARED.concat(['x'])", "SHARED", "SHARED.concat(['x'])", "inputs"), + ("inputs: [`${NAME}x`]", "NAME", "[`${NAME}x`]", "inputs"), + ("inputs: C ? ['a'] : ['b']", "C", "C ? ['a'] : ['b']", "inputs"), + // `&&` / `||` need an import only when it decides or is the result + // (the snapshot's `shortCircuit-*` probes are the ones that don't). + ("inputs: [NAME || 'x']", "NAME", "[NAME || 'x']", "inputs"), + ("inputs: ['' || NAME]", "NAME", "['' || NAME]", "inputs"), + ("inputs: ['x' && NAME]", "NAME", "['x' && NAME]", "inputs"), + ("inputs: 1 ? SHARED : ['x']", "SHARED", "1 ? SHARED : ['x']", "inputs"), + ("outputs: [0 || NAME]", "NAME", "[0 || NAME]", "outputs"), + ("inputs: mk()", "mk", "mk()", "inputs"), + ("outputs: [...OUTS]", "OUTS", "[...OUTS]", "outputs"), + ]; + for (meta, name, span, field) in cases { + let source = format!( + "import {{Directive}} from '@angular/core'; +import {{SHARED, NAME, ALIAS, REQ, OUTS, C, mk}} from './shared'; +import * as ns from './shared'; +@Directive({{selector: '[d]', {meta}}}) +export class Dir {{}} +" + ); + let result = transform(&source); + let message = format!( + "@Directive.{field} depends on '{name}', which is imported from another module. \ + OXC compiles one file at a time and cannot evaluate values from other files." + ); + assert_eq!(errors(&result, &source), vec![(message, span.to_string())], "{meta}"); + } + + // A reference is all a transform needs: nothing to evaluate. + let source = "import {Directive} from '@angular/core'; +import {fn} from './shared'; +@Directive({selector: '[d]', inputs: [{name: 'x', transform: fn}]}) +export class Dir {} +"; + let result = transform(source); + assert!(errors(&result, source).is_empty(), "{:?}", errors(&result, source)); + assert!(strip(&result.code).contains(r#"inputs:{x:[2,"x","x",fn]}"#), "{}", result.code); +} + +/// A transform returned by a function the metadata calls is emitted where the +/// directive is compiled, outside that function. A parameter becomes the +/// argument it was passed (the snapshot's `scope-*` probes); anything else that +/// uses the function's parameters, `this` or `arguments` would mean something +/// else there, or nothing. ngtsc 22.1.7 emits `(v) => v + name` as written +/// (`name` is then `window.name`) and `booleanAttribute` for `o.t` (the name +/// the argument was first given); oxc reports these instead. +#[test] +fn transforms_using_the_parameters_of_a_called_function_are_reported() { + let cases = [ + ( + "function make(name: string) { return [{ name, transform: (v: string) => v + name }]; }", + "make('x')", + ), + ( + "function make(o: any) { return [{ name: 'x', transform: o.t }]; }", + "make({ t: booleanAttribute })", + ), + ("class H { static make() { return [{ name: 'x', transform: this.t }]; } }", "H.make()"), + ( + "function make() { return [{ name: 'x', transform: arguments[0] }]; }", + "make(booleanAttribute)", + ), + // Scoped like JavaScript: only a nested non-arrow function has its own + // `arguments`, and a name declared in a block isn't in scope after it + // (the snapshot's `scope-shadowed*` probes are the names that are). + ( + "function make(name: string) { return [{ name, transform: (v: string) => arguments.length }]; }", + "make('x')", + ), + ( + "function make(name: string) { return [{ name, transform: (v: string) => { { const name = v; } return name; } }]; }", + "make('x')", + ), + ( + "function make(name: string) { return [{ name, transform: (v: string) => ({ [name]: v }) }]; }", + "make('x')", + ), + ( + "function make(name: string) { return [{ name, transform: (v: string) => { switch (name) { case 'x': let name = v; return name; } return v; } }]; }", + "make('x')", + ), + ( + "function make(name: string) { return [{ name, transform: function (v: string) { return (() => name)(); } }]; }", + "make('x')", + ), + // Types are erased (the snapshot's `scope-typeOnly-*` probes), but the + // runtime parts next to them aren't: the expression inside `x!`, `as`, + // `satisfies` and `x`, a parameter's default and a call's arguments. + ( + "function make(name: string) { return [{ name: 'x', transform: (v: string) => name! }]; }", + "make('x')", + ), + ( + "function make(name: string) { return [{ name: 'x', transform: (v: string) => name as typeof name }]; }", + "make('x')", + ), + ( + "function make(name: string) { return [{ name: 'x', transform: (v: string) => name satisfies string }]; }", + "make('x')", + ), + ( + "function make(name: string) { return [{ name: 'x', transform: (v: string) => name }]; }", + "make('x')", + ), + ( + "function make(name: string) { return [{ name: 'x', transform: (v: string, d: typeof name = name) => v }]; }", + "make('x')", + ), + ( + "function id(v: T) { return v; } +function make(name: string) { return [{ name: 'x', transform: (v: string) => id(name) }]; }", + "make('x')", + ), + ]; + for (helper, inputs) in cases { + let source = format!( + "import {{Directive, booleanAttribute}} from '@angular/core'; +{helper} +@Directive({{selector: '[d]', inputs: {inputs}}}) +export class Dir {{ x: any; }} +" + ); + let result = transform(&source); + let message = "@Directive.inputs: the transform of \"x\" uses a parameter of the \ + function it's written in. OXC can't emit it outside that function."; + assert_eq!( + errors(&result, &source), + vec![(message.to_string(), inputs.to_string())], + "{helper}" + ); + } +} + +/// A value ngtsc reads through `typeof NS.X` is written in the namespace, so a +/// transform in it that uses the namespace's declarations means nothing where +/// the directive is compiled. ngtsc 22.1.7 emits these as written (`fn`, +/// `(v) => P`), which throws a ReferenceError when the module loads; oxc +/// reports them. The snapshot's `r6-ns-*` probes cover the values it reads. +#[test] +fn transforms_using_a_namespace_s_declarations_are_reported() { + let cases = [ + ( + "namespace NS { export function fn(v: string) { return 1; } export const INPUTS = [{ name: 'x', transform: fn }]; }", + "typeof NS.INPUTS", + "X", + "NS", + ), + ( + "namespace NS { const P = 1; export const INPUTS = [{ name: 'x', transform: (v: string) => P }]; }", + "typeof NS.INPUTS", + "X", + "NS", + ), + ( + "namespace NS { const P = 1; export namespace Inner { export const INPUTS = [{ name: 'x', transform: (v: string) => P }]; } }", + "typeof NS.Inner.INPUTS", + "X", + "NS.Inner", + ), + ( + "namespace NS { const P = 1; export function make() { return [{ name: 'x', transform: (v: string) => P }]; } }", + "typeof NS.make", + "X()", + "NS", + ), + ]; + for (namespace, ty, inputs, name) in cases { + let source = format!( + "import {{Directive}} from '@angular/core'; +{namespace} +declare const X: {ty}; +@Directive({{selector: '[d]', inputs: {inputs}}}) +export class Dir {{ x: any; }} +" + ); + let result = transform(&source); + let message = format!( + "@Directive.inputs: the transform of \"x\" uses a declaration of namespace {name}, \ + which isn't in scope outside it. OXC can't emit it there." + ); + assert_eq!(errors(&result, &source), vec![(message, inputs.to_string())], "{namespace}"); + } + + // Names that resolve at the top level are emitted: the transform's own + // parameters, a top-level function and an import. + let source = "import {Directive, booleanAttribute} from '@angular/core'; +function top(v: string) { return 1; } +namespace NS { + export const INPUTS = [ + { name: 'a', transform: (v: string) => v.length }, + { name: 'b', transform: top }, + { name: 'c', transform: booleanAttribute }, + ]; +} +declare const X: typeof NS.INPUTS; +@Directive({selector: '[d]', inputs: X}) +export class Dir { a: any; b: any; c: any; } +"; + let result = transform(source); + assert!(errors(&result, source).is_empty(), "{:?}", errors(&result, source)); + assert!( + strip(&result.code).contains( + r#"inputs:{a:[2,"a","a",(v)=>v.length],b:[2,"b","b",top],c:[2,"c","c",booleanAttribute]}"# + ), + "{}", + result.code + ); +} + +/// ngtsc reports a signal input or output also listed in `inputs:` / +/// `outputs:` only for Angular's own `input()`, `model()`, `output()` and +/// `outputFromObservable()`: imported from their module by name (under any +/// alias) or through a namespace import. ngtsc 22.1.7 compiles these without +/// an error, since `input` here isn't Angular's. The snapshot's +/// `initializerApi-*` probes cover the ones ngtsc rejects, and the outputs. +/// +/// oxc still compiles such a member as a signal input (it recognises signal +/// members by name), where ngtsc makes it a plain one: that's why these compare +/// only the diagnostics. +#[test] +fn only_angular_initializer_apis_collide_with_metadata() { + let cases = [ + ("import * as local from './other';", "local.input()"), + ("import {input} from './other';", "input()"), + ("import {input} from './other';", "input.required()"), + ("import {model} from './other';", "model()"), + ]; + for (import, initializer) in cases { + let source = format!( + "import {{Directive}} from '@angular/core'; +{import} +@Directive({{selector: '[d]', inputs: ['value']}}) +export class Dir {{ value = {initializer}; }} +" + ); + let result = transform(&source); + assert_eq!(errors(&result, &source), vec![], "{import} {initializer}"); + } +} + +/// Inputs ngtsc can't compile either (it overflows its stack on the recursive +/// ones and on a chain of 3000 consts) must not overflow oxc's stack or hang. +#[test] +fn evaluation_is_bounded() { + let header = "import {Directive} from '@angular/core';\n"; + let class = |inputs: &str| { + format!("@Directive({{selector: '[d]', inputs: {inputs}}})\nexport class Dir {{}}\n") + }; + let chain = |n: usize, link: &dyn Fn(usize) -> String| { + let mut source = format!("{header}const X0 = ['a'];\n"); + for i in 1..n { + source += &format!("const X{i} = {};\n", link(i - 1)); + } + source + &class(&format!("X{}", n - 1)) + }; + let unreadable = + "Failed to resolve @Directive.inputs to an array Value could not be determined statically."; + let cases = [ + // Long chains are evaluated one link at a time. + (chain(3000, &|prev| format!("X{prev}")), None), + (chain(3000, &|prev| format!("[...X{prev}]")), None), + ( + format!( + "{header}{}const X3000 = ['a'];\n{}", + (0..3000).map(|i| format!("const X{i} = [...X{}];\n", i + 1)).collect::(), + class("X0") + ), + None, + ), + // ... also in a namespace, read through `typeof`. + ( + format!( + "{header}namespace NS {{\nconst X0 = ['a'];\n{}export const Y = X2999;\n}}\ndeclare const Z: typeof NS.Y;\n{}", + (1..3000).map(|i| format!("const X{i} = [...X{}];\n", i - 1)).collect::(), + class("Z") + ), + None, + ), + // Cycles and recursion. + (format!("{header}const A: any = B;\nconst B: any = A;\n{}", class("A")), Some(unreadable)), + ( + format!("{header}function f(): any {{ return f(); }}\n{}", class("f()")), + Some(unreadable), + ), + ( + format!( + "{header}function f(x: any): any {{ return x ? f(x) : f(x); }}\n{}", + class("f(1)") + ), + Some(unreadable), + ), + (format!("{header}class U {{ static a: any = U.a; }}\n{}", class("U.a")), Some(unreadable)), + (format!("{header}enum E {{ A = E.A }}\n{}", class("[`${E.A}`]")), None), + // ... also where a spread argument's elements were written. + ( + format!( + "{header}const A: any[] = [...A];\nfunction f(...a: any[]) {{ return a; }}\n{}", + class("f(...A)") + ), + None, + ), + // Exponential growth. + ( + format!( + "{header}const A0 = ['a'];\n{}{}", + (1..64) + .map(|i| format!("const A{i} = [...A{0}, ...A{0}];\n", i - 1)) + .collect::(), + class("A63") + ), + Some(unreadable), + ), + ( + format!( + "{header}const A0 = ['a'];\n{}function f(...a: any[]) {{ return a; }}\n{}", + (1..64) + .map(|i| format!("const A{i} = [...A{0}, ...A{0}];\n", i - 1)) + .collect::(), + class("f(...A63)") + ), + None, + ), + ( + format!( + "{header}function f(n: number): any {{ return n > 0 ? [...f(n - 1), ...f(n - 1)] : ['a']; }}\n{}", + class("f(64)") + ), + None, + ), + ]; + for (source, expected) in cases { + let result = transform(&source); + let messages: Vec = errors(&result, &source).into_iter().map(|e| e.0).collect(); + match expected { + Some(expected) => assert_eq!(messages, vec![expected.to_string()]), + // Anything but a crash (a dynamic element is reported, not compiled). + None => assert!(messages.len() <= 1, "{messages:?}"), + } + } +} diff --git a/crates/oxc_angular_compiler/tests/fixtures/decorator_metadata_ngtsc.json b/crates/oxc_angular_compiler/tests/fixtures/decorator_metadata_ngtsc.json new file mode 100644 index 000000000..7d801e61e --- /dev/null +++ b/crates/oxc_angular_compiler/tests/fixtures/decorator_metadata_ngtsc.json @@ -0,0 +1,5756 @@ +{ + "compilerCli": "22.1.7", + "fixtures": [ + { + "name": "ngtsc_spec: should allow directives with no selector that are not in NgModules", + "origin": "packages/compiler-cli/test/ngtsc/ngtsc_spec.ts", + "files": { + "test.ts": "\n import {Directive} from '@angular/core';\n\n @Directive({})\n export class BaseDir {}\n\n @Directive({})\n export abstract class AbstractBaseDir {}\n\n @Directive()\n export abstract class EmptyDir {}\n\n @Directive({\n inputs: ['a', 'b'],\n standalone: false,\n })\n export class TestDirWithInputs {}\n " + }, + "diagnostics": [], + "classes": { + "BaseDir": { + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + }, + "AbstractBaseDir": { + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + }, + "EmptyDir": { + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + }, + "TestDirWithInputs": { + "inputs": "{a:\"a\",b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "ngtsc_spec: should wrap \"inputs\" and \"outputs\" keys if they contain unsafe characters", + "origin": "packages/compiler-cli/test/ngtsc/ngtsc_spec.ts", + "files": { + "test.ts": "\n import {Directive, Input} from '@angular/core';\n\n @Directive({\n selector: '[somedir]',\n inputs: ['track-type', 'track-name', 'inputTrackName', 'src.xl'],\n outputs: ['output-track-type', 'output-track-name', 'outputTrackName', 'output.event']\n })\n export class SomeDir {\n @Input('track-type') trackType: string;\n @Input('track-name') trackName: string;\n }\n " + }, + "diagnostics": [], + "classes": { + "SomeDir": { + "inputs": "{\"track-type\":\"track-type\",\"track-name\":\"track-name\",inputTrackName:\"inputTrackName\",\"src.xl\":\"src.xl\",trackType:[0,\"track-type\",\"trackType\"],trackName:[0,\"track-name\",\"trackName\"]}", + "outputs": "{\"output-track-type\":\"output-track-type\",\"output-track-name\":\"output-track-name\",outputTrackName:\"outputTrackName\",\"output.event\":\"output.event\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "ngtsc_spec: should generate the correct declaration for class members decorated with @Input", + "origin": "packages/compiler-cli/test/ngtsc/ngtsc_spec.ts", + "files": { + "test.ts": "\n import {Directive, Input} from '@angular/core';\n\n @Directive({\n selector: '[dir]',\n standalone: false,\n })\n export class TestDir {\n @Input() noArgs: any;\n @Input('aliasedStringArg') stringArg: any;\n @Input({required: true}) requiredNoAlias: any;\n @Input({alias: 'aliasedRequiredWithAlias', required: true}) requiredWithAlias: any;\n }\n " + }, + "diagnostics": [], + "classes": { + "TestDir": { + "inputs": "{noArgs:\"noArgs\",stringArg:[0,\"aliasedStringArg\",\"stringArg\"],requiredNoAlias:\"requiredNoAlias\",requiredWithAlias:[0,\"aliasedRequiredWithAlias\",\"requiredWithAlias\"]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "ngtsc_spec: should generate the correct declaration for directives using the `inputs` array", + "origin": "packages/compiler-cli/test/ngtsc/ngtsc_spec.ts", + "files": { + "test.ts": "\n import {Directive, Input} from '@angular/core';\n\n @Directive({\n selector: '[dir]',\n inputs: [\n 'plain',\n 'withAlias: aliasedWithAlias',\n {name: 'plainLiteral'},\n {name: 'aliasedLiteral', alias: 'alisedLiteralAlias'},\n {name: 'requiredLiteral', required: true},\n {name: 'requiredAlisedLiteral', alias: 'requiredAlisedLiteralAlias', required: true}\n ],\n standalone: false,\n })\n export class TestDir {\n plainLiteral: any;\n aliasedLiteral: any;\n requiredLiteral: any;\n requiredAlisedLiteral: any;\n }\n " + }, + "diagnostics": [], + "classes": { + "TestDir": { + "inputs": "{plain:\"plain\",withAlias:[0,\"aliasedWithAlias\",\"withAlias\"],plainLiteral:\"plainLiteral\",aliasedLiteral:[0,\"alisedLiteralAlias\",\"aliasedLiteral\"],requiredLiteral:\"requiredLiteral\",requiredAlisedLiteral:[0,\"requiredAlisedLiteralAlias\",\"requiredAlisedLiteral\"]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "ngtsc_spec: should handle queries that use an InjectionToken", + "origin": "packages/compiler-cli/test/ngtsc/ngtsc_spec.ts", + "files": { + "test.ts": "\n import {Component, ContentChild, InjectionToken, ViewChild} from '@angular/core';\n\n const TOKEN = new InjectionToken('token');\n\n @Component({\n selector: 'test',\n template: '
',\n })\n class FooCmp {\n @ViewChild(TOKEN) viewChild: any;\n @ContentChild(TOKEN) contentChild: any;\n }\n " + }, + "diagnostics": [], + "classes": { + "FooCmp": { + "contentQueries": "{if(rf&1){i0.ɵɵcontentQuery(dirIndex,TOKEN,5);}if(rf&2){let_t;i0.ɵɵqueryRefresh(_t=i0.ɵɵloadQuery())&&(ctx.contentChild=_t.first);}}", + "viewQuery": "{if(rf&1){i0.ɵɵviewQuery(TOKEN,5);}if(rf&2){let_t;i0.ɵɵqueryRefresh(_t=i0.ɵɵloadQuery())&&(ctx.viewChild=_t.first);}}" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "ngtsc_spec: should compile an input with a transform function that contains a generic parameter", + "origin": "packages/compiler-cli/test/ngtsc/ngtsc_spec.ts", + "files": { + "types.ts": "\n export interface GenericWrapper {\n value: T;\n }\n ", + "test.ts": "\n import {Directive, Input} from '@angular/core';\n import {GenericWrapper} from './types';\n\n function toNumber(value: boolean | string | GenericWrapper) { return 1; }\n\n @Directive({standalone: true})\n export class Dir {\n @Input({transform: toNumber}) value!: number;\n }\n " + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{value:[2,\"value\",\"value\",toNumber]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_value": "staticngAcceptInputType_value:boolean|string|i1.GenericWrapper;" + } + }, + "dtsImports": ["import * as i1 from \"./types\";"], + "diagnosticSpans": [] + }, + { + "name": "ngtsc_spec: should compile an input with a transform function that contains nested generic parameters", + "origin": "packages/compiler-cli/test/ngtsc/ngtsc_spec.ts", + "files": { + "types.ts": "\n export interface GenericWrapper {\n value: T;\n }\n ", + "other-types.ts": "\n export class GenericClass {\n foo: T;\n }\n ", + "test.ts": "\n import {Directive, Input} from '@angular/core';\n import {GenericWrapper} from './types';\n import {GenericClass} from './other-types';\n\n function toNumber(value: boolean | string | GenericWrapper>) { return 1; }\n\n @Directive({standalone: true})\n export class Dir {\n @Input({transform: toNumber}) value!: number;\n }\n " + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{value:[2,\"value\",\"value\",toNumber]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_value": "staticngAcceptInputType_value:boolean|string|i1.GenericWrapper>;" + } + }, + "dtsImports": ["import * as i1 from \"./types\";", "import * as i2 from \"./other-types\";"], + "diagnosticSpans": [] + }, + { + "name": "ngtsc_spec: should compile an input with an inline transform function", + "origin": "packages/compiler-cli/test/ngtsc/ngtsc_spec.ts", + "files": { + "node_modules/external/index.d.ts": "\n export interface ExternalObj {\n foo: boolean;\n }\n\n export type ExternalToNumberType = string | boolean | ExternalObj;\n ", + "test.ts": "\n import {Directive, Input} from '@angular/core';\n import {ExternalToNumberType} from 'external';\n\n @Directive({standalone: true})\n export class Dir {\n @Input({transform: (value: ExternalToNumberType) => value ? 1 : 0}) value!: number;\n }\n " + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{value:[2,\"value\",\"value\",(value)=>value?1:0]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_value": "staticngAcceptInputType_value:i1.ExternalToNumberType;" + } + }, + "dtsImports": ["import * as i1 from \"external\";"], + "diagnosticSpans": [] + }, + { + "name": "ngtsc_spec: should treat an input transform function only with a `this` parameter as unknown", + "origin": "packages/compiler-cli/test/ngtsc/ngtsc_spec.ts", + "files": { + "test.ts": "\n import {Directive, Input} from '@angular/core';\n\n function toNumber(this: Dir) { return 1; }\n\n @Directive({standalone: true})\n export class Dir {\n @Input({transform: toNumber}) value!: number;\n }\n " + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{value:[2,\"value\",\"value\",toNumber]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_value": "staticngAcceptInputType_value:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "authoring_inputs_spec: should fail if signal input is also declared in `inputs` decorator field.", + "origin": "packages/compiler-cli/test/ngtsc/authoring_inputs_spec.ts", + "files": { + "test.ts": "\n import {Directive, input} from '@angular/core';\n\n @Directive({\n inputs: ['data'],\n })\n export class TestDir {\n data = input('test');\n }\n " + }, + "diagnostics": ["Input \"data\" is also declared as non-signal in @Directive."], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[159, 180]] + }, + { + "name": "authoring_outputs_spec: should fail if used with output declared in @Directive metadata", + "origin": "packages/compiler-cli/test/ngtsc/authoring_outputs_spec.ts", + "files": { + "test.ts": "\n import {Directive, Output, output} from '@angular/core';\n\n @Directive({\n selector: 'test',\n outputs: ['click'],\n })\n export class TestDir {\n click = output({alias: 'publicClick'});\n }\n " + }, + "diagnostics": ["Output \"click\" is unexpectedly declared in @Directive as well."], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[212, 251]] + }, + { + "name": "authoring_outputs_spec: should fail if used with output declared in @Component metadata", + "origin": "packages/compiler-cli/test/ngtsc/authoring_outputs_spec.ts", + "files": { + "test.ts": "\n import {Component, Output, output} from '@angular/core';\n\n @Component({\n selector: 'test',\n template: '',\n outputs: ['click'],\n })\n export class TestDir {\n click = output({alias: 'publicClick'});\n }\n " + }, + "diagnostics": ["Output \"click\" is unexpectedly declared in @Component as well."], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[238, 277]] + }, + { + "name": "authoring_models_spec: should report a diagnostic if a model input is also declared in the `inputs` field", + "origin": "packages/compiler-cli/test/ngtsc/authoring_models_spec.ts", + "files": { + "test.ts": "\n import {Directive, model} from '@angular/core';\n\n @Directive({\n inputs: ['value'],\n })\n export class TestDir {\n value = model('test');\n }\n " + }, + "diagnostics": ["Input \"value\" is also declared as non-signal in @Directive."], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[160, 182]] + }, + { + "name": "compliance/r3_view_compiler_bindings/order_bindings", + "origin": "packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/order_bindings.ts", + "files": { + "test.ts": "import {Component} from '@angular/core';\n\n@Component({\n selector: 'some-elem',\n template: ``,\n inputs: ['attr1', 'prop1', 'attrInterp1', 'propInterp1'],\n})\nexport class SomeCmp {}\n\n@Component({\n selector: 'my-cmp',\n imports: [SomeCmp],\n host: {\n 'literal1': 'foo',\n '(event1)': 'foo()',\n '[attr.attr1]': 'foo',\n '[id]': 'foo',\n '[class.class1]': 'false',\n '[style.style1]': 'true',\n '[class]': 'foo',\n '[style]': 'foo',\n },\n template: `\n\t\t\n\t`,\n})\nexport class MyCmp {\n foo: any;\n}\n" + }, + "diagnostics": [], + "classes": { + "SomeCmp": { + "inputs": "{attr1:\"attr1\",prop1:\"prop1\",attrInterp1:\"attrInterp1\",propInterp1:\"propInterp1\"}", + "dts:ɵcmp": "staticɵcmp:i0.ɵɵComponentDeclaration;" + }, + "MyCmp": { + "dts:ɵcmp": "staticɵcmp:i0.ɵɵComponentDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: transform-inlineArrowNoParams", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, booleanAttribute} from \"@angular/core\";\n\n@Directive({selector: \"[d]\"})\nexport class Dir { @Input({transform: () => 1}) v: any; }\n@Directive({selector: \"[e]\", inputs: [{name: \"w\", transform: () => 1}]})\nexport class Dir2 { w: any; }\n" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{v:[2,\"v\",\"v\",()=>1]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_v": "staticngAcceptInputType_v:unknown;" + }, + "Dir2": { + "inputs": "{w:[2,\"w\",\"w\",()=>1]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_w": "staticngAcceptInputType_w:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: transform-importedFn", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, booleanAttribute} from \"@angular/core\";\n\n@Directive({selector: \"[d]\"})\nexport class Dir { @Input({transform: booleanAttribute}) v: any; }\n@Directive({selector: \"[e]\", inputs: [{name: \"w\", transform: booleanAttribute}]})\nexport class Dir2 { w: any; }\n" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{v:[2,\"v\",\"v\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_v": "staticngAcceptInputType_v:unknown;" + }, + "Dir2": { + "inputs": "{w:[2,\"w\",\"w\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_w": "staticngAcceptInputType_w:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: transform-thisOnly", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, booleanAttribute} from \"@angular/core\";\nexport function f(this: any) { return 1; }\n@Directive({selector: \"[d]\"})\nexport class Dir { @Input({transform: f}) v: any; }\n@Directive({selector: \"[e]\", inputs: [{name: \"w\", transform: f}]})\nexport class Dir2 { w: any; }\n" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{v:[2,\"v\",\"v\",f]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_v": "staticngAcceptInputType_v:unknown;" + }, + "Dir2": { + "inputs": "{w:[2,\"w\",\"w\",f]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_w": "staticngAcceptInputType_w:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: eval-inputsObj", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nimport * as core from \"@angular/core\";\n\n@Directive({selector: \"[d]\", inputs: {a: 1}})\nexport class Dir { a: any; }\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type '{ a: number }'." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[125, 131]] + }, + { + "name": "probe: eval-inputsCall", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nimport * as core from \"@angular/core\";\ndeclare function mk(): any;\n@Directive({selector: \"[d]\", inputs: mk()})\nexport class Dir { a: any; }\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[152, 156]] + }, + { + "name": "probe: eval-inputsRef", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nimport * as core from \"@angular/core\";\nexport class U {}\n@Directive({selector: \"[d]\", inputs: U})\nexport class Dir { a: any; }\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'U'." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[142, 143]] + }, + { + "name": "probe: eval-inputsElemRef", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nimport * as core from \"@angular/core\";\nexport class U {}\n@Directive({selector: \"[d]\", inputs: [U]})\nexport class Dir { a: any; }\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is a reference to 'U'." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[142, 145]] + }, + { + "name": "probe: eval-inputsElemDyn", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nimport * as core from \"@angular/core\";\ndeclare const X: any;\n@Directive({selector: \"[d]\", inputs: [X]})\nexport class Dir { a: any; }\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is a reference to 'X'." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[146, 149]] + }, + { + "name": "probe: eval-inputsNameNum", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nimport * as core from \"@angular/core\";\n\n@Directive({selector: \"[d]\", inputs: [{name: 1}]})\nexport class Dir { a: any; }\n" + }, + "diagnostics": [ + "Value at position 0 of @Directive.inputs array must have a \"name\" property Value is of type 'number'." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[125, 136]] + }, + { + "name": "probe: eval-inputsNameRef", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nimport * as core from \"@angular/core\";\nexport class U {}\n@Directive({selector: \"[d]\", inputs: [{name: U}]})\nexport class Dir { a: any; }\n" + }, + "diagnostics": [ + "Value at position 0 of @Directive.inputs array must have a \"name\" property Value is a reference to 'U'." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[142, 153]] + }, + { + "name": "probe: eval-multiErr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nimport * as core from \"@angular/core\";\n\n@Directive({selector: \"[d]\", inputs: [1, 2], outputs: [3]})\nexport class Dir { @Input({transform: null}) v: any; }\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'number'." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[125, 131]] + }, + { + "name": "probe: resolve-fnMultiStmt", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nfunction mk() { const x = [\"w\"]; return x; }\n@Directive({selector: \"[d]\", inputs: mk()})\nexport class Dir { w: any; a: any; }\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[130, 134]] + }, + { + "name": "probe: resolve-arrowConstFn", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nconst mk = () => [\"w\"];\n@Directive({selector: \"[d]\", inputs: mk()})\nexport class Dir { w: any; a: any; }\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[109, 113]] + }, + { + "name": "probe: resolve-letArr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nlet INPUTS = [\"w\"];\n@Directive({selector: \"[d]\", inputs: INPUTS})\nexport class Dir { w: any; a: any; }\n" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{w:\"w\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: resolve-varArr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nvar INPUTS = [\"w\"];\n@Directive({selector: \"[d]\", inputs: INPUTS})\nexport class Dir { w: any; a: any; }\n" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{w:\"w\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: resolve-letStrElem", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nlet S = \"w\";\n@Directive({selector: \"[d]\", inputs: [S]})\nexport class Dir { w: any; a: any; }\n" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{w:\"w\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: resolve-inputsTplName", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nconst P = \"w\";\n@Directive({selector: \"[d]\", inputs: [`${P}`]})\nexport class Dir { w: any; a: any; }\n" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{w:\"w\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: resolve-inputsNameViaConst", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\nconst N = \"w\";\n@Directive({selector: \"[d]\", inputs: [{name: N, alias: `x${N}`}]})\nexport class Dir { w: any; a: any; }\n" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{w:[0,\"xw\",\"w\"]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: resolve-inputsHole", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from \"@angular/core\";\n\n@Directive({selector: \"[d]\", inputs: [\"w\", , \"a\"]})\nexport class Dir { w: any; a: any; }\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[86, 98]] + }, + { + "name": "probe: basic-all-forms", + "origin": "probe", + "files": { + "test.ts": "import { Component, Directive, Input, Output, EventEmitter, input } from '@angular/core';\n\nexport function bool(v: unknown): boolean { return v === '' || v === true; }\n\n@Directive({\n selector: 'a',\n inputs: ['x', 'legacy: legacyAlias', { name: 'y', alias: 'why', transform: bool }, { name: 'r', required: true }, { name: 'z' }],\n outputs: ['ev', 'ev2: evAlias'],\n})\nexport class A {\n x?: string; legacy?: string; y?: boolean; r!: string; z?: string;\n ev = new EventEmitter(); ev2 = new EventEmitter();\n}\n\n@Directive({ inputs: ['baseIn', { name: 'baseT', alias: 'bt', transform: bool }], outputs: ['baseOut: bo'] })\nexport abstract class B {\n @Input() p?: string;\n @Input({ alias: 'q-q', transform: bool }) q?: boolean;\n baseIn?: string; baseT?: boolean; baseOut = new EventEmitter();\n}\n\n@Directive({ selector: 'c' })\nexport class C extends B {}\n\n@Component({\n selector: 'd',\n template: '',\n inputs: ['x', 'legacy: legacyAlias', { name: 'y', alias: 'why', transform: bool }, { name: 'r', required: true }],\n outputs: ['ev', 'ev2: evAlias'],\n})\nexport class D extends B {\n @Input('overridden') x?: string;\n sig = input();\n legacy?: string; y?: boolean; r!: string;\n ev = new EventEmitter(); ev2 = new EventEmitter();\n}\n" + }, + "diagnostics": [], + "classes": { + "A": { + "inputs": "{x:\"x\",legacy:[0,\"legacyAlias\",\"legacy\"],y:[2,\"why\",\"y\",bool],r:\"r\",z:\"z\"}", + "outputs": "{ev:\"ev\",ev2:\"evAlias\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_y": "staticngAcceptInputType_y:unknown;" + }, + "B": { + "inputs": "{baseIn:\"baseIn\",baseT:[2,\"bt\",\"baseT\",bool],p:\"p\",q:[2,\"q-q\",\"q\",bool]}", + "outputs": "{baseOut:\"bo\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_baseT": "staticngAcceptInputType_baseT:unknown;", + "dts:ngAcceptInputType_q": "staticngAcceptInputType_q:unknown;" + }, + "C": { + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + }, + "D": { + "inputs": "{x:[0,\"overridden\",\"x\"],legacy:[0,\"legacyAlias\",\"legacy\"],y:[2,\"why\",\"y\",bool],r:\"r\",sig:[1,\"sig\"]}", + "outputs": "{ev:\"ev\",ev2:\"evAlias\"}", + "dts:ɵcmp": "staticɵcmp:i0.ɵɵComponentDeclaration;", + "dts:ngAcceptInputType_y": "staticngAcceptInputType_y:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: review-duplicateMetadataEntries", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, booleanAttribute} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a', 'b', 'a: c'], outputs: ['o', 'p', 'o: q']})\nexport class Dir { a: any; b: any; o: any; p: any; }" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{a:[0,\"c\",\"a\"],b:\"b\"}", + "outputs": "{o:\"q\",p:\"p\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: review-extraColonSegments", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, booleanAttribute} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a: b: c'], outputs: ['o: p: q']})\nexport class Dir { a: any; o: any; }" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{a:[0,\"b\",\"a\"]}", + "outputs": "{o:\"p\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: review-dtsMemberOverridesImportedTransform", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, booleanAttribute} from '@angular/core';\nexport function localFn(v: string) { return 1; }\n@Directive({selector: '[d]', inputs: [{name: 'x', transform: localFn}]})\nexport class Dir { @Input({transform: booleanAttribute}) x: any; }" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: review-numberSmall", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from \"@angular/core\";\n@Directive({selector: \"[d]\", inputs: [`b${1e-7}`]})\nexport class Dir {}" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{\"b1e-7\":\"b1e-7\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: review-numberLarge", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from \"@angular/core\";\n@Directive({selector: \"[d]\", inputs: [`d${123456789012345680000}`]})\nexport class Dir {}" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{d123456789012345680000:\"d123456789012345680000\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: review-numberMicro", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from \"@angular/core\";\n@Directive({selector: \"[d]\", inputs: [`e${0.000001}`]})\nexport class Dir {}" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{\"e0.000001\":\"e0.000001\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: review-numberFraction", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from \"@angular/core\";\n@Directive({selector: \"[d]\", inputs: [`f${1.5}`]})\nexport class Dir {}" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{\"f1.5\":\"f1.5\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: review-infinityGlobal", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from \"@angular/core\";\n@Directive({selector: \"[d]\", inputs: [`c${Infinity}`]})\nexport class Dir {}" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[78, 94]] + }, + { + "name": "probe: review-nanGlobal", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from \"@angular/core\";\n@Directive({selector: \"[d]\", inputs: [`n${NaN}`]})\nexport class Dir {}" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[78, 89]] + }, + { + "name": "probe: review-undefinedKeyword", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from \"@angular/core\";\n@Directive({selector: \"[d]\", inputs: [`u${undefined}`]})\nexport class Dir {}" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{uundefined:\"uundefined\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: review-numberExponent", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from \"@angular/core\";\n@Directive({selector: \"[d]\", inputs: [`a: x${1e21}`, `b: y${2.5e300}`]})\nexport class Dir {}" + }, + "diagnostics": [], + "classes": { + "Dir": { + "inputs": "{a:[0,\"x1e+21\",\"a\"],b:[0,\"y2.5e+300\",\"b\"]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: eval-binaryPlus", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a' + '', 'b' + 1, 'c' + true, 'd' + null]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",b1:\"b1\",ctrue:\"ctrue\",dnull:\"dnull\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-binaryArith", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [`x${1 + 2 * 3 - 4 / 2 % 3}`, `y${2 ** 10}`, `z${5 & 3}${5 | 3}${5 ^ 3}${1 << 3}${-16 >> 2}${-16 >>> 28}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x5:\"x5\",y1024:\"y1024\",\"z1768-415\":\"z1768-415\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-binaryCompare", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [`a${1 < 2}${1 <= 1}${2 > 3}${2 >= 3}${1 == 1}${'1' === '1'}${1 != 2}${1 !== 1}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{atruetruefalsefalsetruetruetruefalse:\"atruetruefalsefalsetruetruetruefalse\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-conditional", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst C = true;\n@Directive({selector: '[d]', inputs: C ? ['a'] : ['b']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-conditionalFalsy", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst C = '';\n@Directive({selector: '[d]', inputs: C ? ['a'] : ['b']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-conditionalDynamic", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\ndeclare function f(): boolean;\n@Directive({selector: '[d]', inputs: f() ? ['a'] : ['b']})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[116, 135]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-logicalOr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst N = null;\n@Directive({selector: '[d]', inputs: N || ['a']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-logicalAnd", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a'] && ['b']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-logicalElem", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [false || 'a', 'b' && 'c', '' || 'd']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",c:\"c\",d:\"d\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-nullish", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst N = null;\n@Directive({selector: '[d]', inputs: N ?? ['a']})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[101, 111]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-unary", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [`a${-1}`, `b${+'3'}`, `c${!0}`, `d${~1}`, `e${-'x'}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{\"a-1\":\"a-1\",b3:\"b3\",ctrue:\"ctrue\",\"d-2\":\"d-2\",eNaN:\"eNaN\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [typeof 'a']})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 97]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-void", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [void 0]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 93]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-tplEnum", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nenum E { A = 'x', B = 2 }\nenum N { A, B }\n@Directive({selector: '[d]', inputs: [`a${E.A}`, `b${E.B}`, `c${N.B}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{ax:\"ax\",b2:\"b2\",c1:\"c1\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-enumMember", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nenum E { A = 'x' }\n@Directive({selector: '[d]', inputs: [E.A]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'E'." + ], + "diagnosticSpans": [[104, 109]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-enumMemberBinary", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nenum E { A = 'x' }\n@Directive({selector: '[d]', inputs: [E.A + '', 'y' + E.A]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\",yx:\"yx\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-enumWhole", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nenum E { A = 'x' }\n@Directive({selector: '[d]', inputs: E})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type '{ A: E }'." + ], + "diagnosticSpans": [[104, 105]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-enumMissing", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nenum E { A = 'x' }\n@Directive({selector: '[d]', inputs: [E.B]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'undefined'." + ], + "diagnosticSpans": [[104, 109]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-constEnum", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst enum E { A = 'x' }\n@Directive({selector: '[d]', inputs: [`${E.A}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-enumComputedInit", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nenum E { A = 'x', B = A + 'y' }\n@Directive({selector: '[d]', inputs: [`${E.B}`]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[117, 127]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-destructObj", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst {A} = {A: ['a']};\n@Directive({selector: '[d]', inputs: A})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-destructObjRename", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst {A: B} = {A: ['a']};\n@Directive({selector: '[d]', inputs: B})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-destructObjNested", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst {A: {B}} = {A: {B: ['a']}};\n@Directive({selector: '[d]', inputs: B})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-destructArr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst [A, , B] = [['a'], 0, ['b']];\n@Directive({selector: '[d]', inputs: [...A, ...B]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-destructDefault", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst {A = ['z']} = {} as any;\n@Directive({selector: '[d]', inputs: A})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type 'undefined'." + ], + "diagnosticSpans": [[116, 117]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-destructRestArr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst [, ...R] = ['a', 'b', 'c'];\n@Directive({selector: '[d]', inputs: R})\nexport class Dir {}\n" + }, + "diagnostics": ["Failed to resolve @Directive.inputs to an array Value is of type 'string'."], + "diagnosticSpans": [[119, 120]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-destructRestObj", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst {...R} = {R: ['a']};\n@Directive({selector: '[d]', inputs: R})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-destructFromConst", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst O = {A: ['a']};\nconst {A} = O;\n@Directive({selector: '[d]', inputs: A})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-destructStringKey", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst {'A': B} = {A: ['a']};\n@Directive({selector: '[d]', inputs: B})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[114, 115]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-fnCall", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction mk() { return ['a']; }\n@Directive({selector: '[d]', inputs: mk()})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-fnCallParams", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction mk(x: string, y: string) { return [x, y + '2']; }\n@Directive({selector: '[d]', inputs: mk('a', 'b')})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",b2:\"b2\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-fnCallDefault", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction mk(x = 'd') { return [x]; }\n@Directive({selector: '[d]', inputs: mk()})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{d:\"d\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-fnCallRest", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction mk(...xs: string[]) { return xs; }\n@Directive({selector: '[d]', inputs: mk('a', 'b')})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-fnCallMissingArg", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction mk(x?: string) { return [x]; }\n@Directive({selector: '[d]', inputs: mk()})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'undefined'." + ], + "diagnosticSpans": [[125, 129]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-fnCallNested", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction a(x: string) { return x + '1'; }\nfunction b(x: string) { return [a(x), a(x + 'z')]; }\n@Directive({selector: '[d]', inputs: b('q')})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{q1:\"q1\",qz1:\"qz1\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-fnCallExported", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nexport function mk() { return ['a']; }\n@Directive({selector: '[d]', inputs: mk()})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-fnCallConstRef", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst P = 'p';\nfunction mk() { return [P]; }\n@Directive({selector: '[d]', inputs: mk()})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{p:\"p\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-fnCallEmptyReturn", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction mk() { return; }\n@Directive({selector: '[d]', inputs: mk()})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type 'undefined'." + ], + "diagnosticSpans": [[111, 115]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-fnCallSpreadArgs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction mk(a: string, b: string) { return [a, b]; }\nconst ARGS = ['x', 'y'] as const;\n@Directive({selector: '[d]', inputs: mk(...ARGS)})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\",y:\"y\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-fnOverload", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction mk(): string[];\nfunction mk(x?: string) { return ['a']; }\n@Directive({selector: '[d]', inputs: mk()})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[152, 156]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-fnDeclare", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\ndeclare function mk(): string[];\n@Directive({selector: '[d]', inputs: mk()})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[118, 122]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-fnExpressionConst", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst mk = function () { return ['a']; };\n@Directive({selector: '[d]', inputs: mk()})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[127, 131]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-fnParamDestructured", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction mk({a}: {a: string}) { return [a]; }\n@Directive({selector: '[d]', inputs: mk({a: 'x'})})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[131, 143]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-fnParamShadow", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst x = 'outer';\nfunction mk(x: string) { return [x]; }\n@Directive({selector: '[d]', inputs: mk('inner')})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{inner:\"inner\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-fnUsesOuterParam", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction mk(x: string) { return [x]; }\nfunction outer(y: string) { return mk(y); }\n@Directive({selector: '[d]', inputs: outer('z')})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{z:\"z\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-staticMethodCall", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nclass U { static mk() { return ['a']; } }\n@Directive({selector: '[d]', inputs: U.mk()})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-staticMethodArrowProp", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nclass U { static mk = () => ['a']; }\n@Directive({selector: '[d]', inputs: U.mk()})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[122, 128]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-callNonFunction", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst X = ['a'];\n@Directive({selector: '[d]', inputs: X()})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[102, 105]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-callClass", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nclass U {}\n@Directive({selector: '[d]', inputs: U()})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[96, 99]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-concat", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst A = ['a'];\n@Directive({selector: '[d]', inputs: A.concat(['b'], 'c', [['d']])})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type '[string]'." + ], + "diagnosticSpans": [[102, 131]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-concatSpread", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst A = ['a'];\nconst B = ['b'];\n@Directive({selector: '[d]', inputs: [...A.concat(B)]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-slice", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst A = ['a', 'b'];\n@Directive({selector: '[d]', inputs: A.slice()})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-sliceArgs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst A = ['a', 'b'];\n@Directive({selector: '[d]', inputs: A.slice(1)})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[107, 117]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-length", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst A = ['a', 'b'];\n@Directive({selector: '[d]', inputs: [`n${A.length}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{n2:\"n2\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-stringConcat", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a'.concat('b', 1, true, null)]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{ab1truenull:\"ab1truenull\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-stringConcatBad", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a'.concat({} as any)]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 108]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-stringLength", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [`n${'😀'.length}`, `m${'abc'.length}`]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 124]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-stringIndex", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['abc'[0]]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 95]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-arrayMethodOther", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst A = ['a'];\n@Directive({selector: '[d]', inputs: A.map(x => x)})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[102, 115]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-declareTuple", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\ndeclare const X: ['a', 'b'];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-declareReadonlyTuple", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\ndeclare const X: readonly ['a', 'b: c'];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",b:[0,\"c\",\"b\"]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-declareNamedTuple", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\ndeclare const X: [first: 'a'];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-declareStringArray", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\ndeclare const X: string[];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'X'." + ], + "diagnosticSpans": [[112, 113]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-declareTypeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst Y = ['a'];\ndeclare const X: typeof Y;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-declareLiteralElem", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\ndeclare const S: 'a';\n@Directive({selector: '[d]', inputs: [S]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-declareNumberElem", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\ndeclare const S: 1;\n@Directive({selector: '[d]', inputs: [`n${S}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{n1:\"n1\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-declareLet", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\ndeclare let X: ['a'];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-letNoInit", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nlet X;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type 'undefined'." + ], + "diagnosticSpans": [[92, 93]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-letNoInitElem", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nlet X: string;\n@Directive({selector: '[d]', inputs: [X]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'undefined'." + ], + "diagnosticSpans": [[100, 103]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-varTwice", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nvar X = ['a'];\nvar X = ['b'];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-varTwiceFirstEmpty", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nvar X;\nvar X = ['b'];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type 'undefined'." + ], + "diagnosticSpans": [[107, 108]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-unknownName", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: FOO})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[85, 88]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-unknownNameElem", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [FOO]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 90]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-libGlobal", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [Infinity]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is a reference to 'Infinity'." + ], + "diagnosticSpans": [[85, 95]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-namespaceImport", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nimport * as core from '@angular/core';\n@Directive({selector: '[d]', inputs: core})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type '(module)'." + ], + "diagnosticSpans": [[117, 121]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-namespaceImportElem", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nimport * as core from '@angular/core';\n@Directive({selector: '[d]', inputs: [core]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type '(module)'." + ], + "diagnosticSpans": [[117, 123]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-objMethod", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [{name: 'a', m() {}}]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 106]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-objShorthand", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst name = 'a';\n@Directive({selector: '[d]', inputs: [{name}]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-objComputedStr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst K = 'name';\n@Directive({selector: '[d]', inputs: [{[K]: 'a'}]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-objComputedNum", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [{[1]: 'x', name: 'a'}]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 108]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-objNumericKey", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [{1: 'x', name: 'a'}]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: eval-objComputedNumDescribe", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: {[1]: 'x'}})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[85, 95]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-objSpreadArray", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [{...['x'], name: 'a'}]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 108]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: eval-objGetter", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [{name: 'a', get x() { return 1; }}]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 121]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: msg-spreadNonArray", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst S = 'ab';\n@Directive({selector: '[d]', inputs: ['x', ...S]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[101, 112]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: msg-spreadUnknown", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\ndeclare function f(): any;\n@Directive({selector: '[d]', inputs: ['x', ...f()]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[112, 125]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: msg-spreadRef", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nclass U {}\n@Directive({selector: '[d]', inputs: ['x', ...U]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[96, 107]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: msg-spreadNonArrayOutputs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst S = {a: 1};\n@Directive({selector: '[d]', outputs: ['x', ...S]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve outputs at position 1 to a string Value could not be determined statically." + ], + "diagnosticSpans": [[104, 115]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: msg-spreadObjInArr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst S = {a: 1};\n@Directive({selector: '[d]', inputs: [...S]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[103, 109]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: msg-objSpreadNonObject", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [{...'x', name: 'a'}]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 106]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: msg-objSpreadDynamic", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\ndeclare function f(): any;\n@Directive({selector: '[d]', inputs: [{...f(), name: 'a'}]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[112, 133]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: msg-enumOutputs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nenum E { A = 'x' }\n@Directive({selector: '[d]', outputs: [E.A]})\nexport class Dir {}\n" + }, + "diagnostics": ["Failed to resolve outputs at position 0 to a string Value is of type 'E'."], + "diagnosticSpans": [[105, 110]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: msg-anonymousClassRef", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [class {}]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 95]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: rej-satisfies", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a'] satisfies string[]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[85, 109]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: rej-typeAssertion", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a']})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[85, 100]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: rej-elemSatisfies", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a' satisfies string]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[85, 107]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: rej-arrayStringIndex", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst X = ['a', 'b'];\n@Directive({selector: '[d]', inputs: [X['0']]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[107, 115]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: rej-arrayNumIndex", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst X = ['a', 'b'];\n@Directive({selector: '[d]', inputs: [X[1]]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: rej-arrayFloatIndex", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst X = ['a', 'b'];\n@Directive({selector: '[d]', inputs: [X[0.5]]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[107, 115]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: rej-arrayOutOfRange", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst X = ['a', 'b'];\n@Directive({selector: '[d]', inputs: [X[5]]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'undefined'." + ], + "diagnosticSpans": [[107, 113]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: rej-arrayNegIndex", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst X = ['a', 'b'];\n@Directive({selector: '[d]', inputs: [X[-1]]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'undefined'." + ], + "diagnosticSpans": [[107, 114]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: rej-arrayPropOther", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst X = ['a', 'b'];\n@Directive({selector: '[d]', inputs: [X.foo]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[107, 114]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: rej-objectIndexNum", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nconst O = {1: 'a'};\n@Directive({selector: '[d]', inputs: [O[1]]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: rej-computedConfigKey", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nconst K = 'inputs';\n@Directive({selector: '[d]', [K]: ['a']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: rej-computedConfigKeyLiteral", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\n@Directive({selector: '[d]', ['inputs']: ['a']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: rej-stringConfigKey", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\n@Directive({selector: '[d]', 'inputs': ['a'], \"outputs\": ['o']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "outputs": "{o:\"o\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: rej-shorthandConfigKey", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nconst inputs = ['a'];\n@Directive({selector: '[d]', inputs})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: rej-duplicateConfigKey", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a'], inputs: ['b']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: key-proto", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['__proto__', 'b']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: key-protoObj", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: [{name: '__proto__'}, 'b']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: key-protoAlias", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a: __proto__', 'b']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:[0,\"__proto__\",\"a\"],b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: key-protoOutputs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', outputs: ['__proto__', 'b']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "outputs": "{b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: key-integerOrder", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['b', '1', 'a', '0', '10', '2']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "note": "ngtsc leaves integer-like keys unquoted in the JS map (and `01`, `1e3` too, which changes or breaks them); oxc quotes them. The order is compared through the .d.ts." + }, + { + "name": "probe: key-integerLike", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['b', '01', '4294967294', '4294967295', '-1', '1.5', '1e3', ' 3']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "note": "ngtsc leaves integer-like keys unquoted in the JS map (and `01`, `1e3` too, which changes or breaks them); oxc quotes them. The order is compared through the .d.ts." + }, + { + "name": "probe: key-integerOutputs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', outputs: ['b', '1', 'a']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "note": "ngtsc leaves integer-like keys unquoted in the JS map (and `01`, `1e3` too, which changes or breaks them); oxc quotes them. The order is compared through the .d.ts." + }, + { + "name": "probe: key-integerMerge", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['b', '1']})\nexport class Dir { @Input() a: any; @Input() c: any; }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "note": "ngtsc leaves integer-like keys unquoted in the JS map (and `01`, `1e3` too, which changes or breaks them); oxc quotes them. The order is compared through the .d.ts." + }, + { + "name": "probe: key-protoWithMember", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['__proto__']})\nexport class Dir { @Input() a: any; }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: key-whitespaceAlias", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a: spaced alias ', ' b ']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:[0,\"spaced alias\",\"a\"],b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: depth-chain300", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nconst X0 = ['a'];\nconst X1 = X0;\nconst X2 = X1;\nconst X3 = X2;\nconst X4 = X3;\nconst X5 = X4;\nconst X6 = X5;\nconst X7 = X6;\nconst X8 = X7;\nconst X9 = X8;\nconst X10 = X9;\nconst X11 = X10;\nconst X12 = X11;\nconst X13 = X12;\nconst X14 = X13;\nconst X15 = X14;\nconst X16 = X15;\nconst X17 = X16;\nconst X18 = X17;\nconst X19 = X18;\nconst X20 = X19;\nconst X21 = X20;\nconst X22 = X21;\nconst X23 = X22;\nconst X24 = X23;\nconst X25 = X24;\nconst X26 = X25;\nconst X27 = X26;\nconst X28 = X27;\nconst X29 = X28;\nconst X30 = X29;\nconst X31 = X30;\nconst X32 = X31;\nconst X33 = X32;\nconst X34 = X33;\nconst X35 = X34;\nconst X36 = X35;\nconst X37 = X36;\nconst X38 = X37;\nconst X39 = X38;\nconst X40 = X39;\nconst X41 = X40;\nconst X42 = X41;\nconst X43 = X42;\nconst X44 = X43;\nconst X45 = X44;\nconst X46 = X45;\nconst X47 = X46;\nconst X48 = X47;\nconst X49 = X48;\nconst X50 = X49;\nconst X51 = X50;\nconst X52 = X51;\nconst X53 = X52;\nconst X54 = X53;\nconst X55 = X54;\nconst X56 = X55;\nconst X57 = X56;\nconst X58 = X57;\nconst X59 = X58;\nconst X60 = X59;\nconst X61 = X60;\nconst X62 = X61;\nconst X63 = X62;\nconst X64 = X63;\nconst X65 = X64;\nconst X66 = X65;\nconst X67 = X66;\nconst X68 = X67;\nconst X69 = X68;\nconst X70 = X69;\nconst X71 = X70;\nconst X72 = X71;\nconst X73 = X72;\nconst X74 = X73;\nconst X75 = X74;\nconst X76 = X75;\nconst X77 = X76;\nconst X78 = X77;\nconst X79 = X78;\nconst X80 = X79;\nconst X81 = X80;\nconst X82 = X81;\nconst X83 = X82;\nconst X84 = X83;\nconst X85 = X84;\nconst X86 = X85;\nconst X87 = X86;\nconst X88 = X87;\nconst X89 = X88;\nconst X90 = X89;\nconst X91 = X90;\nconst X92 = X91;\nconst X93 = X92;\nconst X94 = X93;\nconst X95 = X94;\nconst X96 = X95;\nconst X97 = X96;\nconst X98 = X97;\nconst X99 = X98;\nconst X100 = X99;\nconst X101 = X100;\nconst X102 = X101;\nconst X103 = X102;\nconst X104 = X103;\nconst X105 = X104;\nconst X106 = X105;\nconst X107 = X106;\nconst X108 = X107;\nconst X109 = X108;\nconst X110 = X109;\nconst X111 = X110;\nconst X112 = X111;\nconst X113 = X112;\nconst X114 = X113;\nconst X115 = X114;\nconst X116 = X115;\nconst X117 = X116;\nconst X118 = X117;\nconst X119 = X118;\nconst X120 = X119;\nconst X121 = X120;\nconst X122 = X121;\nconst X123 = X122;\nconst X124 = X123;\nconst X125 = X124;\nconst X126 = X125;\nconst X127 = X126;\nconst X128 = X127;\nconst X129 = X128;\nconst X130 = X129;\nconst X131 = X130;\nconst X132 = X131;\nconst X133 = X132;\nconst X134 = X133;\nconst X135 = X134;\nconst X136 = X135;\nconst X137 = X136;\nconst X138 = X137;\nconst X139 = X138;\nconst X140 = X139;\nconst X141 = X140;\nconst X142 = X141;\nconst X143 = X142;\nconst X144 = X143;\nconst X145 = X144;\nconst X146 = X145;\nconst X147 = X146;\nconst X148 = X147;\nconst X149 = X148;\nconst X150 = X149;\nconst X151 = X150;\nconst X152 = X151;\nconst X153 = X152;\nconst X154 = X153;\nconst X155 = X154;\nconst X156 = X155;\nconst X157 = X156;\nconst X158 = X157;\nconst X159 = X158;\nconst X160 = X159;\nconst X161 = X160;\nconst X162 = X161;\nconst X163 = X162;\nconst X164 = X163;\nconst X165 = X164;\nconst X166 = X165;\nconst X167 = X166;\nconst X168 = X167;\nconst X169 = X168;\nconst X170 = X169;\nconst X171 = X170;\nconst X172 = X171;\nconst X173 = X172;\nconst X174 = X173;\nconst X175 = X174;\nconst X176 = X175;\nconst X177 = X176;\nconst X178 = X177;\nconst X179 = X178;\nconst X180 = X179;\nconst X181 = X180;\nconst X182 = X181;\nconst X183 = X182;\nconst X184 = X183;\nconst X185 = X184;\nconst X186 = X185;\nconst X187 = X186;\nconst X188 = X187;\nconst X189 = X188;\nconst X190 = X189;\nconst X191 = X190;\nconst X192 = X191;\nconst X193 = X192;\nconst X194 = X193;\nconst X195 = X194;\nconst X196 = X195;\nconst X197 = X196;\nconst X198 = X197;\nconst X199 = X198;\nconst X200 = X199;\nconst X201 = X200;\nconst X202 = X201;\nconst X203 = X202;\nconst X204 = X203;\nconst X205 = X204;\nconst X206 = X205;\nconst X207 = X206;\nconst X208 = X207;\nconst X209 = X208;\nconst X210 = X209;\nconst X211 = X210;\nconst X212 = X211;\nconst X213 = X212;\nconst X214 = X213;\nconst X215 = X214;\nconst X216 = X215;\nconst X217 = X216;\nconst X218 = X217;\nconst X219 = X218;\nconst X220 = X219;\nconst X221 = X220;\nconst X222 = X221;\nconst X223 = X222;\nconst X224 = X223;\nconst X225 = X224;\nconst X226 = X225;\nconst X227 = X226;\nconst X228 = X227;\nconst X229 = X228;\nconst X230 = X229;\nconst X231 = X230;\nconst X232 = X231;\nconst X233 = X232;\nconst X234 = X233;\nconst X235 = X234;\nconst X236 = X235;\nconst X237 = X236;\nconst X238 = X237;\nconst X239 = X238;\nconst X240 = X239;\nconst X241 = X240;\nconst X242 = X241;\nconst X243 = X242;\nconst X244 = X243;\nconst X245 = X244;\nconst X246 = X245;\nconst X247 = X246;\nconst X248 = X247;\nconst X249 = X248;\nconst X250 = X249;\nconst X251 = X250;\nconst X252 = X251;\nconst X253 = X252;\nconst X254 = X253;\nconst X255 = X254;\nconst X256 = X255;\nconst X257 = X256;\nconst X258 = X257;\nconst X259 = X258;\nconst X260 = X259;\nconst X261 = X260;\nconst X262 = X261;\nconst X263 = X262;\nconst X264 = X263;\nconst X265 = X264;\nconst X266 = X265;\nconst X267 = X266;\nconst X268 = X267;\nconst X269 = X268;\nconst X270 = X269;\nconst X271 = X270;\nconst X272 = X271;\nconst X273 = X272;\nconst X274 = X273;\nconst X275 = X274;\nconst X276 = X275;\nconst X277 = X276;\nconst X278 = X277;\nconst X279 = X278;\nconst X280 = X279;\nconst X281 = X280;\nconst X282 = X281;\nconst X283 = X282;\nconst X284 = X283;\nconst X285 = X284;\nconst X286 = X285;\nconst X287 = X286;\nconst X288 = X287;\nconst X289 = X288;\nconst X290 = X289;\nconst X291 = X290;\nconst X292 = X291;\nconst X293 = X292;\nconst X294 = X293;\nconst X295 = X294;\nconst X296 = X295;\nconst X297 = X296;\nconst X298 = X297;\nconst X299 = X298;\n\n@Directive({selector: '[d]', inputs: X299})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: depth-chain1000", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nconst X0 = ['a'];\nconst X1 = X0;\nconst X2 = X1;\nconst X3 = X2;\nconst X4 = X3;\nconst X5 = X4;\nconst X6 = X5;\nconst X7 = X6;\nconst X8 = X7;\nconst X9 = X8;\nconst X10 = X9;\nconst X11 = X10;\nconst X12 = X11;\nconst X13 = X12;\nconst X14 = X13;\nconst X15 = X14;\nconst X16 = X15;\nconst X17 = X16;\nconst X18 = X17;\nconst X19 = X18;\nconst X20 = X19;\nconst X21 = X20;\nconst X22 = X21;\nconst X23 = X22;\nconst X24 = X23;\nconst X25 = X24;\nconst X26 = X25;\nconst X27 = X26;\nconst X28 = X27;\nconst X29 = X28;\nconst X30 = X29;\nconst X31 = X30;\nconst X32 = X31;\nconst X33 = X32;\nconst X34 = X33;\nconst X35 = X34;\nconst X36 = X35;\nconst X37 = X36;\nconst X38 = X37;\nconst X39 = X38;\nconst X40 = X39;\nconst X41 = X40;\nconst X42 = X41;\nconst X43 = X42;\nconst X44 = X43;\nconst X45 = X44;\nconst X46 = X45;\nconst X47 = X46;\nconst X48 = X47;\nconst X49 = X48;\nconst X50 = X49;\nconst X51 = X50;\nconst X52 = X51;\nconst X53 = X52;\nconst X54 = X53;\nconst X55 = X54;\nconst X56 = X55;\nconst X57 = X56;\nconst X58 = X57;\nconst X59 = X58;\nconst X60 = X59;\nconst X61 = X60;\nconst X62 = X61;\nconst X63 = X62;\nconst X64 = X63;\nconst X65 = X64;\nconst X66 = X65;\nconst X67 = X66;\nconst X68 = X67;\nconst X69 = X68;\nconst X70 = X69;\nconst X71 = X70;\nconst X72 = X71;\nconst X73 = X72;\nconst X74 = X73;\nconst X75 = X74;\nconst X76 = X75;\nconst X77 = X76;\nconst X78 = X77;\nconst X79 = X78;\nconst X80 = X79;\nconst X81 = X80;\nconst X82 = X81;\nconst X83 = X82;\nconst X84 = X83;\nconst X85 = X84;\nconst X86 = X85;\nconst X87 = X86;\nconst X88 = X87;\nconst X89 = X88;\nconst X90 = X89;\nconst X91 = X90;\nconst X92 = X91;\nconst X93 = X92;\nconst X94 = X93;\nconst X95 = X94;\nconst X96 = X95;\nconst X97 = X96;\nconst X98 = X97;\nconst X99 = X98;\nconst X100 = X99;\nconst X101 = X100;\nconst X102 = X101;\nconst X103 = X102;\nconst X104 = X103;\nconst X105 = X104;\nconst X106 = X105;\nconst X107 = X106;\nconst X108 = X107;\nconst X109 = X108;\nconst X110 = X109;\nconst X111 = X110;\nconst X112 = X111;\nconst X113 = X112;\nconst X114 = X113;\nconst X115 = X114;\nconst X116 = X115;\nconst X117 = X116;\nconst X118 = X117;\nconst X119 = X118;\nconst X120 = X119;\nconst X121 = X120;\nconst X122 = X121;\nconst X123 = X122;\nconst X124 = X123;\nconst X125 = X124;\nconst X126 = X125;\nconst X127 = X126;\nconst X128 = X127;\nconst X129 = X128;\nconst X130 = X129;\nconst X131 = X130;\nconst X132 = X131;\nconst X133 = X132;\nconst X134 = X133;\nconst X135 = X134;\nconst X136 = X135;\nconst X137 = X136;\nconst X138 = X137;\nconst X139 = X138;\nconst X140 = X139;\nconst X141 = X140;\nconst X142 = X141;\nconst X143 = X142;\nconst X144 = X143;\nconst X145 = X144;\nconst X146 = X145;\nconst X147 = X146;\nconst X148 = X147;\nconst X149 = X148;\nconst X150 = X149;\nconst X151 = X150;\nconst X152 = X151;\nconst X153 = X152;\nconst X154 = X153;\nconst X155 = X154;\nconst X156 = X155;\nconst X157 = X156;\nconst X158 = X157;\nconst X159 = X158;\nconst X160 = X159;\nconst X161 = X160;\nconst X162 = X161;\nconst X163 = X162;\nconst X164 = X163;\nconst X165 = X164;\nconst X166 = X165;\nconst X167 = X166;\nconst X168 = X167;\nconst X169 = X168;\nconst X170 = X169;\nconst X171 = X170;\nconst X172 = X171;\nconst X173 = X172;\nconst X174 = X173;\nconst X175 = X174;\nconst X176 = X175;\nconst X177 = X176;\nconst X178 = X177;\nconst X179 = X178;\nconst X180 = X179;\nconst X181 = X180;\nconst X182 = X181;\nconst X183 = X182;\nconst X184 = X183;\nconst X185 = X184;\nconst X186 = X185;\nconst X187 = X186;\nconst X188 = X187;\nconst X189 = X188;\nconst X190 = X189;\nconst X191 = X190;\nconst X192 = X191;\nconst X193 = X192;\nconst X194 = X193;\nconst X195 = X194;\nconst X196 = X195;\nconst X197 = X196;\nconst X198 = X197;\nconst X199 = X198;\nconst X200 = X199;\nconst X201 = X200;\nconst X202 = X201;\nconst X203 = X202;\nconst X204 = X203;\nconst X205 = X204;\nconst X206 = X205;\nconst X207 = X206;\nconst X208 = X207;\nconst X209 = X208;\nconst X210 = X209;\nconst X211 = X210;\nconst X212 = X211;\nconst X213 = X212;\nconst X214 = X213;\nconst X215 = X214;\nconst X216 = X215;\nconst X217 = X216;\nconst X218 = X217;\nconst X219 = X218;\nconst X220 = X219;\nconst X221 = X220;\nconst X222 = X221;\nconst X223 = X222;\nconst X224 = X223;\nconst X225 = X224;\nconst X226 = X225;\nconst X227 = X226;\nconst X228 = X227;\nconst X229 = X228;\nconst X230 = X229;\nconst X231 = X230;\nconst X232 = X231;\nconst X233 = X232;\nconst X234 = X233;\nconst X235 = X234;\nconst X236 = X235;\nconst X237 = X236;\nconst X238 = X237;\nconst X239 = X238;\nconst X240 = X239;\nconst X241 = X240;\nconst X242 = X241;\nconst X243 = X242;\nconst X244 = X243;\nconst X245 = X244;\nconst X246 = X245;\nconst X247 = X246;\nconst X248 = X247;\nconst X249 = X248;\nconst X250 = X249;\nconst X251 = X250;\nconst X252 = X251;\nconst X253 = X252;\nconst X254 = X253;\nconst X255 = X254;\nconst X256 = X255;\nconst X257 = X256;\nconst X258 = X257;\nconst X259 = X258;\nconst X260 = X259;\nconst X261 = X260;\nconst X262 = X261;\nconst X263 = X262;\nconst X264 = X263;\nconst X265 = X264;\nconst X266 = X265;\nconst X267 = X266;\nconst X268 = X267;\nconst X269 = X268;\nconst X270 = X269;\nconst X271 = X270;\nconst X272 = X271;\nconst X273 = X272;\nconst X274 = X273;\nconst X275 = X274;\nconst X276 = X275;\nconst X277 = X276;\nconst X278 = X277;\nconst X279 = X278;\nconst X280 = X279;\nconst X281 = X280;\nconst X282 = X281;\nconst X283 = X282;\nconst X284 = X283;\nconst X285 = X284;\nconst X286 = X285;\nconst X287 = X286;\nconst X288 = X287;\nconst X289 = X288;\nconst X290 = X289;\nconst X291 = X290;\nconst X292 = X291;\nconst X293 = X292;\nconst X294 = X293;\nconst X295 = X294;\nconst X296 = X295;\nconst X297 = X296;\nconst X298 = X297;\nconst X299 = X298;\nconst X300 = X299;\nconst X301 = X300;\nconst X302 = X301;\nconst X303 = X302;\nconst X304 = X303;\nconst X305 = X304;\nconst X306 = X305;\nconst X307 = X306;\nconst X308 = X307;\nconst X309 = X308;\nconst X310 = X309;\nconst X311 = X310;\nconst X312 = X311;\nconst X313 = X312;\nconst X314 = X313;\nconst X315 = X314;\nconst X316 = X315;\nconst X317 = X316;\nconst X318 = X317;\nconst X319 = X318;\nconst X320 = X319;\nconst X321 = X320;\nconst X322 = X321;\nconst X323 = X322;\nconst X324 = X323;\nconst X325 = X324;\nconst X326 = X325;\nconst X327 = X326;\nconst X328 = X327;\nconst X329 = X328;\nconst X330 = X329;\nconst X331 = X330;\nconst X332 = X331;\nconst X333 = X332;\nconst X334 = X333;\nconst X335 = X334;\nconst X336 = X335;\nconst X337 = X336;\nconst X338 = X337;\nconst X339 = X338;\nconst X340 = X339;\nconst X341 = X340;\nconst X342 = X341;\nconst X343 = X342;\nconst X344 = X343;\nconst X345 = X344;\nconst X346 = X345;\nconst X347 = X346;\nconst X348 = X347;\nconst X349 = X348;\nconst X350 = X349;\nconst X351 = X350;\nconst X352 = X351;\nconst X353 = X352;\nconst X354 = X353;\nconst X355 = X354;\nconst X356 = X355;\nconst X357 = X356;\nconst X358 = X357;\nconst X359 = X358;\nconst X360 = X359;\nconst X361 = X360;\nconst X362 = X361;\nconst X363 = X362;\nconst X364 = X363;\nconst X365 = X364;\nconst X366 = X365;\nconst X367 = X366;\nconst X368 = X367;\nconst X369 = X368;\nconst X370 = X369;\nconst X371 = X370;\nconst X372 = X371;\nconst X373 = X372;\nconst X374 = X373;\nconst X375 = X374;\nconst X376 = X375;\nconst X377 = X376;\nconst X378 = X377;\nconst X379 = X378;\nconst X380 = X379;\nconst X381 = X380;\nconst X382 = X381;\nconst X383 = X382;\nconst X384 = X383;\nconst X385 = X384;\nconst X386 = X385;\nconst X387 = X386;\nconst X388 = X387;\nconst X389 = X388;\nconst X390 = X389;\nconst X391 = X390;\nconst X392 = X391;\nconst X393 = X392;\nconst X394 = X393;\nconst X395 = X394;\nconst X396 = X395;\nconst X397 = X396;\nconst X398 = X397;\nconst X399 = X398;\nconst X400 = X399;\nconst X401 = X400;\nconst X402 = X401;\nconst X403 = X402;\nconst X404 = X403;\nconst X405 = X404;\nconst X406 = X405;\nconst X407 = X406;\nconst X408 = X407;\nconst X409 = X408;\nconst X410 = X409;\nconst X411 = X410;\nconst X412 = X411;\nconst X413 = X412;\nconst X414 = X413;\nconst X415 = X414;\nconst X416 = X415;\nconst X417 = X416;\nconst X418 = X417;\nconst X419 = X418;\nconst X420 = X419;\nconst X421 = X420;\nconst X422 = X421;\nconst X423 = X422;\nconst X424 = X423;\nconst X425 = X424;\nconst X426 = X425;\nconst X427 = X426;\nconst X428 = X427;\nconst X429 = X428;\nconst X430 = X429;\nconst X431 = X430;\nconst X432 = X431;\nconst X433 = X432;\nconst X434 = X433;\nconst X435 = X434;\nconst X436 = X435;\nconst X437 = X436;\nconst X438 = X437;\nconst X439 = X438;\nconst X440 = X439;\nconst X441 = X440;\nconst X442 = X441;\nconst X443 = X442;\nconst X444 = X443;\nconst X445 = X444;\nconst X446 = X445;\nconst X447 = X446;\nconst X448 = X447;\nconst X449 = X448;\nconst X450 = X449;\nconst X451 = X450;\nconst X452 = X451;\nconst X453 = X452;\nconst X454 = X453;\nconst X455 = X454;\nconst X456 = X455;\nconst X457 = X456;\nconst X458 = X457;\nconst X459 = X458;\nconst X460 = X459;\nconst X461 = X460;\nconst X462 = X461;\nconst X463 = X462;\nconst X464 = X463;\nconst X465 = X464;\nconst X466 = X465;\nconst X467 = X466;\nconst X468 = X467;\nconst X469 = X468;\nconst X470 = X469;\nconst X471 = X470;\nconst X472 = X471;\nconst X473 = X472;\nconst X474 = X473;\nconst X475 = X474;\nconst X476 = X475;\nconst X477 = X476;\nconst X478 = X477;\nconst X479 = X478;\nconst X480 = X479;\nconst X481 = X480;\nconst X482 = X481;\nconst X483 = X482;\nconst X484 = X483;\nconst X485 = X484;\nconst X486 = X485;\nconst X487 = X486;\nconst X488 = X487;\nconst X489 = X488;\nconst X490 = X489;\nconst X491 = X490;\nconst X492 = X491;\nconst X493 = X492;\nconst X494 = X493;\nconst X495 = X494;\nconst X496 = X495;\nconst X497 = X496;\nconst X498 = X497;\nconst X499 = X498;\nconst X500 = X499;\nconst X501 = X500;\nconst X502 = X501;\nconst X503 = X502;\nconst X504 = X503;\nconst X505 = X504;\nconst X506 = X505;\nconst X507 = X506;\nconst X508 = X507;\nconst X509 = X508;\nconst X510 = X509;\nconst X511 = X510;\nconst X512 = X511;\nconst X513 = X512;\nconst X514 = X513;\nconst X515 = X514;\nconst X516 = X515;\nconst X517 = X516;\nconst X518 = X517;\nconst X519 = X518;\nconst X520 = X519;\nconst X521 = X520;\nconst X522 = X521;\nconst X523 = X522;\nconst X524 = X523;\nconst X525 = X524;\nconst X526 = X525;\nconst X527 = X526;\nconst X528 = X527;\nconst X529 = X528;\nconst X530 = X529;\nconst X531 = X530;\nconst X532 = X531;\nconst X533 = X532;\nconst X534 = X533;\nconst X535 = X534;\nconst X536 = X535;\nconst X537 = X536;\nconst X538 = X537;\nconst X539 = X538;\nconst X540 = X539;\nconst X541 = X540;\nconst X542 = X541;\nconst X543 = X542;\nconst X544 = X543;\nconst X545 = X544;\nconst X546 = X545;\nconst X547 = X546;\nconst X548 = X547;\nconst X549 = X548;\nconst X550 = X549;\nconst X551 = X550;\nconst X552 = X551;\nconst X553 = X552;\nconst X554 = X553;\nconst X555 = X554;\nconst X556 = X555;\nconst X557 = X556;\nconst X558 = X557;\nconst X559 = X558;\nconst X560 = X559;\nconst X561 = X560;\nconst X562 = X561;\nconst X563 = X562;\nconst X564 = X563;\nconst X565 = X564;\nconst X566 = X565;\nconst X567 = X566;\nconst X568 = X567;\nconst X569 = X568;\nconst X570 = X569;\nconst X571 = X570;\nconst X572 = X571;\nconst X573 = X572;\nconst X574 = X573;\nconst X575 = X574;\nconst X576 = X575;\nconst X577 = X576;\nconst X578 = X577;\nconst X579 = X578;\nconst X580 = X579;\nconst X581 = X580;\nconst X582 = X581;\nconst X583 = X582;\nconst X584 = X583;\nconst X585 = X584;\nconst X586 = X585;\nconst X587 = X586;\nconst X588 = X587;\nconst X589 = X588;\nconst X590 = X589;\nconst X591 = X590;\nconst X592 = X591;\nconst X593 = X592;\nconst X594 = X593;\nconst X595 = X594;\nconst X596 = X595;\nconst X597 = X596;\nconst X598 = X597;\nconst X599 = X598;\nconst X600 = X599;\nconst X601 = X600;\nconst X602 = X601;\nconst X603 = X602;\nconst X604 = X603;\nconst X605 = X604;\nconst X606 = X605;\nconst X607 = X606;\nconst X608 = X607;\nconst X609 = X608;\nconst X610 = X609;\nconst X611 = X610;\nconst X612 = X611;\nconst X613 = X612;\nconst X614 = X613;\nconst X615 = X614;\nconst X616 = X615;\nconst X617 = X616;\nconst X618 = X617;\nconst X619 = X618;\nconst X620 = X619;\nconst X621 = X620;\nconst X622 = X621;\nconst X623 = X622;\nconst X624 = X623;\nconst X625 = X624;\nconst X626 = X625;\nconst X627 = X626;\nconst X628 = X627;\nconst X629 = X628;\nconst X630 = X629;\nconst X631 = X630;\nconst X632 = X631;\nconst X633 = X632;\nconst X634 = X633;\nconst X635 = X634;\nconst X636 = X635;\nconst X637 = X636;\nconst X638 = X637;\nconst X639 = X638;\nconst X640 = X639;\nconst X641 = X640;\nconst X642 = X641;\nconst X643 = X642;\nconst X644 = X643;\nconst X645 = X644;\nconst X646 = X645;\nconst X647 = X646;\nconst X648 = X647;\nconst X649 = X648;\nconst X650 = X649;\nconst X651 = X650;\nconst X652 = X651;\nconst X653 = X652;\nconst X654 = X653;\nconst X655 = X654;\nconst X656 = X655;\nconst X657 = X656;\nconst X658 = X657;\nconst X659 = X658;\nconst X660 = X659;\nconst X661 = X660;\nconst X662 = X661;\nconst X663 = X662;\nconst X664 = X663;\nconst X665 = X664;\nconst X666 = X665;\nconst X667 = X666;\nconst X668 = X667;\nconst X669 = X668;\nconst X670 = X669;\nconst X671 = X670;\nconst X672 = X671;\nconst X673 = X672;\nconst X674 = X673;\nconst X675 = X674;\nconst X676 = X675;\nconst X677 = X676;\nconst X678 = X677;\nconst X679 = X678;\nconst X680 = X679;\nconst X681 = X680;\nconst X682 = X681;\nconst X683 = X682;\nconst X684 = X683;\nconst X685 = X684;\nconst X686 = X685;\nconst X687 = X686;\nconst X688 = X687;\nconst X689 = X688;\nconst X690 = X689;\nconst X691 = X690;\nconst X692 = X691;\nconst X693 = X692;\nconst X694 = X693;\nconst X695 = X694;\nconst X696 = X695;\nconst X697 = X696;\nconst X698 = X697;\nconst X699 = X698;\nconst X700 = X699;\nconst X701 = X700;\nconst X702 = X701;\nconst X703 = X702;\nconst X704 = X703;\nconst X705 = X704;\nconst X706 = X705;\nconst X707 = X706;\nconst X708 = X707;\nconst X709 = X708;\nconst X710 = X709;\nconst X711 = X710;\nconst X712 = X711;\nconst X713 = X712;\nconst X714 = X713;\nconst X715 = X714;\nconst X716 = X715;\nconst X717 = X716;\nconst X718 = X717;\nconst X719 = X718;\nconst X720 = X719;\nconst X721 = X720;\nconst X722 = X721;\nconst X723 = X722;\nconst X724 = X723;\nconst X725 = X724;\nconst X726 = X725;\nconst X727 = X726;\nconst X728 = X727;\nconst X729 = X728;\nconst X730 = X729;\nconst X731 = X730;\nconst X732 = X731;\nconst X733 = X732;\nconst X734 = X733;\nconst X735 = X734;\nconst X736 = X735;\nconst X737 = X736;\nconst X738 = X737;\nconst X739 = X738;\nconst X740 = X739;\nconst X741 = X740;\nconst X742 = X741;\nconst X743 = X742;\nconst X744 = X743;\nconst X745 = X744;\nconst X746 = X745;\nconst X747 = X746;\nconst X748 = X747;\nconst X749 = X748;\nconst X750 = X749;\nconst X751 = X750;\nconst X752 = X751;\nconst X753 = X752;\nconst X754 = X753;\nconst X755 = X754;\nconst X756 = X755;\nconst X757 = X756;\nconst X758 = X757;\nconst X759 = X758;\nconst X760 = X759;\nconst X761 = X760;\nconst X762 = X761;\nconst X763 = X762;\nconst X764 = X763;\nconst X765 = X764;\nconst X766 = X765;\nconst X767 = X766;\nconst X768 = X767;\nconst X769 = X768;\nconst X770 = X769;\nconst X771 = X770;\nconst X772 = X771;\nconst X773 = X772;\nconst X774 = X773;\nconst X775 = X774;\nconst X776 = X775;\nconst X777 = X776;\nconst X778 = X777;\nconst X779 = X778;\nconst X780 = X779;\nconst X781 = X780;\nconst X782 = X781;\nconst X783 = X782;\nconst X784 = X783;\nconst X785 = X784;\nconst X786 = X785;\nconst X787 = X786;\nconst X788 = X787;\nconst X789 = X788;\nconst X790 = X789;\nconst X791 = X790;\nconst X792 = X791;\nconst X793 = X792;\nconst X794 = X793;\nconst X795 = X794;\nconst X796 = X795;\nconst X797 = X796;\nconst X798 = X797;\nconst X799 = X798;\nconst X800 = X799;\nconst X801 = X800;\nconst X802 = X801;\nconst X803 = X802;\nconst X804 = X803;\nconst X805 = X804;\nconst X806 = X805;\nconst X807 = X806;\nconst X808 = X807;\nconst X809 = X808;\nconst X810 = X809;\nconst X811 = X810;\nconst X812 = X811;\nconst X813 = X812;\nconst X814 = X813;\nconst X815 = X814;\nconst X816 = X815;\nconst X817 = X816;\nconst X818 = X817;\nconst X819 = X818;\nconst X820 = X819;\nconst X821 = X820;\nconst X822 = X821;\nconst X823 = X822;\nconst X824 = X823;\nconst X825 = X824;\nconst X826 = X825;\nconst X827 = X826;\nconst X828 = X827;\nconst X829 = X828;\nconst X830 = X829;\nconst X831 = X830;\nconst X832 = X831;\nconst X833 = X832;\nconst X834 = X833;\nconst X835 = X834;\nconst X836 = X835;\nconst X837 = X836;\nconst X838 = X837;\nconst X839 = X838;\nconst X840 = X839;\nconst X841 = X840;\nconst X842 = X841;\nconst X843 = X842;\nconst X844 = X843;\nconst X845 = X844;\nconst X846 = X845;\nconst X847 = X846;\nconst X848 = X847;\nconst X849 = X848;\nconst X850 = X849;\nconst X851 = X850;\nconst X852 = X851;\nconst X853 = X852;\nconst X854 = X853;\nconst X855 = X854;\nconst X856 = X855;\nconst X857 = X856;\nconst X858 = X857;\nconst X859 = X858;\nconst X860 = X859;\nconst X861 = X860;\nconst X862 = X861;\nconst X863 = X862;\nconst X864 = X863;\nconst X865 = X864;\nconst X866 = X865;\nconst X867 = X866;\nconst X868 = X867;\nconst X869 = X868;\nconst X870 = X869;\nconst X871 = X870;\nconst X872 = X871;\nconst X873 = X872;\nconst X874 = X873;\nconst X875 = X874;\nconst X876 = X875;\nconst X877 = X876;\nconst X878 = X877;\nconst X879 = X878;\nconst X880 = X879;\nconst X881 = X880;\nconst X882 = X881;\nconst X883 = X882;\nconst X884 = X883;\nconst X885 = X884;\nconst X886 = X885;\nconst X887 = X886;\nconst X888 = X887;\nconst X889 = X888;\nconst X890 = X889;\nconst X891 = X890;\nconst X892 = X891;\nconst X893 = X892;\nconst X894 = X893;\nconst X895 = X894;\nconst X896 = X895;\nconst X897 = X896;\nconst X898 = X897;\nconst X899 = X898;\nconst X900 = X899;\nconst X901 = X900;\nconst X902 = X901;\nconst X903 = X902;\nconst X904 = X903;\nconst X905 = X904;\nconst X906 = X905;\nconst X907 = X906;\nconst X908 = X907;\nconst X909 = X908;\nconst X910 = X909;\nconst X911 = X910;\nconst X912 = X911;\nconst X913 = X912;\nconst X914 = X913;\nconst X915 = X914;\nconst X916 = X915;\nconst X917 = X916;\nconst X918 = X917;\nconst X919 = X918;\nconst X920 = X919;\nconst X921 = X920;\nconst X922 = X921;\nconst X923 = X922;\nconst X924 = X923;\nconst X925 = X924;\nconst X926 = X925;\nconst X927 = X926;\nconst X928 = X927;\nconst X929 = X928;\nconst X930 = X929;\nconst X931 = X930;\nconst X932 = X931;\nconst X933 = X932;\nconst X934 = X933;\nconst X935 = X934;\nconst X936 = X935;\nconst X937 = X936;\nconst X938 = X937;\nconst X939 = X938;\nconst X940 = X939;\nconst X941 = X940;\nconst X942 = X941;\nconst X943 = X942;\nconst X944 = X943;\nconst X945 = X944;\nconst X946 = X945;\nconst X947 = X946;\nconst X948 = X947;\nconst X949 = X948;\nconst X950 = X949;\nconst X951 = X950;\nconst X952 = X951;\nconst X953 = X952;\nconst X954 = X953;\nconst X955 = X954;\nconst X956 = X955;\nconst X957 = X956;\nconst X958 = X957;\nconst X959 = X958;\nconst X960 = X959;\nconst X961 = X960;\nconst X962 = X961;\nconst X963 = X962;\nconst X964 = X963;\nconst X965 = X964;\nconst X966 = X965;\nconst X967 = X966;\nconst X968 = X967;\nconst X969 = X968;\nconst X970 = X969;\nconst X971 = X970;\nconst X972 = X971;\nconst X973 = X972;\nconst X974 = X973;\nconst X975 = X974;\nconst X976 = X975;\nconst X977 = X976;\nconst X978 = X977;\nconst X979 = X978;\nconst X980 = X979;\nconst X981 = X980;\nconst X982 = X981;\nconst X983 = X982;\nconst X984 = X983;\nconst X985 = X984;\nconst X986 = X985;\nconst X987 = X986;\nconst X988 = X987;\nconst X989 = X988;\nconst X990 = X989;\nconst X991 = X990;\nconst X992 = X991;\nconst X993 = X992;\nconst X994 = X993;\nconst X995 = X994;\nconst X996 = X995;\nconst X997 = X996;\nconst X998 = X997;\nconst X999 = X998;\n\n@Directive({selector: '[d]', inputs: X999})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: depth-chainSpread300", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nconst X0 = ['a'];\nconst X1 = [...X0];\nconst X2 = [...X1];\nconst X3 = [...X2];\nconst X4 = [...X3];\nconst X5 = [...X4];\nconst X6 = [...X5];\nconst X7 = [...X6];\nconst X8 = [...X7];\nconst X9 = [...X8];\nconst X10 = [...X9];\nconst X11 = [...X10];\nconst X12 = [...X11];\nconst X13 = [...X12];\nconst X14 = [...X13];\nconst X15 = [...X14];\nconst X16 = [...X15];\nconst X17 = [...X16];\nconst X18 = [...X17];\nconst X19 = [...X18];\nconst X20 = [...X19];\nconst X21 = [...X20];\nconst X22 = [...X21];\nconst X23 = [...X22];\nconst X24 = [...X23];\nconst X25 = [...X24];\nconst X26 = [...X25];\nconst X27 = [...X26];\nconst X28 = [...X27];\nconst X29 = [...X28];\nconst X30 = [...X29];\nconst X31 = [...X30];\nconst X32 = [...X31];\nconst X33 = [...X32];\nconst X34 = [...X33];\nconst X35 = [...X34];\nconst X36 = [...X35];\nconst X37 = [...X36];\nconst X38 = [...X37];\nconst X39 = [...X38];\nconst X40 = [...X39];\nconst X41 = [...X40];\nconst X42 = [...X41];\nconst X43 = [...X42];\nconst X44 = [...X43];\nconst X45 = [...X44];\nconst X46 = [...X45];\nconst X47 = [...X46];\nconst X48 = [...X47];\nconst X49 = [...X48];\nconst X50 = [...X49];\nconst X51 = [...X50];\nconst X52 = [...X51];\nconst X53 = [...X52];\nconst X54 = [...X53];\nconst X55 = [...X54];\nconst X56 = [...X55];\nconst X57 = [...X56];\nconst X58 = [...X57];\nconst X59 = [...X58];\nconst X60 = [...X59];\nconst X61 = [...X60];\nconst X62 = [...X61];\nconst X63 = [...X62];\nconst X64 = [...X63];\nconst X65 = [...X64];\nconst X66 = [...X65];\nconst X67 = [...X66];\nconst X68 = [...X67];\nconst X69 = [...X68];\nconst X70 = [...X69];\nconst X71 = [...X70];\nconst X72 = [...X71];\nconst X73 = [...X72];\nconst X74 = [...X73];\nconst X75 = [...X74];\nconst X76 = [...X75];\nconst X77 = [...X76];\nconst X78 = [...X77];\nconst X79 = [...X78];\nconst X80 = [...X79];\nconst X81 = [...X80];\nconst X82 = [...X81];\nconst X83 = [...X82];\nconst X84 = [...X83];\nconst X85 = [...X84];\nconst X86 = [...X85];\nconst X87 = [...X86];\nconst X88 = [...X87];\nconst X89 = [...X88];\nconst X90 = [...X89];\nconst X91 = [...X90];\nconst X92 = [...X91];\nconst X93 = [...X92];\nconst X94 = [...X93];\nconst X95 = [...X94];\nconst X96 = [...X95];\nconst X97 = [...X96];\nconst X98 = [...X97];\nconst X99 = [...X98];\nconst X100 = [...X99];\nconst X101 = [...X100];\nconst X102 = [...X101];\nconst X103 = [...X102];\nconst X104 = [...X103];\nconst X105 = [...X104];\nconst X106 = [...X105];\nconst X107 = [...X106];\nconst X108 = [...X107];\nconst X109 = [...X108];\nconst X110 = [...X109];\nconst X111 = [...X110];\nconst X112 = [...X111];\nconst X113 = [...X112];\nconst X114 = [...X113];\nconst X115 = [...X114];\nconst X116 = [...X115];\nconst X117 = [...X116];\nconst X118 = [...X117];\nconst X119 = [...X118];\nconst X120 = [...X119];\nconst X121 = [...X120];\nconst X122 = [...X121];\nconst X123 = [...X122];\nconst X124 = [...X123];\nconst X125 = [...X124];\nconst X126 = [...X125];\nconst X127 = [...X126];\nconst X128 = [...X127];\nconst X129 = [...X128];\nconst X130 = [...X129];\nconst X131 = [...X130];\nconst X132 = [...X131];\nconst X133 = [...X132];\nconst X134 = [...X133];\nconst X135 = [...X134];\nconst X136 = [...X135];\nconst X137 = [...X136];\nconst X138 = [...X137];\nconst X139 = [...X138];\nconst X140 = [...X139];\nconst X141 = [...X140];\nconst X142 = [...X141];\nconst X143 = [...X142];\nconst X144 = [...X143];\nconst X145 = [...X144];\nconst X146 = [...X145];\nconst X147 = [...X146];\nconst X148 = [...X147];\nconst X149 = [...X148];\nconst X150 = [...X149];\nconst X151 = [...X150];\nconst X152 = [...X151];\nconst X153 = [...X152];\nconst X154 = [...X153];\nconst X155 = [...X154];\nconst X156 = [...X155];\nconst X157 = [...X156];\nconst X158 = [...X157];\nconst X159 = [...X158];\nconst X160 = [...X159];\nconst X161 = [...X160];\nconst X162 = [...X161];\nconst X163 = [...X162];\nconst X164 = [...X163];\nconst X165 = [...X164];\nconst X166 = [...X165];\nconst X167 = [...X166];\nconst X168 = [...X167];\nconst X169 = [...X168];\nconst X170 = [...X169];\nconst X171 = [...X170];\nconst X172 = [...X171];\nconst X173 = [...X172];\nconst X174 = [...X173];\nconst X175 = [...X174];\nconst X176 = [...X175];\nconst X177 = [...X176];\nconst X178 = [...X177];\nconst X179 = [...X178];\nconst X180 = [...X179];\nconst X181 = [...X180];\nconst X182 = [...X181];\nconst X183 = [...X182];\nconst X184 = [...X183];\nconst X185 = [...X184];\nconst X186 = [...X185];\nconst X187 = [...X186];\nconst X188 = [...X187];\nconst X189 = [...X188];\nconst X190 = [...X189];\nconst X191 = [...X190];\nconst X192 = [...X191];\nconst X193 = [...X192];\nconst X194 = [...X193];\nconst X195 = [...X194];\nconst X196 = [...X195];\nconst X197 = [...X196];\nconst X198 = [...X197];\nconst X199 = [...X198];\nconst X200 = [...X199];\nconst X201 = [...X200];\nconst X202 = [...X201];\nconst X203 = [...X202];\nconst X204 = [...X203];\nconst X205 = [...X204];\nconst X206 = [...X205];\nconst X207 = [...X206];\nconst X208 = [...X207];\nconst X209 = [...X208];\nconst X210 = [...X209];\nconst X211 = [...X210];\nconst X212 = [...X211];\nconst X213 = [...X212];\nconst X214 = [...X213];\nconst X215 = [...X214];\nconst X216 = [...X215];\nconst X217 = [...X216];\nconst X218 = [...X217];\nconst X219 = [...X218];\nconst X220 = [...X219];\nconst X221 = [...X220];\nconst X222 = [...X221];\nconst X223 = [...X222];\nconst X224 = [...X223];\nconst X225 = [...X224];\nconst X226 = [...X225];\nconst X227 = [...X226];\nconst X228 = [...X227];\nconst X229 = [...X228];\nconst X230 = [...X229];\nconst X231 = [...X230];\nconst X232 = [...X231];\nconst X233 = [...X232];\nconst X234 = [...X233];\nconst X235 = [...X234];\nconst X236 = [...X235];\nconst X237 = [...X236];\nconst X238 = [...X237];\nconst X239 = [...X238];\nconst X240 = [...X239];\nconst X241 = [...X240];\nconst X242 = [...X241];\nconst X243 = [...X242];\nconst X244 = [...X243];\nconst X245 = [...X244];\nconst X246 = [...X245];\nconst X247 = [...X246];\nconst X248 = [...X247];\nconst X249 = [...X248];\nconst X250 = [...X249];\nconst X251 = [...X250];\nconst X252 = [...X251];\nconst X253 = [...X252];\nconst X254 = [...X253];\nconst X255 = [...X254];\nconst X256 = [...X255];\nconst X257 = [...X256];\nconst X258 = [...X257];\nconst X259 = [...X258];\nconst X260 = [...X259];\nconst X261 = [...X260];\nconst X262 = [...X261];\nconst X263 = [...X262];\nconst X264 = [...X263];\nconst X265 = [...X264];\nconst X266 = [...X265];\nconst X267 = [...X266];\nconst X268 = [...X267];\nconst X269 = [...X268];\nconst X270 = [...X269];\nconst X271 = [...X270];\nconst X272 = [...X271];\nconst X273 = [...X272];\nconst X274 = [...X273];\nconst X275 = [...X274];\nconst X276 = [...X275];\nconst X277 = [...X276];\nconst X278 = [...X277];\nconst X279 = [...X278];\nconst X280 = [...X279];\nconst X281 = [...X280];\nconst X282 = [...X281];\nconst X283 = [...X282];\nconst X284 = [...X283];\nconst X285 = [...X284];\nconst X286 = [...X285];\nconst X287 = [...X286];\nconst X288 = [...X287];\nconst X289 = [...X288];\nconst X290 = [...X289];\nconst X291 = [...X290];\nconst X292 = [...X291];\nconst X293 = [...X292];\nconst X294 = [...X293];\nconst X295 = [...X294];\nconst X296 = [...X295];\nconst X297 = [...X296];\nconst X298 = [...X297];\nconst X299 = [...X298];\n\n@Directive({selector: '[d]', inputs: X299})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: depth-chainSpread1000", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nconst X0 = ['a'];\nconst X1 = [...X0];\nconst X2 = [...X1];\nconst X3 = [...X2];\nconst X4 = [...X3];\nconst X5 = [...X4];\nconst X6 = [...X5];\nconst X7 = [...X6];\nconst X8 = [...X7];\nconst X9 = [...X8];\nconst X10 = [...X9];\nconst X11 = [...X10];\nconst X12 = [...X11];\nconst X13 = [...X12];\nconst X14 = [...X13];\nconst X15 = [...X14];\nconst X16 = [...X15];\nconst X17 = [...X16];\nconst X18 = [...X17];\nconst X19 = [...X18];\nconst X20 = [...X19];\nconst X21 = [...X20];\nconst X22 = [...X21];\nconst X23 = [...X22];\nconst X24 = [...X23];\nconst X25 = [...X24];\nconst X26 = [...X25];\nconst X27 = [...X26];\nconst X28 = [...X27];\nconst X29 = [...X28];\nconst X30 = [...X29];\nconst X31 = [...X30];\nconst X32 = [...X31];\nconst X33 = [...X32];\nconst X34 = [...X33];\nconst X35 = [...X34];\nconst X36 = [...X35];\nconst X37 = [...X36];\nconst X38 = [...X37];\nconst X39 = [...X38];\nconst X40 = [...X39];\nconst X41 = [...X40];\nconst X42 = [...X41];\nconst X43 = [...X42];\nconst X44 = [...X43];\nconst X45 = [...X44];\nconst X46 = [...X45];\nconst X47 = [...X46];\nconst X48 = [...X47];\nconst X49 = [...X48];\nconst X50 = [...X49];\nconst X51 = [...X50];\nconst X52 = [...X51];\nconst X53 = [...X52];\nconst X54 = [...X53];\nconst X55 = [...X54];\nconst X56 = [...X55];\nconst X57 = [...X56];\nconst X58 = [...X57];\nconst X59 = [...X58];\nconst X60 = [...X59];\nconst X61 = [...X60];\nconst X62 = [...X61];\nconst X63 = [...X62];\nconst X64 = [...X63];\nconst X65 = [...X64];\nconst X66 = [...X65];\nconst X67 = [...X66];\nconst X68 = [...X67];\nconst X69 = [...X68];\nconst X70 = [...X69];\nconst X71 = [...X70];\nconst X72 = [...X71];\nconst X73 = [...X72];\nconst X74 = [...X73];\nconst X75 = [...X74];\nconst X76 = [...X75];\nconst X77 = [...X76];\nconst X78 = [...X77];\nconst X79 = [...X78];\nconst X80 = [...X79];\nconst X81 = [...X80];\nconst X82 = [...X81];\nconst X83 = [...X82];\nconst X84 = [...X83];\nconst X85 = [...X84];\nconst X86 = [...X85];\nconst X87 = [...X86];\nconst X88 = [...X87];\nconst X89 = [...X88];\nconst X90 = [...X89];\nconst X91 = [...X90];\nconst X92 = [...X91];\nconst X93 = [...X92];\nconst X94 = [...X93];\nconst X95 = [...X94];\nconst X96 = [...X95];\nconst X97 = [...X96];\nconst X98 = [...X97];\nconst X99 = [...X98];\nconst X100 = [...X99];\nconst X101 = [...X100];\nconst X102 = [...X101];\nconst X103 = [...X102];\nconst X104 = [...X103];\nconst X105 = [...X104];\nconst X106 = [...X105];\nconst X107 = [...X106];\nconst X108 = [...X107];\nconst X109 = [...X108];\nconst X110 = [...X109];\nconst X111 = [...X110];\nconst X112 = [...X111];\nconst X113 = [...X112];\nconst X114 = [...X113];\nconst X115 = [...X114];\nconst X116 = [...X115];\nconst X117 = [...X116];\nconst X118 = [...X117];\nconst X119 = [...X118];\nconst X120 = [...X119];\nconst X121 = [...X120];\nconst X122 = [...X121];\nconst X123 = [...X122];\nconst X124 = [...X123];\nconst X125 = [...X124];\nconst X126 = [...X125];\nconst X127 = [...X126];\nconst X128 = [...X127];\nconst X129 = [...X128];\nconst X130 = [...X129];\nconst X131 = [...X130];\nconst X132 = [...X131];\nconst X133 = [...X132];\nconst X134 = [...X133];\nconst X135 = [...X134];\nconst X136 = [...X135];\nconst X137 = [...X136];\nconst X138 = [...X137];\nconst X139 = [...X138];\nconst X140 = [...X139];\nconst X141 = [...X140];\nconst X142 = [...X141];\nconst X143 = [...X142];\nconst X144 = [...X143];\nconst X145 = [...X144];\nconst X146 = [...X145];\nconst X147 = [...X146];\nconst X148 = [...X147];\nconst X149 = [...X148];\nconst X150 = [...X149];\nconst X151 = [...X150];\nconst X152 = [...X151];\nconst X153 = [...X152];\nconst X154 = [...X153];\nconst X155 = [...X154];\nconst X156 = [...X155];\nconst X157 = [...X156];\nconst X158 = [...X157];\nconst X159 = [...X158];\nconst X160 = [...X159];\nconst X161 = [...X160];\nconst X162 = [...X161];\nconst X163 = [...X162];\nconst X164 = [...X163];\nconst X165 = [...X164];\nconst X166 = [...X165];\nconst X167 = [...X166];\nconst X168 = [...X167];\nconst X169 = [...X168];\nconst X170 = [...X169];\nconst X171 = [...X170];\nconst X172 = [...X171];\nconst X173 = [...X172];\nconst X174 = [...X173];\nconst X175 = [...X174];\nconst X176 = [...X175];\nconst X177 = [...X176];\nconst X178 = [...X177];\nconst X179 = [...X178];\nconst X180 = [...X179];\nconst X181 = [...X180];\nconst X182 = [...X181];\nconst X183 = [...X182];\nconst X184 = [...X183];\nconst X185 = [...X184];\nconst X186 = [...X185];\nconst X187 = [...X186];\nconst X188 = [...X187];\nconst X189 = [...X188];\nconst X190 = [...X189];\nconst X191 = [...X190];\nconst X192 = [...X191];\nconst X193 = [...X192];\nconst X194 = [...X193];\nconst X195 = [...X194];\nconst X196 = [...X195];\nconst X197 = [...X196];\nconst X198 = [...X197];\nconst X199 = [...X198];\nconst X200 = [...X199];\nconst X201 = [...X200];\nconst X202 = [...X201];\nconst X203 = [...X202];\nconst X204 = [...X203];\nconst X205 = [...X204];\nconst X206 = [...X205];\nconst X207 = [...X206];\nconst X208 = [...X207];\nconst X209 = [...X208];\nconst X210 = [...X209];\nconst X211 = [...X210];\nconst X212 = [...X211];\nconst X213 = [...X212];\nconst X214 = [...X213];\nconst X215 = [...X214];\nconst X216 = [...X215];\nconst X217 = [...X216];\nconst X218 = [...X217];\nconst X219 = [...X218];\nconst X220 = [...X219];\nconst X221 = [...X220];\nconst X222 = [...X221];\nconst X223 = [...X222];\nconst X224 = [...X223];\nconst X225 = [...X224];\nconst X226 = [...X225];\nconst X227 = [...X226];\nconst X228 = [...X227];\nconst X229 = [...X228];\nconst X230 = [...X229];\nconst X231 = [...X230];\nconst X232 = [...X231];\nconst X233 = [...X232];\nconst X234 = [...X233];\nconst X235 = [...X234];\nconst X236 = [...X235];\nconst X237 = [...X236];\nconst X238 = [...X237];\nconst X239 = [...X238];\nconst X240 = [...X239];\nconst X241 = [...X240];\nconst X242 = [...X241];\nconst X243 = [...X242];\nconst X244 = [...X243];\nconst X245 = [...X244];\nconst X246 = [...X245];\nconst X247 = [...X246];\nconst X248 = [...X247];\nconst X249 = [...X248];\nconst X250 = [...X249];\nconst X251 = [...X250];\nconst X252 = [...X251];\nconst X253 = [...X252];\nconst X254 = [...X253];\nconst X255 = [...X254];\nconst X256 = [...X255];\nconst X257 = [...X256];\nconst X258 = [...X257];\nconst X259 = [...X258];\nconst X260 = [...X259];\nconst X261 = [...X260];\nconst X262 = [...X261];\nconst X263 = [...X262];\nconst X264 = [...X263];\nconst X265 = [...X264];\nconst X266 = [...X265];\nconst X267 = [...X266];\nconst X268 = [...X267];\nconst X269 = [...X268];\nconst X270 = [...X269];\nconst X271 = [...X270];\nconst X272 = [...X271];\nconst X273 = [...X272];\nconst X274 = [...X273];\nconst X275 = [...X274];\nconst X276 = [...X275];\nconst X277 = [...X276];\nconst X278 = [...X277];\nconst X279 = [...X278];\nconst X280 = [...X279];\nconst X281 = [...X280];\nconst X282 = [...X281];\nconst X283 = [...X282];\nconst X284 = [...X283];\nconst X285 = [...X284];\nconst X286 = [...X285];\nconst X287 = [...X286];\nconst X288 = [...X287];\nconst X289 = [...X288];\nconst X290 = [...X289];\nconst X291 = [...X290];\nconst X292 = [...X291];\nconst X293 = [...X292];\nconst X294 = [...X293];\nconst X295 = [...X294];\nconst X296 = [...X295];\nconst X297 = [...X296];\nconst X298 = [...X297];\nconst X299 = [...X298];\nconst X300 = [...X299];\nconst X301 = [...X300];\nconst X302 = [...X301];\nconst X303 = [...X302];\nconst X304 = [...X303];\nconst X305 = [...X304];\nconst X306 = [...X305];\nconst X307 = [...X306];\nconst X308 = [...X307];\nconst X309 = [...X308];\nconst X310 = [...X309];\nconst X311 = [...X310];\nconst X312 = [...X311];\nconst X313 = [...X312];\nconst X314 = [...X313];\nconst X315 = [...X314];\nconst X316 = [...X315];\nconst X317 = [...X316];\nconst X318 = [...X317];\nconst X319 = [...X318];\nconst X320 = [...X319];\nconst X321 = [...X320];\nconst X322 = [...X321];\nconst X323 = [...X322];\nconst X324 = [...X323];\nconst X325 = [...X324];\nconst X326 = [...X325];\nconst X327 = [...X326];\nconst X328 = [...X327];\nconst X329 = [...X328];\nconst X330 = [...X329];\nconst X331 = [...X330];\nconst X332 = [...X331];\nconst X333 = [...X332];\nconst X334 = [...X333];\nconst X335 = [...X334];\nconst X336 = [...X335];\nconst X337 = [...X336];\nconst X338 = [...X337];\nconst X339 = [...X338];\nconst X340 = [...X339];\nconst X341 = [...X340];\nconst X342 = [...X341];\nconst X343 = [...X342];\nconst X344 = [...X343];\nconst X345 = [...X344];\nconst X346 = [...X345];\nconst X347 = [...X346];\nconst X348 = [...X347];\nconst X349 = [...X348];\nconst X350 = [...X349];\nconst X351 = [...X350];\nconst X352 = [...X351];\nconst X353 = [...X352];\nconst X354 = [...X353];\nconst X355 = [...X354];\nconst X356 = [...X355];\nconst X357 = [...X356];\nconst X358 = [...X357];\nconst X359 = [...X358];\nconst X360 = [...X359];\nconst X361 = [...X360];\nconst X362 = [...X361];\nconst X363 = [...X362];\nconst X364 = [...X363];\nconst X365 = [...X364];\nconst X366 = [...X365];\nconst X367 = [...X366];\nconst X368 = [...X367];\nconst X369 = [...X368];\nconst X370 = [...X369];\nconst X371 = [...X370];\nconst X372 = [...X371];\nconst X373 = [...X372];\nconst X374 = [...X373];\nconst X375 = [...X374];\nconst X376 = [...X375];\nconst X377 = [...X376];\nconst X378 = [...X377];\nconst X379 = [...X378];\nconst X380 = [...X379];\nconst X381 = [...X380];\nconst X382 = [...X381];\nconst X383 = [...X382];\nconst X384 = [...X383];\nconst X385 = [...X384];\nconst X386 = [...X385];\nconst X387 = [...X386];\nconst X388 = [...X387];\nconst X389 = [...X388];\nconst X390 = [...X389];\nconst X391 = [...X390];\nconst X392 = [...X391];\nconst X393 = [...X392];\nconst X394 = [...X393];\nconst X395 = [...X394];\nconst X396 = [...X395];\nconst X397 = [...X396];\nconst X398 = [...X397];\nconst X399 = [...X398];\nconst X400 = [...X399];\nconst X401 = [...X400];\nconst X402 = [...X401];\nconst X403 = [...X402];\nconst X404 = [...X403];\nconst X405 = [...X404];\nconst X406 = [...X405];\nconst X407 = [...X406];\nconst X408 = [...X407];\nconst X409 = [...X408];\nconst X410 = [...X409];\nconst X411 = [...X410];\nconst X412 = [...X411];\nconst X413 = [...X412];\nconst X414 = [...X413];\nconst X415 = [...X414];\nconst X416 = [...X415];\nconst X417 = [...X416];\nconst X418 = [...X417];\nconst X419 = [...X418];\nconst X420 = [...X419];\nconst X421 = [...X420];\nconst X422 = [...X421];\nconst X423 = [...X422];\nconst X424 = [...X423];\nconst X425 = [...X424];\nconst X426 = [...X425];\nconst X427 = [...X426];\nconst X428 = [...X427];\nconst X429 = [...X428];\nconst X430 = [...X429];\nconst X431 = [...X430];\nconst X432 = [...X431];\nconst X433 = [...X432];\nconst X434 = [...X433];\nconst X435 = [...X434];\nconst X436 = [...X435];\nconst X437 = [...X436];\nconst X438 = [...X437];\nconst X439 = [...X438];\nconst X440 = [...X439];\nconst X441 = [...X440];\nconst X442 = [...X441];\nconst X443 = [...X442];\nconst X444 = [...X443];\nconst X445 = [...X444];\nconst X446 = [...X445];\nconst X447 = [...X446];\nconst X448 = [...X447];\nconst X449 = [...X448];\nconst X450 = [...X449];\nconst X451 = [...X450];\nconst X452 = [...X451];\nconst X453 = [...X452];\nconst X454 = [...X453];\nconst X455 = [...X454];\nconst X456 = [...X455];\nconst X457 = [...X456];\nconst X458 = [...X457];\nconst X459 = [...X458];\nconst X460 = [...X459];\nconst X461 = [...X460];\nconst X462 = [...X461];\nconst X463 = [...X462];\nconst X464 = [...X463];\nconst X465 = [...X464];\nconst X466 = [...X465];\nconst X467 = [...X466];\nconst X468 = [...X467];\nconst X469 = [...X468];\nconst X470 = [...X469];\nconst X471 = [...X470];\nconst X472 = [...X471];\nconst X473 = [...X472];\nconst X474 = [...X473];\nconst X475 = [...X474];\nconst X476 = [...X475];\nconst X477 = [...X476];\nconst X478 = [...X477];\nconst X479 = [...X478];\nconst X480 = [...X479];\nconst X481 = [...X480];\nconst X482 = [...X481];\nconst X483 = [...X482];\nconst X484 = [...X483];\nconst X485 = [...X484];\nconst X486 = [...X485];\nconst X487 = [...X486];\nconst X488 = [...X487];\nconst X489 = [...X488];\nconst X490 = [...X489];\nconst X491 = [...X490];\nconst X492 = [...X491];\nconst X493 = [...X492];\nconst X494 = [...X493];\nconst X495 = [...X494];\nconst X496 = [...X495];\nconst X497 = [...X496];\nconst X498 = [...X497];\nconst X499 = [...X498];\nconst X500 = [...X499];\nconst X501 = [...X500];\nconst X502 = [...X501];\nconst X503 = [...X502];\nconst X504 = [...X503];\nconst X505 = [...X504];\nconst X506 = [...X505];\nconst X507 = [...X506];\nconst X508 = [...X507];\nconst X509 = [...X508];\nconst X510 = [...X509];\nconst X511 = [...X510];\nconst X512 = [...X511];\nconst X513 = [...X512];\nconst X514 = [...X513];\nconst X515 = [...X514];\nconst X516 = [...X515];\nconst X517 = [...X516];\nconst X518 = [...X517];\nconst X519 = [...X518];\nconst X520 = [...X519];\nconst X521 = [...X520];\nconst X522 = [...X521];\nconst X523 = [...X522];\nconst X524 = [...X523];\nconst X525 = [...X524];\nconst X526 = [...X525];\nconst X527 = [...X526];\nconst X528 = [...X527];\nconst X529 = [...X528];\nconst X530 = [...X529];\nconst X531 = [...X530];\nconst X532 = [...X531];\nconst X533 = [...X532];\nconst X534 = [...X533];\nconst X535 = [...X534];\nconst X536 = [...X535];\nconst X537 = [...X536];\nconst X538 = [...X537];\nconst X539 = [...X538];\nconst X540 = [...X539];\nconst X541 = [...X540];\nconst X542 = [...X541];\nconst X543 = [...X542];\nconst X544 = [...X543];\nconst X545 = [...X544];\nconst X546 = [...X545];\nconst X547 = [...X546];\nconst X548 = [...X547];\nconst X549 = [...X548];\nconst X550 = [...X549];\nconst X551 = [...X550];\nconst X552 = [...X551];\nconst X553 = [...X552];\nconst X554 = [...X553];\nconst X555 = [...X554];\nconst X556 = [...X555];\nconst X557 = [...X556];\nconst X558 = [...X557];\nconst X559 = [...X558];\nconst X560 = [...X559];\nconst X561 = [...X560];\nconst X562 = [...X561];\nconst X563 = [...X562];\nconst X564 = [...X563];\nconst X565 = [...X564];\nconst X566 = [...X565];\nconst X567 = [...X566];\nconst X568 = [...X567];\nconst X569 = [...X568];\nconst X570 = [...X569];\nconst X571 = [...X570];\nconst X572 = [...X571];\nconst X573 = [...X572];\nconst X574 = [...X573];\nconst X575 = [...X574];\nconst X576 = [...X575];\nconst X577 = [...X576];\nconst X578 = [...X577];\nconst X579 = [...X578];\nconst X580 = [...X579];\nconst X581 = [...X580];\nconst X582 = [...X581];\nconst X583 = [...X582];\nconst X584 = [...X583];\nconst X585 = [...X584];\nconst X586 = [...X585];\nconst X587 = [...X586];\nconst X588 = [...X587];\nconst X589 = [...X588];\nconst X590 = [...X589];\nconst X591 = [...X590];\nconst X592 = [...X591];\nconst X593 = [...X592];\nconst X594 = [...X593];\nconst X595 = [...X594];\nconst X596 = [...X595];\nconst X597 = [...X596];\nconst X598 = [...X597];\nconst X599 = [...X598];\nconst X600 = [...X599];\nconst X601 = [...X600];\nconst X602 = [...X601];\nconst X603 = [...X602];\nconst X604 = [...X603];\nconst X605 = [...X604];\nconst X606 = [...X605];\nconst X607 = [...X606];\nconst X608 = [...X607];\nconst X609 = [...X608];\nconst X610 = [...X609];\nconst X611 = [...X610];\nconst X612 = [...X611];\nconst X613 = [...X612];\nconst X614 = [...X613];\nconst X615 = [...X614];\nconst X616 = [...X615];\nconst X617 = [...X616];\nconst X618 = [...X617];\nconst X619 = [...X618];\nconst X620 = [...X619];\nconst X621 = [...X620];\nconst X622 = [...X621];\nconst X623 = [...X622];\nconst X624 = [...X623];\nconst X625 = [...X624];\nconst X626 = [...X625];\nconst X627 = [...X626];\nconst X628 = [...X627];\nconst X629 = [...X628];\nconst X630 = [...X629];\nconst X631 = [...X630];\nconst X632 = [...X631];\nconst X633 = [...X632];\nconst X634 = [...X633];\nconst X635 = [...X634];\nconst X636 = [...X635];\nconst X637 = [...X636];\nconst X638 = [...X637];\nconst X639 = [...X638];\nconst X640 = [...X639];\nconst X641 = [...X640];\nconst X642 = [...X641];\nconst X643 = [...X642];\nconst X644 = [...X643];\nconst X645 = [...X644];\nconst X646 = [...X645];\nconst X647 = [...X646];\nconst X648 = [...X647];\nconst X649 = [...X648];\nconst X650 = [...X649];\nconst X651 = [...X650];\nconst X652 = [...X651];\nconst X653 = [...X652];\nconst X654 = [...X653];\nconst X655 = [...X654];\nconst X656 = [...X655];\nconst X657 = [...X656];\nconst X658 = [...X657];\nconst X659 = [...X658];\nconst X660 = [...X659];\nconst X661 = [...X660];\nconst X662 = [...X661];\nconst X663 = [...X662];\nconst X664 = [...X663];\nconst X665 = [...X664];\nconst X666 = [...X665];\nconst X667 = [...X666];\nconst X668 = [...X667];\nconst X669 = [...X668];\nconst X670 = [...X669];\nconst X671 = [...X670];\nconst X672 = [...X671];\nconst X673 = [...X672];\nconst X674 = [...X673];\nconst X675 = [...X674];\nconst X676 = [...X675];\nconst X677 = [...X676];\nconst X678 = [...X677];\nconst X679 = [...X678];\nconst X680 = [...X679];\nconst X681 = [...X680];\nconst X682 = [...X681];\nconst X683 = [...X682];\nconst X684 = [...X683];\nconst X685 = [...X684];\nconst X686 = [...X685];\nconst X687 = [...X686];\nconst X688 = [...X687];\nconst X689 = [...X688];\nconst X690 = [...X689];\nconst X691 = [...X690];\nconst X692 = [...X691];\nconst X693 = [...X692];\nconst X694 = [...X693];\nconst X695 = [...X694];\nconst X696 = [...X695];\nconst X697 = [...X696];\nconst X698 = [...X697];\nconst X699 = [...X698];\nconst X700 = [...X699];\nconst X701 = [...X700];\nconst X702 = [...X701];\nconst X703 = [...X702];\nconst X704 = [...X703];\nconst X705 = [...X704];\nconst X706 = [...X705];\nconst X707 = [...X706];\nconst X708 = [...X707];\nconst X709 = [...X708];\nconst X710 = [...X709];\nconst X711 = [...X710];\nconst X712 = [...X711];\nconst X713 = [...X712];\nconst X714 = [...X713];\nconst X715 = [...X714];\nconst X716 = [...X715];\nconst X717 = [...X716];\nconst X718 = [...X717];\nconst X719 = [...X718];\nconst X720 = [...X719];\nconst X721 = [...X720];\nconst X722 = [...X721];\nconst X723 = [...X722];\nconst X724 = [...X723];\nconst X725 = [...X724];\nconst X726 = [...X725];\nconst X727 = [...X726];\nconst X728 = [...X727];\nconst X729 = [...X728];\nconst X730 = [...X729];\nconst X731 = [...X730];\nconst X732 = [...X731];\nconst X733 = [...X732];\nconst X734 = [...X733];\nconst X735 = [...X734];\nconst X736 = [...X735];\nconst X737 = [...X736];\nconst X738 = [...X737];\nconst X739 = [...X738];\nconst X740 = [...X739];\nconst X741 = [...X740];\nconst X742 = [...X741];\nconst X743 = [...X742];\nconst X744 = [...X743];\nconst X745 = [...X744];\nconst X746 = [...X745];\nconst X747 = [...X746];\nconst X748 = [...X747];\nconst X749 = [...X748];\nconst X750 = [...X749];\nconst X751 = [...X750];\nconst X752 = [...X751];\nconst X753 = [...X752];\nconst X754 = [...X753];\nconst X755 = [...X754];\nconst X756 = [...X755];\nconst X757 = [...X756];\nconst X758 = [...X757];\nconst X759 = [...X758];\nconst X760 = [...X759];\nconst X761 = [...X760];\nconst X762 = [...X761];\nconst X763 = [...X762];\nconst X764 = [...X763];\nconst X765 = [...X764];\nconst X766 = [...X765];\nconst X767 = [...X766];\nconst X768 = [...X767];\nconst X769 = [...X768];\nconst X770 = [...X769];\nconst X771 = [...X770];\nconst X772 = [...X771];\nconst X773 = [...X772];\nconst X774 = [...X773];\nconst X775 = [...X774];\nconst X776 = [...X775];\nconst X777 = [...X776];\nconst X778 = [...X777];\nconst X779 = [...X778];\nconst X780 = [...X779];\nconst X781 = [...X780];\nconst X782 = [...X781];\nconst X783 = [...X782];\nconst X784 = [...X783];\nconst X785 = [...X784];\nconst X786 = [...X785];\nconst X787 = [...X786];\nconst X788 = [...X787];\nconst X789 = [...X788];\nconst X790 = [...X789];\nconst X791 = [...X790];\nconst X792 = [...X791];\nconst X793 = [...X792];\nconst X794 = [...X793];\nconst X795 = [...X794];\nconst X796 = [...X795];\nconst X797 = [...X796];\nconst X798 = [...X797];\nconst X799 = [...X798];\nconst X800 = [...X799];\nconst X801 = [...X800];\nconst X802 = [...X801];\nconst X803 = [...X802];\nconst X804 = [...X803];\nconst X805 = [...X804];\nconst X806 = [...X805];\nconst X807 = [...X806];\nconst X808 = [...X807];\nconst X809 = [...X808];\nconst X810 = [...X809];\nconst X811 = [...X810];\nconst X812 = [...X811];\nconst X813 = [...X812];\nconst X814 = [...X813];\nconst X815 = [...X814];\nconst X816 = [...X815];\nconst X817 = [...X816];\nconst X818 = [...X817];\nconst X819 = [...X818];\nconst X820 = [...X819];\nconst X821 = [...X820];\nconst X822 = [...X821];\nconst X823 = [...X822];\nconst X824 = [...X823];\nconst X825 = [...X824];\nconst X826 = [...X825];\nconst X827 = [...X826];\nconst X828 = [...X827];\nconst X829 = [...X828];\nconst X830 = [...X829];\nconst X831 = [...X830];\nconst X832 = [...X831];\nconst X833 = [...X832];\nconst X834 = [...X833];\nconst X835 = [...X834];\nconst X836 = [...X835];\nconst X837 = [...X836];\nconst X838 = [...X837];\nconst X839 = [...X838];\nconst X840 = [...X839];\nconst X841 = [...X840];\nconst X842 = [...X841];\nconst X843 = [...X842];\nconst X844 = [...X843];\nconst X845 = [...X844];\nconst X846 = [...X845];\nconst X847 = [...X846];\nconst X848 = [...X847];\nconst X849 = [...X848];\nconst X850 = [...X849];\nconst X851 = [...X850];\nconst X852 = [...X851];\nconst X853 = [...X852];\nconst X854 = [...X853];\nconst X855 = [...X854];\nconst X856 = [...X855];\nconst X857 = [...X856];\nconst X858 = [...X857];\nconst X859 = [...X858];\nconst X860 = [...X859];\nconst X861 = [...X860];\nconst X862 = [...X861];\nconst X863 = [...X862];\nconst X864 = [...X863];\nconst X865 = [...X864];\nconst X866 = [...X865];\nconst X867 = [...X866];\nconst X868 = [...X867];\nconst X869 = [...X868];\nconst X870 = [...X869];\nconst X871 = [...X870];\nconst X872 = [...X871];\nconst X873 = [...X872];\nconst X874 = [...X873];\nconst X875 = [...X874];\nconst X876 = [...X875];\nconst X877 = [...X876];\nconst X878 = [...X877];\nconst X879 = [...X878];\nconst X880 = [...X879];\nconst X881 = [...X880];\nconst X882 = [...X881];\nconst X883 = [...X882];\nconst X884 = [...X883];\nconst X885 = [...X884];\nconst X886 = [...X885];\nconst X887 = [...X886];\nconst X888 = [...X887];\nconst X889 = [...X888];\nconst X890 = [...X889];\nconst X891 = [...X890];\nconst X892 = [...X891];\nconst X893 = [...X892];\nconst X894 = [...X893];\nconst X895 = [...X894];\nconst X896 = [...X895];\nconst X897 = [...X896];\nconst X898 = [...X897];\nconst X899 = [...X898];\nconst X900 = [...X899];\nconst X901 = [...X900];\nconst X902 = [...X901];\nconst X903 = [...X902];\nconst X904 = [...X903];\nconst X905 = [...X904];\nconst X906 = [...X905];\nconst X907 = [...X906];\nconst X908 = [...X907];\nconst X909 = [...X908];\nconst X910 = [...X909];\nconst X911 = [...X910];\nconst X912 = [...X911];\nconst X913 = [...X912];\nconst X914 = [...X913];\nconst X915 = [...X914];\nconst X916 = [...X915];\nconst X917 = [...X916];\nconst X918 = [...X917];\nconst X919 = [...X918];\nconst X920 = [...X919];\nconst X921 = [...X920];\nconst X922 = [...X921];\nconst X923 = [...X922];\nconst X924 = [...X923];\nconst X925 = [...X924];\nconst X926 = [...X925];\nconst X927 = [...X926];\nconst X928 = [...X927];\nconst X929 = [...X928];\nconst X930 = [...X929];\nconst X931 = [...X930];\nconst X932 = [...X931];\nconst X933 = [...X932];\nconst X934 = [...X933];\nconst X935 = [...X934];\nconst X936 = [...X935];\nconst X937 = [...X936];\nconst X938 = [...X937];\nconst X939 = [...X938];\nconst X940 = [...X939];\nconst X941 = [...X940];\nconst X942 = [...X941];\nconst X943 = [...X942];\nconst X944 = [...X943];\nconst X945 = [...X944];\nconst X946 = [...X945];\nconst X947 = [...X946];\nconst X948 = [...X947];\nconst X949 = [...X948];\nconst X950 = [...X949];\nconst X951 = [...X950];\nconst X952 = [...X951];\nconst X953 = [...X952];\nconst X954 = [...X953];\nconst X955 = [...X954];\nconst X956 = [...X955];\nconst X957 = [...X956];\nconst X958 = [...X957];\nconst X959 = [...X958];\nconst X960 = [...X959];\nconst X961 = [...X960];\nconst X962 = [...X961];\nconst X963 = [...X962];\nconst X964 = [...X963];\nconst X965 = [...X964];\nconst X966 = [...X965];\nconst X967 = [...X966];\nconst X968 = [...X967];\nconst X969 = [...X968];\nconst X970 = [...X969];\nconst X971 = [...X970];\nconst X972 = [...X971];\nconst X973 = [...X972];\nconst X974 = [...X973];\nconst X975 = [...X974];\nconst X976 = [...X975];\nconst X977 = [...X976];\nconst X978 = [...X977];\nconst X979 = [...X978];\nconst X980 = [...X979];\nconst X981 = [...X980];\nconst X982 = [...X981];\nconst X983 = [...X982];\nconst X984 = [...X983];\nconst X985 = [...X984];\nconst X986 = [...X985];\nconst X987 = [...X986];\nconst X988 = [...X987];\nconst X989 = [...X988];\nconst X990 = [...X989];\nconst X991 = [...X990];\nconst X992 = [...X991];\nconst X993 = [...X992];\nconst X994 = [...X993];\nconst X995 = [...X994];\nconst X996 = [...X995];\nconst X997 = [...X996];\nconst X998 = [...X997];\nconst X999 = [...X998];\n\n@Directive({selector: '[d]', inputs: X999})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-enumIndex", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nenum E { A = 5, B, C = 'c', D }\n@Directive({selector: '[d]', inputs: [`x${E.A}${E.B}${E.C}${E.D}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x51c3:\"x51c3\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-enumStringKey", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nenum E { 'x-y' = 'q' }\n@Directive({selector: '[d]', inputs: [`${E['x-y']}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{q:\"q\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-enumSelfRef", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nenum E { A = 'x', B = A }\n@Directive({selector: '[d]', inputs: [`${E.B}`]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[133, 143]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-enumTwice", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nenum E { A = 'x' }\nenum E { B = 1 }\n@Directive({selector: '[d]', inputs: [`${E.A}`, `${E.B}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\",undefined:\"undefined\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-enumOutsideConst", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nconst A = 'outer';\nenum E { A = 'x', B = A }\n@Directive({selector: '[d]', inputs: [`${E.B}`]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[152, 162]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-thisParam", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nfunction mk(this: any, x: string) { return [x]; }\n@Directive({selector: '[d]', inputs: mk('a', 'b')})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-typeofQualified", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nenum E { A = 'x' }\ndeclare const X: typeof E.A;\n@Directive({selector: '[d]', inputs: [X]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is a reference to 'A'." + ], + "diagnosticSpans": [[155, 158]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-tupleNonLiteral", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\ndeclare const X: ['a', string];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[139, 140]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-tupleNeg", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\ndeclare const X: [-1];\n@Directive({selector: '[d]', inputs: [`${X[0]}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{\"-1\":\"-1\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-tupleTemplateType", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\ndeclare const X: [`a`];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-declareTypeRefClass", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nclass U {}\ndeclare const X: U;\n@Directive({selector: '[d]', inputs: [X]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is a reference to 'U'." + ], + "diagnosticSpans": [[138, 141]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-declareTypeRefAlias", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\ntype T = ['a'];\ndeclare const X: T;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'X'." + ], + "diagnosticSpans": [[143, 144]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-declareTupleOptional", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\ndeclare const X: ['a'?];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[132, 133]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-declareTupleRest", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\ndeclare const X: ['a', ...string[]];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[144, 145]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-declareUnion", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\ndeclare const X: ['a'] | ['b'];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'X'." + ], + "diagnosticSpans": [[139, 140]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-declareNull", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\ndeclare const X: [null, true, 1];\n@Directive({selector: '[d]', inputs: [`${X[0]}${X[1]}${X[2]}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{nulltrue1:\"nulltrue1\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-declareExport", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nexport declare const X: ['a'];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-declareVar", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\ndeclare var X: ['a'];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-describeObjOrder", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: {b: 1, 1: 2, a: {c: 1}, 'x-y': [1]}})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type '{ b: number; 1: number; a: object; 'x-y': Array }'." + ], + "diagnosticSpans": [[107, 142]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-describeNested", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [[{a: 1}, [1]]]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type '[object, Array]'." + ], + "diagnosticSpans": [[107, 122]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-describeFunction", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nconst A = ['a'];\n@Directive({selector: '[d]', inputs: [A.concat]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'Function'." + ], + "diagnosticSpans": [[124, 134]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-describeBuiltinStr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a'.concat]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'Function'." + ], + "diagnosticSpans": [[107, 119]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-staticMethodRef", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nclass U { static m() { return 1; } }\n@Directive({selector: '[d]', inputs: [U.m]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is a reference to 'm'." + ], + "diagnosticSpans": [[144, 149]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-staticFieldUndefined", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nclass U { static m: string; }\n@Directive({selector: '[d]', inputs: [U.m]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is a reference to 'm'." + ], + "diagnosticSpans": [[137, 142]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-staticPrototype", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nclass U {}\n@Directive({selector: '[d]', inputs: [U.prototype]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'undefined'." + ], + "diagnosticSpans": [[118, 131]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-staticInstanceMember", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nclass U { m = 'a'; }\n@Directive({selector: '[d]', inputs: [U.m]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'undefined'." + ], + "diagnosticSpans": [[128, 133]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-fnRef", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nfunction mk() { return 1; }\n@Directive({selector: '[d]', inputs: [mk]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is a reference to 'mk'." + ], + "diagnosticSpans": [[135, 139]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-fnRefMember", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nfunction mk() { return 1; }\n@Directive({selector: '[d]', inputs: [mk.name]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[135, 144]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-mathPI", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [`${Math.PI}`]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[107, 121]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-condEnumZero", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nenum E { A }\n@Directive({selector: '[d]', inputs: E.A ? ['t'] : ['f']})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{t:\"t\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-unaryArray", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [`${-['5']}${+[]}${!['a']}${~['3']}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{\"-50false-4\":\"-50false-4\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-binaryArrayOperand", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [['a'] + '']})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[107, 119]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-andRef", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nclass U {}\n@Directive({selector: '[d]', inputs: [U && 'a', null && U]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'null'." + ], + "diagnosticSpans": [[118, 139]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-orDynamicRhs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\ndeclare function f(): any;\n@Directive({selector: '[d]', inputs: ['a' || f()]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[134, 146]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-plusNumStr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [`x${1 + 2 + '3'}${'1' - 1}${'0x10' * 1}${' 12 ' * 1}${'' * 1}${'1e3' * 1}${'abc' * 1}${null + 1}${undefined + 1}${true + true}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x330161201000NaN1NaN2:\"x330161201000NaN1NaN2\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-looseEq", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [`${null == undefined}${'1' == 1}${true == 1}${null == 0}${'' == 0}${'a' < 'b'}${'10' < '9'}${'10' < 9}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{truetruetruefalsetruetruetruefalse:\"truetruetruefalsetruetruetruefalse\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-divZero", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [`${1 / 0}${-1 / 0}${0 / 0}${5 % 0}${-0}${2 ** -1}${7 % -3}${-7 % 3}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{\"Infinity-InfinityNaNNaN00.51-1\":\"Infinity-InfinityNaNNaN00.51-1\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-bitwise", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [`${2 ** 31 | 0}${1.9 | 0}${-1 >>> 0}${1 << 33}${'3' & 1}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{\"-21474836481429496729521\":\"-21474836481429496729521\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-hexLiteral", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [`${0x1F}${1_000}${0b11}${0o7}${.5}${5.}${1e-7}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{\"311000370.551e-7\":\"311000370.551e-7\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-bigint", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [`${1n}`]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[107, 116]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-regex", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [/a/]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[107, 112]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-new", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [new String('a')]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[107, 124]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-optionalChain", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nconst O = {a: 'x'};\n@Directive({selector: '[d]', inputs: [O?.a]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-nonNull", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nconst O = {a: 'x'};\n@Directive({selector: '[d]', inputs: [O.a!]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-asConst", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['a', 'b'] as const})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-parenSeq", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [('x', 'a')]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[107, 119]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-assign", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nlet Y;\n@Directive({selector: '[d]', inputs: [Y = 'a']})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[114, 123]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-classExpr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [class Foo {}]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[107, 121]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-objProtoKey", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [{__proto__: 'x', name: 'a'}]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-objDupKey", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [{name: 'a', name: 'b'}]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-objDescribeDup", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: {a: 1, b: 2, a: 'x'}})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type '{ a: string; b: number }'." + ], + "diagnosticSpans": [[107, 127]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-objIndexUndefinedKey", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nconst O = {a: 1};\n@Directive({selector: '[d]', inputs: [`${O.b}`]})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{undefined:\"undefined\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: misc-arrayHoleDescribe", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [, 'a']})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[107, 114]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-arrayInArrayDescribe", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: {a: [1, [2]]}})\nexport class Dir {}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type '{ a: Array }'." + ], + "diagnosticSpans": [[107, 120]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: misc-emptyArray", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: []})\nexport class Dir {}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: span-signalCollision", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['v']})\nexport class Dir {\n v = input();\n}\n" + }, + "diagnostics": ["Input \"v\" is also declared as non-signal in @Directive."], + "diagnosticSpans": [[136, 156]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: span-outputCollision", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', outputs: ['o']})\nexport class Dir {\n o = output();\n}\n" + }, + "diagnostics": ["Output \"o\" is unexpectedly declared in @Directive as well."], + "diagnosticSpans": [[137, 158]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: span-decoratedCollision", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: ['v']})\nexport class Dir {\n /** doc */ @Input() w: any;\n // c\n readonly v = model();\n}\n" + }, + "diagnostics": ["Input \"v\" is also declared as non-signal in @Directive."], + "diagnosticSpans": [[173, 202]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: span-shorthandInputs", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nconst inputs = [1];\n@Directive({selector: '[d]', inputs})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'number'." + ], + "diagnosticSpans": [[90, 96]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: span-multiline", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: [\n 'a',\n 2,\n ]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'number'." + ], + "diagnosticSpans": [[107, 128]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: span-unicode", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\nconst S = '😀';\n@Directive({selector: '[d]', inputs: [S, 1]})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'number'." + ], + "diagnosticSpans": [[123, 129]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: span-parenthesized", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, input, output, model} from '@angular/core';\n@Directive({selector: '[d]', inputs: (([1]))})\nexport class Dir {}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'number'." + ], + "diagnosticSpans": [[107, 114]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: scope-paramImport", + "origin": "probe", + "files": { + "test.ts": "import { Directive, booleanAttribute } from '@angular/core';\nfunction make(name: string, transform: any) { return [{ name, transform }]; }\n@Directive({ selector: '[d]', inputs: make('x', booleanAttribute) })\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-paramLocal", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nexport function toBool(v: unknown): boolean { return !!v; }\nfunction make(name: string, transform: any) { return [{ name, transform }]; }\n@Directive({ selector: '[d]', inputs: make('x', toBool) })\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",toBool]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-paramArrow", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nfunction make(name: string, transform: any) { return [{ name, transform }]; }\n@Directive({ selector: '[d]', inputs: make('x', (v: string) => v.length) })\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>v.length]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-bodyRef", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nexport function toBool(v: unknown): boolean { return !!v; }\nfunction make(name: string) { return [{ name, transform: toBool }]; }\n@Directive({ selector: '[d]', inputs: make('x') })\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",toBool]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-constObj", + "origin": "probe", + "files": { + "test.ts": "import { Directive, booleanAttribute } from '@angular/core';\nconst X = { name: 'x', transform: booleanAttribute };\n@Directive({ selector: '[d]', inputs: [X] })\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-staticMethod", + "origin": "probe", + "files": { + "test.ts": "import { Directive, booleanAttribute } from '@angular/core';\nclass H { static make(t: any) { return [{ name: 'x', transform: t }]; } }\n@Directive({ selector: '[d]', inputs: H.make(booleanAttribute) })\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-nestedHelper", + "origin": "probe", + "files": { + "test.ts": "import { Directive, booleanAttribute } from '@angular/core';\nfunction inner(n: string, t: any) { return [{ name: n, transform: t }]; }\nfunction outer(t: any) { return inner('x', t); }\n@Directive({ selector: '[d]', inputs: outer(booleanAttribute) })\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-defaultParam", + "origin": "probe", + "files": { + "test.ts": "import { Directive, booleanAttribute } from '@angular/core';\nfunction make(n: string, t: any = booleanAttribute) { return [{ name: n, transform: t }]; }\n@Directive({ selector: '[d]', inputs: make('x') })\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-aliasParam", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nfunction make(n: string, a: string) { return [{ name: n, alias: a }]; }\n@Directive({ selector: '[d]', inputs: make('x', 'why') })\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[0,\"why\",\"x\"]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-aliasImportParam", + "origin": "probe", + "files": { + "test.ts": "import { Directive, booleanAttribute as ba } from '@angular/core';\nfunction make(name: string, transform: any) { return [{ name, transform }]; }\n@Directive({ selector: '[d]', inputs: make('x', ba) })\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",ba]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-bodyArrowSafe", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nfunction make(name: string) { return [{ name, transform: (v: string) => v.length }]; }\n@Directive({ selector: '[d]', inputs: make('x') })\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>v.length]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: initializerApi-inputCoreAlias", + "origin": "probe", + "files": { + "test.ts": "import { Directive, input as inp } from '@angular/core';\n@Directive({ selector: '[d]', inputs: ['value'] })\nexport class D { value = inp(); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": ["Input \"value\" is also declared as non-signal in @Directive."], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[125, 139]] + }, + { + "name": "probe: initializerApi-inputRequiredCoreNamespace", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nimport * as core from '@angular/core';\n@Directive({ selector: '[d]', inputs: ['value'] })\nexport class D { value = core.input.required(); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": ["Input \"value\" is also declared as non-signal in @Directive."], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[150, 180]] + }, + { + "name": "probe: initializerApi-modelCoreOutputsMeta", + "origin": "probe", + "files": { + "test.ts": "import { Directive, model } from '@angular/core';\n@Directive({ selector: '[d]', outputs: ['valueChange'] })\nexport class D { value = model(); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{value:[1,\"value\"]}", + "outputs": "{valueChange:\"valueChange\",value:\"valueChange\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: initializerApi-modelCoreAlias", + "origin": "probe", + "files": { + "test.ts": "import { Directive, model as m } from '@angular/core';\n@Directive({ selector: '[d]', inputs: ['value'] })\nexport class D { value = m(); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": ["Input \"value\" is also declared as non-signal in @Directive."], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[123, 135]] + }, + { + "name": "probe: initializerApi-inputAsCast", + "origin": "probe", + "files": { + "test.ts": "import { Directive, input } from '@angular/core';\n@Directive({ selector: '[d]', inputs: ['value'] })\nexport class D { value = (input() as any); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": ["Input \"value\" is also declared as non-signal in @Directive."], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[118, 143]] + }, + { + "name": "probe: initializerApi-inputTypeOnlyImportCast", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nimport type { input } from '@angular/core';\n@Directive({ selector: '[d]', inputs: ['value'] })\nexport class D { value = (input as any)(); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{value:\"value\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: initializerApi-outputOtherModule", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nimport { output } from './other';\n@Directive({ selector: '[d]', outputs: ['changed'] })\nexport class D { changed = output(); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "outputs": "{changed:\"changed\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: initializerApi-outputFromObservable", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nimport { outputFromObservable } from '@angular/core/rxjs-interop';\nimport { Subject } from 'rxjs';\n@Directive({ selector: '[d]', outputs: ['changed'] })\nexport class D { changed = outputFromObservable(new Subject()); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": ["Output \"changed\" is unexpectedly declared in @Directive as well."], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[213, 265]] + }, + { + "name": "probe: initializerApi-outputFromObservableOtherModule", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nimport { outputFromObservable } from './other';\n@Directive({ selector: '[d]', outputs: ['changed'] })\nexport class D { changed = outputFromObservable(null); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "outputs": "{changed:\"changed\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: initializerApi-outputFromObservableNamespace", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nimport * as rx from '@angular/core/rxjs-interop';\nimport { Subject } from 'rxjs';\n@Directive({ selector: '[d]', outputs: ['changed'] })\nexport class D { changed = rx.outputFromObservable(new Subject()); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": ["Output \"changed\" is unexpectedly declared in @Directive as well."], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[196, 251]] + }, + { + "name": "probe: initializerApi-outputFromObservableOnCoreNamespace", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nimport * as core from '@angular/core';\n@Directive({ selector: '[d]', outputs: ['changed'] })\nexport class D { changed = (core as any).outputFromObservable(null); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "outputs": "{changed:\"changed\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: initializerApi-outputCoreAlias", + "origin": "probe", + "files": { + "test.ts": "import { Directive, output as out } from '@angular/core';\n@Directive({ selector: '[d]', outputs: ['changed'] })\nexport class D { changed = out(); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": ["Output \"changed\" is unexpectedly declared in @Directive as well."], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[129, 145]] + }, + { + "name": "probe: initializerApi-outputCoreNamespace", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nimport * as core from '@angular/core';\n@Directive({ selector: '[d]', outputs: ['changed'] })\nexport class D { changed = core.output(); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": ["Output \"changed\" is unexpectedly declared in @Directive as well."], + "classes": {}, + "dtsImports": [], + "diagnosticSpans": [[153, 177]] + }, + { + "name": "probe: initializerApi-outputLocalFunction", + "origin": "probe", + "files": { + "test.ts": "import { Directive } from '@angular/core';\nconst output = (): any => null;\n@Directive({ selector: '[d]', outputs: ['changed'] })\nexport class D { changed = output(); }\n", + "other.ts": "export function input(...a: any[]): any { return null; }\nexport namespace input { export function required(...a: any[]): any { return null; } }\nexport function model(...a: any[]): any { return null; }\nexport function output(...a: any[]): any { return null; }\nexport function outputFromObservable(...a: any[]): any { return null; }\nexport function viewChild(...a: any[]): any { return null; }\nexport function viewChildren(...a: any[]): any { return null; }\nexport function contentChild(...a: any[]): any { return null; }\nexport function contentChildren(...a: any[]): any { return null; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "outputs": "{changed:\"changed\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-shadowedByParam", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nfunction make(value: string) { return [{name: 'x', transform: (value: string) => value.trim()}]; }\n@Directive({selector: '[d]', inputs: make('v')})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",(value)=>value.trim()]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-shadowedByConst", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nfunction make(value: string) { return [{name: 'x', transform: (v: string) => { const value = v.trim(); return value; }}]; }\n@Directive({selector: '[d]', inputs: make('v')})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>{constvalue=v.trim();returnvalue;}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-shadowedByFunction", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nfunction make(value: string) { return [{name: 'x', transform: (v: string) => { function value(s: string) { return s; } return value(v); }}]; }\n@Directive({selector: '[d]', inputs: make('v')})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>{functionvalue(s){returns;}returnvalue(v);}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-shadowedByLoopAndCatch", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nfunction make(value: string) { return [{name: 'x', transform: (v: string) => { let r = v; for (const value of [v]) { r += value; } try { r += v; } catch (value) { r += value; } return r; }}]; }\n@Directive({selector: '[d]', inputs: make('v')})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>{letr=v;for(constvalueof[v]){r+=value;}try{r+=v;}catch(value){r+=value;}returnr;}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-shadowedInNestedArrow", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nfunction make(value: string) { return [{name: 'x', transform: (v: string) => { var q = () => { var value = 2; return value; }; return q() + v; }}]; }\n@Directive({selector: '[d]', inputs: make('v')})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>{varq=()=>{varvalue=2;returnvalue;};returnq()+v;}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-shadowedByClass", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nfunction make(value: string) { return [{name: 'x', transform: (v: string) => { class value {}; return new value() + v; }}]; }\n@Directive({selector: '[d]', inputs: make('v')})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>{classvalue{};returnnewvalue()+v;}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-ownArguments", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nfunction make(value: string) { return [{name: 'x', transform: function (v: string) { return arguments.length ? v : v + v; }}]; }\n@Directive({selector: '[d]', inputs: make('v')})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",function(v){returnarguments.length?v:v+v;}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-ownThis", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nfunction make(value: string) { return [{name: 'x', transform: function (v: string) { return this ? v : (() => arguments[0])(); }}]; }\n@Directive({selector: '[d]', inputs: make('v')})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",function(v){returnthis?v:(()=>arguments[0])();}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-hoistedVar", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nfunction make(value: string) { return [{name: 'x', transform: (v: string) => { if (v) { var value = v; } return value; }}]; }\n@Directive({selector: '[d]', inputs: make('v')})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>{if(v){varvalue=v;}returnvalue;}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-shadowedByClassName", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nfunction make(value: string) { return [{name: 'x', transform: (v: string) => new (class value { m() { return value; } })().m() && v}]; }\n@Directive({selector: '[d]', inputs: make('v')})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>new(classvalue{m(){returnvalue;}})().m()&&v]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-shadowedByDestructuredParam", + "origin": "probe", + "files": { + "test.ts": "import {Directive} from '@angular/core';\nfunction make(value: string) { return [{name: 'x', transform: ({value}: {value: string}) => value}]; }\n@Directive({selector: '[d]', inputs: make('v')})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",({value})=>value]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-spreadConstArgs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, booleanAttribute} from '@angular/core';\nexport function toBool(v: string) { return !!v; }\nfunction make(name: string, t: any) { return [{name, transform: t}]; }\nconst args = ['x', booleanAttribute] as const;\n@Directive({selector: '[d]', inputs: make(...args)})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-restParam", + "origin": "probe", + "files": { + "test.ts": "import {Directive, booleanAttribute} from '@angular/core';\nexport function toBool(v: string) { return !!v; }\nfunction make(...a: any[]) { return [{name: a[0], transform: a[1]}]; }\n@Directive({selector: '[d]', inputs: make('x', booleanAttribute)})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-restAfterParam", + "origin": "probe", + "files": { + "test.ts": "import {Directive, booleanAttribute} from '@angular/core';\nexport function toBool(v: string) { return !!v; }\nfunction make(name: string, ...ts: any[]) { return [{name, transform: ts[0]}]; }\n@Directive({selector: '[d]', inputs: make('x', booleanAttribute)})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-spreadArrayLiteral", + "origin": "probe", + "files": { + "test.ts": "import {Directive, booleanAttribute} from '@angular/core';\nexport function toBool(v: string) { return !!v; }\nfunction make(name: string, t: any) { return [{name, transform: t}]; }\n@Directive({selector: '[d]', inputs: make(...['x', toBool])})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",toBool]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-spreadAfterArg", + "origin": "probe", + "files": { + "test.ts": "import {Directive, booleanAttribute} from '@angular/core';\nexport function toBool(v: string) { return !!v; }\nfunction make(name: string, t: any) { return [{name, transform: t}]; }\n@Directive({selector: '[d]', inputs: make('x', ...[toBool])})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",toBool]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-spreadRestThrough", + "origin": "probe", + "files": { + "test.ts": "import {Directive, booleanAttribute} from '@angular/core';\nexport function toBool(v: string) { return !!v; }\nfunction make(name: string, t: any) { return [{name, transform: t}]; }\nfunction outer(...a: any[]) { return make(...a); }\n@Directive({selector: '[d]', inputs: outer('x', toBool)})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",toBool]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-restLocalFunction", + "origin": "probe", + "files": { + "test.ts": "import {Directive, booleanAttribute} from '@angular/core';\nexport function toBool(v: string) { return !!v; }\nfunction make(name: string, ...ts: any[]) { return [{name, transform: ts[0]}]; }\n@Directive({selector: '[d]', inputs: make('x', toBool)})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",toBool]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-spreadBeforeArgs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, booleanAttribute} from '@angular/core';\nexport function toBool(v: string) { return !!v; }\nfunction make(name: string, t: any, u: any) { return [{name, transform: u}]; }\nconst pre = ['x'];\n@Directive({selector: '[d]', inputs: make(...pre, toBool, booleanAttribute)})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: scope-restSpreadImport", + "origin": "probe", + "files": { + "test.ts": "import {Directive, booleanAttribute} from '@angular/core';\nfunction make(name: string, t: any) { return [{name, transform: t}]; }\nfunction outer(name: string, ...a: any[]) { return make(name, ...a); }\n@Directive({selector: '[d]', inputs: outer('x', booleanAttribute)})\nexport class D { x: any; }\n" + }, + "diagnostics": [], + "classes": { + "D": { + "inputs": "{x:[2,\"x\",\"x\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_x": "staticngAcceptInputType_x:unknown;" + } + }, + "dtsImports": [], + "diagnosticSpans": [] + }, + { + "name": "probe: shortCircuit-metaOr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', inputs: [LOCAL || NAME]})\nexport class Dir {\n localAlias: any;\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{localAlias:\"localAlias\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: shortCircuit-metaObjAliasOr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', inputs: [{name: 'x', alias: LOCAL || NAME}]})\nexport class Dir {\n x: any;\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[0,\"localAlias\",\"x\"]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: shortCircuit-metaObjAliasAnd", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', inputs: [{name: 'x', alias: (EMPTY_LOCAL && NAME) || 'z'}]})\nexport class Dir {\n x: any;\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[0,\"z\",\"x\"]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: shortCircuit-metaCond", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', inputs: [true ? 'x' : NAME]})\nexport class Dir {\n x: any;\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: shortCircuit-metaArrayOr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', inputs: ['x'] || NAME})\nexport class Dir {\n x: any;\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: shortCircuit-metaOutOr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', outputs: [LOCAL || NAME]})\nexport class Dir {\n localAlias = new EventEmitter();\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "outputs": "{localAlias:\"localAlias\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: shortCircuit-metaOutCond", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', outputs: [1 ? LOCAL : NAME]})\nexport class Dir {\n localAlias = new EventEmitter();\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "outputs": "{localAlias:\"localAlias\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: shortCircuit-metaObjRequiredOr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', inputs: [{name: 'x', required: true || FLAG}]})\nexport class Dir {\n x: any;\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: shortCircuit-metaObjRequiredAnd", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', inputs: [{name: 'x', required: 0 && FLAG}]})\nexport class Dir {\n x: any;\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: shortCircuit-metaSpreadOr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', inputs: [...(['x'] || NAME)]})\nexport class Dir {\n x: any;\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: shortCircuit-metaNestedOr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', inputs: [(EMPTY_LOCAL || 0) || (LOCAL && 'x') || NAME]})\nexport class Dir {\n x: any;\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: shortCircuit-metaDynamicUnselected", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', inputs: ['x' || window.name]})\nexport class Dir {\n x: any;\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[310, 330]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: shortCircuit-metaCondImportUnselected", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Component, Input, Output, ViewChild, ContentChild, ElementRef, booleanAttribute, EventEmitter} from '@angular/core';\nimport {NAME, FLAG, TRUE_FLAG, Token, EMPTY, toBool} from './other';\nconst LOCAL: string = 'localAlias';\nconst EMPTY_LOCAL: string = '';\n@Directive({selector: '[d]', inputs: 0 ? NAME : ['x']})\nexport class Dir {\n x: any;\n}\n", + "other.ts": "import {booleanAttribute} from '@angular/core';\nexport const NAME = 'y';\nexport const EMPTY = '';\nexport const FLAG = false;\nexport const TRUE_FLAG = true;\nexport class Token {}\nexport function toBool(v: string) { return !!v; }\nexport interface Other { a: string }\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:\"x\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-returnType", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: string): typeof name => v}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>v]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-paramType", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: typeof name) => 1}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>1]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-as", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: string) => v as typeof name}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>v]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-satisfies", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: string) => v satisfies typeof name}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>v]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-angleAssert", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: string) => v}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>v]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-typeArgs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction id(v: T): T { return v; }\nfunction make(name: string) { return [{name: 'x', transform: (v: string) => id(v)}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>id(v)]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-newTypeArgs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: string) => new Set([v]).size}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>newSet([v]).size]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-instantiation", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction id(v: T): T { return v; }\nfunction make(name: string) { return [{name: 'x', transform: (v: string) => (id)(v)}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>(id)(v)]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-fnThisParam", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: function (this: typeof name, v: string) { return v; }}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",function(v){returnv;}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-fnReturnType", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: function (v: string): typeof name { return v; }}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",function(v){returnv;}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-bodyVarType", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: string) => { const y: typeof name = v; return y; }}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>{consty=v;returny;}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-bodyTypeAlias", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: string) => { type T = typeof name; return v as T; }}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>{returnv;}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-bodyInterface", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: string) => { interface I { p: typeof name } return (v as unknown as I).p; }}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>{returnv.p;}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-bodyClassMethod", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: string) => { class K { m(): typeof name { return v; } } return new K().m(); }}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>{classK{m(){returnv;}}returnnewK().m();}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-nestedGeneric", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: string) => { function g(x: T): T { return x; } return g(v); }}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>{functiong(x){returnx;}returng(v);}]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-predicate", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: unknown): v is typeof name => true}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>true]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: scope-typeOnly-nonNullType", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input} from '@angular/core';\nfunction make(name: string) { return [{name: 'x', transform: (v: string) => (v as typeof name)!}]; }\n@Directive({selector: '[d]', inputs: make('x')})\nexport class Dir {\n x: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{x:[2,\"x\",\"x\",(v)=>v]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-plain", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\n@Directive({selector: '[d]', inputs: NS.INPUTS})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[163, 172]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-exported-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nexport namespace NS { export const INPUTS = ['a']; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-module-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nmodule NS { export const INPUTS = ['a']; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-nested-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export namespace Inner { export const X = ['a']; } }\ndeclare const Y: typeof NS.Inner.X;\n@Directive({selector: '[d]', inputs: Y})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-nested-plain", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export namespace Inner { export const X = ['a']; } }\n@Directive({selector: '[d]', inputs: NS.Inner.X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[178, 188]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-dotted-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace A.B { export const X = ['a']; }\ndeclare const Y: typeof A.B.X;\n@Directive({selector: '[d]', inputs: Y})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-nonexported-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { const INPUTS = ['a']; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'X'." + ], + "diagnosticSpans": [[184, 185]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-declare-typed", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\ndeclare namespace NS { const INPUTS: ['a']; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type 'undefined'." + ], + "diagnosticSpans": [[191, 192]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-declare-export-typed", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\ndeclare namespace NS { export const INPUTS: ['a']; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type 'undefined'." + ], + "diagnosticSpans": [[198, 199]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-declare-const-inside", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export declare const INPUTS: ['a']; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-inner-ref", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { const P = 'a'; export const INPUTS = [P]; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-shadow", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nconst P = 'top';\nnamespace NS { const P = 'a'; export const INPUTS = [P]; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n top: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-merged-exported", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const P = 'a'; }\nnamespace NS { export const INPUTS = [P]; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-merged-private", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { const P = 'a'; }\nconst P = 'b';\nnamespace NS { export const INPUTS = [P]; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-top-ref", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nconst P = 'a';\nnamespace NS { export const INPUTS = [P]; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-self-qualified", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const P = 'a'; export const INPUTS = [NS.P]; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[214, 215]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-nested-outer-ref", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { const P = 'a'; export namespace Inner { export const X = [P, 'b']; } }\ndeclare const Y: typeof NS.Inner.X;\n@Directive({selector: '[d]', inputs: Y})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-let", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export let INPUTS = ['a']; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-var-uninit", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export let INPUTS; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is of type 'undefined'." + ], + "diagnosticSpans": [[181, 182]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-destructure", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const {INPUTS} = {INPUTS: ['a']}; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-enum-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export enum E { A = 'a' } }\ndeclare const X: typeof NS.E;\n@Directive({selector: '[d]', inputs: [X.A]})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'E'." + ], + "diagnosticSpans": [[183, 188]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-enum-plain", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export enum E { A = 'a' } }\n@Directive({selector: '[d]', inputs: [NS.E.A]})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value could not be determined statically." + ], + "diagnosticSpans": [[153, 161]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-enum-member-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export enum E { A = 'a' } }\ndeclare const X: typeof NS.E.A;\n@Directive({selector: '[d]', inputs: [X]})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is a reference to 'A'." + ], + "diagnosticSpans": [[185, 188]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-class-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export class C { static I = ['a']; } }\ndeclare const X: typeof NS.C;\n@Directive({selector: '[d]', inputs: X.I})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-whole-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\ndeclare const X: typeof NS;\n@Directive({selector: '[d]', inputs: X.INPUTS})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[191, 199]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-fn-transform-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export function fn(v: string) { return 1; } }\ndeclare const T: typeof NS.fn;\n@Directive({selector: '[d]', inputs: [{name: 'a', transform: T}]})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:[2,\"a\",\"a\",fn]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_a": "staticngAcceptInputType_a:string;" + } + }, + "dtsImports": [], + "skip": "ngtsc emits `fn` for `transform: T` with `declare const T: typeof NS.fn` (a function declared in a namespace), an identifier that isn't in scope, so its output throws a ReferenceError when the module loads; oxc keeps `T`" + }, + { + "name": "probe: r6-ns-fn-transform-member-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export function fn(v: string) { return 1; } }\ndeclare const T: typeof NS.fn;\n@Directive({selector: '[d]', })\nexport class Dir {\n @Input({transform: T}) a: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:[2,\"a\",\"a\",fn]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_a": "staticngAcceptInputType_a:string;" + } + }, + "dtsImports": [], + "skip": "ngtsc emits `fn` for `transform: T` with `declare const T: typeof NS.fn` (a function declared in a namespace), an identifier that isn't in scope, so its output throws a ReferenceError when the module loads; oxc keeps `T`" + }, + { + "name": "probe: r6-ns-fn-call-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export function make() { return ['a']; } }\ndeclare const F: typeof NS.make;\n@Directive({selector: '[d]', inputs: F()})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-alias-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS = ['a']; }\ndeclare const X: typeof NS.INPUTS;\nconst Y = X;\n@Directive({selector: '[d]', inputs: Y})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-typeof-member-object", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const CFG = {i: ['a']}; }\ndeclare const X: typeof NS.CFG;\n@Directive({selector: '[d]', inputs: X.i})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-outputs-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const OUTS = ['a']; }\ndeclare const X: typeof NS.OUTS;\n@Directive({selector: '[d]', outputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "outputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-typeof-inside-ns", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const A = ['a']; export declare const B: typeof A; }\ndeclare const X: typeof NS.B;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-typeof-imported", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nimport {SHARED} from './other';\nnamespace NS { export const INPUTS = SHARED; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n", + "other.ts": "export const SHARED = ['a'];\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [], + "skip": "needs declarations from another file; oxc compiles one file at a time (imported consts, types and transforms are opaque)" + }, + { + "name": "probe: r6-tpl-enum-value", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nenum E { [`A`] = 'a' }\n@Directive({selector: '[d]', inputs: [E.A]})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'E'." + ], + "diagnosticSpans": [[133, 138]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-tpl-enum-value-ns", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export enum E { [`A`] = 'a' } }\ndeclare const X: typeof NS.E;\n@Directive({selector: '[d]', inputs: [X.A]})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'E'." + ], + "diagnosticSpans": [[187, 192]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-shadow-top-typeof-name", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nconst INPUTS = ['top'];\nnamespace NS { export const INPUTS = ['a']; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n top: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-typeof-unknown-member", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\ndeclare const X: typeof NS.MISSING;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'X'." + ], + "diagnosticSpans": [[199, 200]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-fn-ref-inside", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export function fn(v: string) { return 1; } export const INPUTS = [{name: 'a', transform: fn}]; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:[2,\"a\",\"a\",fn]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_a": "staticngAcceptInputType_a:string;" + } + }, + "dtsImports": [], + "skip": "the transform is written in a namespace and uses the namespace's declarations; ngtsc emits it as written (`fn`, `(v) => P`), which isn't in scope where the directive is compiled, so its output throws a ReferenceError when the module loads; oxc reports it (see `transforms_using_a_namespace_s_declarations_are_reported`)" + }, + { + "name": "probe: r6-ns-arrow-local-inside", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { const P = 1; export const INPUTS = [{name: 'a', transform: (v: string) => P}]; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:[2,\"a\",\"a\",(v)=>P]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_a": "staticngAcceptInputType_a:string;" + } + }, + "dtsImports": [], + "skip": "the transform is written in a namespace and uses the namespace's declarations; ngtsc emits it as written (`fn`, `(v) => P`), which isn't in scope where the directive is compiled, so its output throws a ReferenceError when the module loads; oxc reports it (see `transforms_using_a_namespace_s_declarations_are_reported`)" + }, + { + "name": "probe: r6-ns-imported-transform-inside", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nimport {booleanAttribute} from '@angular/core';\nnamespace NS { export const INPUTS = [{name: 'a', transform: booleanAttribute}]; }\ndeclare const X: typeof NS.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n", + "other.ts": "export const SHARED = ['a'];\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:[2,\"a\",\"a\",booleanAttribute]}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;", + "dts:ngAcceptInputType_a": "staticngAcceptInputType_a:unknown;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-whole-direct", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\ndeclare const X: typeof NS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'NS'." + ], + "diagnosticSpans": [[191, 192]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-value-direct", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\n@Directive({selector: '[d]', inputs: NS})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'NS'." + ], + "diagnosticSpans": [[163, 165]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-typeref-class", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export class C {} }\ndeclare const X: [NS.C];\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is a reference to 'C'." + ], + "diagnosticSpans": [[170, 171]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-enum-merged", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nenum E { A = 'a' }\nnamespace E { export const I = ['a']; }\ndeclare const X: typeof E.I;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-fn-body-ns-ref", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { const P = 'a'; export function make() { return [P]; } }\ndeclare const F: typeof NS.make;\n@Directive({selector: '[d]', inputs: F()})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-class-static-ns-ref", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { const P = 'a'; export class C { static I = [P]; } }\ndeclare const X: typeof NS.C;\n@Directive({selector: '[d]', inputs: X.I})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-typeof-chain", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const A = ['a']; export const B = [...A, 'b']; }\ndeclare const X: typeof NS.B;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-local-nested-member", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { namespace Hidden { export const X = ['a']; } }\ndeclare const Y: typeof NS.Hidden.X;\n@Directive({selector: '[d]', inputs: Y})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'Y'." + ], + "diagnosticSpans": [[209, 210]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-dotted-merge", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace A.B { export const X = 'a'; }\nnamespace A { export namespace B { export const Y = [X, 'b']; } }\ndeclare const Z: typeof A.B.Y;\n@Directive({selector: '[d]', inputs: Z})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\",b:\"b\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-ns-shadowed-head", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS = ['a']; }\nnamespace Outer { const NS = {INPUTS: ['b']}; export declare const X: typeof NS.INPUTS; }\ndeclare const Y: typeof Outer.X;\n@Directive({selector: '[d]', inputs: Y})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'INPUTS'." + ], + "diagnosticSpans": [[279, 280]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-inputs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace U { export function f(v: string) { return 1; } }\n@Directive({selector: '[d]', inputs: U})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'U'." + ], + "diagnosticSpans": [[169, 170]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-inputs-arr", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace U { export function f(v: string) { return 1; } }\n@Directive({selector: '[d]', inputs: [U]})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is a reference to 'U'." + ], + "diagnosticSpans": [[169, 172]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-empty-declare-module", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\ndeclare module M { }\n@Directive({selector: '[d]', inputs: M})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[131, 132]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-types-only", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace N { export type T = string; }\n@Directive({selector: '[d]', inputs: N})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[150, 151]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-declare", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\ndeclare namespace U { function f(v: string): number; }\n@Directive({selector: '[d]', inputs: U})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'U'." + ], + "diagnosticSpans": [[165, 166]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-constenum-only", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace U { export const enum E { A } }\n@Directive({selector: '[d]', inputs: U})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'U'." + ], + "diagnosticSpans": [[152, 153]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-nested-inst", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace U { export namespace V { export const a = 1; } }\n@Directive({selector: '[d]', inputs: U})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'U'." + ], + "diagnosticSpans": [[169, 170]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-nested-noninst", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace U { export namespace V { export type T = 1; } }\n@Directive({selector: '[d]', inputs: U})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[168, 169]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-declare-const-only", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\ndeclare namespace U { const a: number; }\n@Directive({selector: '[d]', inputs: U})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'U'." + ], + "diagnosticSpans": [[151, 152]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-class-only", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace U { class C {} }\n@Directive({selector: '[d]', inputs: U})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'U'." + ], + "diagnosticSpans": [[137, 138]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-typeof-noninst-inner", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace U { export namespace V { export type T = 1; } export const a = 1; }\ndeclare const X: typeof U.V;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'X'." + ], + "diagnosticSpans": [[217, 218]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-typeof-inst-inner", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace U { export namespace V { export const b = 1; } }\ndeclare const X: typeof U.V;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'V'." + ], + "diagnosticSpans": [[198, 199]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-nsval-block-shadow-noninst", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nconst P = ['a'];\nnamespace U { namespace P { export type T = 1; } export const I = P; }\ndeclare const X: typeof U.I;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-inputs", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\nimport I = NS.INPUTS;\n@Directive({selector: '[d]', inputs: I})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-nested", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export namespace Inner { export const X = ['a']; } }\nimport I = NS.Inner.X;\n@Directive({selector: '[d]', inputs: I})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-namespace", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\nimport A = NS;\n@Directive({selector: '[d]', inputs: A})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'NS'." + ], + "diagnosticSpans": [[178, 179]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-namespace-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\nimport A = NS;\ndeclare const X: typeof A.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-in-ns", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\nnamespace O { export import J = NS.INPUTS; }\ndeclare const X: typeof O.J;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-in-ns-local", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\nnamespace O { import J = NS.INPUTS; export const K = J; }\ndeclare const X: typeof O.K;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-missing", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\nimport I = NS.MISSING;\n@Directive({selector: '[d]', inputs: I})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[186, 187]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-cycle", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace A { export import B = C.D; }\nnamespace C { export import D = A.B; }\nimport I = A.B;\n@Directive({selector: '[d]', inputs: I})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[204, 205]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-of-alias", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\nimport I = NS.INPUTS;\nimport J = I;\n@Directive({selector: '[d]', inputs: J})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[199, 200]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-exported", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\nexport import I = NS.INPUTS;\n@Directive({selector: '[d]', inputs: I})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-enum", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export enum E { A = 'a' } }\nimport E = NS.E;\n@Directive({selector: '[d]', inputs: [E.A]})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "@Directive.inputs array can only contain strings or object literals Value is of type 'E'." + ], + "diagnosticSpans": [[170, 175]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-typeonly-value", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export type T = string; }\nimport T = NS.T;\n@Directive({selector: '[d]', inputs: T})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[168, 169]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-noninst-namespace", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace M { export type T = 1; }\nimport A = M;\n@Directive({selector: '[d]', inputs: A})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[159, 160]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-qualified-noninst", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace O { export namespace M { export type T = 1; } export const z = 1; }\nimport A = O.M;\n@Directive({selector: '[d]', inputs: A})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value could not be determined statically." + ], + "diagnosticSpans": [[204, 205]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-alias-namespace", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\nimport A = NS;\nimport B = A;\n@Directive({selector: '[d]', inputs: B})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [ + "Failed to resolve @Directive.inputs to an array Value is a reference to 'NS'." + ], + "diagnosticSpans": [[192, 193]], + "classes": {}, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-alias-namespace-typeof", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\nimport A = NS;\nimport B = A;\ndeclare const X: typeof B.INPUTS;\n@Directive({selector: '[d]', inputs: X})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + }, + { + "name": "probe: r6-alias-qualified-through-alias", + "origin": "probe", + "files": { + "test.ts": "import {Directive, Input, ContentChild, ViewChild} from '@angular/core';\nnamespace NS { export const INPUTS: ['a'] = ['a']; }\nimport A = NS;\nimport I = A.INPUTS;\n@Directive({selector: '[d]', inputs: I})\nexport class Dir {\n a: any;\n b: any;\n}\n" + }, + "diagnostics": [], + "diagnosticSpans": [], + "classes": { + "Dir": { + "inputs": "{a:\"a\"}", + "dts:ɵdir": "staticɵdir:i0.ɵɵDirectiveDeclaration;" + } + }, + "dtsImports": [] + } + ] +} diff --git a/napi/angular-compiler/e2e/compare/fixtures/inputs-outputs/decorator-metadata-angular-spec.fixture.ts b/napi/angular-compiler/e2e/compare/fixtures/inputs-outputs/decorator-metadata-angular-spec.fixture.ts new file mode 100644 index 000000000..95950f8ff --- /dev/null +++ b/napi/angular-compiler/e2e/compare/fixtures/inputs-outputs/decorator-metadata-angular-spec.fixture.ts @@ -0,0 +1,248 @@ +/** + * `inputs:` / `outputs:` in decorator metadata, ported from Angular's own tests + * (angular/angular v22.1.7). Acceptance-spec sources are made standalone + * (`imports: [Dir]`) in place of their TestBed NgModule, and drop the TestBed-only + * `changeDetection: Eager`; directive metadata is verbatim. + */ +import type { Fixture } from '../types.js' + +const fixture = ( + name: string, + description: string, + className: string, + sourceCode: string, +): Fixture => ({ + type: 'full-transform', + name: `decorator-metadata-${name}`, + category: 'inputs-outputs', + description, + className, + sourceCode: sourceCode.trim(), +}) + +export const fixtures: Fixture[] = [ + // compiler-cli/test/ngtsc/ngtsc_spec.ts: 'should allow directives with no selector that are not in NgModules' + fixture( + 'no-selector', + 'selectorless directive with an inputs array', + 'TestDirWithInputs', + ` +import {Directive} from '@angular/core'; + +@Directive({}) +export class BaseDir {} + +@Directive({}) +export abstract class AbstractBaseDir {} + +@Directive() +export abstract class EmptyDir {} + +@Directive({ + inputs: ['a', 'b'], + standalone: false, +}) +export class TestDirWithInputs {} +`, + ), + // ngtsc_spec.ts: 'should wrap "inputs" and "outputs" keys if they contain unsafe characters' + fixture( + 'unsafe-keys', + 'inputs/outputs names that need quoting, alongside @Input members', + 'SomeDir', + ` +import {Directive, Input} from '@angular/core'; + +@Directive({ + selector: '[somedir]', + inputs: ['track-type', 'track-name', 'inputTrackName', 'src.xl'], + outputs: ['output-track-type', 'output-track-name', 'outputTrackName', 'output.event'] +}) +export class SomeDir { + @Input('track-type') trackType: string; + @Input('track-name') trackName: string; +} +`, + ), + // ngtsc_spec.ts: 'should generate the correct declaration for directives using the `inputs` array' + fixture( + 'all-forms', + 'every string and object form of the inputs array', + 'TestDir', + ` +import {Directive, Input} from '@angular/core'; + +@Directive({ + selector: '[dir]', + inputs: [ + 'plain', + 'withAlias: aliasedWithAlias', + {name: 'plainLiteral'}, + {name: 'aliasedLiteral', alias: 'alisedLiteralAlias'}, + {name: 'requiredLiteral', required: true}, + {name: 'requiredAlisedLiteral', alias: 'requiredAlisedLiteralAlias', required: true} + ], + standalone: false, +}) +export class TestDir { + plainLiteral: any; + aliasedLiteral: any; + requiredLiteral: any; + requiredAlisedLiteral: any; +} +`, + ), + // ngtsc_spec.ts: 'should *not* generate a validator fn for attribute and property bindings when *not* on