Просмотр исходного кода

feat(docs): Group core plugin docs

Michael Bromley 2 лет назад
Родитель
Сommit
28324c7657
64 измененных файлов с 394 добавлено и 298 удалено
  1. 39 11
      docs/assets/styles/_markdown.scss
  2. 2 2
      docs/assets/styles/_menu.scss
  3. 1 1
      docs/assets/styles/_search-widget.scss
  4. 2 2
      docs/content/deployment/production-configuration/index.md
  5. 1 1
      docs/content/developer-guide/customizing-models.md
  6. 7 7
      docs/content/developer-guide/customizing-the-order-process/index.md
  7. 6 6
      docs/content/developer-guide/payment-integrations/index.md
  8. 1 1
      docs/content/developer-guide/shipping.md
  9. 5 0
      docs/content/plugins/_index.md
  10. 2 4
      docs/content/storefront/order-workflow/_index.md
  11. BIN
      docs/content/storefront/order-workflow/order_class_diagram.png
  12. 3 3
      docs/data/build.json
  13. 0 6
      docs/diagrams/order-class-diagram.puml
  14. 1 1
      docs/layouts/_default/baseof.html
  15. 5 0
      docs/layouts/partials/menu-filetree.html
  16. 1 1
      docs/layouts/shortcodes/shop-api-operation.html
  17. 2 2
      packages/admin-ui-plugin/src/plugin.ts
  18. 1 1
      packages/asset-server-plugin/src/hashed-asset-naming-strategy.ts
  19. 1 1
      packages/asset-server-plugin/src/local-asset-storage-strategy.ts
  20. 1 1
      packages/asset-server-plugin/src/plugin.ts
  21. 53 23
      packages/asset-server-plugin/src/s3-asset-storage-strategy.ts
  22. 2 2
      packages/asset-server-plugin/src/sharp-asset-preview-strategy.ts
  23. 4 4
      packages/asset-server-plugin/src/types.ts
  24. 3 3
      packages/common/src/shared-types.ts
  25. 1 1
      packages/core/src/api/decorators/relations.decorator.ts
  26. 1 1
      packages/core/src/bootstrap.ts
  27. 1 1
      packages/core/src/config/catalog/default-product-variant-price-calculation-strategy.ts
  28. 1 1
      packages/core/src/config/catalog/default-stock-display-strategy.ts
  29. 1 1
      packages/core/src/config/catalog/default-stock-location-strategy.ts
  30. 2 2
      packages/core/src/config/catalog/product-variant-price-calculation-strategy.ts
  31. 1 1
      packages/core/src/config/catalog/stock-display-strategy.ts
  32. 4 3
      packages/core/src/config/catalog/stock-location-strategy.ts
  33. 1 1
      packages/core/src/config/vendure-config.ts
  34. 6 2
      packages/core/src/i18n/i18n.service.ts
  35. 1 1
      packages/core/src/plugin/default-search-plugin/default-search-plugin.ts
  36. 1 1
      packages/core/src/plugin/default-search-plugin/types.ts
  37. 1 1
      packages/core/src/service/services/order.service.ts
  38. 85 68
      packages/dev-server/dev-config.ts
  39. 3 3
      packages/elasticsearch-plugin/src/options.ts
  40. 1 1
      packages/elasticsearch-plugin/src/plugin.ts
  41. 1 1
      packages/email-plugin/src/email-generator.ts
  42. 1 1
      packages/email-plugin/src/email-sender.ts
  43. 7 7
      packages/email-plugin/src/event-handler.ts
  44. 1 1
      packages/email-plugin/src/event-listener.ts
  45. 1 1
      packages/email-plugin/src/handlebars-mjml-generator.ts
  46. 3 3
      packages/email-plugin/src/nodemailer-email-sender.ts
  47. 1 1
      packages/email-plugin/src/plugin.ts
  48. 38 33
      packages/email-plugin/src/types.ts
  49. 1 1
      packages/harden-plugin/src/harden.plugin.ts
  50. 1 1
      packages/harden-plugin/src/middleware/query-complexity-plugin.ts
  51. 1 1
      packages/harden-plugin/src/types.ts
  52. 1 1
      packages/job-queue-plugin/src/bullmq/bullmq-job-queue-strategy.ts
  53. 1 1
      packages/job-queue-plugin/src/bullmq/plugin.ts
  54. 2 2
      packages/job-queue-plugin/src/bullmq/types.ts
  55. 1 1
      packages/payments-plugin/src/braintree/braintree.plugin.ts
  56. 2 2
      packages/payments-plugin/src/braintree/types.ts
  57. 8 8
      packages/payments-plugin/src/mollie/mollie.plugin.ts
  58. 1 1
      packages/payments-plugin/src/stripe/stripe.plugin.ts
  59. 1 1
      packages/payments-plugin/src/stripe/types.ts
  60. 9 9
      scripts/docs/generate-graphql-docs.ts
  61. 1 1
      scripts/docs/generate-typescript-docs.ts
  62. 1 1
      scripts/docs/typescript-docgen-types.ts
  63. 9 5
      scripts/docs/typescript-docs-parser.ts
  64. 47 42
      scripts/docs/typescript-docs-renderer.ts

+ 39 - 11
docs/assets/styles/_markdown.scss

@@ -22,8 +22,8 @@ $block-border-radius: 4px;
     }
     }
 
 
     h1 {
     h1 {
-       font-size: 1.875rem;
-       line-height: 2.25rem;
+        font-size: 1.875rem;
+        line-height: 2.25rem;
     }
     }
 
 
     h1:first-of-type {
     h1:first-of-type {
@@ -32,11 +32,25 @@ $block-border-radius: 4px;
         margin-bottom: 0;
         margin-bottom: 0;
         font-size: 2.8em;
         font-size: 2.8em;
 
 
-        @media all and (max-width: $sm-breakpoint){
+        @media all and (max-width: $sm-breakpoint) {
             font-size: 2em;
             font-size: 2em;
         }
         }
     }
     }
 
 
+    .symbol {
+        border: 1px solid #ddd;
+        border-radius: 4px;
+        padding: 24px;
+        margin: 24px 0 48px 0;
+        background-color: #fafafa;
+
+        > h1:first-of-type {
+            text-transform: none;
+            font-size: 1.875rem;
+            line-height: 2.25rem;
+        }
+    }
+
     h1:not(:first-of-type) {
     h1:not(:first-of-type) {
         margin-top: 48px;
         margin-top: 48px;
         padding: 3px 12px;
         padding: 3px 12px;
@@ -66,7 +80,11 @@ $block-border-radius: 4px;
         margin-top: 32px;
         margin-top: 32px;
     }
     }
 
 
-    h1, h2, h3, h4, h5 {
+    h1,
+    h2,
+    h3,
+    h4,
+    h5 {
         font-family: $brand-font-face;
         font-family: $brand-font-face;
         letter-spacing: -0.025em;
         letter-spacing: -0.025em;
         line-height: 1.25;
         line-height: 1.25;
@@ -81,7 +99,9 @@ $block-border-radius: 4px;
         margin: 16px 0;
         margin: 16px 0;
     }
     }
 
 
-    b, optgroup, strong {
+    b,
+    optgroup,
+    strong {
         font-weight: 700;
         font-weight: 700;
     }
     }
 
 
@@ -89,7 +109,8 @@ $block-border-radius: 4px;
         text-decoration: none;
         text-decoration: none;
         color: $color-link;
         color: $color-link;
 
 
-        &:hover, &:visited:hover {
+        &:hover,
+        &:visited:hover {
             color: lighten($color-link, 10%);
             color: lighten($color-link, 10%);
         }
         }
 
 
@@ -98,7 +119,8 @@ $block-border-radius: 4px;
         }
         }
     }
     }
 
 
-    ul, ol:not(.breadcrumbs) {
+    ul,
+    ol:not(.breadcrumbs) {
         list-style-type: initial;
         list-style-type: initial;
         padding-left: 40px;
         padding-left: 40px;
     }
     }
@@ -151,8 +173,12 @@ $block-border-radius: 4px;
         color: $gray-700;
         color: $gray-700;
         border-left: 2px solid $gray-300;
         border-left: 2px solid $gray-300;
 
 
-        :first-child { margin-top: 0; }
-        :last-child { margin-bottom: 0; }
+        :first-child {
+            margin-top: 0;
+        }
+        :last-child {
+            margin-bottom: 0;
+        }
 
 
         &::before {
         &::before {
             content: '“';
             content: '“';
@@ -170,7 +196,8 @@ $block-border-radius: 4px;
         th {
         th {
             text-align: left;
             text-align: left;
         }
         }
-        td, th {
+        td,
+        th {
             padding: $padding-4;
             padding: $padding-4;
         }
         }
         tr:nth-child(odd) td {
         tr:nth-child(odd) td {
@@ -193,7 +220,8 @@ $block-border-radius: 4px;
                 box-shadow: none;
                 box-shadow: none;
             }
             }
         }
         }
-        figcaption p, figcaption h4 {
+        figcaption p,
+        figcaption h4 {
             text-align: center;
             text-align: center;
             font-size: $font-size-base;
             font-size: $font-size-base;
             margin-top: $padding-4;
             margin-top: $padding-4;

+ 2 - 2
docs/assets/styles/_menu.scss

@@ -27,7 +27,7 @@ nav.book-menu {
     }
     }
 
 
     ul:not(.expanded) > .section {
     ul:not(.expanded) > .section {
-        &:nth-of-type(7) {
+        &:nth-of-type(6) {
             &::before {
             &::before {
                 position: absolute;
                 position: absolute;
                 top: 0;
                 top: 0;
@@ -40,7 +40,7 @@ nav.book-menu {
             padding-top: 1em;
             padding-top: 1em;
         }
         }
 
 
-        &:nth-of-type(n + 7) {
+        &:nth-of-type(n + 6) {
             color: $gray-700;
             color: $gray-700;
         }
         }
     }
     }

+ 1 - 1
docs/assets/styles/_search-widget.scss

@@ -66,7 +66,7 @@
         }
         }
         .title {
         .title {
             color: $gray-800;
             color: $gray-800;
-            width: 200px;
+            width: 300px;
             font-size: 12px;
             font-size: 12px;
             margin-right: 6px;
             margin-right: 6px;
         }
         }

+ 2 - 2
docs/content/deployment/production-configuration/index.md

@@ -45,7 +45,7 @@ export const config: VendureConfig = {
 
 
 ## API hardening
 ## API hardening
 
 
-It is recommended that you install and configure the [HardenPlugin]({{< relref "harden-plugin" >}}) for all production deployments. This plugin locks down your schema (disabling introspection and field suggestions) and protects your Shop API against malicious queries that could otherwise overwhelm your server.
+It is recommended that you install and configure the [HardenPlugin]({{< relref "typescript-api/core-plugins/harden-plugin" >}}) for all production deployments. This plugin locks down your schema (disabling introspection and field suggestions) and protects your Shop API against malicious queries that could otherwise overwhelm your server.
 
 
 Install the plugin: 
 Install the plugin: 
 
 
@@ -78,7 +78,7 @@ export const config: VendureConfig = {
 ```
 ```
 
 
 {{< alert primary >}}
 {{< alert primary >}}
-For a detailed explanation of how to best configure this plugin, see the [HardenPlugin docs]({{< relref "harden-plugin" >}}).
+For a detailed explanation of how to best configure this plugin, see the [HardenPlugin docs]({{< relref "typescript-api/core-plugins/harden-plugin" >}}).
 {{< /alert >}}
 {{< /alert >}}
 
 
 ## ID Strategy
 ## ID Strategy

+ 1 - 1
docs/content/developer-guide/customizing-models.md

@@ -29,7 +29,7 @@ With the example config above, the following will occur:
 
 
 1. The database schema will be altered, and a column will be added for each custom field. **Note: changes to custom fields require a database migration**. See the [Migrations guide]({{< relref "migrations" >}}).
 1. The database schema will be altered, and a column will be added for each custom field. **Note: changes to custom fields require a database migration**. See the [Migrations guide]({{< relref "migrations" >}}).
 2. The GraphQL APIs will be modified to add the custom fields to the `Product` and `User` types respectively.
 2. The GraphQL APIs will be modified to add the custom fields to the `Product` and `User` types respectively.
-3. If you are using the [admin-ui-plugin]({{< relref "/typescript-api/admin-ui-plugin" >}}), the Admin UI detail pages will now contain form inputs to allow the custom field data to be added or edited.
+3. If you are using the [admin-ui-plugin]({{< relref "/typescript-api/core-plugins/admin-ui-plugin" >}}), the Admin UI detail pages will now contain form inputs to allow the custom field data to be added or edited.
 
 
 The values of the custom fields can then be set and queried via the GraphQL APIs:
 The values of the custom fields can then be set and queried via the GraphQL APIs:
 
 

+ 7 - 7
docs/content/developer-guide/customizing-the-order-process/index.md

@@ -5,13 +5,13 @@ showtoc: true
 
 
 # Customizing the Order Process
 # Customizing the Order Process
 
 
-Vendure defines an order process which is based on a [finite state machine]({{< relref "fsm" >}}). This means that the [`Order.state` property]({{< relref "order" >}}#state) will be one of a set of [pre-defined states]({{< relref "order-state" >}}). From the current state, the Order can then transition (change) to another state, and the available next states depend on what the current state is.
+Vendure defines an order process which is based on a [finite state machine]({{< relref "fsm" >}}). This means that the [`Order.state` property]({{< relref "order" >}}#state) will be one of a set of [pre-defined states]({{< relref "order-process" >}}#orderstate). From the current state, the Order can then transition (change) to another state, and the available next states depend on what the current state is.
 
 
 So, as an example, all orders begin in the `AddingItems` state. This means that the Customer is adding items to his or her shopping cart. From there, the Order can transition to the `ArrangingPayment` state. A diagram of the default states and transitions can be found in the [Order Workflow guide]({{< relref "order-workflow" >}}).
 So, as an example, all orders begin in the `AddingItems` state. This means that the Customer is adding items to his or her shopping cart. From there, the Order can transition to the `ArrangingPayment` state. A diagram of the default states and transitions can be found in the [Order Workflow guide]({{< relref "order-workflow" >}}).
 
 
 ## Defining custom states and transitions
 ## Defining custom states and transitions
 
 
-Sometimes you might need to modify the default Order process to better match your business needs. This is done by defining one or more [`CustomOrderProcess`]({{< relref "custom-order-process" >}}) objects and passing them to the [`OrderOptions.process`]({{< relref "order-options" >}}#process) config property.
+Sometimes you might need to modify the default Order process to better match your business needs. This is done by defining one or more [`OrderProcess`]({{< relref "order-process" >}}) objects and passing them to the [`OrderOptions.process`]({{< relref "order-options" >}}#process) config property.
 
 
 ### Example: Adding a new state
 ### Example: Adding a new state
 
 
@@ -35,9 +35,9 @@ Here's how we would define the new state:
 
 
 ```TypeScript
 ```TypeScript
 // customer-validation-process.ts
 // customer-validation-process.ts
-import { CustomOrderProcess } from '@vendure/core';
+import { OrderProcess } from '@vendure/core';
 
 
-export const customerValidationProcess: CustomOrderProcess<'ValidatingCustomer'> = {
+export const customerValidationProcess: OrderProcess<'ValidatingCustomer'> = {
   transitions: {
   transitions: {
     AddingItems: {
     AddingItems: {
       to: ['ValidatingCustomer'],
       to: ['ValidatingCustomer'],
@@ -72,9 +72,9 @@ export const config: VendureConfig = {
 
 
  To add multiple new States you need to extend the generic type like this:
  To add multiple new States you need to extend the generic type like this:
  ```TypeScript
  ```TypeScript
-import { CustomOrderProcess } from '@vendure/core';
+import { OrderProcess } from '@vendure/core';
 
 
-export const customerValidationProcess: CustomOrderProcess<'ValidatingCustomer'|'AnotherState'> = {...}
+export const customerValidationProcess: OrderProcess<'ValidatingCustomer'|'AnotherState'> = {...}
  ```
  ```
 This way multiple custom states gets defined.
 This way multiple custom states gets defined.
 
 
@@ -91,7 +91,7 @@ This allows us to perform our custom logic and potentially prevent the transitio
 // in our onTransitionStart function.
 // in our onTransitionStart function.
 let taxIdService: TaxIdService;
 let taxIdService: TaxIdService;
 
 
-const customerValidationProcess: CustomOrderProcess<'ValidatingCustomer'> = {
+const customerValidationProcess: OrderProcess<'ValidatingCustomer'> = {
   transitions: {
   transitions: {
     AddingItems: {
     AddingItems: {
       to: ['ValidatingCustomer'],
       to: ['ValidatingCustomer'],

+ 6 - 6
docs/content/developer-guide/payment-integrations/index.md

@@ -146,9 +146,9 @@ If the `createPayment()` function returns a result with the state set to `'Autho
 
 
 ## Custom Payment Flows
 ## Custom Payment Flows
 
 
-If you need to support an entirely different payment flow than the above, it is also possible to do so by configuring a [CustomPaymentProcess]({{< relref "custom-payment-process" >}}). This allows new Payment states and transitions to be defined, as well as allowing custom logic to run on Payment state transitions.
+If you need to support an entirely different payment flow than the above, it is also possible to do so by configuring a [PaymentProcess]({{< relref "payment-process" >}}). This allows new Payment states and transitions to be defined, as well as allowing custom logic to run on Payment state transitions.
 
 
-Here's an example which adds a new "Validating" state to the Payment state machine, and combines it with a [CustomOrderProcess]({{< relref "custom-order-process" >}}), [PaymentMethodHandler]({{< relref "payment-method-handler" >}}) and [OrderPlacedStrategy]({{< relref "order-placed-strategy" >}}).
+Here's an example which adds a new "Validating" state to the Payment state machine, and combines it with a [OrderProcess]({{< relref "order-process" >}}), [PaymentMethodHandler]({{< relref "payment-method-handler" >}}) and [OrderPlacedStrategy]({{< relref "order-placed-strategy" >}}).
 
 
 ```TypeScript
 ```TypeScript
 // types.ts
 // types.ts
@@ -158,7 +158,7 @@ import { CustomOrderStates } from '@vendure/core';
  * Declare your custom state in special interface to make it type-safe
  * Declare your custom state in special interface to make it type-safe
  */
  */
 declare module '@vendure/core' {
 declare module '@vendure/core' {
-  interface CustomPaymentStates {
+  interface PaymentStates {
     Validating: never;
     Validating: never;
   }
   }
 }
 }
@@ -167,7 +167,7 @@ declare module '@vendure/core' {
  * Define a new "Validating" Payment state, and set up the
  * Define a new "Validating" Payment state, and set up the
  * permitted transitions to/from it.
  * permitted transitions to/from it.
  */
  */
-const customPaymentProcess: CustomPaymentProcess<'Validating'> = {
+const customPaymentProcess: PaymentProcess<'Validating'> = {
   transitions: {
   transitions: {
     Created: {
     Created: {
       to: ['Validating'],
       to: ['Validating'],
@@ -183,7 +183,7 @@ const customPaymentProcess: CustomPaymentProcess<'Validating'> = {
  * Define a new "ValidatingPayment" Order state, and set up the
  * Define a new "ValidatingPayment" Order state, and set up the
  * permitted transitions to/from it.
  * permitted transitions to/from it.
  */
  */
-const customOrderProcess: CustomOrderProcess<'ValidatingPayment'> = {
+const customOrderProcess: OrderProcess<'ValidatingPayment'> = {
   transitions: {
   transitions: {
     ArrangingPayment: {
     ArrangingPayment: {
       to: ['ValidatingPayment'],
       to: ['ValidatingPayment'],
@@ -236,7 +236,7 @@ export const config: VendureConfig = {
     orderPlacedStrategy: new MyOrderPlacedStrategy(),
     orderPlacedStrategy: new MyOrderPlacedStrategy(),
   },
   },
   paymentOptions: {
   paymentOptions: {
-    customPaymentProcess: [customPaymentProcess],
+    process: [customPaymentProcess],
     paymentMethodHandlers: [myPaymentHandler],
     paymentMethodHandlers: [myPaymentHandler],
   },
   },
 };
 };

+ 1 - 1
docs/content/developer-guide/shipping.md

@@ -151,7 +151,7 @@ Like Orders, Fulfillments are governed by a [finite state machine]({{< relref "f
 * `Delivered` The Fulfillment has arrived with the customer
 * `Delivered` The Fulfillment has arrived with the customer
 * `Cancelled` The Fulfillment has been cancelled 
 * `Cancelled` The Fulfillment has been cancelled 
 
 
-These states cover the typical workflow for fulfilling orders. However, it is possible to customize the fulfillment workflow by defining a [CustomFulfillmentProcess]({{< relref "custom-fulfillment-process" >}}) and passing it to your VendureConfig:
+These states cover the typical workflow for fulfilling orders. However, it is possible to customize the fulfillment workflow by defining a [FulfillmentProcess]({{< relref "fulfillment-process" >}}) and passing it to your VendureConfig:
 
 
 ```TypeScript
 ```TypeScript
 export const config: VendureConfig = {
 export const config: VendureConfig = {

+ 5 - 0
docs/content/plugins/_index.md

@@ -19,3 +19,8 @@ Plugins are the method by which the built-in functionality of Vendure can be ext
 These abilities make plugins a very versatile and powerful means of implementing custom business requirements.
 These abilities make plugins a very versatile and powerful means of implementing custom business requirements.
 
 
 This section details the official Vendure plugins included in the main Vendure repo, as well as a guide on writing your own plugins for Vendure.
 This section details the official Vendure plugins included in the main Vendure repo, as well as a guide on writing your own plugins for Vendure.
+
+{{< alert "primary" >}}
+  Vendure provides a set of **core plugins** covering common functionality such as assets handling, email sending, and search. For 
+documentation on these, see the [Core Plugins section]({{< relref "core-plugins" >}}).
+{{< /alert >}}

+ 2 - 4
docs/content/storefront/order-workflow/_index.md

@@ -9,7 +9,7 @@ An Order is a collection of one or more ProductVariants which can be purchased b
 
 
 ## Order State
 ## Order State
 
 
-Every Order has a `state` property of type [`OrderState`]({{< relref "order-state" >}}). The following diagram shows the default states and how an Order transitions from one to the next.
+Every Order has a `state` property of type [`OrderState`]({{< relref "order-process" >}}#orderstate). The following diagram shows the default states and how an Order transitions from one to the next.
 
 
 {{% alert %}}
 {{% alert %}}
 Note that this default workflow can be modified to better fit your business processes. See the [Customizing the Order Process guide]({{< relref "customizing-the-order-process" >}}).
 Note that this default workflow can be modified to better fit your business processes. See the [Customizing the Order Process guide]({{< relref "customizing-the-order-process" >}}).
@@ -19,14 +19,12 @@ Note that this default workflow can be modified to better fit your business proc
 
 
 ## Structure of an Order
 ## Structure of an Order
 
 
-In Vendure an [Order]({{< relref "order" >}}) consists of one or more [OrderLines]({{< relref "order-line" >}}) (representing a given quantity of a particular SKU), which in turn contain one or more [OrderItems]({{< relref "order-item" >}}) (which represent the individual physical units). 
+In Vendure an [Order]({{< relref "order" >}}) consists of one or more [OrderLines]({{< relref "order-line" >}}) (representing a given quantity of a particular SKU).
 
 
 Here is a simplified diagram illustrating this relationship:
 Here is a simplified diagram illustrating this relationship:
 
 
 {{< figure src="./order_class_diagram.png" >}}
 {{< figure src="./order_class_diagram.png" >}}
 
 
-So if the customer adds 2 *Widgets* to the Order, there will be **one OrderLine** containing **two OrderItems**, one for each of the added *Widgets*.
-
 ## Shop client order workflow
 ## Shop client order workflow
 
 
 The [GraphQL Shop API Guide]({{< relref "/storefront/shop-api-guide" >}}#order-flow) lists the GraphQL operations you will need to implement this workflow in your storefront client application.
 The [GraphQL Shop API Guide]({{< relref "/storefront/shop-api-guide" >}}#order-flow) lists the GraphQL operations you will need to implement this workflow in your storefront client application.

BIN
docs/content/storefront/order-workflow/order_class_diagram.png


+ 3 - 3
docs/data/build.json

@@ -1,4 +1,4 @@
 {
 {
-  "version": "1.5.2",
-  "commit": "c4811d42e"
-}
+  "version": "2.0.0-beta.3",
+  "commit": "90a914bf5"
+}

+ 0 - 6
docs/diagrams/order-class-diagram.puml

@@ -26,13 +26,7 @@ class OrderLine {
     items: OrderItem[]
     items: OrderItem[]
 }
 }
 
 
-class OrderItem {
-    unitPrice: number
-    taxRate: number
-}
-
 Order -- OrderState
 Order -- OrderState
 Order *-- "0..*" OrderLine
 Order *-- "0..*" OrderLine
-OrderLine *-- "0..*" OrderItem
 
 
 @enduml
 @enduml

+ 1 - 1
docs/layouts/_default/baseof.html

@@ -87,7 +87,7 @@
                  x-transition:leave-end="opacity-0"
                  x-transition:leave-end="opacity-0"
             ></div>
             ></div>
 
 
-            <div class="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-3xl w-full sm:p-6"
+            <div class="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl w-full sm:p-6"
                  x-show="open"
                  x-show="open"
                  x-transition:enter="ease-out duration-300"
                  x-transition:enter="ease-out duration-300"
                  x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
                  x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"

+ 5 - 0
docs/layouts/partials/menu-filetree.html

@@ -1,4 +1,9 @@
 <ul>
 <ul>
+    <li>
+    <ul><li><a href="/getting-started" {{- if eq .Page.Title "Getting Started" }} class="text-blue-700 active"{{ end }}>
+      Getting Started
+    </a></li></ul>
+    </li>
     <li>{{ template "book-section" (dict "Section" .Site.Sections "CurrentPage" $.Permalink) }}</li>
     <li>{{ template "book-section" (dict "Section" .Site.Sections "CurrentPage" $.Permalink) }}</li>
 </ul>
 </ul>
 
 

+ 1 - 1
docs/layouts/shortcodes/shop-api-operation.html

@@ -1,4 +1,4 @@
 {{- $operationName := .Get "operation" -}}
 {{- $operationName := .Get "operation" -}}
 {{- $operationSegment := cond (eq (.Get "type") "query") "queries" "mutations" -}}
 {{- $operationSegment := cond (eq (.Get "type") "query") "queries" "mutations" -}}
-{{- $link := (print "/docs/graphql-api/shop/" $operationSegment  "#" (lower $operationName) ) -}}
+{{- $link := (print "/graphql-api/shop/" $operationSegment  "#" (lower $operationName) ) -}}
 <em>{{ .Get "type" }}</em> <a href="{{ $link }}"><code>{{ $operationName }}</code></a>
 <em>{{ .Get "type" }}</em> <a href="{{ $link }}"><code>{{ $operationName }}</code></a>

+ 2 - 2
packages/admin-ui-plugin/src/plugin.ts

@@ -34,7 +34,7 @@ import { MetricsService } from './service/metrics.service';
  * @description
  * @description
  * Configuration options for the {@link AdminUiPlugin}.
  * Configuration options for the {@link AdminUiPlugin}.
  *
  *
- * @docsCategory AdminUiPlugin
+ * @docsCategory core plugins/AdminUiPlugin
  */
  */
 export interface AdminUiPluginOptions {
 export interface AdminUiPluginOptions {
     /**
     /**
@@ -101,7 +101,7 @@ export interface AdminUiPluginOptions {
  * };
  * };
  * ```
  * ```
  *
  *
- * @docsCategory AdminUiPlugin
+ * @docsCategory core plugins/AdminUiPlugin
  */
  */
 @VendurePlugin({
 @VendurePlugin({
     imports: [PluginCommonModule],
     imports: [PluginCommonModule],

+ 1 - 1
packages/asset-server-plugin/src/hashed-asset-naming-strategy.ts

@@ -15,7 +15,7 @@ import path from 'path';
  * With this strategy, even with 200,000 total assets stored, each directory would
  * With this strategy, even with 200,000 total assets stored, each directory would
  * only contain less than 800 files.
  * only contain less than 800 files.
  *
  *
- * @docsCategory AssetServerPlugin
+ * @docsCategory core plugins/AssetServerPlugin
  */
  */
 export class HashedAssetNamingStrategy extends DefaultAssetNamingStrategy {
 export class HashedAssetNamingStrategy extends DefaultAssetNamingStrategy {
     generateSourceFileName(ctx: RequestContext, originalFileName: string, conflictFileName?: string): string {
     generateSourceFileName(ctx: RequestContext, originalFileName: string, conflictFileName?: string): string {

+ 1 - 1
packages/asset-server-plugin/src/local-asset-storage-strategy.ts

@@ -10,7 +10,7 @@ import { Stream } from 'stream';
  * @description
  * @description
  * A persistence strategy which saves files to the local file system.
  * A persistence strategy which saves files to the local file system.
  *
  *
- * @docsCategory AssetServerPlugin
+ * @docsCategory core plugins/AssetServerPlugin
  */
  */
 export class LocalAssetStorageStrategy implements AssetStorageStrategy {
 export class LocalAssetStorageStrategy implements AssetStorageStrategy {
     toAbsoluteUrl: ((reqest: Request, identifier: string) => string) | undefined;
     toAbsoluteUrl: ((reqest: Request, identifier: string) => string) | undefined;

+ 1 - 1
packages/asset-server-plugin/src/plugin.ts

@@ -134,7 +134,7 @@ import { AssetServerOptions, ImageTransformPreset } from './types';
  * By default, the AssetServerPlugin will cache every transformed image, so that the transformation only needs to be performed a single time for
  * By default, the AssetServerPlugin will cache every transformed image, so that the transformation only needs to be performed a single time for
  * a given configuration. Caching can be disabled per-request by setting the `?cache=false` query parameter.
  * a given configuration. Caching can be disabled per-request by setting the `?cache=false` query parameter.
  *
  *
- * @docsCategory AssetServerPlugin
+ * @docsCategory core plugins/AssetServerPlugin
  */
  */
 @VendurePlugin({
 @VendurePlugin({
     imports: [PluginCommonModule],
     imports: [PluginCommonModule],

+ 53 - 23
packages/asset-server-plugin/src/s3-asset-storage-strategy.ts

@@ -13,7 +13,7 @@ import { AssetServerOptions } from './types';
  * @description
  * @description
  * Configuration for connecting to AWS S3.
  * Configuration for connecting to AWS S3.
  *
  *
- * @docsCategory asset-server-plugin
+ * @docsCategory core plugins/AssetServerPlugin
  * @docsPage S3AssetStorageStrategy
  * @docsPage S3AssetStorageStrategy
  */
  */
 export interface S3Config {
 export interface S3Config {
@@ -107,7 +107,7 @@ export interface S3Config {
  *     }),
  *     }),
  * }),
  * }),
  * ```
  * ```
- * @docsCategory asset-server-plugin
+ * @docsCategory core plugins/AssetServerPlugin
  * @docsPage S3AssetStorageStrategy
  * @docsPage S3AssetStorageStrategy
  */
  */
 export function configureS3AssetStorage(s3Config: S3Config) {
 export function configureS3AssetStorage(s3Config: S3Config) {
@@ -151,25 +151,36 @@ export class S3AssetStorageStrategy implements AssetStorageStrategy {
     private libStorage: typeof import('@aws-sdk/lib-storage');
     private libStorage: typeof import('@aws-sdk/lib-storage');
     private s3Client: import('@aws-sdk/client-s3').S3Client;
     private s3Client: import('@aws-sdk/client-s3').S3Client;
 
 
-    constructor(private s3Config: S3Config, public readonly toAbsoluteUrl: (request: Request, identifier: string) => string) {}
+    constructor(
+        private s3Config: S3Config,
+        public readonly toAbsoluteUrl: (request: Request, identifier: string) => string,
+    ) {}
 
 
     async init() {
     async init() {
         try {
         try {
             this.AWS = await import('@aws-sdk/client-s3');
             this.AWS = await import('@aws-sdk/client-s3');
         } catch (err: any) {
         } catch (err: any) {
-            Logger.error('Could not find the "@aws-sdk/client-s3" package. Make sure it is installed', loggerCtx, err.stack);
+            Logger.error(
+                'Could not find the "@aws-sdk/client-s3" package. Make sure it is installed',
+                loggerCtx,
+                err.stack,
+            );
         }
         }
 
 
         try {
         try {
             this.libStorage = await import('@aws-sdk/lib-storage');
             this.libStorage = await import('@aws-sdk/lib-storage');
         } catch (err: any) {
         } catch (err: any) {
-            Logger.error('Could not find the "@aws-sdk/lib-storage" package. Make sure it is installed', loggerCtx, err.stack);
+            Logger.error(
+                'Could not find the "@aws-sdk/lib-storage" package. Make sure it is installed',
+                loggerCtx,
+                err.stack,
+            );
         }
         }
 
 
         const config = {
         const config = {
             ...this.s3Config.nativeS3Configuration,
             ...this.s3Config.nativeS3Configuration,
-            credentials: await this.getCredentials() // Avoid credentials overriden by nativeS3Configuration
-        } satisfies S3ClientConfig
+            credentials: await this.getCredentials(), // Avoid credentials overriden by nativeS3Configuration
+        } satisfies S3ClientConfig;
 
 
         this.s3Client = new this.AWS.S3Client(config);
         this.s3Client = new this.AWS.S3Client(config);
 
 
@@ -206,7 +217,11 @@ export class S3AssetStorageStrategy implements AssetStorageStrategy {
         const body = await this.readFile(identifier);
         const body = await this.readFile(identifier);
 
 
         if (!body) {
         if (!body) {
-            return new Readable({ read() { this.push(null); } });
+            return new Readable({
+                read() {
+                    this.push(null);
+                },
+            });
         }
         }
 
 
         return body;
         return body;
@@ -220,7 +235,7 @@ export class S3AssetStorageStrategy implements AssetStorageStrategy {
     }
     }
 
 
     private async writeFile(fileName: string, data: PutObjectRequest['Body'] | string | Uint8Array | Buffer) {
     private async writeFile(fileName: string, data: PutObjectRequest['Body'] | string | Uint8Array | Buffer) {
-        const { Upload } = this.libStorage
+        const { Upload } = this.libStorage;
 
 
         const upload = new Upload({
         const upload = new Upload({
             client: this.s3Client,
             client: this.s3Client,
@@ -232,7 +247,7 @@ export class S3AssetStorageStrategy implements AssetStorageStrategy {
             },
             },
         });
         });
 
 
-        return upload.done().then((result) => {
+        return upload.done().then(result => {
             if (!('Key' in result) || !result.Key) {
             if (!('Key' in result) || !result.Key) {
                 Logger.error(`Got undefined Key for ${fileName}`, loggerCtx);
                 Logger.error(`Got undefined Key for ${fileName}`, loggerCtx);
                 throw new Error(`Got undefined Key for ${fileName}`);
                 throw new Error(`Got undefined Key for ${fileName}`);
@@ -243,12 +258,12 @@ export class S3AssetStorageStrategy implements AssetStorageStrategy {
     }
     }
 
 
     async deleteFile(identifier: string) {
     async deleteFile(identifier: string) {
-        const { DeleteObjectCommand } = this.AWS
+        const { DeleteObjectCommand } = this.AWS;
         await this.s3Client.send(new DeleteObjectCommand(this.getObjectParams(identifier)));
         await this.s3Client.send(new DeleteObjectCommand(this.getObjectParams(identifier)));
     }
     }
 
 
     async fileExists(fileName: string) {
     async fileExists(fileName: string) {
-        const { HeadObjectCommand } = this.AWS
+        const { HeadObjectCommand } = this.AWS;
 
 
         try {
         try {
             await this.s3Client.send(new HeadObjectCommand(this.getObjectParams(fileName)));
             await this.s3Client.send(new HeadObjectCommand(this.getObjectParams(fileName)));
@@ -266,21 +281,27 @@ export class S3AssetStorageStrategy implements AssetStorageStrategy {
     }
     }
 
 
     private async ensureBucket(bucket = this.s3Config.bucket) {
     private async ensureBucket(bucket = this.s3Config.bucket) {
-        const { HeadBucketCommand, CreateBucketCommand } = this.AWS
+        const { HeadBucketCommand, CreateBucketCommand } = this.AWS;
 
 
         try {
         try {
             await this.s3Client.send(new HeadBucketCommand({ Bucket: bucket }));
             await this.s3Client.send(new HeadBucketCommand({ Bucket: bucket }));
             Logger.verbose(`Found S3 bucket "${bucket}"`, loggerCtx);
             Logger.verbose(`Found S3 bucket "${bucket}"`, loggerCtx);
-            return
+            return;
         } catch (err: any) {
         } catch (err: any) {
-            Logger.verbose(`Could not find bucket "${bucket}: ${JSON.stringify(err.message)}". Attempting to create...`);
+            Logger.verbose(
+                `Could not find bucket "${bucket}: ${JSON.stringify(err.message)}". Attempting to create...`,
+            );
         }
         }
 
 
         try {
         try {
-            await this.s3Client.send(new CreateBucketCommand({Bucket: bucket, ACL: 'private'}));
+            await this.s3Client.send(new CreateBucketCommand({ Bucket: bucket, ACL: 'private' }));
             Logger.verbose(`Created S3 bucket "${bucket}"`, loggerCtx);
             Logger.verbose(`Created S3 bucket "${bucket}"`, loggerCtx);
         } catch (err: any) {
         } catch (err: any) {
-            Logger.error(`Could not find nor create the S3 bucket "${bucket}: ${JSON.stringify(err.message)}"`, loggerCtx, err.stack);
+            Logger.error(
+                `Could not find nor create the S3 bucket "${bucket}: ${JSON.stringify(err.message)}"`,
+                loggerCtx,
+                err.stack,
+            );
         }
         }
     }
     }
 
 
@@ -292,16 +313,25 @@ export class S3AssetStorageStrategy implements AssetStorageStrategy {
         if (this.isCredentialsProfile(this.s3Config.credentials)) {
         if (this.isCredentialsProfile(this.s3Config.credentials)) {
             Logger.warn(
             Logger.warn(
                 'The "profile" property of the "s3Config.credentials" is deprecated. ' +
                 'The "profile" property of the "s3Config.credentials" is deprecated. ' +
-                'Please use the "fromIni()" function from the "@aws-sdk/credential-provider-ini" or "@aws-sdk/credential-providers" package instead.',
-                loggerCtx
+                    'Please use the "fromIni()" function from the "@aws-sdk/credential-provider-ini" or "@aws-sdk/credential-providers" package instead.',
+                loggerCtx,
             );
             );
-            return (await import('@aws-sdk/credential-provider-ini')).fromIni({ profile: this.s3Config.credentials.profile  });
+            return (await import('@aws-sdk/credential-provider-ini')).fromIni({
+                profile: this.s3Config.credentials.profile,
+            });
         }
         }
 
 
-        return this.s3Config.credentials
+        return this.s3Config.credentials;
     }
     }
 
 
-    private isCredentialsProfile(credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider,): credentials is AwsCredentialIdentity & { profile: string } {
-        return credentials !== null && typeof credentials === 'object' && 'profile' in credentials && Object.keys(credentials).length === 1;
+    private isCredentialsProfile(
+        credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider,
+    ): credentials is AwsCredentialIdentity & { profile: string } {
+        return (
+            credentials !== null &&
+            typeof credentials === 'object' &&
+            'profile' in credentials &&
+            Object.keys(credentials).length === 1
+        );
     }
     }
 }
 }

+ 2 - 2
packages/asset-server-plugin/src/sharp-asset-preview-strategy.ts

@@ -11,7 +11,7 @@ import { loggerCtx } from './constants';
  * preview images of uploaded binary files. For non-image binaries, a generic "file" icon with the mime type
  * preview images of uploaded binary files. For non-image binaries, a generic "file" icon with the mime type
  * overlay will be generated.
  * overlay will be generated.
  *
  *
- * @docsCategory AssetServerPlugin
+ * @docsCategory core plugins/AssetServerPlugin
  * @docsPage SharpAssetPreviewStrategy
  * @docsPage SharpAssetPreviewStrategy
  */
  */
 interface SharpAssetPreviewConfig {
 interface SharpAssetPreviewConfig {
@@ -88,7 +88,7 @@ interface SharpAssetPreviewConfig {
  * }),
  * }),
  * ```
  * ```
  *
  *
- * @docsCategory AssetServerPlugin
+ * @docsCategory core plugins/AssetServerPlugin
  * @docsPage SharpAssetPreviewStrategy
  * @docsPage SharpAssetPreviewStrategy
  * @docsWeight 0
  * @docsWeight 0
  */
  */

+ 4 - 4
packages/asset-server-plugin/src/types.ts

@@ -16,7 +16,7 @@ export type ImageTransformFormat = 'jpg' | 'jpeg' | 'png' | 'webp' | 'avif';
  * * resize: Preserving aspect ratio, resizes the image to be as large as possible
  * * resize: Preserving aspect ratio, resizes the image to be as large as possible
  * while ensuring its dimensions are less than or equal to both those specified.
  * while ensuring its dimensions are less than or equal to both those specified.
  *
  *
- * @docsCategory AssetServerPlugin
+ * @docsCategory core plugins/AssetServerPlugin
  */
  */
 export type ImageTransformMode = 'crop' | 'resize';
 export type ImageTransformMode = 'crop' | 'resize';
 
 
@@ -34,7 +34,7 @@ export type ImageTransformMode = 'crop' | 'resize';
  *
  *
  * `http://localhost:3000/assets/some-asset.jpg?w=50&h=50&mode=crop`
  * `http://localhost:3000/assets/some-asset.jpg?w=50&h=50&mode=crop`
  *
  *
- * @docsCategory AssetServerPlugin
+ * @docsCategory core plugins/AssetServerPlugin
  */
  */
 export interface ImageTransformPreset {
 export interface ImageTransformPreset {
     name: string;
     name: string;
@@ -47,7 +47,7 @@ export interface ImageTransformPreset {
  * @description
  * @description
  * A configuration option for the Cache-Control header in the AssetServerPlugin asset response.
  * A configuration option for the Cache-Control header in the AssetServerPlugin asset response.
  *
  *
- * @docsCategory AssetServerPlugin
+ * @docsCategory core plugins/AssetServerPlugin
  */
  */
 export type CacheConfig = {
 export type CacheConfig = {
     /**
     /**
@@ -67,7 +67,7 @@ export type CacheConfig = {
  * @description
  * @description
  * The configuration options for the AssetServerPlugin.
  * The configuration options for the AssetServerPlugin.
  *
  *
- * @docsCategory AssetServerPlugin
+ * @docsCategory core plugins/AssetServerPlugin
  */
  */
 export interface AssetServerOptions {
 export interface AssetServerOptions {
     /**
     /**

+ 3 - 3
packages/common/src/shared-types.ts

@@ -211,7 +211,7 @@ export type CustomFieldsObject = { [key: string]: any };
  * The values are loaded at run-time by the Admin UI app, and allow core configuration to be
  * The values are loaded at run-time by the Admin UI app, and allow core configuration to be
  * managed without the need to re-build the application.
  * managed without the need to re-build the application.
  *
  *
- * @docsCategory AdminUiPlugin
+ * @docsCategory core plugins/AdminUiPlugin
  */
  */
 export interface AdminUiConfig {
 export interface AdminUiConfig {
     /**
     /**
@@ -328,7 +328,7 @@ export interface AdminUiConfig {
  * @description
  * @description
  * Configures the path to a custom-build of the Admin UI app.
  * Configures the path to a custom-build of the Admin UI app.
  *
  *
- * @docsCategory common
+ * @docsCategory core plugins/AdminUiPlugin
  */
  */
 export interface AdminUiAppConfig {
 export interface AdminUiAppConfig {
     /**
     /**
@@ -356,7 +356,7 @@ export interface AdminUiAppConfig {
  * @description
  * @description
  * Information about the Admin UI app dev server.
  * Information about the Admin UI app dev server.
  *
  *
- * @docsCategory common
+ * @docsCategory core plugins/AdminUiPlugin
  */
  */
 export interface AdminUiAppDevModeConfig {
 export interface AdminUiAppDevModeConfig {
     /**
     /**

+ 1 - 1
packages/core/src/api/decorators/relations.decorator.ts

@@ -128,7 +128,7 @@ const cache = new TtlCache({ cacheSize: 500, ttl: 5 * 60 * 1000 });
  * ```
  * ```
  *
  *
  * @docsCategory request
  * @docsCategory request
- * @docsPage Api Decorator
+ * @docsPage Relations Decorator
  * @since 1.6.0
  * @since 1.6.0
  */
  */
 export const Relations: <T extends VendureEntity>(data: FieldsDecoratorConfig<T>) => ParameterDecorator =
 export const Relations: <T extends VendureEntity>(data: FieldsDecoratorConfig<T>) => ParameterDecorator =

+ 1 - 1
packages/core/src/bootstrap.ts

@@ -39,7 +39,7 @@ export type VendureBootstrapFunction = (config: VendureConfig) => Promise<INestA
  *     console.log(err);
  *     console.log(err);
  * });
  * });
  * ```
  * ```
- * @docsCategory
+ * @docsCategory common
  * */
  * */
 export async function bootstrap(userConfig: Partial<VendureConfig>): Promise<INestApplication> {
 export async function bootstrap(userConfig: Partial<VendureConfig>): Promise<INestApplication> {
     const config = await preBootstrapConfig(userConfig);
     const config = await preBootstrapConfig(userConfig);

+ 1 - 1
packages/core/src/config/catalog/default-product-variant-price-calculation-strategy.ts

@@ -13,7 +13,7 @@ import {
  * @description
  * @description
  * A default ProductVariant price calculation function.
  * A default ProductVariant price calculation function.
  *
  *
- * @docsCategory tax
+ * @docsCategory products & stock
  */
  */
 export class DefaultProductVariantPriceCalculationStrategy implements ProductVariantPriceCalculationStrategy {
 export class DefaultProductVariantPriceCalculationStrategy implements ProductVariantPriceCalculationStrategy {
     private taxRateService: TaxRateService;
     private taxRateService: TaxRateService;

+ 1 - 1
packages/core/src/config/catalog/default-stock-display-strategy.ts

@@ -9,7 +9,7 @@ import { StockDisplayStrategy } from './stock-display-strategy';
  * Low stock is defined as a saleable stock level less than or equal to the `lowStockLevel` as passed in
  * Low stock is defined as a saleable stock level less than or equal to the `lowStockLevel` as passed in
  * to the constructor (defaults to `2`).
  * to the constructor (defaults to `2`).
  *
  *
- * @docsCategory configuration
+ * @docsCategory products & stock
  */
  */
 export class DefaultStockDisplayStrategy implements StockDisplayStrategy {
 export class DefaultStockDisplayStrategy implements StockDisplayStrategy {
     constructor(private lowStockLevel: number = 2) {}
     constructor(private lowStockLevel: number = 2) {}

+ 1 - 1
packages/core/src/config/catalog/default-stock-location-strategy.ts

@@ -15,7 +15,7 @@ import { AvailableStock, LocationWithQuantity, StockLocationStrategy } from './s
  * The DefaultStockLocationStrategy is the default implementation of the {@link StockLocationStrategy}.
  * The DefaultStockLocationStrategy is the default implementation of the {@link StockLocationStrategy}.
  * It assumes only a single StockLocation and that all stock is allocated from that location.
  * It assumes only a single StockLocation and that all stock is allocated from that location.
  *
  *
- * @docsCategory catalog
+ * @docsCategory products & stock
  * @since 2.0.0
  * @since 2.0.0
  */
  */
 export class DefaultStockLocationStrategy implements StockLocationStrategy {
 export class DefaultStockLocationStrategy implements StockLocationStrategy {

+ 2 - 2
packages/core/src/config/catalog/product-variant-price-calculation-strategy.ts

@@ -8,7 +8,7 @@ import { Zone } from '../../entity/zone/zone.entity';
  * @description
  * @description
  * Defines how ProductVariant are calculated based on the input price, tax zone and current request context.
  * Defines how ProductVariant are calculated based on the input price, tax zone and current request context.
  *
  *
- * @docsCategory configuration
+ * @docsCategory  products & stock
  * @docsPage ProductVariantPriceCalculationStrategy
  * @docsPage ProductVariantPriceCalculationStrategy
  */
  */
 export interface ProductVariantPriceCalculationStrategy extends InjectableStrategy {
 export interface ProductVariantPriceCalculationStrategy extends InjectableStrategy {
@@ -19,7 +19,7 @@ export interface ProductVariantPriceCalculationStrategy extends InjectableStrate
  * @description
  * @description
  * The arguments passed the `calculate` method of the configured {@link ProductVariantPriceCalculationStrategy}.
  * The arguments passed the `calculate` method of the configured {@link ProductVariantPriceCalculationStrategy}.
  *
  *
- * @docsCategory configuration
+ * @docsCategory products & stock
  * @docsPage ProductVariantPriceCalculationStrategy
  * @docsPage ProductVariantPriceCalculationStrategy
  */
  */
 export interface ProductVariantPriceCalculationArgs {
 export interface ProductVariantPriceCalculationArgs {

+ 1 - 1
packages/core/src/config/catalog/stock-display-strategy.ts

@@ -9,7 +9,7 @@ import { ProductVariant } from '../../entity/product-variant/product-variant.ent
  * sensitive information. However, the storefront will usually want to display _some_ indication
  * sensitive information. However, the storefront will usually want to display _some_ indication
  * of whether a given ProductVariant is in stock.
  * of whether a given ProductVariant is in stock.
  *
  *
- * @docsCategory configuration
+ * @docsCategory products & stock
  */
  */
 export interface StockDisplayStrategy extends InjectableStrategy {
 export interface StockDisplayStrategy extends InjectableStrategy {
     /**
     /**

+ 4 - 3
packages/core/src/config/catalog/stock-location-strategy.ts

@@ -11,7 +11,7 @@ import { StockLocation } from '../../entity/stock-location/stock-location.entity
  * The overall available stock for a ProductVariant as determined by the logic of the
  * The overall available stock for a ProductVariant as determined by the logic of the
  * {@link StockLocationStrategy}'s `getAvailableStock` method.
  * {@link StockLocationStrategy}'s `getAvailableStock` method.
  *
  *
- * @docsCategory catalog
+ * @docsCategory products & stock
  * @since 2.0.0
  * @since 2.0.0
  * @docsPage StockLocationStrategy
  * @docsPage StockLocationStrategy
  */
  */
@@ -25,7 +25,7 @@ export interface AvailableStock {
  * Returned by the `StockLocationStrategy` methods to indicate how much stock from each
  * Returned by the `StockLocationStrategy` methods to indicate how much stock from each
  * location should be used in the allocation/sale/release/cancellation of an OrderLine.
  * location should be used in the allocation/sale/release/cancellation of an OrderLine.
  *
  *
- * @docsCategory catalog
+ * @docsCategory products & stock
  * @since 2.0.0
  * @since 2.0.0
  * @docsPage StockLocationStrategy
  * @docsPage StockLocationStrategy
  */
  */
@@ -41,7 +41,8 @@ export interface LocationWithQuantity {
  * from each location. It is also used to determine the available stock for a given
  * from each location. It is also used to determine the available stock for a given
  * {@link ProductVariant}.
  * {@link ProductVariant}.
  *
  *
- * @docsCategory catalog
+ * @docsCategory products & stock
+ * @docsWeight 0
  * @since 2.0.0
  * @since 2.0.0
  */
  */
 export interface StockLocationStrategy extends InjectableStrategy {
 export interface StockLocationStrategy extends InjectableStrategy {

+ 1 - 1
packages/core/src/config/vendure-config.ts

@@ -647,7 +647,7 @@ export interface AssetOptions {
  * @description
  * @description
  * Options related to products and collections.
  * Options related to products and collections.
  *
  *
- * @docsCategory configuration
+ * @docsCategory products & stock
  */
  */
 export interface CatalogOptions {
 export interface CatalogOptions {
     /**
     /**

+ 6 - 2
packages/core/src/i18n/i18n.service.ts

@@ -18,7 +18,8 @@ import { I18nError } from './i18n-error';
  * @description
  * @description
  * I18n resources used for translations
  * I18n resources used for translations
  *
  *
- * @docsCategory Translation
+ * @docsCategory common
+ * @docsPage I18nService
  */
  */
 export interface VendureTranslationResources {
 export interface VendureTranslationResources {
     error: any;
     error: any;
@@ -35,7 +36,10 @@ export interface I18nRequest extends Request {
  * The `i18next-express-middleware` middleware detects the client's preferred language based on
  * The `i18next-express-middleware` middleware detects the client's preferred language based on
  * the `Accept-Language` header or "lang" query param and adds language-specific translation
  * the `Accept-Language` header or "lang" query param and adds language-specific translation
  * functions to the Express request / response objects.
  * functions to the Express request / response objects.
- * @docsCategory Translation
+ *
+ * @docsCategory common
+ * @docsPage I18nService
+ * @docsWeight 0
  */
  */
 @Injectable()
 @Injectable()
 export class I18nService implements OnModuleInit {
 export class I18nService implements OnModuleInit {

+ 1 - 1
packages/core/src/plugin/default-search-plugin/default-search-plugin.ts

@@ -64,7 +64,7 @@ export interface DefaultSearchReindexResponse extends SearchReindexResponse {
  * };
  * };
  * ```
  * ```
  *
  *
- * @docsCategory DefaultSearchPlugin
+ * @docsCategory core plugins/DefaultSearchPlugin
  */
  */
 @VendurePlugin({
 @VendurePlugin({
     imports: [PluginCommonModule],
     imports: [PluginCommonModule],

+ 1 - 1
packages/core/src/plugin/default-search-plugin/types.ts

@@ -10,7 +10,7 @@ import { SearchStrategy } from './search-strategy/search-strategy';
  * @description
  * @description
  * Options which configure the behaviour of the DefaultSearchPlugin
  * Options which configure the behaviour of the DefaultSearchPlugin
  *
  *
- * @docsCategory DefaultSearchPlugin
+ * @docsCategory core plugins/DefaultSearchPlugin
  */
  */
 export interface DefaultSearchPluginInitOptions {
 export interface DefaultSearchPluginInitOptions {
     /**
     /**

+ 1 - 1
packages/core/src/service/services/order.service.ts

@@ -167,7 +167,7 @@ export class OrderService {
     /**
     /**
      * @description
      * @description
      * Returns an array of all the configured states and transitions of the order process. This is
      * Returns an array of all the configured states and transitions of the order process. This is
-     * based on the default order process plus all configured {@link CustomOrderProcess} objects
+     * based on the default order process plus all configured {@link OrderProcess} objects
      * defined in the {@link OrderOptions} `process` array.
      * defined in the {@link OrderOptions} `process` array.
      */
      */
     getOrderProcessStates(): OrderProcessState[] {
     getOrderProcessStates(): OrderProcessState[] {

+ 85 - 68
packages/dev-server/dev-config.ts

@@ -63,26 +63,43 @@ export const devConfig: VendureConfig = {
     },
     },
 
 
     customFields: {
     customFields: {
-        // ProductVariant: [
-        //     {
-        //         name: 'weight',
-        //         type: 'int',
-        //         defaultValue: 0,
-        //         nullable: false,
-        //         min: 0,
-        //         step: 1,
-        //         public: true,
-        //         label: [{ languageCode: LanguageCode.en, value: 'Weight' }],
-        //         ui: { component: 'number-form-input', suffix: 'g' },
-        //     },
-        //     {
-        //         name: 'gtin',
-        //         type: 'string',
-        //         nullable: true,
-        //         public: true,
-        //         label: [{ languageCode: LanguageCode.en, value: 'GTIN (barcode)' }],
-        //     },
-        // ],
+        Product: [
+            {
+                name: 'searchKeywords',
+                label: [{ languageCode: LanguageCode.en, value: 'Search keywords' }],
+                type: 'string',
+                defaultValue: '',
+                public: false,
+            },
+            {
+                name: 'noIndex',
+                label: [{ languageCode: LanguageCode.en, value: 'Do not allow crawlers to index' }],
+                type: 'boolean',
+                defaultValue: false,
+                public: true,
+                ui: { tab: 'SEO' },
+            },
+        ],
+        ProductVariant: [
+            {
+                name: 'weight',
+                type: 'int',
+                defaultValue: 0,
+                nullable: false,
+                min: 0,
+                step: 1,
+                public: true,
+                label: [{ languageCode: LanguageCode.en, value: 'Weight' }],
+                ui: { component: 'number-form-input', suffix: 'g' },
+            },
+            {
+                name: 'gtin',
+                type: 'string',
+                nullable: true,
+                public: true,
+                label: [{ languageCode: LanguageCode.en, value: 'GTIN (barcode)' }],
+            },
+        ],
     },
     },
 
 
     /* customFields: {
     /* customFields: {
@@ -395,53 +412,53 @@ export const devConfig: VendureConfig = {
             route: 'admin',
             route: 'admin',
             port: 5001,
             port: 5001,
             // Un-comment to compile a custom admin ui
             // Un-comment to compile a custom admin ui
-            app: compileUiExtensions({
-                outputPath: path.join(__dirname, './custom-admin-ui'),
-                extensions: [
-                    {
-                        id: 'test-ui-extension',
-                        extensionPath: path.join(__dirname, 'test-plugins/with-ui-extension/ui'),
-                        ngModules: [
-                            {
-                                type: 'lazy',
-                                route: 'greetz',
-                                ngModuleFileName: 'greeter.module.ts',
-                                ngModuleName: 'GreeterModule',
-                            },
-                            {
-                                type: 'shared',
-                                ngModuleFileName: 'greeter-shared.module.ts',
-                                ngModuleName: 'GreeterSharedModule',
-                            },
-                        ],
-                    },
-                    {
-                        globalStyles: path.join(
-                            __dirname,
-                            'test-plugins/with-ui-extension/ui/custom-theme.scss',
-                        ),
-                    },
-                    {
-                        id: 'external-ui-extension',
-                        extensionPath: path.join(__dirname, 'test-plugins/with-external-ui-extension'),
-                        ngModules: [
-                            {
-                                type: 'lazy',
-                                route: 'greet',
-                                ngModuleFileName: 'external-ui-extension.ts',
-                                ngModuleName: 'ExternalUiExtensionModule',
-                            },
-                        ],
-                        staticAssets: [
-                            {
-                                path: path.join(__dirname, 'test-plugins/with-external-ui-extension/app'),
-                                rename: 'external-app',
-                            },
-                        ],
-                    },
-                ],
-                devMode: true,
-            }),
+            // app: compileUiExtensions({
+            //     outputPath: path.join(__dirname, './custom-admin-ui'),
+            //     extensions: [
+            //         {
+            //             id: 'test-ui-extension',
+            //             extensionPath: path.join(__dirname, 'test-plugins/with-ui-extension/ui'),
+            //             ngModules: [
+            //                 {
+            //                     type: 'lazy',
+            //                     route: 'greetz',
+            //                     ngModuleFileName: 'greeter.module.ts',
+            //                     ngModuleName: 'GreeterModule',
+            //                 },
+            //                 {
+            //                     type: 'shared',
+            //                     ngModuleFileName: 'greeter-shared.module.ts',
+            //                     ngModuleName: 'GreeterSharedModule',
+            //                 },
+            //             ],
+            //         },
+            //         {
+            //             globalStyles: path.join(
+            //                 __dirname,
+            //                 'test-plugins/with-ui-extension/ui/custom-theme.scss',
+            //             ),
+            //         },
+            //         {
+            //             id: 'external-ui-extension',
+            //             extensionPath: path.join(__dirname, 'test-plugins/with-external-ui-extension'),
+            //             ngModules: [
+            //                 {
+            //                     type: 'lazy',
+            //                     route: 'greet',
+            //                     ngModuleFileName: 'external-ui-extension.ts',
+            //                     ngModuleName: 'ExternalUiExtensionModule',
+            //                 },
+            //             ],
+            //             staticAssets: [
+            //                 {
+            //                     path: path.join(__dirname, 'test-plugins/with-external-ui-extension/app'),
+            //                     rename: 'external-app',
+            //                 },
+            //             ],
+            //         },
+            //     ],
+            //     devMode: true,
+            // }),
         }),
         }),
     ],
     ],
 };
 };
@@ -480,7 +497,7 @@ function getDbConfig(): DataSourceOptions {
         default:
         default:
             console.log('Using mysql connection');
             console.log('Using mysql connection');
             return {
             return {
-                synchronize: false,
+                synchronize: true,
                 type: 'mariadb',
                 type: 'mariadb',
                 host: '127.0.0.1',
                 host: '127.0.0.1',
                 port: 3306,
                 port: 3306,

+ 3 - 3
packages/elasticsearch-plugin/src/options.ts

@@ -16,7 +16,7 @@ import {
  * @description
  * @description
  * Configuration options for the {@link ElasticsearchPlugin}.
  * Configuration options for the {@link ElasticsearchPlugin}.
  *
  *
- * @docsCategory ElasticsearchPlugin
+ * @docsCategory core plugins/ElasticsearchPlugin
  * @docsPage ElasticsearchOptions
  * @docsPage ElasticsearchOptions
  */
  */
 export interface ElasticsearchOptions {
 export interface ElasticsearchOptions {
@@ -371,7 +371,7 @@ export interface ElasticsearchOptions {
  * @description
  * @description
  * Configuration options for the internal Elasticsearch query which is generated when performing a search.
  * Configuration options for the internal Elasticsearch query which is generated when performing a search.
  *
  *
- * @docsCategory ElasticsearchPlugin
+ * @docsCategory core plugins/ElasticsearchPlugin
  * @docsPage ElasticsearchOptions
  * @docsPage ElasticsearchOptions
  */
  */
 export interface SearchConfig {
 export interface SearchConfig {
@@ -656,7 +656,7 @@ export interface SearchConfig {
  *
  *
  * Boosting a field acts as a score multiplier for matches against that field.
  * Boosting a field acts as a score multiplier for matches against that field.
  *
  *
- * @docsCategory ElasticsearchPlugin
+ * @docsCategory core plugins/ElasticsearchPlugin
  * @docsPage ElasticsearchOptions
  * @docsPage ElasticsearchOptions
  */
  */
 export interface BoostFieldsConfig {
 export interface BoostFieldsConfig {

+ 1 - 1
packages/elasticsearch-plugin/src/plugin.ts

@@ -217,7 +217,7 @@ function getCustomResolvers(options: ElasticsearchRuntimeOptions) {
  *}
  *}
  * ```
  * ```
  *
  *
- * @docsCategory ElasticsearchPlugin
+ * @docsCategory core plugins/ElasticsearchPlugin
  */
  */
 @VendurePlugin({
 @VendurePlugin({
     imports: [PluginCommonModule],
     imports: [PluginCommonModule],

+ 1 - 1
packages/email-plugin/src/email-generator.ts

@@ -6,7 +6,7 @@ import { EmailDetails, EmailPluginOptions } from './types';
  * @description
  * @description
  * An EmailGenerator generates the subject and body details of an email.
  * An EmailGenerator generates the subject and body details of an email.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage EmailGenerator
  * @docsPage EmailGenerator
  * @docsWeight 0
  * @docsWeight 0
  */
  */

+ 1 - 1
packages/email-plugin/src/email-sender.ts

@@ -38,7 +38,7 @@ import { EmailDetails, EmailTransportOptions } from './types';
  * };
  * };
  * ```
  * ```
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage EmailSender
  * @docsPage EmailSender
  * @docsWeight 0
  * @docsWeight 0
  */
  */

+ 7 - 7
packages/email-plugin/src/event-handler.ts

@@ -126,7 +126,7 @@ import {
  * };
  * };
  * ```
  * ```
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  */
  */
 export class EmailEventHandler<T extends string = string, Event extends EventWithContext = EventWithContext> {
 export class EmailEventHandler<T extends string = string, Event extends EventWithContext = EventWithContext> {
     private setRecipientFn: (event: Event) => string;
     private setRecipientFn: (event: Event) => string;
@@ -144,7 +144,7 @@ export class EmailEventHandler<T extends string = string, Event extends EventWit
     };
     };
     private _mockEvent: Omit<Event, 'ctx' | 'data'> | undefined;
     private _mockEvent: Omit<Event, 'ctx' | 'data'> | undefined;
 
 
-    constructor(public listener: EmailEventListener<T>, public event: Type<Event>) { }
+    constructor(public listener: EmailEventListener<T>, public event: Type<Event>) {}
 
 
     /** @internal */
     /** @internal */
     get type(): T {
     get type(): T {
@@ -268,7 +268,7 @@ export class EmailEventHandler<T extends string = string, Event extends EventWit
      * @description
      * @description
      * Add configuration for another template other than the default `"body.hbs"`. Use this method to define specific
      * Add configuration for another template other than the default `"body.hbs"`. Use this method to define specific
      * templates for channels or languageCodes other than the default.
      * templates for channels or languageCodes other than the default.
-     * 
+     *
      * @deprecated Define a custom TemplateLoader on plugin initalization to define templates based on the RequestContext.
      * @deprecated Define a custom TemplateLoader on plugin initalization to define templates based on the RequestContext.
      * E.g. `EmailPlugin.init({ templateLoader: new CustomTemplateLoader() })`
      * E.g. `EmailPlugin.init({ templateLoader: new CustomTemplateLoader() })`
      */
      */
@@ -350,13 +350,13 @@ export class EmailEventHandler<T extends string = string, Event extends EventWit
         if (!this.setRecipientFn) {
         if (!this.setRecipientFn) {
             throw new Error(
             throw new Error(
                 `No setRecipientFn has been defined. ` +
                 `No setRecipientFn has been defined. ` +
-                `Remember to call ".setRecipient()" when setting up the EmailEventHandler for ${this.type}`,
+                    `Remember to call ".setRecipient()" when setting up the EmailEventHandler for ${this.type}`,
             );
             );
         }
         }
         if (this.from === undefined) {
         if (this.from === undefined) {
             throw new Error(
             throw new Error(
                 `No from field has been defined. ` +
                 `No from field has been defined. ` +
-                `Remember to call ".setFrom()" when setting up the EmailEventHandler for ${this.type}`,
+                    `Remember to call ".setFrom()" when setting up the EmailEventHandler for ${this.type}`,
             );
             );
         }
         }
         const { ctx } = event;
         const { ctx } = event;
@@ -366,7 +366,7 @@ export class EmailEventHandler<T extends string = string, Event extends EventWit
         if (subject == null) {
         if (subject == null) {
             throw new Error(
             throw new Error(
                 `No subject field has been defined. ` +
                 `No subject field has been defined. ` +
-                `Remember to call ".setSubject()" when setting up the EmailEventHandler for ${this.type}`,
+                    `Remember to call ".setSubject()" when setting up the EmailEventHandler for ${this.type}`,
             );
             );
         }
         }
         const recipient = this.setRecipientFn(event);
         const recipient = this.setRecipientFn(event);
@@ -433,7 +433,7 @@ export class EmailEventHandler<T extends string = string, Event extends EventWit
  * Identical to the {@link EmailEventHandler} but with a `data` property added to the `event` based on the result
  * Identical to the {@link EmailEventHandler} but with a `data` property added to the `event` based on the result
  * of the `.loadData()` function.
  * of the `.loadData()` function.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  */
  */
 export class EmailEventHandlerWithAsyncData<
 export class EmailEventHandlerWithAsyncData<
     Data,
     Data,

+ 1 - 1
packages/email-plugin/src/event-listener.ts

@@ -8,7 +8,7 @@ import { EventWithContext } from './types';
  * An EmailEventListener is used to listen for events and set up a {@link EmailEventHandler} which
  * An EmailEventListener is used to listen for events and set up a {@link EmailEventHandler} which
  * defines how an email will be generated from this event.
  * defines how an email will be generated from this event.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  */
  */
 export class EmailEventListener<T extends string> {
 export class EmailEventListener<T extends string> {
     public type: T;
     public type: T;

+ 1 - 1
packages/email-plugin/src/handlebars-mjml-generator.ts

@@ -17,7 +17,7 @@ import {
  * Uses Handlebars (https://handlebarsjs.com/) to output MJML (https://mjml.io) which is then
  * Uses Handlebars (https://handlebarsjs.com/) to output MJML (https://mjml.io) which is then
  * compiled down to responsive email HTML.
  * compiled down to responsive email HTML.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage EmailGenerator
  * @docsPage EmailGenerator
  */
  */
 export class HandlebarsMjmlGenerator implements EmailGenerator {
 export class HandlebarsMjmlGenerator implements EmailGenerator {

+ 3 - 3
packages/email-plugin/src/nodemailer-email-sender.ts

@@ -17,7 +17,7 @@ import {
     SendmailTransportOptions,
     SendmailTransportOptions,
     SESTransportOptions,
     SESTransportOptions,
     SMTPTransportOptions,
     SMTPTransportOptions,
-} from './types'
+} from './types';
 
 
 export type StreamTransportInfo = {
 export type StreamTransportInfo = {
     envelope: {
     envelope: {
@@ -32,13 +32,13 @@ export type StreamTransportInfo = {
  * @description
  * @description
  * Uses the configured transport to send the generated email.
  * Uses the configured transport to send the generated email.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage EmailSender
  * @docsPage EmailSender
  */
  */
 export class NodemailerEmailSender implements EmailSender {
 export class NodemailerEmailSender implements EmailSender {
     private _smtpTransport: Mail | undefined;
     private _smtpTransport: Mail | undefined;
     private _sendMailTransport: Mail | undefined;
     private _sendMailTransport: Mail | undefined;
-    private _sesTransport: Mail |undefined
+    private _sesTransport: Mail | undefined;
 
 
     async send(email: EmailDetails, options: EmailTransportOptions) {
     async send(email: EmailDetails, options: EmailTransportOptions) {
         switch (options.type) {
         switch (options.type) {

+ 1 - 1
packages/email-plugin/src/plugin.ts

@@ -272,7 +272,7 @@ import {
  * };
  * };
  * ```
  * ```
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  */
  */
 @VendurePlugin({
 @VendurePlugin({
     imports: [PluginCommonModule],
     imports: [PluginCommonModule],

+ 38 - 33
packages/email-plugin/src/types.ts

@@ -2,7 +2,7 @@ import { LanguageCode } from '@vendure/common/lib/generated-types';
 import { Omit } from '@vendure/common/lib/omit';
 import { Omit } from '@vendure/common/lib/omit';
 import { Injector, RequestContext, SerializedRequestContext, VendureEvent } from '@vendure/core';
 import { Injector, RequestContext, SerializedRequestContext, VendureEvent } from '@vendure/core';
 import { Attachment } from 'nodemailer/lib/mailer';
 import { Attachment } from 'nodemailer/lib/mailer';
-import SESTransport from 'nodemailer/lib/ses-transport'
+import SESTransport from 'nodemailer/lib/ses-transport';
 import SMTPTransport from 'nodemailer/lib/smtp-transport';
 import SMTPTransport from 'nodemailer/lib/smtp-transport';
 
 
 import { EmailGenerator } from './email-generator';
 import { EmailGenerator } from './email-generator';
@@ -15,7 +15,7 @@ import { EmailEventHandler } from './event-handler';
  * {@link RequestContext}, which is used to determine the channel and language
  * {@link RequestContext}, which is used to determine the channel and language
  * to use when generating the email.
  * to use when generating the email.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Email Plugin Types
  * @docsPage Email Plugin Types
  */
  */
 export type EventWithContext = VendureEvent & { ctx: RequestContext };
 export type EventWithContext = VendureEvent & { ctx: RequestContext };
@@ -25,7 +25,7 @@ export type EventWithContext = VendureEvent & { ctx: RequestContext };
  * A VendureEvent with a {@link RequestContext} and a `data` property which contains the
  * A VendureEvent with a {@link RequestContext} and a `data` property which contains the
  * value resolved from the {@link EmailEventHandler}`.loadData()` callback.
  * value resolved from the {@link EmailEventHandler}`.loadData()` callback.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Email Plugin Types
  * @docsPage Email Plugin Types
  */
  */
 export type EventWithAsyncData<Event extends EventWithContext, R> = Event & { data: R };
 export type EventWithAsyncData<Event extends EventWithContext, R> = Event & { data: R };
@@ -34,7 +34,7 @@ export type EventWithAsyncData<Event extends EventWithContext, R> = Event & { da
  * @description
  * @description
  * Configuration for the EmailPlugin.
  * Configuration for the EmailPlugin.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage EmailPluginOptions
  * @docsPage EmailPluginOptions
  * */
  * */
 export interface EmailPluginOptions {
 export interface EmailPluginOptions {
@@ -42,7 +42,7 @@ export interface EmailPluginOptions {
      * @description
      * @description
      * The path to the location of the email templates. In a default Vendure installation,
      * The path to the location of the email templates. In a default Vendure installation,
      * the templates are installed to `<project root>/vendure/email/templates`.
      * the templates are installed to `<project root>/vendure/email/templates`.
-     * 
+     *
      * @deprecated Use `templateLoader` to define a template path: `templateLoader: new FileBasedTemplateLoader('../your-path/templates')`
      * @deprecated Use `templateLoader` to define a template path: `templateLoader: new FileBasedTemplateLoader('../your-path/templates')`
      */
      */
     templatePath?: string;
     templatePath?: string;
@@ -58,7 +58,12 @@ export interface EmailPluginOptions {
      * @description
      * @description
      * Configures how the emails are sent.
      * Configures how the emails are sent.
      */
      */
-    transport: EmailTransportOptions | ((injector?: Injector, ctx?: RequestContext) => EmailTransportOptions | Promise<EmailTransportOptions>)
+    transport:
+        | EmailTransportOptions
+        | ((
+              injector?: Injector,
+              ctx?: RequestContext,
+          ) => EmailTransportOptions | Promise<EmailTransportOptions>);
     /**
     /**
      * @description
      * @description
      * An array of {@link EmailEventHandler}s which define which Vendure events will trigger
      * An array of {@link EmailEventHandler}s which define which Vendure events will trigger
@@ -99,7 +104,7 @@ export type InitializedEmailPluginOptions = EmailPluginOptions & { templateLoade
  * @description
  * @description
  * Configuration for running the EmailPlugin in development mode.
  * Configuration for running the EmailPlugin in development mode.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage EmailPluginOptions
  * @docsPage EmailPluginOptions
  */
  */
 export interface EmailPluginDevModeOptions extends Omit<EmailPluginOptions, 'transport'> {
 export interface EmailPluginDevModeOptions extends Omit<EmailPluginOptions, 'transport'> {
@@ -120,7 +125,7 @@ export interface EmailPluginDevModeOptions extends Omit<EmailPluginOptions, 'tra
  * @description
  * @description
  * A union of all the possible transport options for sending emails.
  * A union of all the possible transport options for sending emails.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Transport Options
  * @docsPage Transport Options
  */
  */
 export type EmailTransportOptions =
 export type EmailTransportOptions =
@@ -135,7 +140,7 @@ export type EmailTransportOptions =
  * @description
  * @description
  * The SMTP transport options of [Nodemailer](https://nodemailer.com/smtp/)
  * The SMTP transport options of [Nodemailer](https://nodemailer.com/smtp/)
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Transport Options
  * @docsPage Transport Options
  */
  */
 export interface SMTPTransportOptions extends SMTPTransport.Options {
 export interface SMTPTransportOptions extends SMTPTransport.Options {
@@ -184,7 +189,7 @@ export interface SMTPTransportOptions extends SMTPTransport.Options {
  *   ],
  *   ],
  * };
  * };
  *  ```
  *  ```
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Transport Options
  * @docsPage Transport Options
  */
  */
 export interface SESTransportOptions extends SESTransport.Options {
 export interface SESTransportOptions extends SESTransport.Options {
@@ -195,7 +200,7 @@ export interface SESTransportOptions extends SESTransport.Options {
  * @description
  * @description
  * Uses the local Sendmail program to send the email.
  * Uses the local Sendmail program to send the email.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Transport Options
  * @docsPage Transport Options
  */
  */
 export interface SendmailTransportOptions {
 export interface SendmailTransportOptions {
@@ -210,7 +215,7 @@ export interface SendmailTransportOptions {
  * @description
  * @description
  * Outputs the email as an HTML file for development purposes.
  * Outputs the email as an HTML file for development purposes.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Transport Options
  * @docsPage Transport Options
  */
  */
 export interface FileTransportOptions {
 export interface FileTransportOptions {
@@ -226,7 +231,7 @@ export interface FileTransportOptions {
  * Does nothing with the generated email. Intended for use in testing where we don't care about the email transport,
  * Does nothing with the generated email. Intended for use in testing where we don't care about the email transport,
  * or when using a custom {@link EmailSender} which does not require transport options.
  * or when using a custom {@link EmailSender} which does not require transport options.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Transport Options
  * @docsPage Transport Options
  */
  */
 export interface NoopTransportOptions {
 export interface NoopTransportOptions {
@@ -237,7 +242,7 @@ export interface NoopTransportOptions {
  * @description
  * @description
  * The final, generated email details to be sent.
  * The final, generated email details to be sent.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Email Plugin Types
  * @docsPage Email Plugin Types
  */
  */
 export interface EmailDetails<Type extends 'serialized' | 'unserialized' = 'unserialized'> {
 export interface EmailDetails<Type extends 'serialized' | 'unserialized' = 'unserialized'> {
@@ -255,7 +260,7 @@ export interface EmailDetails<Type extends 'serialized' | 'unserialized' = 'unse
  * @description
  * @description
  * Forwards the raw GeneratedEmailContext object to a provided callback, for use in testing.
  * Forwards the raw GeneratedEmailContext object to a provided callback, for use in testing.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Transport Options
  * @docsPage Transport Options
  */
  */
 export interface TestingTransportOptions {
 export interface TestingTransportOptions {
@@ -271,7 +276,7 @@ export interface TestingTransportOptions {
  * @description
  * @description
  * A function used to load async data for use by an {@link EmailEventHandler}.
  * A function used to load async data for use by an {@link EmailEventHandler}.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Email Plugin Types
  * @docsPage Email Plugin Types
  */
  */
 export type LoadDataFn<Event extends EventWithContext, R> = (context: {
 export type LoadDataFn<Event extends EventWithContext, R> = (context: {
@@ -290,7 +295,7 @@ export type OptionalToNullable<O> = {
  * only uses the `path` property to define a filesystem path or a URL pointing to
  * only uses the `path` property to define a filesystem path or a URL pointing to
  * the attachment file.
  * the attachment file.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Email Plugin Types
  * @docsPage Email Plugin Types
  */
  */
 export type EmailAttachment = Omit<Attachment, 'raw'> & { path?: string };
 export type EmailAttachment = Omit<Attachment, 'raw'> & { path?: string };
@@ -317,8 +322,8 @@ export type IntermediateEmailDetails = {
  * @description
  * @description
  * Configures the {@link EmailEventHandler} to handle a particular channel & languageCode
  * Configures the {@link EmailEventHandler} to handle a particular channel & languageCode
  * combination.
  * combination.
- * 
- * @deprecated Use a custom {@link TemplateLoader} instead. 
+ *
+ * @deprecated Use a custom {@link TemplateLoader} instead.
  */
  */
 export interface EmailTemplateConfig {
 export interface EmailTemplateConfig {
     /**
     /**
@@ -347,30 +352,30 @@ export interface EmailTemplateConfig {
 }
 }
 
 
 export interface LoadTemplateInput {
 export interface LoadTemplateInput {
-    type: string,
-    templateName: string
+    type: string;
+    templateName: string;
 }
 }
 
 
 export interface Partial {
 export interface Partial {
-    name: string,
-    content: string
+    name: string;
+    content: string;
 }
 }
 
 
 /**
 /**
  * @description
  * @description
  * Load an email template based on the given request context, type and template name
  * Load an email template based on the given request context, type and template name
  * and return the template as a string.
  * and return the template as a string.
- * 
+ *
  * @example
  * @example
  * ```TypeScript
  * ```TypeScript
  * import { EmailPlugin, TemplateLoader } from '@vendure/email-plugin';
  * import { EmailPlugin, TemplateLoader } from '@vendure/email-plugin';
- * 
+ *
  * class MyTemplateLoader implements TemplateLoader {
  * class MyTemplateLoader implements TemplateLoader {
  *      loadTemplate(injector, ctx, { type, templateName }){
  *      loadTemplate(injector, ctx, { type, templateName }){
  *          return myCustomTemplateFunction(ctx);
  *          return myCustomTemplateFunction(ctx);
  *      }
  *      }
  * }
  * }
- * 
+ *
  * // In vendure-config.ts:
  * // In vendure-config.ts:
  * ...
  * ...
  * EmailPlugin.init({
  * EmailPlugin.init({
@@ -379,7 +384,7 @@ export interface Partial {
  * })
  * })
  * ```
  * ```
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Custom Template Loader
  * @docsPage Custom Template Loader
  */
  */
 export interface TemplateLoader {
 export interface TemplateLoader {
@@ -388,8 +393,8 @@ export interface TemplateLoader {
      */
      */
     loadTemplate(injector: Injector, ctx: RequestContext, input: LoadTemplateInput): Promise<string>;
     loadTemplate(injector: Injector, ctx: RequestContext, input: LoadTemplateInput): Promise<string>;
     /**
     /**
-     * Load partials and return their contents. 
-     * This method is only called during initalization, i.e. during server startup. 
+     * Load partials and return their contents.
+     * This method is only called during initalization, i.e. during server startup.
      */
      */
     loadPartials?(): Promise<Partial[]>;
     loadPartials?(): Promise<Partial[]>;
 }
 }
@@ -399,7 +404,7 @@ export interface TemplateLoader {
  * A function used to define template variables available to email templates.
  * A function used to define template variables available to email templates.
  * See {@link EmailEventHandler}.setTemplateVars().
  * See {@link EmailEventHandler}.setTemplateVars().
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Email Plugin Types
  * @docsPage Email Plugin Types
  */
  */
 export type SetTemplateVarsFn<Event> = (
 export type SetTemplateVarsFn<Event> = (
@@ -413,7 +418,7 @@ export type SetTemplateVarsFn<Event> = (
  * See https://nodemailer.com/message/attachments/ for more information about
  * See https://nodemailer.com/message/attachments/ for more information about
  * how attachments work in Nodemailer.
  * how attachments work in Nodemailer.
  *
  *
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Email Plugin Types
  * @docsPage Email Plugin Types
  */
  */
 export type SetAttachmentsFn<Event> = (event: Event) => EmailAttachment[] | Promise<EmailAttachment[]>;
 export type SetAttachmentsFn<Event> = (event: Event) => EmailAttachment[] | Promise<EmailAttachment[]>;
@@ -423,7 +428,7 @@ export type SetAttachmentsFn<Event> = (event: Event) => EmailAttachment[] | Prom
  * Optional address-related fields for sending the email.
  * Optional address-related fields for sending the email.
  *
  *
  * @since 1.1.0
  * @since 1.1.0
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Email Plugin Types
  * @docsPage Email Plugin Types
  */
  */
 export interface OptionalAddressFields {
 export interface OptionalAddressFields {
@@ -449,7 +454,7 @@ export interface OptionalAddressFields {
  * A function used to set the {@link OptionalAddressFields}.
  * A function used to set the {@link OptionalAddressFields}.
  *
  *
  * @since 1.1.0
  * @since 1.1.0
- * @docsCategory EmailPlugin
+ * @docsCategory core plugins/EmailPlugin
  * @docsPage Email Plugin Types
  * @docsPage Email Plugin Types
  */
  */
 export type SetOptionalAddressFieldsFn<Event> = (
 export type SetOptionalAddressFieldsFn<Event> = (

+ 1 - 1
packages/harden-plugin/src/harden.plugin.ts

@@ -137,7 +137,7 @@ import { HardenPluginOptions } from './types';
  * verbose 16/12/22, 14:12 - [HardenPlugin] Query complexity [ProductList]: 35
  * verbose 16/12/22, 14:12 - [HardenPlugin] Query complexity [ProductList]: 35
  * ```
  * ```
  *
  *
- * @docsCategory HardenPlugin
+ * @docsCategory core plugins/HardenPlugin
  */
  */
 @VendurePlugin({
 @VendurePlugin({
     providers: [
     providers: [

+ 1 - 1
packages/harden-plugin/src/middleware/query-complexity-plugin.ts

@@ -90,7 +90,7 @@ function isAdminApi(schema: GraphQLSchema): boolean {
  * When selecting PaginatedList types, the "take" argument is used to estimate a complexity
  * When selecting PaginatedList types, the "take" argument is used to estimate a complexity
  * factor. If the "take" argument is omitted, a default factor of 1000 is applied.
  * factor. If the "take" argument is omitted, a default factor of 1000 is applied.
  *
  *
- * @docsCategory HardenPlugin
+ * @docsCategory core plugins/HardenPlugin
  */
  */
 export function defaultVendureComplexityEstimator(
 export function defaultVendureComplexityEstimator(
     customComplexityFactors: { [path: string]: number },
     customComplexityFactors: { [path: string]: number },

+ 1 - 1
packages/harden-plugin/src/types.ts

@@ -4,7 +4,7 @@ import { ComplexityEstimator } from 'graphql-query-complexity/dist/cjs/QueryComp
  * @description
  * @description
  * Options that can be passed to the `.init()` static method of the HardenPlugin.
  * Options that can be passed to the `.init()` static method of the HardenPlugin.
  *
  *
- * @docsCategory HardenPlugin
+ * @docsCategory core plugins/HardenPlugin
  */
  */
 export interface HardenPluginOptions {
 export interface HardenPluginOptions {
     /**
     /**

+ 1 - 1
packages/job-queue-plugin/src/bullmq/bullmq-job-queue-strategy.ts

@@ -26,7 +26,7 @@ const DEFAULT_CONCURRENCY = 3;
  * This JobQueueStrategy uses [BullMQ](https://docs.bullmq.io/) to implement a push-based job queue
  * This JobQueueStrategy uses [BullMQ](https://docs.bullmq.io/) to implement a push-based job queue
  * on top of Redis. It should not be used alone, but as part of the {@link BullMQJobQueuePlugin}.
  * on top of Redis. It should not be used alone, but as part of the {@link BullMQJobQueuePlugin}.
  *
  *
- * @docsCategory job-queue-plugin
+ * @docsCategory core plugins/JobQueuePlugin
  */
  */
 export class BullMQJobQueueStrategy implements InspectableJobQueueStrategy {
 export class BullMQJobQueueStrategy implements InspectableJobQueueStrategy {
     private redisConnection: Redis | Cluster;
     private redisConnection: Redis | Cluster;

+ 1 - 1
packages/job-queue-plugin/src/bullmq/plugin.ts

@@ -97,7 +97,7 @@ import { BullMQPluginOptions } from './types';
  * };
  * };
  * ```
  * ```
  *
  *
- * @docsCategory job-queue-plugin
+ * @docsCategory core plugins/JobQueuePlugin
  */
  */
 @VendurePlugin({
 @VendurePlugin({
     imports: [PluginCommonModule],
     imports: [PluginCommonModule],

+ 2 - 2
packages/job-queue-plugin/src/bullmq/types.ts

@@ -7,7 +7,7 @@ import { QueueOptions } from 'bullmq';
  * Configuration options for the {@link BullMQJobQueuePlugin}.
  * Configuration options for the {@link BullMQJobQueuePlugin}.
  *
  *
  * @since 1.2.0
  * @since 1.2.0
- * @docsCategory job-queue-plugin
+ * @docsCategory core plugins/JobQueuePlugin
  * @docsPage BullMQPluginOptions
  * @docsPage BullMQPluginOptions
  * @docsWeight 0
  * @docsWeight 0
  */
  */
@@ -84,7 +84,7 @@ export interface BullMQPluginOptions {
  * Configuration for the backoff function when retrying failed jobs.
  * Configuration for the backoff function when retrying failed jobs.
  *
  *
  * @since 1.3.0
  * @since 1.3.0
- * @docsCategory job-queue-plugin
+ * @docsCategory core plugins/JobQueuePlugin
  * @docsPage BullMQPluginOptions
  * @docsPage BullMQPluginOptions
  * @docsWeight 1
  * @docsWeight 1
  */
  */

+ 1 - 1
packages/payments-plugin/src/braintree/braintree.plugin.ts

@@ -235,7 +235,7 @@ import { BraintreePluginOptions } from './types';
  *   );
  *   );
  * ```
  * ```
  *
  *
- * @docsCategory payments-plugin
+ * @docsCategory core plugins/PaymentsPlugin
  * @docsPage BraintreePlugin
  * @docsPage BraintreePlugin
  */
  */
 @VendurePlugin({
 @VendurePlugin({

+ 2 - 2
packages/payments-plugin/src/braintree/types.ts

@@ -5,7 +5,7 @@ import { Environment, Transaction } from 'braintree';
 
 
 import { braintreePaymentMethodHandler } from './braintree.handler';
 import { braintreePaymentMethodHandler } from './braintree.handler';
 
 
-export type PaymentMethodArgsHash = ConfigArgValues<typeof braintreePaymentMethodHandler['args']>;
+export type PaymentMethodArgsHash = ConfigArgValues<(typeof braintreePaymentMethodHandler)['args']>;
 
 
 // Note: deep import is necessary here because CustomCustomerFields is also extended in the Stripe
 // Note: deep import is necessary here because CustomCustomerFields is also extended in the Stripe
 // plugin. Reference: https://github.com/microsoft/TypeScript/issues/46617
 // plugin. Reference: https://github.com/microsoft/TypeScript/issues/46617
@@ -19,7 +19,7 @@ declare module '@vendure/core/dist/entity/custom-entity-fields' {
  * @description
  * @description
  * Options for the Braintree plugin.
  * Options for the Braintree plugin.
  *
  *
- * @docsCategory payments-plugin
+ * @docsCategory core plugins/PaymentsPlugin
  * @docsPage BraintreePlugin
  * @docsPage BraintreePlugin
  */
  */
 export interface BraintreePluginOptions {
 export interface BraintreePluginOptions {

+ 8 - 8
packages/payments-plugin/src/mollie/mollie.plugin.ts

@@ -11,7 +11,7 @@ import { MollieService } from './mollie.service';
  * @description
  * @description
  * Configuration options for the Mollie payments plugin.
  * Configuration options for the Mollie payments plugin.
  *
  *
- * @docsCategory payments-plugin
+ * @docsCategory core plugins/PaymentsPlugin
  * @docsPage MolliePlugin
  * @docsPage MolliePlugin
  */
  */
 export interface MolliePluginOptions {
 export interface MolliePluginOptions {
@@ -26,9 +26,9 @@ export interface MolliePluginOptions {
      * @description
      * @description
      * For backwards compatibility, by default set to false.
      * For backwards compatibility, by default set to false.
      * This option will be deprecated in a future version.
      * This option will be deprecated in a future version.
-     * When enabled, the `redirectUrl` can be passed via the `createPaymentIntent` mutation 
+     * When enabled, the `redirectUrl` can be passed via the `createPaymentIntent` mutation
      * instead of being configured in the Payment Method.
      * instead of being configured in the Payment Method.
-     * 
+     *
      * @default false
      * @default false
      * @since 2.0.0
      * @since 2.0.0
      */
      */
@@ -67,15 +67,15 @@ export interface MolliePluginOptions {
  * 3. Set your Mollie apiKey in the `API Key` field.
  * 3. Set your Mollie apiKey in the `API Key` field.
  *
  *
  * ## Specifying the redirectUrl
  * ## Specifying the redirectUrl
- * 
+ *
  * Currently, there are two ways to specify the `redirectUrl` to which the customer is redirected after completing the payment:
  * Currently, there are two ways to specify the `redirectUrl` to which the customer is redirected after completing the payment:
- * 1. Configure the `redirectUrl` in the PaymentMethod. 
+ * 1. Configure the `redirectUrl` in the PaymentMethod.
  * 2. Pass the `redirectUrl` as an argument to the `createPaymentIntent` mutation.
  * 2. Pass the `redirectUrl` as an argument to the `createPaymentIntent` mutation.
- * 
+ *
  * Which method is used depends on the value of the `useDynamicRedirectUrl` option while initializing the plugin.
  * Which method is used depends on the value of the `useDynamicRedirectUrl` option while initializing the plugin.
  * By default, this option is set to `false` for backwards compatibility. In a future version, this option will be deprecated.
  * By default, this option is set to `false` for backwards compatibility. In a future version, this option will be deprecated.
  * Upon deprecation, the `redirectUrl` will always be passed as an argument to the `createPaymentIntent` mutation.
  * Upon deprecation, the `redirectUrl` will always be passed as an argument to the `createPaymentIntent` mutation.
- * 
+ *
  * TODO toevoegen van /code weggehaald..!
  * TODO toevoegen van /code weggehaald..!
  * ## Storefront usage
  * ## Storefront usage
  *
  *
@@ -144,7 +144,7 @@ export interface MolliePluginOptions {
  * If you don't want this behaviour (Authorized first), you can set 'autoCapture=true' on the payment method. This option will immediately
  * If you don't want this behaviour (Authorized first), you can set 'autoCapture=true' on the payment method. This option will immediately
  * capture the payment after a customer authorizes the payment.
  * capture the payment after a customer authorizes the payment.
  *
  *
- * @docsCategory payments-plugin
+ * @docsCategory core plugins/PaymentsPlugin
  * @docsPage MolliePlugin
  * @docsPage MolliePlugin
  * @docsWeight 0
  * @docsWeight 0
  */
  */

+ 1 - 1
packages/payments-plugin/src/stripe/stripe.plugin.ts

@@ -153,7 +153,7 @@ import { StripePluginOptions } from './types';
  *    ```
  *    ```
  * 4. The Stripe CLI will create a webhook signing secret you can then use in your config of the StripePlugin.
  * 4. The Stripe CLI will create a webhook signing secret you can then use in your config of the StripePlugin.
  *
  *
- * @docsCategory payments-plugin
+ * @docsCategory core plugins/PaymentsPlugin
  * @docsPage StripePlugin
  * @docsPage StripePlugin
  */
  */
 @VendurePlugin({
 @VendurePlugin({

+ 1 - 1
packages/payments-plugin/src/stripe/types.ts

@@ -15,7 +15,7 @@ declare module '@vendure/core/dist/entity/custom-entity-fields' {
  * @description
  * @description
  * Configuration options for the Stripe payments plugin.
  * Configuration options for the Stripe payments plugin.
  *
  *
- * @docsCategory payments-plugin
+ * @docsCategory core plugins/PaymentsPlugin
  * @docsPage StripePlugin
  * @docsPage StripePlugin
  */
  */
 export interface StripePluginOptions {
 export interface StripePluginOptions {

+ 9 - 9
scripts/docs/generate-graphql-docs.ts

@@ -48,11 +48,11 @@ generateGraphqlDocs(outputPath);
 
 
 function generateGraphqlDocs(hugoOutputPath: string) {
 function generateGraphqlDocs(hugoOutputPath: string) {
     const timeStart = +new Date();
     const timeStart = +new Date();
-    let queriesOutput = generateFrontMatter('Queries', 1) + `\n\n# Queries\n\n`;
-    let mutationsOutput = generateFrontMatter('Mutations', 2) + `\n\n# Mutations\n\n`;
-    let objectTypesOutput = generateFrontMatter('Types', 3) + `\n\n# Types\n\n`;
-    let inputTypesOutput = generateFrontMatter('Input Objects', 4) + `\n\n# Input Objects\n\n`;
-    let enumsOutput = generateFrontMatter('Enums', 5) + `\n\n# Enums\n\n`;
+    let queriesOutput = generateFrontMatter('Queries', 1) + '\n\n# Queries\n\n';
+    let mutationsOutput = generateFrontMatter('Mutations', 2) + '\n\n# Mutations\n\n';
+    let objectTypesOutput = generateFrontMatter('Types', 3) + '\n\n# Types\n\n';
+    let inputTypesOutput = generateFrontMatter('Input Objects', 4) + '\n\n# Input Objects\n\n';
+    let enumsOutput = generateFrontMatter('Enums', 5) + '\n\n# Enums\n\n';
     const sortByName = (a: { name: string }, b: { name: string }) => (a.name < b.name ? -1 : 1);
     const sortByName = (a: { name: string }, b: { name: string }) => (a.name < b.name ? -1 : 1);
     const sortedTypes = Object.values(schema.getTypeMap()).sort(sortByName);
     const sortedTypes = Object.values(schema.getTypeMap()).sort(sortByName);
     for (const type of sortedTypes) {
     for (const type of sortedTypes) {
@@ -81,7 +81,7 @@ function generateGraphqlDocs(hugoOutputPath: string) {
                 objectTypesOutput += `## ${type.name}\n\n`;
                 objectTypesOutput += `## ${type.name}\n\n`;
                 objectTypesOutput += renderDescription(type);
                 objectTypesOutput += renderDescription(type);
                 objectTypesOutput += renderFields(type);
                 objectTypesOutput += renderFields(type);
-                objectTypesOutput += `\n`;
+                objectTypesOutput += '\n';
             }
             }
         }
         }
 
 
@@ -106,7 +106,7 @@ function generateGraphqlDocs(hugoOutputPath: string) {
             inputTypesOutput += `## ${type.name}\n\n`;
             inputTypesOutput += `## ${type.name}\n\n`;
             inputTypesOutput += renderDescription(type);
             inputTypesOutput += renderDescription(type);
             inputTypesOutput += renderFields(type);
             inputTypesOutput += renderFields(type);
-            inputTypesOutput += `\n`;
+            inputTypesOutput += '\n';
         }
         }
 
 
         if (isUnionType(type)) {
         if (isUnionType(type)) {
@@ -203,7 +203,7 @@ function unwrapType(type: GraphQLType): GraphQLNamedType {
     if (isNamedType(type)) {
     if (isNamedType(type)) {
         return type;
         return type;
     }
     }
-    let innerType = type;
+    let innerType = type as GraphQLType;
     while (!isNamedType(innerType)) {
     while (!isNamedType(innerType)) {
         innerType = innerType.ofType;
         innerType = innerType.ofType;
     }
     }
@@ -213,7 +213,7 @@ function unwrapType(type: GraphQLType): GraphQLNamedType {
 function getTargetApiFromArgs(): TargetApi {
 function getTargetApiFromArgs(): TargetApi {
     const apiArg = process.argv.find(arg => /--api=(shop|admin)/.test(arg));
     const apiArg = process.argv.find(arg => /--api=(shop|admin)/.test(arg));
     if (!apiArg) {
     if (!apiArg) {
-        console.error(`\nPlease specify which GraphQL API to generate docs for: --api=<shop|admin>\n`);
+        console.error('\nPlease specify which GraphQL API to generate docs for: --api=<shop|admin>\n');
         process.exit(1);
         process.exit(1);
         return null as never;
         return null as never;
     }
     }

+ 1 - 1
scripts/docs/generate-typescript-docs.ts

@@ -84,7 +84,7 @@ function generateTypescriptDocs(config: DocsSectionConfig[], isWatchMode: boolea
                 globalTypeMap.set(declaration.title, pathToTypeDoc);
                 globalTypeMap.set(declaration.title, pathToTypeDoc);
             }
             }
         }
         }
-        const docsUrl = `/docs`;
+        const docsUrl = ``;
         const generatedCount = new TypescriptDocsRenderer().render(
         const generatedCount = new TypescriptDocsRenderer().render(
             docsPages,
             docsPages,
             docsUrl,
             docsUrl,

+ 1 - 1
scripts/docs/typescript-docgen-types.ts

@@ -29,7 +29,7 @@ export interface MethodInfo extends MemberInfo {
 
 
 export interface DocsPage {
 export interface DocsPage {
     title: string;
     title: string;
-    category: string;
+    category: string[];
     declarations: ParsedDeclaration[];
     declarations: ParsedDeclaration[];
     fileName: string;
     fileName: string;
 }
 }

+ 9 - 5
scripts/docs/typescript-docs-parser.ts

@@ -20,7 +20,7 @@ import {
  */
  */
 export class TypescriptDocsParser {
 export class TypescriptDocsParser {
     private readonly atTokenPlaceholder = '__EscapedAtToken__';
     private readonly atTokenPlaceholder = '__EscapedAtToken__';
-    private readonly commentBlockEndTokenPlaceholder = '__EscapedCommentBlockEndToken__'
+    private readonly commentBlockEndTokenPlaceholder = '__EscapedCommentBlockEndToken__';
 
 
     /**
     /**
      * Parses the TypeScript files given by the filePaths array and returns the
      * Parses the TypeScript files given by the filePaths array and returns the
@@ -58,7 +58,7 @@ export class TypescriptDocsParser {
                     const fileName = normalizedTitle === declaration.category ? '_index' : normalizedTitle;
                     const fileName = normalizedTitle === declaration.category ? '_index' : normalizedTitle;
                     pages.set(pageTitle, {
                     pages.set(pageTitle, {
                         title: pageTitle,
                         title: pageTitle,
-                        category: declaration.category,
+                        category: declaration.category.split('/'),
                         declarations: [declaration],
                         declarations: [declaration],
                         fileName,
                         fileName,
                     });
                     });
@@ -156,7 +156,7 @@ export class TypescriptDocsParser {
         } else if (ts.isEnumDeclaration(statement)) {
         } else if (ts.isEnumDeclaration(statement)) {
             return {
             return {
                 ...info,
                 ...info,
-                kind: 'enum' as 'enum',
+                kind: 'enum' as const,
                 members: this.parseMembers(statement.members) as PropertyInfo[],
                 members: this.parseMembers(statement.members) as PropertyInfo[],
             };
             };
         } else if (ts.isFunctionDeclaration(statement)) {
         } else if (ts.isFunctionDeclaration(statement)) {
@@ -457,13 +457,17 @@ export class TypescriptDocsParser {
      * pattern in descriptions and therefore it is supported as long as the leading '/' is escaped ("\/").
      * pattern in descriptions and therefore it is supported as long as the leading '/' is escaped ("\/").
      */
      */
     private replaceEscapedTokens(content: string): string {
     private replaceEscapedTokens(content: string): string {
-        return content.replace(/\\@/g, this.atTokenPlaceholder).replace(/\\\/\*/g, this.commentBlockEndTokenPlaceholder);
+        return content
+            .replace(/\\@/g, this.atTokenPlaceholder)
+            .replace(/\\\/\*/g, this.commentBlockEndTokenPlaceholder);
     }
     }
 
 
     /**
     /**
      * Restores "@" and "/*" tokens which were replaced by the replaceEscapedTokens() method.
      * Restores "@" and "/*" tokens which were replaced by the replaceEscapedTokens() method.
      */
      */
     private restoreTokens(content: string): string {
     private restoreTokens(content: string): string {
-        return content.replace(new RegExp(this.atTokenPlaceholder, 'g'), '@').replace(new RegExp(this.commentBlockEndTokenPlaceholder, 'g'), '/*');
+        return content
+            .replace(new RegExp(this.atTokenPlaceholder, 'g'), '@')
+            .replace(new RegExp(this.commentBlockEndTokenPlaceholder, 'g'), '/*');
     }
     }
 }
 }

+ 47 - 42
scripts/docs/typescript-docs-renderer.ts

@@ -32,6 +32,7 @@ export class TypescriptDocsRenderer {
             markdown += `\n# ${page.title}\n`;
             markdown += `\n# ${page.title}\n`;
             const declarationsByWeight = page.declarations.sort((a, b) => a.weight - b.weight);
             const declarationsByWeight = page.declarations.sort((a, b) => a.weight - b.weight);
             for (const info of declarationsByWeight) {
             for (const info of declarationsByWeight) {
+                markdown += '<div class="symbol">\n';
                 switch (info.kind) {
                 switch (info.kind) {
                     case 'interface':
                     case 'interface':
                         markdown += this.renderInterfaceOrClass(info, typeMap, docsUrl);
                         markdown += this.renderInterfaceOrClass(info, typeMap, docsUrl);
@@ -54,19 +55,23 @@ export class TypescriptDocsRenderer {
                     default:
                     default:
                         assertNever(info);
                         assertNever(info);
                 }
                 }
-                // markdown += '{{< /declaration >}}\n';
+                markdown += '</div>\n';
             }
             }
 
 
-            const categoryDir = path.join(outputPath, page.category);
-            const indexFile = path.join(categoryDir, '_index.md');
+            const categoryDir = path.join(outputPath, ...page.category);
             if (!fs.existsSync(categoryDir)) {
             if (!fs.existsSync(categoryDir)) {
                 fs.mkdirsSync(categoryDir);
                 fs.mkdirsSync(categoryDir);
             }
             }
-            if (!fs.existsSync(indexFile)) {
-                const indexFileContent =
-                    generateFrontMatter(page.category, 10, false) + `\n\n# ${page.category}`;
-                fs.writeFileSync(indexFile, indexFileContent);
-                generatedCount++;
+            const pathParts = [];
+            for (const subCategory of page.category) {
+                pathParts.push(subCategory);
+                const indexFile = path.join(outputPath, ...pathParts, '_index.md');
+                if (!fs.existsSync(indexFile)) {
+                    const indexFileContent =
+                        generateFrontMatter(subCategory, 10, false) + `\n\n# ${subCategory}`;
+                    fs.writeFileSync(indexFile, indexFileContent);
+                    generatedCount++;
+                }
             }
             }
 
 
             fs.writeFileSync(path.join(categoryDir, page.fileName + '.md'), markdown);
             fs.writeFileSync(path.join(categoryDir, page.fileName + '.md'), markdown);
@@ -88,19 +93,19 @@ export class TypescriptDocsRenderer {
         output += `\n\n# ${title}\n\n`;
         output += `\n\n# ${title}\n\n`;
         output += this.renderGenerationInfoShortcode(info);
         output += this.renderGenerationInfoShortcode(info);
         output += `${this.renderDescription(description, knownTypeMap, docsUrl)}\n\n`;
         output += `${this.renderDescription(description, knownTypeMap, docsUrl)}\n\n`;
-        output += `## Signature\n\n`;
+        output += '## Signature\n\n';
         output +=
         output +=
             info.kind === 'interface' ? this.renderInterfaceSignature(info) : this.renderClassSignature(info);
             info.kind === 'interface' ? this.renderInterfaceSignature(info) : this.renderClassSignature(info);
         if (info.extendsClause) {
         if (info.extendsClause) {
-            output += `## Extends\n\n`;
+            output += '## Extends\n\n';
             output += `${this.renderHeritageClause(info.extendsClause, knownTypeMap, docsUrl)}\n`;
             output += `${this.renderHeritageClause(info.extendsClause, knownTypeMap, docsUrl)}\n`;
         }
         }
         if (info.kind === 'class' && info.implementsClause) {
         if (info.kind === 'class' && info.implementsClause) {
-            output += `## Implements\n\n`;
+            output += '## Implements\n\n';
             output += `${this.renderHeritageClause(info.implementsClause, knownTypeMap, docsUrl)}\n`;
             output += `${this.renderHeritageClause(info.implementsClause, knownTypeMap, docsUrl)}\n`;
         }
         }
         if (info.members && info.members.length) {
         if (info.members && info.members.length) {
-            output += `## Members\n\n`;
+            output += '## Members\n\n';
             output += `${this.renderMembers(info, knownTypeMap, docsUrl)}\n`;
             output += `${this.renderMembers(info, knownTypeMap, docsUrl)}\n`;
         }
         }
         return output;
         return output;
@@ -115,10 +120,10 @@ export class TypescriptDocsRenderer {
         output += `\n\n# ${title}\n\n`;
         output += `\n\n# ${title}\n\n`;
         output += this.renderGenerationInfoShortcode(typeAliasInfo);
         output += this.renderGenerationInfoShortcode(typeAliasInfo);
         output += `${this.renderDescription(description, knownTypeMap, docsUrl)}\n\n`;
         output += `${this.renderDescription(description, knownTypeMap, docsUrl)}\n\n`;
-        output += `## Signature\n\n`;
+        output += '## Signature\n\n';
         output += this.renderTypeAliasSignature(typeAliasInfo);
         output += this.renderTypeAliasSignature(typeAliasInfo);
         if (typeAliasInfo.members && typeAliasInfo.members.length) {
         if (typeAliasInfo.members && typeAliasInfo.members.length) {
-            output += `## Members\n\n`;
+            output += '## Members\n\n';
             output += `${this.renderMembers(typeAliasInfo, knownTypeMap, docsUrl)}\n`;
             output += `${this.renderMembers(typeAliasInfo, knownTypeMap, docsUrl)}\n`;
         }
         }
         return output;
         return output;
@@ -130,7 +135,7 @@ export class TypescriptDocsRenderer {
         output += `\n\n# ${title}\n\n`;
         output += `\n\n# ${title}\n\n`;
         output += this.renderGenerationInfoShortcode(enumInfo);
         output += this.renderGenerationInfoShortcode(enumInfo);
         output += `${this.renderDescription(description, knownTypeMap, docsUrl)}\n\n`;
         output += `${this.renderDescription(description, knownTypeMap, docsUrl)}\n\n`;
-        output += `## Signature\n\n`;
+        output += '## Signature\n\n';
         output += this.renderEnumSignature(enumInfo);
         output += this.renderEnumSignature(enumInfo);
         return output;
         return output;
     }
     }
@@ -141,10 +146,10 @@ export class TypescriptDocsRenderer {
         output += `\n\n# ${title}\n\n`;
         output += `\n\n# ${title}\n\n`;
         output += this.renderGenerationInfoShortcode(functionInfo);
         output += this.renderGenerationInfoShortcode(functionInfo);
         output += `${this.renderDescription(description, knownTypeMap, docsUrl)}\n\n`;
         output += `${this.renderDescription(description, knownTypeMap, docsUrl)}\n\n`;
-        output += `## Signature\n\n`;
+        output += '## Signature\n\n';
         output += this.renderFunctionSignature(functionInfo, knownTypeMap);
         output += this.renderFunctionSignature(functionInfo, knownTypeMap);
         if (parameters.length) {
         if (parameters.length) {
-            output += `## Parameters\n\n`;
+            output += '## Parameters\n\n';
             output += this.renderFunctionParams(parameters, knownTypeMap, docsUrl);
             output += this.renderFunctionParams(parameters, knownTypeMap, docsUrl);
         }
         }
         return output;
         return output;
@@ -165,15 +170,15 @@ export class TypescriptDocsRenderer {
     private renderInterfaceSignature(interfaceInfo: InterfaceInfo): string {
     private renderInterfaceSignature(interfaceInfo: InterfaceInfo): string {
         const { fullText, members } = interfaceInfo;
         const { fullText, members } = interfaceInfo;
         let output = '';
         let output = '';
-        output += `\`\`\`TypeScript\n`;
+        output += '```TypeScript\n';
         output += `interface ${fullText} `;
         output += `interface ${fullText} `;
         if (interfaceInfo.extendsClause) {
         if (interfaceInfo.extendsClause) {
             output += interfaceInfo.extendsClause.getText() + ' ';
             output += interfaceInfo.extendsClause.getText() + ' ';
         }
         }
-        output += `{\n`;
-        output += members.map(member => `  ${member.fullText}`).join(`\n`);
-        output += `\n}\n`;
-        output += `\`\`\`\n`;
+        output += '{\n';
+        output += members.map(member => `  ${member.fullText}`).join('\n');
+        output += '\n}\n';
+        output += '```\n';
 
 
         return output;
         return output;
     }
     }
@@ -181,7 +186,7 @@ export class TypescriptDocsRenderer {
     private renderClassSignature(classInfo: ClassInfo): string {
     private renderClassSignature(classInfo: ClassInfo): string {
         const { fullText, members } = classInfo;
         const { fullText, members } = classInfo;
         let output = '';
         let output = '';
-        output += `\`\`\`TypeScript\n`;
+        output += '```TypeScript\n';
         output += `class ${fullText} `;
         output += `class ${fullText} `;
         if (classInfo.extendsClause) {
         if (classInfo.extendsClause) {
             output += classInfo.extendsClause.getText() + ' ';
             output += classInfo.extendsClause.getText() + ' ';
@@ -189,7 +194,7 @@ export class TypescriptDocsRenderer {
         if (classInfo.implementsClause) {
         if (classInfo.implementsClause) {
             output += classInfo.implementsClause.getText() + ' ';
             output += classInfo.implementsClause.getText() + ' ';
         }
         }
-        output += `{\n`;
+        output += '{\n';
         const renderModifiers = (modifiers: string[]) => (modifiers.length ? modifiers.join(' ') + ' ' : '');
         const renderModifiers = (modifiers: string[]) => (modifiers.length ? modifiers.join(' ') + ' ' : '');
         output += members
         output += members
             .map(member => {
             .map(member => {
@@ -206,9 +211,9 @@ export class TypescriptDocsRenderer {
                     return `  ${renderModifiers(member.modifiers)}${member.fullText}`;
                     return `  ${renderModifiers(member.modifiers)}${member.fullText}`;
                 }
                 }
             })
             })
-            .join(`\n`);
-        output += `\n}\n`;
-        output += `\`\`\`\n`;
+            .join('\n');
+        output += '\n}\n';
+        output += '```\n';
 
 
         return output;
         return output;
     }
     }
@@ -216,36 +221,36 @@ export class TypescriptDocsRenderer {
     private renderTypeAliasSignature(typeAliasInfo: TypeAliasInfo): string {
     private renderTypeAliasSignature(typeAliasInfo: TypeAliasInfo): string {
         const { fullText, members, type } = typeAliasInfo;
         const { fullText, members, type } = typeAliasInfo;
         let output = '';
         let output = '';
-        output += `\`\`\`TypeScript\n`;
+        output += '```TypeScript\n';
         output += `type ${fullText} = `;
         output += `type ${fullText} = `;
         if (members) {
         if (members) {
-            output += `{\n`;
-            output += members.map(member => `  ${member.fullText}`).join(`\n`);
-            output += `\n}\n`;
+            output += '{\n';
+            output += members.map(member => `  ${member.fullText}`).join('\n');
+            output += '\n}\n';
         } else {
         } else {
-            output += type.getText() + `\n`;
+            output += type.getText() + '\n';
         }
         }
-        output += `\`\`\`\n`;
+        output += '```\n';
         return output;
         return output;
     }
     }
 
 
     private renderEnumSignature(enumInfo: EnumInfo): string {
     private renderEnumSignature(enumInfo: EnumInfo): string {
         const { fullText, members } = enumInfo;
         const { fullText, members } = enumInfo;
         let output = '';
         let output = '';
-        output += `\`\`\`TypeScript\n`;
+        output += '```TypeScript\n';
         output += `enum ${fullText} `;
         output += `enum ${fullText} `;
         if (members) {
         if (members) {
-            output += `{\n`;
+            output += '{\n';
             output += members
             output += members
                 .map(member => {
                 .map(member => {
                     let line = member.description ? `  // ${member.description}\n` : '';
                     let line = member.description ? `  // ${member.description}\n` : '';
                     line += `  ${member.fullText}`;
                     line += `  ${member.fullText}`;
                     return line;
                     return line;
                 })
                 })
-                .join(`\n`);
-            output += `\n}\n`;
+                .join('\n');
+            output += '\n}\n';
         }
         }
-        output += `\`\`\`\n`;
+        output += '```\n';
         return output;
         return output;
     }
     }
 
 
@@ -253,9 +258,9 @@ export class TypescriptDocsRenderer {
         const { fullText, parameters, type } = functionInfo;
         const { fullText, parameters, type } = functionInfo;
         const args = parameters.map(p => this.renderParameter(p, p.type)).join(', ');
         const args = parameters.map(p => this.renderParameter(p, p.type)).join(', ');
         let output = '';
         let output = '';
-        output += `\`\`\`TypeScript\n`;
+        output += '```TypeScript\n';
         output += `function ${fullText}(${args}): ${type ? type.getText() : 'void'}\n`;
         output += `function ${fullText}(${args}): ${type ? type.getText() : 'void'}\n`;
-        output += `\`\`\`\n`;
+        output += '```\n';
         return output;
         return output;
     }
     }
 
 
@@ -307,7 +312,7 @@ export class TypescriptDocsRenderer {
                 experimentalParam = 'experimental="true"';
                 experimentalParam = 'experimental="true"';
             }
             }
             output += `### ${member.name}\n\n`;
             output += `### ${member.name}\n\n`;
-            output += `{{< member-info kind="${[...member.modifiers, member.kind].join(
+            output += `{{< member-info kind="${[member.kind].join(
                 ' ',
                 ' ',
             )}" type="${type}" ${defaultParam} ${sinceParam}${experimentalParam}>}}\n\n`;
             )}" type="${type}" ${defaultParam} ${sinceParam}${experimentalParam}>}}\n\n`;
             output += `{{< member-description >}}${this.renderDescription(
             output += `{{< member-description >}}${this.renderDescription(
@@ -340,7 +345,7 @@ export class TypescriptDocsRenderer {
         }
         }
         let experimental = '';
         let experimental = '';
         if (info.experimental) {
         if (info.experimental) {
-            experimental = ` experimental="true"`;
+            experimental = ' experimental="true"';
         }
         }
         return `{{< generation-info sourceFile="${sourceFile}" sourceLine="${info.sourceLine}" packageName="${info.packageName}"${sinceData}${experimental}>}}\n\n`;
         return `{{< generation-info sourceFile="${sourceFile}" sourceLine="${info.sourceLine}" packageName="${info.packageName}"${sinceData}${experimental}>}}\n\n`;
     }
     }