create-efc-app 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ async function scaffold(opts) {
23
23
  }
24
24
  async function writePackageJson(dest, opts) {
25
25
  const deps = {
26
- "express-file-cluster": "^0.1.0"
26
+ "express-file-cluster": "^0.1.1"
27
27
  };
28
28
  if (opts.database === "mongodb") deps["mongoose"] = "^8.0.0";
29
29
  if (opts.database === "postgresql") {
@@ -135,12 +135,14 @@ NODE_ENV=development
135
135
  DATABASE_URL=
136
136
  JWT_SECRET=${secret}
137
137
  REDIS_URL=redis://localhost:6379
138
+ CORS_ORIGINS=http://localhost:3000
138
139
  `;
139
140
  const example = `PORT=3000
140
141
  NODE_ENV=development
141
142
  DATABASE_URL=
142
143
  JWT_SECRET=<generate with: openssl rand -hex 64>
143
144
  REDIS_URL=redis://localhost:6379
145
+ CORS_ORIGINS=http://localhost:3000,https://yourapp.com
144
146
  `;
145
147
  await fs.outputFile(path.join(dest, ".env"), dotenv);
146
148
  await fs.outputFile(path.join(dest, ".env.example"), example);
@@ -286,7 +288,7 @@ async function main() {
286
288
  Your project is ready!
287
289
 
288
290
  `) + chalk.dim(` cd ${projectName}
289
- `) + chalk.dim(` npm run dev
291
+ `) + chalk.dim(` efc start dev
290
292
  `)
291
293
  );
292
294
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/scaffold.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport chalk from 'chalk';\nimport { spawn } from 'node:child_process';\nimport { scaffold, type ScaffoldOptions } from './scaffold.js';\n\nasync function main(): Promise<void> {\n console.log();\n p.intro(chalk.bgCyan(chalk.black(' create-efc-app ')));\n\n const projectName = await p.text({\n message: 'Project name:',\n placeholder: 'my-api',\n defaultValue: 'my-api',\n validate: (v) => (!v.trim() ? 'Project name is required' : undefined),\n });\n if (p.isCancel(projectName)) { p.cancel('Cancelled'); process.exit(0); }\n\n const language = await p.select({\n message: 'Language:',\n options: [\n { value: 'typescript', label: 'TypeScript', hint: 'recommended' },\n { value: 'javascript', label: 'JavaScript' },\n ],\n });\n if (p.isCancel(language)) { p.cancel('Cancelled'); process.exit(0); }\n\n const database = await p.select({\n message: 'Database:',\n options: [\n { value: 'mongodb', label: 'MongoDB', hint: 'Mongoose' },\n { value: 'postgresql', label: 'PostgreSQL', hint: 'Drizzle ORM' },\n ],\n });\n if (p.isCancel(database)) { p.cancel('Cancelled'); process.exit(0); }\n\n const authStrategy = await p.select({\n message: 'Authentication strategy:',\n options: [\n { value: 'http-only', label: 'http-only', hint: 'secure cookie — recommended for SSR' },\n { value: 'localStorage', label: 'localStorage', hint: 'bearer token — for SPAs' },\n ],\n });\n if (p.isCancel(authStrategy)) { p.cancel('Cancelled'); process.exit(0); }\n\n const cluster = await p.confirm({\n message: 'Enable multi-core clustering?',\n initialValue: true,\n });\n if (p.isCancel(cluster)) { p.cancel('Cancelled'); process.exit(0); }\n\n const tasks = await p.confirm({\n message: 'Enable background tasks?',\n initialValue: true,\n });\n if (p.isCancel(tasks)) { p.cancel('Cancelled'); process.exit(0); }\n\n let taskBackend: ScaffoldOptions['taskBackend'];\n if (tasks) {\n const backend = await p.select({\n message: 'Task queue backend:',\n options: [\n { value: 'bullmq', label: 'BullMQ', hint: 'Redis' },\n { value: 'pg-boss', label: 'pg-boss', hint: 'PostgreSQL' },\n ],\n });\n if (p.isCancel(backend)) { p.cancel('Cancelled'); process.exit(0); }\n taskBackend = backend as ScaffoldOptions['taskBackend'];\n }\n\n const opts: ScaffoldOptions = {\n projectName: projectName as string,\n language: language as ScaffoldOptions['language'],\n database: database as ScaffoldOptions['database'],\n authStrategy: authStrategy as ScaffoldOptions['authStrategy'],\n cluster: cluster as boolean,\n tasks: tasks as boolean,\n ...(taskBackend !== undefined && { taskBackend }),\n };\n\n const spinner = p.spinner();\n spinner.start('Scaffolding project…');\n\n try {\n await scaffold(opts);\n spinner.stop('Project created');\n } catch (err) {\n spinner.stop('Failed to scaffold project');\n console.error(err);\n process.exit(1);\n }\n\n spinner.start('Installing dependencies…');\n await npmInstall(projectName as string);\n spinner.stop('Dependencies installed');\n\n p.outro(\n chalk.green(`\\nYour project is ready!\\n\\n`) +\n chalk.dim(` cd ${projectName as string}\\n`) +\n chalk.dim(` npm run dev\\n`),\n );\n}\n\nfunction npmInstall(projectDir: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn('npm', ['install'], {\n cwd: projectDir,\n stdio: 'ignore',\n });\n child.on('exit', (code) => (code === 0 ? resolve() : reject(new Error(`npm install failed`))));\n });\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","import fs from 'fs-extra';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\n\nexport interface ScaffoldOptions {\n projectName: string;\n language: 'typescript' | 'javascript';\n database: 'mongodb' | 'postgresql';\n authStrategy: 'http-only' | 'localStorage';\n cluster: boolean;\n tasks: boolean;\n taskBackend?: 'bullmq' | 'pg-boss';\n}\n\nexport async function scaffold(opts: ScaffoldOptions): Promise<void> {\n const dest = path.resolve(process.cwd(), opts.projectName);\n await fs.ensureDir(dest);\n\n await writePackageJson(dest, opts);\n await writeTsConfig(dest, opts);\n await writeEfcConfig(dest, opts);\n await writeEntryPoint(dest, opts);\n await writeGitignore(dest);\n await writeEnvFiles(dest);\n await writeExampleRoute(dest, opts);\n if (opts.tasks) await writeExampleTask(dest, opts);\n}\n\nasync function writePackageJson(dest: string, opts: ScaffoldOptions): Promise<void> {\n const deps: Record<string, string> = {\n 'express-file-cluster': '^0.1.0',\n };\n if (opts.database === 'mongodb') deps['mongoose'] = '^8.0.0';\n if (opts.database === 'postgresql') {\n deps['pg'] = '^8.0.0';\n deps['drizzle-orm'] = '^0.33.0';\n }\n if (opts.tasks && opts.taskBackend === 'bullmq') deps['bullmq'] = '^5.0.0';\n if (opts.tasks && opts.taskBackend === 'pg-boss') deps['pg-boss'] = '^10.0.0';\n\n const devDeps: Record<string, string> = {\n vitest: '^2.0.0',\n };\n if (opts.language === 'typescript') {\n devDeps['typescript'] = '^5.5.0';\n devDeps['@types/node'] = '^22.0.0';\n devDeps['@types/express'] = '^4.17.21';\n devDeps['tsup'] = '^8.2.0';\n devDeps['tsx'] = '^4.0.0';\n }\n\n const pkg = {\n name: opts.projectName,\n version: '0.1.0',\n type: 'module',\n scripts: {\n dev: 'efc start dev',\n build: 'efc build prod',\n start: 'efc start prod',\n test: 'efc run tests',\n },\n dependencies: deps,\n devDependencies: devDeps,\n };\n\n await fs.writeJson(path.join(dest, 'package.json'), pkg, { spaces: 2 });\n}\n\nasync function writeTsConfig(dest: string, opts: ScaffoldOptions): Promise<void> {\n if (opts.language !== 'typescript') return;\n const config = {\n compilerOptions: {\n target: 'ES2022',\n module: 'NodeNext',\n moduleResolution: 'NodeNext',\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n outDir: './dist',\n rootDir: './src',\n },\n include: ['src/**/*'],\n exclude: ['node_modules', 'dist'],\n };\n await fs.writeJson(path.join(dest, 'tsconfig.json'), config, { spaces: 2 });\n}\n\nasync function writeEfcConfig(dest: string, opts: ScaffoldOptions): Promise<void> {\n const ext = opts.language === 'typescript' ? 'ts' : 'js';\n const tasks = opts.tasks\n ? `{\n backend: '${opts.taskBackend ?? 'bullmq'}',\n redisUrl: process.env.REDIS_URL,\n concurrency: 5,\n }`\n : 'false';\n\n const content = `import type { EFCConfig } from 'express-file-cluster';\n\nconst config: EFCConfig = {\n port: Number(process.env.PORT) || 3000,\n apiDir: './src/api',\n tasksDir: './src/tasks',\n database: '${opts.database}',\n databaseUrl: process.env.DATABASE_URL!,\n authStrategy: '${opts.authStrategy}',\n jwtSecret: process.env.JWT_SECRET!,\n cluster: process.env.NODE_ENV === 'production',\n tasks: ${tasks},\n globalMiddlewares: [],\n};\n\nexport default config;\n`;\n await fs.outputFile(path.join(dest, `efc.config.${ext}`), content);\n}\n\nasync function writeEntryPoint(dest: string, opts: ScaffoldOptions): Promise<void> {\n const ext = opts.language === 'typescript' ? 'ts' : 'js';\n const content = `import { ignite } from 'express-file-cluster';\nimport { fileURLToPath } from 'url';\nimport path from 'path';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nignite({\n port: Number(process.env.PORT) || 3000,\n apiDir: path.join(__dirname, 'api'),\n tasksDir: path.join(__dirname, 'tasks'),\n database: '${opts.database}',\n databaseUrl: process.env.DATABASE_URL,\n authStrategy: '${opts.authStrategy}',\n jwtSecret: process.env.JWT_SECRET,\n cluster: process.env.NODE_ENV === 'production',\n}).catch(console.error);\n`;\n await fs.outputFile(path.join(dest, 'src', `index.${ext}`), content);\n}\n\nasync function writeGitignore(dest: string): Promise<void> {\n await fs.outputFile(\n path.join(dest, '.gitignore'),\n 'node_modules/\\ndist/\\n.env\\n.env.local\\n*.log\\n',\n );\n}\n\nasync function writeEnvFiles(dest: string): Promise<void> {\n const secret = crypto.randomBytes(64).toString('hex');\n const dotenv = `PORT=3000\\nNODE_ENV=development\\nDATABASE_URL=\\nJWT_SECRET=${secret}\\nREDIS_URL=redis://localhost:6379\\n`;\n const example = `PORT=3000\\nNODE_ENV=development\\nDATABASE_URL=\\nJWT_SECRET=<generate with: openssl rand -hex 64>\\nREDIS_URL=redis://localhost:6379\\n`;\n await fs.outputFile(path.join(dest, '.env'), dotenv);\n await fs.outputFile(path.join(dest, '.env.example'), example);\n}\n\nasync function writeExampleRoute(dest: string, opts: ScaffoldOptions): Promise<void> {\n const ext = opts.language === 'typescript' ? 'ts' : 'js';\n const content =\n opts.language === 'typescript'\n ? `import type { Request, Response } from 'express';\n\nexport const GET = async (_req: Request, res: Response) => {\n res.json({ status: 'OK', timestamp: new Date().toISOString() });\n};\n`\n : `export const GET = async (_req, res) => {\n res.json({ status: 'OK', timestamp: new Date().toISOString() });\n};\n`;\n await fs.outputFile(path.join(dest, 'src', 'api', `health.${ext}`), content);\n}\n\nasync function writeExampleTask(dest: string, opts: ScaffoldOptions): Promise<void> {\n const ext = opts.language === 'typescript' ? 'ts' : 'js';\n const content =\n opts.language === 'typescript'\n ? `import { defineTask } from 'express-file-cluster/tasks';\n\ninterface SendEmailPayload {\n to: string;\n subject: string;\n body: string;\n}\n\nexport default defineTask<SendEmailPayload>(async (payload) => {\n // TODO: wire up your mailer\n console.log('[SendEmail] Sending to', payload.to);\n});\n`\n : `import { defineTask } from 'express-file-cluster/tasks';\n\nexport default defineTask(async (payload) => {\n console.log('[SendEmail] Sending to', payload.to);\n});\n`;\n await fs.outputFile(path.join(dest, 'src', 'tasks', `SendEmail.${ext}`), content);\n}\n"],"mappings":"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","names":["spinner"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/scaffold.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport chalk from 'chalk';\nimport { spawn } from 'node:child_process';\nimport { scaffold, type ScaffoldOptions } from './scaffold.js';\n\nasync function main(): Promise<void> {\n console.log();\n p.intro(chalk.bgCyan(chalk.black(' create-efc-app ')));\n\n const projectName = await p.text({\n message: 'Project name:',\n placeholder: 'my-api',\n defaultValue: 'my-api',\n validate: (v) => (!v.trim() ? 'Project name is required' : undefined),\n });\n if (p.isCancel(projectName)) { p.cancel('Cancelled'); process.exit(0); }\n\n const language = await p.select({\n message: 'Language:',\n options: [\n { value: 'typescript', label: 'TypeScript', hint: 'recommended' },\n { value: 'javascript', label: 'JavaScript' },\n ],\n });\n if (p.isCancel(language)) { p.cancel('Cancelled'); process.exit(0); }\n\n const database = await p.select({\n message: 'Database:',\n options: [\n { value: 'mongodb', label: 'MongoDB', hint: 'Mongoose' },\n { value: 'postgresql', label: 'PostgreSQL', hint: 'Drizzle ORM' },\n ],\n });\n if (p.isCancel(database)) { p.cancel('Cancelled'); process.exit(0); }\n\n const authStrategy = await p.select({\n message: 'Authentication strategy:',\n options: [\n { value: 'http-only', label: 'http-only', hint: 'secure cookie — recommended for SSR' },\n { value: 'localStorage', label: 'localStorage', hint: 'bearer token — for SPAs' },\n ],\n });\n if (p.isCancel(authStrategy)) { p.cancel('Cancelled'); process.exit(0); }\n\n const cluster = await p.confirm({\n message: 'Enable multi-core clustering?',\n initialValue: true,\n });\n if (p.isCancel(cluster)) { p.cancel('Cancelled'); process.exit(0); }\n\n const tasks = await p.confirm({\n message: 'Enable background tasks?',\n initialValue: true,\n });\n if (p.isCancel(tasks)) { p.cancel('Cancelled'); process.exit(0); }\n\n let taskBackend: ScaffoldOptions['taskBackend'];\n if (tasks) {\n const backend = await p.select({\n message: 'Task queue backend:',\n options: [\n { value: 'bullmq', label: 'BullMQ', hint: 'Redis' },\n { value: 'pg-boss', label: 'pg-boss', hint: 'PostgreSQL' },\n ],\n });\n if (p.isCancel(backend)) { p.cancel('Cancelled'); process.exit(0); }\n taskBackend = backend as ScaffoldOptions['taskBackend'];\n }\n\n const opts: ScaffoldOptions = {\n projectName: projectName as string,\n language: language as ScaffoldOptions['language'],\n database: database as ScaffoldOptions['database'],\n authStrategy: authStrategy as ScaffoldOptions['authStrategy'],\n cluster: cluster as boolean,\n tasks: tasks as boolean,\n ...(taskBackend !== undefined && { taskBackend }),\n };\n\n const spinner = p.spinner();\n spinner.start('Scaffolding project…');\n\n try {\n await scaffold(opts);\n spinner.stop('Project created');\n } catch (err) {\n spinner.stop('Failed to scaffold project');\n console.error(err);\n process.exit(1);\n }\n\n spinner.start('Installing dependencies…');\n await npmInstall(projectName as string);\n spinner.stop('Dependencies installed');\n\n p.outro(\n chalk.green(`\\nYour project is ready!\\n\\n`) +\n chalk.dim(` cd ${projectName as string}\\n`) +\n chalk.dim(` efc start dev\\n`),\n );\n}\n\nfunction npmInstall(projectDir: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn('npm', ['install'], {\n cwd: projectDir,\n stdio: 'ignore',\n });\n child.on('exit', (code) => (code === 0 ? resolve() : reject(new Error(`npm install failed`))));\n });\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","import fs from 'fs-extra';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\n\nexport interface ScaffoldOptions {\n projectName: string;\n language: 'typescript' | 'javascript';\n database: 'mongodb' | 'postgresql';\n authStrategy: 'http-only' | 'localStorage';\n cluster: boolean;\n tasks: boolean;\n taskBackend?: 'bullmq' | 'pg-boss';\n}\n\nexport async function scaffold(opts: ScaffoldOptions): Promise<void> {\n const dest = path.resolve(process.cwd(), opts.projectName);\n await fs.ensureDir(dest);\n\n await writePackageJson(dest, opts);\n await writeTsConfig(dest, opts);\n await writeEfcConfig(dest, opts);\n await writeEntryPoint(dest, opts);\n await writeGitignore(dest);\n await writeEnvFiles(dest);\n await writeExampleRoute(dest, opts);\n if (opts.tasks) await writeExampleTask(dest, opts);\n}\n\nasync function writePackageJson(dest: string, opts: ScaffoldOptions): Promise<void> {\n const deps: Record<string, string> = {\n 'express-file-cluster': '^0.1.1',\n };\n if (opts.database === 'mongodb') deps['mongoose'] = '^8.0.0';\n if (opts.database === 'postgresql') {\n deps['pg'] = '^8.0.0';\n deps['drizzle-orm'] = '^0.33.0';\n }\n if (opts.tasks && opts.taskBackend === 'bullmq') deps['bullmq'] = '^5.0.0';\n if (opts.tasks && opts.taskBackend === 'pg-boss') deps['pg-boss'] = '^10.0.0';\n\n const devDeps: Record<string, string> = {\n vitest: '^2.0.0',\n };\n if (opts.language === 'typescript') {\n devDeps['typescript'] = '^5.5.0';\n devDeps['@types/node'] = '^22.0.0';\n devDeps['@types/express'] = '^4.17.21';\n devDeps['tsup'] = '^8.2.0';\n devDeps['tsx'] = '^4.0.0';\n }\n\n const pkg = {\n name: opts.projectName,\n version: '0.1.0',\n type: 'module',\n scripts: {\n dev: 'efc start dev',\n build: 'efc build prod',\n start: 'efc start prod',\n test: 'efc run tests',\n },\n dependencies: deps,\n devDependencies: devDeps,\n };\n\n await fs.writeJson(path.join(dest, 'package.json'), pkg, { spaces: 2 });\n}\n\nasync function writeTsConfig(dest: string, opts: ScaffoldOptions): Promise<void> {\n if (opts.language !== 'typescript') return;\n const config = {\n compilerOptions: {\n target: 'ES2022',\n module: 'NodeNext',\n moduleResolution: 'NodeNext',\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n outDir: './dist',\n rootDir: './src',\n },\n include: ['src/**/*'],\n exclude: ['node_modules', 'dist'],\n };\n await fs.writeJson(path.join(dest, 'tsconfig.json'), config, { spaces: 2 });\n}\n\nasync function writeEfcConfig(dest: string, opts: ScaffoldOptions): Promise<void> {\n const ext = opts.language === 'typescript' ? 'ts' : 'js';\n const tasks = opts.tasks\n ? `{\n backend: '${opts.taskBackend ?? 'bullmq'}',\n redisUrl: process.env.REDIS_URL,\n concurrency: 5,\n }`\n : 'false';\n\n const content = `import type { EFCConfig } from 'express-file-cluster';\n\nconst config: EFCConfig = {\n port: Number(process.env.PORT) || 3000,\n apiDir: './src/api',\n tasksDir: './src/tasks',\n database: '${opts.database}',\n databaseUrl: process.env.DATABASE_URL!,\n authStrategy: '${opts.authStrategy}',\n jwtSecret: process.env.JWT_SECRET!,\n cluster: process.env.NODE_ENV === 'production',\n tasks: ${tasks},\n globalMiddlewares: [],\n};\n\nexport default config;\n`;\n await fs.outputFile(path.join(dest, `efc.config.${ext}`), content);\n}\n\nasync function writeEntryPoint(dest: string, opts: ScaffoldOptions): Promise<void> {\n const ext = opts.language === 'typescript' ? 'ts' : 'js';\n const content = `import { ignite } from 'express-file-cluster';\nimport { fileURLToPath } from 'url';\nimport path from 'path';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nignite({\n port: Number(process.env.PORT) || 3000,\n apiDir: path.join(__dirname, 'api'),\n tasksDir: path.join(__dirname, 'tasks'),\n database: '${opts.database}',\n databaseUrl: process.env.DATABASE_URL,\n authStrategy: '${opts.authStrategy}',\n jwtSecret: process.env.JWT_SECRET,\n cluster: process.env.NODE_ENV === 'production',\n}).catch(console.error);\n`;\n await fs.outputFile(path.join(dest, 'src', `index.${ext}`), content);\n}\n\nasync function writeGitignore(dest: string): Promise<void> {\n await fs.outputFile(\n path.join(dest, '.gitignore'),\n 'node_modules/\\ndist/\\n.env\\n.env.local\\n*.log\\n',\n );\n}\n\nasync function writeEnvFiles(dest: string): Promise<void> {\n const secret = crypto.randomBytes(64).toString('hex');\n const dotenv = `PORT=3000\\nNODE_ENV=development\\nDATABASE_URL=\\nJWT_SECRET=${secret}\\nREDIS_URL=redis://localhost:6379\\nCORS_ORIGINS=http://localhost:3000\\n`;\n const example = `PORT=3000\\nNODE_ENV=development\\nDATABASE_URL=\\nJWT_SECRET=<generate with: openssl rand -hex 64>\\nREDIS_URL=redis://localhost:6379\\nCORS_ORIGINS=http://localhost:3000,https://yourapp.com\\n`;\n await fs.outputFile(path.join(dest, '.env'), dotenv);\n await fs.outputFile(path.join(dest, '.env.example'), example);\n}\n\nasync function writeExampleRoute(dest: string, opts: ScaffoldOptions): Promise<void> {\n const ext = opts.language === 'typescript' ? 'ts' : 'js';\n const content =\n opts.language === 'typescript'\n ? `import type { Request, Response } from 'express';\n\nexport const GET = async (_req: Request, res: Response) => {\n res.json({ status: 'OK', timestamp: new Date().toISOString() });\n};\n`\n : `export const GET = async (_req, res) => {\n res.json({ status: 'OK', timestamp: new Date().toISOString() });\n};\n`;\n await fs.outputFile(path.join(dest, 'src', 'api', `health.${ext}`), content);\n}\n\nasync function writeExampleTask(dest: string, opts: ScaffoldOptions): Promise<void> {\n const ext = opts.language === 'typescript' ? 'ts' : 'js';\n const content =\n opts.language === 'typescript'\n ? `import { defineTask } from 'express-file-cluster/tasks';\n\ninterface SendEmailPayload {\n to: string;\n subject: string;\n body: string;\n}\n\nexport default defineTask<SendEmailPayload>(async (payload) => {\n // TODO: wire up your mailer\n console.log('[SendEmail] Sending to', payload.to);\n});\n`\n : `import { defineTask } from 'express-file-cluster/tasks';\n\nexport default defineTask(async (payload) => {\n console.log('[SendEmail] Sending to', payload.to);\n});\n`;\n await fs.outputFile(path.join(dest, 'src', 'tasks', `SendEmail.${ext}`), content);\n}\n"],"mappings":"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","names":["spinner"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-efc-app",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Interactive scaffolder for Express File Cluster projects",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/index.ts CHANGED
@@ -96,7 +96,7 @@ async function main(): Promise<void> {
96
96
  p.outro(
97
97
  chalk.green(`\nYour project is ready!\n\n`) +
98
98
  chalk.dim(` cd ${projectName as string}\n`) +
99
- chalk.dim(` npm run dev\n`),
99
+ chalk.dim(` efc start dev\n`),
100
100
  );
101
101
  }
102
102
 
package/src/scaffold.ts CHANGED
@@ -28,7 +28,7 @@ export async function scaffold(opts: ScaffoldOptions): Promise<void> {
28
28
 
29
29
  async function writePackageJson(dest: string, opts: ScaffoldOptions): Promise<void> {
30
30
  const deps: Record<string, string> = {
31
- 'express-file-cluster': '^0.1.0',
31
+ 'express-file-cluster': '^0.1.1',
32
32
  };
33
33
  if (opts.database === 'mongodb') deps['mongoose'] = '^8.0.0';
34
34
  if (opts.database === 'postgresql') {
@@ -146,8 +146,8 @@ async function writeGitignore(dest: string): Promise<void> {
146
146
 
147
147
  async function writeEnvFiles(dest: string): Promise<void> {
148
148
  const secret = crypto.randomBytes(64).toString('hex');
149
- const dotenv = `PORT=3000\nNODE_ENV=development\nDATABASE_URL=\nJWT_SECRET=${secret}\nREDIS_URL=redis://localhost:6379\n`;
150
- const example = `PORT=3000\nNODE_ENV=development\nDATABASE_URL=\nJWT_SECRET=<generate with: openssl rand -hex 64>\nREDIS_URL=redis://localhost:6379\n`;
149
+ const dotenv = `PORT=3000\nNODE_ENV=development\nDATABASE_URL=\nJWT_SECRET=${secret}\nREDIS_URL=redis://localhost:6379\nCORS_ORIGINS=http://localhost:3000\n`;
150
+ const example = `PORT=3000\nNODE_ENV=development\nDATABASE_URL=\nJWT_SECRET=<generate with: openssl rand -hex 64>\nREDIS_URL=redis://localhost:6379\nCORS_ORIGINS=http://localhost:3000,https://yourapp.com\n`;
151
151
  await fs.outputFile(path.join(dest, '.env'), dotenv);
152
152
  await fs.outputFile(path.join(dest, '.env.example'), example);
153
153
  }