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/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 {};
@@ -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
+ });