diff --git a/examples/example-vite-react-sdk/src/App.tsx b/examples/example-vite-react-sdk/src/App.tsx index 5bc3a868..b33fd68b 100644 --- a/examples/example-vite-react-sdk/src/App.tsx +++ b/examples/example-vite-react-sdk/src/App.tsx @@ -1,11 +1,12 @@ import { useEffect, useMemo } from "react"; import { SDK, createDojoStore } from "@dojoengine/sdk"; -import { Schema } from "./bindings.ts"; - -import { useDojo } from "./useDojo.tsx"; import { getEntityIdFromKeys } from "@dojoengine/utils"; import { addAddressPadding } from "starknet"; +import { Models, Schema } from "./bindings.ts"; +import { useDojo } from "./useDojo.tsx"; +import useModel from "./useModel.tsx"; + export const useDojoStore = createDojoStore(); function App({ db }: { db: SDK }) { @@ -109,13 +110,8 @@ function App({ db }: { db: SDK }) { fetchEntities(); }, [db, account?.account.address]); - const position = useMemo(() => { - return entities[entityId]?.models?.dojo_starter.Position; - }, [entities]); - - const moves = useMemo(() => { - return entities[entityId]?.models?.dojo_starter.Moves; - }, [entities]); + const moves = useModel(entityId, Models.Moves); + const position = useModel(entityId, Models.Position); return (
diff --git a/examples/example-vite-react-sdk/src/bindings.ts b/examples/example-vite-react-sdk/src/bindings.ts index ffb7c58b..eec0eb34 100644 --- a/examples/example-vite-react-sdk/src/bindings.ts +++ b/examples/example-vite-react-sdk/src/bindings.ts @@ -39,6 +39,12 @@ type Schema = { }; }; +enum Models { + Moves = "dojo_starter-Moves", + DirectionsAvailable = "dojo_starter-DirectionsAvailable", + Position = "dojo_starter-Position", +} + const schema: Schema = { dojo_starter: { Moves: { @@ -62,4 +68,4 @@ const schema: Schema = { }; export type { Schema, Moves, DirectionsAvailable, Position, Vec2 }; -export { Direction, schema }; +export { Direction, schema, Models }; diff --git a/examples/example-vite-react-sdk/src/main.tsx b/examples/example-vite-react-sdk/src/main.tsx index 2bd91d8a..e80f690e 100644 --- a/examples/example-vite-react-sdk/src/main.tsx +++ b/examples/example-vite-react-sdk/src/main.tsx @@ -8,7 +8,7 @@ import { init } from "@dojoengine/sdk"; import { Schema, schema } from "./bindings.ts"; import { dojoConfig } from "../dojoConfig.ts"; import { DojoContextProvider } from "./DojoContext.tsx"; -import { setupBurner } from "./setupBurner.tsx"; +import { setupBurnerManager } from "@dojoengine/create-burner"; async function main() { const db = await init( @@ -20,20 +20,20 @@ async function main() { worldAddress: dojoConfig.manifest.world.address, }, domain: { - name: "Example", + name: "WORLD_NAME", version: "1.0", - chainId: "your-chain-id", + chainId: "KATANA", revision: "1", }, }, schema ); - const burnerManager = await setupBurner(dojoConfig); - createRoot(document.getElementById("root")!).render( - + diff --git a/examples/example-vite-react-sdk/src/useModel.tsx b/examples/example-vite-react-sdk/src/useModel.tsx new file mode 100644 index 00000000..228e4cc7 --- /dev/null +++ b/examples/example-vite-react-sdk/src/useModel.tsx @@ -0,0 +1,28 @@ +import { useDojoStore } from "./App"; +import { Schema } from "./bindings"; + +/** + * Custom hook to retrieve a specific model for a given entityId within a specified namespace. + * + * @param entityId - The ID of the entity. + * @param model - The model to retrieve, specified as a string in the format "namespace-modelName". + * @returns The model structure if found, otherwise undefined. + */ +function useModel( + entityId: string, + model: `${N}-${M}` +): Schema[N][M] | undefined { + const [namespace, modelName] = model.split("-") as [N, M]; + + // Select only the specific model data for the given entityId + const modelData = useDojoStore( + (state) => + state.entities[entityId]?.models?.[namespace]?.[modelName] as + | Schema[N][M] + | undefined + ); + + return modelData; +} + +export default useModel; diff --git a/media/dojo-mark-full-dark.svg b/media/dojo-mark-full-dark.svg new file mode 100644 index 00000000..5bc5b839 --- /dev/null +++ b/media/dojo-mark-full-dark.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/package.json b/package.json index 2083ac89..15833a19 100644 --- a/package.json +++ b/package.json @@ -10,19 +10,16 @@ "docs": "npx typedoc --out docs", "prepare": "husky install" }, - "dependencies": { - "@commitlint/cli": "^18.4.4", - "@commitlint/config-conventional": "^18.4.4", - "@ianvs/prettier-plugin-sort-imports": "^4.3.1", - "react": "^18.2.0" - }, "devDependencies": { - "husky": "^9.0.11", + "husky": "^9.1.6", "lerna": "^8.1.5", "prettier": "^3.3.3", "tsup": "^8.1.0", "typedoc": "^0.26.7", "typedoc-material-theme": "^1.1.0", - "typedoc-plugin-coverage": "^3.3.0" + "typedoc-plugin-coverage": "^3.3.0", + "@commitlint/cli": "^18.4.4", + "@commitlint/config-conventional": "^18.4.4", + "@ianvs/prettier-plugin-sort-imports": "^4.3.1" } } diff --git a/packages/create-burner/src/manager/index.ts b/packages/create-burner/src/manager/index.ts index aa6c25ad..a45299ce 100644 --- a/packages/create-burner/src/manager/index.ts +++ b/packages/create-burner/src/manager/index.ts @@ -1,3 +1,4 @@ export { BurnerManager } from "./burnerManager"; export { PredeployedManager } from "./predeployedManager"; export { prefundAccount } from "./prefundAccount"; +export { setupBurnerManager } from "./setupBurnerManager"; diff --git a/examples/example-vite-react-sdk/src/setupBurner.tsx b/packages/create-burner/src/manager/setupBurnerManager.ts similarity index 86% rename from examples/example-vite-react-sdk/src/setupBurner.tsx rename to packages/create-burner/src/manager/setupBurnerManager.ts index a5d8db33..650e59ad 100644 --- a/examples/example-vite-react-sdk/src/setupBurner.tsx +++ b/packages/create-burner/src/manager/setupBurnerManager.ts @@ -1,8 +1,8 @@ import { DojoConfig } from "@dojoengine/core"; -import { BurnerManager } from "@dojoengine/create-burner"; import { Account, RpcProvider } from "starknet"; +import { BurnerManager } from ".."; -export const setupBurner = async (config: DojoConfig) => { +export const setupBurnerManager = async (config: DojoConfig) => { const burnerManager = new BurnerManager({ masterAccount: new Account( { diff --git a/packages/create-dojo/bin/index.d.ts b/packages/create-dojo/bin/index.d.ts deleted file mode 100644 index cb0ff5c3..00000000 --- a/packages/create-dojo/bin/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/packages/create-dojo/bin/index.js b/packages/create-dojo/bin/index.js deleted file mode 100644 index bac2619b..00000000 --- a/packages/create-dojo/bin/index.js +++ /dev/null @@ -1,83 +0,0 @@ -// src/commands/start.ts -import { Command } from "commander"; -import path from "path"; -import { promises as fs } from "fs"; -import { execSync } from "child_process"; -import prompts from "prompts"; -var repos = [ - { name: "client", url: "https://github.com/example/client-repo.git" }, - { name: "contracts", url: "https://github.com/example/contracts-repo.git" }, -]; -async function init(projectName, cwd) { - const projectPath = path.join(cwd, projectName); - await fs.mkdir(projectPath, { recursive: true }); - for (const repo of repos) { - console.log(`Cloning ${repo.name} repository...`); - execSync(`git clone ${repo.url} ${path.join(projectPath, repo.name)}`, { - stdio: "inherit", - }); - } - const readmePath = path.join(projectPath, "README.md"); - const readmeContent = `# ${projectName} - -This project contains: -- Client -- Contracts`; - await fs.writeFile(readmePath, readmeContent); - console.log(`Project initialized at ${projectPath}`); -} -var start = new Command() - .name("start") - .description( - "initialize a new project with client and contracts repositories" - ) - .option("-c, --cwd ", "the working directory", process.cwd()) - .action(async (options) => { - try { - const cwd = path.resolve(options.cwd); - const response = await prompts({ - type: "text", - name: "projectName", - message: "What would you like to name your project?", - }); - if (!response.projectName) { - console.error("Project name is required."); - process.exit(1); - } - await init(response.projectName, cwd); - console.log("Initialization complete"); - } catch (error) { - console.error("An error occurred:", error); - process.exit(1); - } - }); - -// src/index.ts -import { Command as Command2 } from "commander"; - -// src/utils/get-package-info.ts -import path2 from "path"; -import fs2 from "fs-extra"; -function getPackageInfo() { - const packageJsonPath = path2.join("package.json"); - return fs2.readJSONSync(packageJsonPath); -} - -// src/index.ts -process.on("SIGINT", () => process.exit(0)); -process.on("SIGTERM", () => process.exit(0)); -async function main() { - const packageInfo = await getPackageInfo(); - const program = new Command2() - .name("@dojoengine") - .description("install a dojoc client") - .version( - packageInfo.version || "1.0.0", - "-v, --version", - "display the version number" - ); - program.addCommand(start); - program.parse(); -} -main(); -//# sourceMappingURL=index.js.map diff --git a/packages/create-dojo/bin/index.js.map b/packages/create-dojo/bin/index.js.map deleted file mode 100644 index aacf2339..00000000 --- a/packages/create-dojo/bin/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../src/commands/start.ts","../src/index.ts","../src/utils/get-package-info.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport path from \"path\";\nimport { promises as fs } from \"fs\";\nimport { execSync } from \"child_process\";\nimport prompts from \"prompts\";\n\nconst repos = [\n { name: \"client\", url: \"https://github.com/example/client-repo.git\" },\n { name: \"contracts\", url: \"https://github.com/example/contracts-repo.git\" },\n];\n\nasync function init(projectName: string, cwd: string) {\n const projectPath = path.join(cwd, projectName);\n\n // Create project directory\n await fs.mkdir(projectPath, { recursive: true });\n\n // Clone repositories\n for (const repo of repos) {\n console.log(`Cloning ${repo.name} repository...`);\n execSync(`git clone ${repo.url} ${path.join(projectPath, repo.name)}`, {\n stdio: \"inherit\",\n });\n }\n\n // Create README\n const readmePath = path.join(projectPath, \"README.md\");\n const readmeContent = `# ${projectName}\\n\\nThis project contains:\\n- Client\\n- Contracts`;\n await fs.writeFile(readmePath, readmeContent);\n\n console.log(`Project initialized at ${projectPath}`);\n}\n\nexport const start = new Command()\n .name(\"start\")\n .description(\"initialize a new project with client and contracts repositories\")\n .option(\"-c, --cwd \", \"the working directory\", process.cwd())\n .action(async (options) => {\n try {\n const cwd = path.resolve(options.cwd);\n\n // Prompt for project name\n const response = await prompts({\n type: \"text\",\n name: \"projectName\",\n message: \"What would you like to name your project?\",\n });\n\n if (!response.projectName) {\n console.error(\"Project name is required.\");\n process.exit(1);\n }\n\n await init(response.projectName, cwd);\n console.log(\"Initialization complete\");\n } catch (error) {\n console.error(\"An error occurred:\", error);\n process.exit(1);\n }\n });\n","\n// #!/usr/bin/env node\nimport { start } from \"./commands/start\"\n\nimport { Command } from \"commander\"\n\nimport { getPackageInfo } from \"./utils/get-package-info\"\n\nprocess.on(\"SIGINT\", () => process.exit(0))\nprocess.on(\"SIGTERM\", () => process.exit(0))\n\nasync function main() {\n const packageInfo = await getPackageInfo()\n\n const program = new Command()\n .name(\"@dojoengine\")\n .description(\"install a dojoc client\")\n .version(\n packageInfo.version || \"1.0.0\",\n \"-v, --version\",\n \"display the version number\"\n )\n\n program.addCommand(start)\n\n program.parse()\n}\n\nmain()\n\n// import spawn from \"cross-spawn\";\n// import * as fs from \"fs\";\n// import https from \"https\";\n// import path from \"path\";\n\n// import { input, select } from \"@inquirer/prompts\";\n\n// const templates = [\n// {\n// value: \"react-app\",\n// description: \"React app using Dojo\",\n// },\n// {\n// value: \"react-phaser-example\",\n// description: \"React/Phaser app using Dojo\",\n// },\n// {\n// value: \"react-pwa-app\",\n// description: \"React Progressive Web Apps using Dojo\",\n// },\n// {\n// value: \"react-threejs\",\n// description: \"React Threejs using Dojo\",\n// },\n// ];\n\n// run();\n\n// async function run() {\n// try {\n// const { template, projectName } = await prompt();\n\n// // Create the main project directory\n// const projectPath = path.join(process.cwd(), projectName);\n// fs.mkdirSync(projectPath, { recursive: true });\n\n// // Create client and dojo-starter directories inside the main project directory\n// const clientPath = path.join(projectPath, 'client');\n// const dojoStarterPath = path.join(projectPath, 'dojo-starter');\n// fs.mkdirSync(clientPath, { recursive: true });\n// fs.mkdirSync(dojoStarterPath, { recursive: true });\n\n// // clone template using degit into client directory\n// console.log(`Downloading ${template} into client directory...`);\n// spawn.sync(\"npx\", [\n// \"degit\",\n// `dojoengine/dojo.js/examples/react/${template}`,\n// clientPath, // Cloning directly into the client directory\n// ]);\n\n// // Ensure the client directory exists before rewriting package.json\n// if (!fs.existsSync(clientPath)) {\n// throw new Error(`Client directory not found at ${clientPath}`);\n// }\n\n// // rewrite package.json in client directory\n// await rewritePackageJson(projectName);\n\n// // clone dojo-starter into the dojo-starter directory\n// console.log(`Downloading dojo-starter...`);\n// spawn.sync(\"npx\", [\"degit\", `dojoengine/dojo-starter`, dojoStarterPath]);\n \n// console.log(\"Congrats! Your new project has been set up successfully.\\n\");\n// console.log(`Navigate into your project directory with:\\n cd ${projectName}\\n`);\n// console.log(\"You can then build the starter and run the client.\\n\");\n// console.log(\"For detailed instructions, follow the README here:\\n\");\n\n// console.log('https://book.dojoengine.org/cairo/hello-dojo');\n\n// } catch (e) {\n// console.error(`Error: ${e}`);\n// }\n// }\n\n// async function rewritePackageJson(projectName: string) {\n// // The package.json is expected to be in the 'client' subdirectory\n// const clientPath = path.join(process.cwd(), projectName, 'client');\n// process.chdir(clientPath);\n\n// const packageJsonPath = path.join(\"package.json\");\n// // Check if package.json exists before reading it\n// if (!fs.existsSync(packageJsonPath)) {\n// throw new Error(`package.json not found in ${clientPath}`);\n// }\n// const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, \"utf-8\"));\n// const latestVersion = await getLatestVersion();\n\n// // rename using projectName\n// packageJson.name = projectName;\n\n// // rewrite all link:dojo-packages/packages/... with latest version\n// for (let dep of Object.keys(packageJson.dependencies)) {\n// if (\n// dep.startsWith(\"@dojoengine\") &&\n// packageJson.dependencies[dep].startsWith(\"workspace:\")\n// ) {\n// packageJson.dependencies[dep] = latestVersion;\n// }\n// }\n\n// fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));\n// }\n\n// async function prompt(): Promise<{ template: string; projectName: string }> {\n// const template = await select({\n// message: \"Select a template\",\n// choices: templates,\n// });\n\n// const projectName = await input({\n// message: \"Project name \",\n// validate: (input: string) => {\n// if (/^([A-Za-z\\-\\_\\d])+$/.test(input)) return true;\n// else\n// return \"Project name may only include letters, numbers, underscores and hashes.\";\n// },\n// default: template,\n// });\n\n// return { template, projectName };\n// }\n\n// async function getLatestVersion(): Promise {\n// return new Promise((resolve, reject) => {\n// https\n// .get(\n// \"https://registry.npmjs.org/-/package/@dojoengine/core/dist-tags\",\n// (res) => {\n// if (res.statusCode === 200) {\n// let body = \"\";\n// res.on(\"data\", (data) => (body += data));\n// res.on(\"end\", () => {\n// resolve(JSON.parse(body).latest);\n// });\n// } else {\n// reject();\n// }\n// }\n// )\n// .on(\"error\", () => {\n// reject();\n// });\n// });\n// }\n","import path from \"path\"\nimport fs from \"fs-extra\"\nimport { type PackageJson } from \"type-fest\"\n\nexport function getPackageInfo() {\n const packageJsonPath = path.join(\"package.json\")\n\n return fs.readJSONSync(packageJsonPath) as PackageJson\n}"],"mappings":";AAAA,SAAS,eAAe;AACxB,OAAO,UAAU;AACjB,SAAS,YAAY,UAAU;AAC/B,SAAS,gBAAgB;AACzB,OAAO,aAAa;AAEpB,IAAM,QAAQ;AAAA,EACZ,EAAE,MAAM,UAAU,KAAK,6CAA6C;AAAA,EACpE,EAAE,MAAM,aAAa,KAAK,gDAAgD;AAC5E;AAEA,eAAe,KAAK,aAAqB,KAAa;AACpD,QAAM,cAAc,KAAK,KAAK,KAAK,WAAW;AAG9C,QAAM,GAAG,MAAM,aAAa,EAAE,WAAW,KAAK,CAAC;AAG/C,aAAW,QAAQ,OAAO;AACxB,YAAQ,IAAI,WAAW,KAAK,IAAI,gBAAgB;AAChD,aAAS,aAAa,KAAK,GAAG,IAAI,KAAK,KAAK,aAAa,KAAK,IAAI,CAAC,IAAI;AAAA,MACrE,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAGA,QAAM,aAAa,KAAK,KAAK,aAAa,WAAW;AACrD,QAAM,gBAAgB,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA;AACtC,QAAM,GAAG,UAAU,YAAY,aAAa;AAE5C,UAAQ,IAAI,0BAA0B,WAAW,EAAE;AACrD;AAEO,IAAM,QAAQ,IAAI,QAAQ,EAC9B,KAAK,OAAO,EACZ,YAAY,iEAAiE,EAC7E,OAAO,mBAAmB,yBAAyB,QAAQ,IAAI,CAAC,EAChE,OAAO,OAAO,YAAY;AACzB,MAAI;AACF,UAAM,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAGpC,UAAM,WAAW,MAAM,QAAQ;AAAA,MAC7B,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAED,QAAI,CAAC,SAAS,aAAa;AACzB,cAAQ,MAAM,2BAA2B;AACzC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,KAAK,SAAS,aAAa,GAAG;AACpC,YAAQ,IAAI,yBAAyB;AAAA,EACvC,SAAS,OAAO;AACd,YAAQ,MAAM,sBAAsB,KAAK;AACzC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;;;ACvDH,SAAS,WAAAA,gBAAe;;;ACJxB,OAAOC,WAAU;AACjB,OAAOC,SAAQ;AAGR,SAAS,iBAAiB;AAC/B,QAAM,kBAAkBD,MAAK,KAAK,cAAc;AAEhD,SAAOC,IAAG,aAAa,eAAe;AACxC;;;ADAA,QAAQ,GAAG,UAAU,MAAM,QAAQ,KAAK,CAAC,CAAC;AAC1C,QAAQ,GAAG,WAAW,MAAM,QAAQ,KAAK,CAAC,CAAC;AAE3C,eAAe,OAAO;AACpB,QAAM,cAAc,MAAM,eAAe;AAEzC,QAAM,UAAU,IAAIC,SAAQ,EACzB,KAAK,aAAa,EAClB,YAAY,wBAAwB,EACpC;AAAA,IACC,YAAY,WAAW;AAAA,IACvB;AAAA,IACA;AAAA,EACF;AAEF,UAAQ,WAAW,KAAK;AAExB,UAAQ,MAAM;AAChB;AAEA,KAAK;","names":["Command","path","fs","Command"]} \ No newline at end of file diff --git a/packages/create-dojo/src/commands/start.ts b/packages/create-dojo/src/commands/start.ts index 73a0ec11..3b9ac429 100644 --- a/packages/create-dojo/src/commands/start.ts +++ b/packages/create-dojo/src/commands/start.ts @@ -1,3 +1,5 @@ +#!/usr/bin/env node + import { Command } from "commander"; import path from "path"; import { promises as fs } from "fs"; @@ -10,6 +12,14 @@ const templates = [ value: "example-vite-react-sdk", description: "React app using Dojo SDK", }, + { + value: "example-vite-kitchen-sink", + description: "Vite app with a variety of Dojo features", + }, + { + value: "example-vanillajs-phaser-recs", + description: "Vanilla JS/Phaser app using Dojo RECS", + }, { value: "example-vite-react-phaser-recs", description: "React/Phaser app using Dojo RECS", @@ -22,10 +32,6 @@ const templates = [ value: "example-vite-react-threejs-recs", description: "React Three.js app using Dojo RECS", }, - { - value: "example-vite-react-sdk", - description: "Basic react app using the sdk", - }, { value: "example-vue-app-recs", description: "Basic vite app using RECS" }, ]; @@ -54,6 +60,21 @@ async function init(projectName: string, cwd: string, template: string) { // Rewrite package.json in client directory await rewritePackageJson(projectName, clientPath); + console.log(`Cloning dojo-starter repository...`); + const gitCloneResult = spawn.sync( + "git", + [ + "clone", + "https://github.com/dojoengine/dojo-starter.git", + dojoStarterPath, + ], + { stdio: "inherit" } + ); + + if (gitCloneResult.status !== 0) { + throw new Error(`Failed to clone dojo-starter repository.`); + } + // Clone dojo-starter console.log(`Downloading dojo-starter...`); spawn.sync("npx", ["degit", `dojoengine/dojo-starter`, dojoStarterPath], { @@ -120,14 +141,31 @@ export const start = new Command() .name("start") .description("initialize a new project with a selected template") .option("-c, --cwd ", "the working directory", process.cwd()) + .option("-t, --template