allez-orm 1.0.11 → 1.0.13
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/package.json +4 -4
- package/tools/allez-orm.mjs +274 -125
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "allez-orm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "AllezORM: lightweight browser SQLite ORM (sql.js) + schema generator CLI",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"allez-orm": "tools/allez-orm.mjs"
|
|
8
|
+
},
|
|
6
9
|
"main": "./allez-orm.mjs",
|
|
7
10
|
"module": "./allez-orm.mjs",
|
|
8
11
|
"browser": "./allez-orm.mjs",
|
|
@@ -32,9 +35,6 @@
|
|
|
32
35
|
"ddl:audit": "node tools/ddl-audit.mjs",
|
|
33
36
|
"prepublishOnly": "node tests/test-cli.mjs && node tools/ddl-audit.mjs"
|
|
34
37
|
},
|
|
35
|
-
"bin": {
|
|
36
|
-
"allez-orm": "tools/allez-orm.mjs"
|
|
37
|
-
},
|
|
38
38
|
"files": [
|
|
39
39
|
"allez-orm.mjs",
|
|
40
40
|
"index.d.ts",
|
package/tools/allez-orm.mjs
CHANGED
|
@@ -7,25 +7,17 @@
|
|
|
7
7
|
* - Optional "stamps": created_at, updated_at, deleted_at
|
|
8
8
|
* - Optional unique / not-null markers
|
|
9
9
|
* - Optional ON DELETE behavior for *all* FKs via --onDelete=
|
|
10
|
-
* - Auto index per FK column in extraSQL
|
|
10
|
+
* - Auto index per FK column in extraSQL (emitted as backticked strings)
|
|
11
11
|
* - Auto-create stub schemas for FK target tables if missing
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* Field syntax (comma or symbol sugar):
|
|
17
|
-
* name -> bare column "name TEXT"
|
|
18
|
-
* name! -> NOT NULL
|
|
19
|
-
* name:text -> explicit SQL type
|
|
20
|
-
* name:text! -> TEXT NOT NULL
|
|
21
|
-
* email:text!+ -> TEXT UNIQUE NOT NULL
|
|
22
|
-
* user_id:text->users
|
|
23
|
-
* org_id:integer->orgs
|
|
24
|
-
* slug:text,unique -> you can also use ",unique" or ",notnull"
|
|
13
|
+
* New:
|
|
14
|
+
* - from-json <file>: bulk-generate schemas from a JSON config
|
|
15
|
+
* - --print-json-schema: output the JSON Schema used for validation
|
|
25
16
|
*
|
|
26
|
-
*
|
|
27
|
-
* -
|
|
28
|
-
* -
|
|
17
|
+
* Usage:
|
|
18
|
+
* allez-orm create table <name> [fields...] [--dir=schemas_cli] [--stamps] [-f|--force] [--onDelete=cascade|restrict|setnull|noaction]
|
|
19
|
+
* allez-orm from-json <config.json> [--dir=schemas_cli] [-f|--force]
|
|
20
|
+
* allez-orm --print-json-schema
|
|
29
21
|
*/
|
|
30
22
|
|
|
31
23
|
import fs from "node:fs";
|
|
@@ -33,17 +25,20 @@ import path from "node:path";
|
|
|
33
25
|
import process from "node:process";
|
|
34
26
|
|
|
35
27
|
const argv = process.argv.slice(2);
|
|
28
|
+
|
|
36
29
|
const usage = () => {
|
|
37
30
|
console.log(`
|
|
38
31
|
Usage:
|
|
39
32
|
allez-orm create table <name> [options] [fields...]
|
|
33
|
+
allez-orm from-json <config.json> [--dir=<outDir>] [-f|--force]
|
|
34
|
+
allez-orm --print-json-schema
|
|
40
35
|
|
|
41
36
|
Options:
|
|
42
37
|
--dir=<outDir> Output directory (default: schemas_cli)
|
|
43
38
|
--stamps Add created_at, updated_at, deleted_at columns
|
|
44
|
-
--onDelete=<mode> ON DELETE
|
|
45
|
-
-f, --force Overwrite existing
|
|
46
|
-
--help Show
|
|
39
|
+
--onDelete=<mode> ON DELETE for *all* FKs (cascade|restrict|setnull|noaction). Default: none
|
|
40
|
+
-f, --force Overwrite existing files
|
|
41
|
+
--help Show help
|
|
47
42
|
|
|
48
43
|
Field syntax:
|
|
49
44
|
col[:type][!][+][->target] or "col:type,unique,notnull"
|
|
@@ -51,11 +46,6 @@ Field syntax:
|
|
|
51
46
|
`);
|
|
52
47
|
};
|
|
53
48
|
|
|
54
|
-
if (!argv.length || argv.includes("--help") || argv.includes("-h")) {
|
|
55
|
-
usage();
|
|
56
|
-
process.exit(0);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
49
|
const die = (m, code = 1) => { console.error(m); process.exit(code); };
|
|
60
50
|
|
|
61
51
|
function parseOptions(args) {
|
|
@@ -67,11 +57,15 @@ function parseOptions(args) {
|
|
|
67
57
|
cmd: null,
|
|
68
58
|
sub: null,
|
|
69
59
|
table: null,
|
|
70
|
-
fields: []
|
|
60
|
+
fields: [],
|
|
61
|
+
jsonFile: null,
|
|
62
|
+
printJsonSchema: false,
|
|
71
63
|
};
|
|
72
64
|
const positional = [];
|
|
73
65
|
for (const a of args) {
|
|
74
|
-
if (a
|
|
66
|
+
if (a === "--help" || a === "-h") {
|
|
67
|
+
usage(); process.exit(0);
|
|
68
|
+
} else if (a.startsWith("--dir=")) {
|
|
75
69
|
out.dir = a.slice(6);
|
|
76
70
|
} else if (a === "--stamps") {
|
|
77
71
|
out.stamps = true;
|
|
@@ -83,42 +77,214 @@ function parseOptions(args) {
|
|
|
83
77
|
out.onDelete = v;
|
|
84
78
|
} else if (a === "-f" || a === "--force") {
|
|
85
79
|
out.force = true;
|
|
80
|
+
} else if (a === "--print-json-schema") {
|
|
81
|
+
out.printJsonSchema = true;
|
|
86
82
|
} else if (a.startsWith("-")) {
|
|
87
83
|
die(`Unknown option: ${a}`);
|
|
88
84
|
} else {
|
|
89
85
|
positional.push(a);
|
|
90
86
|
}
|
|
91
87
|
}
|
|
92
|
-
|
|
88
|
+
|
|
89
|
+
// env var ALLEZ_FORCE=1 is honored (does not break positional parsing)
|
|
90
|
+
if (process.env.ALLEZ_FORCE === "1") out.force = true;
|
|
91
|
+
|
|
93
92
|
out.cmd = positional[0] || null;
|
|
94
93
|
out.sub = positional[1] || null;
|
|
95
|
-
out.table = positional[2] || null;
|
|
96
|
-
out.fields = positional.slice(3);
|
|
97
94
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
out.
|
|
95
|
+
if (out.cmd === "create" && out.sub === "table") {
|
|
96
|
+
out.table = positional[2] || null;
|
|
97
|
+
out.fields = positional.slice(3);
|
|
98
|
+
} else if (out.cmd === "from-json") {
|
|
99
|
+
out.jsonFile = positional[1] || null;
|
|
101
100
|
}
|
|
101
|
+
|
|
102
102
|
return out;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
const opts = parseOptions(argv);
|
|
106
106
|
|
|
107
|
-
//
|
|
108
|
-
|
|
107
|
+
// ---------------- JSON Schema (string) ----------------
|
|
108
|
+
|
|
109
|
+
const CONFIG_JSON_SCHEMA = JSON.stringify({
|
|
110
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
111
|
+
$id: "https://allez-orm.dev/allez.config.schema.json",
|
|
112
|
+
type: "object",
|
|
113
|
+
additionalProperties: false,
|
|
114
|
+
properties: {
|
|
115
|
+
outDir: { type: "string" },
|
|
116
|
+
defaultOnDelete: { enum: ["cascade","restrict","setnull","noaction",null] },
|
|
117
|
+
tables: {
|
|
118
|
+
type: "array",
|
|
119
|
+
items: {
|
|
120
|
+
type: "object",
|
|
121
|
+
additionalProperties: false,
|
|
122
|
+
properties: {
|
|
123
|
+
name: { type: "string", minLength: 1 },
|
|
124
|
+
stamps: { type: "boolean" },
|
|
125
|
+
fields: {
|
|
126
|
+
type: "array",
|
|
127
|
+
items: {
|
|
128
|
+
type: "object",
|
|
129
|
+
additionalProperties: false,
|
|
130
|
+
required: ["name"],
|
|
131
|
+
properties: {
|
|
132
|
+
name: { type: "string" },
|
|
133
|
+
type: { type: "string" },
|
|
134
|
+
unique: { type: "boolean" },
|
|
135
|
+
notnull: { type: "boolean" },
|
|
136
|
+
fk: {
|
|
137
|
+
type: ["object", "null"],
|
|
138
|
+
additionalProperties: false,
|
|
139
|
+
properties: {
|
|
140
|
+
table: { type: "string" },
|
|
141
|
+
column: { type: "string", default: "id" }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
required: ["name","fields"]
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
required: ["tables"]
|
|
153
|
+
}, null, 2);
|
|
154
|
+
|
|
155
|
+
// ---------------- command switchboard ----------------
|
|
156
|
+
|
|
157
|
+
if (opts.printJsonSchema) {
|
|
158
|
+
console.log(CONFIG_JSON_SCHEMA);
|
|
159
|
+
process.exit(0);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!opts.cmd) {
|
|
163
|
+
usage();
|
|
164
|
+
process.exit(0);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (opts.cmd === "from-json") {
|
|
168
|
+
if (!opts.jsonFile) die("from-json requires a <config.json> path");
|
|
169
|
+
runFromJson(opts).catch(e => die(e.stack || String(e)));
|
|
170
|
+
// will exit inside
|
|
171
|
+
} else if (opts.cmd === "create" && opts.sub === "table" && opts.table) {
|
|
172
|
+
fs.mkdirSync(opts.dir, { recursive: true });
|
|
173
|
+
generateOne({
|
|
174
|
+
outDir: opts.dir,
|
|
175
|
+
name: opts.table,
|
|
176
|
+
stamps: opts.stamps,
|
|
177
|
+
onDelete: opts.onDelete,
|
|
178
|
+
force: opts.force,
|
|
179
|
+
fieldTokens: opts.fields
|
|
180
|
+
}).then(() => process.exit(0))
|
|
181
|
+
.catch(e => die(e.stack || String(e)));
|
|
182
|
+
} else {
|
|
109
183
|
usage();
|
|
110
|
-
die("Expected: create table <name>
|
|
184
|
+
die("Expected: create table <name> … or from-json <config.json>");
|
|
111
185
|
}
|
|
112
186
|
|
|
113
|
-
//
|
|
114
|
-
|
|
187
|
+
// ---------------- core generator (shared) ----------------
|
|
188
|
+
|
|
189
|
+
async function generateOne({ outDir, name, stamps, onDelete, force, fieldTokens }) {
|
|
190
|
+
const outFile = path.join(outDir, `${name}.schema.js`);
|
|
191
|
+
if (fs.existsSync(outFile) && !force) {
|
|
192
|
+
die(`Refusing to overwrite existing file: ${outFile}\n(use -f or ALLEZ_FORCE=1)`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Parse tokens into field descriptors
|
|
196
|
+
const fields = fieldTokens.map(parseFieldToken).filter(Boolean);
|
|
197
|
+
|
|
198
|
+
// Ensure id PK
|
|
199
|
+
const hasId = fields.some(f => f.name === "id");
|
|
200
|
+
if (!hasId) {
|
|
201
|
+
fields.unshift({ name: "id", type: "INTEGER", notnull: true, unique: false, fk: null, pk: true });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// stamps
|
|
205
|
+
if (stamps) {
|
|
206
|
+
fields.push(
|
|
207
|
+
{ name: "created_at", type: "TEXT", notnull: true },
|
|
208
|
+
{ name: "updated_at", type: "TEXT", notnull: true },
|
|
209
|
+
{ name: "deleted_at", type: "TEXT", notnull: false }
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// SQL
|
|
214
|
+
const columnLines = fields.map(f => sqlForColumn(f, onDelete));
|
|
215
|
+
|
|
216
|
+
// FK indexes
|
|
217
|
+
const extraSQL = [];
|
|
218
|
+
for (const f of fields) {
|
|
219
|
+
if (f.fk) {
|
|
220
|
+
extraSQL.push(
|
|
221
|
+
`\`CREATE INDEX IF NOT EXISTS idx_${name}_${f.name}_fk ON ${name}(${f.name});\``
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// module text
|
|
227
|
+
const moduleText = `// ${name}.schema.js (generated by tools/allez-orm.mjs)
|
|
228
|
+
const ${camel(name)}Schema = {
|
|
229
|
+
table: "${name}",
|
|
230
|
+
version: 1,
|
|
231
|
+
createSQL: \`
|
|
232
|
+
CREATE TABLE IF NOT EXISTS ${name} (
|
|
233
|
+
${columnLines.join(",\n ")}
|
|
234
|
+
);\`,
|
|
235
|
+
extraSQL: [
|
|
236
|
+
${extraSQL.join("\n ")}
|
|
237
|
+
]
|
|
238
|
+
};
|
|
239
|
+
export default ${camel(name)}Schema;
|
|
240
|
+
`;
|
|
241
|
+
|
|
242
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
243
|
+
fs.writeFileSync(outFile, moduleText, "utf8");
|
|
244
|
+
console.log(`Wrote ${outFile}`);
|
|
115
245
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
246
|
+
// stub FK targets
|
|
247
|
+
const fkTargets = Array.from(new Set(fields.filter(f => f.fk).map(f => f.fk.table)))
|
|
248
|
+
.filter(t => t && t !== name);
|
|
249
|
+
|
|
250
|
+
for (const t of fkTargets) {
|
|
251
|
+
const stubPath = path.join(outDir, `${t}.schema.js`);
|
|
252
|
+
if (!fs.existsSync(stubPath)) {
|
|
253
|
+
const stub = `// ${t}.schema.js (generated by tools/allez-orm.mjs - stub for FK target)
|
|
254
|
+
const ${camel(t)}Schema = {
|
|
255
|
+
table: "${t}",
|
|
256
|
+
version: 1,
|
|
257
|
+
createSQL: \`
|
|
258
|
+
CREATE TABLE IF NOT EXISTS ${t} (
|
|
259
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT
|
|
260
|
+
);\`,
|
|
261
|
+
extraSQL: [
|
|
262
|
+
|
|
263
|
+
]
|
|
264
|
+
};
|
|
265
|
+
export default ${camel(t)}Schema;
|
|
266
|
+
`;
|
|
267
|
+
fs.writeFileSync(stubPath, stub, "utf8");
|
|
268
|
+
console.log(`Wrote stub ${stubPath}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
119
271
|
}
|
|
120
272
|
|
|
121
|
-
|
|
273
|
+
function sqlForColumn(f, onDelete) {
|
|
274
|
+
if (f.pk) return `id INTEGER PRIMARY KEY AUTOINCREMENT`;
|
|
275
|
+
let s = `${f.name} ${f.type}`;
|
|
276
|
+
// ordering (UNIQUE then NOT NULL) matches tests
|
|
277
|
+
if (f.unique) s += ` UNIQUE`;
|
|
278
|
+
if (f.notnull) s += ` NOT NULL`;
|
|
279
|
+
if (f.fk) {
|
|
280
|
+
s += ` REFERENCES ${f.fk.table}(${f.fk.column || "id"})`;
|
|
281
|
+
if (onDelete) {
|
|
282
|
+
const map = { cascade: "CASCADE", restrict: "RESTRICT", setnull: "SET NULL", noaction: "NO ACTION" };
|
|
283
|
+
s += ` ON DELETE ${map[onDelete]}`;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return s;
|
|
287
|
+
}
|
|
122
288
|
|
|
123
289
|
function parseFieldToken(tok) {
|
|
124
290
|
// Accept "col[:type][!][+][->target]" OR "col:type,unique,notnull"
|
|
@@ -155,7 +321,7 @@ function parseFieldToken(tok) {
|
|
|
155
321
|
type = null;
|
|
156
322
|
}
|
|
157
323
|
|
|
158
|
-
//
|
|
324
|
+
// flags from both name and type segments
|
|
159
325
|
const nameHasBang = /!/.test(name);
|
|
160
326
|
const nameHasPlus = /\+/.test(name);
|
|
161
327
|
const typeHasBang = type ? /!/.test(type) : false;
|
|
@@ -164,7 +330,7 @@ function parseFieldToken(tok) {
|
|
|
164
330
|
if (nameHasBang || typeHasBang) ret.notnull = true;
|
|
165
331
|
if (nameHasPlus || typeHasPlus) ret.unique = true;
|
|
166
332
|
|
|
167
|
-
// Clean trailing !/+ off name and type
|
|
333
|
+
// Clean trailing !/+ off name and type
|
|
168
334
|
name = name.replace(/[!+]+$/,"").trim();
|
|
169
335
|
if (type) {
|
|
170
336
|
type = type.replace(/[!+]+$/,"").trim();
|
|
@@ -183,101 +349,84 @@ function parseFieldToken(tok) {
|
|
|
183
349
|
return ret;
|
|
184
350
|
}
|
|
185
351
|
|
|
186
|
-
|
|
352
|
+
function camel(s){return s.replace(/[-_](.)/g,(_,c)=>c.toUpperCase());}
|
|
187
353
|
|
|
188
|
-
//
|
|
189
|
-
const hasId = fields.some(f => f.name === "id");
|
|
190
|
-
if (!hasId) {
|
|
191
|
-
fields.unshift({ name: "id", type: "INTEGER", notnull: true, unique: false, fk: null, pk: true });
|
|
192
|
-
}
|
|
354
|
+
// ---------------- from-json implementation (resilient) ----------------
|
|
193
355
|
|
|
194
|
-
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
{ name: "created_at", type: "TEXT", notnull: true },
|
|
198
|
-
{ name: "updated_at", type: "TEXT", notnull: true },
|
|
199
|
-
{ name: "deleted_at", type: "TEXT", notnull: false }
|
|
200
|
-
);
|
|
356
|
+
function pick(obj, ...keys) {
|
|
357
|
+
for (const k of keys) { if (obj && obj[k] !== undefined) return obj[k]; }
|
|
358
|
+
return undefined;
|
|
201
359
|
}
|
|
202
360
|
|
|
203
|
-
|
|
361
|
+
async function runFromJson(cliOpts) {
|
|
362
|
+
const file = path.resolve(cliOpts.jsonFile);
|
|
363
|
+
if (!fs.existsSync(file)) die(`Config not found: ${file}`);
|
|
204
364
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
365
|
+
const raw = fs.readFileSync(file, "utf8");
|
|
366
|
+
let cfg;
|
|
367
|
+
try { cfg = JSON.parse(raw); } catch (e) { die(`Invalid JSON: ${e.message}`); }
|
|
208
368
|
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
|
|
369
|
+
// allow OutDir/DefaultOnDelete/Tables
|
|
370
|
+
const outDir = cliOpts.dir || pick(cfg, "outDir", "OutDir") || "schemas_cli";
|
|
371
|
+
const defaultOnDelete = pick(cfg, "defaultOnDelete", "DefaultOnDelete") ?? null;
|
|
212
372
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const map = { cascade: "CASCADE", restrict: "RESTRICT", setnull: "SET NULL", noaction: "NO ACTION" };
|
|
217
|
-
s += ` ON DELETE ${map[opts.onDelete]}`;
|
|
218
|
-
}
|
|
373
|
+
const tables = pick(cfg, "tables", "Tables");
|
|
374
|
+
if (!Array.isArray(tables)) {
|
|
375
|
+
die(`Config must have an array at "tables" (or "Tables").`);
|
|
219
376
|
}
|
|
220
|
-
return s;
|
|
221
|
-
}
|
|
222
377
|
|
|
223
|
-
|
|
378
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
224
379
|
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
380
|
+
for (let ti = 0; ti < tables.length; ti++) {
|
|
381
|
+
const tRaw = tables[ti] || {};
|
|
382
|
+
const tName = pick(tRaw, "name", "Name");
|
|
383
|
+
if (!tName || typeof tName !== "string") {
|
|
384
|
+
die(`Table at index ${ti} is missing "name".`);
|
|
385
|
+
}
|
|
386
|
+
const tStamps = !!pick(tRaw, "stamps", "Stamps");
|
|
234
387
|
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
createSQL: \`
|
|
241
|
-
CREATE TABLE IF NOT EXISTS ${opts.table} (
|
|
242
|
-
${columnLines.join(",\n ")}
|
|
243
|
-
);\`,
|
|
244
|
-
extraSQL: [
|
|
245
|
-
${extraSQL.join("\n ")}
|
|
246
|
-
]
|
|
247
|
-
};
|
|
248
|
-
export default ${camel(opts.table)}Schema;
|
|
249
|
-
`;
|
|
388
|
+
// accept fields/Fields OR columns/Columns
|
|
389
|
+
let fieldsList = pick(tRaw, "fields", "Fields", "columns", "Columns");
|
|
390
|
+
if (!Array.isArray(fieldsList)) {
|
|
391
|
+
die(`Table "${tName}" must have "fields" (or "Fields"/"columns"/"Columns") array.`);
|
|
392
|
+
}
|
|
250
393
|
|
|
251
|
-
|
|
252
|
-
|
|
394
|
+
const tokens = [];
|
|
395
|
+
for (let fi = 0; fi < fieldsList.length; fi++) {
|
|
396
|
+
const f = fieldsList[fi] || {};
|
|
397
|
+
const name = pick(f, "name", "Name");
|
|
398
|
+
if (!name || typeof name !== "string") {
|
|
399
|
+
die(`Table "${tName}" field #${fi} is missing "name".`);
|
|
400
|
+
}
|
|
401
|
+
const typeRaw = pick(f, "type", "Type");
|
|
402
|
+
const type = (typeRaw ? String(typeRaw) : "TEXT").toLowerCase();
|
|
253
403
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
404
|
+
const unique = !!pick(f, "unique", "Unique");
|
|
405
|
+
// support notnull, notNull, NotNull
|
|
406
|
+
const notnull = !!pick(f, "notnull", "notNull", "NotNull");
|
|
257
407
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const ${camel(t)}Schema = {
|
|
263
|
-
table: "${t}",
|
|
264
|
-
version: 1,
|
|
265
|
-
createSQL: \`
|
|
266
|
-
CREATE TABLE IF NOT EXISTS ${t} (
|
|
267
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT
|
|
268
|
-
);\`,
|
|
269
|
-
extraSQL: [
|
|
270
|
-
|
|
271
|
-
]
|
|
272
|
-
};
|
|
273
|
-
export default ${camel(t)}Schema;
|
|
274
|
-
`;
|
|
275
|
-
fs.writeFileSync(stubPath, stub, "utf8");
|
|
276
|
-
console.log(`Wrote stub ${stubPath}`);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
408
|
+
// FK variations: fk/FK with table/Table, column/Column
|
|
409
|
+
const fkRaw = pick(f, "fk", "FK");
|
|
410
|
+
const fkTable = fkRaw ? pick(fkRaw, "table", "Table") : undefined;
|
|
411
|
+
const fkCol = fkRaw ? (pick(fkRaw, "column", "Column") || "id") : undefined;
|
|
279
412
|
|
|
280
|
-
|
|
413
|
+
let token = name + `:${type}`;
|
|
414
|
+
if (notnull) token += `!`;
|
|
415
|
+
if (unique) token += `+`;
|
|
416
|
+
if (fkTable) token += `->` + fkTable;
|
|
281
417
|
|
|
282
|
-
|
|
283
|
-
|
|
418
|
+
tokens.push(token);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
await generateOne({
|
|
422
|
+
outDir,
|
|
423
|
+
name: tName,
|
|
424
|
+
stamps: tStamps,
|
|
425
|
+
onDelete: defaultOnDelete || null,
|
|
426
|
+
force: cliOpts.force,
|
|
427
|
+
fieldTokens: tokens
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
process.exit(0);
|
|
432
|
+
}
|