diff --git a/packages/config/package.json b/packages/config/package.json
index 364ad15bea..b3669839bd 100644
--- a/packages/config/package.json
+++ b/packages/config/package.json
@@ -38,10 +38,12 @@
"esbuild": "^0.17.15",
"ethers": "^5.7.2",
"find-up": "^6.3.0",
+ "tapable": "^2.2.1",
"zod": "^3.21.4",
"zod-validation-error": "^1.3.0"
},
"devDependencies": {
+ "@types/tapable": "^2.2.3",
"tsup": "^6.7.0"
},
"gitHead": "914a1e0ae4a573d685841ca2ea921435057deb8f"
diff --git a/packages/config/src/library/core.ts b/packages/config/src/library/core.ts
index e9e1fc990a..b42f2084e0 100644
--- a/packages/config/src/library/core.ts
+++ b/packages/config/src/library/core.ts
@@ -1,12 +1,17 @@
import { MergeReturnType, UnionToIntersection } from "@latticexyz/common/type-utils";
-import { Plugins } from "./types";
+import { MudPlugin, Plugins } from "./types";
// Helper type to infer the input types from a plugins config as union (InputA | InputB)
-type PluginsInput
= Parameters
[0];
+type PluginsInput
= Parameters
[1];
// Infer the plugin input types as intersection (InputA & InputB)
export type MergedPluginsInput
= UnionToIntersection>;
+// Helper to strongly type a plugin definition
+export function defineMUDPlugin
(plugin: P): P {
+ return plugin;
+}
+
/**
* Helper function to sequentially apply `expandConfig` of each plugin and strongly type the result.
*/
@@ -16,7 +21,7 @@ export function mudCoreConfig
{
let expanded = config as any;
for (const { expandConfig } of Object.values(plugins)) {
- expanded = { ...expanded, ...expandConfig(config) };
+ expanded = { ...expanded, ...expandConfig(plugins, config) };
}
return expanded;
}
diff --git a/packages/config/src/library/types.ts b/packages/config/src/library/types.ts
index 7acbc65db0..7eb629cc87 100644
--- a/packages/config/src/library/types.ts
+++ b/packages/config/src/library/types.ts
@@ -1,11 +1,14 @@
+import { Hook } from "tapable";
+
/*
* Every plugin defines an `Input`, an `Expanded` type,
* and a `expandConfig` function to map from `Input` to `Expanded`.
- * Do distinguish plugins from each other in TypeScript, they also define a unique `id` string.
+ * To distinguish plugins from each other in TypeScript, they also define a unique `id` string.
*/
export interface MudPlugin {
id: string;
- expandConfig: (config: C) => Expanded;
+ expandConfig: (plugins: Plugins, config: C) => Expanded;
+ hooks: Record>;
}
/**
@@ -13,7 +16,7 @@ export interface MudPlugin {
* The config is later expanded by calling the expandConfig method of each
* plugin in order of appearance in the map. We use a map instead of an array,
* because it makes it easier to type check for the existence of expected
- * plugins in the map. (Object keys order is guaranteed since ES2015, see
+ * plugins in the map. Object keys order is guaranteed since ES2015, see
* https://www.stefanjudis.com/today-i-learned/property-order-is-predictable-in-javascript-objects-since-es2015/
*/
export type Config = { plugins: Plugins };
diff --git a/packages/store/package.json b/packages/store/package.json
index 9abca9b640..5e15c5828a 100644
--- a/packages/store/package.json
+++ b/packages/store/package.json
@@ -53,6 +53,7 @@
"@latticexyz/config": "workspace:*",
"@latticexyz/schema-type": "workspace:*",
"ethers": "^5.7.2",
+ "tapable": "^2.2.1",
"zod": "^3.21.4"
},
"devDependencies": {
@@ -60,6 +61,7 @@
"@types/ejs": "^3.1.1",
"@types/mocha": "^9.1.1",
"@types/node": "^18.15.11",
+ "@types/tapable": "^2.2.3",
"ds-test": "https://github.com/dapphub/ds-test.git#c9ce3f25bde29fc5eb9901842bf02850dfd2d084",
"ejs": "^3.1.8",
"forge-std": "https://github.com/foundry-rs/forge-std.git#b4f121555729b3afb3c5ffccb62ff4b6e2818fd3",
diff --git a/packages/store/ts/library/config/mudConfig.test-d.ts b/packages/store/ts/library/config/mudConfig.test-d.ts
index a1cf0bb45b..e54a4dd1fc 100644
--- a/packages/store/ts/library/config/mudConfig.test-d.ts
+++ b/packages/store/ts/library/config/mudConfig.test-d.ts
@@ -1,17 +1,11 @@
import { describe, expectTypeOf } from "vitest";
-import { mudConfig } from ".";
+import { mudConfig, TABLE_DEFAULTS } from ".";
import { storePlugin } from "./plugin";
-/*
-describe("mudConfig", () => {
- // Test possible inference confusion.
- // This would fail if you remove `AsDependent` from `MUDUserConfig`
- expectTypeOf<
- ReturnType<
- typeof mudConfig<
+type AutoExpandedConfig = ReturnType<
+ typeof mudConfig<
{ storePlugin: typeof storePlugin },
{
- plugins: { storePlugin: typeof storePlugin },
tables: {
Table1: {
keySchema: {
@@ -33,32 +27,53 @@ describe("mudConfig", () => {
};
}
>
- >
- >().toEqualTypeOf<{
- plugins: { storePlugin: typeof storePlugin },
- enums: {
- Enum1: ["E1"];
- Enum2: ["E1"];
- };
- tables: {
- Table1: {
- keySchema: {
- a: "Enum1";
- };
- schema: {
- b: "Enum2";
- };
+>;
+
+type ManuallyExpandedConfig = {
+ enums: {
+ Enum1: ["E1"];
+ Enum2: ["E1"];
+ };
+ tables: {
+ Table1: {
+ keySchema: {
+ a: "Enum1";
};
- Table2: {
- schema: {
- a: "uint32";
- };
+ schema: {
+ b: "Enum2";
};
+ directory: typeof TABLE_DEFAULTS.directory;
+ name: "Table1";
+ tableIdArgument: typeof TABLE_DEFAULTS.tableIdArgument;
+ storeArgument: typeof TABLE_DEFAULTS.storeArgument;
+ dataStruct: boolean;
+ ephemeral: typeof TABLE_DEFAULTS.ephemeral;
};
- namespace: "";
- storeImportPath: "@latticexyz/store/src/";
- userTypesPath: "Types";
- codegenDirectory: "codegen";
- }>();
+ Table2: {
+ schema: {
+ a: "uint32";
+ };
+ directory: typeof TABLE_DEFAULTS.directory;
+ name: "Table2";
+ tableIdArgument: typeof TABLE_DEFAULTS.tableIdArgument;
+ storeArgument: typeof TABLE_DEFAULTS.storeArgument;
+ dataStruct: boolean;
+ keySchema: typeof TABLE_DEFAULTS.keySchema;
+ ephemeral: typeof TABLE_DEFAULTS.ephemeral;
+ };
+ };
+ namespace: "";
+ storeImportPath: "@latticexyz/store/src/";
+ userTypesPath: "Types";
+ codegenDirectory: "codegen";
+};
+
+const _test1: AutoExpandedConfig = {} as ManuallyExpandedConfig;
+// TODO fix weak types
+const _test2: ManuallyExpandedConfig = {} as AutoExpandedConfig;
+
+describe("mudConfig", () => {
+ // Test possible inference confusion.
+ // This would fail if you remove `AsDependent` from `MUDUserConfig`
+ expectTypeOf().toEqualTypeOf();
});
-*/
diff --git a/packages/store/ts/library/config/plugin.ts b/packages/store/ts/library/config/plugin.ts
index 5d5cbcf260..845d109a9d 100644
--- a/packages/store/ts/library/config/plugin.ts
+++ b/packages/store/ts/library/config/plugin.ts
@@ -1,8 +1,9 @@
-import { fromZodErrorCustom, MudPlugin } from "@latticexyz/config";
+import { defineMUDPlugin, fromZodErrorCustom, Plugins } from "@latticexyz/config";
+import { SyncHook } from "tapable";
import { ZodError } from "zod";
import { ExpandStoreUserConfig, StoreUserConfig, zPluginStoreConfig } from "./storeConfig";
-export function expandConfig(config: C) {
+export function expandConfig(plugins: Plugins, config: C) {
// This function gets called within mudConfig.
// The call order of config extenders depends on the order of their imports.
// Any config validation and transformation should be placed here.
@@ -17,7 +18,11 @@ export function expandConfig(config: C) {
}
}
-export const storePlugin = {
+export const storePlugin = defineMUDPlugin({
id: "mud-store-plugin",
expandConfig,
-} as const satisfies MudPlugin;
+ hooks: {
+ preTablegen: new SyncHook(["mudConfig"]),
+ postTablegen: new SyncHook(["mudConfig"]),
+ },
+});
diff --git a/packages/store/ts/library/config/storeConfig.test-d.ts b/packages/store/ts/library/config/storeConfig.test-d.ts
index 5f7e020b9d..836e0f3b5d 100644
--- a/packages/store/ts/library/config/storeConfig.test-d.ts
+++ b/packages/store/ts/library/config/storeConfig.test-d.ts
@@ -1,15 +1,22 @@
+import { MergedPluginsInput } from "@latticexyz/config";
import { describe, expectTypeOf } from "vitest";
import { z } from "zod";
-import { zStoreConfig, MUDUserConfig } from "./storeConfig";
-/*
+import { storePlugin } from "./plugin";
+import { zStoreConfig, StoreUserConfig } from "./storeConfig";
+
describe("StoreUserConfig", () => {
+ // Check that the plugin uses the correct type
+ expectTypeOf<
+ Omit, "plugins">
+ >().toEqualTypeOf();
+
// Typecheck manual interfaces against zod
- expectTypeOf().toEqualTypeOf>();
+ expectTypeOf().toEqualTypeOf>();
// type equality isn't deep for optionals
- expectTypeOf().toEqualTypeOf["tables"][string]>();
- expectTypeOf[string]>().toEqualTypeOf<
+ expectTypeOf().toEqualTypeOf["tables"][string]>();
+ expectTypeOf[string]>().toEqualTypeOf<
NonNullable>["enums"]>[string]
>();
// TODO If more nested schemas are added, provide separate tests for them
-});*/
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index be8b39f32b..8db78ad3a0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,4 +1,4 @@
-lockfileVersion: '6.1'
+lockfileVersion: '6.0'
settings:
autoInstallPeers: true
@@ -238,6 +238,9 @@ importers:
find-up:
specifier: ^6.3.0
version: 6.3.0
+ tapable:
+ specifier: ^2.2.1
+ version: 2.2.1
zod:
specifier: ^3.21.4
version: 3.21.4
@@ -245,6 +248,9 @@ importers:
specifier: ^1.3.0
version: 1.3.0(zod@3.21.4)
devDependencies:
+ '@types/tapable':
+ specifier: ^2.2.3
+ version: 2.2.3
tsup:
specifier: ^6.7.0
version: 6.7.0(typescript@4.9.5)
@@ -928,6 +934,9 @@ importers:
ethers:
specifier: ^5.7.2
version: 5.7.2
+ tapable:
+ specifier: ^2.2.1
+ version: 2.2.1
zod:
specifier: ^3.21.4
version: 3.21.4
@@ -944,6 +953,9 @@ importers:
'@types/node':
specifier: ^18.15.11
version: 18.15.11
+ '@types/tapable':
+ specifier: ^2.2.3
+ version: 2.2.3
ds-test:
specifier: https://github.com/dapphub/ds-test.git#c9ce3f25bde29fc5eb9901842bf02850dfd2d084
version: github.com/dapphub/ds-test/c9ce3f25bde29fc5eb9901842bf02850dfd2d084
@@ -3795,6 +3807,12 @@ packages:
resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==}
dev: true
+ /@types/tapable@2.2.3:
+ resolution: {integrity: sha512-WA0xhgs1wenECj1psxojjYOIc1Zkn+4gNtuWmF+dcFs5UfsQa8WhWs+8WG4D2RuaX7DufNPU+KsOEF7KqlByuw==}
+ dependencies:
+ tapable: 2.2.1
+ dev: true
+
/@types/throttle-debounce@5.0.0:
resolution: {integrity: sha512-Pb7k35iCGFcGPECoNE4DYp3Oyf2xcTd3FbFQxXUI9hEYKUl6YX+KLf7HrBmgVcD05nl50LIH6i+80js4iYmWbw==}
dev: true
@@ -13542,6 +13560,10 @@ packages:
- ts-node
dev: true
+ /tapable@2.2.1:
+ resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+ engines: {node: '>=6'}
+
/tar-stream@1.6.2:
resolution: {integrity: sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==}
engines: {node: '>= 0.8.0'}