@ronin/compiler 0.5.0 → 0.5.1

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +160 -107
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -59,7 +59,7 @@ const schemas: Array<Schema> = [{
59
59
 
60
60
  const statements: Array<Statements> = compileQueries(queries, schemas);
61
61
  // [{
62
- // statement: 'SELECT * FROM "accounts" ORDER BY "ronin.createdAt" DESC LIMIT 101',
62
+ // statement: 'SELECT * FROM "accounts"',
63
63
  // params: [],
64
64
  // returning: true,
65
65
  // }]
package/dist/index.js CHANGED
@@ -317,7 +317,7 @@ var getTableForSchema = (schema) => {
317
317
  return convertToSnakeCase(schema.pluralSlug);
318
318
  };
319
319
  var composeMetaSchemaSlug = (suffix) => convertToCamelCase(`ronin_${suffix}`);
320
- var composeAssociationSchemaSlug = (schema, field) => composeMetaSchemaSlug(`${schema.pluralSlug}_${field.slug}`);
320
+ var composeAssociationSchemaSlug = (schema, field) => composeMetaSchemaSlug(`${schema.slug}_${field.slug}`);
321
321
  var getFieldSelector = (field, fieldPath, rootTable) => {
322
322
  const symbol = rootTable?.startsWith(RONIN_SCHEMA_SYMBOLS.FIELD) ? `${rootTable.replace(RONIN_SCHEMA_SYMBOLS.FIELD, "").slice(0, -1)}.` : "";
323
323
  const tablePrefix = symbol || (rootTable ? `"${rootTable}".` : "");
@@ -352,6 +352,53 @@ var getFieldFromSchema = (schema, fieldPath, instructionName, rootTable) => {
352
352
  const fieldSelector = getFieldSelector(schemaField, fieldPath, rootTable);
353
353
  return { field: schemaField, fieldSelector };
354
354
  };
355
+ var slugToName = (slug) => {
356
+ const name = slug.replace(/([a-z])([A-Z])/g, "$1 $2");
357
+ return title(name);
358
+ };
359
+ var VOWELS = ["a", "e", "i", "o", "u"];
360
+ var pluralize = (word) => {
361
+ const lastLetter = word.slice(-1).toLowerCase();
362
+ const secondLastLetter = word.slice(-2, -1).toLowerCase();
363
+ if (lastLetter === "y" && !VOWELS.includes(secondLastLetter)) {
364
+ return `${word.slice(0, -1)}ies`;
365
+ }
366
+ if (lastLetter === "s" || word.slice(-2).toLowerCase() === "ch" || word.slice(-2).toLowerCase() === "sh" || word.slice(-2).toLowerCase() === "ex") {
367
+ return `${word}es`;
368
+ }
369
+ return `${word}s`;
370
+ };
371
+ var schemaSettings = [
372
+ ["pluralSlug", "slug", pluralize],
373
+ ["name", "slug", slugToName],
374
+ ["pluralName", "pluralSlug", slugToName],
375
+ ["idPrefix", "slug", (slug) => slug.slice(0, 3)]
376
+ ];
377
+ var addDefaultSchemaFields = (schema, isNew) => {
378
+ const copiedSchema = { ...schema };
379
+ for (const [setting, base, generator] of schemaSettings) {
380
+ if (copiedSchema[setting] || !copiedSchema[base]) continue;
381
+ copiedSchema[setting] = generator(copiedSchema[base]);
382
+ }
383
+ const newFields = copiedSchema.fields || [];
384
+ if (isNew || newFields.length > 0) {
385
+ if (!copiedSchema.identifiers) copiedSchema.identifiers = {};
386
+ if (!copiedSchema.identifiers.name) {
387
+ const suitableField = newFields.find(
388
+ (field) => field.type === "string" && field.required === true && ["name"].includes(field.slug)
389
+ );
390
+ copiedSchema.identifiers.name = suitableField?.slug || "id";
391
+ }
392
+ if (!copiedSchema.identifiers.slug) {
393
+ const suitableField = newFields.find(
394
+ (field) => field.type === "string" && field.unique === true && field.required === true && ["slug", "handle"].includes(field.slug)
395
+ );
396
+ copiedSchema.identifiers.slug = suitableField?.slug || "id";
397
+ }
398
+ copiedSchema.fields = [...SYSTEM_FIELDS, ...newFields];
399
+ }
400
+ return copiedSchema;
401
+ };
355
402
  var SYSTEM_FIELDS = [
356
403
  {
357
404
  name: "ID",
@@ -392,16 +439,12 @@ var SYSTEM_FIELDS = [
392
439
  ];
393
440
  var SYSTEM_SCHEMAS = [
394
441
  {
395
- name: "Schema",
396
- pluralName: "Schemas",
397
442
  slug: "schema",
398
- pluralSlug: "schemas",
399
443
  identifiers: {
400
444
  name: "name",
401
445
  slug: "slug"
402
446
  },
403
447
  fields: [
404
- ...SYSTEM_FIELDS,
405
448
  { slug: "name", type: "string" },
406
449
  { slug: "pluralName", type: "string" },
407
450
  { slug: "slug", type: "string" },
@@ -412,20 +455,18 @@ var SYSTEM_SCHEMAS = [
412
455
  { slug: "identifiers.slug", type: "string" },
413
456
  { slug: "fields", type: "json" },
414
457
  { slug: "indexes", type: "json" },
415
- { slug: "triggers", type: "json" }
458
+ { slug: "triggers", type: "json" },
459
+ { slug: "including", type: "json" },
460
+ { slug: "for", type: "json" }
416
461
  ]
417
462
  },
418
463
  {
419
- name: "Field",
420
- pluralName: "Fields",
421
464
  slug: "field",
422
- pluralSlug: "fields",
423
465
  identifiers: {
424
466
  name: "name",
425
467
  slug: "slug"
426
468
  },
427
469
  fields: [
428
- ...SYSTEM_FIELDS,
429
470
  { slug: "name", type: "string" },
430
471
  { slug: "slug", type: "string", required: true },
431
472
  { slug: "type", type: "string", required: true },
@@ -448,16 +489,12 @@ var SYSTEM_SCHEMAS = [
448
489
  ]
449
490
  },
450
491
  {
451
- name: "Index",
452
- pluralName: "Indexes",
453
492
  slug: "index",
454
- pluralSlug: "indexes",
455
493
  identifiers: {
456
494
  name: "slug",
457
495
  slug: "slug"
458
496
  },
459
497
  fields: [
460
- ...SYSTEM_FIELDS,
461
498
  { slug: "slug", type: "string", required: true },
462
499
  {
463
500
  slug: "schema",
@@ -470,16 +507,12 @@ var SYSTEM_SCHEMAS = [
470
507
  ]
471
508
  },
472
509
  {
473
- name: "Trigger",
474
- pluralName: "Triggers",
475
510
  slug: "trigger",
476
- pluralSlug: "triggers",
477
511
  identifiers: {
478
512
  name: "slug",
479
513
  slug: "slug"
480
514
  },
481
515
  fields: [
482
- ...SYSTEM_FIELDS,
483
516
  { slug: "slug", type: "string", required: true },
484
517
  { slug: "schema", type: "reference", target: { slug: "schema" }, required: true },
485
518
  { slug: "cause", type: "string", required: true },
@@ -487,85 +520,89 @@ var SYSTEM_SCHEMAS = [
487
520
  { slug: "effects", type: "json", required: true }
488
521
  ]
489
522
  }
490
- ];
523
+ ].map((schema) => addDefaultSchemaFields(schema, true));
491
524
  var SYSTEM_SCHEMA_SLUGS = SYSTEM_SCHEMAS.flatMap(({ slug, pluralSlug }) => [
492
525
  slug,
493
526
  pluralSlug
494
527
  ]);
495
- var prepareSchema = (schema) => {
496
- const copiedSchema = { ...schema };
497
- if (!copiedSchema.pluralSlug) copiedSchema.pluralSlug = pluralize(copiedSchema.slug);
498
- if (!copiedSchema.name) copiedSchema.name = slugToName(copiedSchema.slug);
499
- if (!copiedSchema.pluralName)
500
- copiedSchema.pluralName = slugToName(copiedSchema.pluralSlug);
501
- if (!copiedSchema.idPrefix) copiedSchema.idPrefix = copiedSchema.slug.slice(0, 3);
502
- if (!copiedSchema.identifiers) copiedSchema.identifiers = {};
503
- if (!copiedSchema.identifiers.name) copiedSchema.identifiers.name = "id";
504
- if (!copiedSchema.identifiers.slug) copiedSchema.identifiers.slug = "id";
505
- return copiedSchema;
506
- };
507
528
  var addSystemSchemas = (schemas) => {
508
- const list = [...SYSTEM_SCHEMAS, ...schemas].map(prepareSchema);
509
- for (const schema of list) {
510
- const defaultIncluding = {};
529
+ const associativeSchemas = schemas.flatMap((schema) => {
530
+ const addedSchemas = [];
511
531
  for (const field of schema.fields || []) {
512
532
  if (field.type === "reference" && !field.slug.startsWith("ronin.")) {
513
- const relatedSchema = getSchemaBySlug(list, field.target.slug);
533
+ const relatedSchema = getSchemaBySlug(schemas, field.target.slug);
514
534
  let fieldSlug = relatedSchema.slug;
515
535
  if (field.kind === "many") {
516
536
  fieldSlug = composeAssociationSchemaSlug(schema, field);
517
- list.push({
537
+ addedSchemas.push({
518
538
  pluralSlug: fieldSlug,
519
539
  slug: fieldSlug,
520
- identifiers: {
521
- name: "id",
522
- slug: "id"
523
- },
524
540
  fields: [
525
541
  {
526
542
  slug: "source",
527
543
  type: "reference",
528
- target: schema
544
+ target: { slug: schema.slug }
529
545
  },
530
546
  {
531
547
  slug: "target",
532
548
  type: "reference",
533
- target: relatedSchema
549
+ target: { slug: relatedSchema.slug }
534
550
  }
535
551
  ]
536
552
  });
537
553
  }
538
- defaultIncluding[field.slug] = {
539
- get: {
540
- [fieldSlug]: {
541
- with: {
542
- // Compare the `id` field of the related schema to the reference field on
543
- // the root schema (`field.slug`).
544
- id: `${RONIN_SCHEMA_SYMBOLS.FIELD}${field.slug}`
545
- }
554
+ }
555
+ }
556
+ return addedSchemas;
557
+ });
558
+ return [...SYSTEM_SCHEMAS, ...associativeSchemas, ...schemas];
559
+ };
560
+ var addDefaultSchemaShortcuts = (list, schema) => {
561
+ const defaultIncluding = {};
562
+ for (const field of schema.fields || []) {
563
+ if (field.type === "reference" && !field.slug.startsWith("ronin.")) {
564
+ const relatedSchema = getSchemaBySlug(list, field.target.slug);
565
+ let fieldSlug = relatedSchema.slug;
566
+ if (field.kind === "many") {
567
+ fieldSlug = composeAssociationSchemaSlug(schema, field);
568
+ }
569
+ defaultIncluding[field.slug] = {
570
+ get: {
571
+ [fieldSlug]: {
572
+ with: {
573
+ // Compare the `id` field of the related schema to the reference field on
574
+ // the root schema (`field.slug`).
575
+ id: `${RONIN_SCHEMA_SYMBOLS.FIELD}${field.slug}`
546
576
  }
547
577
  }
548
- };
549
- const relatedSchemaToModify = getSchemaBySlug(list, field.target.slug);
550
- if (!relatedSchemaToModify) throw new Error("Missing related schema");
551
- relatedSchemaToModify.including = {
552
- [schema.pluralSlug]: {
553
- get: {
554
- [schema.pluralSlug]: {
555
- with: {
556
- [field.slug]: `${RONIN_SCHEMA_SYMBOLS.FIELD}id`
557
- }
558
- }
559
- }
560
- },
561
- ...relatedSchemaToModify.including
562
- };
563
- }
578
+ }
579
+ };
564
580
  }
565
- schema.fields = [...SYSTEM_FIELDS, ...schema.fields || []];
581
+ }
582
+ const childSchemas = list.map((subSchema) => {
583
+ const field = subSchema.fields?.find((field2) => {
584
+ return field2.type === "reference" && field2.target.slug === schema.slug;
585
+ });
586
+ if (!field) return null;
587
+ return { schema: subSchema, field };
588
+ }).filter((match) => match !== null);
589
+ for (const childMatch of childSchemas) {
590
+ const { schema: childSchema, field: childField } = childMatch;
591
+ const pluralSlug = childSchema.pluralSlug;
592
+ defaultIncluding[pluralSlug] = {
593
+ get: {
594
+ [pluralSlug]: {
595
+ with: {
596
+ [childField.slug]: `${RONIN_SCHEMA_SYMBOLS.FIELD}id`
597
+ }
598
+ }
599
+ }
600
+ };
601
+ }
602
+ if (Object.keys(defaultIncluding).length > 0) {
566
603
  schema.including = { ...defaultIncluding, ...schema.including };
567
604
  }
568
- return list;
605
+ return schema;
569
606
  };
570
607
  var mappedInstructions = {
571
608
  create: "to",
@@ -713,28 +750,38 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
713
750
  dependencyStatements.push({ statement: statement2, params });
714
751
  return queryInstructions;
715
752
  }
716
- let statement = `${tableAction} TABLE "${tableName}"`;
753
+ const statement = `${tableAction} TABLE "${tableName}"`;
717
754
  if (kind === "schemas") {
718
- const providedFields = instructionList?.fields || [];
719
- const fields = [...SYSTEM_FIELDS, ...providedFields];
720
755
  if (queryType === "create" || queryType === "set") {
721
- queryInstructions.to = prepareSchema(queryInstructions.to);
756
+ const schemaWithFields = addDefaultSchemaFields(
757
+ queryInstructions.to,
758
+ queryType === "create"
759
+ );
760
+ const schemaWithShortcuts = addDefaultSchemaShortcuts(schemas, schemaWithFields);
761
+ queryInstructions.to = schemaWithShortcuts;
722
762
  }
723
763
  if (queryType === "create") {
764
+ const { fields } = queryInstructions.to;
724
765
  const columns = fields.map(getFieldStatement).filter(Boolean);
725
- statement += ` (${columns.join(", ")})`;
766
+ dependencyStatements.push({
767
+ statement: `${statement} (${columns.join(", ")})`,
768
+ params: []
769
+ });
726
770
  schemas.push(queryInstructions.to);
727
771
  } else if (queryType === "set") {
728
772
  const newSlug = queryInstructions.to?.pluralSlug;
729
773
  if (newSlug) {
730
774
  const newTable = convertToSnakeCase(newSlug);
731
- statement += ` RENAME TO "${newTable}"`;
775
+ dependencyStatements.push({
776
+ statement: `${statement} RENAME TO "${newTable}"`,
777
+ params: []
778
+ });
732
779
  }
733
780
  Object.assign(targetSchema, queryInstructions.to);
734
781
  } else if (queryType === "drop") {
735
782
  schemas.splice(schemas.indexOf(targetSchema), 1);
783
+ dependencyStatements.push({ statement, params: [] });
736
784
  }
737
- dependencyStatements.push({ statement, params: [] });
738
785
  return queryInstructions;
739
786
  }
740
787
  if (kind === "fields") {
@@ -746,35 +793,27 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
746
793
  fields: ["type"]
747
794
  });
748
795
  }
749
- statement += ` ADD COLUMN ${getFieldStatement(instructionList)}`;
796
+ dependencyStatements.push({
797
+ statement: `${statement} ADD COLUMN ${getFieldStatement(instructionList)}`,
798
+ params: []
799
+ });
750
800
  } else if (queryType === "set") {
751
801
  const newSlug = queryInstructions.to?.slug;
752
802
  if (newSlug) {
753
- statement += ` RENAME COLUMN "${slug}" TO "${newSlug}"`;
803
+ dependencyStatements.push({
804
+ statement: `${statement} RENAME COLUMN "${slug}" TO "${newSlug}"`,
805
+ params: []
806
+ });
754
807
  }
755
808
  } else if (queryType === "drop") {
756
- statement += ` DROP COLUMN "${slug}"`;
809
+ dependencyStatements.push({
810
+ statement: `${statement} DROP COLUMN "${slug}"`,
811
+ params: []
812
+ });
757
813
  }
758
- dependencyStatements.push({ statement, params: [] });
759
814
  }
760
815
  return queryInstructions;
761
816
  };
762
- var slugToName = (slug) => {
763
- const name = slug.replace(/([a-z])([A-Z])/g, "$1 $2");
764
- return title(name);
765
- };
766
- var VOWELS = ["a", "e", "i", "o", "u"];
767
- var pluralize = (word) => {
768
- const lastLetter = word.slice(-1).toLowerCase();
769
- const secondLastLetter = word.slice(-2, -1).toLowerCase();
770
- if (lastLetter === "y" && !VOWELS.includes(secondLastLetter)) {
771
- return `${word.slice(0, -1)}ies`;
772
- }
773
- if (lastLetter === "s" || word.slice(-2).toLowerCase() === "ch" || word.slice(-2).toLowerCase() === "sh" || word.slice(-2).toLowerCase() === "ex") {
774
- return `${word}es`;
775
- }
776
- return `${word}s`;
777
- };
778
817
 
779
818
  // src/instructions/before-after.ts
780
819
  var CURSOR_SEPARATOR = ",";
@@ -783,15 +822,22 @@ var handleBeforeOrAfter = (schema, statementParams, instructions, rootTable) =>
783
822
  if (!(instructions.before || instructions.after)) {
784
823
  throw new RoninError({
785
824
  message: "The `before` or `after` instruction must not be empty.",
786
- code: "MISSING_INSTRUCTION",
787
- queries: null
825
+ code: "MISSING_INSTRUCTION"
788
826
  });
789
827
  }
790
828
  if (instructions.before && instructions.after) {
791
829
  throw new RoninError({
792
830
  message: "The `before` and `after` instructions cannot co-exist. Choose one.",
793
- code: "MUTUALLY_EXCLUSIVE_INSTRUCTIONS",
794
- queries: null
831
+ code: "MUTUALLY_EXCLUSIVE_INSTRUCTIONS"
832
+ });
833
+ }
834
+ if (!instructions.limitedTo) {
835
+ let message = "When providing a pagination cursor in the `before` or `after`";
836
+ message += " instruction, a `limitedTo` instruction must be provided as well, to";
837
+ message += " define the page size.";
838
+ throw new RoninError({
839
+ message,
840
+ code: "MISSING_INSTRUCTION"
795
841
  });
796
842
  }
797
843
  const { ascending = [], descending = [] } = instructions.orderedBy || {};
@@ -951,9 +997,10 @@ var handleIncluding = (schemas, statementParams, schema, instruction, rootTable)
951
997
 
952
998
  // src/instructions/limited-to.ts
953
999
  var handleLimitedTo = (single, instruction) => {
954
- const pageSize = instruction || 100;
955
- const finalPageSize = pageSize + 1;
956
- return `LIMIT ${single ? "1" : finalPageSize} `;
1000
+ let amount;
1001
+ if (instruction) amount = instruction + 1;
1002
+ if (single) amount = 1;
1003
+ return `LIMIT ${amount} `;
957
1004
  };
958
1005
 
959
1006
  // src/instructions/ordered-by.ts
@@ -1217,7 +1264,7 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
1217
1264
  );
1218
1265
  if (forStatement.length > 0) conditions.push(forStatement);
1219
1266
  }
1220
- if ((queryType === "get" || queryType === "count") && !single) {
1267
+ if ((queryType === "get" || queryType === "count") && !single && instructions?.limitedTo) {
1221
1268
  instructions = instructions || {};
1222
1269
  instructions.orderedBy = instructions.orderedBy || {};
1223
1270
  instructions.orderedBy.ascending = instructions.orderedBy.ascending || [];
@@ -1244,7 +1291,8 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
1244
1291
  before: instructions.before,
1245
1292
  after: instructions.after,
1246
1293
  with: instructions.with,
1247
- orderedBy: instructions.orderedBy
1294
+ orderedBy: instructions.orderedBy,
1295
+ limitedTo: instructions.limitedTo
1248
1296
  },
1249
1297
  isJoining ? table : void 0
1250
1298
  );
@@ -1265,7 +1313,7 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
1265
1313
  );
1266
1314
  statement += `${orderedByStatement} `;
1267
1315
  }
1268
- if (queryType === "get" && !isJoiningMultipleRows) {
1316
+ if (queryType === "get" && !isJoiningMultipleRows && (single || instructions?.limitedTo)) {
1269
1317
  statement += handleLimitedTo(single, instructions?.limitedTo);
1270
1318
  }
1271
1319
  if (["create", "set", "drop"].includes(queryType) && returning) {
@@ -1284,13 +1332,18 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
1284
1332
 
1285
1333
  // src/index.ts
1286
1334
  var compileQueries = (queries, schemas, options) => {
1287
- const schemaList = addSystemSchemas(schemas);
1335
+ const schemaList = addSystemSchemas(schemas).map((schema) => {
1336
+ return addDefaultSchemaFields(schema, true);
1337
+ });
1338
+ const schemaListWithShortcuts = schemaList.map((schema) => {
1339
+ return addDefaultSchemaShortcuts(schemaList, schema);
1340
+ });
1288
1341
  const dependencyStatements = [];
1289
1342
  const mainStatements = [];
1290
1343
  for (const query of queries) {
1291
1344
  const result = compileQueryInput(
1292
1345
  query,
1293
- schemaList,
1346
+ schemaListWithShortcuts,
1294
1347
  options?.inlineValues ? null : []
1295
1348
  );
1296
1349
  dependencyStatements.push(...result.dependencies);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ronin/compiler",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "description": "Compiles RONIN queries to SQL statements.",
6
6
  "publishConfig": {