diff --git a/crates/codegen/runtime/cargo/crate/src/runtime/compilation/internal_builder.rs b/crates/codegen/runtime/cargo/crate/src/runtime/compilation/internal_builder.rs index e26c336aa5..79c68f4e8e 100644 --- a/crates/codegen/runtime/cargo/crate/src/runtime/compilation/internal_builder.rs +++ b/crates/codegen/runtime/cargo/crate/src/runtime/compilation/internal_builder.rs @@ -39,7 +39,7 @@ impl InternalCompilationBuilder { pub fn add_file( &mut self, - id: String, + file_id: String, contents: &str, ) -> Result { let parse_output = self.parser.parse(Parser::ROOT_KIND, contents); @@ -47,8 +47,8 @@ impl InternalCompilationBuilder { let import_strings = extract_imports(parse_output.create_tree_cursor()).map_err(AddFileError::Internal)?; - let file = File::new(id.clone(), parse_output); - self.files.insert(id, file); + let file = File::new(file_id.clone(), parse_output); + self.files.insert(file_id, file); Ok(AddFileResponse { import_strings }) } diff --git a/crates/codegen/runtime/cargo/wasm/src/runtime/interface/compilation.wit.jinja2 b/crates/codegen/runtime/cargo/wasm/src/runtime/interface/compilation.wit.jinja2 index 2198c6792c..e47ae9b452 100644 --- a/crates/codegen/runtime/cargo/wasm/src/runtime/interface/compilation.wit.jinja2 +++ b/crates/codegen/runtime/cargo/wasm/src/runtime/interface/compilation.wit.jinja2 @@ -8,7 +8,7 @@ interface compilation { create: static func(version: string) -> result; /// Adds a source file to the compilation unit. - add-file: func(id: string, contents: string) -> result; + add-file: func(file-id: string, contents: string) -> result; /// Adds an import relationship between files. add-import: func(file-id: string, import-string: string, imported-file-id: string) -> result<_, string>; diff --git a/crates/codegen/runtime/cargo/wasm/src/runtime/interface/generated/compilation.wit b/crates/codegen/runtime/cargo/wasm/src/runtime/interface/generated/compilation.wit index 5c173ca75e..489e07fcf4 100644 --- a/crates/codegen/runtime/cargo/wasm/src/runtime/interface/generated/compilation.wit +++ b/crates/codegen/runtime/cargo/wasm/src/runtime/interface/generated/compilation.wit @@ -10,7 +10,7 @@ interface compilation { create: static func(version: string) -> result; /// Adds a source file to the compilation unit. - add-file: func(id: string, contents: string) -> result; + add-file: func(file-id: string, contents: string) -> result; /// Adds an import relationship between files. add-import: func(file-id: string, import-string: string, imported-file-id: string) -> result<_, string>; diff --git a/crates/codegen/runtime/cargo/wasm/src/runtime/wrappers/compilation/mod.rs b/crates/codegen/runtime/cargo/wasm/src/runtime/wrappers/compilation/mod.rs index e391e55d21..691ce8e4fc 100644 --- a/crates/codegen/runtime/cargo/wasm/src/runtime/wrappers/compilation/mod.rs +++ b/crates/codegen/runtime/cargo/wasm/src/runtime/wrappers/compilation/mod.rs @@ -42,9 +42,9 @@ define_refcell_wrapper! { InternalCompilationBuilder { .map_err(|e| e.to_string()) } - fn add_file(&self, id: String, contents: String) -> Result { + fn add_file(&self, file_id: String, contents: String) -> Result { self._borrow_mut_ffi() - .add_file(id, &contents) + .add_file(file_id, &contents) .map(IntoFFI::_into_ffi) .map_err(|e| e.to_string()) } diff --git a/crates/codegen/runtime/npm/package/src/runtime/compilation/builder.mts b/crates/codegen/runtime/npm/package/src/runtime/compilation/builder.mts new file mode 100644 index 0000000000..78e144d7c6 --- /dev/null +++ b/crates/codegen/runtime/npm/package/src/runtime/compilation/builder.mts @@ -0,0 +1,72 @@ +import * as generated from "../../../wasm/index.mjs"; + +const InternalCompilationBuilder = generated.compilation.InternalCompilationBuilder; +type InternalCompilationBuilder = generated.compilation.InternalCompilationBuilder; + +export interface CompilationOptions { + /** + * The language version to parse files with. + */ + version: string; + + resolveImport: (fileId: string, importString: string) => Promise; + + /** + * Callback used by this builder to load the contents of a file. + * The user is responsible for fetching the file from the filesystem. + * If the file is not found, the callback should return undefined. + * If the file cannot be read, the callback should throw an error, and it will be propagated to the caller. + */ + loadFileContents: (fileId: string) => Promise; +} + +/** + * A builder for creating compilation units. + * Allows incrementally building a transitive list of all files and their imports. + */ +export class CompilationBuilder { + private readonly pendingTasks: Promise[] = []; + private readonly resolvedFileIds: Set = new Set(); + + private constructor( + private readonly options: CompilationOptions, + private readonly internal: InternalCompilationBuilder, + ) {} + + /** + * Creates a new compilation builder for the specified language version. + */ + public create(options: CompilationOptions): CompilationBuilder { + const internal = InternalCompilationBuilder.create(options.version); + return new CompilationBuilder(options, internal); + } + + /** + * Adds a source file to the compilation unit. + */ + public async addFile(fileId: string, contents: string): Promise { + const { importStrings } = this.internal.addFile(fileId, contents); + + for (const importString of importStrings) { + this.pendingTasks.push(this.addImport(fileId, importString)); + } + + await this.executePendingTasks(); + } + + private async addImport(fileId: string, importString: string): Promise { + const importedFileId = await this.options.resolveImport(fileId, importString); + if (importedFileId === undefined) { + return; + } + + this.internal.addImport(fileId, importString, importedFileId); + } + + private async executePendingTasks(): Promise { + while (this.pendingTasks.length > 0) { + const pendingTasks = this.pendingTasks.splice(0); + await Promise.all(pendingTasks); + } + } +} diff --git a/crates/codegen/runtime/npm/package/src/runtime/compilation/index.mts b/crates/codegen/runtime/npm/package/src/runtime/compilation/index.mts index 9034425b6c..ac2b6c2353 100644 --- a/crates/codegen/runtime/npm/package/src/runtime/compilation/index.mts +++ b/crates/codegen/runtime/npm/package/src/runtime/compilation/index.mts @@ -1,8 +1,9 @@ import * as generated from "../../../wasm/index.mjs"; +import * as builder from "./builder.mjs"; -// This internal implementation is exposed via an async wrapper below: -const InternalCompilationBuilder = generated.compilation.InternalCompilationBuilder; -type InternalCompilationBuilder = generated.compilation.InternalCompilationBuilder; +// This generated 'InternalCompilationBuilder' is exposed via an async wrapper here: +export const CompilationBuilder = builder.CompilationBuilder; +export type CompilationBuilder = builder.CompilationBuilder; export const CompilationUnit = generated.compilation.CompilationUnit; export type CompilationUnit = generated.compilation.CompilationUnit; @@ -12,29 +13,3 @@ export type FilesIterator = generated.compilation.FilesIterator; export const File = generated.compilation.File; export type File = generated.compilation.File; - -/** - * A builder for creating compilation units. - * Allows incrementally building a transitive list of all files and their imports. - */ -export class CompilationBuilder { - private constructor(private readonly internal: InternalCompilationBuilder) {} - - /** - * Creates a new compilation builder for the specified language version. - */ - public create(version: string): CompilationBuilder { - return new CompilationBuilder(InternalCompilationBuilder.create(version)); - } - - /** - * Adds a source file to the compilation unit. - */ - public async addFile(id: string, contents: string): Promise { - const queue = new PromiseQueue(); - - this.enqueueFileAdditions(queue, id, contents); - - await queue.everything(); - } -} diff --git a/crates/solidity/outputs/cargo/crate/src/generated/compilation/internal_builder.rs b/crates/solidity/outputs/cargo/crate/src/generated/compilation/internal_builder.rs index 48dcf52b67..e0c91c55e1 100644 --- a/crates/solidity/outputs/cargo/crate/src/generated/compilation/internal_builder.rs +++ b/crates/solidity/outputs/cargo/crate/src/generated/compilation/internal_builder.rs @@ -41,7 +41,7 @@ impl InternalCompilationBuilder { pub fn add_file( &mut self, - id: String, + file_id: String, contents: &str, ) -> Result { let parse_output = self.parser.parse(Parser::ROOT_KIND, contents); @@ -49,8 +49,8 @@ impl InternalCompilationBuilder { let import_strings = extract_imports(parse_output.create_tree_cursor()).map_err(AddFileError::Internal)?; - let file = File::new(id.clone(), parse_output); - self.files.insert(id, file); + let file = File::new(file_id.clone(), parse_output); + self.files.insert(file_id, file); Ok(AddFileResponse { import_strings }) } diff --git a/crates/solidity/outputs/cargo/wasm/src/generated/interface/generated/compilation.wit b/crates/solidity/outputs/cargo/wasm/src/generated/interface/generated/compilation.wit index 5c173ca75e..489e07fcf4 100644 --- a/crates/solidity/outputs/cargo/wasm/src/generated/interface/generated/compilation.wit +++ b/crates/solidity/outputs/cargo/wasm/src/generated/interface/generated/compilation.wit @@ -10,7 +10,7 @@ interface compilation { create: static func(version: string) -> result; /// Adds a source file to the compilation unit. - add-file: func(id: string, contents: string) -> result; + add-file: func(file-id: string, contents: string) -> result; /// Adds an import relationship between files. add-import: func(file-id: string, import-string: string, imported-file-id: string) -> result<_, string>; diff --git a/crates/solidity/outputs/cargo/wasm/src/generated/wrappers/compilation/mod.rs b/crates/solidity/outputs/cargo/wasm/src/generated/wrappers/compilation/mod.rs index 6b968c6a1a..ff02d34e76 100644 --- a/crates/solidity/outputs/cargo/wasm/src/generated/wrappers/compilation/mod.rs +++ b/crates/solidity/outputs/cargo/wasm/src/generated/wrappers/compilation/mod.rs @@ -44,9 +44,9 @@ define_refcell_wrapper! { InternalCompilationBuilder { .map_err(|e| e.to_string()) } - fn add_file(&self, id: String, contents: String) -> Result { + fn add_file(&self, file_id: String, contents: String) -> Result { self._borrow_mut_ffi() - .add_file(id, &contents) + .add_file(file_id, &contents) .map(IntoFFI::_into_ffi) .map_err(|e| e.to_string()) } diff --git a/crates/solidity/outputs/npm/package/src/generated/compilation/builder.mts b/crates/solidity/outputs/npm/package/src/generated/compilation/builder.mts new file mode 100644 index 0000000000..f332ebf26a --- /dev/null +++ b/crates/solidity/outputs/npm/package/src/generated/compilation/builder.mts @@ -0,0 +1,58 @@ +// This file is generated automatically by infrastructure scripts. Please don't edit by hand. + +import * as generated from "../../../wasm/index.mjs"; + +const InternalCompilationBuilder = generated.compilation.InternalCompilationBuilder; +type InternalCompilationBuilder = generated.compilation.InternalCompilationBuilder; + +export interface CompilationOptions { + /** + * The language version to parse files with. + */ + version: string; + + resolveImport: (fileId: string, importString: string) => Promise; +} + +/** + * A builder for creating compilation units. + * Allows incrementally building a transitive list of all files and their imports. + */ +export class CompilationBuilder { + private readonly queue: Promise[] = []; + + private constructor( + private readonly options: CompilationOptions, + private readonly internal: InternalCompilationBuilder, + ) {} + + /** + * Creates a new compilation builder for the specified language version. + */ + public create(options: CompilationOptions): CompilationBuilder { + const internal = InternalCompilationBuilder.create(options.version); + return new CompilationBuilder(options, internal); + } + + /** + * Adds a source file to the compilation unit. + */ + public async addFile(fileId: string, contents: string): Promise { + const { importStrings } = this.internal.addFile(id, contents); + + for (const importString of importStrings) { + this.queue.push( + (async () => { + const importedFileId = await this.options.resolveImport(id, importString); + })(), + ); + } + + await this.waitForQueue(); + } + + private async waitForQueue(): Promise { + const queue = this.queue.splice(0); + await Promise.all(queue); + } +} diff --git a/crates/solidity/outputs/npm/package/src/generated/compilation/index.mts b/crates/solidity/outputs/npm/package/src/generated/compilation/index.mts index b8a2159452..7af6f53daa 100644 --- a/crates/solidity/outputs/npm/package/src/generated/compilation/index.mts +++ b/crates/solidity/outputs/npm/package/src/generated/compilation/index.mts @@ -1 +1,17 @@ // This file is generated automatically by infrastructure scripts. Please don't edit by hand. + +import * as generated from "../../../wasm/index.mjs"; +import * as builder from "./builder.mjs"; + +// This generated 'InternalCompilationBuilder' is exposed via an async wrapper here: +export const CompilationBuilder = builder.CompilationBuilder; +export type CompilationBuilder = builder.CompilationBuilder; + +export const CompilationUnit = generated.compilation.CompilationUnit; +export type CompilationUnit = generated.compilation.CompilationUnit; + +export const FilesIterator = generated.compilation.FilesIterator; +export type FilesIterator = generated.compilation.FilesIterator; + +export const File = generated.compilation.File; +export type File = generated.compilation.File; diff --git a/crates/testlang/outputs/cargo/crate/src/generated/compilation/internal_builder.rs b/crates/testlang/outputs/cargo/crate/src/generated/compilation/internal_builder.rs index 48dcf52b67..e0c91c55e1 100644 --- a/crates/testlang/outputs/cargo/crate/src/generated/compilation/internal_builder.rs +++ b/crates/testlang/outputs/cargo/crate/src/generated/compilation/internal_builder.rs @@ -41,7 +41,7 @@ impl InternalCompilationBuilder { pub fn add_file( &mut self, - id: String, + file_id: String, contents: &str, ) -> Result { let parse_output = self.parser.parse(Parser::ROOT_KIND, contents); @@ -49,8 +49,8 @@ impl InternalCompilationBuilder { let import_strings = extract_imports(parse_output.create_tree_cursor()).map_err(AddFileError::Internal)?; - let file = File::new(id.clone(), parse_output); - self.files.insert(id, file); + let file = File::new(file_id.clone(), parse_output); + self.files.insert(file_id, file); Ok(AddFileResponse { import_strings }) } diff --git a/crates/testlang/outputs/cargo/wasm/src/generated/interface/generated/compilation.wit b/crates/testlang/outputs/cargo/wasm/src/generated/interface/generated/compilation.wit index 5c173ca75e..489e07fcf4 100644 --- a/crates/testlang/outputs/cargo/wasm/src/generated/interface/generated/compilation.wit +++ b/crates/testlang/outputs/cargo/wasm/src/generated/interface/generated/compilation.wit @@ -10,7 +10,7 @@ interface compilation { create: static func(version: string) -> result; /// Adds a source file to the compilation unit. - add-file: func(id: string, contents: string) -> result; + add-file: func(file-id: string, contents: string) -> result; /// Adds an import relationship between files. add-import: func(file-id: string, import-string: string, imported-file-id: string) -> result<_, string>; diff --git a/crates/testlang/outputs/cargo/wasm/src/generated/wrappers/compilation/mod.rs b/crates/testlang/outputs/cargo/wasm/src/generated/wrappers/compilation/mod.rs index 6b968c6a1a..ff02d34e76 100644 --- a/crates/testlang/outputs/cargo/wasm/src/generated/wrappers/compilation/mod.rs +++ b/crates/testlang/outputs/cargo/wasm/src/generated/wrappers/compilation/mod.rs @@ -44,9 +44,9 @@ define_refcell_wrapper! { InternalCompilationBuilder { .map_err(|e| e.to_string()) } - fn add_file(&self, id: String, contents: String) -> Result { + fn add_file(&self, file_id: String, contents: String) -> Result { self._borrow_mut_ffi() - .add_file(id, &contents) + .add_file(file_id, &contents) .map(IntoFFI::_into_ffi) .map_err(|e| e.to_string()) } diff --git a/crates/testlang/outputs/npm/package/src/generated/compilation/builder.mts b/crates/testlang/outputs/npm/package/src/generated/compilation/builder.mts new file mode 100644 index 0000000000..f332ebf26a --- /dev/null +++ b/crates/testlang/outputs/npm/package/src/generated/compilation/builder.mts @@ -0,0 +1,58 @@ +// This file is generated automatically by infrastructure scripts. Please don't edit by hand. + +import * as generated from "../../../wasm/index.mjs"; + +const InternalCompilationBuilder = generated.compilation.InternalCompilationBuilder; +type InternalCompilationBuilder = generated.compilation.InternalCompilationBuilder; + +export interface CompilationOptions { + /** + * The language version to parse files with. + */ + version: string; + + resolveImport: (fileId: string, importString: string) => Promise; +} + +/** + * A builder for creating compilation units. + * Allows incrementally building a transitive list of all files and their imports. + */ +export class CompilationBuilder { + private readonly queue: Promise[] = []; + + private constructor( + private readonly options: CompilationOptions, + private readonly internal: InternalCompilationBuilder, + ) {} + + /** + * Creates a new compilation builder for the specified language version. + */ + public create(options: CompilationOptions): CompilationBuilder { + const internal = InternalCompilationBuilder.create(options.version); + return new CompilationBuilder(options, internal); + } + + /** + * Adds a source file to the compilation unit. + */ + public async addFile(fileId: string, contents: string): Promise { + const { importStrings } = this.internal.addFile(id, contents); + + for (const importString of importStrings) { + this.queue.push( + (async () => { + const importedFileId = await this.options.resolveImport(id, importString); + })(), + ); + } + + await this.waitForQueue(); + } + + private async waitForQueue(): Promise { + const queue = this.queue.splice(0); + await Promise.all(queue); + } +} diff --git a/crates/testlang/outputs/npm/package/src/generated/compilation/index.mts b/crates/testlang/outputs/npm/package/src/generated/compilation/index.mts index b8a2159452..7af6f53daa 100644 --- a/crates/testlang/outputs/npm/package/src/generated/compilation/index.mts +++ b/crates/testlang/outputs/npm/package/src/generated/compilation/index.mts @@ -1 +1,17 @@ // This file is generated automatically by infrastructure scripts. Please don't edit by hand. + +import * as generated from "../../../wasm/index.mjs"; +import * as builder from "./builder.mjs"; + +// This generated 'InternalCompilationBuilder' is exposed via an async wrapper here: +export const CompilationBuilder = builder.CompilationBuilder; +export type CompilationBuilder = builder.CompilationBuilder; + +export const CompilationUnit = generated.compilation.CompilationUnit; +export type CompilationUnit = generated.compilation.CompilationUnit; + +export const FilesIterator = generated.compilation.FilesIterator; +export type FilesIterator = generated.compilation.FilesIterator; + +export const File = generated.compilation.File; +export type File = generated.compilation.File;