@dereekb/dbx-cli 13.11.5 → 13.11.6
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/firebase-api-manifest/main.js +53 -13
- package/firebase-api-manifest/package.json +1 -1
- package/index.cjs.js +21 -11
- package/index.esm.js +21 -11
- package/manifest-extract/index.cjs.js +62 -3
- package/manifest-extract/index.esm.js +62 -3
- package/manifest-extract/package.json +1 -1
- package/package.json +5 -5
- package/src/lib/manifest/types.d.ts +5 -1
|
@@ -386,6 +386,7 @@ function readJsDocSummary(node) {
|
|
|
386
386
|
|
|
387
387
|
// packages/dbx-cli/manifest-extract/src/lib/extract-models.ts
|
|
388
388
|
import { Node as Node3, Project as Project3 } from "ts-morph";
|
|
389
|
+
var PASSTHROUGH_TYPE_WRAPPERS = /* @__PURE__ */ new Set(["Partial", "Required", "Readonly", "NonNullable", "MaybeMap", "Pick", "Omit"]);
|
|
389
390
|
var IDENTITY_FN = "firestoreModelIdentity";
|
|
390
391
|
var CONVERTER_FN_NAMES = ["snapshotConverterFunctions", "firestoreSubObject", "firestoreObjectArray"];
|
|
391
392
|
var SUB_OBJECT_FN = "firestoreSubObject";
|
|
@@ -456,7 +457,7 @@ function readInterfaces(sourceFile) {
|
|
|
456
457
|
function buildInterface(decl) {
|
|
457
458
|
const jsDocs = decl.getJsDocs();
|
|
458
459
|
const hasDbxModelTag = jsDocsHaveTag(jsDocs, "dbxModel");
|
|
459
|
-
const extendsNames = decl.getExtends().map(
|
|
460
|
+
const extendsNames = decl.getExtends().map(resolveExtendsName);
|
|
460
461
|
const props = [];
|
|
461
462
|
for (const prop of decl.getProperties()) {
|
|
462
463
|
const propJsDocs = prop.getJsDocs();
|
|
@@ -481,6 +482,39 @@ function buildInterface(decl) {
|
|
|
481
482
|
props
|
|
482
483
|
};
|
|
483
484
|
}
|
|
485
|
+
function resolveExtendsName(expr) {
|
|
486
|
+
const head = expr.getExpression().getText();
|
|
487
|
+
let result = head;
|
|
488
|
+
if (PASSTHROUGH_TYPE_WRAPPERS.has(head)) {
|
|
489
|
+
const typeArgs = expr.getTypeArguments();
|
|
490
|
+
if (typeArgs.length > 0) {
|
|
491
|
+
const peeled = peelTypeNode(typeArgs[0]);
|
|
492
|
+
if (peeled !== void 0) {
|
|
493
|
+
result = peeled;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return result;
|
|
498
|
+
}
|
|
499
|
+
function peelTypeNode(node) {
|
|
500
|
+
let current = node;
|
|
501
|
+
while (Node3.isParenthesizedTypeNode(current)) {
|
|
502
|
+
current = current.getTypeNode();
|
|
503
|
+
}
|
|
504
|
+
let result;
|
|
505
|
+
if (Node3.isTypeReference(current)) {
|
|
506
|
+
const name = current.getTypeName().getText();
|
|
507
|
+
if (PASSTHROUGH_TYPE_WRAPPERS.has(name)) {
|
|
508
|
+
const inner = current.getTypeArguments();
|
|
509
|
+
if (inner.length > 0) {
|
|
510
|
+
result = peelTypeNode(inner[0]);
|
|
511
|
+
}
|
|
512
|
+
} else {
|
|
513
|
+
result = name;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return result;
|
|
517
|
+
}
|
|
484
518
|
function readConverters(sourceFile) {
|
|
485
519
|
const out = [];
|
|
486
520
|
for (const statement of sourceFile.getVariableStatements()) {
|
|
@@ -1088,7 +1122,7 @@ function escapeRegExp(value) {
|
|
|
1088
1122
|
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/emit.ts
|
|
1089
1123
|
import { format, resolveConfig } from "prettier";
|
|
1090
1124
|
async function renderManifest(input) {
|
|
1091
|
-
const { outputFile, entries, projectName, namespace, modelEntries, modelNamespace } = input;
|
|
1125
|
+
const { outputFile, entries, projectName, namespace, modelEntries, modelNamespace, emitConverters = false } = input;
|
|
1092
1126
|
const importsByPackage = /* @__PURE__ */ new Map();
|
|
1093
1127
|
for (const entry of entries) {
|
|
1094
1128
|
if (!entry.packageName || !entry.validatorName) continue;
|
|
@@ -1106,7 +1140,7 @@ async function renderManifest(input) {
|
|
|
1106
1140
|
const modelSection = emitModels ? `
|
|
1107
1141
|
|
|
1108
1142
|
export const ${modelNamespace}: CliModelManifest = [
|
|
1109
|
-
${(modelEntries ?? []).map((m) => renderModelEntry(m)).join(",\n")}
|
|
1143
|
+
${(modelEntries ?? []).map((m) => renderModelEntry(m, emitConverters)).join(",\n")}
|
|
1110
1144
|
];
|
|
1111
1145
|
` : "";
|
|
1112
1146
|
const source = `/* eslint-disable @nx/enforce-module-boundaries */
|
|
@@ -1152,7 +1186,7 @@ async function formatWithPrettier(source, outputFile) {
|
|
|
1152
1186
|
const config = await resolveConfig(outputFile);
|
|
1153
1187
|
return format(source, { ...config, filepath: outputFile });
|
|
1154
1188
|
}
|
|
1155
|
-
function renderModelEntry(entry) {
|
|
1189
|
+
function renderModelEntry(entry, emitConverters) {
|
|
1156
1190
|
const fields = [
|
|
1157
1191
|
`modelType: ${JSON.stringify(entry.modelType)}`,
|
|
1158
1192
|
`modelName: ${JSON.stringify(entry.modelName)}`,
|
|
@@ -1163,26 +1197,26 @@ function renderModelEntry(entry) {
|
|
|
1163
1197
|
entry.description ? `description: ${JSON.stringify(entry.description)}` : void 0,
|
|
1164
1198
|
`sourcePackage: ${JSON.stringify(entry.sourcePackage)}`,
|
|
1165
1199
|
`sourceFile: ${JSON.stringify(entry.sourceFile)}`,
|
|
1166
|
-
`fields: ${renderModelFields(entry.fields)}`
|
|
1200
|
+
`fields: ${renderModelFields(entry.fields, emitConverters)}`
|
|
1167
1201
|
];
|
|
1168
1202
|
return ` { ${fields.filter((v) => Boolean(v)).join(", ")} }`;
|
|
1169
1203
|
}
|
|
1170
|
-
function renderModelFields(fields) {
|
|
1204
|
+
function renderModelFields(fields, emitConverters) {
|
|
1171
1205
|
if (fields.length === 0) return "[]";
|
|
1172
|
-
const items = fields.map((field) => renderModelField(field));
|
|
1206
|
+
const items = fields.map((field) => renderModelField(field, emitConverters));
|
|
1173
1207
|
return `[${items.join(", ")}]`;
|
|
1174
1208
|
}
|
|
1175
|
-
function renderModelField(field) {
|
|
1209
|
+
function renderModelField(field, emitConverters) {
|
|
1176
1210
|
const parts = [
|
|
1177
1211
|
`name: ${JSON.stringify(field.name)}`,
|
|
1178
1212
|
`longName: ${JSON.stringify(field.longName)}`,
|
|
1179
|
-
`converter: ${JSON.stringify(field.converter)}
|
|
1213
|
+
emitConverters && field.converter !== void 0 ? `converter: ${JSON.stringify(field.converter)}` : void 0,
|
|
1180
1214
|
field.tsType ? `tsType: ${JSON.stringify(field.tsType)}` : void 0,
|
|
1181
1215
|
`optional: ${field.optional ? "true" : "false"}`,
|
|
1182
1216
|
field.description ? `description: ${JSON.stringify(field.description)}` : void 0,
|
|
1183
1217
|
field.enumRef ? `enumRef: ${JSON.stringify(field.enumRef)}` : void 0,
|
|
1184
1218
|
field.syncFlag ? `syncFlag: ${JSON.stringify(field.syncFlag)}` : void 0,
|
|
1185
|
-
field.nestedFields ? `nestedFields: ${renderModelFields(field.nestedFields)}` : void 0,
|
|
1219
|
+
field.nestedFields ? `nestedFields: ${renderModelFields(field.nestedFields, emitConverters)}` : void 0,
|
|
1186
1220
|
field.nestedFields ? `nestedIsArray: ${field.nestedIsArray ? "true" : "false"}` : void 0
|
|
1187
1221
|
];
|
|
1188
1222
|
return `{ ${parts.filter((v) => Boolean(v)).join(", ")} }`;
|
|
@@ -1271,7 +1305,7 @@ async function main() {
|
|
|
1271
1305
|
const modelEntries = flags.emitModels ? assembleModels({ extractions: modelSources }) : [];
|
|
1272
1306
|
const filteredModelEntries = flags.only ? modelEntries.filter((m) => flags.only?.has(m.modelType)) : modelEntries;
|
|
1273
1307
|
ensureOutputDir(outputDir);
|
|
1274
|
-
const formatted = await renderManifest({ outputFile, entries: collected, projectName, namespace, modelEntries: filteredModelEntries, modelNamespace: deriveModelNamespace(flags.project) });
|
|
1308
|
+
const formatted = await renderManifest({ outputFile, entries: collected, projectName, namespace, modelEntries: filteredModelEntries, modelNamespace: deriveModelNamespace(flags.project), emitConverters: flags.emitModelConverters });
|
|
1275
1309
|
if (existsSync3(outputFile) && readFileSync6(outputFile, "utf8") === formatted) {
|
|
1276
1310
|
console.log(`[unchanged] ${relative2(WORKSPACE_ROOT, outputFile)}`);
|
|
1277
1311
|
} else {
|
|
@@ -1312,11 +1346,14 @@ function parseFlags(argv) {
|
|
|
1312
1346
|
let output;
|
|
1313
1347
|
let project;
|
|
1314
1348
|
let emitModels = false;
|
|
1349
|
+
let emitModelConverters = false;
|
|
1315
1350
|
for (const arg of argv) {
|
|
1316
1351
|
if (arg === "--strict") {
|
|
1317
1352
|
strict = true;
|
|
1318
1353
|
} else if (arg === "--emit-models") {
|
|
1319
1354
|
emitModels = true;
|
|
1355
|
+
} else if (arg === "--emit-model-converters") {
|
|
1356
|
+
emitModelConverters = true;
|
|
1320
1357
|
} else if (arg.startsWith("--only=")) {
|
|
1321
1358
|
const list = arg.slice("--only=".length).split(",").map((s) => s.trim()).filter(Boolean);
|
|
1322
1359
|
if (list.length > 0) only = new Set(list);
|
|
@@ -1328,7 +1365,7 @@ function parseFlags(argv) {
|
|
|
1328
1365
|
project = arg.slice("--project=".length);
|
|
1329
1366
|
}
|
|
1330
1367
|
}
|
|
1331
|
-
return { only, strict, functionsConfig, output, project, emitModels };
|
|
1368
|
+
return { only, strict, functionsConfig, output, project, emitModels, emitModelConverters };
|
|
1332
1369
|
}
|
|
1333
1370
|
function printUsageAndExit() {
|
|
1334
1371
|
console.error(String.raw`generate-api-manifest
|
|
@@ -1350,7 +1387,10 @@ Optional:
|
|
|
1350
1387
|
--strict Fail when any validator binding is missing.
|
|
1351
1388
|
--emit-models Opt in to emitting <NAMESPACE>_MODEL_MANIFEST. Off by default —
|
|
1352
1389
|
pair with the runtime \`modelManifest\` option on \`runCli\` to
|
|
1353
|
-
enable the built-in \`model-info\` command
|
|
1390
|
+
enable the built-in \`model-info\` command.
|
|
1391
|
+
--emit-model-converters Opt in to including each field's converter expression text inside
|
|
1392
|
+
the model manifest. Off by default — useful for downstream tooling
|
|
1393
|
+
(e.g. dbx-components MCP) but unused by the CLI.`);
|
|
1354
1394
|
process.exit(1);
|
|
1355
1395
|
}
|
|
1356
1396
|
try {
|
package/index.cjs.js
CHANGED
|
@@ -5616,27 +5616,37 @@ function _unsupported_iterable_to_array$2(o, minLen) {
|
|
|
5616
5616
|
}
|
|
5617
5617
|
function renderFieldsTree(fields, indent) {
|
|
5618
5618
|
var out = [];
|
|
5619
|
+
var includeConverter = fields.some(function(f) {
|
|
5620
|
+
return f.converter !== undefined;
|
|
5621
|
+
});
|
|
5622
|
+
var header = includeConverter ? [
|
|
5623
|
+
'NAME',
|
|
5624
|
+
'LONG NAME',
|
|
5625
|
+
'TYPE',
|
|
5626
|
+
'OPTIONAL',
|
|
5627
|
+
'CONVERTER'
|
|
5628
|
+
] : [
|
|
5629
|
+
'NAME',
|
|
5630
|
+
'LONG NAME',
|
|
5631
|
+
'TYPE',
|
|
5632
|
+
'OPTIONAL'
|
|
5633
|
+
];
|
|
5619
5634
|
var rows = [
|
|
5620
|
-
|
|
5621
|
-
'NAME',
|
|
5622
|
-
'LONG NAME',
|
|
5623
|
-
'TYPE',
|
|
5624
|
-
'OPTIONAL',
|
|
5625
|
-
'CONVERTER'
|
|
5626
|
-
]
|
|
5635
|
+
header
|
|
5627
5636
|
];
|
|
5628
5637
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
5629
5638
|
try {
|
|
5630
5639
|
for(var _iterator = fields[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
5631
5640
|
var field = _step.value;
|
|
5632
5641
|
var _field_tsType;
|
|
5633
|
-
|
|
5642
|
+
var row = [
|
|
5634
5643
|
field.name,
|
|
5635
5644
|
field.longName,
|
|
5636
5645
|
(_field_tsType = field.tsType) !== null && _field_tsType !== void 0 ? _field_tsType : '',
|
|
5637
|
-
field.optional ? 'yes' : 'no'
|
|
5638
|
-
|
|
5639
|
-
|
|
5646
|
+
field.optional ? 'yes' : 'no'
|
|
5647
|
+
];
|
|
5648
|
+
if (includeConverter) row.push(field.converter ? truncate(field.converter, 60) : '');
|
|
5649
|
+
rows.push(row);
|
|
5640
5650
|
}
|
|
5641
5651
|
} catch (err) {
|
|
5642
5652
|
_didIteratorError = true;
|
package/index.esm.js
CHANGED
|
@@ -5614,27 +5614,37 @@ function _unsupported_iterable_to_array$2(o, minLen) {
|
|
|
5614
5614
|
}
|
|
5615
5615
|
function renderFieldsTree(fields, indent) {
|
|
5616
5616
|
var out = [];
|
|
5617
|
+
var includeConverter = fields.some(function(f) {
|
|
5618
|
+
return f.converter !== undefined;
|
|
5619
|
+
});
|
|
5620
|
+
var header = includeConverter ? [
|
|
5621
|
+
'NAME',
|
|
5622
|
+
'LONG NAME',
|
|
5623
|
+
'TYPE',
|
|
5624
|
+
'OPTIONAL',
|
|
5625
|
+
'CONVERTER'
|
|
5626
|
+
] : [
|
|
5627
|
+
'NAME',
|
|
5628
|
+
'LONG NAME',
|
|
5629
|
+
'TYPE',
|
|
5630
|
+
'OPTIONAL'
|
|
5631
|
+
];
|
|
5617
5632
|
var rows = [
|
|
5618
|
-
|
|
5619
|
-
'NAME',
|
|
5620
|
-
'LONG NAME',
|
|
5621
|
-
'TYPE',
|
|
5622
|
-
'OPTIONAL',
|
|
5623
|
-
'CONVERTER'
|
|
5624
|
-
]
|
|
5633
|
+
header
|
|
5625
5634
|
];
|
|
5626
5635
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
5627
5636
|
try {
|
|
5628
5637
|
for(var _iterator = fields[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
5629
5638
|
var field = _step.value;
|
|
5630
5639
|
var _field_tsType;
|
|
5631
|
-
|
|
5640
|
+
var row = [
|
|
5632
5641
|
field.name,
|
|
5633
5642
|
field.longName,
|
|
5634
5643
|
(_field_tsType = field.tsType) !== null && _field_tsType !== void 0 ? _field_tsType : '',
|
|
5635
|
-
field.optional ? 'yes' : 'no'
|
|
5636
|
-
|
|
5637
|
-
|
|
5644
|
+
field.optional ? 'yes' : 'no'
|
|
5645
|
+
];
|
|
5646
|
+
if (includeConverter) row.push(field.converter ? truncate(field.converter, 60) : '');
|
|
5647
|
+
rows.push(row);
|
|
5638
5648
|
}
|
|
5639
5649
|
} catch (err) {
|
|
5640
5650
|
_didIteratorError = true;
|
|
@@ -484,6 +484,25 @@ function _object_spread(target) {
|
|
|
484
484
|
}
|
|
485
485
|
return target;
|
|
486
486
|
}
|
|
487
|
+
/**
|
|
488
|
+
* TS utility/structural wrappers that don't change the field surface for
|
|
489
|
+
* inheritance walks — `Partial<T>`, `Required<T>`, `Readonly<T>`,
|
|
490
|
+
* `NonNullable<T>` preserve every property, and `Pick<T, K>` / `Omit<T, K>`
|
|
491
|
+
* leave the original `T` reachable for long-name resolution. `MaybeMap<T>` is
|
|
492
|
+
* the workspace's own pass-through that decorates each prop with `Maybe<…>`
|
|
493
|
+
* without renaming. `extends` walks need to see through these to find the
|
|
494
|
+
* concrete ancestor interface — `getExpression()` alone returns just the
|
|
495
|
+
* leftmost identifier (`Partial`, `Pick`, …) and silently drops the inner
|
|
496
|
+
* model, leaving every inherited `@dbxModelVariable` tag unreachable.
|
|
497
|
+
*/ var PASSTHROUGH_TYPE_WRAPPERS = new Set([
|
|
498
|
+
'Partial',
|
|
499
|
+
'Required',
|
|
500
|
+
'Readonly',
|
|
501
|
+
'NonNullable',
|
|
502
|
+
'MaybeMap',
|
|
503
|
+
'Pick',
|
|
504
|
+
'Omit'
|
|
505
|
+
]);
|
|
487
506
|
var IDENTITY_FN = 'firestoreModelIdentity';
|
|
488
507
|
var CONVERTER_FN_NAMES = [
|
|
489
508
|
'snapshotConverterFunctions',
|
|
@@ -647,9 +666,7 @@ function readInterfaces(sourceFile) {
|
|
|
647
666
|
function buildInterface(decl) {
|
|
648
667
|
var jsDocs = decl.getJsDocs();
|
|
649
668
|
var hasDbxModelTag = jsDocsHaveTag(jsDocs, 'dbxModel');
|
|
650
|
-
var extendsNames = decl.getExtends().map(
|
|
651
|
-
return e.getExpression().getText();
|
|
652
|
-
});
|
|
669
|
+
var extendsNames = decl.getExtends().map(resolveExtendsName);
|
|
653
670
|
var props = [];
|
|
654
671
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
655
672
|
try {
|
|
@@ -693,6 +710,48 @@ function buildInterface(decl) {
|
|
|
693
710
|
props: props
|
|
694
711
|
};
|
|
695
712
|
}
|
|
713
|
+
/**
|
|
714
|
+
* Resolves an `extends` clause to the concrete ancestor interface name,
|
|
715
|
+
* peeling any leading {@link PASSTHROUGH_TYPE_WRAPPERS}. Returns the leftmost
|
|
716
|
+
* identifier of the unwrapped expression so the inheritance walker can chain
|
|
717
|
+
* through utility-wrapped declarations like
|
|
718
|
+
* `extends Partial<MaybeMap<Omit<Base, '…'>>>`.
|
|
719
|
+
*
|
|
720
|
+
* @param expr - the `ExpressionWithTypeArguments` produced by `getExtends()`
|
|
721
|
+
* @returns the resolved interface name, or the original leftmost identifier when no inner reference is reachable
|
|
722
|
+
*/ function resolveExtendsName(expr) {
|
|
723
|
+
var head = expr.getExpression().getText();
|
|
724
|
+
var result = head;
|
|
725
|
+
if (PASSTHROUGH_TYPE_WRAPPERS.has(head)) {
|
|
726
|
+
var typeArgs = expr.getTypeArguments();
|
|
727
|
+
if (typeArgs.length > 0) {
|
|
728
|
+
var peeled = peelTypeNode(typeArgs[0]);
|
|
729
|
+
if (peeled !== undefined) {
|
|
730
|
+
result = peeled;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return result;
|
|
735
|
+
}
|
|
736
|
+
function peelTypeNode(node) {
|
|
737
|
+
var current = node;
|
|
738
|
+
while(tsMorph.Node.isParenthesizedTypeNode(current)){
|
|
739
|
+
current = current.getTypeNode();
|
|
740
|
+
}
|
|
741
|
+
var result;
|
|
742
|
+
if (tsMorph.Node.isTypeReference(current)) {
|
|
743
|
+
var name = current.getTypeName().getText();
|
|
744
|
+
if (PASSTHROUGH_TYPE_WRAPPERS.has(name)) {
|
|
745
|
+
var inner = current.getTypeArguments();
|
|
746
|
+
if (inner.length > 0) {
|
|
747
|
+
result = peelTypeNode(inner[0]);
|
|
748
|
+
}
|
|
749
|
+
} else {
|
|
750
|
+
result = name;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return result;
|
|
754
|
+
}
|
|
696
755
|
function readConverters(sourceFile) {
|
|
697
756
|
var out = [];
|
|
698
757
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
@@ -482,6 +482,25 @@ function _object_spread(target) {
|
|
|
482
482
|
}
|
|
483
483
|
return target;
|
|
484
484
|
}
|
|
485
|
+
/**
|
|
486
|
+
* TS utility/structural wrappers that don't change the field surface for
|
|
487
|
+
* inheritance walks — `Partial<T>`, `Required<T>`, `Readonly<T>`,
|
|
488
|
+
* `NonNullable<T>` preserve every property, and `Pick<T, K>` / `Omit<T, K>`
|
|
489
|
+
* leave the original `T` reachable for long-name resolution. `MaybeMap<T>` is
|
|
490
|
+
* the workspace's own pass-through that decorates each prop with `Maybe<…>`
|
|
491
|
+
* without renaming. `extends` walks need to see through these to find the
|
|
492
|
+
* concrete ancestor interface — `getExpression()` alone returns just the
|
|
493
|
+
* leftmost identifier (`Partial`, `Pick`, …) and silently drops the inner
|
|
494
|
+
* model, leaving every inherited `@dbxModelVariable` tag unreachable.
|
|
495
|
+
*/ var PASSTHROUGH_TYPE_WRAPPERS = new Set([
|
|
496
|
+
'Partial',
|
|
497
|
+
'Required',
|
|
498
|
+
'Readonly',
|
|
499
|
+
'NonNullable',
|
|
500
|
+
'MaybeMap',
|
|
501
|
+
'Pick',
|
|
502
|
+
'Omit'
|
|
503
|
+
]);
|
|
485
504
|
var IDENTITY_FN = 'firestoreModelIdentity';
|
|
486
505
|
var CONVERTER_FN_NAMES = [
|
|
487
506
|
'snapshotConverterFunctions',
|
|
@@ -645,9 +664,7 @@ function readInterfaces(sourceFile) {
|
|
|
645
664
|
function buildInterface(decl) {
|
|
646
665
|
var jsDocs = decl.getJsDocs();
|
|
647
666
|
var hasDbxModelTag = jsDocsHaveTag(jsDocs, 'dbxModel');
|
|
648
|
-
var extendsNames = decl.getExtends().map(
|
|
649
|
-
return e.getExpression().getText();
|
|
650
|
-
});
|
|
667
|
+
var extendsNames = decl.getExtends().map(resolveExtendsName);
|
|
651
668
|
var props = [];
|
|
652
669
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
653
670
|
try {
|
|
@@ -691,6 +708,48 @@ function buildInterface(decl) {
|
|
|
691
708
|
props: props
|
|
692
709
|
};
|
|
693
710
|
}
|
|
711
|
+
/**
|
|
712
|
+
* Resolves an `extends` clause to the concrete ancestor interface name,
|
|
713
|
+
* peeling any leading {@link PASSTHROUGH_TYPE_WRAPPERS}. Returns the leftmost
|
|
714
|
+
* identifier of the unwrapped expression so the inheritance walker can chain
|
|
715
|
+
* through utility-wrapped declarations like
|
|
716
|
+
* `extends Partial<MaybeMap<Omit<Base, '…'>>>`.
|
|
717
|
+
*
|
|
718
|
+
* @param expr - the `ExpressionWithTypeArguments` produced by `getExtends()`
|
|
719
|
+
* @returns the resolved interface name, or the original leftmost identifier when no inner reference is reachable
|
|
720
|
+
*/ function resolveExtendsName(expr) {
|
|
721
|
+
var head = expr.getExpression().getText();
|
|
722
|
+
var result = head;
|
|
723
|
+
if (PASSTHROUGH_TYPE_WRAPPERS.has(head)) {
|
|
724
|
+
var typeArgs = expr.getTypeArguments();
|
|
725
|
+
if (typeArgs.length > 0) {
|
|
726
|
+
var peeled = peelTypeNode(typeArgs[0]);
|
|
727
|
+
if (peeled !== undefined) {
|
|
728
|
+
result = peeled;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
return result;
|
|
733
|
+
}
|
|
734
|
+
function peelTypeNode(node) {
|
|
735
|
+
var current = node;
|
|
736
|
+
while(Node.isParenthesizedTypeNode(current)){
|
|
737
|
+
current = current.getTypeNode();
|
|
738
|
+
}
|
|
739
|
+
var result;
|
|
740
|
+
if (Node.isTypeReference(current)) {
|
|
741
|
+
var name = current.getTypeName().getText();
|
|
742
|
+
if (PASSTHROUGH_TYPE_WRAPPERS.has(name)) {
|
|
743
|
+
var inner = current.getTypeArguments();
|
|
744
|
+
if (inner.length > 0) {
|
|
745
|
+
result = peelTypeNode(inner[0]);
|
|
746
|
+
}
|
|
747
|
+
} else {
|
|
748
|
+
result = name;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return result;
|
|
752
|
+
}
|
|
694
753
|
function readConverters(sourceFile) {
|
|
695
754
|
var out = [];
|
|
696
755
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/dbx-cli",
|
|
3
|
-
"version": "13.11.
|
|
3
|
+
"version": "13.11.6",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"bin": {
|
|
6
6
|
"dbx-cli-generate-firebase-api-manifest": "firebase-api-manifest/main.js"
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"@dereekb/date": "13.11.
|
|
28
|
-
"@dereekb/firebase": "13.11.
|
|
29
|
-
"@dereekb/nestjs": "13.11.
|
|
30
|
-
"@dereekb/util": "13.11.
|
|
27
|
+
"@dereekb/date": "13.11.6",
|
|
28
|
+
"@dereekb/firebase": "13.11.6",
|
|
29
|
+
"@dereekb/nestjs": "13.11.6",
|
|
30
|
+
"@dereekb/util": "13.11.6",
|
|
31
31
|
"arktype": "^2.2.0",
|
|
32
32
|
"yargs": "^18.0.0"
|
|
33
33
|
},
|
|
@@ -22,8 +22,12 @@ export interface CliModelField {
|
|
|
22
22
|
/**
|
|
23
23
|
* Verbatim converter expression text from the converter's `fields` literal
|
|
24
24
|
* (e.g. `firestoreDate()`, `firestoreObjectArray({ objectField: foo })`).
|
|
25
|
+
*
|
|
26
|
+
* Opt-in. Only emitted when the manifest is generated with
|
|
27
|
+
* `--emit-model-converters`; downstream tooling (e.g. the dbx-components MCP)
|
|
28
|
+
* uses this text, while the CLI itself does not need it.
|
|
25
29
|
*/
|
|
26
|
-
readonly converter
|
|
30
|
+
readonly converter?: string;
|
|
27
31
|
/**
|
|
28
32
|
* TypeScript type text from the interface property declaration, when the
|
|
29
33
|
* field's interface property could be located (e.g. `Date`, `Maybe<NotificationUserState>`).
|