prisma-normalize 0.1.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/LICENSE +20 -0
- package/README.md +111 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +82 -0
- package/dist/cli.js.map +1 -0
- package/dist/normalize.d.ts +11 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +133 -0
- package/dist/normalize.js.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yaroslavnikiforov
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# prisma-normalize
|
|
2
|
+
|
|
3
|
+
Rewrite snake_case identifiers in Prisma schema files to PascalCase / camelCase while preserving the original database names via `@map` and `@@map`.
|
|
4
|
+
|
|
5
|
+
Useful after `prisma db pull` against a snake_case database when you want idiomatic camelCase TypeScript without touching the schema by hand.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -D prisma-normalize
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Use
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# walk a directory for .prisma files and rewrite in place
|
|
17
|
+
prisma-normalize ./prisma
|
|
18
|
+
|
|
19
|
+
# or a specific file
|
|
20
|
+
prisma-normalize ./prisma/schema.prisma
|
|
21
|
+
|
|
22
|
+
# CI mode: don't write, exit 1 if anything would change
|
|
23
|
+
prisma-normalize --check ./prisma
|
|
24
|
+
|
|
25
|
+
# print what would change without writing
|
|
26
|
+
prisma-normalize --dry-run ./prisma
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Wire into your workflow:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"scripts": {
|
|
34
|
+
"prisma:pull": "prisma db pull && prisma-normalize ./prisma && prisma format",
|
|
35
|
+
"lint:prisma": "prisma-normalize --check ./prisma"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## What it does
|
|
41
|
+
|
|
42
|
+
Given:
|
|
43
|
+
|
|
44
|
+
```prisma
|
|
45
|
+
model blog_post {
|
|
46
|
+
id Int @id @default(autoincrement())
|
|
47
|
+
author_id Int
|
|
48
|
+
created_at DateTime @default(now())
|
|
49
|
+
title String @db.VarChar(255)
|
|
50
|
+
author user @relation(fields: [author_id], references: [id])
|
|
51
|
+
|
|
52
|
+
@@index([author_id])
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
It produces:
|
|
57
|
+
|
|
58
|
+
```prisma
|
|
59
|
+
model BlogPost {
|
|
60
|
+
id Int @id @default(autoincrement())
|
|
61
|
+
authorId Int @map("author_id")
|
|
62
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
63
|
+
title String @db.VarChar(255)
|
|
64
|
+
author User @relation(fields: [authorId], references: [id])
|
|
65
|
+
|
|
66
|
+
@@index([authorId])
|
|
67
|
+
@@map("blog_post")
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The database schema is untouched — Prisma translates camelCase ↔ snake_case at query time.
|
|
72
|
+
|
|
73
|
+
## Rules
|
|
74
|
+
|
|
75
|
+
- `model` / `view` / `enum` names in snake_case → PascalCase, with `@@map("original_name")` appended.
|
|
76
|
+
- Scalar field names in snake_case → camelCase, with `@map("original_name")` appended.
|
|
77
|
+
- Relation field names → camelCase, **no** `@map` (relations are not DB columns).
|
|
78
|
+
- Field type references update to the renamed type.
|
|
79
|
+
- `@relation(fields: [...])`, `@@index([...])`, `@@unique([...])`, `@@id([...])` field lists update to the renamed field names.
|
|
80
|
+
- Already-normalized blocks are left alone — the transform is idempotent.
|
|
81
|
+
- Constraint names inside attributes (`@id(map: "pk_…")`, `@@index([…], map: "idx_…")`) are preserved untouched.
|
|
82
|
+
|
|
83
|
+
## Multiple files
|
|
84
|
+
|
|
85
|
+
When you pass a directory, the CLI does a two-pass walk: first it scans every `.prisma` file to learn all model/enum/view names, then it transforms each file using that shared map. This is what makes cross-file type references rename correctly — e.g. a `user` relation field in `post.prisma` becomes `User` because `model user` was discovered in `user.prisma`.
|
|
86
|
+
|
|
87
|
+
After running, you'll usually want to chain `prisma format` to tidy column alignment:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
prisma-normalize ./prisma && prisma format
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Library API
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { normalizeSchema, buildTypeMap } from 'prisma-normalize'
|
|
97
|
+
|
|
98
|
+
// Single file — local typeMap built automatically:
|
|
99
|
+
const { output, changed } = normalizeSchema(schemaText)
|
|
100
|
+
|
|
101
|
+
// Multiple files sharing a typeMap (recommended for projects split across files):
|
|
102
|
+
const typeMap = buildTypeMap(allFileContents)
|
|
103
|
+
for (const content of allFileContents) {
|
|
104
|
+
const { output } = normalizeSchema(content, typeMap)
|
|
105
|
+
// write output back...
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { buildTypeMap, normalizeSchema } from './normalize.js';
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const flags = new Set(args.filter(a => a.startsWith('--') || a === '-h'));
|
|
7
|
+
const paths = args.filter(a => !a.startsWith('-'));
|
|
8
|
+
const isCheck = flags.has('--check');
|
|
9
|
+
const isDryRun = flags.has('--dry-run');
|
|
10
|
+
const isHelp = flags.has('--help') || flags.has('-h');
|
|
11
|
+
if (isHelp || paths.length === 0) {
|
|
12
|
+
printHelp();
|
|
13
|
+
process.exit(isHelp ? 0 : 2);
|
|
14
|
+
}
|
|
15
|
+
function printHelp() {
|
|
16
|
+
process.stdout.write(`Usage: prisma-normalize [options] <path>...
|
|
17
|
+
|
|
18
|
+
Walks <path>(s) for .prisma files and rewrites snake_case
|
|
19
|
+
identifiers to PascalCase/camelCase while preserving the
|
|
20
|
+
original DB names via @map / @@map directives.
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
--check Don't write; exit 1 if any file would change. For CI.
|
|
24
|
+
--dry-run Don't write; print the normalized output to stdout.
|
|
25
|
+
--help, -h Show this help.
|
|
26
|
+
`);
|
|
27
|
+
}
|
|
28
|
+
function findPrismaFiles(p) {
|
|
29
|
+
const abs = resolve(p);
|
|
30
|
+
const stat = statSync(abs);
|
|
31
|
+
if (stat.isFile()) {
|
|
32
|
+
return abs.endsWith('.prisma') ? [abs] : [];
|
|
33
|
+
}
|
|
34
|
+
const out = [];
|
|
35
|
+
for (const entry of readdirSync(abs, { withFileTypes: true })) {
|
|
36
|
+
if (entry.name === 'node_modules' || entry.name.startsWith('.'))
|
|
37
|
+
continue;
|
|
38
|
+
const full = join(abs, entry.name);
|
|
39
|
+
if (entry.isDirectory()) {
|
|
40
|
+
out.push(...findPrismaFiles(full));
|
|
41
|
+
}
|
|
42
|
+
else if (entry.name.endsWith('.prisma')) {
|
|
43
|
+
out.push(full);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
const allFiles = paths.flatMap(findPrismaFiles);
|
|
49
|
+
if (allFiles.length === 0) {
|
|
50
|
+
process.stderr.write('No .prisma files found.\n');
|
|
51
|
+
process.exit(2);
|
|
52
|
+
}
|
|
53
|
+
const fileContents = allFiles.map(file => ({
|
|
54
|
+
file,
|
|
55
|
+
content: readFileSync(file, 'utf8'),
|
|
56
|
+
}));
|
|
57
|
+
const typeMap = buildTypeMap(fileContents.map(f => f.content));
|
|
58
|
+
let changedCount = 0;
|
|
59
|
+
let unchangedCount = 0;
|
|
60
|
+
for (const { file, content } of fileContents) {
|
|
61
|
+
const { output, changed } = normalizeSchema(content, typeMap);
|
|
62
|
+
if (!changed) {
|
|
63
|
+
unchangedCount++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
changedCount++;
|
|
67
|
+
if (isCheck) {
|
|
68
|
+
process.stdout.write(`would change: ${file}\n`);
|
|
69
|
+
}
|
|
70
|
+
else if (isDryRun) {
|
|
71
|
+
process.stdout.write(`--- ${file} ---\n${output}\n`);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
writeFileSync(file, output, 'utf8');
|
|
75
|
+
process.stdout.write(`changed: ${file}\n`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
process.stdout.write(`\n${changedCount} changed, ${unchangedCount} unchanged (${allFiles.length} total)\n`);
|
|
79
|
+
if (isCheck && changedCount > 0) {
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type NormalizeResult = {
|
|
2
|
+
output: string;
|
|
3
|
+
changed: boolean;
|
|
4
|
+
};
|
|
5
|
+
export type TypeMap = {
|
|
6
|
+
renameMap: Map<string, string>;
|
|
7
|
+
modelNames: Set<string>;
|
|
8
|
+
};
|
|
9
|
+
export declare function buildTypeMap(inputs: string[]): TypeMap;
|
|
10
|
+
export declare function normalizeSchema(input: string, typeMap?: TypeMap): NormalizeResult;
|
|
11
|
+
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CACxB,CAAA;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAStD;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,OAAO,GAChB,eAAe,CAwBjB"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export function buildTypeMap(inputs) {
|
|
2
|
+
const renameMap = new Map();
|
|
3
|
+
const modelNames = new Set();
|
|
4
|
+
for (const input of inputs) {
|
|
5
|
+
const local = collectBlockNames(input);
|
|
6
|
+
for (const [k, v] of local.renameMap)
|
|
7
|
+
renameMap.set(k, v);
|
|
8
|
+
for (const n of local.modelNames)
|
|
9
|
+
modelNames.add(n);
|
|
10
|
+
}
|
|
11
|
+
return { renameMap, modelNames };
|
|
12
|
+
}
|
|
13
|
+
export function normalizeSchema(input, typeMap) {
|
|
14
|
+
const { renameMap, modelNames } = typeMap ?? collectBlockNames(input);
|
|
15
|
+
const output = input.replace(/^(model|enum|view|type)\s+(\w+)\s*\{([\s\S]*?)^\}/gm, (_match, kind, name, body) => {
|
|
16
|
+
const newName = renameMap.get(name) ?? name;
|
|
17
|
+
const wasRenamed = newName !== name;
|
|
18
|
+
const isModelLike = kind === 'model' || kind === 'view' || kind === 'type';
|
|
19
|
+
const newBody = isModelLike
|
|
20
|
+
? transformModelBody(body, renameMap, modelNames)
|
|
21
|
+
: body;
|
|
22
|
+
const finalBody = wasRenamed && !/@@map\(/.test(body)
|
|
23
|
+
? appendMapDirective(newBody, name)
|
|
24
|
+
: newBody;
|
|
25
|
+
return `${kind} ${newName} {${finalBody}}`;
|
|
26
|
+
});
|
|
27
|
+
return { output, changed: output !== input };
|
|
28
|
+
}
|
|
29
|
+
function collectBlockNames(input) {
|
|
30
|
+
const renameMap = new Map();
|
|
31
|
+
const modelNames = new Set();
|
|
32
|
+
const blockHeaderRe = /^(model|enum|view|type)\s+(\w+)\s*\{/gm;
|
|
33
|
+
let m;
|
|
34
|
+
while ((m = blockHeaderRe.exec(input)) !== null) {
|
|
35
|
+
const kind = m[1];
|
|
36
|
+
const name = m[2];
|
|
37
|
+
if (kind === 'model' || kind === 'view' || kind === 'type') {
|
|
38
|
+
modelNames.add(name);
|
|
39
|
+
}
|
|
40
|
+
if (needsRename(name)) {
|
|
41
|
+
renameMap.set(name, toPascal(name));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { renameMap, modelNames };
|
|
45
|
+
}
|
|
46
|
+
function transformModelBody(body, renameMap, modelNames) {
|
|
47
|
+
return body
|
|
48
|
+
.split('\n')
|
|
49
|
+
.map(line => transformLine(line, renameMap, modelNames))
|
|
50
|
+
.join('\n');
|
|
51
|
+
}
|
|
52
|
+
function transformLine(line, renameMap, modelNames) {
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
if (!trimmed)
|
|
55
|
+
return line;
|
|
56
|
+
if (trimmed.startsWith('//'))
|
|
57
|
+
return line;
|
|
58
|
+
if (trimmed.startsWith('@@'))
|
|
59
|
+
return transformBlockAttr(line);
|
|
60
|
+
const fieldRe = /^(\s*)(\w+)(\s+)(\w+(?:\?|\[\])?)([ \t].*)?$/;
|
|
61
|
+
const m = fieldRe.exec(line);
|
|
62
|
+
if (!m)
|
|
63
|
+
return line;
|
|
64
|
+
const indent = m[1];
|
|
65
|
+
const fieldName = m[2];
|
|
66
|
+
const sep = m[3];
|
|
67
|
+
const typeAnno = m[4];
|
|
68
|
+
const rest = m[5] ?? '';
|
|
69
|
+
const baseType = typeAnno.replace(/(\?|\[\])$/, '');
|
|
70
|
+
const typeSuffix = typeAnno.slice(baseType.length);
|
|
71
|
+
const renamedBase = renameMap.get(baseType);
|
|
72
|
+
const newType = renamedBase ? `${renamedBase}${typeSuffix}` : typeAnno;
|
|
73
|
+
const isRelationField = modelNames.has(baseType);
|
|
74
|
+
let newFieldName = fieldName;
|
|
75
|
+
let mapAttr = '';
|
|
76
|
+
if (fieldName.includes('_') && needsRename(fieldName)) {
|
|
77
|
+
newFieldName = toCamel(fieldName);
|
|
78
|
+
if (!isRelationField && !/@map\(/.test(rest)) {
|
|
79
|
+
mapAttr = ` @map("${fieldName}")`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const newRest = rewriteRelationFieldRefs(rest);
|
|
83
|
+
const newSep = adjustSep(sep, fieldName.length, newFieldName.length);
|
|
84
|
+
return `${indent}${newFieldName}${newSep}${newType}${newRest}${mapAttr}`;
|
|
85
|
+
}
|
|
86
|
+
function transformBlockAttr(line) {
|
|
87
|
+
return line.replace(/(@@(?:index|unique|id))\(\[([^\]]+)\]/g, (_full, prefix, list) => {
|
|
88
|
+
const items = list.split(',').map(s => s.trim());
|
|
89
|
+
const newItems = items.map(f => f.includes('_') && needsRename(f) ? toCamel(f) : f);
|
|
90
|
+
return `${prefix}([${newItems.join(', ')}]`;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function rewriteRelationFieldRefs(rest) {
|
|
94
|
+
return rest.replace(/@relation\(([^)]*)\)/g, (_full, args) => {
|
|
95
|
+
const updated = args
|
|
96
|
+
.replace(/fields:\s*\[([^\]]*)\]/g, (_a, list) => `fields: [${renameFieldList(list)}]`)
|
|
97
|
+
.replace(/references:\s*\[([^\]]*)\]/g, (_a, list) => `references: [${renameFieldList(list)}]`);
|
|
98
|
+
return `@relation(${updated})`;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function renameFieldList(list) {
|
|
102
|
+
return list
|
|
103
|
+
.split(',')
|
|
104
|
+
.map(s => s.trim())
|
|
105
|
+
.map(f => (f.includes('_') && needsRename(f) ? toCamel(f) : f))
|
|
106
|
+
.join(', ');
|
|
107
|
+
}
|
|
108
|
+
function appendMapDirective(body, originalName) {
|
|
109
|
+
const trimmed = body.replace(/\s+$/, '');
|
|
110
|
+
return `${trimmed}\n\n @@map("${originalName}")\n`;
|
|
111
|
+
}
|
|
112
|
+
function adjustSep(sep, oldLen, newLen) {
|
|
113
|
+
const diff = newLen - oldLen;
|
|
114
|
+
if (diff === 0)
|
|
115
|
+
return sep;
|
|
116
|
+
if (diff > 0) {
|
|
117
|
+
if (sep.length > diff)
|
|
118
|
+
return ' '.repeat(sep.length - diff);
|
|
119
|
+
return ' ';
|
|
120
|
+
}
|
|
121
|
+
return sep + ' '.repeat(-diff);
|
|
122
|
+
}
|
|
123
|
+
function needsRename(name) {
|
|
124
|
+
return /^[a-z][a-z0-9_]*$/.test(name);
|
|
125
|
+
}
|
|
126
|
+
function toCamel(s) {
|
|
127
|
+
return s.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
128
|
+
}
|
|
129
|
+
function toPascal(s) {
|
|
130
|
+
const camel = toCamel(s);
|
|
131
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "prisma-normalize",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Rewrite snake_case identifiers in Prisma schema files to PascalCase/camelCase while preserving the original DB names via @map / @@map.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"prisma-normalize": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/normalize.js",
|
|
10
|
+
"types": "./dist/normalize.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/normalize.d.ts",
|
|
14
|
+
"import": "./dist/normalize.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"prisma",
|
|
27
|
+
"schema",
|
|
28
|
+
"snake-case",
|
|
29
|
+
"camel-case",
|
|
30
|
+
"normalize",
|
|
31
|
+
"map"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc && chmod +x dist/cli.js",
|
|
35
|
+
"test": "node --import tsx --test tests/*.test.ts",
|
|
36
|
+
"clean": "rm -rf dist",
|
|
37
|
+
"prepublishOnly": "npm run clean && npm run build && npm test"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/yaroslavnikiforov/prisma-normalize.git"
|
|
42
|
+
},
|
|
43
|
+
"author": "yaroslavnikiforov <yaroslav.nikiforov.93@gmail.com> (https://github.com/yaroslavnikiforov)",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/yaroslavnikiforov/prisma-normalize/issues"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/yaroslavnikiforov/prisma-normalize#readme",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"registry": "https://registry.npmjs.org/",
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^20.19.0",
|
|
55
|
+
"tsx": "^4.19.2",
|
|
56
|
+
"typescript": "^5.6.3"
|
|
57
|
+
}
|
|
58
|
+
}
|