@dboio/cli 0.9.8 → 0.10.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.
@@ -1,17 +1,23 @@
1
- import { mkdir, stat, writeFile, access } from 'fs/promises';
1
+ import { mkdir, stat, readFile, writeFile, access } from 'fs/promises';
2
2
  import { join } from 'path';
3
3
  import { DEFAULT_PROJECT_DIRS } from './structure.js';
4
4
  import { log } from './logger.js';
5
+ import { applyTrashIcon } from './folder-icon.js';
5
6
 
6
7
  /**
7
8
  * Scaffold the standard DBO project directory structure in cwd.
8
9
  * Creates missing directories, skips existing ones, warns on name conflicts.
9
10
  * Also creates app.json with {} if absent.
10
11
  *
12
+ * When appShortName is provided, also creates media sub-directories:
13
+ * media/<appShortName>/app/
14
+ * media/<appShortName>/user/
15
+ *
11
16
  * @param {string} [cwd=process.cwd()]
17
+ * @param {{ appShortName?: string }} [options]
12
18
  * @returns {Promise<{ created: string[], skipped: string[], warned: string[] }>}
13
19
  */
14
- export async function scaffoldProjectDirs(cwd = process.cwd()) {
20
+ export async function scaffoldProjectDirs(cwd = process.cwd(), options = {}) {
15
21
  const created = [];
16
22
  const skipped = [];
17
23
  const warned = [];
@@ -33,6 +39,29 @@ export async function scaffoldProjectDirs(cwd = process.cwd()) {
33
39
  }
34
40
  }
35
41
 
42
+ // Create media sub-directories when app short name is known:
43
+ // media/<appShortName>/app/ — app-level media assets
44
+ // media/<appShortName>/user/ — user-uploaded media
45
+ if (options.appShortName) {
46
+ const mediaSubs = [
47
+ `media/${options.appShortName}/app`,
48
+ `media/${options.appShortName}/user`,
49
+ ];
50
+ for (const sub of mediaSubs) {
51
+ const target = join(cwd, sub);
52
+ try {
53
+ await stat(target);
54
+ skipped.push(sub);
55
+ } catch {
56
+ await mkdir(target, { recursive: true });
57
+ created.push(sub);
58
+ }
59
+ }
60
+ }
61
+
62
+ // Best-effort: apply trash icon to the trash directory
63
+ await applyTrashIcon(join(cwd, 'trash'));
64
+
36
65
  // Create app.json if absent
37
66
  const appJsonPath = join(cwd, 'app.json');
38
67
  try {
@@ -42,6 +71,57 @@ export async function scaffoldProjectDirs(cwd = process.cwd()) {
42
71
  created.push('app.json');
43
72
  }
44
73
 
74
+ // Create manifest.json if absent
75
+ const manifestPath = join(cwd, 'manifest.json');
76
+ try {
77
+ await access(manifestPath);
78
+ } catch {
79
+ // Try to resolve values from local app.json; fall back to empty strings
80
+ let appName = '';
81
+ let shortName = '';
82
+ let description = '';
83
+ let bgColor = '#ffffff';
84
+ let domain = '';
85
+
86
+ try {
87
+ const appData = JSON.parse(await readFile(appJsonPath, 'utf8'));
88
+ appName = appData.Name || '';
89
+ shortName = appData.ShortName || '';
90
+ description = appData.Description || '';
91
+ domain = appData._domain || '';
92
+
93
+ // Find background_color from extension children (widget matching ShortName)
94
+ if (shortName && Array.isArray(appData.children?.extension)) {
95
+ for (const ext of appData.children.extension) {
96
+ if (ext.Descriptor === 'widget' && ext.String1 === shortName && ext.String4) {
97
+ bgColor = ext.String4;
98
+ break;
99
+ }
100
+ }
101
+ }
102
+ } catch { /* app.json missing or unparseable — use empty defaults */ }
103
+
104
+ const manifest = {
105
+ name: `${appName} | ${domain}`,
106
+ short_name: shortName,
107
+ description,
108
+ orientation: 'portrait',
109
+ start_url: shortName ? `/app/${shortName}/ui/` : '',
110
+ lang: 'en',
111
+ scope: shortName ? `/app/${shortName}/ui/` : '',
112
+ display_override: ['window-control-overlay', 'minimal-ui'],
113
+ display: 'standalone',
114
+ background_color: bgColor,
115
+ theme_color: '#000000',
116
+ id: shortName,
117
+ screenshots: [],
118
+ ios: {},
119
+ icons: [],
120
+ };
121
+ await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
122
+ created.push('manifest.json');
123
+ }
124
+
45
125
  return { created, skipped, warned };
46
126
  }
47
127
 
@@ -13,6 +13,7 @@ export const DEFAULT_PROJECT_DIRS = [
13
13
  'app_version',
14
14
  'docs',
15
15
  'site',
16
+ 'media',
16
17
  'extension',
17
18
  'data_source',
18
19
  'group',
@@ -43,6 +44,7 @@ export const ENTITY_DIR_NAMES = new Set([
43
44
  'extension',
44
45
  'app_version',
45
46
  'data_source',
47
+ 'media',
46
48
  'site',
47
49
  'group',
48
50
  'integration',