@skalfa/skalfa-app 1.0.2 → 1.0.5
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/.env.example +8 -16
- package/app/auth/edit/page.tsx +1 -1
- package/app/auth/login/page.tsx +1 -1
- package/app/auth/me/page.tsx +1 -1
- package/app/auth/register/page.tsx +1 -1
- package/app/auth/verify/page.tsx +1 -1
- package/app/dashboard/layout.tsx +2 -2
- package/app/dashboard/page.tsx +1 -1
- package/app/index.ts +1 -0
- package/app/layout.tsx +2 -4
- package/app/page.tsx +2 -2
- package/bun.lock +39 -24
- package/components/index.ts +1 -3
- package/package.json +8 -7
- package/styles/components.css +1392 -0
- package/styles/globals.css +40 -175
- package/styles/utilities.css +37 -0
- package/tsconfig.json +4 -2
- package/utils/commands/skalfa.ts +3 -0
- package/blueprints/starter.blueprint.json +0 -103
- package/components/base.components/accordion/Accordion.component.tsx +0 -82
- package/components/base.components/breadcrumb/Breadcrumb.component.tsx +0 -80
- package/components/base.components/button/Button.component.tsx +0 -91
- package/components/base.components/button/IconButton.component.tsx +0 -88
- package/components/base.components/button/button.decorate.ts +0 -82
- package/components/base.components/card/AlertCard.component.tsx +0 -69
- package/components/base.components/card/Card.component.tsx +0 -25
- package/components/base.components/card/DashboardCard.component.tsx +0 -44
- package/components/base.components/card/GalleryCard.component.tsx +0 -50
- package/components/base.components/card/ProductCard.component.tsx +0 -65
- package/components/base.components/card/ProfileCard.component.tsx +0 -71
- package/components/base.components/carousel/Carousel.component.tsx +0 -113
- package/components/base.components/chip/Chip.component.tsx +0 -39
- package/components/base.components/document/DocumentViewer.component.tsx +0 -164
- package/components/base.components/document/ExportExcel.component.tsx +0 -340
- package/components/base.components/document/ImportExcel.component.tsx +0 -315
- package/components/base.components/document/PrintTable.component.tsx +0 -204
- package/components/base.components/document/RenderPDF.component.tsx +0 -416
- package/components/base.components/index.ts +0 -85
- package/components/base.components/input/Checkbox.component.tsx +0 -109
- package/components/base.components/input/Input.component.tsx +0 -332
- package/components/base.components/input/InputCheckbox.component.tsx +0 -174
- package/components/base.components/input/InputCurrency.component.tsx +0 -163
- package/components/base.components/input/InputDate.component.tsx +0 -352
- package/components/base.components/input/InputDatetime.component.tsx +0 -260
- package/components/base.components/input/InputDocument.component.tsx +0 -352
- package/components/base.components/input/InputImage.component.tsx +0 -533
- package/components/base.components/input/InputMap.component.tsx +0 -318
- package/components/base.components/input/InputNumber.component.tsx +0 -192
- package/components/base.components/input/InputOtp.component.tsx +0 -169
- package/components/base.components/input/InputPassword.component.tsx +0 -236
- package/components/base.components/input/InputRadio.component.tsx +0 -175
- package/components/base.components/input/InputTime.component.tsx +0 -276
- package/components/base.components/input/InputValues.component.tsx +0 -68
- package/components/base.components/input/Radio.component.tsx +0 -102
- package/components/base.components/input/Select.component.tsx +0 -541
- package/components/base.components/modal/BottomSheet.component.tsx +0 -246
- package/components/base.components/modal/FloatingPage.component.tsx +0 -104
- package/components/base.components/modal/Modal.component.tsx +0 -96
- package/components/base.components/modal/ModalConfirm.component.tsx +0 -218
- package/components/base.components/modal/Toast.component.tsx +0 -126
- package/components/base.components/nav/Bottombar.component.tsx +0 -116
- package/components/base.components/nav/Footer.component.tsx +0 -144
- package/components/base.components/nav/Headbar.component.tsx +0 -104
- package/components/base.components/nav/Navbar.component.tsx +0 -100
- package/components/base.components/nav/Sidebar.component.tsx +0 -301
- package/components/base.components/nav/Tabbar.component.tsx +0 -60
- package/components/base.components/nav/Wizard.component.tsx +0 -73
- package/components/base.components/supervision/FormSupervision.component.tsx +0 -434
- package/components/base.components/supervision/TableSupervision.component.tsx +0 -697
- package/components/base.components/table/ControlBar.component.tsx +0 -497
- package/components/base.components/table/FilterComponent.tsx +0 -518
- package/components/base.components/table/Pagination.component.tsx +0 -159
- package/components/base.components/table/Table.component.tsx +0 -469
- package/components/base.components/typography/TypographyArticle.component.tsx +0 -26
- package/components/base.components/typography/TypographyColumn.component.tsx +0 -20
- package/components/base.components/typography/TypographyContent.component.tsx +0 -20
- package/components/base.components/typography/TypographyTips.component.tsx +0 -20
- package/components/base.components/wrap/Draggable.component.tsx +0 -303
- package/components/base.components/wrap/IDBProvider.tsx +0 -12
- package/components/base.components/wrap/Image.component.tsx +0 -10
- package/components/base.components/wrap/OutsideClick.component.tsx +0 -48
- package/components/base.components/wrap/ScrollContainer.component.tsx +0 -104
- package/components/base.components/wrap/ShortcutProvider.tsx +0 -57
- package/components/base.components/wrap/Swipe.component.tsx +0 -93
- package/components/construct.components/example.tsx +0 -1
- package/components/construct.components/index.ts +0 -5
- package/components/structure.components/example.tsx +0 -1
- package/components/structure.components/index.ts +0 -5
- package/langs/index.ts +0 -1
- package/langs/validation.langs.ts +0 -17
- package/schema/idb/app.schema.ts +0 -9
- package/schema/index.ts +0 -5
- package/utils/commands/barrels.ts +0 -28
- package/utils/commands/blueprint.ts +0 -421
- package/utils/commands/light.ts +0 -21
- package/utils/commands/logger.ts +0 -42
- package/utils/commands/stubs/table-blueprint.stub +0 -13
- package/utils/commands/use-pdf.ts +0 -29
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import { exec } from "child_process";
|
|
4
|
-
import { logger } from "./logger";
|
|
5
|
-
|
|
6
|
-
const rootDir = path.resolve();
|
|
7
|
-
const configText = fs.readFileSync("barrels.json", "utf8");
|
|
8
|
-
const config = JSON.parse(configText);
|
|
9
|
-
const directories: string[] = Array.isArray(config.directory) ? config.directory : [config.directory];
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
directories.forEach((dir) => {
|
|
13
|
-
const absoluteDir = path.join(rootDir, dir);
|
|
14
|
-
|
|
15
|
-
if (!fs.existsSync(absoluteDir)) {
|
|
16
|
-
logger.error(`Barrels error: ${absoluteDir} directory not found`)
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
fs.watch(absoluteDir, { recursive: true }, (_, filename) => {
|
|
21
|
-
if (filename && (filename.endsWith(".ts") || filename.endsWith(".tsx")) && filename !== "index.ts") {
|
|
22
|
-
exec("npx barrelsby -c barrels.json", { cwd: rootDir })
|
|
23
|
-
logger.info("Barrels updated " + absoluteDir + "/index.ts")
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
logger.start("Barrels watched " + directories.join(", "))
|
|
@@ -1,421 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { ValidationRules, conversion } from "@skalfa/skalfa-app-core";
|
|
4
|
-
import { logger } from "./logger";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
type SchemaMap = Record<string, string>
|
|
9
|
-
|
|
10
|
-
type ColumnItem = {
|
|
11
|
-
selector ?: string
|
|
12
|
-
label : string
|
|
13
|
-
sortable ?: boolean
|
|
14
|
-
item ?: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type FormItem = {
|
|
18
|
-
type ?: string
|
|
19
|
-
col ?: number
|
|
20
|
-
construction : {
|
|
21
|
-
name : string
|
|
22
|
-
label : string
|
|
23
|
-
placeholder : string
|
|
24
|
-
validations : ValidationRules[]
|
|
25
|
-
serverOptionControl ?: {
|
|
26
|
-
path : string
|
|
27
|
-
}
|
|
28
|
-
fields ?: FormItem[]
|
|
29
|
-
wrap ?: boolean
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
type DetailItem = {
|
|
34
|
-
label : string
|
|
35
|
-
item : string
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
type ParsedSchema = {
|
|
39
|
-
columns : ColumnItem[]
|
|
40
|
-
forms : FormItem[]
|
|
41
|
-
details : DetailItem[]
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
type BlueprintPage = {
|
|
45
|
-
features ?: string
|
|
46
|
-
path ?: string
|
|
47
|
-
schema ?: Record<string, string>
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
type BlueprintStruct = {
|
|
51
|
-
model : string
|
|
52
|
-
controllers ?: string[]
|
|
53
|
-
schema ?: SchemaMap
|
|
54
|
-
pages ?: false | Record<string, BlueprintPage>
|
|
55
|
-
[key: string]: any
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
type LoadedBlueprintFile = {
|
|
59
|
-
file : string
|
|
60
|
-
blueprints : BlueprintStruct[]
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const renderJS = (value: unknown, indent = 0): string => {
|
|
66
|
-
const pad = " ".repeat(indent)
|
|
67
|
-
|
|
68
|
-
if (Array.isArray(value)) {
|
|
69
|
-
if (!value.length) return "[]"
|
|
70
|
-
|
|
71
|
-
if (value.every(v => typeof v === "string")) {
|
|
72
|
-
return `[${value.map(v => JSON.stringify(v)).join(", ")}]`
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return `[\n${value
|
|
76
|
-
.map(v => pad + " " + renderJS(v, indent + 2))
|
|
77
|
-
.join(",\n")}\n${pad}]`
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (value && typeof value === "object") {
|
|
81
|
-
const entries = Object.entries(value as Record<string, unknown>)
|
|
82
|
-
if (!entries.length) return "{}"
|
|
83
|
-
|
|
84
|
-
return `{\n${entries
|
|
85
|
-
.map(([k, v]) => `${pad} ${k}: ${renderJS(v, indent + 2)}`)
|
|
86
|
-
.join(",\n")}\n${pad}}`
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (typeof value === "string") return JSON.stringify(value)
|
|
90
|
-
|
|
91
|
-
return String(value)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function extractValidationArray(def: string = ""): ValidationRules[] {
|
|
95
|
-
const rules: ValidationRules[] = []
|
|
96
|
-
|
|
97
|
-
if (def.includes("required")) rules.push("required")
|
|
98
|
-
|
|
99
|
-
if (def.includes("email")) rules.push("email")
|
|
100
|
-
|
|
101
|
-
if (def.includes("url")) rules.push("url")
|
|
102
|
-
|
|
103
|
-
const min = def.match(/min,(\d+)/)
|
|
104
|
-
if (min) rules.push(`min:${min[1]}`)
|
|
105
|
-
|
|
106
|
-
const max = def.match(/max,(\d+)/)
|
|
107
|
-
if (max) rules.push(`max:${max[1]}`)
|
|
108
|
-
|
|
109
|
-
return rules
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function extractFormType(rules: string[]): string | undefined {
|
|
113
|
-
const rule = rules.find(r => r.startsWith("form:"))
|
|
114
|
-
return rule ? rule.replace("form:", "") : undefined
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function inferFormType(pageDef = "", modelDef = ""): string {
|
|
118
|
-
const explicit = pageDef.split("|")
|
|
119
|
-
if (explicit && explicit[0] && explicit[0] != "text") return explicit[0]
|
|
120
|
-
|
|
121
|
-
if (modelDef.includes("type:integer") || modelDef.includes("type:float")) {
|
|
122
|
-
return "number"
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (modelDef.includes("type:date")) return "date"
|
|
126
|
-
if (modelDef.includes("type:datetime")) return "datetime"
|
|
127
|
-
if (modelDef.includes("type:image")) return "image"
|
|
128
|
-
|
|
129
|
-
return "default"
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function parseFeatures(features?: string): { controlBar: string[]; action: string[] } {
|
|
133
|
-
const controlBar: string[] = []
|
|
134
|
-
const action: string[] = []
|
|
135
|
-
|
|
136
|
-
if (!features) {
|
|
137
|
-
return { controlBar: ["CREATE"], action: ["EDIT", "DELETE"] }
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const list = features.split(" ")
|
|
141
|
-
|
|
142
|
-
if (list.includes("create")) controlBar.push("CREATE")
|
|
143
|
-
|
|
144
|
-
controlBar.push("SEARCH", "SORT", "SELECTABLE")
|
|
145
|
-
|
|
146
|
-
if (list.includes("import")) controlBar.push("IMPORT")
|
|
147
|
-
if (list.includes("export")) controlBar.push("EXPORT")
|
|
148
|
-
if (list.includes("print")) controlBar.push("PRINT")
|
|
149
|
-
|
|
150
|
-
if (list.includes("update") || list.includes("edit")) action.push("EDIT")
|
|
151
|
-
if (list.includes("delete")) action.push("DELETE")
|
|
152
|
-
if (list.includes("detail")) action.push("DETAIL")
|
|
153
|
-
|
|
154
|
-
return { controlBar, action }
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function parseModelSchema(schema: SchemaMap = {}): ParsedSchema {
|
|
158
|
-
const columns: ColumnItem[] = []
|
|
159
|
-
const forms: FormItem[] = []
|
|
160
|
-
const details: DetailItem[] = []
|
|
161
|
-
|
|
162
|
-
for (const [field, def] of Object.entries(schema)) {
|
|
163
|
-
const label = conversion.strPascal(field, " ")
|
|
164
|
-
|
|
165
|
-
if (def.includes("selectable")) {
|
|
166
|
-
columns.push({ selector: field, label })
|
|
167
|
-
details.push({ label, item: field })
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (def.includes("fillable")) {
|
|
171
|
-
const fieldType = inferFormType("", def);
|
|
172
|
-
forms.push({
|
|
173
|
-
...(fieldType != "default" ? { type: fieldType } : {}),
|
|
174
|
-
construction: {
|
|
175
|
-
name: field,
|
|
176
|
-
label,
|
|
177
|
-
placeholder: "Masukkan " + label.toLowerCase(),
|
|
178
|
-
validations: extractValidationArray(def)
|
|
179
|
-
}
|
|
180
|
-
})
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return { columns, forms, details }
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function resolvePath(page: BlueprintPage, controllers: string[] | boolean | undefined, model: string): string {
|
|
188
|
-
if (page.path) return page.path
|
|
189
|
-
|
|
190
|
-
if (Array.isArray(controllers)) {
|
|
191
|
-
const match = controllers
|
|
192
|
-
if (match) return "/" + match[1]
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return "/" + model.split("/").pop()
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function parsePageSchema(pageSchema: Record<string, string>, modelSchema: SchemaMap = {}): ParsedSchema {
|
|
199
|
-
const columns: ColumnItem[] = []
|
|
200
|
-
const forms: FormItem[] = []
|
|
201
|
-
const details: DetailItem[] = []
|
|
202
|
-
|
|
203
|
-
for (const [field, def] of Object.entries(pageSchema)) {
|
|
204
|
-
const rules = def.replace(/\|/g, " ").split(" ").filter(Boolean)
|
|
205
|
-
|
|
206
|
-
const defaultLabel = conversion.strPascal(field, " ")
|
|
207
|
-
|
|
208
|
-
const colLabelRule = rules.find(r => r.includes("column:label,"))
|
|
209
|
-
const colLabel = conversion.strPascal(colLabelRule ? colLabelRule.split(",")[1] : defaultLabel, " ")
|
|
210
|
-
|
|
211
|
-
const formLabelRule = rules.find(r => r.includes("form:label,"))
|
|
212
|
-
const formLabel = conversion.strPascal(formLabelRule ? formLabelRule.split(",")[1] : (colLabelRule ? colLabel : defaultLabel), " ")
|
|
213
|
-
|
|
214
|
-
const detailLabel = colLabelRule ? colLabel : (formLabelRule ? formLabel : defaultLabel)
|
|
215
|
-
|
|
216
|
-
const hasColumn = rules.some(r => r.includes("column:"))
|
|
217
|
-
const hasForm = rules.some(r => r.includes("form:")) || rules.every(r => !r.includes("column:"))
|
|
218
|
-
|
|
219
|
-
const hasDetail = rules.includes("detail")
|
|
220
|
-
|
|
221
|
-
if (hasColumn) {
|
|
222
|
-
columns.push({
|
|
223
|
-
selector: field,
|
|
224
|
-
label: colLabel,
|
|
225
|
-
...(rules.includes("column:sortable") || rules.includes("sortable") ? { sortable: true } : {})
|
|
226
|
-
})
|
|
227
|
-
if (!hasDetail) details.push({ label: detailLabel, item: field })
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (hasForm) {
|
|
231
|
-
const typeRules = rules.filter(r => !r.startsWith("form:label,"))
|
|
232
|
-
const typeRule = extractFormType(typeRules)
|
|
233
|
-
let fieldType = inferFormType(typeRule, modelSchema[field] || "");
|
|
234
|
-
|
|
235
|
-
let selectPath = ""
|
|
236
|
-
const selectRule = rules.find(r => r.startsWith("select,") || r.startsWith("form:select,"))
|
|
237
|
-
if (selectRule) {
|
|
238
|
-
fieldType = "select"
|
|
239
|
-
selectPath = selectRule.split(",")[1]
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (typeRule === "check") fieldType = "boolean"
|
|
243
|
-
if (typeRule === "currency") fieldType = "currency"
|
|
244
|
-
if (typeRule === "image") fieldType = "image"
|
|
245
|
-
if (typeRule === "date") fieldType = "date"
|
|
246
|
-
if (typeRule === "time") fieldType = "time"
|
|
247
|
-
|
|
248
|
-
let col: number | undefined
|
|
249
|
-
const colRule = rules.find(r => /col,(\d+)/.test(r))
|
|
250
|
-
|
|
251
|
-
if (colRule) {
|
|
252
|
-
const n = Number(colRule.split(',').pop())
|
|
253
|
-
if (n >= 1 && n <= 12) col = n
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
forms.push({
|
|
257
|
-
...(fieldType != "default" && fieldType != "text" ? { type: fieldType } : {}),
|
|
258
|
-
...(col ? { col } : {}),
|
|
259
|
-
construction: {
|
|
260
|
-
name: field,
|
|
261
|
-
label: formLabel,
|
|
262
|
-
placeholder: "Masukkan " + formLabel.toLowerCase(),
|
|
263
|
-
validations: extractValidationArray(modelSchema[field] || ""),
|
|
264
|
-
...(selectPath ? { serverOptionControl: { path: selectPath } } : {}),
|
|
265
|
-
...(rules.includes("wrap") ? { wrap: true } : {})
|
|
266
|
-
}
|
|
267
|
-
})
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (hasDetail) details.push({ label: detailLabel, item: field })
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const formMap = new Map<string, FormItem>()
|
|
274
|
-
forms.forEach(f => formMap.set(f.construction.name, f))
|
|
275
|
-
|
|
276
|
-
const nestedForms: FormItem[] = []
|
|
277
|
-
|
|
278
|
-
forms.forEach(f => {
|
|
279
|
-
const name = f.construction.name
|
|
280
|
-
if (name.includes(".")) {
|
|
281
|
-
const parts = name.split(".")
|
|
282
|
-
const selfName = parts.pop() as string
|
|
283
|
-
const parentName = parts.join(".")
|
|
284
|
-
const parent = formMap.get(parentName)
|
|
285
|
-
|
|
286
|
-
if (parent) {
|
|
287
|
-
if (!parent.type) parent.type = "cluster"
|
|
288
|
-
if (!parent.construction.fields) parent.construction.fields = []
|
|
289
|
-
|
|
290
|
-
f.construction.name = selfName
|
|
291
|
-
|
|
292
|
-
parent.construction.fields.push(f)
|
|
293
|
-
} else {
|
|
294
|
-
nestedForms.push(f)
|
|
295
|
-
}
|
|
296
|
-
} else {
|
|
297
|
-
nestedForms.push(f)
|
|
298
|
-
}
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
return { columns, forms: nestedForms, details }
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
function loadBlueprintFiles(dir: string = "blueprints"): LoadedBlueprintFile[] {
|
|
307
|
-
const basePath = path.join(process.cwd(), dir)
|
|
308
|
-
|
|
309
|
-
if (!fs.existsSync(basePath)) {
|
|
310
|
-
throw new Error("Blueprint folder not found")
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return fs.readdirSync(basePath)
|
|
314
|
-
.filter(f => f.endsWith(".blueprint.json"))
|
|
315
|
-
.map(file => {
|
|
316
|
-
const fullPath = path.join(basePath, file)
|
|
317
|
-
const content = JSON.parse(fs.readFileSync(fullPath, "utf-8"))
|
|
318
|
-
|
|
319
|
-
if (!Array.isArray(content)) {
|
|
320
|
-
throw new Error(`${file} must export array of blueprints`)
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return {
|
|
324
|
-
file: file.replace(".blueprint.json", ""),
|
|
325
|
-
blueprints: content as BlueprintStruct[],
|
|
326
|
-
}
|
|
327
|
-
})
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
const blueprintMarker = `// ============================================
|
|
331
|
-
// ## file THIS FILE IS AUTO-GENERATED BY BLUEPRINT
|
|
332
|
-
// ?? Blueprint : {{ blueprint }}
|
|
333
|
-
// !! If this comment is removed, blueprint engine WILL NOT override this file.
|
|
334
|
-
// ============================================
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
`
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
// ===============================
|
|
341
|
-
// ## Main generator
|
|
342
|
-
// ===============================
|
|
343
|
-
|
|
344
|
-
export async function blueprint(options?: { only?: string[] }): Promise<void> {
|
|
345
|
-
const stub = fs.readFileSync(path.join(process.cwd(), "/utils/commands/stubs/table-blueprint.stub"), "utf-8")
|
|
346
|
-
|
|
347
|
-
const loaded = loadBlueprintFiles()
|
|
348
|
-
|
|
349
|
-
for (const file of loaded) {
|
|
350
|
-
for (const bp of file.blueprints) {
|
|
351
|
-
|
|
352
|
-
const pagesToGenerate: Record<string, BlueprintPage> = { ...(bp.pages || {}) }
|
|
353
|
-
|
|
354
|
-
for (const [key, val] of Object.entries(bp)) {
|
|
355
|
-
if (typeof val === "object" && val["schema"]) {
|
|
356
|
-
pagesToGenerate[key] = val as BlueprintPage
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
for (const [key, page] of Object.entries(pagesToGenerate)) {
|
|
361
|
-
|
|
362
|
-
const route = key;
|
|
363
|
-
const name = conversion.strPascal(route.split("/").pop() as string)
|
|
364
|
-
|
|
365
|
-
if (options?.only && !options.only.includes(name)) continue
|
|
366
|
-
|
|
367
|
-
const outDir = path.join(process.cwd(), "app", route)
|
|
368
|
-
fs.mkdirSync(outDir, { recursive: true })
|
|
369
|
-
|
|
370
|
-
const filePath = path.join(outDir, "page.tsx");
|
|
371
|
-
|
|
372
|
-
if (fs.existsSync(filePath)) {
|
|
373
|
-
const content = fs.readFileSync(filePath, "utf-8")
|
|
374
|
-
|
|
375
|
-
if (!content.includes("AUTO-GENERATED BY BLUEPRINT")) {
|
|
376
|
-
logger.info(`Skip overridden file: ${filePath}`)
|
|
377
|
-
continue
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
const schema = bp.schema ?? {}
|
|
382
|
-
|
|
383
|
-
const parsed: ParsedSchema = page?.schema ? parsePageSchema(page.schema, schema) : parseModelSchema(schema)
|
|
384
|
-
|
|
385
|
-
const { controlBar, action } = parseFeatures(page?.features)
|
|
386
|
-
|
|
387
|
-
const fetchPath = resolvePath(page, bp.controllers, bp.model)
|
|
388
|
-
|
|
389
|
-
let properties = `
|
|
390
|
-
fetchControl={{
|
|
391
|
-
path: "${fetchPath}"
|
|
392
|
-
}}
|
|
393
|
-
columnControl={${renderJS(parsed.columns, 8)}}
|
|
394
|
-
formControl={{
|
|
395
|
-
fields: ${renderJS(parsed.forms, 10)}
|
|
396
|
-
}}
|
|
397
|
-
`
|
|
398
|
-
if (parsed.details) {
|
|
399
|
-
properties += ` detailControl={${renderJS(parsed.details, 8)}}\n`
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (controlBar.length) {
|
|
403
|
-
properties += ` controlBar={${JSON.stringify(controlBar)}}\n`
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
if (action.length) {
|
|
407
|
-
properties += ` actionControl={${JSON.stringify(action)}}\n`
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
const content = stub
|
|
411
|
-
.replace(/{{ marker }}/g, blueprintMarker.replace(/{{ blueprint }}/g, file.file + ".blueprint.json"))
|
|
412
|
-
.replace(/{{ name }}/g, name)
|
|
413
|
-
.replace(/{{ title }}/g, name)
|
|
414
|
-
.replace(/{{ properties }}/g, properties)
|
|
415
|
-
|
|
416
|
-
fs.writeFileSync(path.join(outDir, "page.tsx"), content)
|
|
417
|
-
logger.info(`Generated: ${filePath}`)
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}
|
package/utils/commands/light.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
import { usePdf } from "./use-pdf";
|
|
3
|
-
import { blueprint } from "./blueprint";
|
|
4
|
-
import { logger } from "./logger";
|
|
5
|
-
|
|
6
|
-
const program = new Command();
|
|
7
|
-
|
|
8
|
-
program.name("light").description("Next Light CLI").version("1.0.0");
|
|
9
|
-
|
|
10
|
-
program.command("use-pdf").description("Copy pdf.worker.min.mjs ke folder public/").action(usePdf );
|
|
11
|
-
program.command("blueprint")
|
|
12
|
-
.option("-o, --only <names...>", "Run only specific blueprints")
|
|
13
|
-
.description("Generate blueprint")
|
|
14
|
-
.action(async (opts) => {
|
|
15
|
-
await blueprint({ only: opts.only })
|
|
16
|
-
|
|
17
|
-
logger.info("Success run all blueprints!")
|
|
18
|
-
process.exit(0);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
program.parse();
|
package/utils/commands/logger.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
type LogType = "start" | "info" | "error" | "warning" | "cavity" | "socket" | "cavityError" | "socketError";
|
|
3
|
-
|
|
4
|
-
const colors: Record<LogType | "default", string> = {
|
|
5
|
-
default : "\x1b[0m", // default
|
|
6
|
-
start : "\x1b[32m", // green
|
|
7
|
-
info : "\x1b[36m", // cyan
|
|
8
|
-
error : "\x1b[31m", // red
|
|
9
|
-
warning : "\x1b[33m", // yellow
|
|
10
|
-
cavity : "\x1b[34m", // blue
|
|
11
|
-
cavityError : "\x1b[31m", // red
|
|
12
|
-
socket : "\x1b[35m", // magenta
|
|
13
|
-
socketError : "\x1b[31m", // red
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const prefixes: Record<LogType, string> = {
|
|
17
|
-
start : "START",
|
|
18
|
-
info : "INFO",
|
|
19
|
-
error : "ERROR",
|
|
20
|
-
warning : "WARNING",
|
|
21
|
-
cavity : "CAVITY",
|
|
22
|
-
socket : "SOCKET",
|
|
23
|
-
cavityError : "CAVITY ERROR",
|
|
24
|
-
socketError : "SOCKET ERROR",
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
function log(type: LogType, ...msg: unknown[]) {
|
|
28
|
-
const color = colors[type];
|
|
29
|
-
const prefix = prefixes[type];
|
|
30
|
-
console.log(`${color}[${prefix}]${colors.default}`, ...msg);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const logger = {
|
|
34
|
-
start : (...msg: unknown[]) => log("start", ...msg),
|
|
35
|
-
info : (...msg: unknown[]) => log("info", ...msg),
|
|
36
|
-
error : (...msg: unknown[]) => log("error", ...msg),
|
|
37
|
-
warning : (...msg: unknown[]) => log("warning", ...msg),
|
|
38
|
-
cavity : (...msg: unknown[]) => log("cavity", ...msg),
|
|
39
|
-
cavityError : (...msg: unknown[]) => log("cavityError", ...msg),
|
|
40
|
-
socket : (...msg: unknown[]) => log("socket", ...msg),
|
|
41
|
-
socketError : (...msg: unknown[]) => log("socketError", ...msg),
|
|
42
|
-
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { fileURLToPath } from "url";
|
|
4
|
-
import { logger } from "./logger";
|
|
5
|
-
|
|
6
|
-
export function usePdf() {
|
|
7
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
-
const __dirname = path.dirname(__filename);
|
|
9
|
-
const projectRoot = path.join(__dirname, "..", "..");
|
|
10
|
-
|
|
11
|
-
const source = path.join(
|
|
12
|
-
projectRoot,
|
|
13
|
-
"node_modules",
|
|
14
|
-
"pdfjs-dist",
|
|
15
|
-
"legacy",
|
|
16
|
-
"build",
|
|
17
|
-
"pdf.worker.min.mjs"
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
const target = path.join(projectRoot, "public", "pdf.worker.min.mjs");
|
|
21
|
-
|
|
22
|
-
if (!fs.existsSync(source)) {
|
|
23
|
-
logger.error(`Gagal: pdf.worker.min.mjs tidak ditemukan.`)
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
fs.copyFileSync(source, target);
|
|
28
|
-
logger.info("Berhasil memindahkan worker ke public/pdf.worker.min.mjs")
|
|
29
|
-
}
|