@nera-static/nera 1.4.1 → 1.5.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 +6 -1
- package/bin/nera.js +1 -0
- package/package.json +1 -1
- package/src/scaffold.js +53 -6
package/README.md
CHANGED
|
@@ -14,11 +14,16 @@ cd my-site
|
|
|
14
14
|
npm run dev # or: nera dev
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
Already in the folder the site should live in? `nera new .` scaffolds into the
|
|
18
|
+
current folder instead. The folder must be empty apart from dotfiles such as
|
|
19
|
+
`.git` (an existing `.gitignore` is kept), and the package is named after the
|
|
20
|
+
folder (`My Bakery` → `my-bakery`).
|
|
21
|
+
|
|
17
22
|
## Commands
|
|
18
23
|
|
|
19
24
|
| Command | What it does |
|
|
20
25
|
|---|---|
|
|
21
|
-
| `nera new <name>` | Scaffold a new thin Nera site (one dependency: `@nera-static/nera`). Add `--theme <name>` to start from a theme. |
|
|
26
|
+
| `nera new <name>` | Scaffold a new thin Nera site (one dependency: `@nera-static/nera`). Add `--theme <name>` to start from a theme. `nera new .` scaffolds into the current, empty folder. |
|
|
22
27
|
| `nera build` | Render `pages/` → `public/`. With `--check`, then run `nera check` on the result — one command for CI. |
|
|
23
28
|
| `nera dev` | Build, serve `public/`, and rebuild on change with live reload. Watches `pages/`, `config/`, `theme/` and a local theme (`theme: ./themes/<name>`). |
|
|
24
29
|
| `nera serve` | Serve the already-built `public/` folder. |
|
package/bin/nera.js
CHANGED
|
@@ -13,6 +13,7 @@ const usage = `Nera — the static site generator CLI
|
|
|
13
13
|
|
|
14
14
|
Usage:
|
|
15
15
|
nera new <name> scaffold a new Nera site (--theme <name> to start from a theme)
|
|
16
|
+
nera new . scaffold into the current, empty folder
|
|
16
17
|
nera build render pages/ → public/ (--check to check the output after)
|
|
17
18
|
nera dev build, serve and live-reload
|
|
18
19
|
nera serve serve the built public/ folder
|
package/package.json
CHANGED
package/src/scaffold.js
CHANGED
|
@@ -12,7 +12,8 @@ const VALID_PROJECT_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/
|
|
|
12
12
|
export function validateProjectName(projectName) {
|
|
13
13
|
if (typeof projectName !== 'string' || projectName.trim() === '') {
|
|
14
14
|
throw new Error(
|
|
15
|
-
'Project name is required. Usage: nera new <project-name>'
|
|
15
|
+
'Project name is required. Usage: nera new <project-name> ' +
|
|
16
|
+
'(or `nera new .` for the current folder)'
|
|
16
17
|
)
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -26,6 +27,43 @@ export function validateProjectName(projectName) {
|
|
|
26
27
|
return projectName
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
// `nera new .` scaffolds into the current folder. Only these two spellings:
|
|
31
|
+
// `..`, absolute paths and every other name still go through
|
|
32
|
+
// validateProjectName unchanged.
|
|
33
|
+
const CURRENT_DIR = new Set(['.', './'])
|
|
34
|
+
|
|
35
|
+
export const isCurrentDir = (projectName) => CURRENT_DIR.has(projectName)
|
|
36
|
+
|
|
37
|
+
// The package name for a site scaffolded in place, derived from the folder's
|
|
38
|
+
// basename and normalised until it passes validateProjectName: accents
|
|
39
|
+
// dropped (`Bäckerei` → `backerei`), lower-case, each run of other characters
|
|
40
|
+
// → one `-`, no leading dot, dash or underscore.
|
|
41
|
+
export function projectNameFromDir(dir) {
|
|
42
|
+
const name = path
|
|
43
|
+
.basename(path.resolve(dir))
|
|
44
|
+
.normalize('NFKD')
|
|
45
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
46
|
+
.toLowerCase()
|
|
47
|
+
.replace(/[^a-z0-9._-]+/g, '-')
|
|
48
|
+
.replace(/^[._-]+/, '')
|
|
49
|
+
.replace(/-+$/, '')
|
|
50
|
+
return name === '' ? 'nera-site' : validateProjectName(name)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// In-place scaffolding only goes ahead in an empty folder. Dotfiles such as
|
|
54
|
+
// `.git` or an editor folder do not count: `git init` first is the usual way
|
|
55
|
+
// to start a project.
|
|
56
|
+
async function assertEmptyDir(dir) {
|
|
57
|
+
const entries = (await fs.readdir(dir)).filter((n) => !n.startsWith('.'))
|
|
58
|
+
if (entries.length > 0) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`The current folder is not empty (${entries.slice(0, 3).join(', ')}` +
|
|
61
|
+
`${entries.length > 3 ? ', …' : ''}). Run \`nera new .\` in an ` +
|
|
62
|
+
'empty folder, or `nera new <name>` to create a new one.'
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
29
67
|
// What `--theme` accepts, mirroring the three forms `app.theme` takes in
|
|
30
68
|
// @nera-static/core (src/theme.js): a bare name (`example` →
|
|
31
69
|
// @nera-static/theme-example), a full package name (`@acme/my-theme`), or a
|
|
@@ -85,6 +123,10 @@ async function copyDir(src, dest, { skipStarters = false } = {}) {
|
|
|
85
123
|
await copyDir(from, to, { skipStarters })
|
|
86
124
|
} else if (skipStarters && (await isStarterTemplate(from))) {
|
|
87
125
|
continue
|
|
126
|
+
} else if (fssync.existsSync(to)) {
|
|
127
|
+
// Only in place, and then only a dotfile such as .gitignore can be
|
|
128
|
+
// there already (assertEmptyDir): the user's copy wins.
|
|
129
|
+
console.log(` • Kept your existing ${path.basename(to)}`)
|
|
88
130
|
} else {
|
|
89
131
|
await fs.copyFile(from, to)
|
|
90
132
|
}
|
|
@@ -126,17 +168,21 @@ async function applyTheme(targetDir, theme) {
|
|
|
126
168
|
export async function scaffoldProject(projectName, options = {}) {
|
|
127
169
|
const { install = true, cwd = process.cwd(), theme } = options
|
|
128
170
|
|
|
129
|
-
|
|
171
|
+
const inPlace = isCurrentDir(projectName)
|
|
172
|
+
if (!inPlace) validateProjectName(projectName)
|
|
130
173
|
if (theme !== undefined) validateThemeSpec(theme)
|
|
131
174
|
|
|
132
|
-
const targetDir = path.resolve(cwd, projectName)
|
|
133
|
-
if (
|
|
175
|
+
const targetDir = path.resolve(cwd, inPlace ? '.' : projectName)
|
|
176
|
+
if (inPlace) {
|
|
177
|
+
await assertEmptyDir(targetDir)
|
|
178
|
+
} else if (fssync.existsSync(targetDir)) {
|
|
134
179
|
throw new Error(`Target directory "${projectName}" already exists.`)
|
|
135
180
|
}
|
|
181
|
+
const packageName = inPlace ? projectNameFromDir(targetDir) : projectName
|
|
136
182
|
|
|
137
183
|
console.log(`📦 Creating a new Nera site in ${targetDir}...`)
|
|
138
184
|
await copyDir(templateDir(), targetDir, { skipStarters: Boolean(theme) })
|
|
139
|
-
await personalize(targetDir,
|
|
185
|
+
await personalize(targetDir, packageName)
|
|
140
186
|
if (theme) await applyTheme(targetDir, theme)
|
|
141
187
|
|
|
142
188
|
if (install) {
|
|
@@ -150,6 +196,7 @@ export async function scaffoldProject(projectName, options = {}) {
|
|
|
150
196
|
}
|
|
151
197
|
|
|
152
198
|
console.log('✅ Done!')
|
|
153
|
-
|
|
199
|
+
const cdLine = inPlace ? '' : ` cd ${projectName}\n`
|
|
200
|
+
console.log(`👉 Next steps:\n${cdLine} nera dev\n`)
|
|
154
201
|
return targetDir
|
|
155
202
|
}
|