@ronin/compiler 0.2.1 → 0.2.2

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/README.md CHANGED
@@ -49,7 +49,6 @@ const query = {
49
49
 
50
50
  const schemas = [
51
51
  {
52
- pluralSlug: 'accounts',
53
52
  slug: 'account',
54
53
  },
55
54
  ];
package/dist/index.d.ts CHANGED
@@ -4667,7 +4667,7 @@ type SchemaFieldNormal = SchemaFieldBasics & {
4667
4667
  type SchemaFieldReferenceAction = 'CASCADE' | 'RESTRICT' | 'SET NULL' | 'SET DEFAULT' | 'NO ACTION';
4668
4668
  type SchemaFieldReference = SchemaFieldBasics & {
4669
4669
  type: 'reference';
4670
- target: Omit<Partial<Schema>, 'pluralSlug'> & Pick<Schema, 'pluralSlug'>;
4670
+ target: Omit<Partial<Schema>, 'slug'> & Pick<Schema, 'slug'>;
4671
4671
  kind?: 'one' | 'many';
4672
4672
  actions?: {
4673
4673
  onDelete?: SchemaFieldReferenceAction;
@@ -4679,7 +4679,7 @@ interface Schema {
4679
4679
  name?: string;
4680
4680
  pluralName?: string;
4681
4681
  slug: string;
4682
- pluralSlug: string;
4682
+ pluralSlug?: string;
4683
4683
  identifiers?: {
4684
4684
  title?: string;
4685
4685
  slug?: string;
package/dist/index.js CHANGED
@@ -82,6 +82,7 @@ var splitQuery = (query) => {
82
82
  };
83
83
 
84
84
  // src/utils/schema.ts
85
+ import title from "title";
85
86
  var getSchemaBySlug = (schemas, slug) => {
86
87
  const schema = schemas.find((schema2) => {
87
88
  return schema2.slug === slug || schema2.pluralSlug === slug;
@@ -97,9 +98,6 @@ var getSchemaBySlug = (schemas, slug) => {
97
98
  var getTableForSchema = (schema) => {
98
99
  return convertToSnakeCase(schema.pluralSlug);
99
100
  };
100
- var getSchemaName = (schema) => {
101
- return schema.name || schema.slug;
102
- };
103
101
  var composeMetaSchemaSlug = (suffix) => convertToCamelCase(`ronin_${suffix}`);
104
102
  var composeAssociationSchemaSlug = (schema, field) => composeMetaSchemaSlug(`${schema.pluralSlug}_${field.slug}`);
105
103
  var getFieldSelector = (field, fieldPath, rootTable) => {
@@ -126,7 +124,7 @@ var getFieldFromSchema = (schema, fieldPath, instructionName, rootTable) => {
126
124
  schemaField = schemaFields.find((field) => field.slug === fieldPath);
127
125
  if (!schemaField) {
128
126
  throw new RoninError({
129
- message: `${errorPrefix} does not exist in schema "${getSchemaName(schema)}".`,
127
+ message: `${errorPrefix} does not exist in schema "${schema.name}".`,
130
128
  code: "FIELD_NOT_FOUND",
131
129
  field: fieldPath,
132
130
  queries: null
@@ -201,27 +199,62 @@ var SYSTEM_SCHEMAS = [
201
199
  { slug: "name", type: "string" },
202
200
  { slug: "slug", type: "string", required: true },
203
201
  { slug: "type", type: "string", required: true },
204
- { slug: "schema", type: "reference", target: { pluralSlug: "schemas" } },
202
+ {
203
+ slug: "schema",
204
+ type: "reference",
205
+ target: { slug: "schema" },
206
+ required: true
207
+ },
205
208
  { slug: "required", type: "boolean" },
206
209
  { slug: "defaultValue", type: "string" },
207
210
  { slug: "unique", type: "boolean" },
208
211
  { slug: "autoIncrement", type: "boolean" },
209
212
  // Only allowed for fields of type "reference".
210
- { slug: "target", type: "reference", target: { pluralSlug: "schemas" } },
213
+ { slug: "target", type: "reference", target: { slug: "schema" } },
211
214
  { slug: "kind", type: "string" },
212
215
  { slug: "actions", type: "group" },
213
216
  { slug: "actions.onDelete", type: "string" },
214
217
  { slug: "actions.onUpdate", type: "string" }
215
218
  ]
219
+ },
220
+ {
221
+ name: "Index",
222
+ pluralName: "Indexes",
223
+ slug: "index",
224
+ pluralSlug: "indexes",
225
+ fields: [
226
+ ...SYSTEM_FIELDS,
227
+ { slug: "slug", type: "string", required: true },
228
+ {
229
+ slug: "schema",
230
+ type: "reference",
231
+ target: { slug: "schema" },
232
+ required: true
233
+ },
234
+ { slug: "unique", type: "boolean" },
235
+ { slug: "filter", type: "json" }
236
+ ]
216
237
  }
217
238
  ];
239
+ var SYSTEM_SCHEMA_SLUGS = SYSTEM_SCHEMAS.flatMap(({ slug, pluralSlug }) => [
240
+ slug,
241
+ pluralSlug
242
+ ]);
243
+ var prepareSchema = (schema) => {
244
+ const copiedSchema = { ...schema };
245
+ if (!copiedSchema.pluralSlug) copiedSchema.pluralSlug = pluralize(copiedSchema.slug);
246
+ if (!copiedSchema.name) copiedSchema.name = slugToName(copiedSchema.slug);
247
+ if (!copiedSchema.pluralName)
248
+ copiedSchema.pluralName = slugToName(copiedSchema.pluralSlug);
249
+ return copiedSchema;
250
+ };
218
251
  var addSystemSchemas = (schemas) => {
219
- const list = [...SYSTEM_SCHEMAS, ...schemas].map((schema) => ({ ...schema }));
252
+ const list = [...SYSTEM_SCHEMAS, ...schemas].map(prepareSchema);
220
253
  for (const schema of list) {
221
254
  const defaultIncluding = {};
222
255
  for (const field of schema.fields || []) {
223
256
  if (field.type === "reference" && !field.slug.startsWith("ronin.")) {
224
- const relatedSchema = getSchemaBySlug(list, field.target.pluralSlug);
257
+ const relatedSchema = getSchemaBySlug(list, field.target.slug);
225
258
  let fieldSlug = relatedSchema.slug;
226
259
  if (field.kind === "many") {
227
260
  fieldSlug = composeAssociationSchemaSlug(schema, field);
@@ -253,7 +286,7 @@ var addSystemSchemas = (schemas) => {
253
286
  }
254
287
  }
255
288
  };
256
- const relatedSchemaToModify = getSchemaBySlug(list, field.target.pluralSlug);
289
+ const relatedSchemaToModify = getSchemaBySlug(list, field.target.slug);
257
290
  if (!relatedSchemaToModify) throw new Error("Missing related schema");
258
291
  relatedSchemaToModify.including = {
259
292
  [schema.pluralSlug]: {
@@ -298,7 +331,7 @@ var getFieldStatement = (field) => {
298
331
  statement += ` DEFAULT ${field.defaultValue}`;
299
332
  if (field.type === "reference") {
300
333
  const actions = field.actions || {};
301
- const targetTable = convertToSnakeCase(field.target.pluralSlug);
334
+ const targetTable = convertToSnakeCase(pluralize(field.target.slug));
302
335
  statement += ` REFERENCES ${targetTable}("id")`;
303
336
  for (const trigger in actions) {
304
337
  const triggerName = trigger.toUpperCase().slice(2);
@@ -311,65 +344,72 @@ var getFieldStatement = (field) => {
311
344
  var addSchemaQueries = (queryDetails, writeStatements) => {
312
345
  const { queryType, querySchema, queryInstructions } = queryDetails;
313
346
  if (!["create", "set", "drop"].includes(queryType)) return;
314
- if (!["schema", "schemas", "field", "fields"].includes(querySchema)) return;
347
+ if (!SYSTEM_SCHEMA_SLUGS.includes(querySchema)) return;
315
348
  const instructionName = mappedInstructions[queryType];
316
349
  const instructionList = queryInstructions[instructionName];
317
- const kind = ["schema", "schemas"].includes(querySchema) ? "schemas" : "fields";
318
- const instructionTarget = kind === "schemas" ? instructionList : instructionList?.schema;
350
+ const kind = getSchemaBySlug(SYSTEM_SCHEMAS, querySchema).pluralSlug;
319
351
  let tableAction = "ALTER";
320
- let schemaPluralSlug = null;
321
352
  let queryTypeReadable = null;
322
353
  switch (queryType) {
323
354
  case "create": {
324
- if (kind === "schemas") tableAction = "CREATE";
325
- schemaPluralSlug = instructionTarget?.pluralSlug;
355
+ if (kind === "schemas" || kind === "indexes") tableAction = "CREATE";
326
356
  queryTypeReadable = "creating";
327
357
  break;
328
358
  }
329
359
  case "set": {
330
360
  if (kind === "schemas") tableAction = "ALTER";
331
- schemaPluralSlug = instructionTarget?.pluralSlug?.being || instructionTarget?.pluralSlug;
332
361
  queryTypeReadable = "updating";
333
362
  break;
334
363
  }
335
364
  case "drop": {
336
- if (kind === "schemas") tableAction = "DROP";
337
- schemaPluralSlug = instructionTarget?.pluralSlug?.being || instructionTarget?.pluralSlug;
365
+ if (kind === "schemas" || kind === "indexes") tableAction = "DROP";
338
366
  queryTypeReadable = "deleting";
339
367
  break;
340
368
  }
341
369
  }
342
- if (!schemaPluralSlug) {
343
- const field = kind === "schemas" ? "pluralSlug" : "schema.pluralSlug";
370
+ const slug = instructionList?.slug?.being || instructionList?.slug;
371
+ if (!slug) {
344
372
  throw new RoninError({
345
- message: `When ${queryTypeReadable} ${kind}, a \`${field}\` field must be provided in the \`${instructionName}\` instruction.`,
373
+ message: `When ${queryTypeReadable} ${kind}, a \`slug\` field must be provided in the \`${instructionName}\` instruction.`,
346
374
  code: "MISSING_FIELD",
347
- fields: [field]
375
+ fields: ["slug"]
348
376
  });
349
377
  }
350
- const table = convertToSnakeCase(schemaPluralSlug);
351
- const fields = [...SYSTEM_FIELDS];
352
- let statement = `${tableAction} TABLE "${table}"`;
378
+ const schemaInstruction = instructionList?.schema;
379
+ const schemaSlug = schemaInstruction?.slug?.being || schemaInstruction?.slug;
380
+ if (kind !== "schemas" && !schemaSlug) {
381
+ throw new RoninError({
382
+ message: `When ${queryTypeReadable} ${kind}, a \`schema.slug\` field must be provided in the \`${instructionName}\` instruction.`,
383
+ code: "MISSING_FIELD",
384
+ fields: ["schema.slug"]
385
+ });
386
+ }
387
+ const tableName = convertToSnakeCase(pluralize(kind === "schemas" ? slug : schemaSlug));
388
+ if (kind === "indexes") {
389
+ const indexName = convertToSnakeCase(slug);
390
+ const unique = instructionList?.unique;
391
+ let statement2 = `${tableAction}${unique ? " UNIQUE" : ""} INDEX "${indexName}"`;
392
+ if (queryType === "create") statement2 += ` ON "${tableName}"`;
393
+ writeStatements.push(statement2);
394
+ return;
395
+ }
396
+ let statement = `${tableAction} TABLE "${tableName}"`;
353
397
  if (kind === "schemas") {
398
+ const fields = [...SYSTEM_FIELDS];
354
399
  if (queryType === "create") {
355
400
  const columns = fields.map(getFieldStatement).filter(Boolean);
356
401
  statement += ` (${columns.join(", ")})`;
357
402
  } else if (queryType === "set") {
358
- const newSlug = queryInstructions.to?.pluralSlug;
403
+ const newSlug = queryInstructions.to?.slug;
359
404
  if (newSlug) {
360
- const newTable = convertToSnakeCase(newSlug);
405
+ const newTable = convertToSnakeCase(pluralize(newSlug));
361
406
  statement += ` RENAME TO "${newTable}"`;
362
407
  }
363
408
  }
364
- } else if (kind === "fields") {
365
- const fieldSlug = instructionTarget?.slug?.being || instructionList?.slug;
366
- if (!fieldSlug) {
367
- throw new RoninError({
368
- message: `When ${queryTypeReadable} fields, a \`slug\` field must be provided in the \`${instructionName}\` instruction.`,
369
- code: "MISSING_FIELD",
370
- fields: ["slug"]
371
- });
372
- }
409
+ writeStatements.push(statement);
410
+ return;
411
+ }
412
+ if (kind === "fields") {
373
413
  if (queryType === "create") {
374
414
  if (!instructionList.type) {
375
415
  throw new RoninError({
@@ -382,13 +422,29 @@ var addSchemaQueries = (queryDetails, writeStatements) => {
382
422
  } else if (queryType === "set") {
383
423
  const newSlug = queryInstructions.to?.slug;
384
424
  if (newSlug) {
385
- statement += ` RENAME COLUMN "${fieldSlug}" TO "${newSlug}"`;
425
+ statement += ` RENAME COLUMN "${slug}" TO "${newSlug}"`;
386
426
  }
387
427
  } else if (queryType === "drop") {
388
- statement += ` DROP COLUMN "${fieldSlug}"`;
428
+ statement += ` DROP COLUMN "${slug}"`;
389
429
  }
430
+ writeStatements.push(statement);
431
+ }
432
+ };
433
+ var slugToName = (slug) => {
434
+ const name = slug.replace(/([a-z])([A-Z])/g, "$1 $2");
435
+ return title(name);
436
+ };
437
+ var VOWELS = ["a", "e", "i", "o", "u"];
438
+ var pluralize = (word) => {
439
+ const lastLetter = word.slice(-1).toLowerCase();
440
+ const secondLastLetter = word.slice(-2, -1).toLowerCase();
441
+ if (lastLetter === "y" && !VOWELS.includes(secondLastLetter)) {
442
+ return `${word.slice(0, -1)}ies`;
443
+ }
444
+ if (lastLetter === "s" || word.slice(-2).toLowerCase() === "ch" || word.slice(-2).toLowerCase() === "sh" || word.slice(-2).toLowerCase() === "ex") {
445
+ return `${word}es`;
390
446
  }
391
- writeStatements.push(statement);
447
+ return `${word}s`;
392
448
  };
393
449
 
394
450
  // src/instructions/with.ts
@@ -509,7 +565,7 @@ var composeConditions = (schemas, schema, statementValues, instructionName, valu
509
565
  if (keys.length === 1 && keys[0] === "id") {
510
566
  recordTarget = values[0];
511
567
  } else {
512
- const relatedSchema = getSchemaBySlug(schemas, schemaField.target.pluralSlug);
568
+ const relatedSchema = getSchemaBySlug(schemas, schemaField.target.slug);
513
569
  const subQuery = {
514
570
  get: {
515
571
  [relatedSchema.slug]: {
@@ -673,7 +729,7 @@ var handleFor = (schemas, schema, statementValues, instruction, rootTable) => {
673
729
  const forFilter = schema.for?.[shortcut];
674
730
  if (!forFilter) {
675
731
  throw new RoninError({
676
- message: `The provided \`for\` shortcut "${shortcut}" does not exist in schema "${getSchemaName(schema)}".`,
732
+ message: `The provided \`for\` shortcut "${shortcut}" does not exist in schema "${schema.name}".`,
677
733
  code: "INVALID_FOR_VALUE"
678
734
  });
679
735
  }
@@ -705,7 +761,7 @@ var handleIncluding = (schemas, statementValues, schema, instruction, rootTable)
705
761
  const includingQuery = schema.including?.[shortcut];
706
762
  if (!includingQuery) {
707
763
  throw new RoninError({
708
- message: `The provided \`including\` shortcut "${shortcut}" does not exist in schema "${getSchemaName(schema)}".`,
764
+ message: `The provided \`including\` shortcut "${shortcut}" does not exist in schema "${schema.name}".`,
709
765
  code: "INVALID_INCLUDING_VALUE"
710
766
  });
711
767
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ronin/compiler",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "description": "Compiles RONIN queries to SQL statements.",
6
6
  "publishConfig": {
@@ -28,11 +28,13 @@
28
28
  "author": "ronin",
29
29
  "license": "Apache-2.0",
30
30
  "dependencies": {
31
- "@paralleldrive/cuid2": "2.2.2"
31
+ "@paralleldrive/cuid2": "2.2.2",
32
+ "title": "3.5.3"
32
33
  },
33
34
  "devDependencies": {
34
35
  "@biomejs/biome": "1.9.2",
35
36
  "@types/bun": "1.1.10",
37
+ "@types/title": "3.4.3",
36
38
  "tsup": "8.3.0",
37
39
  "typescript": "5.6.2",
38
40
  "zod": "3.23.8"