@openrewrite/recipes-angular 1.17.0 → 1.18.0
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/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +83 -1
- package/dist/index.js.map +1 -1
- package/dist/libraries/cdk/find-cdk-signature-changes.d.ts +48 -0
- package/dist/libraries/cdk/find-cdk-signature-changes.d.ts.map +1 -0
- package/dist/libraries/cdk/find-cdk-signature-changes.js +323 -0
- package/dist/libraries/cdk/find-cdk-signature-changes.js.map +1 -0
- package/dist/libraries/cdk/find-removed-cdk-apis.d.ts +32 -0
- package/dist/libraries/cdk/find-removed-cdk-apis.d.ts.map +1 -0
- package/dist/libraries/cdk/find-removed-cdk-apis.js +75 -0
- package/dist/libraries/cdk/find-removed-cdk-apis.js.map +1 -0
- package/dist/libraries/cdk/migrate-cdk-sass-imports.d.ts +17 -0
- package/dist/libraries/cdk/migrate-cdk-sass-imports.d.ts.map +1 -0
- package/dist/libraries/cdk/migrate-cdk-sass-imports.js +177 -0
- package/dist/libraries/cdk/migrate-cdk-sass-imports.js.map +1 -0
- package/dist/libraries/cdk/migrate-cdk-stepper.d.ts +8 -0
- package/dist/libraries/cdk/migrate-cdk-stepper.d.ts.map +1 -0
- package/dist/libraries/cdk/migrate-cdk-stepper.js +92 -0
- package/dist/libraries/cdk/migrate-cdk-stepper.js.map +1 -0
- package/dist/libraries/cdk/migrate-deprecated-overlay-sass.d.ts +9 -0
- package/dist/libraries/cdk/migrate-deprecated-overlay-sass.d.ts.map +1 -0
- package/dist/libraries/cdk/migrate-deprecated-overlay-sass.js +57 -0
- package/dist/libraries/cdk/migrate-deprecated-overlay-sass.js.map +1 -0
- package/dist/libraries/cdk/migrate-drag-drop-registry.d.ts +8 -0
- package/dist/libraries/cdk/migrate-drag-drop-registry.d.ts.map +1 -0
- package/dist/libraries/cdk/migrate-drag-drop-registry.js +118 -0
- package/dist/libraries/cdk/migrate-drag-drop-registry.js.map +1 -0
- package/dist/libraries/cdk/migrate-focus-trap-factory-create.d.ts +8 -0
- package/dist/libraries/cdk/migrate-focus-trap-factory-create.d.ts.map +1 -0
- package/dist/libraries/cdk/migrate-focus-trap-factory-create.js +74 -0
- package/dist/libraries/cdk/migrate-focus-trap-factory-create.js.map +1 -0
- package/dist/libraries/cdk/remove-cdk-super-arguments.d.ts +8 -0
- package/dist/libraries/cdk/remove-cdk-super-arguments.d.ts.map +1 -0
- package/dist/libraries/cdk/remove-cdk-super-arguments.js +111 -0
- package/dist/libraries/cdk/remove-cdk-super-arguments.js.map +1 -0
- package/dist/libraries/cdk/remove-portal-factory-resolver-arguments.d.ts +8 -0
- package/dist/libraries/cdk/remove-portal-factory-resolver-arguments.d.ts.map +1 -0
- package/dist/libraries/cdk/remove-portal-factory-resolver-arguments.js +108 -0
- package/dist/libraries/cdk/remove-portal-factory-resolver-arguments.js.map +1 -0
- package/dist/libraries/cdk/remove-tilde-from-stylesheet-imports.d.ts +8 -0
- package/dist/libraries/cdk/remove-tilde-from-stylesheet-imports.d.ts.map +1 -0
- package/dist/libraries/cdk/remove-tilde-from-stylesheet-imports.js +27 -0
- package/dist/libraries/cdk/remove-tilde-from-stylesheet-imports.js.map +1 -0
- package/dist/libraries/cdk/rename-portal-template-selectors.d.ts +27 -0
- package/dist/libraries/cdk/rename-portal-template-selectors.d.ts.map +1 -0
- package/dist/libraries/cdk/rename-portal-template-selectors.js +75 -0
- package/dist/libraries/cdk/rename-portal-template-selectors.js.map +1 -0
- package/dist/libraries/cdk/upgrade-to-cdk.d.ts +38 -0
- package/dist/libraries/cdk/upgrade-to-cdk.d.ts.map +1 -0
- package/dist/libraries/cdk/upgrade-to-cdk.js +173 -0
- package/dist/libraries/cdk/upgrade-to-cdk.js.map +1 -0
- package/dist/libraries/components/lst.d.ts +9 -0
- package/dist/libraries/components/lst.d.ts.map +1 -0
- package/dist/libraries/components/lst.js +37 -0
- package/dist/libraries/components/lst.js.map +1 -0
- package/dist/libraries/components/stylesheets.d.ts +13 -0
- package/dist/libraries/components/stylesheets.d.ts.map +1 -0
- package/dist/libraries/components/stylesheets.js +71 -0
- package/dist/libraries/components/stylesheets.js.map +1 -0
- package/dist/libraries/components/symbols.d.ts +27 -0
- package/dist/libraries/components/symbols.d.ts.map +1 -0
- package/dist/libraries/components/symbols.js +120 -0
- package/dist/libraries/components/symbols.js.map +1 -0
- package/dist/libraries/components/templates.d.ts +24 -0
- package/dist/libraries/components/templates.d.ts.map +1 -0
- package/dist/libraries/components/templates.js +141 -0
- package/dist/libraries/components/templates.js.map +1 -0
- package/dist/libraries/primeng/18/mark-deprecated-components.d.ts.map +1 -1
- package/dist/libraries/primeng/18/mark-deprecated-components.js +9 -5
- package/dist/libraries/primeng/18/mark-deprecated-components.js.map +1 -1
- package/dist/migration/add-trust-proxy-headers.d.ts.map +1 -1
- package/dist/migration/add-trust-proxy-headers.js +2 -1
- package/dist/migration/add-trust-proxy-headers.js.map +1 -1
- package/dist/migration/replace-initial-navigation.d.ts.map +1 -1
- package/dist/migration/replace-initial-navigation.js +2 -2
- package/dist/migration/replace-initial-navigation.js.map +1 -1
- package/dist/migration/update-component-template-url.js +1 -1
- package/dist/migration/update-component-template-url.js.map +1 -1
- package/dist/migration/upgrade-to-angular-13.d.ts.map +1 -1
- package/dist/migration/upgrade-to-angular-13.js +2 -0
- package/dist/migration/upgrade-to-angular-13.js.map +1 -1
- package/dist/migration/upgrade-to-angular-14.d.ts.map +1 -1
- package/dist/migration/upgrade-to-angular-14.js +2 -0
- package/dist/migration/upgrade-to-angular-14.js.map +1 -1
- package/dist/migration/upgrade-to-angular-17.d.ts.map +1 -1
- package/dist/migration/upgrade-to-angular-17.js +2 -0
- package/dist/migration/upgrade-to-angular-17.js.map +1 -1
- package/dist/migration/upgrade-to-angular-20.d.ts.map +1 -1
- package/dist/migration/upgrade-to-angular-20.js +2 -0
- package/dist/migration/upgrade-to-angular-20.js.map +1 -1
- package/dist/migration/upgrade-to-angular-21.d.ts.map +1 -1
- package/dist/migration/upgrade-to-angular-21.js +2 -0
- package/dist/migration/upgrade-to-angular-21.js.map +1 -1
- package/dist/migration/upgrade-to-angular-22.d.ts.map +1 -1
- package/dist/migration/upgrade-to-angular-22.js +2 -0
- package/dist/migration/upgrade-to-angular-22.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +97 -0
- package/src/libraries/cdk/find-cdk-signature-changes.ts +209 -0
- package/src/libraries/cdk/find-removed-cdk-apis.ts +76 -0
- package/src/libraries/cdk/migrate-cdk-sass-imports.ts +174 -0
- package/src/libraries/cdk/migrate-cdk-stepper.ts +58 -0
- package/src/libraries/cdk/migrate-deprecated-overlay-sass.ts +56 -0
- package/src/libraries/cdk/migrate-drag-drop-registry.ts +79 -0
- package/src/libraries/cdk/migrate-focus-trap-factory-create.ts +51 -0
- package/src/libraries/cdk/remove-cdk-super-arguments.ts +83 -0
- package/src/libraries/cdk/remove-portal-factory-resolver-arguments.ts +88 -0
- package/src/libraries/cdk/remove-tilde-from-stylesheet-imports.ts +22 -0
- package/src/libraries/cdk/rename-portal-template-selectors.ts +51 -0
- package/src/libraries/cdk/upgrade-to-cdk.ts +146 -0
- package/src/libraries/components/lst.ts +33 -0
- package/src/libraries/components/stylesheets.ts +55 -0
- package/src/libraries/components/symbols.ts +116 -0
- package/src/libraries/components/templates.ts +126 -0
- package/src/libraries/primeng/18/mark-deprecated-components.ts +8 -4
- package/src/migration/add-trust-proxy-headers.ts +2 -1
- package/src/migration/replace-initial-navigation.ts +3 -1
- package/src/migration/update-component-template-url.ts +1 -1
- package/src/migration/upgrade-to-angular-13.ts +2 -0
- package/src/migration/upgrade-to-angular-14.ts +2 -0
- package/src/migration/upgrade-to-angular-17.ts +2 -0
- package/src/migration/upgrade-to-angular-20.ts +2 -0
- package/src/migration/upgrade-to-angular-21.ts +2 -0
- package/src/migration/upgrade-to-angular-22.ts +2 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 the original author or authors.
|
|
3
|
+
*
|
|
4
|
+
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {ExecutionContext, foundSearchResult, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
8
|
+
import {JavaScriptVisitor, Template} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {isLiteral, J, Type} from "@openrewrite/rewrite/java";
|
|
10
|
+
import {argumentCount, isCdkClass} from "./find-cdk-signature-changes";
|
|
11
|
+
|
|
12
|
+
export class MigrateFocusTrapFactoryCreate extends Recipe {
|
|
13
|
+
readonly name = "org.openrewrite.angular.cdk.MigrateFocusTrapFactoryCreate";
|
|
14
|
+
readonly displayName = "Pass a config object to `ConfigurableFocusTrapFactory.create`";
|
|
15
|
+
readonly description = "CDK 22 removed the `create(element, deferCaptureElements: boolean)` overload of " +
|
|
16
|
+
"`ConfigurableFocusTrapFactory`. Rewrites `create(element, true)` to `create(element, {defer: true})`, and marks " +
|
|
17
|
+
"calls whose second argument is a boolean expression other than a literal.";
|
|
18
|
+
|
|
19
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
20
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
21
|
+
protected async visitMethodInvocation(method: J.MethodInvocation, p: ExecutionContext): Promise<J | undefined> {
|
|
22
|
+
const m = await super.visitMethodInvocation(method, p) as J.MethodInvocation;
|
|
23
|
+
if (m.name.simpleName !== 'create' || argumentCount(m.arguments) !== 2) return m;
|
|
24
|
+
if (!isCdkClass(m.methodType?.declaringType, 'ConfigurableFocusTrapFactory')) return m;
|
|
25
|
+
|
|
26
|
+
const args = m.arguments.elements;
|
|
27
|
+
const defer = args[1].element;
|
|
28
|
+
if (isLiteral(defer) && typeof defer.value === 'boolean') {
|
|
29
|
+
const applied = await Template.builder()
|
|
30
|
+
.code(`create(element, {defer: ${defer.value}})`)
|
|
31
|
+
.build()
|
|
32
|
+
.apply(m, this.cursor) as J.MethodInvocation;
|
|
33
|
+
const config = applied.arguments?.elements?.[1]?.element;
|
|
34
|
+
if (!config) return m;
|
|
35
|
+
const elements = [args[0], {...args[1], element: {...config, prefix: defer.prefix}}];
|
|
36
|
+
return {...m, arguments: {...m.arguments, elements}} as J.MethodInvocation;
|
|
37
|
+
}
|
|
38
|
+
if (isBoolean(defer)) {
|
|
39
|
+
return foundSearchResult(m, "The boolean overload of `ConfigurableFocusTrapFactory.create` was " +
|
|
40
|
+
"removed in @angular/cdk 22. Pass `{defer: <value>}` instead.");
|
|
41
|
+
}
|
|
42
|
+
return m;
|
|
43
|
+
}
|
|
44
|
+
}();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isBoolean(expression: J): boolean {
|
|
49
|
+
const type = (expression as J & {type?: Type}).type;
|
|
50
|
+
return Type.isPrimitive(type) && type.keyword === 'boolean';
|
|
51
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 the original author or authors.
|
|
3
|
+
*
|
|
4
|
+
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {ExecutionContext, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
8
|
+
import {JavaScriptVisitor, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {J} from "@openrewrite/rewrite/java";
|
|
10
|
+
import {importedNames} from "../components/symbols";
|
|
11
|
+
import {emptyArguments, extendedClassName, isSuperCall} from "../components/lst";
|
|
12
|
+
import {argumentCount} from "./find-cdk-signature-changes";
|
|
13
|
+
|
|
14
|
+
/** Classes whose constructor was `constructor(...args: unknown[])` in CDK 21 and takes no arguments in CDK 22. */
|
|
15
|
+
const ZERO_ARGUMENT_CONSTRUCTORS: Record<string, string[]> = {
|
|
16
|
+
'@angular/cdk/a11y': [
|
|
17
|
+
'AriaDescriber', 'CdkAriaLive', 'CdkMonitorFocus', 'CdkTrapFocus', 'ConfigurableFocusTrapFactory',
|
|
18
|
+
'FocusMonitor', 'FocusTrapFactory', 'HighContrastModeDetector', 'InputModalityDetector',
|
|
19
|
+
'InteractivityChecker', 'LiveAnnouncer'
|
|
20
|
+
],
|
|
21
|
+
'@angular/cdk/accordion': ['CdkAccordionItem'],
|
|
22
|
+
'@angular/cdk/bidi': ['Directionality'],
|
|
23
|
+
'@angular/cdk/clipboard': ['CdkCopyToClipboard', 'Clipboard'],
|
|
24
|
+
'@angular/cdk/dialog': ['CdkDialogContainer', 'Dialog'],
|
|
25
|
+
'@angular/cdk/drag-drop': [
|
|
26
|
+
'CdkDrag', 'CdkDragHandle', 'CdkDragPlaceholder', 'CdkDragPreview', 'CdkDropList', 'DragDrop',
|
|
27
|
+
'DragDropRegistry'
|
|
28
|
+
],
|
|
29
|
+
'@angular/cdk/layout': ['BreakpointObserver', 'MediaMatcher'],
|
|
30
|
+
'@angular/cdk/observers': ['CdkObserveContent', 'ContentObserver'],
|
|
31
|
+
'@angular/cdk/overlay': [
|
|
32
|
+
'CdkConnectedOverlay', 'CdkOverlayOrigin', 'CdkScrollable', 'FullscreenOverlayContainer', 'Overlay',
|
|
33
|
+
'OverlayContainer', 'OverlayPositionBuilder', 'ScrollDispatcher', 'ScrollStrategyOptions', 'ViewportRuler'
|
|
34
|
+
],
|
|
35
|
+
'@angular/cdk/portal': ['CdkPortal', 'CdkPortalOutlet'],
|
|
36
|
+
'@angular/cdk/scrolling': [
|
|
37
|
+
'CdkScrollable', 'CdkVirtualForOf', 'CdkVirtualScrollViewport', 'CdkVirtualScrollable',
|
|
38
|
+
'CdkVirtualScrollableElement', 'CdkVirtualScrollableWindow', 'ScrollDispatcher', 'ViewportRuler'
|
|
39
|
+
],
|
|
40
|
+
'@angular/cdk/stepper': [
|
|
41
|
+
'CdkStep', 'CdkStepHeader', 'CdkStepLabel', 'CdkStepper', 'CdkStepperNext', 'CdkStepperPrevious'
|
|
42
|
+
],
|
|
43
|
+
'@angular/cdk/table': [
|
|
44
|
+
'BaseRowDef', 'CdkCell', 'CdkCellDef', 'CdkCellOutlet', 'CdkColumnDef', 'CdkFooterCell',
|
|
45
|
+
'CdkFooterCellDef', 'CdkFooterRowDef', 'CdkHeaderCell', 'CdkHeaderCellDef', 'CdkHeaderRowDef',
|
|
46
|
+
'CdkNoDataRow', 'CdkRowDef', 'CdkTable', 'CdkTextColumn', 'DataRowOutlet', 'FooterRowOutlet',
|
|
47
|
+
'HeaderRowOutlet', 'NoDataRowOutlet'
|
|
48
|
+
],
|
|
49
|
+
'@angular/cdk/text-field': ['AutofillMonitor', 'CdkAutofill', 'CdkTextareaAutosize'],
|
|
50
|
+
'@angular/cdk/tree': [
|
|
51
|
+
'CdkNestedTreeNode', 'CdkTree', 'CdkTreeNode', 'CdkTreeNodeDef', 'CdkTreeNodeOutlet', 'CdkTreeNodePadding',
|
|
52
|
+
'CdkTreeNodeToggle'
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export class RemoveCdkSuperArguments extends Recipe {
|
|
57
|
+
readonly name = "org.openrewrite.angular.cdk.RemoveCdkSuperArguments";
|
|
58
|
+
readonly displayName = "Remove the `super(...)` arguments of CDK subclasses";
|
|
59
|
+
readonly description = "CDK 22 removed the `constructor(...args: unknown[])` that kept old `super(...)` calls compiling " +
|
|
60
|
+
"after the CDK classes moved to `inject()`. Removes the arguments of `super(...)` in classes that extend one of " +
|
|
61
|
+
"those classes, such as `CdkStepper`, `CdkTable` or `Overlay`, since the CDK already ignored them.";
|
|
62
|
+
|
|
63
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
64
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
65
|
+
private cdkClasses = new Set<string>();
|
|
66
|
+
|
|
67
|
+
protected async visitJsCompilationUnit(cu: JS.CompilationUnit, p: ExecutionContext): Promise<J | undefined> {
|
|
68
|
+
this.cdkClasses = new Set(Object.entries(ZERO_ARGUMENT_CONSTRUCTORS)
|
|
69
|
+
.flatMap(([module, classes]) => [...importedNames(cu, module, classes).keys()]));
|
|
70
|
+
return this.cdkClasses.size === 0 ? cu : super.visitJsCompilationUnit(cu, p);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected async visitFunctionCall(functionCall: JS.FunctionCall, p: ExecutionContext): Promise<J | undefined> {
|
|
74
|
+
const call = await super.visitFunctionCall(functionCall, p) as JS.FunctionCall;
|
|
75
|
+
if (!isSuperCall(call) || argumentCount(call.arguments) === 0) return call;
|
|
76
|
+
const classDecl = this.cursor.firstEnclosing((t: any): t is J.ClassDeclaration => t?.kind === J.Kind.ClassDeclaration);
|
|
77
|
+
const extended = classDecl && extendedClassName(classDecl);
|
|
78
|
+
if (!extended || !this.cdkClasses.has(extended)) return call;
|
|
79
|
+
return {...call, arguments: emptyArguments(call.arguments)} as JS.FunctionCall;
|
|
80
|
+
}
|
|
81
|
+
}();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 the original author or authors.
|
|
3
|
+
*
|
|
4
|
+
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {ExecutionContext, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
8
|
+
import {JavaScriptVisitor, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {Expression, isIdentifier, J, Type} from "@openrewrite/rewrite/java";
|
|
10
|
+
import {importedNames} from "../components/symbols";
|
|
11
|
+
import {argumentCount} from "./find-cdk-signature-changes";
|
|
12
|
+
|
|
13
|
+
interface RemovedParameters {
|
|
14
|
+
/** Index of the `ComponentFactoryResolver` parameter. */
|
|
15
|
+
resolver: number;
|
|
16
|
+
/** Index of a later parameter that CDK 20 removed as well. */
|
|
17
|
+
trailing?: number;
|
|
18
|
+
/** Arguments the CDK 20 constructor accepts at most, so a longer call is unambiguously a CDK 19 one. */
|
|
19
|
+
newArity: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const REMOVED_PARAMETERS: Record<string, RemovedParameters> = {
|
|
23
|
+
// (component, viewContainerRef, injector, componentFactoryResolver, projectableNodes)
|
|
24
|
+
ComponentPortal: {resolver: 3, newArity: 4},
|
|
25
|
+
// (outletElement, componentFactoryResolver, appRef, defaultInjector, document)
|
|
26
|
+
DomPortalOutlet: {resolver: 1, trailing: 4, newArity: 3},
|
|
27
|
+
DomPortalHost: {resolver: 1, trailing: 4, newArity: 3},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export class RemovePortalFactoryResolverArguments extends Recipe {
|
|
31
|
+
readonly name = "org.openrewrite.angular.cdk.RemovePortalFactoryResolverArguments";
|
|
32
|
+
readonly displayName = "Remove the `ComponentFactoryResolver` argument of CDK portals";
|
|
33
|
+
readonly description = "CDK 20 removed the deprecated `ComponentFactoryResolver` parameter of the `ComponentPortal` " +
|
|
34
|
+
"constructor and the `ComponentFactoryResolver` and `Document` parameters of the `DomPortalOutlet` constructor. " +
|
|
35
|
+
"Removes those arguments from `new ComponentPortal(...)` and `new DomPortalOutlet(...)` so the remaining ones " +
|
|
36
|
+
"line up with the new parameter lists. A call that also fits the new parameter list is only changed when the " +
|
|
37
|
+
"argument is typed or named as a `ComponentFactoryResolver`.";
|
|
38
|
+
|
|
39
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
40
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
41
|
+
private portalClasses = new Map<string, string>();
|
|
42
|
+
|
|
43
|
+
protected async visitJsCompilationUnit(cu: JS.CompilationUnit, p: ExecutionContext): Promise<J | undefined> {
|
|
44
|
+
this.portalClasses = importedNames(cu, '@angular/cdk/portal', Object.keys(REMOVED_PARAMETERS));
|
|
45
|
+
if (this.portalClasses.size === 0) return cu;
|
|
46
|
+
return super.visitJsCompilationUnit(cu, p);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
protected async visitNewClass(newClass: J.NewClass, p: ExecutionContext): Promise<J | undefined> {
|
|
50
|
+
const n = await super.visitNewClass(newClass, p) as J.NewClass;
|
|
51
|
+
const clazz = n.class;
|
|
52
|
+
if (!clazz || !isIdentifier(clazz)) return n;
|
|
53
|
+
const portalClass = this.portalClasses.get(clazz.simpleName);
|
|
54
|
+
if (!portalClass) return n;
|
|
55
|
+
|
|
56
|
+
const removed = REMOVED_PARAMETERS[portalClass];
|
|
57
|
+
let elements = n.arguments.elements;
|
|
58
|
+
const resolver = elements[removed.resolver]?.element;
|
|
59
|
+
if (!resolver || (argumentCount(n.arguments) <= removed.newArity && !isResolver(resolver))) return n;
|
|
60
|
+
|
|
61
|
+
if (removed.trailing !== undefined) {
|
|
62
|
+
elements = removeArgument(elements, removed.trailing);
|
|
63
|
+
}
|
|
64
|
+
elements = removeArgument(elements, removed.resolver);
|
|
65
|
+
return {...n, arguments: {...n.arguments, elements}} as J.NewClass;
|
|
66
|
+
}
|
|
67
|
+
}();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function removeArgument(elements: J.RightPadded<Expression>[], index: number): J.RightPadded<Expression>[] {
|
|
72
|
+
if (index >= elements.length) return elements;
|
|
73
|
+
const retained = elements.filter((_, i) => i !== index);
|
|
74
|
+
if (index === elements.length - 1) {
|
|
75
|
+
retained[retained.length - 1] = {...retained[retained.length - 1], after: elements[index].after};
|
|
76
|
+
}
|
|
77
|
+
return retained;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Whether an argument is typed as, or named like, a `ComponentFactoryResolver`. */
|
|
81
|
+
function isResolver(argument: Expression): boolean {
|
|
82
|
+
const type = (argument as Expression & {type?: Type}).type;
|
|
83
|
+
if (Type.isClass(type) && type.fullyQualifiedName.endsWith('ComponentFactoryResolver')) return true;
|
|
84
|
+
const name = isIdentifier(argument) ? argument.simpleName
|
|
85
|
+
: argument.kind === J.Kind.FieldAccess ? (argument as J.FieldAccess).name.element.simpleName
|
|
86
|
+
: undefined;
|
|
87
|
+
return name !== undefined && /resolver/i.test(name);
|
|
88
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 the original author or authors.
|
|
3
|
+
*
|
|
4
|
+
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {StylesheetRecipe, withoutComments} from "../components/stylesheets";
|
|
8
|
+
|
|
9
|
+
const TILDE_IMPORT = /(@(?:import|use|forward)\s+['"])~(@angular\/[^'"]*?)(?:\.scss)?(['"])/g;
|
|
10
|
+
|
|
11
|
+
export class RemoveTildeFromStylesheetImports extends StylesheetRecipe {
|
|
12
|
+
readonly name = "org.openrewrite.angular.cdk.RemoveTildeFromStylesheetImports";
|
|
13
|
+
readonly displayName = "Remove `~` from `@angular` stylesheet imports";
|
|
14
|
+
readonly description = "Removes the webpack-specific `~` prefix and the `.scss` extension from `@import`, `@use` and " +
|
|
15
|
+
"`@forward` rules that load `@angular/cdk` or `@angular/material` stylesheets. The Angular 13 CLI deprecated " +
|
|
16
|
+
"tilde imports. This is the `ng update` tilde-import migration of `@angular/cdk` 13.";
|
|
17
|
+
|
|
18
|
+
protected migrate(content: string): string {
|
|
19
|
+
if (!content.includes('~@angular/')) return content;
|
|
20
|
+
return withoutComments(content, masked => masked.replace(TILDE_IMPORT, '$1$2$3'));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 the original author or authors.
|
|
3
|
+
*
|
|
4
|
+
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {ExecutionContext, foundSearchResult, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
8
|
+
import {PlainText, PlainTextVisitor} from "@openrewrite/rewrite/text";
|
|
9
|
+
import {RenameTemplateAttributes, renameTemplateAttributes} from "../components/templates";
|
|
10
|
+
|
|
11
|
+
export class RenamePortalTemplateSelectors extends RenameTemplateAttributes {
|
|
12
|
+
readonly name = "org.openrewrite.angular.cdk.RenamePortalTemplateSelectors";
|
|
13
|
+
readonly displayName = "Rename the removed CDK portal directive selectors";
|
|
14
|
+
readonly description = "CDK 21 removed `PortalHostDirective` and `TemplatePortalDirective`. Renames the selectors " +
|
|
15
|
+
"only they matched to those of `CdkPortalOutlet` and `CdkPortal`: `[cdkPortalHost]` becomes `[cdkPortalOutlet]`, " +
|
|
16
|
+
"a `#ref=\"cdkPortalHost\"` reference becomes `#ref=\"cdkPortalOutlet\"`, and `<ng-template cdk-portal>` becomes " +
|
|
17
|
+
"`<ng-template cdkPortal>`, in `.html` templates and inline `template` metadata.";
|
|
18
|
+
|
|
19
|
+
protected readonly attributes = [
|
|
20
|
+
{name: 'cdkPortalHost', newName: 'cdkPortalOutlet'},
|
|
21
|
+
{name: 'cdk-portal', newName: 'cdkPortal', element: 'ng-template'},
|
|
22
|
+
];
|
|
23
|
+
protected readonly exportAs = [
|
|
24
|
+
{name: 'cdkPortalHost', newName: 'cdkPortalOutlet'},
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const UNPREFIXED_SELECTORS = [
|
|
29
|
+
{name: 'portal', newName: 'cdkPortal', element: 'ng-template'},
|
|
30
|
+
{name: 'portalHost', newName: 'cdkPortalOutlet'},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
export class FindUnprefixedPortalSelectors extends Recipe {
|
|
34
|
+
readonly name = "org.openrewrite.angular.cdk.FindUnprefixedPortalSelectors";
|
|
35
|
+
readonly displayName = "Find the removed unprefixed CDK portal selectors";
|
|
36
|
+
readonly description = "Finds `.html` templates that use the `portal` or `portalHost` attribute, which only the " +
|
|
37
|
+
"`TemplatePortalDirective` and `PortalHostDirective` that CDK 21 removed matched. These names are too generic to " +
|
|
38
|
+
"rename blindly, so confirm they are the CDK directives and rename them to `cdkPortal` and `cdkPortalOutlet`.";
|
|
39
|
+
|
|
40
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
41
|
+
return new class extends PlainTextVisitor<ExecutionContext> {
|
|
42
|
+
override async visitText(text: PlainText, p: ExecutionContext): Promise<PlainText | undefined> {
|
|
43
|
+
const t = await super.visitText(text, p) as PlainText;
|
|
44
|
+
if (!t || !t.sourcePath.endsWith('.html')) return t;
|
|
45
|
+
if (renameTemplateAttributes(t.text, UNPREFIXED_SELECTORS) === t.text) return t;
|
|
46
|
+
return foundSearchResult(t, "`portal` and `portalHost` stopped matching a CDK directive in " +
|
|
47
|
+
"@angular/cdk 21. If they refer to the CDK portal directives, rename them to `cdkPortal` and `cdkPortalOutlet`.");
|
|
48
|
+
}
|
|
49
|
+
}();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 the original author or authors.
|
|
3
|
+
*
|
|
4
|
+
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {Recipe} from "@openrewrite/rewrite";
|
|
8
|
+
import {renameSymbols} from "../components/symbols";
|
|
9
|
+
import {RemoveTildeFromStylesheetImports} from "./remove-tilde-from-stylesheet-imports";
|
|
10
|
+
import {FindRemovedCdk13Apis, FindRemovedCdk14Apis, FindRemovedCdk20Apis, FindRemovedCdk21Apis, FindRemovedCdk22Apis} from "./find-removed-cdk-apis";
|
|
11
|
+
import {
|
|
12
|
+
FindComponentPortalFactoryResolver,
|
|
13
|
+
FindDialogComponentFactoryResolver,
|
|
14
|
+
FindDropListRefDropWithoutEvent,
|
|
15
|
+
FindFocusTrapWithoutInjector,
|
|
16
|
+
FindOverlayConnectedTo,
|
|
17
|
+
FindSelectionModelVoidOverrides,
|
|
18
|
+
FindTestElementImplementations
|
|
19
|
+
} from "./find-cdk-signature-changes";
|
|
20
|
+
import {MigrateCdkSassImports} from "./migrate-cdk-sass-imports";
|
|
21
|
+
import {RemovePortalFactoryResolverArguments} from "./remove-portal-factory-resolver-arguments";
|
|
22
|
+
import {FindUnprefixedPortalSelectors, RenamePortalTemplateSelectors} from "./rename-portal-template-selectors";
|
|
23
|
+
import {MigrateFocusTrapFactoryCreate} from "./migrate-focus-trap-factory-create";
|
|
24
|
+
import {MigrateCdkStepper} from "./migrate-cdk-stepper";
|
|
25
|
+
import {MigrateDragDropRegistry} from "./migrate-drag-drop-registry";
|
|
26
|
+
import {MigrateDeprecatedOverlaySass} from "./migrate-deprecated-overlay-sass";
|
|
27
|
+
import {RemoveCdkSuperArguments} from "./remove-cdk-super-arguments";
|
|
28
|
+
|
|
29
|
+
// The `@angular/cdk` version bump is part of `UpgradeToAngularN`, which runs these composites as well.
|
|
30
|
+
|
|
31
|
+
export class UpgradeToCdk13CodeChanges extends Recipe {
|
|
32
|
+
readonly name = "org.openrewrite.angular.cdk.UpgradeToCdk13CodeChanges";
|
|
33
|
+
readonly displayName = "Angular CDK 13 code changes";
|
|
34
|
+
readonly description = "The `@angular/cdk` source changes of the version 13 migration: renames " +
|
|
35
|
+
"`CKD_COPY_TO_CLIPBOARD_CONFIG`, removes `~` from `@angular` stylesheet imports, and finds the removed " +
|
|
36
|
+
"`ConnectedPositionStrategy` API. `UpgradeToAngular13` includes this recipe.";
|
|
37
|
+
|
|
38
|
+
async recipeList(): Promise<Recipe[]> {
|
|
39
|
+
return [
|
|
40
|
+
...renameSymbols([
|
|
41
|
+
{module: '@angular/cdk/clipboard', name: 'CKD_COPY_TO_CLIPBOARD_CONFIG', newName: 'CDK_COPY_TO_CLIPBOARD_CONFIG'},
|
|
42
|
+
]),
|
|
43
|
+
new RemoveTildeFromStylesheetImports(),
|
|
44
|
+
new FindRemovedCdk13Apis(),
|
|
45
|
+
new FindOverlayConnectedTo(),
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class UpgradeToCdk14CodeChanges extends Recipe {
|
|
51
|
+
readonly name = "org.openrewrite.angular.cdk.UpgradeToCdk14CodeChanges";
|
|
52
|
+
readonly displayName = "Angular CDK 14 code changes";
|
|
53
|
+
readonly description = "The `@angular/cdk` source changes of the version 14 migration: migrates `CdkStepper` " +
|
|
54
|
+
"subclasses off the private `_orientation` and the removed `_document` parameter, and finds imports from the " +
|
|
55
|
+
"removed `@angular/cdk/testing/protractor` entry point. `UpgradeToAngular14` includes this recipe.";
|
|
56
|
+
|
|
57
|
+
async recipeList(): Promise<Recipe[]> {
|
|
58
|
+
return [
|
|
59
|
+
new MigrateCdkStepper(),
|
|
60
|
+
new FindRemovedCdk14Apis(),
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export class UpgradeToCdk17CodeChanges extends Recipe {
|
|
66
|
+
readonly name = "org.openrewrite.angular.cdk.UpgradeToCdk17CodeChanges";
|
|
67
|
+
readonly displayName = "Angular CDK 17 code changes";
|
|
68
|
+
readonly description = "The `@angular/cdk` source changes of the version 17 migration: replaces the Sass `@import` " +
|
|
69
|
+
"entry points that CDK 17 deleted with `@use '@angular/cdk'`. `UpgradeToAngular17` includes this recipe.";
|
|
70
|
+
|
|
71
|
+
async recipeList(): Promise<Recipe[]> {
|
|
72
|
+
return [new MigrateCdkSassImports()];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export class UpgradeToCdk20CodeChanges extends Recipe {
|
|
77
|
+
readonly name = "org.openrewrite.angular.cdk.UpgradeToCdk20CodeChanges";
|
|
78
|
+
readonly displayName = "Angular CDK 20 code changes";
|
|
79
|
+
readonly description = "The `@angular/cdk` source changes of the version 20 migration: renames the removed " +
|
|
80
|
+
"`*PortalHost` aliases to their `*PortalOutlet` classes, drops the `ComponentFactoryResolver` arguments of the " +
|
|
81
|
+
"portal constructors, migrates `DragDropRegistry` off its type arguments and `scroll` subject, and finds the " +
|
|
82
|
+
"removed dialog, portal and table APIs and the `SelectionModel` overrides that still return `void`. " +
|
|
83
|
+
"`UpgradeToAngular20` includes this recipe.";
|
|
84
|
+
|
|
85
|
+
async recipeList(): Promise<Recipe[]> {
|
|
86
|
+
return [
|
|
87
|
+
...renameSymbols([
|
|
88
|
+
{module: '@angular/cdk/portal', name: 'BasePortalHost', newName: 'BasePortalOutlet'},
|
|
89
|
+
{module: '@angular/cdk/portal', name: 'DomPortalHost', newName: 'DomPortalOutlet'},
|
|
90
|
+
{module: '@angular/cdk/portal', name: 'PortalHost', newName: 'PortalOutlet'},
|
|
91
|
+
]),
|
|
92
|
+
new RemovePortalFactoryResolverArguments(),
|
|
93
|
+
new MigrateDragDropRegistry(),
|
|
94
|
+
new FindRemovedCdk20Apis(),
|
|
95
|
+
new FindDialogComponentFactoryResolver(),
|
|
96
|
+
new FindComponentPortalFactoryResolver(),
|
|
97
|
+
new FindSelectionModelVoidOverrides(),
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class UpgradeToCdk21CodeChanges extends Recipe {
|
|
103
|
+
readonly name = "org.openrewrite.angular.cdk.UpgradeToCdk21CodeChanges";
|
|
104
|
+
readonly displayName = "Angular CDK 21 code changes";
|
|
105
|
+
readonly description = "The `@angular/cdk` source changes of the version 21 migration: replaces the removed " +
|
|
106
|
+
"`TemplatePortalDirective` and `PortalHostDirective` with `CdkPortal` and `CdkPortalOutlet` in code and " +
|
|
107
|
+
"templates, renames the removed overlay Sass variables, and finds the removed `@angular/cdk/a11y` factories and incomplete `TestElement` implementations. " +
|
|
108
|
+
"`UpgradeToAngular21` includes this recipe.";
|
|
109
|
+
|
|
110
|
+
async recipeList(): Promise<Recipe[]> {
|
|
111
|
+
return [
|
|
112
|
+
...renameSymbols([
|
|
113
|
+
{module: '@angular/cdk/portal', name: 'TemplatePortalDirective', newName: 'CdkPortal'},
|
|
114
|
+
{module: '@angular/cdk/portal', name: 'PortalHostDirective', newName: 'CdkPortalOutlet'},
|
|
115
|
+
]),
|
|
116
|
+
new RenamePortalTemplateSelectors(),
|
|
117
|
+
new FindUnprefixedPortalSelectors(),
|
|
118
|
+
new MigrateDeprecatedOverlaySass(),
|
|
119
|
+
new FindRemovedCdk21Apis(),
|
|
120
|
+
new FindTestElementImplementations(),
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export class UpgradeToCdk22CodeChanges extends Recipe {
|
|
126
|
+
readonly name = "org.openrewrite.angular.cdk.UpgradeToCdk22CodeChanges";
|
|
127
|
+
readonly displayName = "Angular CDK 22 code changes";
|
|
128
|
+
readonly description = "The `@angular/cdk` source changes of the version 22 migration: renames `ContextMenuTracker` " +
|
|
129
|
+
"to `MenuTracker`, removes the `super(...)` arguments that CDK constructors no longer accept, rewrites the boolean " +
|
|
130
|
+
"overload of `ConfigurableFocusTrapFactory.create`, and finds the removed " +
|
|
131
|
+
"`AriaDescriber` constants and the calls missing a now-required `Injector` or `event` argument. " +
|
|
132
|
+
"`UpgradeToAngular22` includes this recipe.";
|
|
133
|
+
|
|
134
|
+
async recipeList(): Promise<Recipe[]> {
|
|
135
|
+
return [
|
|
136
|
+
...renameSymbols([
|
|
137
|
+
{module: '@angular/cdk/menu', name: 'ContextMenuTracker', newName: 'MenuTracker'},
|
|
138
|
+
]),
|
|
139
|
+
new RemoveCdkSuperArguments(),
|
|
140
|
+
new MigrateFocusTrapFactoryCreate(),
|
|
141
|
+
new FindRemovedCdk22Apis(),
|
|
142
|
+
new FindFocusTrapWithoutInjector(),
|
|
143
|
+
new FindDropListRefDropWithoutEvent(),
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 the original author or authors.
|
|
3
|
+
*
|
|
4
|
+
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {emptyMarkers, randomId} from "@openrewrite/rewrite";
|
|
8
|
+
import {JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {emptySpace, Expression, isIdentifier, J} from "@openrewrite/rewrite/java";
|
|
10
|
+
|
|
11
|
+
/** The argument list of a call without arguments, which the LST represents as one `J.Empty`. */
|
|
12
|
+
export function emptyArguments(container: J.Container<Expression>): J.Container<Expression> {
|
|
13
|
+
const empty: J.Empty = {kind: J.Kind.Empty, id: randomId(), prefix: emptySpace, markers: emptyMarkers};
|
|
14
|
+
return {
|
|
15
|
+
...container,
|
|
16
|
+
elements: [{kind: J.Kind.RightPadded, element: empty, after: emptySpace, markers: emptyMarkers} as J.RightPadded<Expression>],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The simple name of the class a class declaration extends, with any type arguments stripped. */
|
|
21
|
+
export function extendedClassName(classDecl: J.ClassDeclaration): string | undefined {
|
|
22
|
+
let extended: J | undefined = classDecl.extends?.element;
|
|
23
|
+
if (extended?.kind === JS.Kind.TypeTreeExpression) extended = (extended as JS.TypeTreeExpression).expression;
|
|
24
|
+
if (extended?.kind === JS.Kind.ExpressionWithTypeArguments) extended = (extended as JS.ExpressionWithTypeArguments).clazz;
|
|
25
|
+
if (extended?.kind === J.Kind.ParameterizedType) extended = (extended as J.ParameterizedType).class;
|
|
26
|
+
return extended && isIdentifier(extended) ? extended.simpleName : undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Whether a function call is a `super(...)` constructor call. */
|
|
30
|
+
export function isSuperCall(call: JS.FunctionCall): boolean {
|
|
31
|
+
const fn = call.function?.element;
|
|
32
|
+
return fn !== undefined && isIdentifier(fn) && fn.simpleName === 'super';
|
|
33
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 the original author or authors.
|
|
3
|
+
*
|
|
4
|
+
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {ExecutionContext, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
8
|
+
import {PlainText, PlainTextVisitor} from "@openrewrite/rewrite/text";
|
|
9
|
+
|
|
10
|
+
const COMMENT = /\/\*[\s\S]*?\*\/|\/\/[^\n]*/g;
|
|
11
|
+
const COMMENT_PLACEHOLDER = /__ngComment(\d+)__/g;
|
|
12
|
+
|
|
13
|
+
/** Rewrites the text of `.scss` and `.css` files, the stylesheets the Angular CLI compiles with Sass. */
|
|
14
|
+
export abstract class StylesheetRecipe extends Recipe {
|
|
15
|
+
protected abstract migrate(content: string): string;
|
|
16
|
+
|
|
17
|
+
protected acceptsPath(sourcePath: string): boolean {
|
|
18
|
+
return isStylesheet(sourcePath);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
22
|
+
const migrate = (content: string) => this.migrate(content);
|
|
23
|
+
const acceptsPath = (sourcePath: string) => this.acceptsPath(sourcePath);
|
|
24
|
+
return new class extends PlainTextVisitor<ExecutionContext> {
|
|
25
|
+
override async visitText(text: PlainText, p: ExecutionContext): Promise<PlainText | undefined> {
|
|
26
|
+
const t = await super.visitText(text, p) as PlainText;
|
|
27
|
+
if (!t || !acceptsPath(t.sourcePath)) return t;
|
|
28
|
+
const updated = migrate(t.text);
|
|
29
|
+
return updated === t.text ? t : {...t, text: updated, snippets: []};
|
|
30
|
+
}
|
|
31
|
+
}();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function isStylesheet(sourcePath: string): boolean {
|
|
36
|
+
return sourcePath.endsWith('.scss') || sourcePath.endsWith('.css');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Applies `migrate` to a stylesheet with its comments masked, so commented-out code stays as written. */
|
|
40
|
+
export function withoutComments(content: string, migrate: (masked: string) => string): string {
|
|
41
|
+
const comments: string[] = [];
|
|
42
|
+
const masked = content.replace(COMMENT, comment => {
|
|
43
|
+
comments.push(comment);
|
|
44
|
+
return `__ngComment${comments.length - 1}__`;
|
|
45
|
+
});
|
|
46
|
+
return migrate(masked).replace(COMMENT_PLACEHOLDER, (_, index) => comments[Number(index)]);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function maskComments(content: string): string {
|
|
50
|
+
return content.replace(COMMENT, '');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function escapeRegExp(value: string): string {
|
|
54
|
+
return value.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
|
|
55
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 the original author or authors.
|
|
3
|
+
*
|
|
4
|
+
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {ExecutionContext, findMarker, foundSearchResult, MarkersKind, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
8
|
+
import {ChangeImport, JavaScriptVisitor, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {isIdentifier, J} from "@openrewrite/rewrite/java";
|
|
10
|
+
|
|
11
|
+
/** An exported symbol of an `angular/components` entry point that a major version renamed. */
|
|
12
|
+
export interface RenamedSymbol {
|
|
13
|
+
module: string;
|
|
14
|
+
name: string;
|
|
15
|
+
newName: string;
|
|
16
|
+
newModule?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** An exported symbol of an `angular/components` entry point that a major version removed. */
|
|
20
|
+
export interface RemovedSymbol {
|
|
21
|
+
module: string;
|
|
22
|
+
name: string;
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** One `ChangeImport` per renamed symbol, which also carries every reference bound to the import. */
|
|
27
|
+
export function renameSymbols(symbols: RenamedSymbol[]): Recipe[] {
|
|
28
|
+
return symbols.map(s => new ChangeImport({
|
|
29
|
+
oldModule: s.module,
|
|
30
|
+
oldMember: s.name,
|
|
31
|
+
newModule: s.newModule ?? s.module,
|
|
32
|
+
newMember: s.newName,
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Local names that the imports of a compilation unit bind to the given symbols of the given module. */
|
|
37
|
+
export function importedNames(cu: JS.CompilationUnit, module: string, symbols: string[]): Map<string, string> {
|
|
38
|
+
const names = new Map<string, string>();
|
|
39
|
+
for (const statement of cu.statements) {
|
|
40
|
+
const imp = statement.element;
|
|
41
|
+
if (imp.kind !== JS.Kind.Import) continue;
|
|
42
|
+
const named = (imp as JS.Import).importClause?.namedBindings;
|
|
43
|
+
if (moduleOf(imp as JS.Import) !== module || named?.kind !== JS.Kind.NamedImports) continue;
|
|
44
|
+
for (const element of (named as JS.NamedImports).elements.elements) {
|
|
45
|
+
const spec = element.element as JS.ImportSpecifier;
|
|
46
|
+
const imported = importedName(spec);
|
|
47
|
+
if (imported && symbols.includes(imported)) {
|
|
48
|
+
names.set(localName(spec) ?? imported, imported);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return names;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Marks named imports of removed symbols, so a removed API shows up at the import that pulls it in.
|
|
57
|
+
*/
|
|
58
|
+
export abstract class FindRemovedSymbols extends Recipe {
|
|
59
|
+
protected abstract readonly symbols: RemovedSymbol[];
|
|
60
|
+
|
|
61
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
62
|
+
const symbols = this.symbols;
|
|
63
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
64
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
65
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
66
|
+
const module = moduleOf(imp);
|
|
67
|
+
const named = imp.importClause?.namedBindings;
|
|
68
|
+
if (!module || named?.kind !== JS.Kind.NamedImports) return imp;
|
|
69
|
+
|
|
70
|
+
const removed = symbols.filter(s => s.module === module);
|
|
71
|
+
if (removed.length === 0) return imp;
|
|
72
|
+
|
|
73
|
+
const namedImports = named as JS.NamedImports;
|
|
74
|
+
let changed = false;
|
|
75
|
+
const elements = await Promise.all(namedImports.elements.elements.map(async element => {
|
|
76
|
+
const spec = element.element as JS.ImportSpecifier;
|
|
77
|
+
const symbol = removed.find(s => s.name === importedName(spec));
|
|
78
|
+
if (!symbol || findMarker(spec, MarkersKind.SearchResult)) return element;
|
|
79
|
+
changed = true;
|
|
80
|
+
return {...element, element: foundSearchResult(spec, symbol.message)};
|
|
81
|
+
}));
|
|
82
|
+
if (!changed) return imp;
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
...imp,
|
|
86
|
+
importClause: {
|
|
87
|
+
...imp.importClause!,
|
|
88
|
+
namedBindings: {...namedImports, elements: {...namedImports.elements, elements}},
|
|
89
|
+
},
|
|
90
|
+
} as JS.Import;
|
|
91
|
+
}
|
|
92
|
+
}();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function moduleOf(imp: JS.Import): string | undefined {
|
|
97
|
+
const specifier = imp.moduleSpecifier?.element;
|
|
98
|
+
return specifier?.kind === J.Kind.Literal ? (specifier as J.Literal).value as string : undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function importedName(spec: JS.ImportSpecifier): string | undefined {
|
|
102
|
+
const specifier = spec.specifier;
|
|
103
|
+
if (isIdentifier(specifier)) return specifier.simpleName;
|
|
104
|
+
if (specifier.kind === JS.Kind.Alias) {
|
|
105
|
+
const propertyName = (specifier as JS.Alias).propertyName.element;
|
|
106
|
+
return isIdentifier(propertyName) ? propertyName.simpleName : undefined;
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function localName(spec: JS.ImportSpecifier): string | undefined {
|
|
112
|
+
const specifier = spec.specifier;
|
|
113
|
+
if (specifier.kind !== JS.Kind.Alias) return undefined;
|
|
114
|
+
const alias = (specifier as JS.Alias).alias;
|
|
115
|
+
return isIdentifier(alias) ? alias.simpleName : undefined;
|
|
116
|
+
}
|