@getdashi/cli 0.1.4 → 0.1.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/dist/bin.js CHANGED
@@ -11,6 +11,7 @@ process.on('warning', w => {
11
11
  const { Command } = await import('commander');
12
12
  const { appCreate } = await import('./commands/app-create.js');
13
13
  const { appDev } = await import('./commands/app-dev.js');
14
+ const { appUpgrade } = await import('./commands/app-upgrade.js');
14
15
  const { brand, dim, logError } = await import('./utils/ui.js');
15
16
  const { default: pkg } = await import('../package.json', { assert: { type: 'json' } });
16
17
  // ─── Banner ───────────────────────────────────────────────────────────────────
@@ -56,6 +57,18 @@ ${dim('Examples:')}
56
57
  previewPort: parseInt(opts.previewPort, 10),
57
58
  });
58
59
  });
60
+ app
61
+ .command('upgrade')
62
+ .description('⬆ upgrade @getdashi/* packages to latest')
63
+ .option('-f, --force', 'reinstall even if already at latest')
64
+ .addHelpText('after', `
65
+ ${dim('Examples:')}
66
+ ${dim('$')} dashi app upgrade
67
+ ${dim('$')} dashi app upgrade --force
68
+ `)
69
+ .action(async (opts) => {
70
+ await appUpgrade({ force: opts.force });
71
+ });
59
72
  program.parseAsync(process.argv).catch((err) => {
60
73
  logError(err instanceof Error ? err.message : String(err));
61
74
  process.exit(1);
@@ -5,6 +5,7 @@ import { dirname } from 'node:path';
5
5
  import { execSync } from 'node:child_process';
6
6
  import prompts from 'prompts';
7
7
  import { printBanner, printBox, spinner, logError, logSuccess, logStep, brand, dim, bold, url, } from '../utils/ui.js';
8
+ import { detectPm } from '../utils/detect-pm.js';
8
9
  export async function appCreate({ name, port }) {
9
10
  printBanner('app create');
10
11
  const answers = await prompts([
@@ -49,7 +50,7 @@ export async function appCreate({ name, port }) {
49
50
  const manifestPath = join(targetDir, 'public', 'manifest.json');
50
51
  const manifest = readFileSync(manifestPath, 'utf8').replace(/\{\{APP_NAME\}\}/g, appName);
51
52
  writeFileSync(manifestPath, manifest);
52
- const pm = detectPm();
53
+ const pm = detectPm(targetDir);
53
54
  const spin = spinner('Installing dependencies…');
54
55
  spin.start();
55
56
  try {
@@ -73,18 +74,3 @@ export async function appCreate({ name, port }) {
73
74
  ]);
74
75
  console.log(` Open ${url(`http://localhost:${appPort}`)} once the dev server starts.\n`);
75
76
  }
76
- function detectPm() {
77
- try {
78
- execSync('yarn --version', { stdio: 'ignore' });
79
- return 'yarn';
80
- }
81
- catch {
82
- try {
83
- execSync('pnpm --version', { stdio: 'ignore' });
84
- return 'pnpm';
85
- }
86
- catch {
87
- return 'npm';
88
- }
89
- }
90
- }
@@ -0,0 +1,5 @@
1
+ interface AppUpgradeOptions {
2
+ force?: boolean;
3
+ }
4
+ export declare function appUpgrade({ force }?: AppUpgradeOptions): Promise<void>;
5
+ export {};
@@ -0,0 +1,113 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { execSync } from 'node:child_process';
4
+ import { printBanner, spinner, logError, logSuccess, logStep, logInfo, dim, bold, } from '../utils/ui.js';
5
+ import { detectPm } from '../utils/detect-pm.js';
6
+ const DASHI_PACKAGES = ['@getdashi/client', '@getdashi/cli'];
7
+ function readPackageJson(cwd) {
8
+ const pkgPath = join(cwd, 'package.json');
9
+ if (!existsSync(pkgPath))
10
+ return null;
11
+ try {
12
+ return JSON.parse(readFileSync(pkgPath, 'utf8'));
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ function getInstalledVersion(pkg, cwd) {
19
+ const pkgJsonPath = join(cwd, 'node_modules', pkg, 'package.json');
20
+ if (!existsSync(pkgJsonPath))
21
+ return null;
22
+ try {
23
+ const { version } = JSON.parse(readFileSync(pkgJsonPath, 'utf8'));
24
+ return version ?? null;
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ }
30
+ function isDashiApp(pkg) {
31
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
32
+ return DASHI_PACKAGES.some(p => p in allDeps);
33
+ }
34
+ function presentPackages(pkg, cwd) {
35
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
36
+ const result = new Map();
37
+ for (const p of DASHI_PACKAGES) {
38
+ if (p in allDeps) {
39
+ result.set(p, getInstalledVersion(p, cwd));
40
+ }
41
+ }
42
+ return result;
43
+ }
44
+ export async function appUpgrade({ force = false } = {}) {
45
+ printBanner('app upgrade');
46
+ const cwd = resolve(process.cwd());
47
+ const pkg = readPackageJson(cwd);
48
+ if (!pkg) {
49
+ logError('No package.json found. Run this command from a dashi app directory.');
50
+ process.exit(1);
51
+ }
52
+ if (!isDashiApp(pkg)) {
53
+ logError('This does not appear to be a dashi app (no @getdashi/* packages found).');
54
+ process.exit(1);
55
+ }
56
+ const before = presentPackages(pkg, cwd);
57
+ console.log();
58
+ logStep('Current versions:');
59
+ for (const [name, version] of before) {
60
+ logInfo(`${bold(name)} ${dim(version ?? 'not installed')}`);
61
+ }
62
+ console.log();
63
+ const pm = detectPm(cwd);
64
+ const packageList = [...before.keys()];
65
+ const upgradeCmd = buildUpgradeCmd(pm, packageList);
66
+ logStep(`Running ${dim(upgradeCmd)}`);
67
+ console.log();
68
+ const spin = spinner('Upgrading dashi packages…');
69
+ spin.start();
70
+ try {
71
+ execSync(upgradeCmd, { cwd, stdio: 'pipe' });
72
+ spin.succeed('Packages upgraded');
73
+ }
74
+ catch (err) {
75
+ spin.fail('Upgrade failed');
76
+ logError(err instanceof Error ? err.message : String(err));
77
+ process.exit(1);
78
+ }
79
+ const after = presentPackages(pkg, cwd);
80
+ console.log();
81
+ let anyChanged = false;
82
+ for (const [name, newVersion] of after) {
83
+ const oldVersion = before.get(name);
84
+ if (oldVersion !== newVersion) {
85
+ logSuccess(`${bold(name)} ${dim(oldVersion ?? '—')} → ${newVersion ?? '—'}`);
86
+ anyChanged = true;
87
+ }
88
+ else if (force) {
89
+ logSuccess(`${bold(name)} ${dim('reinstalled')} ${dim(`(${newVersion ?? '—'})`)}`);
90
+ anyChanged = true;
91
+ }
92
+ else {
93
+ logInfo(`${bold(name)} ${dim('already up to date')} ${dim(`(${newVersion ?? '—'})`)}`);
94
+ }
95
+ }
96
+ if (anyChanged) {
97
+ console.log();
98
+ logStep('All dashi packages are up to date.');
99
+ }
100
+ console.log();
101
+ }
102
+ function buildUpgradeCmd(pm, packages) {
103
+ const pkgArgs = packages.map(p => `${p}@latest`).join(' ');
104
+ switch (pm) {
105
+ case 'yarn':
106
+ // `yarn add` rewrites package.json + lockfile to the resolved latest
107
+ return `yarn add ${pkgArgs} --ignore-engines`;
108
+ case 'pnpm':
109
+ return `pnpm add ${pkgArgs}`;
110
+ default:
111
+ return `npm install ${pkgArgs} --engine-strict=false`;
112
+ }
113
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Infers the package manager to use for a given project directory.
3
+ * Prefers the lockfile already present in the directory; falls back to
4
+ * whichever PM is available on PATH (yarn → pnpm → npm).
5
+ */
6
+ export declare function detectPm(cwd?: string): string;
@@ -0,0 +1,29 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { execSync } from 'node:child_process';
4
+ /**
5
+ * Infers the package manager to use for a given project directory.
6
+ * Prefers the lockfile already present in the directory; falls back to
7
+ * whichever PM is available on PATH (yarn → pnpm → npm).
8
+ */
9
+ export function detectPm(cwd = process.cwd()) {
10
+ if (existsSync(join(cwd, 'yarn.lock')))
11
+ return 'yarn';
12
+ if (existsSync(join(cwd, 'pnpm-lock.yaml')))
13
+ return 'pnpm';
14
+ if (existsSync(join(cwd, 'package-lock.json')))
15
+ return 'npm';
16
+ try {
17
+ execSync('yarn --version', { stdio: 'ignore' });
18
+ return 'yarn';
19
+ }
20
+ catch {
21
+ try {
22
+ execSync('pnpm --version', { stdio: 'ignore' });
23
+ return 'pnpm';
24
+ }
25
+ catch {
26
+ return 'npm';
27
+ }
28
+ }
29
+ }
@@ -27,7 +27,11 @@ export function servePreview(port) {
27
27
  const distDir = join(__dirname, '..', '..', 'preview-dist');
28
28
  const server = createServer((req, res) => {
29
29
  const rawPath = req.url?.split('?')[0] ?? '/';
30
- const filePath = rawPath === '/' ? 'index.html' : rawPath.replace(/^\//, '');
30
+ // The preview bundle is built with --base=/_preview/ for the platform, so
31
+ // asset URLs are prefixed with /_preview/. Strip that prefix when serving
32
+ // locally so files resolve to their actual location in preview-dist/.
33
+ const normalizedPath = rawPath.replace(/^\/_preview\//, '/');
34
+ const filePath = normalizedPath === '/' ? 'index.html' : normalizedPath.replace(/^\//, '');
31
35
  const fullPath = join(distDir, filePath);
32
36
  let exists = false;
33
37
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getdashi/cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.7",
4
4
  "license": "AGPL-3.0-only",
5
5
  "description": "Dashi app development CLI",
6
6
  "type": "module",
@@ -1,4 +1,9 @@
1
1
  @import "tailwindcss";
2
+ /* Tailwind v4's auto-detection doesn't always resolve paths correctly when
3
+ Next.js's webpack PostCSS loader compiles this file. Explicit @source
4
+ directives ensure all app and root-level TS/TSX files are scanned. */
5
+ @source ".";
6
+ @source "..";
2
7
 
3
8
  @keyframes rainbow {
4
9
  0% { color: hsl(0, 70%, 68%); }