meld
Version:
Meld: A template language for LLM prompts
344 lines (290 loc) • 10.2 kB
JavaScript
/**
* Custom debug script for testing JSON import functionality
*
* Key features:
* - Custom ImportDirectiveHandler with JSON support
* - Direct service registration without full DI container
* - Manual state event tracking for debugging
*/
// Add reflect-metadata polyfill required by tsyringe
require('reflect-metadata');
// Import the DI container and services
const { container } = require('tsyringe');
const {
ResolutionService,
CircularityService,
DirectiveService,
FileSystemService,
InterpreterService,
OutputService,
ParserService,
PathOperationsService,
PathService,
StateService,
ValidationService
} = require('./dist/index.cjs');
// Import the CLI module directly
const cli = require('./dist/cli.cjs');
// Import fs-extra for NodeFileSystem functionality
const fs = require('fs-extra');
const { Stats } = require('fs');
const { promisify } = require('util');
const { exec } = require('child_process');
const { watch } = require('fs/promises');
const path = require('path');
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
// Set up the environment for debugging
process.env.DEBUG = 'true';
process.env.MELD_DEBUG = '1';
process.env.NODE_ENV = 'development';
// Create a simple NodeFileSystem class
class NodeFileSystem {
readFile(filePath) {
return fs.readFileSync(filePath, 'utf8');
}
writeFile(filePath, content) {
fs.writeFileSync(filePath, content, 'utf8');
}
exists(filePath) {
return fs.existsSync(filePath);
}
stat(path) {
return fs.statSync(path);
}
readDir(path) {
return fs.readdirSync(path);
}
mkdir(path, recursive = true) {
return fs.mkdirSync(path, { recursive });
}
isDirectory(path) {
try {
return fs.statSync(path).isDirectory();
} catch (e) {
return false;
}
}
isFile(path) {
try {
return fs.statSync(path).isFile();
} catch (e) {
return false;
}
}
watch(filePath, callback) {
return fs.watch(filePath, callback);
}
executeCommand(command, options) {
return execSync(command, options).toString();
}
getCwd() {
return process.cwd();
}
dirname(filePath) {
return path.dirname(filePath);
}
}
// Create a minimal StateEventService implementation
class StateEventService {
constructor() {
this.subscribers = new Map();
}
subscribe(eventName, callback) {
if (!this.subscribers.has(eventName)) {
this.subscribers.set(eventName, []);
}
this.subscribers.get(eventName).push(callback);
return () => this.unsubscribe(eventName, callback);
}
unsubscribe(eventName, callback) {
if (!this.subscribers.has(eventName)) return;
const callbacks = this.subscribers.get(eventName);
const index = callbacks.indexOf(callback);
if (index !== -1) {
callbacks.splice(index, 1);
}
}
emit(eventName, data) {
if (!this.subscribers.has(eventName)) return;
for (const callback of this.subscribers.get(eventName)) {
callback(data);
}
}
}
// Create a custom ImportDirectiveHandler that can handle JSON files
class CustomImportDirectiveHandler {
constructor(stateService, pathService, fileSystemService) {
this.stateService = stateService;
this.pathService = pathService;
this.fileSystemService = fileSystemService;
this.kind = 'import';
}
async execute(node, context) {
try {
console.log('Custom import handler executing for node:', JSON.stringify(node, null, 2));
if (!node.directive.path) {
throw new Error('Import directive requires a path');
}
console.log('Resolving path:', JSON.stringify(node.directive.path, null, 2));
// Resolve the path
const resolvedPath = await this.pathService.resolvePath(node.directive.path, context.state);
console.log('Full resolved path object:', JSON.stringify(resolvedPath, null, 2));
// Only throw an error if resolvedPath is null or undefined
if (!resolvedPath) {
throw new Error(`Failed to resolve path: ${node.directive.path.raw}`);
}
// Check if resolvedPath is a string or an object with resolvedPath property
const fullPath = typeof resolvedPath === 'string' ? resolvedPath : resolvedPath.resolvedPath;
console.log('Using resolved path:', fullPath);
// Check if the file exists
const fileExists = await this.fileSystemService.exists(fullPath);
console.log('File exists check result:', fileExists);
if (!fileExists) {
throw new Error(`Import file not found: ${fullPath}`);
}
// Read the file content
const content = await this.fileSystemService.readFile(fullPath, 'utf8');
console.log('File content read, length:', content.length);
// If it's a JSON file, parse it and import the properties
if (fullPath.endsWith('.json')) {
try {
console.log('Parsing JSON file');
const jsonData = JSON.parse(content);
// If specific imports are defined, import only those properties
if (node.directive.imports && node.directive.imports.length > 0) {
console.log('Importing specific properties from JSON');
for (const importItem of node.directive.imports) {
if (importItem.name) {
const value = jsonData[importItem.name];
if (value !== undefined) {
console.log(`Importing ${importItem.name} with value:`, value);
context.state.setDataVar(importItem.name, value);
} else {
console.log(`Warning: Property ${importItem.name} not found in JSON`);
}
}
}
} else {
// Import all properties from the JSON
console.log('Importing all properties from JSON');
for (const [key, value] of Object.entries(jsonData)) {
console.log(`Importing ${key} with value:`, value);
context.state.setDataVar(key, value);
}
}
console.log('JSON import completed successfully');
} catch (jsonError) {
console.error('Error parsing JSON:', jsonError);
throw new Error(`Failed to parse JSON file: ${jsonError.message}`);
}
} else {
// For non-JSON files, delegate to standard import process
console.log('Non-JSON file detected, delegating to standard import process');
// This is a placeholder - in a real implementation, you would handle non-JSON imports here
throw new Error('Non-JSON imports not implemented in this custom handler');
}
// Return the updated state
return context.state;
} catch (error) {
console.error('Error in custom import handler:', error);
throw error;
}
}
}
/**
* Register services with the DI container
*/
function registerServices() {
// Create service instances directly
const nodeFileSystem = new NodeFileSystem();
const stateEventService = new StateEventService();
const stateService = new StateService(stateEventService);
const validationService = new ValidationService();
const pathService = new PathService();
const parserService = new ParserService();
const circularityService = new CircularityService(stateService);
// Create resolution service with its dependencies
const resolutionService = new ResolutionService(
stateService,
nodeFileSystem,
parserService,
pathService
);
// Create directive service
const directiveService = new DirectiveService();
// Create interpreter service
const interpreterService = new InterpreterService();
// Initialize services in the correct order
pathService.initialize();
stateService.setDataVar('debug', true);
// Initialize directive service with all required dependencies
directiveService.initialize(
validationService,
stateService,
pathService,
nodeFileSystem,
parserService,
interpreterService,
circularityService,
resolutionService
);
// Initialize interpreter service with its dependencies
interpreterService.initialize(directiveService, stateService);
// Register custom import handler for JSON files
directiveService.registerHandler(
new CustomImportDirectiveHandler(
stateService,
pathService,
nodeFileSystem
)
);
return {
nodeFileSystem,
stateService,
validationService,
pathService,
parserService,
circularityService,
resolutionService,
directiveService,
interpreterService
};
}
// Command: debug-transform
yargs(hideBin(process.argv))
.scriptName('debug-cli')
.usage('$0 <cmd> [args]')
.command('debug-transform <file>', 'Debug transformations for a file', (yargs) => {
yargs.positional('file', {
describe: 'File to transform',
type: 'string'
});
}, async (argv) => {
console.log(`Debugging transformations for file: ${argv.file}`);
try {
// Register services
const services = registerServices();
// Resolve the file path
const filePath = path.resolve(process.cwd(), argv.file);
console.log(`Resolved file path: ${filePath}`);
// Read the file content
const contentToTransform = fs.readFileSync(filePath, 'utf8');
// Set debug mode in the state service
services.stateService.setDataVar('debug', true);
// Parse the content into nodes
const nodes = await services.parserService.parse(contentToTransform);
console.log(`Parsed ${nodes.length} nodes from file`);
// Set the current file path in the state service
services.stateService.setDataVar('currentFilePath', filePath);
// Interpret the nodes
await services.interpreterService.interpret(nodes, { filePath });
console.log('Transformation completed successfully');
} catch (error) {
console.error('Error during transformation:', error);
process.exit(1);
}
})
.help()
.parse();