@zenstackhq/cli 3.6.4 → 3.7.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/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import { ZModelCodeGenerator, ZModelLanguageMetaData, formatDocument, loadDocume
3
3
  import colors from "colors";
4
4
  import { Command, CommanderError, Option } from "commander";
5
5
  import "dotenv/config";
6
- import { DataModel, Enum, isDataSource, isEnum, isInvocationExpr, isLiteralExpr, isPlugin } from "@zenstackhq/language/ast";
6
+ import { DataModel, Enum, isDataField, isDataSource, isEnum, isInvocationExpr, isLiteralExpr, isPlugin } from "@zenstackhq/language/ast";
7
7
  import path from "node:path";
8
8
  import { invariant, lowerCaseFirst, singleDebounce } from "@zenstackhq/common-helpers";
9
9
  import { PrismaSchemaGenerator, TsSchemaGenerator } from "@zenstackhq/sdk";
@@ -18,7 +18,7 @@ import { execSync } from "child_process";
18
18
  import { fileURLToPath as fileURLToPath$1 } from "url";
19
19
  import { DataFieldAttributeFactory, DataFieldFactory, DataModelFactory, EnumFactory } from "@zenstackhq/language/factory";
20
20
  import { AstUtils } from "langium";
21
- import { getLiteral, getLiteralArray, getStringLiteral } from "@zenstackhq/language/utils";
21
+ import { getAttributeArgLiteral, getLiteral, getLiteralArray, getStringLiteral } from "@zenstackhq/language/utils";
22
22
  import { watch } from "chokidar";
23
23
  import semver from "semver";
24
24
  import { detect, resolveCommand } from "package-manager-detector";
@@ -400,6 +400,18 @@ function getRelationFkName(decl) {
400
400
  return ((decl?.attributes.find((a) => a.decl.ref?.name === "@relation"))?.args.find((a) => a.name === "map")?.value)?.value;
401
401
  }
402
402
  /**
403
+ * Gets the relation name from the @relation attribute's `name` argument.
404
+ * e.g., @relation('myRelation', fields: [...], references: [...]) -> "myRelation"
405
+ * e.g., @relation(name: 'myRelation', fields: [...], references: [...]) -> "myRelation"
406
+ * e.g., @relation(fields: [...], references: [...]) -> undefined
407
+ * e.g., @relation('backRef') -> "backRef"
408
+ */
409
+ function getRelationName(decl) {
410
+ const relationAttr = decl?.attributes?.find((a) => a.decl?.ref?.name === "@relation");
411
+ if (!relationAttr) return void 0;
412
+ return getAttributeArgLiteral(relationAttr, "name");
413
+ }
414
+ /**
403
415
  * Gets the FK field names from the @relation attribute's `fields` argument.
404
416
  * Returns a sorted, comma-separated string of field names for comparison.
405
417
  * e.g., @relation(fields: [userId], references: [id]) -> "userId"
@@ -2146,6 +2158,16 @@ const providers = {
2146
2158
  };
2147
2159
  //#endregion
2148
2160
  //#region src/actions/db.ts
2161
+ function hasRelationFieldsArg(field) {
2162
+ return !!field.attributes.find((a) => a.decl.ref?.name === "@relation")?.args.some((a) => a.name === "fields");
2163
+ }
2164
+ function getReferencedModelName(field) {
2165
+ return field.type.reference?.ref ? getDbName(field.type.reference.ref) : void 0;
2166
+ }
2167
+ function matchesRelationNameFallback(field, relationName, candidate) {
2168
+ const referencedModelName = getReferencedModelName(field);
2169
+ return !!referencedModelName && getRelationName(candidate) === relationName && hasRelationFieldsArg(candidate) === hasRelationFieldsArg(field) && getReferencedModelName(candidate) === referencedModelName;
2170
+ }
2149
2171
  /**
2150
2172
  * CLI action for db related commands
2151
2173
  */
@@ -2305,16 +2327,21 @@ async function runPull(options) {
2305
2327
  }
2306
2328
  newDataModel.fields.forEach((f) => {
2307
2329
  let originalFields = originalDataModel.fields.filter((d) => getDbName(d) === getDbName(f));
2308
- const isRelationField = f.$type === "DataField" && !!f.attributes?.some((a) => a?.decl?.ref?.name === "@relation");
2309
- if (originalFields.length === 0 && isRelationField && !getRelationFieldsKey(f)) return;
2310
2330
  if (originalFields.length === 0) {
2311
- const newFieldsKey = getRelationFieldsKey(f);
2312
- if (newFieldsKey) originalFields = originalDataModel.fields.filter((d) => getRelationFieldsKey(d) === newFieldsKey);
2331
+ const newFieldsKey = isDataField(f) ? getRelationFieldsKey(f) : void 0;
2332
+ if (newFieldsKey) originalFields = originalDataModel.fields.filter((d) => isDataField(d) && getRelationFieldsKey(d) === newFieldsKey);
2313
2333
  }
2314
- if (originalFields.length === 0) originalFields = originalDataModel.fields.filter((d) => getRelationFkName(d) === getRelationFkName(f) && !!getRelationFkName(d) && !!getRelationFkName(f));
2315
- if (originalFields.length === 0) originalFields = originalDataModel.fields.filter((d) => f.$type === "DataField" && d.$type === "DataField" && f.type.reference?.ref && d.type.reference?.ref && getDbName(f.type.reference.ref) === getDbName(d.type.reference.ref));
2334
+ if (originalFields.length === 0) {
2335
+ const newFkName = isDataField(f) ? getRelationFkName(f) : void 0;
2336
+ if (newFkName) originalFields = originalDataModel.fields.filter((d) => isDataField(d) && getRelationFkName(d) === newFkName);
2337
+ }
2338
+ if (originalFields.length === 0) {
2339
+ const newRelName = isDataField(f) ? getRelationName(f) : void 0;
2340
+ if (newRelName) originalFields = originalDataModel.fields.filter((d) => isDataField(d) && isDataField(f) && matchesRelationNameFallback(f, newRelName, d));
2341
+ }
2342
+ if (originalFields.length === 0) originalFields = originalDataModel.fields.filter((d) => isDataField(f) && isDataField(d) && f.type.reference?.ref && d.type.reference?.ref && getDbName(f.type.reference.ref) === getDbName(d.type.reference.ref));
2316
2343
  if (originalFields.length > 1) {
2317
- if (!!getRelationFieldsKey(f)) console.warn(colors.yellow(`Found more original fields, need to tweak the search algorithm. ${originalDataModel.name}->[${originalFields.map((of) => of.name).join(", ")}](${f.name})`));
2344
+ if (!(isDataField(f) && !getRelationFieldsKey(f))) console.warn(colors.yellow(`Found more original fields, need to tweak the search algorithm. ${originalDataModel.name}->[${originalFields.map((of) => of.name).join(", ")}](${f.name})`));
2318
2345
  return;
2319
2346
  }
2320
2347
  const originalField = originalFields.at(0);
@@ -2409,12 +2436,19 @@ async function runPull(options) {
2409
2436
  });
2410
2437
  originalDataModel.fields.filter((f) => {
2411
2438
  if (newDataModel.fields.find((d) => getDbName(d) === getDbName(f))) return false;
2412
- const originalFieldsKey = getRelationFieldsKey(f);
2439
+ const originalFieldsKey = isDataField(f) ? getRelationFieldsKey(f) : void 0;
2413
2440
  if (originalFieldsKey) {
2414
- if (newDataModel.fields.find((d) => getRelationFieldsKey(d) === originalFieldsKey)) return false;
2441
+ if (newDataModel.fields.find((d) => isDataField(d) && getRelationFieldsKey(d) === originalFieldsKey)) return false;
2442
+ }
2443
+ const originalFkName = isDataField(f) ? getRelationFkName(f) : void 0;
2444
+ if (originalFkName) {
2445
+ if (newDataModel.fields.find((d) => isDataField(d) && getRelationFkName(d) === originalFkName)) return false;
2446
+ }
2447
+ const originalRelName = isDataField(f) ? getRelationName(f) : void 0;
2448
+ if (originalRelName) {
2449
+ if (newDataModel.fields.find((d) => isDataField(d) && isDataField(f) && matchesRelationNameFallback(f, originalRelName, d))) return false;
2415
2450
  }
2416
- if (newDataModel.fields.find((d) => getRelationFkName(d) === getRelationFkName(f) && !!getRelationFkName(d) && !!getRelationFkName(f))) return false;
2417
- return !newDataModel.fields.find((d) => f.$type === "DataField" && d.$type === "DataField" && f.type.reference?.ref && d.type.reference?.ref && getDbName(f.type.reference.ref) === getDbName(d.type.reference.ref));
2451
+ return !newDataModel.fields.find((d) => isDataField(f) && isDataField(d) && f.type.reference?.ref && d.type.reference?.ref && getDbName(f.type.reference.ref) === getDbName(d.type.reference.ref));
2418
2452
  }).forEach((f) => {
2419
2453
  const _model = f.$container;
2420
2454
  const index = _model.fields.findIndex((d) => d === f);