Skip to content

Commit 7cdbd79

Browse files
fix(schematics): keep getVertexAI rewrites on the Vertex AI backend
getAI and getVertexAI coexisted in the old vertexai module and default to different backends: plain getAI() talks to the Gemini Developer API, so rewriting getVertexAI as a plain rename silently moved Vertex users onto another Google API. Rewritten calls now become getAI(app, { backend: new VertexAIBackend(location?) }), and every rewritten site is logged with its file and line. Anything the migration cannot rewrite with identical semantics is left in place with a per-site warning, and the moved import path then fails to compile, so nothing changes behavior silently. That covers non-literal options, getVertexAI handed around as a value or re-exported, local declarations that shadow a rewritten name, getAI or VertexAIBackend bound from a non AI Logic source, star re-exports, files with syntax errors, and removed symbols with no drop-in successor (VertexAIOptions). Also addressed from review: an un-aliased export { X } from an old entry point keeps the file's public export name via an alias, a root project without a sourceRoot is walked (with a node_modules guard), and typescript is now an optional peer resolved from the workspace at update time, loaded after the firebase alignment so a resolution failure costs only the rewrite. Direct firebase/vertexai imports migrate under the same rules. The migration is reorganized from one file into schematics/update/v21/vertexai-to-ai/ (rename tables, shared interfaces, compiler resolution, safety analyses, the two scan passes, edit builders, orchestration). applyEdits is exported only so its edit-conflict guard is unit-testable, and ngUpdate's optional compiler parameter exists for the ESM test run, where require is unavailable.
1 parent 6c26e38 commit 7cdbd79

16 files changed

Lines changed: 2134 additions & 375 deletions

docs/ai.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Firebase AI Logic gives you access to the latest generative AI models from Googl
88

99
[Learn more](https://firebase.google.com/docs/ai-logic)
1010

11-
> Firebase AI Logic was previously called **Vertex AI in Firebase**. If you are upgrading from AngularFire 20, the module moved from `@angular/fire/vertexai` to `@angular/fire/ai` and the symbols were renamed (`getVertexAI` to `getAI`, `provideVertexAI` to `provideAI`, `VertexAI` to `AI`). Running `ng update @angular/fire` rewrites these for you. See the [AngularFire 20 to 21 upgrade guide](./version-21-upgrade.md).
11+
> Firebase AI Logic was previously called **Vertex AI in Firebase**. If you are upgrading from AngularFire 20, the module moved from `@angular/fire/vertexai` to `@angular/fire/ai` and most symbols were renamed (`provideVertexAI` to `provideAI`, `VertexAI` to `AI`). One is not a rename: plain `getAI()` uses the Gemini Developer API backend, so the old `getVertexAI()` maps to `getAI(app, { backend: new VertexAIBackend() })`. Running `ng update @angular/fire` rewrites all of this for you and keeps your app on the Vertex AI backend. See the [AngularFire 20 to 21 upgrade guide](./version-21-upgrade.md).
1212
1313
## Dependency Injection
1414

docs/version-21-upgrade.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,21 @@ The Vertex AI module has been renamed to Firebase AI Logic. The `@angular/fire/v
2020

2121
| Before (`@angular/fire/vertexai`) | After (`@angular/fire/ai`) |
2222
|---|---|
23-
| `getVertexAI` | `getAI` |
23+
| `getVertexAI(app?, { location? })` | `getAI(app, { backend: new VertexAIBackend(location?) })` |
2424
| `provideVertexAI` | `provideAI` |
2525
| `VertexAI` | `AI` |
26+
| `VertexAIError` | `AIError` |
27+
| `VertexAIErrorCode` | `AIErrorCode` |
28+
| `VertexAIModel` | `AIModel` |
2629
| `VertexAIInstances` | `AIInstances` |
2730
| `vertexAIInstance$` | `AIInstance$` |
2831
| `VertexAIModule` | `AIModule` |
2932

30-
`ng update @angular/fire` rewrites these imports and identifiers for you. `getGenerativeModel` and `getImagenModel` keep their names. If you import directly from the Firebase SDK, note it also renamed `firebase/vertexai` to `firebase/ai`. See [ai.md](./ai.md) for current usage.
33+
**`getVertexAI` is not a plain rename.** `getAI` already existed alongside it, and a plain `getAI()` call talks to the Gemini Developer API backend, not to Vertex AI. The equivalent of `getVertexAI()` is `getAI(app, { backend: new VertexAIBackend() })`, which is what the migration writes, so your app keeps calling the Vertex AI backend it was configured, enabled, and billed for. A `location` option moves into the `VertexAIBackend` constructor.
34+
35+
`ng update @angular/fire` rewrites these imports and identifiers for you and logs every `getVertexAI` call it rewrites. Code it cannot rewrite safely (for example when the options are not a literal `{ location }` object or that literal references other rewritten symbols, when the function itself is handed around as a value, when a local declaration in the file reuses an imported symbol's name, or when the file already binds `getAI` or `VertexAIBackend` from a source other than AI Logic) is left in place with a warning. The import path itself still moves to the new entry point, so the leftover code fails to compile there, and nothing changes backends silently. A file where a named `getVertexAI` import has any use that cannot be rewritten keeps every use of its named `getVertexAI` imports in place (namespace-style `ns.getVertexAI(...)` calls are judged per call), and each skipped call is logged. `export * from '@angular/fire/vertexai'` is also left alone (rewriting it would silently rename your re-exported public symbols), so replace it with named re-exports by hand. `VertexAIOptions` was removed rather than renamed (the new `AIOptions` takes a `backend` instead of a `location`), so imports of it are left and warned about. Migrate those sites using the table above. `getGenerativeModel` and `getImagenModel` keep their names.
36+
37+
Imports straight from the Firebase SDK (`firebase/vertexai`, gone in SDK 12) are rewritten to `firebase/ai` under the same rules. The rewrite parses your sources with the `typescript` package (an optional peer dependency of `@angular/fire`). Every Angular workspace already has it, but if the migration warns that it could not be resolved, install `typescript` and re-run. See [ai.md](./ai.md) for current usage.
3138

3239
## Other notes
3340

src/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,13 @@
3232
"@angular/platform-browser": "^21.0.0",
3333
"@angular/platform-server": "^21.0.0",
3434
"rxjs": "~7.8.0",
35-
"firebase-tools": "^14.0.0 || ^15.0.0"
35+
"firebase-tools": "^14.0.0 || ^15.0.0",
36+
"typescript": ">=5.8 <6.0"
3637
},
3738
"peerDependenciesMeta": {
3839
"firebase-tools": { "optional": true },
39-
"@angular/platform-server": { "optional": true }
40+
"@angular/platform-server": { "optional": true },
41+
"typescript": { "optional": true }
4042
},
4143
"dependencies": {
4244
"firebase": "^12.4.0",

src/schematics/migration.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"migration-v21": {
1010
"version": "21.0.0",
11-
"description": "Align the workspace's firebase dependency with the range @angular/fire 21 requires, so the install cannot contain two copies of the firebase SDK",
11+
"description": "Align the workspace's firebase dependency with the range @angular/fire 21 requires, and rewrite Vertex AI imports to Firebase AI Logic (getVertexAI callers keep the Vertex AI backend)",
1212
"factory": "./update/v21#ngUpdate"
1313
},
1414
"ng-post-upgate": {

src/schematics/update/v21/index.jasmine.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { logging } from '@angular-devkit/core';
22
import { HostTree, SchematicContext } from '@angular-devkit/schematics';
3+
import * as typescript from 'typescript';
34
import { firebaseVersionRange } from '../../common.js';
45
import { ngUpdate } from './index.js';
56
import 'jasmine';
@@ -40,4 +41,45 @@ describe('migration-v21 ngUpdate', () => {
4041
expect(addTask).not.toHaveBeenCalled();
4142
});
4243

44+
it('keeps the firebase alignment when the rewrite throws', () => {
45+
const logger = new logging.Logger('test');
46+
const warn = spyOn(logger, 'warn');
47+
const addTask = jasmine.createSpy('addTask');
48+
const context = { logger, addTask } as unknown as SchematicContext;
49+
const tree = treeWithFirebase('^11.0.0');
50+
tree.create('angular.json', JSON.stringify({
51+
projects: { app: { root: '', sourceRoot: 'src' } },
52+
}));
53+
tree.create('src/app/foo.ts', `import { getVertexAI } from '@angular/fire/vertexai';`);
54+
const throwingCompiler = {
55+
ScriptTarget: typescript.ScriptTarget,
56+
createSourceFile: () => { throw new Error('boom'); },
57+
} as unknown as typeof typescript;
58+
59+
ngUpdate({ compiler: throwingCompiler })(tree, context);
60+
61+
// The rewrite failure costs only the rewrite, never the firebase alignment.
62+
const written = JSON.parse(tree.readText('package.json'));
63+
expect(written.dependencies.firebase).toBe(firebaseVersionRange);
64+
expect(addTask).toHaveBeenCalledTimes(1);
65+
expect(warn.calls.allArgs().map(callArgs => String(callArgs[0])).join('\n'))
66+
.toContain('Skipped the Vertex AI -> AI Logic source rewrite');
67+
});
68+
69+
it('runs the Vertex AI rewrite and the alignment through one ngUpdate call', () => {
70+
const { context, addTask } = contextWithTaskSpy();
71+
const tree = treeWithFirebase('^11.0.0');
72+
tree.create('angular.json', JSON.stringify({
73+
projects: { app: { root: '', sourceRoot: 'src' } },
74+
}));
75+
tree.create('src/app/foo.ts', `import { getVertexAI } from '@angular/fire/vertexai';`);
76+
77+
ngUpdate({ compiler: typescript })(tree, context);
78+
79+
expect(tree.readText('src/app/foo.ts')).toBe(`import { getAI } from '@angular/fire/ai';`);
80+
const written = JSON.parse(tree.readText('package.json'));
81+
expect(written.dependencies.firebase).toBe(firebaseVersionRange);
82+
expect(addTask).toHaveBeenCalledTimes(1);
83+
});
84+
4385
});

src/schematics/update/v21/index.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
2-
// The explicit index.js subpath keeps this importable from the ESM jasmine run; the bare
2+
// The explicit index.js subpath keeps this importable from the ESM jasmine run. The bare
33
// /tasks directory specifier only resolves under CommonJS.
44
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks/index.js';
5+
import type * as ts from 'typescript';
56
import { alignFirebaseVersion } from '../../common.js';
6-
import { rewriteVertexAIToAI } from './vertexai-to-ai.js';
7+
import { rewriteVertexAIToAI } from './vertexai-to-ai/index.js';
78

89
// ng update re-runs this migration on rc-to-stable transitions (the CLI clamps the migration
910
// range's upper bound to the release version), so it must stay a no-op when nothing changes.
10-
export const ngUpdate = (): Rule => (
11+
export const ngUpdate = (options?: { compiler?: typeof ts }): Rule => (
1112
host: Tree,
1213
context: SchematicContext
1314
) => {
14-
// Rewrite Vertex AI imports to AI Logic (source-only edits, no dependency change).
15-
rewriteVertexAIToAI(host, context);
16-
// Align firebase. This step changes dependencies, so only it schedules an install.
15+
// Align firebase before anything else: it is the one step users cannot do without, so no
16+
// failure below may cost it. This step changes dependencies, so only it schedules an install.
1717
if (alignFirebaseVersion(host, context)) {
1818
context.addTask(new NodePackageInstallTask());
1919
}
20+
// Rewrite Vertex AI imports to AI Logic (source-only edits, no dependency change). Guarded so
21+
// an unexpected rewrite failure costs only the rewrite, never the alignment above.
22+
try {
23+
rewriteVertexAIToAI(host, context, options?.compiler);
24+
} catch (error) {
25+
context.logger.warn(
26+
`Skipped the Vertex AI -> AI Logic source rewrite: ${error}. ` +
27+
'Any remaining @angular/fire/vertexai imports need a manual migration - see the v21 upgrade guide (docs/version-21-upgrade.md).'
28+
);
29+
}
2030
return host;
2131
};

0 commit comments

Comments
 (0)