true-pg 0.4.5 → 0.5.1
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 +17 -17
- package/lib/bin.js +16 -16
- package/lib/config.d.ts +6 -6
- package/lib/config.js +8 -8
- package/lib/imports.d.ts +35 -0
- package/lib/imports.js +102 -0
- package/lib/index.js +70 -59
- package/lib/kysely/index.js +78 -97
- package/lib/types.d.ts +29 -74
- package/lib/types.js +0 -165
- package/lib/util.d.ts +4 -0
- package/lib/util.js +123 -0
- package/lib/util.test.d.ts +1 -0
- package/lib/util.test.js +78 -0
- package/lib/zod/index.js +93 -100
- package/package.json +3 -2
package/lib/types.js
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
import { Canonical, } from "./extractor/index.js";
|
2
|
-
import { dirname, relative } from "node:path/posix";
|
3
|
-
import { join } from "./util.js";
|
4
2
|
// To be updated when we add support for other kinds
|
5
3
|
export const allowed_kind_names = [
|
6
4
|
"tables",
|
@@ -12,168 +10,5 @@ export const allowed_kind_names = [
|
|
12
10
|
"domains",
|
13
11
|
"ranges",
|
14
12
|
];
|
15
|
-
export var Nodes;
|
16
|
-
(function (Nodes) {
|
17
|
-
class ExternalImport {
|
18
|
-
// what to import
|
19
|
-
name;
|
20
|
-
// use `type` syntax?
|
21
|
-
typeOnly;
|
22
|
-
// use `* as` syntax?
|
23
|
-
star;
|
24
|
-
// this is an external import
|
25
|
-
external;
|
26
|
-
// what module to import from
|
27
|
-
module;
|
28
|
-
constructor(args) {
|
29
|
-
this.name = args.name;
|
30
|
-
this.typeOnly = args.typeOnly;
|
31
|
-
this.star = args.star;
|
32
|
-
this.external = true;
|
33
|
-
this.module = args.module;
|
34
|
-
}
|
35
|
-
}
|
36
|
-
Nodes.ExternalImport = ExternalImport;
|
37
|
-
class InternalImport {
|
38
|
-
// what to import
|
39
|
-
name;
|
40
|
-
// underlying type that is being imported
|
41
|
-
canonical_type;
|
42
|
-
// use `type` syntax?
|
43
|
-
typeOnly;
|
44
|
-
// use `* as` syntax?
|
45
|
-
star;
|
46
|
-
// this is an internal import
|
47
|
-
external;
|
48
|
-
constructor(args) {
|
49
|
-
this.name = args.name;
|
50
|
-
this.canonical_type = args.canonical_type;
|
51
|
-
this.star = args.star;
|
52
|
-
this.typeOnly = args.typeOnly;
|
53
|
-
this.external = false;
|
54
|
-
}
|
55
|
-
}
|
56
|
-
Nodes.InternalImport = InternalImport;
|
57
|
-
class ImportList {
|
58
|
-
imports;
|
59
|
-
constructor(imports) {
|
60
|
-
this.imports = imports;
|
61
|
-
}
|
62
|
-
static merge(lists) {
|
63
|
-
return new ImportList(lists.flatMap(l => l.imports));
|
64
|
-
}
|
65
|
-
add(item) {
|
66
|
-
this.imports.push(item);
|
67
|
-
}
|
68
|
-
stringify(context_file, files) {
|
69
|
-
const externals = this.imports.filter(i => i.external);
|
70
|
-
const internals = this.imports.filter(i => !i.external);
|
71
|
-
const modulegroups = {};
|
72
|
-
for (const item of externals) {
|
73
|
-
const group = modulegroups[item.module];
|
74
|
-
if (group)
|
75
|
-
group.push(item);
|
76
|
-
else
|
77
|
-
modulegroups[item.module] = [item];
|
78
|
-
}
|
79
|
-
const out = [];
|
80
|
-
// TODO: normalise external and internal imports and handle the stringification of the imports in a single place
|
81
|
-
{
|
82
|
-
// EXTERNAL IMPORTS
|
83
|
-
const imports = [];
|
84
|
-
for (const module in modulegroups) {
|
85
|
-
const items = modulegroups[module];
|
86
|
-
const star = items.find(i => i.star);
|
87
|
-
const unique = items.filter((i, index, arr) => {
|
88
|
-
if (i.star)
|
89
|
-
return false;
|
90
|
-
if (arr.findIndex(i2 => i2.name === i.name) !== index)
|
91
|
-
return false;
|
92
|
-
return true;
|
93
|
-
});
|
94
|
-
const bits = [];
|
95
|
-
const typeOnlys = unique.filter(i => i.typeOnly);
|
96
|
-
const values = unique.filter(i => !i.typeOnly);
|
97
|
-
// if no values to import, use `import type { ... }` instead of `import { type ... }`
|
98
|
-
const typeInline = values.length !== 0;
|
99
|
-
let import_line = `import `;
|
100
|
-
for (const type of typeOnlys)
|
101
|
-
bits.push(typeInline ? "type " : "" + type.name);
|
102
|
-
for (const type of values)
|
103
|
-
bits.push(type.name);
|
104
|
-
if (bits.length)
|
105
|
-
import_line += (typeInline ? "" : "type ") + "{ " + bits.join(", ") + " }";
|
106
|
-
if (bits.length && star)
|
107
|
-
import_line += `, `;
|
108
|
-
if (star)
|
109
|
-
import_line += `* as ${star.name}`;
|
110
|
-
if (bits.length || star)
|
111
|
-
import_line += ` from `;
|
112
|
-
import_line += `"${module}";`;
|
113
|
-
imports.push(import_line);
|
114
|
-
}
|
115
|
-
out.push(join(imports, "\n"));
|
116
|
-
}
|
117
|
-
{
|
118
|
-
// INTERNAL IMPORTS
|
119
|
-
const imports = [];
|
120
|
-
const unique_types = internals
|
121
|
-
.filter(({ name: name1, canonical_type: int }, index, arr) => {
|
122
|
-
return (arr.findIndex(({ name: name2, canonical_type: int2 }) => {
|
123
|
-
return (
|
124
|
-
// adapter-assigned name
|
125
|
-
name2 === name1 &&
|
126
|
-
// canonical type details
|
127
|
-
int2.name === int.name &&
|
128
|
-
int2.schema === int.schema &&
|
129
|
-
int2.kind === int.kind);
|
130
|
-
}) === index);
|
131
|
-
})
|
132
|
-
.filter(imp => imp.canonical_type.kind === Canonical.Kind.Base ||
|
133
|
-
allowed_kind_names.includes(`${imp.canonical_type.kind}s`))
|
134
|
-
.map(imp => {
|
135
|
-
const t = imp.canonical_type;
|
136
|
-
const schema = files.children[t.schema];
|
137
|
-
const kind = schema.children[`${t.kind}s`];
|
138
|
-
const type = kind.children[t.name];
|
139
|
-
const located_file = `${files.name}/${schema.name}/${kind.kind}/${type.name}.ts`;
|
140
|
-
return { ...imp, located_file };
|
141
|
-
});
|
142
|
-
const group_by_file = {};
|
143
|
-
for (const type of unique_types) {
|
144
|
-
const file = group_by_file[type.located_file] || [];
|
145
|
-
file.push(type);
|
146
|
-
group_by_file[type.located_file] = file;
|
147
|
-
}
|
148
|
-
for (const group in group_by_file) {
|
149
|
-
let relative_path = relative(dirname(context_file), group);
|
150
|
-
if (/^[^\.+\/]/.test(relative_path))
|
151
|
-
relative_path = "./" + relative_path;
|
152
|
-
const items = group_by_file[group];
|
153
|
-
const typeOnlys = items.filter(i => i.typeOnly);
|
154
|
-
const values = items.filter(i => !i.typeOnly);
|
155
|
-
const star = values.find(i => i.star);
|
156
|
-
let import_line = "import ";
|
157
|
-
const bits = [];
|
158
|
-
// if no values to import, use `import type { ... }` instead of `import { type ... }`
|
159
|
-
const typeInline = values.length !== 0;
|
160
|
-
for (const type of typeOnlys)
|
161
|
-
bits.push((typeInline ? "type " : "") + type.name);
|
162
|
-
for (const type of values)
|
163
|
-
bits.push(type.name);
|
164
|
-
if (bits.length)
|
165
|
-
import_line += (typeInline ? "" : "type ") + "{ " + bits.join(", ") + " }";
|
166
|
-
if (star)
|
167
|
-
import_line += `* as ${star.name}`;
|
168
|
-
import_line += ` from "${relative_path}";`;
|
169
|
-
imports.push(import_line);
|
170
|
-
}
|
171
|
-
out.push(join(imports, "\n"));
|
172
|
-
}
|
173
|
-
return join(out);
|
174
|
-
}
|
175
|
-
}
|
176
|
-
Nodes.ImportList = ImportList;
|
177
|
-
})(Nodes || (Nodes = {}));
|
178
13
|
/* convenience function to create a generator with type inference */
|
179
14
|
export const createGenerator = (generatorCreator) => generatorCreator;
|
package/lib/util.d.ts
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
export declare const eq: <T>(a: T, b: T) => boolean;
|
2
|
+
export declare const toPascalCase: (str: string) => string;
|
3
|
+
export declare const to_snake_case: (str: string) => string;
|
1
4
|
export declare const join: (parts: Iterable<string>, joiner?: string) => string;
|
2
5
|
export type UnionKeys<T> = T extends unknown ? keyof T : never;
|
3
6
|
type AddOptionalKeys<K extends PropertyKey> = {
|
@@ -7,6 +10,7 @@ export type Deunionise<B extends object | undefined, T = B> = Simplify<T extends
|
|
7
10
|
export type Simplify<T> = {
|
8
11
|
[KeyType in keyof T]: T[KeyType];
|
9
12
|
} & {};
|
13
|
+
export declare const parens: (str: string, type?: string) => string;
|
10
14
|
export declare const quote: (str: string, using?: string) => string;
|
11
15
|
export declare const quoteI: (str: string, using?: string) => string;
|
12
16
|
export {};
|
package/lib/util.js
CHANGED
@@ -1,7 +1,130 @@
|
|
1
|
+
export const eq = (a, b) => {
|
2
|
+
if (a === b)
|
3
|
+
return true;
|
4
|
+
if (a == null || b == null)
|
5
|
+
return false;
|
6
|
+
if (typeof a !== "object" || typeof b !== "object")
|
7
|
+
return false;
|
8
|
+
for (const key in a)
|
9
|
+
if (!eq(a[key], b[key]))
|
10
|
+
return false;
|
11
|
+
return true;
|
12
|
+
};
|
13
|
+
export const toPascalCase = (str) => {
|
14
|
+
let result = "";
|
15
|
+
let index = 0;
|
16
|
+
let space = false;
|
17
|
+
let leading = true;
|
18
|
+
const len = str.length;
|
19
|
+
while (index < len) {
|
20
|
+
const char = str[index];
|
21
|
+
// keep trailing underscores
|
22
|
+
if (index === len - 1 && char === "_") {
|
23
|
+
// iterate backwards until a non-underscore character is found
|
24
|
+
let index = len - 1;
|
25
|
+
while (index >= 0 && str[index] === "_") {
|
26
|
+
result += "_";
|
27
|
+
index--;
|
28
|
+
}
|
29
|
+
break;
|
30
|
+
}
|
31
|
+
if (leading) {
|
32
|
+
if (char === "_") {
|
33
|
+
result += char;
|
34
|
+
}
|
35
|
+
else if (/[a-zA-Z]/.test(char)) {
|
36
|
+
result += char.toUpperCase();
|
37
|
+
leading = false;
|
38
|
+
}
|
39
|
+
else {
|
40
|
+
// skip leading non-alphabetic characters
|
41
|
+
}
|
42
|
+
}
|
43
|
+
else if (/[a-zA-Z0-9]/.test(char)) {
|
44
|
+
// valid characters
|
45
|
+
if (space)
|
46
|
+
result += char.toUpperCase();
|
47
|
+
else
|
48
|
+
result += char;
|
49
|
+
space = false;
|
50
|
+
}
|
51
|
+
else {
|
52
|
+
// invalid characters, space, underscore, or hyphen
|
53
|
+
// treat as space
|
54
|
+
space = true;
|
55
|
+
}
|
56
|
+
index++;
|
57
|
+
}
|
58
|
+
return result;
|
59
|
+
};
|
60
|
+
export const to_snake_case = (str) => {
|
61
|
+
let result = "";
|
62
|
+
let index = 0;
|
63
|
+
let space = false;
|
64
|
+
let leading = true;
|
65
|
+
let upper = false;
|
66
|
+
const len = str.length;
|
67
|
+
while (index < len) {
|
68
|
+
const char = str[index];
|
69
|
+
// keep trailing underscores
|
70
|
+
if (index === len - 1 && char === "_") {
|
71
|
+
// iterate backwards until a non-underscore character is found
|
72
|
+
let index = len - 1;
|
73
|
+
while (index >= 0 && str[index] === "_") {
|
74
|
+
result += "_";
|
75
|
+
index--;
|
76
|
+
}
|
77
|
+
break;
|
78
|
+
}
|
79
|
+
if (leading) {
|
80
|
+
if (char === "_") {
|
81
|
+
result += char;
|
82
|
+
}
|
83
|
+
else if (/[A-Z]/.test(char)) {
|
84
|
+
result += char.toLowerCase();
|
85
|
+
leading = false;
|
86
|
+
upper = true;
|
87
|
+
}
|
88
|
+
else if (/[a-z]/.test(char)) {
|
89
|
+
result += char.toLowerCase();
|
90
|
+
leading = false;
|
91
|
+
}
|
92
|
+
else {
|
93
|
+
// skip leading non-alphabetic characters
|
94
|
+
}
|
95
|
+
}
|
96
|
+
else if (/[A-Z]/.test(char)) {
|
97
|
+
if (!upper)
|
98
|
+
result += "_";
|
99
|
+
if (space)
|
100
|
+
result += "_";
|
101
|
+
// uppercase characters
|
102
|
+
result += char.toLowerCase();
|
103
|
+
space = false;
|
104
|
+
upper = true;
|
105
|
+
}
|
106
|
+
else if (/[a-z0-9]/.test(char)) {
|
107
|
+
// valid characters
|
108
|
+
if (space)
|
109
|
+
result += "_";
|
110
|
+
result += char;
|
111
|
+
space = false;
|
112
|
+
upper = false;
|
113
|
+
}
|
114
|
+
else {
|
115
|
+
// invalid characters, space, underscore, or hyphen
|
116
|
+
// treat as space
|
117
|
+
space = true;
|
118
|
+
}
|
119
|
+
index++;
|
120
|
+
}
|
121
|
+
return result;
|
122
|
+
};
|
1
123
|
export const join = (parts, joiner = "\n\n") => Array.from(parts).filter(Boolean).join(joiner);
|
2
124
|
const isIdentifierInvalid = (str) => {
|
3
125
|
const invalid = str.match(/[^a-zA-Z0-9_]/);
|
4
126
|
return invalid !== null;
|
5
127
|
};
|
128
|
+
export const parens = (str, type = "()") => `${type[0]}${str}${type[1]}`;
|
6
129
|
export const quote = (str, using = '"') => `${using}${str.replaceAll(using, "\\" + using)}${using}`;
|
7
130
|
export const quoteI = (str, using = '"') => (isIdentifierInvalid(str) ? quote(str, using) : str);
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
package/lib/util.test.js
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
import { describe, it, expect } from "bun:test";
|
2
|
+
import { toPascalCase, to_snake_case } from "./util.js";
|
3
|
+
describe("toPascalCase", () => {
|
4
|
+
it("should convert a string to PascalCase", () => {
|
5
|
+
expect(toPascalCase("hello-world")).toBe("HelloWorld");
|
6
|
+
});
|
7
|
+
it("should skip leading invalid characters", () => {
|
8
|
+
expect(toPascalCase("123hello-world")).toBe("HelloWorld");
|
9
|
+
});
|
10
|
+
it("should skip leading spaces", () => {
|
11
|
+
expect(toPascalCase(" hello-world")).toBe("HelloWorld");
|
12
|
+
});
|
13
|
+
it("should keep leading underscores", () => {
|
14
|
+
expect(toPascalCase("__hello-world")).toBe("__HelloWorld");
|
15
|
+
});
|
16
|
+
it("should keep trailing underscores", () => {
|
17
|
+
expect(toPascalCase("hello_world___")).toBe("HelloWorld___");
|
18
|
+
});
|
19
|
+
it("should keep leading and trailing underscores", () => {
|
20
|
+
expect(toPascalCase("__hello_world__")).toBe("__HelloWorld__");
|
21
|
+
});
|
22
|
+
it("should convert spaces to uppercase", () => {
|
23
|
+
expect(toPascalCase("hello world")).toBe("HelloWorld");
|
24
|
+
});
|
25
|
+
it("should convert hyphens to uppercase", () => {
|
26
|
+
expect(toPascalCase("hello-world")).toBe("HelloWorld");
|
27
|
+
});
|
28
|
+
it("should convert camelCase to PascalCase", () => {
|
29
|
+
expect(toPascalCase("helloWorld")).toBe("HelloWorld");
|
30
|
+
});
|
31
|
+
it("should convert snake_case to PascalCase", () => {
|
32
|
+
expect(toPascalCase("hello_world")).toBe("HelloWorld");
|
33
|
+
});
|
34
|
+
it("should skip invalid characters", () => {
|
35
|
+
expect(toPascalCase("hello?world!")).toBe("HelloWorld");
|
36
|
+
});
|
37
|
+
it("should generate valid identifier", () => {
|
38
|
+
expect(toPascalCase(" _ hello ? world !")).toBe("_HelloWorld");
|
39
|
+
});
|
40
|
+
});
|
41
|
+
describe("to_snake_case", () => {
|
42
|
+
it("should convert a string to snake_case", () => {
|
43
|
+
expect(to_snake_case("hello-world")).toBe("hello_world");
|
44
|
+
});
|
45
|
+
it("should skip leading invalid characters", () => {
|
46
|
+
expect(to_snake_case("123hello-world")).toBe("hello_world");
|
47
|
+
});
|
48
|
+
it("should skip leading spaces", () => {
|
49
|
+
expect(to_snake_case(" hello-world")).toBe("hello_world");
|
50
|
+
});
|
51
|
+
it("should keep leading underscores", () => {
|
52
|
+
expect(to_snake_case("__hello-world")).toBe("__hello_world");
|
53
|
+
});
|
54
|
+
it("should keep trailing underscores", () => {
|
55
|
+
expect(to_snake_case("hello_world___")).toBe("hello_world___");
|
56
|
+
});
|
57
|
+
it("should keep leading and trailing underscores", () => {
|
58
|
+
expect(to_snake_case("__hello_world__")).toBe("__hello_world__");
|
59
|
+
});
|
60
|
+
it("should convert spaces to underscores", () => {
|
61
|
+
expect(to_snake_case("hello world")).toBe("hello_world");
|
62
|
+
});
|
63
|
+
it("should convert camelCase to snake_case", () => {
|
64
|
+
expect(to_snake_case("helloWorld")).toBe("hello_world");
|
65
|
+
});
|
66
|
+
it("should convert PascalCase to snake_case", () => {
|
67
|
+
expect(to_snake_case("HelloWorld")).toBe("hello_world");
|
68
|
+
});
|
69
|
+
it("should skip invalid characters", () => {
|
70
|
+
expect(to_snake_case("hello?world!")).toBe("hello_world");
|
71
|
+
});
|
72
|
+
it("should generate valid identifier", () => {
|
73
|
+
expect(to_snake_case(" _ hello ? world !")).toBe("_hello_world");
|
74
|
+
});
|
75
|
+
it("should convert uppercase characters to underscores", () => {
|
76
|
+
expect(to_snake_case("HELLO_WORLD")).toBe("hello_world");
|
77
|
+
});
|
78
|
+
});
|