diff --git a/src/Program.spec.ts b/src/Program.spec.ts index ce484b0fa..fbdc7eabd 100644 --- a/src/Program.spec.ts +++ b/src/Program.spec.ts @@ -180,6 +180,44 @@ describe('Program', () => { // await program.loadOrReloadFile('components', '') }); + it('validates files added in beforeProgramValidate hook', () => { + const afterFileValidate = sinon.spy(); + + program.plugins = new PluginInterface([{ + name: 'test plugin that adds files during beforeProgramValidate', + beforeProgramValidate: (program: Program) => { + // Add a file that defines an enum + const fileContent = `enum Tasks + MyTask = "_AsyncTask" +end enum`; + program.setFile('source/AsyncTask/Tasks.bs', fileContent); + }, + afterFileValidate: afterFileValidate + }], { logger: createLogger() }); + + // Add a main file that imports the enum + program.setFile('source/main.bs', ` +import "pkg:/source/AsyncTask/Tasks.bs" + +sub main() + print Tasks.MyTask +end sub + `); + + program.validate(); + + // Verify that the Tasks.bs file was validated + const tasksFile = program.getFile('source/AsyncTask/Tasks.bs'); + expect(tasksFile).to.not.be.undefined; + expect(tasksFile.isValidated).to.be.true; + + // Verify afterFileValidate was called for both files (main.bs and Tasks.bs) + expect(afterFileValidate.callCount).to.equal(2); + + // Should have no diagnostics (Tasks should be found) + expectZeroDiagnostics(program); + }); + it(`emits events for scope and file creation`, () => { const beforeProgramValidate = sinon.spy(); const afterProgramValidate = sinon.spy(); @@ -224,6 +262,44 @@ describe('Program', () => { expect(afterFileParse.callCount).to.equal(2); expect(afterFileValidate.callCount).to.equal(2); }); + + it('validates files added in beforeProgramValidate hook', () => { + const afterFileValidate = sinon.spy(); + + program.plugins = new PluginInterface([{ + name: 'test plugin that adds files during beforeProgramValidate', + beforeProgramValidate: (program: Program) => { + // Add a file that defines an enum + const fileContent = `enum Tasks + MyTask = "_AsyncTask" +end enum`; + program.setFile('source/AsyncTask/Tasks.bs', fileContent); + }, + afterFileValidate: afterFileValidate + }], { logger: createLogger() }); + + // Add a main file that imports the enum + program.setFile('source/main.bs', ` +import "pkg:/source/AsyncTask/Tasks.bs" + +sub main() + print Tasks.MyTask +end sub + `); + + program.validate(); + + // Verify that the Tasks.bs file was validated + const tasksFile = program.getFile('source/AsyncTask/Tasks.bs'); + expect(tasksFile).to.not.be.undefined; + expect(tasksFile.isValidated).to.be.true; + + // Verify afterFileValidate was called for both files (main.bs and Tasks.bs) + expect(afterFileValidate.callCount).to.equal(2); + + // Should have no diagnostics (Tasks should be found) + expectZeroDiagnostics(program); + }); }); describe('validate', () => { diff --git a/src/Program.ts b/src/Program.ts index d50052cef..5e945bee5 100644 --- a/src/Program.ts +++ b/src/Program.ts @@ -727,23 +727,27 @@ export class Program { this.plugins.emit('beforeProgramValidate', this); beforeProgramValidateWasEmitted = true; }) - .forEach(Object.values(this.files), (file) => { - if (!file.isValidated) { - this.plugins.emit('beforeFileValidate', { - program: this, - file: file - }); - - //emit an event to allow plugins to contribute to the file validation process - this.plugins.emit('onFileValidate', { - program: this, - file: file - }); - //call file.validate() IF the file has that function defined - file.validate?.(); - file.isValidated = true; - - this.plugins.emit('afterFileValidate', file); + .once(() => { + // Capture files AFTER beforeProgramValidate event to include any files added by plugins + const filesToValidate = Object.values(this.files); + for (const file of filesToValidate) { + if (!file.isValidated) { + this.plugins.emit('beforeFileValidate', { + program: this, + file: file + }); + + //emit an event to allow plugins to contribute to the file validation process + this.plugins.emit('onFileValidate', { + program: this, + file: file + }); + //call file.validate() IF the file has that function defined + file.validate?.(); + file.isValidated = true; + + this.plugins.emit('afterFileValidate', file); + } } }) .forEach(Object.values(this.scopes), (scope) => {