firebase-contract 0.1.0-rc.9 → 0.1.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/README.md +140 -63
- package/dist/lib/_internal/compiler/compiler.d.ts.map +1 -1
- package/dist/lib/_internal/compiler/compiler.js +50 -21
- package/dist/lib/_internal/generators/api/api-dto-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/api/api-dto-generator.js +1 -8
- package/dist/lib/_internal/generators/api/api-types-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/api/api-types-generator.js +11 -5
- package/dist/lib/_internal/generators/api/task-payload-generator.js +3 -3
- package/dist/lib/_internal/generators/data-connect/graphql-generator.d.ts +0 -7
- package/dist/lib/_internal/generators/data-connect/graphql-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/data-connect/graphql-generator.js +5 -4
- package/dist/lib/_internal/generators/data-connect/operations-generator.d.ts +0 -7
- package/dist/lib/_internal/generators/data-connect/operations-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/data-connect/operations-generator.js +8 -7
- package/dist/lib/_internal/generators/firestore/firestore-projection-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/firestore/firestore-projection-generator.js +7 -22
- package/dist/lib/_internal/generators/firestore/firestore-split-generator.d.ts +1 -1
- package/dist/lib/_internal/generators/firestore/firestore-split-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/firestore/firestore-split-generator.js +69 -47
- package/dist/lib/_internal/generators/generator.d.ts +2 -0
- package/dist/lib/_internal/generators/generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/index.d.ts +2 -5
- package/dist/lib/_internal/generators/index.d.ts.map +1 -1
- package/dist/lib/_internal/generators/index.js +4 -15
- package/dist/lib/_internal/generators/support/jsdoc.d.ts +3 -0
- package/dist/lib/_internal/generators/support/jsdoc.d.ts.map +1 -0
- package/dist/lib/_internal/generators/support/jsdoc.js +9 -0
- package/dist/lib/_internal/generators/support/split-variant.d.ts +9 -0
- package/dist/lib/_internal/generators/support/split-variant.d.ts.map +1 -0
- package/dist/lib/_internal/generators/support/split-variant.js +13 -0
- package/dist/lib/_internal/generators/support/split.d.ts +1 -1
- package/dist/lib/_internal/generators/support/split.d.ts.map +1 -1
- package/dist/lib/_internal/generators/support/split.js +3 -2
- package/dist/lib/_internal/generators/typescript/render.d.ts +15 -0
- package/dist/lib/_internal/generators/typescript/render.d.ts.map +1 -0
- package/dist/lib/_internal/generators/typescript/render.js +92 -0
- package/dist/lib/_internal/generators/typescript/typescript-generator.d.ts +2 -1
- package/dist/lib/_internal/generators/typescript/typescript-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/typescript/typescript-generator.js +12 -78
- package/dist/lib/_internal/generators/typescript/typescript-split-generator.d.ts +1 -1
- package/dist/lib/_internal/generators/typescript/typescript-split-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/typescript/typescript-split-generator.js +4 -84
- package/dist/lib/_internal/generators/zod/render.d.ts +12 -0
- package/dist/lib/_internal/generators/zod/render.d.ts.map +1 -0
- package/dist/lib/_internal/generators/zod/render.js +59 -0
- package/dist/lib/_internal/generators/zod/zod-generator.d.ts +2 -1
- package/dist/lib/_internal/generators/zod/zod-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/zod/zod-generator.js +11 -55
- package/dist/lib/_internal/generators/zod/zod-split-generator.d.ts +1 -1
- package/dist/lib/_internal/generators/zod/zod-split-generator.d.ts.map +1 -1
- package/dist/lib/_internal/generators/zod/zod-split-generator.js +4 -56
- package/dist/lib/_internal/ir/build-ir.d.ts.map +1 -1
- package/dist/lib/_internal/ir/build-ir.js +93 -8
- package/dist/lib/_internal/ir/ir.d.ts +18 -5
- package/dist/lib/_internal/ir/ir.d.ts.map +1 -1
- package/dist/lib/_internal/ir/ir.js +1 -0
- package/dist/lib/_internal/parser/parse.d.ts +5 -3
- package/dist/lib/_internal/parser/parse.d.ts.map +1 -1
- package/dist/lib/_internal/parser/parse.js +103 -14
- package/dist/lib/_internal/parser/raw-document.d.ts +18 -2
- package/dist/lib/_internal/parser/raw-document.d.ts.map +1 -1
- package/dist/lib/_internal/resolver/import-resolver.js +1 -1
- package/dist/lib/_internal/validation/rules.d.ts +39 -0
- package/dist/lib/_internal/validation/rules.d.ts.map +1 -1
- package/dist/lib/_internal/validation/rules.js +133 -1
- package/dist/lib/cli/commands/generate.d.ts.map +1 -1
- package/dist/lib/cli/commands/generate.js +6 -4
- package/dist/lib/cli/commands/index.d.ts.map +1 -1
- package/dist/lib/cli/commands/index.js +2 -0
- package/dist/lib/cli/commands/init.d.ts.map +1 -1
- package/dist/lib/cli/commands/init.js +2 -18
- package/dist/lib/cli/commands/where.d.ts +3 -0
- package/dist/lib/cli/commands/where.d.ts.map +1 -0
- package/dist/lib/cli/commands/where.js +57 -0
- package/dist/lib/cli/config.d.ts +3 -2
- package/dist/lib/cli/config.d.ts.map +1 -1
- package/dist/lib/cli/config.js +3 -0
- package/package.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { error, warning } from '../diagnostics.js';
|
|
2
|
-
import {
|
|
2
|
+
import { constantCase } from '../generators/support/naming.js';
|
|
3
|
+
import { isTable } from '../generators/support/split.js';
|
|
4
|
+
import { findFragment, findModel } from '../ir/ir.js';
|
|
3
5
|
const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
4
6
|
/** Every field type must resolve to a known scalar, enum, or model. */
|
|
5
7
|
export const unresolvedTypes = ir => ir.models.flatMap(model => model.fields
|
|
@@ -267,6 +269,134 @@ export const unions = ir => ir.unions.flatMap(union => {
|
|
|
267
269
|
}
|
|
268
270
|
return diagnostics;
|
|
269
271
|
});
|
|
272
|
+
/**
|
|
273
|
+
* A firestore doc's `extends` must reference a declared fragment. An unknown
|
|
274
|
+
* name would otherwise resolve to no fields and silently drop the shared
|
|
275
|
+
* columns (`extends: [metaa]` → no `_meta_`), the exact silent-coercion class
|
|
276
|
+
* the contract-is-truth rules exist to kill.
|
|
277
|
+
*/
|
|
278
|
+
export const fragmentRefs = ir => ir.firestore.flatMap(doc => doc.extends
|
|
279
|
+
.filter(name => !findFragment(ir, name))
|
|
280
|
+
.map(name => error('UNKNOWN_FRAGMENT', `Firestore doc "${doc.name}" extends unknown fragment "${name}"`, {
|
|
281
|
+
file: doc.sourceFile,
|
|
282
|
+
path: `firestore.${doc.name}.extends`,
|
|
283
|
+
})));
|
|
284
|
+
const RESERVED_OWNER = 'the generated runtime';
|
|
285
|
+
/**
|
|
286
|
+
* Type names must be unique within every output namespace — the export surface
|
|
287
|
+
* where generated identifiers converge (a split layout still merges into one
|
|
288
|
+
* barrel, so physical files are irrelevant). Renames (`gqlName` / `fsName`)
|
|
289
|
+
* deliberately move a type between namespaces, so each namespace is checked
|
|
290
|
+
* independently against its *effective* names:
|
|
291
|
+
*
|
|
292
|
+
* - TypeScript types module: model/enum/union logical names, enum const
|
|
293
|
+
* (`CONSTANT_CASE`) and `…Key` companions, plus the reserved `Json` type
|
|
294
|
+
* - zod schemas module: logical names and their `…Schema` companions
|
|
295
|
+
* - unions module: union names/schemas plus the variant schemas it imports
|
|
296
|
+
* - firestore module: doc names, co-located enums/embedded models under
|
|
297
|
+
* `fsName ?? name`, plus the `FIRESTORE_DATABASES` runtime
|
|
298
|
+
*
|
|
299
|
+
* GraphQL names are checked separately ({@link graphqlNameCollisions}) because
|
|
300
|
+
* that namespace is scoped per data-connect-graphql declaration, not global.
|
|
301
|
+
*
|
|
302
|
+
* Same-kind duplicates are already DUPLICATE_DEFINITION at merge time; this
|
|
303
|
+
* rule closes every cross-kind and derived-identifier hole. Each colliding
|
|
304
|
+
* definition pair is reported once (the first namespace that trips).
|
|
305
|
+
*/
|
|
306
|
+
export const nameCollisions = ir => {
|
|
307
|
+
const diagnostics = [];
|
|
308
|
+
const reportedPairs = new Set();
|
|
309
|
+
const check = (space, claims) => {
|
|
310
|
+
const first = new Map();
|
|
311
|
+
for (const claim of claims) {
|
|
312
|
+
const prev = first.get(claim.identifier);
|
|
313
|
+
if (!prev) {
|
|
314
|
+
first.set(claim.identifier, claim);
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (prev.owner === claim.owner)
|
|
318
|
+
continue;
|
|
319
|
+
const pair = [prev.owner, claim.owner].sort().join(' vs ');
|
|
320
|
+
if (reportedPairs.has(pair))
|
|
321
|
+
continue;
|
|
322
|
+
reportedPairs.add(pair);
|
|
323
|
+
diagnostics.push(error('NAME_COLLISION', `${prev.owner} and ${claim.owner} both emit "${claim.identifier}" in the ${space} output`, {
|
|
324
|
+
file: claim.file ?? prev.file,
|
|
325
|
+
path: claim.path,
|
|
326
|
+
}));
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
const claim = (owner, file, path, identifiers) => identifiers.map(identifier => ({ owner, identifier, file, path }));
|
|
330
|
+
// TypeScript types module: the logical namespace every other output derives from.
|
|
331
|
+
check('TypeScript types', [
|
|
332
|
+
{ owner: RESERVED_OWNER, identifier: 'Json', path: '(generated)' },
|
|
333
|
+
...ir.models.flatMap(m => claim(`model "${m.name}"`, m.sourceFile, `models.${m.name}`, [m.name])),
|
|
334
|
+
...ir.enums.flatMap(e => claim(`enum "${e.name}"`, e.sourceFile, `enums.${e.name}`, [e.name, `${e.name}Key`, constantCase(e.name)])),
|
|
335
|
+
...ir.unions.flatMap(u => claim(`union "${u.name}"`, u.sourceFile, `unions.${u.name}`, [u.name])),
|
|
336
|
+
]);
|
|
337
|
+
// zod schemas module: logical names plus their `…Schema` consts.
|
|
338
|
+
check('zod schemas', [
|
|
339
|
+
...ir.models.flatMap(m => claim(`model "${m.name}"`, m.sourceFile, `models.${m.name}`, [m.name, `${m.name}Schema`])),
|
|
340
|
+
...ir.enums.flatMap(e => claim(`enum "${e.name}"`, e.sourceFile, `enums.${e.name}`, [e.name, `${e.name}Schema`])),
|
|
341
|
+
]);
|
|
342
|
+
// unions module: union declarations plus the variant schemas imported into it.
|
|
343
|
+
const variantOwners = new Map();
|
|
344
|
+
for (const union of ir.unions)
|
|
345
|
+
for (const variant of union.variants)
|
|
346
|
+
variantOwners.set(variant, `model "${variant}"`);
|
|
347
|
+
check('unions', [
|
|
348
|
+
...ir.unions.flatMap(u => claim(`union "${u.name}"`, u.sourceFile, `unions.${u.name}`, [u.name, `${u.name}Schema`])),
|
|
349
|
+
...[...variantOwners.entries()].map(([variant, owner]) => {
|
|
350
|
+
const model = findModel(ir, variant);
|
|
351
|
+
return { owner, identifier: `${variant}Schema`, file: model?.sourceFile, path: `models.${variant}` };
|
|
352
|
+
}),
|
|
353
|
+
]);
|
|
354
|
+
// firestore module: doc schemas + co-located enums/embedded models under fsName-effective names.
|
|
355
|
+
// Tables are imported as `Dc…Schema` aliases, so they don't claim names here.
|
|
356
|
+
check('firestore', [
|
|
357
|
+
...['FIRESTORE_DATABASES', 'FirestoreDatabaseKey', 'FirestoreDatabaseId'].map(identifier => ({ owner: RESERVED_OWNER, identifier, path: '(generated)' })),
|
|
358
|
+
...ir.firestore.flatMap(d => claim(`firestore doc "${d.name}"`, d.sourceFile, `firestore.${d.name}`, [d.name, `${d.name}Schema`])),
|
|
359
|
+
...ir.enums.flatMap(e => {
|
|
360
|
+
const fs = e.fsName ?? e.name;
|
|
361
|
+
return claim(`enum "${e.name}"`, e.sourceFile, `enums.${e.name}`, [fs, `${fs}Key`, constantCase(fs), `${fs}Schema`]);
|
|
362
|
+
}),
|
|
363
|
+
...ir.models
|
|
364
|
+
.filter(m => !isTable(m))
|
|
365
|
+
.flatMap(m => {
|
|
366
|
+
const fs = m.fsName ?? m.name;
|
|
367
|
+
return claim(`model "${m.name}"`, m.sourceFile, `models.${m.name}`, [fs, `${fs}Schema`]);
|
|
368
|
+
}),
|
|
369
|
+
]);
|
|
370
|
+
return diagnostics;
|
|
371
|
+
};
|
|
372
|
+
/**
|
|
373
|
+
* GraphQL name uniqueness under `gqlName ?? name`. Unlike {@link nameCollisions}
|
|
374
|
+
* this is NOT a default rule: a GraphQL schema exists per `data-connect-graphql`
|
|
375
|
+
* declaration, scoped to the declaring yml's import subtree — distinct services
|
|
376
|
+
* may intentionally reuse a gqlName (e.g. a `T2…` logical name mapped back to
|
|
377
|
+
* the plain name inside its own service). The compiler runs this check only
|
|
378
|
+
* when the entry document itself declares a GraphQL-emitting generator, i.e.
|
|
379
|
+
* exactly at the scope the schema is generated from.
|
|
380
|
+
*/
|
|
381
|
+
export const graphqlNameCollisions = ir => {
|
|
382
|
+
const diagnostics = [];
|
|
383
|
+
const first = new Map();
|
|
384
|
+
const claims = [
|
|
385
|
+
...ir.models.map(m => ({ owner: `model "${m.name}"`, name: m.gqlName ?? m.name, file: m.sourceFile, path: `models.${m.name}` })),
|
|
386
|
+
...ir.enums.map(e => ({ owner: `enum "${e.name}"`, name: e.gqlName ?? e.name, file: e.sourceFile, path: `enums.${e.name}` })),
|
|
387
|
+
];
|
|
388
|
+
for (const { owner, name, file, path } of claims) {
|
|
389
|
+
const prev = first.get(name);
|
|
390
|
+
if (!prev) {
|
|
391
|
+
first.set(name, { owner, file, path });
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
if (prev.owner === owner)
|
|
395
|
+
continue;
|
|
396
|
+
diagnostics.push(error('NAME_COLLISION', `${prev.owner} and ${owner} both emit "${name}" in the GraphQL schema output`, { file: file ?? prev.file, path }));
|
|
397
|
+
}
|
|
398
|
+
return diagnostics;
|
|
399
|
+
};
|
|
270
400
|
export const DEFAULT_RULES = [
|
|
271
401
|
unresolvedTypes,
|
|
272
402
|
emptyEnums,
|
|
@@ -279,4 +409,6 @@ export const DEFAULT_RULES = [
|
|
|
279
409
|
apis,
|
|
280
410
|
firestore,
|
|
281
411
|
unions,
|
|
412
|
+
fragmentRefs,
|
|
413
|
+
nameCollisions,
|
|
282
414
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../../src/lib/cli/commands/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA4BnC,eAAO,MAAM,gBAAgB,GAAI,SAAS,OAAO,KAAG,
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../../src/lib/cli/commands/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA4BnC,eAAO,MAAM,gBAAgB,GAAI,SAAS,OAAO,KAAG,IAiFnD,CAAA"}
|
|
@@ -27,14 +27,16 @@ export const registerGenerate = (program) => {
|
|
|
27
27
|
.action((entryArg, flags) => {
|
|
28
28
|
const config = loadConfig();
|
|
29
29
|
const entry = entryArg ?? config.entry ?? 'contract.yml';
|
|
30
|
-
const outDir = flags.outDir ??
|
|
31
|
-
const generators = flags.generators?.split(',').map(name => name.trim()).filter(Boolean)
|
|
30
|
+
const outDir = flags.outDir ?? 'generated';
|
|
31
|
+
const generators = flags.generators?.split(',').map(name => name.trim()).filter(Boolean);
|
|
32
32
|
const header = flags.header === true ? DEFAULT_HEADER : typeof flags.header === 'string' ? toHeaderComment(flags.header) : undefined;
|
|
33
33
|
const registry = createDefaultRegistry();
|
|
34
34
|
const context = header !== undefined ? { header } : undefined;
|
|
35
35
|
// Without -o/-g, run every generator declared in the contract
|
|
36
|
-
// graph — the one-command flow.
|
|
37
|
-
|
|
36
|
+
// graph — the one-command flow. Only explicit flags switch back to
|
|
37
|
+
// single-target mode; the config file cannot (it must never silently
|
|
38
|
+
// disable the contract's own generators: declarations).
|
|
39
|
+
const explicit = flags.outDir !== undefined || flags.generators !== undefined;
|
|
38
40
|
if (!explicit) {
|
|
39
41
|
const all = generateAll(entry, { registry, write: !flags.check, ...(context ? { context } : {}) });
|
|
40
42
|
if (all.targets.length > 0 || !all.ok) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/cli/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/cli/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAQnC,iDAAiD;AACjD,eAAO,MAAM,gBAAgB,GAAI,SAAS,OAAO,KAAG,IAMnD,CAAA"}
|
|
@@ -2,10 +2,12 @@ import { registerGenerate } from './generate.js';
|
|
|
2
2
|
import { registerInit } from './init.js';
|
|
3
3
|
import { registerInspect } from './inspect.js';
|
|
4
4
|
import { registerValidate } from './validate.js';
|
|
5
|
+
import { registerWhere } from './where.js';
|
|
5
6
|
/** Register every CLI command on the program. */
|
|
6
7
|
export const registerCommands = (program) => {
|
|
7
8
|
registerInit(program);
|
|
8
9
|
registerValidate(program);
|
|
9
10
|
registerGenerate(program);
|
|
10
11
|
registerInspect(program);
|
|
12
|
+
registerWhere(program);
|
|
11
13
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/lib/cli/commands/init.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/lib/cli/commands/init.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAgDnC,eAAO,MAAM,YAAY,GAAI,SAAS,OAAO,KAAG,IAgB/C,CAAA"}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { existsSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import { CONFIG_FILENAME } from '../config.js';
|
|
4
|
-
const EXAMPLE_CONTRACT =
|
|
5
|
-
|
|
6
|
-
# Declare generators once (name + output template); entries opt in below.
|
|
4
|
+
const EXAMPLE_CONTRACT = `# Declare generators once (name + output template); entries opt in below.
|
|
7
5
|
generators:
|
|
8
6
|
- { generator: typescript, out: generated }
|
|
9
7
|
- { generator: zod, out: generated }
|
|
@@ -36,21 +34,7 @@ models:
|
|
|
36
34
|
metadata: { type: json, optional: true }
|
|
37
35
|
createdAt: timestamp
|
|
38
36
|
`;
|
|
39
|
-
const EXAMPLE_CONFIG = JSON.stringify({
|
|
40
|
-
entry: 'contract.yml',
|
|
41
|
-
outDir: 'generated',
|
|
42
|
-
generators: [
|
|
43
|
-
'typescript',
|
|
44
|
-
'zod',
|
|
45
|
-
'data-connect-graphql',
|
|
46
|
-
'data-connect-operations',
|
|
47
|
-
'data-connect-adapter',
|
|
48
|
-
'firestore-types',
|
|
49
|
-
'firestore',
|
|
50
|
-
'api-types',
|
|
51
|
-
'api-validation',
|
|
52
|
-
],
|
|
53
|
-
}, null, 2) + '\n';
|
|
37
|
+
const EXAMPLE_CONFIG = JSON.stringify({ entry: 'contract.yml' }, null, 2) + '\n';
|
|
54
38
|
const writeIfAbsent = (path, content, force) => {
|
|
55
39
|
if (existsSync(path) && !force) {
|
|
56
40
|
return false;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"where.d.ts","sourceRoot":"","sources":["../../../../src/lib/cli/commands/where.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA+CnC,eAAO,MAAM,aAAa,GAAI,SAAS,OAAO,KAAG,IA2BhD,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { compile } from '../../_internal/compiler/compiler.js';
|
|
2
|
+
import { hasErrors } from '../../_internal/diagnostics.js';
|
|
3
|
+
import { formatDiagnostics } from '../../_internal/format-diagnostics.js';
|
|
4
|
+
import { loadConfig } from '../config.js';
|
|
5
|
+
const definitionsOf = (ir) => [
|
|
6
|
+
...ir.models.map(m => ({
|
|
7
|
+
kind: 'model',
|
|
8
|
+
name: m.name,
|
|
9
|
+
sourceFile: m.sourceFile,
|
|
10
|
+
yamlPath: `models.${m.name}`,
|
|
11
|
+
renames: [
|
|
12
|
+
...(m.gqlName ? [`gqlName: ${m.gqlName}`] : []),
|
|
13
|
+
...(m.fsName ? [`fsName: ${m.fsName}`] : []),
|
|
14
|
+
...(m.table ? [`table: ${m.table}`] : []),
|
|
15
|
+
],
|
|
16
|
+
})),
|
|
17
|
+
...ir.enums.map(e => ({
|
|
18
|
+
kind: 'enum',
|
|
19
|
+
name: e.name,
|
|
20
|
+
sourceFile: e.sourceFile,
|
|
21
|
+
yamlPath: `enums.${e.name}`,
|
|
22
|
+
renames: [...(e.gqlName ? [`gqlName: ${e.gqlName}`] : []), ...(e.fsName ? [`fsName: ${e.fsName}`] : [])],
|
|
23
|
+
})),
|
|
24
|
+
...ir.unions.map(u => ({ kind: 'union', name: u.name, sourceFile: u.sourceFile, yamlPath: `unions.${u.name}`, renames: [] })),
|
|
25
|
+
...ir.firestore.map(d => ({ kind: 'firestore doc', name: d.name, sourceFile: d.sourceFile, yamlPath: `firestore.${d.name}`, renames: [] })),
|
|
26
|
+
...ir.envelopes.map(e => ({ kind: 'envelope', name: e.name, sourceFile: e.sourceFile, yamlPath: `envelopes.${e.name}`, renames: [] })),
|
|
27
|
+
...ir.apis.map(a => ({ kind: 'api', name: a.name, sourceFile: a.sourceFile, yamlPath: `apis.${a.name}`, renames: [] })),
|
|
28
|
+
...ir.operations.map(o => ({ kind: 'operation', name: o.name, sourceFile: o.sourceFile, yamlPath: `operations.${o.name}`, renames: [] })),
|
|
29
|
+
];
|
|
30
|
+
/** True when the definition is known by `query` under any of its names. */
|
|
31
|
+
const matches = (definition, query) => definition.name === query || definition.renames.some(rename => rename.endsWith(`: ${query}`));
|
|
32
|
+
export const registerWhere = (program) => {
|
|
33
|
+
program
|
|
34
|
+
.command('where')
|
|
35
|
+
.description('Locate the yml that defines a type (matches logical, gqlName, fsName, and table names)')
|
|
36
|
+
.argument('<name>', 'type name to look up')
|
|
37
|
+
.argument('[entry]', 'entry contract file')
|
|
38
|
+
.action((name, entryArg) => {
|
|
39
|
+
const config = loadConfig();
|
|
40
|
+
const entry = entryArg ?? config.entry ?? 'contract.yml';
|
|
41
|
+
const { ir, diagnostics } = compile(entry);
|
|
42
|
+
if (hasErrors(diagnostics)) {
|
|
43
|
+
console.error(formatDiagnostics(diagnostics));
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
const found = definitionsOf(ir).filter(definition => matches(definition, name));
|
|
47
|
+
if (found.length === 0) {
|
|
48
|
+
console.error(`"${name}" is not defined in ${entry} or its imports`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
for (const definition of found) {
|
|
52
|
+
const location = definition.sourceFile ?? '(unknown file)';
|
|
53
|
+
const renames = definition.renames.length > 0 ? ` [${definition.renames.join(', ')}]` : '';
|
|
54
|
+
console.log(`${definition.kind} ${definition.name} ${location} (${definition.yamlPath})${renames}`);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
package/dist/lib/cli/config.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export declare const CONFIG_FILENAME = "firebase-contract.json";
|
|
2
2
|
export interface FbcConfig {
|
|
3
3
|
entry?: string;
|
|
4
|
-
outDir?: string;
|
|
5
|
-
generators?: string[];
|
|
6
4
|
}
|
|
7
5
|
/**
|
|
8
6
|
* Load `firebase-contract.json` from `cwd` if present. CLI flags override these
|
|
9
7
|
* values; the config only supplies defaults so common invocations stay short.
|
|
8
|
+
* Deliberately only `entry`: a config-side outDir/generators used to flip
|
|
9
|
+
* `fbc generate` into single-target mode, silently ignoring every `generators:`
|
|
10
|
+
* declaration in the contract graph — output routing belongs in the yml.
|
|
10
11
|
*/
|
|
11
12
|
export declare const loadConfig: (cwd?: string) => FbcConfig;
|
|
12
13
|
//# sourceMappingURL=config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/config.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,2BAA2B,CAAA;AAEvD,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/config.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,2BAA2B,CAAA;AAEvD,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,MAAK,MAAsB,KAAG,SAWxD,CAAA"}
|
package/dist/lib/cli/config.js
CHANGED
|
@@ -4,6 +4,9 @@ export const CONFIG_FILENAME = 'firebase-contract.json';
|
|
|
4
4
|
/**
|
|
5
5
|
* Load `firebase-contract.json` from `cwd` if present. CLI flags override these
|
|
6
6
|
* values; the config only supplies defaults so common invocations stay short.
|
|
7
|
+
* Deliberately only `entry`: a config-side outDir/generators used to flip
|
|
8
|
+
* `fbc generate` into single-target mode, silently ignoring every `generators:`
|
|
9
|
+
* declaration in the contract graph — output routing belongs in the yml.
|
|
7
10
|
*/
|
|
8
11
|
export const loadConfig = (cwd = process.cwd()) => {
|
|
9
12
|
const path = resolve(cwd, CONFIG_FILENAME);
|