Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

create-dynamic-app

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

create-dynamic-app - npm Package Compare versions

Comparing version 2.0.1 to 2.0.3

.prettierignore

167

create-dynamic-app.js
#!/usr/bin/env node
import inquirer from 'inquirer';
import { simpleGit } from 'simple-git';
import chalk from 'chalk';
import figlet from 'figlet';
import { promises as fs } from 'fs';
import { generateNextApp } from './generate-next.js';
import { generateReactApp } from './generate-react.js';
import { promptForChains } from './utils.js';
import inquirer from "inquirer";
import { simpleGit } from "simple-git";
import chalk from "chalk";
import figlet from "figlet";
import { promises as fs } from "fs";
import { generateNextApp } from "./generate-next.js";
import { generateReactApp } from "./generate-react.js";
import { promptForChains } from "./utils.js";
console.log(chalk.yellow(figlet.textSync('Create Dynamic App', { horizontalLayout: 'full' })));
console.log(
chalk.yellow(
figlet.textSync("Create Dynamic App", { horizontalLayout: "full" }),
),
);
console.log(chalk.green('Welcome to the Dynamic App Creator! Follow the prompts to set up your project.'));
console.log(
chalk.green(
"Welcome to the Dynamic App Creator! Follow the prompts to set up your project.",
),
);

@@ -19,12 +27,12 @@ async function askFrameworkQuestion() {

{
type: 'list',
name: 'framework',
message: 'What framework would you like to use?',
type: "list",
name: "framework",
message: "What framework would you like to use?",
choices: [
{ name: 'NextJS', value: 'nextjs' },
{ name: 'ReactJS', value: 'react' },
{ name: 'React Native', value: 'react-native' },
{ name: 'Scaffold Eth Hacker Edition', value: 'scaffold-eth' }
{ name: "NextJS", value: "nextjs" },
{ name: "ReactJS", value: "react" },
{ name: "React Native", value: "react-native" },
{ name: "Scaffold Eth Hacker Edition", value: "scaffold-eth" },
],
}
},
];

@@ -37,10 +45,11 @@ return inquirer.prompt(frameworkQuestion);

{
type: 'list',
name: 'library',
message: 'For Ethereum interactions, are you happy with Viem, or do you need Ethers?',
type: "list",
name: "library",
message:
"For Ethereum interactions, are you happy with Viem, or do you need Ethers?",
choices: [
{ name: 'Happy with Viem', value: 'viem' },
{ name: 'I need Ethers', value: 'ethers' }
]
}
{ name: "Happy with Viem", value: "viem" },
{ name: "I need Ethers", value: "ethers" },
],
},
];

@@ -53,10 +62,10 @@ return inquirer.prompt(libraryQuestion);

{
type: 'list',
name: 'wagmi',
message: 'Do you want to use Wagmi on top of Viem?',
type: "list",
name: "wagmi",
message: "Do you want to use Wagmi on top of Viem?",
choices: [
{ name: 'Yes', value: 'wagmi' },
{ name: 'No', value: '' }
]
}
{ name: "Yes", value: "wagmi" },
{ name: "No", value: "" },
],
},
];

@@ -76,25 +85,39 @@ return inquirer.prompt(wagmiQuestion);

async function generateApp(repoUrl, directoryName, answers, selectedChains) {
console.log(answers);
if (answers.framework === 'nextjs' || answers.framework === 'react') {
console.log('answers',answers)
const useViem = answers.library === 'viem';
const useWagmi = answers.wagmi === 'wagmi';
if (answers.framework === "nextjs" || answers.framework === "react") {
const useViem = answers.library === "viem";
const useWagmi = answers.wagmi === "wagmi";
if (answers.framework === 'nextjs') {
await generateNextApp(process.cwd(), directoryName, useViem, useWagmi, selectedChains);
} else if (answers.framework === 'react') {
await generateReactApp(process.cwd(), directoryName, useViem, useWagmi, selectedChains);
if (answers.framework === "nextjs") {
await generateNextApp(
process.cwd(),
directoryName,
useViem,
useWagmi,
selectedChains,
);
} else if (answers.framework === "react") {
await generateReactApp(
process.cwd(),
directoryName,
useViem,
useWagmi,
selectedChains,
);
}
console.log(chalk.green(`Project setup complete! Check it out in the ${directoryName} directory.`));
console.log(
chalk.green(
`Project setup complete! Check it out in the ${directoryName} directory.`,
),
);
} else {
let framework;
if (repoUrl === 'https://github.com/dynamic-labs/hackathon-starter-kit') {
framework = 'yarn';
if (repoUrl === "https://github.com/dynamic-labs/hackathon-starter-kit") {
framework = "yarn";
} else {
framework = 'npm';
framework = "npm";
}
const generateMessage = (directoryName) => {
if (repoUrl === 'https://github.com/dynamic-labs/hackathon-starter-kit') {
if (repoUrl === "https://github.com/dynamic-labs/hackathon-starter-kit") {
return `Project setup complete! Cd into the ${directoryName} directory and run yarn install.`;

@@ -110,7 +133,7 @@ } else {

const repoGit = simpleGit(directoryName);
await repoGit.removeRemote('origin');
await repoGit.removeRemote("origin");
console.log(chalk.green(generateMessage(directoryName)));
} catch (error) {
console.error(chalk.red('Failed to clone the repository:', error));
console.error(chalk.red("Failed to clone the repository:", error));
}

@@ -122,6 +145,7 @@ }

const answer = await inquirer.prompt({
type: 'input',
name: 'newDirectory',
type: "input",
name: "newDirectory",
message: `The directory "${originalDirectory}" already exists. Enter a new directory name:`,
validate: input => input ? true : 'Please enter a valid directory name.'
validate: (input) =>
input ? true : "Please enter a valid directory name.",
});

@@ -139,6 +163,8 @@

export function generateRepoUrl(answers) {
if (answers.framework === 'react-native') return `https://github.com/dynamic-labs/react-native-expo`;
if (answers.framework === 'scaffold-eth') return `https://github.com/dynamic-labs/hackathon-starter-kit`;
return `https://github.com/dynamic-labs/${answers.framework}-${answers.library}${answers.wagmi ? '-' + answers.wagmi : ''}`;
if (answers.framework === "react-native")
return `https://github.com/dynamic-labs/react-native-expo`;
if (answers.framework === "scaffold-eth")
return `https://github.com/dynamic-labs/hackathon-starter-kit`;
return `https://github.com/dynamic-labs/${answers.framework}-${answers.library}${answers.wagmi ? "-" + answers.wagmi : ""}`;
}

@@ -149,5 +175,5 @@

const selectedChains = await promptForChains();
const hasEthereum = selectedChains.some(chain => chain.name === 'Ethereum');
const hasEthereum = selectedChains.some((chain) => chain.name === "Ethereum");
let answers = { ...initialAnswers, library: '', wagmi: '' };
let answers = { ...initialAnswers, library: "", wagmi: "" };

@@ -158,3 +184,3 @@ if (hasEthereum) {

if (answers.library === 'viem') {
if (answers.library === "viem") {
const wagmiAnswer = await askWagmiQuestion();

@@ -168,6 +194,6 @@ answers.wagmi = wagmiAnswer.wagmi;

if (answers.framework === 'scaffold-eth') {
directoryName = process.argv[2] || 'my-hacker-project';
if (answers.framework === "scaffold-eth") {
directoryName = process.argv[2] || "my-hacker-project";
} else {
directoryName = process.argv[2] || 'my-dynamic-project';
directoryName = process.argv[2] || "my-dynamic-project";
}

@@ -179,13 +205,22 @@

directoryName = await askForNewDirectory(directoryName, repoUrl);
}
}
await generateApp(repoUrl, directoryName, answers, selectedChains);
if (answers.framework === 'scaffold-eth') {
if (answers.framework === "scaffold-eth") {
const url = "https://github.com/dynamic-labs/hackathon-starter-kit";
console.log(chalk.magenta('Check out the documentation for the Scaffold Eth Hacker Edition at ') + url + chalk.magenta(' to get started!'));
console.log(
chalk.magenta(
"Check out the documentation for the Scaffold Eth Hacker Edition at ",
) +
url +
chalk.magenta(" to get started!"),
);
} else {
const url = "https://app.dynamic.xyz/dashboard/developer/api";
console.log(chalk.magenta('Make sure to grab your Environment ID from ') + url + chalk.magenta(' and add it to the DynamicContextProvider!'));
console.log(
chalk.magenta("Make sure to grab your Environment ID from ") +
url +
chalk.magenta(" and add it to the DynamicContextProvider!"),
);
}

@@ -192,0 +227,0 @@ }

@@ -1,19 +0,41 @@

import fs from 'fs/promises';
import path from 'path';
import { execSync } from 'child_process';
import figlet from 'figlet';
import { generateLayoutJsContent, pageJs, PageCss, generateProvidersContent, generateDynamicLibContent, wagmiContent } from './next-templates/index.js';
import { addSolanaDependencies, createConfigOverrides } from './next-templates/solana-config.js';
import { promptForChains, checkExistingDirectories } from './utils.js';
import chalk from 'chalk';
import fs from "fs/promises";
import path from "path";
import { execSync } from "child_process";
import figlet from "figlet";
import {
generateLayoutJsContent,
pageJs,
PageCss,
generateProvidersContent,
generateDynamicLibContent,
generateMethodsContent,
MethodsCss,
wagmiContent,
} from "./next-templates/index.js";
import {
addSolanaDependencies,
createConfigOverrides,
} from "./next-templates/solana-config.js";
import { promptForChains, checkExistingDirectories } from "./utils.js";
import chalk from "chalk";
import fetch from 'node-fetch';
export const generateNextApp = async (parentDir, appName, useViem, useWagmi, selectedChains) => {
export const generateNextApp = async (
parentDir,
appName,
useViem,
useWagmi,
selectedChains,
) => {
const baseDir = path.join(parentDir, appName);
const hasSolana = selectedChains.some(chain => chain.name === 'Solana');
const hasSolana = selectedChains.some((chain) => chain.name === "Solana");
console.log(chalk.blue(`Creating Next.js app: ${appName}`));
execSync(`npx create-next-app@latest ${baseDir} --eslint --typescript --app --no-src-dir --import-alias "@/*" --no-tailwind`, { stdio: 'inherit' });
execSync(
`npx create-next-app@latest ${baseDir} --eslint --typescript --app --no-src-dir --import-alias "@/*" --no-tailwind`,
{ stdio: "inherit" },
);
const packageJsonPath = path.join(baseDir, 'package.json');
let packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
const packageJsonPath = path.join(baseDir, "package.json");
let packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));

@@ -23,9 +45,15 @@ packageJson.dependencies = {

"@dynamic-labs/sdk-react-core": "^3.0.0-alpha",
...(useViem && selectedChains.some(chain => chain.name === 'Ethereum') ? { viem: "*" } : !useViem && selectedChains.some(chain => chain.name === 'Ethereum') ?{ "@dynamic-labs/ethers-v6": "^3.0.0-alpha" } : {}),
...(useViem && selectedChains.some((chain) => chain.name === "Ethereum")
? { viem: "*" }
: !useViem && selectedChains.some((chain) => chain.name === "Ethereum")
? { "@dynamic-labs/ethers-v6": "^3.0.0-alpha" }
: {}),
...(useWagmi && {
"@dynamic-labs/wagmi-connector": "^3.0.0-alpha",
"@tanstack/react-query": "^5.27.5",
wagmi: useViem ? "^2.5.7" : "*"
wagmi: useViem ? "^2.5.7" : "*",
}),
...Object.fromEntries(selectedChains.map(chain => [chain.package, "^3.0.0-alpha"]))
...Object.fromEntries(
selectedChains.map((chain) => [chain.package, "^3.0.0-alpha"]),
),
};

@@ -38,19 +66,41 @@

await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
const libDir = path.join(baseDir, 'lib');
const libDir = path.join(baseDir, "lib");
await fs.mkdir(libDir, { recursive: true });
const componentsDir = path.join(baseDir, "app/components");
await fs.mkdir(componentsDir, { recursive: true });
const filesToCreate = [
{ path: path.join(baseDir, 'app', 'layout.tsx'), content: generateLayoutJsContent(useViem, useWagmi, selectedChains) },
{ path: path.join(baseDir, 'app', 'page.tsx'), content: pageJs },
{ path: path.join(baseDir, 'app', 'page.css'), content: PageCss },
{ path: path.join(baseDir, 'lib', 'providers.tsx'), content: generateProvidersContent(useWagmi, selectedChains) },
{ path: path.join(baseDir, 'lib', 'dynamic.ts'), content: generateDynamicLibContent(useViem, useWagmi, selectedChains) },
...(useWagmi ? [{ path: path.join(baseDir, 'lib', 'wagmi.ts'), content: wagmiContent }] : [])
{
path: path.join(baseDir, "app", "layout.tsx"),
content: generateLayoutJsContent(useViem, useWagmi, selectedChains),
},
{ path: path.join(baseDir, "app", "page.tsx"), content: pageJs },
{ path: path.join(baseDir, "app", "page.css"), content: PageCss },
{path: path.join(baseDir, "app/components", "Methods.js"), content: generateMethodsContent(selectedChains)},
{path: path.join(baseDir, "app/components", "Methods.css"), content: MethodsCss},
{
path: path.join(baseDir, "lib", "providers.tsx"),
content: generateProvidersContent(useWagmi, selectedChains),
},
{
path: path.join(baseDir, "lib", "dynamic.ts"),
content: generateDynamicLibContent(useViem, useWagmi, selectedChains),
},
...(useWagmi
? [{ path: path.join(baseDir, "lib", "wagmi.ts"), content: wagmiContent }]
: []),
];
await Promise.all(filesToCreate.map(file => fs.writeFile(file.path, file.content)));
await Promise.all(
filesToCreate.map((file) => fs.writeFile(file.path, file.content)),
);
if (hasSolana) {
await fs.writeFile(path.join(baseDir, 'next.config.js'), createConfigOverrides());
await fs.writeFile(
path.join(baseDir, "next.config.js"),
createConfigOverrides(),
);
}

@@ -64,5 +114,12 @@

const copyAssets = async (baseDir) => {
const assetsDir = path.join(process.cwd(), 'assets');
const publicDir = path.join(baseDir, 'public');
const publicDir = path.join(baseDir, "public");
// Hardcoded remote URLs for assets
const assetUrls = [
{name: "image-light", url: 'https://cdn.prod.website-files.com/626692727bba3f384e008e8a/66e1b9f35ab65dff341d8266_image-light.png'},
{name: "image-dark", url: 'https://cdn.prod.website-files.com/626692727bba3f384e008e8a/66e1b9f31261f4025001cbff_image-dark.png'},
{name: "logo-light", url: 'https://cdn.prod.website-files.com/626692727bba3f384e008e8a/66e1b9f3915ce2792a3677b1_logo-light.png'},
{name: "logo-dark", url: 'https://cdn.prod.website-files.com/626692727bba3f384e008e8a/66e1b9e7144147903a39ab97_logo-dark.png'}
];
try {

@@ -72,8 +129,16 @@ // Ensure the public directory exists

const files = await fs.readdir(assetsDir);
await Promise.all(files.map(file =>
fs.copyFile(path.join(assetsDir, file), path.join(publicDir, file))
));
// Download and save each asset
await Promise.all(assetUrls.map(async (asset) => {
const response = await fetch(asset.url);
if (response.ok) {
const fileName = `${asset.name}.png`;
const filePath = path.join(publicDir, fileName);
const arrayBuffer = await response.arrayBuffer();
await fs.writeFile(filePath, Buffer.from(arrayBuffer));
}
}));
console.log(chalk.green("Assets copied successfully from remote URLs."));
} catch (error) {
console.log(chalk.red('Assets folder not found or error copying assets:', error.message));
console.error(chalk.red(`Error copying assets from remote URLs: ${error.message}`));
}

@@ -84,4 +149,4 @@ };

const customDir = process.argv[2];
const nextDir = customDir ? process.cwd() : path.join(process.cwd(), 'next');
const nextDir = customDir ? process.cwd() : path.join(process.cwd(), "next");
if (!customDir) {

@@ -91,7 +156,9 @@ await fs.mkdir(nextDir, { recursive: true });

console.log(chalk.cyan(figlet.textSync('Next.js Apps', { horizontalLayout: 'full' })));
console.log(
chalk.cyan(figlet.textSync("Next.js Apps", { horizontalLayout: "full" })),
);
// Ask for chains first
const selectedChains = await promptForChains();
const hasEthereum = selectedChains.some(chain => chain.name === 'Ethereum');
const hasEthereum = selectedChains.some((chain) => chain.name === "Ethereum");

@@ -101,9 +168,9 @@ let appConfigs;

appConfigs = [
{ name: 'next-app-viem', useViem: true, useWagmi: false },
{ name: 'next-app-viem-wagmi', useViem: true, useWagmi: true },
{ name: 'next-app-ethers', useViem: false, useWagmi: false },
{ name: "next-app-viem", useViem: true, useWagmi: false },
{ name: "next-app-viem-wagmi", useViem: true, useWagmi: true },
{ name: "next-app-ethers", useViem: false, useWagmi: false },
];
} else {
appConfigs = [
{ name: 'next-app-vanilla', useViem: false, useWagmi: false },
{ name: "next-app-vanilla", useViem: false, useWagmi: false },
];

@@ -113,19 +180,36 @@ }

if (!customDir) {
await checkExistingDirectories(appConfigs.map(config => config.name), nextDir);
await checkExistingDirectories(
appConfigs.map((config) => config.name),
nextDir,
);
}
console.log(chalk.yellow(`\n🚀 About to generate ${appConfigs.length} example app${appConfigs.length > 1 ? 's' : ''}:\n`));
appConfigs.forEach(config => {
const nextIcon = '▲ '; // Next.js icon (triangle)
const viemIcon = config.useViem ? '🔷 ' : '📘 '; // Viem icon (blue diamond) or Ethers icon (blue book)
const wagmiIcon = config.useWagmi ? '🔗 ' : ''; // Wagmi icon (chain link)
console.log(chalk.green(`${nextIcon}${viemIcon}${config.name} ${wagmiIcon}`));
console.log(
chalk.yellow(
`\n🚀 About to generate ${appConfigs.length} example app${appConfigs.length > 1 ? "s" : ""}:\n`,
),
);
appConfigs.forEach((config) => {
const nextIcon = "▲ "; // Next.js icon (triangle)
const viemIcon = config.useViem ? "🔷 " : "📘 "; // Viem icon (blue diamond) or Ethers icon (blue book)
const wagmiIcon = config.useWagmi ? "🔗 " : ""; // Wagmi icon (chain link)
console.log(
chalk.green(`${nextIcon}${viemIcon}${config.name} ${wagmiIcon}`),
);
});
console.log(chalk.yellow('\nGenerating apps... 🛠️\n'));
console.log(chalk.yellow("\nGenerating apps... 🛠️\n"));
await Promise.all(appConfigs.map(config =>
generateNextApp(nextDir, config.name, config.useViem, config.useWagmi, selectedChains)
));
await Promise.all(
appConfigs.map((config) =>
generateNextApp(
nextDir,
config.name,
config.useViem,
config.useWagmi,
selectedChains,
),
),
);

@@ -135,11 +219,27 @@ if (customDir) {

} else {
const createdApps = (await Promise.all(appConfigs.map(async config =>
(await fs.stat(path.join(nextDir, config.name)).catch(() => null)) ? config.name : null
))).filter(Boolean);
const createdApps = (
await Promise.all(
appConfigs.map(async (config) =>
(await fs.stat(path.join(nextDir, config.name)).catch(() => null))
? config.name
: null,
),
)
).filter(Boolean);
if (createdApps.length === appConfigs.length) {
console.log(chalk.green('All example apps were created successfully in the "next" directory.'));
console.log(
chalk.green(
'All example apps were created successfully in the "next" directory.',
),
);
} else {
const missingApps = appConfigs.map(config => config.name).filter(name => !createdApps.includes(name));
console.error(chalk.red(`Error: The following apps were not created: ${missingApps.join(', ')}`));
const missingApps = appConfigs
.map((config) => config.name)
.filter((name) => !createdApps.includes(name));
console.error(
chalk.red(
`Error: The following apps were not created: ${missingApps.join(", ")}`,
),
);
}

@@ -151,2 +251,2 @@ }

main().catch(console.error);
}
}

@@ -1,34 +0,51 @@

import fs from 'fs/promises';
import path from 'path';
import { execSync } from 'child_process';
import figlet from 'figlet';
import mainCss from './react-templates/mainCss.js';
import mainJs from './react-templates/main.js';
import generateAppJsContent from './react-templates/app.js';
import { addSolanaDependencies, createConfigOverrides, updatePackageJsonScripts } from './react-templates/solana-config.js';
import { promptForChains, checkExistingDirectories } from './utils.js';
import chalk from 'chalk';
import fs from "fs/promises";
import path from "path";
import { execSync } from "child_process";
import figlet from "figlet";
import mainCss from "./react-templates/mainCss.js";
import mainJs from "./react-templates/main.js";
import generateAppJsContent from "./react-templates/app.js";
import generateMethodsContent from "./react-templates/methods.js";
import methodsCss from "./react-templates/methodsCss.js";
import {
addSolanaDependencies,
createConfigOverrides,
updatePackageJsonScripts,
} from "./react-templates/solana-config.js";
import { promptForChains, checkExistingDirectories } from "./utils.js";
import chalk from "chalk";
import fetch from 'node-fetch';
export const generateReactApp = async (parentDir, appName, useViem, useWagmi, selectedChains) => {
export const generateReactApp = async (
parentDir,
appName,
useViem,
useWagmi,
selectedChains,
) => {
const baseDir = path.join(parentDir, appName);
const hasSolana = selectedChains.some(chain => chain.name === 'Solana');
const hasSolana = selectedChains.some((chain) => chain.name === "Solana");
const hasEthereum = selectedChains.some((chain) => chain.name === "Ethereum");
console.log(chalk.blue(`Creating React app: ${appName}`));
execSync(`npx create-react-app ${baseDir}`);
const packageJsonPath = path.join(baseDir, 'package.json');
let packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
const packageJsonPath = path.join(baseDir, "package.json");
let packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
packageJson.dependencies = {
...packageJson.dependencies,
"@dynamic-labs/sdk-react-core": "^3.0.0-alpha",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
...(useViem && selectedChains.some(chain => chain.name === 'Ethereum') ? { viem: "*" } : !useViem && selectedChains.some(chain => chain.name === 'Ethereum') ?{ "@dynamic-labs/ethers-v6": "^3.0.0-alpha" } : {}),
...(useViem && hasEthereum
? { viem: "*" }
: !useViem && hasEthereum
? { "@dynamic-labs/ethers-v6": "^3.0.0-alpha" }
: {}),
...(useWagmi && {
"@dynamic-labs/wagmi-connector": "^3.0.0-alpha",
"@tanstack/react-query": "^5.27.5",
wagmi: useViem ? "^2.5.7" : "*"
wagmi: useViem ? "^2.5.7" : "*",
}),
...Object.fromEntries(selectedChains.map(chain => [chain.package, "^3.0.0-alpha"]))
...Object.fromEntries(
selectedChains.map((chain) => [chain.package, "^3.0.0-alpha"]),
),
};

@@ -42,3 +59,3 @@

...packageJson.devDependencies,
"web-vitals": "^2.1.4"
"web-vitals": "^2.1.4",
};

@@ -48,3 +65,3 @@

...(packageJson.overrides ?? {}),
typescript: "4.9.5"
typescript: "4.9.5",
};

@@ -59,9 +76,15 @@

const appJsContent = generateAppJsContent(useViem, useWagmi, selectedChains);
await fs.writeFile(path.join(baseDir, 'src', 'App.js'), appJsContent);
await fs.writeFile(path.join(baseDir, "src", "App.js"), appJsContent);
await fs.writeFile(path.join(baseDir, 'src', 'Main.js'), mainJs);
await fs.writeFile(path.join(baseDir, 'src', 'Main.css'), mainCss);
await fs.writeFile(path.join(baseDir, "src", "Main.js"), mainJs);
await fs.writeFile(path.join(baseDir, "src", "Main.css"), mainCss);
await fs.writeFile(
path.join(baseDir, "src", "Methods.js"),
generateMethodsContent(selectedChains),
);
await fs.writeFile(path.join(baseDir, "src", "Methods.css"), methodsCss);
if (hasSolana) {
const configOverridesPath = path.join(baseDir, 'config-overrides.js');
const configOverridesPath = path.join(baseDir, "config-overrides.js");
const configOverridesContent = createConfigOverrides();

@@ -77,5 +100,12 @@ await fs.writeFile(configOverridesPath, configOverridesContent);

const copyAssets = async (baseDir) => {
const assetsDir = path.join(process.cwd(), 'assets');
const publicDir = path.join(baseDir, 'public');
const publicDir = path.join(baseDir, "public");
// Hardcoded remote URLs for assets
const assetUrls = [
{name: "image-light", url: 'https://cdn.prod.website-files.com/626692727bba3f384e008e8a/66e1b9f35ab65dff341d8266_image-light.png'},
{name: "image-dark", url: 'https://cdn.prod.website-files.com/626692727bba3f384e008e8a/66e1b9f31261f4025001cbff_image-dark.png'},
{name: "logo-light", url: 'https://cdn.prod.website-files.com/626692727bba3f384e008e8a/66e1b9f3915ce2792a3677b1_logo-light.png'},
{name: "logo-dark", url: 'https://cdn.prod.website-files.com/626692727bba3f384e008e8a/66e1b9e7144147903a39ab97_logo-dark.png'}
];
try {

@@ -85,12 +115,16 @@ // Ensure the public directory exists

const files = await fs.readdir(assetsDir);
await Promise.all(files.map(file =>
fs.copyFile(path.join(assetsDir, file), path.join(publicDir, file))
));
// Download and save each asset
await Promise.all(assetUrls.map(async (asset) => {
const response = await fetch(asset.url);
if (response.ok) {
const fileName = `${asset.name}.png`;
const filePath = path.join(publicDir, fileName);
const arrayBuffer = await response.arrayBuffer();
await fs.writeFile(filePath, Buffer.from(arrayBuffer));
}
}));
console.log(chalk.green("Assets copied successfully from remote URLs."));
} catch (error) {
if (error.code === 'ENOENT' && error.path === assetsDir) {
console.log(chalk.yellow('Assets folder not found. Skipping asset copy.'));
} else {
console.error(chalk.red(`Error copying assets: ${error.message}`));
}
console.error(chalk.red(`Error copying assets from remote URLs: ${error.message}`));
}

@@ -101,4 +135,6 @@ };

const customDir = process.argv[2];
const reactDir = customDir ? process.cwd() : path.join(process.cwd(), 'react');
const reactDir = customDir
? process.cwd()
: path.join(process.cwd(), "react");
if (!customDir) {

@@ -108,6 +144,8 @@ await fs.mkdir(reactDir, { recursive: true });

console.log(chalk.cyan(figlet.textSync('React Apps', { horizontalLayout: 'full' })));
console.log(
chalk.cyan(figlet.textSync("React Apps", { horizontalLayout: "full" })),
);
const selectedChains = await promptForChains();
const hasEthereum = selectedChains.some(chain => chain.name === 'Ethereum');
const hasEthereum = selectedChains.some((chain) => chain.name === "Ethereum");

@@ -117,9 +155,9 @@ let appConfigs;

appConfigs = [
{ name: 'react-app-viem', useViem: true, useWagmi: false },
{ name: 'react-app-viem-wagmi', useViem: true, useWagmi: true },
{ name: 'react-app-ethers', useViem: false, useWagmi: false },
{ name: "react-app-viem", useViem: true, useWagmi: false },
{ name: "react-app-viem-wagmi", useViem: true, useWagmi: true },
{ name: "react-app-ethers", useViem: false, useWagmi: false },
];
} else {
appConfigs = [
{ name: 'react-app-vanilla', useViem: false, useWagmi: false },
{ name: "react-app-vanilla", useViem: false, useWagmi: false },
];

@@ -129,19 +167,40 @@ }

if (!customDir) {
await checkExistingDirectories(appConfigs.map(config => config.name), reactDir);
await checkExistingDirectories(
appConfigs.map((config) => config.name),
reactDir,
);
}
console.log(chalk.yellow(`\n🚀 About to generate ${appConfigs.length} example app${appConfigs.length > 1 ? 's' : ''}:\n`));
appConfigs.forEach(config => {
const reactIcon = '⚛️ '; // React icon
const viemIcon = config.useViem ? '🔷 ' : (hasEthereum && !config.useViem ? '📘 ' : ''); // Viem icon (blue diamond) or Ethers icon (blue book) if Ethereum
const wagmiIcon = config.useWagmi ? '🔗 ' : ''; // Wagmi icon (chain link)
console.log(chalk.green(`${reactIcon}${viemIcon}${config.name} ${wagmiIcon}`));
console.log(
chalk.yellow(
`\n🚀 About to generate ${appConfigs.length} example app${appConfigs.length > 1 ? "s" : ""}:\n`,
),
);
appConfigs.forEach((config) => {
const reactIcon = "⚛️ "; // React icon
const viemIcon = config.useViem
? "🔷 "
: hasEthereum && !config.useViem
? "📘 "
: ""; // Viem icon (blue diamond) or Ethers icon (blue book) if Ethereum
const wagmiIcon = config.useWagmi ? "🔗 " : ""; // Wagmi icon (chain link)
console.log(
chalk.green(`${reactIcon}${viemIcon}${config.name} ${wagmiIcon}`),
);
});
console.log(chalk.yellow('\nGenerating app(s)... 🛠️\n'));
console.log(chalk.yellow("\nGenerating app(s)... 🛠️\n"));
await Promise.all(appConfigs.map(config =>
generateReactApp(reactDir, config.name, config.useViem, config.useWagmi, selectedChains)
));
await Promise.all(
appConfigs.map((config) =>
generateReactApp(
reactDir,
config.name,
config.useViem,
config.useWagmi,
selectedChains,
),
),
);

@@ -151,11 +210,27 @@ if (customDir) {

} else {
const createdApps = (await Promise.all(appConfigs.map(async config =>
(await fs.stat(path.join(reactDir, config.name)).catch(() => null)) ? config.name : null
))).filter(Boolean);
const createdApps = (
await Promise.all(
appConfigs.map(async (config) =>
(await fs.stat(path.join(reactDir, config.name)).catch(() => null))
? config.name
: null,
),
)
).filter(Boolean);
if (createdApps.length === appConfigs.length) {
console.log(chalk.green('All example apps were created successfully in the "react" directory.'));
console.log(
chalk.green(
'All example apps were created successfully in the "react" directory.',
),
);
} else {
const missingApps = appConfigs.map(config => config.name).filter(name => !createdApps.includes(name));
console.error(chalk.red(`Error: The following apps were not created: ${missingApps.join(', ')}`));
const missingApps = appConfigs
.map((config) => config.name)
.filter((name) => !createdApps.includes(name));
console.error(
chalk.red(
`Error: The following apps were not created: ${missingApps.join(", ")}`,
),
);
}

@@ -167,2 +242,2 @@ }

main().catch(console.error);
}
}
const generateDynamicLibContent = (useViem, useWagmi, selectedChains) => {
let imports = `export * from "@dynamic-labs/sdk-react-core";\n`;
if (!useViem && selectedChains.some(chain => chain.name === 'Ethereum')) {
imports += `export * from "@dynamic-labs/ethers-v6";\n`;
}
let imports = `export * from "@dynamic-labs/sdk-react-core";\n`;
return imports;
if (!useViem && selectedChains.some((chain) => chain.name === "Ethereum")) {
imports += `export * from "@dynamic-labs/ethers-v6";\n`;
}
selectedChains.forEach((chain) => {
imports += `export * from "@dynamic-labs/${chain.name.toLowerCase()}";\n`;
});
return imports;
};
export default generateDynamicLibContent;

@@ -1,8 +0,11 @@

import page from './page.js';
import generateLayoutContent from './layout.js';
import generateDynamicLibContent from './dynamic.js';
import generateProvidersContent from './providers.js';
import wagmiContent from './wagmi.js';
import PageCss from './pageCss.js';
import page from "./page.js";
import generateLayoutContent from "./layout.js";
import generateDynamicLibContent from "./dynamic.js";
import generateProvidersContent from "./providers.js";
import generateMethodsContent from "./methods.js";
import wagmiContent from "./wagmi.js";
import PageCss from "./pageCss.js";
import MethodsCss from "./methodsCss.js";
export {

@@ -13,4 +16,6 @@ page as pageJs,

generateProvidersContent,
generateMethodsContent,
wagmiContent,
PageCss,
MethodsCss,
};
const generateLayoutContent = () => {
let imports = `import "./globals.css";\n`;
imports += `import type { Metadata } from "next";\n`;
imports += `import { Inter } from "next/font/google";\n`;
imports += `import Providers from "@/lib/providers";\n`;
let content = `
let imports = `import "./globals.css";\n`;
imports += `import type { Metadata } from "next";\n`;
imports += `import { Inter } from "next/font/google";\n`;
imports += `import Providers from "@/lib/providers";\n`;
let content = `
const inter = Inter({ subsets: ["latin"] });

@@ -27,5 +27,5 @@

return `${imports}\n${content}`;
return `${imports}\n${content}`;
};
export default generateLayoutContent;
export default generateLayoutContent;

@@ -6,2 +6,3 @@ const page = `

import { useState, useEffect } from 'react';
import DynamicMethods from "@/app/components/Methods";
import './page.css';

@@ -38,2 +39,3 @@

<DynamicWidget />
<DynamicMethods isDarkMode={isDarkMode} />
</div>

@@ -40,0 +42,0 @@ <div className="footer">

@@ -73,2 +73,3 @@ const pageCss = `

justify-content: center;
gap: 20px;
}

@@ -118,4 +119,4 @@

}
`
`;
export default pageCss;
const generateProvidersContent = (useWagmi, selectedChains) => {
let imports = `'use client';\n\n`;
imports += `import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";\n`;
let imports = `'use client';\n\n`;
imports += `import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";\n`;
selectedChains.forEach(chain => {
imports += `import { ${chain.connector} } from "${chain.package}";\n`;
});
if (useWagmi) {
imports += `import { WagmiProvider } from "wagmi";\n`;
imports += `import { QueryClient, QueryClientProvider } from "@tanstack/react-query";\n`;
imports += `import { DynamicWagmiConnector } from "@dynamic-labs/wagmi-connector";\n`;
imports += `import { config } from "@/lib/wagmi";\n`;
}
selectedChains.forEach((chain) => {
imports += `import { ${chain.connector} } from "${chain.package}";\n`;
});
const walletConnectors = selectedChains.map(chain => chain.connector).join(', ');
if (useWagmi) {
imports += `import { WagmiProvider } from "wagmi";\n`;
imports += `import { QueryClient, QueryClientProvider } from "@tanstack/react-query";\n`;
imports += `import { DynamicWagmiConnector } from "@dynamic-labs/wagmi-connector";\n`;
imports += `import { config } from "@/lib/wagmi";\n`;
}
let content = `
const walletConnectors = selectedChains
.map((chain) => chain.connector)
.join(", ");
let content = `
export default function Providers({

@@ -25,3 +27,3 @@ children,

${useWagmi ? `const queryClient = new QueryClient();` : ''}
${useWagmi ? `const queryClient = new QueryClient();` : ""}

@@ -36,3 +38,5 @@ return (

>
${useWagmi ? `
${
useWagmi
? `
<WagmiProvider config={config}>

@@ -45,3 +49,5 @@ <QueryClientProvider client={queryClient}>

</WagmiProvider>
` : '{children}'}
`
: "{children}"
}
</DynamicContextProvider>

@@ -51,5 +57,5 @@ );

return `${imports}\n${content}`;
}
return `${imports}\n${content}`;
};
export default generateProvidersContent;
export default generateProvidersContent;
export const addSolanaDependencies = (packageJson) => {
packageJson.dependencies['crypto-browserify'] = "^3.12.0";
packageJson.dependencies['stream-browserify'] = "^3.0.0";
packageJson.dependencies['process'] = "^0.11.10";
packageJson.dependencies["crypto-browserify"] = "^3.12.0";
packageJson.dependencies["stream-browserify"] = "^3.0.0";
packageJson.dependencies["process"] = "^0.11.10";
// Add the browser field
packageJson.browser = {
...packageJson.browser,
crypto: false
crypto: false,
};

@@ -49,2 +49,2 @@ // Add the solana overrides

return packageJson;
};
};

@@ -19,4 +19,4 @@ const wagmi = `

}
`
`;
export default wagmi;
{
"name": "create-dynamic-app",
"version": "2.0.1",
"version": "2.0.3",
"scripts": {

@@ -20,4 +20,8 @@ "generate-react": "node generate-react.js",

"inquirer": "^10.1.8",
"node-fetch": "^3.3.2",
"simple-git": "^3.26.0"
},
"devDependencies": {
"prettier": "3.3.3"
}
}

@@ -1,65 +0,70 @@

export default function generateAppJsContent(useViem, useWagmi, selectedChains) {
let imports = `import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";\n`;
selectedChains.forEach(chain => {
imports += `import { ${chain.connector} } from "${chain.package}";\n`;
});
if (useWagmi) {
imports += `import { DynamicWagmiConnector } from "@dynamic-labs/wagmi-connector";\n`;
imports += `import { createConfig, WagmiProvider } from "wagmi";\n`;
imports += `import { QueryClient, QueryClientProvider } from "@tanstack/react-query";\n`;
imports += `import { http } from "viem";\n`;
imports += `import { mainnet } from "viem/chains";\n`;
}
imports += `import Main from "./Main";\n\n`;
export default function generateAppJsContent(
useViem,
useWagmi,
selectedChains,
) {
let imports = `import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";\n`;
const walletConnectors = selectedChains.map(chain => chain.connector).join(', ');
selectedChains.forEach((chain) => {
imports += `import { ${chain.connector} } from "${chain.package}";\n`;
});
let content = '';
if (useWagmi) {
content = `
const config = createConfig({
chains: [mainnet],
multiInjectedProviderDiscovery: false,
transports: {
[mainnet.id]: http(),
},
});
const queryClient = new QueryClient();
const App = () => (
<DynamicContextProvider
theme="auto"
settings={{
environmentId: "2762a57b-faa4-41ce-9f16-abff9300e2c9",
walletConnectors: [${walletConnectors}],
}}
>
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<DynamicWagmiConnector>
<Main />
</DynamicWagmiConnector>
</QueryClientProvider>
</WagmiProvider>
</DynamicContextProvider>
);`;
} else {
content = `
const App = () => (
<DynamicContextProvider
theme="auto"
settings={{
environmentId: "2762a57b-faa4-41ce-9f16-abff9300e2c9",
walletConnectors: [${walletConnectors}],
}}
>
<Main />
</DynamicContextProvider>
);`;
}
if (useWagmi) {
imports += `import { DynamicWagmiConnector } from "@dynamic-labs/wagmi-connector";\n`;
imports += `import { createConfig, WagmiProvider } from "wagmi";\n`;
imports += `import { QueryClient, QueryClientProvider } from "@tanstack/react-query";\n`;
imports += `import { http } from "viem";\n`;
imports += `import { mainnet } from "viem/chains";\n`;
}
return `${imports}\n${content}\n\nexport default App;`;
}
imports += `import Main from "./Main";\n\n`;
const walletConnectors = selectedChains
.map((chain) => chain.connector)
.join(", ");
let content = "";
if (useWagmi) {
content = `const config = createConfig({
chains: [mainnet],
multiInjectedProviderDiscovery: false,
transports: {
[mainnet.id]: http(),
},
});
const queryClient = new QueryClient();
const App = () => (
<DynamicContextProvider
theme="auto"
settings={{
environmentId: "2762a57b-faa4-41ce-9f16-abff9300e2c9",
walletConnectors: [${walletConnectors}],
}}
>
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<DynamicWagmiConnector>
<Main />
</DynamicWagmiConnector>
</QueryClientProvider>
</WagmiProvider>
</DynamicContextProvider>
);`;
} else {
content = `const App = () => (
<DynamicContextProvider
theme="auto"
settings={{
environmentId: "2762a57b-faa4-41ce-9f16-abff9300e2c9",
walletConnectors: [${walletConnectors}],
}}
>
<Main />
</DynamicContextProvider>
);`;
}
return `${imports}\n${content}\n\nexport default App;`;
}
const main = `
import { DynamicWidget } from "@dynamic-labs/sdk-react-core";
import { useState, useEffect } from 'react';
import DynamicMethods from './Methods.js';
import './Main.css';

@@ -30,2 +31,3 @@

<DynamicWidget />
<DynamicMethods isDarkMode={isDarkMode} />
</div>

@@ -41,4 +43,4 @@ <div className="footer">

export default Main;
`
`;
export default main;

@@ -7,6 +7,8 @@ const mainCss = `

align-items: center;
justify-content: center;
justify-content: flex-start;
background: #f1f1f3;
color: #333;
transition: background-color 0.3s, color 0.3s;
position: relative;
padding-bottom: 60px;
}

@@ -74,17 +76,29 @@

justify-content: center;
flex-grow: 1;
width: 100%;
padding: 60px 0;
}
.footer {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 60px;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 20px;
}
.footer-image {
position: absolute;
bottom: 0px;
right: 20px;
height: 15rem;
width: auto;
margin-left: 8px;
position: absolute;
bottom: 0;
right: 20px;
}
.footer-text {
position: absolute;
bottom: 6px;
right: 20px;
color: #666;

@@ -96,6 +110,2 @@ font-size: 14px;

.container.dark .footer {
background-color: rgba(42, 42, 42, 0.8);
}
.container.dark .footer-text {

@@ -120,4 +130,4 @@ color: #ccc;

}
`
`;
export default mainCss;
export const addSolanaDependencies = (packageJson) => {
packageJson.dependencies['crypto-browserify'] = "^3.12.0";
packageJson.dependencies['stream-browserify'] = "^3.0.0";
packageJson.dependencies['process'] = "^0.11.10";
packageJson.dependencies["crypto-browserify"] = "^3.12.0";
packageJson.dependencies["stream-browserify"] = "^3.0.0";
packageJson.dependencies["process"] = "^0.11.10";
// Add the browser field
packageJson.browser = {
...packageJson.browser,
crypto: false
crypto: false,
};

@@ -10,0 +10,0 @@ // Add the solana overrides

@@ -8,3 +8,4 @@ ## Dynamic Example App Generator

- **Next.js Projects**:
- `next-viem`: Next.js project using Viem.
- `next-viem`: Next.js project using Viem.
- `next-viem-wagmi`: Next.js project using both Viem and Wagmi.

@@ -35,2 +36,1 @@ - `next-ethers`: Next.js project using ethers.js.

The main logic for the generation can be found in the `generate-next.ts` and `generate-react.ts` files at the root of the project.

@@ -1,14 +0,42 @@

import inquirer from 'inquirer';
import chalk from 'chalk';
import fs from 'fs/promises';
import path from 'path';
import inquirer from "inquirer";
import chalk from "chalk";
import fs from "fs/promises";
import path from "path";
export const chainOptions = [
{ name: 'Ethereum', package: '@dynamic-labs/ethereum', connector: 'EthereumWalletConnectors' },
{ name: 'Algorand', package: '@dynamic-labs/algorand', connector: 'AlgorandWalletConnectors' },
{ name: 'Solana', package: '@dynamic-labs/solana', connector: 'SolanaWalletConnectors' },
{ name: 'Flow', package: '@dynamic-labs/flow', connector: 'FlowWalletConnectors' },
{ name: 'Starknet', package: '@dynamic-labs/starknet', connector: 'StarknetWalletConnectors' },
{ name: 'Cosmos', package: '@dynamic-labs/cosmos', connector: 'CosmosWalletConnectors' },
{ name: 'Bitcoin', package: '@dynamic-labs/bitcoin', connector: 'BitcoinWalletConnectors' },
{
name: "Ethereum",
package: "@dynamic-labs/ethereum",
connector: "EthereumWalletConnectors",
},
{
name: "Algorand",
package: "@dynamic-labs/algorand",
connector: "AlgorandWalletConnectors",
},
{
name: "Solana",
package: "@dynamic-labs/solana",
connector: "SolanaWalletConnectors",
},
{
name: "Flow",
package: "@dynamic-labs/flow",
connector: "FlowWalletConnectors",
},
{
name: "Starknet",
package: "@dynamic-labs/starknet",
connector: "StarknetWalletConnectors",
},
{
name: "Cosmos",
package: "@dynamic-labs/cosmos",
connector: "CosmosWalletConnectors",
},
{
name: "Bitcoin",
package: "@dynamic-labs/bitcoin",
connector: "BitcoinWalletConnectors",
},
];

@@ -19,15 +47,38 @@

name: chain.name,
value: index
value: index,
}));
const answers = await inquirer.prompt([
{
type: 'checkbox',
name: 'selectedChains',
message: 'Select the chains you want to include:',
choices: chainChoices
while (true) {
const answers = await inquirer.prompt([
{
type: "checkbox",
name: "selectedChains",
message: "Select the chains you want to include:",
choices: chainChoices,
},
]);
if (answers.selectedChains.length === 0) {
console.log(chalk.yellow("Warning: No chains selected."));
const confirmAnswer = await inquirer.prompt([
{
type: "list",
name: "action",
message:
"Do you want to continue without selecting any chains or go back to the selection?",
choices: [
{ name: "Continue without chains", value: "continue" },
{ name: "Go back to chain selection", value: "back" },
],
},
]);
if (confirmAnswer.action === "continue") {
return [];
}
// If 'back' is selected, the loop will continue and prompt again
} else {
return answers.selectedChains.map((index) => chainOptions[index]);
}
]);
return answers.selectedChains.map(index => chainOptions[index]);
}
};

@@ -48,21 +99,28 @@

if (existingDirs.length > 0) {
console.log(chalk.yellow(`The following directories already exist: ${existingDirs.join(', ')}`));
console.log(
chalk.yellow(
`The following directories already exist: ${existingDirs.join(", ")}`,
),
);
const answer = await inquirer.prompt({
type: 'confirm',
name: 'overwrite',
message: 'Do you want to overwrite these directories?',
default: false
type: "confirm",
name: "overwrite",
message: "Do you want to overwrite these directories?",
default: false,
});
if (!answer.overwrite) {
console.log(chalk.red('Operation cancelled.'));
console.log(chalk.red("Operation cancelled."));
process.exit(0);
} else {
for (const dir of existingDirs) {
await fs.rm(path.join(parentDir, dir), { recursive: true, force: true });
await fs.rm(path.join(parentDir, dir), {
recursive: true,
force: true,
});
}
}
} else {
console.log(chalk.green('All directories are available for creation.'));
console.log(chalk.green("All directories are available for creation."));
}
};
};

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc