create-awesome-node-app 0.4.26 → 0.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
@@ -67,6 +67,21 @@ create-awesome-node-app --template react-vite-boilerplate --addons jotai materia
67
67
 
68
68
  This example uses the `react-vite-boilerplate` template and applies the `jotai`, `material-ui`, and `github-setup` extensions.
69
69
 
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
+
70
85
  ## 🔗 Full List of Templates and Extensions
71
86
 
72
87
  You can find the full list of available templates and extensions in the [cna-templates repository](https://github.com/Create-Node-App/cna-templates).
package/dist/index.cjs CHANGED
@@ -1342,14 +1342,14 @@ var require_templates = __commonJS({
1342
1342
  }
1343
1343
  return results;
1344
1344
  }
1345
- function buildStyle(chalk2, styles) {
1345
+ function buildStyle(chalk3, styles) {
1346
1346
  const enabled = {};
1347
1347
  for (const layer of styles) {
1348
1348
  for (const style of layer.styles) {
1349
1349
  enabled[style[0]] = layer.inverse ? null : style.slice(1);
1350
1350
  }
1351
1351
  }
1352
- let current = chalk2;
1352
+ let current = chalk3;
1353
1353
  for (const [styleName, styles2] of Object.entries(enabled)) {
1354
1354
  if (!Array.isArray(styles2)) {
1355
1355
  continue;
@@ -1361,7 +1361,7 @@ var require_templates = __commonJS({
1361
1361
  }
1362
1362
  return current;
1363
1363
  }
1364
- module2.exports = (chalk2, temporary) => {
1364
+ module2.exports = (chalk3, temporary) => {
1365
1365
  const styles = [];
1366
1366
  const chunks = [];
1367
1367
  let chunk = [];
@@ -1371,13 +1371,13 @@ var require_templates = __commonJS({
1371
1371
  } else if (style) {
1372
1372
  const string = chunk.join("");
1373
1373
  chunk = [];
1374
- chunks.push(styles.length === 0 ? string : buildStyle(chalk2, styles)(string));
1374
+ chunks.push(styles.length === 0 ? string : buildStyle(chalk3, styles)(string));
1375
1375
  styles.push({ inverse, styles: parseStyle(style) });
1376
1376
  } else if (close) {
1377
1377
  if (styles.length === 0) {
1378
1378
  throw new Error("Found extraneous } in Chalk template literal");
1379
1379
  }
1380
- chunks.push(buildStyle(chalk2, styles)(chunk.join("")));
1380
+ chunks.push(buildStyle(chalk3, styles)(chunk.join("")));
1381
1381
  chunk = [];
1382
1382
  styles.pop();
1383
1383
  } else {
@@ -1425,16 +1425,16 @@ var require_source = __commonJS({
1425
1425
  }
1426
1426
  };
1427
1427
  var chalkFactory = (options) => {
1428
- const chalk3 = {};
1429
- applyOptions(chalk3, options);
1430
- chalk3.template = (...arguments_) => chalkTag(chalk3.template, ...arguments_);
1431
- Object.setPrototypeOf(chalk3, Chalk.prototype);
1432
- Object.setPrototypeOf(chalk3.template, chalk3);
1433
- chalk3.template.constructor = () => {
1428
+ const chalk4 = {};
1429
+ applyOptions(chalk4, options);
1430
+ chalk4.template = (...arguments_) => chalkTag(chalk4.template, ...arguments_);
1431
+ Object.setPrototypeOf(chalk4, Chalk.prototype);
1432
+ Object.setPrototypeOf(chalk4.template, chalk4);
1433
+ chalk4.template.constructor = () => {
1434
1434
  throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.");
1435
1435
  };
1436
- chalk3.template.Instance = ChalkClass;
1437
- return chalk3.template;
1436
+ chalk4.template.Instance = ChalkClass;
1437
+ return chalk4.template;
1438
1438
  };
1439
1439
  function Chalk(options) {
1440
1440
  return chalkFactory(options);
@@ -1545,7 +1545,7 @@ var require_source = __commonJS({
1545
1545
  return openAll + string + closeAll;
1546
1546
  };
1547
1547
  var template;
1548
- var chalkTag = (chalk3, ...strings) => {
1548
+ var chalkTag = (chalk4, ...strings) => {
1549
1549
  const [firstString] = strings;
1550
1550
  if (!isArray(firstString) || !isArray(firstString.raw)) {
1551
1551
  return strings.join(" ");
@@ -1561,20 +1561,20 @@ var require_source = __commonJS({
1561
1561
  if (template === void 0) {
1562
1562
  template = require_templates();
1563
1563
  }
1564
- return template(chalk3, parts.join(""));
1564
+ return template(chalk4, parts.join(""));
1565
1565
  };
1566
1566
  Object.defineProperties(Chalk.prototype, styles);
1567
- var chalk2 = Chalk();
1568
- chalk2.supportsColor = stdoutColor;
1569
- chalk2.stderr = Chalk({ level: stderrColor ? stderrColor.level : 0 });
1570
- chalk2.stderr.supportsColor = stderrColor;
1571
- module2.exports = chalk2;
1567
+ var chalk3 = Chalk();
1568
+ chalk3.supportsColor = stdoutColor;
1569
+ chalk3.stderr = Chalk({ level: stderrColor ? stderrColor.level : 0 });
1570
+ chalk3.stderr.supportsColor = stderrColor;
1571
+ module2.exports = chalk3;
1572
1572
  }
1573
1573
  });
1574
1574
 
1575
1575
  // src/index.ts
1576
1576
  var import_commander = require("commander");
1577
- var import_chalk = __toESM(require_source(), 1);
1577
+ var import_chalk2 = __toESM(require_source(), 1);
1578
1578
  var import_semver = __toESM(require("semver"), 1);
1579
1579
  var import_core = require("@create-node-app/core");
1580
1580
 
@@ -1612,12 +1612,24 @@ var getTemplateCategories = async (cliArgs) => {
1612
1612
  return [cliArgs.category];
1613
1613
  }
1614
1614
  const templateData = await getTemplateData();
1615
+ if (templateData.categories && templateData.categories.length > 0) {
1616
+ return templateData.categories.map((category) => category.slug);
1617
+ }
1615
1618
  const categories = /* @__PURE__ */ new Set();
1616
1619
  templateData.templates.forEach((template) => {
1617
1620
  categories.add(template.category);
1618
1621
  });
1619
1622
  return Array.from(categories);
1620
1623
  };
1624
+ var getCategoryData = async (categorySlug) => {
1625
+ const templateData = await getTemplateData();
1626
+ if (templateData.categories && templateData.categories.length > 0) {
1627
+ return templateData.categories.find(
1628
+ (category) => category.slug === categorySlug
1629
+ );
1630
+ }
1631
+ return void 0;
1632
+ };
1621
1633
  var getTemplatesForCategory = async (category, cliArgs) => {
1622
1634
  const selectedCategory = (cliArgs == null ? void 0 : cliArgs.category) || category;
1623
1635
  if (!selectedCategory) {
@@ -1661,63 +1673,87 @@ var isValidUrl = (url) => {
1661
1673
  return false;
1662
1674
  }
1663
1675
  };
1664
- var getCnaOptions = async (options) => {
1676
+ var processNonInteractiveOptions = async (options) => {
1665
1677
  const categories = await getTemplateCategories();
1666
- if (import_ci_info.isCI || !options.interactive) {
1667
- let matchedTemplate;
1668
- if (options.template && !isValidUrl(options.template)) {
1669
- const allTemplates = (await Promise.all(
1670
- categories.map((category) => getTemplatesForCategory(category))
1671
- )).flat();
1672
- matchedTemplate = allTemplates.find(
1673
- (template) => template.slug === options.template
1678
+ let matchedTemplate;
1679
+ const templatesOrExtensions = [];
1680
+ if (options.template && !isValidUrl(options.template)) {
1681
+ const allTemplates = (await Promise.all(
1682
+ categories.map((category) => getTemplatesForCategory(category))
1683
+ )).flat();
1684
+ matchedTemplate = allTemplates.find(
1685
+ (template) => template.slug === options.template
1686
+ );
1687
+ if (matchedTemplate) {
1688
+ templatesOrExtensions.push({ url: matchedTemplate.url });
1689
+ if (matchedTemplate.customOptions) {
1690
+ matchedTemplate.customOptions.forEach((customOption) => {
1691
+ if (customOption.name && customOption.initial !== void 0) {
1692
+ options[customOption.name] = customOption.initial;
1693
+ }
1694
+ });
1695
+ }
1696
+ } else {
1697
+ throw new Error(
1698
+ `Invalid template slug: '${options.template}'. Please provide a valid template slug.`
1674
1699
  );
1675
- if (matchedTemplate) {
1676
- options.template = matchedTemplate.url;
1677
- if (matchedTemplate.customOptions) {
1678
- matchedTemplate.customOptions.forEach((customOption) => {
1679
- if (customOption.name && customOption.initial !== void 0) {
1680
- options[customOption.name] = customOption.initial;
1681
- }
1682
- });
1700
+ }
1701
+ } else if (options.template) {
1702
+ templatesOrExtensions.push({ url: options.template });
1703
+ }
1704
+ if (options.addons && Array.isArray(options.addons)) {
1705
+ const extensionsGroupedByCategory = await getExtensionsGroupedByCategory([
1706
+ (matchedTemplate == null ? void 0 : matchedTemplate.type) || "custom",
1707
+ "all"
1708
+ ]);
1709
+ const extensions = options.addons.map((addon) => {
1710
+ if (!isValidUrl(addon)) {
1711
+ for (const extensions2 of Object.values(extensionsGroupedByCategory)) {
1712
+ const matchedExtension = extensions2.find(
1713
+ (extension) => extension.slug === addon
1714
+ );
1715
+ if (matchedExtension) {
1716
+ return matchedExtension.url;
1717
+ }
1683
1718
  }
1684
- } else {
1685
1719
  throw new Error(
1686
- `Invalid template slug: '${options.template}'. Please provide a valid template slug.`
1720
+ `Invalid extension slug: '${addon}'. Please provide a valid extension slug.`
1687
1721
  );
1688
1722
  }
1689
- }
1690
- if (options.addons && Array.isArray(options.addons)) {
1691
- const extensionsGroupedByCategory2 = await getExtensionsGroupedByCategory([
1692
- (matchedTemplate == null ? void 0 : matchedTemplate.type) || "custom",
1693
- "all"
1694
- ]);
1695
- options.addons = options.addons.map((addon) => {
1696
- if (!isValidUrl(addon)) {
1697
- for (const extensions of Object.values(extensionsGroupedByCategory2)) {
1698
- const matchedExtension = extensions.find(
1699
- (extension) => extension.slug === addon
1700
- );
1701
- if (matchedExtension) {
1702
- return matchedExtension.url;
1703
- }
1704
- }
1705
- throw new Error(
1706
- `Invalid extension slug: '${addon}'. Please provide a valid extension slug.`
1707
- );
1708
- }
1709
- return addon;
1710
- });
1711
- }
1712
- if (options.verbose) {
1713
- console.log(JSON.stringify(options, null, 2));
1714
- }
1715
- return options;
1723
+ return addon;
1724
+ }).map((addon) => ({ url: addon }));
1725
+ templatesOrExtensions.push(...extensions);
1726
+ }
1727
+ if (options.extend && Array.isArray(options.extend)) {
1728
+ const additionalExtensions = options.extend.filter(Boolean).map((extension) => ({ url: extension }));
1729
+ templatesOrExtensions.push(...additionalExtensions);
1730
+ }
1731
+ if (options.aiTool && !["cursor", "copilot", "none"].includes(options.aiTool)) {
1732
+ throw new Error("Invalid --ai-tool option. Use: cursor, copilot, or none");
1716
1733
  }
1734
+ options.aiTool = options.aiTool || "none";
1735
+ options.templatesOrExtensions = templatesOrExtensions;
1736
+ if (options.verbose) {
1737
+ console.log(JSON.stringify(options, null, 2));
1738
+ }
1739
+ return options;
1740
+ };
1741
+ var processInteractiveOptions = async (options) => {
1742
+ const categories = await getTemplateCategories();
1743
+ const categoryDataPromises = categories.map(async (categorySlug) => {
1744
+ const categoryData = await getCategoryData(categorySlug);
1745
+ return {
1746
+ slug: categorySlug,
1747
+ name: (categoryData == null ? void 0 : categoryData.name) || categorySlug,
1748
+ description: (categoryData == null ? void 0 : categoryData.description) || ""
1749
+ };
1750
+ });
1751
+ const categoryDataList = await Promise.all(categoryDataPromises);
1717
1752
  const categoriesOptions = [
1718
- ...categories.map((category) => ({
1719
- title: category,
1720
- value: category
1753
+ ...categoryDataList.map((category) => ({
1754
+ title: category.name,
1755
+ value: category.slug,
1756
+ description: category.description
1721
1757
  })),
1722
1758
  {
1723
1759
  title: "None of the above",
@@ -1742,6 +1778,30 @@ var getCnaOptions = async (options) => {
1742
1778
  })),
1743
1779
  initial: options.packageManager ? PACKAGE_MANAGERS.indexOf(options.packageManager) : 0
1744
1780
  },
1781
+ {
1782
+ type: "select",
1783
+ name: "aiTool",
1784
+ message: "Which AI coding tool would you like to configure?",
1785
+ choices: [
1786
+ {
1787
+ title: "Cursor Rules",
1788
+ value: "cursor",
1789
+ description: "Add .cursorrules configuration for Cursor IDE"
1790
+ },
1791
+ {
1792
+ title: "GitHub Copilot Instructions",
1793
+ value: "copilot",
1794
+ description: "Add .github/copilot-instructions.md for GitHub Copilot"
1795
+ },
1796
+ {
1797
+ title: "None",
1798
+ value: "none",
1799
+ description: "Don't add any AI tool configuration"
1800
+ }
1801
+ ],
1802
+ initial: 2
1803
+ // Default to "None"
1804
+ },
1745
1805
  {
1746
1806
  type: "select",
1747
1807
  name: "category",
@@ -1811,13 +1871,16 @@ var getCnaOptions = async (options) => {
1811
1871
  (existingTemplate == null ? void 0 : existingTemplate.type) || "custom",
1812
1872
  "all"
1813
1873
  ]);
1814
- for (const [category, extensions] of Object.entries(
1874
+ for (const [categorySlug, extensions] of Object.entries(
1815
1875
  extensionsGroupedByCategory
1816
1876
  )) {
1877
+ const categoryData = await getCategoryData(categorySlug);
1878
+ const categoryName = (categoryData == null ? void 0 : categoryData.name) || categorySlug;
1879
+ const categoryDescription = (categoryData == null ? void 0 : categoryData.description) || "";
1817
1880
  const { selected } = await (0, import_prompts.default)({
1818
1881
  type: "multiselect",
1819
1882
  name: "selected",
1820
- message: `Select extensions for ${category}`,
1883
+ message: `Select extensions for ${categoryName}${categoryDescription ? `: ${categoryDescription}` : ""}`,
1821
1884
  choices: extensions.map((extension) => {
1822
1885
  var _a;
1823
1886
  return {
@@ -1852,6 +1915,8 @@ var getCnaOptions = async (options) => {
1852
1915
  }
1853
1916
  const { ...nextAppOptions } = {
1854
1917
  extend: [],
1918
+ aiTool: "none",
1919
+ // Default value
1855
1920
  ...options,
1856
1921
  ...baseInput,
1857
1922
  ...templateInput,
@@ -1868,11 +1933,19 @@ var getCnaOptions = async (options) => {
1868
1933
  }
1869
1934
  return nextOptions;
1870
1935
  };
1936
+ var getCnaOptions = async (options) => {
1937
+ const shouldUseInteractiveMode = !import_ci_info.isCI && options.interactive;
1938
+ if (shouldUseInteractiveMode) {
1939
+ return processInteractiveOptions(options);
1940
+ } else {
1941
+ return processNonInteractiveOptions(options);
1942
+ }
1943
+ };
1871
1944
 
1872
1945
  // package.json
1873
1946
  var package_default = {
1874
1947
  name: "create-awesome-node-app",
1875
- version: "0.4.26",
1948
+ version: "0.5.0",
1876
1949
  type: "module",
1877
1950
  description: "Command line tool to create Node apps with a lot of different templates and extensions.",
1878
1951
  license: "MIT",
@@ -1937,11 +2010,87 @@ var package_default = {
1937
2010
  }
1938
2011
  };
1939
2012
 
2013
+ // src/list.ts
2014
+ var import_chalk = __toESM(require_source(), 1);
2015
+ var listTemplates = async () => {
2016
+ const categories = await getTemplateCategories();
2017
+ console.log(import_chalk.default.bold.blue("\nAvailable Templates:"));
2018
+ for (const categorySlug of categories) {
2019
+ const categoryData = await getCategoryData(categorySlug);
2020
+ const templates = await getTemplatesForCategory(categorySlug);
2021
+ const categoryName = (categoryData == null ? void 0 : categoryData.name) || categorySlug;
2022
+ console.log(import_chalk.default.bold.green(`
2023
+ ${categoryName}:`));
2024
+ if (categoryData == null ? void 0 : categoryData.description) {
2025
+ console.log(` ${categoryData.description}`);
2026
+ }
2027
+ templates.forEach((template) => {
2028
+ console.log(
2029
+ ` ${import_chalk.default.yellow(template.name)} (${import_chalk.default.cyan(template.slug)})`
2030
+ );
2031
+ console.log(` ${template.description}`);
2032
+ if (template.labels && template.labels.length > 0) {
2033
+ console.log(` Keywords: ${template.labels.join(", ")}`);
2034
+ }
2035
+ });
2036
+ }
2037
+ };
2038
+ var listAddons = async ({
2039
+ templateSlug,
2040
+ templateType
2041
+ }) => {
2042
+ if (templateSlug && !templateType) {
2043
+ templateType = await getTemplateTypeFromSlug(templateSlug);
2044
+ }
2045
+ const types = templateType ? [templateType, "all"] : ["all"];
2046
+ const extensionsGroupedByCategory = await getExtensionsGroupedByCategory(
2047
+ types
2048
+ );
2049
+ console.log(import_chalk.default.bold.blue("\nAvailable Addons:"));
2050
+ if (templateSlug) {
2051
+ console.log(
2052
+ import_chalk.default.bold.green(`
2053
+ Compatible with template: ${templateSlug}`)
2054
+ );
2055
+ }
2056
+ for (const [categorySlug, extensions] of Object.entries(
2057
+ extensionsGroupedByCategory
2058
+ )) {
2059
+ const categoryData = await getCategoryData(categorySlug);
2060
+ const categoryName = (categoryData == null ? void 0 : categoryData.name) || categorySlug;
2061
+ console.log(import_chalk.default.bold.green(`
2062
+ ${categoryName}:`));
2063
+ if (categoryData == null ? void 0 : categoryData.description) {
2064
+ console.log(` ${categoryData.description}`);
2065
+ }
2066
+ extensions.forEach((extension) => {
2067
+ console.log(
2068
+ ` ${import_chalk.default.yellow(extension.name)} (${import_chalk.default.cyan(extension.slug)})`
2069
+ );
2070
+ console.log(` ${extension.description}`);
2071
+ if (extension.labels && extension.labels.length > 0) {
2072
+ console.log(` Keywords: ${extension.labels.join(", ")}`);
2073
+ }
2074
+ });
2075
+ }
2076
+ };
2077
+ var getTemplateTypeFromSlug = async (templateSlug) => {
2078
+ const categories = await getTemplateCategories();
2079
+ for (const category of categories) {
2080
+ const templates = await getTemplatesForCategory(category);
2081
+ const template = templates.find((t) => t.slug === templateSlug);
2082
+ if (template) {
2083
+ return template.type;
2084
+ }
2085
+ }
2086
+ return void 0;
2087
+ };
2088
+
1940
2089
  // src/index.ts
1941
2090
  var program = new import_commander.Command();
1942
2091
  var main = async () => {
1943
2092
  let projectName = "my-project";
1944
- program.version(package_default.version).arguments("[project-directory]").usage(`${import_chalk.default.green("[project-directory]")} [options]`).option("-v, --verbose", "print additional logs").option("-i, --info", "print environment debug info").option(
2093
+ program.version(package_default.version).arguments("[project-directory]").usage(`${import_chalk2.default.green("[project-directory]")} [options]`).option("-v, --verbose", "print additional logs").option("-i, --info", "print environment debug info").option(
1945
2094
  "--no-install",
1946
2095
  "Generate package.json without installing dependencies"
1947
2096
  ).option(
@@ -1950,7 +2099,10 @@ var main = async () => {
1950
2099
  ).option(
1951
2100
  "--addons [extensions...]",
1952
2101
  "specify extensions to apply for the boilerplate generation"
1953
- ).option("--use-yarn", "use yarn instead of npm or pnpm").option("--use-pnpm", "use pnpm instead of yarn or npm").option("--interactive", "run in interactive mode to select options", false).action((providedProjectName) => {
2102
+ ).option("--use-yarn", "use yarn instead of npm or pnpm").option("--use-pnpm", "use pnpm instead of yarn or npm").option(
2103
+ "--ai-tool <tool>",
2104
+ "specify AI tool configuration (cursor, copilot, none)"
2105
+ ).option("--interactive", "run in interactive mode to select options", false).option("--list-templates", "list all available templates").option("--list-addons", "list all available addons").action((providedProjectName) => {
1954
2106
  projectName = providedProjectName || projectName;
1955
2107
  });
1956
2108
  program.parse(process.argv);
@@ -1960,7 +2112,7 @@ var main = async () => {
1960
2112
  if (latest && import_semver.default.lt(package_default.version, latest)) {
1961
2113
  console.log();
1962
2114
  console.error(
1963
- import_chalk.default.yellow(
2115
+ import_chalk2.default.yellow(
1964
2116
  `You are running \`create-awesome-node-app\` ${package_default.version}, which is behind the latest release (${latest}).
1965
2117
 
1966
2118
  We recommend always using the latest version of create-awesome-node-app if possible.`
@@ -1968,10 +2120,19 @@ We recommend always using the latest version of create-awesome-node-app if possi
1968
2120
  );
1969
2121
  return;
1970
2122
  }
1971
- const options = await getCnaOptions({ ...opts, projectName });
1972
- const { useYarn, usePnpm, ...restOptions } = options;
2123
+ if (opts.listTemplates) {
2124
+ await listTemplates();
2125
+ return;
2126
+ }
2127
+ if (opts.listAddons) {
2128
+ await listAddons({
2129
+ templateSlug: opts.template
2130
+ });
2131
+ return;
2132
+ }
2133
+ const { useYarn, usePnpm, ...restOpts } = opts;
1973
2134
  const packageManager = useYarn ? "yarn" : usePnpm ? "pnpm" : "npm";
1974
- const templatesOrExtensions = [restOptions.template].concat(Array.isArray(restOptions.addons) ? restOptions.addons : []).concat(Array.isArray(restOptions.extend) ? restOptions.extend : []).reduce((acc, templateOrExtension) => {
2135
+ const templatesOrExtensions = [restOpts.template].concat(Array.isArray(restOpts.extend) ? restOpts.extend : []).reduce((acc, templateOrExtension) => {
1975
2136
  if (!templateOrExtension) {
1976
2137
  return acc;
1977
2138
  }
@@ -1981,7 +2142,7 @@ We recommend always using the latest version of create-awesome-node-app if possi
1981
2142
  }, []);
1982
2143
  return (0, import_core.createNodeApp)(
1983
2144
  projectName,
1984
- { ...restOptions, packageManager, templatesOrExtensions, projectName },
2145
+ { ...restOpts, packageManager, templatesOrExtensions, projectName },
1985
2146
  getCnaOptions
1986
2147
  );
1987
2148
  };
package/dist/index.js CHANGED
@@ -1348,14 +1348,14 @@ var require_templates = __commonJS({
1348
1348
  }
1349
1349
  return results;
1350
1350
  }
1351
- function buildStyle(chalk2, styles) {
1351
+ function buildStyle(chalk3, styles) {
1352
1352
  const enabled = {};
1353
1353
  for (const layer of styles) {
1354
1354
  for (const style of layer.styles) {
1355
1355
  enabled[style[0]] = layer.inverse ? null : style.slice(1);
1356
1356
  }
1357
1357
  }
1358
- let current = chalk2;
1358
+ let current = chalk3;
1359
1359
  for (const [styleName, styles2] of Object.entries(enabled)) {
1360
1360
  if (!Array.isArray(styles2)) {
1361
1361
  continue;
@@ -1367,7 +1367,7 @@ var require_templates = __commonJS({
1367
1367
  }
1368
1368
  return current;
1369
1369
  }
1370
- module.exports = (chalk2, temporary) => {
1370
+ module.exports = (chalk3, temporary) => {
1371
1371
  const styles = [];
1372
1372
  const chunks = [];
1373
1373
  let chunk = [];
@@ -1377,13 +1377,13 @@ var require_templates = __commonJS({
1377
1377
  } else if (style) {
1378
1378
  const string = chunk.join("");
1379
1379
  chunk = [];
1380
- chunks.push(styles.length === 0 ? string : buildStyle(chalk2, styles)(string));
1380
+ chunks.push(styles.length === 0 ? string : buildStyle(chalk3, styles)(string));
1381
1381
  styles.push({ inverse, styles: parseStyle(style) });
1382
1382
  } else if (close) {
1383
1383
  if (styles.length === 0) {
1384
1384
  throw new Error("Found extraneous } in Chalk template literal");
1385
1385
  }
1386
- chunks.push(buildStyle(chalk2, styles)(chunk.join("")));
1386
+ chunks.push(buildStyle(chalk3, styles)(chunk.join("")));
1387
1387
  chunk = [];
1388
1388
  styles.pop();
1389
1389
  } else {
@@ -1431,16 +1431,16 @@ var require_source = __commonJS({
1431
1431
  }
1432
1432
  };
1433
1433
  var chalkFactory = (options) => {
1434
- const chalk3 = {};
1435
- applyOptions(chalk3, options);
1436
- chalk3.template = (...arguments_) => chalkTag(chalk3.template, ...arguments_);
1437
- Object.setPrototypeOf(chalk3, Chalk.prototype);
1438
- Object.setPrototypeOf(chalk3.template, chalk3);
1439
- chalk3.template.constructor = () => {
1434
+ const chalk4 = {};
1435
+ applyOptions(chalk4, options);
1436
+ chalk4.template = (...arguments_) => chalkTag(chalk4.template, ...arguments_);
1437
+ Object.setPrototypeOf(chalk4, Chalk.prototype);
1438
+ Object.setPrototypeOf(chalk4.template, chalk4);
1439
+ chalk4.template.constructor = () => {
1440
1440
  throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.");
1441
1441
  };
1442
- chalk3.template.Instance = ChalkClass;
1443
- return chalk3.template;
1442
+ chalk4.template.Instance = ChalkClass;
1443
+ return chalk4.template;
1444
1444
  };
1445
1445
  function Chalk(options) {
1446
1446
  return chalkFactory(options);
@@ -1551,7 +1551,7 @@ var require_source = __commonJS({
1551
1551
  return openAll + string + closeAll;
1552
1552
  };
1553
1553
  var template;
1554
- var chalkTag = (chalk3, ...strings) => {
1554
+ var chalkTag = (chalk4, ...strings) => {
1555
1555
  const [firstString] = strings;
1556
1556
  if (!isArray(firstString) || !isArray(firstString.raw)) {
1557
1557
  return strings.join(" ");
@@ -1567,19 +1567,19 @@ var require_source = __commonJS({
1567
1567
  if (template === void 0) {
1568
1568
  template = require_templates();
1569
1569
  }
1570
- return template(chalk3, parts.join(""));
1570
+ return template(chalk4, parts.join(""));
1571
1571
  };
1572
1572
  Object.defineProperties(Chalk.prototype, styles);
1573
- var chalk2 = Chalk();
1574
- chalk2.supportsColor = stdoutColor;
1575
- chalk2.stderr = Chalk({ level: stderrColor ? stderrColor.level : 0 });
1576
- chalk2.stderr.supportsColor = stderrColor;
1577
- module.exports = chalk2;
1573
+ var chalk3 = Chalk();
1574
+ chalk3.supportsColor = stdoutColor;
1575
+ chalk3.stderr = Chalk({ level: stderrColor ? stderrColor.level : 0 });
1576
+ chalk3.stderr.supportsColor = stderrColor;
1577
+ module.exports = chalk3;
1578
1578
  }
1579
1579
  });
1580
1580
 
1581
1581
  // src/index.ts
1582
- var import_chalk = __toESM(require_source(), 1);
1582
+ var import_chalk2 = __toESM(require_source(), 1);
1583
1583
  import { Command } from "commander";
1584
1584
  import semver from "semver";
1585
1585
  import {
@@ -1622,12 +1622,24 @@ var getTemplateCategories = async (cliArgs) => {
1622
1622
  return [cliArgs.category];
1623
1623
  }
1624
1624
  const templateData = await getTemplateData();
1625
+ if (templateData.categories && templateData.categories.length > 0) {
1626
+ return templateData.categories.map((category) => category.slug);
1627
+ }
1625
1628
  const categories = /* @__PURE__ */ new Set();
1626
1629
  templateData.templates.forEach((template) => {
1627
1630
  categories.add(template.category);
1628
1631
  });
1629
1632
  return Array.from(categories);
1630
1633
  };
1634
+ var getCategoryData = async (categorySlug) => {
1635
+ const templateData = await getTemplateData();
1636
+ if (templateData.categories && templateData.categories.length > 0) {
1637
+ return templateData.categories.find(
1638
+ (category) => category.slug === categorySlug
1639
+ );
1640
+ }
1641
+ return void 0;
1642
+ };
1631
1643
  var getTemplatesForCategory = async (category, cliArgs) => {
1632
1644
  const selectedCategory = (cliArgs == null ? void 0 : cliArgs.category) || category;
1633
1645
  if (!selectedCategory) {
@@ -1671,63 +1683,87 @@ var isValidUrl = (url) => {
1671
1683
  return false;
1672
1684
  }
1673
1685
  };
1674
- var getCnaOptions = async (options) => {
1686
+ var processNonInteractiveOptions = async (options) => {
1675
1687
  const categories = await getTemplateCategories();
1676
- if (isCI || !options.interactive) {
1677
- let matchedTemplate;
1678
- if (options.template && !isValidUrl(options.template)) {
1679
- const allTemplates = (await Promise.all(
1680
- categories.map((category) => getTemplatesForCategory(category))
1681
- )).flat();
1682
- matchedTemplate = allTemplates.find(
1683
- (template) => template.slug === options.template
1688
+ let matchedTemplate;
1689
+ const templatesOrExtensions = [];
1690
+ if (options.template && !isValidUrl(options.template)) {
1691
+ const allTemplates = (await Promise.all(
1692
+ categories.map((category) => getTemplatesForCategory(category))
1693
+ )).flat();
1694
+ matchedTemplate = allTemplates.find(
1695
+ (template) => template.slug === options.template
1696
+ );
1697
+ if (matchedTemplate) {
1698
+ templatesOrExtensions.push({ url: matchedTemplate.url });
1699
+ if (matchedTemplate.customOptions) {
1700
+ matchedTemplate.customOptions.forEach((customOption) => {
1701
+ if (customOption.name && customOption.initial !== void 0) {
1702
+ options[customOption.name] = customOption.initial;
1703
+ }
1704
+ });
1705
+ }
1706
+ } else {
1707
+ throw new Error(
1708
+ `Invalid template slug: '${options.template}'. Please provide a valid template slug.`
1684
1709
  );
1685
- if (matchedTemplate) {
1686
- options.template = matchedTemplate.url;
1687
- if (matchedTemplate.customOptions) {
1688
- matchedTemplate.customOptions.forEach((customOption) => {
1689
- if (customOption.name && customOption.initial !== void 0) {
1690
- options[customOption.name] = customOption.initial;
1691
- }
1692
- });
1710
+ }
1711
+ } else if (options.template) {
1712
+ templatesOrExtensions.push({ url: options.template });
1713
+ }
1714
+ if (options.addons && Array.isArray(options.addons)) {
1715
+ const extensionsGroupedByCategory = await getExtensionsGroupedByCategory([
1716
+ (matchedTemplate == null ? void 0 : matchedTemplate.type) || "custom",
1717
+ "all"
1718
+ ]);
1719
+ const extensions = options.addons.map((addon) => {
1720
+ if (!isValidUrl(addon)) {
1721
+ for (const extensions2 of Object.values(extensionsGroupedByCategory)) {
1722
+ const matchedExtension = extensions2.find(
1723
+ (extension) => extension.slug === addon
1724
+ );
1725
+ if (matchedExtension) {
1726
+ return matchedExtension.url;
1727
+ }
1693
1728
  }
1694
- } else {
1695
1729
  throw new Error(
1696
- `Invalid template slug: '${options.template}'. Please provide a valid template slug.`
1730
+ `Invalid extension slug: '${addon}'. Please provide a valid extension slug.`
1697
1731
  );
1698
1732
  }
1699
- }
1700
- if (options.addons && Array.isArray(options.addons)) {
1701
- const extensionsGroupedByCategory2 = await getExtensionsGroupedByCategory([
1702
- (matchedTemplate == null ? void 0 : matchedTemplate.type) || "custom",
1703
- "all"
1704
- ]);
1705
- options.addons = options.addons.map((addon) => {
1706
- if (!isValidUrl(addon)) {
1707
- for (const extensions of Object.values(extensionsGroupedByCategory2)) {
1708
- const matchedExtension = extensions.find(
1709
- (extension) => extension.slug === addon
1710
- );
1711
- if (matchedExtension) {
1712
- return matchedExtension.url;
1713
- }
1714
- }
1715
- throw new Error(
1716
- `Invalid extension slug: '${addon}'. Please provide a valid extension slug.`
1717
- );
1718
- }
1719
- return addon;
1720
- });
1721
- }
1722
- if (options.verbose) {
1723
- console.log(JSON.stringify(options, null, 2));
1724
- }
1725
- return options;
1733
+ return addon;
1734
+ }).map((addon) => ({ url: addon }));
1735
+ templatesOrExtensions.push(...extensions);
1736
+ }
1737
+ if (options.extend && Array.isArray(options.extend)) {
1738
+ const additionalExtensions = options.extend.filter(Boolean).map((extension) => ({ url: extension }));
1739
+ templatesOrExtensions.push(...additionalExtensions);
1740
+ }
1741
+ if (options.aiTool && !["cursor", "copilot", "none"].includes(options.aiTool)) {
1742
+ throw new Error("Invalid --ai-tool option. Use: cursor, copilot, or none");
1726
1743
  }
1744
+ options.aiTool = options.aiTool || "none";
1745
+ options.templatesOrExtensions = templatesOrExtensions;
1746
+ if (options.verbose) {
1747
+ console.log(JSON.stringify(options, null, 2));
1748
+ }
1749
+ return options;
1750
+ };
1751
+ var processInteractiveOptions = async (options) => {
1752
+ const categories = await getTemplateCategories();
1753
+ const categoryDataPromises = categories.map(async (categorySlug) => {
1754
+ const categoryData = await getCategoryData(categorySlug);
1755
+ return {
1756
+ slug: categorySlug,
1757
+ name: (categoryData == null ? void 0 : categoryData.name) || categorySlug,
1758
+ description: (categoryData == null ? void 0 : categoryData.description) || ""
1759
+ };
1760
+ });
1761
+ const categoryDataList = await Promise.all(categoryDataPromises);
1727
1762
  const categoriesOptions = [
1728
- ...categories.map((category) => ({
1729
- title: category,
1730
- value: category
1763
+ ...categoryDataList.map((category) => ({
1764
+ title: category.name,
1765
+ value: category.slug,
1766
+ description: category.description
1731
1767
  })),
1732
1768
  {
1733
1769
  title: "None of the above",
@@ -1752,6 +1788,30 @@ var getCnaOptions = async (options) => {
1752
1788
  })),
1753
1789
  initial: options.packageManager ? PACKAGE_MANAGERS.indexOf(options.packageManager) : 0
1754
1790
  },
1791
+ {
1792
+ type: "select",
1793
+ name: "aiTool",
1794
+ message: "Which AI coding tool would you like to configure?",
1795
+ choices: [
1796
+ {
1797
+ title: "Cursor Rules",
1798
+ value: "cursor",
1799
+ description: "Add .cursorrules configuration for Cursor IDE"
1800
+ },
1801
+ {
1802
+ title: "GitHub Copilot Instructions",
1803
+ value: "copilot",
1804
+ description: "Add .github/copilot-instructions.md for GitHub Copilot"
1805
+ },
1806
+ {
1807
+ title: "None",
1808
+ value: "none",
1809
+ description: "Don't add any AI tool configuration"
1810
+ }
1811
+ ],
1812
+ initial: 2
1813
+ // Default to "None"
1814
+ },
1755
1815
  {
1756
1816
  type: "select",
1757
1817
  name: "category",
@@ -1821,13 +1881,16 @@ var getCnaOptions = async (options) => {
1821
1881
  (existingTemplate == null ? void 0 : existingTemplate.type) || "custom",
1822
1882
  "all"
1823
1883
  ]);
1824
- for (const [category, extensions] of Object.entries(
1884
+ for (const [categorySlug, extensions] of Object.entries(
1825
1885
  extensionsGroupedByCategory
1826
1886
  )) {
1887
+ const categoryData = await getCategoryData(categorySlug);
1888
+ const categoryName = (categoryData == null ? void 0 : categoryData.name) || categorySlug;
1889
+ const categoryDescription = (categoryData == null ? void 0 : categoryData.description) || "";
1827
1890
  const { selected } = await prompts({
1828
1891
  type: "multiselect",
1829
1892
  name: "selected",
1830
- message: `Select extensions for ${category}`,
1893
+ message: `Select extensions for ${categoryName}${categoryDescription ? `: ${categoryDescription}` : ""}`,
1831
1894
  choices: extensions.map((extension) => {
1832
1895
  var _a;
1833
1896
  return {
@@ -1862,6 +1925,8 @@ var getCnaOptions = async (options) => {
1862
1925
  }
1863
1926
  const { ...nextAppOptions } = {
1864
1927
  extend: [],
1928
+ aiTool: "none",
1929
+ // Default value
1865
1930
  ...options,
1866
1931
  ...baseInput,
1867
1932
  ...templateInput,
@@ -1878,11 +1943,19 @@ var getCnaOptions = async (options) => {
1878
1943
  }
1879
1944
  return nextOptions;
1880
1945
  };
1946
+ var getCnaOptions = async (options) => {
1947
+ const shouldUseInteractiveMode = !isCI && options.interactive;
1948
+ if (shouldUseInteractiveMode) {
1949
+ return processInteractiveOptions(options);
1950
+ } else {
1951
+ return processNonInteractiveOptions(options);
1952
+ }
1953
+ };
1881
1954
 
1882
1955
  // package.json
1883
1956
  var package_default = {
1884
1957
  name: "create-awesome-node-app",
1885
- version: "0.4.26",
1958
+ version: "0.5.0",
1886
1959
  type: "module",
1887
1960
  description: "Command line tool to create Node apps with a lot of different templates and extensions.",
1888
1961
  license: "MIT",
@@ -1947,11 +2020,87 @@ var package_default = {
1947
2020
  }
1948
2021
  };
1949
2022
 
2023
+ // src/list.ts
2024
+ var import_chalk = __toESM(require_source(), 1);
2025
+ var listTemplates = async () => {
2026
+ const categories = await getTemplateCategories();
2027
+ console.log(import_chalk.default.bold.blue("\nAvailable Templates:"));
2028
+ for (const categorySlug of categories) {
2029
+ const categoryData = await getCategoryData(categorySlug);
2030
+ const templates = await getTemplatesForCategory(categorySlug);
2031
+ const categoryName = (categoryData == null ? void 0 : categoryData.name) || categorySlug;
2032
+ console.log(import_chalk.default.bold.green(`
2033
+ ${categoryName}:`));
2034
+ if (categoryData == null ? void 0 : categoryData.description) {
2035
+ console.log(` ${categoryData.description}`);
2036
+ }
2037
+ templates.forEach((template) => {
2038
+ console.log(
2039
+ ` ${import_chalk.default.yellow(template.name)} (${import_chalk.default.cyan(template.slug)})`
2040
+ );
2041
+ console.log(` ${template.description}`);
2042
+ if (template.labels && template.labels.length > 0) {
2043
+ console.log(` Keywords: ${template.labels.join(", ")}`);
2044
+ }
2045
+ });
2046
+ }
2047
+ };
2048
+ var listAddons = async ({
2049
+ templateSlug,
2050
+ templateType
2051
+ }) => {
2052
+ if (templateSlug && !templateType) {
2053
+ templateType = await getTemplateTypeFromSlug(templateSlug);
2054
+ }
2055
+ const types = templateType ? [templateType, "all"] : ["all"];
2056
+ const extensionsGroupedByCategory = await getExtensionsGroupedByCategory(
2057
+ types
2058
+ );
2059
+ console.log(import_chalk.default.bold.blue("\nAvailable Addons:"));
2060
+ if (templateSlug) {
2061
+ console.log(
2062
+ import_chalk.default.bold.green(`
2063
+ Compatible with template: ${templateSlug}`)
2064
+ );
2065
+ }
2066
+ for (const [categorySlug, extensions] of Object.entries(
2067
+ extensionsGroupedByCategory
2068
+ )) {
2069
+ const categoryData = await getCategoryData(categorySlug);
2070
+ const categoryName = (categoryData == null ? void 0 : categoryData.name) || categorySlug;
2071
+ console.log(import_chalk.default.bold.green(`
2072
+ ${categoryName}:`));
2073
+ if (categoryData == null ? void 0 : categoryData.description) {
2074
+ console.log(` ${categoryData.description}`);
2075
+ }
2076
+ extensions.forEach((extension) => {
2077
+ console.log(
2078
+ ` ${import_chalk.default.yellow(extension.name)} (${import_chalk.default.cyan(extension.slug)})`
2079
+ );
2080
+ console.log(` ${extension.description}`);
2081
+ if (extension.labels && extension.labels.length > 0) {
2082
+ console.log(` Keywords: ${extension.labels.join(", ")}`);
2083
+ }
2084
+ });
2085
+ }
2086
+ };
2087
+ var getTemplateTypeFromSlug = async (templateSlug) => {
2088
+ const categories = await getTemplateCategories();
2089
+ for (const category of categories) {
2090
+ const templates = await getTemplatesForCategory(category);
2091
+ const template = templates.find((t) => t.slug === templateSlug);
2092
+ if (template) {
2093
+ return template.type;
2094
+ }
2095
+ }
2096
+ return void 0;
2097
+ };
2098
+
1950
2099
  // src/index.ts
1951
2100
  var program = new Command();
1952
2101
  var main = async () => {
1953
2102
  let projectName = "my-project";
1954
- program.version(package_default.version).arguments("[project-directory]").usage(`${import_chalk.default.green("[project-directory]")} [options]`).option("-v, --verbose", "print additional logs").option("-i, --info", "print environment debug info").option(
2103
+ program.version(package_default.version).arguments("[project-directory]").usage(`${import_chalk2.default.green("[project-directory]")} [options]`).option("-v, --verbose", "print additional logs").option("-i, --info", "print environment debug info").option(
1955
2104
  "--no-install",
1956
2105
  "Generate package.json without installing dependencies"
1957
2106
  ).option(
@@ -1960,7 +2109,10 @@ var main = async () => {
1960
2109
  ).option(
1961
2110
  "--addons [extensions...]",
1962
2111
  "specify extensions to apply for the boilerplate generation"
1963
- ).option("--use-yarn", "use yarn instead of npm or pnpm").option("--use-pnpm", "use pnpm instead of yarn or npm").option("--interactive", "run in interactive mode to select options", false).action((providedProjectName) => {
2112
+ ).option("--use-yarn", "use yarn instead of npm or pnpm").option("--use-pnpm", "use pnpm instead of yarn or npm").option(
2113
+ "--ai-tool <tool>",
2114
+ "specify AI tool configuration (cursor, copilot, none)"
2115
+ ).option("--interactive", "run in interactive mode to select options", false).option("--list-templates", "list all available templates").option("--list-addons", "list all available addons").action((providedProjectName) => {
1964
2116
  projectName = providedProjectName || projectName;
1965
2117
  });
1966
2118
  program.parse(process.argv);
@@ -1970,7 +2122,7 @@ var main = async () => {
1970
2122
  if (latest && semver.lt(package_default.version, latest)) {
1971
2123
  console.log();
1972
2124
  console.error(
1973
- import_chalk.default.yellow(
2125
+ import_chalk2.default.yellow(
1974
2126
  `You are running \`create-awesome-node-app\` ${package_default.version}, which is behind the latest release (${latest}).
1975
2127
 
1976
2128
  We recommend always using the latest version of create-awesome-node-app if possible.`
@@ -1978,10 +2130,19 @@ We recommend always using the latest version of create-awesome-node-app if possi
1978
2130
  );
1979
2131
  return;
1980
2132
  }
1981
- const options = await getCnaOptions({ ...opts, projectName });
1982
- const { useYarn, usePnpm, ...restOptions } = options;
2133
+ if (opts.listTemplates) {
2134
+ await listTemplates();
2135
+ return;
2136
+ }
2137
+ if (opts.listAddons) {
2138
+ await listAddons({
2139
+ templateSlug: opts.template
2140
+ });
2141
+ return;
2142
+ }
2143
+ const { useYarn, usePnpm, ...restOpts } = opts;
1983
2144
  const packageManager = useYarn ? "yarn" : usePnpm ? "pnpm" : "npm";
1984
- const templatesOrExtensions = [restOptions.template].concat(Array.isArray(restOptions.addons) ? restOptions.addons : []).concat(Array.isArray(restOptions.extend) ? restOptions.extend : []).reduce((acc, templateOrExtension) => {
2145
+ const templatesOrExtensions = [restOpts.template].concat(Array.isArray(restOpts.extend) ? restOpts.extend : []).reduce((acc, templateOrExtension) => {
1985
2146
  if (!templateOrExtension) {
1986
2147
  return acc;
1987
2148
  }
@@ -1991,7 +2152,7 @@ We recommend always using the latest version of create-awesome-node-app if possi
1991
2152
  }, []);
1992
2153
  return createNodeApp(
1993
2154
  projectName,
1994
- { ...restOptions, packageManager, templatesOrExtensions, projectName },
2155
+ { ...restOpts, packageManager, templatesOrExtensions, projectName },
1995
2156
  getCnaOptions
1996
2157
  );
1997
2158
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-awesome-node-app",
3
- "version": "0.4.26",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Command line tool to create Node apps with a lot of different templates and extensions.",
6
6
  "license": "MIT",