create-epinoetics-app 1.0.6 → 1.0.7
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/bin/create-epinoetics-app.js +8 -3
- package/package.json +1 -1
- package/src/cloner.js +20 -2
- package/src/syncer.js +99 -0
|
@@ -6,6 +6,7 @@ import { resolve, dirname } from 'path';
|
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { run } from '../src/index.js';
|
|
8
8
|
import { runAdd } from '../src/adder.js';
|
|
9
|
+
import { runSync } from '../src/syncer.js';
|
|
9
10
|
|
|
10
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
const pkg = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf8'));
|
|
@@ -37,8 +38,12 @@ program
|
|
|
37
38
|
program
|
|
38
39
|
.command('add')
|
|
39
40
|
.description('Add a feature to an existing project (run from code/api-nest)')
|
|
40
|
-
.action(() =>
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
.action(() => runAdd());
|
|
42
|
+
|
|
43
|
+
// ── sync ──────────────────────────────────────────────────────────
|
|
44
|
+
program
|
|
45
|
+
.command('sync')
|
|
46
|
+
.description('Pull latest boilerplate updates (run from code/api-nest)')
|
|
47
|
+
.action(() => runSync());
|
|
43
48
|
|
|
44
49
|
program.parse(process.argv);
|
package/package.json
CHANGED
package/src/cloner.js
CHANGED
|
@@ -80,8 +80,26 @@ export async function cloneAndMergeNest({ projectName, database, selectedFeature
|
|
|
80
80
|
// 6. Write scaffold.config.json
|
|
81
81
|
writeScaffoldConfig({ dest, database, selectedFeatures, repoUrl });
|
|
82
82
|
|
|
83
|
-
// 7.
|
|
84
|
-
|
|
83
|
+
// 7. Protect src/features/ from future syncs
|
|
84
|
+
writeGitExclude({ dest });
|
|
85
|
+
|
|
86
|
+
// NOTE: .git is intentionally kept so `sync` can pull updates later
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── Write .git/info/exclude to protect key dirs from sync ─────────
|
|
90
|
+
function writeGitExclude({ dest }) {
|
|
91
|
+
const excludePath = join(dest, '.git', 'info', 'exclude');
|
|
92
|
+
const lines = [
|
|
93
|
+
'# Added by create-epinoetics-app — do not edit',
|
|
94
|
+
'src/features/',
|
|
95
|
+
'scaffold.config.json',
|
|
96
|
+
'.env',
|
|
97
|
+
'.env.local',
|
|
98
|
+
].join('\n');
|
|
99
|
+
|
|
100
|
+
fs.mkdirSync(join(dest, '.git', 'info'), { recursive: true });
|
|
101
|
+
fs.writeFileSync(excludePath, lines, 'utf8');
|
|
102
|
+
p.log.step(`Protected src/features/ from sync`);
|
|
85
103
|
}
|
|
86
104
|
|
|
87
105
|
// ── Write scaffold.config.json ────────────────────────────────────
|
package/src/syncer.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { simpleGit } from 'simple-git';
|
|
5
|
+
import { NEST_DATABASES } from './repos.js';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
|
|
8
|
+
export async function runSync() {
|
|
9
|
+
console.log('');
|
|
10
|
+
p.intro(
|
|
11
|
+
chalk.bgHex('#7c3aed').white(' create-epinoetics-app sync ') +
|
|
12
|
+
chalk.dim(' pull latest boilerplate updates')
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const cwd = process.cwd();
|
|
16
|
+
|
|
17
|
+
// ── 1. Read scaffold.config.json ──────────────────────────────────
|
|
18
|
+
const configPath = join(cwd, 'scaffold.config.json');
|
|
19
|
+
if (!fs.existsSync(configPath)) {
|
|
20
|
+
p.log.error(
|
|
21
|
+
'No scaffold.config.json found.\n' +
|
|
22
|
+
chalk.dim('Make sure you are inside the code/api-nest directory.')
|
|
23
|
+
);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
28
|
+
const database = NEST_DATABASES.find(d => d.id === config.database);
|
|
29
|
+
|
|
30
|
+
p.note(
|
|
31
|
+
[
|
|
32
|
+
`${chalk.dim('branch '.padEnd(14))} ${chalk.cyan(config.branch)}`,
|
|
33
|
+
`${chalk.dim('features'.padEnd(14))} ${config.features.length ? config.features.map(f => chalk.cyan(f)).join(chalk.dim(', ')) : chalk.dim('none')}`,
|
|
34
|
+
].join('\n'),
|
|
35
|
+
'Syncing'
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const confirm = await p.confirm({
|
|
39
|
+
message: `Pull latest changes from ${chalk.cyan(config.branch)}?`,
|
|
40
|
+
});
|
|
41
|
+
if (p.isCancel(confirm) || !confirm) return cancel();
|
|
42
|
+
|
|
43
|
+
// ── 2. Make sure .git exists ──────────────────────────────────────
|
|
44
|
+
const gitDir = join(cwd, '.git');
|
|
45
|
+
if (!fs.existsSync(gitDir)) {
|
|
46
|
+
p.log.error(
|
|
47
|
+
'.git directory not found.\n' +
|
|
48
|
+
chalk.dim('Re-scaffold this project to enable sync.')
|
|
49
|
+
);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ── 3. Stash any local changes ────────────────────────────────────
|
|
54
|
+
const git = simpleGit(cwd);
|
|
55
|
+
const spinner = p.spinner();
|
|
56
|
+
|
|
57
|
+
spinner.start('Stashing local changes...');
|
|
58
|
+
try {
|
|
59
|
+
await git.stash(['push', '--include-untracked', '-m', 'create-epinoetics-app sync']);
|
|
60
|
+
spinner.stop(`${chalk.green('✓')} Local changes stashed`);
|
|
61
|
+
} catch {
|
|
62
|
+
spinner.stop(chalk.dim('Nothing to stash'));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── 4. Pull latest from the db branch ────────────────────────────
|
|
66
|
+
const pullSpinner = p.spinner();
|
|
67
|
+
pullSpinner.start(`Pulling latest from ${chalk.cyan(config.branch)}...`);
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
await git.pull('origin', config.branch, ['--rebase']);
|
|
71
|
+
pullSpinner.stop(`${chalk.green('✓')} Pulled latest changes`);
|
|
72
|
+
} catch (err) {
|
|
73
|
+
pullSpinner.stop(`${chalk.red('✗')} Pull failed ${chalk.dim(err.message)}`);
|
|
74
|
+
p.log.warn('Restoring your stashed changes...');
|
|
75
|
+
await git.stash(['pop']).catch(() => {});
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── 5. Restore stashed changes ────────────────────────────────────
|
|
80
|
+
const stashList = await git.stashList();
|
|
81
|
+
if (stashList.total > 0) {
|
|
82
|
+
const restoreSpinner = p.spinner();
|
|
83
|
+
restoreSpinner.start('Restoring your local changes...');
|
|
84
|
+
try {
|
|
85
|
+
await git.stash(['pop']);
|
|
86
|
+
restoreSpinner.stop(`${chalk.green('✓')} Local changes restored`);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
restoreSpinner.stop(`${chalk.yellow('⚠')} Conflict restoring stash — resolve manually`);
|
|
89
|
+
p.log.warn(`Run ${chalk.cyan('git stash pop')} to restore your changes.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
p.outro(chalk.green('Sync complete!'));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function cancel() {
|
|
97
|
+
p.cancel('Cancelled.');
|
|
98
|
+
process.exit(0);
|
|
99
|
+
}
|