@cellajs/create-cella 0.0.4 → 0.0.6
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/index.ts +3 -0
- package/package.json +10 -6
- package/src/add-remote.ts +55 -0
- package/src/cli.ts +107 -0
- package/src/constants.ts +43 -0
- package/src/create.ts +183 -0
- package/src/{index.js → index.ts} +30 -16
- package/src/utils/clean-template.ts +111 -0
- package/src/utils/is-empty-directory.ts +15 -0
- package/src/utils/package-json.ts +25 -0
- package/src/utils/run-git-command.ts +57 -0
- package/src/utils/run-package-manager-command.ts +74 -0
- package/src/utils/validate-project-name.ts +22 -0
- package/LICENSE +0 -21
- package/index.js +0 -3
- package/src/cli.js +0 -94
- package/src/constants.js +0 -39
- package/src/create.js +0 -167
- package/src/utils/clean-template.js +0 -99
- package/src/utils/is-empty-directory.js +0 -15
- package/src/utils/package-json.js +0 -22
- package/src/utils/run-git-command.js +0 -39
- package/src/utils/run-package-manager-command.js +0 -68
- package/src/utils/validate-project-name.js +0 -28
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
type GitCommandType = 'merge' | 'diff' | 'other';
|
|
4
|
+
|
|
5
|
+
interface RunGitCommandOptions {
|
|
6
|
+
targetFolder: string;
|
|
7
|
+
command: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Helper to determine the type of Git command for custom handling
|
|
11
|
+
function getGitCommandType(command: string): GitCommandType {
|
|
12
|
+
if (command.startsWith('merge')) return 'merge';
|
|
13
|
+
if (command.startsWith('diff')) return 'diff';
|
|
14
|
+
return 'other';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Helper to check if a Git command is successful
|
|
18
|
+
function isGitCommandSuccess(gitCommand: GitCommandType, code: number | null, errOutput: string): boolean {
|
|
19
|
+
if (gitCommand === 'merge') return code === 0 || (code === 1 && !errOutput);
|
|
20
|
+
if (gitCommand === 'diff') return code === 0 || (code === 2 && !errOutput);
|
|
21
|
+
return code === 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Function to run a Git command and handle its output
|
|
25
|
+
export async function runGitCommand({ targetFolder, command }: RunGitCommandOptions): Promise<string> {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const gitCommand = getGitCommandType(command);
|
|
28
|
+
const child = spawn(`git ${command}`, [], { cwd: targetFolder, shell: true, timeout: 60000 });
|
|
29
|
+
|
|
30
|
+
let output = '';
|
|
31
|
+
let errOutput = '';
|
|
32
|
+
|
|
33
|
+
child.on('error', (error: Error) => {
|
|
34
|
+
reject(error);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
child.on('exit', (code: number | null) => {
|
|
38
|
+
if (isGitCommandSuccess(gitCommand, code, errOutput)) {
|
|
39
|
+
resolve(output.trim());
|
|
40
|
+
} else {
|
|
41
|
+
reject(
|
|
42
|
+
`Git ${gitCommand} command failed with exit code ${code}, stderr: "${errOutput.trim()}", stdout: "${output.trim()}"`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Collect stdout data
|
|
48
|
+
child.stdout.on('data', (data: Buffer) => {
|
|
49
|
+
output += data.toString();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Collect stderr data
|
|
53
|
+
child.stderr.on('data', (data: Buffer) => {
|
|
54
|
+
errOutput += data.toString();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import spawn from 'cross-spawn';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Executes a command using the specified package manager (e.g., pnpm).
|
|
5
|
+
*
|
|
6
|
+
* @param packageManager - The package manager to use (e.g., 'pnpm').
|
|
7
|
+
* @param args - The arguments to pass to the package manager command.
|
|
8
|
+
* @param env - Additional environment variables to set during command execution.
|
|
9
|
+
* @returns A promise that resolves if the command executes successfully; otherwise, it rejects with an error message.
|
|
10
|
+
*/
|
|
11
|
+
export async function runPackageManagerCommand(
|
|
12
|
+
packageManager: string,
|
|
13
|
+
args: string[],
|
|
14
|
+
env: Record<string, string> = {}
|
|
15
|
+
): Promise<void> {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const child = spawn(packageManager, args, {
|
|
18
|
+
env: {
|
|
19
|
+
...process.env,
|
|
20
|
+
...env,
|
|
21
|
+
},
|
|
22
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Buffer for capturing stderr and stdout output
|
|
26
|
+
let stderrBuffer = '';
|
|
27
|
+
let stdoutBuffer = '';
|
|
28
|
+
|
|
29
|
+
// Capture stderr output
|
|
30
|
+
child.stderr?.on('data', (data: Buffer) => {
|
|
31
|
+
stderrBuffer += data.toString();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Capture stdout output
|
|
35
|
+
child.stdout?.on('data', (data: Buffer) => {
|
|
36
|
+
stdoutBuffer += data.toString();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Handle process exit
|
|
40
|
+
child.on('close', (code: number | null) => {
|
|
41
|
+
if (code !== 0) {
|
|
42
|
+
reject(
|
|
43
|
+
`"${packageManager} ${args.join(' ')}" failed ${stdoutBuffer} ${stderrBuffer}`
|
|
44
|
+
);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
resolve();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Installs dependencies using the specified package manager.
|
|
54
|
+
*
|
|
55
|
+
* @param packageManager - The package manager to use for installation (e.g., 'pnpm').
|
|
56
|
+
* @returns A promise that resolves if the installation completes successfully; otherwise, it rejects with an error.
|
|
57
|
+
*/
|
|
58
|
+
export async function install(packageManager: string): Promise<void> {
|
|
59
|
+
return runPackageManagerCommand(packageManager, ['install'], {
|
|
60
|
+
NODE_ENV: 'development',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Generates SQL files using the specified package manager.
|
|
66
|
+
*
|
|
67
|
+
* @param packageManager - The package manager to use for generation (e.g., 'pnpm').
|
|
68
|
+
* @returns A promise that resolves if the generation completes successfully; otherwise, it rejects with an error.
|
|
69
|
+
*/
|
|
70
|
+
export async function generate(packageManager: string): Promise<void> {
|
|
71
|
+
return runPackageManagerCommand(packageManager, ['generate'], {
|
|
72
|
+
NODE_ENV: 'development',
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import validate from 'validate-npm-package-name';
|
|
2
|
+
|
|
3
|
+
interface ValidationResult {
|
|
4
|
+
valid: boolean;
|
|
5
|
+
problems?: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function validateProjectName(name: string): ValidationResult {
|
|
9
|
+
const nameValidation = validate(name);
|
|
10
|
+
|
|
11
|
+
if (nameValidation.validForNewPackages) {
|
|
12
|
+
return { valid: true };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
valid: false,
|
|
17
|
+
problems: [
|
|
18
|
+
...(nameValidation.errors || []),
|
|
19
|
+
...(nameValidation.warnings || []),
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024 CellaJS
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/index.js
DELETED
package/src/cli.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { basename, resolve } from 'node:path'
|
|
2
|
-
import { Command, InvalidArgumentError } from 'commander'
|
|
3
|
-
|
|
4
|
-
import { NAME } from './constants.js'
|
|
5
|
-
|
|
6
|
-
import { packageJson } from './utils/package-json.js'
|
|
7
|
-
import { validateProjectName } from './utils/validate-project-name.js'
|
|
8
|
-
|
|
9
|
-
// Initialize CLI variables
|
|
10
|
-
let directory = null;
|
|
11
|
-
let newBranchName = null;
|
|
12
|
-
let createNewBranch = null;
|
|
13
|
-
const packageManager = 'pnpm';
|
|
14
|
-
|
|
15
|
-
// Set up the CLI command using Commander
|
|
16
|
-
export const command = new Command(NAME)
|
|
17
|
-
.version(packageJson.version, '-v, --version', `Output the current version of ${NAME}.`)
|
|
18
|
-
.argument('[directory]', 'The directory name for the new project.')
|
|
19
|
-
.usage('[directory] [options]')
|
|
20
|
-
.helpOption('-h, --help', 'Display this help message.')
|
|
21
|
-
.option(
|
|
22
|
-
'--skip-new-branch',
|
|
23
|
-
'Skip creating a new branch during initialization.',
|
|
24
|
-
false,
|
|
25
|
-
)
|
|
26
|
-
.option(
|
|
27
|
-
'--skip-install',
|
|
28
|
-
'Skip the installation of packages.',
|
|
29
|
-
false,
|
|
30
|
-
)
|
|
31
|
-
.option(
|
|
32
|
-
'--skip-generate',
|
|
33
|
-
'Skip generating SQL files.',
|
|
34
|
-
false,
|
|
35
|
-
)
|
|
36
|
-
.option(
|
|
37
|
-
'--skip-clean',
|
|
38
|
-
'Skip cleaning the `cella` template.',
|
|
39
|
-
false,
|
|
40
|
-
)
|
|
41
|
-
.option(
|
|
42
|
-
'--skip-git',
|
|
43
|
-
'Skip initializing a git repository.',
|
|
44
|
-
false,
|
|
45
|
-
)
|
|
46
|
-
.option(
|
|
47
|
-
'--new-branch-name <name>',
|
|
48
|
-
'Specify a new branch name to create and use.',
|
|
49
|
-
(name) => {
|
|
50
|
-
if (typeof name === 'string') {
|
|
51
|
-
name = name.trim();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (name) {
|
|
55
|
-
const validation = validateProjectName(basename(resolve(name)))
|
|
56
|
-
if (!validation.valid) throw new InvalidArgumentError(`Invalid branch name: ${validation.problems[0]}`);
|
|
57
|
-
|
|
58
|
-
createNewBranch = true;
|
|
59
|
-
newBranchName = name;
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
)
|
|
63
|
-
.action((name) => {
|
|
64
|
-
if (typeof name === 'string') {
|
|
65
|
-
name = name.trim()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (name) {
|
|
69
|
-
const validation = validateProjectName(basename(resolve(name)));
|
|
70
|
-
if (!validation.valid) throw new InvalidArgumentError(`Invalid project name: ${validation.problems[0]}`);
|
|
71
|
-
|
|
72
|
-
directory = name;
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
.parse();
|
|
76
|
-
|
|
77
|
-
// Gather the CLI options and arguments
|
|
78
|
-
const options = command.opts({
|
|
79
|
-
skipNewBranch: false,
|
|
80
|
-
skipClean: false,
|
|
81
|
-
skipGit: false,
|
|
82
|
-
skipInstall: false,
|
|
83
|
-
skipGenerate: false,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// Export the CLI configuration for use in other modules
|
|
87
|
-
export const cli = {
|
|
88
|
-
options,
|
|
89
|
-
args: command.args,
|
|
90
|
-
directory,
|
|
91
|
-
newBranchName,
|
|
92
|
-
createNewBranch,
|
|
93
|
-
packageManager,
|
|
94
|
-
};
|
package/src/constants.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
export const NAME = 'create-cella'
|
|
2
|
-
|
|
3
|
-
// URL of the template repository
|
|
4
|
-
export const TEMPLATE_URL = 'github:cellajs/cella';
|
|
5
|
-
|
|
6
|
-
// Import package.json dynamically for version and website information
|
|
7
|
-
import packageJson from '../package.json' with { type: 'json' };
|
|
8
|
-
|
|
9
|
-
// Export version, website and author from package.json
|
|
10
|
-
export const VERSION = packageJson.version;
|
|
11
|
-
export const AUTHOR = packageJson.author;
|
|
12
|
-
export const WEBSITE = packageJson.homepage;
|
|
13
|
-
|
|
14
|
-
// Files or folders to be removed from the template after downloading
|
|
15
|
-
export const TO_REMOVE = [
|
|
16
|
-
'info',
|
|
17
|
-
'./cli/create-cella'
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
// Specific folder contents to be cleaned out from the template
|
|
21
|
-
export const TO_CLEAN = [
|
|
22
|
-
'./backend/drizzle'
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
// Files to copy/paste after downloading
|
|
26
|
-
export const TO_COPY = {
|
|
27
|
-
'./backend/.env.example': './backend/.env',
|
|
28
|
-
'./tus/.env.example': './tus/.env',
|
|
29
|
-
'./info/QUICKSTART.md': 'README.md',
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// ASCII title for the CLI output
|
|
33
|
-
export const CELLA_TITLE = `
|
|
34
|
-
_ _
|
|
35
|
-
▒▓█████▓▒ ___ ___| | | __ _
|
|
36
|
-
▒▓█ █▓▒ / __/ _ \\ | |/ _\` |
|
|
37
|
-
▒▓█ █▓▒ | (_| __/ | | (_| |
|
|
38
|
-
▒▓█████▓▒ \\___\\___|_|_|\\__,_|
|
|
39
|
-
`;
|
package/src/create.js
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import { mkdir } from 'node:fs/promises';
|
|
2
|
-
import { existsSync } from 'node:fs'
|
|
3
|
-
import { join, relative } from 'node:path';
|
|
4
|
-
import colors from 'picocolors';
|
|
5
|
-
import { downloadTemplate } from "giget";
|
|
6
|
-
import yoctoSpinner from 'yocto-spinner';
|
|
7
|
-
|
|
8
|
-
import { TEMPLATE_URL } from './constants.js';
|
|
9
|
-
|
|
10
|
-
import { install, generate } from './utils/run-package-manager-command.js';
|
|
11
|
-
import { cleanTemplate } from './utils/clean-template.js';
|
|
12
|
-
import { runGitCommand } from './utils/run-git-command.js';
|
|
13
|
-
|
|
14
|
-
export async function create({
|
|
15
|
-
projectName,
|
|
16
|
-
targetFolder,
|
|
17
|
-
newBranchName,
|
|
18
|
-
skipInstall,
|
|
19
|
-
skipGit,
|
|
20
|
-
skipClean,
|
|
21
|
-
skipGenerate,
|
|
22
|
-
packageManager,
|
|
23
|
-
}) {
|
|
24
|
-
// Save the original working directory
|
|
25
|
-
const originalCwd = process.cwd();
|
|
26
|
-
|
|
27
|
-
console.info();
|
|
28
|
-
|
|
29
|
-
// Create the target folder if it doesn't exist
|
|
30
|
-
const createFolderSpinner = yoctoSpinner({
|
|
31
|
-
text: 'Creating project folder',
|
|
32
|
-
}).start();
|
|
33
|
-
|
|
34
|
-
await mkdir(targetFolder, { recursive: true });
|
|
35
|
-
process.chdir(targetFolder);
|
|
36
|
-
|
|
37
|
-
createFolderSpinner.success('Project folder created');
|
|
38
|
-
|
|
39
|
-
// Download the template from the specified URL
|
|
40
|
-
const downloadSpinner = yoctoSpinner({
|
|
41
|
-
text: 'Downloading `cella` template',
|
|
42
|
-
}).start();
|
|
43
|
-
|
|
44
|
-
await downloadTemplate(TEMPLATE_URL, {
|
|
45
|
-
cwd: process.cwd(),
|
|
46
|
-
dir: targetFolder,
|
|
47
|
-
force: true,
|
|
48
|
-
provider: "github",
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
downloadSpinner.success('`cella` template downloaded');
|
|
52
|
-
|
|
53
|
-
// Clean the template if the skipClean flag is not set
|
|
54
|
-
if (!skipClean) {
|
|
55
|
-
const cleanSpinner = yoctoSpinner({
|
|
56
|
-
text: 'cleaning `cella` template',
|
|
57
|
-
}).start();
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
await cleanTemplate({
|
|
61
|
-
targetFolder,
|
|
62
|
-
projectName,
|
|
63
|
-
})
|
|
64
|
-
cleanSpinner.success('`cella` template cleaned')
|
|
65
|
-
} catch (e) {
|
|
66
|
-
console.error(e);
|
|
67
|
-
cleanSpinner.error('Failed to clean `cella` template');
|
|
68
|
-
process.exit(1);
|
|
69
|
-
}
|
|
70
|
-
} else {
|
|
71
|
-
console.info(`${colors.yellow('⚠')} --skip-clean > Skip cleaning \`cella\` template'`)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Install dependencies if the skipInstall flag is not set
|
|
75
|
-
if (!skipInstall) {
|
|
76
|
-
const installSpinner = yoctoSpinner({
|
|
77
|
-
text: 'installing dependencies',
|
|
78
|
-
}).start();
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
await install(packageManager)
|
|
82
|
-
installSpinner.success('Dependencies installed');
|
|
83
|
-
} catch (e) {
|
|
84
|
-
console.error(e);
|
|
85
|
-
installSpinner.error('Failed to install dependencies');
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
} else {
|
|
89
|
-
console.info(`${colors.yellow('⚠')} --skip-install > Skip installing dependencies`)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Generate SQL files if the skipGenerate flag is not set
|
|
93
|
-
if (!skipGenerate) {
|
|
94
|
-
const installSpinner = yoctoSpinner({
|
|
95
|
-
text: 'generating SQL files',
|
|
96
|
-
}).start();
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
await generate(packageManager)
|
|
100
|
-
installSpinner.success('SQL files generated');
|
|
101
|
-
} catch (e) {
|
|
102
|
-
console.error(e);
|
|
103
|
-
installSpinner.error('Failed to generate SQL files');
|
|
104
|
-
process.exit(1);
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
console.info(`${colors.yellow('⚠')} --skip-generate > Skip generating SQL files`)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Initialize Git repository if skipGit flag is not set
|
|
111
|
-
if (!skipGit) {
|
|
112
|
-
const gitSpinner = yoctoSpinner({
|
|
113
|
-
text: 'initializing git repository',
|
|
114
|
-
}).start();
|
|
115
|
-
|
|
116
|
-
const gitFolderPath = join(targetFolder, '.git');
|
|
117
|
-
|
|
118
|
-
if (!existsSync(gitFolderPath)) {
|
|
119
|
-
try {
|
|
120
|
-
// Run Git commands to initialize the repository and make the first commit
|
|
121
|
-
await runGitCommand({ targetFolder, command: 'init' });
|
|
122
|
-
await runGitCommand({ targetFolder, command: 'add .' });
|
|
123
|
-
await runGitCommand({ targetFolder, command: 'commit -m "Initial commit"' });
|
|
124
|
-
|
|
125
|
-
// If a new branch name is specified, create and checkout the branch
|
|
126
|
-
if (newBranchName) {
|
|
127
|
-
await runGitCommand({ targetFolder, command: `branch ${newBranchName}` });
|
|
128
|
-
await runGitCommand({ targetFolder, command: `checkout ${newBranchName}` });
|
|
129
|
-
gitSpinner.success(`Git repository initialized, initial commit created, and new branch ${newBranchName} created`);
|
|
130
|
-
} else {
|
|
131
|
-
gitSpinner.success('Git repository initialized and initial commit created');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
} catch (e) {
|
|
135
|
-
console.error(e);
|
|
136
|
-
gitSpinner.error('Failed to initialize Git repository or create branch');
|
|
137
|
-
process.exit(1);
|
|
138
|
-
}
|
|
139
|
-
} else {
|
|
140
|
-
gitSpinner.warning('Git repository already initialized > Skip git init')
|
|
141
|
-
}
|
|
142
|
-
} else {
|
|
143
|
-
console.info(`${colors.yellow('⚠')} --skip-git > Skip git init`)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Final success message indicating project creation
|
|
147
|
-
console.info()
|
|
148
|
-
console.info(`${colors.green('Success')} Created ${projectName} at ${targetFolder}`)
|
|
149
|
-
console.info()
|
|
150
|
-
|
|
151
|
-
// Check if the working directory needs to be changed
|
|
152
|
-
const needsCd = originalCwd !== targetFolder;
|
|
153
|
-
if (needsCd) {
|
|
154
|
-
// Calculate the relative path between the original working directory and the target folder
|
|
155
|
-
const relativePath = relative(originalCwd, targetFolder);
|
|
156
|
-
|
|
157
|
-
console.info('now go to your project using:')
|
|
158
|
-
console.info(colors.cyan(` cd ./${relativePath}`)); // Adding './' to make it clear it's a relative path
|
|
159
|
-
console.info()
|
|
160
|
-
}
|
|
161
|
-
console.info(`${needsCd ? 'then ' : ''}quick start with:`)
|
|
162
|
-
console.info(colors.cyan(` ${packageManager} quick`))
|
|
163
|
-
console.info()
|
|
164
|
-
|
|
165
|
-
console.info('Read the readme in project root for more info on how to get started!')
|
|
166
|
-
console.info(`Enjoy building ${projectName} using cella! 🎉`)
|
|
167
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import colors from 'picocolors';
|
|
4
|
-
|
|
5
|
-
import { TO_CLEAN, TO_REMOVE, TO_COPY } from '../constants.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Cleans the specified template by removing designated folders and files.
|
|
9
|
-
* @param {Object} params - Parameters containing targetFolder.
|
|
10
|
-
* @param {string} params.targetFolder - The folder to clean.
|
|
11
|
-
*/
|
|
12
|
-
export async function cleanTemplate({
|
|
13
|
-
targetFolder,
|
|
14
|
-
projectName,
|
|
15
|
-
}) {
|
|
16
|
-
// Change the current working directory to targetFolder if not already set
|
|
17
|
-
if (process.cwd() !== targetFolder) {
|
|
18
|
-
process.chdir(targetFolder);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return new Promise(async (resolve, reject) => {
|
|
22
|
-
try {
|
|
23
|
-
// Copy specified files
|
|
24
|
-
for (const [src, dest] of Object.entries(TO_COPY)) {
|
|
25
|
-
const srcAbsolutePath = path.resolve(targetFolder, src);
|
|
26
|
-
const destAbsolutePath = path.resolve(targetFolder, dest);
|
|
27
|
-
await copyFile(srcAbsolutePath, destAbsolutePath);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Clean specified folder contents
|
|
31
|
-
await Promise.all(TO_CLEAN.map(folderPath => {
|
|
32
|
-
const absolutePath = path.resolve(targetFolder, folderPath);
|
|
33
|
-
return removeFolderContents(absolutePath);
|
|
34
|
-
}));
|
|
35
|
-
|
|
36
|
-
// Remove specified files and folders
|
|
37
|
-
await Promise.all(TO_REMOVE.map(filePath => {
|
|
38
|
-
const absolutePath = path.resolve(targetFolder, filePath);
|
|
39
|
-
return removeFileOrFolder(absolutePath);
|
|
40
|
-
}));
|
|
41
|
-
|
|
42
|
-
resolve();
|
|
43
|
-
} catch (err) {
|
|
44
|
-
reject(`Error during the cleaning process: ${err}`);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Removes all contents within a specified folder.
|
|
51
|
-
* @param {string} folderPath - The path of the folder to clean.
|
|
52
|
-
*/
|
|
53
|
-
export async function removeFolderContents(folderPath) {
|
|
54
|
-
// List all files in the folder
|
|
55
|
-
const files = await fs.readdir(folderPath);
|
|
56
|
-
|
|
57
|
-
await Promise.all(files.map(async (file) => {
|
|
58
|
-
const filePath = path.join(folderPath, file);
|
|
59
|
-
|
|
60
|
-
// Get the file or folder statistics
|
|
61
|
-
const stat = await fs.lstat(filePath);
|
|
62
|
-
|
|
63
|
-
// If it's a directory, remove it and all its contents
|
|
64
|
-
if (stat.isDirectory()) {
|
|
65
|
-
await fs.rm(filePath, { recursive: true, force: true });
|
|
66
|
-
} else {
|
|
67
|
-
// If it's a file, remove it
|
|
68
|
-
await fs.rm(filePath);
|
|
69
|
-
}
|
|
70
|
-
}));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Removes a specified file or folder.
|
|
75
|
-
* @param {string} pathToRemove - The path to the file or folder to remove.
|
|
76
|
-
*/
|
|
77
|
-
export async function removeFileOrFolder(pathToRemove) {
|
|
78
|
-
await fs.rm(pathToRemove, { recursive: true, force: true });
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Helper function to copy files if the source exists
|
|
82
|
-
export async function copyFile(src, dest) {
|
|
83
|
-
try {
|
|
84
|
-
// Check if the source file exists
|
|
85
|
-
await fs.access(src);
|
|
86
|
-
|
|
87
|
-
// Ensure the destination directory exists
|
|
88
|
-
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
89
|
-
|
|
90
|
-
// Copy the file
|
|
91
|
-
await fs.copyFile(src, dest);
|
|
92
|
-
} catch (err) {
|
|
93
|
-
if (err.code === 'ENOENT') {
|
|
94
|
-
console.info(`\n${colors.yellow('⚠')} Source file "${src}" does not exist > Skip copy`);
|
|
95
|
-
} else {
|
|
96
|
-
throw err;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { readdir } from 'node:fs/promises'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Checks if a directory is empty or only contains a .git directory.
|
|
5
|
-
*
|
|
6
|
-
* @param {string} path - The path of the directory to check.
|
|
7
|
-
* @returns {Promise<boolean>} - Resolves to true if the directory is empty or contains only a .git folder, false otherwise.
|
|
8
|
-
* @throws {Error} - Throws an error if the path is not a directory or if there's an issue reading the directory.
|
|
9
|
-
*/
|
|
10
|
-
export async function isEmptyDirectory(path) {
|
|
11
|
-
const files = await readdir(path);
|
|
12
|
-
|
|
13
|
-
// Check if directory is empty or contains only the .git directory
|
|
14
|
-
return files.length === 0 || (files.length === 1 && files[0] === '.git');
|
|
15
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises'
|
|
2
|
-
import { resolve } from 'node:path'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Reads and parses the package.json file located in the root directory.
|
|
7
|
-
*
|
|
8
|
-
* @returns {Promise<object>} - A promise that resolves to the parsed package.json object.
|
|
9
|
-
* @throws {Error} - Throws an error if the package.json file cannot be found or read, or if the JSON is invalid.
|
|
10
|
-
*/
|
|
11
|
-
async function readPackageJson() {
|
|
12
|
-
const PACKAGE_JSON_FILE = resolve(
|
|
13
|
-
fileURLToPath(import.meta.url),
|
|
14
|
-
'../../../package.json',
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
const packageJson = await readFile(PACKAGE_JSON_FILE, 'utf-8')
|
|
18
|
-
return JSON.parse(packageJson)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Load the package.json at module initialization
|
|
22
|
-
export const packageJson = await readPackageJson()
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Executes a Git command in the specified target folder.
|
|
5
|
-
*
|
|
6
|
-
* @param {Object} options - Options for running the Git command.
|
|
7
|
-
* @param {string} options.targetFolder - The folder in which to run the command.
|
|
8
|
-
* @param {string} options.command - The Git command to execute (e.g., 'init', 'commit -m "message"', etc.).
|
|
9
|
-
* @returns {Promise<void>} - A promise that resolves if the command executes successfully; otherwise, it rejects with an error message.
|
|
10
|
-
* @throws {Error} - Throws an error if the Git command fails or if there is an error starting the process.
|
|
11
|
-
*/
|
|
12
|
-
export async function runGitCommand({ targetFolder, command }) {
|
|
13
|
-
return new Promise((resolve, reject) => {
|
|
14
|
-
const child = spawn(`git ${command}`, [], {
|
|
15
|
-
cwd: targetFolder,
|
|
16
|
-
shell: true,
|
|
17
|
-
timeout: 60000,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
// Handle process errors
|
|
21
|
-
child.on("error", (error) => {
|
|
22
|
-
reject(error);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
// Handle command exit
|
|
26
|
-
child.on("exit", (code) => {
|
|
27
|
-
if (code === 0) {
|
|
28
|
-
resolve();
|
|
29
|
-
} else {
|
|
30
|
-
reject(`Git command failed with exit code ${code}`);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// Swallow stdout and stderr
|
|
35
|
-
child.stdout.on("data", () => {});
|
|
36
|
-
child.stderr.on("data", () => {});
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|