frappe-codegen 1.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/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +158 -0
- package/dist/cli.js +531 -0
- package/dist/index.d.mts +115 -0
- package/dist/index.d.ts +115 -0
- package/dist/index.js +387 -0
- package/dist/index.mjs +373 -0
- package/package.json +88 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'fs';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
// src/config.ts
|
|
5
|
+
var DEFAULT_CONFIG_NAME = "frappe-codegen.config.json";
|
|
6
|
+
function loadConfigFile(path) {
|
|
7
|
+
const raw = readFileSync(path, "utf8");
|
|
8
|
+
const parsed = JSON.parse(raw);
|
|
9
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
10
|
+
throw new Error(`frappe-codegen: ${path} must be a JSON object`);
|
|
11
|
+
}
|
|
12
|
+
const obj = parsed;
|
|
13
|
+
if ("apiKey" in obj || "apiSecret" in obj || "api-key" in obj) {
|
|
14
|
+
throw new Error("frappe-codegen: do not put API secrets in the config file; use --api-key / FRAPPE_API_KEY");
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
url: typeof obj.url === "string" ? obj.url : void 0,
|
|
18
|
+
out: typeof obj.out === "string" ? obj.out : void 0,
|
|
19
|
+
includeHidden: typeof obj.includeHidden === "boolean" ? obj.includeHidden : void 0,
|
|
20
|
+
followTables: typeof obj.followTables === "boolean" ? obj.followTables : void 0,
|
|
21
|
+
doctypes: Array.isArray(obj.doctypes) ? obj.doctypes.filter((d) => typeof d === "string") : void 0,
|
|
22
|
+
modules: Array.isArray(obj.modules) ? obj.modules.filter((d) => typeof d === "string") : void 0
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function findDefaultConfigPath(cwd = process.cwd()) {
|
|
26
|
+
const candidate = resolve(cwd, DEFAULT_CONFIG_NAME);
|
|
27
|
+
return existsSync(candidate) ? candidate : void 0;
|
|
28
|
+
}
|
|
29
|
+
function envString(name) {
|
|
30
|
+
const value = process.env[name];
|
|
31
|
+
return value && value.length > 0 ? value : void 0;
|
|
32
|
+
}
|
|
33
|
+
function mergeConfig(file, overlay) {
|
|
34
|
+
const envUrl = envString("FRAPPE_URL");
|
|
35
|
+
const envKey = envString("FRAPPE_API_KEY");
|
|
36
|
+
const envSecret = envString("FRAPPE_API_SECRET");
|
|
37
|
+
const doctypes = [...file?.doctypes ?? [], ...overlay.doctypes ?? []];
|
|
38
|
+
const modules = [...file?.modules ?? [], ...overlay.modules ?? []];
|
|
39
|
+
return {
|
|
40
|
+
url: overlay.url ?? envUrl ?? file?.url,
|
|
41
|
+
out: overlay.out ?? file?.out ?? "./frappe-types.generated.ts",
|
|
42
|
+
includeHidden: overlay.includeHidden ?? file?.includeHidden ?? false,
|
|
43
|
+
followTables: overlay.followTables ?? file?.followTables ?? true,
|
|
44
|
+
doctypes: [...new Set(doctypes)],
|
|
45
|
+
modules: [...new Set(modules)],
|
|
46
|
+
apiKey: overlay.apiKey ?? envKey,
|
|
47
|
+
apiSecret: overlay.apiSecret ?? envSecret,
|
|
48
|
+
dryRun: overlay.dryRun ?? false,
|
|
49
|
+
emitDocTypeMap: overlay.emitDocTypeMap ?? true,
|
|
50
|
+
includeLabels: overlay.includeLabels ?? true
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/field-types.ts
|
|
55
|
+
var NUMBER_FIELD_TYPES = /* @__PURE__ */ new Set(["Int", "Float", "Currency", "Percent", "Rating", "Duration"]);
|
|
56
|
+
var STRING_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
57
|
+
"Data",
|
|
58
|
+
"Small Text",
|
|
59
|
+
"Text",
|
|
60
|
+
"Long Text",
|
|
61
|
+
"Text Editor",
|
|
62
|
+
"Code",
|
|
63
|
+
"HTML Editor",
|
|
64
|
+
"Markdown Editor",
|
|
65
|
+
"Password",
|
|
66
|
+
"Read Only",
|
|
67
|
+
"Dynamic Link",
|
|
68
|
+
"Date",
|
|
69
|
+
"Datetime",
|
|
70
|
+
"Time",
|
|
71
|
+
"Attach",
|
|
72
|
+
"Attach Image",
|
|
73
|
+
"Barcode",
|
|
74
|
+
"Color",
|
|
75
|
+
"Signature",
|
|
76
|
+
"Phone",
|
|
77
|
+
"Icon",
|
|
78
|
+
"Autocomplete"
|
|
79
|
+
]);
|
|
80
|
+
var UNKNOWN_FIELD_TYPES = /* @__PURE__ */ new Set(["JSON", "Geolocation"]);
|
|
81
|
+
var LAYOUT_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
82
|
+
"Section Break",
|
|
83
|
+
"Column Break",
|
|
84
|
+
"Tab Break",
|
|
85
|
+
"Fold",
|
|
86
|
+
"Heading",
|
|
87
|
+
"Button",
|
|
88
|
+
"HTML",
|
|
89
|
+
"Image"
|
|
90
|
+
]);
|
|
91
|
+
function isTableFieldType(fieldtype) {
|
|
92
|
+
return fieldtype === "Table" || fieldtype === "Table MultiSelect";
|
|
93
|
+
}
|
|
94
|
+
function selectLiterals(options) {
|
|
95
|
+
if (!options) return [];
|
|
96
|
+
return options.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map(selectValue);
|
|
97
|
+
}
|
|
98
|
+
function selectValue(line) {
|
|
99
|
+
const colon = line.indexOf(":");
|
|
100
|
+
const comma = line.indexOf(",");
|
|
101
|
+
if (colon > 0 && (comma < 0 || colon < comma)) {
|
|
102
|
+
return line.slice(0, colon).trim();
|
|
103
|
+
}
|
|
104
|
+
if (comma > 0) {
|
|
105
|
+
return line.slice(0, comma).trim();
|
|
106
|
+
}
|
|
107
|
+
return line;
|
|
108
|
+
}
|
|
109
|
+
function mapFieldType(field, ctx = {}) {
|
|
110
|
+
const { fieldtype, options } = field;
|
|
111
|
+
if (fieldtype === "Check") {
|
|
112
|
+
return "0 | 1";
|
|
113
|
+
}
|
|
114
|
+
if (fieldtype === "Select") {
|
|
115
|
+
const literals = selectLiterals(options);
|
|
116
|
+
if (literals.length === 0) {
|
|
117
|
+
return "string";
|
|
118
|
+
}
|
|
119
|
+
return literals.map((literal) => JSON.stringify(literal)).join(" | ");
|
|
120
|
+
}
|
|
121
|
+
if (isTableFieldType(fieldtype)) {
|
|
122
|
+
const childInterface = options ? ctx.resolveChildInterfaceName?.(options) : void 0;
|
|
123
|
+
return childInterface ? `${childInterface}[]` : "FrappeDoc<Record<string, unknown>>[]";
|
|
124
|
+
}
|
|
125
|
+
if (fieldtype === "Link") {
|
|
126
|
+
if (options && options.trim().length > 0) {
|
|
127
|
+
return `Link<${JSON.stringify(options.trim())}>`;
|
|
128
|
+
}
|
|
129
|
+
return "string";
|
|
130
|
+
}
|
|
131
|
+
if (NUMBER_FIELD_TYPES.has(fieldtype)) {
|
|
132
|
+
return "number";
|
|
133
|
+
}
|
|
134
|
+
if (UNKNOWN_FIELD_TYPES.has(fieldtype)) {
|
|
135
|
+
return "unknown";
|
|
136
|
+
}
|
|
137
|
+
if (STRING_FIELD_TYPES.has(fieldtype)) {
|
|
138
|
+
return "string";
|
|
139
|
+
}
|
|
140
|
+
return "unknown";
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/generate.ts
|
|
144
|
+
var DEFAULTS = {
|
|
145
|
+
includeHidden: false,
|
|
146
|
+
includeLabels: true,
|
|
147
|
+
emitDocTypeMap: true
|
|
148
|
+
};
|
|
149
|
+
function toInterfaceName(doctype) {
|
|
150
|
+
const cleaned = doctype.replace(/[^a-zA-Z0-9 ]/g, " ").split(" ").filter(Boolean).map((word) => word[0].toUpperCase() + word.slice(1)).join("");
|
|
151
|
+
return /^[A-Za-z_]/.test(cleaned) ? cleaned : `_${cleaned}`;
|
|
152
|
+
}
|
|
153
|
+
function assertUniqueInterfaceNames(metas) {
|
|
154
|
+
const byName = /* @__PURE__ */ new Map();
|
|
155
|
+
for (const meta of metas) {
|
|
156
|
+
const id = toInterfaceName(meta.name);
|
|
157
|
+
const list = byName.get(id) ?? [];
|
|
158
|
+
list.push(meta.name);
|
|
159
|
+
byName.set(id, list);
|
|
160
|
+
}
|
|
161
|
+
const collisions = [...byName.entries()].filter(([, names]) => names.length > 1);
|
|
162
|
+
if (collisions.length === 0) return;
|
|
163
|
+
const detail = collisions.map(([id, names]) => `${id} <= ${names.map((n) => JSON.stringify(n)).join(", ")}`).join("; ");
|
|
164
|
+
throw new Error(`frappe-codegen: interface name collision: ${detail}`);
|
|
165
|
+
}
|
|
166
|
+
function shouldEmitField(field, options) {
|
|
167
|
+
if (LAYOUT_FIELD_TYPES.has(field.fieldtype)) return false;
|
|
168
|
+
if (field.is_virtual) return false;
|
|
169
|
+
if (field.hidden && !options.includeHidden) return false;
|
|
170
|
+
if (!field.fieldname) return false;
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
function isValidIdentifier(name) {
|
|
174
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
175
|
+
}
|
|
176
|
+
function propertyKey(fieldname) {
|
|
177
|
+
return isValidIdentifier(fieldname) ? fieldname : JSON.stringify(fieldname);
|
|
178
|
+
}
|
|
179
|
+
function isCheckRequiredOnRead(field) {
|
|
180
|
+
return field.fieldtype === "Check";
|
|
181
|
+
}
|
|
182
|
+
function fieldComment(field, includeLabels) {
|
|
183
|
+
if (field.fieldtype === "Password") {
|
|
184
|
+
const secret = "GET usually returns empty or masked (`*`); use db.getPassword to read the secret.";
|
|
185
|
+
if (includeLabels && field.label) {
|
|
186
|
+
return ` /** ${field.label.replace(/\*\//g, "*\u2215")} \u2014 ${secret} */
|
|
187
|
+
`;
|
|
188
|
+
}
|
|
189
|
+
return ` /** ${secret} */
|
|
190
|
+
`;
|
|
191
|
+
}
|
|
192
|
+
if (includeLabels && field.label) {
|
|
193
|
+
return ` /** ${field.label.replace(/\*\//g, "*\u2215")} */
|
|
194
|
+
`;
|
|
195
|
+
}
|
|
196
|
+
return "";
|
|
197
|
+
}
|
|
198
|
+
function generateInterface(meta, allMetas = [meta], opts = {}) {
|
|
199
|
+
const options = { ...DEFAULTS, ...opts };
|
|
200
|
+
const interfaceName = toInterfaceName(meta.name);
|
|
201
|
+
const knownDoctypes = new Set(allMetas.map((m) => m.name));
|
|
202
|
+
const emitted = meta.fields.filter((field) => shouldEmitField(field, options));
|
|
203
|
+
const checkKeys = emitted.filter((field) => field.fieldtype === "Check").map((field) => field.fieldname);
|
|
204
|
+
const fieldLines = emitted.map((field) => {
|
|
205
|
+
const optional = field.reqd || isCheckRequiredOnRead(field) ? "" : "?";
|
|
206
|
+
const type = mapFieldType(field, {
|
|
207
|
+
resolveChildInterfaceName: (childDoctype) => knownDoctypes.has(childDoctype) ? toInterfaceName(childDoctype) : void 0
|
|
208
|
+
});
|
|
209
|
+
return `${fieldComment(field, options.includeLabels)} ${propertyKey(field.fieldname)}${optional}: ${type}`;
|
|
210
|
+
});
|
|
211
|
+
const doctypeLine = ` doctype: ${JSON.stringify(meta.name)}`;
|
|
212
|
+
const inner = [doctypeLine, ...fieldLines].join("\n");
|
|
213
|
+
const body = `{
|
|
214
|
+
${inner}
|
|
215
|
+
}`;
|
|
216
|
+
const insertAlias = checkKeys.length === 0 ? `export type ${interfaceName}Insert = FrappeInsert<${interfaceName}>` : `export type ${interfaceName}Insert = FrappeInsert<Omit<${interfaceName}, ${checkKeys.map((k) => JSON.stringify(k)).join(" | ")}> & Partial<Pick<${interfaceName}, ${checkKeys.map((k) => JSON.stringify(k)).join(" | ")}>>>`;
|
|
217
|
+
return [
|
|
218
|
+
`/** Generated from DocType \`${meta.name}\`. Do not edit by hand \u2014 regenerate with \`frappe-codegen --help\`. */`,
|
|
219
|
+
`export type ${interfaceName} = FrappeDoc<${body}>`,
|
|
220
|
+
insertAlias
|
|
221
|
+
].join("\n");
|
|
222
|
+
}
|
|
223
|
+
function collectImports(source) {
|
|
224
|
+
const names = ["FrappeDoc", "FrappeInsert"];
|
|
225
|
+
if (source.includes("Link<")) {
|
|
226
|
+
names.push("Link");
|
|
227
|
+
}
|
|
228
|
+
return `import type { ${names.join(", ")} } from 'frappe-js-client/types'`;
|
|
229
|
+
}
|
|
230
|
+
function generateModule(metas, opts = {}) {
|
|
231
|
+
const options = { ...DEFAULTS, ...opts };
|
|
232
|
+
assertUniqueInterfaceNames(metas);
|
|
233
|
+
const interfaces = metas.map((meta) => generateInterface(meta, metas, options)).join("\n\n");
|
|
234
|
+
const header = [
|
|
235
|
+
"/**",
|
|
236
|
+
" * This file was generated by frappe-codegen. Do not edit by hand \u2014",
|
|
237
|
+
' * regenerate it instead: `frappe-codegen --url <site> --doctype "..." --out <this file>`.',
|
|
238
|
+
" */",
|
|
239
|
+
collectImports(interfaces),
|
|
240
|
+
""
|
|
241
|
+
].join("\n");
|
|
242
|
+
if (!options.emitDocTypeMap) {
|
|
243
|
+
return `${header}
|
|
244
|
+
${interfaces}
|
|
245
|
+
`;
|
|
246
|
+
}
|
|
247
|
+
const mapEntries = metas.map((meta) => ` ${JSON.stringify(meta.name)}: ${toInterfaceName(meta.name)}`).join("\n");
|
|
248
|
+
const insertEntries = metas.map((meta) => ` ${JSON.stringify(meta.name)}: ${toInterfaceName(meta.name)}Insert`).join("\n");
|
|
249
|
+
const map = [
|
|
250
|
+
"",
|
|
251
|
+
"/** Maps a DocType name to its generated read type. Pass as `createFrappeClient<GeneratedDocTypes>(...)`. */",
|
|
252
|
+
"export interface GeneratedDocTypes {",
|
|
253
|
+
mapEntries,
|
|
254
|
+
"}",
|
|
255
|
+
"",
|
|
256
|
+
"/** Maps a DocType name to its insert payload type. */",
|
|
257
|
+
"export interface GeneratedInserts {",
|
|
258
|
+
insertEntries,
|
|
259
|
+
"}"
|
|
260
|
+
].join("\n");
|
|
261
|
+
return `${header}
|
|
262
|
+
${interfaces}
|
|
263
|
+
${map}
|
|
264
|
+
`;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/metadata.ts
|
|
268
|
+
async function fetchDocTypeMeta(client, doctype) {
|
|
269
|
+
try {
|
|
270
|
+
const raw = await client.db.getMeta(doctype);
|
|
271
|
+
return {
|
|
272
|
+
name: raw.name ?? doctype,
|
|
273
|
+
fields: raw.fields ?? [],
|
|
274
|
+
istable: raw.istable,
|
|
275
|
+
issingle: raw.issingle
|
|
276
|
+
};
|
|
277
|
+
} catch (error) {
|
|
278
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
279
|
+
const name = error instanceof Error ? error.name : "";
|
|
280
|
+
if (name === "FeatureNotSupportedError" || name === "NotFoundError") {
|
|
281
|
+
throw new Error(
|
|
282
|
+
`frappe-codegen requires Frappe v15+ REST API v2 (GET /api/v2/doctype/{doctype}/meta). Could not fetch metadata for ${JSON.stringify(doctype)}: ${message}`,
|
|
283
|
+
{ cause: error }
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
var FETCH_CONCURRENCY = 4;
|
|
290
|
+
async function mapPool(items, concurrency, mapper) {
|
|
291
|
+
if (items.length === 0) return [];
|
|
292
|
+
const results = new Array(items.length);
|
|
293
|
+
let next = 0;
|
|
294
|
+
async function worker() {
|
|
295
|
+
while (next < items.length) {
|
|
296
|
+
const index = next;
|
|
297
|
+
next += 1;
|
|
298
|
+
results[index] = await mapper(items[index]);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());
|
|
302
|
+
await Promise.all(workers);
|
|
303
|
+
return results;
|
|
304
|
+
}
|
|
305
|
+
async function fetchDocTypeMetas(client, doctypes) {
|
|
306
|
+
return mapPool(doctypes, FETCH_CONCURRENCY, (doctype) => fetchDocTypeMeta(client, doctype));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/resolve.ts
|
|
310
|
+
async function listDocTypesInModule(client, moduleName) {
|
|
311
|
+
const names = [];
|
|
312
|
+
for await (const row of client.db.paginate("DocType", {
|
|
313
|
+
filters: [["module", "=", moduleName]],
|
|
314
|
+
fields: ["name"],
|
|
315
|
+
limit: 100
|
|
316
|
+
})) {
|
|
317
|
+
if (typeof row.name === "string") {
|
|
318
|
+
names.push(row.name);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return names;
|
|
322
|
+
}
|
|
323
|
+
async function resolveDocTypes(client, seeds) {
|
|
324
|
+
const names = new Set(seeds.doctypes);
|
|
325
|
+
for (const moduleName of seeds.modules) {
|
|
326
|
+
for (const name of await listDocTypesInModule(client, moduleName)) {
|
|
327
|
+
names.add(name);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return [...names];
|
|
331
|
+
}
|
|
332
|
+
function childTableDoctypes(meta) {
|
|
333
|
+
const children = [];
|
|
334
|
+
for (const field of meta.fields) {
|
|
335
|
+
if (isTableFieldType(field.fieldtype) && field.options) {
|
|
336
|
+
children.push(field.options);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return children;
|
|
340
|
+
}
|
|
341
|
+
async function followChildTables(client, seeds) {
|
|
342
|
+
const byName = /* @__PURE__ */ new Map();
|
|
343
|
+
for (const meta of seeds) {
|
|
344
|
+
byName.set(meta.name, meta);
|
|
345
|
+
}
|
|
346
|
+
const pending = /* @__PURE__ */ new Set();
|
|
347
|
+
for (const meta of seeds) {
|
|
348
|
+
for (const child of childTableDoctypes(meta)) {
|
|
349
|
+
if (!byName.has(child)) pending.add(child);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
while (pending.size > 0) {
|
|
353
|
+
const batch = [...pending];
|
|
354
|
+
pending.clear();
|
|
355
|
+
const fetched = await fetchDocTypeMetas(client, batch);
|
|
356
|
+
for (const meta of fetched) {
|
|
357
|
+
byName.set(meta.name, meta);
|
|
358
|
+
for (const child of childTableDoctypes(meta)) {
|
|
359
|
+
if (!byName.has(child)) pending.add(child);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return [...byName.values()];
|
|
364
|
+
}
|
|
365
|
+
async function fetchWithOptionalFollow(client, doctypes, followTables) {
|
|
366
|
+
const seeds = await fetchDocTypeMetas(client, doctypes);
|
|
367
|
+
if (!followTables) return seeds;
|
|
368
|
+
return followChildTables(client, seeds);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export { DEFAULT_CONFIG_NAME, assertUniqueInterfaceNames, fetchDocTypeMeta, fetchDocTypeMetas, fetchWithOptionalFollow, findDefaultConfigPath, followChildTables, generateInterface, generateModule, loadConfigFile, mergeConfig, resolveDocTypes, toInterfaceName };
|
|
372
|
+
//# sourceMappingURL=index.mjs.map
|
|
373
|
+
//# sourceMappingURL=index.mjs.map
|
package/package.json
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "frappe-codegen",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI that reads a live Frappe site's DocType metadata and emits typed TypeScript interfaces for frappe-js-client.",
|
|
5
|
+
"author": "Dhia A. Shalabi <dhia.shalabi@gmail.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=20"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"frappe",
|
|
14
|
+
"frappe-framework",
|
|
15
|
+
"codegen",
|
|
16
|
+
"typescript",
|
|
17
|
+
"cli"
|
|
18
|
+
],
|
|
19
|
+
"homepage": "https://dhiashalabi.github.io/frappe-js-client/",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/dhiashalabi/frappe-js-client.git",
|
|
23
|
+
"directory": "packages/codegen"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/dhiashalabi/frappe-js-client/issues"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"frappe-codegen": "./dist/cli.js"
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.js",
|
|
32
|
+
"module": "./dist/index.mjs",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"import": {
|
|
37
|
+
"types": "./dist/index.d.mts",
|
|
38
|
+
"default": "./dist/index.mjs"
|
|
39
|
+
},
|
|
40
|
+
"require": {
|
|
41
|
+
"types": "./dist/index.d.ts",
|
|
42
|
+
"default": "./dist/index.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"./package.json": "./package.json"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"!dist/**/*.map",
|
|
50
|
+
"LICENSE",
|
|
51
|
+
"README.md",
|
|
52
|
+
"CHANGELOG.md"
|
|
53
|
+
],
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"frappe-js-client": "^3.3.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
59
|
+
"@eslint/js": "^10.0.1",
|
|
60
|
+
"@types/node": "^26.4.1",
|
|
61
|
+
"@vitest/coverage-v8": "^5.0.0",
|
|
62
|
+
"eslint": "^10.10.0",
|
|
63
|
+
"eslint-config-prettier": "^10.1.8",
|
|
64
|
+
"eslint-plugin-simple-import-sort": "^14.0.0",
|
|
65
|
+
"globals": "^17.12.0",
|
|
66
|
+
"publint": "^0.3.24",
|
|
67
|
+
"tsup": "^8.5.1",
|
|
68
|
+
"typescript": "~6.0.3",
|
|
69
|
+
"typescript-eslint": "^8.70.0",
|
|
70
|
+
"vitest": "^5.0.0"
|
|
71
|
+
},
|
|
72
|
+
"publishConfig": {
|
|
73
|
+
"access": "public"
|
|
74
|
+
},
|
|
75
|
+
"scripts": {
|
|
76
|
+
"build": "pnpm clean && tsup && node scripts/verify-dist.mjs",
|
|
77
|
+
"codegen": "node ./dist/cli.js",
|
|
78
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
79
|
+
"test": "vitest run --coverage",
|
|
80
|
+
"test:watch": "vitest",
|
|
81
|
+
"lint": "eslint .",
|
|
82
|
+
"lint:fix": "eslint . --fix",
|
|
83
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
84
|
+
"publint": "publint",
|
|
85
|
+
"attw": "attw --pack . --profile node16",
|
|
86
|
+
"gate": "pnpm typecheck && pnpm lint && pnpm test && pnpm build && pnpm publint && pnpm attw"
|
|
87
|
+
}
|
|
88
|
+
}
|