generate-ui-cli 2.1.7 → 2.2.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 +47 -0
- package/dist/commands/angular.js +221 -12
- package/dist/commands/generate.js +120 -2
- package/dist/commands/login.js +44 -17
- package/dist/generate-ui/routes.gen.js +4 -0
- package/dist/generators/angular/feature.generator.js +1112 -137
- package/dist/generators/angular/menu.generator.js +165 -0
- package/dist/index.js +19 -4
- package/dist/license/permissions.js +1 -1
- package/dist/license/token.js +19 -3
- package/dist/postinstall.js +7 -0
- package/dist/runtime/logger.js +29 -0
- package/package.json +1 -1
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateMenu = generateMenu;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
function generateMenu(schemasRoot) {
|
|
10
|
+
const menu = loadMenuConfig(schemasRoot) ??
|
|
11
|
+
buildMenuFromRoutes(loadRoutesConfig(schemasRoot)) ?? {
|
|
12
|
+
groups: [],
|
|
13
|
+
ungrouped: []
|
|
14
|
+
};
|
|
15
|
+
const out = path_1.default.join(schemasRoot, 'menu.gen.ts');
|
|
16
|
+
const content = `
|
|
17
|
+
export type GeneratedMenuItem = {
|
|
18
|
+
id: string
|
|
19
|
+
label: string
|
|
20
|
+
route: string
|
|
21
|
+
hidden?: boolean
|
|
22
|
+
icon?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type GeneratedMenuGroup = {
|
|
26
|
+
id: string
|
|
27
|
+
label: string
|
|
28
|
+
items: GeneratedMenuItem[]
|
|
29
|
+
hidden?: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type GeneratedMenu = {
|
|
33
|
+
groups: GeneratedMenuGroup[]
|
|
34
|
+
ungrouped: GeneratedMenuItem[]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const generatedMenu: GeneratedMenu = ${JSON.stringify(normalizeMenu(menu), null, 2)}
|
|
38
|
+
`;
|
|
39
|
+
fs_1.default.writeFileSync(out, content.trimStart());
|
|
40
|
+
}
|
|
41
|
+
function loadMenuConfig(schemasRoot) {
|
|
42
|
+
const overridePath = path_1.default.join(schemasRoot, 'menu.overrides.json');
|
|
43
|
+
const basePath = path_1.default.join(schemasRoot, 'menu.json');
|
|
44
|
+
if (fs_1.default.existsSync(overridePath)) {
|
|
45
|
+
const override = JSON.parse(fs_1.default.readFileSync(overridePath, 'utf-8'));
|
|
46
|
+
const hasOverride = Array.isArray(override?.groups) &&
|
|
47
|
+
override.groups.length > 0
|
|
48
|
+
? true
|
|
49
|
+
: Array.isArray(override?.ungrouped) &&
|
|
50
|
+
override.ungrouped.length > 0;
|
|
51
|
+
if (hasOverride) {
|
|
52
|
+
return override;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (fs_1.default.existsSync(basePath)) {
|
|
56
|
+
return JSON.parse(fs_1.default.readFileSync(basePath, 'utf-8'));
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
function loadRoutesConfig(schemasRoot) {
|
|
61
|
+
const routesPath = path_1.default.join(schemasRoot, 'routes.json');
|
|
62
|
+
if (!fs_1.default.existsSync(routesPath))
|
|
63
|
+
return null;
|
|
64
|
+
try {
|
|
65
|
+
return JSON.parse(fs_1.default.readFileSync(routesPath, 'utf-8'));
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function normalizeMenu(value) {
|
|
72
|
+
return {
|
|
73
|
+
groups: Array.isArray(value?.groups)
|
|
74
|
+
? value.groups.map(normalizeGroup)
|
|
75
|
+
: [],
|
|
76
|
+
ungrouped: Array.isArray(value?.ungrouped)
|
|
77
|
+
? value.ungrouped.map(normalizeItem)
|
|
78
|
+
: []
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function normalizeGroup(value) {
|
|
82
|
+
return {
|
|
83
|
+
id: String(value?.id ?? ''),
|
|
84
|
+
label: String(value?.label ?? ''),
|
|
85
|
+
hidden: Boolean(value?.hidden) || undefined,
|
|
86
|
+
items: Array.isArray(value?.items)
|
|
87
|
+
? value.items.map(normalizeItem)
|
|
88
|
+
: []
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function normalizeItem(value) {
|
|
92
|
+
return {
|
|
93
|
+
id: String(value?.id ?? ''),
|
|
94
|
+
label: String(value?.label ?? ''),
|
|
95
|
+
route: String(value?.route ?? ''),
|
|
96
|
+
hidden: Boolean(value?.hidden) || undefined,
|
|
97
|
+
icon: value?.icon ? String(value.icon) : undefined
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function buildMenuFromRoutes(routes) {
|
|
101
|
+
if (!Array.isArray(routes) || routes.length === 0)
|
|
102
|
+
return null;
|
|
103
|
+
const groups = [];
|
|
104
|
+
const ungrouped = [];
|
|
105
|
+
const groupMap = new Map();
|
|
106
|
+
for (const route of routes) {
|
|
107
|
+
if (!route?.path || !route?.operationId)
|
|
108
|
+
continue;
|
|
109
|
+
const item = {
|
|
110
|
+
id: String(route.operationId),
|
|
111
|
+
label: String(route.label ?? route.operationId),
|
|
112
|
+
route: normalizeRoutePath(String(route.path ?? route.operationId ?? ''))
|
|
113
|
+
};
|
|
114
|
+
const rawGroup = route.group ? String(route.group) : '';
|
|
115
|
+
if (!rawGroup) {
|
|
116
|
+
ungrouped.push(item);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const groupId = toKebab(rawGroup);
|
|
120
|
+
let group = groupMap.get(groupId);
|
|
121
|
+
if (!group) {
|
|
122
|
+
group = {
|
|
123
|
+
id: groupId,
|
|
124
|
+
label: toLabel(rawGroup),
|
|
125
|
+
items: []
|
|
126
|
+
};
|
|
127
|
+
groupMap.set(groupId, group);
|
|
128
|
+
groups.push(group);
|
|
129
|
+
}
|
|
130
|
+
group.items.push(item);
|
|
131
|
+
}
|
|
132
|
+
return { groups, ungrouped };
|
|
133
|
+
}
|
|
134
|
+
function toKebab(value) {
|
|
135
|
+
return String(value)
|
|
136
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
137
|
+
.replace(/[_\\s]+/g, '-')
|
|
138
|
+
.toLowerCase();
|
|
139
|
+
}
|
|
140
|
+
function toLabel(value) {
|
|
141
|
+
return String(value)
|
|
142
|
+
.replace(/[_-]/g, ' ')
|
|
143
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
144
|
+
.replace(/\\b\\w/g, char => char.toUpperCase());
|
|
145
|
+
}
|
|
146
|
+
function normalizeRoutePath(value) {
|
|
147
|
+
if (!value)
|
|
148
|
+
return value;
|
|
149
|
+
if (value.includes('/'))
|
|
150
|
+
return value.replace(/^\//, '');
|
|
151
|
+
const pascal = toPascalCase(value);
|
|
152
|
+
return toRouteSegment(pascal);
|
|
153
|
+
}
|
|
154
|
+
function toRouteSegment(value) {
|
|
155
|
+
if (!value)
|
|
156
|
+
return value;
|
|
157
|
+
return value[0].toLowerCase() + value.slice(1);
|
|
158
|
+
}
|
|
159
|
+
function toPascalCase(value) {
|
|
160
|
+
return String(value)
|
|
161
|
+
.split(/[^a-zA-Z0-9]+/)
|
|
162
|
+
.filter(Boolean)
|
|
163
|
+
.map(part => part[0].toUpperCase() + part.slice(1))
|
|
164
|
+
.join('');
|
|
165
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -7,12 +7,15 @@ const angular_1 = require("./commands/angular");
|
|
|
7
7
|
const login_1 = require("./commands/login");
|
|
8
8
|
const config_1 = require("./runtime/config");
|
|
9
9
|
const telemetry_1 = require("./telemetry");
|
|
10
|
+
const logger_1 = require("./runtime/logger");
|
|
10
11
|
const program = new commander_1.Command();
|
|
11
12
|
program
|
|
12
13
|
.name('generate-ui')
|
|
13
14
|
.description('Generate UI from OpenAPI')
|
|
14
15
|
.version((0, config_1.getCliVersion)())
|
|
15
|
-
.option('--no-telemetry', 'Disable telemetry')
|
|
16
|
+
.option('--no-telemetry', 'Disable telemetry')
|
|
17
|
+
.option('--dev', 'Enable verbose logs')
|
|
18
|
+
.option('--verbose', 'Enable verbose logs (same as --dev)');
|
|
16
19
|
/**
|
|
17
20
|
* 1️⃣ OpenAPI → Screen schemas
|
|
18
21
|
*/
|
|
@@ -23,7 +26,8 @@ program
|
|
|
23
26
|
.option('--output <path>', 'Output directory for generate-ui (default: ./src/generate-ui or ./generate-ui)')
|
|
24
27
|
.option('-d, --debug', 'Explain merge decisions')
|
|
25
28
|
.action(async (options) => {
|
|
26
|
-
const { telemetry } = program.opts();
|
|
29
|
+
const { telemetry, dev, verbose } = program.opts();
|
|
30
|
+
(0, logger_1.setVerbose)(Boolean(dev || verbose));
|
|
27
31
|
try {
|
|
28
32
|
await (0, telemetry_1.trackGenerateCalled)();
|
|
29
33
|
await (0, generate_1.generate)({
|
|
@@ -46,7 +50,8 @@ program
|
|
|
46
50
|
.option('-s, --schemas <path>', 'Directory containing generate-ui (with overlays/)')
|
|
47
51
|
.option('-f, --features <path>', 'Angular features output directory')
|
|
48
52
|
.action(async (options) => {
|
|
49
|
-
const { telemetry } = program.opts();
|
|
53
|
+
const { telemetry, dev, verbose } = program.opts();
|
|
54
|
+
(0, logger_1.setVerbose)(Boolean(dev || verbose));
|
|
50
55
|
try {
|
|
51
56
|
await (0, angular_1.angular)({
|
|
52
57
|
schemasPath: options.schemas,
|
|
@@ -65,7 +70,8 @@ program
|
|
|
65
70
|
.command('login')
|
|
66
71
|
.description('Login to unlock Dev features')
|
|
67
72
|
.action(async () => {
|
|
68
|
-
const { telemetry } = program.opts();
|
|
73
|
+
const { telemetry, dev, verbose } = program.opts();
|
|
74
|
+
(0, logger_1.setVerbose)(Boolean(dev || verbose));
|
|
69
75
|
try {
|
|
70
76
|
await (0, login_1.login)({ telemetryEnabled: telemetry });
|
|
71
77
|
}
|
|
@@ -76,6 +82,15 @@ program
|
|
|
76
82
|
function handleCliError(error) {
|
|
77
83
|
if (error instanceof Error) {
|
|
78
84
|
console.error(error.message.replace(/\\n/g, '\n'));
|
|
85
|
+
if ((0, logger_1.isVerbose)() && error.stack) {
|
|
86
|
+
console.error('');
|
|
87
|
+
console.error('🔎 Stack trace:');
|
|
88
|
+
console.error(error.stack);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.error('');
|
|
92
|
+
console.error('ℹ️ Tip: re-run with --dev to see detailed logs.');
|
|
93
|
+
}
|
|
79
94
|
}
|
|
80
95
|
else {
|
|
81
96
|
console.error('Unexpected error');
|
|
@@ -86,7 +86,7 @@ async function getPermissions() {
|
|
|
86
86
|
return { plan: cache.plan, features: cache.features };
|
|
87
87
|
}
|
|
88
88
|
if (tokenPresent) {
|
|
89
|
-
throw new Error('
|
|
89
|
+
throw new Error('Login concluído, mas não foi possível validar sua licença agora. Verifique sua conexão com a API e tente novamente.');
|
|
90
90
|
}
|
|
91
91
|
return FREE_DEFAULT;
|
|
92
92
|
}
|
package/dist/license/token.js
CHANGED
|
@@ -25,16 +25,32 @@ function loadToken() {
|
|
|
25
25
|
const parsed = JSON.parse(fs_1.default.readFileSync(TOKEN_PATH, 'utf-8'));
|
|
26
26
|
if (!parsed.accessToken || !parsed.expiresAt)
|
|
27
27
|
return null;
|
|
28
|
-
const expiresAt =
|
|
29
|
-
if (
|
|
28
|
+
const expiresAt = normalizeExpiresAt(parsed.expiresAt);
|
|
29
|
+
if (!expiresAt || expiresAt <= Date.now()) {
|
|
30
30
|
return null;
|
|
31
31
|
}
|
|
32
|
-
return
|
|
32
|
+
return {
|
|
33
|
+
...parsed,
|
|
34
|
+
expiresAt: new Date(expiresAt).toISOString()
|
|
35
|
+
};
|
|
33
36
|
}
|
|
34
37
|
catch {
|
|
35
38
|
return null;
|
|
36
39
|
}
|
|
37
40
|
}
|
|
41
|
+
function normalizeExpiresAt(value) {
|
|
42
|
+
const trimmed = String(value).trim();
|
|
43
|
+
if (!trimmed.length)
|
|
44
|
+
return null;
|
|
45
|
+
const asNumber = Number(trimmed);
|
|
46
|
+
if (Number.isFinite(asNumber)) {
|
|
47
|
+
return asNumber < 1e12 ? asNumber * 1000 : asNumber;
|
|
48
|
+
}
|
|
49
|
+
const parsed = new Date(trimmed).getTime();
|
|
50
|
+
if (Number.isNaN(parsed))
|
|
51
|
+
return null;
|
|
52
|
+
return parsed;
|
|
53
|
+
}
|
|
38
54
|
function saveToken(token) {
|
|
39
55
|
ensureConfigDir();
|
|
40
56
|
fs_1.default.writeFileSync(TOKEN_PATH, JSON.stringify(token, null, 2));
|
package/dist/postinstall.js
CHANGED
|
@@ -26,3 +26,10 @@ async function sendInstallEvent() {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
void sendInstallEvent();
|
|
29
|
+
console.log('');
|
|
30
|
+
console.log('👋 GenerateUI CLI installed');
|
|
31
|
+
console.log(' Quick start:');
|
|
32
|
+
console.log(' 1) generate-ui generate --openapi /path/to/openapi.yaml');
|
|
33
|
+
console.log(' 2) generate-ui angular --schemas /path/to/generate-ui --features /path/to/app/src/app/features');
|
|
34
|
+
console.log(' Tip: customize screens in generate-ui/overlays and menu in generate-ui/menu.overrides.json');
|
|
35
|
+
console.log('');
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setVerbose = setVerbose;
|
|
4
|
+
exports.isVerbose = isVerbose;
|
|
5
|
+
exports.logDebug = logDebug;
|
|
6
|
+
exports.logStep = logStep;
|
|
7
|
+
exports.logTip = logTip;
|
|
8
|
+
let verbose = false;
|
|
9
|
+
function setVerbose(value) {
|
|
10
|
+
verbose = value;
|
|
11
|
+
}
|
|
12
|
+
function isVerbose() {
|
|
13
|
+
return verbose;
|
|
14
|
+
}
|
|
15
|
+
function logDebug(message) {
|
|
16
|
+
if (!verbose)
|
|
17
|
+
return;
|
|
18
|
+
console.log(`🔎 ${message}`);
|
|
19
|
+
}
|
|
20
|
+
function logStep(message) {
|
|
21
|
+
if (!verbose)
|
|
22
|
+
return;
|
|
23
|
+
console.log(`🧭 ${message}`);
|
|
24
|
+
}
|
|
25
|
+
function logTip(message) {
|
|
26
|
+
if (!verbose)
|
|
27
|
+
return;
|
|
28
|
+
console.log(`💡 ${message}`);
|
|
29
|
+
}
|