@ronin/compiler 0.2.1 → 0.2.2

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