create-nextjs-cms 0.5.66 → 0.5.67
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/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +77 -0
- package/dist/helpers/check-directory.d.ts +6 -0
- package/dist/helpers/check-directory.d.ts.map +1 -0
- package/dist/helpers/check-directory.js +47 -0
- package/dist/{lib → helpers}/exec-utils.d.ts +1 -0
- package/dist/helpers/exec-utils.d.ts.map +1 -0
- package/dist/{lib → helpers}/exec-utils.js +2 -1
- package/dist/helpers/get-version.d.ts +2 -0
- package/dist/helpers/get-version.d.ts.map +1 -0
- package/dist/helpers/get-version.js +11 -0
- package/dist/helpers/logger.d.ts.map +1 -0
- package/dist/helpers/render-title.d.ts +3 -0
- package/dist/helpers/render-title.d.ts.map +1 -0
- package/dist/{lib → helpers}/render-title.js +10 -0
- package/dist/{lib → helpers}/utils.d.ts +9 -0
- package/dist/helpers/utils.d.ts.map +1 -0
- package/dist/{lib → helpers}/utils.js +42 -0
- package/dist/index.js +49 -235
- package/dist/lib/cms-setup.d.ts +1 -2
- package/dist/lib/cms-setup.d.ts.map +1 -1
- package/dist/lib/cms-setup.js +9 -13
- package/dist/lib/create-project.d.ts +9 -0
- package/dist/lib/create-project.d.ts.map +1 -0
- package/dist/lib/create-project.js +69 -0
- package/dist/lib/git.d.ts +6 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +116 -0
- package/dist/lib/install-deps.d.ts.map +1 -1
- package/dist/lib/install-deps.js +13 -14
- package/dist/lib/validate-app-name.d.ts +2 -0
- package/dist/lib/validate-app-name.d.ts.map +1 -0
- package/dist/lib/validate-app-name.js +10 -0
- package/package.json +1 -1
- package/templates/default/package.json +2 -1
- package/dist/lib/exec-utils.d.ts.map +0 -1
- package/dist/lib/global-package.d.ts +0 -10
- package/dist/lib/global-package.d.ts.map +0 -1
- package/dist/lib/global-package.js +0 -158
- package/dist/lib/logger.d.ts.map +0 -1
- package/dist/lib/render-title.d.ts +0 -2
- package/dist/lib/render-title.d.ts.map +0 -1
- package/dist/lib/utils.d.ts.map +0 -1
- /package/dist/{lib → helpers}/logger.d.ts +0 -0
- /package/dist/{lib → helpers}/logger.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAOA,UAAU,UAAU;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,EAAE,CAAC,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAC,EAAE,CAAA;IACjD,GAAG,EAAE,OAAO,CAAA;IACZ,gBAAgB,EAAE,OAAO,CAAA;CAC5B;AASD,eAAO,MAAM,MAAM,QAAa,OAAO,CAAC,UAAU,CAwEjD,CAAA"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { validateAppName } from '../lib/validate-app-name.js';
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { removeTrailingSlash } from '../helpers/utils.js';
|
|
6
|
+
import { getCreateNextjsCmsVersion } from '../helpers/get-version.js';
|
|
7
|
+
const defaultOptions = {
|
|
8
|
+
appName: 'my-cms-app',
|
|
9
|
+
sectionsToAdd: [],
|
|
10
|
+
git: false,
|
|
11
|
+
databaseProvider: 'mysql',
|
|
12
|
+
};
|
|
13
|
+
export const runCli = async () => {
|
|
14
|
+
const version = getCreateNextjsCmsVersion();
|
|
15
|
+
const program = new Command('create-nextjs-cms')
|
|
16
|
+
.version(version, '-v, --version', 'Output the current version of create-nextjs-cms.')
|
|
17
|
+
.argument('[directory]', 'The directory to create the project in')
|
|
18
|
+
.usage('[directory] [options]')
|
|
19
|
+
.helpOption('-h, --help', 'Display this help message.')
|
|
20
|
+
.allowUnknownOption()
|
|
21
|
+
.parse(process.argv);
|
|
22
|
+
let providedName = program.args[0];
|
|
23
|
+
if (providedName) {
|
|
24
|
+
providedName = removeTrailingSlash(providedName).trim();
|
|
25
|
+
// Validate name
|
|
26
|
+
const _notValid = validateAppName(providedName);
|
|
27
|
+
if (_notValid !== undefined) {
|
|
28
|
+
p.log.message(`${chalk.gray(`${_notValid}`)}`);
|
|
29
|
+
providedName = undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const project = await p.group({
|
|
33
|
+
name: providedName
|
|
34
|
+
? () => Promise.resolve(providedName)
|
|
35
|
+
: () => p.text({
|
|
36
|
+
message: 'What is your project named?',
|
|
37
|
+
placeholder: defaultOptions.appName,
|
|
38
|
+
defaultValue: defaultOptions.appName,
|
|
39
|
+
validate: validateAppName,
|
|
40
|
+
}),
|
|
41
|
+
sections: () => {
|
|
42
|
+
return p.multiselect({
|
|
43
|
+
message: `Add sample sections? ${chalk.gray('(hit enter with no selections to skip)')}`,
|
|
44
|
+
options: [
|
|
45
|
+
{ value: 'blog', label: 'Blog' },
|
|
46
|
+
{ value: 'category', label: 'Category' },
|
|
47
|
+
{ value: 'simple', label: 'Settings' },
|
|
48
|
+
],
|
|
49
|
+
required: false,
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
database: () => {
|
|
53
|
+
return p.select({
|
|
54
|
+
message: 'What database provider would you like to use?',
|
|
55
|
+
options: [{ value: 'mysql', label: 'MySQL', hint: 'Other database providers coming soon' }],
|
|
56
|
+
initialValue: 'mysql',
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
git: () => {
|
|
60
|
+
return p.confirm({
|
|
61
|
+
message: 'Should we initialize a Git repository and stage the changes?',
|
|
62
|
+
initialValue: false,
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
}, {
|
|
66
|
+
onCancel() {
|
|
67
|
+
p.outro('❌ Aborting installation');
|
|
68
|
+
process.exit(1);
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
appName: project.name,
|
|
73
|
+
sectionsToAdd: project.sections,
|
|
74
|
+
git: project.git,
|
|
75
|
+
databaseProvider: project.database,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-directory.d.ts","sourceRoot":"","sources":["../../src/helpers/check-directory.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,gBAAgB,GACzB,SAAS,MAAM,KAChB,OAAO,CAAC;IACP,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,OAAO,CAAA;CACvB,CAwCA,CAAA"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { expandHome } from './utils.js';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { basename } from 'node:path';
|
|
5
|
+
import fs from 'fs-extra';
|
|
6
|
+
import { isEmptyDir } from './utils.js';
|
|
7
|
+
import * as p from '@clack/prompts';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
export const resolveDirectory = async (appName) => {
|
|
10
|
+
// Resolve target path from the caller's CWD
|
|
11
|
+
const rawTarget = expandHome(appName);
|
|
12
|
+
const targetDir = path.isAbsolute(rawTarget) ? rawTarget : resolve(process.cwd(), rawTarget);
|
|
13
|
+
// Derive package name from final path
|
|
14
|
+
const projectName = basename(targetDir);
|
|
15
|
+
const targetIsCwd = path.normalize(targetDir) === path.normalize(process.cwd());
|
|
16
|
+
if (targetIsCwd) {
|
|
17
|
+
// Using current directory (".")
|
|
18
|
+
if (!(await fs.pathExists(targetDir))) {
|
|
19
|
+
await fs.ensureDir(targetDir);
|
|
20
|
+
}
|
|
21
|
+
else if (!(await isEmptyDir(targetDir))) {
|
|
22
|
+
p.log.error('Current directory is not empty. Choose an empty folder or a new directory name.');
|
|
23
|
+
p.log.message(chalk.gray('Tip: You can also specify a directory name like this: \n') +
|
|
24
|
+
chalk.green('pnpm create nextjs-cms ') +
|
|
25
|
+
chalk.italic.magenta('my-app'));
|
|
26
|
+
p.log.message(' ');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
if (await fs.pathExists(targetDir)) {
|
|
32
|
+
if (!(await isEmptyDir(targetDir))) {
|
|
33
|
+
p.log.error(`Directory "${targetDir}" is not empty.`);
|
|
34
|
+
p.log.message('Please choose an empty directory or a different directory name.');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
await fs.ensureDir(targetDir);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
targetDir,
|
|
44
|
+
projectName,
|
|
45
|
+
targetIsCwd,
|
|
46
|
+
};
|
|
47
|
+
};
|
|
@@ -20,6 +20,7 @@ export declare const execWithSpinner: (options: ExecWithSpinnerOptions) => Promi
|
|
|
20
20
|
*/
|
|
21
21
|
export declare const execWithoutSpinner: (command: string, args: string[], options?: {
|
|
22
22
|
cwd?: string;
|
|
23
|
+
stdin?: "pipe" | "ignore" | "inherit";
|
|
23
24
|
stdout?: "pipe" | "ignore" | "inherit";
|
|
24
25
|
stderr?: "pipe" | "ignore" | "inherit";
|
|
25
26
|
reject?: boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec-utils.d.ts","sourceRoot":"","sources":["../../src/helpers/exec-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAE9B,MAAM,WAAW,sBAAsB;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACtC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACvD,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,aAAa,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAU,SAAS,sBAAsB,KAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAsDzF,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC3B,SAAS,MAAM,EACf,MAAM,MAAM,EAAE,EACd,UAAS;IACL,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACrC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACtC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC1B,KACP,OAAO,CAAC,IAAI,CAWd,CAAA"}
|
|
@@ -50,9 +50,10 @@ export const execWithSpinner = async (options) => {
|
|
|
50
50
|
* Execute a command without a spinner (for cases where we want direct output)
|
|
51
51
|
*/
|
|
52
52
|
export const execWithoutSpinner = async (command, args, options = {}) => {
|
|
53
|
-
const { cwd, stdout, stderr, reject = true, env } = options;
|
|
53
|
+
const { cwd, stdin, stdout, stderr, reject = true, env } = options;
|
|
54
54
|
await execa(command, args, {
|
|
55
55
|
cwd,
|
|
56
|
+
stdin: stdin ?? (stdout === 'inherit' || stderr === 'inherit' ? 'inherit' : undefined),
|
|
56
57
|
stdout,
|
|
57
58
|
stderr,
|
|
58
59
|
reject,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-version.d.ts","sourceRoot":"","sources":["../../src/helpers/get-version.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,yBAAyB,QAAO,MAW5C,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
export const getCreateNextjsCmsVersion = () => {
|
|
6
|
+
/** Resolve __dirname for ESM */
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
const packageJson = JSON.parse(readFileSync(path.join(__dirname, '../../package.json'), 'utf-8'));
|
|
10
|
+
return packageJson.version;
|
|
11
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/helpers/logger.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM;qBACE,OAAO,EAAE;mBAGX,OAAO,EAAE;kBAGV,OAAO,EAAE;kBAGT,OAAO,EAAE;qBAGN,OAAO,EAAE;CAG7B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-title.d.ts","sourceRoot":"","sources":["../../src/helpers/render-title.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,WAAW,YASvB,CAAA;AAED,eAAO,MAAM,YAAY,YAUxB,CAAA"}
|
|
@@ -19,3 +19,13 @@ export const renderTitle = () => {
|
|
|
19
19
|
}
|
|
20
20
|
console.log(titleGradient.multiline(TITLE_TEXT));
|
|
21
21
|
};
|
|
22
|
+
export const renderFooter = () => {
|
|
23
|
+
const titleGradient = gradient(Object.values(poimandresTheme));
|
|
24
|
+
// resolves weird behavior where the ascii is offset
|
|
25
|
+
const pkgManager = detectPackageManager();
|
|
26
|
+
if (pkgManager === 'yarn' || pkgManager === 'pnpm') {
|
|
27
|
+
console.log('');
|
|
28
|
+
}
|
|
29
|
+
console.log();
|
|
30
|
+
console.log(titleGradient.multiline('Your NextJS CMS project has been created successfully!'));
|
|
31
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare const TITLE_TEXT = "\n _\n ___ _ __ ___ __ _| |_ ___\n / __| '__/ _ \\/ _\\` | __/ _ \\\n | (__| | | __/ (_| | || __/\n \\___|_| \\___|\\__,_|\\__\\___|\n _ _\n _ __ _____ _| |_ (_)___\n | '_ \\ / _ \\ \\/ / __|| / __|\n | | | | __/> <| |_ | \\__ \\\n |_| |_|\\___/_/\\_\\__|/ |___/\n |__/\n ___ _ __ ___ ___\n / __| '_ \\` _ \\/ __|\n | (__| | | | | \\__ \\\n \\___|_| |_| |_|___/\n";
|
|
2
2
|
/** Expand ~ to home */
|
|
3
3
|
export declare function expandHome(p: string): string;
|
|
4
|
+
export declare const removeTrailingSlash: (input: string) => string;
|
|
4
5
|
/** Validate npm package name (simple rule) */
|
|
5
6
|
export declare function isValidPkgName(name: string): boolean;
|
|
6
7
|
/** Check if a directory is empty */
|
|
@@ -10,4 +11,12 @@ export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
|
|
|
10
11
|
export declare function detectPackageManager(): 'pnpm' | 'npm' | 'yarn' | 'bun';
|
|
11
12
|
/** Validate template structure */
|
|
12
13
|
export declare function validateTemplate(templateDir: string): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Generates a random secret string using crypto.randomBytes
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateSecret(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Updates .env file with generated random secrets
|
|
20
|
+
*/
|
|
21
|
+
export declare function updateEnvSecrets(targetDir: string): Promise<void>;
|
|
13
22
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/helpers/utils.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,UAAU,4jBAgBtB,CAAA;AAED,uBAAuB;AACvB,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAG5C;AAED,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,WAMhD,CAAA;AAED,8CAA8C;AAC9C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGpD;AAED,oCAAoC;AACpC,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO9D;AAED,uCAAuC;AACvC,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;AAC5D,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAkBtE;AAED,kCAAkC;AAClC,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAczE;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBvE"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
+
import { randomBytes } from 'node:crypto';
|
|
5
|
+
import { log } from '@clack/prompts';
|
|
6
|
+
import chalk from 'chalk';
|
|
4
7
|
export const TITLE_TEXT = `
|
|
5
8
|
_
|
|
6
9
|
___ _ __ ___ __ _| |_ ___
|
|
@@ -24,8 +27,16 @@ export function expandHome(p) {
|
|
|
24
27
|
return path.join(os.homedir(), p.slice(1));
|
|
25
28
|
return p;
|
|
26
29
|
}
|
|
30
|
+
export const removeTrailingSlash = (input) => {
|
|
31
|
+
if (input.length > 1 && input.endsWith('/')) {
|
|
32
|
+
input = input.slice(0, -1);
|
|
33
|
+
}
|
|
34
|
+
return input;
|
|
35
|
+
};
|
|
27
36
|
/** Validate npm package name (simple rule) */
|
|
28
37
|
export function isValidPkgName(name) {
|
|
38
|
+
if (name === '.')
|
|
39
|
+
return true;
|
|
29
40
|
return /^[a-zA-Z0-9-_]+$/.test(name);
|
|
30
41
|
}
|
|
31
42
|
/** Check if a directory is empty */
|
|
@@ -74,3 +85,34 @@ export async function validateTemplate(templateDir) {
|
|
|
74
85
|
missingFiles.forEach((file) => console.warn(` - ${file}`));
|
|
75
86
|
}
|
|
76
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Generates a random secret string using crypto.randomBytes
|
|
90
|
+
*/
|
|
91
|
+
export function generateSecret() {
|
|
92
|
+
return randomBytes(32).toString('hex');
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Updates .env file with generated random secrets
|
|
96
|
+
*/
|
|
97
|
+
export async function updateEnvSecrets(targetDir) {
|
|
98
|
+
const envPath = path.join(targetDir, '.env');
|
|
99
|
+
if (!(await fs.pathExists(envPath))) {
|
|
100
|
+
log.warn('No .env file found; skipping secret generation.');
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
let envContent = await fs.readFile(envPath, 'utf-8');
|
|
105
|
+
// Replace placeholder secrets with generated ones
|
|
106
|
+
envContent = envContent.replace(/ACCESS_TOKEN_SECRET=.*/, `ACCESS_TOKEN_SECRET=${generateSecret()}`);
|
|
107
|
+
envContent = envContent.replace(/REFRESH_TOKEN_SECRET=.*/, `REFRESH_TOKEN_SECRET=${generateSecret()}`);
|
|
108
|
+
envContent = envContent.replace(/CSRF_TOKEN_SECRET=.*/, `CSRF_TOKEN_SECRET=${generateSecret()}`);
|
|
109
|
+
envContent = envContent.replace(/ACCESS_TOKEN_EXPIRATION=.*/, 'ACCESS_TOKEN_EXPIRATION=2h');
|
|
110
|
+
envContent = envContent.replace(/REFRESH_TOKEN_EXPIRATION=.*/, 'REFRESH_TOKEN_EXPIRATION=1y');
|
|
111
|
+
await fs.writeFile(envPath, envContent, 'utf-8');
|
|
112
|
+
log.message(` - Generated random secrets in root ${chalk.green('.env')} file`);
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
log.warn('Could not update .env secrets automatically.');
|
|
116
|
+
log.error(` Error: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,262 +1,76 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import path, { dirname, resolve, relative, basename } from 'node:path';
|
|
6
|
-
import { Command } from 'commander';
|
|
7
|
-
import { text, log } from '@clack/prompts';
|
|
8
|
-
import { expandHome, isValidPkgName, isEmptyDir, detectPackageManager, validateTemplate } from './lib/utils.js';
|
|
9
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
+
import * as p from '@clack/prompts';
|
|
4
|
+
import { detectPackageManager } from './helpers/utils.js';
|
|
10
5
|
import chalk from 'chalk';
|
|
11
|
-
import { renderTitle } from './
|
|
6
|
+
import { renderFooter, renderTitle } from './helpers/render-title.js';
|
|
12
7
|
import { installDependencies } from './lib/install-deps.js';
|
|
13
8
|
import { runCmsSetup } from './lib/cms-setup.js';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const packageJson = JSON.parse(readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
|
|
20
|
-
/**
|
|
21
|
-
* Your build outputs to: dist/index.js
|
|
22
|
-
* Template is published at: templates/default/
|
|
23
|
-
* So at runtime we need ../templates/default from dist/index.js.
|
|
24
|
-
*/
|
|
25
|
-
const templateDir = fileURLToPath(new URL('../templates/default/', import.meta.url));
|
|
9
|
+
import { runCli } from './cli/index.js';
|
|
10
|
+
import { initializeGit } from './lib/git.js';
|
|
11
|
+
import { resolveDirectory } from './helpers/check-directory.js';
|
|
12
|
+
import { createProject } from './lib/create-project.js';
|
|
13
|
+
import { logger } from './helpers/logger.js';
|
|
26
14
|
const handleSigTerm = () => process.exit(0);
|
|
27
15
|
process.on('SIGINT', handleSigTerm);
|
|
28
16
|
process.on('SIGTERM', handleSigTerm);
|
|
29
|
-
/**
|
|
30
|
-
* Generates a random secret string using crypto.randomBytes
|
|
31
|
-
*/
|
|
32
|
-
function generateSecret() {
|
|
33
|
-
return randomBytes(32).toString('hex');
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Updates .env file with generated random secrets
|
|
37
|
-
*/
|
|
38
|
-
async function updateEnvSecrets(targetDir) {
|
|
39
|
-
const envPath = path.join(targetDir, '.env');
|
|
40
|
-
if (!(await fs.pathExists(envPath))) {
|
|
41
|
-
log.warn('No .env file found; skipping secret generation.');
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
let envContent = await fs.readFile(envPath, 'utf-8');
|
|
46
|
-
// Replace placeholder secrets with generated ones
|
|
47
|
-
envContent = envContent.replace(/ACCESS_TOKEN_SECRET=.*/, `ACCESS_TOKEN_SECRET=${generateSecret()}`);
|
|
48
|
-
envContent = envContent.replace(/REFRESH_TOKEN_SECRET=.*/, `REFRESH_TOKEN_SECRET=${generateSecret()}`);
|
|
49
|
-
envContent = envContent.replace(/CSRF_TOKEN_SECRET=.*/, `CSRF_TOKEN_SECRET=${generateSecret()}`);
|
|
50
|
-
envContent = envContent.replace(/ACCESS_TOKEN_EXPIRATION=.*/, 'ACCESS_TOKEN_EXPIRATION=2h');
|
|
51
|
-
envContent = envContent.replace(/REFRESH_TOKEN_EXPIRATION=.*/, 'REFRESH_TOKEN_EXPIRATION=1y');
|
|
52
|
-
await fs.writeFile(envPath, envContent, 'utf-8');
|
|
53
|
-
log.message('Generated random secrets in .env file');
|
|
54
|
-
}
|
|
55
|
-
catch (e) {
|
|
56
|
-
log.warn('Could not update .env secrets automatically.');
|
|
57
|
-
log.error(` Error: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
17
|
async function createNextjsCms() {
|
|
61
18
|
renderTitle();
|
|
62
|
-
log
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.helpOption('-h, --help', 'Display this help message.')
|
|
69
|
-
.action((name) => {
|
|
70
|
-
// Commander does not implicitly support negated options. When they are used
|
|
71
|
-
// by the user they will be interpreted as the positional argument (name) in
|
|
72
|
-
// the action handler.
|
|
73
|
-
if (name && typeof name === 'string' && !name.startsWith('--no-')) {
|
|
74
|
-
projectPath = name;
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
.allowUnknownOption()
|
|
78
|
-
.parse(process.argv);
|
|
79
|
-
const options = {
|
|
80
|
-
targetDir: '',
|
|
81
|
-
projectName: '',
|
|
82
|
-
templateDir: templateDir,
|
|
83
|
-
sectionsToAdd: [],
|
|
84
|
-
};
|
|
85
|
-
let targetIsCwd = false;
|
|
19
|
+
console.log();
|
|
20
|
+
p.intro(chalk.cyan('Create new project'));
|
|
21
|
+
// Run CLI to get user input
|
|
22
|
+
const { appName, sectionsToAdd, git /*, databaseProvider*/ } = await runCli();
|
|
23
|
+
// Resolve directory
|
|
24
|
+
const { targetDir, projectName, targetIsCwd } = await resolveDirectory(appName);
|
|
86
25
|
try {
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
message: 'What is your project named?',
|
|
99
|
-
placeholder: 'my-cms-app',
|
|
100
|
-
defaultValue: 'my-cms-app',
|
|
101
|
-
validate: (name) => {
|
|
102
|
-
const validation = isValidPkgName(basename(resolve(name)));
|
|
103
|
-
if (validation) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
return 'Invalid project name: Project name can only contain letters, numbers, hyphens, and underscores.';
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
if (typeof res === 'string') {
|
|
110
|
-
projectPath = res.trim();
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (!projectPath) {
|
|
114
|
-
log.error('\nPlease specify the project directory:\n' +
|
|
115
|
-
` ${chalk.cyan(program.name())} ${chalk.green('<project-directory>')}\n` +
|
|
116
|
-
'For example:\n' +
|
|
117
|
-
` ${chalk.cyan(program.name())} ${chalk.green('my-cms-app')}\n\n` +
|
|
118
|
-
`Run ${chalk.cyan(`${program.name()} --help`)} to see all options.`);
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
// Resolve target path from the caller's CWD
|
|
122
|
-
const rawTarget = expandHome(projectPath);
|
|
123
|
-
options.targetDir = path.isAbsolute(rawTarget) ? rawTarget : resolve(process.cwd(), rawTarget);
|
|
124
|
-
// Derive package name from final path
|
|
125
|
-
options.projectName = basename(options.targetDir);
|
|
126
|
-
// Validate name
|
|
127
|
-
if (!isValidPkgName(options.projectName)) {
|
|
128
|
-
log.error(`Could not create a project called ${chalk.red(`"${options.projectName}"`)} because of npm naming restrictions:`);
|
|
129
|
-
log.message(` ${chalk.red('*')} Project name can only contain letters, numbers, hyphens, and underscores.`);
|
|
130
|
-
process.exit(1);
|
|
131
|
-
}
|
|
132
|
-
targetIsCwd = path.normalize(options.targetDir) === path.normalize(process.cwd());
|
|
133
|
-
if (targetIsCwd) {
|
|
134
|
-
// Using current directory (e.g., ".")
|
|
135
|
-
if (!(await fs.pathExists(options.targetDir))) {
|
|
136
|
-
await fs.ensureDir(options.targetDir);
|
|
137
|
-
}
|
|
138
|
-
else if (!(await isEmptyDir(options.targetDir))) {
|
|
139
|
-
log.error('Current directory is not empty. Choose an empty folder or a new directory name.');
|
|
140
|
-
log.message(chalk.gray('Tip: You can also specify a directory name like this: \n') +
|
|
141
|
-
chalk.green('pnpm create nextjs-cms ') +
|
|
142
|
-
chalk.italic.magenta('my-app'));
|
|
143
|
-
log.message(' ');
|
|
144
|
-
process.exit(1);
|
|
145
|
-
}
|
|
26
|
+
// Detect package manager
|
|
27
|
+
const preferredPM = detectPackageManager();
|
|
28
|
+
// Create project
|
|
29
|
+
await createProject({ targetDir, sectionsToAdd, projectName, preferredPM });
|
|
30
|
+
// Change working directory to the project
|
|
31
|
+
process.chdir(targetDir);
|
|
32
|
+
// Install dependencies first
|
|
33
|
+
let depsInstalled = false;
|
|
34
|
+
try {
|
|
35
|
+
await installDependencies({ projectDir: targetDir });
|
|
36
|
+
depsInstalled = true;
|
|
146
37
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (!(await isEmptyDir(options.targetDir))) {
|
|
150
|
-
log.error(`Directory "${options.targetDir}" is not empty.`);
|
|
151
|
-
log.message('Please choose an empty directory or a different directory name.');
|
|
152
|
-
process.exit(1);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
await fs.ensureDir(options.targetDir);
|
|
157
|
-
}
|
|
38
|
+
catch {
|
|
39
|
+
depsInstalled = false;
|
|
158
40
|
}
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
message: 'Would you like to add default sections?',
|
|
163
|
-
options: [
|
|
164
|
-
{ value: 'blog', label: 'Blog' },
|
|
165
|
-
{ value: 'category', label: 'Category' },
|
|
166
|
-
{ value: 'simple', label: 'Settings' },
|
|
167
|
-
],
|
|
168
|
-
required: false,
|
|
169
|
-
});
|
|
170
|
-
options.sectionsToAdd = sectionsRes;
|
|
171
|
-
log.step(`Creating project in: ${options.targetDir}`);
|
|
172
|
-
// Copy template → project dir with a filter to skip build/cache artifacts
|
|
173
|
-
await fs.copy(options.templateDir, options.targetDir, {
|
|
174
|
-
filter: (src) => {
|
|
175
|
-
const rel = relative(options.templateDir, src);
|
|
176
|
-
if (!rel || rel === '.')
|
|
177
|
-
return true;
|
|
178
|
-
return (!rel.startsWith('node_modules') &&
|
|
179
|
-
!rel.startsWith('.next') &&
|
|
180
|
-
!rel.startsWith('dist') &&
|
|
181
|
-
!rel.startsWith('.turbo') &&
|
|
182
|
-
!rel.includes('tsconfig.tsbuildinfo'));
|
|
183
|
-
},
|
|
184
|
-
// Overwrite is safe since target is ensured empty (or new)
|
|
185
|
-
overwrite: true,
|
|
186
|
-
errorOnExist: false,
|
|
187
|
-
});
|
|
188
|
-
// Generate random secrets for .env file
|
|
189
|
-
await updateEnvSecrets(options.targetDir);
|
|
190
|
-
// npm excludes .gitignore from packages, so we rename it back from _gitignore
|
|
191
|
-
const gitignoreTemplatePath = path.join(options.targetDir, '_gitignore');
|
|
192
|
-
const gitignorePath = path.join(options.targetDir, '.gitignore');
|
|
193
|
-
if (await fs.pathExists(gitignoreTemplatePath)) {
|
|
194
|
-
await fs.move(gitignoreTemplatePath, gitignorePath);
|
|
41
|
+
// Initialize Git repository if requested
|
|
42
|
+
if (git) {
|
|
43
|
+
await initializeGit(targetDir);
|
|
195
44
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
switch (section) {
|
|
200
|
-
case 'blog':
|
|
201
|
-
await createBlogSection(options.targetDir);
|
|
202
|
-
break;
|
|
203
|
-
case 'category':
|
|
204
|
-
await createCategorySection(options.targetDir);
|
|
205
|
-
break;
|
|
206
|
-
case 'simple':
|
|
207
|
-
await createSimpleSection(options.targetDir);
|
|
208
|
-
break;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
log.message(`${options.sectionsToAdd.join(', ')} section${options.sectionsToAdd.length > 1 ? 's' : ''} added successfully`);
|
|
45
|
+
if (depsInstalled) {
|
|
46
|
+
// Finally run CMS setup
|
|
47
|
+
await runCmsSetup({ dir: targetDir, preferredPM });
|
|
212
48
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
pkg.name = options.projectName;
|
|
219
|
-
await fs.writeJson(packageJsonPath, pkg, { spaces: 2 });
|
|
220
|
-
}
|
|
221
|
-
catch {
|
|
222
|
-
log.warn('Could not update package.json name automatically.');
|
|
223
|
-
}
|
|
49
|
+
renderFooter();
|
|
50
|
+
logger.message('\nNext steps:');
|
|
51
|
+
logger.message(`cd ${targetDir}`);
|
|
52
|
+
if (depsInstalled) {
|
|
53
|
+
logger.message(`${preferredPM} dev`);
|
|
224
54
|
}
|
|
225
55
|
else {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const preferredPM = detectPackageManager();
|
|
230
|
-
let installed = false;
|
|
231
|
-
try {
|
|
232
|
-
log.step(`Using ${chalk.green(preferredPM)} as the package manager...`);
|
|
233
|
-
// Install dependencies first
|
|
234
|
-
await installDependencies({ projectDir: options.targetDir });
|
|
235
|
-
installed = true;
|
|
236
|
-
// Then remove any existing global package
|
|
237
|
-
await removeGlobalPackage('nextjs-cms-kit');
|
|
238
|
-
// Install the global package
|
|
239
|
-
await installGlobalPackage('nextjs-cms-kit', packageJson.version);
|
|
240
|
-
// Finally run CMS setup
|
|
241
|
-
await runCmsSetup({ dir: options.targetDir, installed, preferredPM });
|
|
242
|
-
}
|
|
243
|
-
catch (error) {
|
|
244
|
-
log.error(`❌ Failed to complete project setup: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
245
|
-
process.exit(1);
|
|
56
|
+
logger.message(`${preferredPM} install`);
|
|
57
|
+
logger.message(`${preferredPM} nextjs-cms-kit --dev setup`);
|
|
58
|
+
logger.message(`${preferredPM} dev`);
|
|
246
59
|
}
|
|
60
|
+
console.log('\n');
|
|
247
61
|
}
|
|
248
62
|
catch (error) {
|
|
249
|
-
|
|
63
|
+
p.outro(chalk.red(`❌ Failed to create project: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
250
64
|
// Clean up partial installation on error
|
|
251
|
-
if ((await fs.pathExists(
|
|
252
|
-
log.info('🧹 Cleaning up partial installation...');
|
|
65
|
+
if ((await fs.pathExists(targetDir)) && !targetIsCwd) {
|
|
66
|
+
p.log.info('🧹 Cleaning up partial installation...');
|
|
253
67
|
try {
|
|
254
|
-
await fs.remove(
|
|
255
|
-
log.success('✅ Cleanup completed.');
|
|
68
|
+
await fs.remove(targetDir);
|
|
69
|
+
p.log.success('✅ Cleanup completed.');
|
|
256
70
|
}
|
|
257
71
|
catch {
|
|
258
|
-
log.error('⚠️ Could not clean up partial installation.');
|
|
259
|
-
log.message(` Please manually remove: ${
|
|
72
|
+
p.log.error('⚠️ Could not clean up partial installation.');
|
|
73
|
+
p.log.message(` Please manually remove: ${targetDir}`);
|
|
260
74
|
}
|
|
261
75
|
}
|
|
262
76
|
process.exit(1);
|
package/dist/lib/cms-setup.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cms-setup.d.ts","sourceRoot":"","sources":["../../src/lib/cms-setup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cms-setup.d.ts","sourceRoot":"","sources":["../../src/lib/cms-setup.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,WAAW,GAAU,sBAAsB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,kBAU3F,CAAA"}
|
package/dist/lib/cms-setup.js
CHANGED
|
@@ -1,29 +1,25 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { logger } from '
|
|
3
|
-
import { execWithoutSpinner } from '
|
|
4
|
-
import
|
|
5
|
-
const runCmsSetupCommand = async (projectDir) => {
|
|
2
|
+
import { logger } from '../helpers/logger.js';
|
|
3
|
+
import { execWithoutSpinner } from '../helpers/exec-utils.js';
|
|
4
|
+
import * as p from '@clack/prompts';
|
|
5
|
+
const runCmsSetupCommand = async ({ preferredPM, projectDir, }) => {
|
|
6
6
|
// For all package managers, use inherit for stdout/stderr to avoid hanging issues
|
|
7
7
|
// The command will show its own output and the spinner will just indicate progress
|
|
8
|
-
await execWithoutSpinner('nextjs-cms-kit',
|
|
8
|
+
await execWithoutSpinner(preferredPM, ['nextjs-cms-kit', '--dev', 'setup'], {
|
|
9
9
|
cwd: projectDir,
|
|
10
10
|
stdout: 'inherit',
|
|
11
11
|
stderr: 'inherit',
|
|
12
12
|
});
|
|
13
13
|
return null;
|
|
14
14
|
};
|
|
15
|
-
export const runCmsSetup = async ({ dir,
|
|
16
|
-
log.info(chalk.cyan('Running CMS setup
|
|
15
|
+
export const runCmsSetup = async ({ dir, preferredPM }) => {
|
|
16
|
+
p.log.info(chalk.cyan('Running CMS setup'));
|
|
17
17
|
try {
|
|
18
|
-
await runCmsSetupCommand(dir);
|
|
19
|
-
logger.success('\n🎉 Your NextJS CMS project has been created successfully!');
|
|
20
|
-
logger.message('\nNext steps:');
|
|
21
|
-
logger.message(` cd ${dir}`);
|
|
22
|
-
logger.message(installed ? ` ${preferredPM} dev` : ` ${preferredPM} install && ${preferredPM} dev`);
|
|
18
|
+
await runCmsSetupCommand({ preferredPM, projectDir: dir });
|
|
23
19
|
}
|
|
24
20
|
catch (error) {
|
|
25
21
|
logger.error('⚠️ CMS setup failed. You may need to run it manually:');
|
|
26
|
-
logger.warn(
|
|
22
|
+
logger.warn(`${preferredPM} nextjs-cms-kit --dev setup`);
|
|
27
23
|
logger.error(` Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
28
24
|
throw error;
|
|
29
25
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface CreateProjectOptions {
|
|
2
|
+
targetDir: string;
|
|
3
|
+
sectionsToAdd: ('blog' | 'category' | 'simple')[];
|
|
4
|
+
projectName: string;
|
|
5
|
+
preferredPM: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const createProject: ({ targetDir, sectionsToAdd, projectName, preferredPM }: CreateProjectOptions) => Promise<void>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=create-project.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-project.d.ts","sourceRoot":"","sources":["../../src/lib/create-project.ts"],"names":[],"mappings":"AASA,UAAU,oBAAoB;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,CAAC,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAC,EAAE,CAAA;IACjD,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;CACtB;AAED,eAAO,MAAM,aAAa,GAAU,wDAAwD,oBAAoB,kBAmE/G,CAAA"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { relative } from 'node:path';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import * as p from '@clack/prompts';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { updateEnvSecrets } from '../helpers/utils.js';
|
|
8
|
+
import { createBlogSection, createCategorySection, createSimpleSection } from './section-creators.js';
|
|
9
|
+
export const createProject = async ({ targetDir, sectionsToAdd, projectName, preferredPM }) => {
|
|
10
|
+
p.log.info(`Creating project in: ${chalk.green(targetDir)}`);
|
|
11
|
+
p.log.message(` - Using ${chalk.green(preferredPM)}`);
|
|
12
|
+
const templateDir = fileURLToPath(new URL('../../templates/default/', import.meta.url));
|
|
13
|
+
// Copy template → project dir with a filter to skip build/cache artifacts
|
|
14
|
+
await fs.copy(templateDir, targetDir, {
|
|
15
|
+
filter: (src) => {
|
|
16
|
+
const rel = relative(templateDir, src);
|
|
17
|
+
if (!rel || rel === '.')
|
|
18
|
+
return true;
|
|
19
|
+
return (!rel.startsWith('node_modules') &&
|
|
20
|
+
!rel.startsWith('.next') &&
|
|
21
|
+
!rel.startsWith('dist') &&
|
|
22
|
+
!rel.startsWith('.turbo') &&
|
|
23
|
+
!rel.includes('tsconfig.tsbuildinfo'));
|
|
24
|
+
},
|
|
25
|
+
// Overwrite is safe since target is ensured empty (or new)
|
|
26
|
+
overwrite: true,
|
|
27
|
+
errorOnExist: false,
|
|
28
|
+
});
|
|
29
|
+
// Generate random secrets for .env file
|
|
30
|
+
await updateEnvSecrets(targetDir);
|
|
31
|
+
// npm excludes .gitignore from packages, so we rename it back from _gitignore
|
|
32
|
+
const gitignoreTemplatePath = path.join(targetDir, '_gitignore');
|
|
33
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
34
|
+
if (await fs.pathExists(gitignoreTemplatePath)) {
|
|
35
|
+
await fs.move(gitignoreTemplatePath, gitignorePath);
|
|
36
|
+
}
|
|
37
|
+
// Create selected sections
|
|
38
|
+
if (sectionsToAdd.length > 0) {
|
|
39
|
+
for (const section of sectionsToAdd) {
|
|
40
|
+
switch (section) {
|
|
41
|
+
case 'blog':
|
|
42
|
+
await createBlogSection(targetDir);
|
|
43
|
+
break;
|
|
44
|
+
case 'category':
|
|
45
|
+
await createCategorySection(targetDir);
|
|
46
|
+
break;
|
|
47
|
+
case 'simple':
|
|
48
|
+
await createSimpleSection(targetDir);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
p.log.message(` - ${chalk.green(sectionsToAdd.join(', '))} section${sectionsToAdd.length > 1 ? 's' : ''} added successfully`);
|
|
53
|
+
}
|
|
54
|
+
// Update package.json name (if template contains package.json)
|
|
55
|
+
const packageJsonPath = path.join(targetDir, 'package.json');
|
|
56
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
57
|
+
try {
|
|
58
|
+
const pkg = (await fs.readJson(packageJsonPath));
|
|
59
|
+
pkg.name = projectName;
|
|
60
|
+
await fs.writeJson(packageJsonPath, pkg, { spaces: 2 });
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
p.log.warn('Could not update package.json name automatically.');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
p.log.warn('No package.json found in template root; skipping name update.');
|
|
68
|
+
}
|
|
69
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** @returns Whether or not the provided directory has a `.git` subdirectory in it. */
|
|
2
|
+
export declare const isRootGitRepo: (dir: string) => boolean;
|
|
3
|
+
/** @returns Whether or not this directory or a parent directory has a `.git` directory. */
|
|
4
|
+
export declare const isInsideGitRepo: (dir: string) => Promise<boolean>;
|
|
5
|
+
export declare const initializeGit: (projectDir: string) => Promise<void>;
|
|
6
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/lib/git.ts"],"names":[],"mappings":"AAiBA,sFAAsF;AACtF,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,KAAG,OAE3C,CAAA;AAED,2FAA2F;AAC3F,eAAO,MAAM,eAAe,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,OAAO,CAYlE,CAAA;AAkBD,eAAO,MAAM,aAAa,GAAU,YAAY,MAAM,kBA0ErD,CAAA"}
|
package/dist/lib/git.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import * as p from '@clack/prompts';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { execa } from 'execa';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
const isGitInstalled = (dir) => {
|
|
9
|
+
try {
|
|
10
|
+
execSync('git --version', { cwd: dir });
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/** @returns Whether or not the provided directory has a `.git` subdirectory in it. */
|
|
18
|
+
export const isRootGitRepo = (dir) => {
|
|
19
|
+
return fs.existsSync(path.join(dir, '.git'));
|
|
20
|
+
};
|
|
21
|
+
/** @returns Whether or not this directory or a parent directory has a `.git` directory. */
|
|
22
|
+
export const isInsideGitRepo = async (dir) => {
|
|
23
|
+
try {
|
|
24
|
+
// If this command succeeds, we're inside a git repo
|
|
25
|
+
await execa('git', ['rev-parse', '--is-inside-work-tree'], {
|
|
26
|
+
cwd: dir,
|
|
27
|
+
stdout: 'ignore',
|
|
28
|
+
});
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Else, it will throw a git-error and we return false
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const getGitVersion = () => {
|
|
37
|
+
const stdout = execSync('git --version').toString().trim();
|
|
38
|
+
const gitVersionTag = stdout.split(' ')[2];
|
|
39
|
+
const major = gitVersionTag?.split('.')[0];
|
|
40
|
+
const minor = gitVersionTag?.split('.')[1];
|
|
41
|
+
return { major: Number(major), minor: Number(minor) };
|
|
42
|
+
};
|
|
43
|
+
/** @returns The git config value of "init.defaultBranch". If it is not set, returns "main". */
|
|
44
|
+
const getDefaultBranch = () => {
|
|
45
|
+
const stdout = execSync('git config --global init.defaultBranch || echo main').toString().trim();
|
|
46
|
+
return stdout;
|
|
47
|
+
};
|
|
48
|
+
// This initializes the Git-repository for the project
|
|
49
|
+
export const initializeGit = async (projectDir) => {
|
|
50
|
+
p.log.info(chalk.cyan('Initializing Git'));
|
|
51
|
+
if (!isGitInstalled(projectDir)) {
|
|
52
|
+
p.log.warn(`❌ ${chalk.yellow('Git is not installed. Skipping Git initialization')}`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const spinner = ora('Creating a new git repo...\n').start();
|
|
56
|
+
const isRoot = isRootGitRepo(projectDir);
|
|
57
|
+
const isInside = await isInsideGitRepo(projectDir);
|
|
58
|
+
const dirName = path.parse(projectDir).name; // skip full path for logging
|
|
59
|
+
if (isInside && isRoot) {
|
|
60
|
+
// Dir is a root git repo
|
|
61
|
+
spinner.stop();
|
|
62
|
+
const overwriteGit = await p.confirm({
|
|
63
|
+
message: `${chalk.redBright.bold('Warning:')} Git is already initialized in "${dirName}". Initializing a new git repository would delete the previous history. Would you like to continue anyways?`,
|
|
64
|
+
initialValue: false,
|
|
65
|
+
});
|
|
66
|
+
if (!overwriteGit) {
|
|
67
|
+
p.log.message(` - ${chalk.gray('Skipping Git initialization')}`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
// Deleting the .git folder
|
|
71
|
+
fs.removeSync(path.join(projectDir, '.git'));
|
|
72
|
+
}
|
|
73
|
+
else if (isInside && !isRoot) {
|
|
74
|
+
// Dir is inside a git worktree
|
|
75
|
+
spinner.stop();
|
|
76
|
+
const initializeChildGitRepo = await p.confirm({
|
|
77
|
+
message: `${chalk.redBright.bold('Warning:')} "${dirName}" is already in a git worktree. Would you still like to initialize a new git repository in this directory?`,
|
|
78
|
+
initialValue: false,
|
|
79
|
+
});
|
|
80
|
+
if (!initializeChildGitRepo) {
|
|
81
|
+
p.log.message(` - ${chalk.gray('Skipping Git initialization')}`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// We're good to go, initializing the git repo
|
|
86
|
+
try {
|
|
87
|
+
const branchName = getDefaultBranch();
|
|
88
|
+
// --initial-branch flag was added in git v2.28.0
|
|
89
|
+
const { major, minor } = getGitVersion();
|
|
90
|
+
if (major < 2 || (major == 2 && minor < 28)) {
|
|
91
|
+
await execa('git', ['init'], { cwd: projectDir });
|
|
92
|
+
// symbolic-ref is used here due to refs/heads/master not existing
|
|
93
|
+
// It is only created after the first commit
|
|
94
|
+
// https://superuser.com/a/1419674
|
|
95
|
+
await execa('git', ['symbolic-ref', 'HEAD', `refs/heads/${branchName}`], {
|
|
96
|
+
cwd: projectDir,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
await execa('git', ['init', `--initial-branch=${branchName}`], {
|
|
101
|
+
cwd: projectDir,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
await execa('git', ['add', '.'], { cwd: projectDir });
|
|
105
|
+
spinner.stop();
|
|
106
|
+
p.log.message(`${chalk.green(' - ✓ Successfully initialized and staged')} ${chalk.green.bold('git')}`);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
spinner.stop();
|
|
110
|
+
// Safeguard, should be unreachable
|
|
111
|
+
p.log.message(` - ❌ ${chalk.yellow('could not initialize git. Update git to the latest version!')}`);
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
spinner.stop();
|
|
115
|
+
}
|
|
116
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-deps.d.ts","sourceRoot":"","sources":["../../src/lib/install-deps.ts"],"names":[],"mappings":"AA4DA,eAAO,MAAM,mBAAmB,GAAU,gBAAgB;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"install-deps.d.ts","sourceRoot":"","sources":["../../src/lib/install-deps.ts"],"names":[],"mappings":"AA4DA,eAAO,MAAM,mBAAmB,GAAU,gBAAgB;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,kBA8D/E,CAAA"}
|
package/dist/lib/install-deps.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import { log } from '@clack/prompts';
|
|
4
|
-
import { logger } from '
|
|
5
|
-
import { detectPackageManager } from '
|
|
6
|
-
import { execWithSpinner, execWithoutSpinner } from '
|
|
4
|
+
import { logger } from '../helpers/logger.js';
|
|
5
|
+
import { detectPackageManager } from '../helpers/utils.js';
|
|
6
|
+
import { execWithSpinner, execWithoutSpinner } from '../helpers/exec-utils.js';
|
|
7
7
|
const runInstallCommand = async (pkgManager, projectDir) => {
|
|
8
8
|
switch (pkgManager) {
|
|
9
9
|
// When using npm, inherit both stdout and stderr so that the progress bar is shown
|
|
@@ -52,7 +52,7 @@ const runInstallCommand = async (pkgManager, projectDir) => {
|
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
54
|
export const installDependencies = async ({ projectDir }) => {
|
|
55
|
-
log.info(chalk.cyan('Installing dependencies
|
|
55
|
+
log.info(chalk.cyan('Installing dependencies'));
|
|
56
56
|
const pkgManager = detectPackageManager();
|
|
57
57
|
try {
|
|
58
58
|
const installSpinner = await runInstallCommand(pkgManager, projectDir);
|
|
@@ -60,11 +60,8 @@ export const installDependencies = async ({ projectDir }) => {
|
|
|
60
60
|
// If not, create a new spinner for success message
|
|
61
61
|
if (installSpinner) {
|
|
62
62
|
installSpinner.stop();
|
|
63
|
-
// log.message(chalk.green('Successfully installed dependencies!'))
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
// log.message(chalk.green('Successfully installed dependencies!'))
|
|
67
63
|
}
|
|
64
|
+
log.message(chalk.green(' - ✓ Successfully installed dependencies'));
|
|
68
65
|
}
|
|
69
66
|
catch (error) {
|
|
70
67
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -76,7 +73,7 @@ export const installDependencies = async ({ projectDir }) => {
|
|
|
76
73
|
// Run pnpm install -g to recreate global node_modules (non-interactive)
|
|
77
74
|
try {
|
|
78
75
|
const fixSpinner = ora('Fixing pnpm global store...').start();
|
|
79
|
-
await execWithoutSpinner('pnpm', ['install', '-g', '--
|
|
76
|
+
await execWithoutSpinner('pnpm', ['install', '-g', '--force'], {
|
|
80
77
|
stdout: 'ignore',
|
|
81
78
|
stderr: 'ignore',
|
|
82
79
|
reject: false, // Don't fail if this doesn't work
|
|
@@ -84,20 +81,22 @@ export const installDependencies = async ({ projectDir }) => {
|
|
|
84
81
|
CI: 'true', // Make pnpm non-interactive
|
|
85
82
|
},
|
|
86
83
|
});
|
|
87
|
-
fixSpinner.
|
|
84
|
+
fixSpinner.stop();
|
|
85
|
+
log.message(' - Fixed pnpm global store');
|
|
88
86
|
// Retry the original install command
|
|
89
|
-
log.message('Retrying pnpm install...');
|
|
87
|
+
log.message(' - Retrying pnpm install...');
|
|
90
88
|
const retrySpinner = await runInstallCommand(pkgManager, projectDir);
|
|
91
89
|
if (retrySpinner) {
|
|
92
|
-
retrySpinner.
|
|
90
|
+
retrySpinner.stop();
|
|
91
|
+
log.message(chalk.green(' - ✓ Successfully installed dependencies'));
|
|
93
92
|
}
|
|
94
93
|
else {
|
|
95
|
-
|
|
94
|
+
log.message(chalk.green(' - ✓ Successfully installed dependencies'));
|
|
96
95
|
}
|
|
97
96
|
return;
|
|
98
97
|
}
|
|
99
98
|
catch (retryError) {
|
|
100
|
-
ora().fail(chalk.red('Failed to fix pnpm path length issue'));
|
|
99
|
+
ora().fail(chalk.red(' - ❌ Failed to fix pnpm path length issue'));
|
|
101
100
|
logger.error('⚠️ Could not automatically fix the issue.');
|
|
102
101
|
logger.message('');
|
|
103
102
|
logger.message('Try one of these solutions:');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-app-name.d.ts","sourceRoot":"","sources":["../../src/lib/validate-app-name.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,kHAQ/C,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { isValidPkgName } from '../helpers/utils.js';
|
|
2
|
+
export const validateAppName = (rawInput) => {
|
|
3
|
+
const valid = isValidPkgName(rawInput);
|
|
4
|
+
if (valid) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
else {
|
|
8
|
+
return 'Invalid project name: Project name can only contain letters, numbers, hyphens, and underscores.';
|
|
9
|
+
}
|
|
10
|
+
};
|
package/package.json
CHANGED
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"nanoid": "^5.1.2",
|
|
65
65
|
"next": "16.1.1",
|
|
66
66
|
"next-themes": "^0.4.6",
|
|
67
|
-
"nextjs-cms": "0.5.
|
|
67
|
+
"nextjs-cms": "0.5.67",
|
|
68
68
|
"plaiceholder": "^3.0.0",
|
|
69
69
|
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
70
70
|
"qrcode": "^1.5.4",
|
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
"eslint-config-prettier": "^10.0.1",
|
|
98
98
|
"eslint-plugin-prettier": "^5.2.3",
|
|
99
99
|
"fs-extra": "^11.3.3",
|
|
100
|
+
"nextjs-cms-kit": "0.5.67",
|
|
100
101
|
"postcss": "^8.5.1",
|
|
101
102
|
"prettier": "3.5.0",
|
|
102
103
|
"raw-loader": "^4.0.2",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"exec-utils.d.ts","sourceRoot":"","sources":["../../src/lib/exec-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAE9B,MAAM,WAAW,sBAAsB;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACtC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACvD,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,aAAa,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAU,SAAS,sBAAsB,KAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAsDzF,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC3B,SAAS,MAAM,EACf,MAAM,MAAM,EAAE,EACd,UAAS;IACL,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACtC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC1B,KACP,OAAO,CAAC,IAAI,CAUd,CAAA"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Remove a package globally from all package managers (to ensure clean state)
|
|
3
|
-
* Silent - spinner shown but no output, no errors, no success messages
|
|
4
|
-
*/
|
|
5
|
-
export declare const removeGlobalPackage: (packageName: string) => Promise<void>;
|
|
6
|
-
/**
|
|
7
|
-
* Install a package globally using the detected package manager
|
|
8
|
-
*/
|
|
9
|
-
export declare const installGlobalPackage: (packageName: string, version?: string) => Promise<void>;
|
|
10
|
-
//# sourceMappingURL=global-package.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"global-package.d.ts","sourceRoot":"","sources":["../../src/lib/global-package.ts"],"names":[],"mappings":"AAyIA;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,IAAI,CAc3E,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAAU,aAAa,MAAM,EAAE,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAsB9F,CAAA"}
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import ora from 'ora';
|
|
3
|
-
import { detectPackageManager } from './utils.js';
|
|
4
|
-
import { execWithSpinner, execWithoutSpinner } from './exec-utils.js';
|
|
5
|
-
import { log } from '@clack/prompts';
|
|
6
|
-
const getGlobalRemoveCommand = (pkgManager, packageName) => {
|
|
7
|
-
switch (pkgManager) {
|
|
8
|
-
case 'pnpm':
|
|
9
|
-
return ['pnpm', ['remove', '-g', packageName]];
|
|
10
|
-
case 'npm':
|
|
11
|
-
return ['npm', ['uninstall', '-g', packageName]];
|
|
12
|
-
case 'yarn':
|
|
13
|
-
return ['yarn', ['global', 'remove', packageName]];
|
|
14
|
-
case 'bun':
|
|
15
|
-
return ['bun', ['remove', '-g', packageName]];
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
const getGlobalInstallCommand = (pkgManager, packageName, version) => {
|
|
19
|
-
const packageSpec = version ? `${packageName}@${version}` : packageName;
|
|
20
|
-
switch (pkgManager) {
|
|
21
|
-
case 'pnpm':
|
|
22
|
-
return ['pnpm', ['add', '-g', packageSpec]];
|
|
23
|
-
case 'npm':
|
|
24
|
-
return ['npm', ['install', '-g', packageSpec]];
|
|
25
|
-
case 'yarn':
|
|
26
|
-
return ['yarn', ['global', 'add', packageSpec]];
|
|
27
|
-
case 'bun':
|
|
28
|
-
return ['bun', ['add', '-g', packageSpec]];
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
const runGlobalRemoveCommand = async (pkgManager, packageName) => {
|
|
32
|
-
const [command, args] = getGlobalRemoveCommand(pkgManager, packageName);
|
|
33
|
-
// Silent execution with spinner - no output, no errors that quit
|
|
34
|
-
let stderrOutput = '';
|
|
35
|
-
let spinner = await execWithSpinner({
|
|
36
|
-
command,
|
|
37
|
-
args,
|
|
38
|
-
spinnerText: `Removing ${packageName} globally...`,
|
|
39
|
-
stdout: 'ignore',
|
|
40
|
-
stderr: 'pipe', // Capture stderr to detect path length errors
|
|
41
|
-
onStderrHandle: (data) => {
|
|
42
|
-
stderrOutput += data.toString();
|
|
43
|
-
},
|
|
44
|
-
checkExitCode: false, // Don't throw on errors - we want to silently ignore failures
|
|
45
|
-
});
|
|
46
|
-
// Check if we got the pnpm path length error
|
|
47
|
-
if (pkgManager === 'pnpm' && stderrOutput.toUpperCase().includes('PNPM_VIRTUAL_STORE_DIR_MAX_LENGTH_DIFF')) {
|
|
48
|
-
// Stop the current spinner before starting the fix
|
|
49
|
-
spinner?.stop();
|
|
50
|
-
// Run pnpm install -g to fix the issue (silently)
|
|
51
|
-
spinner = await execWithSpinner({
|
|
52
|
-
command: 'pnpm',
|
|
53
|
-
args: ['install', '-g', '--yes'],
|
|
54
|
-
stdout: 'ignore',
|
|
55
|
-
stderr: 'ignore',
|
|
56
|
-
checkExitCode: false,
|
|
57
|
-
});
|
|
58
|
-
spinner?.stop();
|
|
59
|
-
// Retry the remove command
|
|
60
|
-
spinner = await execWithSpinner({
|
|
61
|
-
command,
|
|
62
|
-
args,
|
|
63
|
-
spinnerText: `Removing ${packageName} globally...`,
|
|
64
|
-
stdout: 'ignore',
|
|
65
|
-
stderr: 'ignore',
|
|
66
|
-
checkExitCode: false,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
return spinner;
|
|
70
|
-
};
|
|
71
|
-
const runGlobalInstallCommand = async (pkgManager, packageName, version) => {
|
|
72
|
-
const [command, args] = getGlobalInstallCommand(pkgManager, packageName, version);
|
|
73
|
-
switch (pkgManager) {
|
|
74
|
-
// When using npm, inherit the stderr stream so that the progress bar is shown
|
|
75
|
-
case 'npm':
|
|
76
|
-
await execWithoutSpinner(command, args, {
|
|
77
|
-
stdout: 'inherit',
|
|
78
|
-
stderr: 'inherit',
|
|
79
|
-
});
|
|
80
|
-
return null;
|
|
81
|
-
// When using yarn or pnpm, use the stdout stream and ora spinner to show the progress
|
|
82
|
-
case 'pnpm':
|
|
83
|
-
return execWithSpinner({
|
|
84
|
-
command,
|
|
85
|
-
args,
|
|
86
|
-
spinnerText: `Installing ${packageName} globally...`,
|
|
87
|
-
onDataHandle: (spinner) => (data) => {
|
|
88
|
-
const text = data.toString();
|
|
89
|
-
if (text.includes('Progress')) {
|
|
90
|
-
spinner.text = text.includes('|') ? (text.split(' | ')[1] ?? '') : text;
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
case 'yarn':
|
|
95
|
-
return execWithSpinner({
|
|
96
|
-
command,
|
|
97
|
-
args,
|
|
98
|
-
spinnerText: `Installing ${packageName} globally...`,
|
|
99
|
-
onDataHandle: (spinner) => (data) => {
|
|
100
|
-
spinner.text = data.toString();
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
// When using bun, the stdout stream is ignored and the spinner is shown
|
|
104
|
-
case 'bun':
|
|
105
|
-
return execWithSpinner({
|
|
106
|
-
command,
|
|
107
|
-
args,
|
|
108
|
-
spinnerText: `Installing ${packageName} globally...`,
|
|
109
|
-
stdout: 'ignore',
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
/**
|
|
114
|
-
* Remove a package globally from all package managers (to ensure clean state)
|
|
115
|
-
* Silent - spinner shown but no output, no errors, no success messages
|
|
116
|
-
*/
|
|
117
|
-
export const removeGlobalPackage = async (packageName) => {
|
|
118
|
-
const allPMs = ['pnpm', 'npm', 'yarn', 'bun'];
|
|
119
|
-
for (const pm of allPMs) {
|
|
120
|
-
try {
|
|
121
|
-
const spinner = await runGlobalRemoveCommand(pm, packageName);
|
|
122
|
-
// Stop spinner silently without showing success/fail
|
|
123
|
-
if (spinner) {
|
|
124
|
-
spinner.stop();
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
// Silently ignore all errors - package might not be installed with this PM
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
/**
|
|
133
|
-
* Install a package globally using the detected package manager
|
|
134
|
-
*/
|
|
135
|
-
export const installGlobalPackage = async (packageName, version) => {
|
|
136
|
-
log.info(chalk.cyan(`Installing ${packageName} globally...`));
|
|
137
|
-
const pkgManager = detectPackageManager();
|
|
138
|
-
try {
|
|
139
|
-
const installSpinner = await runGlobalInstallCommand(pkgManager, packageName, version);
|
|
140
|
-
// If the spinner was used to show the progress, use succeed method on it
|
|
141
|
-
// If not, create a new spinner for success message
|
|
142
|
-
if (installSpinner) {
|
|
143
|
-
installSpinner.stop();
|
|
144
|
-
// log.message(chalk.green(`Successfully installed ${packageName} globally!`))
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
// log.message(chalk.green(`Successfully installed ${packageName} globally!`))
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch (error) {
|
|
151
|
-
// Stop any existing spinner and show failure
|
|
152
|
-
ora().fail(chalk.red(`Failed to install ${packageName} globally`));
|
|
153
|
-
log.warn(`⚠️ Could not install ${packageName} globally.`);
|
|
154
|
-
const [command, args] = getGlobalInstallCommand(pkgManager, packageName, version);
|
|
155
|
-
log.message(` - Please run ${chalk.gray(`${command} ${args.join(' ')}`)} manually.`);
|
|
156
|
-
throw error;
|
|
157
|
-
}
|
|
158
|
-
};
|
package/dist/lib/logger.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM;qBACE,OAAO,EAAE;mBAGX,OAAO,EAAE;kBAGV,OAAO,EAAE;kBAGT,OAAO,EAAE;qBAGN,OAAO,EAAE;CAG7B,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"render-title.d.ts","sourceRoot":"","sources":["../../src/lib/render-title.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,WAAW,YASvB,CAAA"}
|
package/dist/lib/utils.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,4jBAgBtB,CAAA;AAED,uBAAuB;AACvB,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAG5C;AAED,8CAA8C;AAC9C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,oCAAoC;AACpC,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO9D;AAED,uCAAuC;AACvC,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;AAC5D,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAkBtE;AAED,kCAAkC;AAClC,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAczE"}
|
|
File without changes
|
|
File without changes
|