@nera-static/nera 1.4.0 → 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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nera-static/nera",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "The Nera CLI — scaffold, build, preview and update a Nera static site with one command over the @nera-static/core engine",
5
5
  "type": "module",
6
6
  "bin": {
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
- validateProjectName(projectName)
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 (fssync.existsSync(targetDir)) {
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, projectName)
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
- console.log(`👉 Next steps:\n cd ${projectName}\n nera dev\n`)
199
+ const cdLine = inPlace ? '' : ` cd ${projectName}\n`
200
+ console.log(`👉 Next steps:\n${cdLine} nera dev\n`)
154
201
  return targetDir
155
202
  }
@@ -3,3 +3,4 @@ public
3
3
  .env
4
4
  .npmrc
5
5
  .vite
6
+ .nera-cache