@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 +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"
|