appwrite-utils-cli 1.0.4 → 1.0.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.
- package/README.md +95 -0
- package/dist/config/yamlConfig.js +10 -1
- package/dist/interactiveCLI.d.ts +1 -0
- package/dist/interactiveCLI.js +67 -0
- package/dist/main.js +43 -0
- package/dist/utils/constantsGenerator.d.ts +19 -0
- package/dist/utils/constantsGenerator.js +309 -0
- package/dist/utils/loadConfigs.js +1 -1
- package/package.json +1 -1
- package/src/config/yamlConfig.ts +10 -1
- package/src/interactiveCLI.ts +74 -0
- package/src/main.ts +53 -0
- package/src/utils/constantsGenerator.ts +352 -0
- package/src/utils/loadConfigs.ts +1 -1
package/README.md
CHANGED
@@ -23,6 +23,7 @@
|
|
23
23
|
### Development Tools
|
24
24
|
- **Database Migrations**: Full migration control with progress tracking and operation summaries
|
25
25
|
- **Schema Generation**: Generate TypeScript and JSON schemas from database configurations
|
26
|
+
- **Constants Generation**: Generate cross-language constants files (TypeScript, Python, PHP, Dart, JSON, Env) for database, collection, bucket, and function IDs
|
26
27
|
- **Data Transfer**: Transfer data between databases, collections, and instances with real-time progress
|
27
28
|
- **Configuration Sync**: Bidirectional synchronization between local YAML configs and Appwrite projects
|
28
29
|
- **Function Management**: Deploy and manage Appwrite Functions with specification updates
|
@@ -218,8 +219,53 @@ Available options:
|
|
218
219
|
- `--functionId`: Function ID to update
|
219
220
|
- `--specification`: New function specification (s-0.5vcpu-512mb to s-8vcpu-8gb)
|
220
221
|
|
222
|
+
### Constants Generation
|
223
|
+
- `--generateConstants` / `--constants`: Generate cross-language constants files with database, collection, bucket, and function IDs
|
224
|
+
- `--constantsLanguages`: Comma-separated list of languages for constants generation (default: typescript)
|
225
|
+
- Available languages: `typescript`, `javascript`, `python`, `php`, `dart`, `json`, `env`
|
226
|
+
- `--constantsOutput`: Output directory for generated constants files (default: config-folder/constants)
|
227
|
+
|
221
228
|
## Examples
|
222
229
|
|
230
|
+
### Generate Constants
|
231
|
+
|
232
|
+
Generate cross-language constants files for all your Appwrite resource IDs:
|
233
|
+
|
234
|
+
```bash
|
235
|
+
# Generate TypeScript constants (default)
|
236
|
+
npx appwrite-utils-cli appwrite-migrate --generateConstants
|
237
|
+
|
238
|
+
# Generate multiple language formats
|
239
|
+
npx appwrite-utils-cli appwrite-migrate --generateConstants --constantsLanguages="typescript,python,php,json"
|
240
|
+
|
241
|
+
# Generate all available formats
|
242
|
+
npx appwrite-utils-cli appwrite-migrate --generateConstants --constantsLanguages="typescript,javascript,python,php,dart,json,env"
|
243
|
+
|
244
|
+
# Generate with custom output directory
|
245
|
+
npx appwrite-utils-cli appwrite-migrate --generateConstants --constantsOutput="./my-constants"
|
246
|
+
```
|
247
|
+
|
248
|
+
This generates constants files in your configuration directory (e.g., `.appwrite/constants/`) containing:
|
249
|
+
- Database IDs
|
250
|
+
- Collection IDs
|
251
|
+
- Bucket IDs
|
252
|
+
- Function IDs
|
253
|
+
|
254
|
+
**Example TypeScript output:**
|
255
|
+
```typescript
|
256
|
+
export const DATABASE_IDS = {
|
257
|
+
MAIN_DATABASE: "main"
|
258
|
+
} as const;
|
259
|
+
|
260
|
+
export const COLLECTION_IDS = {
|
261
|
+
USERS: "01JYDBQTB5W8SCBAYB654CCADQ",
|
262
|
+
POSTS: "01JYDBQTB5W8SCBAYB654POSTS"
|
263
|
+
} as const;
|
264
|
+
|
265
|
+
// Type helpers and utility arrays included
|
266
|
+
export type DatabaseId = typeof DATABASE_IDS[keyof typeof DATABASE_IDS];
|
267
|
+
```
|
268
|
+
|
223
269
|
### Transfer Databases
|
224
270
|
|
225
271
|
Transfer databases within the same project or from a local to a remote project:
|
@@ -281,6 +327,54 @@ This updated CLI ensures that developers have robust tools at their fingertips t
|
|
281
327
|
|
282
328
|
## Changelog
|
283
329
|
|
330
|
+
### 1.0.6 - Cross-Language Constants Generation
|
331
|
+
|
332
|
+
**🚀 Enhanced Developer Experience with Multi-Language Constants**
|
333
|
+
|
334
|
+
#### Constants Generation System
|
335
|
+
- **Cross-Language Support**: Generate constants in 7 languages for seamless multi-platform development
|
336
|
+
- **TypeScript**: Type-safe constants with `as const` and helper types
|
337
|
+
- **JavaScript**: ES6 modules with utility arrays
|
338
|
+
- **Python**: Class-based constants with snake_case dictionaries
|
339
|
+
- **PHP**: Static class methods and associative arrays
|
340
|
+
- **Dart**: Maps with individual getter methods (camelCase)
|
341
|
+
- **JSON**: Structured data with metadata for cross-platform integration
|
342
|
+
- **Environment Variables**: Prefixed environment variables for deployment
|
343
|
+
- **Smart Directory Structure**: Constants generated in `{config-folder}/constants/` by default
|
344
|
+
- **CLI Integration**: Both command-line and interactive mode support
|
345
|
+
- **Custom Output**: Support for custom output directories when needed
|
346
|
+
|
347
|
+
#### Resource ID Extraction
|
348
|
+
- **Database IDs**: Extract all configured database identifiers
|
349
|
+
- **Collection IDs**: Generate constants for all collection ULIDs/names
|
350
|
+
- **Bucket IDs**: Include storage bucket identifiers
|
351
|
+
- **Function IDs**: Support for Appwrite Function identifiers
|
352
|
+
- **Naming Conventions**: Language-appropriate naming (UPPER_CASE, camelCase, snake_case)
|
353
|
+
|
354
|
+
#### Developer Experience Improvements
|
355
|
+
- **Interactive Mode**: Checkbox selection for languages with smart defaults
|
356
|
+
- **CLI Commands**: Simple `--generateConstants` with language and output options
|
357
|
+
- **Type Safety**: Full TypeScript support with generated types and helpers
|
358
|
+
- **Cross-Platform Compatibility**: Enable seamless development across different tech stacks
|
359
|
+
|
360
|
+
#### Usage Examples
|
361
|
+
```bash
|
362
|
+
# Default TypeScript generation
|
363
|
+
npx appwrite-utils-cli appwrite-migrate --generateConstants
|
364
|
+
|
365
|
+
# Multi-language generation
|
366
|
+
npx appwrite-utils-cli appwrite-migrate --generateConstants --constantsLanguages="typescript,python,php,json"
|
367
|
+
|
368
|
+
# All formats with custom output
|
369
|
+
npx appwrite-utils-cli appwrite-migrate --generateConstants --constantsLanguages="typescript,javascript,python,php,dart,json,env" --constantsOutput="./constants"
|
370
|
+
```
|
371
|
+
|
372
|
+
**Migration Benefits**:
|
373
|
+
- Eliminates hardcoded resource IDs across codebases
|
374
|
+
- Enables type-safe resource access in TypeScript projects
|
375
|
+
- Supports multi-language development workflows
|
376
|
+
- Maintains constants alongside configuration for easy maintenance
|
377
|
+
|
284
378
|
### 1.0.1 - Function Templates & Attribute Type Improvements
|
285
379
|
|
286
380
|
**🚀 Enhanced Function Management & Type System Fixes**
|
@@ -377,6 +471,7 @@ This updated CLI ensures that developers have robust tools at their fingertips t
|
|
377
471
|
|
378
472
|
### Changelog
|
379
473
|
|
474
|
+
- 1.0.5: Fixed `.` directories being ignored. Normally a good thing
|
380
475
|
- 1.0.4: Fixed `appwriteConfig.yaml` being the name for the converted config, instead of `config.yaml`
|
381
476
|
- 1.0.3: Fixed appwriteConfig detection for `--it` so it detects when you can migrate your config
|
382
477
|
- 1.0.2: Fixed migrations, sorry about that!
|
@@ -243,13 +243,17 @@ export const findYamlConfig = (startDir) => {
|
|
243
243
|
path.join(startDir, "appwrite.yaml"),
|
244
244
|
path.join(startDir, "appwrite.yml"),
|
245
245
|
];
|
246
|
+
console.log(`DEBUG: Checking YAML paths in ${startDir}:`);
|
246
247
|
for (const configPath of possiblePaths) {
|
248
|
+
console.log(` - ${configPath}: ${fs.existsSync(configPath)}`);
|
247
249
|
if (fs.existsSync(configPath)) {
|
248
250
|
return configPath;
|
249
251
|
}
|
250
252
|
}
|
251
253
|
// Recursively search subdirectories for .appwrite folders
|
254
|
+
console.log(`DEBUG: Starting recursive search from ${startDir}`);
|
252
255
|
const yamlConfigInSubdirs = findYamlConfigRecursive(startDir);
|
256
|
+
console.log(`DEBUG: Recursive search result: ${yamlConfigInSubdirs}`);
|
253
257
|
if (yamlConfigInSubdirs) {
|
254
258
|
return yamlConfigInSubdirs;
|
255
259
|
}
|
@@ -307,23 +311,28 @@ const shouldIgnoreDirectory = (dirName) => {
|
|
307
311
|
return ignoredDirs.includes(dirName) ||
|
308
312
|
dirName.startsWith('.git') ||
|
309
313
|
dirName.startsWith('node_modules') ||
|
310
|
-
dirName.startsWith('.');
|
314
|
+
(dirName.startsWith('.') && dirName !== '.appwrite');
|
311
315
|
};
|
312
316
|
const findYamlConfigRecursive = (dir, depth = 0) => {
|
313
317
|
// Limit search depth to prevent infinite recursion
|
314
318
|
if (depth > 5) {
|
319
|
+
console.log(`DEBUG: Stopping search at depth ${depth} in ${dir}`);
|
315
320
|
return null;
|
316
321
|
}
|
317
322
|
if (shouldIgnoreDirectory(path.basename(dir))) {
|
323
|
+
console.log(`DEBUG: Ignoring directory ${dir}`);
|
318
324
|
return null;
|
319
325
|
}
|
320
326
|
try {
|
321
327
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
328
|
+
console.log(`DEBUG: Searching directory ${dir} at depth ${depth}, found ${entries.length} entries`);
|
322
329
|
for (const entry of entries) {
|
323
330
|
if (entry.isDirectory() && !shouldIgnoreDirectory(entry.name)) {
|
324
331
|
const fullPath = path.join(dir, entry.name);
|
332
|
+
console.log(`DEBUG: Checking subdirectory: ${fullPath}`);
|
325
333
|
// Check if this is an .appwrite directory
|
326
334
|
if (entry.name === ".appwrite") {
|
335
|
+
console.log(`DEBUG: Found .appwrite directory at ${fullPath}`);
|
327
336
|
const configPaths = [
|
328
337
|
path.join(fullPath, "appwriteConfig.yaml"),
|
329
338
|
path.join(fullPath, "appwriteConfig.yml"),
|
package/dist/interactiveCLI.d.ts
CHANGED
package/dist/interactiveCLI.js
CHANGED
@@ -13,6 +13,7 @@ import { DateTime } from "luxon";
|
|
13
13
|
import { createFunctionTemplate, deleteFunction, downloadLatestFunctionDeployment, getFunction, listFunctions, listSpecifications, } from "./functions/methods.js";
|
14
14
|
import { deployLocalFunction } from "./functions/deployments.js";
|
15
15
|
import { join } from "node:path";
|
16
|
+
import path from "path";
|
16
17
|
import fs from "node:fs";
|
17
18
|
import { SchemaGenerator } from "./shared/schemaGenerator.js";
|
18
19
|
import { ConfirmationDialogs } from "./shared/confirmationDialogs.js";
|
@@ -36,6 +37,7 @@ var CHOICES;
|
|
36
37
|
CHOICES["WIPE_DATABASE"] = "Wipe database";
|
37
38
|
CHOICES["WIPE_COLLECTIONS"] = "Wipe collections";
|
38
39
|
CHOICES["GENERATE_SCHEMAS"] = "Generate schemas";
|
40
|
+
CHOICES["GENERATE_CONSTANTS"] = "\uD83D\uDCCB Generate cross-language constants (TypeScript, Python, PHP, Dart, etc.)";
|
39
41
|
CHOICES["IMPORT_DATA"] = "Import data";
|
40
42
|
CHOICES["RELOAD_CONFIG"] = "Reload configuration files";
|
41
43
|
CHOICES["UPDATE_FUNCTION_SPEC"] = "Update function specifications";
|
@@ -123,6 +125,10 @@ export class InteractiveCLI {
|
|
123
125
|
await this.initControllerIfNeeded();
|
124
126
|
await this.generateSchemas();
|
125
127
|
break;
|
128
|
+
case CHOICES.GENERATE_CONSTANTS:
|
129
|
+
await this.initControllerIfNeeded();
|
130
|
+
await this.generateConstants();
|
131
|
+
break;
|
126
132
|
case CHOICES.IMPORT_DATA:
|
127
133
|
await this.initControllerIfNeeded();
|
128
134
|
await this.importData();
|
@@ -1167,6 +1173,66 @@ export class InteractiveCLI {
|
|
1167
1173
|
schemaGenerator.generateSchemas({ format: schemaType, verbose: true });
|
1168
1174
|
MessageFormatter.success("Schema generation completed", { prefix: "Schemas" });
|
1169
1175
|
}
|
1176
|
+
async generateConstants() {
|
1177
|
+
console.log(chalk.yellow("Generating cross-language constants..."));
|
1178
|
+
if (!this.controller?.config) {
|
1179
|
+
MessageFormatter.error("No configuration found", undefined, { prefix: "Constants" });
|
1180
|
+
return;
|
1181
|
+
}
|
1182
|
+
// Prompt for languages
|
1183
|
+
const { languages } = await inquirer.prompt([
|
1184
|
+
{
|
1185
|
+
type: "checkbox",
|
1186
|
+
name: "languages",
|
1187
|
+
message: "Select languages for constants generation:",
|
1188
|
+
choices: [
|
1189
|
+
{ name: "TypeScript", value: "typescript", checked: true },
|
1190
|
+
{ name: "JavaScript", value: "javascript" },
|
1191
|
+
{ name: "Python", value: "python" },
|
1192
|
+
{ name: "PHP", value: "php" },
|
1193
|
+
{ name: "Dart", value: "dart" },
|
1194
|
+
{ name: "JSON", value: "json" },
|
1195
|
+
{ name: "Environment Variables", value: "env" },
|
1196
|
+
],
|
1197
|
+
validate: (input) => {
|
1198
|
+
if (input.length === 0) {
|
1199
|
+
return "Please select at least one language";
|
1200
|
+
}
|
1201
|
+
return true;
|
1202
|
+
},
|
1203
|
+
},
|
1204
|
+
]);
|
1205
|
+
// Determine default output directory based on config location
|
1206
|
+
const configPath = this.controller.getAppwriteFolderPath();
|
1207
|
+
const defaultOutputDir = configPath
|
1208
|
+
? path.join(configPath, "constants")
|
1209
|
+
: path.join(process.cwd(), "constants");
|
1210
|
+
// Prompt for output directory
|
1211
|
+
const { outputDir } = await inquirer.prompt([
|
1212
|
+
{
|
1213
|
+
type: "input",
|
1214
|
+
name: "outputDir",
|
1215
|
+
message: "Output directory for constants files:",
|
1216
|
+
default: defaultOutputDir,
|
1217
|
+
validate: (input) => {
|
1218
|
+
if (!input.trim()) {
|
1219
|
+
return "Output directory cannot be empty";
|
1220
|
+
}
|
1221
|
+
return true;
|
1222
|
+
},
|
1223
|
+
},
|
1224
|
+
]);
|
1225
|
+
try {
|
1226
|
+
const { ConstantsGenerator } = await import("./utils/constantsGenerator.js");
|
1227
|
+
const generator = new ConstantsGenerator(this.controller.config);
|
1228
|
+
MessageFormatter.info(`Generating constants for: ${languages.join(", ")}`, { prefix: "Constants" });
|
1229
|
+
await generator.generateFiles(languages, outputDir);
|
1230
|
+
MessageFormatter.success(`Constants generated in ${outputDir}`, { prefix: "Constants" });
|
1231
|
+
}
|
1232
|
+
catch (error) {
|
1233
|
+
MessageFormatter.error("Failed to generate constants", error instanceof Error ? error : new Error(String(error)), { prefix: "Constants" });
|
1234
|
+
}
|
1235
|
+
}
|
1170
1236
|
async importData() {
|
1171
1237
|
console.log(chalk.yellow("Importing data..."));
|
1172
1238
|
const { doBackup } = await inquirer.prompt([
|
@@ -1386,6 +1452,7 @@ export class InteractiveCLI {
|
|
1386
1452
|
try {
|
1387
1453
|
// Check for YAML config first
|
1388
1454
|
const yamlConfigPath = findYamlConfig(this.currentDir);
|
1455
|
+
console.log(`DEBUG: YAML config search from ${this.currentDir}, found: ${yamlConfigPath}`);
|
1389
1456
|
if (yamlConfigPath) {
|
1390
1457
|
this.isUsingTypeScriptConfig = false;
|
1391
1458
|
MessageFormatter.info("Using YAML configuration", { prefix: "Config" });
|
package/dist/main.js
CHANGED
@@ -13,6 +13,7 @@ import chalk from "chalk";
|
|
13
13
|
import { listSpecifications } from "./functions/methods.js";
|
14
14
|
import { MessageFormatter } from "./shared/messageFormatter.js";
|
15
15
|
import { ConfirmationDialogs } from "./shared/confirmationDialogs.js";
|
16
|
+
import path from "path";
|
16
17
|
const argv = yargs(hideBin(process.argv))
|
17
18
|
.option("config", {
|
18
19
|
type: "string",
|
@@ -158,6 +159,21 @@ const argv = yargs(hideBin(process.argv))
|
|
158
159
|
alias: ["migrate"],
|
159
160
|
type: "boolean",
|
160
161
|
description: "Migrate appwriteConfig.ts to .appwrite structure with YAML configuration",
|
162
|
+
})
|
163
|
+
.option("generateConstants", {
|
164
|
+
alias: ["constants"],
|
165
|
+
type: "boolean",
|
166
|
+
description: "Generate cross-language constants file with database, collection, bucket, and function IDs",
|
167
|
+
})
|
168
|
+
.option("constantsLanguages", {
|
169
|
+
type: "string",
|
170
|
+
description: "Comma-separated list of languages for constants (typescript,javascript,python,php,dart,json,env)",
|
171
|
+
default: "typescript",
|
172
|
+
})
|
173
|
+
.option("constantsOutput", {
|
174
|
+
type: "string",
|
175
|
+
description: "Output directory for generated constants files (default: config-folder/constants)",
|
176
|
+
default: "auto",
|
161
177
|
})
|
162
178
|
.parse();
|
163
179
|
async function main() {
|
@@ -186,6 +202,33 @@ async function main() {
|
|
186
202
|
await migrateConfig(process.cwd());
|
187
203
|
return;
|
188
204
|
}
|
205
|
+
if (argv.generateConstants) {
|
206
|
+
const { ConstantsGenerator } = await import("./utils/constantsGenerator.js");
|
207
|
+
if (!controller.config) {
|
208
|
+
MessageFormatter.error("No Appwrite configuration found", undefined, { prefix: "Constants" });
|
209
|
+
return;
|
210
|
+
}
|
211
|
+
const languages = argv.constantsLanguages.split(",").map(l => l.trim());
|
212
|
+
// Determine output directory - use config folder/constants by default, or custom path if specified
|
213
|
+
let outputDir;
|
214
|
+
if (argv.constantsOutput === "auto") {
|
215
|
+
// Default case: use config directory + constants, fallback to current directory
|
216
|
+
const configPath = controller.getAppwriteFolderPath();
|
217
|
+
outputDir = configPath
|
218
|
+
? path.join(configPath, "constants")
|
219
|
+
: path.join(process.cwd(), "constants");
|
220
|
+
}
|
221
|
+
else {
|
222
|
+
// Custom output directory specified
|
223
|
+
outputDir = argv.constantsOutput;
|
224
|
+
}
|
225
|
+
MessageFormatter.info(`Generating constants for languages: ${languages.join(", ")}`, { prefix: "Constants" });
|
226
|
+
const generator = new ConstantsGenerator(controller.config);
|
227
|
+
await generator.generateFiles(languages, outputDir);
|
228
|
+
operationStats.generatedConstants = languages.length;
|
229
|
+
MessageFormatter.success(`Constants generated in ${outputDir}`, { prefix: "Constants" });
|
230
|
+
return;
|
231
|
+
}
|
189
232
|
if (!controller.config) {
|
190
233
|
MessageFormatter.error("No Appwrite connection found", undefined, { prefix: "CLI" });
|
191
234
|
return;
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { type AppwriteConfig } from "appwrite-utils";
|
2
|
+
export type SupportedLanguage = "typescript" | "javascript" | "python" | "php" | "dart" | "json" | "env";
|
3
|
+
export declare class ConstantsGenerator {
|
4
|
+
private config;
|
5
|
+
private constants;
|
6
|
+
constructor(config: AppwriteConfig);
|
7
|
+
private extractConstants;
|
8
|
+
private toConstantName;
|
9
|
+
private toCamelCase;
|
10
|
+
private toSnakeCase;
|
11
|
+
generateTypeScript(): string;
|
12
|
+
generateJavaScript(): string;
|
13
|
+
generatePython(): string;
|
14
|
+
generatePHP(): string;
|
15
|
+
generateDart(): string;
|
16
|
+
generateJSON(): string;
|
17
|
+
generateEnv(): string;
|
18
|
+
generateFiles(languages: SupportedLanguage[], outputDir: string): Promise<void>;
|
19
|
+
}
|
@@ -0,0 +1,309 @@
|
|
1
|
+
import fs from "fs/promises";
|
2
|
+
import path from "path";
|
3
|
+
import {} from "appwrite-utils";
|
4
|
+
import { MessageFormatter } from "../shared/messageFormatter.js";
|
5
|
+
export class ConstantsGenerator {
|
6
|
+
config;
|
7
|
+
constants;
|
8
|
+
constructor(config) {
|
9
|
+
this.config = config;
|
10
|
+
this.constants = this.extractConstants();
|
11
|
+
}
|
12
|
+
extractConstants() {
|
13
|
+
const constants = {
|
14
|
+
databases: {},
|
15
|
+
collections: {},
|
16
|
+
buckets: {},
|
17
|
+
functions: {}
|
18
|
+
};
|
19
|
+
// Extract database IDs
|
20
|
+
this.config.databases?.forEach(db => {
|
21
|
+
if (db.$id) {
|
22
|
+
const key = this.toConstantName(db.name || db.$id);
|
23
|
+
constants.databases[key] = db.$id;
|
24
|
+
}
|
25
|
+
});
|
26
|
+
// Extract collection IDs
|
27
|
+
this.config.collections?.forEach(collection => {
|
28
|
+
if (collection.$id) {
|
29
|
+
const key = this.toConstantName(collection.name || collection.$id);
|
30
|
+
constants.collections[key] = collection.$id;
|
31
|
+
}
|
32
|
+
});
|
33
|
+
// Extract bucket IDs
|
34
|
+
this.config.buckets?.forEach(bucket => {
|
35
|
+
if (bucket.$id) {
|
36
|
+
const key = this.toConstantName(bucket.name || bucket.$id);
|
37
|
+
constants.buckets[key] = bucket.$id;
|
38
|
+
}
|
39
|
+
});
|
40
|
+
// Extract function IDs
|
41
|
+
this.config.functions?.forEach(func => {
|
42
|
+
if (func.$id) {
|
43
|
+
const key = this.toConstantName(func.name || func.$id);
|
44
|
+
constants.functions[key] = func.$id;
|
45
|
+
}
|
46
|
+
});
|
47
|
+
return constants;
|
48
|
+
}
|
49
|
+
toConstantName(name) {
|
50
|
+
return name
|
51
|
+
.replace(/[^a-zA-Z0-9]/g, '_')
|
52
|
+
.replace(/_+/g, '_')
|
53
|
+
.replace(/^_|_$/g, '')
|
54
|
+
.toUpperCase();
|
55
|
+
}
|
56
|
+
toCamelCase(name) {
|
57
|
+
return name
|
58
|
+
.toLowerCase()
|
59
|
+
.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
60
|
+
}
|
61
|
+
toSnakeCase(name) {
|
62
|
+
return name.toLowerCase();
|
63
|
+
}
|
64
|
+
generateTypeScript() {
|
65
|
+
const { databases, collections, buckets, functions } = this.constants;
|
66
|
+
return `// Auto-generated Appwrite constants
|
67
|
+
// Generated on ${new Date().toISOString()}
|
68
|
+
|
69
|
+
export const DATABASE_IDS = {
|
70
|
+
${Object.entries(databases).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
71
|
+
} as const;
|
72
|
+
|
73
|
+
export const COLLECTION_IDS = {
|
74
|
+
${Object.entries(collections).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
75
|
+
} as const;
|
76
|
+
|
77
|
+
export const BUCKET_IDS = {
|
78
|
+
${Object.entries(buckets).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
79
|
+
} as const;
|
80
|
+
|
81
|
+
export const FUNCTION_IDS = {
|
82
|
+
${Object.entries(functions).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
83
|
+
} as const;
|
84
|
+
|
85
|
+
// Type helpers
|
86
|
+
export type DatabaseId = typeof DATABASE_IDS[keyof typeof DATABASE_IDS];
|
87
|
+
export type CollectionId = typeof COLLECTION_IDS[keyof typeof COLLECTION_IDS];
|
88
|
+
export type BucketId = typeof BUCKET_IDS[keyof typeof BUCKET_IDS];
|
89
|
+
export type FunctionId = typeof FUNCTION_IDS[keyof typeof FUNCTION_IDS];
|
90
|
+
|
91
|
+
// Helper objects for runtime use
|
92
|
+
export const ALL_DATABASE_IDS = Object.values(DATABASE_IDS);
|
93
|
+
export const ALL_COLLECTION_IDS = Object.values(COLLECTION_IDS);
|
94
|
+
export const ALL_BUCKET_IDS = Object.values(BUCKET_IDS);
|
95
|
+
export const ALL_FUNCTION_IDS = Object.values(FUNCTION_IDS);
|
96
|
+
`;
|
97
|
+
}
|
98
|
+
generateJavaScript() {
|
99
|
+
const { databases, collections, buckets, functions } = this.constants;
|
100
|
+
return `// Auto-generated Appwrite constants
|
101
|
+
// Generated on ${new Date().toISOString()}
|
102
|
+
|
103
|
+
export const DATABASE_IDS = {
|
104
|
+
${Object.entries(databases).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
105
|
+
};
|
106
|
+
|
107
|
+
export const COLLECTION_IDS = {
|
108
|
+
${Object.entries(collections).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
109
|
+
};
|
110
|
+
|
111
|
+
export const BUCKET_IDS = {
|
112
|
+
${Object.entries(buckets).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
113
|
+
};
|
114
|
+
|
115
|
+
export const FUNCTION_IDS = {
|
116
|
+
${Object.entries(functions).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
117
|
+
};
|
118
|
+
|
119
|
+
// Helper arrays for runtime use
|
120
|
+
export const ALL_DATABASE_IDS = Object.values(DATABASE_IDS);
|
121
|
+
export const ALL_COLLECTION_IDS = Object.values(COLLECTION_IDS);
|
122
|
+
export const ALL_BUCKET_IDS = Object.values(BUCKET_IDS);
|
123
|
+
export const ALL_FUNCTION_IDS = Object.values(FUNCTION_IDS);
|
124
|
+
`;
|
125
|
+
}
|
126
|
+
generatePython() {
|
127
|
+
const { databases, collections, buckets, functions } = this.constants;
|
128
|
+
return `# Auto-generated Appwrite constants
|
129
|
+
# Generated on ${new Date().toISOString()}
|
130
|
+
|
131
|
+
class DatabaseIds:
|
132
|
+
"""Database ID constants"""
|
133
|
+
${Object.entries(databases).map(([key, value]) => ` ${key} = "${value}"`).join('\n')}
|
134
|
+
|
135
|
+
class CollectionIds:
|
136
|
+
"""Collection ID constants"""
|
137
|
+
${Object.entries(collections).map(([key, value]) => ` ${key} = "${value}"`).join('\n')}
|
138
|
+
|
139
|
+
class BucketIds:
|
140
|
+
"""Bucket ID constants"""
|
141
|
+
${Object.entries(buckets).map(([key, value]) => ` ${key} = "${value}"`).join('\n')}
|
142
|
+
|
143
|
+
class FunctionIds:
|
144
|
+
"""Function ID constants"""
|
145
|
+
${Object.entries(functions).map(([key, value]) => ` ${key} = "${value}"`).join('\n')}
|
146
|
+
|
147
|
+
# Helper dictionaries for runtime use
|
148
|
+
DATABASE_ID_MAP = {
|
149
|
+
${Object.entries(databases).map(([key, value]) => ` "${this.toSnakeCase(key)}": "${value}"`).join(',\n')}
|
150
|
+
}
|
151
|
+
|
152
|
+
COLLECTION_ID_MAP = {
|
153
|
+
${Object.entries(collections).map(([key, value]) => ` "${this.toSnakeCase(key)}": "${value}"`).join(',\n')}
|
154
|
+
}
|
155
|
+
|
156
|
+
BUCKET_ID_MAP = {
|
157
|
+
${Object.entries(buckets).map(([key, value]) => ` "${this.toSnakeCase(key)}": "${value}"`).join(',\n')}
|
158
|
+
}
|
159
|
+
|
160
|
+
FUNCTION_ID_MAP = {
|
161
|
+
${Object.entries(functions).map(([key, value]) => ` "${this.toSnakeCase(key)}": "${value}"`).join(',\n')}
|
162
|
+
}
|
163
|
+
`;
|
164
|
+
}
|
165
|
+
generatePHP() {
|
166
|
+
const { databases, collections, buckets, functions } = this.constants;
|
167
|
+
return `<?php
|
168
|
+
// Auto-generated Appwrite constants
|
169
|
+
// Generated on ${new Date().toISOString()}
|
170
|
+
|
171
|
+
class AppwriteConstants {
|
172
|
+
|
173
|
+
const DATABASE_IDS = [
|
174
|
+
${Object.entries(databases).map(([key, value]) => ` '${key}' => '${value}'`).join(',\n')}
|
175
|
+
];
|
176
|
+
|
177
|
+
const COLLECTION_IDS = [
|
178
|
+
${Object.entries(collections).map(([key, value]) => ` '${key}' => '${value}'`).join(',\n')}
|
179
|
+
];
|
180
|
+
|
181
|
+
const BUCKET_IDS = [
|
182
|
+
${Object.entries(buckets).map(([key, value]) => ` '${key}' => '${value}'`).join(',\n')}
|
183
|
+
];
|
184
|
+
|
185
|
+
const FUNCTION_IDS = [
|
186
|
+
${Object.entries(functions).map(([key, value]) => ` '${key}' => '${value}'`).join(',\n')}
|
187
|
+
];
|
188
|
+
|
189
|
+
/**
|
190
|
+
* Get all database IDs as array
|
191
|
+
*/
|
192
|
+
public static function getAllDatabaseIds(): array {
|
193
|
+
return array_values(self::DATABASE_IDS);
|
194
|
+
}
|
195
|
+
|
196
|
+
/**
|
197
|
+
* Get all collection IDs as array
|
198
|
+
*/
|
199
|
+
public static function getAllCollectionIds(): array {
|
200
|
+
return array_values(self::COLLECTION_IDS);
|
201
|
+
}
|
202
|
+
|
203
|
+
/**
|
204
|
+
* Get all bucket IDs as array
|
205
|
+
*/
|
206
|
+
public static function getAllBucketIds(): array {
|
207
|
+
return array_values(self::BUCKET_IDS);
|
208
|
+
}
|
209
|
+
|
210
|
+
/**
|
211
|
+
* Get all function IDs as array
|
212
|
+
*/
|
213
|
+
public static function getAllFunctionIds(): array {
|
214
|
+
return array_values(self::FUNCTION_IDS);
|
215
|
+
}
|
216
|
+
}
|
217
|
+
`;
|
218
|
+
}
|
219
|
+
generateDart() {
|
220
|
+
const { databases, collections, buckets, functions } = this.constants;
|
221
|
+
return `// Auto-generated Appwrite constants
|
222
|
+
// Generated on ${new Date().toISOString()}
|
223
|
+
|
224
|
+
class AppwriteConstants {
|
225
|
+
|
226
|
+
static const Map<String, String> databaseIds = {
|
227
|
+
${Object.entries(databases).map(([key, value]) => ` '${this.toCamelCase(key)}': '${value}'`).join(',\n')}
|
228
|
+
};
|
229
|
+
|
230
|
+
static const Map<String, String> collectionIds = {
|
231
|
+
${Object.entries(collections).map(([key, value]) => ` '${this.toCamelCase(key)}': '${value}'`).join(',\n')}
|
232
|
+
};
|
233
|
+
|
234
|
+
static const Map<String, String> bucketIds = {
|
235
|
+
${Object.entries(buckets).map(([key, value]) => ` '${this.toCamelCase(key)}': '${value}'`).join(',\n')}
|
236
|
+
};
|
237
|
+
|
238
|
+
static const Map<String, String> functionIds = {
|
239
|
+
${Object.entries(functions).map(([key, value]) => ` '${this.toCamelCase(key)}': '${value}'`).join(',\n')}
|
240
|
+
};
|
241
|
+
|
242
|
+
// Helper getters for individual IDs
|
243
|
+
${Object.entries(databases).map(([key, value]) => ` static String get ${this.toCamelCase(key)}DatabaseId => '${value}';`).join('\n')}
|
244
|
+
|
245
|
+
${Object.entries(collections).map(([key, value]) => ` static String get ${this.toCamelCase(key)}CollectionId => '${value}';`).join('\n')}
|
246
|
+
|
247
|
+
${Object.entries(buckets).map(([key, value]) => ` static String get ${this.toCamelCase(key)}BucketId => '${value}';`).join('\n')}
|
248
|
+
|
249
|
+
${Object.entries(functions).map(([key, value]) => ` static String get ${this.toCamelCase(key)}FunctionId => '${value}';`).join('\n')}
|
250
|
+
}
|
251
|
+
`;
|
252
|
+
}
|
253
|
+
generateJSON() {
|
254
|
+
return JSON.stringify({
|
255
|
+
meta: {
|
256
|
+
generated: new Date().toISOString(),
|
257
|
+
generator: "appwrite-utils-cli"
|
258
|
+
},
|
259
|
+
databases: this.constants.databases,
|
260
|
+
collections: this.constants.collections,
|
261
|
+
buckets: this.constants.buckets,
|
262
|
+
functions: this.constants.functions
|
263
|
+
}, null, 2);
|
264
|
+
}
|
265
|
+
generateEnv() {
|
266
|
+
const { databases, collections, buckets, functions } = this.constants;
|
267
|
+
const lines = [
|
268
|
+
"# Auto-generated Appwrite constants",
|
269
|
+
`# Generated on ${new Date().toISOString()}`,
|
270
|
+
"",
|
271
|
+
"# Database IDs",
|
272
|
+
...Object.entries(databases).map(([key, value]) => `DATABASE_${key}=${value}`),
|
273
|
+
"",
|
274
|
+
"# Collection IDs",
|
275
|
+
...Object.entries(collections).map(([key, value]) => `COLLECTION_${key}=${value}`),
|
276
|
+
"",
|
277
|
+
"# Bucket IDs",
|
278
|
+
...Object.entries(buckets).map(([key, value]) => `BUCKET_${key}=${value}`),
|
279
|
+
"",
|
280
|
+
"# Function IDs",
|
281
|
+
...Object.entries(functions).map(([key, value]) => `FUNCTION_${key}=${value}`)
|
282
|
+
];
|
283
|
+
return lines.join('\n');
|
284
|
+
}
|
285
|
+
async generateFiles(languages, outputDir) {
|
286
|
+
await fs.mkdir(outputDir, { recursive: true });
|
287
|
+
const generators = {
|
288
|
+
typescript: () => ({ content: this.generateTypeScript(), filename: "appwrite-constants.ts" }),
|
289
|
+
javascript: () => ({ content: this.generateJavaScript(), filename: "appwrite-constants.js" }),
|
290
|
+
python: () => ({ content: this.generatePython(), filename: "appwrite_constants.py" }),
|
291
|
+
php: () => ({ content: this.generatePHP(), filename: "AppwriteConstants.php" }),
|
292
|
+
dart: () => ({ content: this.generateDart(), filename: "appwrite_constants.dart" }),
|
293
|
+
json: () => ({ content: this.generateJSON(), filename: "appwrite-constants.json" }),
|
294
|
+
env: () => ({ content: this.generateEnv(), filename: ".env.appwrite" })
|
295
|
+
};
|
296
|
+
for (const language of languages) {
|
297
|
+
const generator = generators[language];
|
298
|
+
if (generator) {
|
299
|
+
const { content, filename } = generator();
|
300
|
+
const filePath = path.join(outputDir, filename);
|
301
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
302
|
+
MessageFormatter.success(`Generated ${language} constants: ${filePath}`, { prefix: "Constants" });
|
303
|
+
}
|
304
|
+
else {
|
305
|
+
MessageFormatter.error(`Unsupported language: ${language}`, undefined, { prefix: "Constants" });
|
306
|
+
}
|
307
|
+
}
|
308
|
+
}
|
309
|
+
}
|
@@ -64,7 +64,7 @@ const shouldIgnoreDirectory = (dirName) => {
|
|
64
64
|
return ignoredDirs.includes(dirName) ||
|
65
65
|
dirName.startsWith('.git') ||
|
66
66
|
dirName.startsWith('node_modules') ||
|
67
|
-
dirName.startsWith('.');
|
67
|
+
(dirName.startsWith('.') && dirName !== '.appwrite');
|
68
68
|
};
|
69
69
|
const findAppwriteConfigTS = (dir, depth = 0) => {
|
70
70
|
// Limit search depth to prevent infinite recursion
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "appwrite-utils-cli",
|
3
3
|
"description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
|
4
|
-
"version": "1.0.
|
4
|
+
"version": "1.0.6",
|
5
5
|
"main": "src/main.ts",
|
6
6
|
"type": "module",
|
7
7
|
"repository": {
|
package/src/config/yamlConfig.ts
CHANGED
@@ -256,14 +256,18 @@ export const findYamlConfig = (startDir: string): string | null => {
|
|
256
256
|
path.join(startDir, "appwrite.yml"),
|
257
257
|
];
|
258
258
|
|
259
|
+
console.log(`DEBUG: Checking YAML paths in ${startDir}:`);
|
259
260
|
for (const configPath of possiblePaths) {
|
261
|
+
console.log(` - ${configPath}: ${fs.existsSync(configPath)}`);
|
260
262
|
if (fs.existsSync(configPath)) {
|
261
263
|
return configPath;
|
262
264
|
}
|
263
265
|
}
|
264
266
|
|
265
267
|
// Recursively search subdirectories for .appwrite folders
|
268
|
+
console.log(`DEBUG: Starting recursive search from ${startDir}`);
|
266
269
|
const yamlConfigInSubdirs = findYamlConfigRecursive(startDir);
|
270
|
+
console.log(`DEBUG: Recursive search result: ${yamlConfigInSubdirs}`);
|
267
271
|
if (yamlConfigInSubdirs) {
|
268
272
|
return yamlConfigInSubdirs;
|
269
273
|
}
|
@@ -326,28 +330,33 @@ const shouldIgnoreDirectory = (dirName: string): boolean => {
|
|
326
330
|
return ignoredDirs.includes(dirName) ||
|
327
331
|
dirName.startsWith('.git') ||
|
328
332
|
dirName.startsWith('node_modules') ||
|
329
|
-
dirName.startsWith('.');
|
333
|
+
(dirName.startsWith('.') && dirName !== '.appwrite');
|
330
334
|
};
|
331
335
|
|
332
336
|
const findYamlConfigRecursive = (dir: string, depth: number = 0): string | null => {
|
333
337
|
// Limit search depth to prevent infinite recursion
|
334
338
|
if (depth > 5) {
|
339
|
+
console.log(`DEBUG: Stopping search at depth ${depth} in ${dir}`);
|
335
340
|
return null;
|
336
341
|
}
|
337
342
|
|
338
343
|
if (shouldIgnoreDirectory(path.basename(dir))) {
|
344
|
+
console.log(`DEBUG: Ignoring directory ${dir}`);
|
339
345
|
return null;
|
340
346
|
}
|
341
347
|
|
342
348
|
try {
|
343
349
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
350
|
+
console.log(`DEBUG: Searching directory ${dir} at depth ${depth}, found ${entries.length} entries`);
|
344
351
|
|
345
352
|
for (const entry of entries) {
|
346
353
|
if (entry.isDirectory() && !shouldIgnoreDirectory(entry.name)) {
|
347
354
|
const fullPath = path.join(dir, entry.name);
|
355
|
+
console.log(`DEBUG: Checking subdirectory: ${fullPath}`);
|
348
356
|
|
349
357
|
// Check if this is an .appwrite directory
|
350
358
|
if (entry.name === ".appwrite") {
|
359
|
+
console.log(`DEBUG: Found .appwrite directory at ${fullPath}`);
|
351
360
|
const configPaths = [
|
352
361
|
path.join(fullPath, "appwriteConfig.yaml"),
|
353
362
|
path.join(fullPath, "appwriteConfig.yml"),
|
package/src/interactiveCLI.ts
CHANGED
@@ -41,6 +41,7 @@ import {
|
|
41
41
|
} from "./functions/methods.js";
|
42
42
|
import { deployLocalFunction } from "./functions/deployments.js";
|
43
43
|
import { join } from "node:path";
|
44
|
+
import path from "path";
|
44
45
|
import fs from "node:fs";
|
45
46
|
import { SchemaGenerator } from "./shared/schemaGenerator.js";
|
46
47
|
import { ConfirmationDialogs } from "./shared/confirmationDialogs.js";
|
@@ -64,6 +65,7 @@ enum CHOICES {
|
|
64
65
|
WIPE_DATABASE = "Wipe database",
|
65
66
|
WIPE_COLLECTIONS = "Wipe collections",
|
66
67
|
GENERATE_SCHEMAS = "Generate schemas",
|
68
|
+
GENERATE_CONSTANTS = "📋 Generate cross-language constants (TypeScript, Python, PHP, Dart, etc.)",
|
67
69
|
IMPORT_DATA = "Import data",
|
68
70
|
RELOAD_CONFIG = "Reload configuration files",
|
69
71
|
UPDATE_FUNCTION_SPEC = "Update function specifications",
|
@@ -159,6 +161,10 @@ export class InteractiveCLI {
|
|
159
161
|
await this.initControllerIfNeeded();
|
160
162
|
await this.generateSchemas();
|
161
163
|
break;
|
164
|
+
case CHOICES.GENERATE_CONSTANTS:
|
165
|
+
await this.initControllerIfNeeded();
|
166
|
+
await this.generateConstants();
|
167
|
+
break;
|
162
168
|
case CHOICES.IMPORT_DATA:
|
163
169
|
await this.initControllerIfNeeded();
|
164
170
|
await this.importData();
|
@@ -1595,6 +1601,73 @@ export class InteractiveCLI {
|
|
1595
1601
|
MessageFormatter.success("Schema generation completed", { prefix: "Schemas" });
|
1596
1602
|
}
|
1597
1603
|
|
1604
|
+
private async generateConstants(): Promise<void> {
|
1605
|
+
console.log(chalk.yellow("Generating cross-language constants..."));
|
1606
|
+
|
1607
|
+
if (!this.controller?.config) {
|
1608
|
+
MessageFormatter.error("No configuration found", undefined, { prefix: "Constants" });
|
1609
|
+
return;
|
1610
|
+
}
|
1611
|
+
|
1612
|
+
// Prompt for languages
|
1613
|
+
const { languages } = await inquirer.prompt([
|
1614
|
+
{
|
1615
|
+
type: "checkbox",
|
1616
|
+
name: "languages",
|
1617
|
+
message: "Select languages for constants generation:",
|
1618
|
+
choices: [
|
1619
|
+
{ name: "TypeScript", value: "typescript", checked: true },
|
1620
|
+
{ name: "JavaScript", value: "javascript" },
|
1621
|
+
{ name: "Python", value: "python" },
|
1622
|
+
{ name: "PHP", value: "php" },
|
1623
|
+
{ name: "Dart", value: "dart" },
|
1624
|
+
{ name: "JSON", value: "json" },
|
1625
|
+
{ name: "Environment Variables", value: "env" },
|
1626
|
+
],
|
1627
|
+
validate: (input) => {
|
1628
|
+
if (input.length === 0) {
|
1629
|
+
return "Please select at least one language";
|
1630
|
+
}
|
1631
|
+
return true;
|
1632
|
+
},
|
1633
|
+
},
|
1634
|
+
]);
|
1635
|
+
|
1636
|
+
// Determine default output directory based on config location
|
1637
|
+
const configPath = this.controller!.getAppwriteFolderPath();
|
1638
|
+
const defaultOutputDir = configPath
|
1639
|
+
? path.join(configPath, "constants")
|
1640
|
+
: path.join(process.cwd(), "constants");
|
1641
|
+
|
1642
|
+
// Prompt for output directory
|
1643
|
+
const { outputDir } = await inquirer.prompt([
|
1644
|
+
{
|
1645
|
+
type: "input",
|
1646
|
+
name: "outputDir",
|
1647
|
+
message: "Output directory for constants files:",
|
1648
|
+
default: defaultOutputDir,
|
1649
|
+
validate: (input) => {
|
1650
|
+
if (!input.trim()) {
|
1651
|
+
return "Output directory cannot be empty";
|
1652
|
+
}
|
1653
|
+
return true;
|
1654
|
+
},
|
1655
|
+
},
|
1656
|
+
]);
|
1657
|
+
|
1658
|
+
try {
|
1659
|
+
const { ConstantsGenerator } = await import("./utils/constantsGenerator.js");
|
1660
|
+
const generator = new ConstantsGenerator(this.controller.config);
|
1661
|
+
|
1662
|
+
MessageFormatter.info(`Generating constants for: ${languages.join(", ")}`, { prefix: "Constants" });
|
1663
|
+
await generator.generateFiles(languages, outputDir);
|
1664
|
+
|
1665
|
+
MessageFormatter.success(`Constants generated in ${outputDir}`, { prefix: "Constants" });
|
1666
|
+
} catch (error) {
|
1667
|
+
MessageFormatter.error("Failed to generate constants", error instanceof Error ? error : new Error(String(error)), { prefix: "Constants" });
|
1668
|
+
}
|
1669
|
+
}
|
1670
|
+
|
1598
1671
|
private async importData(): Promise<void> {
|
1599
1672
|
console.log(chalk.yellow("Importing data..."));
|
1600
1673
|
|
@@ -1906,6 +1979,7 @@ export class InteractiveCLI {
|
|
1906
1979
|
try {
|
1907
1980
|
// Check for YAML config first
|
1908
1981
|
const yamlConfigPath = findYamlConfig(this.currentDir);
|
1982
|
+
console.log(`DEBUG: YAML config search from ${this.currentDir}, found: ${yamlConfigPath}`);
|
1909
1983
|
if (yamlConfigPath) {
|
1910
1984
|
this.isUsingTypeScriptConfig = false;
|
1911
1985
|
MessageFormatter.info("Using YAML configuration", { prefix: "Config" });
|
package/src/main.ts
CHANGED
@@ -15,6 +15,7 @@ import chalk from "chalk";
|
|
15
15
|
import { listSpecifications } from "./functions/methods.js";
|
16
16
|
import { MessageFormatter } from "./shared/messageFormatter.js";
|
17
17
|
import { ConfirmationDialogs } from "./shared/confirmationDialogs.js";
|
18
|
+
import path from "path";
|
18
19
|
|
19
20
|
interface CliOptions {
|
20
21
|
config?: string;
|
@@ -49,6 +50,9 @@ interface CliOptions {
|
|
49
50
|
functionId?: string;
|
50
51
|
specification?: string;
|
51
52
|
migrateConfig?: boolean;
|
53
|
+
generateConstants?: boolean;
|
54
|
+
constantsLanguages?: string;
|
55
|
+
constantsOutput?: string;
|
52
56
|
}
|
53
57
|
|
54
58
|
type ParsedArgv = ArgumentsCamelCase<CliOptions>;
|
@@ -203,6 +207,21 @@ const argv = yargs(hideBin(process.argv))
|
|
203
207
|
type: "boolean",
|
204
208
|
description: "Migrate appwriteConfig.ts to .appwrite structure with YAML configuration",
|
205
209
|
})
|
210
|
+
.option("generateConstants", {
|
211
|
+
alias: ["constants"],
|
212
|
+
type: "boolean",
|
213
|
+
description: "Generate cross-language constants file with database, collection, bucket, and function IDs",
|
214
|
+
})
|
215
|
+
.option("constantsLanguages", {
|
216
|
+
type: "string",
|
217
|
+
description: "Comma-separated list of languages for constants (typescript,javascript,python,php,dart,json,env)",
|
218
|
+
default: "typescript",
|
219
|
+
})
|
220
|
+
.option("constantsOutput", {
|
221
|
+
type: "string",
|
222
|
+
description: "Output directory for generated constants files (default: config-folder/constants)",
|
223
|
+
default: "auto",
|
224
|
+
})
|
206
225
|
.parse() as ParsedArgv;
|
207
226
|
|
208
227
|
async function main() {
|
@@ -235,6 +254,40 @@ async function main() {
|
|
235
254
|
return;
|
236
255
|
}
|
237
256
|
|
257
|
+
if (argv.generateConstants) {
|
258
|
+
const { ConstantsGenerator } = await import("./utils/constantsGenerator.js");
|
259
|
+
type SupportedLanguage = import("./utils/constantsGenerator.js").SupportedLanguage;
|
260
|
+
|
261
|
+
if (!controller.config) {
|
262
|
+
MessageFormatter.error("No Appwrite configuration found", undefined, { prefix: "Constants" });
|
263
|
+
return;
|
264
|
+
}
|
265
|
+
|
266
|
+
const languages = argv.constantsLanguages!.split(",").map(l => l.trim()) as SupportedLanguage[];
|
267
|
+
|
268
|
+
// Determine output directory - use config folder/constants by default, or custom path if specified
|
269
|
+
let outputDir: string;
|
270
|
+
if (argv.constantsOutput === "auto") {
|
271
|
+
// Default case: use config directory + constants, fallback to current directory
|
272
|
+
const configPath = controller.getAppwriteFolderPath();
|
273
|
+
outputDir = configPath
|
274
|
+
? path.join(configPath, "constants")
|
275
|
+
: path.join(process.cwd(), "constants");
|
276
|
+
} else {
|
277
|
+
// Custom output directory specified
|
278
|
+
outputDir = argv.constantsOutput!;
|
279
|
+
}
|
280
|
+
|
281
|
+
MessageFormatter.info(`Generating constants for languages: ${languages.join(", ")}`, { prefix: "Constants" });
|
282
|
+
|
283
|
+
const generator = new ConstantsGenerator(controller.config);
|
284
|
+
await generator.generateFiles(languages, outputDir);
|
285
|
+
|
286
|
+
operationStats.generatedConstants = languages.length;
|
287
|
+
MessageFormatter.success(`Constants generated in ${outputDir}`, { prefix: "Constants" });
|
288
|
+
return;
|
289
|
+
}
|
290
|
+
|
238
291
|
if (!controller.config) {
|
239
292
|
MessageFormatter.error("No Appwrite connection found", undefined, { prefix: "CLI" });
|
240
293
|
return;
|
@@ -0,0 +1,352 @@
|
|
1
|
+
import fs from "fs/promises";
|
2
|
+
import path from "path";
|
3
|
+
import { type AppwriteConfig } from "appwrite-utils";
|
4
|
+
import { MessageFormatter } from "../shared/messageFormatter.js";
|
5
|
+
|
6
|
+
export type SupportedLanguage =
|
7
|
+
| "typescript"
|
8
|
+
| "javascript"
|
9
|
+
| "python"
|
10
|
+
| "php"
|
11
|
+
| "dart"
|
12
|
+
| "json"
|
13
|
+
| "env";
|
14
|
+
|
15
|
+
interface Constants {
|
16
|
+
databases: Record<string, string>;
|
17
|
+
collections: Record<string, string>;
|
18
|
+
buckets: Record<string, string>;
|
19
|
+
functions: Record<string, string>;
|
20
|
+
}
|
21
|
+
|
22
|
+
export class ConstantsGenerator {
|
23
|
+
private config: AppwriteConfig;
|
24
|
+
private constants: Constants;
|
25
|
+
|
26
|
+
constructor(config: AppwriteConfig) {
|
27
|
+
this.config = config;
|
28
|
+
this.constants = this.extractConstants();
|
29
|
+
}
|
30
|
+
|
31
|
+
private extractConstants(): Constants {
|
32
|
+
const constants: Constants = {
|
33
|
+
databases: {},
|
34
|
+
collections: {},
|
35
|
+
buckets: {},
|
36
|
+
functions: {}
|
37
|
+
};
|
38
|
+
|
39
|
+
// Extract database IDs
|
40
|
+
this.config.databases?.forEach(db => {
|
41
|
+
if (db.$id) {
|
42
|
+
const key = this.toConstantName(db.name || db.$id);
|
43
|
+
constants.databases[key] = db.$id;
|
44
|
+
}
|
45
|
+
});
|
46
|
+
|
47
|
+
// Extract collection IDs
|
48
|
+
this.config.collections?.forEach(collection => {
|
49
|
+
if (collection.$id) {
|
50
|
+
const key = this.toConstantName(collection.name || collection.$id);
|
51
|
+
constants.collections[key] = collection.$id;
|
52
|
+
}
|
53
|
+
});
|
54
|
+
|
55
|
+
// Extract bucket IDs
|
56
|
+
this.config.buckets?.forEach(bucket => {
|
57
|
+
if (bucket.$id) {
|
58
|
+
const key = this.toConstantName(bucket.name || bucket.$id);
|
59
|
+
constants.buckets[key] = bucket.$id;
|
60
|
+
}
|
61
|
+
});
|
62
|
+
|
63
|
+
// Extract function IDs
|
64
|
+
this.config.functions?.forEach(func => {
|
65
|
+
if (func.$id) {
|
66
|
+
const key = this.toConstantName(func.name || func.$id);
|
67
|
+
constants.functions[key] = func.$id;
|
68
|
+
}
|
69
|
+
});
|
70
|
+
|
71
|
+
return constants;
|
72
|
+
}
|
73
|
+
|
74
|
+
private toConstantName(name: string): string {
|
75
|
+
return name
|
76
|
+
.replace(/[^a-zA-Z0-9]/g, '_')
|
77
|
+
.replace(/_+/g, '_')
|
78
|
+
.replace(/^_|_$/g, '')
|
79
|
+
.toUpperCase();
|
80
|
+
}
|
81
|
+
|
82
|
+
private toCamelCase(name: string): string {
|
83
|
+
return name
|
84
|
+
.toLowerCase()
|
85
|
+
.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
86
|
+
}
|
87
|
+
|
88
|
+
private toSnakeCase(name: string): string {
|
89
|
+
return name.toLowerCase();
|
90
|
+
}
|
91
|
+
|
92
|
+
generateTypeScript(): string {
|
93
|
+
const { databases, collections, buckets, functions } = this.constants;
|
94
|
+
|
95
|
+
return `// Auto-generated Appwrite constants
|
96
|
+
// Generated on ${new Date().toISOString()}
|
97
|
+
|
98
|
+
export const DATABASE_IDS = {
|
99
|
+
${Object.entries(databases).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
100
|
+
} as const;
|
101
|
+
|
102
|
+
export const COLLECTION_IDS = {
|
103
|
+
${Object.entries(collections).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
104
|
+
} as const;
|
105
|
+
|
106
|
+
export const BUCKET_IDS = {
|
107
|
+
${Object.entries(buckets).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
108
|
+
} as const;
|
109
|
+
|
110
|
+
export const FUNCTION_IDS = {
|
111
|
+
${Object.entries(functions).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
112
|
+
} as const;
|
113
|
+
|
114
|
+
// Type helpers
|
115
|
+
export type DatabaseId = typeof DATABASE_IDS[keyof typeof DATABASE_IDS];
|
116
|
+
export type CollectionId = typeof COLLECTION_IDS[keyof typeof COLLECTION_IDS];
|
117
|
+
export type BucketId = typeof BUCKET_IDS[keyof typeof BUCKET_IDS];
|
118
|
+
export type FunctionId = typeof FUNCTION_IDS[keyof typeof FUNCTION_IDS];
|
119
|
+
|
120
|
+
// Helper objects for runtime use
|
121
|
+
export const ALL_DATABASE_IDS = Object.values(DATABASE_IDS);
|
122
|
+
export const ALL_COLLECTION_IDS = Object.values(COLLECTION_IDS);
|
123
|
+
export const ALL_BUCKET_IDS = Object.values(BUCKET_IDS);
|
124
|
+
export const ALL_FUNCTION_IDS = Object.values(FUNCTION_IDS);
|
125
|
+
`;
|
126
|
+
}
|
127
|
+
|
128
|
+
generateJavaScript(): string {
|
129
|
+
const { databases, collections, buckets, functions } = this.constants;
|
130
|
+
|
131
|
+
return `// Auto-generated Appwrite constants
|
132
|
+
// Generated on ${new Date().toISOString()}
|
133
|
+
|
134
|
+
export const DATABASE_IDS = {
|
135
|
+
${Object.entries(databases).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
136
|
+
};
|
137
|
+
|
138
|
+
export const COLLECTION_IDS = {
|
139
|
+
${Object.entries(collections).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
140
|
+
};
|
141
|
+
|
142
|
+
export const BUCKET_IDS = {
|
143
|
+
${Object.entries(buckets).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
144
|
+
};
|
145
|
+
|
146
|
+
export const FUNCTION_IDS = {
|
147
|
+
${Object.entries(functions).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
|
148
|
+
};
|
149
|
+
|
150
|
+
// Helper arrays for runtime use
|
151
|
+
export const ALL_DATABASE_IDS = Object.values(DATABASE_IDS);
|
152
|
+
export const ALL_COLLECTION_IDS = Object.values(COLLECTION_IDS);
|
153
|
+
export const ALL_BUCKET_IDS = Object.values(BUCKET_IDS);
|
154
|
+
export const ALL_FUNCTION_IDS = Object.values(FUNCTION_IDS);
|
155
|
+
`;
|
156
|
+
}
|
157
|
+
|
158
|
+
generatePython(): string {
|
159
|
+
const { databases, collections, buckets, functions } = this.constants;
|
160
|
+
|
161
|
+
return `# Auto-generated Appwrite constants
|
162
|
+
# Generated on ${new Date().toISOString()}
|
163
|
+
|
164
|
+
class DatabaseIds:
|
165
|
+
"""Database ID constants"""
|
166
|
+
${Object.entries(databases).map(([key, value]) => ` ${key} = "${value}"`).join('\n')}
|
167
|
+
|
168
|
+
class CollectionIds:
|
169
|
+
"""Collection ID constants"""
|
170
|
+
${Object.entries(collections).map(([key, value]) => ` ${key} = "${value}"`).join('\n')}
|
171
|
+
|
172
|
+
class BucketIds:
|
173
|
+
"""Bucket ID constants"""
|
174
|
+
${Object.entries(buckets).map(([key, value]) => ` ${key} = "${value}"`).join('\n')}
|
175
|
+
|
176
|
+
class FunctionIds:
|
177
|
+
"""Function ID constants"""
|
178
|
+
${Object.entries(functions).map(([key, value]) => ` ${key} = "${value}"`).join('\n')}
|
179
|
+
|
180
|
+
# Helper dictionaries for runtime use
|
181
|
+
DATABASE_ID_MAP = {
|
182
|
+
${Object.entries(databases).map(([key, value]) => ` "${this.toSnakeCase(key)}": "${value}"`).join(',\n')}
|
183
|
+
}
|
184
|
+
|
185
|
+
COLLECTION_ID_MAP = {
|
186
|
+
${Object.entries(collections).map(([key, value]) => ` "${this.toSnakeCase(key)}": "${value}"`).join(',\n')}
|
187
|
+
}
|
188
|
+
|
189
|
+
BUCKET_ID_MAP = {
|
190
|
+
${Object.entries(buckets).map(([key, value]) => ` "${this.toSnakeCase(key)}": "${value}"`).join(',\n')}
|
191
|
+
}
|
192
|
+
|
193
|
+
FUNCTION_ID_MAP = {
|
194
|
+
${Object.entries(functions).map(([key, value]) => ` "${this.toSnakeCase(key)}": "${value}"`).join(',\n')}
|
195
|
+
}
|
196
|
+
`;
|
197
|
+
}
|
198
|
+
|
199
|
+
generatePHP(): string {
|
200
|
+
const { databases, collections, buckets, functions } = this.constants;
|
201
|
+
|
202
|
+
return `<?php
|
203
|
+
// Auto-generated Appwrite constants
|
204
|
+
// Generated on ${new Date().toISOString()}
|
205
|
+
|
206
|
+
class AppwriteConstants {
|
207
|
+
|
208
|
+
const DATABASE_IDS = [
|
209
|
+
${Object.entries(databases).map(([key, value]) => ` '${key}' => '${value}'`).join(',\n')}
|
210
|
+
];
|
211
|
+
|
212
|
+
const COLLECTION_IDS = [
|
213
|
+
${Object.entries(collections).map(([key, value]) => ` '${key}' => '${value}'`).join(',\n')}
|
214
|
+
];
|
215
|
+
|
216
|
+
const BUCKET_IDS = [
|
217
|
+
${Object.entries(buckets).map(([key, value]) => ` '${key}' => '${value}'`).join(',\n')}
|
218
|
+
];
|
219
|
+
|
220
|
+
const FUNCTION_IDS = [
|
221
|
+
${Object.entries(functions).map(([key, value]) => ` '${key}' => '${value}'`).join(',\n')}
|
222
|
+
];
|
223
|
+
|
224
|
+
/**
|
225
|
+
* Get all database IDs as array
|
226
|
+
*/
|
227
|
+
public static function getAllDatabaseIds(): array {
|
228
|
+
return array_values(self::DATABASE_IDS);
|
229
|
+
}
|
230
|
+
|
231
|
+
/**
|
232
|
+
* Get all collection IDs as array
|
233
|
+
*/
|
234
|
+
public static function getAllCollectionIds(): array {
|
235
|
+
return array_values(self::COLLECTION_IDS);
|
236
|
+
}
|
237
|
+
|
238
|
+
/**
|
239
|
+
* Get all bucket IDs as array
|
240
|
+
*/
|
241
|
+
public static function getAllBucketIds(): array {
|
242
|
+
return array_values(self::BUCKET_IDS);
|
243
|
+
}
|
244
|
+
|
245
|
+
/**
|
246
|
+
* Get all function IDs as array
|
247
|
+
*/
|
248
|
+
public static function getAllFunctionIds(): array {
|
249
|
+
return array_values(self::FUNCTION_IDS);
|
250
|
+
}
|
251
|
+
}
|
252
|
+
`;
|
253
|
+
}
|
254
|
+
|
255
|
+
generateDart(): string {
|
256
|
+
const { databases, collections, buckets, functions } = this.constants;
|
257
|
+
|
258
|
+
return `// Auto-generated Appwrite constants
|
259
|
+
// Generated on ${new Date().toISOString()}
|
260
|
+
|
261
|
+
class AppwriteConstants {
|
262
|
+
|
263
|
+
static const Map<String, String> databaseIds = {
|
264
|
+
${Object.entries(databases).map(([key, value]) => ` '${this.toCamelCase(key)}': '${value}'`).join(',\n')}
|
265
|
+
};
|
266
|
+
|
267
|
+
static const Map<String, String> collectionIds = {
|
268
|
+
${Object.entries(collections).map(([key, value]) => ` '${this.toCamelCase(key)}': '${value}'`).join(',\n')}
|
269
|
+
};
|
270
|
+
|
271
|
+
static const Map<String, String> bucketIds = {
|
272
|
+
${Object.entries(buckets).map(([key, value]) => ` '${this.toCamelCase(key)}': '${value}'`).join(',\n')}
|
273
|
+
};
|
274
|
+
|
275
|
+
static const Map<String, String> functionIds = {
|
276
|
+
${Object.entries(functions).map(([key, value]) => ` '${this.toCamelCase(key)}': '${value}'`).join(',\n')}
|
277
|
+
};
|
278
|
+
|
279
|
+
// Helper getters for individual IDs
|
280
|
+
${Object.entries(databases).map(([key, value]) => ` static String get ${this.toCamelCase(key)}DatabaseId => '${value}';`).join('\n')}
|
281
|
+
|
282
|
+
${Object.entries(collections).map(([key, value]) => ` static String get ${this.toCamelCase(key)}CollectionId => '${value}';`).join('\n')}
|
283
|
+
|
284
|
+
${Object.entries(buckets).map(([key, value]) => ` static String get ${this.toCamelCase(key)}BucketId => '${value}';`).join('\n')}
|
285
|
+
|
286
|
+
${Object.entries(functions).map(([key, value]) => ` static String get ${this.toCamelCase(key)}FunctionId => '${value}';`).join('\n')}
|
287
|
+
}
|
288
|
+
`;
|
289
|
+
}
|
290
|
+
|
291
|
+
generateJSON(): string {
|
292
|
+
return JSON.stringify({
|
293
|
+
meta: {
|
294
|
+
generated: new Date().toISOString(),
|
295
|
+
generator: "appwrite-utils-cli"
|
296
|
+
},
|
297
|
+
databases: this.constants.databases,
|
298
|
+
collections: this.constants.collections,
|
299
|
+
buckets: this.constants.buckets,
|
300
|
+
functions: this.constants.functions
|
301
|
+
}, null, 2);
|
302
|
+
}
|
303
|
+
|
304
|
+
generateEnv(): string {
|
305
|
+
const { databases, collections, buckets, functions } = this.constants;
|
306
|
+
|
307
|
+
const lines = [
|
308
|
+
"# Auto-generated Appwrite constants",
|
309
|
+
`# Generated on ${new Date().toISOString()}`,
|
310
|
+
"",
|
311
|
+
"# Database IDs",
|
312
|
+
...Object.entries(databases).map(([key, value]) => `DATABASE_${key}=${value}`),
|
313
|
+
"",
|
314
|
+
"# Collection IDs",
|
315
|
+
...Object.entries(collections).map(([key, value]) => `COLLECTION_${key}=${value}`),
|
316
|
+
"",
|
317
|
+
"# Bucket IDs",
|
318
|
+
...Object.entries(buckets).map(([key, value]) => `BUCKET_${key}=${value}`),
|
319
|
+
"",
|
320
|
+
"# Function IDs",
|
321
|
+
...Object.entries(functions).map(([key, value]) => `FUNCTION_${key}=${value}`)
|
322
|
+
];
|
323
|
+
|
324
|
+
return lines.join('\n');
|
325
|
+
}
|
326
|
+
|
327
|
+
async generateFiles(languages: SupportedLanguage[], outputDir: string): Promise<void> {
|
328
|
+
await fs.mkdir(outputDir, { recursive: true });
|
329
|
+
|
330
|
+
const generators = {
|
331
|
+
typescript: () => ({ content: this.generateTypeScript(), filename: "appwrite-constants.ts" }),
|
332
|
+
javascript: () => ({ content: this.generateJavaScript(), filename: "appwrite-constants.js" }),
|
333
|
+
python: () => ({ content: this.generatePython(), filename: "appwrite_constants.py" }),
|
334
|
+
php: () => ({ content: this.generatePHP(), filename: "AppwriteConstants.php" }),
|
335
|
+
dart: () => ({ content: this.generateDart(), filename: "appwrite_constants.dart" }),
|
336
|
+
json: () => ({ content: this.generateJSON(), filename: "appwrite-constants.json" }),
|
337
|
+
env: () => ({ content: this.generateEnv(), filename: ".env.appwrite" })
|
338
|
+
};
|
339
|
+
|
340
|
+
for (const language of languages) {
|
341
|
+
const generator = generators[language];
|
342
|
+
if (generator) {
|
343
|
+
const { content, filename } = generator();
|
344
|
+
const filePath = path.join(outputDir, filename);
|
345
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
346
|
+
MessageFormatter.success(`Generated ${language} constants: ${filePath}`, { prefix: "Constants" });
|
347
|
+
} else {
|
348
|
+
MessageFormatter.error(`Unsupported language: ${language}`, undefined, { prefix: "Constants" });
|
349
|
+
}
|
350
|
+
}
|
351
|
+
}
|
352
|
+
}
|
package/src/utils/loadConfigs.ts
CHANGED
@@ -69,7 +69,7 @@ const shouldIgnoreDirectory = (dirName: string): boolean => {
|
|
69
69
|
return ignoredDirs.includes(dirName) ||
|
70
70
|
dirName.startsWith('.git') ||
|
71
71
|
dirName.startsWith('node_modules') ||
|
72
|
-
dirName.startsWith('.');
|
72
|
+
(dirName.startsWith('.') && dirName !== '.appwrite');
|
73
73
|
};
|
74
74
|
|
75
75
|
const findAppwriteConfigTS = (dir: string, depth: number = 0): string | null => {
|