ember.nvp 0.0.0 → 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/.github/workflows/plan-release.yml +61 -0
- package/.github/workflows/publish.yml +41 -0
- package/.nvmrc +1 -0
- package/.prettierignore +2 -0
- package/.release-plan.json +22 -0
- package/CHANGELOG.md +17 -0
- package/README.md +222 -1
- package/RELEASE.md +27 -0
- package/package.json +28 -3
- package/src/cli/generator.js +19 -0
- package/src/cli/index.js +48 -0
- package/src/cli/questions/layers.js +75 -0
- package/src/cli/questions/name.js +22 -0
- package/src/cli/questions/package-manager.js +21 -0
- package/src/layers/eslint/files/eslint.config.mjs +37 -0
- package/src/layers/eslint/index.js +42 -0
- package/src/layers/minimal/files/app/app.ts +10 -0
- package/src/layers/minimal/files/app/config.ts +18 -0
- package/src/layers/minimal/files/app/router.ts +9 -0
- package/src/layers/minimal/files/app/routes/.gitkeep +0 -0
- package/src/layers/minimal/files/app/templates/application.gts +7 -0
- package/src/layers/minimal/files/index.html +19 -0
- package/src/layers/minimal/files/tsconfig.json +18 -0
- package/src/layers/minimal/files/vite.config.mjs +13 -0
- package/src/layers/minimal/files-js/babel.config.js +38 -0
- package/src/layers/minimal/files-ts/babel.config.js +46 -0
- package/src/layers/minimal/index.js +89 -0
- package/src/layers/prettier/files/.prettierignore +18 -0
- package/src/layers/prettier/files/.prettierrc.cjs +15 -0
- package/src/layers/prettier/index.js +38 -0
- package/src/layers/qunit/files/run-tests.mjs +30 -0
- package/src/layers/qunit/files/tests/index.html +21 -0
- package/src/layers/qunit/files/tests/integration/.gitkeep +0 -0
- package/src/layers/qunit/files/tests/test-helper.ts +12 -0
- package/src/layers/qunit/files/tests/unit/.gitkeep +0 -0
- package/src/layers/qunit/index.js +53 -0
- package/src/layers/typescript/index.js +0 -0
- package/src/layers/vitest/files/tests/integration/.gitkeep +0 -0
- package/src/layers/vitest/files/tests/test-helper.ts +5 -0
- package/src/layers/vitest/files/tests/unit/.gitkeep +0 -0
- package/src/layers/vitest/files/vitest.config.mjs +22 -0
- package/src/layers/vitest/index.js +49 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: Plan Release
|
|
2
|
+
on:
|
|
3
|
+
workflow_dispatch:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- master
|
|
8
|
+
pull_request_target: # This workflow has permissions on the repo, do NOT run code from PRs in this workflow. See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
|
|
9
|
+
types:
|
|
10
|
+
- labeled
|
|
11
|
+
- unlabeled
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: plan-release # only the latest one of these should ever be running
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
should-run-release-plan-prepare:
|
|
19
|
+
name: Should we run release-plan prepare?
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
outputs:
|
|
22
|
+
should-prepare: ${{ steps.should-prepare.outputs.should-prepare }}
|
|
23
|
+
steps:
|
|
24
|
+
- uses: release-plan/actions/should-prepare-release@v1
|
|
25
|
+
with:
|
|
26
|
+
ref: 'main'
|
|
27
|
+
id: should-prepare
|
|
28
|
+
|
|
29
|
+
create-prepare-release-pr:
|
|
30
|
+
name: Create Prepare Release PR
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
timeout-minutes: 5
|
|
33
|
+
needs: should-run-release-plan-prepare
|
|
34
|
+
permissions:
|
|
35
|
+
contents: write
|
|
36
|
+
issues: read
|
|
37
|
+
pull-requests: write
|
|
38
|
+
if: needs.should-run-release-plan-prepare.outputs.should-prepare == 'true'
|
|
39
|
+
steps:
|
|
40
|
+
- uses: release-plan/actions/prepare@v1
|
|
41
|
+
name: Run release-plan prepare
|
|
42
|
+
with:
|
|
43
|
+
ref: 'main'
|
|
44
|
+
env:
|
|
45
|
+
GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }}
|
|
46
|
+
id: explanation
|
|
47
|
+
|
|
48
|
+
- uses: peter-evans/create-pull-request@v8
|
|
49
|
+
name: Create Prepare Release PR
|
|
50
|
+
with:
|
|
51
|
+
commit-message: "Prepare Release ${{ steps.explanation.outputs.new-version}} using 'release-plan'"
|
|
52
|
+
labels: "internal"
|
|
53
|
+
sign-commits: true
|
|
54
|
+
branch: release-preview
|
|
55
|
+
title: Prepare Release ${{ steps.explanation.outputs.new-version }}
|
|
56
|
+
body: |
|
|
57
|
+
This PR is a preview of the release that [release-plan](https://github.com/embroider-build/release-plan) has prepared. To release you should just merge this PR 👍
|
|
58
|
+
|
|
59
|
+
-----------------------------------------
|
|
60
|
+
|
|
61
|
+
${{ steps.explanation.outputs.text }}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# For every push to the primary branch with .release-plan.json modified,
|
|
2
|
+
# runs release-plan.
|
|
3
|
+
|
|
4
|
+
name: Publish Stable
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
push:
|
|
9
|
+
branches:
|
|
10
|
+
- main
|
|
11
|
+
- master
|
|
12
|
+
paths:
|
|
13
|
+
- '.release-plan.json'
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: publish-${{ github.head_ref || github.ref }}
|
|
17
|
+
cancel-in-progress: true
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
publish:
|
|
21
|
+
name: "NPM Publish"
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
permissions:
|
|
24
|
+
contents: write
|
|
25
|
+
id-token: write
|
|
26
|
+
attestations: write
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v6
|
|
30
|
+
- uses: pnpm/action-setup@v4
|
|
31
|
+
- uses: actions/setup-node@v6
|
|
32
|
+
with:
|
|
33
|
+
node-version: 22
|
|
34
|
+
registry-url: 'https://registry.npmjs.org'
|
|
35
|
+
cache: pnpm
|
|
36
|
+
- run: npm install -g npm@latest # ensure that the globally installed npm is new enough to support OIDC
|
|
37
|
+
- run: pnpm install --frozen-lockfile
|
|
38
|
+
- name: Publish to NPM
|
|
39
|
+
run: NPM_CONFIG_PROVENANCE=true pnpm release-plan publish
|
|
40
|
+
env:
|
|
41
|
+
GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }}
|
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24
|
package/.prettierignore
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"solution": {
|
|
3
|
+
"ember.nvp": {
|
|
4
|
+
"impact": "minor",
|
|
5
|
+
"oldVersion": "0.0.0",
|
|
6
|
+
"newVersion": "0.1.0",
|
|
7
|
+
"tagName": "latest",
|
|
8
|
+
"constraints": [
|
|
9
|
+
{
|
|
10
|
+
"impact": "minor",
|
|
11
|
+
"reason": "Appears in changelog section :rocket: Enhancement"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"impact": "patch",
|
|
15
|
+
"reason": "Appears in changelog section :house: Internal"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"pkgJSONPath": "./package.json"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"description": "## Release (2026-01-21)\n\n* ember.nvp 0.1.0 (minor)\n\n#### :rocket: Enhancement\n* `ember.nvp`\n * [#1](https://github.com/NullVoxPopuli/ember.nvp/pull/1) Implementation planning ([@NullVoxPopuli](https://github.com/NullVoxPopuli))\n\n#### :house: Internal\n* `ember.nvp`\n * [#2](https://github.com/NullVoxPopuli/ember.nvp/pull/2) pnpm dlx create-release-plan-setup@latest --update ([@NullVoxPopuli](https://github.com/NullVoxPopuli))\n * [#1](https://github.com/NullVoxPopuli/ember.nvp/pull/1) Implementation planning ([@NullVoxPopuli](https://github.com/NullVoxPopuli))\n\n#### Committers: 1\n- [@NullVoxPopuli](https://github.com/NullVoxPopuli)\n"
|
|
22
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Release (2026-01-21)
|
|
4
|
+
|
|
5
|
+
* ember.nvp 0.1.0 (minor)
|
|
6
|
+
|
|
7
|
+
#### :rocket: Enhancement
|
|
8
|
+
* `ember.nvp`
|
|
9
|
+
* [#1](https://github.com/NullVoxPopuli/ember.nvp/pull/1) Implementation planning ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
|
|
10
|
+
|
|
11
|
+
#### :house: Internal
|
|
12
|
+
* `ember.nvp`
|
|
13
|
+
* [#2](https://github.com/NullVoxPopuli/ember.nvp/pull/2) pnpm dlx create-release-plan-setup@latest --update ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
|
|
14
|
+
* [#1](https://github.com/NullVoxPopuli/ember.nvp/pull/1) Implementation planning ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
|
|
15
|
+
|
|
16
|
+
#### Committers: 1
|
|
17
|
+
- [@NullVoxPopuli](https://github.com/NullVoxPopuli)
|
package/README.md
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
_ember project generator: alternative to the official blueprints, but more modern -- hopefully one day to upstream back in to ember-cli / official blueprints_
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
## Reqs
|
|
7
6
|
|
|
8
7
|
- node 24+
|
|
@@ -14,7 +13,229 @@ _ember project generator: alternative to the official blueprints, but more moder
|
|
|
14
13
|
- choose your features
|
|
15
14
|
|
|
16
15
|
Good for:
|
|
16
|
+
|
|
17
17
|
- demos
|
|
18
18
|
- reproductions
|
|
19
19
|
- existing monorepos
|
|
20
20
|
- example integrations with other tools
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
Install dependencies first:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then run the interactive CLI:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
node src/cli/index.js
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or link it locally for easier testing:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm link --global
|
|
40
|
+
ember.nvp
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Example Session
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
┌ ember.nvp
|
|
47
|
+
│
|
|
48
|
+
◆ What is your project name?
|
|
49
|
+
│ my-awesome-app
|
|
50
|
+
│
|
|
51
|
+
◇ Base Layer
|
|
52
|
+
│ minimal layer is always included.
|
|
53
|
+
│ It provides: type: "module", no compat, no testing, no linting.
|
|
54
|
+
│ Perfect for demos and reproductions.
|
|
55
|
+
│
|
|
56
|
+
◆ Select additional features: (press space to select)
|
|
57
|
+
│ ◻ ESLint - Code linting with ESLint
|
|
58
|
+
│ ◻ Prettier - Code formatting with Prettier
|
|
59
|
+
│ ◻ QUnit - Testing with QUnit
|
|
60
|
+
│ ◻ Vitest - Testing with Vitest (modern & fast)
|
|
61
|
+
│
|
|
62
|
+
◆ Which package manager?
|
|
63
|
+
│ ● pnpm - Fast, disk space efficient
|
|
64
|
+
│ npm - Node default
|
|
65
|
+
│ yarn - Classic alternative
|
|
66
|
+
│
|
|
67
|
+
◇ Project created!
|
|
68
|
+
│
|
|
69
|
+
◇ Next steps
|
|
70
|
+
│ cd my-awesome-app
|
|
71
|
+
│ pnpm install
|
|
72
|
+
│ pnpm start
|
|
73
|
+
│
|
|
74
|
+
└ ✓ Project ready! Happy coding!
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The CLI will guide you through:
|
|
78
|
+
|
|
79
|
+
1. **Project name** - Choose your project name
|
|
80
|
+
2. **Feature selection** - Select which layers you want (linting, testing, formatting)
|
|
81
|
+
3. **Package manager** - Choose pnpm, npm, or yarn
|
|
82
|
+
|
|
83
|
+
## Layers
|
|
84
|
+
|
|
85
|
+
Each layer is a standalone module that can add features to your ember project:
|
|
86
|
+
|
|
87
|
+
### 🎯 Minimal (always included)
|
|
88
|
+
|
|
89
|
+
The base layer, matching the `--minimal` flag from [ember-cli/ember-app-blueprint#49](https://github.com/ember-cli/ember-app-blueprint/pull/49):
|
|
90
|
+
|
|
91
|
+
- ✅ `"type": "module"` in package.json
|
|
92
|
+
- ✅ No @embroider/compat (faster builds)
|
|
93
|
+
- ✅ No testing framework (minimal setup)
|
|
94
|
+
- ✅ No linting or formatting
|
|
95
|
+
- ✅ No ember-welcome-page
|
|
96
|
+
- ✅ Vite-based with modern Ember
|
|
97
|
+
|
|
98
|
+
Perfect for demos, reproductions, and learning!
|
|
99
|
+
|
|
100
|
+
### 📝 ESLint (optional)
|
|
101
|
+
|
|
102
|
+
Adds modern ESLint configuration with:
|
|
103
|
+
|
|
104
|
+
- TypeScript support
|
|
105
|
+
- Ember plugin
|
|
106
|
+
- Flat config (ESLint 9+)
|
|
107
|
+
- Prettier compatibility
|
|
108
|
+
|
|
109
|
+
### 🎨 Prettier (optional)
|
|
110
|
+
|
|
111
|
+
Code formatting with:
|
|
112
|
+
|
|
113
|
+
- GTS/GJS template support
|
|
114
|
+
- Sensible defaults
|
|
115
|
+
- Format scripts
|
|
116
|
+
|
|
117
|
+
### 🧪 QUnit (optional)
|
|
118
|
+
|
|
119
|
+
Traditional Ember testing with:
|
|
120
|
+
|
|
121
|
+
- QUnit test framework
|
|
122
|
+
- @ember/test-helpers
|
|
123
|
+
- Test HTML setup
|
|
124
|
+
- Playwright for CI
|
|
125
|
+
|
|
126
|
+
### ⚡ Vitest (optional)
|
|
127
|
+
|
|
128
|
+
Modern testing alternative:
|
|
129
|
+
|
|
130
|
+
- Vitest (faster than QUnit)
|
|
131
|
+
- @ember/test-helpers
|
|
132
|
+
- Happy DOM
|
|
133
|
+
- Watch mode & UI
|
|
134
|
+
|
|
135
|
+
## Architecture
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
src/
|
|
139
|
+
├── cli/ # Interactive CLI entry point
|
|
140
|
+
│ └── index.js # Main CLI with prompts
|
|
141
|
+
├── layers/ # Feature layers (auto-discovered)
|
|
142
|
+
│ ├── minimal/ # Base layer (always included)
|
|
143
|
+
│ │ ├── index.js # Layer definition with run function
|
|
144
|
+
│ │ └── files/ # Template files to copy
|
|
145
|
+
│ ├── eslint/
|
|
146
|
+
│ │ ├── index.js
|
|
147
|
+
│ │ └── files/
|
|
148
|
+
│ ├── prettier/
|
|
149
|
+
│ │ ├── index.js
|
|
150
|
+
│ │ └── files/
|
|
151
|
+
│ ├── qunit/
|
|
152
|
+
│ │ ├── index.js
|
|
153
|
+
│ │ └── files/
|
|
154
|
+
│ └── vitest/
|
|
155
|
+
│ ├── index.js
|
|
156
|
+
│ └── files/
|
|
157
|
+
└── lib/ # Shared utilities
|
|
158
|
+
└── generator.js # Project generation orchestration
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Layer Architecture
|
|
162
|
+
|
|
163
|
+
Each layer now uses **ember-apply** utilities for robust file and package.json manipulation:
|
|
164
|
+
|
|
165
|
+
- **`index.js`** - Exports an object with:
|
|
166
|
+
- `label` - Display name
|
|
167
|
+
- `description` - Short description
|
|
168
|
+
- `run({ targetDir, projectName })` - Async function that applies the layer
|
|
169
|
+
|
|
170
|
+
- **`files/`** - Directory containing template files to copy to the target project
|
|
171
|
+
- Use `__PROJECT_NAME__` placeholder for project name substitution
|
|
172
|
+
- Files are copied using `ember-apply`'s `files.applyFolder()`
|
|
173
|
+
|
|
174
|
+
### How Layers Work
|
|
175
|
+
|
|
176
|
+
1. **Discovery**: CLI scans `src/layers/` and imports each `index.js`
|
|
177
|
+
2. **Selection**: User selects which optional layers to include
|
|
178
|
+
3. **Execution**: Each layer's `run()` function is called in sequence:
|
|
179
|
+
```js
|
|
180
|
+
await layer.run({ targetDir: "/path/to/project", projectName: "my-app" });
|
|
181
|
+
```
|
|
182
|
+
4. **Layer Functions**: Inside `run()`, layers use `ember-apply` to:
|
|
183
|
+
- Copy files from `files/` directory
|
|
184
|
+
- Add dependencies/devDependencies to package.json
|
|
185
|
+
- Add npm scripts
|
|
186
|
+
- Modify package.json metadata
|
|
187
|
+
- Transform existing files (e.g., replace placeholders)
|
|
188
|
+
|
|
189
|
+
### Adding New Layers
|
|
190
|
+
|
|
191
|
+
To add a new layer, create a new directory in `src/layers/` with:
|
|
192
|
+
|
|
193
|
+
1. **`index.js`** - Layer definition:
|
|
194
|
+
|
|
195
|
+
```js
|
|
196
|
+
import { packageJson, files } from "ember-apply";
|
|
197
|
+
import { dirname, join } from "node:path";
|
|
198
|
+
import { fileURLToPath } from "node:url";
|
|
199
|
+
|
|
200
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
201
|
+
|
|
202
|
+
export default {
|
|
203
|
+
label: "My Feature",
|
|
204
|
+
description: "What this feature does",
|
|
205
|
+
|
|
206
|
+
async run({ targetDir, projectName }) {
|
|
207
|
+
// Copy files from files/ directory
|
|
208
|
+
await files.applyFolder(join(__dirname, "files"), targetDir);
|
|
209
|
+
|
|
210
|
+
// Add dependencies
|
|
211
|
+
await packageJson.addDependencies(
|
|
212
|
+
{
|
|
213
|
+
"some-package": "^1.0.0",
|
|
214
|
+
},
|
|
215
|
+
targetDir,
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// Add devDependencies
|
|
219
|
+
await packageJson.addDevDependencies(
|
|
220
|
+
{
|
|
221
|
+
"dev-package": "^2.0.0",
|
|
222
|
+
},
|
|
223
|
+
targetDir,
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// Add scripts
|
|
227
|
+
await packageJson.addScripts(
|
|
228
|
+
{
|
|
229
|
+
"my-script": 'echo "Hello"',
|
|
230
|
+
},
|
|
231
|
+
targetDir,
|
|
232
|
+
);
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
2. **`files/`** directory - Template files to copy:
|
|
238
|
+
- Use `__PROJECT_NAME__` as a placeholder for the project name
|
|
239
|
+
- Files will be copied to the target directory maintaining structure
|
|
240
|
+
|
|
241
|
+
The CLI will automatically discover and offer it as an option!
|
package/RELEASE.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Release Process
|
|
2
|
+
|
|
3
|
+
Releases in this repo are mostly automated using [release-plan](https://github.com/embroider-build/release-plan/). Once you label all your PRs correctly (see below) you will have an automatically generated PR that updates your CHANGELOG.md file and a `.release-plan.json` that is used to prepare the release once the PR is merged.
|
|
4
|
+
|
|
5
|
+
## Preparation
|
|
6
|
+
|
|
7
|
+
Since the majority of the actual release process is automated, the remaining tasks before releasing are:
|
|
8
|
+
|
|
9
|
+
- correctly labeling **all** pull requests that have been merged since the last release
|
|
10
|
+
- updating pull request titles so they make sense to our users
|
|
11
|
+
|
|
12
|
+
Some great information on why this is important can be found at [keepachangelog.com](https://keepachangelog.com/en/1.1.0/), but the overall
|
|
13
|
+
guiding principle here is that changelogs are for humans, not machines.
|
|
14
|
+
|
|
15
|
+
When reviewing merged PR's the labels to be used are:
|
|
16
|
+
|
|
17
|
+
- breaking - Used when the PR is considered a breaking change.
|
|
18
|
+
- enhancement - Used when the PR adds a new feature or enhancement.
|
|
19
|
+
- bug - Used when the PR fixes a bug included in a previous release.
|
|
20
|
+
- documentation - Used when the PR adds or updates documentation.
|
|
21
|
+
- internal - Internal changes or things that don't fit in any other category.
|
|
22
|
+
|
|
23
|
+
**Note:** `release-plan` requires that **all** PRs are labeled. If a PR doesn't fit in a category it's fine to label it as `internal`
|
|
24
|
+
|
|
25
|
+
## Release
|
|
26
|
+
|
|
27
|
+
Once the prep work is completed, the actual release is straight forward: you just need to merge the open [Plan Release](https://github.com/NullVoxPopuli/ember.nvp/pulls?q=is%3Apr+is%3Aopen+%22Prepare+Release%22+in%3Atitle) PR
|
package/package.json
CHANGED
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ember.nvp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "ember project generator, alternative to the official blueprints, but more modern -- hopefully one day to upstream back in to ember-cli / official blueprints",
|
|
5
|
-
"
|
|
6
|
-
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git@github.com:NullVoxPopuli/ember.nvp.git"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"bin": "./src/cli/index.js",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@clack/prompts": "^0.11.0",
|
|
13
|
+
"ember-apply": "^2.16.0",
|
|
14
|
+
"picocolors": "^1.1.1"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@ember/app-tsconfig": "^2.0.0",
|
|
18
|
+
"@ember/library-tsconfig": "^2.0.0",
|
|
19
|
+
"@glint/ember-tsc": "^1.0.9",
|
|
20
|
+
"@glint/template": "^1.7.3",
|
|
21
|
+
"@glint/tsserver-plugin": "^2.0.9",
|
|
22
|
+
"ember-source": "^6.10.0",
|
|
23
|
+
"prettier": "^3.7.4",
|
|
24
|
+
"prettier-plugin-ember-template-tag": "^2.1.2",
|
|
25
|
+
"release-plan": "^0.17.4"
|
|
26
|
+
},
|
|
7
27
|
"engines": {
|
|
8
28
|
"node": ">= 24"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"format": "prettier . --write",
|
|
32
|
+
"lint:format": "prettier . --check",
|
|
33
|
+
"new": "node ./src/cli/index.js"
|
|
9
34
|
}
|
|
10
35
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate project files by running layer functions
|
|
5
|
+
*/
|
|
6
|
+
export async function generateProject(projectPath, projectName, layers) {
|
|
7
|
+
// Create project directory
|
|
8
|
+
await mkdir(projectPath, { recursive: true });
|
|
9
|
+
|
|
10
|
+
// Run each layer's run function in sequence
|
|
11
|
+
for (const layer of layers) {
|
|
12
|
+
if (typeof layer.run === "function") {
|
|
13
|
+
await layer.run({
|
|
14
|
+
targetDir: projectPath,
|
|
15
|
+
projectName,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
package/src/cli/index.js
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
import { generateProject } from "./generator.js";
|
|
7
|
+
import { askName } from './questions/name.js';
|
|
8
|
+
import { askLayers } from './questions/layers.js';
|
|
9
|
+
import { askPackageManager } from './questions/package-manager.js';
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
console.clear();
|
|
13
|
+
|
|
14
|
+
p.intro(pc.bgCyan(pc.black(" ember.nvp ")));
|
|
15
|
+
|
|
16
|
+
const projectName = await askName();
|
|
17
|
+
const selectedLayers = await askLayers();
|
|
18
|
+
const packageManager = await askPackageManager();
|
|
19
|
+
|
|
20
|
+
const s = p.spinner();
|
|
21
|
+
s.start("Creating your Ember app...");
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
// Generate the project
|
|
25
|
+
const projectPath = join(process.cwd(), projectName);
|
|
26
|
+
await generateProject(projectPath, projectName, selectedLayers);
|
|
27
|
+
|
|
28
|
+
s.stop("Project created!");
|
|
29
|
+
|
|
30
|
+
p.note(
|
|
31
|
+
`cd ${projectName}\n` +
|
|
32
|
+
`${packageManager} install\n` +
|
|
33
|
+
`${packageManager} ${packageManager === "npm" ? "run " : ""}start`,
|
|
34
|
+
"Next steps",
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
p.outro(pc.green("✓") + " Project ready! " + pc.dim("Happy coding!"));
|
|
38
|
+
} catch (err) {
|
|
39
|
+
s.stop("Failed to create project");
|
|
40
|
+
p.cancel(`Error: ${err.message}`);
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
main().catch((err) => {
|
|
46
|
+
console.error(err);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import * as p from "@clack/prompts";
|
|
5
|
+
|
|
6
|
+
export async function askLayers() {
|
|
7
|
+
|
|
8
|
+
// Discover available layers
|
|
9
|
+
const layers = await discoverLayers();
|
|
10
|
+
|
|
11
|
+
// Separate minimal from other layers
|
|
12
|
+
const minimalLayer = layers.find((l) => l.name === "minimal");
|
|
13
|
+
const optionalLayers = layers.filter((l) => l.name !== "minimal");
|
|
14
|
+
|
|
15
|
+
p.note(
|
|
16
|
+
`${pc.cyan("minimal")} layer is always included.\n` +
|
|
17
|
+
'It provides: type: "module", no compat, no testing, no linting.\n' +
|
|
18
|
+
"Perfect for demos and reproductions.",
|
|
19
|
+
"Base Layer",
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// Let user select additional features
|
|
23
|
+
const selectedFeatures = await p.multiselect({
|
|
24
|
+
message: "Select additional features:",
|
|
25
|
+
options: optionalLayers.map((layer) => ({
|
|
26
|
+
value: layer.name,
|
|
27
|
+
label: layer.label || layer.name,
|
|
28
|
+
hint: layer.description,
|
|
29
|
+
})),
|
|
30
|
+
required: false,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (p.isCancel(selectedFeatures)) {
|
|
34
|
+
p.cancel("Operation cancelled");
|
|
35
|
+
return process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Build the final configuration
|
|
39
|
+
const selectedLayers = [
|
|
40
|
+
minimalLayer,
|
|
41
|
+
...optionalLayers.filter((l) => selectedFeatures.includes(l.name)),
|
|
42
|
+
].filter(Boolean);
|
|
43
|
+
|
|
44
|
+
return selectedLayers;
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function discoverLayers() {
|
|
49
|
+
const layersDir = join(import.meta.dirname, '..', "..", "layers");
|
|
50
|
+
const entries = await readdir(layersDir, { withFileTypes: true });
|
|
51
|
+
|
|
52
|
+
const layers = [];
|
|
53
|
+
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
if (entry.isDirectory()) {
|
|
56
|
+
const layerPath = join(layersDir, entry.name, "index.js");
|
|
57
|
+
try {
|
|
58
|
+
const layer = await import(layerPath);
|
|
59
|
+
layers.push({
|
|
60
|
+
name: entry.name,
|
|
61
|
+
...layer.default,
|
|
62
|
+
});
|
|
63
|
+
} catch (err) {
|
|
64
|
+
// Skip layers that don't have proper exports
|
|
65
|
+
console.warn(
|
|
66
|
+
`Warning: Could not load layer ${entry.name}:`,
|
|
67
|
+
err.message,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return layers;
|
|
74
|
+
}
|
|
75
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
|
|
3
|
+
export async function askName() {
|
|
4
|
+
const projectName = await p.text({
|
|
5
|
+
message: "What is your project name?",
|
|
6
|
+
placeholder: "my-app",
|
|
7
|
+
defaultValue: 'my-app',
|
|
8
|
+
validate(value) {
|
|
9
|
+
if (value.length === 0) return "Project name is required";
|
|
10
|
+
if (!/^[a-z0-9-]+$/.test(value)) {
|
|
11
|
+
return "Project name must be lowercase and can only contain letters, numbers, and hyphens";
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
if (p.isCancel(projectName)) {
|
|
17
|
+
p.cancel("Operation cancelled");
|
|
18
|
+
return process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return projectName;
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
|
|
3
|
+
export async function askPackageManager() {
|
|
4
|
+
|
|
5
|
+
const packageManager = await p.select({
|
|
6
|
+
message: "Which package manager?",
|
|
7
|
+
options: [
|
|
8
|
+
{ value: "pnpm", label: "pnpm", hint: "Fast, disk space efficient" },
|
|
9
|
+
{ value: "npm", label: "npm", hint: "Node default" },
|
|
10
|
+
{ value: "yarn", label: "yarn", hint: "Classic alternative" },
|
|
11
|
+
],
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
if (p.isCancel(packageManager)) {
|
|
15
|
+
p.cancel("Operation cancelled");
|
|
16
|
+
return process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return packageManager;
|
|
20
|
+
}
|
|
21
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import globals from "globals";
|
|
2
|
+
import js from "@eslint/js";
|
|
3
|
+
import ts from "typescript-eslint";
|
|
4
|
+
import ember from "eslint-plugin-ember";
|
|
5
|
+
import prettier from "eslint-config-prettier";
|
|
6
|
+
|
|
7
|
+
export default [
|
|
8
|
+
js.configs.recommended,
|
|
9
|
+
...ts.configs.recommended,
|
|
10
|
+
...ember.configs["flat/recommended"],
|
|
11
|
+
prettier,
|
|
12
|
+
{
|
|
13
|
+
languageOptions: {
|
|
14
|
+
globals: {
|
|
15
|
+
...globals.browser,
|
|
16
|
+
},
|
|
17
|
+
parserOptions: {
|
|
18
|
+
ecmaVersion: "latest",
|
|
19
|
+
sourceType: "module",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
files: ["**/*.gts", "**/*.gjs"],
|
|
25
|
+
languageOptions: {
|
|
26
|
+
parser: ts.parser,
|
|
27
|
+
parserOptions: {
|
|
28
|
+
ecmaVersion: "latest",
|
|
29
|
+
sourceType: "module",
|
|
30
|
+
extraFileExtensions: [".gts", ".gjs"],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
ignores: ["dist/", "node_modules/", ".vite/"],
|
|
36
|
+
},
|
|
37
|
+
];
|