create-time2ship 1.0.0
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/LICENSE +15 -0
- package/README.md +91 -0
- package/bin/cli.js +25 -0
- package/package.json +52 -0
- package/src/index.js +41 -0
- package/src/prompts.js +92 -0
- package/src/setup.js +177 -0
- package/src/templates.js +87 -0
- package/src/utils.js +106 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Time2Build
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# create-time2ship
|
|
2
|
+
|
|
3
|
+
Create a new Time2Ship full-stack application in seconds.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx create-time2ship my-app
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### With npx (recommended)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx create-time2ship my-app
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### With npm
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm create time2ship my-app
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### With yarn
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
yarn create time2ship my-app
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### With pnpm
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm create time2ship my-app
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## What You Get
|
|
38
|
+
|
|
39
|
+
Time2Ship is a production-ready full-stack monorepo featuring:
|
|
40
|
+
|
|
41
|
+
- โก **Next.js 16** with React 19
|
|
42
|
+
- ๐ก๏ธ **Express API** with TypeScript
|
|
43
|
+
- ๐๏ธ **PostgreSQL** with Drizzle ORM
|
|
44
|
+
- ๐ณ **Docker** ready
|
|
45
|
+
- ๐ **JWT Authentication** out of the box
|
|
46
|
+
- ๐งช **Comprehensive testing** setup
|
|
47
|
+
- ๐ง **Email service** with templates
|
|
48
|
+
- ๐จ **Tailwind CSS 4**
|
|
49
|
+
|
|
50
|
+
## Interactive Setup
|
|
51
|
+
|
|
52
|
+
The CLI will guide you through:
|
|
53
|
+
|
|
54
|
+
1. **Project Name** - Choose your project name
|
|
55
|
+
2. **Description** - Optional project description
|
|
56
|
+
3. **Package Manager** - npm, yarn, or pnpm
|
|
57
|
+
4. **Dependencies** - Install now or later
|
|
58
|
+
5. **Git** - Initialize git repository
|
|
59
|
+
6. **Environment** - Auto-generate .env files
|
|
60
|
+
|
|
61
|
+
## After Creation
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cd my-app
|
|
65
|
+
|
|
66
|
+
# Start with Docker (recommended)
|
|
67
|
+
docker-compose up
|
|
68
|
+
|
|
69
|
+
# Or run individually
|
|
70
|
+
docker-compose up db # Database
|
|
71
|
+
cd apps/api && npm run dev # API
|
|
72
|
+
cd apps/client && npm run dev # Client
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Your app will be available at:
|
|
76
|
+
- Frontend: http://localhost:3000
|
|
77
|
+
- API: http://localhost:3001
|
|
78
|
+
|
|
79
|
+
## Documentation
|
|
80
|
+
|
|
81
|
+
Full documentation available in your project's README.md
|
|
82
|
+
|
|
83
|
+
## Support
|
|
84
|
+
|
|
85
|
+
- ๐ [Documentation](https://github.com/time2build-ai/time2ship)
|
|
86
|
+
- ๐ [Report Issues](https://github.com/time2build-ai/time2ship/issues)
|
|
87
|
+
- ๐ฌ [Discussions](https://github.com/time2build-ai/time2ship/discussions)
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
ISC
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* create-time2ship CLI
|
|
5
|
+
* Entry point for the package
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const currentNodeVersion = process.versions.node;
|
|
9
|
+
const semver = currentNodeVersion.split('.');
|
|
10
|
+
const major = parseInt(semver[0], 10);
|
|
11
|
+
|
|
12
|
+
// Check Node.js version
|
|
13
|
+
if (major < 18) {
|
|
14
|
+
console.error(
|
|
15
|
+
'You are running Node ' +
|
|
16
|
+
currentNodeVersion +
|
|
17
|
+
'.\n' +
|
|
18
|
+
'create-time2ship requires Node 18 or higher.\n' +
|
|
19
|
+
'Please update your version of Node.'
|
|
20
|
+
);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Run the main CLI
|
|
25
|
+
require('../src/index.js');
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-time2ship",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create a new Time2Ship full-stack application in seconds",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-time2ship": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node test.js",
|
|
11
|
+
"lint": "eslint .",
|
|
12
|
+
"format": "prettier --write \"**/*.js\""
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"create-time2ship",
|
|
16
|
+
"time2ship",
|
|
17
|
+
"boilerplate",
|
|
18
|
+
"nextjs",
|
|
19
|
+
"express",
|
|
20
|
+
"fullstack",
|
|
21
|
+
"typescript",
|
|
22
|
+
"postgresql",
|
|
23
|
+
"monorepo",
|
|
24
|
+
"template",
|
|
25
|
+
"cli"
|
|
26
|
+
],
|
|
27
|
+
"author": "Thiago Lopez <thiago@time2build.ai>",
|
|
28
|
+
"license": "ISC",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/time2build-ai/create-time2ship.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/time2build-ai/create-time2ship/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/time2build-ai/create-time2ship#readme",
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"chalk": "^4.1.2",
|
|
42
|
+
"execa": "^5.1.1",
|
|
43
|
+
"fs-extra": "^11.1.1",
|
|
44
|
+
"ora": "^5.4.1",
|
|
45
|
+
"prompts": "^2.4.2",
|
|
46
|
+
"validate-npm-package-name": "^5.0.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"eslint": "^8.50.0",
|
|
50
|
+
"prettier": "^3.0.3"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const { runPrompts } = require('./prompts');
|
|
4
|
+
const { setupProject } = require('./setup');
|
|
5
|
+
const { printWelcome, printSuccess, printNextSteps } = require('./utils');
|
|
6
|
+
|
|
7
|
+
async function run() {
|
|
8
|
+
try {
|
|
9
|
+
// Print welcome message
|
|
10
|
+
printWelcome();
|
|
11
|
+
|
|
12
|
+
// Get user input
|
|
13
|
+
const config = await runPrompts();
|
|
14
|
+
|
|
15
|
+
if (!config) {
|
|
16
|
+
console.log(chalk.yellow('\nSetup cancelled.'));
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Setup the project
|
|
21
|
+
const projectPath = path.join(process.cwd(), config.projectName);
|
|
22
|
+
await setupProject(config, projectPath);
|
|
23
|
+
|
|
24
|
+
// Print success and next steps
|
|
25
|
+
printSuccess(config.projectName);
|
|
26
|
+
printNextSteps(config);
|
|
27
|
+
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(chalk.red('\nโ Error:'), error.message);
|
|
30
|
+
console.error(error.stack);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Handle unhandled rejections
|
|
36
|
+
process.on('unhandledRejection', (err) => {
|
|
37
|
+
console.error(chalk.red('\nUnhandled error:'), err);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
run();
|
package/src/prompts.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const prompts = require('prompts');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const validatePackageName = require('validate-npm-package-name');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Validate project name
|
|
9
|
+
*/
|
|
10
|
+
function validateProjectName(name) {
|
|
11
|
+
const validation = validatePackageName(name);
|
|
12
|
+
|
|
13
|
+
if (!validation.validForNewPackages) {
|
|
14
|
+
const errors = [
|
|
15
|
+
...(validation.errors || []),
|
|
16
|
+
...(validation.warnings || [])
|
|
17
|
+
];
|
|
18
|
+
return errors[0] || 'Invalid project name';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Check if directory already exists
|
|
22
|
+
const targetDir = path.join(process.cwd(), name);
|
|
23
|
+
if (fs.existsSync(targetDir)) {
|
|
24
|
+
return `Directory "${name}" already exists. Please choose a different name.`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Run interactive prompts
|
|
32
|
+
*/
|
|
33
|
+
async function runPrompts() {
|
|
34
|
+
const questions = [
|
|
35
|
+
{
|
|
36
|
+
type: 'text',
|
|
37
|
+
name: 'projectName',
|
|
38
|
+
message: 'What is your project name?',
|
|
39
|
+
initial: 'my-time2ship-app',
|
|
40
|
+
validate: validateProjectName
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'text',
|
|
44
|
+
name: 'description',
|
|
45
|
+
message: 'Project description (optional):',
|
|
46
|
+
initial: 'A Time2Ship application'
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: 'select',
|
|
50
|
+
name: 'packageManager',
|
|
51
|
+
message: 'Which package manager do you want to use?',
|
|
52
|
+
choices: [
|
|
53
|
+
{ title: 'npm', value: 'npm' },
|
|
54
|
+
{ title: 'yarn', value: 'yarn' },
|
|
55
|
+
{ title: 'pnpm', value: 'pnpm' }
|
|
56
|
+
],
|
|
57
|
+
initial: 0
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
type: 'confirm',
|
|
61
|
+
name: 'installDeps',
|
|
62
|
+
message: 'Install dependencies now?',
|
|
63
|
+
initial: true
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
type: 'confirm',
|
|
67
|
+
name: 'initGit',
|
|
68
|
+
message: 'Initialize a git repository?',
|
|
69
|
+
initial: true
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: 'confirm',
|
|
73
|
+
name: 'setupEnv',
|
|
74
|
+
message: 'Create environment files (.env)?',
|
|
75
|
+
initial: true
|
|
76
|
+
}
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
const response = await prompts(questions, {
|
|
80
|
+
onCancel: () => {
|
|
81
|
+
console.log(chalk.red('\nโ Setup cancelled'));
|
|
82
|
+
process.exit(0);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return response;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = {
|
|
90
|
+
runPrompts,
|
|
91
|
+
validateProjectName
|
|
92
|
+
};
|
package/src/setup.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const ora = require('ora');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
const { execa } = require('execa');
|
|
7
|
+
const { generateEnvFiles } = require('./templates');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Clone the Time2Ship repository
|
|
11
|
+
*/
|
|
12
|
+
async function cloneRepository(targetDir) {
|
|
13
|
+
const spinner = ora('Cloning Time2Ship boilerplate...').start();
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
// Clone from GitHub
|
|
17
|
+
execSync(
|
|
18
|
+
`git clone --depth 1 https://github.com/time2build-ai/time2ship.git "${targetDir}"`,
|
|
19
|
+
{ stdio: 'pipe' }
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// Remove .git directory
|
|
23
|
+
await fs.remove(path.join(targetDir, '.git'));
|
|
24
|
+
|
|
25
|
+
spinner.succeed('Boilerplate cloned successfully');
|
|
26
|
+
} catch (error) {
|
|
27
|
+
spinner.fail('Failed to clone repository');
|
|
28
|
+
throw new Error(`Git clone failed: ${error.message}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Install dependencies
|
|
34
|
+
*/
|
|
35
|
+
async function installDependencies(projectPath, packageManager) {
|
|
36
|
+
const spinner = ora(`Installing dependencies with ${packageManager}...`).start();
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
await execa(packageManager, ['install'], {
|
|
40
|
+
cwd: projectPath,
|
|
41
|
+
stdio: 'pipe'
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
spinner.succeed('Dependencies installed');
|
|
45
|
+
} catch (error) {
|
|
46
|
+
spinner.fail('Failed to install dependencies');
|
|
47
|
+
console.log(chalk.yellow('\nYou can install dependencies manually later by running:'));
|
|
48
|
+
console.log(chalk.cyan(` cd ${path.basename(projectPath)}`));
|
|
49
|
+
console.log(chalk.cyan(` ${packageManager} install\n`));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Initialize git repository
|
|
55
|
+
*/
|
|
56
|
+
async function initializeGit(projectPath) {
|
|
57
|
+
const spinner = ora('Initializing git repository...').start();
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
execSync('git init', { cwd: projectPath, stdio: 'pipe' });
|
|
61
|
+
execSync('git add .', { cwd: projectPath, stdio: 'pipe' });
|
|
62
|
+
execSync('git commit -m "Initial commit from create-time2ship"', {
|
|
63
|
+
cwd: projectPath,
|
|
64
|
+
stdio: 'pipe'
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
spinner.succeed('Git repository initialized');
|
|
68
|
+
} catch (error) {
|
|
69
|
+
spinner.fail('Failed to initialize git');
|
|
70
|
+
console.log(chalk.yellow('You can initialize git manually later.'));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Setup environment files
|
|
76
|
+
*/
|
|
77
|
+
async function setupEnvironmentFiles(projectPath, config) {
|
|
78
|
+
const spinner = ora('Creating environment files...').start();
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const { apiEnv, clientEnv } = generateEnvFiles(config);
|
|
82
|
+
|
|
83
|
+
// Write API .env
|
|
84
|
+
const apiEnvPath = path.join(projectPath, 'apps', 'api', '.env');
|
|
85
|
+
await fs.writeFile(apiEnvPath, apiEnv);
|
|
86
|
+
|
|
87
|
+
// Write Client .env
|
|
88
|
+
const clientEnvPath = path.join(projectPath, 'apps', 'client', '.env.local');
|
|
89
|
+
await fs.writeFile(clientEnvPath, clientEnv);
|
|
90
|
+
|
|
91
|
+
spinner.succeed('Environment files created');
|
|
92
|
+
} catch (error) {
|
|
93
|
+
spinner.fail('Failed to create environment files');
|
|
94
|
+
console.log(chalk.yellow('\nYou can create environment files manually:'));
|
|
95
|
+
console.log(chalk.cyan(' cp apps/api/.env.example apps/api/.env'));
|
|
96
|
+
console.log(chalk.cyan(' cp apps/client/.env.example apps/client/.env.local\n'));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Update package.json with project details
|
|
102
|
+
*/
|
|
103
|
+
async function updateProjectPackageJson(projectPath, config) {
|
|
104
|
+
const spinner = ora('Updating package.json...').start();
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
// Update root package.json
|
|
108
|
+
const rootPkgPath = path.join(projectPath, 'package.json');
|
|
109
|
+
const rootPkg = await fs.readJson(rootPkgPath);
|
|
110
|
+
|
|
111
|
+
rootPkg.name = config.projectName;
|
|
112
|
+
rootPkg.description = config.description;
|
|
113
|
+
rootPkg.version = '0.1.0';
|
|
114
|
+
|
|
115
|
+
await fs.writeJson(rootPkgPath, rootPkg, { spaces: 2 });
|
|
116
|
+
|
|
117
|
+
// Update client package.json
|
|
118
|
+
const clientPkgPath = path.join(projectPath, 'apps', 'client', 'package.json');
|
|
119
|
+
const clientPkg = await fs.readJson(clientPkgPath);
|
|
120
|
+
|
|
121
|
+
clientPkg.name = `${config.projectName}-client`;
|
|
122
|
+
clientPkg.version = '0.1.0';
|
|
123
|
+
|
|
124
|
+
await fs.writeJson(clientPkgPath, clientPkg, { spaces: 2 });
|
|
125
|
+
|
|
126
|
+
// Update API package.json
|
|
127
|
+
const apiPkgPath = path.join(projectPath, 'apps', 'api', 'package.json');
|
|
128
|
+
const apiPkg = await fs.readJson(apiPkgPath);
|
|
129
|
+
|
|
130
|
+
apiPkg.name = `${config.projectName}-api`;
|
|
131
|
+
apiPkg.version = '0.1.0';
|
|
132
|
+
|
|
133
|
+
await fs.writeJson(apiPkgPath, apiPkg, { spaces: 2 });
|
|
134
|
+
|
|
135
|
+
spinner.succeed('package.json files updated');
|
|
136
|
+
} catch (error) {
|
|
137
|
+
spinner.warn('Could not update package.json files');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Main setup function
|
|
143
|
+
*/
|
|
144
|
+
async function setupProject(config, projectPath) {
|
|
145
|
+
console.log(); // Empty line for spacing
|
|
146
|
+
|
|
147
|
+
// 1. Clone repository
|
|
148
|
+
await cloneRepository(projectPath);
|
|
149
|
+
|
|
150
|
+
// 2. Update package.json files
|
|
151
|
+
await updateProjectPackageJson(projectPath, config);
|
|
152
|
+
|
|
153
|
+
// 3. Setup environment files
|
|
154
|
+
if (config.setupEnv) {
|
|
155
|
+
await setupEnvironmentFiles(projectPath, config);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 4. Install dependencies
|
|
159
|
+
if (config.installDeps) {
|
|
160
|
+
await installDependencies(projectPath, config.packageManager);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 5. Initialize git
|
|
164
|
+
if (config.initGit) {
|
|
165
|
+
await initializeGit(projectPath);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
console.log(); // Empty line for spacing
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
module.exports = {
|
|
172
|
+
setupProject,
|
|
173
|
+
cloneRepository,
|
|
174
|
+
installDependencies,
|
|
175
|
+
initializeGit,
|
|
176
|
+
setupEnvironmentFiles
|
|
177
|
+
};
|
package/src/templates.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate a random secret
|
|
5
|
+
*/
|
|
6
|
+
function generateSecret(length = 32) {
|
|
7
|
+
return crypto.randomBytes(length).toString('base64');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Generate API .env file content
|
|
12
|
+
*/
|
|
13
|
+
function generateApiEnv(config) {
|
|
14
|
+
return `# ๐ฅ๏ธ Server Configuration
|
|
15
|
+
NODE_ENV=development
|
|
16
|
+
PORT=3001
|
|
17
|
+
|
|
18
|
+
# ๐๏ธ Database Configuration
|
|
19
|
+
DB_HOST=localhost
|
|
20
|
+
DB_PORT=5432
|
|
21
|
+
DB_NAME=${config.projectName.replace(/-/g, '_')}_db
|
|
22
|
+
DB_USER=postgres
|
|
23
|
+
DB_PASSWORD=postgres
|
|
24
|
+
|
|
25
|
+
# ๐ JWT Secrets (Auto-generated - Change in production!)
|
|
26
|
+
JWT_ACCESS_SECRET=${generateSecret()}
|
|
27
|
+
JWT_REFRESH_SECRET=${generateSecret()}
|
|
28
|
+
|
|
29
|
+
# ๐ง Email Configuration (Update with your SMTP details)
|
|
30
|
+
EMAIL_HOST=smtp.gmail.com
|
|
31
|
+
EMAIL_PORT=587
|
|
32
|
+
EMAIL_USER=your-email@gmail.com
|
|
33
|
+
EMAIL_PASSWORD=your-app-password
|
|
34
|
+
EMAIL_FROM=noreply@${config.projectName}.com
|
|
35
|
+
|
|
36
|
+
# ๐ URLs
|
|
37
|
+
CLIENT_URL=http://localhost:3000
|
|
38
|
+
API_URL=http://localhost:3001
|
|
39
|
+
|
|
40
|
+
# ๐ OTP Configuration
|
|
41
|
+
OTP_EXPIRY_MINUTES=10
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Generate Client .env file content
|
|
47
|
+
*/
|
|
48
|
+
function generateClientEnv(config) {
|
|
49
|
+
return `# ๐ API Configuration
|
|
50
|
+
NEXT_PUBLIC_API_URL=http://localhost:3001
|
|
51
|
+
API_URL=http://api:3001
|
|
52
|
+
|
|
53
|
+
# ๐ฑ App Configuration
|
|
54
|
+
NEXT_PUBLIC_APP_NAME=${config.projectName}
|
|
55
|
+
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Generate both environment files
|
|
61
|
+
*/
|
|
62
|
+
function generateEnvFiles(config) {
|
|
63
|
+
return {
|
|
64
|
+
apiEnv: generateApiEnv(config),
|
|
65
|
+
clientEnv: generateClientEnv(config)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Update package.json content
|
|
71
|
+
*/
|
|
72
|
+
function updatePackageJson(packageJson, config) {
|
|
73
|
+
return {
|
|
74
|
+
...packageJson,
|
|
75
|
+
name: config.projectName,
|
|
76
|
+
description: config.description,
|
|
77
|
+
version: '0.1.0'
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
generateEnvFiles,
|
|
83
|
+
generateApiEnv,
|
|
84
|
+
generateClientEnv,
|
|
85
|
+
updatePackageJson,
|
|
86
|
+
generateSecret
|
|
87
|
+
};
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Print welcome banner
|
|
5
|
+
*/
|
|
6
|
+
function printWelcome() {
|
|
7
|
+
console.log();
|
|
8
|
+
console.log(chalk.blue.bold(' โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
9
|
+
console.log(chalk.blue.bold(' โ โ'));
|
|
10
|
+
console.log(chalk.blue.bold(' โ ๐ Welcome to Time2Ship ๐ โ'));
|
|
11
|
+
console.log(chalk.blue.bold(' โ โ'));
|
|
12
|
+
console.log(chalk.blue.bold(' โ Ship your ideas faster! โ'));
|
|
13
|
+
console.log(chalk.blue.bold(' โ โ'));
|
|
14
|
+
console.log(chalk.blue.bold(' โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
15
|
+
console.log();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Print success message
|
|
20
|
+
*/
|
|
21
|
+
function printSuccess(projectName) {
|
|
22
|
+
console.log();
|
|
23
|
+
console.log(chalk.green.bold(' โ Success!'), `Your project "${projectName}" is ready!`);
|
|
24
|
+
console.log();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Print next steps
|
|
29
|
+
*/
|
|
30
|
+
function printNextSteps(config) {
|
|
31
|
+
const { projectName, installDeps, setupEnv, packageManager } = config;
|
|
32
|
+
|
|
33
|
+
console.log(chalk.cyan.bold('๐ Next Steps:\n'));
|
|
34
|
+
|
|
35
|
+
// Step 1: Navigate to directory
|
|
36
|
+
console.log(chalk.white(' 1. Navigate to your project:'));
|
|
37
|
+
console.log(chalk.gray(` cd ${projectName}\n`));
|
|
38
|
+
|
|
39
|
+
// Step 2: Install dependencies (if not done)
|
|
40
|
+
if (!installDeps) {
|
|
41
|
+
console.log(chalk.white(' 2. Install dependencies:'));
|
|
42
|
+
console.log(chalk.gray(` ${packageManager} install\n`));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Step 3: Configure environment (if not done)
|
|
46
|
+
if (!setupEnv) {
|
|
47
|
+
console.log(chalk.white(` ${installDeps ? '2' : '3'}. Set up environment variables:`));
|
|
48
|
+
console.log(chalk.gray(' cp apps/api/.env.example apps/api/.env'));
|
|
49
|
+
console.log(chalk.gray(' cp apps/client/.env.example apps/client/.env.local'));
|
|
50
|
+
console.log(chalk.yellow(' โ ๏ธ Update the .env files with your configuration\n'));
|
|
51
|
+
} else {
|
|
52
|
+
console.log(chalk.white(` ${installDeps ? '2' : '3'}. Review and update environment variables:`));
|
|
53
|
+
console.log(chalk.gray(' apps/api/.env'));
|
|
54
|
+
console.log(chalk.gray(' apps/client/.env.local'));
|
|
55
|
+
console.log(chalk.yellow(' โ ๏ธ JWT secrets have been auto-generated\n'));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Step 4: Start development
|
|
59
|
+
const stepNum = installDeps && setupEnv ? '3' : installDeps || setupEnv ? '4' : '5';
|
|
60
|
+
console.log(chalk.white(` ${stepNum}. Start development with Docker:`));
|
|
61
|
+
console.log(chalk.gray(' docker-compose up\n'));
|
|
62
|
+
|
|
63
|
+
console.log(chalk.white(' Or run services individually:'));
|
|
64
|
+
console.log(chalk.gray(' # Terminal 1 - Database'));
|
|
65
|
+
console.log(chalk.gray(' docker-compose up db\n'));
|
|
66
|
+
console.log(chalk.gray(' # Terminal 2 - API'));
|
|
67
|
+
console.log(chalk.gray(' cd apps/api && npm run dev\n'));
|
|
68
|
+
console.log(chalk.gray(' # Terminal 3 - Client'));
|
|
69
|
+
console.log(chalk.gray(' cd apps/client && npm run dev\n'));
|
|
70
|
+
|
|
71
|
+
// URLs
|
|
72
|
+
console.log(chalk.cyan.bold('๐ Your app will be available at:\n'));
|
|
73
|
+
console.log(chalk.white(' Frontend:'), chalk.blue.underline('http://localhost:3000'));
|
|
74
|
+
console.log(chalk.white(' API: '), chalk.blue.underline('http://localhost:3001'));
|
|
75
|
+
console.log(chalk.white(' Database:'), chalk.gray('localhost:5432\n'));
|
|
76
|
+
|
|
77
|
+
// Documentation
|
|
78
|
+
console.log(chalk.cyan.bold('๐ Documentation:\n'));
|
|
79
|
+
console.log(chalk.gray(` Check out the README.md in your project for detailed docs\n`));
|
|
80
|
+
|
|
81
|
+
// Happy coding
|
|
82
|
+
console.log(chalk.green.bold(' Happy shipping! ๐\n'));
|
|
83
|
+
|
|
84
|
+
// Support
|
|
85
|
+
console.log(chalk.gray(' Need help? Visit: ') + chalk.blue.underline('https://github.com/time2build-ai/time2ship'));
|
|
86
|
+
console.log();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check if a command exists
|
|
91
|
+
*/
|
|
92
|
+
function commandExists(command) {
|
|
93
|
+
try {
|
|
94
|
+
require('child_process').execSync(`which ${command}`, { stdio: 'ignore' });
|
|
95
|
+
return true;
|
|
96
|
+
} catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = {
|
|
102
|
+
printWelcome,
|
|
103
|
+
printSuccess,
|
|
104
|
+
printNextSteps,
|
|
105
|
+
commandExists
|
|
106
|
+
};
|