@eui/core 22.0.0-alpha.14 → 22.0.0-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/docs/changelog.html +20 -0
- package/docs/interfaces/Edit-3.html +1 -1
- package/docs/interfaces/Edit-4.html +1 -1
- package/docs/interfaces/Schema-13.html +1 -1
- package/docs/interfaces/Schema-15.html +1 -1
- package/docs/interfaces/Schema-16.html +1 -1
- package/docs/interfaces/Schema-17.html +1 -1
- package/docs/interfaces/Schema-18.html +1 -1
- package/docs/interfaces/Schema-2.html +1 -52
- package/docs/interfaces/Schema-20.html +1 -1
- package/docs/interfaces/Schema-21.html +1 -1
- package/docs/interfaces/Schema-3.html +52 -1
- package/docs/interfaces/Schema-7.html +1 -1
- package/docs/interfaces/Schema-8.html +1 -1
- package/docs/js/search/search_index.js +2 -2
- package/docs/json/documentation.json +493 -493
- package/docs/llms.txt +65 -65
- package/docs/miscellaneous/functions.html +484 -484
- package/docs/miscellaneous/variables.html +31 -31
- package/docs/properties.html +1 -1
- package/package.json +2 -2
|
@@ -968,12 +968,12 @@
|
|
|
968
968
|
},
|
|
969
969
|
{
|
|
970
970
|
"name": "Edit",
|
|
971
|
-
"id": "interface-Edit-
|
|
972
|
-
"file": "packages/core/schematics/migrate-
|
|
971
|
+
"id": "interface-Edit-d36032102ed30a7ada1e3d36bb9ca41b7234b855760cac9783a25818f7ffe2097e1ebd8e808b574ddec0760f827272f6cd561f45f3eb1578f87f39ee2633730a-3",
|
|
972
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
973
973
|
"deprecated": false,
|
|
974
974
|
"deprecationMessage": "",
|
|
975
975
|
"type": "interface",
|
|
976
|
-
"sourceCode": "import { DirEntry, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';\nimport { parseTemplate, TmplAstElement, TmplAstNode } from '@angular/compiler';\nimport * as ts from 'typescript';\nimport { logDryRun, logDryRunNote } from '../utils/dry-run';\nimport { EUI_SIGNAL_MAP, EUI_SELECTOR_TO_CLASS } from './signal-map';\n\ninterface Schema {\n path?: string;\n dryRun?: boolean;\n}\n\ninterface Edit {\n start: number;\n end: number;\n replacement: string;\n}\n\n/**\n * Migrates consumer code that accesses EUI component instances programmatically.\n *\n * When EUI migrated @Input() → input(), @ViewChild() → viewChild(), and\n * @ContentChild() → contentChild(), properties that were plain class fields\n * became signal getters. Consumers who access them via @ViewChild need to\n * change `comp.prop` to `comp.prop()`.\n *\n * This schematic:\n * 1. Finds @ViewChild / @ContentChild fields typed as EUI component classes\n * 2. Resolves untyped @ViewChild('refName') by reading the component template\n * and matching #refName to an EUI element tag/selector\n * 3. Tracks which local variable names hold EUI component references (incl. aliases)\n * 4. Rewrites property accesses on those references: `ref.prop` → `ref.prop()`\n * (only for properties listed in the signal map)\n * 5. Does NOT touch template bindings — those are unaffected by signal migrations\n */\nexport function migrateToSignals(options: Schema = {}): Rule {\n return (tree: Tree, context: SchematicContext) => {\n const scanPath = options.path ? '/' + options.path.replace(/^\\.?\\//, '').replace(/\\/$/, '') : '';\n let fileCount = 0;\n let propCount = 0;\n\n visitDir(tree.getDir(scanPath || '/'), (path) => {\n if (!path.endsWith('.ts')) return;\n // Skip spec files — test code may use different patterns\n if (path.endsWith('.spec.ts')) return;\n\n const buffer = tree.read(path);\n if (!buffer) return;\n\n const original = buffer.toString('utf-8');\n\n // Fast bail: file must mention at least one EUI class name or have\n // ViewChild/ContentChild with string refs (which might reference EUI elements)\n if (!hasEuiReference(original) && !hasStringRefQuery(original)) return;\n\n const result = migrateTypeScript(original, path, tree, context); if (result.source !== original) {\n if (options.dryRun) {\n logDryRun(context, `Would migrate ${result.editCount} signal property access(es) in ${path}`);\n } else {\n tree.overwrite(path, result.source);\n }\n fileCount++;\n propCount += result.editCount;\n }\n });\n\n context.logger.info(`Migrated ${propCount} property access(es) to signal getters in ${fileCount} file(s).`);\n if (options.dryRun) {\n logDryRunNote(context);\n }\n return tree;\n };\n}\n\n/** Quick check: does the file reference any EUI class name from the signal map? */\nfunction hasEuiReference(source: string): boolean {\n for (const className of Object.keys(EUI_SIGNAL_MAP)) {\n if (source.includes(className)) return true;\n }\n return false;\n}\n\n/** Quick check: does the file have @ViewChild('...') or @ContentChild('...') with string refs? */\nfunction hasStringRefQuery(source: string): boolean {\n return /(@ViewChild|@ContentChild|@ViewChildren|@ContentChildren)\\s*\\(\\s*['\"]/.test(source);\n}\n\ninterface MigrateResult {\n source: string;\n editCount: number;\n}\n\nfunction migrateTypeScript(source: string, filePath: string, tree: Tree, context: SchematicContext): MigrateResult {\n const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);\n const edits: Edit[] = [];\n\n // Phase 0: Build template ref map (#refName → EUI class name) from the component's template\n const templateRefMap = buildTemplateRefMap(sourceFile, source, filePath, tree);\n\n // Phase 1: Collect all variables/properties typed as EUI classes\n const euiRefs = collectEuiReferences(sourceFile, source, templateRefMap);\n\n if (euiRefs.size === 0) {\n return { source, editCount: 0 };\n }\n\n // Phase 2: Find property accesses on those references and rewrite signal props\n const visitNode = (node: ts.Node): void => {\n // Match: ref.prop (PropertyAccessExpression)\n if (ts.isPropertyAccessExpression(node)) {\n const rewrite = tryRewritePropertyAccess(node, euiRefs, sourceFile, source);\n if (rewrite) {\n edits.push(rewrite);\n }\n }\n ts.forEachChild(node, visitNode);\n };\n\n ts.forEachChild(sourceFile, visitNode);\n\n if (edits.length === 0) {\n return { source, editCount: 0 };\n }\n\n return {\n source: applyEdits(source, edits),\n editCount: edits.length,\n };\n}\n\n/**\n * Scans the source file for class properties or variables typed as EUI component classes.\n * Returns a map: localName → Set of signal property names.\n *\n * Handles:\n * @ViewChild(EuiXxxComponent) name: EuiXxxComponent;\n * @ViewChild(EuiXxxComponent) name!: EuiXxxComponent;\n * @ViewChild('ref') name: EuiXxxComponent;\n * @ContentChild(EuiXxxComponent) name: EuiXxxComponent;\n * @ViewChild('ref') name: ElementRef; (resolved via template #ref → EUI selector)\n * someVar: EuiXxxComponent; (typed class field, even without decorator)\n * const alias = this.viewChildRef; (alias tracking)\n */\nfunction collectEuiReferences(\n sourceFile: ts.SourceFile,\n source: string,\n templateRefMap: Map<string, string>,\n): Map<string, ReadonlySet<string>> {\n const refs = new Map<string, ReadonlySet<string>>();\n\n // Pass 1: collect directly-typed references\n const visitDirect = (node: ts.Node): void => {\n // Class property declarations\n if (ts.isPropertyDeclaration(node) && node.name && ts.isIdentifier(node.name)) {\n const euiClass = resolveEuiClassName(node, sourceFile, templateRefMap);\n if (euiClass && EUI_SIGNAL_MAP[euiClass]) {\n refs.set(node.name.text, EUI_SIGNAL_MAP[euiClass]);\n }\n }\n\n // Variable declarations (const x: EuiXxxComponent = ...)\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {\n const typeName = getTypeAnnotationName(node.type);\n if (typeName && EUI_SIGNAL_MAP[typeName]) {\n refs.set(node.name.text, EUI_SIGNAL_MAP[typeName]);\n }\n }\n\n // Parameter declarations (method(ref: EuiXxxComponent))\n if (ts.isParameter(node) && ts.isIdentifier(node.name)) {\n const typeName = getTypeAnnotationName(node.type);\n if (typeName && EUI_SIGNAL_MAP[typeName]) {\n refs.set(node.name.text, EUI_SIGNAL_MAP[typeName]);\n }\n }\n\n ts.forEachChild(node, visitDirect);\n };\n\n ts.forEachChild(sourceFile, visitDirect);\n\n // Pass 2: track aliases — variables assigned from already-tracked references\n // e.g. const alias = this.viewChildRef; or const alias = someParam;\n const visitAlias = (node: ts.Node): void => {\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {\n // Skip if already tracked (has explicit type annotation)\n if (refs.has(node.name.text)) {\n ts.forEachChild(node, visitAlias);\n return;\n }\n\n const aliasedRef = resolveRefName(node.initializer);\n if (aliasedRef && refs.has(aliasedRef)) {\n refs.set(node.name.text, refs.get(aliasedRef)!);\n }\n }\n\n ts.forEachChild(node, visitAlias);\n };\n\n ts.forEachChild(sourceFile, visitAlias);\n return refs;\n}\n\n/**\n * Resolves the EUI class name from a property declaration.\n * Checks: type annotation → decorator class argument → template ref map.\n */\nfunction resolveEuiClassName(\n node: ts.PropertyDeclaration,\n sourceFile: ts.SourceFile,\n templateRefMap: Map<string, string>,\n): string | null {\n // Check type annotation first\n const typeName = getTypeAnnotationName(node.type);\n if (typeName && EUI_SIGNAL_MAP[typeName]) {\n return typeName;\n }\n\n // Check generic type argument: ElementRef<EuiXxxComponent>\n if (node.type && ts.isTypeReferenceNode(node.type) && node.type.typeArguments?.length) {\n const genericArg = getTypeAnnotationName(node.type.typeArguments[0]);\n if (genericArg && EUI_SIGNAL_MAP[genericArg]) {\n return genericArg;\n }\n }\n\n // Check decorator arguments: @ViewChild(EuiXxxComponent) or @ViewChild('refName')\n if (node.modifiers) {\n for (const modifier of node.modifiers) {\n if (!ts.isDecorator(modifier)) continue;\n if (!ts.isCallExpression(modifier.expression)) continue;\n\n const decoratorName = getDecoratorName(modifier.expression);\n if (!decoratorName) continue;\n if (!['ViewChild', 'ViewChildren', 'ContentChild', 'ContentChildren'].includes(decoratorName)) continue;\n\n const args = modifier.expression.arguments;\n if (args.length > 0) {\n const firstArg = args[0];\n\n // @ViewChild(EuiXxxComponent) — class reference\n if (ts.isIdentifier(firstArg) && EUI_SIGNAL_MAP[firstArg.text]) {\n return firstArg.text;\n }\n\n // @ViewChild('refName') — string template ref, resolve via template\n if (ts.isStringLiteralLike(firstArg)) {\n const refName = firstArg.text;\n const euiClass = templateRefMap.get(refName);\n if (euiClass && EUI_SIGNAL_MAP[euiClass]) {\n return euiClass;\n }\n }\n }\n }\n }\n\n return null;\n}\n\nfunction getDecoratorName(callExpr: ts.CallExpression): string | null {\n if (ts.isIdentifier(callExpr.expression)) {\n return callExpr.expression.text;\n }\n return null;\n}\n\nfunction getTypeAnnotationName(typeNode: ts.TypeNode | undefined): string | null {\n if (!typeNode) return null;\n if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {\n return typeNode.typeName.text;\n }\n return null;\n}\n\n/**\n * Checks if a property access `ref.prop` should be rewritten to `ref.prop()`.\n *\n * We rewrite if:\n * - `ref` is tracked in euiRefs\n * - `prop` is in the signal set for that EUI class\n * - The access is NOT already a call expression (not `ref.prop()` or `ref.prop(args)`)\n * - The access is NOT a write target (not `ref.prop = value`)\n * - The access is NOT a template binding argument (those are unaffected)\n */\nfunction tryRewritePropertyAccess(\n node: ts.PropertyAccessExpression,\n euiRefs: Map<string, ReadonlySet<string>>,\n sourceFile: ts.SourceFile,\n source: string,\n): Edit | null {\n const propName = node.name.text;\n\n // Resolve the reference name — handle `this.ref.prop` and `ref.prop`\n const refName = resolveRefName(node.expression);\n if (!refName) return null;\n\n const signalProps = euiRefs.get(refName);\n if (!signalProps || !signalProps.has(propName)) return null;\n\n // Skip if parent is already a call expression on this property: ref.prop(...)\n if (ts.isCallExpression(node.parent) && node.parent.expression === node) {\n return null;\n }\n\n // Skip if this is the left side of an assignment: ref.prop = value\n if (ts.isBinaryExpression(node.parent) &&\n node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&\n node.parent.left === node) {\n return null;\n }\n\n // Skip if this is a compound assignment: ref.prop += value, ref.prop &&= value, etc.\n if (ts.isBinaryExpression(node.parent) && isAssignmentOperator(node.parent.operatorToken.kind) && node.parent.left === node) {\n return null;\n }\n\n // Skip if this is used with delete: delete ref.prop\n if (ts.isDeleteExpression(node.parent) && node.parent.expression === node) {\n return null;\n }\n\n // Skip if inside a typeof: typeof ref.prop\n if (ts.isTypeOfExpression(node.parent) && node.parent.expression === node) {\n return null;\n }\n\n // Skip property access chains that go deeper: ref.prop.something → ref.prop().something\n // This IS correct — we still want to add ()\n\n // Rewrite: insert () after the property name\n const end = node.name.getEnd();\n return {\n start: end,\n end: end,\n replacement: '()',\n };\n}\n\nfunction isAssignmentOperator(kind: ts.SyntaxKind): boolean {\n return kind >= ts.SyntaxKind.FirstAssignment && kind <= ts.SyntaxKind.LastAssignment;\n}\n\n/**\n * Resolves a reference name from an expression.\n * Handles: `ref`, `this.ref`, `this._ref`\n */\nfunction resolveRefName(expr: ts.Expression): string | null {\n // Direct identifier: ref.prop\n if (ts.isIdentifier(expr)) {\n return expr.text;\n }\n\n // this.ref.prop\n if (ts.isPropertyAccessExpression(expr) && expr.expression.kind === ts.SyntaxKind.ThisKeyword) {\n return expr.name.text;\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Template ref resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a map of template reference variable names (#ref) to EUI class names.\n *\n * Parses the component's template (inline or external) and finds elements like:\n * <eui-alert #myAlert> → myAlert → EuiAlertComponent\n * <input euiAutocomplete #ac> → ac → EuiAutocompleteComponent\n *\n * Returns Map<refName, euiClassName>.\n */\nfunction buildTemplateRefMap(\n sourceFile: ts.SourceFile,\n source: string,\n filePath: string,\n tree: Tree,\n): Map<string, string> {\n const refMap = new Map<string, string>();\n\n // Find @Component decorator\n const componentMeta = findComponentMetadata(sourceFile, source);\n if (!componentMeta) return refMap;\n\n let templateSource: string | null = null;\n\n if (componentMeta.inlineTemplate !== null) {\n templateSource = componentMeta.inlineTemplate;\n } else if (componentMeta.templateUrl) {\n // Resolve templateUrl relative to the TS file\n const dir = filePath.substring(0, filePath.lastIndexOf('/'));\n const templatePath = `${dir}/${componentMeta.templateUrl}`;\n const buffer = tree.read(templatePath);\n if (buffer) {\n templateSource = buffer.toString('utf-8');\n }\n }\n\n if (!templateSource) return refMap;\n\n // Parse the template and collect #refs\n try {\n const parsed = parseTemplate(templateSource, '', { preserveWhitespaces: true });\n collectTemplateRefs(parsed.nodes, refMap);\n } catch {\n // Template parse failure — silently skip (might be malformed or use third-party syntax)\n }\n\n return refMap;\n}\n\n/**\n * Extracts @Component metadata: templateUrl or inline template content.\n */\nfunction findComponentMetadata(\n sourceFile: ts.SourceFile,\n source: string,\n): { templateUrl: string | null; inlineTemplate: string | null } | null {\n let result: { templateUrl: string | null; inlineTemplate: string | null } | null = null;\n\n const visit = (node: ts.Node): void => {\n if (result) return;\n\n if (ts.isDecorator(node) && ts.isCallExpression(node.expression)) {\n const name = getDecoratorName(node.expression);\n if (name !== 'Component') {\n ts.forEachChild(node, visit);\n return;\n }\n\n const args = node.expression.arguments;\n if (args.length === 0 || !ts.isObjectLiteralExpression(args[0])) {\n ts.forEachChild(node, visit);\n return;\n }\n\n const meta: { templateUrl: string | null; inlineTemplate: string | null } = {\n templateUrl: null,\n inlineTemplate: null,\n };\n\n for (const prop of args[0].properties) {\n if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) continue;\n\n if (prop.name.text === 'templateUrl') {\n const init = unwrapExpression(prop.initializer);\n if (ts.isStringLiteralLike(init)) {\n meta.templateUrl = init.text;\n }\n }\n\n if (prop.name.text === 'template') {\n const init = unwrapExpression(prop.initializer);\n if (ts.isStringLiteralLike(init)) {\n meta.inlineTemplate = init.text;\n } else if (ts.isNoSubstitutionTemplateLiteral(init)) {\n meta.inlineTemplate = init.text;\n }\n }\n }\n\n result = meta;\n return;\n }\n\n ts.forEachChild(node, visit);\n };\n\n ts.forEachChild(sourceFile, visit);\n return result;\n}\n\nfunction unwrapExpression(expression: ts.Expression): ts.Expression {\n let current = expression;\n while (ts.isParenthesizedExpression(current)) current = current.expression;\n return current;\n}\n\n/**\n * Recursively walks template AST nodes, collecting #refName → EUI class name mappings.\n *\n * For each element with a template reference (#ref), we try to resolve its EUI class:\n * 1. Match element tag name against EUI_SELECTOR_TO_CLASS (e.g. 'eui-alert' → EuiAlertComponent)\n * 2. Match element attributes against attribute selectors (e.g. [euiAutocomplete] → EuiAutocompleteComponent)\n */\nfunction collectTemplateRefs(nodes: TmplAstNode[], refMap: Map<string, string>): void {\n for (const node of nodes) {\n if (node instanceof TmplAstElement) {\n if (node.references && node.references.length > 0) {\n const euiClass = resolveElementToEuiClass(node);\n if (euiClass) {\n for (const ref of node.references) {\n refMap.set(ref.name, euiClass);\n }\n }\n }\n collectTemplateRefs(node.children, refMap);\n }\n }\n}\n\n/**\n * Given a template element, resolves it to an EUI class name by checking:\n * 1. Tag name: <eui-alert> → look up 'eui-alert' in EUI_SELECTOR_TO_CLASS\n * 2. Attribute selectors: <input euiAutocomplete> → look up 'input[euiAutocomplete]' or '[euiAutocomplete]'\n */\nfunction resolveElementToEuiClass(element: TmplAstElement): string | null {\n const tag = element.name;\n\n // Direct tag match: eui-alert, eui-dialog, etc.\n if (EUI_SELECTOR_TO_CLASS[tag]) {\n return EUI_SELECTOR_TO_CLASS[tag];\n }\n\n // Attribute selector match: check all attributes against known patterns\n const allAttrNames = [\n ...element.attributes.map((a) => a.name),\n ...element.inputs.map((i) => i.name),\n ];\n\n for (const attrName of allAttrNames) {\n // Try tag[attr] pattern first (e.g. 'button[euiButton]', 'input[euiAutocomplete]')\n const tagAttrSelector = `${tag}[${attrName}]`;\n if (EUI_SELECTOR_TO_CLASS[tagAttrSelector]) {\n return EUI_SELECTOR_TO_CLASS[tagAttrSelector];\n }\n\n // Try [attr] pattern (e.g. '[euiTooltip]', '[euiLoading]')\n const attrSelector = `[${attrName}]`;\n if (EUI_SELECTOR_TO_CLASS[attrSelector]) {\n return EUI_SELECTOR_TO_CLASS[attrSelector];\n }\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Edit helpers\n// ---------------------------------------------------------------------------\n\nfunction applyEdits(source: string, edits: Edit[]): string {\n const unique = deduplicateEdits(edits);\n let result = source;\n for (const edit of unique.sort((a, b) => b.start - a.start)) {\n result = result.slice(0, edit.start) + edit.replacement + result.slice(edit.end);\n }\n return result;\n}\n\nfunction deduplicateEdits(edits: Edit[]): Edit[] {\n const seen = new Map<string, Edit>();\n for (const edit of edits) {\n const key = `${edit.start}:${edit.end}`;\n seen.set(key, edit);\n }\n return Array.from(seen.values());\n}\n\nfunction visitDir(dir: DirEntry, callback: (path: string) => void): void {\n for (const file of dir.subfiles) {\n if (file.endsWith('.d.ts')) continue;\n if (!file.endsWith('.ts')) continue;\n callback(`${dir.path}/${file}`);\n }\n for (const sub of dir.subdirs) {\n if (sub === 'node_modules' || sub === 'dist') continue;\n visitDir(dir.dir(sub), callback);\n }\n}\n",
|
|
976
|
+
"sourceCode": "import { DirEntry, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';\nimport * as ts from 'typescript';\nimport { logDryRun, logDryRunNote } from '../utils/dry-run';\n\ninterface Schema {\n path?: string;\n dryRun?: boolean;\n}\n\ninterface Edit {\n start: number;\n end: number;\n replacement: string;\n}\n\nconst OLD_CLASS = 'EuiTooltipConfig';\nconst NEW_INTERFACE = 'EuiTooltipInterface';\n\nexport function migrateEuiTooltip(options: Schema = {}): Rule {\n return (tree: Tree, context: SchematicContext) => {\n const scanPath = options.path ? '/' + options.path.replace(/^\\.?\\//, '').replace(/\\/$/, '') : '';\n let fileCount = 0;\n\n visitDir(tree.getDir(scanPath || '/'), (path) => {\n if (!path.endsWith('.ts')) return;\n\n const buffer = tree.read(path);\n if (!buffer) return;\n\n const original = buffer.toString('utf-8');\n if (!original.includes(OLD_CLASS)) return;\n\n const result = migrateTypeScript(original, path, context);\n\n if (result !== original) {\n if (options.dryRun) {\n logDryRun(context, `Would migrate EuiTooltipConfig → EuiTooltipInterface in ${path}`);\n } else {\n tree.overwrite(path, result);\n }\n fileCount++;\n }\n });\n\n context.logger.info(`Migrated EuiTooltipConfig → EuiTooltipInterface in ${fileCount} file(s).`);\n if (options.dryRun) {\n logDryRunNote(context);\n }\n return tree;\n };\n}\n\nfunction migrateTypeScript(source: string, filePath: string, context: SchematicContext): string {\n const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);\n const edits: Edit[] = [];\n\n // Track if EuiTooltipInterface is already imported\n let hasInterfaceImport = false;\n let classImportDecl: ts.ImportDeclaration | null = null;\n let classImportModuleSpecifier: string | null = null;\n\n // First pass: analyze imports\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) continue;\n const namedBindings = stmt.importClause?.namedBindings;\n if (!namedBindings || !ts.isNamedImports(namedBindings)) continue;\n\n for (const specifier of namedBindings.elements) {\n if (specifier.name.text === NEW_INTERFACE) {\n hasInterfaceImport = true;\n }\n if (specifier.name.text === OLD_CLASS) {\n classImportDecl = stmt;\n classImportModuleSpecifier = (stmt.moduleSpecifier as ts.StringLiteral).text;\n }\n }\n }\n\n // Second pass: handle import declarations\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) continue;\n const namedBindings = stmt.importClause?.namedBindings;\n if (!namedBindings || !ts.isNamedImports(namedBindings)) continue;\n\n const specifiers = namedBindings.elements;\n const classSpecifier = specifiers.find((s) => s.name.text === OLD_CLASS);\n if (!classSpecifier) continue;\n\n if (hasInterfaceImport) {\n // EuiTooltipInterface is already imported elsewhere → remove EuiTooltipConfig from this import\n removeImportSpecifier(namedBindings, classSpecifier, sourceFile, edits);\n } else {\n // Rename EuiTooltipConfig → EuiTooltipInterface in the import\n edits.push({\n start: classSpecifier.name.getStart(sourceFile),\n end: classSpecifier.name.getEnd(),\n replacement: NEW_INTERFACE,\n });\n hasInterfaceImport = true;\n }\n }\n\n // Third pass: replace `new EuiTooltipConfig(...)` → spread/cast to interface\n const visitNewExpressions = (node: ts.Node): void => {\n if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === OLD_CLASS) {\n const args = node.arguments;\n if (args && args.length === 1) {\n const arg = args[0];\n // `new EuiTooltipConfig({ ... })` → `{ ... } as EuiTooltipInterface`\n // But if the argument is just a variable, we keep it: `varName as EuiTooltipInterface`\n const argText = source.slice(arg.getStart(sourceFile), arg.getEnd());\n\n if (ts.isObjectLiteralExpression(arg)) {\n // Inline object: `new EuiTooltipConfig({ x: 1 })` → `{ x: 1 }`\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: argText,\n });\n } else {\n // Variable or expression: `new EuiTooltipConfig(opts)` → `opts`\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: argText,\n });\n }\n } else if (!args || args.length === 0) {\n // `new EuiTooltipConfig()` → `{} as EuiTooltipInterface`\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: `{} as ${NEW_INTERFACE}`,\n });\n }\n return; // don't recurse into children we've already replaced\n }\n ts.forEachChild(node, visitNewExpressions);\n };\n\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) {\n visitNewExpressions(stmt);\n }\n }\n\n // Fourth pass: rename all remaining identifier references (type annotations, etc.)\n const visitRefs = (node: ts.Node): void => {\n if (ts.isImportDeclaration(node)) return;\n // Skip nodes we already covered in new expressions\n if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === OLD_CLASS) return;\n\n if (ts.isIdentifier(node) && node.text === OLD_CLASS) {\n // Ensure this is not part of an import declaration\n if (!isPartOfImport(node)) {\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: NEW_INTERFACE,\n });\n }\n }\n ts.forEachChild(node, visitRefs);\n };\n\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) {\n visitRefs(stmt);\n }\n }\n\n return applyEdits(source, edits);\n}\n\nfunction isPartOfImport(node: ts.Node): boolean {\n let current: ts.Node | undefined = node.parent;\n while (current) {\n if (ts.isImportDeclaration(current)) return true;\n current = current.parent;\n }\n return false;\n}\n\nfunction removeImportSpecifier(\n namedImports: ts.NamedImports,\n specifier: ts.ImportSpecifier,\n sourceFile: ts.SourceFile,\n edits: Edit[],\n): void {\n const elements = namedImports.elements;\n if (elements.length === 1) {\n // Remove the entire import declaration\n const importDecl = namedImports.parent.parent;\n let end = importDecl.getEnd();\n // Also remove trailing newline if present\n const fullText = sourceFile.getFullText();\n if (fullText[end] === '\\n') end++;\n edits.push({\n start: importDecl.getStart(sourceFile),\n end,\n replacement: '',\n });\n } else {\n // Remove just this specifier with surrounding comma/whitespace\n const idx = elements.indexOf(specifier);\n let start: number;\n let end: number;\n if (idx < elements.length - 1) {\n // Not the last → remove from this specifier start to next specifier start\n start = specifier.getStart(sourceFile);\n end = elements[idx + 1].getStart(sourceFile);\n } else {\n // Last element → remove from previous element end to this end\n start = elements[idx - 1].getEnd();\n end = specifier.getEnd();\n }\n edits.push({ start, end, replacement: '' });\n }\n}\n\nfunction applyEdits(source: string, edits: Edit[]): string {\n const unique = deduplicateEdits(edits);\n let result = source;\n for (const edit of unique.sort((a, b) => b.start - a.start)) {\n result = result.slice(0, edit.start) + edit.replacement + result.slice(edit.end);\n }\n return result;\n}\n\nfunction deduplicateEdits(edits: Edit[]): Edit[] {\n const seen = new Map<string, Edit>();\n for (const edit of edits) {\n const key = `${edit.start}:${edit.end}`;\n seen.set(key, edit);\n }\n return Array.from(seen.values());\n}\n\nfunction visitDir(dir: DirEntry, callback: (path: string) => void): void {\n for (const file of dir.subfiles) {\n if (file.endsWith('.d.ts')) continue;\n if (!file.endsWith('.ts')) continue;\n callback(`${dir.path}/${file}`);\n }\n for (const sub of dir.subdirs) {\n if (sub === 'node_modules' || sub === 'dist') continue;\n visitDir(dir.dir(sub), callback);\n }\n}\n",
|
|
977
977
|
"displayName": "Edit",
|
|
978
978
|
"properties": [
|
|
979
979
|
{
|
|
@@ -985,7 +985,7 @@
|
|
|
985
985
|
"indexKey": "",
|
|
986
986
|
"optional": false,
|
|
987
987
|
"description": "",
|
|
988
|
-
"line":
|
|
988
|
+
"line": 12,
|
|
989
989
|
"rawdescription": "\n"
|
|
990
990
|
},
|
|
991
991
|
{
|
|
@@ -997,7 +997,7 @@
|
|
|
997
997
|
"indexKey": "",
|
|
998
998
|
"optional": false,
|
|
999
999
|
"description": "",
|
|
1000
|
-
"line":
|
|
1000
|
+
"line": 13,
|
|
1001
1001
|
"rawdescription": "\n"
|
|
1002
1002
|
},
|
|
1003
1003
|
{
|
|
@@ -1009,7 +1009,7 @@
|
|
|
1009
1009
|
"indexKey": "",
|
|
1010
1010
|
"optional": false,
|
|
1011
1011
|
"description": "",
|
|
1012
|
-
"line":
|
|
1012
|
+
"line": 11,
|
|
1013
1013
|
"rawdescription": "\n"
|
|
1014
1014
|
}
|
|
1015
1015
|
],
|
|
@@ -1027,12 +1027,12 @@
|
|
|
1027
1027
|
},
|
|
1028
1028
|
{
|
|
1029
1029
|
"name": "Edit",
|
|
1030
|
-
"id": "interface-Edit-
|
|
1031
|
-
"file": "packages/core/schematics/migrate-
|
|
1030
|
+
"id": "interface-Edit-cac2ac952d8245647e8a48b450e8f127a36bcffb394cf2cd81fe9c0810ed02c19e5e1629dbf29a68a5c6f675aed99570e9ac04ae470e13315e20c92cc0c6c453-4",
|
|
1031
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
1032
1032
|
"deprecated": false,
|
|
1033
1033
|
"deprecationMessage": "",
|
|
1034
1034
|
"type": "interface",
|
|
1035
|
-
"sourceCode": "import { DirEntry, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';\nimport * as ts from 'typescript';\nimport { logDryRun, logDryRunNote } from '../utils/dry-run';\n\ninterface Schema {\n path?: string;\n dryRun?: boolean;\n}\n\ninterface Edit {\n start: number;\n end: number;\n replacement: string;\n}\n\nconst OLD_CLASS = 'EuiTooltipConfig';\nconst NEW_INTERFACE = 'EuiTooltipInterface';\n\nexport function migrateEuiTooltip(options: Schema = {}): Rule {\n return (tree: Tree, context: SchematicContext) => {\n const scanPath = options.path ? '/' + options.path.replace(/^\\.?\\//, '').replace(/\\/$/, '') : '';\n let fileCount = 0;\n\n visitDir(tree.getDir(scanPath || '/'), (path) => {\n if (!path.endsWith('.ts')) return;\n\n const buffer = tree.read(path);\n if (!buffer) return;\n\n const original = buffer.toString('utf-8');\n if (!original.includes(OLD_CLASS)) return;\n\n const result = migrateTypeScript(original, path, context);\n\n if (result !== original) {\n if (options.dryRun) {\n logDryRun(context, `Would migrate EuiTooltipConfig → EuiTooltipInterface in ${path}`);\n } else {\n tree.overwrite(path, result);\n }\n fileCount++;\n }\n });\n\n context.logger.info(`Migrated EuiTooltipConfig → EuiTooltipInterface in ${fileCount} file(s).`);\n if (options.dryRun) {\n logDryRunNote(context);\n }\n return tree;\n };\n}\n\nfunction migrateTypeScript(source: string, filePath: string, context: SchematicContext): string {\n const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);\n const edits: Edit[] = [];\n\n // Track if EuiTooltipInterface is already imported\n let hasInterfaceImport = false;\n let classImportDecl: ts.ImportDeclaration | null = null;\n let classImportModuleSpecifier: string | null = null;\n\n // First pass: analyze imports\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) continue;\n const namedBindings = stmt.importClause?.namedBindings;\n if (!namedBindings || !ts.isNamedImports(namedBindings)) continue;\n\n for (const specifier of namedBindings.elements) {\n if (specifier.name.text === NEW_INTERFACE) {\n hasInterfaceImport = true;\n }\n if (specifier.name.text === OLD_CLASS) {\n classImportDecl = stmt;\n classImportModuleSpecifier = (stmt.moduleSpecifier as ts.StringLiteral).text;\n }\n }\n }\n\n // Second pass: handle import declarations\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) continue;\n const namedBindings = stmt.importClause?.namedBindings;\n if (!namedBindings || !ts.isNamedImports(namedBindings)) continue;\n\n const specifiers = namedBindings.elements;\n const classSpecifier = specifiers.find((s) => s.name.text === OLD_CLASS);\n if (!classSpecifier) continue;\n\n if (hasInterfaceImport) {\n // EuiTooltipInterface is already imported elsewhere → remove EuiTooltipConfig from this import\n removeImportSpecifier(namedBindings, classSpecifier, sourceFile, edits);\n } else {\n // Rename EuiTooltipConfig → EuiTooltipInterface in the import\n edits.push({\n start: classSpecifier.name.getStart(sourceFile),\n end: classSpecifier.name.getEnd(),\n replacement: NEW_INTERFACE,\n });\n hasInterfaceImport = true;\n }\n }\n\n // Third pass: replace `new EuiTooltipConfig(...)` → spread/cast to interface\n const visitNewExpressions = (node: ts.Node): void => {\n if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === OLD_CLASS) {\n const args = node.arguments;\n if (args && args.length === 1) {\n const arg = args[0];\n // `new EuiTooltipConfig({ ... })` → `{ ... } as EuiTooltipInterface`\n // But if the argument is just a variable, we keep it: `varName as EuiTooltipInterface`\n const argText = source.slice(arg.getStart(sourceFile), arg.getEnd());\n\n if (ts.isObjectLiteralExpression(arg)) {\n // Inline object: `new EuiTooltipConfig({ x: 1 })` → `{ x: 1 }`\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: argText,\n });\n } else {\n // Variable or expression: `new EuiTooltipConfig(opts)` → `opts`\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: argText,\n });\n }\n } else if (!args || args.length === 0) {\n // `new EuiTooltipConfig()` → `{} as EuiTooltipInterface`\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: `{} as ${NEW_INTERFACE}`,\n });\n }\n return; // don't recurse into children we've already replaced\n }\n ts.forEachChild(node, visitNewExpressions);\n };\n\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) {\n visitNewExpressions(stmt);\n }\n }\n\n // Fourth pass: rename all remaining identifier references (type annotations, etc.)\n const visitRefs = (node: ts.Node): void => {\n if (ts.isImportDeclaration(node)) return;\n // Skip nodes we already covered in new expressions\n if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === OLD_CLASS) return;\n\n if (ts.isIdentifier(node) && node.text === OLD_CLASS) {\n // Ensure this is not part of an import declaration\n if (!isPartOfImport(node)) {\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: NEW_INTERFACE,\n });\n }\n }\n ts.forEachChild(node, visitRefs);\n };\n\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) {\n visitRefs(stmt);\n }\n }\n\n return applyEdits(source, edits);\n}\n\nfunction isPartOfImport(node: ts.Node): boolean {\n let current: ts.Node | undefined = node.parent;\n while (current) {\n if (ts.isImportDeclaration(current)) return true;\n current = current.parent;\n }\n return false;\n}\n\nfunction removeImportSpecifier(\n namedImports: ts.NamedImports,\n specifier: ts.ImportSpecifier,\n sourceFile: ts.SourceFile,\n edits: Edit[],\n): void {\n const elements = namedImports.elements;\n if (elements.length === 1) {\n // Remove the entire import declaration\n const importDecl = namedImports.parent.parent;\n let end = importDecl.getEnd();\n // Also remove trailing newline if present\n const fullText = sourceFile.getFullText();\n if (fullText[end] === '\\n') end++;\n edits.push({\n start: importDecl.getStart(sourceFile),\n end,\n replacement: '',\n });\n } else {\n // Remove just this specifier with surrounding comma/whitespace\n const idx = elements.indexOf(specifier);\n let start: number;\n let end: number;\n if (idx < elements.length - 1) {\n // Not the last → remove from this specifier start to next specifier start\n start = specifier.getStart(sourceFile);\n end = elements[idx + 1].getStart(sourceFile);\n } else {\n // Last element → remove from previous element end to this end\n start = elements[idx - 1].getEnd();\n end = specifier.getEnd();\n }\n edits.push({ start, end, replacement: '' });\n }\n}\n\nfunction applyEdits(source: string, edits: Edit[]): string {\n const unique = deduplicateEdits(edits);\n let result = source;\n for (const edit of unique.sort((a, b) => b.start - a.start)) {\n result = result.slice(0, edit.start) + edit.replacement + result.slice(edit.end);\n }\n return result;\n}\n\nfunction deduplicateEdits(edits: Edit[]): Edit[] {\n const seen = new Map<string, Edit>();\n for (const edit of edits) {\n const key = `${edit.start}:${edit.end}`;\n seen.set(key, edit);\n }\n return Array.from(seen.values());\n}\n\nfunction visitDir(dir: DirEntry, callback: (path: string) => void): void {\n for (const file of dir.subfiles) {\n if (file.endsWith('.d.ts')) continue;\n if (!file.endsWith('.ts')) continue;\n callback(`${dir.path}/${file}`);\n }\n for (const sub of dir.subdirs) {\n if (sub === 'node_modules' || sub === 'dist') continue;\n visitDir(dir.dir(sub), callback);\n }\n}\n",
|
|
1035
|
+
"sourceCode": "import { DirEntry, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';\nimport { parseTemplate, TmplAstElement, TmplAstNode } from '@angular/compiler';\nimport * as ts from 'typescript';\nimport { logDryRun, logDryRunNote } from '../utils/dry-run';\nimport { EUI_SIGNAL_MAP, EUI_SELECTOR_TO_CLASS } from './signal-map';\n\ninterface Schema {\n path?: string;\n dryRun?: boolean;\n}\n\ninterface Edit {\n start: number;\n end: number;\n replacement: string;\n}\n\n/**\n * Migrates consumer code that accesses EUI component instances programmatically.\n *\n * When EUI migrated @Input() → input(), @ViewChild() → viewChild(), and\n * @ContentChild() → contentChild(), properties that were plain class fields\n * became signal getters. Consumers who access them via @ViewChild need to\n * change `comp.prop` to `comp.prop()`.\n *\n * This schematic:\n * 1. Finds @ViewChild / @ContentChild fields typed as EUI component classes\n * 2. Resolves untyped @ViewChild('refName') by reading the component template\n * and matching #refName to an EUI element tag/selector\n * 3. Tracks which local variable names hold EUI component references (incl. aliases)\n * 4. Rewrites property accesses on those references: `ref.prop` → `ref.prop()`\n * (only for properties listed in the signal map)\n * 5. Does NOT touch template bindings — those are unaffected by signal migrations\n */\nexport function migrateToSignals(options: Schema = {}): Rule {\n return (tree: Tree, context: SchematicContext) => {\n const scanPath = options.path ? '/' + options.path.replace(/^\\.?\\//, '').replace(/\\/$/, '') : '';\n let fileCount = 0;\n let propCount = 0;\n\n visitDir(tree.getDir(scanPath || '/'), (path) => {\n if (!path.endsWith('.ts')) return;\n // Skip spec files — test code may use different patterns\n if (path.endsWith('.spec.ts')) return;\n\n const buffer = tree.read(path);\n if (!buffer) return;\n\n const original = buffer.toString('utf-8');\n\n // Fast bail: file must mention at least one EUI class name or have\n // ViewChild/ContentChild with string refs (which might reference EUI elements)\n if (!hasEuiReference(original) && !hasStringRefQuery(original)) return;\n\n const result = migrateTypeScript(original, path, tree, context); if (result.source !== original) {\n if (options.dryRun) {\n logDryRun(context, `Would migrate ${result.editCount} signal property access(es) in ${path}`);\n } else {\n tree.overwrite(path, result.source);\n }\n fileCount++;\n propCount += result.editCount;\n }\n });\n\n context.logger.info(`Migrated ${propCount} property access(es) to signal getters in ${fileCount} file(s).`);\n if (options.dryRun) {\n logDryRunNote(context);\n }\n return tree;\n };\n}\n\n/** Quick check: does the file reference any EUI class name from the signal map? */\nfunction hasEuiReference(source: string): boolean {\n for (const className of Object.keys(EUI_SIGNAL_MAP)) {\n if (source.includes(className)) return true;\n }\n return false;\n}\n\n/** Quick check: does the file have @ViewChild('...') or @ContentChild('...') with string refs? */\nfunction hasStringRefQuery(source: string): boolean {\n return /(@ViewChild|@ContentChild|@ViewChildren|@ContentChildren)\\s*\\(\\s*['\"]/.test(source);\n}\n\ninterface MigrateResult {\n source: string;\n editCount: number;\n}\n\nfunction migrateTypeScript(source: string, filePath: string, tree: Tree, context: SchematicContext): MigrateResult {\n const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);\n const edits: Edit[] = [];\n\n // Phase 0: Build template ref map (#refName → EUI class name) from the component's template\n const templateRefMap = buildTemplateRefMap(sourceFile, source, filePath, tree);\n\n // Phase 1: Collect all variables/properties typed as EUI classes\n const euiRefs = collectEuiReferences(sourceFile, source, templateRefMap);\n\n if (euiRefs.size === 0) {\n return { source, editCount: 0 };\n }\n\n // Phase 2: Find property accesses on those references and rewrite signal props\n const visitNode = (node: ts.Node): void => {\n // Match: ref.prop (PropertyAccessExpression)\n if (ts.isPropertyAccessExpression(node)) {\n const rewrite = tryRewritePropertyAccess(node, euiRefs, sourceFile, source);\n if (rewrite) {\n edits.push(rewrite);\n }\n }\n ts.forEachChild(node, visitNode);\n };\n\n ts.forEachChild(sourceFile, visitNode);\n\n if (edits.length === 0) {\n return { source, editCount: 0 };\n }\n\n return {\n source: applyEdits(source, edits),\n editCount: edits.length,\n };\n}\n\n/**\n * Scans the source file for class properties or variables typed as EUI component classes.\n * Returns a map: localName → Set of signal property names.\n *\n * Handles:\n * @ViewChild(EuiXxxComponent) name: EuiXxxComponent;\n * @ViewChild(EuiXxxComponent) name!: EuiXxxComponent;\n * @ViewChild('ref') name: EuiXxxComponent;\n * @ContentChild(EuiXxxComponent) name: EuiXxxComponent;\n * @ViewChild('ref') name: ElementRef; (resolved via template #ref → EUI selector)\n * someVar: EuiXxxComponent; (typed class field, even without decorator)\n * const alias = this.viewChildRef; (alias tracking)\n */\nfunction collectEuiReferences(\n sourceFile: ts.SourceFile,\n source: string,\n templateRefMap: Map<string, string>,\n): Map<string, ReadonlySet<string>> {\n const refs = new Map<string, ReadonlySet<string>>();\n\n // Pass 1: collect directly-typed references\n const visitDirect = (node: ts.Node): void => {\n // Class property declarations\n if (ts.isPropertyDeclaration(node) && node.name && ts.isIdentifier(node.name)) {\n const euiClass = resolveEuiClassName(node, sourceFile, templateRefMap);\n if (euiClass && EUI_SIGNAL_MAP[euiClass]) {\n refs.set(node.name.text, EUI_SIGNAL_MAP[euiClass]);\n }\n }\n\n // Variable declarations (const x: EuiXxxComponent = ...)\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {\n const typeName = getTypeAnnotationName(node.type);\n if (typeName && EUI_SIGNAL_MAP[typeName]) {\n refs.set(node.name.text, EUI_SIGNAL_MAP[typeName]);\n }\n }\n\n // Parameter declarations (method(ref: EuiXxxComponent))\n if (ts.isParameter(node) && ts.isIdentifier(node.name)) {\n const typeName = getTypeAnnotationName(node.type);\n if (typeName && EUI_SIGNAL_MAP[typeName]) {\n refs.set(node.name.text, EUI_SIGNAL_MAP[typeName]);\n }\n }\n\n ts.forEachChild(node, visitDirect);\n };\n\n ts.forEachChild(sourceFile, visitDirect);\n\n // Pass 2: track aliases — variables assigned from already-tracked references\n // e.g. const alias = this.viewChildRef; or const alias = someParam;\n const visitAlias = (node: ts.Node): void => {\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {\n // Skip if already tracked (has explicit type annotation)\n if (refs.has(node.name.text)) {\n ts.forEachChild(node, visitAlias);\n return;\n }\n\n const aliasedRef = resolveRefName(node.initializer);\n if (aliasedRef && refs.has(aliasedRef)) {\n refs.set(node.name.text, refs.get(aliasedRef)!);\n }\n }\n\n ts.forEachChild(node, visitAlias);\n };\n\n ts.forEachChild(sourceFile, visitAlias);\n return refs;\n}\n\n/**\n * Resolves the EUI class name from a property declaration.\n * Checks: type annotation → decorator class argument → template ref map.\n */\nfunction resolveEuiClassName(\n node: ts.PropertyDeclaration,\n sourceFile: ts.SourceFile,\n templateRefMap: Map<string, string>,\n): string | null {\n // Check type annotation first\n const typeName = getTypeAnnotationName(node.type);\n if (typeName && EUI_SIGNAL_MAP[typeName]) {\n return typeName;\n }\n\n // Check generic type argument: ElementRef<EuiXxxComponent>\n if (node.type && ts.isTypeReferenceNode(node.type) && node.type.typeArguments?.length) {\n const genericArg = getTypeAnnotationName(node.type.typeArguments[0]);\n if (genericArg && EUI_SIGNAL_MAP[genericArg]) {\n return genericArg;\n }\n }\n\n // Check decorator arguments: @ViewChild(EuiXxxComponent) or @ViewChild('refName')\n if (node.modifiers) {\n for (const modifier of node.modifiers) {\n if (!ts.isDecorator(modifier)) continue;\n if (!ts.isCallExpression(modifier.expression)) continue;\n\n const decoratorName = getDecoratorName(modifier.expression);\n if (!decoratorName) continue;\n if (!['ViewChild', 'ViewChildren', 'ContentChild', 'ContentChildren'].includes(decoratorName)) continue;\n\n const args = modifier.expression.arguments;\n if (args.length > 0) {\n const firstArg = args[0];\n\n // @ViewChild(EuiXxxComponent) — class reference\n if (ts.isIdentifier(firstArg) && EUI_SIGNAL_MAP[firstArg.text]) {\n return firstArg.text;\n }\n\n // @ViewChild('refName') — string template ref, resolve via template\n if (ts.isStringLiteralLike(firstArg)) {\n const refName = firstArg.text;\n const euiClass = templateRefMap.get(refName);\n if (euiClass && EUI_SIGNAL_MAP[euiClass]) {\n return euiClass;\n }\n }\n }\n }\n }\n\n return null;\n}\n\nfunction getDecoratorName(callExpr: ts.CallExpression): string | null {\n if (ts.isIdentifier(callExpr.expression)) {\n return callExpr.expression.text;\n }\n return null;\n}\n\nfunction getTypeAnnotationName(typeNode: ts.TypeNode | undefined): string | null {\n if (!typeNode) return null;\n if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {\n return typeNode.typeName.text;\n }\n return null;\n}\n\n/**\n * Checks if a property access `ref.prop` should be rewritten to `ref.prop()`.\n *\n * We rewrite if:\n * - `ref` is tracked in euiRefs\n * - `prop` is in the signal set for that EUI class\n * - The access is NOT already a call expression (not `ref.prop()` or `ref.prop(args)`)\n * - The access is NOT a write target (not `ref.prop = value`)\n * - The access is NOT a template binding argument (those are unaffected)\n */\nfunction tryRewritePropertyAccess(\n node: ts.PropertyAccessExpression,\n euiRefs: Map<string, ReadonlySet<string>>,\n sourceFile: ts.SourceFile,\n source: string,\n): Edit | null {\n const propName = node.name.text;\n\n // Resolve the reference name — handle `this.ref.prop` and `ref.prop`\n const refName = resolveRefName(node.expression);\n if (!refName) return null;\n\n const signalProps = euiRefs.get(refName);\n if (!signalProps || !signalProps.has(propName)) return null;\n\n // Skip if parent is already a call expression on this property: ref.prop(...)\n if (ts.isCallExpression(node.parent) && node.parent.expression === node) {\n return null;\n }\n\n // Skip if this is the left side of an assignment: ref.prop = value\n if (ts.isBinaryExpression(node.parent) &&\n node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&\n node.parent.left === node) {\n return null;\n }\n\n // Skip if this is a compound assignment: ref.prop += value, ref.prop &&= value, etc.\n if (ts.isBinaryExpression(node.parent) && isAssignmentOperator(node.parent.operatorToken.kind) && node.parent.left === node) {\n return null;\n }\n\n // Skip if this is used with delete: delete ref.prop\n if (ts.isDeleteExpression(node.parent) && node.parent.expression === node) {\n return null;\n }\n\n // Skip if inside a typeof: typeof ref.prop\n if (ts.isTypeOfExpression(node.parent) && node.parent.expression === node) {\n return null;\n }\n\n // Skip property access chains that go deeper: ref.prop.something → ref.prop().something\n // This IS correct — we still want to add ()\n\n // Rewrite: insert () after the property name\n const end = node.name.getEnd();\n return {\n start: end,\n end: end,\n replacement: '()',\n };\n}\n\nfunction isAssignmentOperator(kind: ts.SyntaxKind): boolean {\n return kind >= ts.SyntaxKind.FirstAssignment && kind <= ts.SyntaxKind.LastAssignment;\n}\n\n/**\n * Resolves a reference name from an expression.\n * Handles: `ref`, `this.ref`, `this._ref`\n */\nfunction resolveRefName(expr: ts.Expression): string | null {\n // Direct identifier: ref.prop\n if (ts.isIdentifier(expr)) {\n return expr.text;\n }\n\n // this.ref.prop\n if (ts.isPropertyAccessExpression(expr) && expr.expression.kind === ts.SyntaxKind.ThisKeyword) {\n return expr.name.text;\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Template ref resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a map of template reference variable names (#ref) to EUI class names.\n *\n * Parses the component's template (inline or external) and finds elements like:\n * <eui-alert #myAlert> → myAlert → EuiAlertComponent\n * <input euiAutocomplete #ac> → ac → EuiAutocompleteComponent\n *\n * Returns Map<refName, euiClassName>.\n */\nfunction buildTemplateRefMap(\n sourceFile: ts.SourceFile,\n source: string,\n filePath: string,\n tree: Tree,\n): Map<string, string> {\n const refMap = new Map<string, string>();\n\n // Find @Component decorator\n const componentMeta = findComponentMetadata(sourceFile, source);\n if (!componentMeta) return refMap;\n\n let templateSource: string | null = null;\n\n if (componentMeta.inlineTemplate !== null) {\n templateSource = componentMeta.inlineTemplate;\n } else if (componentMeta.templateUrl) {\n // Resolve templateUrl relative to the TS file\n const dir = filePath.substring(0, filePath.lastIndexOf('/'));\n const templatePath = `${dir}/${componentMeta.templateUrl}`;\n const buffer = tree.read(templatePath);\n if (buffer) {\n templateSource = buffer.toString('utf-8');\n }\n }\n\n if (!templateSource) return refMap;\n\n // Parse the template and collect #refs\n try {\n const parsed = parseTemplate(templateSource, '', { preserveWhitespaces: true });\n collectTemplateRefs(parsed.nodes, refMap);\n } catch {\n // Template parse failure — silently skip (might be malformed or use third-party syntax)\n }\n\n return refMap;\n}\n\n/**\n * Extracts @Component metadata: templateUrl or inline template content.\n */\nfunction findComponentMetadata(\n sourceFile: ts.SourceFile,\n source: string,\n): { templateUrl: string | null; inlineTemplate: string | null } | null {\n let result: { templateUrl: string | null; inlineTemplate: string | null } | null = null;\n\n const visit = (node: ts.Node): void => {\n if (result) return;\n\n if (ts.isDecorator(node) && ts.isCallExpression(node.expression)) {\n const name = getDecoratorName(node.expression);\n if (name !== 'Component') {\n ts.forEachChild(node, visit);\n return;\n }\n\n const args = node.expression.arguments;\n if (args.length === 0 || !ts.isObjectLiteralExpression(args[0])) {\n ts.forEachChild(node, visit);\n return;\n }\n\n const meta: { templateUrl: string | null; inlineTemplate: string | null } = {\n templateUrl: null,\n inlineTemplate: null,\n };\n\n for (const prop of args[0].properties) {\n if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) continue;\n\n if (prop.name.text === 'templateUrl') {\n const init = unwrapExpression(prop.initializer);\n if (ts.isStringLiteralLike(init)) {\n meta.templateUrl = init.text;\n }\n }\n\n if (prop.name.text === 'template') {\n const init = unwrapExpression(prop.initializer);\n if (ts.isStringLiteralLike(init)) {\n meta.inlineTemplate = init.text;\n } else if (ts.isNoSubstitutionTemplateLiteral(init)) {\n meta.inlineTemplate = init.text;\n }\n }\n }\n\n result = meta;\n return;\n }\n\n ts.forEachChild(node, visit);\n };\n\n ts.forEachChild(sourceFile, visit);\n return result;\n}\n\nfunction unwrapExpression(expression: ts.Expression): ts.Expression {\n let current = expression;\n while (ts.isParenthesizedExpression(current)) current = current.expression;\n return current;\n}\n\n/**\n * Recursively walks template AST nodes, collecting #refName → EUI class name mappings.\n *\n * For each element with a template reference (#ref), we try to resolve its EUI class:\n * 1. Match element tag name against EUI_SELECTOR_TO_CLASS (e.g. 'eui-alert' → EuiAlertComponent)\n * 2. Match element attributes against attribute selectors (e.g. [euiAutocomplete] → EuiAutocompleteComponent)\n */\nfunction collectTemplateRefs(nodes: TmplAstNode[], refMap: Map<string, string>): void {\n for (const node of nodes) {\n if (node instanceof TmplAstElement) {\n if (node.references && node.references.length > 0) {\n const euiClass = resolveElementToEuiClass(node);\n if (euiClass) {\n for (const ref of node.references) {\n refMap.set(ref.name, euiClass);\n }\n }\n }\n collectTemplateRefs(node.children, refMap);\n }\n }\n}\n\n/**\n * Given a template element, resolves it to an EUI class name by checking:\n * 1. Tag name: <eui-alert> → look up 'eui-alert' in EUI_SELECTOR_TO_CLASS\n * 2. Attribute selectors: <input euiAutocomplete> → look up 'input[euiAutocomplete]' or '[euiAutocomplete]'\n */\nfunction resolveElementToEuiClass(element: TmplAstElement): string | null {\n const tag = element.name;\n\n // Direct tag match: eui-alert, eui-dialog, etc.\n if (EUI_SELECTOR_TO_CLASS[tag]) {\n return EUI_SELECTOR_TO_CLASS[tag];\n }\n\n // Attribute selector match: check all attributes against known patterns\n const allAttrNames = [\n ...element.attributes.map((a) => a.name),\n ...element.inputs.map((i) => i.name),\n ];\n\n for (const attrName of allAttrNames) {\n // Try tag[attr] pattern first (e.g. 'button[euiButton]', 'input[euiAutocomplete]')\n const tagAttrSelector = `${tag}[${attrName}]`;\n if (EUI_SELECTOR_TO_CLASS[tagAttrSelector]) {\n return EUI_SELECTOR_TO_CLASS[tagAttrSelector];\n }\n\n // Try [attr] pattern (e.g. '[euiTooltip]', '[euiLoading]')\n const attrSelector = `[${attrName}]`;\n if (EUI_SELECTOR_TO_CLASS[attrSelector]) {\n return EUI_SELECTOR_TO_CLASS[attrSelector];\n }\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Edit helpers\n// ---------------------------------------------------------------------------\n\nfunction applyEdits(source: string, edits: Edit[]): string {\n const unique = deduplicateEdits(edits);\n let result = source;\n for (const edit of unique.sort((a, b) => b.start - a.start)) {\n result = result.slice(0, edit.start) + edit.replacement + result.slice(edit.end);\n }\n return result;\n}\n\nfunction deduplicateEdits(edits: Edit[]): Edit[] {\n const seen = new Map<string, Edit>();\n for (const edit of edits) {\n const key = `${edit.start}:${edit.end}`;\n seen.set(key, edit);\n }\n return Array.from(seen.values());\n}\n\nfunction visitDir(dir: DirEntry, callback: (path: string) => void): void {\n for (const file of dir.subfiles) {\n if (file.endsWith('.d.ts')) continue;\n if (!file.endsWith('.ts')) continue;\n callback(`${dir.path}/${file}`);\n }\n for (const sub of dir.subdirs) {\n if (sub === 'node_modules' || sub === 'dist') continue;\n visitDir(dir.dir(sub), callback);\n }\n}\n",
|
|
1036
1036
|
"displayName": "Edit",
|
|
1037
1037
|
"properties": [
|
|
1038
1038
|
{
|
|
@@ -1044,7 +1044,7 @@
|
|
|
1044
1044
|
"indexKey": "",
|
|
1045
1045
|
"optional": false,
|
|
1046
1046
|
"description": "",
|
|
1047
|
-
"line":
|
|
1047
|
+
"line": 14,
|
|
1048
1048
|
"rawdescription": "\n"
|
|
1049
1049
|
},
|
|
1050
1050
|
{
|
|
@@ -1056,7 +1056,7 @@
|
|
|
1056
1056
|
"indexKey": "",
|
|
1057
1057
|
"optional": false,
|
|
1058
1058
|
"description": "",
|
|
1059
|
-
"line":
|
|
1059
|
+
"line": 15,
|
|
1060
1060
|
"rawdescription": "\n"
|
|
1061
1061
|
},
|
|
1062
1062
|
{
|
|
@@ -1068,7 +1068,7 @@
|
|
|
1068
1068
|
"indexKey": "",
|
|
1069
1069
|
"optional": false,
|
|
1070
1070
|
"description": "",
|
|
1071
|
-
"line":
|
|
1071
|
+
"line": 13,
|
|
1072
1072
|
"rawdescription": "\n"
|
|
1073
1073
|
}
|
|
1074
1074
|
],
|
|
@@ -3100,12 +3100,12 @@
|
|
|
3100
3100
|
},
|
|
3101
3101
|
{
|
|
3102
3102
|
"name": "Schema",
|
|
3103
|
-
"id": "interface-Schema-
|
|
3104
|
-
"file": "packages/core/schematics/migrate-
|
|
3103
|
+
"id": "interface-Schema-d36032102ed30a7ada1e3d36bb9ca41b7234b855760cac9783a25818f7ffe2097e1ebd8e808b574ddec0760f827272f6cd561f45f3eb1578f87f39ee2633730a-20",
|
|
3104
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
3105
3105
|
"deprecated": false,
|
|
3106
3106
|
"deprecationMessage": "",
|
|
3107
3107
|
"type": "interface",
|
|
3108
|
-
"sourceCode": "import { DirEntry, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';\nimport { parseTemplate, TmplAstElement, TmplAstNode } from '@angular/compiler';\nimport * as ts from 'typescript';\nimport { logDryRun, logDryRunNote } from '../utils/dry-run';\nimport { EUI_SIGNAL_MAP, EUI_SELECTOR_TO_CLASS } from './signal-map';\n\ninterface Schema {\n path?: string;\n dryRun?: boolean;\n}\n\ninterface Edit {\n start: number;\n end: number;\n replacement: string;\n}\n\n/**\n * Migrates consumer code that accesses EUI component instances programmatically.\n *\n * When EUI migrated @Input() → input(), @ViewChild() → viewChild(), and\n * @ContentChild() → contentChild(), properties that were plain class fields\n * became signal getters. Consumers who access them via @ViewChild need to\n * change `comp.prop` to `comp.prop()`.\n *\n * This schematic:\n * 1. Finds @ViewChild / @ContentChild fields typed as EUI component classes\n * 2. Resolves untyped @ViewChild('refName') by reading the component template\n * and matching #refName to an EUI element tag/selector\n * 3. Tracks which local variable names hold EUI component references (incl. aliases)\n * 4. Rewrites property accesses on those references: `ref.prop` → `ref.prop()`\n * (only for properties listed in the signal map)\n * 5. Does NOT touch template bindings — those are unaffected by signal migrations\n */\nexport function migrateToSignals(options: Schema = {}): Rule {\n return (tree: Tree, context: SchematicContext) => {\n const scanPath = options.path ? '/' + options.path.replace(/^\\.?\\//, '').replace(/\\/$/, '') : '';\n let fileCount = 0;\n let propCount = 0;\n\n visitDir(tree.getDir(scanPath || '/'), (path) => {\n if (!path.endsWith('.ts')) return;\n // Skip spec files — test code may use different patterns\n if (path.endsWith('.spec.ts')) return;\n\n const buffer = tree.read(path);\n if (!buffer) return;\n\n const original = buffer.toString('utf-8');\n\n // Fast bail: file must mention at least one EUI class name or have\n // ViewChild/ContentChild with string refs (which might reference EUI elements)\n if (!hasEuiReference(original) && !hasStringRefQuery(original)) return;\n\n const result = migrateTypeScript(original, path, tree, context); if (result.source !== original) {\n if (options.dryRun) {\n logDryRun(context, `Would migrate ${result.editCount} signal property access(es) in ${path}`);\n } else {\n tree.overwrite(path, result.source);\n }\n fileCount++;\n propCount += result.editCount;\n }\n });\n\n context.logger.info(`Migrated ${propCount} property access(es) to signal getters in ${fileCount} file(s).`);\n if (options.dryRun) {\n logDryRunNote(context);\n }\n return tree;\n };\n}\n\n/** Quick check: does the file reference any EUI class name from the signal map? */\nfunction hasEuiReference(source: string): boolean {\n for (const className of Object.keys(EUI_SIGNAL_MAP)) {\n if (source.includes(className)) return true;\n }\n return false;\n}\n\n/** Quick check: does the file have @ViewChild('...') or @ContentChild('...') with string refs? */\nfunction hasStringRefQuery(source: string): boolean {\n return /(@ViewChild|@ContentChild|@ViewChildren|@ContentChildren)\\s*\\(\\s*['\"]/.test(source);\n}\n\ninterface MigrateResult {\n source: string;\n editCount: number;\n}\n\nfunction migrateTypeScript(source: string, filePath: string, tree: Tree, context: SchematicContext): MigrateResult {\n const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);\n const edits: Edit[] = [];\n\n // Phase 0: Build template ref map (#refName → EUI class name) from the component's template\n const templateRefMap = buildTemplateRefMap(sourceFile, source, filePath, tree);\n\n // Phase 1: Collect all variables/properties typed as EUI classes\n const euiRefs = collectEuiReferences(sourceFile, source, templateRefMap);\n\n if (euiRefs.size === 0) {\n return { source, editCount: 0 };\n }\n\n // Phase 2: Find property accesses on those references and rewrite signal props\n const visitNode = (node: ts.Node): void => {\n // Match: ref.prop (PropertyAccessExpression)\n if (ts.isPropertyAccessExpression(node)) {\n const rewrite = tryRewritePropertyAccess(node, euiRefs, sourceFile, source);\n if (rewrite) {\n edits.push(rewrite);\n }\n }\n ts.forEachChild(node, visitNode);\n };\n\n ts.forEachChild(sourceFile, visitNode);\n\n if (edits.length === 0) {\n return { source, editCount: 0 };\n }\n\n return {\n source: applyEdits(source, edits),\n editCount: edits.length,\n };\n}\n\n/**\n * Scans the source file for class properties or variables typed as EUI component classes.\n * Returns a map: localName → Set of signal property names.\n *\n * Handles:\n * @ViewChild(EuiXxxComponent) name: EuiXxxComponent;\n * @ViewChild(EuiXxxComponent) name!: EuiXxxComponent;\n * @ViewChild('ref') name: EuiXxxComponent;\n * @ContentChild(EuiXxxComponent) name: EuiXxxComponent;\n * @ViewChild('ref') name: ElementRef; (resolved via template #ref → EUI selector)\n * someVar: EuiXxxComponent; (typed class field, even without decorator)\n * const alias = this.viewChildRef; (alias tracking)\n */\nfunction collectEuiReferences(\n sourceFile: ts.SourceFile,\n source: string,\n templateRefMap: Map<string, string>,\n): Map<string, ReadonlySet<string>> {\n const refs = new Map<string, ReadonlySet<string>>();\n\n // Pass 1: collect directly-typed references\n const visitDirect = (node: ts.Node): void => {\n // Class property declarations\n if (ts.isPropertyDeclaration(node) && node.name && ts.isIdentifier(node.name)) {\n const euiClass = resolveEuiClassName(node, sourceFile, templateRefMap);\n if (euiClass && EUI_SIGNAL_MAP[euiClass]) {\n refs.set(node.name.text, EUI_SIGNAL_MAP[euiClass]);\n }\n }\n\n // Variable declarations (const x: EuiXxxComponent = ...)\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {\n const typeName = getTypeAnnotationName(node.type);\n if (typeName && EUI_SIGNAL_MAP[typeName]) {\n refs.set(node.name.text, EUI_SIGNAL_MAP[typeName]);\n }\n }\n\n // Parameter declarations (method(ref: EuiXxxComponent))\n if (ts.isParameter(node) && ts.isIdentifier(node.name)) {\n const typeName = getTypeAnnotationName(node.type);\n if (typeName && EUI_SIGNAL_MAP[typeName]) {\n refs.set(node.name.text, EUI_SIGNAL_MAP[typeName]);\n }\n }\n\n ts.forEachChild(node, visitDirect);\n };\n\n ts.forEachChild(sourceFile, visitDirect);\n\n // Pass 2: track aliases — variables assigned from already-tracked references\n // e.g. const alias = this.viewChildRef; or const alias = someParam;\n const visitAlias = (node: ts.Node): void => {\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {\n // Skip if already tracked (has explicit type annotation)\n if (refs.has(node.name.text)) {\n ts.forEachChild(node, visitAlias);\n return;\n }\n\n const aliasedRef = resolveRefName(node.initializer);\n if (aliasedRef && refs.has(aliasedRef)) {\n refs.set(node.name.text, refs.get(aliasedRef)!);\n }\n }\n\n ts.forEachChild(node, visitAlias);\n };\n\n ts.forEachChild(sourceFile, visitAlias);\n return refs;\n}\n\n/**\n * Resolves the EUI class name from a property declaration.\n * Checks: type annotation → decorator class argument → template ref map.\n */\nfunction resolveEuiClassName(\n node: ts.PropertyDeclaration,\n sourceFile: ts.SourceFile,\n templateRefMap: Map<string, string>,\n): string | null {\n // Check type annotation first\n const typeName = getTypeAnnotationName(node.type);\n if (typeName && EUI_SIGNAL_MAP[typeName]) {\n return typeName;\n }\n\n // Check generic type argument: ElementRef<EuiXxxComponent>\n if (node.type && ts.isTypeReferenceNode(node.type) && node.type.typeArguments?.length) {\n const genericArg = getTypeAnnotationName(node.type.typeArguments[0]);\n if (genericArg && EUI_SIGNAL_MAP[genericArg]) {\n return genericArg;\n }\n }\n\n // Check decorator arguments: @ViewChild(EuiXxxComponent) or @ViewChild('refName')\n if (node.modifiers) {\n for (const modifier of node.modifiers) {\n if (!ts.isDecorator(modifier)) continue;\n if (!ts.isCallExpression(modifier.expression)) continue;\n\n const decoratorName = getDecoratorName(modifier.expression);\n if (!decoratorName) continue;\n if (!['ViewChild', 'ViewChildren', 'ContentChild', 'ContentChildren'].includes(decoratorName)) continue;\n\n const args = modifier.expression.arguments;\n if (args.length > 0) {\n const firstArg = args[0];\n\n // @ViewChild(EuiXxxComponent) — class reference\n if (ts.isIdentifier(firstArg) && EUI_SIGNAL_MAP[firstArg.text]) {\n return firstArg.text;\n }\n\n // @ViewChild('refName') — string template ref, resolve via template\n if (ts.isStringLiteralLike(firstArg)) {\n const refName = firstArg.text;\n const euiClass = templateRefMap.get(refName);\n if (euiClass && EUI_SIGNAL_MAP[euiClass]) {\n return euiClass;\n }\n }\n }\n }\n }\n\n return null;\n}\n\nfunction getDecoratorName(callExpr: ts.CallExpression): string | null {\n if (ts.isIdentifier(callExpr.expression)) {\n return callExpr.expression.text;\n }\n return null;\n}\n\nfunction getTypeAnnotationName(typeNode: ts.TypeNode | undefined): string | null {\n if (!typeNode) return null;\n if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {\n return typeNode.typeName.text;\n }\n return null;\n}\n\n/**\n * Checks if a property access `ref.prop` should be rewritten to `ref.prop()`.\n *\n * We rewrite if:\n * - `ref` is tracked in euiRefs\n * - `prop` is in the signal set for that EUI class\n * - The access is NOT already a call expression (not `ref.prop()` or `ref.prop(args)`)\n * - The access is NOT a write target (not `ref.prop = value`)\n * - The access is NOT a template binding argument (those are unaffected)\n */\nfunction tryRewritePropertyAccess(\n node: ts.PropertyAccessExpression,\n euiRefs: Map<string, ReadonlySet<string>>,\n sourceFile: ts.SourceFile,\n source: string,\n): Edit | null {\n const propName = node.name.text;\n\n // Resolve the reference name — handle `this.ref.prop` and `ref.prop`\n const refName = resolveRefName(node.expression);\n if (!refName) return null;\n\n const signalProps = euiRefs.get(refName);\n if (!signalProps || !signalProps.has(propName)) return null;\n\n // Skip if parent is already a call expression on this property: ref.prop(...)\n if (ts.isCallExpression(node.parent) && node.parent.expression === node) {\n return null;\n }\n\n // Skip if this is the left side of an assignment: ref.prop = value\n if (ts.isBinaryExpression(node.parent) &&\n node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&\n node.parent.left === node) {\n return null;\n }\n\n // Skip if this is a compound assignment: ref.prop += value, ref.prop &&= value, etc.\n if (ts.isBinaryExpression(node.parent) && isAssignmentOperator(node.parent.operatorToken.kind) && node.parent.left === node) {\n return null;\n }\n\n // Skip if this is used with delete: delete ref.prop\n if (ts.isDeleteExpression(node.parent) && node.parent.expression === node) {\n return null;\n }\n\n // Skip if inside a typeof: typeof ref.prop\n if (ts.isTypeOfExpression(node.parent) && node.parent.expression === node) {\n return null;\n }\n\n // Skip property access chains that go deeper: ref.prop.something → ref.prop().something\n // This IS correct — we still want to add ()\n\n // Rewrite: insert () after the property name\n const end = node.name.getEnd();\n return {\n start: end,\n end: end,\n replacement: '()',\n };\n}\n\nfunction isAssignmentOperator(kind: ts.SyntaxKind): boolean {\n return kind >= ts.SyntaxKind.FirstAssignment && kind <= ts.SyntaxKind.LastAssignment;\n}\n\n/**\n * Resolves a reference name from an expression.\n * Handles: `ref`, `this.ref`, `this._ref`\n */\nfunction resolveRefName(expr: ts.Expression): string | null {\n // Direct identifier: ref.prop\n if (ts.isIdentifier(expr)) {\n return expr.text;\n }\n\n // this.ref.prop\n if (ts.isPropertyAccessExpression(expr) && expr.expression.kind === ts.SyntaxKind.ThisKeyword) {\n return expr.name.text;\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Template ref resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a map of template reference variable names (#ref) to EUI class names.\n *\n * Parses the component's template (inline or external) and finds elements like:\n * <eui-alert #myAlert> → myAlert → EuiAlertComponent\n * <input euiAutocomplete #ac> → ac → EuiAutocompleteComponent\n *\n * Returns Map<refName, euiClassName>.\n */\nfunction buildTemplateRefMap(\n sourceFile: ts.SourceFile,\n source: string,\n filePath: string,\n tree: Tree,\n): Map<string, string> {\n const refMap = new Map<string, string>();\n\n // Find @Component decorator\n const componentMeta = findComponentMetadata(sourceFile, source);\n if (!componentMeta) return refMap;\n\n let templateSource: string | null = null;\n\n if (componentMeta.inlineTemplate !== null) {\n templateSource = componentMeta.inlineTemplate;\n } else if (componentMeta.templateUrl) {\n // Resolve templateUrl relative to the TS file\n const dir = filePath.substring(0, filePath.lastIndexOf('/'));\n const templatePath = `${dir}/${componentMeta.templateUrl}`;\n const buffer = tree.read(templatePath);\n if (buffer) {\n templateSource = buffer.toString('utf-8');\n }\n }\n\n if (!templateSource) return refMap;\n\n // Parse the template and collect #refs\n try {\n const parsed = parseTemplate(templateSource, '', { preserveWhitespaces: true });\n collectTemplateRefs(parsed.nodes, refMap);\n } catch {\n // Template parse failure — silently skip (might be malformed or use third-party syntax)\n }\n\n return refMap;\n}\n\n/**\n * Extracts @Component metadata: templateUrl or inline template content.\n */\nfunction findComponentMetadata(\n sourceFile: ts.SourceFile,\n source: string,\n): { templateUrl: string | null; inlineTemplate: string | null } | null {\n let result: { templateUrl: string | null; inlineTemplate: string | null } | null = null;\n\n const visit = (node: ts.Node): void => {\n if (result) return;\n\n if (ts.isDecorator(node) && ts.isCallExpression(node.expression)) {\n const name = getDecoratorName(node.expression);\n if (name !== 'Component') {\n ts.forEachChild(node, visit);\n return;\n }\n\n const args = node.expression.arguments;\n if (args.length === 0 || !ts.isObjectLiteralExpression(args[0])) {\n ts.forEachChild(node, visit);\n return;\n }\n\n const meta: { templateUrl: string | null; inlineTemplate: string | null } = {\n templateUrl: null,\n inlineTemplate: null,\n };\n\n for (const prop of args[0].properties) {\n if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) continue;\n\n if (prop.name.text === 'templateUrl') {\n const init = unwrapExpression(prop.initializer);\n if (ts.isStringLiteralLike(init)) {\n meta.templateUrl = init.text;\n }\n }\n\n if (prop.name.text === 'template') {\n const init = unwrapExpression(prop.initializer);\n if (ts.isStringLiteralLike(init)) {\n meta.inlineTemplate = init.text;\n } else if (ts.isNoSubstitutionTemplateLiteral(init)) {\n meta.inlineTemplate = init.text;\n }\n }\n }\n\n result = meta;\n return;\n }\n\n ts.forEachChild(node, visit);\n };\n\n ts.forEachChild(sourceFile, visit);\n return result;\n}\n\nfunction unwrapExpression(expression: ts.Expression): ts.Expression {\n let current = expression;\n while (ts.isParenthesizedExpression(current)) current = current.expression;\n return current;\n}\n\n/**\n * Recursively walks template AST nodes, collecting #refName → EUI class name mappings.\n *\n * For each element with a template reference (#ref), we try to resolve its EUI class:\n * 1. Match element tag name against EUI_SELECTOR_TO_CLASS (e.g. 'eui-alert' → EuiAlertComponent)\n * 2. Match element attributes against attribute selectors (e.g. [euiAutocomplete] → EuiAutocompleteComponent)\n */\nfunction collectTemplateRefs(nodes: TmplAstNode[], refMap: Map<string, string>): void {\n for (const node of nodes) {\n if (node instanceof TmplAstElement) {\n if (node.references && node.references.length > 0) {\n const euiClass = resolveElementToEuiClass(node);\n if (euiClass) {\n for (const ref of node.references) {\n refMap.set(ref.name, euiClass);\n }\n }\n }\n collectTemplateRefs(node.children, refMap);\n }\n }\n}\n\n/**\n * Given a template element, resolves it to an EUI class name by checking:\n * 1. Tag name: <eui-alert> → look up 'eui-alert' in EUI_SELECTOR_TO_CLASS\n * 2. Attribute selectors: <input euiAutocomplete> → look up 'input[euiAutocomplete]' or '[euiAutocomplete]'\n */\nfunction resolveElementToEuiClass(element: TmplAstElement): string | null {\n const tag = element.name;\n\n // Direct tag match: eui-alert, eui-dialog, etc.\n if (EUI_SELECTOR_TO_CLASS[tag]) {\n return EUI_SELECTOR_TO_CLASS[tag];\n }\n\n // Attribute selector match: check all attributes against known patterns\n const allAttrNames = [\n ...element.attributes.map((a) => a.name),\n ...element.inputs.map((i) => i.name),\n ];\n\n for (const attrName of allAttrNames) {\n // Try tag[attr] pattern first (e.g. 'button[euiButton]', 'input[euiAutocomplete]')\n const tagAttrSelector = `${tag}[${attrName}]`;\n if (EUI_SELECTOR_TO_CLASS[tagAttrSelector]) {\n return EUI_SELECTOR_TO_CLASS[tagAttrSelector];\n }\n\n // Try [attr] pattern (e.g. '[euiTooltip]', '[euiLoading]')\n const attrSelector = `[${attrName}]`;\n if (EUI_SELECTOR_TO_CLASS[attrSelector]) {\n return EUI_SELECTOR_TO_CLASS[attrSelector];\n }\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Edit helpers\n// ---------------------------------------------------------------------------\n\nfunction applyEdits(source: string, edits: Edit[]): string {\n const unique = deduplicateEdits(edits);\n let result = source;\n for (const edit of unique.sort((a, b) => b.start - a.start)) {\n result = result.slice(0, edit.start) + edit.replacement + result.slice(edit.end);\n }\n return result;\n}\n\nfunction deduplicateEdits(edits: Edit[]): Edit[] {\n const seen = new Map<string, Edit>();\n for (const edit of edits) {\n const key = `${edit.start}:${edit.end}`;\n seen.set(key, edit);\n }\n return Array.from(seen.values());\n}\n\nfunction visitDir(dir: DirEntry, callback: (path: string) => void): void {\n for (const file of dir.subfiles) {\n if (file.endsWith('.d.ts')) continue;\n if (!file.endsWith('.ts')) continue;\n callback(`${dir.path}/${file}`);\n }\n for (const sub of dir.subdirs) {\n if (sub === 'node_modules' || sub === 'dist') continue;\n visitDir(dir.dir(sub), callback);\n }\n}\n",
|
|
3108
|
+
"sourceCode": "import { DirEntry, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';\nimport * as ts from 'typescript';\nimport { logDryRun, logDryRunNote } from '../utils/dry-run';\n\ninterface Schema {\n path?: string;\n dryRun?: boolean;\n}\n\ninterface Edit {\n start: number;\n end: number;\n replacement: string;\n}\n\nconst OLD_CLASS = 'EuiTooltipConfig';\nconst NEW_INTERFACE = 'EuiTooltipInterface';\n\nexport function migrateEuiTooltip(options: Schema = {}): Rule {\n return (tree: Tree, context: SchematicContext) => {\n const scanPath = options.path ? '/' + options.path.replace(/^\\.?\\//, '').replace(/\\/$/, '') : '';\n let fileCount = 0;\n\n visitDir(tree.getDir(scanPath || '/'), (path) => {\n if (!path.endsWith('.ts')) return;\n\n const buffer = tree.read(path);\n if (!buffer) return;\n\n const original = buffer.toString('utf-8');\n if (!original.includes(OLD_CLASS)) return;\n\n const result = migrateTypeScript(original, path, context);\n\n if (result !== original) {\n if (options.dryRun) {\n logDryRun(context, `Would migrate EuiTooltipConfig → EuiTooltipInterface in ${path}`);\n } else {\n tree.overwrite(path, result);\n }\n fileCount++;\n }\n });\n\n context.logger.info(`Migrated EuiTooltipConfig → EuiTooltipInterface in ${fileCount} file(s).`);\n if (options.dryRun) {\n logDryRunNote(context);\n }\n return tree;\n };\n}\n\nfunction migrateTypeScript(source: string, filePath: string, context: SchematicContext): string {\n const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);\n const edits: Edit[] = [];\n\n // Track if EuiTooltipInterface is already imported\n let hasInterfaceImport = false;\n let classImportDecl: ts.ImportDeclaration | null = null;\n let classImportModuleSpecifier: string | null = null;\n\n // First pass: analyze imports\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) continue;\n const namedBindings = stmt.importClause?.namedBindings;\n if (!namedBindings || !ts.isNamedImports(namedBindings)) continue;\n\n for (const specifier of namedBindings.elements) {\n if (specifier.name.text === NEW_INTERFACE) {\n hasInterfaceImport = true;\n }\n if (specifier.name.text === OLD_CLASS) {\n classImportDecl = stmt;\n classImportModuleSpecifier = (stmt.moduleSpecifier as ts.StringLiteral).text;\n }\n }\n }\n\n // Second pass: handle import declarations\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) continue;\n const namedBindings = stmt.importClause?.namedBindings;\n if (!namedBindings || !ts.isNamedImports(namedBindings)) continue;\n\n const specifiers = namedBindings.elements;\n const classSpecifier = specifiers.find((s) => s.name.text === OLD_CLASS);\n if (!classSpecifier) continue;\n\n if (hasInterfaceImport) {\n // EuiTooltipInterface is already imported elsewhere → remove EuiTooltipConfig from this import\n removeImportSpecifier(namedBindings, classSpecifier, sourceFile, edits);\n } else {\n // Rename EuiTooltipConfig → EuiTooltipInterface in the import\n edits.push({\n start: classSpecifier.name.getStart(sourceFile),\n end: classSpecifier.name.getEnd(),\n replacement: NEW_INTERFACE,\n });\n hasInterfaceImport = true;\n }\n }\n\n // Third pass: replace `new EuiTooltipConfig(...)` → spread/cast to interface\n const visitNewExpressions = (node: ts.Node): void => {\n if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === OLD_CLASS) {\n const args = node.arguments;\n if (args && args.length === 1) {\n const arg = args[0];\n // `new EuiTooltipConfig({ ... })` → `{ ... } as EuiTooltipInterface`\n // But if the argument is just a variable, we keep it: `varName as EuiTooltipInterface`\n const argText = source.slice(arg.getStart(sourceFile), arg.getEnd());\n\n if (ts.isObjectLiteralExpression(arg)) {\n // Inline object: `new EuiTooltipConfig({ x: 1 })` → `{ x: 1 }`\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: argText,\n });\n } else {\n // Variable or expression: `new EuiTooltipConfig(opts)` → `opts`\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: argText,\n });\n }\n } else if (!args || args.length === 0) {\n // `new EuiTooltipConfig()` → `{} as EuiTooltipInterface`\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: `{} as ${NEW_INTERFACE}`,\n });\n }\n return; // don't recurse into children we've already replaced\n }\n ts.forEachChild(node, visitNewExpressions);\n };\n\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) {\n visitNewExpressions(stmt);\n }\n }\n\n // Fourth pass: rename all remaining identifier references (type annotations, etc.)\n const visitRefs = (node: ts.Node): void => {\n if (ts.isImportDeclaration(node)) return;\n // Skip nodes we already covered in new expressions\n if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === OLD_CLASS) return;\n\n if (ts.isIdentifier(node) && node.text === OLD_CLASS) {\n // Ensure this is not part of an import declaration\n if (!isPartOfImport(node)) {\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: NEW_INTERFACE,\n });\n }\n }\n ts.forEachChild(node, visitRefs);\n };\n\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) {\n visitRefs(stmt);\n }\n }\n\n return applyEdits(source, edits);\n}\n\nfunction isPartOfImport(node: ts.Node): boolean {\n let current: ts.Node | undefined = node.parent;\n while (current) {\n if (ts.isImportDeclaration(current)) return true;\n current = current.parent;\n }\n return false;\n}\n\nfunction removeImportSpecifier(\n namedImports: ts.NamedImports,\n specifier: ts.ImportSpecifier,\n sourceFile: ts.SourceFile,\n edits: Edit[],\n): void {\n const elements = namedImports.elements;\n if (elements.length === 1) {\n // Remove the entire import declaration\n const importDecl = namedImports.parent.parent;\n let end = importDecl.getEnd();\n // Also remove trailing newline if present\n const fullText = sourceFile.getFullText();\n if (fullText[end] === '\\n') end++;\n edits.push({\n start: importDecl.getStart(sourceFile),\n end,\n replacement: '',\n });\n } else {\n // Remove just this specifier with surrounding comma/whitespace\n const idx = elements.indexOf(specifier);\n let start: number;\n let end: number;\n if (idx < elements.length - 1) {\n // Not the last → remove from this specifier start to next specifier start\n start = specifier.getStart(sourceFile);\n end = elements[idx + 1].getStart(sourceFile);\n } else {\n // Last element → remove from previous element end to this end\n start = elements[idx - 1].getEnd();\n end = specifier.getEnd();\n }\n edits.push({ start, end, replacement: '' });\n }\n}\n\nfunction applyEdits(source: string, edits: Edit[]): string {\n const unique = deduplicateEdits(edits);\n let result = source;\n for (const edit of unique.sort((a, b) => b.start - a.start)) {\n result = result.slice(0, edit.start) + edit.replacement + result.slice(edit.end);\n }\n return result;\n}\n\nfunction deduplicateEdits(edits: Edit[]): Edit[] {\n const seen = new Map<string, Edit>();\n for (const edit of edits) {\n const key = `${edit.start}:${edit.end}`;\n seen.set(key, edit);\n }\n return Array.from(seen.values());\n}\n\nfunction visitDir(dir: DirEntry, callback: (path: string) => void): void {\n for (const file of dir.subfiles) {\n if (file.endsWith('.d.ts')) continue;\n if (!file.endsWith('.ts')) continue;\n callback(`${dir.path}/${file}`);\n }\n for (const sub of dir.subdirs) {\n if (sub === 'node_modules' || sub === 'dist') continue;\n visitDir(dir.dir(sub), callback);\n }\n}\n",
|
|
3109
3109
|
"displayName": "Schema",
|
|
3110
3110
|
"properties": [
|
|
3111
3111
|
{
|
|
@@ -3117,7 +3117,7 @@
|
|
|
3117
3117
|
"indexKey": "",
|
|
3118
3118
|
"optional": true,
|
|
3119
3119
|
"description": "",
|
|
3120
|
-
"line":
|
|
3120
|
+
"line": 7,
|
|
3121
3121
|
"rawdescription": "\n"
|
|
3122
3122
|
},
|
|
3123
3123
|
{
|
|
@@ -3129,7 +3129,7 @@
|
|
|
3129
3129
|
"indexKey": "",
|
|
3130
3130
|
"optional": true,
|
|
3131
3131
|
"description": "",
|
|
3132
|
-
"line":
|
|
3132
|
+
"line": 6,
|
|
3133
3133
|
"rawdescription": "\n"
|
|
3134
3134
|
}
|
|
3135
3135
|
],
|
|
@@ -3147,12 +3147,12 @@
|
|
|
3147
3147
|
},
|
|
3148
3148
|
{
|
|
3149
3149
|
"name": "Schema",
|
|
3150
|
-
"id": "interface-Schema-
|
|
3151
|
-
"file": "packages/core/schematics/migrate-to-
|
|
3150
|
+
"id": "interface-Schema-cac2ac952d8245647e8a48b450e8f127a36bcffb394cf2cd81fe9c0810ed02c19e5e1629dbf29a68a5c6f675aed99570e9ac04ae470e13315e20c92cc0c6c453-21",
|
|
3151
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
3152
3152
|
"deprecated": false,
|
|
3153
3153
|
"deprecationMessage": "",
|
|
3154
3154
|
"type": "interface",
|
|
3155
|
-
"sourceCode": "import { parseTemplate, TmplAstElement, TmplAstNode } from '@angular/compiler';\nimport { DirEntry, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';\nimport * as ts from 'typescript';\nimport { logDryRun, logDryRunNote } from '../utils/dry-run';\n\ninterface ModuleMapping {\n importPath: string;\n selectors: Record<string, string>;\n}\n\nconst MODULE_MAPPINGS: Record<string, ModuleMapping> = {\n EuiAccordionModule: {\n importPath: '@eui/components/eui-accordion',\n selectors: {\n 'eui-accordion': 'EuiAccordionComponent',\n 'eui-accordion-item': 'EuiAccordionItemComponent',\n euiAccordionItemHeader: 'EuiAccordionItemHeaderDirective',\n },\n },\n EuiAlertModule: {\n importPath: '@eui/components/eui-alert',\n selectors: {\n 'eui-alert': 'EuiAlertComponent',\n euiAlert: 'EuiAlertComponent',\n 'eui-alert-title': 'EuiAlertTitleComponent',\n },\n },\n EuiAutocompleteModule: {\n importPath: '@eui/components/eui-autocomplete',\n selectors: {\n 'eui-autocomplete': 'EuiAutocompleteComponent',\n euiAutocomplete: 'EuiAutocompleteComponent',\n 'eui-autocomplete-option': 'EuiAutocompleteOptionComponent',\n 'eui-autocomplete-option-group': 'EuiAutocompleteOptionGroupComponent',\n 'eui-autocomplete-panel': 'EuiAutocompletePanelComponent',\n },\n },\n EuiAvatarModule: {\n importPath: '@eui/components/eui-avatar',\n selectors: {\n 'eui-avatar': 'EuiAvatarComponent',\n euiAvatar: 'EuiAvatarComponent',\n },\n },\n EuiBadgeModule: {\n importPath: '@eui/components/eui-badge',\n selectors: {\n 'eui-badge': 'EuiBadgeComponent',\n euiBadge: 'EuiBadgeComponent',\n },\n },\n EuiBlockContentModule: {\n importPath: '@eui/components/eui-block-content',\n selectors: {\n 'eui-block-content': 'EuiBlockContentComponent',\n },\n },\n EuiBreadcrumbModule: {\n importPath: '@eui/components/eui-breadcrumb',\n selectors: {\n 'eui-breadcrumb': 'EuiBreadcrumbComponent',\n },\n },\n EuiButtonModule: {\n importPath: '@eui/components/eui-button',\n selectors: {\n euiButton: 'EuiButtonComponent',\n },\n },\n EuiButtonGroupModule: {\n importPath: '@eui/components/eui-button-group',\n selectors: {\n 'eui-button-group': 'EuiButtonGroupComponent',\n },\n },\n EuiCardModule: {\n importPath: '@eui/components/eui-card',\n selectors: {\n 'eui-card': 'EuiCardComponent',\n 'eui-card-header': 'EuiCardHeaderComponent',\n 'eui-card-header-title': 'EuiCardHeaderTitleComponent',\n 'eui-card-content': 'EuiCardContentComponent',\n 'eui-card-footer': 'EuiCardFooterComponent',\n 'eui-card-media': 'EuiCardMediaComponent',\n },\n },\n EuiChipModule: {\n importPath: '@eui/components/eui-chip',\n selectors: {\n 'eui-chip': 'EuiChipComponent',\n euiChip: 'EuiChipComponent',\n },\n },\n EuiChipListModule: {\n importPath: '@eui/components/eui-chip-list',\n selectors: {\n 'eui-chip-list': 'EuiChipListComponent',\n },\n },\n EuiChipGroupModule: {\n importPath: '@eui/components/eui-chip-group',\n selectors: {\n 'eui-chip-group': 'EuiChipGroupComponent',\n },\n },\n EuiDashboardCardModule: {\n importPath: '@eui/components/eui-dashboard-card',\n selectors: {\n 'eui-dashboard-card': 'EuiDashboardCardComponent',\n 'eui-dashboard-card-content': 'EuiDashboardCardContentComponent',\n 'eui-dashboard-card-content-header': 'EuiDashboardCardContentHeaderComponent',\n 'eui-dashboard-card-content-body': 'EuiDashboardCardContentBodyComponent',\n 'eui-dashboard-card-content-footer': 'EuiDashboardCardContentFooterComponent',\n },\n },\n EuiDashboardButtonModule: {\n importPath: '@eui/components/eui-dashboard-card',\n selectors: {\n 'eui-dashboard-card': 'EuiDashboardCardComponent',\n 'eui-dashboard-card-content': 'EuiDashboardCardContentComponent',\n 'eui-dashboard-card-content-header': 'EuiDashboardCardContentHeaderComponent',\n 'eui-dashboard-card-content-body': 'EuiDashboardCardContentBodyComponent',\n 'eui-dashboard-card-content-footer': 'EuiDashboardCardContentFooterComponent',\n },\n },\n EuiDatepickerModule: {\n importPath: '@eui/components/eui-datepicker',\n selectors: {\n 'eui-datepicker': 'EuiDatepickerComponent',\n },\n },\n EuiDateRangeSelectorModule: {\n importPath: '@eui/components/eui-date-range-selector',\n selectors: {\n 'eui-date-range-selector': 'EuiDateRangeSelectorComponent',\n },\n },\n EuiDialogModule: {\n importPath: '@eui/components/eui-dialog',\n selectors: {\n 'eui-dialog': 'EuiDialogComponent',\n 'eui-dialog-header': 'EuiDialogHeaderDirective',\n 'eui-dialog-footer': 'EuiDialogFooterDirective',\n 'eui-dialog-container': 'EuiDialogContainerComponent',\n },\n },\n EuiDisableContentModule: {\n importPath: '@eui/components/eui-disable-content',\n selectors: {\n 'eui-disable-content': 'EuiDisableContentComponent',\n },\n },\n EuiDiscussionThreadModule: {\n importPath: '@eui/components/eui-discussion-thread',\n selectors: {\n 'eui-discussion-thread': 'EuiDiscussionThreadComponent',\n 'eui-discussion-thread-item': 'EuiDiscussionThreadItemComponent',\n },\n },\n EuiDropdownModule: {\n importPath: '@eui/components/eui-dropdown',\n selectors: {\n 'eui-dropdown': 'EuiDropdownComponent',\n },\n },\n EuiFeedbackMessageModule: {\n importPath: '@eui/components/eui-feedback-message',\n selectors: {\n 'eui-feedback-message': 'EuiFeedbackMessageComponent',\n },\n },\n EuiFieldsetModule: {\n importPath: '@eui/components/eui-fieldset',\n selectors: {\n 'eui-fieldset': 'EuiFieldsetComponent',\n euiFieldsetLabelRightContent: 'EuiFieldsetLabelRightContentTagDirective',\n euiFieldsetLabelExtraContent: 'EuiFieldsetLabelExtraContentTagDirective',\n },\n },\n EuiFileUploadModule: {\n importPath: '@eui/components/eui-file-upload',\n selectors: {\n 'eui-file-upload': 'EuiFileUploadComponent',\n },\n },\n EuiGrowlModule: {\n importPath: '@eui/components/eui-growl',\n selectors: {\n 'eui-growl': 'EuiGrowlComponent',\n },\n },\n EuiIconModule: {\n importPath: '@eui/components/eui-icon',\n selectors: {\n 'eui-icon-svg': 'EuiIconSvgComponent',\n euiIconSvg: 'EuiIconSvgComponent',\n },\n },\n EuiIconButtonModule: {\n importPath: '@eui/components/eui-icon-button',\n selectors: {\n 'eui-icon-button': 'EuiIconButtonComponent',\n },\n },\n EuiIconToggleModule: {\n importPath: '@eui/components/eui-icon-toggle',\n selectors: {\n 'eui-icon-toggle': 'EuiIconToggleComponent',\n },\n },\n EuiInputCheckboxModule: {\n importPath: '@eui/components/eui-input-checkbox',\n selectors: {\n euiInputCheckBox: 'EuiInputCheckboxComponent',\n },\n },\n EuiInputGroupModule: {\n importPath: '@eui/components/eui-input-group',\n selectors: {\n euiInputGroup: 'EuiInputGroupComponent',\n 'eui-input-group-addon': 'EuiInputGroupAddOnComponent',\n euiInputGroupAddOn: 'EuiInputGroupAddOnComponent',\n 'eui-input-group-addon-item': 'EuiInputGroupAddOnItemComponent',\n euiInputGroupAddOnItem: 'EuiInputGroupAddOnItemComponent',\n },\n },\n EuiInputNumberModule: {\n importPath: '@eui/components/eui-input-number',\n selectors: {\n euiInputNumber: 'EuiInputNumberComponent',\n },\n },\n EuiInputRadioModule: {\n importPath: '@eui/components/eui-input-radio',\n selectors: {\n euiInputRadio: 'EuiInputRadioComponent',\n },\n },\n EuiInputTextModule: {\n importPath: '@eui/components/eui-input-text',\n selectors: {\n euiInputText: 'EuiInputTextComponent',\n },\n },\n EuiLabelModule: {\n importPath: '@eui/components/eui-label',\n selectors: {\n 'eui-label': 'EuiLabelComponent',\n euiLabel: 'EuiLabelComponent',\n },\n },\n EuiListModule: {\n importPath: '@eui/components/eui-list',\n selectors: {\n 'eui-list': 'EuiListComponent',\n euiList: 'EuiListComponent',\n 'eui-list-item': 'EuiListItemComponent',\n euiListItem: 'EuiListItemComponent',\n },\n },\n EuiMenuModule: {\n importPath: '@eui/components/eui-menu',\n selectors: {\n 'eui-menu': 'EuiMenuComponent',\n 'eui-menu-item': 'EuiMenuItemComponent',\n },\n },\n EuiMessageBoxModule: {\n importPath: '@eui/components/eui-message-box',\n selectors: {\n 'eui-message-box': 'EuiMessageBoxComponent',\n 'eui-message-box-footer': 'EuiMessageBoxFooterDirective',\n },\n },\n EuiOverlayModule: {\n importPath: '@eui/components/eui-overlay',\n selectors: {\n 'eui-overlay': 'EuiOverlayComponent',\n },\n },\n EuiPageModule: {\n importPath: '@eui/components/eui-page',\n selectors: {\n 'eui-page': 'EuiPageComponent',\n },\n },\n EuiPaginatorModule: {\n importPath: '@eui/components/eui-paginator',\n selectors: {\n 'eui-paginator': 'EuiPaginatorComponent',\n },\n },\n EuiPopoverModule: {\n importPath: '@eui/components/eui-popover',\n selectors: {\n 'eui-popover': 'EuiPopoverComponent',\n },\n },\n EuiProgressBarModule: {\n importPath: '@eui/components/eui-progress-bar',\n selectors: {\n 'eui-progress-bar': 'EuiProgressBarComponent',\n },\n },\n EuiProgressCircleModule: {\n importPath: '@eui/components/eui-progress-circle',\n selectors: {\n 'eui-progress-circle': 'EuiProgressCircleComponent',\n },\n },\n EuiSelectModule: {\n importPath: '@eui/components/eui-select',\n selectors: {\n euiSelect: 'EuiSelectComponent',\n },\n },\n EuiSidebarMenuModule: {\n importPath: '@eui/components/eui-sidebar-menu',\n selectors: {\n 'eui-sidebar-menu': 'EuiSidebarMenuComponent',\n },\n },\n EuiSkeletonModule: {\n importPath: '@eui/components/eui-skeleton',\n selectors: {\n 'eui-skeleton': 'EuiSkeletonComponent',\n },\n },\n EuiSlideToggleModule: {\n importPath: '@eui/components/eui-slide-toggle',\n selectors: {\n 'eui-slide-toggle': 'EuiSlideToggleComponent',\n },\n },\n EuiTableModule: {\n importPath: '@eui/components/eui-table',\n selectors: {\n 'eui-table': 'EuiTableComponent',\n euiTable: 'EuiTableComponent',\n 'eui-table-filter': 'EuiTableFilterComponent',\n isSortable: 'EuiTableSortableColComponent',\n isStickyCol: 'EuiTableStickyColDirective',\n isHeaderSelectable: 'EuiTableSelectableHeaderComponent',\n isDataSelectable: 'EuiTableSelectableRowComponent',\n isExpandableRow: 'EuiTableExpandableRowDirective',\n },\n },\n EuiTableV2Module: {\n importPath: '@eui/components/eui-table',\n selectors: {\n 'eui-table': 'EuiTableComponent',\n euiTable: 'EuiTableComponent',\n 'eui-table-filter': 'EuiTableFilterComponent',\n isSortable: 'EuiTableSortableColComponent',\n isStickyCol: 'EuiTableStickyColDirective',\n isHeaderSelectable: 'EuiTableSelectableHeaderComponent',\n isDataSelectable: 'EuiTableSelectableRowComponent',\n isExpandableRow: 'EuiTableExpandableRowDirective',\n },\n },\n EuiTabsModule: {\n importPath: '@eui/components/eui-tabs',\n selectors: {\n 'eui-tabs': 'EuiTabsComponent',\n 'eui-tab': 'EuiTabComponent',\n 'eui-tab-header': 'EuiTabHeaderComponent',\n 'eui-tab-body': 'EuiTabBodyComponent',\n },\n },\n EuiTextAreaModule: {\n importPath: '@eui/components/eui-textarea',\n selectors: {\n euiTextArea: 'EuiTextareaComponent',\n },\n },\n EuiTimelineModule: {\n importPath: '@eui/components/eui-timeline',\n selectors: {\n 'eui-timeline': 'EuiTimelineComponent',\n 'eui-timeline-item': 'EuiTimelineItemComponent',\n },\n },\n EuiTimepickerModule: {\n importPath: '@eui/components/eui-timepicker',\n selectors: {\n 'eui-timepicker': 'EuiTimepickerComponent',\n },\n },\n EuiTreeModule: {\n importPath: '@eui/components/eui-tree',\n selectors: {\n 'eui-tree': 'EuiTreeComponent',\n },\n },\n EuiTreeListModule: {\n importPath: '@eui/components/eui-tree-list',\n selectors: {\n 'eui-tree-list': 'EuiTreeListComponent',\n 'eui-tree-list-item': 'EuiTreeListItemComponent',\n },\n },\n EuiUserProfileModule: {\n importPath: '@eui/components/eui-user-profile',\n selectors: {\n 'eui-user-profile': 'EuiUserProfileComponent',\n },\n },\n EuiWizardModule: {\n importPath: '@eui/components/eui-wizard',\n selectors: {\n 'eui-wizard': 'EuiWizardComponent',\n 'eui-wizard-step': 'EuiWizardStepComponent',\n },\n },\n EuiTooltipDirectiveModule: {\n importPath: '@eui/components/directives',\n selectors: {\n euiTooltip: 'EuiTooltipDirective',\n },\n },\n EuiTemplateDirectiveModule: {\n importPath: '@eui/components/directives',\n selectors: {\n euiTemplate: 'EuiTemplateDirective',\n },\n },\n EuiResizableDirectiveModule: {\n importPath: '@eui/components/directives',\n selectors: {\n euiResizable: 'EuiResizableDirective',\n 'eui-resizable': 'EuiResizableComponent',\n },\n },\n EuiMaxLengthDirectiveModule: {\n importPath: '@eui/components/directives',\n selectors: {\n euiEditorMaxlength: 'EuiMaxLengthDirective',\n },\n },\n EuiTruncatePipeModule: {\n importPath: '@eui/components/pipes',\n selectors: {\n euiTruncate: 'EuiTruncatePipe',\n },\n },\n EuiLayoutModule: {\n importPath: '@eui/components/layout',\n selectors: {\n 'eui-app': 'EuiAppComponent',\n 'eui-header': 'EuiHeaderComponent',\n 'eui-footer': 'EuiFooterComponent',\n 'eui-toolbar': 'EuiToolbarComponent',\n 'eui-sidebar-toggle': 'EuiSidebarToggleComponent',\n },\n },\n};\n\ninterface Schema {\n path?: string;\n dryRun?: boolean;\n}\n\nexport function migrateToStandalone(options: Schema = {}): Rule {\n return (tree: Tree, context: SchematicContext) => {\n const scanPath = options.path ? '/' + options.path.replace(/^\\.?\\//, '').replace(/\\/$/, '') : '';\n const allModuleNames = Object.keys(MODULE_MAPPINGS);\n\n visitDir(tree.getDir(scanPath || '/'), (path) => {\n const buffer = tree.read(path);\n if (!buffer) return;\n\n const source = buffer.toString('utf-8');\n if (!allModuleNames.some((m) => source.includes(m))) return;\n\n const sourceFile = ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true);\n const componentDecorators = findComponentDecorators(sourceFile);\n\n for (const decorator of componentDecorators) {\n const importsNode = findImportsArrayNode(decorator);\n if (!importsNode) continue;\n\n const currentSource = tree.read(path)!.toString('utf-8');\n const modulesInArray = allModuleNames.filter((m) => hasModuleInArray(importsNode, m, source));\n if (modulesInArray.length === 0) continue;\n\n const templateSelectors = getTemplateSelectors(tree, path, decorator, source);\n const replacements = buildReplacements(modulesInArray, templateSelectors);\n if (replacements.size === 0) continue;\n\n const result = applyReplacements(currentSource, path, replacements);\n if (options.dryRun) {\n logDryRun(context, `Would replace module imports with standalone imports in ${path}`);\n } else {\n tree.overwrite(path, result);\n }\n }\n });\n\n context.logger.info('Migration to standalone imports complete.');\n if (options.dryRun) {\n logDryRunNote(context);\n }\n return tree;\n };\n}\n\nfunction visitDir(dir: DirEntry, callback: (path: string) => void): void {\n for (const file of dir.subfiles) {\n if (file.endsWith('.d.ts')) continue;\n if (!file.endsWith('.ts')) continue;\n callback(`${dir.path}/${file}`);\n }\n for (const sub of dir.subdirs) {\n if (sub === 'node_modules' || sub === 'dist') continue;\n visitDir(dir.dir(sub), callback);\n }\n}\n\nfunction buildReplacements(moduleNames: string[], templateSelectors: Set<string>): Map<string, { components: string[]; importPath: string }> {\n const map = new Map<string, { components: string[]; importPath: string }>();\n\n for (const moduleName of moduleNames) {\n const mapping = MODULE_MAPPINGS[moduleName];\n const components: string[] = [];\n\n for (const [selector, component] of Object.entries(mapping.selectors)) {\n if (templateSelectors.has(selector)) {\n components.push(component);\n }\n }\n\n if (components.length > 0) {\n map.set(moduleName, { components: [...new Set(components)].sort(), importPath: mapping.importPath });\n }\n }\n\n return map;\n}\n\nfunction findComponentDecorators(sourceFile: ts.SourceFile): ts.Decorator[] {\n const decorators: ts.Decorator[] = [];\n const visit = (node: ts.Node): void => {\n if (ts.isClassDeclaration(node)) {\n const decs = ts.getDecorators(node);\n if (decs) {\n for (const dec of decs) {\n if (ts.isCallExpression(dec.expression) && ts.isIdentifier(dec.expression.expression) && dec.expression.expression.text === 'Component') {\n decorators.push(dec);\n }\n }\n }\n }\n ts.forEachChild(node, visit);\n };\n visit(sourceFile);\n return decorators;\n}\n\nfunction findImportsArrayNode(decorator: ts.Decorator): ts.ArrayLiteralExpression | undefined {\n const call = decorator.expression as ts.CallExpression;\n const metadata = call.arguments[0];\n if (!ts.isObjectLiteralExpression(metadata)) return undefined;\n\n for (const prop of metadata.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'imports') {\n if (ts.isArrayLiteralExpression(prop.initializer)) {\n return prop.initializer;\n }\n }\n }\n return undefined;\n}\n\nfunction hasModuleInArray(array: ts.ArrayLiteralExpression, moduleName: string, source: string): boolean {\n return array.elements.some((el) => source.slice(el.getStart(), el.getEnd()).trim() === moduleName);\n}\n\nfunction getTemplateSelectors(tree: Tree, tsPath: string, decorator: ts.Decorator, source: string): Set<string> {\n const call = decorator.expression as ts.CallExpression;\n const metadata = call.arguments[0] as ts.ObjectLiteralExpression;\n const allSelectors = Object.values(MODULE_MAPPINGS).flatMap((m) => Object.keys(m.selectors));\n\n for (const prop of metadata.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'template') {\n const init = prop.initializer;\n if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {\n return findSelectorsInTemplate(init.text, allSelectors);\n }\n }\n }\n\n for (const prop of metadata.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'templateUrl') {\n if (ts.isStringLiteral(prop.initializer)) {\n const dir = tsPath.substring(0, tsPath.lastIndexOf('/'));\n const templateBuffer = tree.read(`${dir}/${prop.initializer.text}`);\n if (templateBuffer) {\n return findSelectorsInTemplate(templateBuffer.toString('utf-8'), allSelectors);\n }\n }\n }\n }\n\n return new Set();\n}\n\nfunction findSelectorsInTemplate(html: string, knownSelectors: string[]): Set<string> {\n const selectors = new Set<string>();\n const parsed = parseTemplate(html, '', { preserveWhitespaces: true });\n\n const visit = (nodes: TmplAstNode[]): void => {\n for (const node of nodes) {\n if (node instanceof TmplAstElement) {\n if (knownSelectors.includes(node.name)) selectors.add(node.name);\n for (const attr of node.attributes) {\n if (knownSelectors.includes(attr.name)) selectors.add(attr.name);\n }\n visit(node.children);\n }\n }\n };\n visit(parsed.nodes);\n return selectors;\n}\n\nfunction applyReplacements(source: string, filePath: string, replacements: Map<string, { components: string[]; importPath: string }>): string {\n const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true);\n let result = replaceInImportsArray(source, sourceFile, replacements);\n result = updateEsImports(result, filePath, replacements);\n return result;\n}\n\nfunction replaceInImportsArray(source: string, sourceFile: ts.SourceFile, replacements: Map<string, { components: string[]; importPath: string }>): string {\n let result = source;\n const visit = (node: ts.Node): void => {\n if (ts.isClassDeclaration(node)) {\n const decs = ts.getDecorators(node);\n if (!decs) return;\n for (const dec of decs) {\n if (!ts.isCallExpression(dec.expression) || !ts.isIdentifier(dec.expression.expression) || dec.expression.expression.text !== 'Component') continue;\n const metadata = dec.expression.arguments[0];\n if (!ts.isObjectLiteralExpression(metadata)) continue;\n for (const prop of metadata.properties) {\n if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name) || prop.name.text !== 'imports') continue;\n if (!ts.isArrayLiteralExpression(prop.initializer)) continue;\n const newElements = prop.initializer.elements\n .map((el) => {\n const text = result.slice(el.getStart(sourceFile), el.getEnd()).trim();\n const replacement = replacements.get(text);\n return replacement ? replacement.components.join(', ') : text;\n })\n .join(', ');\n result = result.slice(0, prop.initializer.getStart(sourceFile) + 1) + newElements + result.slice(prop.initializer.getEnd() - 1);\n }\n }\n }\n ts.forEachChild(node, visit);\n };\n visit(sourceFile);\n return result;\n}\n\nfunction updateEsImports(source: string, filePath: string, replacements: Map<string, { components: string[]; importPath: string }>): string {\n let result = source;\n\n for (const [moduleName, { components, importPath }] of replacements) {\n const sf = ts.createSourceFile(filePath, result, ts.ScriptTarget.Latest, true);\n\n for (const stmt of sf.statements) {\n if (!ts.isImportDeclaration(stmt) || !stmt.importClause?.namedBindings || !ts.isNamedImports(stmt.importClause.namedBindings)) continue;\n const namedBindings = stmt.importClause.namedBindings;\n const importNames = namedBindings.elements.map((el) => el.name.text);\n if (!importNames.includes(moduleName)) continue;\n\n const moduleSpecifier = (stmt.moduleSpecifier as ts.StringLiteral).text;\n const remaining = importNames.filter((n) => n !== moduleName);\n const toAdd = components.filter((c) => !importNames.includes(c));\n\n if (moduleSpecifier === importPath) {\n const newNames = [...remaining, ...toAdd].sort();\n const newClause = `{ ${newNames.join(', ')} }`;\n result = result.slice(0, namedBindings.getStart(sf)) + newClause + result.slice(namedBindings.getEnd());\n } else {\n if (remaining.length > 0) {\n const newClause = `{ ${remaining.join(', ')} }`;\n result = result.slice(0, namedBindings.getStart(sf)) + newClause + result.slice(namedBindings.getEnd());\n } else {\n result = result.slice(0, stmt.getStart(sf)) + result.slice(stmt.getEnd()).replace(/^\\r?\\n/, '');\n }\n\n if (toAdd.length > 0) {\n const updatedSf = ts.createSourceFile(filePath, result, ts.ScriptTarget.Latest, true);\n const existing = updatedSf.statements.find(\n (s) => ts.isImportDeclaration(s) && ts.isStringLiteral(s.moduleSpecifier) && s.moduleSpecifier.text === importPath,\n ) as ts.ImportDeclaration | undefined;\n\n if (existing?.importClause?.namedBindings && ts.isNamedImports(existing.importClause.namedBindings)) {\n const existingNames = existing.importClause.namedBindings.elements.map((el) => el.name.text);\n const allNames = [...new Set([...existingNames, ...toAdd])].sort();\n const newClause = `{ ${allNames.join(', ')} }`;\n result = result.slice(0, existing.importClause.namedBindings.getStart(updatedSf)) + newClause + result.slice(existing.importClause.namedBindings.getEnd());\n } else {\n const newImport = `import { ${toAdd.join(', ')} } from '${importPath}';\\n`;\n result = newImport + result;\n }\n }\n }\n break;\n }\n }\n\n return result;\n}\n",
|
|
3155
|
+
"sourceCode": "import { DirEntry, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';\nimport { parseTemplate, TmplAstElement, TmplAstNode } from '@angular/compiler';\nimport * as ts from 'typescript';\nimport { logDryRun, logDryRunNote } from '../utils/dry-run';\nimport { EUI_SIGNAL_MAP, EUI_SELECTOR_TO_CLASS } from './signal-map';\n\ninterface Schema {\n path?: string;\n dryRun?: boolean;\n}\n\ninterface Edit {\n start: number;\n end: number;\n replacement: string;\n}\n\n/**\n * Migrates consumer code that accesses EUI component instances programmatically.\n *\n * When EUI migrated @Input() → input(), @ViewChild() → viewChild(), and\n * @ContentChild() → contentChild(), properties that were plain class fields\n * became signal getters. Consumers who access them via @ViewChild need to\n * change `comp.prop` to `comp.prop()`.\n *\n * This schematic:\n * 1. Finds @ViewChild / @ContentChild fields typed as EUI component classes\n * 2. Resolves untyped @ViewChild('refName') by reading the component template\n * and matching #refName to an EUI element tag/selector\n * 3. Tracks which local variable names hold EUI component references (incl. aliases)\n * 4. Rewrites property accesses on those references: `ref.prop` → `ref.prop()`\n * (only for properties listed in the signal map)\n * 5. Does NOT touch template bindings — those are unaffected by signal migrations\n */\nexport function migrateToSignals(options: Schema = {}): Rule {\n return (tree: Tree, context: SchematicContext) => {\n const scanPath = options.path ? '/' + options.path.replace(/^\\.?\\//, '').replace(/\\/$/, '') : '';\n let fileCount = 0;\n let propCount = 0;\n\n visitDir(tree.getDir(scanPath || '/'), (path) => {\n if (!path.endsWith('.ts')) return;\n // Skip spec files — test code may use different patterns\n if (path.endsWith('.spec.ts')) return;\n\n const buffer = tree.read(path);\n if (!buffer) return;\n\n const original = buffer.toString('utf-8');\n\n // Fast bail: file must mention at least one EUI class name or have\n // ViewChild/ContentChild with string refs (which might reference EUI elements)\n if (!hasEuiReference(original) && !hasStringRefQuery(original)) return;\n\n const result = migrateTypeScript(original, path, tree, context); if (result.source !== original) {\n if (options.dryRun) {\n logDryRun(context, `Would migrate ${result.editCount} signal property access(es) in ${path}`);\n } else {\n tree.overwrite(path, result.source);\n }\n fileCount++;\n propCount += result.editCount;\n }\n });\n\n context.logger.info(`Migrated ${propCount} property access(es) to signal getters in ${fileCount} file(s).`);\n if (options.dryRun) {\n logDryRunNote(context);\n }\n return tree;\n };\n}\n\n/** Quick check: does the file reference any EUI class name from the signal map? */\nfunction hasEuiReference(source: string): boolean {\n for (const className of Object.keys(EUI_SIGNAL_MAP)) {\n if (source.includes(className)) return true;\n }\n return false;\n}\n\n/** Quick check: does the file have @ViewChild('...') or @ContentChild('...') with string refs? */\nfunction hasStringRefQuery(source: string): boolean {\n return /(@ViewChild|@ContentChild|@ViewChildren|@ContentChildren)\\s*\\(\\s*['\"]/.test(source);\n}\n\ninterface MigrateResult {\n source: string;\n editCount: number;\n}\n\nfunction migrateTypeScript(source: string, filePath: string, tree: Tree, context: SchematicContext): MigrateResult {\n const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);\n const edits: Edit[] = [];\n\n // Phase 0: Build template ref map (#refName → EUI class name) from the component's template\n const templateRefMap = buildTemplateRefMap(sourceFile, source, filePath, tree);\n\n // Phase 1: Collect all variables/properties typed as EUI classes\n const euiRefs = collectEuiReferences(sourceFile, source, templateRefMap);\n\n if (euiRefs.size === 0) {\n return { source, editCount: 0 };\n }\n\n // Phase 2: Find property accesses on those references and rewrite signal props\n const visitNode = (node: ts.Node): void => {\n // Match: ref.prop (PropertyAccessExpression)\n if (ts.isPropertyAccessExpression(node)) {\n const rewrite = tryRewritePropertyAccess(node, euiRefs, sourceFile, source);\n if (rewrite) {\n edits.push(rewrite);\n }\n }\n ts.forEachChild(node, visitNode);\n };\n\n ts.forEachChild(sourceFile, visitNode);\n\n if (edits.length === 0) {\n return { source, editCount: 0 };\n }\n\n return {\n source: applyEdits(source, edits),\n editCount: edits.length,\n };\n}\n\n/**\n * Scans the source file for class properties or variables typed as EUI component classes.\n * Returns a map: localName → Set of signal property names.\n *\n * Handles:\n * @ViewChild(EuiXxxComponent) name: EuiXxxComponent;\n * @ViewChild(EuiXxxComponent) name!: EuiXxxComponent;\n * @ViewChild('ref') name: EuiXxxComponent;\n * @ContentChild(EuiXxxComponent) name: EuiXxxComponent;\n * @ViewChild('ref') name: ElementRef; (resolved via template #ref → EUI selector)\n * someVar: EuiXxxComponent; (typed class field, even without decorator)\n * const alias = this.viewChildRef; (alias tracking)\n */\nfunction collectEuiReferences(\n sourceFile: ts.SourceFile,\n source: string,\n templateRefMap: Map<string, string>,\n): Map<string, ReadonlySet<string>> {\n const refs = new Map<string, ReadonlySet<string>>();\n\n // Pass 1: collect directly-typed references\n const visitDirect = (node: ts.Node): void => {\n // Class property declarations\n if (ts.isPropertyDeclaration(node) && node.name && ts.isIdentifier(node.name)) {\n const euiClass = resolveEuiClassName(node, sourceFile, templateRefMap);\n if (euiClass && EUI_SIGNAL_MAP[euiClass]) {\n refs.set(node.name.text, EUI_SIGNAL_MAP[euiClass]);\n }\n }\n\n // Variable declarations (const x: EuiXxxComponent = ...)\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {\n const typeName = getTypeAnnotationName(node.type);\n if (typeName && EUI_SIGNAL_MAP[typeName]) {\n refs.set(node.name.text, EUI_SIGNAL_MAP[typeName]);\n }\n }\n\n // Parameter declarations (method(ref: EuiXxxComponent))\n if (ts.isParameter(node) && ts.isIdentifier(node.name)) {\n const typeName = getTypeAnnotationName(node.type);\n if (typeName && EUI_SIGNAL_MAP[typeName]) {\n refs.set(node.name.text, EUI_SIGNAL_MAP[typeName]);\n }\n }\n\n ts.forEachChild(node, visitDirect);\n };\n\n ts.forEachChild(sourceFile, visitDirect);\n\n // Pass 2: track aliases — variables assigned from already-tracked references\n // e.g. const alias = this.viewChildRef; or const alias = someParam;\n const visitAlias = (node: ts.Node): void => {\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {\n // Skip if already tracked (has explicit type annotation)\n if (refs.has(node.name.text)) {\n ts.forEachChild(node, visitAlias);\n return;\n }\n\n const aliasedRef = resolveRefName(node.initializer);\n if (aliasedRef && refs.has(aliasedRef)) {\n refs.set(node.name.text, refs.get(aliasedRef)!);\n }\n }\n\n ts.forEachChild(node, visitAlias);\n };\n\n ts.forEachChild(sourceFile, visitAlias);\n return refs;\n}\n\n/**\n * Resolves the EUI class name from a property declaration.\n * Checks: type annotation → decorator class argument → template ref map.\n */\nfunction resolveEuiClassName(\n node: ts.PropertyDeclaration,\n sourceFile: ts.SourceFile,\n templateRefMap: Map<string, string>,\n): string | null {\n // Check type annotation first\n const typeName = getTypeAnnotationName(node.type);\n if (typeName && EUI_SIGNAL_MAP[typeName]) {\n return typeName;\n }\n\n // Check generic type argument: ElementRef<EuiXxxComponent>\n if (node.type && ts.isTypeReferenceNode(node.type) && node.type.typeArguments?.length) {\n const genericArg = getTypeAnnotationName(node.type.typeArguments[0]);\n if (genericArg && EUI_SIGNAL_MAP[genericArg]) {\n return genericArg;\n }\n }\n\n // Check decorator arguments: @ViewChild(EuiXxxComponent) or @ViewChild('refName')\n if (node.modifiers) {\n for (const modifier of node.modifiers) {\n if (!ts.isDecorator(modifier)) continue;\n if (!ts.isCallExpression(modifier.expression)) continue;\n\n const decoratorName = getDecoratorName(modifier.expression);\n if (!decoratorName) continue;\n if (!['ViewChild', 'ViewChildren', 'ContentChild', 'ContentChildren'].includes(decoratorName)) continue;\n\n const args = modifier.expression.arguments;\n if (args.length > 0) {\n const firstArg = args[0];\n\n // @ViewChild(EuiXxxComponent) — class reference\n if (ts.isIdentifier(firstArg) && EUI_SIGNAL_MAP[firstArg.text]) {\n return firstArg.text;\n }\n\n // @ViewChild('refName') — string template ref, resolve via template\n if (ts.isStringLiteralLike(firstArg)) {\n const refName = firstArg.text;\n const euiClass = templateRefMap.get(refName);\n if (euiClass && EUI_SIGNAL_MAP[euiClass]) {\n return euiClass;\n }\n }\n }\n }\n }\n\n return null;\n}\n\nfunction getDecoratorName(callExpr: ts.CallExpression): string | null {\n if (ts.isIdentifier(callExpr.expression)) {\n return callExpr.expression.text;\n }\n return null;\n}\n\nfunction getTypeAnnotationName(typeNode: ts.TypeNode | undefined): string | null {\n if (!typeNode) return null;\n if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {\n return typeNode.typeName.text;\n }\n return null;\n}\n\n/**\n * Checks if a property access `ref.prop` should be rewritten to `ref.prop()`.\n *\n * We rewrite if:\n * - `ref` is tracked in euiRefs\n * - `prop` is in the signal set for that EUI class\n * - The access is NOT already a call expression (not `ref.prop()` or `ref.prop(args)`)\n * - The access is NOT a write target (not `ref.prop = value`)\n * - The access is NOT a template binding argument (those are unaffected)\n */\nfunction tryRewritePropertyAccess(\n node: ts.PropertyAccessExpression,\n euiRefs: Map<string, ReadonlySet<string>>,\n sourceFile: ts.SourceFile,\n source: string,\n): Edit | null {\n const propName = node.name.text;\n\n // Resolve the reference name — handle `this.ref.prop` and `ref.prop`\n const refName = resolveRefName(node.expression);\n if (!refName) return null;\n\n const signalProps = euiRefs.get(refName);\n if (!signalProps || !signalProps.has(propName)) return null;\n\n // Skip if parent is already a call expression on this property: ref.prop(...)\n if (ts.isCallExpression(node.parent) && node.parent.expression === node) {\n return null;\n }\n\n // Skip if this is the left side of an assignment: ref.prop = value\n if (ts.isBinaryExpression(node.parent) &&\n node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&\n node.parent.left === node) {\n return null;\n }\n\n // Skip if this is a compound assignment: ref.prop += value, ref.prop &&= value, etc.\n if (ts.isBinaryExpression(node.parent) && isAssignmentOperator(node.parent.operatorToken.kind) && node.parent.left === node) {\n return null;\n }\n\n // Skip if this is used with delete: delete ref.prop\n if (ts.isDeleteExpression(node.parent) && node.parent.expression === node) {\n return null;\n }\n\n // Skip if inside a typeof: typeof ref.prop\n if (ts.isTypeOfExpression(node.parent) && node.parent.expression === node) {\n return null;\n }\n\n // Skip property access chains that go deeper: ref.prop.something → ref.prop().something\n // This IS correct — we still want to add ()\n\n // Rewrite: insert () after the property name\n const end = node.name.getEnd();\n return {\n start: end,\n end: end,\n replacement: '()',\n };\n}\n\nfunction isAssignmentOperator(kind: ts.SyntaxKind): boolean {\n return kind >= ts.SyntaxKind.FirstAssignment && kind <= ts.SyntaxKind.LastAssignment;\n}\n\n/**\n * Resolves a reference name from an expression.\n * Handles: `ref`, `this.ref`, `this._ref`\n */\nfunction resolveRefName(expr: ts.Expression): string | null {\n // Direct identifier: ref.prop\n if (ts.isIdentifier(expr)) {\n return expr.text;\n }\n\n // this.ref.prop\n if (ts.isPropertyAccessExpression(expr) && expr.expression.kind === ts.SyntaxKind.ThisKeyword) {\n return expr.name.text;\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Template ref resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a map of template reference variable names (#ref) to EUI class names.\n *\n * Parses the component's template (inline or external) and finds elements like:\n * <eui-alert #myAlert> → myAlert → EuiAlertComponent\n * <input euiAutocomplete #ac> → ac → EuiAutocompleteComponent\n *\n * Returns Map<refName, euiClassName>.\n */\nfunction buildTemplateRefMap(\n sourceFile: ts.SourceFile,\n source: string,\n filePath: string,\n tree: Tree,\n): Map<string, string> {\n const refMap = new Map<string, string>();\n\n // Find @Component decorator\n const componentMeta = findComponentMetadata(sourceFile, source);\n if (!componentMeta) return refMap;\n\n let templateSource: string | null = null;\n\n if (componentMeta.inlineTemplate !== null) {\n templateSource = componentMeta.inlineTemplate;\n } else if (componentMeta.templateUrl) {\n // Resolve templateUrl relative to the TS file\n const dir = filePath.substring(0, filePath.lastIndexOf('/'));\n const templatePath = `${dir}/${componentMeta.templateUrl}`;\n const buffer = tree.read(templatePath);\n if (buffer) {\n templateSource = buffer.toString('utf-8');\n }\n }\n\n if (!templateSource) return refMap;\n\n // Parse the template and collect #refs\n try {\n const parsed = parseTemplate(templateSource, '', { preserveWhitespaces: true });\n collectTemplateRefs(parsed.nodes, refMap);\n } catch {\n // Template parse failure — silently skip (might be malformed or use third-party syntax)\n }\n\n return refMap;\n}\n\n/**\n * Extracts @Component metadata: templateUrl or inline template content.\n */\nfunction findComponentMetadata(\n sourceFile: ts.SourceFile,\n source: string,\n): { templateUrl: string | null; inlineTemplate: string | null } | null {\n let result: { templateUrl: string | null; inlineTemplate: string | null } | null = null;\n\n const visit = (node: ts.Node): void => {\n if (result) return;\n\n if (ts.isDecorator(node) && ts.isCallExpression(node.expression)) {\n const name = getDecoratorName(node.expression);\n if (name !== 'Component') {\n ts.forEachChild(node, visit);\n return;\n }\n\n const args = node.expression.arguments;\n if (args.length === 0 || !ts.isObjectLiteralExpression(args[0])) {\n ts.forEachChild(node, visit);\n return;\n }\n\n const meta: { templateUrl: string | null; inlineTemplate: string | null } = {\n templateUrl: null,\n inlineTemplate: null,\n };\n\n for (const prop of args[0].properties) {\n if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) continue;\n\n if (prop.name.text === 'templateUrl') {\n const init = unwrapExpression(prop.initializer);\n if (ts.isStringLiteralLike(init)) {\n meta.templateUrl = init.text;\n }\n }\n\n if (prop.name.text === 'template') {\n const init = unwrapExpression(prop.initializer);\n if (ts.isStringLiteralLike(init)) {\n meta.inlineTemplate = init.text;\n } else if (ts.isNoSubstitutionTemplateLiteral(init)) {\n meta.inlineTemplate = init.text;\n }\n }\n }\n\n result = meta;\n return;\n }\n\n ts.forEachChild(node, visit);\n };\n\n ts.forEachChild(sourceFile, visit);\n return result;\n}\n\nfunction unwrapExpression(expression: ts.Expression): ts.Expression {\n let current = expression;\n while (ts.isParenthesizedExpression(current)) current = current.expression;\n return current;\n}\n\n/**\n * Recursively walks template AST nodes, collecting #refName → EUI class name mappings.\n *\n * For each element with a template reference (#ref), we try to resolve its EUI class:\n * 1. Match element tag name against EUI_SELECTOR_TO_CLASS (e.g. 'eui-alert' → EuiAlertComponent)\n * 2. Match element attributes against attribute selectors (e.g. [euiAutocomplete] → EuiAutocompleteComponent)\n */\nfunction collectTemplateRefs(nodes: TmplAstNode[], refMap: Map<string, string>): void {\n for (const node of nodes) {\n if (node instanceof TmplAstElement) {\n if (node.references && node.references.length > 0) {\n const euiClass = resolveElementToEuiClass(node);\n if (euiClass) {\n for (const ref of node.references) {\n refMap.set(ref.name, euiClass);\n }\n }\n }\n collectTemplateRefs(node.children, refMap);\n }\n }\n}\n\n/**\n * Given a template element, resolves it to an EUI class name by checking:\n * 1. Tag name: <eui-alert> → look up 'eui-alert' in EUI_SELECTOR_TO_CLASS\n * 2. Attribute selectors: <input euiAutocomplete> → look up 'input[euiAutocomplete]' or '[euiAutocomplete]'\n */\nfunction resolveElementToEuiClass(element: TmplAstElement): string | null {\n const tag = element.name;\n\n // Direct tag match: eui-alert, eui-dialog, etc.\n if (EUI_SELECTOR_TO_CLASS[tag]) {\n return EUI_SELECTOR_TO_CLASS[tag];\n }\n\n // Attribute selector match: check all attributes against known patterns\n const allAttrNames = [\n ...element.attributes.map((a) => a.name),\n ...element.inputs.map((i) => i.name),\n ];\n\n for (const attrName of allAttrNames) {\n // Try tag[attr] pattern first (e.g. 'button[euiButton]', 'input[euiAutocomplete]')\n const tagAttrSelector = `${tag}[${attrName}]`;\n if (EUI_SELECTOR_TO_CLASS[tagAttrSelector]) {\n return EUI_SELECTOR_TO_CLASS[tagAttrSelector];\n }\n\n // Try [attr] pattern (e.g. '[euiTooltip]', '[euiLoading]')\n const attrSelector = `[${attrName}]`;\n if (EUI_SELECTOR_TO_CLASS[attrSelector]) {\n return EUI_SELECTOR_TO_CLASS[attrSelector];\n }\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Edit helpers\n// ---------------------------------------------------------------------------\n\nfunction applyEdits(source: string, edits: Edit[]): string {\n const unique = deduplicateEdits(edits);\n let result = source;\n for (const edit of unique.sort((a, b) => b.start - a.start)) {\n result = result.slice(0, edit.start) + edit.replacement + result.slice(edit.end);\n }\n return result;\n}\n\nfunction deduplicateEdits(edits: Edit[]): Edit[] {\n const seen = new Map<string, Edit>();\n for (const edit of edits) {\n const key = `${edit.start}:${edit.end}`;\n seen.set(key, edit);\n }\n return Array.from(seen.values());\n}\n\nfunction visitDir(dir: DirEntry, callback: (path: string) => void): void {\n for (const file of dir.subfiles) {\n if (file.endsWith('.d.ts')) continue;\n if (!file.endsWith('.ts')) continue;\n callback(`${dir.path}/${file}`);\n }\n for (const sub of dir.subdirs) {\n if (sub === 'node_modules' || sub === 'dist') continue;\n visitDir(dir.dir(sub), callback);\n }\n}\n",
|
|
3156
3156
|
"displayName": "Schema",
|
|
3157
3157
|
"properties": [
|
|
3158
3158
|
{
|
|
@@ -3164,7 +3164,7 @@
|
|
|
3164
3164
|
"indexKey": "",
|
|
3165
3165
|
"optional": true,
|
|
3166
3166
|
"description": "",
|
|
3167
|
-
"line":
|
|
3167
|
+
"line": 9,
|
|
3168
3168
|
"rawdescription": "\n"
|
|
3169
3169
|
},
|
|
3170
3170
|
{
|
|
@@ -3176,7 +3176,7 @@
|
|
|
3176
3176
|
"indexKey": "",
|
|
3177
3177
|
"optional": true,
|
|
3178
3178
|
"description": "",
|
|
3179
|
-
"line":
|
|
3179
|
+
"line": 8,
|
|
3180
3180
|
"rawdescription": "\n"
|
|
3181
3181
|
}
|
|
3182
3182
|
],
|
|
@@ -3194,12 +3194,12 @@
|
|
|
3194
3194
|
},
|
|
3195
3195
|
{
|
|
3196
3196
|
"name": "Schema",
|
|
3197
|
-
"id": "interface-Schema-
|
|
3198
|
-
"file": "packages/core/schematics/migrate-
|
|
3197
|
+
"id": "interface-Schema-817c4b549cc3eab9fcf4936acab2e71182d90a4480369f5c003cbbda10792efa587ad99d65acf049f64e7030e32589e4fabc4a6d7a5621e4fe54725ef91dc9e8-22",
|
|
3198
|
+
"file": "packages/core/schematics/migrate-to-standalone/index.ts",
|
|
3199
3199
|
"deprecated": false,
|
|
3200
3200
|
"deprecationMessage": "",
|
|
3201
3201
|
"type": "interface",
|
|
3202
|
-
"sourceCode": "import { DirEntry, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';\nimport * as ts from 'typescript';\nimport { logDryRun, logDryRunNote } from '../utils/dry-run';\n\ninterface Schema {\n path?: string;\n dryRun?: boolean;\n}\n\ninterface Edit {\n start: number;\n end: number;\n replacement: string;\n}\n\nconst OLD_CLASS = 'EuiTooltipConfig';\nconst NEW_INTERFACE = 'EuiTooltipInterface';\n\nexport function migrateEuiTooltip(options: Schema = {}): Rule {\n return (tree: Tree, context: SchematicContext) => {\n const scanPath = options.path ? '/' + options.path.replace(/^\\.?\\//, '').replace(/\\/$/, '') : '';\n let fileCount = 0;\n\n visitDir(tree.getDir(scanPath || '/'), (path) => {\n if (!path.endsWith('.ts')) return;\n\n const buffer = tree.read(path);\n if (!buffer) return;\n\n const original = buffer.toString('utf-8');\n if (!original.includes(OLD_CLASS)) return;\n\n const result = migrateTypeScript(original, path, context);\n\n if (result !== original) {\n if (options.dryRun) {\n logDryRun(context, `Would migrate EuiTooltipConfig → EuiTooltipInterface in ${path}`);\n } else {\n tree.overwrite(path, result);\n }\n fileCount++;\n }\n });\n\n context.logger.info(`Migrated EuiTooltipConfig → EuiTooltipInterface in ${fileCount} file(s).`);\n if (options.dryRun) {\n logDryRunNote(context);\n }\n return tree;\n };\n}\n\nfunction migrateTypeScript(source: string, filePath: string, context: SchematicContext): string {\n const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);\n const edits: Edit[] = [];\n\n // Track if EuiTooltipInterface is already imported\n let hasInterfaceImport = false;\n let classImportDecl: ts.ImportDeclaration | null = null;\n let classImportModuleSpecifier: string | null = null;\n\n // First pass: analyze imports\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) continue;\n const namedBindings = stmt.importClause?.namedBindings;\n if (!namedBindings || !ts.isNamedImports(namedBindings)) continue;\n\n for (const specifier of namedBindings.elements) {\n if (specifier.name.text === NEW_INTERFACE) {\n hasInterfaceImport = true;\n }\n if (specifier.name.text === OLD_CLASS) {\n classImportDecl = stmt;\n classImportModuleSpecifier = (stmt.moduleSpecifier as ts.StringLiteral).text;\n }\n }\n }\n\n // Second pass: handle import declarations\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) continue;\n const namedBindings = stmt.importClause?.namedBindings;\n if (!namedBindings || !ts.isNamedImports(namedBindings)) continue;\n\n const specifiers = namedBindings.elements;\n const classSpecifier = specifiers.find((s) => s.name.text === OLD_CLASS);\n if (!classSpecifier) continue;\n\n if (hasInterfaceImport) {\n // EuiTooltipInterface is already imported elsewhere → remove EuiTooltipConfig from this import\n removeImportSpecifier(namedBindings, classSpecifier, sourceFile, edits);\n } else {\n // Rename EuiTooltipConfig → EuiTooltipInterface in the import\n edits.push({\n start: classSpecifier.name.getStart(sourceFile),\n end: classSpecifier.name.getEnd(),\n replacement: NEW_INTERFACE,\n });\n hasInterfaceImport = true;\n }\n }\n\n // Third pass: replace `new EuiTooltipConfig(...)` → spread/cast to interface\n const visitNewExpressions = (node: ts.Node): void => {\n if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === OLD_CLASS) {\n const args = node.arguments;\n if (args && args.length === 1) {\n const arg = args[0];\n // `new EuiTooltipConfig({ ... })` → `{ ... } as EuiTooltipInterface`\n // But if the argument is just a variable, we keep it: `varName as EuiTooltipInterface`\n const argText = source.slice(arg.getStart(sourceFile), arg.getEnd());\n\n if (ts.isObjectLiteralExpression(arg)) {\n // Inline object: `new EuiTooltipConfig({ x: 1 })` → `{ x: 1 }`\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: argText,\n });\n } else {\n // Variable or expression: `new EuiTooltipConfig(opts)` → `opts`\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: argText,\n });\n }\n } else if (!args || args.length === 0) {\n // `new EuiTooltipConfig()` → `{} as EuiTooltipInterface`\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: `{} as ${NEW_INTERFACE}`,\n });\n }\n return; // don't recurse into children we've already replaced\n }\n ts.forEachChild(node, visitNewExpressions);\n };\n\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) {\n visitNewExpressions(stmt);\n }\n }\n\n // Fourth pass: rename all remaining identifier references (type annotations, etc.)\n const visitRefs = (node: ts.Node): void => {\n if (ts.isImportDeclaration(node)) return;\n // Skip nodes we already covered in new expressions\n if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === OLD_CLASS) return;\n\n if (ts.isIdentifier(node) && node.text === OLD_CLASS) {\n // Ensure this is not part of an import declaration\n if (!isPartOfImport(node)) {\n edits.push({\n start: node.getStart(sourceFile),\n end: node.getEnd(),\n replacement: NEW_INTERFACE,\n });\n }\n }\n ts.forEachChild(node, visitRefs);\n };\n\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) {\n visitRefs(stmt);\n }\n }\n\n return applyEdits(source, edits);\n}\n\nfunction isPartOfImport(node: ts.Node): boolean {\n let current: ts.Node | undefined = node.parent;\n while (current) {\n if (ts.isImportDeclaration(current)) return true;\n current = current.parent;\n }\n return false;\n}\n\nfunction removeImportSpecifier(\n namedImports: ts.NamedImports,\n specifier: ts.ImportSpecifier,\n sourceFile: ts.SourceFile,\n edits: Edit[],\n): void {\n const elements = namedImports.elements;\n if (elements.length === 1) {\n // Remove the entire import declaration\n const importDecl = namedImports.parent.parent;\n let end = importDecl.getEnd();\n // Also remove trailing newline if present\n const fullText = sourceFile.getFullText();\n if (fullText[end] === '\\n') end++;\n edits.push({\n start: importDecl.getStart(sourceFile),\n end,\n replacement: '',\n });\n } else {\n // Remove just this specifier with surrounding comma/whitespace\n const idx = elements.indexOf(specifier);\n let start: number;\n let end: number;\n if (idx < elements.length - 1) {\n // Not the last → remove from this specifier start to next specifier start\n start = specifier.getStart(sourceFile);\n end = elements[idx + 1].getStart(sourceFile);\n } else {\n // Last element → remove from previous element end to this end\n start = elements[idx - 1].getEnd();\n end = specifier.getEnd();\n }\n edits.push({ start, end, replacement: '' });\n }\n}\n\nfunction applyEdits(source: string, edits: Edit[]): string {\n const unique = deduplicateEdits(edits);\n let result = source;\n for (const edit of unique.sort((a, b) => b.start - a.start)) {\n result = result.slice(0, edit.start) + edit.replacement + result.slice(edit.end);\n }\n return result;\n}\n\nfunction deduplicateEdits(edits: Edit[]): Edit[] {\n const seen = new Map<string, Edit>();\n for (const edit of edits) {\n const key = `${edit.start}:${edit.end}`;\n seen.set(key, edit);\n }\n return Array.from(seen.values());\n}\n\nfunction visitDir(dir: DirEntry, callback: (path: string) => void): void {\n for (const file of dir.subfiles) {\n if (file.endsWith('.d.ts')) continue;\n if (!file.endsWith('.ts')) continue;\n callback(`${dir.path}/${file}`);\n }\n for (const sub of dir.subdirs) {\n if (sub === 'node_modules' || sub === 'dist') continue;\n visitDir(dir.dir(sub), callback);\n }\n}\n",
|
|
3202
|
+
"sourceCode": "import { parseTemplate, TmplAstElement, TmplAstNode } from '@angular/compiler';\nimport { DirEntry, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';\nimport * as ts from 'typescript';\nimport { logDryRun, logDryRunNote } from '../utils/dry-run';\n\ninterface ModuleMapping {\n importPath: string;\n selectors: Record<string, string>;\n}\n\nconst MODULE_MAPPINGS: Record<string, ModuleMapping> = {\n EuiAccordionModule: {\n importPath: '@eui/components/eui-accordion',\n selectors: {\n 'eui-accordion': 'EuiAccordionComponent',\n 'eui-accordion-item': 'EuiAccordionItemComponent',\n euiAccordionItemHeader: 'EuiAccordionItemHeaderDirective',\n },\n },\n EuiAlertModule: {\n importPath: '@eui/components/eui-alert',\n selectors: {\n 'eui-alert': 'EuiAlertComponent',\n euiAlert: 'EuiAlertComponent',\n 'eui-alert-title': 'EuiAlertTitleComponent',\n },\n },\n EuiAutocompleteModule: {\n importPath: '@eui/components/eui-autocomplete',\n selectors: {\n 'eui-autocomplete': 'EuiAutocompleteComponent',\n euiAutocomplete: 'EuiAutocompleteComponent',\n 'eui-autocomplete-option': 'EuiAutocompleteOptionComponent',\n 'eui-autocomplete-option-group': 'EuiAutocompleteOptionGroupComponent',\n 'eui-autocomplete-panel': 'EuiAutocompletePanelComponent',\n },\n },\n EuiAvatarModule: {\n importPath: '@eui/components/eui-avatar',\n selectors: {\n 'eui-avatar': 'EuiAvatarComponent',\n euiAvatar: 'EuiAvatarComponent',\n },\n },\n EuiBadgeModule: {\n importPath: '@eui/components/eui-badge',\n selectors: {\n 'eui-badge': 'EuiBadgeComponent',\n euiBadge: 'EuiBadgeComponent',\n },\n },\n EuiBlockContentModule: {\n importPath: '@eui/components/eui-block-content',\n selectors: {\n 'eui-block-content': 'EuiBlockContentComponent',\n },\n },\n EuiBreadcrumbModule: {\n importPath: '@eui/components/eui-breadcrumb',\n selectors: {\n 'eui-breadcrumb': 'EuiBreadcrumbComponent',\n },\n },\n EuiButtonModule: {\n importPath: '@eui/components/eui-button',\n selectors: {\n euiButton: 'EuiButtonComponent',\n },\n },\n EuiButtonGroupModule: {\n importPath: '@eui/components/eui-button-group',\n selectors: {\n 'eui-button-group': 'EuiButtonGroupComponent',\n },\n },\n EuiCardModule: {\n importPath: '@eui/components/eui-card',\n selectors: {\n 'eui-card': 'EuiCardComponent',\n 'eui-card-header': 'EuiCardHeaderComponent',\n 'eui-card-header-title': 'EuiCardHeaderTitleComponent',\n 'eui-card-content': 'EuiCardContentComponent',\n 'eui-card-footer': 'EuiCardFooterComponent',\n 'eui-card-media': 'EuiCardMediaComponent',\n },\n },\n EuiChipModule: {\n importPath: '@eui/components/eui-chip',\n selectors: {\n 'eui-chip': 'EuiChipComponent',\n euiChip: 'EuiChipComponent',\n },\n },\n EuiChipListModule: {\n importPath: '@eui/components/eui-chip-list',\n selectors: {\n 'eui-chip-list': 'EuiChipListComponent',\n },\n },\n EuiChipGroupModule: {\n importPath: '@eui/components/eui-chip-group',\n selectors: {\n 'eui-chip-group': 'EuiChipGroupComponent',\n },\n },\n EuiDashboardCardModule: {\n importPath: '@eui/components/eui-dashboard-card',\n selectors: {\n 'eui-dashboard-card': 'EuiDashboardCardComponent',\n 'eui-dashboard-card-content': 'EuiDashboardCardContentComponent',\n 'eui-dashboard-card-content-header': 'EuiDashboardCardContentHeaderComponent',\n 'eui-dashboard-card-content-body': 'EuiDashboardCardContentBodyComponent',\n 'eui-dashboard-card-content-footer': 'EuiDashboardCardContentFooterComponent',\n },\n },\n EuiDashboardButtonModule: {\n importPath: '@eui/components/eui-dashboard-card',\n selectors: {\n 'eui-dashboard-card': 'EuiDashboardCardComponent',\n 'eui-dashboard-card-content': 'EuiDashboardCardContentComponent',\n 'eui-dashboard-card-content-header': 'EuiDashboardCardContentHeaderComponent',\n 'eui-dashboard-card-content-body': 'EuiDashboardCardContentBodyComponent',\n 'eui-dashboard-card-content-footer': 'EuiDashboardCardContentFooterComponent',\n },\n },\n EuiDatepickerModule: {\n importPath: '@eui/components/eui-datepicker',\n selectors: {\n 'eui-datepicker': 'EuiDatepickerComponent',\n },\n },\n EuiDateRangeSelectorModule: {\n importPath: '@eui/components/eui-date-range-selector',\n selectors: {\n 'eui-date-range-selector': 'EuiDateRangeSelectorComponent',\n },\n },\n EuiDialogModule: {\n importPath: '@eui/components/eui-dialog',\n selectors: {\n 'eui-dialog': 'EuiDialogComponent',\n 'eui-dialog-header': 'EuiDialogHeaderDirective',\n 'eui-dialog-footer': 'EuiDialogFooterDirective',\n 'eui-dialog-container': 'EuiDialogContainerComponent',\n },\n },\n EuiDisableContentModule: {\n importPath: '@eui/components/eui-disable-content',\n selectors: {\n 'eui-disable-content': 'EuiDisableContentComponent',\n },\n },\n EuiDiscussionThreadModule: {\n importPath: '@eui/components/eui-discussion-thread',\n selectors: {\n 'eui-discussion-thread': 'EuiDiscussionThreadComponent',\n 'eui-discussion-thread-item': 'EuiDiscussionThreadItemComponent',\n },\n },\n EuiDropdownModule: {\n importPath: '@eui/components/eui-dropdown',\n selectors: {\n 'eui-dropdown': 'EuiDropdownComponent',\n },\n },\n EuiFeedbackMessageModule: {\n importPath: '@eui/components/eui-feedback-message',\n selectors: {\n 'eui-feedback-message': 'EuiFeedbackMessageComponent',\n },\n },\n EuiFieldsetModule: {\n importPath: '@eui/components/eui-fieldset',\n selectors: {\n 'eui-fieldset': 'EuiFieldsetComponent',\n euiFieldsetLabelRightContent: 'EuiFieldsetLabelRightContentTagDirective',\n euiFieldsetLabelExtraContent: 'EuiFieldsetLabelExtraContentTagDirective',\n },\n },\n EuiFileUploadModule: {\n importPath: '@eui/components/eui-file-upload',\n selectors: {\n 'eui-file-upload': 'EuiFileUploadComponent',\n },\n },\n EuiGrowlModule: {\n importPath: '@eui/components/eui-growl',\n selectors: {\n 'eui-growl': 'EuiGrowlComponent',\n },\n },\n EuiIconModule: {\n importPath: '@eui/components/eui-icon',\n selectors: {\n 'eui-icon-svg': 'EuiIconSvgComponent',\n euiIconSvg: 'EuiIconSvgComponent',\n },\n },\n EuiIconButtonModule: {\n importPath: '@eui/components/eui-icon-button',\n selectors: {\n 'eui-icon-button': 'EuiIconButtonComponent',\n },\n },\n EuiIconToggleModule: {\n importPath: '@eui/components/eui-icon-toggle',\n selectors: {\n 'eui-icon-toggle': 'EuiIconToggleComponent',\n },\n },\n EuiInputCheckboxModule: {\n importPath: '@eui/components/eui-input-checkbox',\n selectors: {\n euiInputCheckBox: 'EuiInputCheckboxComponent',\n },\n },\n EuiInputGroupModule: {\n importPath: '@eui/components/eui-input-group',\n selectors: {\n euiInputGroup: 'EuiInputGroupComponent',\n 'eui-input-group-addon': 'EuiInputGroupAddOnComponent',\n euiInputGroupAddOn: 'EuiInputGroupAddOnComponent',\n 'eui-input-group-addon-item': 'EuiInputGroupAddOnItemComponent',\n euiInputGroupAddOnItem: 'EuiInputGroupAddOnItemComponent',\n },\n },\n EuiInputNumberModule: {\n importPath: '@eui/components/eui-input-number',\n selectors: {\n euiInputNumber: 'EuiInputNumberComponent',\n },\n },\n EuiInputRadioModule: {\n importPath: '@eui/components/eui-input-radio',\n selectors: {\n euiInputRadio: 'EuiInputRadioComponent',\n },\n },\n EuiInputTextModule: {\n importPath: '@eui/components/eui-input-text',\n selectors: {\n euiInputText: 'EuiInputTextComponent',\n },\n },\n EuiLabelModule: {\n importPath: '@eui/components/eui-label',\n selectors: {\n 'eui-label': 'EuiLabelComponent',\n euiLabel: 'EuiLabelComponent',\n },\n },\n EuiListModule: {\n importPath: '@eui/components/eui-list',\n selectors: {\n 'eui-list': 'EuiListComponent',\n euiList: 'EuiListComponent',\n 'eui-list-item': 'EuiListItemComponent',\n euiListItem: 'EuiListItemComponent',\n },\n },\n EuiMenuModule: {\n importPath: '@eui/components/eui-menu',\n selectors: {\n 'eui-menu': 'EuiMenuComponent',\n 'eui-menu-item': 'EuiMenuItemComponent',\n },\n },\n EuiMessageBoxModule: {\n importPath: '@eui/components/eui-message-box',\n selectors: {\n 'eui-message-box': 'EuiMessageBoxComponent',\n 'eui-message-box-footer': 'EuiMessageBoxFooterDirective',\n },\n },\n EuiOverlayModule: {\n importPath: '@eui/components/eui-overlay',\n selectors: {\n 'eui-overlay': 'EuiOverlayComponent',\n },\n },\n EuiPageModule: {\n importPath: '@eui/components/eui-page',\n selectors: {\n 'eui-page': 'EuiPageComponent',\n },\n },\n EuiPaginatorModule: {\n importPath: '@eui/components/eui-paginator',\n selectors: {\n 'eui-paginator': 'EuiPaginatorComponent',\n },\n },\n EuiPopoverModule: {\n importPath: '@eui/components/eui-popover',\n selectors: {\n 'eui-popover': 'EuiPopoverComponent',\n },\n },\n EuiProgressBarModule: {\n importPath: '@eui/components/eui-progress-bar',\n selectors: {\n 'eui-progress-bar': 'EuiProgressBarComponent',\n },\n },\n EuiProgressCircleModule: {\n importPath: '@eui/components/eui-progress-circle',\n selectors: {\n 'eui-progress-circle': 'EuiProgressCircleComponent',\n },\n },\n EuiSelectModule: {\n importPath: '@eui/components/eui-select',\n selectors: {\n euiSelect: 'EuiSelectComponent',\n },\n },\n EuiSidebarMenuModule: {\n importPath: '@eui/components/eui-sidebar-menu',\n selectors: {\n 'eui-sidebar-menu': 'EuiSidebarMenuComponent',\n },\n },\n EuiSkeletonModule: {\n importPath: '@eui/components/eui-skeleton',\n selectors: {\n 'eui-skeleton': 'EuiSkeletonComponent',\n },\n },\n EuiSlideToggleModule: {\n importPath: '@eui/components/eui-slide-toggle',\n selectors: {\n 'eui-slide-toggle': 'EuiSlideToggleComponent',\n },\n },\n EuiTableModule: {\n importPath: '@eui/components/eui-table',\n selectors: {\n 'eui-table': 'EuiTableComponent',\n euiTable: 'EuiTableComponent',\n 'eui-table-filter': 'EuiTableFilterComponent',\n isSortable: 'EuiTableSortableColComponent',\n isStickyCol: 'EuiTableStickyColDirective',\n isHeaderSelectable: 'EuiTableSelectableHeaderComponent',\n isDataSelectable: 'EuiTableSelectableRowComponent',\n isExpandableRow: 'EuiTableExpandableRowDirective',\n },\n },\n EuiTableV2Module: {\n importPath: '@eui/components/eui-table',\n selectors: {\n 'eui-table': 'EuiTableComponent',\n euiTable: 'EuiTableComponent',\n 'eui-table-filter': 'EuiTableFilterComponent',\n isSortable: 'EuiTableSortableColComponent',\n isStickyCol: 'EuiTableStickyColDirective',\n isHeaderSelectable: 'EuiTableSelectableHeaderComponent',\n isDataSelectable: 'EuiTableSelectableRowComponent',\n isExpandableRow: 'EuiTableExpandableRowDirective',\n },\n },\n EuiTabsModule: {\n importPath: '@eui/components/eui-tabs',\n selectors: {\n 'eui-tabs': 'EuiTabsComponent',\n 'eui-tab': 'EuiTabComponent',\n 'eui-tab-header': 'EuiTabHeaderComponent',\n 'eui-tab-body': 'EuiTabBodyComponent',\n },\n },\n EuiTextAreaModule: {\n importPath: '@eui/components/eui-textarea',\n selectors: {\n euiTextArea: 'EuiTextareaComponent',\n },\n },\n EuiTimelineModule: {\n importPath: '@eui/components/eui-timeline',\n selectors: {\n 'eui-timeline': 'EuiTimelineComponent',\n 'eui-timeline-item': 'EuiTimelineItemComponent',\n },\n },\n EuiTimepickerModule: {\n importPath: '@eui/components/eui-timepicker',\n selectors: {\n 'eui-timepicker': 'EuiTimepickerComponent',\n },\n },\n EuiTreeModule: {\n importPath: '@eui/components/eui-tree',\n selectors: {\n 'eui-tree': 'EuiTreeComponent',\n },\n },\n EuiTreeListModule: {\n importPath: '@eui/components/eui-tree-list',\n selectors: {\n 'eui-tree-list': 'EuiTreeListComponent',\n 'eui-tree-list-item': 'EuiTreeListItemComponent',\n },\n },\n EuiUserProfileModule: {\n importPath: '@eui/components/eui-user-profile',\n selectors: {\n 'eui-user-profile': 'EuiUserProfileComponent',\n },\n },\n EuiWizardModule: {\n importPath: '@eui/components/eui-wizard',\n selectors: {\n 'eui-wizard': 'EuiWizardComponent',\n 'eui-wizard-step': 'EuiWizardStepComponent',\n },\n },\n EuiTooltipDirectiveModule: {\n importPath: '@eui/components/directives',\n selectors: {\n euiTooltip: 'EuiTooltipDirective',\n },\n },\n EuiTemplateDirectiveModule: {\n importPath: '@eui/components/directives',\n selectors: {\n euiTemplate: 'EuiTemplateDirective',\n },\n },\n EuiResizableDirectiveModule: {\n importPath: '@eui/components/directives',\n selectors: {\n euiResizable: 'EuiResizableDirective',\n 'eui-resizable': 'EuiResizableComponent',\n },\n },\n EuiMaxLengthDirectiveModule: {\n importPath: '@eui/components/directives',\n selectors: {\n euiEditorMaxlength: 'EuiMaxLengthDirective',\n },\n },\n EuiTruncatePipeModule: {\n importPath: '@eui/components/pipes',\n selectors: {\n euiTruncate: 'EuiTruncatePipe',\n },\n },\n EuiLayoutModule: {\n importPath: '@eui/components/layout',\n selectors: {\n 'eui-app': 'EuiAppComponent',\n 'eui-header': 'EuiHeaderComponent',\n 'eui-footer': 'EuiFooterComponent',\n 'eui-toolbar': 'EuiToolbarComponent',\n 'eui-sidebar-toggle': 'EuiSidebarToggleComponent',\n },\n },\n};\n\ninterface Schema {\n path?: string;\n dryRun?: boolean;\n}\n\nexport function migrateToStandalone(options: Schema = {}): Rule {\n return (tree: Tree, context: SchematicContext) => {\n const scanPath = options.path ? '/' + options.path.replace(/^\\.?\\//, '').replace(/\\/$/, '') : '';\n const allModuleNames = Object.keys(MODULE_MAPPINGS);\n\n visitDir(tree.getDir(scanPath || '/'), (path) => {\n const buffer = tree.read(path);\n if (!buffer) return;\n\n const source = buffer.toString('utf-8');\n if (!allModuleNames.some((m) => source.includes(m))) return;\n\n const sourceFile = ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true);\n const componentDecorators = findComponentDecorators(sourceFile);\n\n for (const decorator of componentDecorators) {\n const importsNode = findImportsArrayNode(decorator);\n if (!importsNode) continue;\n\n const currentSource = tree.read(path)!.toString('utf-8');\n const modulesInArray = allModuleNames.filter((m) => hasModuleInArray(importsNode, m, source));\n if (modulesInArray.length === 0) continue;\n\n const templateSelectors = getTemplateSelectors(tree, path, decorator, source);\n const replacements = buildReplacements(modulesInArray, templateSelectors);\n if (replacements.size === 0) continue;\n\n const result = applyReplacements(currentSource, path, replacements);\n if (options.dryRun) {\n logDryRun(context, `Would replace module imports with standalone imports in ${path}`);\n } else {\n tree.overwrite(path, result);\n }\n }\n });\n\n context.logger.info('Migration to standalone imports complete.');\n if (options.dryRun) {\n logDryRunNote(context);\n }\n return tree;\n };\n}\n\nfunction visitDir(dir: DirEntry, callback: (path: string) => void): void {\n for (const file of dir.subfiles) {\n if (file.endsWith('.d.ts')) continue;\n if (!file.endsWith('.ts')) continue;\n callback(`${dir.path}/${file}`);\n }\n for (const sub of dir.subdirs) {\n if (sub === 'node_modules' || sub === 'dist') continue;\n visitDir(dir.dir(sub), callback);\n }\n}\n\nfunction buildReplacements(moduleNames: string[], templateSelectors: Set<string>): Map<string, { components: string[]; importPath: string }> {\n const map = new Map<string, { components: string[]; importPath: string }>();\n\n for (const moduleName of moduleNames) {\n const mapping = MODULE_MAPPINGS[moduleName];\n const components: string[] = [];\n\n for (const [selector, component] of Object.entries(mapping.selectors)) {\n if (templateSelectors.has(selector)) {\n components.push(component);\n }\n }\n\n if (components.length > 0) {\n map.set(moduleName, { components: [...new Set(components)].sort(), importPath: mapping.importPath });\n }\n }\n\n return map;\n}\n\nfunction findComponentDecorators(sourceFile: ts.SourceFile): ts.Decorator[] {\n const decorators: ts.Decorator[] = [];\n const visit = (node: ts.Node): void => {\n if (ts.isClassDeclaration(node)) {\n const decs = ts.getDecorators(node);\n if (decs) {\n for (const dec of decs) {\n if (ts.isCallExpression(dec.expression) && ts.isIdentifier(dec.expression.expression) && dec.expression.expression.text === 'Component') {\n decorators.push(dec);\n }\n }\n }\n }\n ts.forEachChild(node, visit);\n };\n visit(sourceFile);\n return decorators;\n}\n\nfunction findImportsArrayNode(decorator: ts.Decorator): ts.ArrayLiteralExpression | undefined {\n const call = decorator.expression as ts.CallExpression;\n const metadata = call.arguments[0];\n if (!ts.isObjectLiteralExpression(metadata)) return undefined;\n\n for (const prop of metadata.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'imports') {\n if (ts.isArrayLiteralExpression(prop.initializer)) {\n return prop.initializer;\n }\n }\n }\n return undefined;\n}\n\nfunction hasModuleInArray(array: ts.ArrayLiteralExpression, moduleName: string, source: string): boolean {\n return array.elements.some((el) => source.slice(el.getStart(), el.getEnd()).trim() === moduleName);\n}\n\nfunction getTemplateSelectors(tree: Tree, tsPath: string, decorator: ts.Decorator, source: string): Set<string> {\n const call = decorator.expression as ts.CallExpression;\n const metadata = call.arguments[0] as ts.ObjectLiteralExpression;\n const allSelectors = Object.values(MODULE_MAPPINGS).flatMap((m) => Object.keys(m.selectors));\n\n for (const prop of metadata.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'template') {\n const init = prop.initializer;\n if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {\n return findSelectorsInTemplate(init.text, allSelectors);\n }\n }\n }\n\n for (const prop of metadata.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'templateUrl') {\n if (ts.isStringLiteral(prop.initializer)) {\n const dir = tsPath.substring(0, tsPath.lastIndexOf('/'));\n const templateBuffer = tree.read(`${dir}/${prop.initializer.text}`);\n if (templateBuffer) {\n return findSelectorsInTemplate(templateBuffer.toString('utf-8'), allSelectors);\n }\n }\n }\n }\n\n return new Set();\n}\n\nfunction findSelectorsInTemplate(html: string, knownSelectors: string[]): Set<string> {\n const selectors = new Set<string>();\n const parsed = parseTemplate(html, '', { preserveWhitespaces: true });\n\n const visit = (nodes: TmplAstNode[]): void => {\n for (const node of nodes) {\n if (node instanceof TmplAstElement) {\n if (knownSelectors.includes(node.name)) selectors.add(node.name);\n for (const attr of node.attributes) {\n if (knownSelectors.includes(attr.name)) selectors.add(attr.name);\n }\n visit(node.children);\n }\n }\n };\n visit(parsed.nodes);\n return selectors;\n}\n\nfunction applyReplacements(source: string, filePath: string, replacements: Map<string, { components: string[]; importPath: string }>): string {\n const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true);\n let result = replaceInImportsArray(source, sourceFile, replacements);\n result = updateEsImports(result, filePath, replacements);\n return result;\n}\n\nfunction replaceInImportsArray(source: string, sourceFile: ts.SourceFile, replacements: Map<string, { components: string[]; importPath: string }>): string {\n let result = source;\n const visit = (node: ts.Node): void => {\n if (ts.isClassDeclaration(node)) {\n const decs = ts.getDecorators(node);\n if (!decs) return;\n for (const dec of decs) {\n if (!ts.isCallExpression(dec.expression) || !ts.isIdentifier(dec.expression.expression) || dec.expression.expression.text !== 'Component') continue;\n const metadata = dec.expression.arguments[0];\n if (!ts.isObjectLiteralExpression(metadata)) continue;\n for (const prop of metadata.properties) {\n if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name) || prop.name.text !== 'imports') continue;\n if (!ts.isArrayLiteralExpression(prop.initializer)) continue;\n const newElements = prop.initializer.elements\n .map((el) => {\n const text = result.slice(el.getStart(sourceFile), el.getEnd()).trim();\n const replacement = replacements.get(text);\n return replacement ? replacement.components.join(', ') : text;\n })\n .join(', ');\n result = result.slice(0, prop.initializer.getStart(sourceFile) + 1) + newElements + result.slice(prop.initializer.getEnd() - 1);\n }\n }\n }\n ts.forEachChild(node, visit);\n };\n visit(sourceFile);\n return result;\n}\n\nfunction updateEsImports(source: string, filePath: string, replacements: Map<string, { components: string[]; importPath: string }>): string {\n let result = source;\n\n for (const [moduleName, { components, importPath }] of replacements) {\n const sf = ts.createSourceFile(filePath, result, ts.ScriptTarget.Latest, true);\n\n for (const stmt of sf.statements) {\n if (!ts.isImportDeclaration(stmt) || !stmt.importClause?.namedBindings || !ts.isNamedImports(stmt.importClause.namedBindings)) continue;\n const namedBindings = stmt.importClause.namedBindings;\n const importNames = namedBindings.elements.map((el) => el.name.text);\n if (!importNames.includes(moduleName)) continue;\n\n const moduleSpecifier = (stmt.moduleSpecifier as ts.StringLiteral).text;\n const remaining = importNames.filter((n) => n !== moduleName);\n const toAdd = components.filter((c) => !importNames.includes(c));\n\n if (moduleSpecifier === importPath) {\n const newNames = [...remaining, ...toAdd].sort();\n const newClause = `{ ${newNames.join(', ')} }`;\n result = result.slice(0, namedBindings.getStart(sf)) + newClause + result.slice(namedBindings.getEnd());\n } else {\n if (remaining.length > 0) {\n const newClause = `{ ${remaining.join(', ')} }`;\n result = result.slice(0, namedBindings.getStart(sf)) + newClause + result.slice(namedBindings.getEnd());\n } else {\n result = result.slice(0, stmt.getStart(sf)) + result.slice(stmt.getEnd()).replace(/^\\r?\\n/, '');\n }\n\n if (toAdd.length > 0) {\n const updatedSf = ts.createSourceFile(filePath, result, ts.ScriptTarget.Latest, true);\n const existing = updatedSf.statements.find(\n (s) => ts.isImportDeclaration(s) && ts.isStringLiteral(s.moduleSpecifier) && s.moduleSpecifier.text === importPath,\n ) as ts.ImportDeclaration | undefined;\n\n if (existing?.importClause?.namedBindings && ts.isNamedImports(existing.importClause.namedBindings)) {\n const existingNames = existing.importClause.namedBindings.elements.map((el) => el.name.text);\n const allNames = [...new Set([...existingNames, ...toAdd])].sort();\n const newClause = `{ ${allNames.join(', ')} }`;\n result = result.slice(0, existing.importClause.namedBindings.getStart(updatedSf)) + newClause + result.slice(existing.importClause.namedBindings.getEnd());\n } else {\n const newImport = `import { ${toAdd.join(', ')} } from '${importPath}';\\n`;\n result = newImport + result;\n }\n }\n }\n break;\n }\n }\n\n return result;\n}\n",
|
|
3203
3203
|
"displayName": "Schema",
|
|
3204
3204
|
"properties": [
|
|
3205
3205
|
{
|
|
@@ -3211,7 +3211,7 @@
|
|
|
3211
3211
|
"indexKey": "",
|
|
3212
3212
|
"optional": true,
|
|
3213
3213
|
"description": "",
|
|
3214
|
-
"line":
|
|
3214
|
+
"line": 460,
|
|
3215
3215
|
"rawdescription": "\n"
|
|
3216
3216
|
},
|
|
3217
3217
|
{
|
|
@@ -3223,7 +3223,7 @@
|
|
|
3223
3223
|
"indexKey": "",
|
|
3224
3224
|
"optional": true,
|
|
3225
3225
|
"description": "",
|
|
3226
|
-
"line":
|
|
3226
|
+
"line": 459,
|
|
3227
3227
|
"rawdescription": "\n"
|
|
3228
3228
|
}
|
|
3229
3229
|
],
|
|
@@ -24833,7 +24833,7 @@
|
|
|
24833
24833
|
},
|
|
24834
24834
|
{
|
|
24835
24835
|
"name": "applyEdits",
|
|
24836
|
-
"file": "packages/core/schematics/migrate-
|
|
24836
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
24837
24837
|
"ctype": "miscellaneous",
|
|
24838
24838
|
"subtype": "function",
|
|
24839
24839
|
"coverageIgnore": false,
|
|
@@ -24878,7 +24878,7 @@
|
|
|
24878
24878
|
},
|
|
24879
24879
|
{
|
|
24880
24880
|
"name": "applyEdits",
|
|
24881
|
-
"file": "packages/core/schematics/migrate-
|
|
24881
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
24882
24882
|
"ctype": "miscellaneous",
|
|
24883
24883
|
"subtype": "function",
|
|
24884
24884
|
"coverageIgnore": false,
|
|
@@ -27068,7 +27068,7 @@
|
|
|
27068
27068
|
},
|
|
27069
27069
|
{
|
|
27070
27070
|
"name": "deduplicateEdits",
|
|
27071
|
-
"file": "packages/core/schematics/migrate-
|
|
27071
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
27072
27072
|
"ctype": "miscellaneous",
|
|
27073
27073
|
"subtype": "function",
|
|
27074
27074
|
"coverageIgnore": false,
|
|
@@ -27098,7 +27098,7 @@
|
|
|
27098
27098
|
},
|
|
27099
27099
|
{
|
|
27100
27100
|
"name": "deduplicateEdits",
|
|
27101
|
-
"file": "packages/core/schematics/migrate-
|
|
27101
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
27102
27102
|
"ctype": "miscellaneous",
|
|
27103
27103
|
"subtype": "function",
|
|
27104
27104
|
"coverageIgnore": false,
|
|
@@ -33623,7 +33623,7 @@
|
|
|
33623
33623
|
},
|
|
33624
33624
|
{
|
|
33625
33625
|
"name": "migrateTypeScript",
|
|
33626
|
-
"file": "packages/core/schematics/migrate-
|
|
33626
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
33627
33627
|
"ctype": "miscellaneous",
|
|
33628
33628
|
"subtype": "function",
|
|
33629
33629
|
"coverageIgnore": false,
|
|
@@ -33645,12 +33645,6 @@
|
|
|
33645
33645
|
"deprecated": false,
|
|
33646
33646
|
"deprecationMessage": ""
|
|
33647
33647
|
},
|
|
33648
|
-
{
|
|
33649
|
-
"name": "tree",
|
|
33650
|
-
"type": "Tree",
|
|
33651
|
-
"deprecated": false,
|
|
33652
|
-
"deprecationMessage": ""
|
|
33653
|
-
},
|
|
33654
33648
|
{
|
|
33655
33649
|
"name": "context",
|
|
33656
33650
|
"type": "SchematicContext",
|
|
@@ -33658,7 +33652,7 @@
|
|
|
33658
33652
|
"deprecationMessage": ""
|
|
33659
33653
|
}
|
|
33660
33654
|
],
|
|
33661
|
-
"returnType": "
|
|
33655
|
+
"returnType": "string",
|
|
33662
33656
|
"jsdoctags": [
|
|
33663
33657
|
{
|
|
33664
33658
|
"name": "source",
|
|
@@ -33678,15 +33672,6 @@
|
|
|
33678
33672
|
"text": "param"
|
|
33679
33673
|
}
|
|
33680
33674
|
},
|
|
33681
|
-
{
|
|
33682
|
-
"name": "tree",
|
|
33683
|
-
"type": "Tree",
|
|
33684
|
-
"deprecated": false,
|
|
33685
|
-
"deprecationMessage": "",
|
|
33686
|
-
"tagName": {
|
|
33687
|
-
"text": "param"
|
|
33688
|
-
}
|
|
33689
|
-
},
|
|
33690
33675
|
{
|
|
33691
33676
|
"name": "context",
|
|
33692
33677
|
"type": "SchematicContext",
|
|
@@ -33700,7 +33685,7 @@
|
|
|
33700
33685
|
},
|
|
33701
33686
|
{
|
|
33702
33687
|
"name": "migrateTypeScript",
|
|
33703
|
-
"file": "packages/core/schematics/migrate-
|
|
33688
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
33704
33689
|
"ctype": "miscellaneous",
|
|
33705
33690
|
"subtype": "function",
|
|
33706
33691
|
"coverageIgnore": false,
|
|
@@ -33722,6 +33707,12 @@
|
|
|
33722
33707
|
"deprecated": false,
|
|
33723
33708
|
"deprecationMessage": ""
|
|
33724
33709
|
},
|
|
33710
|
+
{
|
|
33711
|
+
"name": "tree",
|
|
33712
|
+
"type": "Tree",
|
|
33713
|
+
"deprecated": false,
|
|
33714
|
+
"deprecationMessage": ""
|
|
33715
|
+
},
|
|
33725
33716
|
{
|
|
33726
33717
|
"name": "context",
|
|
33727
33718
|
"type": "SchematicContext",
|
|
@@ -33729,7 +33720,7 @@
|
|
|
33729
33720
|
"deprecationMessage": ""
|
|
33730
33721
|
}
|
|
33731
33722
|
],
|
|
33732
|
-
"returnType": "
|
|
33723
|
+
"returnType": "MigrateResult",
|
|
33733
33724
|
"jsdoctags": [
|
|
33734
33725
|
{
|
|
33735
33726
|
"name": "source",
|
|
@@ -33749,6 +33740,15 @@
|
|
|
33749
33740
|
"text": "param"
|
|
33750
33741
|
}
|
|
33751
33742
|
},
|
|
33743
|
+
{
|
|
33744
|
+
"name": "tree",
|
|
33745
|
+
"type": "Tree",
|
|
33746
|
+
"deprecated": false,
|
|
33747
|
+
"deprecationMessage": "",
|
|
33748
|
+
"tagName": {
|
|
33749
|
+
"text": "param"
|
|
33750
|
+
}
|
|
33751
|
+
},
|
|
33752
33752
|
{
|
|
33753
33753
|
"name": "context",
|
|
33754
33754
|
"type": "SchematicContext",
|
|
@@ -36664,7 +36664,7 @@
|
|
|
36664
36664
|
},
|
|
36665
36665
|
{
|
|
36666
36666
|
"name": "visitDir",
|
|
36667
|
-
"file": "packages/core/schematics/migrate-
|
|
36667
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
36668
36668
|
"ctype": "miscellaneous",
|
|
36669
36669
|
"subtype": "function",
|
|
36670
36670
|
"coverageIgnore": false,
|
|
@@ -36709,7 +36709,7 @@
|
|
|
36709
36709
|
},
|
|
36710
36710
|
{
|
|
36711
36711
|
"name": "visitDir",
|
|
36712
|
-
"file": "packages/core/schematics/migrate-to-
|
|
36712
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
36713
36713
|
"ctype": "miscellaneous",
|
|
36714
36714
|
"subtype": "function",
|
|
36715
36715
|
"coverageIgnore": false,
|
|
@@ -36754,7 +36754,7 @@
|
|
|
36754
36754
|
},
|
|
36755
36755
|
{
|
|
36756
36756
|
"name": "visitDir",
|
|
36757
|
-
"file": "packages/core/schematics/migrate-
|
|
36757
|
+
"file": "packages/core/schematics/migrate-to-standalone/index.ts",
|
|
36758
36758
|
"ctype": "miscellaneous",
|
|
36759
36759
|
"subtype": "function",
|
|
36760
36760
|
"coverageIgnore": false,
|
|
@@ -48390,10 +48390,10 @@
|
|
|
48390
48390
|
]
|
|
48391
48391
|
}
|
|
48392
48392
|
],
|
|
48393
|
-
"packages/core/schematics/migrate-
|
|
48393
|
+
"packages/core/schematics/migrate-eui-tooltip/index.ts": [
|
|
48394
48394
|
{
|
|
48395
48395
|
"name": "applyEdits",
|
|
48396
|
-
"file": "packages/core/schematics/migrate-
|
|
48396
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
48397
48397
|
"ctype": "miscellaneous",
|
|
48398
48398
|
"subtype": "function",
|
|
48399
48399
|
"coverageIgnore": false,
|
|
@@ -48436,189 +48436,9 @@
|
|
|
48436
48436
|
}
|
|
48437
48437
|
]
|
|
48438
48438
|
},
|
|
48439
|
-
{
|
|
48440
|
-
"name": "buildTemplateRefMap",
|
|
48441
|
-
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
48442
|
-
"ctype": "miscellaneous",
|
|
48443
|
-
"subtype": "function",
|
|
48444
|
-
"coverageIgnore": false,
|
|
48445
|
-
"deprecated": false,
|
|
48446
|
-
"deprecationMessage": "",
|
|
48447
|
-
"rawdescription": "Builds a map of template reference variable names (#ref) to EUI class names.\n\nParses the component's template (inline or external) and finds elements like:\n <eui-alert #myAlert> → myAlert → EuiAlertComponent\n <input euiAutocomplete #ac> → ac → EuiAutocompleteComponent\n\nReturns Map<refName, euiClassName>.",
|
|
48448
|
-
"description": "<p>Builds a map of template reference variable names (#ref) to EUI class names.</p>\n<p>Parses the component's template (inline or external) and finds elements like:\n <eui-alert #myAlert> → myAlert → EuiAlertComponent\n <input euiAutocomplete #ac> → ac → EuiAutocompleteComponent</p>\n<p>Returns Map<refName, euiClassName>.</p>\n",
|
|
48449
|
-
"displayName": "buildTemplateRefMap",
|
|
48450
|
-
"args": [
|
|
48451
|
-
{
|
|
48452
|
-
"name": "sourceFile",
|
|
48453
|
-
"deprecated": false,
|
|
48454
|
-
"deprecationMessage": ""
|
|
48455
|
-
},
|
|
48456
|
-
{
|
|
48457
|
-
"name": "source",
|
|
48458
|
-
"type": "string",
|
|
48459
|
-
"deprecated": false,
|
|
48460
|
-
"deprecationMessage": ""
|
|
48461
|
-
},
|
|
48462
|
-
{
|
|
48463
|
-
"name": "filePath",
|
|
48464
|
-
"type": "string",
|
|
48465
|
-
"deprecated": false,
|
|
48466
|
-
"deprecationMessage": ""
|
|
48467
|
-
},
|
|
48468
|
-
{
|
|
48469
|
-
"name": "tree",
|
|
48470
|
-
"type": "Tree",
|
|
48471
|
-
"deprecated": false,
|
|
48472
|
-
"deprecationMessage": ""
|
|
48473
|
-
}
|
|
48474
|
-
],
|
|
48475
|
-
"returnType": "Map<string, string>",
|
|
48476
|
-
"jsdoctags": [
|
|
48477
|
-
{
|
|
48478
|
-
"name": "sourceFile",
|
|
48479
|
-
"deprecated": false,
|
|
48480
|
-
"deprecationMessage": "",
|
|
48481
|
-
"tagName": {
|
|
48482
|
-
"text": "param"
|
|
48483
|
-
}
|
|
48484
|
-
},
|
|
48485
|
-
{
|
|
48486
|
-
"name": "source",
|
|
48487
|
-
"type": "string",
|
|
48488
|
-
"deprecated": false,
|
|
48489
|
-
"deprecationMessage": "",
|
|
48490
|
-
"tagName": {
|
|
48491
|
-
"text": "param"
|
|
48492
|
-
}
|
|
48493
|
-
},
|
|
48494
|
-
{
|
|
48495
|
-
"name": "filePath",
|
|
48496
|
-
"type": "string",
|
|
48497
|
-
"deprecated": false,
|
|
48498
|
-
"deprecationMessage": "",
|
|
48499
|
-
"tagName": {
|
|
48500
|
-
"text": "param"
|
|
48501
|
-
}
|
|
48502
|
-
},
|
|
48503
|
-
{
|
|
48504
|
-
"name": "tree",
|
|
48505
|
-
"type": "Tree",
|
|
48506
|
-
"deprecated": false,
|
|
48507
|
-
"deprecationMessage": "",
|
|
48508
|
-
"tagName": {
|
|
48509
|
-
"text": "param"
|
|
48510
|
-
}
|
|
48511
|
-
}
|
|
48512
|
-
]
|
|
48513
|
-
},
|
|
48514
|
-
{
|
|
48515
|
-
"name": "collectEuiReferences",
|
|
48516
|
-
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
48517
|
-
"ctype": "miscellaneous",
|
|
48518
|
-
"subtype": "function",
|
|
48519
|
-
"coverageIgnore": false,
|
|
48520
|
-
"deprecated": false,
|
|
48521
|
-
"deprecationMessage": "",
|
|
48522
|
-
"rawdescription": "Scans the source file for class properties or variables typed as EUI component classes.\nReturns a map: localName → Set of signal property names.\n\nHandles:",
|
|
48523
|
-
"description": "<p>Scans the source file for class properties or variables typed as EUI component classes.\nReturns a map: localName → Set of signal property names.</p>\n<p>Handles:</p>\n",
|
|
48524
|
-
"displayName": "collectEuiReferences",
|
|
48525
|
-
"args": [
|
|
48526
|
-
{
|
|
48527
|
-
"name": "sourceFile",
|
|
48528
|
-
"deprecated": false,
|
|
48529
|
-
"deprecationMessage": ""
|
|
48530
|
-
},
|
|
48531
|
-
{
|
|
48532
|
-
"name": "source",
|
|
48533
|
-
"type": "string",
|
|
48534
|
-
"deprecated": false,
|
|
48535
|
-
"deprecationMessage": ""
|
|
48536
|
-
},
|
|
48537
|
-
{
|
|
48538
|
-
"name": "templateRefMap",
|
|
48539
|
-
"type": "Map",
|
|
48540
|
-
"deprecated": false,
|
|
48541
|
-
"deprecationMessage": ""
|
|
48542
|
-
}
|
|
48543
|
-
],
|
|
48544
|
-
"returnType": "Map<string, ReadonlySet<string>>",
|
|
48545
|
-
"jsdoctags": [
|
|
48546
|
-
{
|
|
48547
|
-
"name": "sourceFile",
|
|
48548
|
-
"deprecated": false,
|
|
48549
|
-
"deprecationMessage": "",
|
|
48550
|
-
"tagName": {
|
|
48551
|
-
"text": "param"
|
|
48552
|
-
}
|
|
48553
|
-
},
|
|
48554
|
-
{
|
|
48555
|
-
"name": "source",
|
|
48556
|
-
"type": "string",
|
|
48557
|
-
"deprecated": false,
|
|
48558
|
-
"deprecationMessage": "",
|
|
48559
|
-
"tagName": {
|
|
48560
|
-
"text": "param"
|
|
48561
|
-
}
|
|
48562
|
-
},
|
|
48563
|
-
{
|
|
48564
|
-
"name": "templateRefMap",
|
|
48565
|
-
"type": "Map",
|
|
48566
|
-
"deprecated": false,
|
|
48567
|
-
"deprecationMessage": "",
|
|
48568
|
-
"tagName": {
|
|
48569
|
-
"text": "param"
|
|
48570
|
-
}
|
|
48571
|
-
}
|
|
48572
|
-
]
|
|
48573
|
-
},
|
|
48574
|
-
{
|
|
48575
|
-
"name": "collectTemplateRefs",
|
|
48576
|
-
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
48577
|
-
"ctype": "miscellaneous",
|
|
48578
|
-
"subtype": "function",
|
|
48579
|
-
"coverageIgnore": false,
|
|
48580
|
-
"deprecated": false,
|
|
48581
|
-
"deprecationMessage": "",
|
|
48582
|
-
"rawdescription": "Recursively walks template AST nodes, collecting #refName → EUI class name mappings.\n\nFor each element with a template reference (#ref), we try to resolve its EUI class:\n1. Match element tag name against EUI_SELECTOR_TO_CLASS (e.g. 'eui-alert' → EuiAlertComponent)\n2. Match element attributes against attribute selectors (e.g. [euiAutocomplete] → EuiAutocompleteComponent)",
|
|
48583
|
-
"description": "<p>Recursively walks template AST nodes, collecting #refName → EUI class name mappings.</p>\n<p>For each element with a template reference (#ref), we try to resolve its EUI class:</p>\n<ol>\n<li>Match element tag name against EUI_SELECTOR_TO_CLASS (e.g. 'eui-alert' → EuiAlertComponent)</li>\n<li>Match element attributes against attribute selectors (e.g. [euiAutocomplete] → EuiAutocompleteComponent)</li>\n</ol>\n",
|
|
48584
|
-
"displayName": "collectTemplateRefs",
|
|
48585
|
-
"args": [
|
|
48586
|
-
{
|
|
48587
|
-
"name": "nodes",
|
|
48588
|
-
"deprecated": false,
|
|
48589
|
-
"deprecationMessage": ""
|
|
48590
|
-
},
|
|
48591
|
-
{
|
|
48592
|
-
"name": "refMap",
|
|
48593
|
-
"type": "Map",
|
|
48594
|
-
"deprecated": false,
|
|
48595
|
-
"deprecationMessage": ""
|
|
48596
|
-
}
|
|
48597
|
-
],
|
|
48598
|
-
"returnType": "void",
|
|
48599
|
-
"jsdoctags": [
|
|
48600
|
-
{
|
|
48601
|
-
"name": "nodes",
|
|
48602
|
-
"deprecated": false,
|
|
48603
|
-
"deprecationMessage": "",
|
|
48604
|
-
"tagName": {
|
|
48605
|
-
"text": "param"
|
|
48606
|
-
}
|
|
48607
|
-
},
|
|
48608
|
-
{
|
|
48609
|
-
"name": "refMap",
|
|
48610
|
-
"type": "Map",
|
|
48611
|
-
"deprecated": false,
|
|
48612
|
-
"deprecationMessage": "",
|
|
48613
|
-
"tagName": {
|
|
48614
|
-
"text": "param"
|
|
48615
|
-
}
|
|
48616
|
-
}
|
|
48617
|
-
]
|
|
48618
|
-
},
|
|
48619
48439
|
{
|
|
48620
48440
|
"name": "deduplicateEdits",
|
|
48621
|
-
"file": "packages/core/schematics/migrate-
|
|
48441
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
48622
48442
|
"ctype": "miscellaneous",
|
|
48623
48443
|
"subtype": "function",
|
|
48624
48444
|
"coverageIgnore": false,
|
|
@@ -48647,42 +48467,27 @@
|
|
|
48647
48467
|
]
|
|
48648
48468
|
},
|
|
48649
48469
|
{
|
|
48650
|
-
"name": "
|
|
48651
|
-
"file": "packages/core/schematics/migrate-
|
|
48470
|
+
"name": "isPartOfImport",
|
|
48471
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
48652
48472
|
"ctype": "miscellaneous",
|
|
48653
48473
|
"subtype": "function",
|
|
48654
48474
|
"coverageIgnore": false,
|
|
48655
48475
|
"deprecated": false,
|
|
48656
48476
|
"deprecationMessage": "",
|
|
48657
|
-
"rawdescription": "
|
|
48658
|
-
"description": "
|
|
48659
|
-
"displayName": "
|
|
48477
|
+
"rawdescription": "",
|
|
48478
|
+
"description": "",
|
|
48479
|
+
"displayName": "isPartOfImport",
|
|
48660
48480
|
"args": [
|
|
48661
48481
|
{
|
|
48662
|
-
"name": "
|
|
48663
|
-
"deprecated": false,
|
|
48664
|
-
"deprecationMessage": ""
|
|
48665
|
-
},
|
|
48666
|
-
{
|
|
48667
|
-
"name": "source",
|
|
48668
|
-
"type": "string",
|
|
48482
|
+
"name": "node",
|
|
48669
48483
|
"deprecated": false,
|
|
48670
48484
|
"deprecationMessage": ""
|
|
48671
48485
|
}
|
|
48672
48486
|
],
|
|
48673
|
-
"returnType": "
|
|
48487
|
+
"returnType": "boolean",
|
|
48674
48488
|
"jsdoctags": [
|
|
48675
48489
|
{
|
|
48676
|
-
"name": "
|
|
48677
|
-
"deprecated": false,
|
|
48678
|
-
"deprecationMessage": "",
|
|
48679
|
-
"tagName": {
|
|
48680
|
-
"text": "param"
|
|
48681
|
-
}
|
|
48682
|
-
},
|
|
48683
|
-
{
|
|
48684
|
-
"name": "source",
|
|
48685
|
-
"type": "string",
|
|
48490
|
+
"name": "node",
|
|
48686
48491
|
"deprecated": false,
|
|
48687
48492
|
"deprecationMessage": "",
|
|
48688
48493
|
"tagName": {
|
|
@@ -48692,8 +48497,8 @@
|
|
|
48692
48497
|
]
|
|
48693
48498
|
},
|
|
48694
48499
|
{
|
|
48695
|
-
"name": "
|
|
48696
|
-
"file": "packages/core/schematics/migrate-
|
|
48500
|
+
"name": "migrateEuiTooltip",
|
|
48501
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
48697
48502
|
"ctype": "miscellaneous",
|
|
48698
48503
|
"subtype": "function",
|
|
48699
48504
|
"coverageIgnore": false,
|
|
@@ -48701,20 +48506,24 @@
|
|
|
48701
48506
|
"deprecationMessage": "",
|
|
48702
48507
|
"rawdescription": "",
|
|
48703
48508
|
"description": "",
|
|
48704
|
-
"displayName": "
|
|
48509
|
+
"displayName": "migrateEuiTooltip",
|
|
48705
48510
|
"args": [
|
|
48706
48511
|
{
|
|
48707
|
-
"name": "
|
|
48512
|
+
"name": "options",
|
|
48513
|
+
"type": "Schema",
|
|
48708
48514
|
"deprecated": false,
|
|
48709
|
-
"deprecationMessage": ""
|
|
48515
|
+
"deprecationMessage": "",
|
|
48516
|
+
"defaultValue": "{}"
|
|
48710
48517
|
}
|
|
48711
48518
|
],
|
|
48712
|
-
"returnType": "
|
|
48519
|
+
"returnType": "Rule",
|
|
48713
48520
|
"jsdoctags": [
|
|
48714
48521
|
{
|
|
48715
|
-
"name": "
|
|
48522
|
+
"name": "options",
|
|
48523
|
+
"type": "Schema",
|
|
48716
48524
|
"deprecated": false,
|
|
48717
48525
|
"deprecationMessage": "",
|
|
48526
|
+
"defaultValue": "{}",
|
|
48718
48527
|
"tagName": {
|
|
48719
48528
|
"text": "param"
|
|
48720
48529
|
}
|
|
@@ -48722,8 +48531,8 @@
|
|
|
48722
48531
|
]
|
|
48723
48532
|
},
|
|
48724
48533
|
{
|
|
48725
|
-
"name": "
|
|
48726
|
-
"file": "packages/core/schematics/migrate-
|
|
48534
|
+
"name": "migrateTypeScript",
|
|
48535
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
48727
48536
|
"ctype": "miscellaneous",
|
|
48728
48537
|
"subtype": "function",
|
|
48729
48538
|
"coverageIgnore": false,
|
|
@@ -48731,50 +48540,50 @@
|
|
|
48731
48540
|
"deprecationMessage": "",
|
|
48732
48541
|
"rawdescription": "",
|
|
48733
48542
|
"description": "",
|
|
48734
|
-
"displayName": "
|
|
48543
|
+
"displayName": "migrateTypeScript",
|
|
48735
48544
|
"args": [
|
|
48736
48545
|
{
|
|
48737
|
-
"name": "
|
|
48546
|
+
"name": "source",
|
|
48547
|
+
"type": "string",
|
|
48548
|
+
"deprecated": false,
|
|
48549
|
+
"deprecationMessage": ""
|
|
48550
|
+
},
|
|
48551
|
+
{
|
|
48552
|
+
"name": "filePath",
|
|
48553
|
+
"type": "string",
|
|
48554
|
+
"deprecated": false,
|
|
48555
|
+
"deprecationMessage": ""
|
|
48556
|
+
},
|
|
48557
|
+
{
|
|
48558
|
+
"name": "context",
|
|
48559
|
+
"type": "SchematicContext",
|
|
48738
48560
|
"deprecated": false,
|
|
48739
48561
|
"deprecationMessage": ""
|
|
48740
48562
|
}
|
|
48741
48563
|
],
|
|
48742
|
-
"returnType": "string
|
|
48564
|
+
"returnType": "string",
|
|
48743
48565
|
"jsdoctags": [
|
|
48744
48566
|
{
|
|
48745
|
-
"name": "
|
|
48567
|
+
"name": "source",
|
|
48568
|
+
"type": "string",
|
|
48746
48569
|
"deprecated": false,
|
|
48747
48570
|
"deprecationMessage": "",
|
|
48748
48571
|
"tagName": {
|
|
48749
48572
|
"text": "param"
|
|
48750
48573
|
}
|
|
48751
|
-
}
|
|
48752
|
-
]
|
|
48753
|
-
},
|
|
48754
|
-
{
|
|
48755
|
-
"name": "hasEuiReference",
|
|
48756
|
-
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
48757
|
-
"ctype": "miscellaneous",
|
|
48758
|
-
"subtype": "function",
|
|
48759
|
-
"coverageIgnore": false,
|
|
48760
|
-
"deprecated": false,
|
|
48761
|
-
"deprecationMessage": "",
|
|
48762
|
-
"rawdescription": "Quick check: does the file reference any EUI class name from the signal map?",
|
|
48763
|
-
"description": "<p>Quick check: does the file reference any EUI class name from the signal map?</p>\n",
|
|
48764
|
-
"displayName": "hasEuiReference",
|
|
48765
|
-
"args": [
|
|
48574
|
+
},
|
|
48766
48575
|
{
|
|
48767
|
-
"name": "
|
|
48576
|
+
"name": "filePath",
|
|
48768
48577
|
"type": "string",
|
|
48769
48578
|
"deprecated": false,
|
|
48770
|
-
"deprecationMessage": ""
|
|
48771
|
-
|
|
48772
|
-
|
|
48773
|
-
|
|
48774
|
-
|
|
48579
|
+
"deprecationMessage": "",
|
|
48580
|
+
"tagName": {
|
|
48581
|
+
"text": "param"
|
|
48582
|
+
}
|
|
48583
|
+
},
|
|
48775
48584
|
{
|
|
48776
|
-
"name": "
|
|
48777
|
-
"type": "
|
|
48585
|
+
"name": "context",
|
|
48586
|
+
"type": "SchematicContext",
|
|
48778
48587
|
"deprecated": false,
|
|
48779
48588
|
"deprecationMessage": "",
|
|
48780
48589
|
"tagName": {
|
|
@@ -48784,29 +48593,66 @@
|
|
|
48784
48593
|
]
|
|
48785
48594
|
},
|
|
48786
48595
|
{
|
|
48787
|
-
"name": "
|
|
48788
|
-
"file": "packages/core/schematics/migrate-
|
|
48596
|
+
"name": "removeImportSpecifier",
|
|
48597
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
48789
48598
|
"ctype": "miscellaneous",
|
|
48790
48599
|
"subtype": "function",
|
|
48791
48600
|
"coverageIgnore": false,
|
|
48792
48601
|
"deprecated": false,
|
|
48793
48602
|
"deprecationMessage": "",
|
|
48794
|
-
"rawdescription": "
|
|
48795
|
-
"description": "
|
|
48796
|
-
"displayName": "
|
|
48603
|
+
"rawdescription": "",
|
|
48604
|
+
"description": "",
|
|
48605
|
+
"displayName": "removeImportSpecifier",
|
|
48797
48606
|
"args": [
|
|
48798
48607
|
{
|
|
48799
|
-
"name": "
|
|
48800
|
-
"
|
|
48608
|
+
"name": "namedImports",
|
|
48609
|
+
"deprecated": false,
|
|
48610
|
+
"deprecationMessage": ""
|
|
48611
|
+
},
|
|
48612
|
+
{
|
|
48613
|
+
"name": "specifier",
|
|
48614
|
+
"deprecated": false,
|
|
48615
|
+
"deprecationMessage": ""
|
|
48616
|
+
},
|
|
48617
|
+
{
|
|
48618
|
+
"name": "sourceFile",
|
|
48619
|
+
"deprecated": false,
|
|
48620
|
+
"deprecationMessage": ""
|
|
48621
|
+
},
|
|
48622
|
+
{
|
|
48623
|
+
"name": "edits",
|
|
48801
48624
|
"deprecated": false,
|
|
48802
48625
|
"deprecationMessage": ""
|
|
48803
48626
|
}
|
|
48804
48627
|
],
|
|
48805
|
-
"returnType": "
|
|
48628
|
+
"returnType": "void",
|
|
48806
48629
|
"jsdoctags": [
|
|
48807
48630
|
{
|
|
48808
|
-
"name": "
|
|
48809
|
-
"
|
|
48631
|
+
"name": "namedImports",
|
|
48632
|
+
"deprecated": false,
|
|
48633
|
+
"deprecationMessage": "",
|
|
48634
|
+
"tagName": {
|
|
48635
|
+
"text": "param"
|
|
48636
|
+
}
|
|
48637
|
+
},
|
|
48638
|
+
{
|
|
48639
|
+
"name": "specifier",
|
|
48640
|
+
"deprecated": false,
|
|
48641
|
+
"deprecationMessage": "",
|
|
48642
|
+
"tagName": {
|
|
48643
|
+
"text": "param"
|
|
48644
|
+
}
|
|
48645
|
+
},
|
|
48646
|
+
{
|
|
48647
|
+
"name": "sourceFile",
|
|
48648
|
+
"deprecated": false,
|
|
48649
|
+
"deprecationMessage": "",
|
|
48650
|
+
"tagName": {
|
|
48651
|
+
"text": "param"
|
|
48652
|
+
}
|
|
48653
|
+
},
|
|
48654
|
+
{
|
|
48655
|
+
"name": "edits",
|
|
48810
48656
|
"deprecated": false,
|
|
48811
48657
|
"deprecationMessage": "",
|
|
48812
48658
|
"tagName": {
|
|
@@ -48816,8 +48662,8 @@
|
|
|
48816
48662
|
]
|
|
48817
48663
|
},
|
|
48818
48664
|
{
|
|
48819
|
-
"name": "
|
|
48820
|
-
"file": "packages/core/schematics/migrate-
|
|
48665
|
+
"name": "visitDir",
|
|
48666
|
+
"file": "packages/core/schematics/migrate-eui-tooltip/index.ts",
|
|
48821
48667
|
"ctype": "miscellaneous",
|
|
48822
48668
|
"subtype": "function",
|
|
48823
48669
|
"coverageIgnore": false,
|
|
@@ -48825,18 +48671,33 @@
|
|
|
48825
48671
|
"deprecationMessage": "",
|
|
48826
48672
|
"rawdescription": "",
|
|
48827
48673
|
"description": "",
|
|
48828
|
-
"displayName": "
|
|
48674
|
+
"displayName": "visitDir",
|
|
48829
48675
|
"args": [
|
|
48830
48676
|
{
|
|
48831
|
-
"name": "
|
|
48677
|
+
"name": "dir",
|
|
48678
|
+
"type": "DirEntry",
|
|
48679
|
+
"deprecated": false,
|
|
48680
|
+
"deprecationMessage": ""
|
|
48681
|
+
},
|
|
48682
|
+
{
|
|
48683
|
+
"name": "callback",
|
|
48832
48684
|
"deprecated": false,
|
|
48833
48685
|
"deprecationMessage": ""
|
|
48834
48686
|
}
|
|
48835
48687
|
],
|
|
48836
|
-
"returnType": "
|
|
48688
|
+
"returnType": "void",
|
|
48837
48689
|
"jsdoctags": [
|
|
48838
48690
|
{
|
|
48839
|
-
"name": "
|
|
48691
|
+
"name": "dir",
|
|
48692
|
+
"type": "DirEntry",
|
|
48693
|
+
"deprecated": false,
|
|
48694
|
+
"deprecationMessage": "",
|
|
48695
|
+
"tagName": {
|
|
48696
|
+
"text": "param"
|
|
48697
|
+
}
|
|
48698
|
+
},
|
|
48699
|
+
{
|
|
48700
|
+
"name": "callback",
|
|
48840
48701
|
"deprecated": false,
|
|
48841
48702
|
"deprecationMessage": "",
|
|
48842
48703
|
"tagName": {
|
|
@@ -48844,35 +48705,48 @@
|
|
|
48844
48705
|
}
|
|
48845
48706
|
}
|
|
48846
48707
|
]
|
|
48847
|
-
}
|
|
48708
|
+
}
|
|
48709
|
+
],
|
|
48710
|
+
"packages/core/schematics/migrate-to-signals/index.ts": [
|
|
48848
48711
|
{
|
|
48849
|
-
"name": "
|
|
48712
|
+
"name": "applyEdits",
|
|
48850
48713
|
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
48851
48714
|
"ctype": "miscellaneous",
|
|
48852
48715
|
"subtype": "function",
|
|
48853
48716
|
"coverageIgnore": false,
|
|
48854
48717
|
"deprecated": false,
|
|
48855
48718
|
"deprecationMessage": "",
|
|
48856
|
-
"rawdescription": "
|
|
48857
|
-
"description": "
|
|
48858
|
-
"displayName": "
|
|
48719
|
+
"rawdescription": "",
|
|
48720
|
+
"description": "",
|
|
48721
|
+
"displayName": "applyEdits",
|
|
48859
48722
|
"args": [
|
|
48860
48723
|
{
|
|
48861
|
-
"name": "
|
|
48862
|
-
"type": "
|
|
48724
|
+
"name": "source",
|
|
48725
|
+
"type": "string",
|
|
48863
48726
|
"deprecated": false,
|
|
48864
|
-
"deprecationMessage": ""
|
|
48865
|
-
|
|
48727
|
+
"deprecationMessage": ""
|
|
48728
|
+
},
|
|
48729
|
+
{
|
|
48730
|
+
"name": "edits",
|
|
48731
|
+
"deprecated": false,
|
|
48732
|
+
"deprecationMessage": ""
|
|
48866
48733
|
}
|
|
48867
48734
|
],
|
|
48868
|
-
"returnType": "
|
|
48735
|
+
"returnType": "string",
|
|
48869
48736
|
"jsdoctags": [
|
|
48870
48737
|
{
|
|
48871
|
-
"name": "
|
|
48872
|
-
"type": "
|
|
48738
|
+
"name": "source",
|
|
48739
|
+
"type": "string",
|
|
48740
|
+
"deprecated": false,
|
|
48741
|
+
"deprecationMessage": "",
|
|
48742
|
+
"tagName": {
|
|
48743
|
+
"text": "param"
|
|
48744
|
+
}
|
|
48745
|
+
},
|
|
48746
|
+
{
|
|
48747
|
+
"name": "edits",
|
|
48873
48748
|
"deprecated": false,
|
|
48874
48749
|
"deprecationMessage": "",
|
|
48875
|
-
"defaultValue": "{}",
|
|
48876
48750
|
"tagName": {
|
|
48877
48751
|
"text": "param"
|
|
48878
48752
|
}
|
|
@@ -48880,17 +48754,22 @@
|
|
|
48880
48754
|
]
|
|
48881
48755
|
},
|
|
48882
48756
|
{
|
|
48883
|
-
"name": "
|
|
48757
|
+
"name": "buildTemplateRefMap",
|
|
48884
48758
|
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
48885
48759
|
"ctype": "miscellaneous",
|
|
48886
48760
|
"subtype": "function",
|
|
48887
48761
|
"coverageIgnore": false,
|
|
48888
48762
|
"deprecated": false,
|
|
48889
48763
|
"deprecationMessage": "",
|
|
48890
|
-
"rawdescription": "",
|
|
48891
|
-
"description": "",
|
|
48892
|
-
"displayName": "
|
|
48764
|
+
"rawdescription": "Builds a map of template reference variable names (#ref) to EUI class names.\n\nParses the component's template (inline or external) and finds elements like:\n <eui-alert #myAlert> → myAlert → EuiAlertComponent\n <input euiAutocomplete #ac> → ac → EuiAutocompleteComponent\n\nReturns Map<refName, euiClassName>.",
|
|
48765
|
+
"description": "<p>Builds a map of template reference variable names (#ref) to EUI class names.</p>\n<p>Parses the component's template (inline or external) and finds elements like:\n <eui-alert #myAlert> → myAlert → EuiAlertComponent\n <input euiAutocomplete #ac> → ac → EuiAutocompleteComponent</p>\n<p>Returns Map<refName, euiClassName>.</p>\n",
|
|
48766
|
+
"displayName": "buildTemplateRefMap",
|
|
48893
48767
|
"args": [
|
|
48768
|
+
{
|
|
48769
|
+
"name": "sourceFile",
|
|
48770
|
+
"deprecated": false,
|
|
48771
|
+
"deprecationMessage": ""
|
|
48772
|
+
},
|
|
48894
48773
|
{
|
|
48895
48774
|
"name": "source",
|
|
48896
48775
|
"type": "string",
|
|
@@ -48908,19 +48787,12 @@
|
|
|
48908
48787
|
"type": "Tree",
|
|
48909
48788
|
"deprecated": false,
|
|
48910
48789
|
"deprecationMessage": ""
|
|
48911
|
-
},
|
|
48912
|
-
{
|
|
48913
|
-
"name": "context",
|
|
48914
|
-
"type": "SchematicContext",
|
|
48915
|
-
"deprecated": false,
|
|
48916
|
-
"deprecationMessage": ""
|
|
48917
48790
|
}
|
|
48918
48791
|
],
|
|
48919
|
-
"returnType": "
|
|
48792
|
+
"returnType": "Map<string, string>",
|
|
48920
48793
|
"jsdoctags": [
|
|
48921
48794
|
{
|
|
48922
|
-
"name": "
|
|
48923
|
-
"type": "string",
|
|
48795
|
+
"name": "sourceFile",
|
|
48924
48796
|
"deprecated": false,
|
|
48925
48797
|
"deprecationMessage": "",
|
|
48926
48798
|
"tagName": {
|
|
@@ -48928,7 +48800,7 @@
|
|
|
48928
48800
|
}
|
|
48929
48801
|
},
|
|
48930
48802
|
{
|
|
48931
|
-
"name": "
|
|
48803
|
+
"name": "source",
|
|
48932
48804
|
"type": "string",
|
|
48933
48805
|
"deprecated": false,
|
|
48934
48806
|
"deprecationMessage": "",
|
|
@@ -48937,8 +48809,8 @@
|
|
|
48937
48809
|
}
|
|
48938
48810
|
},
|
|
48939
48811
|
{
|
|
48940
|
-
"name": "
|
|
48941
|
-
"type": "
|
|
48812
|
+
"name": "filePath",
|
|
48813
|
+
"type": "string",
|
|
48942
48814
|
"deprecated": false,
|
|
48943
48815
|
"deprecationMessage": "",
|
|
48944
48816
|
"tagName": {
|
|
@@ -48946,8 +48818,8 @@
|
|
|
48946
48818
|
}
|
|
48947
48819
|
},
|
|
48948
48820
|
{
|
|
48949
|
-
"name": "
|
|
48950
|
-
"type": "
|
|
48821
|
+
"name": "tree",
|
|
48822
|
+
"type": "Tree",
|
|
48951
48823
|
"deprecated": false,
|
|
48952
48824
|
"deprecationMessage": "",
|
|
48953
48825
|
"tagName": {
|
|
@@ -48957,29 +48829,57 @@
|
|
|
48957
48829
|
]
|
|
48958
48830
|
},
|
|
48959
48831
|
{
|
|
48960
|
-
"name": "
|
|
48832
|
+
"name": "collectEuiReferences",
|
|
48961
48833
|
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
48962
48834
|
"ctype": "miscellaneous",
|
|
48963
48835
|
"subtype": "function",
|
|
48964
48836
|
"coverageIgnore": false,
|
|
48965
48837
|
"deprecated": false,
|
|
48966
48838
|
"deprecationMessage": "",
|
|
48967
|
-
"rawdescription": "
|
|
48968
|
-
"description": "<p>
|
|
48969
|
-
"displayName": "
|
|
48839
|
+
"rawdescription": "Scans the source file for class properties or variables typed as EUI component classes.\nReturns a map: localName → Set of signal property names.\n\nHandles:",
|
|
48840
|
+
"description": "<p>Scans the source file for class properties or variables typed as EUI component classes.\nReturns a map: localName → Set of signal property names.</p>\n<p>Handles:</p>\n",
|
|
48841
|
+
"displayName": "collectEuiReferences",
|
|
48970
48842
|
"args": [
|
|
48971
48843
|
{
|
|
48972
|
-
"name": "
|
|
48973
|
-
"
|
|
48844
|
+
"name": "sourceFile",
|
|
48845
|
+
"deprecated": false,
|
|
48846
|
+
"deprecationMessage": ""
|
|
48847
|
+
},
|
|
48848
|
+
{
|
|
48849
|
+
"name": "source",
|
|
48850
|
+
"type": "string",
|
|
48851
|
+
"deprecated": false,
|
|
48852
|
+
"deprecationMessage": ""
|
|
48853
|
+
},
|
|
48854
|
+
{
|
|
48855
|
+
"name": "templateRefMap",
|
|
48856
|
+
"type": "Map",
|
|
48974
48857
|
"deprecated": false,
|
|
48975
48858
|
"deprecationMessage": ""
|
|
48976
48859
|
}
|
|
48977
48860
|
],
|
|
48978
|
-
"returnType": "string
|
|
48861
|
+
"returnType": "Map<string, ReadonlySet<string>>",
|
|
48979
48862
|
"jsdoctags": [
|
|
48980
48863
|
{
|
|
48981
|
-
"name": "
|
|
48982
|
-
"
|
|
48864
|
+
"name": "sourceFile",
|
|
48865
|
+
"deprecated": false,
|
|
48866
|
+
"deprecationMessage": "",
|
|
48867
|
+
"tagName": {
|
|
48868
|
+
"text": "param"
|
|
48869
|
+
}
|
|
48870
|
+
},
|
|
48871
|
+
{
|
|
48872
|
+
"name": "source",
|
|
48873
|
+
"type": "string",
|
|
48874
|
+
"deprecated": false,
|
|
48875
|
+
"deprecationMessage": "",
|
|
48876
|
+
"tagName": {
|
|
48877
|
+
"text": "param"
|
|
48878
|
+
}
|
|
48879
|
+
},
|
|
48880
|
+
{
|
|
48881
|
+
"name": "templateRefMap",
|
|
48882
|
+
"type": "Map",
|
|
48983
48883
|
"deprecated": false,
|
|
48984
48884
|
"deprecationMessage": "",
|
|
48985
48885
|
"tagName": {
|
|
@@ -48989,46 +48889,33 @@
|
|
|
48989
48889
|
]
|
|
48990
48890
|
},
|
|
48991
48891
|
{
|
|
48992
|
-
"name": "
|
|
48892
|
+
"name": "collectTemplateRefs",
|
|
48993
48893
|
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
48994
48894
|
"ctype": "miscellaneous",
|
|
48995
48895
|
"subtype": "function",
|
|
48996
48896
|
"coverageIgnore": false,
|
|
48997
48897
|
"deprecated": false,
|
|
48998
48898
|
"deprecationMessage": "",
|
|
48999
|
-
"rawdescription": "
|
|
49000
|
-
"description": "<p>
|
|
49001
|
-
"displayName": "
|
|
48899
|
+
"rawdescription": "Recursively walks template AST nodes, collecting #refName → EUI class name mappings.\n\nFor each element with a template reference (#ref), we try to resolve its EUI class:\n1. Match element tag name against EUI_SELECTOR_TO_CLASS (e.g. 'eui-alert' → EuiAlertComponent)\n2. Match element attributes against attribute selectors (e.g. [euiAutocomplete] → EuiAutocompleteComponent)",
|
|
48900
|
+
"description": "<p>Recursively walks template AST nodes, collecting #refName → EUI class name mappings.</p>\n<p>For each element with a template reference (#ref), we try to resolve its EUI class:</p>\n<ol>\n<li>Match element tag name against EUI_SELECTOR_TO_CLASS (e.g. 'eui-alert' → EuiAlertComponent)</li>\n<li>Match element attributes against attribute selectors (e.g. [euiAutocomplete] → EuiAutocompleteComponent)</li>\n</ol>\n",
|
|
48901
|
+
"displayName": "collectTemplateRefs",
|
|
49002
48902
|
"args": [
|
|
49003
48903
|
{
|
|
49004
|
-
"name": "
|
|
49005
|
-
"deprecated": false,
|
|
49006
|
-
"deprecationMessage": ""
|
|
49007
|
-
},
|
|
49008
|
-
{
|
|
49009
|
-
"name": "sourceFile",
|
|
48904
|
+
"name": "nodes",
|
|
49010
48905
|
"deprecated": false,
|
|
49011
48906
|
"deprecationMessage": ""
|
|
49012
48907
|
},
|
|
49013
48908
|
{
|
|
49014
|
-
"name": "
|
|
48909
|
+
"name": "refMap",
|
|
49015
48910
|
"type": "Map",
|
|
49016
48911
|
"deprecated": false,
|
|
49017
48912
|
"deprecationMessage": ""
|
|
49018
48913
|
}
|
|
49019
48914
|
],
|
|
49020
|
-
"returnType": "
|
|
48915
|
+
"returnType": "void",
|
|
49021
48916
|
"jsdoctags": [
|
|
49022
48917
|
{
|
|
49023
|
-
"name": "
|
|
49024
|
-
"deprecated": false,
|
|
49025
|
-
"deprecationMessage": "",
|
|
49026
|
-
"tagName": {
|
|
49027
|
-
"text": "param"
|
|
49028
|
-
}
|
|
49029
|
-
},
|
|
49030
|
-
{
|
|
49031
|
-
"name": "sourceFile",
|
|
48918
|
+
"name": "nodes",
|
|
49032
48919
|
"deprecated": false,
|
|
49033
48920
|
"deprecationMessage": "",
|
|
49034
48921
|
"tagName": {
|
|
@@ -49036,7 +48923,7 @@
|
|
|
49036
48923
|
}
|
|
49037
48924
|
},
|
|
49038
48925
|
{
|
|
49039
|
-
"name": "
|
|
48926
|
+
"name": "refMap",
|
|
49040
48927
|
"type": "Map",
|
|
49041
48928
|
"deprecated": false,
|
|
49042
48929
|
"deprecationMessage": "",
|
|
@@ -49047,27 +48934,27 @@
|
|
|
49047
48934
|
]
|
|
49048
48935
|
},
|
|
49049
48936
|
{
|
|
49050
|
-
"name": "
|
|
48937
|
+
"name": "deduplicateEdits",
|
|
49051
48938
|
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49052
48939
|
"ctype": "miscellaneous",
|
|
49053
48940
|
"subtype": "function",
|
|
49054
48941
|
"coverageIgnore": false,
|
|
49055
48942
|
"deprecated": false,
|
|
49056
48943
|
"deprecationMessage": "",
|
|
49057
|
-
"rawdescription": "
|
|
49058
|
-
"description": "
|
|
49059
|
-
"displayName": "
|
|
48944
|
+
"rawdescription": "",
|
|
48945
|
+
"description": "",
|
|
48946
|
+
"displayName": "deduplicateEdits",
|
|
49060
48947
|
"args": [
|
|
49061
48948
|
{
|
|
49062
|
-
"name": "
|
|
48949
|
+
"name": "edits",
|
|
49063
48950
|
"deprecated": false,
|
|
49064
48951
|
"deprecationMessage": ""
|
|
49065
48952
|
}
|
|
49066
48953
|
],
|
|
49067
|
-
"returnType": "
|
|
48954
|
+
"returnType": "Edit[]",
|
|
49068
48955
|
"jsdoctags": [
|
|
49069
48956
|
{
|
|
49070
|
-
"name": "
|
|
48957
|
+
"name": "edits",
|
|
49071
48958
|
"deprecated": false,
|
|
49072
48959
|
"deprecationMessage": "",
|
|
49073
48960
|
"tagName": {
|
|
@@ -49077,28 +48964,17 @@
|
|
|
49077
48964
|
]
|
|
49078
48965
|
},
|
|
49079
48966
|
{
|
|
49080
|
-
"name": "
|
|
48967
|
+
"name": "findComponentMetadata",
|
|
49081
48968
|
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49082
48969
|
"ctype": "miscellaneous",
|
|
49083
48970
|
"subtype": "function",
|
|
49084
48971
|
"coverageIgnore": false,
|
|
49085
48972
|
"deprecated": false,
|
|
49086
48973
|
"deprecationMessage": "",
|
|
49087
|
-
"rawdescription": "
|
|
49088
|
-
"description": "<p>
|
|
49089
|
-
"displayName": "
|
|
48974
|
+
"rawdescription": "Extracts",
|
|
48975
|
+
"description": "<p>Extracts</p>\n",
|
|
48976
|
+
"displayName": "findComponentMetadata",
|
|
49090
48977
|
"args": [
|
|
49091
|
-
{
|
|
49092
|
-
"name": "node",
|
|
49093
|
-
"deprecated": false,
|
|
49094
|
-
"deprecationMessage": ""
|
|
49095
|
-
},
|
|
49096
|
-
{
|
|
49097
|
-
"name": "euiRefs",
|
|
49098
|
-
"type": "Map",
|
|
49099
|
-
"deprecated": false,
|
|
49100
|
-
"deprecationMessage": ""
|
|
49101
|
-
},
|
|
49102
48978
|
{
|
|
49103
48979
|
"name": "sourceFile",
|
|
49104
48980
|
"deprecated": false,
|
|
@@ -49111,25 +48987,8 @@
|
|
|
49111
48987
|
"deprecationMessage": ""
|
|
49112
48988
|
}
|
|
49113
48989
|
],
|
|
49114
|
-
"returnType": "
|
|
48990
|
+
"returnType": "literal type | null",
|
|
49115
48991
|
"jsdoctags": [
|
|
49116
|
-
{
|
|
49117
|
-
"name": "node",
|
|
49118
|
-
"deprecated": false,
|
|
49119
|
-
"deprecationMessage": "",
|
|
49120
|
-
"tagName": {
|
|
49121
|
-
"text": "param"
|
|
49122
|
-
}
|
|
49123
|
-
},
|
|
49124
|
-
{
|
|
49125
|
-
"name": "euiRefs",
|
|
49126
|
-
"type": "Map",
|
|
49127
|
-
"deprecated": false,
|
|
49128
|
-
"deprecationMessage": "",
|
|
49129
|
-
"tagName": {
|
|
49130
|
-
"text": "param"
|
|
49131
|
-
}
|
|
49132
|
-
},
|
|
49133
48992
|
{
|
|
49134
48993
|
"name": "sourceFile",
|
|
49135
48994
|
"deprecated": false,
|
|
@@ -49150,7 +49009,7 @@
|
|
|
49150
49009
|
]
|
|
49151
49010
|
},
|
|
49152
49011
|
{
|
|
49153
|
-
"name": "
|
|
49012
|
+
"name": "getDecoratorName",
|
|
49154
49013
|
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49155
49014
|
"ctype": "miscellaneous",
|
|
49156
49015
|
"subtype": "function",
|
|
@@ -49159,18 +49018,18 @@
|
|
|
49159
49018
|
"deprecationMessage": "",
|
|
49160
49019
|
"rawdescription": "",
|
|
49161
49020
|
"description": "",
|
|
49162
|
-
"displayName": "
|
|
49021
|
+
"displayName": "getDecoratorName",
|
|
49163
49022
|
"args": [
|
|
49164
49023
|
{
|
|
49165
|
-
"name": "
|
|
49024
|
+
"name": "callExpr",
|
|
49166
49025
|
"deprecated": false,
|
|
49167
49026
|
"deprecationMessage": ""
|
|
49168
49027
|
}
|
|
49169
49028
|
],
|
|
49170
|
-
"returnType": "
|
|
49029
|
+
"returnType": "string | null",
|
|
49171
49030
|
"jsdoctags": [
|
|
49172
49031
|
{
|
|
49173
|
-
"name": "
|
|
49032
|
+
"name": "callExpr",
|
|
49174
49033
|
"deprecated": false,
|
|
49175
49034
|
"deprecationMessage": "",
|
|
49176
49035
|
"tagName": {
|
|
@@ -49180,7 +49039,7 @@
|
|
|
49180
49039
|
]
|
|
49181
49040
|
},
|
|
49182
49041
|
{
|
|
49183
|
-
"name": "
|
|
49042
|
+
"name": "getTypeAnnotationName",
|
|
49184
49043
|
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49185
49044
|
"ctype": "miscellaneous",
|
|
49186
49045
|
"subtype": "function",
|
|
@@ -49189,33 +49048,18 @@
|
|
|
49189
49048
|
"deprecationMessage": "",
|
|
49190
49049
|
"rawdescription": "",
|
|
49191
49050
|
"description": "",
|
|
49192
|
-
"displayName": "
|
|
49051
|
+
"displayName": "getTypeAnnotationName",
|
|
49193
49052
|
"args": [
|
|
49194
49053
|
{
|
|
49195
|
-
"name": "
|
|
49196
|
-
"type": "DirEntry",
|
|
49197
|
-
"deprecated": false,
|
|
49198
|
-
"deprecationMessage": ""
|
|
49199
|
-
},
|
|
49200
|
-
{
|
|
49201
|
-
"name": "callback",
|
|
49054
|
+
"name": "typeNode",
|
|
49202
49055
|
"deprecated": false,
|
|
49203
49056
|
"deprecationMessage": ""
|
|
49204
49057
|
}
|
|
49205
49058
|
],
|
|
49206
|
-
"returnType": "
|
|
49059
|
+
"returnType": "string | null",
|
|
49207
49060
|
"jsdoctags": [
|
|
49208
49061
|
{
|
|
49209
|
-
"name": "
|
|
49210
|
-
"type": "DirEntry",
|
|
49211
|
-
"deprecated": false,
|
|
49212
|
-
"deprecationMessage": "",
|
|
49213
|
-
"tagName": {
|
|
49214
|
-
"text": "param"
|
|
49215
|
-
}
|
|
49216
|
-
},
|
|
49217
|
-
{
|
|
49218
|
-
"name": "callback",
|
|
49062
|
+
"name": "typeNode",
|
|
49219
49063
|
"deprecated": false,
|
|
49220
49064
|
"deprecationMessage": "",
|
|
49221
49065
|
"tagName": {
|
|
@@ -49223,34 +49067,27 @@
|
|
|
49223
49067
|
}
|
|
49224
49068
|
}
|
|
49225
49069
|
]
|
|
49226
|
-
}
|
|
49227
|
-
],
|
|
49228
|
-
"packages/core/schematics/migrate-eui-tooltip/index.ts": [
|
|
49070
|
+
},
|
|
49229
49071
|
{
|
|
49230
|
-
"name": "
|
|
49231
|
-
"file": "packages/core/schematics/migrate-
|
|
49072
|
+
"name": "hasEuiReference",
|
|
49073
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49232
49074
|
"ctype": "miscellaneous",
|
|
49233
49075
|
"subtype": "function",
|
|
49234
49076
|
"coverageIgnore": false,
|
|
49235
49077
|
"deprecated": false,
|
|
49236
49078
|
"deprecationMessage": "",
|
|
49237
|
-
"rawdescription": "",
|
|
49238
|
-
"description": "",
|
|
49239
|
-
"displayName": "
|
|
49079
|
+
"rawdescription": "Quick check: does the file reference any EUI class name from the signal map?",
|
|
49080
|
+
"description": "<p>Quick check: does the file reference any EUI class name from the signal map?</p>\n",
|
|
49081
|
+
"displayName": "hasEuiReference",
|
|
49240
49082
|
"args": [
|
|
49241
49083
|
{
|
|
49242
49084
|
"name": "source",
|
|
49243
49085
|
"type": "string",
|
|
49244
49086
|
"deprecated": false,
|
|
49245
49087
|
"deprecationMessage": ""
|
|
49246
|
-
},
|
|
49247
|
-
{
|
|
49248
|
-
"name": "edits",
|
|
49249
|
-
"deprecated": false,
|
|
49250
|
-
"deprecationMessage": ""
|
|
49251
49088
|
}
|
|
49252
49089
|
],
|
|
49253
|
-
"returnType": "
|
|
49090
|
+
"returnType": "boolean",
|
|
49254
49091
|
"jsdoctags": [
|
|
49255
49092
|
{
|
|
49256
49093
|
"name": "source",
|
|
@@ -49260,39 +49097,33 @@
|
|
|
49260
49097
|
"tagName": {
|
|
49261
49098
|
"text": "param"
|
|
49262
49099
|
}
|
|
49263
|
-
},
|
|
49264
|
-
{
|
|
49265
|
-
"name": "edits",
|
|
49266
|
-
"deprecated": false,
|
|
49267
|
-
"deprecationMessage": "",
|
|
49268
|
-
"tagName": {
|
|
49269
|
-
"text": "param"
|
|
49270
|
-
}
|
|
49271
49100
|
}
|
|
49272
49101
|
]
|
|
49273
49102
|
},
|
|
49274
49103
|
{
|
|
49275
|
-
"name": "
|
|
49276
|
-
"file": "packages/core/schematics/migrate-
|
|
49104
|
+
"name": "hasStringRefQuery",
|
|
49105
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49277
49106
|
"ctype": "miscellaneous",
|
|
49278
49107
|
"subtype": "function",
|
|
49279
49108
|
"coverageIgnore": false,
|
|
49280
49109
|
"deprecated": false,
|
|
49281
49110
|
"deprecationMessage": "",
|
|
49282
|
-
"rawdescription": "",
|
|
49283
|
-
"description": "",
|
|
49284
|
-
"displayName": "
|
|
49111
|
+
"rawdescription": "Quick check: does the file have",
|
|
49112
|
+
"description": "<p>Quick check: does the file have</p>\n",
|
|
49113
|
+
"displayName": "hasStringRefQuery",
|
|
49285
49114
|
"args": [
|
|
49286
49115
|
{
|
|
49287
|
-
"name": "
|
|
49116
|
+
"name": "source",
|
|
49117
|
+
"type": "string",
|
|
49288
49118
|
"deprecated": false,
|
|
49289
49119
|
"deprecationMessage": ""
|
|
49290
49120
|
}
|
|
49291
49121
|
],
|
|
49292
|
-
"returnType": "
|
|
49122
|
+
"returnType": "boolean",
|
|
49293
49123
|
"jsdoctags": [
|
|
49294
49124
|
{
|
|
49295
|
-
"name": "
|
|
49125
|
+
"name": "source",
|
|
49126
|
+
"type": "string",
|
|
49296
49127
|
"deprecated": false,
|
|
49297
49128
|
"deprecationMessage": "",
|
|
49298
49129
|
"tagName": {
|
|
@@ -49302,8 +49133,8 @@
|
|
|
49302
49133
|
]
|
|
49303
49134
|
},
|
|
49304
49135
|
{
|
|
49305
|
-
"name": "
|
|
49306
|
-
"file": "packages/core/schematics/migrate-
|
|
49136
|
+
"name": "isAssignmentOperator",
|
|
49137
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49307
49138
|
"ctype": "miscellaneous",
|
|
49308
49139
|
"subtype": "function",
|
|
49309
49140
|
"coverageIgnore": false,
|
|
@@ -49311,10 +49142,10 @@
|
|
|
49311
49142
|
"deprecationMessage": "",
|
|
49312
49143
|
"rawdescription": "",
|
|
49313
49144
|
"description": "",
|
|
49314
|
-
"displayName": "
|
|
49145
|
+
"displayName": "isAssignmentOperator",
|
|
49315
49146
|
"args": [
|
|
49316
49147
|
{
|
|
49317
|
-
"name": "
|
|
49148
|
+
"name": "kind",
|
|
49318
49149
|
"deprecated": false,
|
|
49319
49150
|
"deprecationMessage": ""
|
|
49320
49151
|
}
|
|
@@ -49322,7 +49153,7 @@
|
|
|
49322
49153
|
"returnType": "boolean",
|
|
49323
49154
|
"jsdoctags": [
|
|
49324
49155
|
{
|
|
49325
|
-
"name": "
|
|
49156
|
+
"name": "kind",
|
|
49326
49157
|
"deprecated": false,
|
|
49327
49158
|
"deprecationMessage": "",
|
|
49328
49159
|
"tagName": {
|
|
@@ -49332,16 +49163,16 @@
|
|
|
49332
49163
|
]
|
|
49333
49164
|
},
|
|
49334
49165
|
{
|
|
49335
|
-
"name": "
|
|
49336
|
-
"file": "packages/core/schematics/migrate-
|
|
49166
|
+
"name": "migrateToSignals",
|
|
49167
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49337
49168
|
"ctype": "miscellaneous",
|
|
49338
49169
|
"subtype": "function",
|
|
49339
49170
|
"coverageIgnore": false,
|
|
49340
49171
|
"deprecated": false,
|
|
49341
49172
|
"deprecationMessage": "",
|
|
49342
|
-
"rawdescription": "",
|
|
49343
|
-
"description": "",
|
|
49344
|
-
"displayName": "
|
|
49173
|
+
"rawdescription": "Migrates consumer code that accesses EUI component instances programmatically.\n\nWhen EUI migrated",
|
|
49174
|
+
"description": "<p>Migrates consumer code that accesses EUI component instances programmatically.</p>\n<p>When EUI migrated</p>\n",
|
|
49175
|
+
"displayName": "migrateToSignals",
|
|
49345
49176
|
"args": [
|
|
49346
49177
|
{
|
|
49347
49178
|
"name": "options",
|
|
@@ -49367,7 +49198,7 @@
|
|
|
49367
49198
|
},
|
|
49368
49199
|
{
|
|
49369
49200
|
"name": "migrateTypeScript",
|
|
49370
|
-
"file": "packages/core/schematics/migrate-
|
|
49201
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49371
49202
|
"ctype": "miscellaneous",
|
|
49372
49203
|
"subtype": "function",
|
|
49373
49204
|
"coverageIgnore": false,
|
|
@@ -49389,6 +49220,12 @@
|
|
|
49389
49220
|
"deprecated": false,
|
|
49390
49221
|
"deprecationMessage": ""
|
|
49391
49222
|
},
|
|
49223
|
+
{
|
|
49224
|
+
"name": "tree",
|
|
49225
|
+
"type": "Tree",
|
|
49226
|
+
"deprecated": false,
|
|
49227
|
+
"deprecationMessage": ""
|
|
49228
|
+
},
|
|
49392
49229
|
{
|
|
49393
49230
|
"name": "context",
|
|
49394
49231
|
"type": "SchematicContext",
|
|
@@ -49396,7 +49233,7 @@
|
|
|
49396
49233
|
"deprecationMessage": ""
|
|
49397
49234
|
}
|
|
49398
49235
|
],
|
|
49399
|
-
"returnType": "
|
|
49236
|
+
"returnType": "MigrateResult",
|
|
49400
49237
|
"jsdoctags": [
|
|
49401
49238
|
{
|
|
49402
49239
|
"name": "source",
|
|
@@ -49416,6 +49253,15 @@
|
|
|
49416
49253
|
"text": "param"
|
|
49417
49254
|
}
|
|
49418
49255
|
},
|
|
49256
|
+
{
|
|
49257
|
+
"name": "tree",
|
|
49258
|
+
"type": "Tree",
|
|
49259
|
+
"deprecated": false,
|
|
49260
|
+
"deprecationMessage": "",
|
|
49261
|
+
"tagName": {
|
|
49262
|
+
"text": "param"
|
|
49263
|
+
}
|
|
49264
|
+
},
|
|
49419
49265
|
{
|
|
49420
49266
|
"name": "context",
|
|
49421
49267
|
"type": "SchematicContext",
|
|
@@ -49428,42 +49274,164 @@
|
|
|
49428
49274
|
]
|
|
49429
49275
|
},
|
|
49430
49276
|
{
|
|
49431
|
-
"name": "
|
|
49432
|
-
"file": "packages/core/schematics/migrate-
|
|
49277
|
+
"name": "resolveElementToEuiClass",
|
|
49278
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49433
49279
|
"ctype": "miscellaneous",
|
|
49434
49280
|
"subtype": "function",
|
|
49435
49281
|
"coverageIgnore": false,
|
|
49436
49282
|
"deprecated": false,
|
|
49437
49283
|
"deprecationMessage": "",
|
|
49438
|
-
"rawdescription": "",
|
|
49439
|
-
"description": "",
|
|
49440
|
-
"displayName": "
|
|
49284
|
+
"rawdescription": "Given a template element, resolves it to an EUI class name by checking:\n1. Tag name: <eui-alert> → look up 'eui-alert' in EUI_SELECTOR_TO_CLASS\n2. Attribute selectors: <input euiAutocomplete> → look up 'input[euiAutocomplete]' or '[euiAutocomplete]'",
|
|
49285
|
+
"description": "<p>Given a template element, resolves it to an EUI class name by checking:</p>\n<ol>\n<li>Tag name: <eui-alert> → look up 'eui-alert' in EUI_SELECTOR_TO_CLASS</li>\n<li>Attribute selectors: <input euiAutocomplete> → look up 'input[euiAutocomplete]' or '[euiAutocomplete]'</li>\n</ol>\n",
|
|
49286
|
+
"displayName": "resolveElementToEuiClass",
|
|
49441
49287
|
"args": [
|
|
49442
49288
|
{
|
|
49443
|
-
"name": "
|
|
49289
|
+
"name": "element",
|
|
49290
|
+
"type": "TmplAstElement",
|
|
49291
|
+
"deprecated": false,
|
|
49292
|
+
"deprecationMessage": ""
|
|
49293
|
+
}
|
|
49294
|
+
],
|
|
49295
|
+
"returnType": "string | null",
|
|
49296
|
+
"jsdoctags": [
|
|
49297
|
+
{
|
|
49298
|
+
"name": "element",
|
|
49299
|
+
"type": "TmplAstElement",
|
|
49300
|
+
"deprecated": false,
|
|
49301
|
+
"deprecationMessage": "",
|
|
49302
|
+
"tagName": {
|
|
49303
|
+
"text": "param"
|
|
49304
|
+
}
|
|
49305
|
+
}
|
|
49306
|
+
]
|
|
49307
|
+
},
|
|
49308
|
+
{
|
|
49309
|
+
"name": "resolveEuiClassName",
|
|
49310
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49311
|
+
"ctype": "miscellaneous",
|
|
49312
|
+
"subtype": "function",
|
|
49313
|
+
"coverageIgnore": false,
|
|
49314
|
+
"deprecated": false,
|
|
49315
|
+
"deprecationMessage": "",
|
|
49316
|
+
"rawdescription": "Resolves the EUI class name from a property declaration.\nChecks: type annotation → decorator class argument → template ref map.",
|
|
49317
|
+
"description": "<p>Resolves the EUI class name from a property declaration.\nChecks: type annotation → decorator class argument → template ref map.</p>\n",
|
|
49318
|
+
"displayName": "resolveEuiClassName",
|
|
49319
|
+
"args": [
|
|
49320
|
+
{
|
|
49321
|
+
"name": "node",
|
|
49444
49322
|
"deprecated": false,
|
|
49445
49323
|
"deprecationMessage": ""
|
|
49446
49324
|
},
|
|
49447
49325
|
{
|
|
49448
|
-
"name": "
|
|
49326
|
+
"name": "sourceFile",
|
|
49449
49327
|
"deprecated": false,
|
|
49450
49328
|
"deprecationMessage": ""
|
|
49451
49329
|
},
|
|
49330
|
+
{
|
|
49331
|
+
"name": "templateRefMap",
|
|
49332
|
+
"type": "Map",
|
|
49333
|
+
"deprecated": false,
|
|
49334
|
+
"deprecationMessage": ""
|
|
49335
|
+
}
|
|
49336
|
+
],
|
|
49337
|
+
"returnType": "string | null",
|
|
49338
|
+
"jsdoctags": [
|
|
49339
|
+
{
|
|
49340
|
+
"name": "node",
|
|
49341
|
+
"deprecated": false,
|
|
49342
|
+
"deprecationMessage": "",
|
|
49343
|
+
"tagName": {
|
|
49344
|
+
"text": "param"
|
|
49345
|
+
}
|
|
49346
|
+
},
|
|
49452
49347
|
{
|
|
49453
49348
|
"name": "sourceFile",
|
|
49454
49349
|
"deprecated": false,
|
|
49350
|
+
"deprecationMessage": "",
|
|
49351
|
+
"tagName": {
|
|
49352
|
+
"text": "param"
|
|
49353
|
+
}
|
|
49354
|
+
},
|
|
49355
|
+
{
|
|
49356
|
+
"name": "templateRefMap",
|
|
49357
|
+
"type": "Map",
|
|
49358
|
+
"deprecated": false,
|
|
49359
|
+
"deprecationMessage": "",
|
|
49360
|
+
"tagName": {
|
|
49361
|
+
"text": "param"
|
|
49362
|
+
}
|
|
49363
|
+
}
|
|
49364
|
+
]
|
|
49365
|
+
},
|
|
49366
|
+
{
|
|
49367
|
+
"name": "resolveRefName",
|
|
49368
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49369
|
+
"ctype": "miscellaneous",
|
|
49370
|
+
"subtype": "function",
|
|
49371
|
+
"coverageIgnore": false,
|
|
49372
|
+
"deprecated": false,
|
|
49373
|
+
"deprecationMessage": "",
|
|
49374
|
+
"rawdescription": "Resolves a reference name from an expression.\nHandles: `ref`, `this.ref`, `this._ref`",
|
|
49375
|
+
"description": "<p>Resolves a reference name from an expression.\nHandles: <code>ref</code>, <code>this.ref</code>, <code>this._ref</code></p>\n",
|
|
49376
|
+
"displayName": "resolveRefName",
|
|
49377
|
+
"args": [
|
|
49378
|
+
{
|
|
49379
|
+
"name": "expr",
|
|
49380
|
+
"deprecated": false,
|
|
49381
|
+
"deprecationMessage": ""
|
|
49382
|
+
}
|
|
49383
|
+
],
|
|
49384
|
+
"returnType": "string | null",
|
|
49385
|
+
"jsdoctags": [
|
|
49386
|
+
{
|
|
49387
|
+
"name": "expr",
|
|
49388
|
+
"deprecated": false,
|
|
49389
|
+
"deprecationMessage": "",
|
|
49390
|
+
"tagName": {
|
|
49391
|
+
"text": "param"
|
|
49392
|
+
}
|
|
49393
|
+
}
|
|
49394
|
+
]
|
|
49395
|
+
},
|
|
49396
|
+
{
|
|
49397
|
+
"name": "tryRewritePropertyAccess",
|
|
49398
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49399
|
+
"ctype": "miscellaneous",
|
|
49400
|
+
"subtype": "function",
|
|
49401
|
+
"coverageIgnore": false,
|
|
49402
|
+
"deprecated": false,
|
|
49403
|
+
"deprecationMessage": "",
|
|
49404
|
+
"rawdescription": "Checks if a property access `ref.prop` should be rewritten to `ref.prop()`.\n\nWe rewrite if:\n- `ref` is tracked in euiRefs\n- `prop` is in the signal set for that EUI class\n- The access is NOT already a call expression (not `ref.prop()` or `ref.prop(args)`)\n- The access is NOT a write target (not `ref.prop = value`)\n- The access is NOT a template binding argument (those are unaffected)",
|
|
49405
|
+
"description": "<p>Checks if a property access <code>ref.prop</code> should be rewritten to <code>ref.prop()</code>.</p>\n<p>We rewrite if:</p>\n<ul>\n<li><code>ref</code> is tracked in euiRefs</li>\n<li><code>prop</code> is in the signal set for that EUI class</li>\n<li>The access is NOT already a call expression (not <code>ref.prop()</code> or <code>ref.prop(args)</code>)</li>\n<li>The access is NOT a write target (not <code>ref.prop = value</code>)</li>\n<li>The access is NOT a template binding argument (those are unaffected)</li>\n</ul>\n",
|
|
49406
|
+
"displayName": "tryRewritePropertyAccess",
|
|
49407
|
+
"args": [
|
|
49408
|
+
{
|
|
49409
|
+
"name": "node",
|
|
49410
|
+
"deprecated": false,
|
|
49455
49411
|
"deprecationMessage": ""
|
|
49456
49412
|
},
|
|
49457
49413
|
{
|
|
49458
|
-
"name": "
|
|
49414
|
+
"name": "euiRefs",
|
|
49415
|
+
"type": "Map",
|
|
49416
|
+
"deprecated": false,
|
|
49417
|
+
"deprecationMessage": ""
|
|
49418
|
+
},
|
|
49419
|
+
{
|
|
49420
|
+
"name": "sourceFile",
|
|
49421
|
+
"deprecated": false,
|
|
49422
|
+
"deprecationMessage": ""
|
|
49423
|
+
},
|
|
49424
|
+
{
|
|
49425
|
+
"name": "source",
|
|
49426
|
+
"type": "string",
|
|
49459
49427
|
"deprecated": false,
|
|
49460
49428
|
"deprecationMessage": ""
|
|
49461
49429
|
}
|
|
49462
49430
|
],
|
|
49463
|
-
"returnType": "
|
|
49431
|
+
"returnType": "Edit | null",
|
|
49464
49432
|
"jsdoctags": [
|
|
49465
49433
|
{
|
|
49466
|
-
"name": "
|
|
49434
|
+
"name": "node",
|
|
49467
49435
|
"deprecated": false,
|
|
49468
49436
|
"deprecationMessage": "",
|
|
49469
49437
|
"tagName": {
|
|
@@ -49471,7 +49439,8 @@
|
|
|
49471
49439
|
}
|
|
49472
49440
|
},
|
|
49473
49441
|
{
|
|
49474
|
-
"name": "
|
|
49442
|
+
"name": "euiRefs",
|
|
49443
|
+
"type": "Map",
|
|
49475
49444
|
"deprecated": false,
|
|
49476
49445
|
"deprecationMessage": "",
|
|
49477
49446
|
"tagName": {
|
|
@@ -49487,7 +49456,38 @@
|
|
|
49487
49456
|
}
|
|
49488
49457
|
},
|
|
49489
49458
|
{
|
|
49490
|
-
"name": "
|
|
49459
|
+
"name": "source",
|
|
49460
|
+
"type": "string",
|
|
49461
|
+
"deprecated": false,
|
|
49462
|
+
"deprecationMessage": "",
|
|
49463
|
+
"tagName": {
|
|
49464
|
+
"text": "param"
|
|
49465
|
+
}
|
|
49466
|
+
}
|
|
49467
|
+
]
|
|
49468
|
+
},
|
|
49469
|
+
{
|
|
49470
|
+
"name": "unwrapExpression",
|
|
49471
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49472
|
+
"ctype": "miscellaneous",
|
|
49473
|
+
"subtype": "function",
|
|
49474
|
+
"coverageIgnore": false,
|
|
49475
|
+
"deprecated": false,
|
|
49476
|
+
"deprecationMessage": "",
|
|
49477
|
+
"rawdescription": "",
|
|
49478
|
+
"description": "",
|
|
49479
|
+
"displayName": "unwrapExpression",
|
|
49480
|
+
"args": [
|
|
49481
|
+
{
|
|
49482
|
+
"name": "expression",
|
|
49483
|
+
"deprecated": false,
|
|
49484
|
+
"deprecationMessage": ""
|
|
49485
|
+
}
|
|
49486
|
+
],
|
|
49487
|
+
"returnType": "ts.Expression",
|
|
49488
|
+
"jsdoctags": [
|
|
49489
|
+
{
|
|
49490
|
+
"name": "expression",
|
|
49491
49491
|
"deprecated": false,
|
|
49492
49492
|
"deprecationMessage": "",
|
|
49493
49493
|
"tagName": {
|
|
@@ -49498,7 +49498,7 @@
|
|
|
49498
49498
|
},
|
|
49499
49499
|
{
|
|
49500
49500
|
"name": "visitDir",
|
|
49501
|
-
"file": "packages/core/schematics/migrate-
|
|
49501
|
+
"file": "packages/core/schematics/migrate-to-signals/index.ts",
|
|
49502
49502
|
"ctype": "miscellaneous",
|
|
49503
49503
|
"subtype": "function",
|
|
49504
49504
|
"coverageIgnore": false,
|