mirror of
https://github.com/neynarxyz/create-farcaster-mini-app.git
synced 2025-11-18 17:09:47 -05:00
formatting
This commit is contained in:
332
bin/init.js
332
bin/init.js
@@ -1,18 +1,19 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
import { execSync } from 'child_process';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import crypto from 'crypto';
|
||||
import fs from 'fs';
|
||||
import inquirer from 'inquirer';
|
||||
import path, { dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const REPO_URL = 'https://github.com/neynarxyz/create-farcaster-mini-app.git';
|
||||
const SCRIPT_VERSION = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')).version;
|
||||
const SCRIPT_VERSION = JSON.parse(
|
||||
fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')
|
||||
).version;
|
||||
|
||||
// ANSI color codes
|
||||
const purple = '\x1b[35m';
|
||||
@@ -48,8 +49,8 @@ async function queryNeynarApp(apiKey) {
|
||||
`https://api.neynar.com/portal/app_by_api_key?starter_kit=true`,
|
||||
{
|
||||
headers: {
|
||||
'x-api-key': apiKey
|
||||
}
|
||||
'x-api-key': apiKey,
|
||||
},
|
||||
}
|
||||
);
|
||||
const data = await response.json();
|
||||
@@ -80,16 +81,17 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useNeynar',
|
||||
message: `🪐 ${purple}${bright}${italic}Neynar is an API that makes it easy to build on Farcaster.${reset}\n\n` +
|
||||
'Benefits of using Neynar in your mini app:\n' +
|
||||
'- Pre-configured webhook handling (no setup required)\n' +
|
||||
'- Automatic mini app analytics in your dev portal\n' +
|
||||
'- Send manual notifications from dev.neynar.com\n' +
|
||||
'- Built-in rate limiting and error handling\n\n' +
|
||||
`${purple}${bright}${italic}A demo API key is included if you would like to try out Neynar before signing up!${reset}\n\n` +
|
||||
'Would you like to use Neynar in your mini app?',
|
||||
default: true
|
||||
}
|
||||
message:
|
||||
`🪐 ${purple}${bright}${italic}Neynar is an API that makes it easy to build on Farcaster.${reset}\n\n` +
|
||||
'Benefits of using Neynar in your mini app:\n' +
|
||||
'- Pre-configured webhook handling (no setup required)\n' +
|
||||
'- Automatic mini app analytics in your dev portal\n' +
|
||||
'- Send manual notifications from dev.neynar.com\n' +
|
||||
'- Built-in rate limiting and error handling\n\n' +
|
||||
`${purple}${bright}${italic}A demo API key is included if you would like to try out Neynar before signing up!${reset}\n\n` +
|
||||
'Would you like to use Neynar in your mini app?',
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -98,8 +100,10 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
break;
|
||||
}
|
||||
|
||||
console.log('\n🪐 Find your Neynar API key at: https://dev.neynar.com/app\n');
|
||||
|
||||
console.log(
|
||||
'\n🪐 Find your Neynar API key at: https://dev.neynar.com/app\n'
|
||||
);
|
||||
|
||||
let neynarKeyAnswer;
|
||||
if (autoAcceptDefaults) {
|
||||
neynarKeyAnswer = { neynarApiKey: null };
|
||||
@@ -109,8 +113,8 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
type: 'password',
|
||||
name: 'neynarApiKey',
|
||||
message: 'Enter your Neynar API key (or press enter to skip):',
|
||||
default: null
|
||||
}
|
||||
default: null,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -126,15 +130,21 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
type: 'confirm',
|
||||
name: 'useDemo',
|
||||
message: 'Would you like to try the demo Neynar API key?',
|
||||
default: true
|
||||
}
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
if (useDemoKey.useDemo) {
|
||||
console.warn('\n⚠️ Note: the demo key is for development purposes only and is aggressively rate limited.');
|
||||
console.log('For production, please sign up for a Neynar account at https://neynar.com/ and configure the API key in your .env or .env.local file with NEYNAR_API_KEY.');
|
||||
console.log(`\n${purple}${bright}${italic}Neynar now has a free tier! See https://neynar.com/#pricing for details.\n${reset}`);
|
||||
console.warn(
|
||||
'\n⚠️ Note: the demo key is for development purposes only and is aggressively rate limited.'
|
||||
);
|
||||
console.log(
|
||||
'For production, please sign up for a Neynar account at https://neynar.com/ and configure the API key in your .env or .env.local file with NEYNAR_API_KEY.'
|
||||
);
|
||||
console.log(
|
||||
`\n${purple}${bright}${italic}Neynar now has a free tier! See https://neynar.com/#pricing for details.\n${reset}`
|
||||
);
|
||||
neynarApiKey = 'FARCASTER_V2_FRAMES_DEMO';
|
||||
}
|
||||
}
|
||||
@@ -144,14 +154,16 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
useNeynar = false;
|
||||
break;
|
||||
}
|
||||
console.log('\n⚠️ No valid API key provided. Would you like to try again?');
|
||||
console.log(
|
||||
'\n⚠️ No valid API key provided. Would you like to try again?'
|
||||
);
|
||||
const { retry } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'retry',
|
||||
message: 'Try configuring Neynar again?',
|
||||
default: true
|
||||
}
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
if (!retry) {
|
||||
useNeynar = false;
|
||||
@@ -176,9 +188,10 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'retry',
|
||||
message: '⚠️ Could not find a client ID for this API key. Would you like to try configuring Neynar again?',
|
||||
default: true
|
||||
}
|
||||
message:
|
||||
'⚠️ Could not find a client ID for this API key. Would you like to try configuring Neynar again?',
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
if (!retry) {
|
||||
useNeynar = false;
|
||||
@@ -191,7 +204,10 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
break;
|
||||
}
|
||||
|
||||
const defaultMiniAppName = (neynarAppName && !neynarAppName.toLowerCase().includes('demo')) ? neynarAppName : undefined;
|
||||
const defaultMiniAppName =
|
||||
neynarAppName && !neynarAppName.toLowerCase().includes('demo')
|
||||
? neynarAppName
|
||||
: undefined;
|
||||
|
||||
let answers;
|
||||
if (autoAcceptDefaults) {
|
||||
@@ -203,7 +219,7 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
buttonText: 'Launch Mini App',
|
||||
useWallet: true,
|
||||
useTunnel: true,
|
||||
enableAnalytics: true
|
||||
enableAnalytics: true,
|
||||
};
|
||||
} else {
|
||||
// If autoAcceptDefaults is false but we have a projectName, we still need to ask for other options
|
||||
@@ -213,26 +229,27 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
name: 'projectName',
|
||||
message: 'What is the name of your mini app?',
|
||||
default: projectName || defaultMiniAppName,
|
||||
validate: (input) => {
|
||||
validate: input => {
|
||||
if (input.trim() === '') {
|
||||
return 'Project name cannot be empty';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'description',
|
||||
message: 'Give a one-line description of your mini app (optional):',
|
||||
default: 'A Farcaster mini app created with Neynar'
|
||||
default: 'A Farcaster mini app created with Neynar',
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'primaryCategory',
|
||||
message: 'It is strongly recommended to choose a primary category and tags to help users discover your mini app.\n\nSelect a primary category:',
|
||||
message:
|
||||
'It is strongly recommended to choose a primary category and tags to help users discover your mini app.\n\nSelect a primary category:',
|
||||
choices: [
|
||||
new inquirer.Separator(),
|
||||
{ name: 'Skip (not recommended)', value: null },
|
||||
@@ -249,36 +266,37 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
{ name: 'Education', value: 'education' },
|
||||
{ name: 'Developer Tools', value: 'developer-tools' },
|
||||
{ name: 'Entertainment', value: 'entertainment' },
|
||||
{ name: 'Art & Creativity', value: 'art-creativity' }
|
||||
{ name: 'Art & Creativity', value: 'art-creativity' },
|
||||
],
|
||||
default: null
|
||||
default: null,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'tags',
|
||||
message: 'Enter tags for your mini app (separate with spaces or commas, optional):',
|
||||
message:
|
||||
'Enter tags for your mini app (separate with spaces or commas, optional):',
|
||||
default: '',
|
||||
filter: (input) => {
|
||||
filter: input => {
|
||||
if (!input.trim()) return [];
|
||||
// Split by both spaces and commas, trim whitespace, and filter out empty strings
|
||||
return input
|
||||
.split(/[,\s]+/)
|
||||
.map(tag => tag.trim())
|
||||
.filter(tag => tag.length > 0);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'buttonText',
|
||||
message: 'Enter the button text for your mini app:',
|
||||
default: 'Launch Mini App',
|
||||
validate: (input) => {
|
||||
validate: input => {
|
||||
if (input.trim() === '') {
|
||||
return 'Button text cannot be empty';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// Merge project name from the first prompt
|
||||
@@ -289,7 +307,8 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useWallet',
|
||||
message: 'Would you like to include wallet and transaction tooling in your mini app?\n' +
|
||||
message:
|
||||
'Would you like to include wallet and transaction tooling in your mini app?\n' +
|
||||
'This includes:\n' +
|
||||
'- EVM wallet connection\n' +
|
||||
'- Transaction signing\n' +
|
||||
@@ -297,8 +316,8 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
'- Chain switching\n' +
|
||||
'- Solana support\n\n' +
|
||||
'Include wallet and transaction features?',
|
||||
default: true
|
||||
}
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
answers.useWallet = walletAnswer.useWallet;
|
||||
|
||||
@@ -307,11 +326,12 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useTunnel',
|
||||
message: 'Would you like to test on mobile and/or test the app with Warpcast developer tools?\n' +
|
||||
message:
|
||||
'Would you like to test on mobile and/or test the app with Warpcast developer tools?\n' +
|
||||
`⚠️ ${yellow}${italic}Both mobile testing and the Warpcast debugger require setting up a tunnel to serve your app from localhost to the broader internet.\n${reset}` +
|
||||
'Configure a tunnel for mobile testing and/or Warpcast developer tools?',
|
||||
default: true
|
||||
}
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
answers.useTunnel = hostingAnswer.useTunnel;
|
||||
|
||||
@@ -320,9 +340,10 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'enableAnalytics',
|
||||
message: 'Would you like to help improve Neynar products by sharing usage data from your mini app?',
|
||||
default: true
|
||||
}
|
||||
message:
|
||||
'Would you like to help improve Neynar products by sharing usage data from your mini app?',
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
answers.enableAnalytics = analyticsAnswer.enableAnalytics;
|
||||
}
|
||||
@@ -337,19 +358,19 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
try {
|
||||
console.log(`\nCloning repository from ${REPO_URL}...`);
|
||||
// Use separate commands for better cross-platform compatibility
|
||||
execSync(`git clone ${REPO_URL} "${projectPath}"`, {
|
||||
execSync(`git clone ${REPO_URL} "${projectPath}"`, {
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
shell: process.platform === 'win32',
|
||||
});
|
||||
execSync('git fetch origin main', {
|
||||
cwd: projectPath,
|
||||
execSync('git fetch origin main', {
|
||||
cwd: projectPath,
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
shell: process.platform === 'win32',
|
||||
});
|
||||
execSync('git reset --hard origin/main', {
|
||||
cwd: projectPath,
|
||||
execSync('git reset --hard origin/main', {
|
||||
cwd: projectPath,
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
shell: process.platform === 'win32',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('\n❌ Error: Failed to create project directory.');
|
||||
@@ -386,47 +407,52 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
|
||||
// Add dependencies
|
||||
packageJson.dependencies = {
|
||||
"@farcaster/auth-client": ">=0.3.0 <1.0.0",
|
||||
"@farcaster/auth-kit": ">=0.6.0 <1.0.0",
|
||||
"@farcaster/frame-core": ">=0.0.29 <1.0.0",
|
||||
"@farcaster/frame-node": ">=0.0.18 <1.0.0",
|
||||
"@farcaster/frame-sdk": ">=0.0.31 <1.0.0",
|
||||
"@farcaster/frame-wagmi-connector": ">=0.0.19 <1.0.0",
|
||||
"@farcaster/mini-app-solana": ">=0.0.17 <1.0.0",
|
||||
"@neynar/react": "^1.2.5",
|
||||
"@radix-ui/react-label": "^2.1.1",
|
||||
"@solana/wallet-adapter-react": "^0.15.38",
|
||||
"@tanstack/react-query": "^5.61.0",
|
||||
"@upstash/redis": "^1.34.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"dotenv": "^16.4.7",
|
||||
"lucide-react": "^0.469.0",
|
||||
"mipd": "^0.0.7",
|
||||
"next": "^15",
|
||||
"next-auth": "^4.24.11",
|
||||
"react": "^19",
|
||||
"react-dom": "^19",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"viem": "^2.23.6",
|
||||
"wagmi": "^2.14.12",
|
||||
"zod": "^3.24.2"
|
||||
'@farcaster/auth-client': '>=0.3.0 <1.0.0',
|
||||
'@farcaster/auth-kit': '>=0.6.0 <1.0.0',
|
||||
'@farcaster/frame-core': '>=0.0.29 <1.0.0',
|
||||
'@farcaster/frame-node': '>=0.0.18 <1.0.0',
|
||||
'@farcaster/frame-sdk': '>=0.0.31 <1.0.0',
|
||||
'@farcaster/frame-wagmi-connector': '>=0.0.19 <1.0.0',
|
||||
'@farcaster/mini-app-solana': '>=0.0.17 <1.0.0',
|
||||
'@neynar/react': '^1.2.5',
|
||||
'@radix-ui/react-label': '^2.1.1',
|
||||
'@solana/wallet-adapter-react': '^0.15.38',
|
||||
'@tanstack/react-query': '^5.61.0',
|
||||
'@upstash/redis': '^1.34.3',
|
||||
'class-variance-authority': '^0.7.1',
|
||||
clsx: '^2.1.1',
|
||||
dotenv: '^16.4.7',
|
||||
'lucide-react': '^0.469.0',
|
||||
mipd: '^0.0.7',
|
||||
next: '^15',
|
||||
'next-auth': '^4.24.11',
|
||||
react: '^19',
|
||||
'react-dom': '^19',
|
||||
'tailwind-merge': '^2.6.0',
|
||||
'tailwindcss-animate': '^1.0.7',
|
||||
viem: '^2.23.6',
|
||||
wagmi: '^2.14.12',
|
||||
zod: '^3.24.2',
|
||||
};
|
||||
|
||||
packageJson.devDependencies = {
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@vercel/sdk": "^1.9.0",
|
||||
"crypto": "^1.0.1",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "15.0.3",
|
||||
"localtunnel": "^2.0.2",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5"
|
||||
'@types/node': '^20',
|
||||
'@types/react': '^19',
|
||||
'@types/react-dom': '^19',
|
||||
'@typescript-eslint/eslint-plugin': '^8.0.0',
|
||||
'@typescript-eslint/parser': '^8.0.0',
|
||||
'@vercel/sdk': '^1.9.0',
|
||||
crypto: '^1.0.1',
|
||||
eslint: '^8.57.0',
|
||||
'eslint-config-next': '15.0.3',
|
||||
'eslint-config-prettier': '^9.1.0',
|
||||
'eslint-plugin-prettier': '^5.2.1',
|
||||
localtunnel: '^2.0.2',
|
||||
'pino-pretty': '^13.0.0',
|
||||
postcss: '^8',
|
||||
prettier: '^3.3.3',
|
||||
tailwindcss: '^3.4.1',
|
||||
typescript: '^5',
|
||||
};
|
||||
|
||||
// Add Neynar SDK if selected
|
||||
@@ -434,6 +460,15 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
packageJson.dependencies['@neynar/nodejs-sdk'] = '^2.19.0';
|
||||
}
|
||||
|
||||
// Update scripts with formatting and linting
|
||||
packageJson.scripts = {
|
||||
...packageJson.scripts,
|
||||
'lint:fix': 'next lint --fix',
|
||||
format: 'prettier --write .',
|
||||
'format:check': 'prettier --check .',
|
||||
'type-check': 'tsc --noEmit',
|
||||
};
|
||||
|
||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
||||
|
||||
// Handle .env file
|
||||
@@ -452,35 +487,43 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
const constantsPath = path.join(projectPath, 'src', 'lib', 'constants.ts');
|
||||
if (fs.existsSync(constantsPath)) {
|
||||
let constantsContent = fs.readFileSync(constantsPath, 'utf8');
|
||||
|
||||
|
||||
// Helper function to escape single quotes in strings
|
||||
const escapeString = (str) => str.replace(/'/g, "\\'");
|
||||
|
||||
const escapeString = str => str.replace(/'/g, "\\'");
|
||||
|
||||
// Helper function to safely replace constants with validation
|
||||
const safeReplace = (content, pattern, replacement, constantName) => {
|
||||
const match = content.match(pattern);
|
||||
if (!match) {
|
||||
console.log(`⚠️ Warning: Could not update ${constantName} in constants.ts. Pattern not found.`);
|
||||
console.log(
|
||||
`⚠️ Warning: Could not update ${constantName} in constants.ts. Pattern not found.`
|
||||
);
|
||||
console.log(`Pattern: ${pattern}`);
|
||||
console.log(`Expected to match in: ${content.split('\n').find(line => line.includes(constantName)) || 'Not found'}`);
|
||||
console.log(
|
||||
`Expected to match in: ${content.split('\n').find(line => line.includes(constantName)) || 'Not found'}`
|
||||
);
|
||||
} else {
|
||||
const newContent = content.replace(pattern, replacement);
|
||||
return newContent;
|
||||
}
|
||||
return content;
|
||||
};
|
||||
|
||||
|
||||
// Regex patterns that match whole lines with export const
|
||||
const patterns = {
|
||||
APP_NAME: /^export const APP_NAME\s*=\s*['"`][^'"`]*['"`];$/m,
|
||||
APP_DESCRIPTION: /^export const APP_DESCRIPTION\s*=\s*['"`][^'"`]*['"`];$/m,
|
||||
APP_PRIMARY_CATEGORY: /^export const APP_PRIMARY_CATEGORY\s*=\s*['"`][^'"`]*['"`];$/m,
|
||||
APP_DESCRIPTION:
|
||||
/^export const APP_DESCRIPTION\s*=\s*['"`][^'"`]*['"`];$/m,
|
||||
APP_PRIMARY_CATEGORY:
|
||||
/^export const APP_PRIMARY_CATEGORY\s*=\s*['"`][^'"`]*['"`];$/m,
|
||||
APP_TAGS: /^export const APP_TAGS\s*=\s*\[[^\]]*\];$/m,
|
||||
APP_BUTTON_TEXT: /^export const APP_BUTTON_TEXT\s*=\s*['"`][^'"`]*['"`];$/m,
|
||||
APP_BUTTON_TEXT:
|
||||
/^export const APP_BUTTON_TEXT\s*=\s*['"`][^'"`]*['"`];$/m,
|
||||
USE_WALLET: /^export const USE_WALLET\s*=\s*(true|false);$/m,
|
||||
ANALYTICS_ENABLED: /^export const ANALYTICS_ENABLED\s*=\s*(true|false);$/m
|
||||
ANALYTICS_ENABLED:
|
||||
/^export const ANALYTICS_ENABLED\s*=\s*(true|false);$/m,
|
||||
};
|
||||
|
||||
|
||||
// Update APP_NAME
|
||||
constantsContent = safeReplace(
|
||||
constantsContent,
|
||||
@@ -488,7 +531,7 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
`export const APP_NAME = '${escapeString(answers.projectName)}';`,
|
||||
'APP_NAME'
|
||||
);
|
||||
|
||||
|
||||
// Update APP_DESCRIPTION
|
||||
constantsContent = safeReplace(
|
||||
constantsContent,
|
||||
@@ -496,7 +539,7 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
`export const APP_DESCRIPTION = '${escapeString(answers.description)}';`,
|
||||
'APP_DESCRIPTION'
|
||||
);
|
||||
|
||||
|
||||
// Update APP_PRIMARY_CATEGORY (always update, null becomes empty string)
|
||||
constantsContent = safeReplace(
|
||||
constantsContent,
|
||||
@@ -504,18 +547,19 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
`export const APP_PRIMARY_CATEGORY = '${escapeString(answers.primaryCategory || '')}';`,
|
||||
'APP_PRIMARY_CATEGORY'
|
||||
);
|
||||
|
||||
|
||||
// Update APP_TAGS
|
||||
const tagsString = answers.tags.length > 0
|
||||
? `['${answers.tags.map(tag => escapeString(tag)).join("', '")}']`
|
||||
: "['neynar', 'starter-kit', 'demo']";
|
||||
const tagsString =
|
||||
answers.tags.length > 0
|
||||
? `['${answers.tags.map(tag => escapeString(tag)).join("', '")}']`
|
||||
: "['neynar', 'starter-kit', 'demo']";
|
||||
constantsContent = safeReplace(
|
||||
constantsContent,
|
||||
patterns.APP_TAGS,
|
||||
`export const APP_TAGS = ${tagsString};`,
|
||||
'APP_TAGS'
|
||||
);
|
||||
|
||||
|
||||
// Update APP_BUTTON_TEXT (always update, use answers value)
|
||||
constantsContent = safeReplace(
|
||||
constantsContent,
|
||||
@@ -523,7 +567,7 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
`export const APP_BUTTON_TEXT = '${escapeString(answers.buttonText || '')}';`,
|
||||
'APP_BUTTON_TEXT'
|
||||
);
|
||||
|
||||
|
||||
// Update USE_WALLET
|
||||
constantsContent = safeReplace(
|
||||
constantsContent,
|
||||
@@ -531,7 +575,7 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
`export const USE_WALLET = ${answers.useWallet};`,
|
||||
'USE_WALLET'
|
||||
);
|
||||
|
||||
|
||||
// Update ANALYTICS_ENABLED
|
||||
constantsContent = safeReplace(
|
||||
constantsContent,
|
||||
@@ -539,24 +583,31 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
`export const ANALYTICS_ENABLED = ${answers.enableAnalytics};`,
|
||||
'ANALYTICS_ENABLED'
|
||||
);
|
||||
|
||||
|
||||
fs.writeFileSync(constantsPath, constantsContent);
|
||||
} else {
|
||||
console.log('⚠️ constants.ts not found, skipping constants update');
|
||||
}
|
||||
|
||||
fs.appendFileSync(envPath, `\nNEXTAUTH_SECRET="${crypto.randomBytes(32).toString('hex')}"`);
|
||||
fs.appendFileSync(
|
||||
envPath,
|
||||
`\nNEXTAUTH_SECRET="${crypto.randomBytes(32).toString('hex')}"`
|
||||
);
|
||||
if (useNeynar && neynarApiKey && neynarClientId) {
|
||||
fs.appendFileSync(envPath, `\nNEYNAR_API_KEY="${neynarApiKey}"`);
|
||||
fs.appendFileSync(envPath, `\nNEYNAR_CLIENT_ID="${neynarClientId}"`);
|
||||
} else if (useNeynar) {
|
||||
console.log('\n⚠️ Could not find a Neynar client ID and/or API key. Please configure Neynar manually in .env.local with NEYNAR_API_KEY and NEYNAR_CLIENT_ID');
|
||||
console.log(
|
||||
'\n⚠️ Could not find a Neynar client ID and/or API key. Please configure Neynar manually in .env.local with NEYNAR_API_KEY and NEYNAR_CLIENT_ID'
|
||||
);
|
||||
}
|
||||
fs.appendFileSync(envPath, `\nUSE_TUNNEL="${answers.useTunnel}"`);
|
||||
|
||||
|
||||
fs.unlinkSync(envExamplePath);
|
||||
} else {
|
||||
console.log('\n.env.example does not exist, skipping copy and remove operations');
|
||||
console.log(
|
||||
'\n.env.example does not exist, skipping copy and remove operations'
|
||||
);
|
||||
}
|
||||
|
||||
// Update README
|
||||
@@ -564,7 +615,9 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
const readmePath = path.join(projectPath, 'README.md');
|
||||
const prependText = `<!-- generated by @neynar/create-farcaster-mini-app version ${SCRIPT_VERSION} -->\n\n`;
|
||||
if (fs.existsSync(readmePath)) {
|
||||
const originalReadmeContent = fs.readFileSync(readmePath, { encoding: 'utf8' });
|
||||
const originalReadmeContent = fs.readFileSync(readmePath, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
const updatedReadmeContent = prependText + originalReadmeContent;
|
||||
fs.writeFileSync(readmePath, updatedReadmeContent);
|
||||
} else {
|
||||
@@ -574,15 +627,15 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
// Install dependencies
|
||||
console.log('\nInstalling dependencies...');
|
||||
|
||||
execSync('npm cache clean --force', {
|
||||
cwd: projectPath,
|
||||
execSync('npm cache clean --force', {
|
||||
cwd: projectPath,
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
shell: process.platform === 'win32',
|
||||
});
|
||||
execSync('npm install', {
|
||||
cwd: projectPath,
|
||||
execSync('npm install', {
|
||||
cwd: projectPath,
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
shell: process.platform === 'win32',
|
||||
});
|
||||
|
||||
// Remove the bin directory
|
||||
@@ -596,12 +649,15 @@ export async function init(projectName = null, autoAcceptDefaults = false) {
|
||||
console.log('\nInitializing git repository...');
|
||||
execSync('git init', { cwd: projectPath });
|
||||
execSync('git add .', { cwd: projectPath });
|
||||
execSync('git commit -m "initial commit from @neynar/create-farcaster-mini-app"', { cwd: projectPath });
|
||||
execSync(
|
||||
'git commit -m "initial commit from @neynar/create-farcaster-mini-app"',
|
||||
{ cwd: projectPath }
|
||||
);
|
||||
|
||||
// Calculate border length based on message length
|
||||
const message = `✨🪐 Successfully created mini app ${finalProjectName} with git and dependencies installed! 🪐✨`;
|
||||
const borderLength = message.length;
|
||||
const borderStars = '✨'.repeat((borderLength / 2) + 1);
|
||||
const borderStars = '✨'.repeat(borderLength / 2 + 1);
|
||||
|
||||
console.log(`\n${borderStars}`);
|
||||
console.log(`${message}`);
|
||||
|
||||
Reference in New Issue
Block a user