xgem-cli 2.0.0-alpha.10 → 2.0.0-alpha.14
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 +37 -63
- package/bin/xgem +46 -15
- package/bin/xgem.js +22 -20
- package/lib/bootstrap.sh +165 -0
- package/lib/ci.sh +44 -0
- package/lib/create.sh +140 -71
- package/lib/flutter.sh +10 -8
- package/lib/git.sh +291 -29
- package/lib/registry.sh +45 -0
- package/lib/release.sh +185 -0
- package/lib/scaffold.sh +21 -2
- package/lib/status.sh +42 -0
- package/lib/utils.sh +11 -0
- package/lib/version.sh +1 -1
- package/lib-win/bootstrap.js +189 -0
- package/lib-win/ci.js +43 -0
- package/lib-win/git.js +275 -21
- package/lib-win/registry.js +43 -0
- package/lib-win/release.js +143 -0
- package/lib-win/scaffold.js +64 -2
- package/lib-win/status.js +53 -0
- package/lib-win/utils.js +6 -1
- package/package.json +1 -1
- package/templates/node/build.sh.tmpl +9 -1
- package/templates/node/hard-clean.sh.tmpl +34 -2
- package/templates/node/lint.sh.tmpl +10 -0
- package/templates/node/start.sh.tmpl +9 -1
- package/templates/node/test.sh.tmpl +10 -0
- package/templates/webframework/build.sh.tmpl +10 -1
- package/templates/webframework/dev.sh.tmpl +9 -1
- package/templates/webframework/hard-clean.sh.tmpl +35 -2
- package/templates/webframework/lint.sh.tmpl +10 -0
- package/templates/webframework/test.sh.tmpl +10 -0
- package/templates-win/node/build.mjs.tmpl +8 -2
- package/templates-win/node/hard-clean.mjs.tmpl +24 -5
- package/templates-win/node/lint.mjs.tmpl +10 -0
- package/templates-win/node/start.mjs.tmpl +8 -1
- package/templates-win/node/test.mjs.tmpl +10 -0
- package/templates-win/webframework/build.mjs.tmpl +8 -1
- package/templates-win/webframework/dev.mjs.tmpl +8 -1
- package/templates-win/webframework/hard-clean.mjs.tmpl +28 -5
- package/templates-win/webframework/lint.mjs.tmpl +10 -0
- package/templates-win/webframework/test.mjs.tmpl +10 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// xgem Windows engine release command — port of lib/release.sh.
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const { spawnSync } = require('node:child_process');
|
|
5
|
+
const { logInfo, logSuccess, logWarn, die } = require('./logger');
|
|
6
|
+
const { prompt, confirm } = require('./utils');
|
|
7
|
+
const { defaultRemote } = require('./git');
|
|
8
|
+
|
|
9
|
+
const VERSION_FILES = ['package.json', 'pubspec.yaml', 'Cargo.toml', 'pyproject.toml'];
|
|
10
|
+
|
|
11
|
+
function detectVersionFiles() {
|
|
12
|
+
return VERSION_FILES.filter((f) => fs.existsSync(f));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function readVersion(file) {
|
|
16
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
17
|
+
if (file === 'package.json') {
|
|
18
|
+
const m = content.match(/"version"\s*:\s*"([^"]+)"/);
|
|
19
|
+
return m ? m[1] : '';
|
|
20
|
+
}
|
|
21
|
+
if (file === 'pubspec.yaml') {
|
|
22
|
+
const m = content.match(/^version:\s*(\S+)/m);
|
|
23
|
+
return m ? m[1] : '';
|
|
24
|
+
}
|
|
25
|
+
const m = content.match(/^version\s*=\s*"([^"]+)"/m);
|
|
26
|
+
return m ? m[1] : '';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function writeVersion(file, newVersion) {
|
|
30
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
31
|
+
let updated;
|
|
32
|
+
if (file === 'package.json') {
|
|
33
|
+
updated = content.replace(/"version"\s*:\s*"[^"]+"/, `"version": "${newVersion}"`);
|
|
34
|
+
} else if (file === 'pubspec.yaml') {
|
|
35
|
+
updated = content.replace(/^version:.*/m, `version: ${newVersion}`);
|
|
36
|
+
} else {
|
|
37
|
+
updated = content.replace(/^version\s*=\s*"[^"]+"/m, `version = "${newVersion}"`);
|
|
38
|
+
}
|
|
39
|
+
fs.writeFileSync(file, updated);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function bumpVersion(version, kind) {
|
|
43
|
+
const plusIdx = version.indexOf('+');
|
|
44
|
+
const core = plusIdx === -1 ? version : version.slice(0, plusIdx);
|
|
45
|
+
const suffix = plusIdx === -1 ? '' : version.slice(plusIdx);
|
|
46
|
+
|
|
47
|
+
const m = core.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
48
|
+
if (!m) return '';
|
|
49
|
+
let [major, minor, patch] = m.slice(1).map(Number);
|
|
50
|
+
if (kind === 'major') { major += 1; minor = 0; patch = 0; }
|
|
51
|
+
else if (kind === 'minor') { minor += 1; patch = 0; }
|
|
52
|
+
else { patch += 1; }
|
|
53
|
+
return `${major}.${minor}.${patch}${suffix}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function git(args, opts = {}) {
|
|
57
|
+
return spawnSync('git', args, { stdio: 'inherit', ...opts });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function gitCapture(args) {
|
|
61
|
+
const result = spawnSync('git', args, { encoding: 'utf8' });
|
|
62
|
+
return (result.stdout || '').trim();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function writeChangelog(version, lastTag) {
|
|
66
|
+
const range = lastTag ? `${lastTag}..HEAD` : 'HEAD';
|
|
67
|
+
const commits = gitCapture(['log', '--format=%s', range]).split(/\r?\n/).filter(Boolean);
|
|
68
|
+
const features = commits.filter((c) => c.startsWith('feat'));
|
|
69
|
+
const fixes = commits.filter((c) => c.startsWith('fix'));
|
|
70
|
+
const other = commits.filter((c) => !c.startsWith('feat') && !c.startsWith('fix'));
|
|
71
|
+
|
|
72
|
+
let section = `## v${version} - ${new Date().toISOString().slice(0, 10)}\n`;
|
|
73
|
+
if (features.length) section += `\n### Features\n${features.map((c) => `- ${c}`).join('\n')}\n`;
|
|
74
|
+
if (fixes.length) section += `\n### Fixes\n${fixes.map((c) => `- ${c}`).join('\n')}\n`;
|
|
75
|
+
if (other.length) section += `\n### Other\n${other.map((c) => `- ${c}`).join('\n')}\n`;
|
|
76
|
+
|
|
77
|
+
const existing = fs.existsSync('CHANGELOG.md')
|
|
78
|
+
? fs.readFileSync('CHANGELOG.md', 'utf8').split('\n').slice(1).join('\n')
|
|
79
|
+
: '';
|
|
80
|
+
fs.writeFileSync('CHANGELOG.md', `# Changelog\n\n${section}\n${existing}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function cmdRelease(bumpKind) {
|
|
84
|
+
const versionFiles = detectVersionFiles();
|
|
85
|
+
if (versionFiles.length === 0) {
|
|
86
|
+
die('No recognized version file (package.json/pubspec.yaml/Cargo.toml/pyproject.toml) found here.');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let file = versionFiles[0];
|
|
90
|
+
if (versionFiles.length > 1) {
|
|
91
|
+
console.log('Multiple version files found:');
|
|
92
|
+
versionFiles.forEach((f, i) => console.log(`${i + 1}) ${f}`));
|
|
93
|
+
const choice = await prompt(`Which is the canonical one to bump? [1-${versionFiles.length}]`);
|
|
94
|
+
const idx = parseInt(choice, 10) - 1;
|
|
95
|
+
if (Number.isNaN(idx) || idx < 0 || idx >= versionFiles.length) die('Invalid selection.');
|
|
96
|
+
file = versionFiles[idx];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const current = readVersion(file);
|
|
100
|
+
if (!current) die(`Could not read a version from ${file}.`);
|
|
101
|
+
logInfo(`Current version in ${file}: ${current}`);
|
|
102
|
+
|
|
103
|
+
if (bumpKind && !['major', 'minor', 'patch'].includes(bumpKind)) {
|
|
104
|
+
die(`Unknown bump kind '${bumpKind}'. Usage: xgem release [major|minor|patch]`);
|
|
105
|
+
}
|
|
106
|
+
if (!bumpKind) {
|
|
107
|
+
bumpKind = (await prompt('Bump [major/minor/patch]', 'patch')) || 'patch';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let newVersion = bumpVersion(current, bumpKind);
|
|
111
|
+
if (!newVersion) {
|
|
112
|
+
logWarn(`'${current}' isn't a plain X.Y.Z version — can't bump it automatically.`);
|
|
113
|
+
newVersion = await prompt('Enter the new version directly');
|
|
114
|
+
if (!newVersion) die('A new version is required.');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
logInfo(`${current} -> ${newVersion}`);
|
|
118
|
+
if (!(await confirm('Write this version, update CHANGELOG.md, commit, and tag?'))) {
|
|
119
|
+
logInfo('Cancelled.');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
writeVersion(file, newVersion);
|
|
124
|
+
|
|
125
|
+
const lastTag = gitCapture(['describe', '--tags', '--abbrev=0']);
|
|
126
|
+
writeChangelog(newVersion, lastTag);
|
|
127
|
+
|
|
128
|
+
git(['add', file, 'CHANGELOG.md']);
|
|
129
|
+
if (git(['commit', '-m', `chore(release): v${newVersion}`]).status !== 0) die('Commit failed.');
|
|
130
|
+
if (git(['tag', '-a', `v${newVersion}`, '-m', `v${newVersion}`]).status !== 0) die('Tag failed.');
|
|
131
|
+
logSuccess(`Committed and tagged v${newVersion}.`);
|
|
132
|
+
|
|
133
|
+
const remote = defaultRemote();
|
|
134
|
+
if (remote && (await confirm(`Push commit and tag to '${remote}'?`))) {
|
|
135
|
+
const currentBranch = gitCapture(['branch', '--show-current']);
|
|
136
|
+
const pushed = git(['push', remote, currentBranch]).status === 0
|
|
137
|
+
&& git(['push', remote, `v${newVersion}`]).status === 0;
|
|
138
|
+
if (pushed) logSuccess(`Pushed v${newVersion} to '${remote}'.`);
|
|
139
|
+
else die('Push failed — the commit and tag are safe locally.');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = { cmdRelease };
|
package/lib-win/scaffold.js
CHANGED
|
@@ -6,6 +6,7 @@ const fs = require('node:fs');
|
|
|
6
6
|
const path = require('node:path');
|
|
7
7
|
const { spawnSync } = require('node:child_process');
|
|
8
8
|
const { logInfo, logSuccess, logWarn, logError, die } = require('./logger');
|
|
9
|
+
const { prompt } = require('./utils');
|
|
9
10
|
|
|
10
11
|
const ALL_FRAMEWORKS = ['flutter', 'node', 'python', 'react', 'vue', 'angular', 'next', 'go', 'rust', 'docker'];
|
|
11
12
|
|
|
@@ -22,8 +23,30 @@ const FRAMEWORK_SCRIPTS = {
|
|
|
22
23
|
docker: ['hard-clean', 'build-up'],
|
|
23
24
|
};
|
|
24
25
|
|
|
26
|
+
const WEB_FRAMEWORKS = ['node', 'react', 'vue', 'angular', 'next'];
|
|
27
|
+
|
|
28
|
+
// Only offer lint/test automation when the current directory's
|
|
29
|
+
// package.json actually declares those scripts, instead of always
|
|
30
|
+
// generating scripts that fail with "Missing script" on projects that
|
|
31
|
+
// don't have them.
|
|
32
|
+
function packageJsonHasScript(scriptName) {
|
|
33
|
+
try {
|
|
34
|
+
const pkg = JSON.parse(require('node:fs').readFileSync('package.json', 'utf8'));
|
|
35
|
+
return Boolean(pkg.scripts && pkg.scripts[scriptName]);
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
25
41
|
function frameworkScripts(fw) {
|
|
26
|
-
|
|
42
|
+
const base = FRAMEWORK_SCRIPTS[fw];
|
|
43
|
+
if (!base) return null;
|
|
44
|
+
if (!WEB_FRAMEWORKS.includes(fw)) return base;
|
|
45
|
+
|
|
46
|
+
const scripts = [...base];
|
|
47
|
+
if (packageJsonHasScript('lint')) scripts.push('lint');
|
|
48
|
+
if (packageJsonHasScript('test')) scripts.push('test');
|
|
49
|
+
return scripts;
|
|
27
50
|
}
|
|
28
51
|
|
|
29
52
|
function templateDir(fw) {
|
|
@@ -77,4 +100,43 @@ function getRemainingFrameworks(configDir) {
|
|
|
77
100
|
return ALL_FRAMEWORKS.filter((fw) => !fs.existsSync(path.join(configDir, fw)));
|
|
78
101
|
}
|
|
79
102
|
|
|
80
|
-
|
|
103
|
+
// Some teams want the generated scripts checked in so collaborators get the
|
|
104
|
+
// same automation; others want them private/local-only. Ask instead of
|
|
105
|
+
// always gitignoring.
|
|
106
|
+
async function updateGitignore(configDir) {
|
|
107
|
+
const gitignorePath = '.gitignore';
|
|
108
|
+
const ignoreChoice = (await prompt(`Should ${configDir}/ be ignored by git (private to you), or tracked so collaborators get the same scripts? [ignore/track]`, 'ignore')).toLowerCase();
|
|
109
|
+
|
|
110
|
+
if (ignoreChoice.startsWith('t')) {
|
|
111
|
+
if (fs.existsSync(gitignorePath)) {
|
|
112
|
+
const lines = fs.readFileSync(gitignorePath, 'utf8').split(/\r?\n/).filter((l) => l !== `${configDir}/`);
|
|
113
|
+
fs.writeFileSync(gitignorePath, lines.join('\n'));
|
|
114
|
+
logInfo(`Removed existing ${configDir}/ entry from .gitignore since you chose to track it.`);
|
|
115
|
+
}
|
|
116
|
+
logSuccess(`${configDir}/ will be tracked in git.`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (fs.existsSync(gitignorePath)) {
|
|
121
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
122
|
+
if (!content.includes(`${configDir}/`)) {
|
|
123
|
+
fs.appendFileSync(gitignorePath, `\n${configDir}/\n`);
|
|
124
|
+
logSuccess('Added automation tracking to .gitignore');
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
fs.writeFileSync(gitignorePath, `${configDir}/\n`);
|
|
128
|
+
logSuccess('Created .gitignore and hidden tracking layer folder references.');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// bookkeeping(fw, configDir) — creates configDir/fw's scripts and updates
|
|
133
|
+
// .gitignore. Called from both `xgem init` and `xgem bootstrap` so a
|
|
134
|
+
// project scaffolded either way ends up in the same state.
|
|
135
|
+
async function bookkeeping(fw, configDir) {
|
|
136
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
137
|
+
injectTemplates(fw, configDir);
|
|
138
|
+
logSuccess(`Successfully appended standard scripts for: ${configDir}/${fw}`);
|
|
139
|
+
await updateGitignore(configDir);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = { ALL_FRAMEWORKS, frameworkScripts, injectTemplates, runScript, getRemainingFrameworks, updateGitignore, bookkeeping };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// xgem status — port of lib/status.sh: a one-screen dashboard across every
|
|
2
|
+
// project xgem has ever scaffolded.
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { spawnSync } = require('node:child_process');
|
|
7
|
+
const { logInfo } = require('./logger');
|
|
8
|
+
const { registryList } = require('./registry');
|
|
9
|
+
|
|
10
|
+
function gitCaptureIn(cwd, args) {
|
|
11
|
+
const result = spawnSync('git', args, { cwd, encoding: 'utf8' });
|
|
12
|
+
return result.status === 0 ? (result.stdout || '').trim() : '';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function statusRow(projectPath, configDir) {
|
|
16
|
+
const branch = gitCaptureIn(projectPath, ['branch', '--show-current']) || '-';
|
|
17
|
+
const porcelain = gitCaptureIn(projectPath, ['status', '--porcelain']);
|
|
18
|
+
const dirty = porcelain ? porcelain.split(/\r?\n/).filter(Boolean).length : 0;
|
|
19
|
+
|
|
20
|
+
const counts = gitCaptureIn(projectPath, ['rev-list', '--left-right', '--count', '@{u}...HEAD']);
|
|
21
|
+
let aheadBehind = '-';
|
|
22
|
+
if (counts) {
|
|
23
|
+
const [behind, ahead] = counts.split(/\s+/);
|
|
24
|
+
aheadBehind = `-${behind} +${ahead}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const configPath = path.join(projectPath, configDir);
|
|
28
|
+
const frameworks = fs.existsSync(configPath)
|
|
29
|
+
? fs.readdirSync(configPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).join(',') || '-'
|
|
30
|
+
: '-';
|
|
31
|
+
|
|
32
|
+
return { projectPath, branch, dirty, aheadBehind, frameworks };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function pad(str, width) {
|
|
36
|
+
return str.length >= width ? str : str + ' '.repeat(width - str.length);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function cmdStatus(configDir) {
|
|
40
|
+
const projects = registryList(configDir);
|
|
41
|
+
if (projects.length === 0) {
|
|
42
|
+
logInfo("No xgem-tracked projects found yet — run 'xgem init' in a project to start tracking it.");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log(`${pad('PROJECT', 45)} ${pad('BRANCH', 20)} ${pad('DIRTY', 6)} ${pad('AHEAD/BEHIND', 12)} FRAMEWORKS`);
|
|
47
|
+
for (const p of projects) {
|
|
48
|
+
const row = statusRow(p, configDir);
|
|
49
|
+
console.log(`${pad(row.projectPath, 45)} ${pad(row.branch, 20)} ${pad(String(row.dirty), 6)} ${pad(row.aheadBehind, 12)} ${row.frameworks}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = { cmdStatus };
|
package/lib-win/utils.js
CHANGED
|
@@ -32,6 +32,11 @@ function getVersion(name, args = ['--version']) {
|
|
|
32
32
|
return out ? out.trim() : null;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// openUrl(url) — best-effort browser open on Windows.
|
|
36
|
+
function openUrl(url) {
|
|
37
|
+
spawnSync('cmd', ['/c', 'start', '""', url], { stdio: 'ignore' });
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
// confirm(prompt) -> Promise<boolean>. Honors XGEM_YES (auto-approve) and
|
|
36
41
|
// XGEM_DRY_RUN (always decline, just like lib/utils.sh's confirm()).
|
|
37
42
|
function confirm(prompt) {
|
|
@@ -64,4 +69,4 @@ function prompt(question, defaultValue = '') {
|
|
|
64
69
|
});
|
|
65
70
|
}
|
|
66
71
|
|
|
67
|
-
module.exports = { detectArch, hasCmd, requireCmd, getVersion, confirm, prompt };
|
|
72
|
+
module.exports = { detectArch, hasCmd, requireCmd, getVersion, confirm, prompt, openUrl };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xgem-cli",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.14",
|
|
4
4
|
"description": "Framework-aware automation CLI: scaffolds and runs clean/build/dev scripts per project type, an environment doctor, a SwiftPM-aware iOS build engine, and a git workflow helper. Runs natively on macOS, Linux, and Windows.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"xgem": "bin/xgem.js"
|
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
echo -e "\033[1;34mBuilding Node project...\033[0m"
|
|
3
|
-
|
|
3
|
+
if [ -f pnpm-lock.yaml ]; then
|
|
4
|
+
pnpm run build
|
|
5
|
+
elif [ -f yarn.lock ]; then
|
|
6
|
+
yarn build
|
|
7
|
+
elif [ -f bun.lockb ] || [ -f bun.lock ]; then
|
|
8
|
+
bun run build
|
|
9
|
+
else
|
|
10
|
+
npm run build
|
|
11
|
+
fi
|
|
@@ -1,3 +1,35 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
# Detect the package manager actually in use instead of assuming npm —
|
|
3
|
+
# blindly deleting yarn.lock/pnpm-lock.yaml on a project that uses them
|
|
4
|
+
# and running `npm install` instead corrupts the project by introducing a
|
|
5
|
+
# conflicting lockfile.
|
|
6
|
+
PKG_MANAGER=npm
|
|
7
|
+
if [ -f pnpm-lock.yaml ]; then
|
|
8
|
+
PKG_MANAGER=pnpm
|
|
9
|
+
elif [ -f yarn.lock ]; then
|
|
10
|
+
PKG_MANAGER=yarn
|
|
11
|
+
elif [ -f bun.lockb ] || [ -f bun.lock ]; then
|
|
12
|
+
PKG_MANAGER=bun
|
|
13
|
+
elif [ -f package.json ]; then
|
|
14
|
+
declared=$(grep -o '"packageManager"[[:space:]]*:[[:space:]]*"[a-z]*' package.json 2>/dev/null | grep -oE '[a-z]+$')
|
|
15
|
+
case "$declared" in npm|yarn|pnpm|bun) PKG_MANAGER=$declared ;; esac
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
if [ -f .nvmrc ] && command -v node >/dev/null 2>&1; then
|
|
19
|
+
wanted=$(tr -d 'v[:space:]' < .nvmrc)
|
|
20
|
+
have=$(node -v | tr -d 'v')
|
|
21
|
+
case "$have" in
|
|
22
|
+
"$wanted"*) ;;
|
|
23
|
+
*) echo -e "\033[33mWarning: .nvmrc wants Node $wanted, but 'node -v' reports $have.\033[0m" ;;
|
|
24
|
+
esac
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
echo -e "\033[1;33mNuking node_modules and resetting package locks (package manager: $PKG_MANAGER)...\033[0m"
|
|
28
|
+
rm -rf node_modules
|
|
29
|
+
|
|
30
|
+
case "$PKG_MANAGER" in
|
|
31
|
+
yarn) rm -f yarn.lock; yarn install ;;
|
|
32
|
+
pnpm) rm -f pnpm-lock.yaml; pnpm install ;;
|
|
33
|
+
bun) rm -f bun.lockb bun.lock; bun install ;;
|
|
34
|
+
*) rm -f package-lock.json; npm install ;;
|
|
35
|
+
esac
|
|
@@ -1,2 +1,11 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
|
|
2
|
+
# Use whatever package manager this project actually uses.
|
|
3
|
+
if [ -f pnpm-lock.yaml ]; then
|
|
4
|
+
pnpm run build
|
|
5
|
+
elif [ -f yarn.lock ]; then
|
|
6
|
+
yarn build
|
|
7
|
+
elif [ -f bun.lockb ] || [ -f bun.lock ]; then
|
|
8
|
+
bun run build
|
|
9
|
+
else
|
|
10
|
+
npm run build
|
|
11
|
+
fi
|
|
@@ -1,3 +1,36 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
# Detect the package manager actually in use instead of assuming npm —
|
|
3
|
+
# blindly deleting yarn.lock/pnpm-lock.yaml on a project that uses them
|
|
4
|
+
# and running `npm install` instead corrupts the project by introducing a
|
|
5
|
+
# conflicting lockfile.
|
|
6
|
+
PKG_MANAGER=npm
|
|
7
|
+
if [ -f pnpm-lock.yaml ]; then
|
|
8
|
+
PKG_MANAGER=pnpm
|
|
9
|
+
elif [ -f yarn.lock ]; then
|
|
10
|
+
PKG_MANAGER=yarn
|
|
11
|
+
elif [ -f bun.lockb ] || [ -f bun.lock ]; then
|
|
12
|
+
PKG_MANAGER=bun
|
|
13
|
+
elif [ -f package.json ]; then
|
|
14
|
+
declared=$(grep -o '"packageManager"[[:space:]]*:[[:space:]]*"[a-z]*' package.json 2>/dev/null | grep -oE '[a-z]+$')
|
|
15
|
+
case "$declared" in npm|yarn|pnpm|bun) PKG_MANAGER=$declared ;; esac
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Warn (don't block) on a Node version mismatch against .nvmrc, if present.
|
|
19
|
+
if [ -f .nvmrc ] && command -v node >/dev/null 2>&1; then
|
|
20
|
+
wanted=$(tr -d 'v[:space:]' < .nvmrc)
|
|
21
|
+
have=$(node -v | tr -d 'v')
|
|
22
|
+
case "$have" in
|
|
23
|
+
"$wanted"*) ;;
|
|
24
|
+
*) echo -e "\033[33mWarning: .nvmrc wants Node $wanted, but 'node -v' reports $have.\033[0m" ;;
|
|
25
|
+
esac
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
echo -e "\033[1;33mResetting __FRAMEWORK__ dependencies (package manager: $PKG_MANAGER)...\033[0m"
|
|
29
|
+
rm -rf node_modules
|
|
30
|
+
|
|
31
|
+
case "$PKG_MANAGER" in
|
|
32
|
+
yarn) rm -f yarn.lock; yarn install ;;
|
|
33
|
+
pnpm) rm -f pnpm-lock.yaml; pnpm install ;;
|
|
34
|
+
bun) rm -f bun.lockb bun.lock; bun install ;;
|
|
35
|
+
*) rm -f package-lock.json; npm install ;;
|
|
36
|
+
esac
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
let cmd = 'npm run build';
|
|
6
|
+
if (existsSync('pnpm-lock.yaml')) cmd = 'pnpm run build';
|
|
7
|
+
else if (existsSync('yarn.lock')) cmd = 'yarn build';
|
|
8
|
+
else if (existsSync('bun.lockb') || existsSync('bun.lock')) cmd = 'bun run build';
|
|
9
|
+
|
|
10
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
|
-
import { rmSync } from 'node:fs';
|
|
3
|
+
import { existsSync, rmSync, readFileSync } from 'node:fs';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
function detectPkgManager() {
|
|
6
|
+
if (existsSync('pnpm-lock.yaml')) return 'pnpm';
|
|
7
|
+
if (existsSync('yarn.lock')) return 'yarn';
|
|
8
|
+
if (existsSync('bun.lockb') || existsSync('bun.lock')) return 'bun';
|
|
9
|
+
if (existsSync('package.json')) {
|
|
10
|
+
try {
|
|
11
|
+
const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
|
|
12
|
+
if (['npm', 'yarn', 'pnpm', 'bun'].includes(pkg.packageManager?.split('@')[0])) {
|
|
13
|
+
return pkg.packageManager.split('@')[0];
|
|
14
|
+
}
|
|
15
|
+
} catch { /* ignore malformed package.json */ }
|
|
16
|
+
}
|
|
17
|
+
return 'npm';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const pm = detectPkgManager();
|
|
21
|
+
console.log(`Nuking node_modules and resetting package locks (package manager: ${pm})...`);
|
|
6
22
|
rmSync('node_modules', { recursive: true, force: true });
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
23
|
+
|
|
24
|
+
const lockfiles = { npm: ['package-lock.json'], yarn: ['yarn.lock'], pnpm: ['pnpm-lock.yaml'], bun: ['bun.lockb', 'bun.lock'] };
|
|
25
|
+
for (const f of lockfiles[pm]) rmSync(f, { force: true });
|
|
26
|
+
|
|
27
|
+
const installCmd = { npm: 'npm install', yarn: 'yarn', pnpm: 'pnpm install', bun: 'bun install' };
|
|
28
|
+
execSync(installCmd[pm], { stdio: 'inherit' });
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
let cmd = 'npm run lint';
|
|
6
|
+
if (existsSync('pnpm-lock.yaml')) cmd = 'pnpm run lint';
|
|
7
|
+
else if (existsSync('yarn.lock')) cmd = 'yarn lint';
|
|
8
|
+
else if (existsSync('bun.lockb') || existsSync('bun.lock')) cmd = 'bun run lint';
|
|
9
|
+
|
|
10
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
|
-
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
let cmd = 'npm run start';
|
|
6
|
+
if (existsSync('pnpm-lock.yaml')) cmd = 'pnpm run start';
|
|
7
|
+
else if (existsSync('yarn.lock')) cmd = 'yarn start';
|
|
8
|
+
else if (existsSync('bun.lockb') || existsSync('bun.lock')) cmd = 'bun run start';
|
|
9
|
+
|
|
10
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
let cmd = 'npm run test';
|
|
6
|
+
if (existsSync('pnpm-lock.yaml')) cmd = 'pnpm run test';
|
|
7
|
+
else if (existsSync('yarn.lock')) cmd = 'yarn test';
|
|
8
|
+
else if (existsSync('bun.lockb') || existsSync('bun.lock')) cmd = 'bun run test';
|
|
9
|
+
|
|
10
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
|
-
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
let cmd = 'npm run build';
|
|
6
|
+
if (existsSync('pnpm-lock.yaml')) cmd = 'pnpm run build';
|
|
7
|
+
else if (existsSync('yarn.lock')) cmd = 'yarn build';
|
|
8
|
+
else if (existsSync('bun.lockb') || existsSync('bun.lock')) cmd = 'bun run build';
|
|
9
|
+
|
|
10
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
|
-
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
let cmd = 'npm run dev';
|
|
6
|
+
if (existsSync('pnpm-lock.yaml')) cmd = 'pnpm run dev';
|
|
7
|
+
else if (existsSync('yarn.lock')) cmd = 'yarn dev';
|
|
8
|
+
else if (existsSync('bun.lockb') || existsSync('bun.lock')) cmd = 'bun run dev';
|
|
9
|
+
|
|
10
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
@@ -1,9 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
|
-
import { rmSync } from 'node:fs';
|
|
3
|
+
import { existsSync, rmSync, readFileSync } from 'node:fs';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// Detect the package manager actually in use instead of assuming npm —
|
|
6
|
+
// blindly deleting yarn.lock/pnpm-lock.yaml on a project that uses them
|
|
7
|
+
// and running `npm install` instead corrupts the project by introducing a
|
|
8
|
+
// conflicting lockfile.
|
|
9
|
+
function detectPkgManager() {
|
|
10
|
+
if (existsSync('pnpm-lock.yaml')) return 'pnpm';
|
|
11
|
+
if (existsSync('yarn.lock')) return 'yarn';
|
|
12
|
+
if (existsSync('bun.lockb') || existsSync('bun.lock')) return 'bun';
|
|
13
|
+
if (existsSync('package.json')) {
|
|
14
|
+
try {
|
|
15
|
+
const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
|
|
16
|
+
if (['npm', 'yarn', 'pnpm', 'bun'].includes(pkg.packageManager?.split('@')[0])) {
|
|
17
|
+
return pkg.packageManager.split('@')[0];
|
|
18
|
+
}
|
|
19
|
+
} catch { /* ignore malformed package.json */ }
|
|
20
|
+
}
|
|
21
|
+
return 'npm';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const pm = detectPkgManager();
|
|
25
|
+
console.log(`Resetting __FRAMEWORK__ dependencies (package manager: ${pm})...`);
|
|
6
26
|
rmSync('node_modules', { recursive: true, force: true });
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
27
|
+
|
|
28
|
+
const lockfiles = { npm: ['package-lock.json'], yarn: ['yarn.lock'], pnpm: ['pnpm-lock.yaml'], bun: ['bun.lockb', 'bun.lock'] };
|
|
29
|
+
for (const f of lockfiles[pm]) rmSync(f, { force: true });
|
|
30
|
+
|
|
31
|
+
const installCmd = { npm: 'npm install', yarn: 'yarn', pnpm: 'pnpm install', bun: 'bun install' };
|
|
32
|
+
execSync(installCmd[pm], { stdio: 'inherit' });
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
let cmd = 'npm run lint';
|
|
6
|
+
if (existsSync('pnpm-lock.yaml')) cmd = 'pnpm run lint';
|
|
7
|
+
else if (existsSync('yarn.lock')) cmd = 'yarn lint';
|
|
8
|
+
else if (existsSync('bun.lockb') || existsSync('bun.lock')) cmd = 'bun run lint';
|
|
9
|
+
|
|
10
|
+
execSync(cmd, { stdio: 'inherit' });
|