create-ng-tailwind 4.0.0 → 4.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/CHANGELOG.md CHANGED
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [4.1.0] - 2026-06-06
9
+
10
+ ### ✨ New Features
11
+
12
+ - **Node.js / Angular auto-compatibility**: The CLI now detects the current Node.js runtime and automatically picks the highest compatible Angular CLI release instead of always pulling `@angular/cli@latest`. This fixes the common `EBADENGINE` failure when scaffolding on a Node version below Angular 22's minimum.
13
+ - **`--ng-version <version>` flag**: New CLI flag to pin a specific Angular CLI major (e.g. `--ng-version=21`, `--ng-version=latest`). When omitted, the highest compatible version is selected automatically.
14
+ - **Friendly preflight error**: If no supported Angular version matches the current Node.js, the CLI exits early with a formatted message listing supported Node↔Angular combinations and upgrade hints — instead of forwarding npm's cryptic engine warnings.
15
+
16
+ ### 📋 Supported Angular Versions
17
+
18
+ | Angular CLI | Required Node.js |
19
+ | ----------- | ---------------- |
20
+ | 22 (latest) | `^22.22.3 \|\| ^24.15.0 \|\| >=26.0.0` |
21
+ | 21 | `^20.19.0 \|\| ^22.12.0 \|\| >=24.0.0` |
22
+ | 20 | `^20.11.1 \|\| ^22.0.0 \|\| ^24.0.0` |
23
+ | 19 | `^18.19.1 \|\| ^20.11.1 \|\| ^22.0.0` |
24
+
25
+ ### 🏗️ Internal
26
+
27
+ - New module `lib/utils/nodeCompat.js` containing the compatibility table, a tiny range matcher, and the resolver.
28
+ - `ProjectManager` now reads `config.angularVersion` instead of hardcoding `@latest` in the scaffold command.
29
+
30
+ ---
31
+
8
32
  ## [4.0.0] - 2025-12-25
9
33
 
10
34
  ### ✨ New Template
package/README.md CHANGED
@@ -128,16 +128,34 @@ npx create-ng-tailwind my-app --template=starter --ssr --zoneless
128
128
  --template=<minimal|starter|dashboard>
129
129
 
130
130
  # Other flags
131
- --ssr # Enable Server-Side Rendering
132
- --zoneless # Create zoneless application
133
- --ai-config # Configure AI tools (Claude, Cursor, etc.)
131
+ --ssr # Enable Server-Side Rendering
132
+ --zoneless # Create zoneless application
133
+ --ai-config # Configure AI tools (Claude, Cursor, etc.)
134
+ --ng-version=<v> # Pin Angular CLI version (latest|21|20|19).
135
+ # Default: auto-pick the highest version compatible
136
+ # with your current Node.js runtime.
134
137
  ```
135
138
 
136
139
  ---
137
140
 
141
+ ## Node.js / Angular Compatibility
142
+
143
+ This CLI auto-detects your Node.js version and picks the highest compatible Angular CLI release. You don't normally need to think about it — but if you want to pin a specific Angular version, pass `--ng-version=<v>`.
144
+
145
+ | Angular CLI | Required Node.js |
146
+ | ----------- | --------------------------------- |
147
+ | 22 (latest) | `^22.22.3 \|\| ^24.15.0 \|\| >=26.0.0` |
148
+ | 21 | `^20.19.0 \|\| ^22.12.0 \|\| >=24.0.0` |
149
+ | 20 | `^20.11.1 \|\| ^22.0.0 \|\| ^24.0.0` |
150
+ | 19 | `^18.19.1 \|\| ^20.11.1 \|\| ^22.0.0` |
151
+
152
+ If no Angular version supports your Node, the CLI prints an upgrade hint and exits.
153
+
154
+ ---
155
+
138
156
  ## Requirements
139
157
 
140
- - Node.js 18+
158
+ - Node.js 18+ (Node 20.11.1+ recommended for Angular 20+)
141
159
  - npm 9+
142
160
 
143
161
  ---
package/lib/cli/index.js CHANGED
@@ -3,6 +3,12 @@ const inquirer = require("inquirer");
3
3
  const chalk = require("chalk");
4
4
  const CLI = require("./interactive");
5
5
  const { createLogger } = require("../utils/logger");
6
+ const {
7
+ ANGULAR_COMPAT,
8
+ pickAngularVersion,
9
+ satisfies,
10
+ buildIncompatMessage,
11
+ } = require("../utils/nodeCompat");
6
12
 
7
13
  class CLIHandler {
8
14
  constructor() {
@@ -27,6 +33,10 @@ class CLIHandler {
27
33
  "--ai-config <tools>",
28
34
  "AI tools to configure (none, claude, cursor, gemini, copilot, jetbrains, windsurf)",
29
35
  )
36
+ .option(
37
+ "--ng-version <version>",
38
+ "Pin Angular CLI version (e.g. latest, 21, 20, 19). Default: auto-pick highest compatible with current Node",
39
+ )
30
40
  .option("--skip-install", "Skip npm install")
31
41
  .parse();
32
42
 
@@ -167,7 +177,41 @@ class CLIHandler {
167
177
  return answers;
168
178
  }
169
179
 
180
+ resolveAngularVersion(requested) {
181
+ const nodeVer = process.versions.node;
182
+
183
+ // User explicitly pinned a version — honor it, but warn if known-incompatible.
184
+ if (requested) {
185
+ const entry = ANGULAR_COMPAT.find((e) => e.version === requested);
186
+ if (entry && !satisfies(nodeVer, entry.nodeRanges)) {
187
+ this.logger.warn(
188
+ `Angular ${requested} requires Node ${entry.nodeRanges.join(" || ")}, but you're on ${nodeVer}. Proceeding anyway — expect engine warnings.`,
189
+ );
190
+ }
191
+ return { version: requested, label: entry ? entry.label : `Angular ${requested}`, pinned: true };
192
+ }
193
+
194
+ // Auto-pick the highest compatible version.
195
+ const picked = pickAngularVersion(nodeVer);
196
+ if (!picked) {
197
+ console.error(chalk.red("\n❌ Incompatible Node.js version\n"));
198
+ console.error(buildIncompatMessage(nodeVer));
199
+ process.exit(1);
200
+ }
201
+
202
+ // Tell the user when we fell back from latest.
203
+ if (picked.version !== "latest") {
204
+ this.logger.info(
205
+ `Node ${nodeVer} doesn't support Angular latest — using ${picked.label}. Pass --ng-version=latest to override.`,
206
+ );
207
+ }
208
+
209
+ return { ...picked, pinned: false };
210
+ }
211
+
170
212
  async buildConfiguration(options, projectName, configOptions) {
213
+ const angular = this.resolveAngularVersion(options.ngVersion);
214
+
171
215
  const config = {
172
216
  projectName,
173
217
  template: options.template || "starter",
@@ -176,6 +220,7 @@ class CLIHandler {
176
220
  ssr: configOptions.ssr || false,
177
221
  zoneless: configOptions.zoneless || false,
178
222
  aiConfig: configOptions.aiConfig || ["none"], // Array of AI tools
223
+ angularVersion: angular.version,
179
224
  skipInstall: options.skipInstall || false,
180
225
  fullPath: require("path").resolve(process.cwd(), projectName),
181
226
  };
@@ -51,7 +51,8 @@ class ProjectManager {
51
51
  const zoneless = this.config.zoneless || false;
52
52
  const aiConfig = this.config.aiConfig || ['none'];
53
53
 
54
- let cmd = `npx @angular/cli@latest new ${this.config.projectName}`;
54
+ const ngVersion = this.config.angularVersion || "latest";
55
+ let cmd = `npx @angular/cli@${ngVersion} new ${this.config.projectName}`;
55
56
  cmd += ` --routing=${routing}`;
56
57
  cmd += ` --style=css`; // Always use CSS (Tailwind v4 official approach)
57
58
  cmd += ` --ssr=${ssr}`;
@@ -0,0 +1,85 @@
1
+ // Node.js -> Angular CLI compatibility matrix.
2
+ // Ordered newest → oldest. The picker walks this list and returns the first
3
+ // Angular version whose engine ranges accept the current Node runtime.
4
+ const ANGULAR_COMPAT = [
5
+ {
6
+ version: "latest",
7
+ label: "Angular 22 (latest)",
8
+ nodeRanges: ["^22.22.3", "^24.15.0", ">=26.0.0"],
9
+ },
10
+ {
11
+ version: "21",
12
+ label: "Angular 21",
13
+ nodeRanges: ["^20.19.0", "^22.12.0", ">=24.0.0"],
14
+ },
15
+ {
16
+ version: "20",
17
+ label: "Angular 20",
18
+ nodeRanges: ["^20.11.1", "^22.0.0", "^24.0.0"],
19
+ },
20
+ {
21
+ version: "19",
22
+ label: "Angular 19",
23
+ nodeRanges: ["^18.19.1", "^20.11.1", "^22.0.0"],
24
+ },
25
+ ];
26
+
27
+ function parseVer(v) {
28
+ const [maj = 0, min = 0, pat = 0] = v.replace(/^v/, "").split(".").map(Number);
29
+ return { maj, min, pat };
30
+ }
31
+
32
+ function gte(a, b) {
33
+ if (a.maj !== b.maj) return a.maj > b.maj;
34
+ if (a.min !== b.min) return a.min > b.min;
35
+ return a.pat >= b.pat;
36
+ }
37
+
38
+ function matchesRange(nodeVer, range) {
39
+ const node = parseVer(nodeVer);
40
+ if (range.startsWith("^")) {
41
+ const target = parseVer(range.slice(1));
42
+ return node.maj === target.maj && gte(node, target);
43
+ }
44
+ if (range.startsWith(">=")) {
45
+ const target = parseVer(range.slice(2));
46
+ return gte(node, target);
47
+ }
48
+ return false;
49
+ }
50
+
51
+ function satisfies(nodeVer, ranges) {
52
+ return ranges.some((r) => matchesRange(nodeVer, r));
53
+ }
54
+
55
+ function pickAngularVersion(nodeVer) {
56
+ for (const entry of ANGULAR_COMPAT) {
57
+ if (satisfies(nodeVer, entry.nodeRanges)) return entry;
58
+ }
59
+ return null;
60
+ }
61
+
62
+ function formatRanges(entry) {
63
+ return entry.nodeRanges.join(" || ");
64
+ }
65
+
66
+ function buildIncompatMessage(nodeVer) {
67
+ const lines = [
68
+ `Node ${nodeVer} is not supported by any Angular CLI version this tool knows about.`,
69
+ "",
70
+ "Supported combinations:",
71
+ ...ANGULAR_COMPAT.map((e) => ` • ${e.label.padEnd(22)} requires Node ${formatRanges(e)}`),
72
+ "",
73
+ "Options:",
74
+ " • Upgrade Node: nvm install 24 && nvm use 24 (or use Homebrew / nodejs.org)",
75
+ " • Or downgrade Angular explicitly via the flag: --ng-version=20",
76
+ ];
77
+ return lines.join("\n");
78
+ }
79
+
80
+ module.exports = {
81
+ ANGULAR_COMPAT,
82
+ pickAngularVersion,
83
+ satisfies,
84
+ buildIncompatMessage,
85
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ng-tailwind",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "description": "🚀 A CLI tool to give starter template for angular project with tailwind css",
5
5
  "main": "bin/create-ng-tailwind.js",
6
6
  "bin": {