@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 +0 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +99 -43
- package/package.json +4 -2
package/README.md
CHANGED
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>, '
|
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
|
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 "${
|
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
|
-
{
|
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: {
|
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(
|
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.
|
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.
|
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.
|
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 (!
|
347
|
+
if (!SYSTEM_SCHEMA_SLUGS.includes(querySchema)) return;
|
315
348
|
const instructionName = mappedInstructions[queryType];
|
316
349
|
const instructionList = queryInstructions[instructionName];
|
317
|
-
const kind =
|
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
|
-
|
343
|
-
|
370
|
+
const slug = instructionList?.slug?.being || instructionList?.slug;
|
371
|
+
if (!slug) {
|
344
372
|
throw new RoninError({
|
345
|
-
message: `When ${queryTypeReadable} ${kind}, a
|
373
|
+
message: `When ${queryTypeReadable} ${kind}, a \`slug\` field must be provided in the \`${instructionName}\` instruction.`,
|
346
374
|
code: "MISSING_FIELD",
|
347
|
-
fields: [
|
375
|
+
fields: ["slug"]
|
348
376
|
});
|
349
377
|
}
|
350
|
-
const
|
351
|
-
const
|
352
|
-
|
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?.
|
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
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
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 "${
|
425
|
+
statement += ` RENAME COLUMN "${slug}" TO "${newSlug}"`;
|
386
426
|
}
|
387
427
|
} else if (queryType === "drop") {
|
388
|
-
statement += ` DROP COLUMN "${
|
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
|
-
|
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.
|
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 "${
|
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 "${
|
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.
|
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"
|