From 570086e7b4980639c0150a150eed1e09591e739a Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Fri, 26 Jul 2024 17:20:45 +0100 Subject: [PATCH] refactor(world): update worldgen with namespaces output (#2974) --- .changeset/four-bugs-warn.md | 5 + .../contracts/src/codegen/world/IWorld.sol | 1 - .../contracts/src/codegen/world/IWorld.sol | 1 - .../src/codegen/world/IWorld.sol | 1 - package.json | 2 +- .../src/codegen/render-solidity/common.ts | 54 +-------- .../src/codegen/render-solidity/types.ts | 12 +- packages/common/src/codegen/utils/index.ts | 1 - .../common/src/codegen/utils/posixPath.ts | 8 -- packages/store/ts/codegen/getTableOptions.ts | 9 +- packages/store/ts/codegen/renderTableIndex.ts | 4 +- packages/store/ts/config/v2/index.ts | 1 + packages/store/ts/config/v2/namespace.ts | 2 +- packages/world/mud.config.ts | 2 +- .../src/codegen/interfaces/IBaseWorld.sol | 1 - packages/world/ts/config/v2/defaults.ts | 2 +- packages/world/ts/config/v2/input.ts | 10 +- packages/world/ts/config/v2/output.ts | 4 +- packages/world/ts/node/findSolidityFiles.ts | 8 +- packages/world/ts/node/getSystemContracts.ts | 49 ++++++-- .../render-solidity/renderWorldInterface.ts | 47 +++++--- .../world/ts/node/render-solidity/types.ts | 18 +-- .../world/ts/node/render-solidity/worldgen.ts | 108 ++++++++++-------- packages/world/ts/node/resolveSystems.ts | 10 +- .../world/ts/scripts/generate-test-tables.ts | 6 +- .../contracts/src/codegen/world/IWorld.sol | 1 - .../contracts/src/codegen/world/IWorld.sol | 1 - .../contracts/src/codegen/world/IWorld.sol | 1 - .../contracts/src/codegen/world/IWorld.sol | 1 - .../contracts/src/codegen/world/IWorld.sol | 1 - .../src/codegen/world/IWorld.sol | 1 - 31 files changed, 159 insertions(+), 213 deletions(-) create mode 100644 .changeset/four-bugs-warn.md delete mode 100644 packages/common/src/codegen/utils/posixPath.ts diff --git a/.changeset/four-bugs-warn.md b/.changeset/four-bugs-warn.md new file mode 100644 index 0000000000..5012c32c54 --- /dev/null +++ b/.changeset/four-bugs-warn.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/world": patch +--- + +Refactored worldgen in preparation for multiple namespaces. diff --git a/e2e/packages/contracts/src/codegen/world/IWorld.sol b/e2e/packages/contracts/src/codegen/world/IWorld.sol index da4671b1e1..e52b4ae995 100644 --- a/e2e/packages/contracts/src/codegen/world/IWorld.sol +++ b/e2e/packages/contracts/src/codegen/world/IWorld.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.24; /* Autogenerated file. Do not edit manually. */ import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; - import { ICustomErrorsSystem } from "./ICustomErrorsSystem.sol"; import { ILibWrapperSystem } from "./ILibWrapperSystem.sol"; import { INumberListSystem } from "./INumberListSystem.sol"; diff --git a/examples/minimal/packages/contracts/src/codegen/world/IWorld.sol b/examples/minimal/packages/contracts/src/codegen/world/IWorld.sol index 6eb7845c59..93717f817d 100644 --- a/examples/minimal/packages/contracts/src/codegen/world/IWorld.sol +++ b/examples/minimal/packages/contracts/src/codegen/world/IWorld.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.24; /* Autogenerated file. Do not edit manually. */ import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; - import { IChatSystem } from "./IChatSystem.sol"; import { IIncrementSystem } from "./IIncrementSystem.sol"; import { IInventorySystem } from "./IInventorySystem.sol"; diff --git a/examples/multiple-namespaces/src/codegen/world/IWorld.sol b/examples/multiple-namespaces/src/codegen/world/IWorld.sol index af8a6d4c3a..f42e816eff 100644 --- a/examples/multiple-namespaces/src/codegen/world/IWorld.sol +++ b/examples/multiple-namespaces/src/codegen/world/IWorld.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.24; /* Autogenerated file. Do not edit manually. */ import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; - import { IHealSystem } from "./IHealSystem.sol"; import { IMoveSystem } from "./IMoveSystem.sol"; diff --git a/package.json b/package.json index 0191d6afa9..2d4be4a2dd 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "url": "https://github.com/latticexyz/mud.git" }, "scripts": { - "all-build": "for dir in packages/store packages/world packages/world-modules packages/cli test/mock-game-contracts e2e/packages/contracts examples/*/packages/contracts templates/*/packages/contracts; do (cd \"$dir\" && pwd && pnpm build); done", + "all-build": "for dir in packages/store packages/world packages/world-modules packages/cli test/mock-game-contracts e2e/packages/contracts examples/*/packages/contracts examples/multiple-namespaces templates/*/packages/contracts; do (cd \"$dir\" && pwd && pnpm build); done", "all-install": "for dir in . docs e2e examples/* templates/*; do (cd \"$dir\" && pwd && pnpm install); done", "bench": "pnpm run --recursive bench", "build": "turbo run build", diff --git a/packages/common/src/codegen/render-solidity/common.ts b/packages/common/src/codegen/render-solidity/common.ts index f73a5162d7..5e339bee9b 100644 --- a/packages/common/src/codegen/render-solidity/common.ts +++ b/packages/common/src/codegen/render-solidity/common.ts @@ -1,13 +1,4 @@ -import path from "path"; -import { - AbsoluteImportDatum, - RelativeImportDatum, - ImportDatum, - StaticResourceData, - RenderKeyTuple, - RenderType, -} from "./types"; -import { posixPath } from "../utils"; +import { ImportDatum, StaticResourceData, RenderKeyTuple, RenderType } from "./types"; import { resourceToHex } from "../../resourceToHex"; import { hexToResource } from "../../hexToResource"; import { renderImportPath } from "./renderImportPath"; @@ -73,54 +64,11 @@ export function renderCommonData({ }; } -/** - * For 2 paths which are relative to a common root, create a relative import path from one to another - * @deprecated Use `renderImportPath` instead. - */ -export function solidityRelativeImportPath(fromPath: string, usedInPath: string): string { - // 1st "./" must be added because path strips it, - // but solidity expects it unless there's "../" ("./../" is fine). - // 2nd and 3rd "./" forcefully avoid absolute paths (everything is relative to `src`). - return posixPath("./" + path.relative("./" + usedInPath, "./" + fromPath)); -} - /** * Aggregates, deduplicates and renders imports for symbols per path. * Identical symbols from different paths are NOT handled, they should be checked before rendering. */ export function renderImports(imports: ImportDatum[]): string { - return renderAbsoluteImports( - imports.map((importDatum) => { - if ("path" in importDatum) { - return importDatum; - } else { - return { - symbol: importDatum.symbol, - path: solidityRelativeImportPath(importDatum.fromPath, importDatum.usedInPath), - }; - } - }), - ); -} - -/** - * Aggregates, deduplicates and renders imports for symbols per path. - * Identical symbols from different paths are NOT handled, they should be checked before rendering. - */ -export function renderRelativeImports(imports: RelativeImportDatum[]): string { - return renderAbsoluteImports( - imports.map(({ symbol, fromPath, usedInPath }) => ({ - symbol, - path: solidityRelativeImportPath(fromPath, usedInPath), - })), - ); -} - -/** - * Aggregates, deduplicates and renders imports for symbols per path. - * Identical symbols from different paths are NOT handled, they should be checked before rendering. - */ -export function renderAbsoluteImports(imports: AbsoluteImportDatum[]): string { // Aggregate symbols by import path, also deduplicating them const aggregatedImports = new Map>(); for (const { symbol, path } of imports) { diff --git a/packages/common/src/codegen/render-solidity/types.ts b/packages/common/src/codegen/render-solidity/types.ts index e61da22518..6eee0afb0b 100644 --- a/packages/common/src/codegen/render-solidity/types.ts +++ b/packages/common/src/codegen/render-solidity/types.ts @@ -1,15 +1,7 @@ -export interface AbsoluteImportDatum { +export type ImportDatum = { symbol: string; path: string; -} - -export interface RelativeImportDatum { - symbol: string; - fromPath: string; - usedInPath: string; -} - -export type ImportDatum = AbsoluteImportDatum | RelativeImportDatum; +}; export interface StaticResourceData { /** Table namespace string */ diff --git a/packages/common/src/codegen/utils/index.ts b/packages/common/src/codegen/utils/index.ts index 574bb1fbd4..6e24e2081d 100644 --- a/packages/common/src/codegen/utils/index.ts +++ b/packages/common/src/codegen/utils/index.ts @@ -3,4 +3,3 @@ export * from "./extractUserTypes"; export * from "./format"; export * from "./formatAndWrite"; export * from "./loadUserTypesFile"; -export * from "./posixPath"; diff --git a/packages/common/src/codegen/utils/posixPath.ts b/packages/common/src/codegen/utils/posixPath.ts deleted file mode 100644 index c267fbcab4..0000000000 --- a/packages/common/src/codegen/utils/posixPath.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Explicitly normalize a given path to a posix path (using `/` as separator). - * This should be used for generating Solidity files that will be consumed by solc, - * because solc expects `/` as path separator, but path.join produces `\` if the user is on windows. - */ -export function posixPath(path: string): string { - return path.replace(/\\/g, "/"); -} diff --git a/packages/store/ts/codegen/getTableOptions.ts b/packages/store/ts/codegen/getTableOptions.ts index b8c4f66158..e985b7074c 100644 --- a/packages/store/ts/codegen/getTableOptions.ts +++ b/packages/store/ts/codegen/getTableOptions.ts @@ -1,18 +1,13 @@ import path from "path"; import { SchemaTypeArrayToElement } from "@latticexyz/schema-type/deprecated"; -import { - ImportDatum, - RenderDynamicField, - RenderField, - RenderKeyTuple, - RenderStaticField, -} from "@latticexyz/common/codegen"; +import { RenderDynamicField, RenderField, RenderKeyTuple, RenderStaticField } from "@latticexyz/common/codegen"; import { RenderTableOptions } from "./types"; import { getSchemaTypeInfo, resolveAbiOrUserType } from "./userType"; import { Table } from "../config/v2/output"; import { getKeySchema, getValueSchema } from "@latticexyz/protocol-parser/internal"; import { UserType } from "./getUserTypes"; import { isDefined } from "@latticexyz/common/utils"; +import { ImportDatum } from "@latticexyz/common/codegen"; export interface TableOptions { /** Path where the file is expected to be written (relative to project root) */ diff --git a/packages/store/ts/codegen/renderTableIndex.ts b/packages/store/ts/codegen/renderTableIndex.ts index 69f14f5432..7ba1e2634e 100644 --- a/packages/store/ts/codegen/renderTableIndex.ts +++ b/packages/store/ts/codegen/renderTableIndex.ts @@ -1,4 +1,4 @@ -import { renderList, renderedSolidityHeader } from "@latticexyz/common/codegen"; +import { renderImportPath, renderList, renderedSolidityHeader } from "@latticexyz/common/codegen"; import { TableOptions } from "./getTableOptions"; import path from "node:path/posix"; @@ -15,7 +15,7 @@ export function renderTableIndex(codegenIndexPath: string, options: TableOptions const imports = [tableName]; if (structName) imports.push(structName); - return `import { ${imports.join(", ")} } from "./${path.relative(path.dirname(codegenIndexPath), outputPath)}";`; + return `import { ${imports.join(", ")} } from "${renderImportPath("./" + path.relative(path.dirname(codegenIndexPath), outputPath))}";`; })} `; } diff --git a/packages/store/ts/config/v2/index.ts b/packages/store/ts/config/v2/index.ts index bc37e67a37..17dfc8a467 100644 --- a/packages/store/ts/config/v2/index.ts +++ b/packages/store/ts/config/v2/index.ts @@ -11,4 +11,5 @@ export * from "./defaults"; export * from "./codegen"; export * from "./enums"; export * from "./userTypes"; +export * from "./namespace"; export * from "./namespacedTables"; diff --git a/packages/store/ts/config/v2/namespace.ts b/packages/store/ts/config/v2/namespace.ts index 0a4b2e3237..ccbd144480 100644 --- a/packages/store/ts/config/v2/namespace.ts +++ b/packages/store/ts/config/v2/namespace.ts @@ -45,7 +45,7 @@ export type resolveNamespace = input export function resolveNamespace( input: input, - scope: scope, + scope: scope = AbiTypeScope as never, ): resolveNamespace { const label = input.label; const namespace = input.namespace ?? label.slice(0, 14); diff --git a/packages/world/mud.config.ts b/packages/world/mud.config.ts index 6ce75e044f..6c0607e052 100644 --- a/packages/world/mud.config.ts +++ b/packages/world/mud.config.ts @@ -2,7 +2,7 @@ import { defineWorld } from "./ts/config/v2/world"; export default defineWorld({ codegen: { - worldImportPath: "../../", + worldImportPath: "./src", worldgenDirectory: "interfaces", worldInterfaceName: "IBaseWorld", }, diff --git a/packages/world/src/codegen/interfaces/IBaseWorld.sol b/packages/world/src/codegen/interfaces/IBaseWorld.sol index 9d2ba9e356..144be64d0f 100644 --- a/packages/world/src/codegen/interfaces/IBaseWorld.sol +++ b/packages/world/src/codegen/interfaces/IBaseWorld.sol @@ -5,7 +5,6 @@ pragma solidity >=0.8.24; import { IStore } from "@latticexyz/store/src/IStore.sol"; import { IWorldKernel } from "../../IWorldKernel.sol"; - import { IRegistrationSystem } from "./IRegistrationSystem.sol"; import { IAccessManagementSystem } from "./IAccessManagementSystem.sol"; import { IBalanceTransferSystem } from "./IBalanceTransferSystem.sol"; diff --git a/packages/world/ts/config/v2/defaults.ts b/packages/world/ts/config/v2/defaults.ts index 1c1ea0a80f..cc034bdb55 100644 --- a/packages/world/ts/config/v2/defaults.ts +++ b/packages/world/ts/config/v2/defaults.ts @@ -20,7 +20,7 @@ export type MODULE_DEFAULTS = typeof MODULE_DEFAULTS; export const CODEGEN_DEFAULTS = { worldInterfaceName: "IWorld", worldgenDirectory: "world", - worldImportPath: "@latticexyz/world/src/", + worldImportPath: "@latticexyz/world/src", } as const satisfies CodegenInput; export type CODEGEN_DEFAULTS = typeof CODEGEN_DEFAULTS; diff --git a/packages/world/ts/config/v2/input.ts b/packages/world/ts/config/v2/input.ts index 15a8964896..f728550dfb 100644 --- a/packages/world/ts/config/v2/input.ts +++ b/packages/world/ts/config/v2/input.ts @@ -1,6 +1,7 @@ import { show } from "@arktype/util"; import { StoreInput } from "@latticexyz/store/config/v2"; import { DynamicResolution, ValueWithType } from "./dynamicResolution"; +import { Codegen } from "./output"; export type SystemInput = { /** @@ -85,14 +86,7 @@ export type DeployInput = { readonly upgradeableWorldImplementation?: boolean; }; -export type CodegenInput = { - /** The name of the World interface to generate. (Default `IWorld`) */ - readonly worldInterfaceName?: string; - /** Directory to output system and world interfaces of `worldgen` (Default "world") */ - readonly worldgenDirectory?: string; - /** Path for world package imports. Default is "@latticexyz/world/src/" */ - readonly worldImportPath?: string; -}; +export type CodegenInput = Partial; export type WorldInput = show< StoreInput & { diff --git a/packages/world/ts/config/v2/output.ts b/packages/world/ts/config/v2/output.ts index 964bab090e..120ba132c5 100644 --- a/packages/world/ts/config/v2/output.ts +++ b/packages/world/ts/config/v2/output.ts @@ -82,8 +82,10 @@ export type Codegen = { /** Directory to output system and world interfaces of `worldgen` (Default "world") */ readonly worldgenDirectory: string; /** - * Path for world package imports. Default is "@latticexyz/world/src/" * @internal + * Absolute import path for a package import or starting with `.` for an import relative to project root dir. + * + * Defaults to `@latticexyz/world/src` if not set. */ readonly worldImportPath: string; }; diff --git a/packages/world/ts/node/findSolidityFiles.ts b/packages/world/ts/node/findSolidityFiles.ts index 3d37a77360..460a971ff3 100644 --- a/packages/world/ts/node/findSolidityFiles.ts +++ b/packages/world/ts/node/findSolidityFiles.ts @@ -1,13 +1,9 @@ import path from "node:path"; import { glob } from "glob"; -import { World } from "../config/v2/output"; // TODO: move to common codegen? -export async function findSolidityFiles({ rootDir, config }: { rootDir: string; config: World }) { - const files = await glob(path.join(config.sourceDirectory, "**", "*.sol"), { - cwd: rootDir, - }); - +export async function findSolidityFiles({ cwd, pattern = "**" }: { cwd?: string; pattern: string }) { + const files = await glob(path.join(pattern, "*.sol"), { cwd }); return files.sort().map((filename) => ({ filename, basename: path.basename(filename, ".sol"), diff --git a/packages/world/ts/node/getSystemContracts.ts b/packages/world/ts/node/getSystemContracts.ts index 93c0aaa81f..8b9e12c497 100644 --- a/packages/world/ts/node/getSystemContracts.ts +++ b/packages/world/ts/node/getSystemContracts.ts @@ -1,9 +1,11 @@ +import path from "node:path"; import { World } from "../config/v2/output"; import { findSolidityFiles } from "./findSolidityFiles"; -export type SolidityContract = { +export type SystemContract = { readonly sourcePath: string; - readonly name: string; + readonly namespaceLabel: string; + readonly systemLabel: string; }; export type GetSystemContractsOptions = { @@ -14,20 +16,43 @@ export type GetSystemContractsOptions = { export async function getSystemContracts({ rootDir, config, -}: GetSystemContractsOptions): Promise { - const solidityFiles = await findSolidityFiles({ rootDir, config }); +}: GetSystemContractsOptions): Promise { + // TODO: get this value from config once multiple namespaces are supported + const multipleNamespaces = false; + const solidityFiles = await findSolidityFiles({ + cwd: rootDir, + pattern: path.join(config.sourceDirectory, "**"), + }); return solidityFiles - .map((file) => ({ - sourcePath: file.filename, - name: file.basename, - })) .filter( (file) => - file.name.endsWith("System") && + file.basename.endsWith("System") && // exclude the base System contract - file.name !== "System" && + file.basename !== "System" && // exclude interfaces - !/^I[A-Z]/.test(file.name), - ); + !/^I[A-Z]/.test(file.basename), + ) + .map((file) => { + const namespaceLabel = (() => { + if (!multipleNamespaces) return config.namespace; + + const relativePath = path.relative(path.join(rootDir, config.sourceDirectory), file.filename); + const [namespacesDir, namespaceDir] = relativePath.split(path.sep); + if (namespacesDir === "namespaces" && namespaceDir) { + return namespaceDir; + } + // TODO: is this too aggressive? will this be problematic for world-modules (systems independent of namespaces)? + // TODO: filter based on excluded systems in config? + throw new Error( + `Expected system file at "${file.filename}" to be in a namespace directory like "${path.join(config.sourceDirectory, "namespaces/{namespace}", relativePath)}"`, + ); + })(); + + return { + sourcePath: file.filename, + namespaceLabel, + systemLabel: file.basename, + }; + }); } diff --git a/packages/world/ts/node/render-solidity/renderWorldInterface.ts b/packages/world/ts/node/render-solidity/renderWorldInterface.ts index 92b3980325..0824b81cc1 100644 --- a/packages/world/ts/node/render-solidity/renderWorldInterface.ts +++ b/packages/world/ts/node/render-solidity/renderWorldInterface.ts @@ -1,34 +1,43 @@ -import { - renderArguments, - renderedSolidityHeader, - renderAbsoluteImports, - renderRelativeImports, - type AbsoluteImportDatum, -} from "@latticexyz/common/codegen"; -import type { RenderWorldOptions } from "./types"; +import { renderArguments, renderedSolidityHeader, renderImports, type ImportDatum } from "@latticexyz/common/codegen"; -export function renderWorldInterface(options: RenderWorldOptions) { - const { interfaceName, storeImportPath, worldImportPath, imports } = options; - const baseImports: AbsoluteImportDatum[] = - interfaceName === "IBaseWorld" +export type RenderWorldOptions = { + /** List of symbols to import, and their file paths */ + imports: ImportDatum[]; + /** Name of the interface to render */ + interfaceName: string; + /** Path for store package imports */ + storeImportPath: string; + /** Path for world package imports */ + worldImportPath: string; +}; + +export function renderWorldInterface({ + interfaceName, + storeImportPath, + worldImportPath, + imports: initialImports, +}: RenderWorldOptions) { + const imports = [ + ...(interfaceName === "IBaseWorld" ? [ { symbol: "IStore", path: `${storeImportPath}/IStore.sol` }, - { symbol: "IWorldKernel", path: `${worldImportPath}IWorldKernel.sol` }, + { symbol: "IWorldKernel", path: `${worldImportPath}/IWorldKernel.sol` }, ] : [ { symbol: "IBaseWorld", - path: `${worldImportPath}codegen/interfaces/IBaseWorld.sol`, + path: `${worldImportPath}/codegen/interfaces/IBaseWorld.sol`, }, - ]; - const importSymbols = [...baseImports, ...imports].map(({ symbol }) => symbol); + ]), + ...initialImports, + ]; + + const importSymbols = imports.map(({ symbol }) => symbol); return ` ${renderedSolidityHeader} - ${renderAbsoluteImports(baseImports)} - - ${renderRelativeImports(imports)} + ${renderImports(imports)} /** * @title ${interfaceName} diff --git a/packages/world/ts/node/render-solidity/types.ts b/packages/world/ts/node/render-solidity/types.ts index ae4317ba6c..d8b9772551 100644 --- a/packages/world/ts/node/render-solidity/types.ts +++ b/packages/world/ts/node/render-solidity/types.ts @@ -1,9 +1,4 @@ -import type { - ImportDatum, - RelativeImportDatum, - ContractInterfaceFunction, - ContractInterfaceError, -} from "@latticexyz/common/codegen"; +import type { ImportDatum, ContractInterfaceFunction, ContractInterfaceError } from "@latticexyz/common/codegen"; export interface RenderSystemInterfaceOptions { /** List of symbols to import, and their file paths */ @@ -13,14 +8,3 @@ export interface RenderSystemInterfaceOptions { functions: ContractInterfaceFunction[]; errors: ContractInterfaceError[]; } - -export interface RenderWorldOptions { - /** List of symbols to import, and their file paths */ - imports: RelativeImportDatum[]; - /** Name of the interface to render */ - interfaceName: string; - /** Path for store package imports */ - storeImportPath: string; - /** Path for world package imports */ - worldImportPath: string; -} diff --git a/packages/world/ts/node/render-solidity/worldgen.ts b/packages/world/ts/node/render-solidity/worldgen.ts index 0ec7be5bd0..76b0ec8944 100644 --- a/packages/world/ts/node/render-solidity/worldgen.ts +++ b/packages/world/ts/node/render-solidity/worldgen.ts @@ -1,6 +1,6 @@ -import fs from "fs"; -import path from "path"; -import { formatAndWriteSolidity, contractToInterface, type RelativeImportDatum } from "@latticexyz/common/codegen"; +import fs from "node:fs/promises"; +import path from "node:path"; +import { formatAndWriteSolidity, contractToInterface, type ImportDatum } from "@latticexyz/common/codegen"; import { renderSystemInterface } from "./renderSystemInterface"; import { renderWorldInterface } from "./renderWorldInterface"; import { World as WorldConfig } from "../../config/v2/output"; @@ -15,63 +15,71 @@ export async function worldgen({ config: WorldConfig; clean?: boolean; }) { - const outDir = path.join(config.sourceDirectory, config.codegen.outputDirectory, config.codegen.worldgenDirectory); + const outDir = path.join( + rootDir, + config.sourceDirectory, + config.codegen.outputDirectory, + config.codegen.worldgenDirectory, + ); if (clean) { - fs.rmSync(outDir, { recursive: true, force: true }); + await fs.rm(outDir, { recursive: true, force: true }); } - const systems = await resolveSystems({ rootDir, config }); + const outputPath = path.join(outDir, config.codegen.worldInterfaceName + ".sol"); - const systemInterfaceImports: RelativeImportDatum[] = []; - for (const system of systems) { - const data = fs.readFileSync(system.sourcePath, "utf8"); - // get external funcions from a contract - const { functions, errors, symbolImports } = contractToInterface(data, system.label); - const imports = symbolImports.map((symbolImport) => { - if (symbolImport.path[0] === ".") { - // relative import - return { - symbol: symbolImport.symbol, - fromPath: path.join(path.dirname(system.sourcePath), symbolImport.path), - usedInPath: outDir, - }; - } else { - // absolute import - return { - symbol: symbolImport.symbol, - path: symbolImport.path, - }; - } - }); - const systemInterfaceName = `I${system.label}`; - const output = renderSystemInterface({ - name: systemInterfaceName, - functionPrefix: system.namespace === "" ? "" : `${system.namespace}__`, - functions, - errors, - imports, - }); - // write to file - const fullOutputPath = path.join(rootDir, outDir, systemInterfaceName + ".sol"); - await formatAndWriteSolidity(output, fullOutputPath, "Generated system interface"); + const systems = (await resolveSystems({ rootDir, config })).map((system) => { + const interfaceName = `I${system.label}`; + return { + ...system, + interfaceName, + interfacePath: path.join(path.dirname(outputPath), `${interfaceName}.sol`), + }; + }); - // prepare imports for IWorld - systemInterfaceImports.push({ - symbol: systemInterfaceName, - fromPath: `${systemInterfaceName}.sol`, - usedInPath: "./", - }); - } + const worldImports = systems.map( + (system): ImportDatum => ({ + symbol: system.interfaceName, + path: "./" + path.relative(path.dirname(outputPath), system.interfacePath), + }), + ); + + await Promise.all( + systems.map(async (system) => { + const data = await fs.readFile(system.sourcePath, "utf8"); + // get external functions from a contract + const { functions, errors, symbolImports } = contractToInterface(data, system.label); + const imports = symbolImports.map( + ({ symbol, path: importPath }): ImportDatum => ({ + symbol, + path: importPath.startsWith(".") + ? "./" + path.relative(outDir, path.join(rootDir, path.dirname(system.sourcePath), importPath)) + : importPath, + }), + ); + const output = renderSystemInterface({ + name: system.interfaceName, + functionPrefix: system.namespace === "" ? "" : `${system.namespace}__`, + functions, + errors, + imports, + }); + // write to file + await formatAndWriteSolidity(output, system.interfacePath, "Generated system interface"); + }), + ); // render IWorld const output = renderWorldInterface({ interfaceName: config.codegen.worldInterfaceName, - imports: systemInterfaceImports, - storeImportPath: config.codegen.storeImportPath, - worldImportPath: config.codegen.worldImportPath, + imports: worldImports, + storeImportPath: config.codegen.storeImportPath.startsWith(".") + ? "./" + path.relative(path.dirname(outputPath), path.join(rootDir, config.codegen.storeImportPath)) + : config.codegen.storeImportPath, + worldImportPath: config.codegen.worldImportPath.startsWith(".") + ? "./" + path.relative(path.dirname(outputPath), path.join(rootDir, config.codegen.worldImportPath)) + : config.codegen.worldImportPath, }); // write to file - const fullOutputPath = path.join(rootDir, outDir, config.codegen.worldInterfaceName + ".sol"); - await formatAndWriteSolidity(output, fullOutputPath, "Generated world interface"); + await formatAndWriteSolidity(output, outputPath, "Generated world interface"); } diff --git a/packages/world/ts/node/resolveSystems.ts b/packages/world/ts/node/resolveSystems.ts index 651cc9e3bf..853590fd48 100644 --- a/packages/world/ts/node/resolveSystems.ts +++ b/packages/world/ts/node/resolveSystems.ts @@ -2,6 +2,7 @@ import { isHex } from "viem"; import { getSystemContracts } from "./getSystemContracts"; import { System, World } from "../config/v2"; import { resolveSystem } from "../config/v2/system"; +import { resolveNamespace } from "@latticexyz/store/config/v2"; export type ResolvedSystem = System & { readonly sourcePath: string; @@ -15,7 +16,7 @@ export async function resolveSystems({ config: World; }): Promise { const systemContracts = await getSystemContracts({ rootDir, config }); - const contractNames = systemContracts.map((contract) => contract.name); + const contractNames = systemContracts.map((contract) => contract.systemLabel); // validate every system in config refers to an existing system contract const missingSystems = Object.keys(config.systems).filter((systemLabel) => !contractNames.includes(systemLabel)); @@ -26,7 +27,12 @@ export async function resolveSystems({ const systems = systemContracts .map((contract): ResolvedSystem => { const systemConfig = - config.systems[contract.name] ?? resolveSystem({ label: contract.name, namespace: config.namespace }); + config.systems[contract.systemLabel] ?? + // TODO: move this to `resolveNamespace({ systems: ... })` + resolveSystem({ + label: contract.systemLabel, + namespace: resolveNamespace({ label: contract.namespaceLabel }).namespace, + }); return { ...systemConfig, sourcePath: contract.sourcePath, diff --git a/packages/world/ts/scripts/generate-test-tables.ts b/packages/world/ts/scripts/generate-test-tables.ts index eb382c1c82..8292cbe737 100644 --- a/packages/world/ts/scripts/generate-test-tables.ts +++ b/packages/world/ts/scripts/generate-test-tables.ts @@ -3,10 +3,10 @@ import { defineWorld } from "../config/v2/world"; import { fileURLToPath } from "node:url"; import path from "node:path"; -const configPath = fileURLToPath(import.meta.url); +const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const config = defineWorld({ - sourceDirectory: "../../test", + sourceDirectory: "test", tables: { Bool: { schema: { @@ -40,4 +40,4 @@ const config = defineWorld({ }, }); -await tablegen({ rootDir: path.dirname(configPath), config }); +await tablegen({ rootDir, config }); diff --git a/templates/phaser/packages/contracts/src/codegen/world/IWorld.sol b/templates/phaser/packages/contracts/src/codegen/world/IWorld.sol index 34615b56ae..3cec7bd18d 100644 --- a/templates/phaser/packages/contracts/src/codegen/world/IWorld.sol +++ b/templates/phaser/packages/contracts/src/codegen/world/IWorld.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.24; /* Autogenerated file. Do not edit manually. */ import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; - import { IIncrementSystem } from "./IIncrementSystem.sol"; /** diff --git a/templates/react-ecs/packages/contracts/src/codegen/world/IWorld.sol b/templates/react-ecs/packages/contracts/src/codegen/world/IWorld.sol index 34615b56ae..3cec7bd18d 100644 --- a/templates/react-ecs/packages/contracts/src/codegen/world/IWorld.sol +++ b/templates/react-ecs/packages/contracts/src/codegen/world/IWorld.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.24; /* Autogenerated file. Do not edit manually. */ import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; - import { IIncrementSystem } from "./IIncrementSystem.sol"; /** diff --git a/templates/react/packages/contracts/src/codegen/world/IWorld.sol b/templates/react/packages/contracts/src/codegen/world/IWorld.sol index b3848a4577..71352c40ca 100644 --- a/templates/react/packages/contracts/src/codegen/world/IWorld.sol +++ b/templates/react/packages/contracts/src/codegen/world/IWorld.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.24; /* Autogenerated file. Do not edit manually. */ import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; - import { ITasksSystem } from "./ITasksSystem.sol"; /** diff --git a/templates/threejs/packages/contracts/src/codegen/world/IWorld.sol b/templates/threejs/packages/contracts/src/codegen/world/IWorld.sol index d27f33eb9a..368699df7d 100644 --- a/templates/threejs/packages/contracts/src/codegen/world/IWorld.sol +++ b/templates/threejs/packages/contracts/src/codegen/world/IWorld.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.24; /* Autogenerated file. Do not edit manually. */ import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; - import { IMoveSystem } from "./IMoveSystem.sol"; /** diff --git a/templates/vanilla/packages/contracts/src/codegen/world/IWorld.sol b/templates/vanilla/packages/contracts/src/codegen/world/IWorld.sol index 34615b56ae..3cec7bd18d 100644 --- a/templates/vanilla/packages/contracts/src/codegen/world/IWorld.sol +++ b/templates/vanilla/packages/contracts/src/codegen/world/IWorld.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.24; /* Autogenerated file. Do not edit manually. */ import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; - import { IIncrementSystem } from "./IIncrementSystem.sol"; /** diff --git a/test/mock-game-contracts/src/codegen/world/IWorld.sol b/test/mock-game-contracts/src/codegen/world/IWorld.sol index d27f33eb9a..368699df7d 100644 --- a/test/mock-game-contracts/src/codegen/world/IWorld.sol +++ b/test/mock-game-contracts/src/codegen/world/IWorld.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.24; /* Autogenerated file. Do not edit manually. */ import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; - import { IMoveSystem } from "./IMoveSystem.sol"; /**