feat(schematics): generate .firebaserc and Firestore starter files during ng add#3714
Conversation
…ring ng add ng add asks the user to pick a Firebase project but never records the choice, so every later firebase-tools command has no default project. It also leaves firebase.json empty and creates no security rules, so a Firestore-selected workspace cannot deploy rules at all. Now, after the project prompt, the schematic records the selection in .firebaserc (merging into an existing file rather than replacing it). When Firestore is selected it also generates firestore.rules and firestore.indexes.json — the same test-mode starter files that 'firebase init firestore' produces, with a warning that the rules expire in 30 days — and wires the firestore section into firebase.json. Existing rules files are left untouched. The new files go through the schematic Tree; firebase.json stays on the real filesystem because firebase-tools reads and rewrites it during the same run, and its firestore section is added only after those rewrites.
| * whose firebase.json already has a `firestore` section is left entirely untouched: its section | ||
| * may point at differently-named files, and creating the default-named ones would only add | ||
| * orphans nothing references. | ||
| */ |
There was a problem hiding this comment.
Worth adding one sentence here noting that this must be called with the pre-init firebaseJson snapshot (not a post-init re-read). If a future firebaseTools.init call adds a firestore section to firebase.json on disk, addFirestoreToFirebaseJson will correctly skip, but the Tree-staged starter files will have already been created and will be orphaned. The ordering constraint is non-obvious without it.
There was a problem hiding this comment.
Good catch - traced it further and it's a bit worse than an orphan: if a future init call adds a firestore section while this is still reading the pre-init snapshot, it'd stage the default-named starter files right on top of the ones that init call would have written to disk - the same collision .firebaserc hit earlier in this PR, which aborts the whole ng add. So:
- I moved the call to run after the init calls with a fresh re-read, rather than just documenting the risk.
- Also worth noting: it now has to run before
addFirestoreToFirebaseJson, since that's what actually adds thefirestoresection on a normal run — added a comment on that ordering too.
| } | ||
| if (!firebaseJson.firestore) { | ||
| firebaseJson.firestore = { rules: 'firestore.rules', indexes: 'firestore.indexes.json' }; | ||
| writeFileSync(join(projectRoot, 'firebase.json'), stringifyFormatted(firebaseJson)); |
There was a problem hiding this comment.
The try/catch above only covers the readFileSync. If this writeFileSync fails (disk full, permissions), the error propagates as a raw Node error with no helpful message. A try/catch around the write with a context.logger.warn pointing the user to add the section manually would be consistent with how the read failure is handled above.
There was a problem hiding this comment.
Done, and widened it a bit further - the try/catch now covers the read, the firestore property check/assignment, and the write together.
- Found one more gap looking at it: a
firebase.jsonthat parses tonull(valid JSON, not a usable object) was crashing on the property read one line above your comment, before it ever reached the write. - All three failure modes now get the same warn-and-continue instead of a raw error.
… handling Tyler's review on angular#3714: createFirestoreStarterFiles staged the Tree before the DataConnect init calls, using a pre-init firebase.json snapshot. If a future init call ever added a firestore section mid-run, the stale snapshot would miss it and stage starter files on top of files firebase-tools already wrote to disk — the same Tree/disk collision the .firebaserc write hit earlier in this PR. Moved the call to run after init with a fresh re-read, and documented the more immediate invariant this enforces: it must run before addFirestoreToFirebaseJson, which is what adds the firestore section on a normal run. Also widened addFirestoreToFirebaseJson's try/catch to cover the firebase.json mutation and write, not just the read — a write failure (disk full, permissions) previously crashed with a raw Node error instead of the warn-and-continue the read path already gets.
….json read fails The re-read added for the firestore starter files was unguarded, so a firebase.json that firebase-tools left unreadable would abort the whole ng add. Before the starter files moved after the init calls, this same failure degraded gracefully — the files were already staged, and addFirestoreToFirebaseJson warned and let setup finish. Restore that: warn and continue. createFirestoreStarterFiles independently checks the disk for each file it would create, so a missing snapshot costs the firestore-section check, not the collision safety it also relies on. Trimmed the surrounding comments to the two constraints a future edit could silently break — read after init, and run before addFirestoreToFirebaseJson — and moved the rationale here. The reason the read sits after the init calls at all: staging against a stale pre-init snapshot risks the Tree/disk collision the .firebaserc write hit earlier in this PR, where a Tree-staged file collides at commit time with one firebase-tools already wrote to disk and aborts the run. No init call adds a firestore section today, so that specific path is defensive against a future one.
Fixes #3713
ng add @angular/firecurrently leaves the workspace un-deployable: the project selected during setup is recorded nowhere,firebase.jsonstays{}(#3559), and selecting Firestore produces no security rules. This PR makes the setup schematic generate the same starter filesfirebase initwould:.firebaserc— records the selected project asprojects.default, merging into an existing file (other aliases andtargetspreserved). Written to the real filesystem after thefirebaseTools.initcalls, since init writes.firebasercon disk during the same run.firestore.rules— only when Firestore is selected and no rules file exists: the same test-mode starter templatefirebase init firestoreuses (30-day expiry), with a warning logged at generation time. Never overwrites an existing rules file.firestore.indexes.json— an empty manifest, so a plainfirebase deploydoesn't fail on the referenced-but-missing file.firebase.jsongains thefirestoresection pointing at both files, written after the Data Connect initialization (which rewrites firebase.json on disk during the same run).The rules and indexes files go through the schematic Tree (visible in the CREATE log, honored by
--dry-run);.firebasercand the firebase.json section are written to the real filesystem because firebase-tools reads and writes both during the same run.Includes 14 specs (
setup/firebaseConfigs.jasmine.ts); verified end-to-end against a real project (all four artifacts generated correctly;firebase useresolves without prompting).Known limitations, not addressed here: the initial
firebase.json'{}'write at the top of the flow (pre-existing) uses a rawwriteFileSyncand fires even under--dry-run; and because the firebase.jsonfirestoresection reaches disk before the Tree-staged rules/indexes files do, a failure late in the schematic can leave firebase.json referencing files that were never written. Moving firebase.json fully onto the Tree — the deeper fix for both — is a larger refactor.