Skip to content

Commit

Permalink
refactor(world): update worldgen with namespaces output (#2974)
Browse files Browse the repository at this point in the history
  • Loading branch information
holic authored Jul 26, 2024
1 parent 69eb63b commit 570086e
Show file tree
Hide file tree
Showing 31 changed files with 159 additions and 213 deletions.
5 changes: 5 additions & 0 deletions .changeset/four-bugs-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/world": patch
---

Refactored worldgen in preparation for multiple namespaces.
1 change: 0 additions & 1 deletion e2e/packages/contracts/src/codegen/world/IWorld.sol

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion examples/multiple-namespaces/src/codegen/world/IWorld.sol

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
54 changes: 1 addition & 53 deletions packages/common/src/codegen/render-solidity/common.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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<string, Set<string>>();
for (const { symbol, path } of imports) {
Expand Down
12 changes: 2 additions & 10 deletions packages/common/src/codegen/render-solidity/types.ts
Original file line number Diff line number Diff line change
@@ -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 */
Expand Down
1 change: 0 additions & 1 deletion packages/common/src/codegen/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ export * from "./extractUserTypes";
export * from "./format";
export * from "./formatAndWrite";
export * from "./loadUserTypesFile";
export * from "./posixPath";
8 changes: 0 additions & 8 deletions packages/common/src/codegen/utils/posixPath.ts

This file was deleted.

9 changes: 2 additions & 7 deletions packages/store/ts/codegen/getTableOptions.ts
Original file line number Diff line number Diff line change
@@ -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) */
Expand Down
4 changes: 2 additions & 2 deletions packages/store/ts/codegen/renderTableIndex.ts
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -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))}";`;
})}
`;
}
1 change: 1 addition & 0 deletions packages/store/ts/config/v2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export * from "./defaults";
export * from "./codegen";
export * from "./enums";
export * from "./userTypes";
export * from "./namespace";
export * from "./namespacedTables";
2 changes: 1 addition & 1 deletion packages/store/ts/config/v2/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export type resolveNamespace<input, scope extends Scope = AbiTypeScope> = input

export function resolveNamespace<const input extends NamespaceInput, scope extends Scope = AbiTypeScope>(
input: input,
scope: scope,
scope: scope = AbiTypeScope as never,
): resolveNamespace<input, scope> {
const label = input.label;
const namespace = input.namespace ?? label.slice(0, 14);
Expand Down
2 changes: 1 addition & 1 deletion packages/world/mud.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { defineWorld } from "./ts/config/v2/world";

export default defineWorld({
codegen: {
worldImportPath: "../../",
worldImportPath: "./src",
worldgenDirectory: "interfaces",
worldInterfaceName: "IBaseWorld",
},
Expand Down
1 change: 0 additions & 1 deletion packages/world/src/codegen/interfaces/IBaseWorld.sol

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/world/ts/config/v2/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 2 additions & 8 deletions packages/world/ts/config/v2/input.ts
Original file line number Diff line number Diff line change
@@ -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 = {
/**
Expand Down Expand Up @@ -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<Codegen>;

export type WorldInput = show<
StoreInput & {
Expand Down
4 changes: 3 additions & 1 deletion packages/world/ts/config/v2/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
8 changes: 2 additions & 6 deletions packages/world/ts/node/findSolidityFiles.ts
Original file line number Diff line number Diff line change
@@ -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"),
Expand Down
49 changes: 37 additions & 12 deletions packages/world/ts/node/getSystemContracts.ts
Original file line number Diff line number Diff line change
@@ -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 = {
Expand All @@ -14,20 +16,43 @@ export type GetSystemContractsOptions = {
export async function getSystemContracts({
rootDir,
config,
}: GetSystemContractsOptions): Promise<readonly SolidityContract[]> {
const solidityFiles = await findSolidityFiles({ rootDir, config });
}: GetSystemContractsOptions): Promise<readonly SystemContract[]> {
// 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,
};
});
}
Loading

0 comments on commit 570086e

Please sign in to comment.