Skip to content

Commit bb2da31

Browse files
Update package versions and enhance CLI functionality
- Bump versions for several packages: create-awesome-node-app (0.4.26), @create-node-app/core (0.3.17), eslint-config-base (0.0.2), eslint-config-next (0.0.2), eslint-config-react (0.0.2), eslint-config-ts (0.0.2), and tsconfig (0.0.2). - Add new CLI options to list available templates and addons. - Implement functions to handle listing templates and addons in the CLI. - Update README to include instructions for listing templates and addons.
1 parent 08b0d46 commit bb2da31

File tree

6 files changed

+303
-61
lines changed

6 files changed

+303
-61
lines changed

package-lock.json

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/create-awesome-node-app/README.md

+15
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,21 @@ create-awesome-node-app --template react-vite-boilerplate --addons jotai materia
6767

6868
This example uses the `react-vite-boilerplate` template and applies the `jotai`, `material-ui`, and `github-setup` extensions.
6969

70+
### Listing Templates and Addons
71+
72+
You can list all available templates and addons using the following flags:
73+
74+
```sh
75+
# List all available templates
76+
create-awesome-node-app --list-templates
77+
78+
# List all available addons
79+
create-awesome-node-app --list-addons
80+
81+
# List addons compatible with a specific template
82+
create-awesome-node-app --template react-vite-boilerplate --list-addons
83+
```
84+
7085
## 🔗 Full List of Templates and Extensions
7186

7287
You can find the full list of available templates and extensions in the [cna-templates repository](https://github.com/Create-Node-App/cna-templates).

packages/create-awesome-node-app/src/index.ts

+21-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { getCnaOptions } from "./options";
1010
import packageJson from "../package.json";
1111
import { TemplateOrExtension } from "@create-node-app/core/loaders";
12+
import { listTemplates, listAddons } from "./list";
1213

1314
const program = new Command();
1415

@@ -36,6 +37,8 @@ const main = async () => {
3637
.option("--use-yarn", "use yarn instead of npm or pnpm")
3738
.option("--use-pnpm", "use pnpm instead of yarn or npm")
3839
.option("--interactive", "run in interactive mode to select options", false)
40+
.option("--list-templates", "list all available templates")
41+
.option("--list-addons", "list all available addons")
3942
.action((providedProjectName: string | undefined) => {
4043
projectName = providedProjectName || projectName;
4144
});
@@ -57,14 +60,26 @@ const main = async () => {
5760
return;
5861
}
5962

60-
const options = await getCnaOptions({ ...opts, projectName });
63+
// Handle list templates flag
64+
if (opts.listTemplates) {
65+
await listTemplates();
66+
return;
67+
}
68+
69+
// Handle list addons flag
70+
if (opts.listAddons) {
71+
await listAddons({
72+
templateSlug: opts.template,
73+
});
74+
return;
75+
}
6176

62-
const { useYarn, usePnpm, ...restOptions } = options;
77+
// Extract package manager options directly from opts
78+
const { useYarn, usePnpm, ...restOpts } = opts;
6379
const packageManager = useYarn ? "yarn" : usePnpm ? "pnpm" : "npm";
6480

65-
const templatesOrExtensions: TemplateOrExtension[] = [restOptions.template]
66-
.concat(Array.isArray(restOptions.addons) ? restOptions.addons : [])
67-
.concat(Array.isArray(restOptions.extend) ? restOptions.extend : [])
81+
const templatesOrExtensions: TemplateOrExtension[] = [restOpts.template]
82+
.concat(Array.isArray(restOpts.extend) ? restOpts.extend : [])
6883
.reduce((acc, templateOrExtension) => {
6984
if (!templateOrExtension) {
7085
return acc;
@@ -76,7 +91,7 @@ const main = async () => {
7691

7792
return createNodeApp(
7893
projectName,
79-
{ ...restOptions, packageManager, templatesOrExtensions, projectName },
94+
{ ...restOpts, packageManager, templatesOrExtensions, projectName },
8095
getCnaOptions
8196
);
8297
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import chalk from "chalk";
2+
import {
3+
getTemplateCategories,
4+
getTemplatesForCategory,
5+
getExtensionsGroupedByCategory,
6+
getCategoryData,
7+
} from "./templates";
8+
9+
/**
10+
* List all available templates grouped by category
11+
*/
12+
export const listTemplates = async () => {
13+
const categories = await getTemplateCategories();
14+
15+
console.log(chalk.bold.blue("\nAvailable Templates:"));
16+
17+
for (const categorySlug of categories) {
18+
const categoryData = await getCategoryData(categorySlug);
19+
const templates = await getTemplatesForCategory(categorySlug);
20+
21+
// Display category name if available, otherwise use the slug
22+
const categoryName = categoryData?.name || categorySlug;
23+
console.log(chalk.bold.green(`\n${categoryName}:`));
24+
25+
// Display category description if available
26+
if (categoryData?.description) {
27+
console.log(` ${categoryData.description}`);
28+
}
29+
30+
templates.forEach((template) => {
31+
console.log(
32+
` ${chalk.yellow(template.name)} (${chalk.cyan(template.slug)})`
33+
);
34+
console.log(` ${template.description}`);
35+
if (template.labels && template.labels.length > 0) {
36+
console.log(` Keywords: ${template.labels.join(", ")}`);
37+
}
38+
});
39+
}
40+
};
41+
42+
/**
43+
* List all available addons grouped by category
44+
* @param templateSlug Optional template slug to filter compatible addons
45+
* @param templateType Optional template type to filter compatible addons
46+
*/
47+
export const listAddons = async ({
48+
templateSlug,
49+
templateType,
50+
}: {
51+
templateSlug?: string;
52+
templateType?: string;
53+
}) => {
54+
// If templateSlug is provided but templateType is not, try to get the template type
55+
if (templateSlug && !templateType) {
56+
templateType = await getTemplateTypeFromSlug(templateSlug);
57+
}
58+
59+
const types = templateType ? [templateType, "all"] : ["all"];
60+
const extensionsGroupedByCategory = await getExtensionsGroupedByCategory(
61+
types
62+
);
63+
64+
console.log(chalk.bold.blue("\nAvailable Addons:"));
65+
66+
if (templateSlug) {
67+
console.log(
68+
chalk.bold.green(`\nCompatible with template: ${templateSlug}`)
69+
);
70+
}
71+
72+
for (const [categorySlug, extensions] of Object.entries(
73+
extensionsGroupedByCategory
74+
)) {
75+
const categoryData = await getCategoryData(categorySlug);
76+
77+
// Display category name if available, otherwise use the slug
78+
const categoryName = categoryData?.name || categorySlug;
79+
console.log(chalk.bold.green(`\n${categoryName}:`));
80+
81+
// Display category description if available
82+
if (categoryData?.description) {
83+
console.log(` ${categoryData.description}`);
84+
}
85+
86+
extensions.forEach((extension) => {
87+
console.log(
88+
` ${chalk.yellow(extension.name)} (${chalk.cyan(extension.slug)})`
89+
);
90+
console.log(` ${extension.description}`);
91+
if (extension.labels && extension.labels.length > 0) {
92+
console.log(` Keywords: ${extension.labels.join(", ")}`);
93+
}
94+
});
95+
}
96+
};
97+
98+
/**
99+
* Get template type from template slug
100+
* @param templateSlug The template slug to look up
101+
*/
102+
export const getTemplateTypeFromSlug = async (
103+
templateSlug: string
104+
): Promise<string | undefined> => {
105+
const categories = await getTemplateCategories();
106+
107+
for (const category of categories) {
108+
const templates = await getTemplatesForCategory(category);
109+
const template = templates.find((t) => t.slug === templateSlug);
110+
111+
if (template) {
112+
return template.type;
113+
}
114+
}
115+
116+
return undefined;
117+
};

0 commit comments

Comments
 (0)