@drzl/validation-core 1.1.0 → 2.0.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 +20 -0
- package/dist/index.cjs +167 -2
- package/dist/index.d.cts +164 -1
- package/dist/index.d.ts +164 -1
- package/dist/index.js +148 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -37,3 +37,23 @@ Shared interfaces and helpers used by validation generators.
|
|
|
37
37
|
- Helpers
|
|
38
38
|
- `insertColumns(table)`, `updateColumns(table)`, `selectColumns(table)`
|
|
39
39
|
- `formatCode(code, filePath, formatOpts)`
|
|
40
|
+
- File names (shared by every generator that writes a file and a barrel)
|
|
41
|
+
- `moduleFileName(tsName, fileSuffix)` -> `users.zod.ts`
|
|
42
|
+
- `moduleSpecifier(tsName, fileSuffix, importExtension?)` -> `./users.zod.js`, the
|
|
43
|
+
specifier a sibling module needs to import that file.
|
|
44
|
+
- `importSpecifier(relativePath, importExtension?)` does the same for a path a generator
|
|
45
|
+
already has in hand, e.g. `./types/users.ts` -> `./types/users.js`.
|
|
46
|
+
- Both read the same `fileSuffix`, so a barrel can never name a file that was not written.
|
|
47
|
+
- `ImportExtension` is `'js' | 'none' | 'ts'`, defaulting to `DEFAULT_IMPORT_EXTENSION`
|
|
48
|
+
(`'js'`). `'js'` is the only form that resolves under `bundler`, `node10`, `node16` and
|
|
49
|
+
`nodenext`, in both CommonJS and ESM, with no compiler flag. `'none'` is the pre-2.0
|
|
50
|
+
output and misses `node16`/`nodenext` ES modules. `'ts'` needs
|
|
51
|
+
`allowImportingTsExtensions`. `.mts` and `.cts` become `.mjs` and `.cjs` under both
|
|
52
|
+
`'js'` and `'none'`, since an extensionless specifier never resolves to them.
|
|
53
|
+
- Naming (shared by every generator that emits a schema name)
|
|
54
|
+
- `resolveAffix({ affix, schemaSuffix })` -> `ResolvedAffix`
|
|
55
|
+
- `schemaName(mode, tsName, resolved)`, `typeName(mode, tsName, resolved)`
|
|
56
|
+
- `validateAffix(affix, schemaSuffix)` -> issues for unusable or colliding names
|
|
57
|
+
- `pascalCase(s)`, `applyTableCase(tsName, 'preserve' | 'pascal')`
|
|
58
|
+
- Calling `resolveAffix()` with no options reproduces the original naming exactly, so the
|
|
59
|
+
generators and the oRPC router can never interpret one config two ways.
|
package/dist/index.cjs
CHANGED
|
@@ -100171,13 +100171,162 @@ ${codeblock}`, options8);
|
|
|
100171
100171
|
// src/index.ts
|
|
100172
100172
|
var index_exports2 = {};
|
|
100173
100173
|
__export(index_exports2, {
|
|
100174
|
+
AFFIX_PROBE_TABLE: () => AFFIX_PROBE_TABLE,
|
|
100175
|
+
DEFAULT_IMPORT_EXTENSION: () => DEFAULT_IMPORT_EXTENSION,
|
|
100176
|
+
DEFAULT_MODE_PREFIX: () => DEFAULT_MODE_PREFIX,
|
|
100177
|
+
DEFAULT_SCHEMA_SUFFIX: () => DEFAULT_SCHEMA_SUFFIX,
|
|
100178
|
+
DEFAULT_TYPE_SUFFIX: () => DEFAULT_TYPE_SUFFIX,
|
|
100179
|
+
IMPORT_EXTENSIONS: () => IMPORT_EXTENSIONS,
|
|
100180
|
+
NAME_MODES: () => NAME_MODES,
|
|
100181
|
+
applyTableCase: () => applyTableCase,
|
|
100174
100182
|
formatCode: () => formatCode,
|
|
100183
|
+
importSpecifier: () => importSpecifier,
|
|
100175
100184
|
insertColumns: () => insertColumns,
|
|
100176
100185
|
isGeneratedColumn: () => isGeneratedColumn,
|
|
100186
|
+
moduleFileName: () => moduleFileName,
|
|
100187
|
+
moduleSpecifier: () => moduleSpecifier,
|
|
100188
|
+
pascalCase: () => pascalCase,
|
|
100189
|
+
resolveAffix: () => resolveAffix,
|
|
100190
|
+
schemaName: () => schemaName,
|
|
100177
100191
|
selectColumns: () => selectColumns,
|
|
100178
|
-
|
|
100192
|
+
typeName: () => typeName,
|
|
100193
|
+
updateColumns: () => updateColumns,
|
|
100194
|
+
validateAffix: () => validateAffix
|
|
100179
100195
|
});
|
|
100180
100196
|
module.exports = __toCommonJS(index_exports2);
|
|
100197
|
+
|
|
100198
|
+
// src/files.ts
|
|
100199
|
+
var IMPORT_EXTENSIONS = ["js", "none", "ts"];
|
|
100200
|
+
var DEFAULT_IMPORT_EXTENSION = "js";
|
|
100201
|
+
var TS_EXTENSIONS = [
|
|
100202
|
+
{ ext: ".mts", js: ".mjs", none: ".mjs" },
|
|
100203
|
+
{ ext: ".cts", js: ".cjs", none: ".cjs" },
|
|
100204
|
+
{ ext: ".tsx", js: ".js", none: "" },
|
|
100205
|
+
{ ext: ".ts", js: ".js", none: "" }
|
|
100206
|
+
];
|
|
100207
|
+
function moduleFileName(tsName, fileSuffix) {
|
|
100208
|
+
return `${tsName}${fileSuffix}`;
|
|
100209
|
+
}
|
|
100210
|
+
function importSpecifier(relativePath, importExtension = DEFAULT_IMPORT_EXTENSION) {
|
|
100211
|
+
for (const { ext, js: js8, none } of TS_EXTENSIONS) {
|
|
100212
|
+
if (!relativePath.endsWith(ext)) continue;
|
|
100213
|
+
const stem = relativePath.slice(0, -ext.length);
|
|
100214
|
+
if (importExtension === "ts") return relativePath;
|
|
100215
|
+
return `${stem}${importExtension === "none" ? none : js8}`;
|
|
100216
|
+
}
|
|
100217
|
+
return relativePath;
|
|
100218
|
+
}
|
|
100219
|
+
function moduleSpecifier(tsName, fileSuffix, importExtension = DEFAULT_IMPORT_EXTENSION) {
|
|
100220
|
+
return importSpecifier(`./${moduleFileName(tsName, fileSuffix)}`, importExtension);
|
|
100221
|
+
}
|
|
100222
|
+
|
|
100223
|
+
// src/naming.ts
|
|
100224
|
+
var NAME_MODES = ["insert", "update", "select"];
|
|
100225
|
+
var DEFAULT_MODE_PREFIX = {
|
|
100226
|
+
insert: "Insert",
|
|
100227
|
+
update: "Update",
|
|
100228
|
+
select: "Select"
|
|
100229
|
+
};
|
|
100230
|
+
var DEFAULT_TYPE_SUFFIX = {
|
|
100231
|
+
insert: "Input",
|
|
100232
|
+
update: "Input",
|
|
100233
|
+
select: "Output"
|
|
100234
|
+
};
|
|
100235
|
+
var DEFAULT_SCHEMA_SUFFIX = "Schema";
|
|
100236
|
+
var AFFIX_PROBE_TABLE = "users";
|
|
100237
|
+
function spread(value, fallback) {
|
|
100238
|
+
if (value === void 0) return { ...fallback };
|
|
100239
|
+
if (typeof value === "string") return { insert: value, update: value, select: value };
|
|
100240
|
+
return {
|
|
100241
|
+
insert: value.insert ?? fallback.insert,
|
|
100242
|
+
update: value.update ?? fallback.update,
|
|
100243
|
+
select: value.select ?? fallback.select
|
|
100244
|
+
};
|
|
100245
|
+
}
|
|
100246
|
+
function pascalCase(s) {
|
|
100247
|
+
return s.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[\s_-]+/).filter(Boolean).map((p5) => p5.charAt(0).toUpperCase() + p5.slice(1)).join("");
|
|
100248
|
+
}
|
|
100249
|
+
function applyTableCase(tsName, tableCase) {
|
|
100250
|
+
return tableCase === "pascal" ? pascalCase(tsName) : tsName;
|
|
100251
|
+
}
|
|
100252
|
+
function resolveAffix(opts) {
|
|
100253
|
+
const affix = opts?.affix;
|
|
100254
|
+
const legacy = opts?.schemaSuffix ?? DEFAULT_SCHEMA_SUFFIX;
|
|
100255
|
+
const legacyMap = {
|
|
100256
|
+
insert: legacy,
|
|
100257
|
+
update: legacy,
|
|
100258
|
+
select: legacy
|
|
100259
|
+
};
|
|
100260
|
+
return {
|
|
100261
|
+
tableCase: affix?.tableCase ?? "preserve",
|
|
100262
|
+
schema: {
|
|
100263
|
+
prefix: spread(affix?.schema?.prefix, DEFAULT_MODE_PREFIX),
|
|
100264
|
+
suffix: spread(affix?.schema?.suffix, legacyMap)
|
|
100265
|
+
},
|
|
100266
|
+
type: {
|
|
100267
|
+
prefix: spread(affix?.type?.prefix, DEFAULT_MODE_PREFIX),
|
|
100268
|
+
suffix: spread(affix?.type?.suffix, DEFAULT_TYPE_SUFFIX)
|
|
100269
|
+
}
|
|
100270
|
+
};
|
|
100271
|
+
}
|
|
100272
|
+
function schemaName(mode, tsName, affix) {
|
|
100273
|
+
return affix.schema.prefix[mode] + applyTableCase(tsName, affix.tableCase) + affix.schema.suffix[mode];
|
|
100274
|
+
}
|
|
100275
|
+
function typeName(mode, tsName, affix) {
|
|
100276
|
+
return affix.type.prefix[mode] + applyTableCase(tsName, affix.tableCase) + affix.type.suffix[mode];
|
|
100277
|
+
}
|
|
100278
|
+
var PREFIX_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
100279
|
+
var SUFFIX_RE = /^[A-Za-z0-9_$]+$/;
|
|
100280
|
+
function validateAffix(affix, schemaSuffix) {
|
|
100281
|
+
const issues = [];
|
|
100282
|
+
if (!affix) return issues;
|
|
100283
|
+
const checkOne = (value, path14, kind) => {
|
|
100284
|
+
if (value === "") return;
|
|
100285
|
+
const ok = kind === "prefix" ? PREFIX_RE.test(value) : SUFFIX_RE.test(value);
|
|
100286
|
+
if (ok) return;
|
|
100287
|
+
issues.push({
|
|
100288
|
+
path: path14,
|
|
100289
|
+
message: `${JSON.stringify(value)} cannot appear in a TypeScript identifier. Use only letters, digits, "_" and "$"` + (kind === "prefix" ? ", and do not start with a digit." : ".")
|
|
100290
|
+
});
|
|
100291
|
+
};
|
|
100292
|
+
const checkValue = (value, base, kind) => {
|
|
100293
|
+
if (value === void 0) return;
|
|
100294
|
+
if (typeof value === "string") {
|
|
100295
|
+
checkOne(value, base, kind);
|
|
100296
|
+
return;
|
|
100297
|
+
}
|
|
100298
|
+
for (const mode of NAME_MODES) {
|
|
100299
|
+
const v7 = value[mode];
|
|
100300
|
+
if (v7 !== void 0) checkOne(v7, [...base, mode], kind);
|
|
100301
|
+
}
|
|
100302
|
+
};
|
|
100303
|
+
checkValue(affix.schema?.prefix, ["schema", "prefix"], "prefix");
|
|
100304
|
+
checkValue(affix.schema?.suffix, ["schema", "suffix"], "suffix");
|
|
100305
|
+
checkValue(affix.type?.prefix, ["type", "prefix"], "prefix");
|
|
100306
|
+
checkValue(affix.type?.suffix, ["type", "suffix"], "suffix");
|
|
100307
|
+
if (issues.length) return issues;
|
|
100308
|
+
const resolved = resolveAffix({ affix, schemaSuffix });
|
|
100309
|
+
const collisions = (space, build) => {
|
|
100310
|
+
const seen = /* @__PURE__ */ new Map();
|
|
100311
|
+
for (const mode of NAME_MODES) {
|
|
100312
|
+
const name = build(mode);
|
|
100313
|
+
const first = seen.get(name);
|
|
100314
|
+
if (first) {
|
|
100315
|
+
issues.push({
|
|
100316
|
+
path: [space],
|
|
100317
|
+
message: `The ${space} names for "${first}" and "${mode}" collide: both resolve to "${name}". All three are emitted into the same file, so at least one prefix or suffix has to differ.`
|
|
100318
|
+
});
|
|
100319
|
+
} else {
|
|
100320
|
+
seen.set(name, mode);
|
|
100321
|
+
}
|
|
100322
|
+
}
|
|
100323
|
+
};
|
|
100324
|
+
collisions("schema", (mode) => schemaName(mode, AFFIX_PROBE_TABLE, resolved));
|
|
100325
|
+
collisions("type", (mode) => typeName(mode, AFFIX_PROBE_TABLE, resolved));
|
|
100326
|
+
return issues;
|
|
100327
|
+
}
|
|
100328
|
+
|
|
100329
|
+
// src/index.ts
|
|
100181
100330
|
function isGeneratedColumn(c5, primaryKeyColumns) {
|
|
100182
100331
|
return c5.isGenerated || primaryKeyColumns.includes(c5.name);
|
|
100183
100332
|
}
|
|
@@ -100219,9 +100368,25 @@ async function formatCode(code, filePath, fmt) {
|
|
|
100219
100368
|
}
|
|
100220
100369
|
// Annotate the CommonJS export names for ESM import in node:
|
|
100221
100370
|
0 && (module.exports = {
|
|
100371
|
+
AFFIX_PROBE_TABLE,
|
|
100372
|
+
DEFAULT_IMPORT_EXTENSION,
|
|
100373
|
+
DEFAULT_MODE_PREFIX,
|
|
100374
|
+
DEFAULT_SCHEMA_SUFFIX,
|
|
100375
|
+
DEFAULT_TYPE_SUFFIX,
|
|
100376
|
+
IMPORT_EXTENSIONS,
|
|
100377
|
+
NAME_MODES,
|
|
100378
|
+
applyTableCase,
|
|
100222
100379
|
formatCode,
|
|
100380
|
+
importSpecifier,
|
|
100223
100381
|
insertColumns,
|
|
100224
100382
|
isGeneratedColumn,
|
|
100383
|
+
moduleFileName,
|
|
100384
|
+
moduleSpecifier,
|
|
100385
|
+
pascalCase,
|
|
100386
|
+
resolveAffix,
|
|
100387
|
+
schemaName,
|
|
100225
100388
|
selectColumns,
|
|
100226
|
-
|
|
100389
|
+
typeName,
|
|
100390
|
+
updateColumns,
|
|
100391
|
+
validateAffix
|
|
100227
100392
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,150 @@
|
|
|
1
1
|
import { Column, Analysis } from '@drzl/analyzer';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* One place that decides what a generated module is called, on disk and in an import.
|
|
5
|
+
*
|
|
6
|
+
* The three validation generators used to name the file from `fileSuffix` but hardcode the
|
|
7
|
+
* default suffix in the barrel, so any custom `fileSuffix` produced `export * from
|
|
8
|
+
* './users.zod'` next to a file called `users.schema.ts` and the consumer's build failed on
|
|
9
|
+
* an unresolved import. Both halves are derived here from the same value now.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* How a relative import of a generated file spells its extension.
|
|
13
|
+
*
|
|
14
|
+
* Generated files land in the consumer's own source tree, so the consumer's
|
|
15
|
+
* `moduleResolution` decides which forms resolve. Measured against tsc 5.9.2 and 7.0.2, for
|
|
16
|
+
* a specifier pointing at a sibling `.ts` file:
|
|
17
|
+
*
|
|
18
|
+
* | form | bundler | node10 | node16 / nodenext (CJS) | node16 / nodenext (ESM) |
|
|
19
|
+
* | --------------- | ------- | ------ | ----------------------- | ----------------------- |
|
|
20
|
+
* | `'js'` | yes | yes | yes | yes |
|
|
21
|
+
* | `'none'` | yes | yes | yes | **no** |
|
|
22
|
+
* | `'ts'` | flag | flag | flag | flag |
|
|
23
|
+
*
|
|
24
|
+
* `'js'` is the default because it is the only form that needs no compiler flag and still
|
|
25
|
+
* resolves in every cell. `'none'` is what drzl emitted before 2.0 and is what a pipeline
|
|
26
|
+
* that cannot map `.js` back to `.ts` wants (webpack without `resolve.extensionAlias`,
|
|
27
|
+
* ts-jest without a `moduleNameMapper`). `'ts'` needs `allowImportingTsExtensions`, and is
|
|
28
|
+
* the only form Node's own type stripping accepts, so it suits a project that runs the
|
|
29
|
+
* generated `.ts` unbuilt.
|
|
30
|
+
*/
|
|
31
|
+
type ImportExtension = (typeof IMPORT_EXTENSIONS)[number];
|
|
32
|
+
/**
|
|
33
|
+
* Every value `ImportExtension` accepts, in the order documentation lists them. The type is
|
|
34
|
+
* derived from this tuple rather than declared alongside it, so a config schema built from
|
|
35
|
+
* it accepts exactly what the type allows and the two cannot drift.
|
|
36
|
+
*/
|
|
37
|
+
declare const IMPORT_EXTENSIONS: readonly ["js", "none", "ts"];
|
|
38
|
+
/** What `importExtension` means when nothing sets it. */
|
|
39
|
+
declare const DEFAULT_IMPORT_EXTENSION: ImportExtension;
|
|
40
|
+
/** Name of the file a table is written to, e.g. `users.zod.ts`. */
|
|
41
|
+
declare function moduleFileName(tsName: string, fileSuffix: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Rewrite the extension of a relative path naming a generated file into the form an import
|
|
44
|
+
* specifier has to spell, e.g. `./users.zod.ts` -> `./users.zod.js`.
|
|
45
|
+
*
|
|
46
|
+
* A path that ends in no TypeScript extension is left whole: such a file cannot be imported
|
|
47
|
+
* at all, and naming a neighbour that does not exist would only hide that.
|
|
48
|
+
*/
|
|
49
|
+
declare function importSpecifier(relativePath: string, importExtension?: ImportExtension): string;
|
|
50
|
+
/**
|
|
51
|
+
* Relative specifier a sibling module needs to import a table's file, e.g.
|
|
52
|
+
* `./users.zod.js`.
|
|
53
|
+
*/
|
|
54
|
+
declare function moduleSpecifier(tsName: string, fileSuffix: string, importExtension?: ImportExtension): string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* One place that decides what a generated identifier is called.
|
|
58
|
+
*
|
|
59
|
+
* Every generator used to build these names by hand with template literals, which is why
|
|
60
|
+
* the oRPC router and the zod/valibot/arktype generators could silently disagree about the
|
|
61
|
+
* same name. They all call through here now, so a single resolved value describes both
|
|
62
|
+
* sides of the import.
|
|
63
|
+
*
|
|
64
|
+
* Defaults reproduce the pre-affix output byte for byte:
|
|
65
|
+
* schema: Insert|Update|Select + <tsName> + (schemaSuffix ?? 'Schema')
|
|
66
|
+
* type: Insert|Update|Select + <tsName> + Input|Input|Output
|
|
67
|
+
*/
|
|
68
|
+
type NameMode = 'insert' | 'update' | 'select';
|
|
69
|
+
/** How the Drizzle export name is cased before it goes into an identifier. */
|
|
70
|
+
type TableCase = 'preserve' | 'pascal';
|
|
71
|
+
/** One affix for all three modes, or a per-mode override map. */
|
|
72
|
+
type AffixValue = string | Partial<Record<NameMode, string>>;
|
|
73
|
+
interface AffixOptions {
|
|
74
|
+
/**
|
|
75
|
+
* `preserve` (default) interpolates the Drizzle export name verbatim, which is what every
|
|
76
|
+
* released version does: `export const users` yields `InsertusersSchema`. `pascal` upper-camels
|
|
77
|
+
* it first, yielding `InsertUsersSchema`. Identifiers only; file names are never re-cased.
|
|
78
|
+
*/
|
|
79
|
+
tableCase?: TableCase;
|
|
80
|
+
/** Affixes for the exported schema constants. */
|
|
81
|
+
schema?: {
|
|
82
|
+
prefix?: AffixValue;
|
|
83
|
+
suffix?: AffixValue;
|
|
84
|
+
};
|
|
85
|
+
/** Affixes for the exported type aliases. Independent of `schema`. */
|
|
86
|
+
type?: {
|
|
87
|
+
prefix?: AffixValue;
|
|
88
|
+
suffix?: AffixValue;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
interface ResolvedAffix {
|
|
92
|
+
tableCase: TableCase;
|
|
93
|
+
schema: {
|
|
94
|
+
prefix: Record<NameMode, string>;
|
|
95
|
+
suffix: Record<NameMode, string>;
|
|
96
|
+
};
|
|
97
|
+
type: {
|
|
98
|
+
prefix: Record<NameMode, string>;
|
|
99
|
+
suffix: Record<NameMode, string>;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
interface AffixIssue {
|
|
103
|
+
/** Path relative to the affix object, e.g. `['schema', 'suffix']`. */
|
|
104
|
+
path: (string | number)[];
|
|
105
|
+
message: string;
|
|
106
|
+
}
|
|
107
|
+
declare const NAME_MODES: readonly NameMode[];
|
|
108
|
+
declare const DEFAULT_MODE_PREFIX: Readonly<Record<NameMode, string>>;
|
|
109
|
+
declare const DEFAULT_TYPE_SUFFIX: Readonly<Record<NameMode, string>>;
|
|
110
|
+
declare const DEFAULT_SCHEMA_SUFFIX = "Schema";
|
|
111
|
+
/** Table name used when a config is checked for invalid or colliding names. */
|
|
112
|
+
declare const AFFIX_PROBE_TABLE = "users";
|
|
113
|
+
/**
|
|
114
|
+
* Real PascalCase, unlike the `cap()` helpers scattered around the repo which only upcase
|
|
115
|
+
* character zero. Splits on `_`, `-`, whitespace and camel boundaries, and leaves the rest of
|
|
116
|
+
* each part alone so acronyms survive (`userID` -> `UserID`, not `Userid`).
|
|
117
|
+
*/
|
|
118
|
+
declare function pascalCase(s: string): string;
|
|
119
|
+
declare function applyTableCase(tsName: string, tableCase: TableCase): string;
|
|
120
|
+
/**
|
|
121
|
+
* Fold an `affix` block and the legacy flat `schemaSuffix` into one fully-populated value.
|
|
122
|
+
* `affix.schema.suffix` wins over `schemaSuffix`; `schemaSuffix` wins over the built-in
|
|
123
|
+
* `'Schema'`. Calling with no arguments returns exactly today's naming.
|
|
124
|
+
*/
|
|
125
|
+
declare function resolveAffix(opts?: {
|
|
126
|
+
affix?: AffixOptions;
|
|
127
|
+
schemaSuffix?: string;
|
|
128
|
+
}): ResolvedAffix;
|
|
129
|
+
/** Name of the exported schema constant, e.g. `InsertusersSchema`. */
|
|
130
|
+
declare function schemaName(mode: NameMode, tsName: string, affix: ResolvedAffix): string;
|
|
131
|
+
/** Name of the exported type alias, e.g. `InsertusersInput`. */
|
|
132
|
+
declare function typeName(mode: NameMode, tsName: string, affix: ResolvedAffix): string;
|
|
133
|
+
/**
|
|
134
|
+
* Reject affixes that cannot produce a compilable file, before anything is written:
|
|
135
|
+
* - characters that are not legal in a TypeScript identifier
|
|
136
|
+
* - two names in the same declaration space resolving to the same string
|
|
137
|
+
*
|
|
138
|
+
* A schema name equal to a type name is allowed on purpose: `export const X` and
|
|
139
|
+
* `export type X` occupy different declaration spaces, and the generators already emit
|
|
140
|
+
* `type ... = z.input<typeof ...>` pairs.
|
|
141
|
+
*
|
|
142
|
+
* Only what the caller actually wrote in `affix` is checked. The legacy flat `schemaSuffix`
|
|
143
|
+
* is not character-checked, because it never was, and rejecting it now would break configs
|
|
144
|
+
* that parse today.
|
|
145
|
+
*/
|
|
146
|
+
declare function validateAffix(affix?: AffixOptions, schemaSuffix?: string): AffixIssue[];
|
|
147
|
+
|
|
3
148
|
interface Table {
|
|
4
149
|
name: string;
|
|
5
150
|
tsName: string;
|
|
@@ -17,8 +162,26 @@ interface FormatOptions {
|
|
|
17
162
|
interface ValidationGenerateOptions {
|
|
18
163
|
outDir: string;
|
|
19
164
|
format?: FormatOptions;
|
|
165
|
+
/**
|
|
166
|
+
* What every generated file is called after the Drizzle export name, e.g. `.zod.ts`
|
|
167
|
+
* yields `users.zod.ts`. The barrel derives its import specifiers from this same value,
|
|
168
|
+
* so a custom suffix keeps resolving.
|
|
169
|
+
*/
|
|
20
170
|
fileSuffix?: string;
|
|
171
|
+
/**
|
|
172
|
+
* How the barrel spells the extension of the files it re-exports. Defaults to `'js'`,
|
|
173
|
+
* so `users.zod.ts` is imported as `./users.zod.js`, the only form that resolves under
|
|
174
|
+
* every `moduleResolution` without a compiler flag. Use `'none'` for the extensionless
|
|
175
|
+
* specifiers drzl emitted before 2.0.
|
|
176
|
+
*/
|
|
177
|
+
importExtension?: ImportExtension;
|
|
21
178
|
schemaSuffix?: string;
|
|
179
|
+
/**
|
|
180
|
+
* Prefixes, suffixes and table casing for the generated identifiers. Omit it and the
|
|
181
|
+
* output is identical to every previous version; `schemaSuffix` stays the fallback for
|
|
182
|
+
* `affix.schema.suffix`.
|
|
183
|
+
*/
|
|
184
|
+
affix?: AffixOptions;
|
|
22
185
|
coerceDates?: 'input' | 'all' | 'none';
|
|
23
186
|
emit?: {
|
|
24
187
|
select?: boolean;
|
|
@@ -38,4 +201,4 @@ declare function updateColumns(table: Table): Column[];
|
|
|
38
201
|
declare function selectColumns(table: Table): Column[];
|
|
39
202
|
declare function formatCode(code: string, filePath: string, fmt?: FormatOptions): Promise<any>;
|
|
40
203
|
|
|
41
|
-
export { type FormatOptions, type Table, type ValidationGenerateOptions, type ValidationLibrary, type ValidationRenderer, formatCode, insertColumns, isGeneratedColumn, selectColumns, updateColumns };
|
|
204
|
+
export { AFFIX_PROBE_TABLE, type AffixIssue, type AffixOptions, type AffixValue, DEFAULT_IMPORT_EXTENSION, DEFAULT_MODE_PREFIX, DEFAULT_SCHEMA_SUFFIX, DEFAULT_TYPE_SUFFIX, type FormatOptions, IMPORT_EXTENSIONS, type ImportExtension, NAME_MODES, type NameMode, type ResolvedAffix, type Table, type TableCase, type ValidationGenerateOptions, type ValidationLibrary, type ValidationRenderer, applyTableCase, formatCode, importSpecifier, insertColumns, isGeneratedColumn, moduleFileName, moduleSpecifier, pascalCase, resolveAffix, schemaName, selectColumns, typeName, updateColumns, validateAffix };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,150 @@
|
|
|
1
1
|
import { Column, Analysis } from '@drzl/analyzer';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* One place that decides what a generated module is called, on disk and in an import.
|
|
5
|
+
*
|
|
6
|
+
* The three validation generators used to name the file from `fileSuffix` but hardcode the
|
|
7
|
+
* default suffix in the barrel, so any custom `fileSuffix` produced `export * from
|
|
8
|
+
* './users.zod'` next to a file called `users.schema.ts` and the consumer's build failed on
|
|
9
|
+
* an unresolved import. Both halves are derived here from the same value now.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* How a relative import of a generated file spells its extension.
|
|
13
|
+
*
|
|
14
|
+
* Generated files land in the consumer's own source tree, so the consumer's
|
|
15
|
+
* `moduleResolution` decides which forms resolve. Measured against tsc 5.9.2 and 7.0.2, for
|
|
16
|
+
* a specifier pointing at a sibling `.ts` file:
|
|
17
|
+
*
|
|
18
|
+
* | form | bundler | node10 | node16 / nodenext (CJS) | node16 / nodenext (ESM) |
|
|
19
|
+
* | --------------- | ------- | ------ | ----------------------- | ----------------------- |
|
|
20
|
+
* | `'js'` | yes | yes | yes | yes |
|
|
21
|
+
* | `'none'` | yes | yes | yes | **no** |
|
|
22
|
+
* | `'ts'` | flag | flag | flag | flag |
|
|
23
|
+
*
|
|
24
|
+
* `'js'` is the default because it is the only form that needs no compiler flag and still
|
|
25
|
+
* resolves in every cell. `'none'` is what drzl emitted before 2.0 and is what a pipeline
|
|
26
|
+
* that cannot map `.js` back to `.ts` wants (webpack without `resolve.extensionAlias`,
|
|
27
|
+
* ts-jest without a `moduleNameMapper`). `'ts'` needs `allowImportingTsExtensions`, and is
|
|
28
|
+
* the only form Node's own type stripping accepts, so it suits a project that runs the
|
|
29
|
+
* generated `.ts` unbuilt.
|
|
30
|
+
*/
|
|
31
|
+
type ImportExtension = (typeof IMPORT_EXTENSIONS)[number];
|
|
32
|
+
/**
|
|
33
|
+
* Every value `ImportExtension` accepts, in the order documentation lists them. The type is
|
|
34
|
+
* derived from this tuple rather than declared alongside it, so a config schema built from
|
|
35
|
+
* it accepts exactly what the type allows and the two cannot drift.
|
|
36
|
+
*/
|
|
37
|
+
declare const IMPORT_EXTENSIONS: readonly ["js", "none", "ts"];
|
|
38
|
+
/** What `importExtension` means when nothing sets it. */
|
|
39
|
+
declare const DEFAULT_IMPORT_EXTENSION: ImportExtension;
|
|
40
|
+
/** Name of the file a table is written to, e.g. `users.zod.ts`. */
|
|
41
|
+
declare function moduleFileName(tsName: string, fileSuffix: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Rewrite the extension of a relative path naming a generated file into the form an import
|
|
44
|
+
* specifier has to spell, e.g. `./users.zod.ts` -> `./users.zod.js`.
|
|
45
|
+
*
|
|
46
|
+
* A path that ends in no TypeScript extension is left whole: such a file cannot be imported
|
|
47
|
+
* at all, and naming a neighbour that does not exist would only hide that.
|
|
48
|
+
*/
|
|
49
|
+
declare function importSpecifier(relativePath: string, importExtension?: ImportExtension): string;
|
|
50
|
+
/**
|
|
51
|
+
* Relative specifier a sibling module needs to import a table's file, e.g.
|
|
52
|
+
* `./users.zod.js`.
|
|
53
|
+
*/
|
|
54
|
+
declare function moduleSpecifier(tsName: string, fileSuffix: string, importExtension?: ImportExtension): string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* One place that decides what a generated identifier is called.
|
|
58
|
+
*
|
|
59
|
+
* Every generator used to build these names by hand with template literals, which is why
|
|
60
|
+
* the oRPC router and the zod/valibot/arktype generators could silently disagree about the
|
|
61
|
+
* same name. They all call through here now, so a single resolved value describes both
|
|
62
|
+
* sides of the import.
|
|
63
|
+
*
|
|
64
|
+
* Defaults reproduce the pre-affix output byte for byte:
|
|
65
|
+
* schema: Insert|Update|Select + <tsName> + (schemaSuffix ?? 'Schema')
|
|
66
|
+
* type: Insert|Update|Select + <tsName> + Input|Input|Output
|
|
67
|
+
*/
|
|
68
|
+
type NameMode = 'insert' | 'update' | 'select';
|
|
69
|
+
/** How the Drizzle export name is cased before it goes into an identifier. */
|
|
70
|
+
type TableCase = 'preserve' | 'pascal';
|
|
71
|
+
/** One affix for all three modes, or a per-mode override map. */
|
|
72
|
+
type AffixValue = string | Partial<Record<NameMode, string>>;
|
|
73
|
+
interface AffixOptions {
|
|
74
|
+
/**
|
|
75
|
+
* `preserve` (default) interpolates the Drizzle export name verbatim, which is what every
|
|
76
|
+
* released version does: `export const users` yields `InsertusersSchema`. `pascal` upper-camels
|
|
77
|
+
* it first, yielding `InsertUsersSchema`. Identifiers only; file names are never re-cased.
|
|
78
|
+
*/
|
|
79
|
+
tableCase?: TableCase;
|
|
80
|
+
/** Affixes for the exported schema constants. */
|
|
81
|
+
schema?: {
|
|
82
|
+
prefix?: AffixValue;
|
|
83
|
+
suffix?: AffixValue;
|
|
84
|
+
};
|
|
85
|
+
/** Affixes for the exported type aliases. Independent of `schema`. */
|
|
86
|
+
type?: {
|
|
87
|
+
prefix?: AffixValue;
|
|
88
|
+
suffix?: AffixValue;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
interface ResolvedAffix {
|
|
92
|
+
tableCase: TableCase;
|
|
93
|
+
schema: {
|
|
94
|
+
prefix: Record<NameMode, string>;
|
|
95
|
+
suffix: Record<NameMode, string>;
|
|
96
|
+
};
|
|
97
|
+
type: {
|
|
98
|
+
prefix: Record<NameMode, string>;
|
|
99
|
+
suffix: Record<NameMode, string>;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
interface AffixIssue {
|
|
103
|
+
/** Path relative to the affix object, e.g. `['schema', 'suffix']`. */
|
|
104
|
+
path: (string | number)[];
|
|
105
|
+
message: string;
|
|
106
|
+
}
|
|
107
|
+
declare const NAME_MODES: readonly NameMode[];
|
|
108
|
+
declare const DEFAULT_MODE_PREFIX: Readonly<Record<NameMode, string>>;
|
|
109
|
+
declare const DEFAULT_TYPE_SUFFIX: Readonly<Record<NameMode, string>>;
|
|
110
|
+
declare const DEFAULT_SCHEMA_SUFFIX = "Schema";
|
|
111
|
+
/** Table name used when a config is checked for invalid or colliding names. */
|
|
112
|
+
declare const AFFIX_PROBE_TABLE = "users";
|
|
113
|
+
/**
|
|
114
|
+
* Real PascalCase, unlike the `cap()` helpers scattered around the repo which only upcase
|
|
115
|
+
* character zero. Splits on `_`, `-`, whitespace and camel boundaries, and leaves the rest of
|
|
116
|
+
* each part alone so acronyms survive (`userID` -> `UserID`, not `Userid`).
|
|
117
|
+
*/
|
|
118
|
+
declare function pascalCase(s: string): string;
|
|
119
|
+
declare function applyTableCase(tsName: string, tableCase: TableCase): string;
|
|
120
|
+
/**
|
|
121
|
+
* Fold an `affix` block and the legacy flat `schemaSuffix` into one fully-populated value.
|
|
122
|
+
* `affix.schema.suffix` wins over `schemaSuffix`; `schemaSuffix` wins over the built-in
|
|
123
|
+
* `'Schema'`. Calling with no arguments returns exactly today's naming.
|
|
124
|
+
*/
|
|
125
|
+
declare function resolveAffix(opts?: {
|
|
126
|
+
affix?: AffixOptions;
|
|
127
|
+
schemaSuffix?: string;
|
|
128
|
+
}): ResolvedAffix;
|
|
129
|
+
/** Name of the exported schema constant, e.g. `InsertusersSchema`. */
|
|
130
|
+
declare function schemaName(mode: NameMode, tsName: string, affix: ResolvedAffix): string;
|
|
131
|
+
/** Name of the exported type alias, e.g. `InsertusersInput`. */
|
|
132
|
+
declare function typeName(mode: NameMode, tsName: string, affix: ResolvedAffix): string;
|
|
133
|
+
/**
|
|
134
|
+
* Reject affixes that cannot produce a compilable file, before anything is written:
|
|
135
|
+
* - characters that are not legal in a TypeScript identifier
|
|
136
|
+
* - two names in the same declaration space resolving to the same string
|
|
137
|
+
*
|
|
138
|
+
* A schema name equal to a type name is allowed on purpose: `export const X` and
|
|
139
|
+
* `export type X` occupy different declaration spaces, and the generators already emit
|
|
140
|
+
* `type ... = z.input<typeof ...>` pairs.
|
|
141
|
+
*
|
|
142
|
+
* Only what the caller actually wrote in `affix` is checked. The legacy flat `schemaSuffix`
|
|
143
|
+
* is not character-checked, because it never was, and rejecting it now would break configs
|
|
144
|
+
* that parse today.
|
|
145
|
+
*/
|
|
146
|
+
declare function validateAffix(affix?: AffixOptions, schemaSuffix?: string): AffixIssue[];
|
|
147
|
+
|
|
3
148
|
interface Table {
|
|
4
149
|
name: string;
|
|
5
150
|
tsName: string;
|
|
@@ -17,8 +162,26 @@ interface FormatOptions {
|
|
|
17
162
|
interface ValidationGenerateOptions {
|
|
18
163
|
outDir: string;
|
|
19
164
|
format?: FormatOptions;
|
|
165
|
+
/**
|
|
166
|
+
* What every generated file is called after the Drizzle export name, e.g. `.zod.ts`
|
|
167
|
+
* yields `users.zod.ts`. The barrel derives its import specifiers from this same value,
|
|
168
|
+
* so a custom suffix keeps resolving.
|
|
169
|
+
*/
|
|
20
170
|
fileSuffix?: string;
|
|
171
|
+
/**
|
|
172
|
+
* How the barrel spells the extension of the files it re-exports. Defaults to `'js'`,
|
|
173
|
+
* so `users.zod.ts` is imported as `./users.zod.js`, the only form that resolves under
|
|
174
|
+
* every `moduleResolution` without a compiler flag. Use `'none'` for the extensionless
|
|
175
|
+
* specifiers drzl emitted before 2.0.
|
|
176
|
+
*/
|
|
177
|
+
importExtension?: ImportExtension;
|
|
21
178
|
schemaSuffix?: string;
|
|
179
|
+
/**
|
|
180
|
+
* Prefixes, suffixes and table casing for the generated identifiers. Omit it and the
|
|
181
|
+
* output is identical to every previous version; `schemaSuffix` stays the fallback for
|
|
182
|
+
* `affix.schema.suffix`.
|
|
183
|
+
*/
|
|
184
|
+
affix?: AffixOptions;
|
|
22
185
|
coerceDates?: 'input' | 'all' | 'none';
|
|
23
186
|
emit?: {
|
|
24
187
|
select?: boolean;
|
|
@@ -38,4 +201,4 @@ declare function updateColumns(table: Table): Column[];
|
|
|
38
201
|
declare function selectColumns(table: Table): Column[];
|
|
39
202
|
declare function formatCode(code: string, filePath: string, fmt?: FormatOptions): Promise<any>;
|
|
40
203
|
|
|
41
|
-
export { type FormatOptions, type Table, type ValidationGenerateOptions, type ValidationLibrary, type ValidationRenderer, formatCode, insertColumns, isGeneratedColumn, selectColumns, updateColumns };
|
|
204
|
+
export { AFFIX_PROBE_TABLE, type AffixIssue, type AffixOptions, type AffixValue, DEFAULT_IMPORT_EXTENSION, DEFAULT_MODE_PREFIX, DEFAULT_SCHEMA_SUFFIX, DEFAULT_TYPE_SUFFIX, type FormatOptions, IMPORT_EXTENSIONS, type ImportExtension, NAME_MODES, type NameMode, type ResolvedAffix, type Table, type TableCase, type ValidationGenerateOptions, type ValidationLibrary, type ValidationRenderer, applyTableCase, formatCode, importSpecifier, insertColumns, isGeneratedColumn, moduleFileName, moduleSpecifier, pascalCase, resolveAffix, schemaName, selectColumns, typeName, updateColumns, validateAffix };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,136 @@
|
|
|
1
1
|
import "./chunk-MKBO26DX.js";
|
|
2
2
|
|
|
3
|
+
// src/files.ts
|
|
4
|
+
var IMPORT_EXTENSIONS = ["js", "none", "ts"];
|
|
5
|
+
var DEFAULT_IMPORT_EXTENSION = "js";
|
|
6
|
+
var TS_EXTENSIONS = [
|
|
7
|
+
{ ext: ".mts", js: ".mjs", none: ".mjs" },
|
|
8
|
+
{ ext: ".cts", js: ".cjs", none: ".cjs" },
|
|
9
|
+
{ ext: ".tsx", js: ".js", none: "" },
|
|
10
|
+
{ ext: ".ts", js: ".js", none: "" }
|
|
11
|
+
];
|
|
12
|
+
function moduleFileName(tsName, fileSuffix) {
|
|
13
|
+
return `${tsName}${fileSuffix}`;
|
|
14
|
+
}
|
|
15
|
+
function importSpecifier(relativePath, importExtension = DEFAULT_IMPORT_EXTENSION) {
|
|
16
|
+
for (const { ext, js, none } of TS_EXTENSIONS) {
|
|
17
|
+
if (!relativePath.endsWith(ext)) continue;
|
|
18
|
+
const stem = relativePath.slice(0, -ext.length);
|
|
19
|
+
if (importExtension === "ts") return relativePath;
|
|
20
|
+
return `${stem}${importExtension === "none" ? none : js}`;
|
|
21
|
+
}
|
|
22
|
+
return relativePath;
|
|
23
|
+
}
|
|
24
|
+
function moduleSpecifier(tsName, fileSuffix, importExtension = DEFAULT_IMPORT_EXTENSION) {
|
|
25
|
+
return importSpecifier(`./${moduleFileName(tsName, fileSuffix)}`, importExtension);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/naming.ts
|
|
29
|
+
var NAME_MODES = ["insert", "update", "select"];
|
|
30
|
+
var DEFAULT_MODE_PREFIX = {
|
|
31
|
+
insert: "Insert",
|
|
32
|
+
update: "Update",
|
|
33
|
+
select: "Select"
|
|
34
|
+
};
|
|
35
|
+
var DEFAULT_TYPE_SUFFIX = {
|
|
36
|
+
insert: "Input",
|
|
37
|
+
update: "Input",
|
|
38
|
+
select: "Output"
|
|
39
|
+
};
|
|
40
|
+
var DEFAULT_SCHEMA_SUFFIX = "Schema";
|
|
41
|
+
var AFFIX_PROBE_TABLE = "users";
|
|
42
|
+
function spread(value, fallback) {
|
|
43
|
+
if (value === void 0) return { ...fallback };
|
|
44
|
+
if (typeof value === "string") return { insert: value, update: value, select: value };
|
|
45
|
+
return {
|
|
46
|
+
insert: value.insert ?? fallback.insert,
|
|
47
|
+
update: value.update ?? fallback.update,
|
|
48
|
+
select: value.select ?? fallback.select
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function pascalCase(s) {
|
|
52
|
+
return s.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[\s_-]+/).filter(Boolean).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
|
|
53
|
+
}
|
|
54
|
+
function applyTableCase(tsName, tableCase) {
|
|
55
|
+
return tableCase === "pascal" ? pascalCase(tsName) : tsName;
|
|
56
|
+
}
|
|
57
|
+
function resolveAffix(opts) {
|
|
58
|
+
const affix = opts?.affix;
|
|
59
|
+
const legacy = opts?.schemaSuffix ?? DEFAULT_SCHEMA_SUFFIX;
|
|
60
|
+
const legacyMap = {
|
|
61
|
+
insert: legacy,
|
|
62
|
+
update: legacy,
|
|
63
|
+
select: legacy
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
tableCase: affix?.tableCase ?? "preserve",
|
|
67
|
+
schema: {
|
|
68
|
+
prefix: spread(affix?.schema?.prefix, DEFAULT_MODE_PREFIX),
|
|
69
|
+
suffix: spread(affix?.schema?.suffix, legacyMap)
|
|
70
|
+
},
|
|
71
|
+
type: {
|
|
72
|
+
prefix: spread(affix?.type?.prefix, DEFAULT_MODE_PREFIX),
|
|
73
|
+
suffix: spread(affix?.type?.suffix, DEFAULT_TYPE_SUFFIX)
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function schemaName(mode, tsName, affix) {
|
|
78
|
+
return affix.schema.prefix[mode] + applyTableCase(tsName, affix.tableCase) + affix.schema.suffix[mode];
|
|
79
|
+
}
|
|
80
|
+
function typeName(mode, tsName, affix) {
|
|
81
|
+
return affix.type.prefix[mode] + applyTableCase(tsName, affix.tableCase) + affix.type.suffix[mode];
|
|
82
|
+
}
|
|
83
|
+
var PREFIX_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
84
|
+
var SUFFIX_RE = /^[A-Za-z0-9_$]+$/;
|
|
85
|
+
function validateAffix(affix, schemaSuffix) {
|
|
86
|
+
const issues = [];
|
|
87
|
+
if (!affix) return issues;
|
|
88
|
+
const checkOne = (value, path, kind) => {
|
|
89
|
+
if (value === "") return;
|
|
90
|
+
const ok = kind === "prefix" ? PREFIX_RE.test(value) : SUFFIX_RE.test(value);
|
|
91
|
+
if (ok) return;
|
|
92
|
+
issues.push({
|
|
93
|
+
path,
|
|
94
|
+
message: `${JSON.stringify(value)} cannot appear in a TypeScript identifier. Use only letters, digits, "_" and "$"` + (kind === "prefix" ? ", and do not start with a digit." : ".")
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
const checkValue = (value, base, kind) => {
|
|
98
|
+
if (value === void 0) return;
|
|
99
|
+
if (typeof value === "string") {
|
|
100
|
+
checkOne(value, base, kind);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
for (const mode of NAME_MODES) {
|
|
104
|
+
const v = value[mode];
|
|
105
|
+
if (v !== void 0) checkOne(v, [...base, mode], kind);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
checkValue(affix.schema?.prefix, ["schema", "prefix"], "prefix");
|
|
109
|
+
checkValue(affix.schema?.suffix, ["schema", "suffix"], "suffix");
|
|
110
|
+
checkValue(affix.type?.prefix, ["type", "prefix"], "prefix");
|
|
111
|
+
checkValue(affix.type?.suffix, ["type", "suffix"], "suffix");
|
|
112
|
+
if (issues.length) return issues;
|
|
113
|
+
const resolved = resolveAffix({ affix, schemaSuffix });
|
|
114
|
+
const collisions = (space, build) => {
|
|
115
|
+
const seen = /* @__PURE__ */ new Map();
|
|
116
|
+
for (const mode of NAME_MODES) {
|
|
117
|
+
const name = build(mode);
|
|
118
|
+
const first = seen.get(name);
|
|
119
|
+
if (first) {
|
|
120
|
+
issues.push({
|
|
121
|
+
path: [space],
|
|
122
|
+
message: `The ${space} names for "${first}" and "${mode}" collide: both resolve to "${name}". All three are emitted into the same file, so at least one prefix or suffix has to differ.`
|
|
123
|
+
});
|
|
124
|
+
} else {
|
|
125
|
+
seen.set(name, mode);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
collisions("schema", (mode) => schemaName(mode, AFFIX_PROBE_TABLE, resolved));
|
|
130
|
+
collisions("type", (mode) => typeName(mode, AFFIX_PROBE_TABLE, resolved));
|
|
131
|
+
return issues;
|
|
132
|
+
}
|
|
133
|
+
|
|
3
134
|
// src/index.ts
|
|
4
135
|
function isGeneratedColumn(c, primaryKeyColumns) {
|
|
5
136
|
return c.isGenerated || primaryKeyColumns.includes(c.name);
|
|
@@ -41,9 +172,25 @@ async function formatCode(code, filePath, fmt) {
|
|
|
41
172
|
return code;
|
|
42
173
|
}
|
|
43
174
|
export {
|
|
175
|
+
AFFIX_PROBE_TABLE,
|
|
176
|
+
DEFAULT_IMPORT_EXTENSION,
|
|
177
|
+
DEFAULT_MODE_PREFIX,
|
|
178
|
+
DEFAULT_SCHEMA_SUFFIX,
|
|
179
|
+
DEFAULT_TYPE_SUFFIX,
|
|
180
|
+
IMPORT_EXTENSIONS,
|
|
181
|
+
NAME_MODES,
|
|
182
|
+
applyTableCase,
|
|
44
183
|
formatCode,
|
|
184
|
+
importSpecifier,
|
|
45
185
|
insertColumns,
|
|
46
186
|
isGeneratedColumn,
|
|
187
|
+
moduleFileName,
|
|
188
|
+
moduleSpecifier,
|
|
189
|
+
pascalCase,
|
|
190
|
+
resolveAffix,
|
|
191
|
+
schemaName,
|
|
47
192
|
selectColumns,
|
|
48
|
-
|
|
193
|
+
typeName,
|
|
194
|
+
updateColumns,
|
|
195
|
+
validateAffix
|
|
49
196
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drzl/validation-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"sideEffects": false,
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@drzl/analyzer": "^1.
|
|
14
|
+
"@drzl/analyzer": "^1.3.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"tsup": "^8.5.0",
|