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.
- package/README.md +34 -2
- package/index.mjs +145 -0
- package/package.json +31 -4
package/README.md
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
-
#
|
|
1
|
+
# create-dashral
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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
|
+
}
|