@ronin/compiler 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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": {