@openrewrite/recipes-angular 0.0.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/package.json +37 -0
- package/src/index.ts +321 -0
- package/src/migration/add-default-configuration.ts +121 -0
- package/src/migration/add-localize-polyfill.ts +51 -0
- package/src/migration/add-module-with-providers-generic.ts +102 -0
- package/src/migration/add-static-false-to-view-queries.ts +92 -0
- package/src/migration/add-testbed-teardown.ts +41 -0
- package/src/migration/enable-aot-build.ts +132 -0
- package/src/migration/explicit-standalone-flag.ts +82 -0
- package/src/migration/migrate-constructor-to-inject.ts +172 -0
- package/src/migration/migrate-input-to-signal.ts +320 -0
- package/src/migration/migrate-output-to-signal.ts +268 -0
- package/src/migration/migrate-query-to-signal.ts +276 -0
- package/src/migration/migrate-to-solution-style-tsconfig.ts +139 -0
- package/src/migration/move-document-to-core.ts +40 -0
- package/src/migration/remove-aot-summaries.ts +72 -0
- package/src/migration/remove-browser-module-with-server-transition.ts +185 -0
- package/src/migration/remove-component-factory-resolver.ts +48 -0
- package/src/migration/remove-default-project.ts +52 -0
- package/src/migration/remove-empty-ng-on-init.ts +80 -0
- package/src/migration/remove-enable-ivy.ts +63 -0
- package/src/migration/remove-entry-components.ts +75 -0
- package/src/migration/remove-es5-browser-support.ts +59 -0
- package/src/migration/remove-extract-css.ts +60 -0
- package/src/migration/remove-ie-polyfills.ts +118 -0
- package/src/migration/remove-module-id.ts +59 -0
- package/src/migration/remove-relative-link-resolution.ts +64 -0
- package/src/migration/remove-standalone-true.ts +50 -0
- package/src/migration/remove-static-false.ts +71 -0
- package/src/migration/remove-zone-js-polyfill.ts +55 -0
- package/src/migration/rename-after-render.ts +32 -0
- package/src/migration/rename-check-no-changes.ts +29 -0
- package/src/migration/rename-file.ts +72 -0
- package/src/migration/rename-pending-tasks.ts +30 -0
- package/src/migration/rename-zoneless-provider.ts +29 -0
- package/src/migration/replace-async-with-wait-for-async.ts +32 -0
- package/src/migration/replace-deep-zone-js-imports.ts +118 -0
- package/src/migration/replace-http-client-module.ts +276 -0
- package/src/migration/replace-initial-navigation.ts +73 -0
- package/src/migration/replace-inject-flags.ts +83 -0
- package/src/migration/replace-load-children-string.ts +48 -0
- package/src/migration/replace-node-sass-with-sass.ts +22 -0
- package/src/migration/replace-router-link-with-href.ts +37 -0
- package/src/migration/replace-testbed-get-with-inject.ts +33 -0
- package/src/migration/replace-untyped-forms.ts +59 -0
- package/src/migration/replace-validator-with-validators.ts +41 -0
- package/src/migration/replace-view-encapsulation-native.ts +51 -0
- package/src/migration/update-component-template-url.ts +186 -0
- package/src/migration/update-tsconfig-module.ts +75 -0
- package/src/migration/update-tsconfig-target.ts +61 -0
- package/src/migration/upgrade-to-angular-10.ts +52 -0
- package/src/migration/upgrade-to-angular-11.ts +52 -0
- package/src/migration/upgrade-to-angular-12.ts +43 -0
- package/src/migration/upgrade-to-angular-13.ts +45 -0
- package/src/migration/upgrade-to-angular-14.ts +44 -0
- package/src/migration/upgrade-to-angular-15.ts +43 -0
- package/src/migration/upgrade-to-angular-16.ts +57 -0
- package/src/migration/upgrade-to-angular-17.ts +43 -0
- package/src/migration/upgrade-to-angular-18.ts +69 -0
- package/src/migration/upgrade-to-angular-19.ts +52 -0
- package/src/migration/upgrade-to-angular-20.ts +47 -0
- package/src/migration/upgrade-to-angular-21.ts +53 -0
- package/src/migration/upgrade-to-angular-8.ts +54 -0
- package/src/migration/upgrade-to-angular-9.ts +69 -0
- package/src/search/find-analyze-for-entry-components-usage.ts +46 -0
- package/src/search/find-angular-decorator.ts +58 -0
- package/src/search/find-angular-http-usage.ts +35 -0
- package/src/search/find-animation-driver-matches-element.ts +38 -0
- package/src/search/find-async-test-helper-usage.ts +45 -0
- package/src/search/find-bare-module-with-providers.ts +47 -0
- package/src/search/find-browser-transfer-state-module-usage.ts +45 -0
- package/src/search/find-common-module-usage.ts +47 -0
- package/src/search/find-compiler-factory-usage.ts +51 -0
- package/src/search/find-date-pipe-default-timezone-usage.ts +46 -0
- package/src/search/find-effect-timing-usage.ts +28 -0
- package/src/search/find-empty-projectable-nodes.ts +68 -0
- package/src/search/find-fake-async-usage.ts +37 -0
- package/src/search/find-hammer-js-usage.ts +48 -0
- package/src/search/find-i18n-usage.ts +94 -0
- package/src/search/find-karma-usage.ts +47 -0
- package/src/search/find-load-children-string-usage.ts +43 -0
- package/src/search/find-missing-injectable.ts +75 -0
- package/src/search/find-ng-class-usage.ts +45 -0
- package/src/search/find-ng-style-usage.ts +45 -0
- package/src/search/find-path-match-type-usage.ts +44 -0
- package/src/search/find-platform-dynamic-server-usage.ts +38 -0
- package/src/search/find-platform-webworker-usage.ts +34 -0
- package/src/search/find-platform-worker-usage.ts +39 -0
- package/src/search/find-preserve-fragment-usage.ts +32 -0
- package/src/search/find-preserve-query-params-usage.ts +32 -0
- package/src/search/find-provided-in-deprecated-usage.ts +65 -0
- package/src/search/find-reflective-injector-usage.ts +45 -0
- package/src/search/find-render-application-usage.ts +47 -0
- package/src/search/find-render-component-type-usage.ts +46 -0
- package/src/search/find-render-module-factory-usage.ts +45 -0
- package/src/search/find-renderer-usage.ts +46 -0
- package/src/search/find-resource-cache-provider-usage.ts +38 -0
- package/src/search/find-root-renderer-usage.ts +47 -0
- package/src/search/find-rxjs-compat-usage.ts +40 -0
- package/src/search/find-server-transfer-state-module-usage.ts +38 -0
- package/src/search/find-setup-testing-router-usage.ts +45 -0
- package/src/search/find-testability-pending-request-usage.ts +38 -0
- package/src/search/find-undecorated-angular-class.ts +78 -0
- package/src/search/find-with-no-dom-reuse-usage.ts +46 -0
- package/src/search/find-wrapped-value-usage.ts +46 -0
- package/src/search/find-zone-js-usage.ts +43 -0
|
@@ -0,0 +1,45 @@
|
|
|
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, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {J, isIdentifier} from "@openrewrite/rewrite/java";
|
|
10
|
+
|
|
11
|
+
export class FindReflectiveInjectorUsage extends Recipe {
|
|
12
|
+
readonly name = "org.openrewrite.angular.search.find-reflective-injector-usage";
|
|
13
|
+
readonly displayName: string = "Find `ReflectiveInjector` usage";
|
|
14
|
+
readonly description: string = "Finds usages of `ReflectiveInjector` which was removed in Angular 16. " +
|
|
15
|
+
"Use `Injector.create` as a replacement.";
|
|
16
|
+
|
|
17
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
18
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
19
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
20
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
21
|
+
if (!imp.moduleSpecifier) return imp;
|
|
22
|
+
|
|
23
|
+
const moduleSpec = imp.moduleSpecifier.element;
|
|
24
|
+
if (moduleSpec.kind !== J.Kind.Literal) return imp;
|
|
25
|
+
|
|
26
|
+
const moduleName = (moduleSpec as J.Literal).value as string;
|
|
27
|
+
if (moduleName !== '@angular/core') return imp;
|
|
28
|
+
|
|
29
|
+
const namedBindings = imp.importClause?.namedBindings;
|
|
30
|
+
if (namedBindings?.kind === JS.Kind.NamedImports) {
|
|
31
|
+
const named = namedBindings as JS.NamedImports;
|
|
32
|
+
for (const specifier of named.elements.elements) {
|
|
33
|
+
const spec = specifier.element as JS.ImportSpecifier;
|
|
34
|
+
if (isIdentifier(spec.specifier) && spec.specifier.simpleName === 'ReflectiveInjector') {
|
|
35
|
+
return foundSearchResult(imp,
|
|
36
|
+
"ReflectiveInjector has been removed in Angular 16. Use Injector.create as a replacement.");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return imp;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
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, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {J, isIdentifier} from "@openrewrite/rewrite/java";
|
|
10
|
+
|
|
11
|
+
export class FindRenderApplicationUsage extends Recipe {
|
|
12
|
+
readonly name = "org.openrewrite.angular.search.find-render-application-usage";
|
|
13
|
+
readonly displayName: string = "Find `renderApplication` usage";
|
|
14
|
+
readonly description: string = "Finds usages of `renderApplication` from `@angular/platform-server`. " +
|
|
15
|
+
"In Angular 16 the signature changed: it no longer accepts a root component as the first argument. " +
|
|
16
|
+
"Use a bootstrapping function that returns `Promise<ApplicationRef>` instead.";
|
|
17
|
+
|
|
18
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
19
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
20
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
21
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
22
|
+
if (!imp.moduleSpecifier) return imp;
|
|
23
|
+
|
|
24
|
+
const moduleSpec = imp.moduleSpecifier.element;
|
|
25
|
+
if (moduleSpec.kind !== J.Kind.Literal) return imp;
|
|
26
|
+
|
|
27
|
+
const moduleName = (moduleSpec as J.Literal).value as string;
|
|
28
|
+
if (moduleName !== '@angular/platform-server') return imp;
|
|
29
|
+
|
|
30
|
+
const namedBindings = imp.importClause?.namedBindings;
|
|
31
|
+
if (namedBindings?.kind === JS.Kind.NamedImports) {
|
|
32
|
+
const named = namedBindings as JS.NamedImports;
|
|
33
|
+
for (const specifier of named.elements.elements) {
|
|
34
|
+
const spec = specifier.element as JS.ImportSpecifier;
|
|
35
|
+
if (isIdentifier(spec.specifier) && spec.specifier.simpleName === 'renderApplication') {
|
|
36
|
+
return foundSearchResult(imp,
|
|
37
|
+
"renderApplication signature changed in Angular 16. " +
|
|
38
|
+
"It no longer accepts a root component directly; pass a bootstrapping function returning Promise<ApplicationRef> instead.");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return imp;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
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, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {J, isIdentifier} from "@openrewrite/rewrite/java";
|
|
10
|
+
|
|
11
|
+
export class FindRenderComponentTypeUsage extends Recipe {
|
|
12
|
+
readonly name = "org.openrewrite.angular.search.find-render-component-type-usage";
|
|
13
|
+
readonly displayName: string = "Find deprecated `RenderComponentType` usage";
|
|
14
|
+
readonly description: string = "Finds imports of the deprecated `RenderComponentType` from `@angular/core`. " +
|
|
15
|
+
"`RenderComponentType` was part of the View Engine API, deprecated in Angular 4, and removed in Angular 9.";
|
|
16
|
+
|
|
17
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
18
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
19
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
20
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
21
|
+
if (!imp.moduleSpecifier) return imp;
|
|
22
|
+
|
|
23
|
+
const moduleSpec = imp.moduleSpecifier.element;
|
|
24
|
+
if (moduleSpec.kind !== J.Kind.Literal) return imp;
|
|
25
|
+
|
|
26
|
+
const moduleName = (moduleSpec as J.Literal).value as string;
|
|
27
|
+
if (moduleName !== '@angular/core') return imp;
|
|
28
|
+
|
|
29
|
+
const namedBindings = imp.importClause?.namedBindings;
|
|
30
|
+
if (namedBindings?.kind === JS.Kind.NamedImports) {
|
|
31
|
+
const named = namedBindings as JS.NamedImports;
|
|
32
|
+
for (const specifier of named.elements.elements) {
|
|
33
|
+
const spec = specifier.element as JS.ImportSpecifier;
|
|
34
|
+
if (isIdentifier(spec.specifier) && spec.specifier.simpleName === 'RenderComponentType') {
|
|
35
|
+
return foundSearchResult(imp,
|
|
36
|
+
"`RenderComponentType` was deprecated in Angular 4 and removed in Angular 9. " +
|
|
37
|
+
"This API was part of View Engine and has no Ivy equivalent.");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return imp;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
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, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {J, isIdentifier} from "@openrewrite/rewrite/java";
|
|
10
|
+
|
|
11
|
+
export class FindRenderModuleFactoryUsage extends Recipe {
|
|
12
|
+
readonly name = "org.openrewrite.angular.search.find-render-module-factory-usage";
|
|
13
|
+
readonly displayName: string = "Find `renderModuleFactory` usage";
|
|
14
|
+
readonly description: string = "Finds usages of `renderModuleFactory` from `@angular/platform-server` " +
|
|
15
|
+
"which was removed in Angular 16. Use `renderModule` instead.";
|
|
16
|
+
|
|
17
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
18
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
19
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
20
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
21
|
+
if (!imp.moduleSpecifier) return imp;
|
|
22
|
+
|
|
23
|
+
const moduleSpec = imp.moduleSpecifier.element;
|
|
24
|
+
if (moduleSpec.kind !== J.Kind.Literal) return imp;
|
|
25
|
+
|
|
26
|
+
const moduleName = (moduleSpec as J.Literal).value as string;
|
|
27
|
+
if (moduleName !== '@angular/platform-server') return imp;
|
|
28
|
+
|
|
29
|
+
const namedBindings = imp.importClause?.namedBindings;
|
|
30
|
+
if (namedBindings?.kind === JS.Kind.NamedImports) {
|
|
31
|
+
const named = namedBindings as JS.NamedImports;
|
|
32
|
+
for (const specifier of named.elements.elements) {
|
|
33
|
+
const spec = specifier.element as JS.ImportSpecifier;
|
|
34
|
+
if (isIdentifier(spec.specifier) && spec.specifier.simpleName === 'renderModuleFactory') {
|
|
35
|
+
return foundSearchResult(imp,
|
|
36
|
+
"renderModuleFactory has been removed in Angular 16. Use renderModule instead.");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return imp;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
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, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {J, isIdentifier} from "@openrewrite/rewrite/java";
|
|
10
|
+
|
|
11
|
+
export class FindRendererUsage extends Recipe {
|
|
12
|
+
readonly name = "org.openrewrite.angular.search.find-renderer-usage";
|
|
13
|
+
readonly displayName: string = "Find deprecated `Renderer` usage";
|
|
14
|
+
readonly description: string = "Finds imports of the deprecated `Renderer` from `@angular/core`. " +
|
|
15
|
+
"`Renderer` was deprecated in Angular 4 and removed in Angular 9. Users should use `Renderer2` instead.";
|
|
16
|
+
|
|
17
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
18
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
19
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
20
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
21
|
+
if (!imp.moduleSpecifier) return imp;
|
|
22
|
+
|
|
23
|
+
const moduleSpec = imp.moduleSpecifier.element;
|
|
24
|
+
if (moduleSpec.kind !== J.Kind.Literal) return imp;
|
|
25
|
+
|
|
26
|
+
const moduleName = (moduleSpec as J.Literal).value as string;
|
|
27
|
+
if (moduleName !== '@angular/core') return imp;
|
|
28
|
+
|
|
29
|
+
const namedBindings = imp.importClause?.namedBindings;
|
|
30
|
+
if (namedBindings?.kind === JS.Kind.NamedImports) {
|
|
31
|
+
const named = namedBindings as JS.NamedImports;
|
|
32
|
+
for (const specifier of named.elements.elements) {
|
|
33
|
+
const spec = specifier.element as JS.ImportSpecifier;
|
|
34
|
+
if (isIdentifier(spec.specifier) && spec.specifier.simpleName === 'Renderer') {
|
|
35
|
+
return foundSearchResult(imp,
|
|
36
|
+
"`Renderer` was deprecated in Angular 4 and removed in Angular 9. " +
|
|
37
|
+
"Use `Renderer2` instead.");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return imp;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {ExecutionContext, foundSearchResult, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
2
|
+
import {JavaScriptVisitor, JS} from "@openrewrite/rewrite/javascript";
|
|
3
|
+
import {J, isIdentifier} from "@openrewrite/rewrite/java";
|
|
4
|
+
|
|
5
|
+
export class FindResourceCacheProviderUsage extends Recipe {
|
|
6
|
+
readonly name = "org.openrewrite.angular.search.find-resource-cache-provider-usage";
|
|
7
|
+
readonly displayName: string = "Find `RESOURCE_CACHE_PROVIDER` usage";
|
|
8
|
+
readonly description: string = "Finds usages of the removed `RESOURCE_CACHE_PROVIDER` from `@angular/platform-browser-dynamic`. This unused API was removed in Angular 18.";
|
|
9
|
+
|
|
10
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
11
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
12
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
13
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
14
|
+
if (!imp.moduleSpecifier) return imp;
|
|
15
|
+
|
|
16
|
+
const moduleSpec = imp.moduleSpecifier.element;
|
|
17
|
+
if (moduleSpec.kind !== J.Kind.Literal) return imp;
|
|
18
|
+
|
|
19
|
+
const moduleName = (moduleSpec as J.Literal).value as string;
|
|
20
|
+
if (moduleName !== '@angular/platform-browser-dynamic') return imp;
|
|
21
|
+
|
|
22
|
+
const namedBindings = imp.importClause?.namedBindings;
|
|
23
|
+
if (namedBindings?.kind === JS.Kind.NamedImports) {
|
|
24
|
+
const named = namedBindings as JS.NamedImports;
|
|
25
|
+
for (const specifier of named.elements.elements) {
|
|
26
|
+
const spec = specifier.element as JS.ImportSpecifier;
|
|
27
|
+
if (isIdentifier(spec.specifier) && spec.specifier.simpleName === 'RESOURCE_CACHE_PROVIDER') {
|
|
28
|
+
return foundSearchResult(imp,
|
|
29
|
+
"RESOURCE_CACHE_PROVIDER has been removed from @angular/platform-browser-dynamic in Angular 18.");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return imp;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
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, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {J, isIdentifier} from "@openrewrite/rewrite/java";
|
|
10
|
+
|
|
11
|
+
export class FindRootRendererUsage extends Recipe {
|
|
12
|
+
readonly name = "org.openrewrite.angular.search.find-root-renderer-usage";
|
|
13
|
+
readonly displayName: string = "Find deprecated `RootRenderer` usage";
|
|
14
|
+
readonly description: string = "Finds imports of the deprecated `RootRenderer` from `@angular/core`. " +
|
|
15
|
+
"`RootRenderer` was part of the View Engine API, deprecated in Angular 4, and removed in Angular 9. " +
|
|
16
|
+
"Use `RendererFactory2` instead.";
|
|
17
|
+
|
|
18
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
19
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
20
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
21
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
22
|
+
if (!imp.moduleSpecifier) return imp;
|
|
23
|
+
|
|
24
|
+
const moduleSpec = imp.moduleSpecifier.element;
|
|
25
|
+
if (moduleSpec.kind !== J.Kind.Literal) return imp;
|
|
26
|
+
|
|
27
|
+
const moduleName = (moduleSpec as J.Literal).value as string;
|
|
28
|
+
if (moduleName !== '@angular/core') return imp;
|
|
29
|
+
|
|
30
|
+
const namedBindings = imp.importClause?.namedBindings;
|
|
31
|
+
if (namedBindings?.kind === JS.Kind.NamedImports) {
|
|
32
|
+
const named = namedBindings as JS.NamedImports;
|
|
33
|
+
for (const specifier of named.elements.elements) {
|
|
34
|
+
const spec = specifier.element as JS.ImportSpecifier;
|
|
35
|
+
if (isIdentifier(spec.specifier) && spec.specifier.simpleName === 'RootRenderer') {
|
|
36
|
+
return foundSearchResult(imp,
|
|
37
|
+
"`RootRenderer` was deprecated in Angular 4 and removed in Angular 9. " +
|
|
38
|
+
"Use `RendererFactory2` instead.");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return imp;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
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, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {J} from "@openrewrite/rewrite/java";
|
|
10
|
+
|
|
11
|
+
const RXJS_6_ENTRY_POINTS = new Set(['rxjs/operators', 'rxjs/ajax', 'rxjs/webSocket', 'rxjs/testing', 'rxjs/fetch']);
|
|
12
|
+
|
|
13
|
+
export class FindRxjsCompatUsage extends Recipe {
|
|
14
|
+
readonly name = "org.openrewrite.angular.search.find-rxjs-compat-usage";
|
|
15
|
+
readonly displayName: string = "Find RxJS 5-style imports requiring `rxjs-compat`";
|
|
16
|
+
readonly description: string = "Finds imports using RxJS 5-style deep import paths " +
|
|
17
|
+
"(e.g. `rxjs/Observable`, `rxjs/add/operator/map`) that require the `rxjs-compat` package. " +
|
|
18
|
+
"These should be migrated to RxJS 6+ import paths before removing `rxjs-compat`.";
|
|
19
|
+
|
|
20
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
21
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
22
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
23
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
24
|
+
if (!imp.moduleSpecifier) return imp;
|
|
25
|
+
|
|
26
|
+
const moduleSpec = imp.moduleSpecifier.element;
|
|
27
|
+
if (moduleSpec.kind !== J.Kind.Literal) return imp;
|
|
28
|
+
|
|
29
|
+
const moduleName = (moduleSpec as J.Literal).value as string;
|
|
30
|
+
if (!moduleName.startsWith('rxjs/')) return imp;
|
|
31
|
+
if (moduleName === 'rxjs' || RXJS_6_ENTRY_POINTS.has(moduleName)) return imp;
|
|
32
|
+
|
|
33
|
+
return foundSearchResult(imp,
|
|
34
|
+
"RxJS 5-style deep import. Migrate to RxJS 6+ imports " +
|
|
35
|
+
"(e.g. `import { Observable } from 'rxjs'`, `import { map } from 'rxjs/operators'`) " +
|
|
36
|
+
"and remove `rxjs-compat`.");
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {ExecutionContext, foundSearchResult, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
2
|
+
import {JavaScriptVisitor, JS} from "@openrewrite/rewrite/javascript";
|
|
3
|
+
import {J, isIdentifier} from "@openrewrite/rewrite/java";
|
|
4
|
+
|
|
5
|
+
export class FindServerTransferStateModuleUsage extends Recipe {
|
|
6
|
+
readonly name = "org.openrewrite.angular.search.find-server-transfer-state-module-usage";
|
|
7
|
+
readonly displayName: string = "Find `ServerTransferStateModule` usage";
|
|
8
|
+
readonly description: string = "Finds usages of the removed `ServerTransferStateModule` from `@angular/platform-server`. In Angular 18, `TransferState` works without providing this module.";
|
|
9
|
+
|
|
10
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
11
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
12
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
13
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
14
|
+
if (!imp.moduleSpecifier) return imp;
|
|
15
|
+
|
|
16
|
+
const moduleSpec = imp.moduleSpecifier.element;
|
|
17
|
+
if (moduleSpec.kind !== J.Kind.Literal) return imp;
|
|
18
|
+
|
|
19
|
+
const moduleName = (moduleSpec as J.Literal).value as string;
|
|
20
|
+
if (moduleName !== '@angular/platform-server') return imp;
|
|
21
|
+
|
|
22
|
+
const namedBindings = imp.importClause?.namedBindings;
|
|
23
|
+
if (namedBindings?.kind === JS.Kind.NamedImports) {
|
|
24
|
+
const named = namedBindings as JS.NamedImports;
|
|
25
|
+
for (const specifier of named.elements.elements) {
|
|
26
|
+
const spec = specifier.element as JS.ImportSpecifier;
|
|
27
|
+
if (isIdentifier(spec.specifier) && spec.specifier.simpleName === 'ServerTransferStateModule') {
|
|
28
|
+
return foundSearchResult(imp,
|
|
29
|
+
"ServerTransferStateModule has been removed in Angular 18. TransferState works without this module.");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return imp;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
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, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {J, isIdentifier} from "@openrewrite/rewrite/java";
|
|
10
|
+
|
|
11
|
+
export class FindSetupTestingRouterUsage extends Recipe {
|
|
12
|
+
readonly name = "org.openrewrite.angular.search.find-setup-testing-router-usage";
|
|
13
|
+
readonly displayName: string = "Find `setupTestingRouter` usage";
|
|
14
|
+
readonly description: string = "Finds usages of the removed `setupTestingRouter` function from `@angular/router/testing`. " +
|
|
15
|
+
"This function was removed in Angular 17. Use `RouterModule.forRoot` or `provideRouter` to set up the Router for tests instead.";
|
|
16
|
+
|
|
17
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
18
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
19
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
20
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
21
|
+
if (!imp.moduleSpecifier) return imp;
|
|
22
|
+
|
|
23
|
+
const moduleSpec = imp.moduleSpecifier.element;
|
|
24
|
+
if (moduleSpec.kind !== J.Kind.Literal) return imp;
|
|
25
|
+
|
|
26
|
+
const moduleName = (moduleSpec as J.Literal).value as string;
|
|
27
|
+
if (moduleName !== '@angular/router/testing') return imp;
|
|
28
|
+
|
|
29
|
+
const namedBindings = imp.importClause?.namedBindings;
|
|
30
|
+
if (namedBindings?.kind === JS.Kind.NamedImports) {
|
|
31
|
+
const named = namedBindings as JS.NamedImports;
|
|
32
|
+
for (const specifier of named.elements.elements) {
|
|
33
|
+
const spec = specifier.element as JS.ImportSpecifier;
|
|
34
|
+
if (isIdentifier(spec.specifier) && spec.specifier.simpleName === 'setupTestingRouter') {
|
|
35
|
+
return foundSearchResult(imp,
|
|
36
|
+
"setupTestingRouter has been removed in Angular 17. Use RouterModule.forRoot or provideRouter instead.");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return imp;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {ExecutionContext, foundSearchResult, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
2
|
+
import {JavaScriptVisitor, JS} from "@openrewrite/rewrite/javascript";
|
|
3
|
+
import {J, isIdentifier} from "@openrewrite/rewrite/java";
|
|
4
|
+
|
|
5
|
+
export class FindTestabilityPendingRequestUsage extends Recipe {
|
|
6
|
+
readonly name = "org.openrewrite.angular.search.find-testability-pending-request-usage";
|
|
7
|
+
readonly displayName: string = "Find removed Testability pending request methods";
|
|
8
|
+
readonly description: string = "Finds imports of `Testability` from `@angular/core`, which had `increasePendingRequestCount`, `decreasePendingRequestCount`, and `getPendingRequestCount` removed in Angular 18. These are now tracked with zones.";
|
|
9
|
+
|
|
10
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
11
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
12
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
13
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
14
|
+
if (!imp.moduleSpecifier) return imp;
|
|
15
|
+
|
|
16
|
+
const moduleSpec = imp.moduleSpecifier.element;
|
|
17
|
+
if (moduleSpec.kind !== J.Kind.Literal) return imp;
|
|
18
|
+
|
|
19
|
+
const moduleName = (moduleSpec as J.Literal).value as string;
|
|
20
|
+
if (moduleName !== '@angular/core') return imp;
|
|
21
|
+
|
|
22
|
+
const namedBindings = imp.importClause?.namedBindings;
|
|
23
|
+
if (namedBindings?.kind === JS.Kind.NamedImports) {
|
|
24
|
+
const named = namedBindings as JS.NamedImports;
|
|
25
|
+
for (const specifier of named.elements.elements) {
|
|
26
|
+
const spec = specifier.element as JS.ImportSpecifier;
|
|
27
|
+
if (isIdentifier(spec.specifier) && spec.specifier.simpleName === 'Testability') {
|
|
28
|
+
return foundSearchResult(imp,
|
|
29
|
+
"Testability pending request methods (increasePendingRequestCount, decreasePendingRequestCount, getPendingRequestCount) have been removed in Angular 18.");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return imp;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
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, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {J, isIdentifier} from "@openrewrite/rewrite/java";
|
|
10
|
+
|
|
11
|
+
const CLASS_DECORATORS = new Set(['Component', 'Directive', 'Pipe', 'Injectable', 'NgModule']);
|
|
12
|
+
const MEMBER_DECORATORS = new Set([
|
|
13
|
+
'Input', 'Output', 'ViewChild', 'ViewChildren',
|
|
14
|
+
'ContentChild', 'ContentChildren', 'HostBinding', 'HostListener'
|
|
15
|
+
]);
|
|
16
|
+
const LIFECYCLE_HOOKS = new Set([
|
|
17
|
+
'ngOnInit', 'ngOnDestroy', 'ngOnChanges', 'ngDoCheck',
|
|
18
|
+
'ngAfterViewInit', 'ngAfterViewChecked',
|
|
19
|
+
'ngAfterContentInit', 'ngAfterContentChecked'
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
export class FindUndecoratedAngularClass extends Recipe {
|
|
23
|
+
readonly name = "org.openrewrite.angular.search.find-undecorated-angular-class";
|
|
24
|
+
readonly displayName: string = "Find undecorated classes with Angular features";
|
|
25
|
+
readonly description: string = "Finds classes that use Angular member decorators (`@Input`, `@Output`, `@ViewChild`, etc.) " +
|
|
26
|
+
"or implement lifecycle hooks (`ngOnInit`, `ngOnDestroy`, etc.) but lack a class-level Angular decorator. " +
|
|
27
|
+
"Angular 9 with Ivy requires all classes using Angular features to have an explicit decorator.";
|
|
28
|
+
|
|
29
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
30
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
31
|
+
override async visitClassDeclaration(classDecl: J.ClassDeclaration, p: ExecutionContext): Promise<J | undefined> {
|
|
32
|
+
const cd = await super.visitClassDeclaration(classDecl, p) as J.ClassDeclaration;
|
|
33
|
+
if (!cd) return cd;
|
|
34
|
+
|
|
35
|
+
const hasClassDecorator = cd.leadingAnnotations.some(a =>
|
|
36
|
+
isIdentifier(a.annotationType) && CLASS_DECORATORS.has(a.annotationType.simpleName)
|
|
37
|
+
);
|
|
38
|
+
if (hasClassDecorator) return cd;
|
|
39
|
+
|
|
40
|
+
let hasAngularFeature = false;
|
|
41
|
+
|
|
42
|
+
for (const stmt of cd.body.statements) {
|
|
43
|
+
const el = stmt.element;
|
|
44
|
+
|
|
45
|
+
if (el.kind === J.Kind.VariableDeclarations) {
|
|
46
|
+
const varDecl = el as J.VariableDeclarations;
|
|
47
|
+
if (varDecl.leadingAnnotations.some(a =>
|
|
48
|
+
isIdentifier(a.annotationType) && MEMBER_DECORATORS.has(a.annotationType.simpleName)
|
|
49
|
+
)) {
|
|
50
|
+
hasAngularFeature = true;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (el.kind === J.Kind.MethodDeclaration) {
|
|
56
|
+
const method = el as J.MethodDeclaration;
|
|
57
|
+
if (LIFECYCLE_HOOKS.has(method.name.simpleName)) {
|
|
58
|
+
hasAngularFeature = true;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
if (method.leadingAnnotations.some(a =>
|
|
62
|
+
isIdentifier(a.annotationType) && MEMBER_DECORATORS.has(a.annotationType.simpleName)
|
|
63
|
+
)) {
|
|
64
|
+
hasAngularFeature = true;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!hasAngularFeature) return cd;
|
|
71
|
+
|
|
72
|
+
return foundSearchResult(cd,
|
|
73
|
+
"Class uses Angular features but has no class-level decorator. " +
|
|
74
|
+
"Angular 9 with Ivy requires an explicit `@Component`, `@Directive`, `@Pipe`, or `@Injectable` decorator.");
|
|
75
|
+
}
|
|
76
|
+
}();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
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, JS} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
import {J, isIdentifier} from "@openrewrite/rewrite/java";
|
|
10
|
+
|
|
11
|
+
export class FindWithNoDomReuseUsage extends Recipe {
|
|
12
|
+
readonly name = "org.openrewrite.angular.search.find-with-no-dom-reuse-usage";
|
|
13
|
+
readonly displayName: string = "Find `withNoDomReuse` usage";
|
|
14
|
+
readonly description: string = "Finds usages of the removed `withNoDomReuse` function from `@angular/platform-browser`. " +
|
|
15
|
+
"This function was removed in Angular 17. To disable hydration, remove the `provideClientHydration()` call " +
|
|
16
|
+
"from your providers or use the `ngSkipHydration` attribute on specific components.";
|
|
17
|
+
|
|
18
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
19
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
20
|
+
protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
|
|
21
|
+
const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
|
|
22
|
+
if (!imp.moduleSpecifier) return imp;
|
|
23
|
+
|
|
24
|
+
const moduleSpec = imp.moduleSpecifier.element;
|
|
25
|
+
if (moduleSpec.kind !== J.Kind.Literal) return imp;
|
|
26
|
+
|
|
27
|
+
const moduleName = (moduleSpec as J.Literal).value as string;
|
|
28
|
+
if (moduleName !== '@angular/platform-browser') return imp;
|
|
29
|
+
|
|
30
|
+
const namedBindings = imp.importClause?.namedBindings;
|
|
31
|
+
if (namedBindings?.kind === JS.Kind.NamedImports) {
|
|
32
|
+
const named = namedBindings as JS.NamedImports;
|
|
33
|
+
for (const specifier of named.elements.elements) {
|
|
34
|
+
const spec = specifier.element as JS.ImportSpecifier;
|
|
35
|
+
if (isIdentifier(spec.specifier) && spec.specifier.simpleName === 'withNoDomReuse') {
|
|
36
|
+
return foundSearchResult(imp,
|
|
37
|
+
"withNoDomReuse has been removed in Angular 17. Remove provideClientHydration() or use ngSkipHydration instead.");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return imp;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|