@ubean/cli 0.1.3 → 0.1.5

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.
Files changed (2) hide show
  1. package/dist/cli.js +145 -7
  2. package/package.json +12 -11
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { S as createFsOps, a as scaffold, i as recoverScaffold, n as listScaffoldableFiles, r as pageCommand, t as deleteScaffold, v as renderTemplate } from "./page-Cmp7s9l2.js";
2
- import { existsSync, mkdirSync } from "node:fs";
2
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
3
3
  import { extname, normalize } from "node:path";
4
4
  import { consola } from "consola";
5
5
  import { defineCommand, runMain } from "citty";
@@ -15,7 +15,8 @@ import { basename, join as join$1, resolve as resolve$1 } from "pathe";
15
15
  import { createUbeanApp } from "@ubean/app";
16
16
  import { createDevRunner, createDevWatcher, logDiagnostics } from "@ubean/dev-server";
17
17
  import { bold, cyan, dim, green } from "kolorist";
18
- import { spawn } from "node:child_process";
18
+ import { execSync, spawn } from "node:child_process";
19
+ import { BUILTIN_MODULES, isBuiltinDisabled } from "@ubean/modules";
19
20
  import { createServer } from "node:http";
20
21
  import { findAvailablePort, waitForPort } from "@ubean/utils";
21
22
  //#region src/config.ts
@@ -1637,20 +1638,157 @@ async function ensureBuildDir(cwd, buildDir) {
1637
1638
  const fullPath = join$1(cwd, buildDir);
1638
1639
  if (!existsSync(fullPath)) mkdirSync(fullPath, { recursive: true });
1639
1640
  }
1641
+ /**
1642
+ * Detects the package manager used in the project by inspecting lockfiles.
1643
+ * Priority: pnpm > yarn > bun > npm (npm as default fallback).
1644
+ */
1645
+ function detectPackageManager(cwd) {
1646
+ if (existsSync(join$1(cwd, "pnpm-lock.yaml"))) return "pnpm";
1647
+ if (existsSync(join$1(cwd, "yarn.lock"))) return "yarn";
1648
+ if (existsSync(join$1(cwd, "bun.lockb")) || existsSync(join$1(cwd, "bun.lock"))) return "bun";
1649
+ return "npm";
1650
+ }
1651
+ /**
1652
+ * Builds the install command args for a given package manager and package list.
1653
+ * Examples:
1654
+ * pnpm → ['add', '@ubean/ui@^0.1.3']
1655
+ * npm → ['install', '@ubean/ui@^0.1.3']
1656
+ */
1657
+ function buildInstallCommand(pm, packages) {
1658
+ switch (pm) {
1659
+ case "pnpm": return {
1660
+ cmd: "pnpm",
1661
+ args: ["add", ...packages]
1662
+ };
1663
+ case "yarn": return {
1664
+ cmd: "yarn",
1665
+ args: ["add", ...packages]
1666
+ };
1667
+ case "bun": return {
1668
+ cmd: "bun",
1669
+ args: ["add", ...packages]
1670
+ };
1671
+ default: return {
1672
+ cmd: "npm",
1673
+ args: ["install", ...packages]
1674
+ };
1675
+ }
1676
+ }
1677
+ /**
1678
+ * Extracts the bare package name from a module path.
1679
+ * '@ubean/ui/vite' → '@ubean/ui'
1680
+ * '@ubean/electron/vite' → '@ubean/electron'
1681
+ */
1682
+ function extractPackageName(modulePath) {
1683
+ if (modulePath.startsWith("@")) return modulePath.split("/").slice(0, 2).join("/");
1684
+ return modulePath.split("/")[0];
1685
+ }
1686
+ /**
1687
+ * Reads the `ubean` package version from the project's package.json so we can
1688
+ * install matching versions of `@ubean/*` extension packages.
1689
+ * Returns `null` if ubean isn't declared or version can't be parsed.
1690
+ */
1691
+ function getUbeanVersion(cwd) {
1692
+ try {
1693
+ const pkgPath = join$1(cwd, "package.json");
1694
+ if (!existsSync(pkgPath)) return null;
1695
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
1696
+ const spec = pkg.dependencies?.ubean || pkg.devDependencies?.ubean;
1697
+ if (!spec) return null;
1698
+ const match = spec.match(/\d+\.\d+\.\d+/);
1699
+ return match ? match[0] : null;
1700
+ } catch {
1701
+ return null;
1702
+ }
1703
+ }
1704
+ /**
1705
+ * Probes whether a module is importable (i.e., installed in node_modules).
1706
+ * Uses dynamic import — same mechanism as `loadBuiltinModule` so detection
1707
+ * behavior matches what the runtime will actually encounter.
1708
+ */
1709
+ async function isModuleInstalled(modulePath) {
1710
+ try {
1711
+ await import(
1712
+ /* @vite-ignore */
1713
+ modulePath
1714
+ );
1715
+ return true;
1716
+ } catch {
1717
+ return false;
1718
+ }
1719
+ }
1720
+ /**
1721
+ * Detects built-in modules enabled in config but not installed, and
1722
+ * auto-installs them using the project's package manager.
1723
+ *
1724
+ * Behavior:
1725
+ * - When `autoInstall` is `false`, only prints warnings with install commands.
1726
+ * - When `autoInstall` is `true` and a package.json exists, runs the install.
1727
+ * - Version pinning: installs `@ubean/<pkg>@^<ubean-version>` to keep in sync.
1728
+ * - Failures fall back to warnings (non-fatal — prepare should still proceed).
1729
+ */
1730
+ async function ensureBuiltinModules(cwd, config, autoInstall) {
1731
+ const missing = [];
1732
+ for (const builtin of BUILTIN_MODULES) {
1733
+ const configValue = config[builtin.key];
1734
+ if (isBuiltinDisabled(configValue)) continue;
1735
+ const packageName = extractPackageName(builtin.modulePath);
1736
+ if (!await isModuleInstalled(builtin.modulePath)) missing.push({
1737
+ key: builtin.key,
1738
+ packageName
1739
+ });
1740
+ }
1741
+ if (missing.length === 0) return;
1742
+ const pm = detectPackageManager(cwd);
1743
+ const ubeanVersion = getUbeanVersion(cwd);
1744
+ const packagesToInstall = missing.map((m) => ubeanVersion ? `${m.packageName}@^${ubeanVersion}` : m.packageName);
1745
+ if (!autoInstall || !existsSync(join$1(cwd, "package.json"))) {
1746
+ for (const m of missing) {
1747
+ const versionSuffix = ubeanVersion ? `@^${ubeanVersion}` : "";
1748
+ logger$2.warn(`Built-in module "${m.key}" is enabled but \`${m.packageName}\` is not installed. Run: ${pm} add ${m.packageName}${versionSuffix}`);
1749
+ }
1750
+ return;
1751
+ }
1752
+ logger$2.info(`Auto-installing missing built-in module packages: ${packagesToInstall.join(", ")}`);
1753
+ logger$2.info(`Using package manager: ${pm}`);
1754
+ const { cmd, args } = buildInstallCommand(pm, packagesToInstall);
1755
+ try {
1756
+ execSync([cmd, ...args].join(" "), {
1757
+ cwd,
1758
+ stdio: "inherit",
1759
+ env: { ...process.env }
1760
+ });
1761
+ logger$2.success(`Installed: ${packagesToInstall.join(", ")}`);
1762
+ } catch (err) {
1763
+ logger$2.error(`Failed to auto-install packages: ${err instanceof Error ? err.message : String(err)}`);
1764
+ for (const m of missing) {
1765
+ const versionSuffix = ubeanVersion ? `@^${ubeanVersion}` : "";
1766
+ logger$2.warn(`Please install manually: ${pm} add ${m.packageName}${versionSuffix}`);
1767
+ }
1768
+ }
1769
+ }
1640
1770
  const prepareCommand = {
1641
1771
  meta: {
1642
1772
  name: "prepare",
1643
1773
  description: "Generate type definitions and prepare the project (runs automatically before dev/build)"
1644
1774
  },
1645
- args: { cwd: {
1646
- type: "string",
1647
- description: "Project root directory",
1648
- default: "."
1649
- } },
1775
+ args: {
1776
+ cwd: {
1777
+ type: "string",
1778
+ description: "Project root directory",
1779
+ default: "."
1780
+ },
1781
+ install: {
1782
+ type: "boolean",
1783
+ description: "Auto-install missing built-in module packages (use --no-install to skip)",
1784
+ default: true
1785
+ }
1786
+ },
1650
1787
  async run({ args }) {
1651
1788
  const cwd = resolve$1(args.cwd || process.cwd());
1652
1789
  logger$2.start(`Preparing ubean project at ${cwd}...`);
1653
1790
  const config = await loadUbeanConfig(cwd);
1791
+ await ensureBuiltinModules(cwd, config, args.install !== false);
1654
1792
  const typesDir = ".ubean";
1655
1793
  await ensureBuildDir(cwd, typesDir);
1656
1794
  logger$2.info("Scanning project files...");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubean/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "CLI for ubean (ubean-next binary: dev, build, prepare, init, preview, page, env, config, devtools, api/layout/middleware/cron/plugin scaffolds)",
5
5
  "bin": {
6
6
  "ubean-next": "./bin/ubean-next.mjs"
@@ -28,15 +28,16 @@
28
28
  "consola": "^3.4.2",
29
29
  "kolorist": "^1.8.0",
30
30
  "pathe": "^2.0.3",
31
- "@ubean/app": "0.1.3",
32
- "@ubean/codegen": "0.1.3",
33
- "@ubean/config": "0.1.3",
34
- "@ubean/build": "0.1.3",
35
- "@ubean/dev-server": "0.1.3",
36
- "@ubean/preset": "0.1.3",
37
- "@ubean/prerender": "0.1.3",
38
- "@ubean/routing": "0.1.3",
39
- "@ubean/utils": "0.1.3"
31
+ "@ubean/build": "0.1.5",
32
+ "@ubean/codegen": "0.1.5",
33
+ "@ubean/config": "0.1.5",
34
+ "@ubean/app": "0.1.5",
35
+ "@ubean/modules": "0.1.5",
36
+ "@ubean/dev-server": "0.1.5",
37
+ "@ubean/prerender": "0.1.5",
38
+ "@ubean/utils": "0.1.5",
39
+ "@ubean/routing": "0.1.5",
40
+ "@ubean/preset": "0.1.5"
40
41
  },
41
42
  "devDependencies": {
42
43
  "@types/node": "^26.1.1",
@@ -44,7 +45,7 @@
44
45
  "vite-plus": "0.2.6"
45
46
  },
46
47
  "peerDependencies": {
47
- "@ubean/devtools": "0.1.3"
48
+ "@ubean/devtools": "0.1.5"
48
49
  },
49
50
  "peerDependenciesMeta": {
50
51
  "@ubean/devtools": {