@cellajs/create-cella 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/index.js +1 -1
- package/package.json +32 -20
- package/src/add-remote.ts +17 -27
- package/src/constants.ts +77 -33
- package/src/create-cella-cli.ts +141 -0
- package/src/create.ts +82 -161
- package/src/modules/cli/commands.ts +58 -0
- package/src/modules/cli/display.ts +62 -0
- package/src/modules/cli/index.ts +3 -0
- package/src/modules/cli/types.ts +35 -0
- package/src/utils/clean-template.ts +24 -15
- package/src/utils/detect-used-ports.ts +57 -0
- package/src/utils/extract-package-json-version-from-uri.ts +29 -0
- package/src/utils/git/command.ts +89 -0
- package/src/utils/git/index.ts +11 -0
- package/src/utils/is-empty-directory.ts +1 -1
- package/src/utils/progress.ts +118 -0
- package/src/utils/run-package-manager-command.ts +12 -37
- package/src/utils/validate-project-name.ts +1 -4
- package/tests/e2e.test.ts +108 -0
- package/tests/validate-project-name.test.ts +22 -0
- package/tsconfig.json +19 -14
- package/tsup.config.ts +6 -5
- package/vitest.config.ts +17 -0
- package/dist/index.js +0 -617
- package/dist/index.js.map +0 -1
- package/src/cli.ts +0 -106
- package/src/index.ts +0 -132
- package/src/utils/run-git-command.ts +0 -57
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Executes a Git command in a specific repository path and returns the trimmed stdout.
|
|
8
|
+
* Uses execFile for safety (no shell injection) aligned with sync package pattern.
|
|
9
|
+
*
|
|
10
|
+
* @param args - The Git command arguments to execute (e.g., ['status'], ['checkout', 'branch-name']).
|
|
11
|
+
* @param repoPath - The path to the Git repository.
|
|
12
|
+
* @param options - Optional settings for command execution.
|
|
13
|
+
* @returns The stdout of the Git command, trimmed of leading and trailing whitespace.
|
|
14
|
+
*/
|
|
15
|
+
export async function runGitCommand(
|
|
16
|
+
args: string[],
|
|
17
|
+
repoPath: string,
|
|
18
|
+
options: { skipEditor?: boolean; maxBuffer?: number } = {},
|
|
19
|
+
): Promise<string> {
|
|
20
|
+
const gitArgs = repoPath ? ['-C', repoPath, ...args] : args;
|
|
21
|
+
|
|
22
|
+
const env = {
|
|
23
|
+
...process.env,
|
|
24
|
+
...(options.skipEditor ? { GIT_EDITOR: 'true' } : {}),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Default to 10MB buffer for typical outputs
|
|
28
|
+
const maxBuffer = options.maxBuffer ?? 10 * 1024 * 1024;
|
|
29
|
+
|
|
30
|
+
const { stdout } = await execFileAsync('git', gitArgs, { env, maxBuffer });
|
|
31
|
+
|
|
32
|
+
return stdout.trim();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Initializes a new Git repository in the specified directory.
|
|
37
|
+
*/
|
|
38
|
+
export async function gitInit(repoPath: string): Promise<string> {
|
|
39
|
+
return runGitCommand(['init'], repoPath);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Stages all files in the repository.
|
|
44
|
+
*/
|
|
45
|
+
export async function gitAddAll(repoPath: string): Promise<string> {
|
|
46
|
+
return runGitCommand(['add', '.'], repoPath);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates a commit with the specified message.
|
|
51
|
+
*/
|
|
52
|
+
export async function gitCommit(repoPath: string, message: string): Promise<string> {
|
|
53
|
+
return runGitCommand(['commit', '-m', message], repoPath);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Creates a new branch with the specified name.
|
|
58
|
+
*/
|
|
59
|
+
export async function gitBranch(repoPath: string, branchName: string): Promise<string> {
|
|
60
|
+
return runGitCommand(['branch', branchName], repoPath);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Checks out the specified branch.
|
|
65
|
+
*/
|
|
66
|
+
export async function gitCheckout(repoPath: string, branchName: string): Promise<string> {
|
|
67
|
+
return runGitCommand(['checkout', branchName], repoPath);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Gets the URL of a remote.
|
|
72
|
+
*/
|
|
73
|
+
export async function gitRemoteGetUrl(repoPath: string, remoteName: string): Promise<string> {
|
|
74
|
+
return runGitCommand(['remote', 'get-url', remoteName], repoPath);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Adds a new remote.
|
|
79
|
+
*/
|
|
80
|
+
export async function gitRemoteAdd(repoPath: string, remoteName: string, remoteUrl: string): Promise<string> {
|
|
81
|
+
return runGitCommand(['remote', 'add', remoteName, remoteUrl], repoPath);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Removes a remote.
|
|
86
|
+
*/
|
|
87
|
+
export async function gitRemoteRemove(repoPath: string, remoteName: string): Promise<string> {
|
|
88
|
+
return runGitCommand(['remote', 'remove', remoteName], repoPath);
|
|
89
|
+
}
|
|
@@ -2,7 +2,7 @@ import { readdir } from 'node:fs/promises';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Checks if a directory is empty or only contains a .git directory.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* @param path - The path of the directory to check.
|
|
7
7
|
* @returns Resolves to true if the directory is empty or contains only a .git folder, false otherwise.
|
|
8
8
|
* @throws Throws an error if the path is not a directory or if there's an issue reading the directory.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Progress Tracker Utility
|
|
3
|
+
*
|
|
4
|
+
* Provides compact progress tracking for CLI operations.
|
|
5
|
+
* Uses a single spinner that updates in place.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import ora, { type Ora } from 'ora';
|
|
9
|
+
import pc from 'picocolors';
|
|
10
|
+
|
|
11
|
+
/** Global reference to the currently active spinner */
|
|
12
|
+
let activeSpinner: Ora | null = null;
|
|
13
|
+
|
|
14
|
+
/** Pause the active spinner (for interactive prompts) */
|
|
15
|
+
export function pauseSpinner(): void {
|
|
16
|
+
if (activeSpinner) {
|
|
17
|
+
activeSpinner.stop();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Resume the active spinner after a prompt */
|
|
22
|
+
export function resumeSpinner(): void {
|
|
23
|
+
if (activeSpinner) {
|
|
24
|
+
activeSpinner.start();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ProgressTracker {
|
|
29
|
+
/** Update the current step (shown as spinner text) */
|
|
30
|
+
step: (message: string) => void;
|
|
31
|
+
/** Mark the tracker as complete with final message */
|
|
32
|
+
done: (message: string) => void;
|
|
33
|
+
/** Mark the tracker as failed with error message */
|
|
34
|
+
fail: (message: string) => void;
|
|
35
|
+
/** Wrap an async operation to auto-fail on error */
|
|
36
|
+
wrap: <T>(fn: () => Promise<T>) => Promise<T>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Creates a progress tracker for a multi-step operation.
|
|
41
|
+
* Shows a single spinner that updates in place.
|
|
42
|
+
*
|
|
43
|
+
* @param title - The initial title for the spinner
|
|
44
|
+
* @returns A ProgressTracker instance
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* const progress = createProgress('creating project');
|
|
48
|
+
* progress.step('downloading template');
|
|
49
|
+
* progress.step('installing dependencies');
|
|
50
|
+
* progress.done('project created');
|
|
51
|
+
*/
|
|
52
|
+
export function createProgress(title: string, silent = false): ProgressTracker {
|
|
53
|
+
const completedSteps: string[] = [];
|
|
54
|
+
|
|
55
|
+
const spinner = ora({
|
|
56
|
+
text: title,
|
|
57
|
+
isSilent: silent,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
activeSpinner = spinner;
|
|
61
|
+
spinner.start();
|
|
62
|
+
|
|
63
|
+
// Helper to log (respects silent mode)
|
|
64
|
+
const log = (msg: string) => {
|
|
65
|
+
if (!silent) console.info(msg);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
step: (message: string) => {
|
|
70
|
+
// Complete previous step with green check if there was one
|
|
71
|
+
if (completedSteps.length > 0) {
|
|
72
|
+
spinner.stop();
|
|
73
|
+
log(`${pc.green('✓')} ${completedSteps[completedSteps.length - 1]}`);
|
|
74
|
+
}
|
|
75
|
+
completedSteps.push(message);
|
|
76
|
+
spinner.text = message;
|
|
77
|
+
spinner.start();
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
done: (message: string) => {
|
|
81
|
+
// Complete the last step with green check
|
|
82
|
+
if (completedSteps.length > 0) {
|
|
83
|
+
spinner.stop();
|
|
84
|
+
log(`${pc.green('✓')} ${completedSteps[completedSteps.length - 1]}`);
|
|
85
|
+
} else {
|
|
86
|
+
spinner.stop();
|
|
87
|
+
}
|
|
88
|
+
activeSpinner = null;
|
|
89
|
+
if (message) {
|
|
90
|
+
log('');
|
|
91
|
+
log(`${pc.green('✓')} ${message}`);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
fail: (message: string) => {
|
|
96
|
+
spinner.stop();
|
|
97
|
+
activeSpinner = null;
|
|
98
|
+
log(`${pc.red('✗')} ${pc.red(message)}`);
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
wrap: async <T>(fn: () => Promise<T>): Promise<T> => {
|
|
102
|
+
try {
|
|
103
|
+
return await fn();
|
|
104
|
+
} catch (error) {
|
|
105
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
106
|
+
// Stop spinner and show tree-style breakdown on failure
|
|
107
|
+
spinner.stop();
|
|
108
|
+
activeSpinner = null;
|
|
109
|
+
log(pc.cyan(`\n${title}`));
|
|
110
|
+
for (const step of completedSteps) {
|
|
111
|
+
log(pc.gray(` ├─ ${step}`));
|
|
112
|
+
}
|
|
113
|
+
log(pc.red(` └─ ✗ ${errorMessage}`));
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import spawn from '
|
|
1
|
+
import spawn from 'nano-spawn';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Executes a command using the specified package manager (e.g., pnpm).
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* @param packageManager - The package manager to use (e.g., 'pnpm').
|
|
7
7
|
* @param args - The arguments to pass to the package manager command.
|
|
8
8
|
* @param env - Additional environment variables to set during command execution.
|
|
@@ -11,47 +11,22 @@ import spawn from 'cross-spawn';
|
|
|
11
11
|
export async function runPackageManagerCommand(
|
|
12
12
|
packageManager: string,
|
|
13
13
|
args: string[],
|
|
14
|
-
env: Record<string, string> = {}
|
|
14
|
+
env: Record<string, string> = {},
|
|
15
15
|
): Promise<void> {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
env: {
|
|
19
|
-
...process.env,
|
|
20
|
-
...env,
|
|
21
|
-
},
|
|
16
|
+
try {
|
|
17
|
+
await spawn(packageManager, args, {
|
|
18
|
+
env: { ...process.env, ...env },
|
|
22
19
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
23
20
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
});
|
|
21
|
+
} catch (error) {
|
|
22
|
+
const err = error as { stdout?: string; stderr?: string };
|
|
23
|
+
throw new Error(`"${packageManager} ${args.join(' ')}" failed ${err.stdout || ''} ${err.stderr || ''}`);
|
|
24
|
+
}
|
|
50
25
|
}
|
|
51
26
|
|
|
52
27
|
/**
|
|
53
28
|
* Installs dependencies using the specified package manager.
|
|
54
|
-
*
|
|
29
|
+
*
|
|
55
30
|
* @param packageManager - The package manager to use for installation (e.g., 'pnpm').
|
|
56
31
|
* @returns A promise that resolves if the installation completes successfully; otherwise, it rejects with an error.
|
|
57
32
|
*/
|
|
@@ -63,7 +38,7 @@ export async function install(packageManager: string): Promise<void> {
|
|
|
63
38
|
|
|
64
39
|
/**
|
|
65
40
|
* Generates SQL files using the specified package manager.
|
|
66
|
-
*
|
|
41
|
+
*
|
|
67
42
|
* @param packageManager - The package manager to use for generation (e.g., 'pnpm').
|
|
68
43
|
* @returns A promise that resolves if the generation completes successfully; otherwise, it rejects with an error.
|
|
69
44
|
*/
|
|
@@ -14,9 +14,6 @@ export function validateProjectName(name: string): ValidationResult {
|
|
|
14
14
|
|
|
15
15
|
return {
|
|
16
16
|
valid: false,
|
|
17
|
-
problems: [
|
|
18
|
-
...(nameValidation.errors || []),
|
|
19
|
-
...(nameValidation.warnings || []),
|
|
20
|
-
],
|
|
17
|
+
problems: [...(nameValidation.errors || []), ...(nameValidation.warnings || [])],
|
|
21
18
|
};
|
|
22
19
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
5
|
+
import { create } from '#/create';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* E2E test for create-cella CLI.
|
|
9
|
+
* This test does a full install and verifies the project is set up correctly.
|
|
10
|
+
* Note: This test is slow (~60s+) as it runs pnpm install and generate.
|
|
11
|
+
*/
|
|
12
|
+
describe('create-cella e2e', () => {
|
|
13
|
+
const projectName = `cella-e2e-test-${Date.now()}`;
|
|
14
|
+
const targetFolder = join(tmpdir(), projectName);
|
|
15
|
+
|
|
16
|
+
beforeAll(async () => {
|
|
17
|
+
// Create a full project with all steps
|
|
18
|
+
await create({
|
|
19
|
+
projectName,
|
|
20
|
+
targetFolder,
|
|
21
|
+
newBranchName: 'development',
|
|
22
|
+
packageManager: 'pnpm',
|
|
23
|
+
silent: true,
|
|
24
|
+
});
|
|
25
|
+
}, 120000); // 2 minute timeout for full install
|
|
26
|
+
|
|
27
|
+
afterAll(() => {
|
|
28
|
+
// Cleanup
|
|
29
|
+
if (existsSync(targetFolder)) {
|
|
30
|
+
rmSync(targetFolder, { recursive: true, force: true });
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('project structure', () => {
|
|
35
|
+
it('should create essential directories', () => {
|
|
36
|
+
expect(existsSync(join(targetFolder, 'backend'))).toBe(true);
|
|
37
|
+
expect(existsSync(join(targetFolder, 'frontend'))).toBe(true);
|
|
38
|
+
expect(existsSync(join(targetFolder, 'config'))).toBe(true);
|
|
39
|
+
expect(existsSync(join(targetFolder, 'locales'))).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should have node_modules installed', () => {
|
|
43
|
+
expect(existsSync(join(targetFolder, 'node_modules'))).toBe(true);
|
|
44
|
+
expect(existsSync(join(targetFolder, 'backend', 'node_modules'))).toBe(true);
|
|
45
|
+
expect(existsSync(join(targetFolder, 'frontend', 'node_modules'))).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('README.md (from QUICKSTART)', () => {
|
|
50
|
+
it('should have README.md with quickstart content', () => {
|
|
51
|
+
const readmePath = join(targetFolder, 'README.md');
|
|
52
|
+
expect(existsSync(readmePath)).toBe(true);
|
|
53
|
+
|
|
54
|
+
const content = readFileSync(readmePath, 'utf-8');
|
|
55
|
+
// Check for QUICKSTART.md content markers
|
|
56
|
+
expect(content).toContain('# Quickstart');
|
|
57
|
+
expect(content).toContain('pnpm quick');
|
|
58
|
+
expect(content).toContain('pnpm docker');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('.env files', () => {
|
|
63
|
+
it('should have backend .env file', () => {
|
|
64
|
+
const envPath = join(targetFolder, 'backend', '.env');
|
|
65
|
+
expect(existsSync(envPath)).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should have frontend .env file', () => {
|
|
69
|
+
const envPath = join(targetFolder, 'frontend', '.env');
|
|
70
|
+
expect(existsSync(envPath)).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('database migrations', () => {
|
|
75
|
+
it('should have generated drizzle migrations', () => {
|
|
76
|
+
const drizzlePath = join(targetFolder, 'backend', 'drizzle');
|
|
77
|
+
expect(existsSync(drizzlePath)).toBe(true);
|
|
78
|
+
|
|
79
|
+
// Check that migration files exist (at least one .sql file)
|
|
80
|
+
const files = readdirSync(drizzlePath);
|
|
81
|
+
const sqlFiles = files.filter((f) => f.endsWith('.sql'));
|
|
82
|
+
expect(sqlFiles.length).toBeGreaterThan(0);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should have drizzle meta folder', () => {
|
|
86
|
+
const metaPath = join(targetFolder, 'backend', 'drizzle', 'meta');
|
|
87
|
+
expect(existsSync(metaPath)).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('git repository', () => {
|
|
92
|
+
it('should have initialized git', () => {
|
|
93
|
+
expect(existsSync(join(targetFolder, '.git'))).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should be on development branch', () => {
|
|
97
|
+
const gitHead = readFileSync(join(targetFolder, '.git', 'HEAD'), 'utf-8');
|
|
98
|
+
expect(gitHead.trim()).toBe('ref: refs/heads/development');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should have upstream remote configured', () => {
|
|
102
|
+
const configPath = join(targetFolder, '.git', 'config');
|
|
103
|
+
const config = readFileSync(configPath, 'utf-8');
|
|
104
|
+
expect(config).toContain('[remote "upstream"]');
|
|
105
|
+
expect(config).toContain('cellajs/cella');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { validateProjectName } from '#/utils/validate-project-name';
|
|
4
|
+
|
|
5
|
+
describe('validateProjectName', () => {
|
|
6
|
+
it('should accept valid project names', () => {
|
|
7
|
+
expect(validateProjectName('my-app')).toEqual({ valid: true });
|
|
8
|
+
expect(validateProjectName('my-cella-app')).toEqual({ valid: true });
|
|
9
|
+
expect(validateProjectName('app123')).toEqual({ valid: true });
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should reject invalid project names', () => {
|
|
13
|
+
const result = validateProjectName('My App');
|
|
14
|
+
expect(result.valid).toBe(false);
|
|
15
|
+
expect(result.problems).toBeDefined();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should reject names starting with dots or underscores', () => {
|
|
19
|
+
expect(validateProjectName('.hidden').valid).toBe(false);
|
|
20
|
+
expect(validateProjectName('_private').valid).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
});
|
package/tsconfig.json
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"allowImportingTsExtensions": true,
|
|
5
|
+
"noEmit": true,
|
|
6
|
+
"target": "ESNext",
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"incremental": true,
|
|
10
|
+
"tsBuildInfoFile": ".tsbuildinfo",
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"composite": false,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"types": ["node"],
|
|
15
|
+
"paths": {
|
|
16
|
+
"#/*": ["./src/*"]
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"include": ["src", "tests", "index.js", "tsup.config.ts"]
|
|
20
|
+
}
|
package/tsup.config.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
1
2
|
import { defineConfig } from 'tsup';
|
|
2
3
|
|
|
3
4
|
export default defineConfig({
|
|
4
|
-
entry:
|
|
5
|
+
entry: { index: 'src/create-cella-cli.ts' },
|
|
5
6
|
outDir: 'dist',
|
|
6
7
|
clean: true,
|
|
7
8
|
minify: false,
|
|
@@ -12,11 +13,11 @@ export default defineConfig({
|
|
|
12
13
|
dts: false,
|
|
13
14
|
esbuildOptions(options) {
|
|
14
15
|
options.alias = {
|
|
15
|
-
'#': './src',
|
|
16
|
+
'#': resolve(__dirname, './src'),
|
|
16
17
|
};
|
|
17
|
-
options.platform = 'node';
|
|
18
|
-
options.mainFields = ['module', 'main'];
|
|
19
|
-
options.conditions = ['module'];
|
|
18
|
+
options.platform = 'node';
|
|
19
|
+
options.mainFields = ['module', 'main'];
|
|
20
|
+
options.conditions = ['module'];
|
|
20
21
|
},
|
|
21
22
|
external: [],
|
|
22
23
|
});
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { defineConfig } from 'vitest/config';
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
resolve: {
|
|
6
|
+
alias: {
|
|
7
|
+
'#': resolve(__dirname, './src'),
|
|
8
|
+
},
|
|
9
|
+
},
|
|
10
|
+
test: {
|
|
11
|
+
globals: true,
|
|
12
|
+
environment: 'node',
|
|
13
|
+
include: ['src/**/*.test.ts', 'tests/**/*.test.ts'],
|
|
14
|
+
testTimeout: 120000, // 2 minutes for E2E tests
|
|
15
|
+
onConsoleLog: () => false, // Suppress console output during tests
|
|
16
|
+
},
|
|
17
|
+
});
|