android2harmony 0.1.5 → 0.1.6

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.
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Resolve App Metadata — discover `bundle_name` / `app_name` from a HarmonyOS
4
+ * project and write `app-metadata.json`.
5
+ *
6
+ * Usage: resolve-metadata-tool.mjs --project-dir <dir> --output <path>
7
+ * Reads `<project-dir>/AppScope/app.json5` (JSON5 with comments/trailing commas),
8
+ * extracts `app.bundleName` and `app.label` (resolving `$string:xxx` references
9
+ * from the base-qualifier string.json), writes `{bundle_name, app_name, project_root}`
10
+ * to `--output`, and emits the same JSON to stdout.
11
+ */
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
14
+ // ---- JSON5 strip (remove comments + trailing commas, then JSON.parse) ----
15
+ function stripJson5(text) {
16
+ // Remove single-line comments (// ...) — but not inside strings
17
+ let result = '';
18
+ let inString = false;
19
+ let stringChar = '';
20
+ for (let i = 0; i < text.length; i++) {
21
+ const ch = text[i];
22
+ const next = text[i + 1];
23
+ if (inString) {
24
+ result += ch;
25
+ if (ch === '\\') {
26
+ result += next ?? '';
27
+ i++;
28
+ continue;
29
+ }
30
+ if (ch === stringChar)
31
+ inString = false;
32
+ continue;
33
+ }
34
+ if (ch === '"' || ch === "'") {
35
+ inString = true;
36
+ stringChar = ch;
37
+ result += ch;
38
+ continue;
39
+ }
40
+ if (ch === '/' && next === '/') {
41
+ while (i < text.length && text[i] !== '\n')
42
+ i++;
43
+ continue;
44
+ }
45
+ if (ch === '/' && next === '*') {
46
+ i += 2;
47
+ while (i < text.length - 1 && !(text[i] === '*' && text[i + 1] === '/'))
48
+ i++;
49
+ i++;
50
+ continue;
51
+ }
52
+ result += ch;
53
+ }
54
+ // Remove trailing commas before } or ]
55
+ result = result.replace(/,(\s*[}\]])/g, '$1');
56
+ return result;
57
+ }
58
+ function parseJson5(filePath) {
59
+ let raw = fs.readFileSync(filePath, 'utf-8');
60
+ if (raw.charCodeAt(0) === 0xfeff)
61
+ raw = raw.slice(1);
62
+ return JSON.parse(stripJson5(raw));
63
+ }
64
+ // ---- string resource resolver ----
65
+ function resolveStringResource(projectRoot, key) {
66
+ const stringJson = path.join(projectRoot, 'AppScope', 'resources', 'base', 'element', 'string.json');
67
+ if (!fs.existsSync(stringJson))
68
+ return null;
69
+ let doc;
70
+ try {
71
+ doc = parseJson5(stringJson);
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ const arr = doc?.string;
77
+ if (!Array.isArray(arr))
78
+ return null;
79
+ const entry = arr.find((e) => e?.name === key);
80
+ const value = entry?.value;
81
+ return typeof value === 'string' && value ? value : null;
82
+ }
83
+ // ---- main ----
84
+ function main() {
85
+ const args = process.argv.slice(2);
86
+ let projectDir = '';
87
+ let output = '';
88
+ for (let i = 0; i < args.length; i++) {
89
+ if (args[i] === '--project-dir')
90
+ projectDir = args[++i];
91
+ else if (args[i] === '--output')
92
+ output = args[++i];
93
+ else if (args[i] === '-h' || args[i] === '--help') {
94
+ console.log('Usage: resolve-metadata-tool.mjs --project-dir <dir> --output <path>');
95
+ process.exit(0);
96
+ }
97
+ }
98
+ if (!projectDir || !output) {
99
+ console.error('Usage: resolve-metadata-tool.mjs --project-dir <dir> --output <path>');
100
+ process.exit(1);
101
+ }
102
+ const projectRoot = path.resolve(projectDir);
103
+ const appJson5 = path.join(projectRoot, 'AppScope', 'app.json5');
104
+ if (!fs.existsSync(appJson5)) {
105
+ console.error(`not a HarmonyOS project root (no AppScope/app.json5): ${projectRoot}`);
106
+ process.exit(1);
107
+ }
108
+ let doc;
109
+ try {
110
+ doc = parseJson5(appJson5);
111
+ }
112
+ catch (e) {
113
+ console.error(`failed to parse ${appJson5}: ${e instanceof Error ? e.message : String(e)}`);
114
+ process.exit(1);
115
+ }
116
+ const app = doc?.app;
117
+ const bundleName = app?.bundleName;
118
+ if (typeof bundleName !== 'string' || !bundleName) {
119
+ console.error(`missing app.bundleName in ${appJson5}`);
120
+ process.exit(1);
121
+ }
122
+ const label = app?.label;
123
+ let appName;
124
+ if (typeof label === 'string' && label.startsWith('$string:')) {
125
+ const key = label.slice('$string:'.length);
126
+ const resolved = resolveStringResource(projectRoot, key);
127
+ if (resolved) {
128
+ appName = resolved;
129
+ }
130
+ else {
131
+ console.error(`warning: string resource '${key}' not found — using literal '${label}'`);
132
+ appName = label;
133
+ }
134
+ }
135
+ else if (typeof label === 'string' && label) {
136
+ appName = label;
137
+ }
138
+ else {
139
+ console.error(`warning: missing app.label — using bundleName as app_name`);
140
+ appName = bundleName;
141
+ }
142
+ const meta = { bundle_name: bundleName, app_name: appName, project_root: projectRoot };
143
+ fs.mkdirSync(path.dirname(path.resolve(output)), { recursive: true });
144
+ fs.writeFileSync(path.resolve(output), JSON.stringify(meta) + '\n', 'utf-8');
145
+ console.log(JSON.stringify(meta));
146
+ }
147
+ main();