@cms-lab/cli 1.2.3 → 1.2.5

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
@@ -11,6 +11,7 @@ npx @cms-lab/cli scan
11
11
  ```sh
12
12
  cms-lab init
13
13
  cms-lab init --cms strapi --router pages
14
+ cms-lab init --cms directus --router pages
14
15
  cms-lab doctor
15
16
  cms-lab doctor --debug --verbose 2
16
17
  cms-lab scan --report
@@ -38,10 +39,12 @@ cms-lab explain CMS-ROUTE-404
38
39
  cms-lab supports Next.js App Router and Pages Router projects using Prismic,
39
40
  Strapi, Directus, WordPress, Contentful, and Sanity. It validates configured CMS
40
41
  route mappings, route reachability, UID gaps, SEO metadata, image alt text, and
41
- project-specific required fields.
42
+ project-specific required fields and relationship minimums.
42
43
 
43
44
  `cms-lab init --cms strapi --router pages` writes a Strapi starter config with
44
45
  collections, single types, route examples, and the Strapi relation route helper.
46
+ `cms-lab init --cms directus --router pages` writes a Directus starter config
47
+ with branch, menu item, category, and non-routable pricing collection examples.
45
48
  Use `site.healthPath` or `site.healthUrl` in config when the app root is not the
46
49
  right page for the initial health probe.
47
50
 
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "./chunk-M5O5QRR3.js";
4
+ } from "./chunk-4XXHRNEA.js";
5
5
 
6
6
  // src/bin.ts
7
7
  var exitCode = await runCli(process.argv.slice(2));
@@ -100,6 +100,9 @@ function groupForDiagnostic(diagnostic) {
100
100
  if (diagnostic.code.startsWith("CMS-FIELD")) {
101
101
  return "fields";
102
102
  }
103
+ if (diagnostic.code.startsWith("CMS-RELATIONSHIP")) {
104
+ return "relationships";
105
+ }
103
106
  if (diagnostic.code.startsWith("SEO-")) {
104
107
  return "seo";
105
108
  }
@@ -372,7 +375,8 @@ function checksSummary(config) {
372
375
  `- Routes: ${checkState(checks?.routes)}`,
373
376
  `- SEO: ${checkState(checks?.seo)}`,
374
377
  `- Image alt text: ${checkState(checks?.a11y ?? checks?.images)}`,
375
- `- Required fields: ${requiredFieldsSummary(config)}`
378
+ `- Required fields: ${requiredFieldsSummary(config)}`,
379
+ `- Relationship rules: ${relationshipRulesSummary(config)}`
376
380
  ];
377
381
  return lines.join("\n");
378
382
  }
@@ -398,6 +402,15 @@ function requiredFieldsSummary(config) {
398
402
  (field) => `${field.type}.${field.path} (${field.severity ?? "error"})`
399
403
  ).join(", ");
400
404
  }
405
+ function relationshipRulesSummary(config) {
406
+ const relationships = config.checks?.relationships ?? [];
407
+ if (relationships.length === 0) {
408
+ return "default";
409
+ }
410
+ return relationships.map(
411
+ (rule) => `${rule.from} -> ${rule.to} (${rule.where.fromField} = ${rule.where.toField}, min ${rule.min ?? 1}, ${rule.severity ?? "warning"})`
412
+ ).join(", ");
413
+ }
401
414
  function projectLabel(project) {
402
415
  if (project.framework === "next" && project.router === "app") {
403
416
  return "Next.js App Router";
@@ -565,7 +578,7 @@ var noColor = {
565
578
  // src/index.ts
566
579
  async function runCli(argv, dependencies = {}) {
567
580
  let exitCode = 0;
568
- const program = new Command().name("cms-lab").description("Catch CMS bugs before deploy.").version("1.2.3").exitOverride().configureOutput({
581
+ const program = new Command().name("cms-lab").description("Catch CMS bugs before deploy.").version("1.2.5").exitOverride().configureOutput({
569
582
  writeOut: (text) => writeStdout(dependencies, text),
570
583
  writeErr: (text) => writeStderr(dependencies, text)
571
584
  });
@@ -669,19 +682,20 @@ Examples:
669
682
  });
670
683
  program.command("init").description("Create a starter cms-lab.config.ts file.").option("--config <path>", "Config file path", "cms-lab.config.ts").option("--force", "Overwrite an existing config file").option(
671
684
  "--cms <provider>",
672
- "Starter CMS provider: prismic or strapi",
685
+ "Starter CMS provider: prismic, strapi, or directus",
673
686
  "prismic"
674
687
  ).option("--router <router>", "Next.js router: app or pages", "app").option("--repository <name>", "Prismic repository name", "my-repo").option("--url <url>", "Site URL", "http://localhost:3000").option(
675
688
  "--strapi-url <url>",
676
689
  "Strapi REST API URL",
677
690
  "http://localhost:1337"
678
- ).option("--strapi-locale <locale>", "Strapi locale query param").addHelpText(
691
+ ).option("--strapi-locale <locale>", "Strapi locale query param").option("--directus-url <url>", "Directus API URL", "http://localhost:8055").addHelpText(
679
692
  "after",
680
693
  `
681
694
  Examples:
682
695
  cms-lab init
683
696
  cms-lab init --repository my-prismic-repo --url http://localhost:3000
684
697
  cms-lab init --cms strapi --router pages --strapi-url http://localhost:1337
698
+ cms-lab init --cms directus --router pages --directus-url http://localhost:8055
685
699
  cms-lab init --config cms-lab.config.ts --force
686
700
  `
687
701
  ).action(async (options) => {
@@ -1403,7 +1417,14 @@ function assertTypeFilterMatched(documents, types) {
1403
1417
  );
1404
1418
  }
1405
1419
  function parseCheckGroups(values) {
1406
- const allowed = /* @__PURE__ */ new Set(["routes", "seo", "a11y", "images", "fields"]);
1420
+ const allowed = /* @__PURE__ */ new Set([
1421
+ "routes",
1422
+ "seo",
1423
+ "a11y",
1424
+ "images",
1425
+ "fields",
1426
+ "relationships"
1427
+ ]);
1407
1428
  const groups = splitList(values);
1408
1429
  for (const group of groups) {
1409
1430
  if (!allowed.has(group)) {
@@ -1621,8 +1642,10 @@ function plural3(value, singular) {
1621
1642
  }
1622
1643
  function parseInitOptions(options) {
1623
1644
  const cms = options.cms ?? "prismic";
1624
- if (cms !== "prismic" && cms !== "strapi") {
1625
- throw new ConfigLoadError("--cms must be one of: prismic, strapi");
1645
+ if (cms !== "prismic" && cms !== "strapi" && cms !== "directus") {
1646
+ throw new ConfigLoadError(
1647
+ "--cms must be one of: prismic, strapi, directus"
1648
+ );
1626
1649
  }
1627
1650
  const router = options.router ?? "app";
1628
1651
  if (router !== "app" && router !== "pages") {
@@ -1634,13 +1657,17 @@ function parseInitOptions(options) {
1634
1657
  repository: options.repository ?? "my-repo",
1635
1658
  url: options.url ?? "http://localhost:3000",
1636
1659
  strapiUrl: options.strapiUrl ?? "http://localhost:1337",
1637
- strapiLocale: options.strapiLocale
1660
+ strapiLocale: options.strapiLocale,
1661
+ directusUrl: options.directusUrl ?? "http://localhost:8055"
1638
1662
  };
1639
1663
  }
1640
1664
  function starterConfig(options) {
1641
1665
  if (options.cms === "strapi") {
1642
1666
  return strapiStarterConfig(options);
1643
1667
  }
1668
+ if (options.cms === "directus") {
1669
+ return directusStarterConfig(options);
1670
+ }
1644
1671
  return `import { defineConfig } from "@cms-lab/core";
1645
1672
 
1646
1673
  export default defineConfig({
@@ -1662,6 +1689,80 @@ export default defineConfig({
1662
1689
  });
1663
1690
  `;
1664
1691
  }
1692
+ function directusStarterConfig(options) {
1693
+ return `import { defineConfig, readCmsDataPath } from "@cms-lab/core";
1694
+
1695
+ export default defineConfig({
1696
+ site: {
1697
+ url: ${JSON.stringify(options.url)},
1698
+ // Use healthPath when your app's root redirects or errors but a locale route is healthy.
1699
+ // healthPath: "/en",
1700
+ },
1701
+ framework: { type: "next", router: ${JSON.stringify(options.router)} },
1702
+ cms: {
1703
+ provider: "directus",
1704
+ url: ${JSON.stringify(options.directusUrl)},
1705
+ token: process.env.DIRECTUS_TOKEN,
1706
+ collections: [
1707
+ { type: "branch", collection: "branches", uidField: "slug" },
1708
+ { type: "menu_item", collection: "menu_items", uidField: "slug" },
1709
+ { type: "category", collection: "menu_categories", uidField: "slug" },
1710
+ {
1711
+ type: "pricing",
1712
+ collection: "item_branch_pricing",
1713
+ uidField: "id",
1714
+ routable: false,
1715
+ },
1716
+ ],
1717
+ },
1718
+ routes: [
1719
+ {
1720
+ type: "branch",
1721
+ pattern: "/branches/:slug",
1722
+ getPath: (doc) => \`/branches/\${doc.uid}\`,
1723
+ },
1724
+ {
1725
+ type: "category",
1726
+ pattern: "/categories/:slug",
1727
+ getPath: (doc) => \`/categories/\${doc.uid}\`,
1728
+ },
1729
+ {
1730
+ type: "menu_item",
1731
+ pattern: "/menu/:branch/:slug",
1732
+ getPath: (doc) => {
1733
+ const branch =
1734
+ readCmsDataPath(doc.data, "branch.slug") ??
1735
+ readCmsDataPath(doc.data, "branch_id.slug") ??
1736
+ "branch";
1737
+
1738
+ return \`/menu/\${branch}/\${doc.uid}\`;
1739
+ },
1740
+ },
1741
+ ],
1742
+ checks: {
1743
+ fields: {
1744
+ required: [
1745
+ { type: "branch", path: "name" },
1746
+ { type: "branch", path: "city" },
1747
+ { type: "menu_item", path: "name" },
1748
+ { type: "menu_item", path: "base_price", severity: "warning" },
1749
+ { type: "pricing", path: "price", severity: "warning" },
1750
+ { type: "pricing", path: "is_available", severity: "warning" },
1751
+ ],
1752
+ },
1753
+ relationships: [
1754
+ {
1755
+ from: "menu_item",
1756
+ to: "pricing",
1757
+ where: { fromField: "id", toField: "menu_item_id" },
1758
+ min: 1,
1759
+ severity: "warning",
1760
+ },
1761
+ ],
1762
+ },
1763
+ });
1764
+ `;
1765
+ }
1665
1766
  function strapiStarterConfig(options) {
1666
1767
  const localeLine = options.strapiLocale ? `
1667
1768
  locale: ${JSON.stringify(options.strapiLocale)},` : "";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runCli
3
- } from "./chunk-M5O5QRR3.js";
3
+ } from "./chunk-4XXHRNEA.js";
4
4
  export {
5
5
  runCli
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cms-lab/cli",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "type": "module",
5
5
  "description": "Catch CMS bugs before deploy.",
6
6
  "license": "MIT",
@@ -47,15 +47,15 @@
47
47
  "dependencies": {
48
48
  "commander": "^14.0.2",
49
49
  "picocolors": "^1.1.1",
50
- "@cms-lab/contentful": "1.2.3",
51
- "@cms-lab/core": "1.2.3",
52
- "@cms-lab/prismic": "1.2.3",
53
- "@cms-lab/reporter": "1.2.3",
54
- "@cms-lab/sanity": "1.2.3",
55
- "@cms-lab/strapi": "1.2.3",
56
- "@cms-lab/next": "1.2.3",
57
- "@cms-lab/wordpress": "1.2.3",
58
- "@cms-lab/directus": "1.2.3"
50
+ "@cms-lab/contentful": "1.2.5",
51
+ "@cms-lab/core": "1.2.5",
52
+ "@cms-lab/directus": "1.2.5",
53
+ "@cms-lab/next": "1.2.5",
54
+ "@cms-lab/prismic": "1.2.5",
55
+ "@cms-lab/reporter": "1.2.5",
56
+ "@cms-lab/sanity": "1.2.5",
57
+ "@cms-lab/strapi": "1.2.5",
58
+ "@cms-lab/wordpress": "1.2.5"
59
59
  },
60
60
  "author": "Afaq Rashid",
61
61
  "scripts": {