@fireberry/cli 0.0.5-beta.9 → 0.2.1

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.
@@ -4,14 +4,24 @@ import yaml from "js-yaml";
4
4
  import chalk from "chalk";
5
5
  import * as tar from "tar";
6
6
  import os from "node:os";
7
- import { Manifest, ManifestComponent, ZippedComponent } from "../api/types.js";
7
+ import {
8
+ Manifest,
9
+ ZippedComponent,
10
+ UntypedManifestComponent,
11
+ RecordComponentSettings,
12
+ GlobalMenuComponentSettings,
13
+ SideMenuComponentSettings,
14
+ } from "../api/types.js";
15
+ import { COMPONENT_TYPE } from "../constants/component-types.js";
16
+ import { HEIGHT_OPTIONS } from "../constants/height-options.js";
8
17
 
9
- export const getManifest = async (): Promise<Manifest> => {
10
- const manifestPath = path.join(process.cwd(), "manifest.yml");
18
+ export const getManifest = async (basePath?: string): Promise<Manifest> => {
19
+ const manifestPath = path.join(basePath || process.cwd(), "manifest.yml");
20
+ const searchDir = basePath || process.cwd();
11
21
 
12
22
  if (!(await fs.pathExists(manifestPath))) {
13
23
  throw new Error(
14
- `No manifest.yml found at ${chalk.yellow(process.cwd())}.\n` +
24
+ `No manifest.yml found at ${chalk.yellow(searchDir)}.\n` +
15
25
  `Please run this command from your Fireberry app directory.`
16
26
  );
17
27
  }
@@ -33,9 +43,147 @@ export const getManifest = async (): Promise<Manifest> => {
33
43
  return manifest;
34
44
  };
35
45
 
46
+ const validateRecordComponentSettings = (
47
+ comp: UntypedManifestComponent
48
+ ): void => {
49
+ const settings = comp.settings as
50
+ | Partial<RecordComponentSettings>
51
+ | undefined;
52
+
53
+ if (!settings) {
54
+ throw new Error(
55
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) is missing required settings`
56
+ );
57
+ }
58
+
59
+ const requiredFields: (keyof RecordComponentSettings)[] = [
60
+ "iconName",
61
+ "iconColor",
62
+ "objectType",
63
+ "height",
64
+ ];
65
+
66
+ for (const fieldName of requiredFields) {
67
+ if (settings[fieldName] === undefined || settings[fieldName] === null) {
68
+ throw new Error(
69
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) is missing required setting: ${fieldName}`
70
+ );
71
+ }
72
+ }
73
+
74
+ if (typeof settings.iconName !== "string") {
75
+ throw new Error(
76
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "iconName" must be a string`
77
+ );
78
+ }
79
+
80
+ if (typeof settings.iconColor !== "string") {
81
+ throw new Error(
82
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "iconColor" must be a string`
83
+ );
84
+ }
85
+
86
+ if (typeof settings.objectType !== "number") {
87
+ throw new Error(
88
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "objectType" must be a number`
89
+ );
90
+ }
91
+ if (!settings.height) {
92
+ throw new Error(
93
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "height" must be one of: ${HEIGHT_OPTIONS.join(" | ")}`
94
+ );
95
+ }
96
+
97
+ if (!HEIGHT_OPTIONS.includes(settings.height as any)) {
98
+ throw new Error(
99
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.RECORD}) setting "height" must be one of: ${HEIGHT_OPTIONS.join(" | ")}`
100
+ );
101
+ }
102
+ };
103
+
104
+ const validateGlobalMenuComponentSettings = (
105
+ comp: UntypedManifestComponent
106
+ ): void => {
107
+ const settings = comp.settings as
108
+ | Partial<GlobalMenuComponentSettings>
109
+ | undefined;
110
+
111
+ if (!settings) {
112
+ throw new Error(
113
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.GLOBAL_MENU}) is missing required settings`
114
+ );
115
+ }
116
+
117
+ if (!settings.displayName) {
118
+ throw new Error(
119
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.GLOBAL_MENU}) is missing required setting: displayName`
120
+ );
121
+ }
122
+
123
+ if (typeof settings.displayName !== "string") {
124
+ throw new Error(
125
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.GLOBAL_MENU}) setting "displayName" must be a string`
126
+ );
127
+ }
128
+ };
129
+
130
+ const validateSideMenuComponentSettings = (
131
+ comp: UntypedManifestComponent
132
+ ): void => {
133
+ const settings = comp.settings as
134
+ | Partial<SideMenuComponentSettings>
135
+ | undefined;
136
+
137
+ if (!settings) {
138
+ throw new Error(
139
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) is missing required settings`
140
+ );
141
+ }
142
+
143
+ if (!settings.icon) {
144
+ throw new Error(
145
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) setting "icon" must be a string`
146
+ );
147
+ }
148
+
149
+ if (!settings.width) {
150
+ throw new Error(
151
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) setting "width" must be a S | M | L`
152
+ );
153
+ }
154
+
155
+ if (
156
+ settings.width !== "S" &&
157
+ settings.width !== "M" &&
158
+ settings.width !== "L"
159
+ ) {
160
+ throw new Error(
161
+ `Component "${comp.title}" (type: ${COMPONENT_TYPE.SIDE_MENU}) setting "width" must be a S | M | L`
162
+ );
163
+ }
164
+ };
165
+
166
+ const validateComponentSettings = (comp: UntypedManifestComponent): void => {
167
+ switch (comp.type) {
168
+ case COMPONENT_TYPE.RECORD:
169
+ validateRecordComponentSettings(comp);
170
+ break;
171
+ case COMPONENT_TYPE.GLOBAL_MENU:
172
+ validateGlobalMenuComponentSettings(comp);
173
+ break;
174
+ case COMPONENT_TYPE.SIDE_MENU:
175
+ validateSideMenuComponentSettings(comp);
176
+ break;
177
+ default:
178
+ throw new Error(
179
+ `Component "${comp.title}" has unsupported type: ${comp.type}. Supported types: ${COMPONENT_TYPE.RECORD}, ${COMPONENT_TYPE.GLOBAL_MENU}`
180
+ );
181
+ }
182
+ };
183
+
36
184
  export const validateComponentBuild = async (
37
185
  componentPath: string,
38
- comp: ManifestComponent
186
+ comp: UntypedManifestComponent
39
187
  ) => {
40
188
  if (!(await fs.pathExists(componentPath))) {
41
189
  throw new Error(
@@ -49,10 +197,13 @@ export const validateComponentBuild = async (
49
197
 
50
198
  if (stats.isDirectory()) {
51
199
  const files = await fs.readdir(componentPath);
200
+
52
201
  if (files.length === 0) {
53
- throw new Error(`Component <${comp.key}> at: /${comp.path} not found`);
202
+ throw new Error(`Component <${comp.id}> at: /${comp.path} not found`);
54
203
  }
55
204
  }
205
+
206
+ validateComponentSettings(comp);
56
207
  };
57
208
 
58
209
  export const zipComponentBuild = async (
@@ -102,31 +253,42 @@ export const zipComponentBuild = async (
102
253
  }
103
254
  };
104
255
 
105
- export const handleComponents = async (
106
- manifest: Manifest
107
- ): Promise<ZippedComponent[]> => {
108
- const components = manifest.components;
256
+ export const validateManifestComponents = async (manifest: Manifest) => {
257
+ const components = manifest.components as unknown as
258
+ | UntypedManifestComponent[]
259
+ | undefined;
109
260
  if (!components || components.length === 0) {
110
- return [];
261
+ throw new Error("No components found in manifest");
262
+ }
263
+
264
+ const ids = components.map((comp) => comp.id);
265
+ if (new Set(ids).size !== ids.length) {
266
+ throw new Error("All component ids must be unique");
111
267
  }
112
268
 
113
- const keys = components.map((comp) => comp.key);
114
- if (new Set(keys).size !== keys.length) {
115
- throw new Error("All component keys must be unique");
269
+ for (const comp of components) {
270
+ const componentPath = path.join(process.cwd(), comp.path);
271
+ await validateComponentBuild(componentPath, comp);
116
272
  }
273
+ };
274
+
275
+ export const handleComponents = async (
276
+ manifest: Manifest
277
+ ): Promise<ZippedComponent[]> => {
278
+ await validateManifestComponents(manifest);
279
+ const components =
280
+ manifest.components as unknown as UntypedManifestComponent[];
117
281
 
118
282
  const zippedComponents: ZippedComponent[] = [];
119
283
 
120
284
  for (const comp of components) {
121
285
  const componentPath = path.join(process.cwd(), comp.path);
122
286
 
123
- await validateComponentBuild(componentPath, comp);
124
-
125
287
  const buildBuffer = await zipComponentBuild(componentPath, comp.title);
126
288
 
127
289
  zippedComponents.push({
128
290
  title: comp.title,
129
- key: comp.key,
291
+ id: comp.id,
130
292
  build: buildBuffer,
131
293
  });
132
294
  }