@ronin/compiler 0.9.0-leo-ron-1083-experimental-204 → 0.9.0-leo-ron-1083-experimental-205

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.d.ts CHANGED
@@ -5961,17 +5961,25 @@ interface Model<T extends Array<ModelField> = Array<ModelField>> {
5961
5961
  */
5962
5962
  tableAlias?: string;
5963
5963
  /**
5964
- * If the model is used to associate two models with each other (in the case of
5965
- * many-cardinality link fields), this property should contain the field slug to which
5966
- * the associative model should be mounted on the source model.
5964
+ * Details that identify the model as a model that was automatically created by RONIN,
5965
+ * instead of being manually created by a developer.
5967
5966
  */
5968
- associationSlug?: string;
5967
+ system?: {
5968
+ /** The model that caused the system model to get created. */
5969
+ model: string | 'root';
5970
+ /**
5971
+ * If the model is used to associate two models with each other (in the case of
5972
+ * many-cardinality link fields), this property should contain the field slug to
5973
+ * which the associative model should be mounted on the source model.
5974
+ */
5975
+ associationSlug?: string;
5976
+ };
5969
5977
  fields: T;
5970
5978
  indexes?: Array<ModelIndex<T>>;
5971
5979
  triggers?: Array<ModelTrigger<T>>;
5972
5980
  presets?: Array<ModelPreset>;
5973
5981
  }
5974
- type PublicModel<T extends Array<ModelField> = Array<ModelField>> = Omit<Partial<Model<T>>, 'slug' | 'identifiers' | 'associationSlug' | 'table' | 'tableAlias'> & {
5982
+ type PublicModel<T extends Array<ModelField> = Array<ModelField>> = Omit<Partial<Model<T>>, 'slug' | 'identifiers' | 'system' | 'table' | 'tableAlias'> & {
5975
5983
  slug: Required<Model['slug']>;
5976
5984
  identifiers?: Partial<Model['identifiers']>;
5977
5985
  };
package/dist/index.js CHANGED
@@ -465,67 +465,67 @@ var SYSTEM_FIELDS = [
465
465
  slug: "ronin.updatedBy"
466
466
  }
467
467
  ];
468
- var SYSTEM_MODELS = [
469
- {
470
- slug: "model",
471
- identifiers: {
472
- name: "name",
473
- slug: "slug"
474
- },
475
- // This name mimics the `sqlite_schema` table in SQLite.
476
- table: "ronin_schema",
477
- fields: [
478
- { slug: "name", type: "string" },
479
- { slug: "pluralName", type: "string" },
480
- { slug: "slug", type: "string" },
481
- { slug: "pluralSlug", type: "string" },
482
- { slug: "idPrefix", type: "string" },
483
- { slug: "table", type: "string" },
484
- { slug: "identifiers", type: "group" },
485
- { slug: "identifiers.name", type: "string" },
486
- { slug: "identifiers.slug", type: "string" },
487
- // Providing an empty object as a default value allows us to use `json_insert`
488
- // without needing to fall back to an empty object in the insertion statement,
489
- // which makes the statement shorter.
490
- { slug: "fields", type: "json", defaultValue: "{}" },
491
- { slug: "indexes", type: "json", defaultValue: "{}" },
492
- { slug: "triggers", type: "json", defaultValue: "{}" },
493
- { slug: "presets", type: "json", defaultValue: "{}" }
494
- ]
495
- }
496
- ];
497
- var addSystemModels = (models) => {
498
- const associativeModels = models.flatMap((model) => {
499
- const addedModels = [];
500
- for (const field of model.fields || []) {
501
- if (field.type === "link" && !field.slug.startsWith("ronin.")) {
502
- const relatedModel = getModelBySlug(models, field.target);
503
- let fieldSlug = relatedModel.slug;
504
- if (field.kind === "many") {
505
- fieldSlug = composeAssociationModelSlug(model, field);
506
- addedModels.push({
507
- pluralSlug: fieldSlug,
508
- slug: fieldSlug,
509
- associationSlug: field.slug,
510
- fields: [
511
- {
512
- slug: "source",
513
- type: "link",
514
- target: model.slug
515
- },
516
- {
517
- slug: "target",
518
- type: "link",
519
- target: relatedModel.slug
520
- }
521
- ]
522
- });
523
- }
468
+ var ROOT_MODEL = {
469
+ slug: "model",
470
+ identifiers: {
471
+ name: "name",
472
+ slug: "slug"
473
+ },
474
+ // This name mimics the `sqlite_schema` table in SQLite.
475
+ table: "ronin_schema",
476
+ // Indicates that the model was automatically generated by RONIN.
477
+ system: { model: "root" },
478
+ fields: [
479
+ { slug: "name", type: "string" },
480
+ { slug: "pluralName", type: "string" },
481
+ { slug: "slug", type: "string" },
482
+ { slug: "pluralSlug", type: "string" },
483
+ { slug: "idPrefix", type: "string" },
484
+ { slug: "table", type: "string" },
485
+ { slug: "identifiers", type: "group" },
486
+ { slug: "identifiers.name", type: "string" },
487
+ { slug: "identifiers.slug", type: "string" },
488
+ // Providing an empty object as a default value allows us to use `json_insert`
489
+ // without needing to fall back to an empty object in the insertion statement,
490
+ // which makes the statement shorter.
491
+ { slug: "fields", type: "json", defaultValue: "{}" },
492
+ { slug: "indexes", type: "json", defaultValue: "{}" },
493
+ { slug: "triggers", type: "json", defaultValue: "{}" },
494
+ { slug: "presets", type: "json", defaultValue: "{}" }
495
+ ]
496
+ };
497
+ var getSystemModels = (models, model) => {
498
+ const addedModels = [];
499
+ for (const field of model.fields || []) {
500
+ if (field.type === "link" && !field.slug.startsWith("ronin.")) {
501
+ const relatedModel = getModelBySlug(models, field.target);
502
+ let fieldSlug = relatedModel.slug;
503
+ if (field.kind === "many") {
504
+ fieldSlug = composeAssociationModelSlug(model, field);
505
+ addedModels.push({
506
+ pluralSlug: fieldSlug,
507
+ slug: fieldSlug,
508
+ system: {
509
+ model: model.slug,
510
+ associationSlug: field.slug
511
+ },
512
+ fields: [
513
+ {
514
+ slug: "source",
515
+ type: "link",
516
+ target: model.slug
517
+ },
518
+ {
519
+ slug: "target",
520
+ type: "link",
521
+ target: relatedModel.slug
522
+ }
523
+ ]
524
+ });
524
525
  }
525
526
  }
526
- return addedModels;
527
- });
528
- return [...SYSTEM_MODELS, ...associativeModels, ...models];
527
+ }
528
+ return addedModels;
529
529
  };
530
530
  var addDefaultModelPresets = (list, model) => {
531
531
  const defaultPresets = [];
@@ -567,7 +567,7 @@ var addDefaultModelPresets = (list, model) => {
567
567
  for (const childMatch of childModels) {
568
568
  const { model: childModel, field: childField } = childMatch;
569
569
  const pluralSlug = childModel.pluralSlug;
570
- const presetSlug = childModel.associationSlug || pluralSlug;
570
+ const presetSlug = childModel.system?.associationSlug || pluralSlug;
571
571
  defaultPresets.push({
572
572
  instructions: {
573
573
  including: {
@@ -648,6 +648,14 @@ var formatModelEntity = (type, entities) => {
648
648
  });
649
649
  return entries ? Object.fromEntries(entries) : void 0;
650
650
  };
651
+ var composeSystemModelStatement = (models, dependencyStatements, action, systemModel) => {
652
+ const { system: _, ...systemModelClean } = systemModel;
653
+ const query = {
654
+ [action]: { model: action === "create" ? systemModelClean : systemModelClean.slug }
655
+ };
656
+ const statement = compileQueryInput(query, models, []);
657
+ dependencyStatements.push(...statement.dependencies);
658
+ };
651
659
  var transformMetaQuery = (models, dependencyStatements, statementParams, query) => {
652
660
  const { queryType } = splitQuery(query);
653
661
  const subAltering = query.alter && !("to" in query.alter);
@@ -676,16 +684,16 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
676
684
  }
677
685
  }
678
686
  if (!(modelSlug && slug)) return query;
679
- const tableAction = ["model", "index", "trigger"].includes(entity) ? action.toUpperCase() : "ALTER";
680
- const tableName = convertToSnakeCase(pluralize(modelSlug));
681
687
  const model = action === "create" && entity === "model" ? null : getModelBySlug(models, modelSlug);
682
- const statement = `${tableAction} TABLE "${tableName}"`;
683
688
  if (entity === "model") {
684
689
  let queryTypeDetails;
685
690
  if (action === "create") {
686
691
  const newModel = jsonValue;
687
692
  const modelWithFields = addDefaultModelFields(newModel, true);
688
- const modelWithPresets = addDefaultModelPresets(models, modelWithFields);
693
+ const modelWithPresets = addDefaultModelPresets(
694
+ [...models, modelWithFields],
695
+ modelWithFields
696
+ );
689
697
  const entities = Object.fromEntries(
690
698
  Object.entries(PLURAL_MODEL_ENTITIES).map(([type, pluralType2]) => {
691
699
  const list = modelWithPresets[pluralType2];
@@ -694,7 +702,7 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
694
702
  );
695
703
  const columns = modelWithPresets.fields.map((field2) => getFieldStatement(models, modelWithPresets, field2)).filter(Boolean);
696
704
  dependencyStatements.push({
697
- statement: `${statement} (${columns.join(", ")})`,
705
+ statement: `CREATE TABLE "${modelWithPresets.table}" (${columns.join(", ")})`,
698
706
  params: []
699
707
  });
700
708
  models.push(modelWithPresets);
@@ -703,16 +711,23 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
703
711
  if (entities[entity2]) finalModel[entity2] = entities[entity2];
704
712
  }
705
713
  queryTypeDetails = { to: finalModel };
714
+ getSystemModels(models, modelWithPresets).map((systemModel) => {
715
+ return composeSystemModelStatement(
716
+ models,
717
+ dependencyStatements,
718
+ "create",
719
+ systemModel
720
+ );
721
+ });
706
722
  }
707
723
  if (action === "alter" && model) {
708
724
  const newModel = jsonValue;
709
725
  const modelWithFields = addDefaultModelFields(newModel, false);
710
726
  const modelWithPresets = addDefaultModelPresets(models, modelWithFields);
711
- const newSlug = modelWithPresets.pluralSlug;
712
- if (newSlug) {
713
- const newTable = convertToSnakeCase(newSlug);
727
+ const newTableName = modelWithPresets.table;
728
+ if (newTableName) {
714
729
  dependencyStatements.push({
715
- statement: `${statement} RENAME TO "${newTable}"`,
730
+ statement: `ALTER TABLE "${model.table}" RENAME TO "${newTableName}"`,
716
731
  params: []
717
732
  });
718
733
  }
@@ -726,8 +741,16 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
726
741
  }
727
742
  if (action === "drop" && model) {
728
743
  models.splice(models.indexOf(model), 1);
729
- dependencyStatements.push({ statement, params: [] });
744
+ dependencyStatements.push({ statement: `DROP TABLE "${model.table}"`, params: [] });
730
745
  queryTypeDetails = { with: { slug } };
746
+ models.filter(({ system }) => system?.model === model.slug).map((systemModel) => {
747
+ return composeSystemModelStatement(
748
+ models,
749
+ dependencyStatements,
750
+ "drop",
751
+ systemModel
752
+ );
753
+ });
731
754
  }
732
755
  const queryTypeAction = action === "create" ? "add" : action === "alter" ? "set" : "remove";
733
756
  return {
@@ -737,7 +760,19 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
737
760
  };
738
761
  }
739
762
  const existingModel = model;
763
+ const pluralType = PLURAL_MODEL_ENTITIES[entity];
764
+ const targetEntityIndex = existingModel[pluralType]?.findIndex(
765
+ (entity2) => entity2.slug === slug
766
+ );
767
+ if ((action === "alter" || action === "drop") && (typeof targetEntityIndex === "undefined" || targetEntityIndex === -1)) {
768
+ throw new RoninError({
769
+ message: `No ${entity} with slug "${slug}" defined in model "${existingModel.name}".`,
770
+ code: MODEL_ENTITY_ERROR_CODES[entity]
771
+ });
772
+ }
773
+ const existingEntity = existingModel[pluralType]?.[targetEntityIndex];
740
774
  if (entity === "field") {
775
+ const statement = `ALTER TABLE "${existingModel.table}"`;
741
776
  if (action === "create") {
742
777
  const field2 = jsonValue;
743
778
  field2.type = field2.type || "string";
@@ -757,17 +792,21 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
757
792
  });
758
793
  }
759
794
  } else if (action === "drop") {
760
- dependencyStatements.push({
761
- statement: `${statement} DROP COLUMN "${slug}"`,
762
- params: []
763
- });
795
+ const existingField = existingEntity;
796
+ if (!(existingField.type === "link" && existingField.kind === "many")) {
797
+ dependencyStatements.push({
798
+ statement: `${statement} DROP COLUMN "${slug}"`,
799
+ params: []
800
+ });
801
+ }
764
802
  }
765
803
  }
804
+ const statementAction = action.toUpperCase();
766
805
  if (entity === "index") {
767
806
  const index = jsonValue;
768
807
  const indexName = convertToSnakeCase(slug);
769
808
  const params = [];
770
- let statement2 = `${tableAction}${index?.unique ? " UNIQUE" : ""} INDEX "${indexName}"`;
809
+ let statement = `${statementAction}${index?.unique ? " UNIQUE" : ""} INDEX "${indexName}"`;
771
810
  if (action === "create") {
772
811
  const columns = index.fields.map((field2) => {
773
812
  let fieldSelector = "";
@@ -780,18 +819,18 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
780
819
  if (field2.order) fieldSelector += ` ${field2.order}`;
781
820
  return fieldSelector;
782
821
  });
783
- statement2 += ` ON "${tableName}" (${columns.join(", ")})`;
822
+ statement += ` ON "${existingModel.table}" (${columns.join(", ")})`;
784
823
  if (index.filter) {
785
824
  const withStatement = handleWith(models, existingModel, params, index.filter);
786
- statement2 += ` WHERE (${withStatement})`;
825
+ statement += ` WHERE (${withStatement})`;
787
826
  }
788
827
  }
789
- dependencyStatements.push({ statement: statement2, params });
828
+ dependencyStatements.push({ statement, params });
790
829
  }
791
830
  if (entity === "trigger") {
792
831
  const triggerName = convertToSnakeCase(slug);
793
832
  const params = [];
794
- let statement2 = `${tableAction} TRIGGER "${triggerName}"`;
833
+ let statement = `${statementAction} TRIGGER "${triggerName}"`;
795
834
  if (action === "create") {
796
835
  const trigger = jsonValue;
797
836
  const statementParts = [`${trigger.when} ${trigger.action}`];
@@ -808,7 +847,7 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
808
847
  });
809
848
  statementParts.push(`OF (${fieldSelectors.join(", ")})`);
810
849
  }
811
- statementParts.push("ON", `"${tableName}"`);
850
+ statementParts.push("ON", `"${existingModel.table}"`);
812
851
  if (trigger.filter || trigger.effects.some((query2) => findInObject(query2, RONIN_MODEL_SYMBOLS.FIELD))) {
813
852
  statementParts.push("FOR EACH ROW");
814
853
  }
@@ -831,21 +870,11 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
831
870
  if (effectStatements.length > 1) statementParts.push("BEGIN");
832
871
  statementParts.push(effectStatements.join("; "));
833
872
  if (effectStatements.length > 1) statementParts.push("END");
834
- statement2 += ` ${statementParts.join(" ")}`;
873
+ statement += ` ${statementParts.join(" ")}`;
835
874
  }
836
- dependencyStatements.push({ statement: statement2, params });
875
+ dependencyStatements.push({ statement, params });
837
876
  }
838
- const pluralType = PLURAL_MODEL_ENTITIES[entity];
839
877
  const field = `${RONIN_MODEL_SYMBOLS.FIELD}${pluralType}`;
840
- const targetEntityIndex = existingModel[pluralType]?.findIndex(
841
- (entity2) => entity2.slug === slug
842
- );
843
- if ((action === "alter" || action === "drop") && (typeof targetEntityIndex === "undefined" || targetEntityIndex === -1)) {
844
- throw new RoninError({
845
- message: `No ${entity} with slug "${slug}" defined in model "${existingModel.name}".`,
846
- code: MODEL_ENTITY_ERROR_CODES[entity]
847
- });
848
- }
849
878
  let json;
850
879
  switch (action) {
851
880
  case "create": {
@@ -867,9 +896,23 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
867
896
  case "drop": {
868
897
  json = `json_remove(${field}, '$.${slug}')`;
869
898
  const targetEntity = existingModel[pluralType];
870
- delete targetEntity[targetEntityIndex];
899
+ targetEntity.splice(targetEntityIndex, 1);
871
900
  }
872
901
  }
902
+ const currentSystemModels = models.filter(({ system }) => {
903
+ return system?.model === existingModel.slug;
904
+ });
905
+ const newSystemModels = getSystemModels(models, existingModel);
906
+ for (const systemModel of currentSystemModels) {
907
+ const exists = newSystemModels.find((model2) => model2.slug === systemModel.slug);
908
+ if (exists) continue;
909
+ composeSystemModelStatement(models, dependencyStatements, "drop", systemModel);
910
+ }
911
+ for (const systemModel of newSystemModels) {
912
+ const exists = currentSystemModels.find((model2) => model2.slug === systemModel.slug);
913
+ if (exists) continue;
914
+ composeSystemModelStatement(models, dependencyStatements, "create", systemModel);
915
+ }
873
916
  return {
874
917
  set: {
875
918
  model: {
@@ -1240,13 +1283,19 @@ var handleTo = (models, model, statementParams, queryType, dependencyStatements,
1240
1283
  };
1241
1284
 
1242
1285
  // src/utils/index.ts
1243
- var compileQueryInput = (query, models, statementParams, options) => {
1286
+ var compileQueryInput = (defaultQuery, models, statementParams, options) => {
1287
+ const dependencyStatements = [];
1288
+ const query = transformMetaQuery(
1289
+ models,
1290
+ dependencyStatements,
1291
+ statementParams,
1292
+ defaultQuery
1293
+ );
1244
1294
  const parsedQuery = splitQuery(query);
1245
1295
  const { queryType, queryModel, queryInstructions } = parsedQuery;
1246
1296
  const model = getModelBySlug(models, queryModel);
1247
1297
  const single = queryModel !== model.pluralSlug;
1248
1298
  let instructions = formatIdentifiers(model, queryInstructions);
1249
- const dependencyStatements = [];
1250
1299
  const returning = options?.returning ?? true;
1251
1300
  if (instructions && Object.hasOwn(instructions, "for")) {
1252
1301
  instructions = handleFor(model, instructions);
@@ -1398,7 +1447,11 @@ var Transaction = class {
1398
1447
  * @returns The composed SQL statements.
1399
1448
  */
1400
1449
  compileQueries = (queries, models, options) => {
1401
- const modelList = addSystemModels(models).map((model) => {
1450
+ const modelList = [
1451
+ ROOT_MODEL,
1452
+ ...models.flatMap((model) => getSystemModels(models, model)),
1453
+ ...models
1454
+ ].map((model) => {
1402
1455
  return addDefaultModelFields(model, true);
1403
1456
  });
1404
1457
  const modelListWithPresets = modelList.map((model) => {
@@ -1407,17 +1460,10 @@ var Transaction = class {
1407
1460
  const dependencyStatements = [];
1408
1461
  const mainStatements = [];
1409
1462
  for (const query of queries) {
1410
- const statementValues = options?.inlineParams ? null : [];
1411
- const transformedQuery = transformMetaQuery(
1412
- modelListWithPresets,
1413
- dependencyStatements,
1414
- statementValues,
1415
- query
1416
- );
1417
1463
  const result = compileQueryInput(
1418
- transformedQuery,
1464
+ query,
1419
1465
  modelListWithPresets,
1420
- statementValues
1466
+ options?.inlineParams ? null : []
1421
1467
  );
1422
1468
  dependencyStatements.push(...result.dependencies);
1423
1469
  mainStatements.push(result.main);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ronin/compiler",
3
- "version": "0.9.0-leo-ron-1083-experimental-204",
3
+ "version": "0.9.0-leo-ron-1083-experimental-205",
4
4
  "type": "module",
5
5
  "description": "Compiles RONIN queries to SQL statements.",
6
6
  "publishConfig": {