create-dashral 0.0.0-stage → 0.1.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.
Files changed (3) hide show
  1. package/README.md +34 -2
  2. package/index.mjs +145 -0
  3. package/package.json +31 -4
package/README.md CHANGED
@@ -1,3 +1,35 @@
1
- # Temporary Holding Version
1
+ # create-dashral
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Start a new [Dashral](https://dashral.com) admin dashboard (Next.js, React, TypeScript, Tailwind CSS) in one command:
4
+
5
+ ```bash
6
+ npx create-dashral my-app
7
+ cd my-app
8
+ pnpm dev
9
+ ```
10
+
11
+ It clones Dashral's newest release into `my-app`, installs the dependencies and runs `pnpm dashral init`. That asks
12
+ which template to keep (Classic, Inset or both), demo pages or a blank page, the languages, your app's name and its
13
+ look. You need **Node.js 20+**, **Git** and **pnpm** (`corepack enable pnpm`).
14
+
15
+ Every answer is also a flag, so it can run without questions:
16
+
17
+ ```bash
18
+ npx create-dashral my-app --template inset --pages blank --languages en --name "Acme" --primary indigo
19
+ ```
20
+
21
+ The theme customizer's **Copy setup command** in the [live demo](https://demo.dashral.com) writes these flags for you.
22
+
23
+ | Option | |
24
+ | --- | --- |
25
+ | `--pro` | Start from Dashral Pro (your GitHub account needs access to it) |
26
+ | `--release v1.2.0` | An earlier release instead of the newest |
27
+ | `--from <url>` | Clone another repository, e.g. a fork, or Pro over SSH |
28
+ | `--no-install` | Skip `pnpm install` |
29
+
30
+ Any other option goes to `pnpm dashral init`; `npx create-dashral --help` lists them.
31
+
32
+ The project keeps the release's Git history, so later releases come in with `pnpm dashral update`. See
33
+ [Getting updates](https://github.com/hasralgana/dashral#getting-updates).
34
+
35
+ MIT licensed.
package/index.mjs ADDED
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ // `npx create-dashral my-app`: a new dashboard from Dashral's newest release, in one command. It clones the repository
3
+ // at the release's tag (with its history, so `pnpm dashral update` can merge later releases), installs the
4
+ // dependencies, then runs the repository's own `pnpm dashral init` with every other flag you passed. Nothing about the
5
+ // setup lives here: the release's scripts/dashral.mjs does it, so this file rarely needs a new version.
6
+ //
7
+ // npx create-dashral my-app
8
+ // npx create-dashral my-app --template inset --pages blank --languages en --name "Acme" --primary indigo
9
+ // npx create-dashral my-app --pro --apps crm,kanban --name "Acme" (Dashral Pro: needs your GitHub access)
10
+
11
+ import { spawnSync } from "node:child_process"
12
+ import { existsSync, readdirSync } from "node:fs"
13
+ import { resolve } from "node:path"
14
+ import { argv, exit, platform, versions } from "node:process"
15
+
16
+ const repos = {
17
+ free: "https://github.com/hasralgana/dashral.git",
18
+ pro: "https://github.com/hasralgana/dashral-pro.git",
19
+ }
20
+
21
+ const help = `Usage: npx create-dashral <folder> [options]
22
+
23
+ Starts a new dashboard in <folder> from Dashral's newest release: clones it, installs the dependencies and runs
24
+ \`pnpm dashral init\`, which asks a few questions (or reads them from the flags below).
25
+
26
+ Options of create-dashral:
27
+ --pro Start from Dashral Pro (your GitHub account needs access to the repository)
28
+ --release v1.2.0 An earlier release instead of the newest
29
+ --from <url> Clone another repository (a fork, or Pro over SSH: git@github.com:hasralgana/dashral-pro.git)
30
+ --no-install Clone and set up only; run \`pnpm install\` yourself
31
+
32
+ Every other option goes to \`pnpm dashral init\`:
33
+ --template classic|inset|both --pages demo|blank --languages all|en --customizer keep|remove
34
+ --name "Your App" --primary --light --dark --card --radius --font --yes
35
+ Pro: --apps all|crm,kanban,… --samples keep|remove
36
+
37
+ The theme customizer's "Copy setup command" at https://demo.dashral.com writes these flags for you.
38
+ Docs: https://dashral.com/docs/installation`
39
+
40
+ // ── Arguments ────────────────────────────────────────────────────────────────────────────────────────────────────
41
+
42
+ /** This script's own flags, and the rest passed to `dashral init` as they were typed. */
43
+ function parseArgs(args) {
44
+ const own = { pro: false, install: true, release: null, from: null, help: false }
45
+ const rest = []
46
+ let folder = null
47
+ for (let i = 0; i < args.length; i++) {
48
+ const arg = args[i]
49
+ const [key, inline] = arg.startsWith("--") ? arg.slice(2).split(/=(.*)/s) : [null]
50
+ const value = () => (inline !== undefined ? inline : args[++i])
51
+ if (arg === "-h" || key === "help") own.help = true
52
+ else if (key === "pro") own.pro = true
53
+ else if (key === "no-install") own.install = false
54
+ else if (key === "release" || key === "from") {
55
+ own[key] = value()
56
+ if (!own[key] || own[key].startsWith("--")) fail(`--${key} needs a value.`)
57
+ } else if (key === null && folder === null) folder = arg
58
+ else rest.push(arg)
59
+ }
60
+ return { folder, own, rest }
61
+ }
62
+
63
+ // ── Helpers ──────────────────────────────────────────────────────────────────────────────────────────────────────
64
+
65
+ function fail(message) {
66
+ console.error(`\n✖ ${message}`)
67
+ exit(1)
68
+ }
69
+
70
+ /** Runs a command in the terminal (its output and questions shown as they come); its exit code, or null. */
71
+ function run(command, args, cwd) {
72
+ // Windows runs pnpm through a .cmd shim, which needs a shell.
73
+ const result = spawnSync(command, args, { cwd, stdio: "inherit", shell: platform === "win32" })
74
+ return result.error ? null : result.status
75
+ }
76
+
77
+ /** A command's output, or null when it can't run. */
78
+ function output(command, args) {
79
+ const result = spawnSync(command, args, { encoding: "utf8", shell: platform === "win32" })
80
+ return result.status === 0 ? result.stdout.trim() : null
81
+ }
82
+
83
+ const versionOf = (tag) => tag.slice(1).split(".").map(Number)
84
+ const newer = (a, b) => {
85
+ const [x, y] = [versionOf(a), versionOf(b)]
86
+ for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] - y[i]
87
+ return 0
88
+ }
89
+
90
+ // ── Main ─────────────────────────────────────────────────────────────────────────────────────────────────────────
91
+
92
+ const { folder, own, rest } = parseArgs(argv.slice(2))
93
+ if (own.help || !folder) {
94
+ console.log(help)
95
+ exit(own.help ? 0 : 1)
96
+ }
97
+
98
+ if (Number(versions.node.split(".")[0]) < 20) fail(`Dashral needs Node.js 20 or newer (this is ${versions.node}).`)
99
+ if (output("git", ["--version"]) === null) fail("Git wasn't found. Install it from https://git-scm.com and run this again.")
100
+ if (own.install && output("pnpm", ["--version"]) === null) {
101
+ fail("pnpm wasn't found. Turn it on with `corepack enable pnpm` (or `npm install -g pnpm`) and run this again.")
102
+ }
103
+
104
+ const target = resolve(folder)
105
+ if (existsSync(target) && readdirSync(target).length) fail(`${folder} isn't empty. Pick a new folder.`)
106
+
107
+ const url = own.from ?? (own.pro ? repos.pro : repos.free)
108
+ const access = own.pro ? " Check that your GitHub account has access to Dashral Pro (accept the invitation GitHub emailed you)." : ""
109
+
110
+ // The release: the newest version tag, or the one --release names.
111
+ console.log(`Looking for the releases of ${url}…`)
112
+ const listed = output("git", ["ls-remote", "--tags", "--refs", url, "v*"])
113
+ if (listed === null) fail(`Couldn't reach ${url}.${access}`)
114
+ const tags = listed
115
+ .split("\n")
116
+ .map((line) => line.split("refs/tags/")[1])
117
+ .filter((tag) => /^v\d+\.\d+\.\d+$/.test(tag ?? ""))
118
+ .sort(newer)
119
+ let release = tags.at(-1)
120
+ if (own.release) {
121
+ release = own.release.startsWith("v") ? own.release : `v${own.release}`
122
+ if (!tags.includes(release)) fail(`There's no release ${release}. The releases: ${tags.join(", ") || "none yet"}.`)
123
+ }
124
+ if (!release) fail(`${url} has no releases yet.`)
125
+
126
+ // The whole history, not a shallow copy: `pnpm dashral update` merges later releases into it.
127
+ console.log(`\nCloning Dashral ${release} into ${folder}…`)
128
+ if (run("git", ["clone", "--quiet", "--branch", release, url, target]) !== 0) fail(`Couldn't clone ${url}.${access}`)
129
+ // A tag checks out without a branch: start yours, so your commits have one.
130
+ if (run("git", ["switch", "--quiet", "-c", "main"], target) !== 0) fail("Couldn't start the main branch.")
131
+
132
+ if (own.install) {
133
+ console.log("\nInstalling the dependencies…")
134
+ if (run("pnpm", ["install"], target) !== 0) fail(`pnpm install stopped. Fix what it says, then in ${folder}: pnpm install && pnpm dashral init`)
135
+ }
136
+
137
+ console.log("")
138
+ if (run("node", ["scripts/dashral.mjs", "init", ...rest], target) !== 0) {
139
+ fail(`The setup stopped. It's a fresh clone, so you can delete ${folder} and start again, or fix what it says and run \`pnpm dashral init\` in it.`)
140
+ }
141
+
142
+ console.log(`
143
+ Your dashboard is in ${folder}:
144
+ cd ${folder}${own.install ? "" : "\n pnpm install"}
145
+ pnpm dev`)
package/package.json CHANGED
@@ -1,6 +1,33 @@
1
1
  {
2
2
  "name": "create-dashral",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Start a new Dashral admin dashboard (Next.js, React, Tailwind CSS) in one command.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "create-dashral": "index.mjs"
9
+ },
10
+ "files": [
11
+ "index.mjs"
12
+ ],
13
+ "engines": {
14
+ "node": ">=20"
15
+ },
16
+ "homepage": "https://dashral.com/docs/installation",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/hasralgana/dashral.git",
20
+ "directory": "packages/create-dashral"
21
+ },
22
+ "bugs": "https://github.com/hasralgana/dashral/issues",
23
+ "keywords": [
24
+ "dashral",
25
+ "dashboard",
26
+ "admin",
27
+ "template",
28
+ "nextjs",
29
+ "react",
30
+ "tailwindcss",
31
+ "shadcn"
32
+ ]
33
+ }