create-ng-tailwind 1.0.0 → 2.0.1
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 +209 -8
- package/CLAUDE.md +178 -0
- package/LICENSE +1 -1
- package/README.md +151 -246
- package/bin/create-ng-tailwind.js +5 -407
- package/lib/cli/index.js +222 -0
- package/lib/cli/interactive.js +26 -0
- package/lib/cli/validators.js +23 -0
- package/lib/config/defaults.js +7 -0
- package/lib/managers/ProjectManager.js +97 -0
- package/lib/managers/TailwindManager.js +100 -0
- package/lib/managers/TemplateManager.js +39 -0
- package/lib/templates/index.js +7 -0
- package/lib/templates/minimal/index.js +24 -0
- package/lib/templates/starter/advanced-features.js +528 -0
- package/lib/templates/starter/features.js +700 -0
- package/lib/templates/starter/index.js +4117 -0
- package/lib/templates/starter/ui-features.js +437 -0
- package/lib/utils/ai-config.js +606 -0
- package/lib/utils/constants.js +14 -0
- package/lib/utils/helpers.js +60 -0
- package/lib/utils/logger.js +109 -0
- package/package.json +6 -15
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const execa = require("execa");
|
|
4
|
+
const TailwindManager = require("./TailwindManager");
|
|
5
|
+
const TemplateManager = require("./TemplateManager");
|
|
6
|
+
const { createAIConfigs } = require("../utils/ai-config");
|
|
7
|
+
|
|
8
|
+
class ProjectManager {
|
|
9
|
+
constructor(config, logger) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.logger = logger;
|
|
12
|
+
this.tailwindManager = new TailwindManager(config, logger);
|
|
13
|
+
this.templateManager = new TemplateManager(config, logger);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async create() {
|
|
17
|
+
// Check if directory exists
|
|
18
|
+
if (await fs.pathExists(this.config.fullPath)) {
|
|
19
|
+
const { overwrite } = await this.logger.prompt([
|
|
20
|
+
{
|
|
21
|
+
type: "confirm",
|
|
22
|
+
name: "overwrite",
|
|
23
|
+
message: `Directory ${this.config.projectName} already exists. Overwrite?`,
|
|
24
|
+
default: false,
|
|
25
|
+
},
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
if (!overwrite) {
|
|
29
|
+
this.logger.warn("Operation cancelled");
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
await fs.remove(this.config.fullPath);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Create Angular project
|
|
37
|
+
await this.createAngularProject();
|
|
38
|
+
|
|
39
|
+
// Configure Tailwind CSS
|
|
40
|
+
await this.tailwindManager.configure();
|
|
41
|
+
|
|
42
|
+
// Apply template
|
|
43
|
+
await this.templateManager.apply();
|
|
44
|
+
|
|
45
|
+
// Create AI configuration files (CLAUDE.md + tool-specific)
|
|
46
|
+
await createAIConfigs(
|
|
47
|
+
this.config.fullPath,
|
|
48
|
+
this.config.projectName,
|
|
49
|
+
this.config.aiConfig,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async createAngularProject() {
|
|
54
|
+
const spinner = this.logger.spinner("Creating Angular project...");
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
// Build ng new command with user's choices
|
|
58
|
+
const routing = this.config.routing !== false;
|
|
59
|
+
const ssr = this.config.ssr || false;
|
|
60
|
+
const zoneless = this.config.zoneless || false;
|
|
61
|
+
const aiConfig = this.config.aiConfig || ["none"];
|
|
62
|
+
|
|
63
|
+
let cmd = `npx @angular/cli@latest new ${this.config.projectName}`;
|
|
64
|
+
cmd += ` --routing=${routing}`;
|
|
65
|
+
cmd += ` --style=css`; // Always use CSS (Tailwind v4 official approach)
|
|
66
|
+
cmd += ` --ssr=${ssr}`;
|
|
67
|
+
|
|
68
|
+
if (zoneless) {
|
|
69
|
+
cmd += ` --zoneless`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Add AI config - Angular CLI supports multiple --ai-config flags
|
|
73
|
+
if (aiConfig.length > 0 && !aiConfig.includes("none")) {
|
|
74
|
+
// Add each AI tool as a separate --ai-config flag
|
|
75
|
+
aiConfig.forEach((tool) => {
|
|
76
|
+
if (tool !== "none") {
|
|
77
|
+
cmd += ` --ai-config=${tool}`;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
cmd += ` --skip-git --package-manager=npm --interactive=false`;
|
|
83
|
+
|
|
84
|
+
await execa.command(cmd, {
|
|
85
|
+
cwd: process.cwd(),
|
|
86
|
+
stdio: "pipe",
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
spinner.succeed("Angular project created");
|
|
90
|
+
} catch (error) {
|
|
91
|
+
spinner.fail("Failed to create Angular project");
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = ProjectManager;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const execa = require("execa");
|
|
4
|
+
|
|
5
|
+
class TailwindManager {
|
|
6
|
+
constructor(config, logger) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
this.logger = logger;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async configure() {
|
|
12
|
+
const chalk = require("chalk");
|
|
13
|
+
const spinner = this.logger.spinner("Configuring Tailwind CSS...");
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
// Install Tailwind v4 with @tailwindcss/postcss
|
|
17
|
+
if (!this.config.skipInstall) {
|
|
18
|
+
spinner.update("Installing Tailwind CSS packages...");
|
|
19
|
+
await execa.command(
|
|
20
|
+
"npm install tailwindcss @tailwindcss/postcss postcss --force",
|
|
21
|
+
{
|
|
22
|
+
cwd: this.config.fullPath,
|
|
23
|
+
stdio: "pipe",
|
|
24
|
+
},
|
|
25
|
+
);
|
|
26
|
+
spinner.stop();
|
|
27
|
+
console.log(chalk.green(" ✔ Tailwind CSS v4 packages installed (tailwindcss, @tailwindcss/postcss, postcss)"));
|
|
28
|
+
spinner.start();
|
|
29
|
+
} else {
|
|
30
|
+
await this.addTailwindToPackageJson();
|
|
31
|
+
spinner.stop();
|
|
32
|
+
console.log(chalk.green(" ✔ Tailwind CSS packages added to package.json"));
|
|
33
|
+
spinner.start();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Create PostCSS config (v4 approach)
|
|
37
|
+
spinner.update("Creating PostCSS configuration...");
|
|
38
|
+
await this.createPostCSSConfig();
|
|
39
|
+
spinner.stop();
|
|
40
|
+
console.log(chalk.green(" ✔ PostCSS configuration created (.postcssrc.json)"));
|
|
41
|
+
spinner.start();
|
|
42
|
+
|
|
43
|
+
// Update styles (v4 approach)
|
|
44
|
+
spinner.update("Setting up Tailwind CSS imports...");
|
|
45
|
+
await this.updateStyles();
|
|
46
|
+
spinner.stop();
|
|
47
|
+
console.log(chalk.green(" ✔ Tailwind CSS imported in styles.css"));
|
|
48
|
+
|
|
49
|
+
spinner.succeed("Tailwind CSS configured");
|
|
50
|
+
} catch (error) {
|
|
51
|
+
spinner.fail("Failed to configure Tailwind CSS");
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async addTailwindToPackageJson() {
|
|
57
|
+
const packageJsonPath = path.join(this.config.fullPath, "package.json");
|
|
58
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
59
|
+
|
|
60
|
+
if (!packageJson.dependencies) {
|
|
61
|
+
packageJson.dependencies = {};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Tailwind CSS v4 goes in dependencies, not devDependencies
|
|
65
|
+
packageJson.dependencies["tailwindcss"] = "^4.0.0";
|
|
66
|
+
packageJson.dependencies["@tailwindcss/postcss"] = "^4.0.0";
|
|
67
|
+
packageJson.dependencies["postcss"] = "^8.4.0";
|
|
68
|
+
|
|
69
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async createPostCSSConfig() {
|
|
73
|
+
// Tailwind CSS v4 uses PostCSS plugin approach
|
|
74
|
+
const postcssConfig = {
|
|
75
|
+
plugins: {
|
|
76
|
+
"@tailwindcss/postcss": {},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
await fs.writeFile(
|
|
81
|
+
path.join(this.config.fullPath, ".postcssrc.json"),
|
|
82
|
+
JSON.stringify(postcssConfig, null, 2),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async updateStyles() {
|
|
87
|
+
// Tailwind CSS v4 uses CSS only (official approach)
|
|
88
|
+
const stylesPath = path.join(this.config.fullPath, "src/styles.css");
|
|
89
|
+
|
|
90
|
+
// Tailwind CSS v4 import syntax
|
|
91
|
+
const stylesContent = `@import "tailwindcss";
|
|
92
|
+
|
|
93
|
+
/* Your custom styles here */
|
|
94
|
+
`;
|
|
95
|
+
|
|
96
|
+
await fs.writeFile(stylesPath, stylesContent);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = TailwindManager;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const templates = require("../templates");
|
|
2
|
+
|
|
3
|
+
class TemplateManager {
|
|
4
|
+
constructor(config, logger) {
|
|
5
|
+
this.config = config;
|
|
6
|
+
this.logger = logger;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async apply() {
|
|
10
|
+
const templateName = this.config.template || "minimal";
|
|
11
|
+
const template = templates[templateName];
|
|
12
|
+
|
|
13
|
+
if (!template) {
|
|
14
|
+
throw new Error(`Template '${templateName}' not found`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Start spinner - it will keep spinning throughout the process
|
|
18
|
+
const spinner = this.logger.spinner(`Applying ${templateName} template...`);
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
// For starter template, show detailed progress with spinner updates
|
|
22
|
+
if (templateName === 'starter') {
|
|
23
|
+
// Pass spinner to template so it can update progress
|
|
24
|
+
await template.apply(this.config, spinner);
|
|
25
|
+
} else {
|
|
26
|
+
// For minimal template, just apply
|
|
27
|
+
await template.apply(this.config);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Stop spinner with success message
|
|
31
|
+
spinner.succeed(`${templateName} template applied`);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
spinner.fail(`Failed to apply ${templateName} template`);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = TemplateManager;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
const minimal = {
|
|
5
|
+
info: {
|
|
6
|
+
name: "Minimal",
|
|
7
|
+
description:
|
|
8
|
+
"Clean Angular + Tailwind CSS starter with zero additional features",
|
|
9
|
+
features: [
|
|
10
|
+
"Latest Angular standalone architecture",
|
|
11
|
+
"Tailwind CSS v4 with PostCSS",
|
|
12
|
+
"Clean project structure",
|
|
13
|
+
"Ready to build your own way",
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
async apply(config) {
|
|
18
|
+
// Minimal template keeps only the basic Angular + Tailwind setup
|
|
19
|
+
// No additional features, components, or services added
|
|
20
|
+
// (No console output needed - TemplateManager shows final checkmark)
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
module.exports = minimal;
|