Skip to content

Commit 3bc095d

Browse files
JeanMechethePunderWoman
authored andcommitted
feat(core): Add a schematics to migrate provideHttpClient to keep using the HttpXhrBackend implementation.
Exisiting applications will be migrated to keep using the XHR backend to prevent any breaking changes. `withXhr()` is to the `provideHttpClient` provider function.
1 parent 5c432fb commit 3bc095d

File tree

9 files changed

+354
-31
lines changed

9 files changed

+354
-31
lines changed

packages/common/http/src/backend-default-value.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

packages/common/http/src/fetch.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@
88

99
import {
1010
DestroyRef,
11+
ɵformatRuntimeError as formatRuntimeError,
1112
inject,
1213
Injectable,
13-
InjectionToken,
1414
NgZone,
15-
ɵformatRuntimeError as formatRuntimeError,
1615
} from '@angular/core';
1716
import {Observable, Observer} from 'rxjs';
1817
import {RuntimeErrorCode} from './errors';
@@ -35,14 +34,6 @@ import type {} from 'zone.js';
3534

3635
const XSSI_PREFIX = /^\)\]\}',?\n/;
3736

38-
/**
39-
* An internal injection token to reference `FetchBackend` implementation
40-
* in a tree-shakable way.
41-
*/
42-
export const FETCH_BACKEND = new InjectionToken<FetchBackend>(
43-
typeof ngDevMode === 'undefined' || ngDevMode ? 'FETCH_BACKEND' : '',
44-
);
45-
4637
/**
4738
* Uses `fetch` to send requests to a backend server.
4839
*

packages/common/http/src/provider.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ import {
1515
} from '@angular/core';
1616

1717
import {HttpBackend, HttpHandler, HttpInterceptorHandler} from './backend';
18-
import {NG_DEFAULT_HTTP_BACKEND} from './backend-default-value';
1918
import {HttpClient} from './client';
20-
import {FETCH_BACKEND, FetchBackend} from './fetch';
19+
import {FetchBackend} from './fetch';
2120
import {HTTP_INTERCEPTOR_FNS, HttpInterceptorFn, legacyInterceptorFnFactory} from './interceptor';
2221
import {
2322
jsonpCallbackContext,
@@ -27,7 +26,6 @@ import {
2726
} from './jsonp';
2827
import {HttpXhrBackend} from './xhr';
2928
import {XSRF_COOKIE_NAME, XSRF_ENABLED, XSRF_HEADER_NAME, xsrfInterceptorFn} from './xsrf';
30-
import {NG_DEFAULT_HTTP_BACKEND} from './backend-default-value';
3129

3230
/**
3331
* Identifies a particular kind of `HttpFeature`.
@@ -113,13 +111,13 @@ export function provideHttpClient(
113111

114112
const providers: Provider[] = [
115113
HttpClient,
116-
NG_DEFAULT_HTTP_BACKEND,
114+
FetchBackend,
117115
HttpInterceptorHandler,
118116
{provide: HttpHandler, useExisting: HttpInterceptorHandler},
119117
{
120118
provide: HttpBackend,
121119
useFactory: () => {
122-
return inject(FETCH_BACKEND, {optional: true}) ?? inject(NG_DEFAULT_HTTP_BACKEND);
120+
return inject(FetchBackend);
123121
},
124122
},
125123
{
@@ -298,7 +296,6 @@ export function withRequestsMadeViaParent(): HttpFeature<HttpFeatureKind.Request
298296
export function withFetch(): HttpFeature<HttpFeatureKind.Fetch> {
299297
return makeHttpFeature(HttpFeatureKind.Fetch, [
300298
FetchBackend,
301-
{provide: FETCH_BACKEND, useExisting: FetchBackend},
302299
{provide: HttpBackend, useExisting: FetchBackend},
303300
]);
304301
}

packages/core/schematics/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ bundle_entrypoints = [
121121
"router-testing-module-migration",
122122
"packages/core/schematics/ng-generate/router-testing-module-migration/index.js",
123123
],
124+
[
125+
"http-xhr-backend",
126+
"packages/core/schematics/migrations/http-xhr-backend/index.js",
127+
],
124128
]
125129

126130
rollup.rollup(
@@ -133,6 +137,7 @@ rollup.rollup(
133137
"//:node_modules/semver",
134138
"//packages/core/schematics:tsconfig_build",
135139
"//packages/core/schematics/migrations/change-detection-eager",
140+
"//packages/core/schematics/migrations/http-xhr-backend",
136141
"//packages/core/schematics/ng-generate/cleanup-unused-imports",
137142
"//packages/core/schematics/ng-generate/common-to-standalone-migration",
138143
"//packages/core/schematics/ng-generate/control-flow-migration",

packages/core/schematics/migrations.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
"version": "22.0.0",
55
"description": "Adds `ChangeDetectionStrategy.Eager` to all components.",
66
"factory": "./bundles/change-detection-eager.cjs#migrate"
7+
},
8+
"http-xhr-backend": {
9+
"version": "22.0.0",
10+
"description": "Adds 'withXhr' to 'provideHttpClient' function calls when the 'HttpXhrBackend' is used. For more information see: https://angular.dev/api/common/http/withXhr",
11+
"factory": "./bundles/http-xhr-backend.cjs#migrate"
712
}
813
}
914
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
load("//tools:defaults.bzl", "jasmine_test", "ts_project")
2+
3+
package(
4+
default_visibility = [
5+
"//packages/core/schematics:__pkg__",
6+
"//packages/core/schematics/test:__pkg__",
7+
],
8+
)
9+
10+
ts_project(
11+
name = "http-xhr-backend",
12+
srcs = glob(
13+
["**/*.ts"],
14+
exclude = ["*.spec.ts"],
15+
),
16+
deps = [
17+
"//:node_modules/@angular-devkit/schematics",
18+
"//:node_modules/typescript",
19+
"//packages/compiler-cli/private",
20+
"//packages/core/schematics/utils",
21+
"//packages/core/schematics/utils/tsurge",
22+
"//packages/core/schematics/utils/tsurge/helpers/angular_devkit",
23+
],
24+
)
25+
26+
ts_project(
27+
name = "test_lib",
28+
testonly = True,
29+
srcs = glob(["*.spec.ts"]),
30+
deps = [
31+
":http-xhr-backend",
32+
"//:node_modules/typescript",
33+
"//packages/compiler-cli",
34+
"//packages/core/schematics/utils/tsurge",
35+
],
36+
)
37+
38+
jasmine_test(
39+
name = "test",
40+
data = [":test_lib"],
41+
)
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {absoluteFrom} from '@angular/compiler-cli';
10+
import {initMockFileSystem} from '@angular/compiler-cli/private/testing';
11+
import {runTsurgeMigration} from '../../utils/tsurge/testing';
12+
import {XhrBackendMigration} from './migration';
13+
14+
describe('http fetch backend migration', () => {
15+
beforeEach(() => {
16+
initMockFileSystem('Native');
17+
});
18+
19+
it('should update an empty provideHttpClient', async () => {
20+
const {fs} = await runTsurgeMigration(new XhrBackendMigration(), [
21+
{
22+
name: absoluteFrom('/index.ts'),
23+
isProgramRootFile: true,
24+
contents: `
25+
import {AppConfig} from '@angular/core';
26+
import {provideHttpClient} from '@angular/common/http';
27+
28+
const config: AppConfig = [
29+
provideHttpClient(),
30+
]
31+
`,
32+
},
33+
]);
34+
35+
const actual = fs.readFile(absoluteFrom('/index.ts'));
36+
expect(actual).toContain('provideHttpClient(withXhr())');
37+
});
38+
39+
it('should update provideHttpClient without withFetch', async () => {
40+
const {fs} = await runTsurgeMigration(new XhrBackendMigration(), [
41+
{
42+
name: absoluteFrom('/index.ts'),
43+
isProgramRootFile: true,
44+
contents: `
45+
import {AppConfig} from '@angular/core';
46+
import {provideHttpClient, withInterceptorsFromDi, withXsrfConfiguration} from '@angular/common/http';
47+
48+
const config: AppConfig = [
49+
provideHttpClient(withInterceptorsFromDi(), withXsrfConfiguration({})),
50+
]
51+
`,
52+
},
53+
]);
54+
55+
const actual = fs.readFile(absoluteFrom('/index.ts'));
56+
expect(actual).toContain(
57+
'provideHttpClient(withXhr(), withInterceptorsFromDi(), withXsrfConfiguration({}))',
58+
);
59+
expect(actual).toMatch(/import \{.*withXhr.*\}/);
60+
});
61+
62+
it('should update provideHttpClient to remove withFetch', async () => {
63+
const {fs} = await runTsurgeMigration(new XhrBackendMigration(), [
64+
{
65+
name: absoluteFrom('/index.ts'),
66+
isProgramRootFile: true,
67+
contents: `
68+
import {AppConfig} from '@angular/core';
69+
import {provideHttpClient, withFetch, withInterceptorsFromDi, withXsrfConfiguration} from '@angular/common/http';
70+
71+
const config: AppConfig = [
72+
provideHttpClient(withFetch(), withInterceptorsFromDi(), withXsrfConfiguration({})),
73+
]
74+
`,
75+
},
76+
]);
77+
78+
const actual = fs.readFile(absoluteFrom('/index.ts'));
79+
expect(actual).toContain(
80+
'provideHttpClient(withInterceptorsFromDi(), withXsrfConfiguration({}))',
81+
);
82+
expect(actual).not.toContain('withFetch');
83+
});
84+
85+
it('should update provideHttpClient to remove withFetch as only arg', async () => {
86+
const {fs} = await runTsurgeMigration(new XhrBackendMigration(), [
87+
{
88+
name: absoluteFrom('/index.ts'),
89+
isProgramRootFile: true,
90+
contents: `
91+
import {AppConfig} from '@angular/core';
92+
import {provideHttpClient, withFetch, withInterceptorsFromDi, withXsrfConfiguration} from '@angular/common/http';
93+
94+
const config: AppConfig = [
95+
provideHttpClient(withFetch()),
96+
]
97+
`,
98+
},
99+
]);
100+
101+
const actual = fs.readFile(absoluteFrom('/index.ts'));
102+
expect(actual).toContain('provideHttpClient()');
103+
expect(actual).not.toContain('withFetch');
104+
});
105+
106+
it('should not update provideHttpClient if withXhr is already present', async () => {
107+
const {fs} = await runTsurgeMigration(new XhrBackendMigration(), [
108+
{
109+
name: absoluteFrom('/index.ts'),
110+
isProgramRootFile: true,
111+
contents: `
112+
import {AppConfig} from '@angular/core';
113+
import {provideHttpClient, withXhr, withInterceptorsFromDi, withXsrfConfiguration} from '@angular/common/http';
114+
115+
const config: AppConfig = [
116+
provideHttpClient(withXhr(), withInterceptorsFromDi(), withXsrfConfiguration({})),
117+
]
118+
`,
119+
},
120+
]);
121+
122+
const actual = fs.readFile(absoluteFrom('/index.ts'));
123+
expect(actual).toContain(
124+
'provideHttpClient(withXhr(), withInterceptorsFromDi(), withXsrfConfiguration({})),',
125+
);
126+
expect(actual).not.toContain('withFetch');
127+
});
128+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import {Rule} from '@angular-devkit/schematics';
10+
import {runMigrationInDevkit} from '../../utils/tsurge/helpers/angular_devkit';
11+
import {XhrBackendMigration} from './migration';
12+
13+
export function migrate(): Rule {
14+
return async (tree) => {
15+
await runMigrationInDevkit({
16+
tree,
17+
getMigration: () => new XhrBackendMigration(),
18+
});
19+
};
20+
}

0 commit comments

Comments
 (0)