@syncular/typegen 0.5.0 → 0.6.0
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 +75 -33
- package/dist/cli.js +43 -17
- package/dist/emit-queries-dart.js +167 -55
- package/dist/emit-queries-kotlin.js +166 -55
- package/dist/emit-queries-swift.js +182 -57
- package/dist/emit-queries.js +289 -123
- package/dist/fmt.d.ts +2 -2
- package/dist/fmt.js +323 -316
- package/dist/generate.d.ts +1 -1
- package/dist/generate.js +28 -9
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/lsp.d.ts +1 -3
- package/dist/lsp.js +359 -185
- package/dist/manifest.d.ts +1 -3
- package/dist/manifest.js +1 -1
- package/dist/query-ir.js +53 -42
- package/dist/query.d.ts +115 -63
- package/dist/query.js +60 -11
- package/dist/syql-lexer.d.ts +2 -0
- package/dist/syql-lexer.js +9 -2
- package/dist/syql-lowering.d.ts +22 -0
- package/dist/syql-lowering.js +411 -0
- package/dist/syql-semantics.d.ts +76 -0
- package/dist/syql-semantics.js +551 -0
- package/dist/syql-validator.d.ts +35 -0
- package/dist/syql-validator.js +966 -0
- package/package.json +3 -3
- package/src/cli.ts +51 -21
- package/src/emit-queries-dart.ts +195 -69
- package/src/emit-queries-kotlin.ts +197 -69
- package/src/emit-queries-swift.ts +224 -68
- package/src/emit-queries.ts +413 -165
- package/src/fmt.ts +377 -303
- package/src/generate.ts +43 -9
- package/src/index.ts +3 -1
- package/src/lsp.ts +425 -215
- package/src/manifest.ts +2 -4
- package/src/query-ir.ts +52 -42
- package/src/query.ts +199 -79
- package/src/syql-lexer.ts +12 -1
- package/src/syql-lowering.ts +638 -0
- package/src/syql-semantics.ts +859 -0
- package/src/syql-validator.ts +1492 -0
- package/dist/syql.d.ts +0 -102
- package/dist/syql.js +0 -1141
- package/src/syql.ts +0 -1470
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncular/typegen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Syncular schema-to-TypeScript type generator and the `syncular` CLI",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Benjamin Kniffler",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"!dist/**/*.test.d.ts"
|
|
49
49
|
],
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@syncular/core": "0.
|
|
52
|
-
"@syncular/server": "0.
|
|
51
|
+
"@syncular/core": "0.6.0",
|
|
52
|
+
"@syncular/server": "0.6.0"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/src/cli.ts
CHANGED
|
@@ -117,33 +117,63 @@ function runGeneratePrint(args: GenerateArgs, name: string): void {
|
|
|
117
117
|
}
|
|
118
118
|
console.log(`-- ${query.name} (${query.file})`);
|
|
119
119
|
console.log(`-- tables: ${query.tables.join(', ')}`);
|
|
120
|
-
if (query.
|
|
121
|
-
console.log('--
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
120
|
+
if (query.syql !== undefined) {
|
|
121
|
+
console.log('-- revision: SYQL 1');
|
|
122
|
+
console.log(`-- backend: ${query.syql.plan.backend}`);
|
|
123
|
+
if (query.syql.inputs.length > 0) {
|
|
124
|
+
console.log('-- public inputs:');
|
|
125
|
+
for (const input of query.syql.inputs) {
|
|
126
|
+
if (input.kind === 'value') {
|
|
127
|
+
console.log(
|
|
128
|
+
`-- ${input.name}: ${input.type}${input.nullable ? ' | null' : ''} (${input.required ? 'required' : 'optional'})`,
|
|
129
|
+
);
|
|
130
|
+
} else if (input.kind === 'group') {
|
|
131
|
+
console.log(
|
|
132
|
+
`-- ${input.name}: optional group (${input.members.map((member) => `${member.name}: ${member.type}${member.nullable ? ' | null' : ''}`).join(', ')})`,
|
|
133
|
+
);
|
|
134
|
+
} else if (input.kind === 'switch') {
|
|
135
|
+
console.log(`-- ${input.name}: switch (default false)`);
|
|
136
|
+
} else if (input.kind === 'sort') {
|
|
137
|
+
console.log(
|
|
138
|
+
`-- ${input.name}: sort [${input.profiles.map((profile) => profile.name).join(', ')}] (default ${input.defaultProfile})`,
|
|
139
|
+
);
|
|
140
|
+
} else {
|
|
141
|
+
console.log(
|
|
142
|
+
`-- ${input.name}: page 1..${input.maxSize} (default ${input.defaultSize})`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
129
146
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
console.log(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
147
|
+
if (query.syql.identity !== undefined) {
|
|
148
|
+
console.log(`-- identity: ${query.syql.identity.join(', ')}`);
|
|
149
|
+
}
|
|
150
|
+
console.log(
|
|
151
|
+
`-- checked statements: ${query.syql.plan.statements.length} (activation controls: ${query.syql.plan.activationControls.join(', ') || 'none'})`,
|
|
152
|
+
);
|
|
153
|
+
for (const statement of query.syql.plan.statements) {
|
|
154
|
+
const selectors = [
|
|
155
|
+
...(statement.activationMask === undefined
|
|
156
|
+
? []
|
|
157
|
+
: [`mask=${statement.activationMask}`]),
|
|
158
|
+
...(statement.sortProfile === undefined
|
|
159
|
+
? []
|
|
160
|
+
: [`sort=${statement.sortProfile}`]),
|
|
161
|
+
];
|
|
162
|
+
console.log(`-- [${selectors.join(', ') || 'default'}]`);
|
|
163
|
+
console.log(statement.sql);
|
|
137
164
|
console.log(
|
|
138
|
-
`--
|
|
165
|
+
`-- binds: ${statement.binds.map((bind) => `:${bind.name}`).join(', ') || '(none)'}`,
|
|
139
166
|
);
|
|
140
167
|
}
|
|
168
|
+
return;
|
|
141
169
|
}
|
|
142
|
-
if (query.
|
|
143
|
-
console.log(
|
|
144
|
-
|
|
145
|
-
|
|
170
|
+
if (query.params.length > 0) {
|
|
171
|
+
console.log('-- params:');
|
|
172
|
+
for (const param of query.params) {
|
|
173
|
+
console.log(`-- :${param.name} ${param.type} (required)`);
|
|
174
|
+
}
|
|
146
175
|
}
|
|
176
|
+
console.log(query.sql);
|
|
147
177
|
}
|
|
148
178
|
|
|
149
179
|
function parseManifestDir(argv: readonly string[]): string {
|
package/src/emit-queries-dart.ts
CHANGED
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { IrColumnType } from './ir';
|
|
11
11
|
import { snakeToCamel } from './naming';
|
|
12
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
AnalyzedQuery,
|
|
14
|
+
QueryColumn,
|
|
15
|
+
QuerySyqlPlanBind,
|
|
16
|
+
QuerySyqlPublicInput,
|
|
17
|
+
} from './query';
|
|
13
18
|
|
|
14
19
|
const DART_TYPE: Readonly<Record<IrColumnType, string>> = {
|
|
15
20
|
string: 'String',
|
|
@@ -85,31 +90,174 @@ function optionalParamValue(type: IrColumnType, name: string): string {
|
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
92
|
|
|
88
|
-
function
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
function syqlInput(query: AnalyzedQuery, name: string): QuerySyqlPublicInput {
|
|
94
|
+
const input = query.syql?.inputs.find((candidate) => candidate.name === name);
|
|
95
|
+
if (input === undefined) throw new Error(`unknown SYQL input ${name}`);
|
|
96
|
+
return input;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function syqlDartType(type: IrColumnType, nullable: boolean): string {
|
|
100
|
+
return `${DART_TYPE[type]}${nullable ? '?' : ''}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function syqlControlActive(query: AnalyzedQuery, name: string): string {
|
|
104
|
+
const input = syqlInput(query, name);
|
|
105
|
+
const access = camelCase(input.langName);
|
|
106
|
+
if (input.kind === 'switch') return access;
|
|
107
|
+
if (input.kind === 'value' && input.nullable) return `${access}.isPresent`;
|
|
108
|
+
if (input.kind === 'value' || input.kind === 'group') {
|
|
109
|
+
return `${access} != null`;
|
|
110
|
+
}
|
|
111
|
+
throw new Error(`${name} is not an activation control`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function syqlBindExpr(query: AnalyzedQuery, bind: QuerySyqlPlanBind): string {
|
|
115
|
+
if (bind.kind === 'condition-active') {
|
|
116
|
+
return bind.controls
|
|
117
|
+
.map((control) => syqlControlActive(query, control))
|
|
118
|
+
.join(' && ');
|
|
119
|
+
}
|
|
120
|
+
const input = syqlInput(query, bind.input);
|
|
121
|
+
const access = camelCase(input.langName);
|
|
122
|
+
if (bind.kind === 'page') return `effective${typeName(access)}`;
|
|
123
|
+
if (bind.kind === 'group-member') {
|
|
124
|
+
if (input.kind !== 'group') throw new Error('group bind/input mismatch');
|
|
125
|
+
const member = input.members.find(
|
|
126
|
+
(candidate) => candidate.name === bind.member,
|
|
127
|
+
);
|
|
128
|
+
if (member === undefined)
|
|
129
|
+
throw new Error(`unknown group member ${bind.member}`);
|
|
130
|
+
const memberAccess = `${access}.${camelCase(member.langName)}`;
|
|
131
|
+
const value = member.nullable
|
|
132
|
+
? optionalParamValue(member.type, memberAccess)
|
|
133
|
+
: paramValue(member.type, memberAccess);
|
|
134
|
+
return `${access} == null ? null : ${value}`;
|
|
135
|
+
}
|
|
136
|
+
if (input.kind !== 'value') throw new Error('value bind/input mismatch');
|
|
137
|
+
if (input.required) {
|
|
138
|
+
return input.nullable
|
|
139
|
+
? optionalParamValue(input.type, access)
|
|
140
|
+
: paramValue(input.type, access);
|
|
141
|
+
}
|
|
142
|
+
return input.nullable
|
|
143
|
+
? `${access}.isPresent ? ${optionalParamValue(input.type, `${access}.value`)} : null`
|
|
144
|
+
: optionalParamValue(input.type, access);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function emitSyqlDartTypes(query: AnalyzedQuery): string[] {
|
|
148
|
+
const lines: string[] = [];
|
|
149
|
+
for (const input of query.syql?.inputs ?? []) {
|
|
150
|
+
if (input.kind === 'group') {
|
|
151
|
+
const name = `${typeName(query.name)}${typeName(input.langName)}`;
|
|
152
|
+
lines.push(`class ${name} {`);
|
|
153
|
+
for (const member of input.members) {
|
|
154
|
+
lines.push(
|
|
155
|
+
` final ${syqlDartType(member.type, member.nullable)} ${camelCase(member.langName)};`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
const args = input.members
|
|
159
|
+
.map((member) => `required this.${camelCase(member.langName)}`)
|
|
160
|
+
.join(', ');
|
|
161
|
+
lines.push(` const ${name}({${args}});`, '}', '');
|
|
162
|
+
} else if (input.kind === 'sort') {
|
|
163
|
+
const name = `${typeName(query.name)}${typeName(input.langName)}`;
|
|
164
|
+
lines.push(
|
|
165
|
+
`enum ${name} { ${input.profiles.map((profile) => camelCase(profile.langName)).join(', ')} }`,
|
|
166
|
+
'',
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (lines[lines.length - 1] === '') lines.pop();
|
|
171
|
+
return lines;
|
|
94
172
|
}
|
|
95
173
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (
|
|
174
|
+
function emitSyqlDartRunner(query: AnalyzedQuery): string[] {
|
|
175
|
+
const metadata = query.syql;
|
|
176
|
+
if (metadata === undefined) throw new Error('missing SYQL metadata');
|
|
177
|
+
const Row = `${typeName(query.name)}Row`;
|
|
178
|
+
const Pascal = pascalCase(query.name);
|
|
99
179
|
const lines: string[] = [];
|
|
100
180
|
lines.push(
|
|
101
|
-
`///
|
|
181
|
+
`/// Tables the ${query.name} query reads (exact invalidation set).`,
|
|
182
|
+
`const List<String> syncular${Pascal}QueryTables = [${query.tables.map(quote).join(', ')}];`,
|
|
183
|
+
'',
|
|
184
|
+
`/// Run the ${query.name} revision-1 SYQL query.`,
|
|
102
185
|
);
|
|
103
|
-
|
|
186
|
+
const args: string[] = [];
|
|
187
|
+
for (const input of metadata.inputs) {
|
|
188
|
+
const name = camelCase(input.langName);
|
|
189
|
+
if (input.kind === 'value') {
|
|
190
|
+
const type = syqlDartType(input.type, input.nullable);
|
|
191
|
+
if (input.required) args.push(`required ${type} ${name}`);
|
|
192
|
+
else if (input.nullable) {
|
|
193
|
+
args.push(
|
|
194
|
+
`SyqlQueryPresence<${type}> ${name} = const SyqlQueryPresence.absent()`,
|
|
195
|
+
);
|
|
196
|
+
} else args.push(`${type}? ${name}`);
|
|
197
|
+
} else if (input.kind === 'group') {
|
|
198
|
+
args.push(`${typeName(query.name)}${typeName(input.langName)}? ${name}`);
|
|
199
|
+
} else if (input.kind === 'switch') {
|
|
200
|
+
args.push(`bool ${name} = false`);
|
|
201
|
+
} else if (input.kind === 'sort') {
|
|
202
|
+
const defaultCase =
|
|
203
|
+
input.profiles.find((profile) => profile.name === input.defaultProfile)
|
|
204
|
+
?.langName ?? input.defaultProfile;
|
|
205
|
+
const type = `${typeName(query.name)}${typeName(input.langName)}`;
|
|
206
|
+
args.push(`${type} ${name} = ${type}.${camelCase(defaultCase)}`);
|
|
207
|
+
} else {
|
|
208
|
+
args.push(`int? ${name}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const argsClause = args.length > 0 ? `, {${args.join(', ')}}` : '';
|
|
104
212
|
lines.push(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
213
|
+
`List<${Row}> syncular${Pascal}Query(SyncularClient client${argsClause}) {`,
|
|
214
|
+
);
|
|
215
|
+
const page = metadata.inputs.find((input) => input.kind === 'page');
|
|
216
|
+
if (page?.kind === 'page') {
|
|
217
|
+
const name = camelCase(page.langName);
|
|
218
|
+
lines.push(
|
|
219
|
+
` final effective${typeName(name)} = ${name} ?? ${page.defaultSize};`,
|
|
220
|
+
` if (effective${typeName(name)} < 1 || effective${typeName(name)} > ${page.maxSize}) {`,
|
|
221
|
+
` throw SyqlQueryInputException('SYQL_RUNTIME_INVALID_PAGE', ${quote(`${query.name}: invalid page size`)});`,
|
|
222
|
+
' }',
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (metadata.plan.backend === 'variants') {
|
|
226
|
+
lines.push(' var activationMask = 0;');
|
|
227
|
+
metadata.plan.activationControls.forEach((control, index) => {
|
|
228
|
+
lines.push(
|
|
229
|
+
` if (${syqlControlActive(query, control)}) activationMask |= ${2 ** index};`,
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
const sort = metadata.inputs.find((input) => input.kind === 'sort');
|
|
234
|
+
const profileCount = sort?.kind === 'sort' ? sort.profiles.length : 1;
|
|
235
|
+
const sortIndex =
|
|
236
|
+
sort?.kind === 'sort' ? `${camelCase(sort.langName)}.index` : '0';
|
|
237
|
+
const index =
|
|
238
|
+
metadata.plan.backend === 'variants'
|
|
239
|
+
? `activationMask * ${profileCount} + ${sortIndex}`
|
|
240
|
+
: sortIndex;
|
|
241
|
+
lines.push(
|
|
242
|
+
` final statementIndex = ${index};`,
|
|
243
|
+
' late final String sql;',
|
|
244
|
+
' late final List<Object?> params;',
|
|
245
|
+
' switch (statementIndex) {',
|
|
246
|
+
);
|
|
247
|
+
metadata.plan.statements.forEach((statement, statementIndex) => {
|
|
248
|
+
const binds = statement.binds
|
|
249
|
+
.map((bind) => syqlBindExpr(query, bind))
|
|
250
|
+
.join(', ');
|
|
251
|
+
lines.push(
|
|
252
|
+
` case ${statementIndex}: sql = ${quote(statement.positionalSql)}; params = <Object?>[${binds}]; break;`,
|
|
253
|
+
);
|
|
254
|
+
});
|
|
255
|
+
lines.push(
|
|
256
|
+
" default: throw StateError('invalid generated SYQL statement index');",
|
|
257
|
+
' }',
|
|
258
|
+
` return client.query(sql, params: params).map(${Row}.fromRow).whereType<${Row}>().toList();`,
|
|
259
|
+
'}',
|
|
108
260
|
);
|
|
109
|
-
lines.push('');
|
|
110
|
-
lines.push(` const ${typeName(query.name)}OrderBy(this.column);`);
|
|
111
|
-
lines.push(' final String column;');
|
|
112
|
-
lines.push('}');
|
|
113
261
|
return lines;
|
|
114
262
|
}
|
|
115
263
|
|
|
@@ -151,6 +299,7 @@ function emitClass(query: AnalyzedQuery): string[] {
|
|
|
151
299
|
}
|
|
152
300
|
|
|
153
301
|
function emitRunner(query: AnalyzedQuery): string[] {
|
|
302
|
+
if (query.syql !== undefined) return emitSyqlDartRunner(query);
|
|
154
303
|
const Row = `${typeName(query.name)}Row`;
|
|
155
304
|
const Pascal = pascalCase(query.name);
|
|
156
305
|
const lines: string[] = [];
|
|
@@ -161,59 +310,24 @@ function emitRunner(query: AnalyzedQuery): string[] {
|
|
|
161
310
|
`const List<String> syncular${Pascal}QueryTables = [${query.tables.map(quote).join(', ')}];`,
|
|
162
311
|
);
|
|
163
312
|
lines.push('');
|
|
164
|
-
|
|
165
|
-
lines.push(
|
|
166
|
-
`const String _${query.name}SqlBase = ${quote(query.positionalSqlBase ?? '')};`,
|
|
167
|
-
);
|
|
168
|
-
} else {
|
|
169
|
-
lines.push(
|
|
170
|
-
`const String _${query.name}Sql = ${quote(query.positionalSql)};`,
|
|
171
|
-
);
|
|
172
|
-
}
|
|
313
|
+
lines.push(`const String _${query.name}Sql = ${quote(query.positionalSql)};`);
|
|
173
314
|
lines.push('');
|
|
174
315
|
lines.push(`/// Run the ${query.name} named query (SELECT-only).`);
|
|
175
316
|
const args: string[] = [];
|
|
176
317
|
for (const p of query.params) {
|
|
177
318
|
const name = camelCase(p.langName);
|
|
178
|
-
|
|
179
|
-
args.push(`${DART_TYPE[p.type]}? ${name}`);
|
|
180
|
-
} else {
|
|
181
|
-
args.push(`required ${DART_TYPE[p.type]} ${name}`);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
if (query.orderBy !== undefined) {
|
|
185
|
-
const defaultCase = camelCase(
|
|
186
|
-
query.orderBy.allowed.find((c) => c.name === query.orderBy?.defaultColumn)
|
|
187
|
-
?.langName ?? query.orderBy.defaultColumn,
|
|
188
|
-
);
|
|
189
|
-
args.push(
|
|
190
|
-
`${typeName(query.name)}OrderBy orderBy = ${typeName(query.name)}OrderBy.${defaultCase}`,
|
|
191
|
-
);
|
|
192
|
-
args.push(
|
|
193
|
-
`SyncularQueryDir dir = SyncularQueryDir.${query.orderBy.defaultDir}`,
|
|
194
|
-
);
|
|
319
|
+
args.push(`required ${DART_TYPE[p.type]} ${name}`);
|
|
195
320
|
}
|
|
196
321
|
const argsClause = args.length > 0 ? `, {${args.join(', ')}}` : '';
|
|
197
322
|
lines.push(
|
|
198
323
|
`List<${Row}> syncular${Pascal}Query(SyncularClient client${argsClause}) {`,
|
|
199
324
|
);
|
|
200
|
-
|
|
201
|
-
const limitTail =
|
|
202
|
-
query.positionalLimitTail !== undefined
|
|
203
|
-
? ` ${quote(query.positionalLimitTail.trim())}`
|
|
204
|
-
: '';
|
|
205
|
-
lines.push(
|
|
206
|
-
` final sql = '$_${query.name}SqlBase order by \${orderBy.column} \${dir.name}'${limitTail === '' ? '' : `\n ' ' ${limitTail.trim()}`};`,
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
const sqlRef = query.orderBy !== undefined ? 'sql' : `_${query.name}Sql`;
|
|
325
|
+
const sqlRef = `_${query.name}Sql`;
|
|
210
326
|
if (query.params.length > 0) {
|
|
211
327
|
const binds = query.params
|
|
212
328
|
.map((p) => {
|
|
213
329
|
const name = camelCase(p.langName);
|
|
214
|
-
return
|
|
215
|
-
? optionalParamValue(p.type, name)
|
|
216
|
-
: paramValue(p.type, name);
|
|
330
|
+
return paramValue(p.type, name);
|
|
217
331
|
})
|
|
218
332
|
.join(', ');
|
|
219
333
|
lines.push(` final params = <Object?>[${binds}];`);
|
|
@@ -246,6 +360,27 @@ export function emitQueriesDartModule(
|
|
|
246
360
|
].join('\n'),
|
|
247
361
|
);
|
|
248
362
|
|
|
363
|
+
if (queries.some((query) => query.syql !== undefined)) {
|
|
364
|
+
parts.push(
|
|
365
|
+
[
|
|
366
|
+
'class SyqlQueryPresence<T> {',
|
|
367
|
+
' final bool isPresent;',
|
|
368
|
+
' final T? value;',
|
|
369
|
+
' const SyqlQueryPresence.absent() : isPresent = false, value = null;',
|
|
370
|
+
' const SyqlQueryPresence.present(this.value) : isPresent = true;',
|
|
371
|
+
'}',
|
|
372
|
+
'',
|
|
373
|
+
'class SyqlQueryInputException implements Exception {',
|
|
374
|
+
' final String code;',
|
|
375
|
+
' final String message;',
|
|
376
|
+
' const SyqlQueryInputException(this.code, this.message);',
|
|
377
|
+
' @override',
|
|
378
|
+
" String toString() => '$code: $message';",
|
|
379
|
+
'}',
|
|
380
|
+
].join('\n'),
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
249
384
|
parts.push(
|
|
250
385
|
[
|
|
251
386
|
'/// Lift a SQLite boolean: a real bool, or 0/1 as a number.',
|
|
@@ -274,19 +409,10 @@ export function emitQueriesDartModule(
|
|
|
274
409
|
].join('\n'),
|
|
275
410
|
);
|
|
276
411
|
|
|
277
|
-
if (queries.some((q) => q.orderBy !== undefined)) {
|
|
278
|
-
parts.push(
|
|
279
|
-
[
|
|
280
|
-
'/// §6 orderBy direction (shared by every orderBy-knob query).',
|
|
281
|
-
'enum SyncularQueryDir { asc, desc }',
|
|
282
|
-
].join('\n'),
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
412
|
for (const query of queries) {
|
|
413
|
+
const syqlTypes = emitSyqlDartTypes(query);
|
|
414
|
+
if (syqlTypes.length > 0) parts.push(syqlTypes.join('\n'));
|
|
287
415
|
parts.push(emitClass(query).join('\n'));
|
|
288
|
-
const orderByEnum = emitOrderByEnum(query);
|
|
289
|
-
if (orderByEnum.length > 0) parts.push(orderByEnum.join('\n'));
|
|
290
416
|
}
|
|
291
417
|
for (const query of queries) {
|
|
292
418
|
parts.push(emitRunner(query).join('\n'));
|