Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 0 additions & 38 deletions src/Program.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,44 +262,6 @@ end sub
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', () => {
Expand Down
38 changes: 17 additions & 21 deletions src/Program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -727,27 +727,23 @@ export class Program {
this.plugins.emit('beforeProgramValidate', this);
beforeProgramValidateWasEmitted = true;
})
.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);
}
.forEachFactory(() => 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);
}
})
.forEach(Object.values(this.scopes), (scope) => {
Expand Down
39 changes: 39 additions & 0 deletions src/common/Sequencer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,43 @@ describe('Sequencer', () => {
}
expect(cancelCalled).to.be.true;
});

it('forEachFactory calls factory function at execution time', () => {
const values = [];
let items = [1, 2];

const sequencer = new Sequencer().forEachFactory(() => items, (i) => {
values.push(i);
});

// Add more items after sequencer is configured but before execution
items.push(3);

sequencer.runSync();

// Should process all items including the one added after configuration
expect(values).to.eql([1, 2, 3]);
});

it('forEachFactory maintains event loop yielding behavior', async () => {
const values = [];
let executionTimes = [];

await new Sequencer({
minSyncDuration: 10 // Very short duration to force frequent yielding
}).forEachFactory(() => [1, 2, 3, 4, 5], (i) => {
executionTimes.push(Date.now());
values.push(i);
// Simulate some work
const start = Date.now();
while (Date.now() - start < 5) {
// busy wait
}
}).run();

expect(values).to.eql([1, 2, 3, 4, 5]);
// With the short minSyncDuration, we should see some gaps in execution times
// indicating the sequencer yielded to the event loop
expect(executionTimes.length).to.equal(5);
});
});
16 changes: 16 additions & 0 deletions src/common/Sequencer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ export class Sequencer {
return this;
}

public forEachFactory<T>(itemsFactory: () => T[], func: (item: T) => any) {
this.actions.push({
args: [],
func: () => {
// Get the items from the factory function at execution time
const items = itemsFactory();
// Create a nested sequencer for the items to maintain event loop yielding behavior
const nestedSequencer = new Sequencer(this.options);
return nestedSequencer
.forEach(items, func)
.runSync();
}
});
return this;
}

private emitter = new EventEmitter();

public onCancel(callback: () => void) {
Expand Down