@ronin/compiler 0.10.2 → 0.10.3-leo-ron-1083-experimental-215
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +38 -2
- package/dist/index.d.ts +16 -5
- package/dist/index.js +69 -41
- package/package.json +1 -1
package/README.md
CHANGED
@@ -70,6 +70,24 @@ executed and their results can be formatted by the compiler as well:
|
|
70
70
|
const results: Array<Result> = transaction.prepareResults(rows);
|
71
71
|
```
|
72
72
|
|
73
|
+
#### Root Model
|
74
|
+
|
75
|
+
Before you can run any statements generated by the compiler that are altering the database schema, you need to create the table of the so-called "root model", which is used to store metadata for all other models.
|
76
|
+
|
77
|
+
This table is called `ronin_schema`, which mimics the default `sqlite_schema` table provided by SQLite. You can generate its respective SQL statements like so:
|
78
|
+
|
79
|
+
```typescript
|
80
|
+
import { Transaction, ROOT_MODEL } from '@ronin/compiler';
|
81
|
+
|
82
|
+
const transaction = new Transaction([
|
83
|
+
{
|
84
|
+
create: { model: ROOT_MODEL }
|
85
|
+
}
|
86
|
+
]);
|
87
|
+
```
|
88
|
+
|
89
|
+
Afterward, run the statements located in `transaction.statements` to create the table for the root model. Once that is done, your database is prepared to run any statements generated by the compiler.
|
90
|
+
|
73
91
|
#### Types
|
74
92
|
|
75
93
|
In total, the following types are being exported:
|
@@ -103,7 +121,25 @@ new Transaction(queries, {
|
|
103
121
|
// Instead of returning an array of parameters for every statement (which allows for
|
104
122
|
// preventing SQL injections), all parameters are inlined directly into the SQL strings.
|
105
123
|
// This option should only be used if the generated SQL will be manually verified.
|
106
|
-
inlineParams: true
|
124
|
+
inlineParams: true,
|
125
|
+
|
126
|
+
// By default, in the generated SQL statements, the compiler does not alias columns if
|
127
|
+
// multiple different tables with the same column names are being joined. Only the table
|
128
|
+
// names themselves are aliased.
|
129
|
+
//
|
130
|
+
// This ensures the cleanest possible SQL statements in conjunction with the default
|
131
|
+
// behavior of SQL databases, where the result of a statement is a list (array) of
|
132
|
+
// values, which are inherently not prone to conflicts.
|
133
|
+
//
|
134
|
+
// If the driver being used instead returns an object for every row, the driver must
|
135
|
+
// ensure the uniqueness of every key in that object, which means prefixing duplicated
|
136
|
+
// column names with the name of the respective table, if multiple tables are joined.
|
137
|
+
//
|
138
|
+
// Drivers that return objects for rows offer this behavior as an option that is
|
139
|
+
// usually called "expand columns". If the driver being used does not offer such an
|
140
|
+
// option, you can instead activate the option in the compiler, which results in longer
|
141
|
+
// SQL statements because any duplicated column name is aliased.
|
142
|
+
expandColumns: true
|
107
143
|
});
|
108
144
|
```
|
109
145
|
|
@@ -117,7 +153,7 @@ bun run dev
|
|
117
153
|
|
118
154
|
Whenever you make a change to the source code, it will then automatically be transpiled again.
|
119
155
|
|
120
|
-
###
|
156
|
+
### Architecture
|
121
157
|
|
122
158
|
The interface of creating new `Transaction` instances (thereby creating new transactions) was chosen in order to define the smallest workload unit that the compiler can operate on.
|
123
159
|
|
package/dist/index.d.ts
CHANGED
@@ -5979,7 +5979,7 @@ interface Model<T extends Array<ModelField> = Array<ModelField>> {
|
|
5979
5979
|
triggers?: Array<ModelTrigger<T>>;
|
5980
5980
|
presets?: Array<ModelPreset>;
|
5981
5981
|
}
|
5982
|
-
type PublicModel<T extends Array<ModelField> = Array<ModelField>> = Omit<Partial<Model<T>>, 'slug' | 'identifiers' | 'system' | '
|
5982
|
+
type PublicModel<T extends Array<ModelField> = Array<ModelField>> = Omit<Partial<Model<T>>, 'slug' | 'identifiers' | 'system' | 'tableAlias'> & {
|
5983
5983
|
slug: Required<Model['slug']>;
|
5984
5984
|
identifiers?: Partial<Model['identifiers']>;
|
5985
5985
|
};
|
@@ -6005,13 +6005,22 @@ type AmountResult = {
|
|
6005
6005
|
};
|
6006
6006
|
type Result = SingleRecordResult | MultipleRecordResult | AmountResult;
|
6007
6007
|
|
6008
|
+
interface TransactionOptions {
|
6009
|
+
/** A list of models that already exist in the database. */
|
6010
|
+
models?: Array<PublicModel>;
|
6011
|
+
/**
|
6012
|
+
* Place statement parameters directly inside the statement strings instead of
|
6013
|
+
* separating them out into a dedicated `params` array.
|
6014
|
+
*/
|
6015
|
+
inlineParams?: boolean;
|
6016
|
+
/** Alias column names that are duplicated when joining multiple tables. */
|
6017
|
+
expandColumns?: boolean;
|
6018
|
+
}
|
6008
6019
|
declare class Transaction {
|
6009
6020
|
statements: Array<Statement>;
|
6010
6021
|
models: Array<Model>;
|
6011
6022
|
private queries;
|
6012
|
-
constructor(queries: Array<Query>, options?:
|
6013
|
-
models?: Array<PublicModel>;
|
6014
|
-
});
|
6023
|
+
constructor(queries: Array<Query>, options?: TransactionOptions);
|
6015
6024
|
/**
|
6016
6025
|
* Composes SQL statements for the provided RONIN queries.
|
6017
6026
|
*
|
@@ -6026,4 +6035,6 @@ declare class Transaction {
|
|
6026
6035
|
prepareResults(results: Array<Array<Row>>): Array<Result>;
|
6027
6036
|
}
|
6028
6037
|
|
6029
|
-
|
6038
|
+
declare const CLEAN_ROOT_MODEL: PublicModel;
|
6039
|
+
|
6040
|
+
export { type PublicModel as Model, type ModelField, type ModelIndex, type ModelPreset, type ModelTrigger, type Query, CLEAN_ROOT_MODEL as ROOT_MODEL, type Result, type Statement, Transaction };
|
package/dist/index.js
CHANGED
@@ -65,6 +65,23 @@ var convertToCamelCase = (str) => {
|
|
65
65
|
return sanitize(str).split(SPLIT_REGEX).map((part, index) => index === 0 ? part.toLowerCase() : capitalize(part)).join("");
|
66
66
|
};
|
67
67
|
var isObject = (value) => value != null && typeof value === "object" && Array.isArray(value) === false;
|
68
|
+
var getSymbol = (value) => {
|
69
|
+
if (!isObject(value)) return null;
|
70
|
+
const objectValue = value;
|
71
|
+
if (RONIN_MODEL_SYMBOLS.QUERY in objectValue) {
|
72
|
+
return {
|
73
|
+
type: "query",
|
74
|
+
value: objectValue[RONIN_MODEL_SYMBOLS.QUERY]
|
75
|
+
};
|
76
|
+
}
|
77
|
+
if (RONIN_MODEL_SYMBOLS.EXPRESSION in objectValue) {
|
78
|
+
return {
|
79
|
+
type: "expression",
|
80
|
+
value: objectValue[RONIN_MODEL_SYMBOLS.EXPRESSION]
|
81
|
+
};
|
82
|
+
}
|
83
|
+
return null;
|
84
|
+
};
|
68
85
|
var findInObject = (obj, pattern, replacer) => {
|
69
86
|
let found = false;
|
70
87
|
for (const key in obj) {
|
@@ -85,14 +102,18 @@ var findInObject = (obj, pattern, replacer) => {
|
|
85
102
|
var flatten = (obj, prefix = "", res = {}) => {
|
86
103
|
for (const key in obj) {
|
87
104
|
const path = prefix ? `${prefix}.${key}` : key;
|
88
|
-
|
89
|
-
|
105
|
+
const value = obj[key];
|
106
|
+
if (typeof value === "object" && value !== null && !getSymbol(value)) {
|
107
|
+
flatten(value, path, res);
|
90
108
|
} else {
|
91
|
-
res[path] =
|
109
|
+
res[path] = value;
|
92
110
|
}
|
93
111
|
}
|
94
112
|
return res;
|
95
113
|
};
|
114
|
+
var omit = (obj, properties) => Object.fromEntries(
|
115
|
+
Object.entries(obj).filter(([key]) => !properties.includes(key))
|
116
|
+
);
|
96
117
|
var expand = (obj) => {
|
97
118
|
return Object.entries(obj).reduce((res, [key, val]) => {
|
98
119
|
key.split(".").reduce((acc, part, i, arr) => {
|
@@ -275,23 +296,6 @@ var formatIdentifiers = ({ identifiers }, queryInstructions) => {
|
|
275
296
|
[type]: newNestedInstructions
|
276
297
|
};
|
277
298
|
};
|
278
|
-
var getSymbol = (value) => {
|
279
|
-
if (!isObject(value)) return null;
|
280
|
-
const objectValue = value;
|
281
|
-
if (RONIN_MODEL_SYMBOLS.QUERY in objectValue) {
|
282
|
-
return {
|
283
|
-
type: "query",
|
284
|
-
value: objectValue[RONIN_MODEL_SYMBOLS.QUERY]
|
285
|
-
};
|
286
|
-
}
|
287
|
-
if (RONIN_MODEL_SYMBOLS.EXPRESSION in objectValue) {
|
288
|
-
return {
|
289
|
-
type: "expression",
|
290
|
-
value: objectValue[RONIN_MODEL_SYMBOLS.EXPRESSION]
|
291
|
-
};
|
292
|
-
}
|
293
|
-
return null;
|
294
|
-
};
|
295
299
|
|
296
300
|
// src/instructions/with.ts
|
297
301
|
var getMatcher = (value, negative) => {
|
@@ -693,7 +697,7 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
|
|
693
697
|
if (!(modelSlug && slug)) return query;
|
694
698
|
const model = action === "create" && entity === "model" ? null : getModelBySlug(models, modelSlug);
|
695
699
|
if (entity === "model") {
|
696
|
-
let queryTypeDetails;
|
700
|
+
let queryTypeDetails = {};
|
697
701
|
if (action === "create") {
|
698
702
|
const newModel = jsonValue;
|
699
703
|
const modelWithFields = addDefaultModelFields(newModel, true);
|
@@ -749,6 +753,8 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
|
|
749
753
|
return handleSystemModel(models, dependencyStatements, "drop", systemModel);
|
750
754
|
});
|
751
755
|
}
|
756
|
+
const modelSlug2 = "to" in queryTypeDetails ? queryTypeDetails?.to?.slug : "with" in queryTypeDetails ? queryTypeDetails?.with?.slug : void 0;
|
757
|
+
if (modelSlug2 === "model") return null;
|
752
758
|
const queryTypeAction = action === "create" ? "add" : action === "alter" ? "set" : "remove";
|
753
759
|
return {
|
754
760
|
[queryTypeAction]: {
|
@@ -1178,32 +1184,44 @@ var handleOrderedBy = (model, instruction) => {
|
|
1178
1184
|
};
|
1179
1185
|
|
1180
1186
|
// src/instructions/selecting.ts
|
1181
|
-
var handleSelecting = (model, statementParams, instructions) => {
|
1187
|
+
var handleSelecting = (models, model, statementParams, instructions, options) => {
|
1182
1188
|
let isJoining = false;
|
1183
1189
|
let statement = instructions.selecting ? instructions.selecting.map((slug) => {
|
1184
1190
|
return getFieldFromModel(model, slug, "selecting").fieldSelector;
|
1185
1191
|
}).join(", ") : "*";
|
1186
1192
|
if (instructions.including) {
|
1187
|
-
const filteredObject = Object.entries(instructions.including).
|
1193
|
+
const filteredObject = Object.entries(instructions.including).flatMap(([key, value]) => {
|
1188
1194
|
const symbol = getSymbol(value);
|
1189
|
-
if (symbol) {
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1195
|
+
if (symbol?.type === "query") {
|
1196
|
+
isJoining = true;
|
1197
|
+
if (!options?.expandColumns) return null;
|
1198
|
+
const { queryModel: queryModelSlug } = splitQuery(symbol.value);
|
1199
|
+
const queryModel = getModelBySlug(models, queryModelSlug);
|
1200
|
+
const tableName = `including_${key}`;
|
1201
|
+
const duplicatedFields = queryModel.fields.filter((field) => {
|
1202
|
+
if (field.type === "group") return null;
|
1203
|
+
return model.fields.some((modelField) => modelField.slug === field.slug);
|
1204
|
+
}).filter((item) => item !== null);
|
1205
|
+
return duplicatedFields.map((field) => ({
|
1206
|
+
key: `${tableName}.${field.slug}`,
|
1207
|
+
value: {
|
1208
|
+
[RONIN_MODEL_SYMBOLS.EXPRESSION]: `${RONIN_MODEL_SYMBOLS.FIELD}${field.slug}`
|
1209
|
+
}
|
1210
|
+
}));
|
1197
1211
|
}
|
1198
|
-
return
|
1199
|
-
}).filter((entry) => entry !== null);
|
1212
|
+
return { key, value };
|
1213
|
+
}).filter((entry) => entry !== null).map((entry) => [entry.key, entry.value]);
|
1200
1214
|
const newObjectEntries = Object.entries(flatten(Object.fromEntries(filteredObject)));
|
1201
1215
|
if (newObjectEntries.length > 0) {
|
1202
1216
|
statement += ", ";
|
1203
1217
|
statement += newObjectEntries.map(([key, value]) => {
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1218
|
+
const symbol = getSymbol(value);
|
1219
|
+
if (symbol?.type === "expression") {
|
1220
|
+
value = `(${parseFieldExpression(model, "including", symbol.value)})`;
|
1221
|
+
} else {
|
1222
|
+
value = prepareStatementValue(statementParams, value);
|
1223
|
+
}
|
1224
|
+
return `${value} as "${key}"`;
|
1207
1225
|
}).join(", ");
|
1208
1226
|
}
|
1209
1227
|
}
|
@@ -1340,6 +1358,7 @@ var compileQueryInput = (defaultQuery, models, statementParams, options) => {
|
|
1340
1358
|
statementParams,
|
1341
1359
|
defaultQuery
|
1342
1360
|
);
|
1361
|
+
if (query === null) return { dependencies: [], main: dependencyStatements[0] };
|
1343
1362
|
const parsedQuery = splitQuery(query);
|
1344
1363
|
const { queryType, queryModel, queryInstructions } = parsedQuery;
|
1345
1364
|
const model = getModelBySlug(models, queryModel);
|
@@ -1349,10 +1368,16 @@ var compileQueryInput = (defaultQuery, models, statementParams, options) => {
|
|
1349
1368
|
if (instructions && Object.hasOwn(instructions, "for")) {
|
1350
1369
|
instructions = handleFor(model, instructions);
|
1351
1370
|
}
|
1352
|
-
const { columns, isJoining } = handleSelecting(
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1371
|
+
const { columns, isJoining } = handleSelecting(
|
1372
|
+
models,
|
1373
|
+
model,
|
1374
|
+
statementParams,
|
1375
|
+
{
|
1376
|
+
selecting: instructions?.selecting,
|
1377
|
+
including: instructions?.including
|
1378
|
+
},
|
1379
|
+
options
|
1380
|
+
);
|
1356
1381
|
let statement = "";
|
1357
1382
|
switch (queryType) {
|
1358
1383
|
case "get":
|
@@ -1512,7 +1537,8 @@ var Transaction = class {
|
|
1512
1537
|
const result = compileQueryInput(
|
1513
1538
|
query,
|
1514
1539
|
modelListWithPresets,
|
1515
|
-
options?.inlineParams ? null : []
|
1540
|
+
options?.inlineParams ? null : [],
|
1541
|
+
{ expandColumns: options?.expandColumns }
|
1516
1542
|
);
|
1517
1543
|
dependencyStatements.push(...result.dependencies);
|
1518
1544
|
mainStatements.push(result.main);
|
@@ -1577,6 +1603,8 @@ var Transaction = class {
|
|
1577
1603
|
});
|
1578
1604
|
}
|
1579
1605
|
};
|
1606
|
+
var CLEAN_ROOT_MODEL = omit(ROOT_MODEL, ["system"]);
|
1580
1607
|
export {
|
1608
|
+
CLEAN_ROOT_MODEL as ROOT_MODEL,
|
1581
1609
|
Transaction
|
1582
1610
|
};
|