@cellajs/create-cella 0.1.7 → 0.2.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/README.md +57 -0
- package/configs/default-config.ts.template +404 -0
- package/dist/index.js +620 -455
- package/dist/index.js.map +1 -1
- package/index.js +1 -1
- package/package.json +35 -16
- package/src/add-remote.ts +0 -50
- package/src/cli.ts +0 -106
- package/src/constants.ts +0 -61
- package/src/create.ts +0 -193
- package/src/index.ts +0 -137
- package/src/utils/clean-template.ts +0 -150
- package/src/utils/extract-package-json-version-from-uri.ts +0 -29
- package/src/utils/is-empty-directory.ts +0 -15
- package/src/utils/run-git-command.ts +0 -57
- package/src/utils/run-package-manager-command.ts +0 -74
- package/src/utils/validate-project-name.ts +0 -22
- package/tsconfig.json +0 -17
- package/tsup.config.ts +0 -22
package/src/create.ts
DELETED
|
@@ -1,193 +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.ts';
|
|
9
|
-
|
|
10
|
-
import { install, generate } from './utils/run-package-manager-command.ts';
|
|
11
|
-
import { cleanTemplate } from './utils/clean-template.ts';
|
|
12
|
-
import { runGitCommand } from './utils/run-git-command.ts';
|
|
13
|
-
import { addRemote } from './add-remote.ts';
|
|
14
|
-
|
|
15
|
-
interface CreateOptions {
|
|
16
|
-
projectName: string;
|
|
17
|
-
targetFolder: string;
|
|
18
|
-
newBranchName?: string | null;
|
|
19
|
-
skipInstall: boolean;
|
|
20
|
-
skipGit: boolean;
|
|
21
|
-
skipClean: boolean;
|
|
22
|
-
skipGenerate: boolean;
|
|
23
|
-
packageManager: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export async function create({
|
|
27
|
-
projectName,
|
|
28
|
-
targetFolder,
|
|
29
|
-
newBranchName,
|
|
30
|
-
skipInstall,
|
|
31
|
-
skipGit,
|
|
32
|
-
skipClean,
|
|
33
|
-
skipGenerate,
|
|
34
|
-
packageManager,
|
|
35
|
-
}: CreateOptions): Promise<void> {
|
|
36
|
-
// Save the original working directory
|
|
37
|
-
const originalCwd = process.cwd();
|
|
38
|
-
|
|
39
|
-
console.info();
|
|
40
|
-
|
|
41
|
-
// Create the target folder if it doesn't exist
|
|
42
|
-
const createFolderSpinner = yoctoSpinner({
|
|
43
|
-
text: 'Creating project folder',
|
|
44
|
-
}).start();
|
|
45
|
-
|
|
46
|
-
await mkdir(targetFolder, { recursive: true });
|
|
47
|
-
process.chdir(targetFolder);
|
|
48
|
-
|
|
49
|
-
createFolderSpinner.success('Project folder created');
|
|
50
|
-
|
|
51
|
-
// Download the template from the specified URL
|
|
52
|
-
const downloadSpinner = yoctoSpinner({
|
|
53
|
-
text: 'Downloading `cella` template',
|
|
54
|
-
}).start();
|
|
55
|
-
|
|
56
|
-
await downloadTemplate(TEMPLATE_URL, {
|
|
57
|
-
cwd: process.cwd(),
|
|
58
|
-
dir: targetFolder,
|
|
59
|
-
force: true,
|
|
60
|
-
provider: 'github',
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
downloadSpinner.success('`cella` template downloaded');
|
|
64
|
-
|
|
65
|
-
// Clean the template if the skipClean flag is not set
|
|
66
|
-
if (!skipClean) {
|
|
67
|
-
const cleanSpinner = yoctoSpinner({
|
|
68
|
-
text: 'cleaning `cella` template',
|
|
69
|
-
}).start();
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
await cleanTemplate({
|
|
73
|
-
targetFolder,
|
|
74
|
-
projectName,
|
|
75
|
-
});
|
|
76
|
-
cleanSpinner.success('`cella` template cleaned');
|
|
77
|
-
} catch (e) {
|
|
78
|
-
console.error(e);
|
|
79
|
-
cleanSpinner.error('Failed to clean `cella` template');
|
|
80
|
-
process.exit(1);
|
|
81
|
-
}
|
|
82
|
-
} else {
|
|
83
|
-
console.info(`${colors.yellow('⚠')} --skip-clean > Skip cleaning \`cella\` template`);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Install dependencies if the skipInstall flag is not set
|
|
87
|
-
if (!skipInstall) {
|
|
88
|
-
const installSpinner = yoctoSpinner({
|
|
89
|
-
text: 'installing dependencies',
|
|
90
|
-
}).start();
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
await install(packageManager);
|
|
94
|
-
installSpinner.success('Dependencies installed');
|
|
95
|
-
} catch (e) {
|
|
96
|
-
console.error(e);
|
|
97
|
-
installSpinner.error('Failed to install dependencies');
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
} else {
|
|
101
|
-
console.info(`${colors.yellow('⚠')} --skip-install > Skip installing dependencies`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Generate SQL files if the skipGenerate flag is not set
|
|
105
|
-
if (!skipGenerate) {
|
|
106
|
-
const generateSpinner = yoctoSpinner({
|
|
107
|
-
text: 'generating SQL files',
|
|
108
|
-
}).start();
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
await generate(packageManager);
|
|
112
|
-
generateSpinner.success('SQL files generated');
|
|
113
|
-
} catch (e) {
|
|
114
|
-
console.error(e);
|
|
115
|
-
generateSpinner.error('Failed to generate SQL files');
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
console.info(`${colors.yellow('⚠')} --skip-generate > Skip generating SQL files`);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Initialize Git repository if skipGit flag is not set
|
|
123
|
-
if (!skipGit) {
|
|
124
|
-
const gitSpinner = yoctoSpinner({
|
|
125
|
-
text: 'initializing git repository',
|
|
126
|
-
}).start();
|
|
127
|
-
|
|
128
|
-
const gitFolderPath = join(targetFolder, '.git');
|
|
129
|
-
|
|
130
|
-
if (!existsSync(gitFolderPath)) {
|
|
131
|
-
try {
|
|
132
|
-
// Run Git commands to initialize the repository and make the first commit
|
|
133
|
-
await runGitCommand({ targetFolder, command: 'init' });
|
|
134
|
-
await runGitCommand({ targetFolder, command: 'add .' });
|
|
135
|
-
await runGitCommand({ targetFolder, command: 'commit -m "Initial commit"' });
|
|
136
|
-
|
|
137
|
-
// If a new branch name is specified, create and checkout the branch
|
|
138
|
-
if (newBranchName) {
|
|
139
|
-
await runGitCommand({ targetFolder, command: `branch ${newBranchName}` });
|
|
140
|
-
await runGitCommand({ targetFolder, command: `checkout ${newBranchName}` });
|
|
141
|
-
gitSpinner.success(`Git repository initialized, initial commit created, and new branch ${newBranchName} created`);
|
|
142
|
-
} else {
|
|
143
|
-
gitSpinner.success('Git repository initialized and initial commit created');
|
|
144
|
-
}
|
|
145
|
-
} catch (e) {
|
|
146
|
-
console.error(e);
|
|
147
|
-
gitSpinner.error('Failed to initialize Git repository or create branch');
|
|
148
|
-
process.exit(1);
|
|
149
|
-
}
|
|
150
|
-
} else {
|
|
151
|
-
gitSpinner.warning('Git repository already initialized > Skip git init');
|
|
152
|
-
}
|
|
153
|
-
} else {
|
|
154
|
-
console.info(`${colors.yellow('⚠')} --skip-git > Skip git init`);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Add Cella as upstream remote
|
|
158
|
-
await addRemote({ targetFolder });
|
|
159
|
-
|
|
160
|
-
// Final success message indicating project creation
|
|
161
|
-
console.info();
|
|
162
|
-
console.info(`${colors.green('Success')} Created ${projectName} at ${targetFolder}`);
|
|
163
|
-
console.info();
|
|
164
|
-
|
|
165
|
-
// Check if the working directory needs to be changed
|
|
166
|
-
const needsCd = originalCwd !== targetFolder;
|
|
167
|
-
const relativePath = relative(originalCwd, targetFolder);
|
|
168
|
-
|
|
169
|
-
if (needsCd) {
|
|
170
|
-
// Calculate the relative path between the original working directory and the target folder
|
|
171
|
-
console.info('now go to your project using:');
|
|
172
|
-
console.info(colors.cyan(` cd ${relativePath}`)); // Adding './' to make it clear it's a relative path
|
|
173
|
-
console.info();
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
console.info(`${needsCd ? 'then ' : ''}quick start using pglite with:`);
|
|
177
|
-
console.info(colors.cyan(` ${packageManager} quick`));
|
|
178
|
-
console.info();
|
|
179
|
-
|
|
180
|
-
console.info('Already have docker installed? Then you can run a full setup:');
|
|
181
|
-
console.info(colors.cyan(` ${packageManager} docker`));
|
|
182
|
-
console.info(colors.cyan(` ${packageManager} dev`));
|
|
183
|
-
console.info(colors.cyan(` ${packageManager} seed`));
|
|
184
|
-
console.info();
|
|
185
|
-
|
|
186
|
-
console.info(`Once running, you can sign in using:`);
|
|
187
|
-
console.info(`email: ${colors.greenBright('admin-test@cellajs.com')}`);
|
|
188
|
-
console.info(`password: ${colors.greenBright('12345678')}`);
|
|
189
|
-
console.info();
|
|
190
|
-
console.info(`For more info, check out: ${relativePath}/README.md`);
|
|
191
|
-
console.info(`Enjoy building ${projectName} using cella! 🎉`);
|
|
192
|
-
console.info();
|
|
193
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
|
|
3
|
-
import { basename, resolve } from 'node:path';
|
|
4
|
-
import { existsSync } from 'node:fs';
|
|
5
|
-
import colors from 'picocolors';
|
|
6
|
-
|
|
7
|
-
import { input, confirm, select } from '@inquirer/prompts';
|
|
8
|
-
|
|
9
|
-
import { cli } from './cli';
|
|
10
|
-
import { extractPackageJsonVersionFromUri } from './utils/extract-package-json-version-from-uri';
|
|
11
|
-
import { validateProjectName } from './utils/validate-project-name.ts';
|
|
12
|
-
import { isEmptyDirectory } from './utils/is-empty-directory.ts';
|
|
13
|
-
import { create } from './create.ts';
|
|
14
|
-
import { LOGO, VERSION, WEBSITE, AUTHOR, GITHUB, DESCRIPTION, TEMPLATE_URL } from './constants.ts';
|
|
15
|
-
|
|
16
|
-
interface CreateOptions {
|
|
17
|
-
projectName: string;
|
|
18
|
-
targetFolder: string;
|
|
19
|
-
newBranchName?: string | null;
|
|
20
|
-
skipInstall: boolean;
|
|
21
|
-
skipGit: boolean;
|
|
22
|
-
skipClean: boolean;
|
|
23
|
-
skipGenerate: boolean;
|
|
24
|
-
packageManager: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function main(): Promise<void> {
|
|
28
|
-
console.info(LOGO);
|
|
29
|
-
|
|
30
|
-
// Get the latest version of the template
|
|
31
|
-
const templateVersion = await extractPackageJsonVersionFromUri(TEMPLATE_URL);
|
|
32
|
-
|
|
33
|
-
// Display CLI version and created by information
|
|
34
|
-
console.info();
|
|
35
|
-
console.info(DESCRIPTION);
|
|
36
|
-
console.info();
|
|
37
|
-
console.info(`Cella version: ${colors.green(templateVersion)}`);
|
|
38
|
-
console.info(`Cli version ${colors.green(VERSION)}`);
|
|
39
|
-
console.info(`Created by ${AUTHOR}`);
|
|
40
|
-
console.info(`${GITHUB} | ${WEBSITE}`);
|
|
41
|
-
console.info();
|
|
42
|
-
|
|
43
|
-
// Skip creating a new branch if --skipNewBranch flag is provided or git is skipped
|
|
44
|
-
if (cli.options.skipNewBranch || cli.options.skipGit) {
|
|
45
|
-
cli.createNewBranch = false;
|
|
46
|
-
cli.newBranchName = null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Skip generating sql files if --skipGenerate flag is provided
|
|
50
|
-
if (cli.options.skipGenerate === true) {
|
|
51
|
-
cli.options.skipGenerate = true;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Skip installing packages if --skipInstall flag is provided
|
|
55
|
-
if (cli.options.skipInstall === true) {
|
|
56
|
-
cli.options.skipInstall = true;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Skip cleaning the template if --skipClean flag is provided
|
|
60
|
-
if (cli.options.skipClean === true) {
|
|
61
|
-
cli.options.skipClean = true;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Skip initializing git if --skipGit flag is provided
|
|
65
|
-
if (cli.options.skipGit === true) {
|
|
66
|
-
cli.options.skipGit = true;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Prompt for project name if not provided
|
|
70
|
-
if (!cli.directory) {
|
|
71
|
-
cli.directory = await input({
|
|
72
|
-
message: 'Enter your project name',
|
|
73
|
-
default: 'my-cella-app',
|
|
74
|
-
validate: (name) => {
|
|
75
|
-
const validation = validateProjectName(basename(resolve(name)));
|
|
76
|
-
return validation.valid ? true : `Invalid project name: ${validation.problems[0]}`;
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Prompt to create a new branch besides the main branch (if not skipped)
|
|
82
|
-
if (cli.createNewBranch === null) {
|
|
83
|
-
cli.createNewBranch = await confirm({
|
|
84
|
-
message: 'Would you like to create a new branch (besides "main")?',
|
|
85
|
-
default: true,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Prompt for new branch name, only if user opted to create a new branch
|
|
90
|
-
if (!cli.newBranchName && cli.createNewBranch) {
|
|
91
|
-
cli.newBranchName = await input({
|
|
92
|
-
message: 'Enter the new branch name',
|
|
93
|
-
default: 'development',
|
|
94
|
-
validate: (name) => {
|
|
95
|
-
const validation = validateProjectName(basename(resolve(name)));
|
|
96
|
-
return validation.valid ? true : `Invalid branch name: ${validation.problems[0]}`;
|
|
97
|
-
},
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const targetFolder = resolve(cli.directory);
|
|
102
|
-
const projectName = basename(targetFolder);
|
|
103
|
-
|
|
104
|
-
// Check if the target folder exists and is not empty
|
|
105
|
-
if (existsSync(targetFolder) && !(await isEmptyDirectory(targetFolder))) {
|
|
106
|
-
const dirName = cli.directory === '.' ? 'Current directory' : `Target directory "${targetFolder}"`;
|
|
107
|
-
const message = `${dirName} is not empty. Please choose how you would like to proceed:`;
|
|
108
|
-
|
|
109
|
-
const action = await select({
|
|
110
|
-
message,
|
|
111
|
-
choices: [
|
|
112
|
-
{ name: 'Cancel and exit', value: 'cancel' },
|
|
113
|
-
{ name: 'Ignore existing files and continue', value: 'ignore' },
|
|
114
|
-
],
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
if (action === 'cancel') {
|
|
118
|
-
process.exit(1);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Proceed with the project creation
|
|
123
|
-
const createOptions: CreateOptions = {
|
|
124
|
-
projectName,
|
|
125
|
-
targetFolder,
|
|
126
|
-
newBranchName: cli.newBranchName,
|
|
127
|
-
skipInstall: cli.options.skipInstall,
|
|
128
|
-
skipGit: cli.options.skipGit,
|
|
129
|
-
skipClean: cli.options.skipClean,
|
|
130
|
-
skipGenerate: cli.options.skipGenerate,
|
|
131
|
-
packageManager: cli.packageManager,
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
await create(createOptions);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
main().catch(console.error);
|
|
@@ -1,150 +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, TO_EDIT } from '../constants.ts';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Cleans the specified template by removing designated folders and files.
|
|
9
|
-
* @param params - Parameters containing the target folder and project name.
|
|
10
|
-
*/
|
|
11
|
-
export async function cleanTemplate({
|
|
12
|
-
targetFolder,
|
|
13
|
-
projectName,
|
|
14
|
-
}: {
|
|
15
|
-
targetFolder: string;
|
|
16
|
-
projectName: string;
|
|
17
|
-
}): Promise<void> {
|
|
18
|
-
// Change the current working directory to targetFolder if not already set
|
|
19
|
-
if (process.cwd() !== targetFolder) {
|
|
20
|
-
process.chdir(targetFolder);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return new Promise<void>(async (resolve, reject) => {
|
|
24
|
-
try {
|
|
25
|
-
// Copy specified files
|
|
26
|
-
for (const [src, dest] of Object.entries(TO_COPY)) {
|
|
27
|
-
const srcAbsolutePath = path.resolve(targetFolder, src);
|
|
28
|
-
const destAbsolutePath = path.resolve(targetFolder, dest);
|
|
29
|
-
await copyFile(srcAbsolutePath, destAbsolutePath);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Clean specified folder contents
|
|
33
|
-
await Promise.all(
|
|
34
|
-
TO_CLEAN.map((folderPath) => {
|
|
35
|
-
const absolutePath = path.resolve(targetFolder, folderPath);
|
|
36
|
-
return removeFolderContents(absolutePath);
|
|
37
|
-
})
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
// Remove specified files and folders
|
|
41
|
-
await Promise.all(
|
|
42
|
-
TO_REMOVE.map((filePath) => {
|
|
43
|
-
const absolutePath = path.resolve(targetFolder, filePath);
|
|
44
|
-
return removeFileOrFolder(absolutePath);
|
|
45
|
-
})
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
// Edit specific files
|
|
49
|
-
await Promise.all(Object.entries(TO_EDIT).map(async ([filePath, edits]) => {
|
|
50
|
-
const absolutePath = path.resolve(targetFolder, filePath);
|
|
51
|
-
await editFile(absolutePath, edits);
|
|
52
|
-
})
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
resolve();
|
|
56
|
-
} catch (err) {
|
|
57
|
-
reject(`Error during the cleaning process: ${err}`);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Removes all contents within a specified folder.
|
|
64
|
-
* @param folderPath - The path of the folder to clean.
|
|
65
|
-
*/
|
|
66
|
-
export async function removeFolderContents(folderPath: string): Promise<void> {
|
|
67
|
-
// List all files in the folder
|
|
68
|
-
const files = await fs.readdir(folderPath);
|
|
69
|
-
|
|
70
|
-
await Promise.all(
|
|
71
|
-
files.map(async (file) => {
|
|
72
|
-
const filePath = path.join(folderPath, file);
|
|
73
|
-
|
|
74
|
-
// Get the file or folder statistics
|
|
75
|
-
const stat = await fs.lstat(filePath);
|
|
76
|
-
|
|
77
|
-
// If it's a directory, remove it and all its contents
|
|
78
|
-
if (stat.isDirectory()) {
|
|
79
|
-
await fs.rm(filePath, { recursive: true, force: true });
|
|
80
|
-
} else {
|
|
81
|
-
// If it's a file, remove it
|
|
82
|
-
await fs.rm(filePath);
|
|
83
|
-
}
|
|
84
|
-
})
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Removes a specified file or folder.
|
|
90
|
-
* @param pathToRemove - The path to the file or folder to remove.
|
|
91
|
-
*/
|
|
92
|
-
export async function removeFileOrFolder(pathToRemove: string): Promise<void> {
|
|
93
|
-
await fs.rm(pathToRemove, { recursive: true, force: true });
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Helper function to copy files if the source exists.
|
|
98
|
-
* @param src - The source file path.
|
|
99
|
-
* @param dest - The destination file path.
|
|
100
|
-
*/
|
|
101
|
-
export async function copyFile(src: string, dest: string): Promise<void> {
|
|
102
|
-
try {
|
|
103
|
-
// Check if the source file exists
|
|
104
|
-
await fs.access(src);
|
|
105
|
-
|
|
106
|
-
// Ensure the destination directory exists
|
|
107
|
-
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
108
|
-
|
|
109
|
-
// Copy the file
|
|
110
|
-
await fs.copyFile(src, dest);
|
|
111
|
-
} catch (err: any) {
|
|
112
|
-
if (err.code === 'ENOENT') {
|
|
113
|
-
console.info(`\n${colors.yellow('⚠')} Source file "${src}" does not exist > Skip copy`);
|
|
114
|
-
} else {
|
|
115
|
-
throw err;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Helper function edit a file by applying regex replacements.
|
|
122
|
-
* @param filePath - The path of the file to edit.
|
|
123
|
-
* @param edits - The list of edits to apply.
|
|
124
|
-
*/
|
|
125
|
-
export async function editFile(filePath: string, edits: Array<{regexMatch: RegExp; replaceWith: string }>): Promise<void> {
|
|
126
|
-
try {
|
|
127
|
-
await fs.access(filePath);
|
|
128
|
-
|
|
129
|
-
// Read the existing file content
|
|
130
|
-
const fileContent = await fs.readFile(filePath, 'utf8');
|
|
131
|
-
let updatedContent = fileContent;
|
|
132
|
-
|
|
133
|
-
// Apply each edit to the content
|
|
134
|
-
edits.forEach(({ regexMatch, replaceWith }) => {
|
|
135
|
-
updatedContent = updatedContent.replace(regexMatch, replaceWith);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Write the updated content back to the file
|
|
139
|
-
if (fileContent !== updatedContent) {
|
|
140
|
-
await fs.writeFile(filePath, updatedContent, 'utf8');
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
} catch (err: any) {
|
|
144
|
-
if (err.code === 'ENOENT') {
|
|
145
|
-
console.info(`\n${colors.yellow('⚠')} Source file "${filePath}" does not exist > Skip edit`);
|
|
146
|
-
} else {
|
|
147
|
-
throw err;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Retrieves the version from the package.json file of a GitHub repository.
|
|
5
|
-
* If the package.json file is not found or an error occurs, it returns 'unknown'.
|
|
6
|
-
*
|
|
7
|
-
* @param repositoryUrl {string} - The GitHub repository URL in the format 'github:user/repo'.
|
|
8
|
-
* @param branch {string} - The branch name (defaults to 'main').
|
|
9
|
-
* @returns {Promise<string>} - The version from the package.json file.
|
|
10
|
-
*/
|
|
11
|
-
export async function extractPackageJsonVersionFromUri(repositoryUrl: string, branch: string = 'main'): Promise<string> {
|
|
12
|
-
// Extract owner and repo from the URL
|
|
13
|
-
const [owner, repo] = repositoryUrl.replace('github:', '').split('/');
|
|
14
|
-
|
|
15
|
-
// Construct the URL for the package.json file in the provided branch
|
|
16
|
-
const packageJsonUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/package.json`;
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
// Fetch the package.json file
|
|
20
|
-
const response = await axios.get(packageJsonUrl);
|
|
21
|
-
const packageJson = response.data;
|
|
22
|
-
|
|
23
|
-
// Return the version from the package.json, or 'unknown' if not found
|
|
24
|
-
return packageJson.version || 'unknown';
|
|
25
|
-
} catch (error) {
|
|
26
|
-
// If there's an error (file not found, etc.), return 'unknown'
|
|
27
|
-
return 'unknown';
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -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 path - The path of the directory to check.
|
|
7
|
-
* @returns Resolves to true if the directory is empty or contains only a .git folder, false otherwise.
|
|
8
|
-
* @throws 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: string): Promise<boolean> {
|
|
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,57 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
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/tsconfig.json
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"allowImportingTsExtensions": true,
|
|
5
|
-
"noEmit": true,
|
|
6
|
-
"jsx": "react-jsx",
|
|
7
|
-
"target": "ESNext",
|
|
8
|
-
"module": "ESNext",
|
|
9
|
-
|
|
10
|
-
"strict": false,
|
|
11
|
-
"incremental": false,
|
|
12
|
-
"declaration": true,
|
|
13
|
-
"composite": false,
|
|
14
|
-
},
|
|
15
|
-
"moduleResolution": "Bundler",
|
|
16
|
-
"include": ["src", "tsup.config.ts"]
|
|
17
|
-
}
|