Files
aliasvault/shared/models/scripts/generate-field-keys.cjs

195 lines
5.0 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Generates FieldKey constants for C#, Swift, and Kotlin from TypeScript source.
*/
const fs = require('fs');
const path = require('path');
// Paths
const REPO_ROOT = path.join(__dirname, '../../..');
const TS_SOURCE = path.join(REPO_ROOT, 'shared/models/src/vault/FieldKey.ts');
const CS_OUTPUT = path.join(REPO_ROOT, 'apps/server/AliasVault.Client/Main/Models/Vault/FieldKey.cs');
const SWIFT_OUTPUT = path.join(REPO_ROOT, 'apps/mobile-app/ios/VaultModels/FieldKey.swift');
const KOTLIN_OUTPUT = path.join(REPO_ROOT, 'apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/models/FieldKey.kt');
/**
* Parse the TypeScript FieldKey.ts file and extract constants
*/
function parseTypeScriptFieldKeys(tsContent) {
const fieldKeys = {};
// Extract field comments
const lines = tsContent.split('\n');
let currentComment = '';
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
// Capture JSDoc comments
if (line.startsWith('/**') || line.startsWith('*')) {
const commentMatch = line.match(/\*\s*(.+)/);
if (commentMatch && !commentMatch[1].startsWith('/')) {
currentComment = commentMatch[1].trim();
}
}
// Match field definition
const fieldMatch = line.match(/^(\w+):\s*'([^']+)',?$/);
if (fieldMatch) {
const [, name, value] = fieldMatch;
fieldKeys[name] = {
value,
comment: currentComment
};
currentComment = '';
}
}
return fieldKeys;
}
/**
* Generate C# static class
*/
function generateCSharp(fieldKeys) {
const header = `// <auto-generated />
// This file is auto-generated from shared/models/src/vault/FieldKey.ts
// Do not edit this file directly. Run 'npm run generate:models' to regenerate.
namespace AliasClientDb.Models;
/// <summary>
/// System field keys for the field-based data model.
/// These keys map to FieldDefinition.FieldKey values.
/// </summary>
public static class FieldKey
{`;
const fields = Object.entries(fieldKeys)
.map(([name, { value, comment }]) => {
return ` /// <summary>
/// ${comment}
/// </summary>
public const string ${name} = "${value}";`;
})
.join('\n\n');
const footer = `
}
`;
return header + '\n' + fields + footer;
}
/**
* Generate Swift enum
*/
function generateSwift(fieldKeys) {
const header = `// <auto-generated />
// This file is auto-generated from shared/models/src/vault/FieldKey.ts
// Do not edit this file directly. Run 'npm run generate:models' to regenerate.
import Foundation
/// System field keys for the field-based data model.
/// These keys map to FieldDefinition.FieldKey values.
public struct FieldKey {`;
const fields = Object.entries(fieldKeys)
.map(([name, { value, comment }]) => {
// Convert PascalCase to camelCase for Swift
const swiftName = name.charAt(0).toLowerCase() + name.slice(1);
return ` /// ${comment}
public static let ${swiftName} = "${value}"`;
})
.join('\n\n');
const footer = `
}
`;
return header + '\n' + fields + footer;
}
/**
* Generate Kotlin object
*/
function generateKotlin(fieldKeys) {
const header = `// <auto-generated />
// This file is auto-generated from shared/models/src/vault/FieldKey.ts
// Do not edit this file directly. Run 'npm run generate:models' to regenerate.
package net.aliasvault.app.vaultstore.models
/**
* System field keys for the field-based data model.
* These keys map to FieldDefinition.FieldKey values.
*/
object FieldKey {`;
const fields = Object.entries(fieldKeys)
.map(([name, { value, comment }]) => {
// Convert to SCREAMING_SNAKE_CASE for Kotlin constants
const kotlinName = name.replace(/([A-Z])/g, '_$1').toUpperCase().replace(/^_/, '');
// Ensure comment ends with a period for Kotlin detekt
const kotlinComment = comment.endsWith('.') ? comment : `${comment}.`;
return ` /**
* ${kotlinComment}
*/
const val ${kotlinName} = "${value}"`;
})
.join('\n\n');
const footer = `
}
`;
return header + '\n' + fields + footer;
}
/**
* Ensure directory exists
*/
function ensureDir(filePath) {
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
/**
* Main execution
*/
function main() {
// Read TypeScript source
if (!fs.existsSync(TS_SOURCE)) {
throw new Error(`Source file not found: ${TS_SOURCE}`);
}
const tsContent = fs.readFileSync(TS_SOURCE, 'utf8');
const fieldKeys = parseTypeScriptFieldKeys(tsContent);
if (Object.keys(fieldKeys).length === 0) {
throw new Error('No field keys found in source file');
}
// Generate C#
ensureDir(CS_OUTPUT);
const csContent = generateCSharp(fieldKeys);
fs.writeFileSync(CS_OUTPUT, csContent, 'utf8');
// Generate Swift
ensureDir(SWIFT_OUTPUT);
const swiftContent = generateSwift(fieldKeys);
fs.writeFileSync(SWIFT_OUTPUT, swiftContent, 'utf8');
// Generate Kotlin
ensureDir(KOTLIN_OUTPUT);
const kotlinContent = generateKotlin(fieldKeys);
fs.writeFileSync(KOTLIN_OUTPUT, kotlinContent, 'utf8');
}
main();