mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-19 16:26:40 +08:00
165 lines
6.9 KiB
TypeScript
165 lines
6.9 KiB
TypeScript
#!/usr/bin/env tsx
|
|
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import type { ClassInfo, PropertyInfo } from './types';
|
|
|
|
interface SerializedAnalysisResult {
|
|
classMap: [string, ClassInfo][];
|
|
accessibleTypes: string[];
|
|
abstractTypes: [string, string[]][];
|
|
allTypesToGenerate: string[];
|
|
typeProperties: [string, PropertyInfo[]][];
|
|
}
|
|
|
|
function generateClaudePrompt(analysisData: SerializedAnalysisResult): string {
|
|
const output: string[] = [];
|
|
|
|
// Convert arrays back to Maps
|
|
const classMap = new Map(analysisData.classMap);
|
|
const abstractTypes = new Map(analysisData.abstractTypes);
|
|
const typeProperties = new Map(analysisData.typeProperties);
|
|
|
|
output.push('# Spine Java Serialization Methods to Generate');
|
|
output.push('');
|
|
output.push('You need to generate writeXXX() methods for the SkeletonSerializer class.');
|
|
output.push('Each method should serialize all properties accessible via getter methods.');
|
|
output.push('');
|
|
output.push('## Task: Port the Java SkeletonSerializer to Other Runtimes');
|
|
output.push('');
|
|
output.push('A complete Java SkeletonSerializer has been generated at:');
|
|
output.push('`spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonSerializer.java`');
|
|
output.push('');
|
|
output.push('Port this serializer to:');
|
|
output.push('- **C++**: Create `SkeletonSerializer.h/cpp` in `spine-cpp/src/spine/`');
|
|
output.push('- **C**: Create `spine_skeleton_serializer.h/c` in `spine-c/src/spine/`');
|
|
output.push('- **TypeScript**: Create `SkeletonSerializer.ts` in `spine-ts/spine-core/src/`');
|
|
output.push('');
|
|
output.push('## Important Porting Notes');
|
|
output.push('');
|
|
output.push('1. **Language Differences**:');
|
|
output.push(' - Java uses getter methods (`getName()`), TypeScript may use properties (`.name`)');
|
|
output.push(' - C uses function calls (`spBoneData_getName()`)');
|
|
output.push(' - Adapt to each language\'s idioms');
|
|
output.push('2. **Type Checking**:');
|
|
output.push(' - Java uses `instanceof`');
|
|
output.push(' - C++ uses custom RTTI (`object->getRTTI().instanceOf()`)');
|
|
output.push(' - C uses type fields or function pointers');
|
|
output.push(' - TypeScript uses `instanceof`');
|
|
output.push('3. **Collections**:');
|
|
output.push(' - Java uses `Array<T>`, `IntArray`, `FloatArray`');
|
|
output.push(' - C++ uses `Vector<T>`');
|
|
output.push(' - C uses arrays with size fields');
|
|
output.push(' - TypeScript uses arrays `T[]`');
|
|
output.push('');
|
|
output.push('## Types Reference');
|
|
output.push('');
|
|
|
|
// First emit abstract types that need instanceof delegation
|
|
for (const [className, classInfo] of classMap) {
|
|
if ((classInfo.isAbstract || classInfo.isInterface) && classInfo.concreteImplementations && classInfo.concreteImplementations.length > 0) {
|
|
output.push(`### ${className} (${classInfo.isInterface ? 'interface' : 'abstract'})`);
|
|
if (classInfo.superTypes.length > 0) {
|
|
output.push(`Extends: ${classInfo.superTypes.join(', ')}`);
|
|
}
|
|
output.push('');
|
|
output.push('This is an abstract class. Generate a write' + className.split('.').pop() + '() method that checks instanceof for these concrete implementations:');
|
|
for (const impl of classInfo.concreteImplementations.sort()) {
|
|
output.push(`- ${impl}`);
|
|
}
|
|
output.push('');
|
|
output.push('Example implementation:');
|
|
output.push('```java');
|
|
output.push(`private void write${className.split('.').pop()}(JsonWriter json, ${className.split('.').pop()} obj) throws IOException {`);
|
|
const first = classInfo.concreteImplementations[0];
|
|
if (first) {
|
|
const shortName = first.split('.').pop()!;
|
|
output.push(` if (obj instanceof ${shortName}) {`);
|
|
output.push(` write${shortName}(json, (${shortName}) obj);`);
|
|
output.push(' } // ... etc for all concrete types');
|
|
output.push(' else {');
|
|
output.push(` throw new RuntimeException("Unknown ${className.split('.').pop()} type: " + obj.getClass().getName());`);
|
|
output.push(' }');
|
|
}
|
|
output.push('}');
|
|
output.push('```');
|
|
output.push('');
|
|
}
|
|
}
|
|
|
|
// Then emit concrete types
|
|
const sortedTypes = Array.from(analysisData.allTypesToGenerate).sort();
|
|
|
|
for (const typeName of sortedTypes) {
|
|
const classInfo = classMap.get(typeName)!;
|
|
|
|
output.push(`### ${typeName}`);
|
|
if (classInfo && classInfo.superTypes.length > 0) {
|
|
output.push(`Extends: ${classInfo.superTypes.join(', ')}`);
|
|
}
|
|
output.push('');
|
|
|
|
const properties = typeProperties.get(typeName) || [];
|
|
const getters = properties.filter(p => p.isGetter);
|
|
const fields = properties.filter(p => !p.isGetter);
|
|
|
|
if (getters.length > 0 || fields.length > 0) {
|
|
if (fields.length > 0) {
|
|
output.push('Public fields:');
|
|
output.push('```java');
|
|
for (const field of fields) {
|
|
output.push(`${field.name} // ${field.type}`);
|
|
}
|
|
output.push('```');
|
|
output.push('');
|
|
}
|
|
|
|
if (getters.length > 0) {
|
|
output.push('Getters to serialize:');
|
|
output.push('```java');
|
|
for (const getter of getters) {
|
|
output.push(`${getter.name} // returns ${getter.type}`);
|
|
}
|
|
output.push('```');
|
|
}
|
|
} else {
|
|
output.push('*No properties found*');
|
|
}
|
|
output.push('');
|
|
}
|
|
|
|
return output.join('\n');
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
// Read analysis result
|
|
const analysisFile = path.join(process.cwd(), 'output', 'analysis-result.json');
|
|
if (!fs.existsSync(analysisFile)) {
|
|
console.error('Analysis result not found. Run analyze-java-api.ts first.');
|
|
process.exit(1);
|
|
}
|
|
|
|
const analysisData: SerializedAnalysisResult = JSON.parse(fs.readFileSync(analysisFile, 'utf8'));
|
|
|
|
// Generate Claude prompt
|
|
const prompt = generateClaudePrompt(analysisData);
|
|
|
|
// Write the prompt file
|
|
const outputFile = path.join(process.cwd(), 'output', 'port-serializer-to-other-runtimes.md');
|
|
fs.writeFileSync(outputFile, prompt);
|
|
|
|
console.log(`Claude prompt written to: ${outputFile}`);
|
|
|
|
} catch (error: any) {
|
|
console.error('Error:', error.message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Allow running as a script or importing the function
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
main();
|
|
}
|
|
|
|
export { generateClaudePrompt }; |