diff --git a/.github/workflows/close_stale.yml b/.github/workflows/close_stale.yml
deleted file mode 100644
index d5cd3cf1..00000000
--- a/.github/workflows/close_stale.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: Close inactive issues
-on:
- schedule:
- - cron: "30 1 * * *"
-
-jobs:
- close-issues:
- runs-on: ubuntu-latest
- permissions:
- issues: write
- pull-requests: write
- steps:
- - uses: actions/stale@v5
- with:
- days-before-issue-stale: 30
- days-before-issue-close: 14
- stale-issue-label: "stale"
- stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
- close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
- days-before-pr-stale: -1
- days-before-pr-close: -1
- repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/README.md b/README.md
index e5902fdd..9ab183d5 100644
--- a/README.md
+++ b/README.md
@@ -9,10 +9,64 @@
ApexDocs is a non-opinionated documentation generator for Salesforce Apex classes.
-It can output documentation in Markdown
-format,
-which allows you to use the Static Site Generator of your choice to create a documentation site that fits your needs,
-hosted in any static web hosting service.
+It can output documentation in Markdown format, which allows you to use the Static Site Generator of your choice to
+create a documentation site that fits your needs, hosted in any static web hosting service.
+
+## 👀 Examples
+
+ApexDocs generates Markdown files, which can be integrated into any Static Site Generation (SSG) engine,
+(e.g. Jekyll, Vitepress, Hugo, Docosaurus, etc.) to create a documentation site that fits your needs.
+
+This gives you the flexibility to create beautiful leveraging your preferred SSG engine, which
+usually provides a wide range of themes, dark mode support, and other features out of the box.
+
+
+

+

+
+
+*These are examples of documentation sites generated using Vitepress.
+Head over to the `examples/vitepress` folder to see the code.*
+
+The extra flexibility also lets you integrate the output documentation with your existing documentation site,
+allowing you to match the look and feel of your existing site.
+
+
+

+
+
+OpenApi REST definitions can be visualized using a tool like ReDoc, Swagger UI, or any other OpenApi viewer.
+
+
+

+
+
+This repo contains several other example implementations in the `examples` directory, showcasing how to integrate
+with different tools.
+
+* [Examples](./examples)
+
+### In the wild
+
+Here are some live projects using ApexDocs:
+
+- [Trailhead Apex Recipes](https://github.com/trailheadapps/apex-recipes)
+- [Salesforce Commerce Apex Reference](https://developer.salesforce.com/docs/commerce/salesforce-commerce/references/comm-apex-reference/cart-reference.html)
+- [Expression (API)](https://cesarparra.github.io/expression/)
+- [Nimble AMS Docs](https://nimbleuser.github.io/nams-api-docs/#/api-reference/)
+
+## 🚀 Features
+
+* Generate documentation for Salesforce Apex classes as Markdown files
+* Generate an OpenApi REST specification based on `@RestResource` classes
+* Generate a changelog based on the differences between two versions of your Salesforce Apex classes
+* Support for grouping blocks of related code within a class
+* Support for ignoring files and members from being documented
+* Namespace support
+* Configuration file support
+* Single line ApexDoc Blocks
+* Custom tag support
+* And much, much more!
## 💿 Installation
@@ -49,38 +103,6 @@ Run the following command to generate a changelog for your Salesforce Apex class
apexdocs changelog --previousVersionDir force-app-previous --currentVersionDir force-app
```
-## 🚀 Features
-
-* Generate documentation for Salesforce Apex classes as Markdown files
-* Generate an OpenApi REST specification based on `@RestResource` classes
-* Generate a changelog based on the differences between two versions of your Salesforce Apex classes
-* Support for grouping blocks of related code within a class
-* Support for ignoring files and members from being documented
-* Namespace support
-* Configuration file support
-* Single line ApexDoc Blocks
-* Custom tag support
-* And much, much more!
-
-## 👀 Demo
-
-ApexDocs generates Markdown files, which can be integrated into any Static Site Generation engine,
-(e.g. Jekyll, Vitepress, Hugo, Docosaurus, etc.) to create a documentation site that fits your needs.
-
-This repo contains several example implementations in the `examples` directory, showcasing how to integrate
-with some of these tools.
-
-* [Examples](./examples)
-
-### In the wild
-
-Here are some live projects using ApexDocs:
-
-- [Trailhead Apex Recipes](https://github.com/trailheadapps/apex-recipes)
-- [Salesforce Commerce Apex Reference](https://developer.salesforce.com/docs/commerce/salesforce-commerce/references/comm-apex-reference/cart-reference.html)
-- [Expression (API)](https://cesarparra.github.io/expression/)
-- [Nimble AMS Docs](https://nimbleuser.github.io/nams-api-docs/#/api-reference/)
-
## ▶️ Available Commands
### Markdown
diff --git a/examples/open-api/docs/openapi.json b/examples/open-api/docs/openapi.json
index 43c4b59f..f2e60a56 100644
--- a/examples/open-api/docs/openapi.json
+++ b/examples/open-api/docs/openapi.json
@@ -10,7 +10,7 @@
}
],
"paths": {
- "AccountService/": {
+ "/AccountService/": {
"description": "Account related operations",
"get": {
"tags": [
@@ -338,7 +338,7 @@
}
}
},
- "Contact/": {
+ "/Contact/": {
"description": "Contact related operations",
"get": {
"tags": [
@@ -359,7 +359,7 @@
}
}
},
- "Order/": {
+ "/Order/": {
"description": "Order related operations",
"get": {
"tags": [
diff --git a/examples/open-api/force-app/main/default/classes/dto/ChildClass.cls-meta.xml b/examples/open-api/force-app/main/default/classes/dto/ChildClass.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/dto/ChildClass.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/examples/open-api/force-app/main/default/classes/dto/Reference1.cls-meta.xml b/examples/open-api/force-app/main/default/classes/dto/Reference1.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/dto/Reference1.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/examples/open-api/force-app/main/default/classes/dto/Reference2.cls-meta.xml b/examples/open-api/force-app/main/default/classes/dto/Reference2.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/dto/Reference2.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/examples/open-api/force-app/main/default/classes/dto/Reference3.cls-meta.xml b/examples/open-api/force-app/main/default/classes/dto/Reference3.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/dto/Reference3.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/examples/open-api/force-app/main/default/classes/dto/Reference4.cls-meta.xml b/examples/open-api/force-app/main/default/classes/dto/Reference4.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/dto/Reference4.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/examples/open-api/force-app/main/default/classes/dto/Reference5.cls-meta.xml b/examples/open-api/force-app/main/default/classes/dto/Reference5.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/dto/Reference5.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/examples/open-api/force-app/main/default/classes/dto/Reference6.cls-meta.xml b/examples/open-api/force-app/main/default/classes/dto/Reference6.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/dto/Reference6.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/examples/open-api/force-app/main/default/classes/dto/Reference7.cls-meta.xml b/examples/open-api/force-app/main/default/classes/dto/Reference7.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/dto/Reference7.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/examples/open-api/force-app/main/default/classes/dto/SampleClass.cls-meta.xml b/examples/open-api/force-app/main/default/classes/dto/SampleClass.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/dto/SampleClass.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/examples/open-api/force-app/main/default/classes/rest/SampleRestResource.cls-meta.xml b/examples/open-api/force-app/main/default/classes/rest/SampleRestResource.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/rest/SampleRestResource.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceToSkip.cls-meta.xml b/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceToSkip.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceToSkip.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithInnerClass.cls b/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithInnerClass.cls
index f1847a67..c3a46488 100644
--- a/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithInnerClass.cls
+++ b/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithInnerClass.cls
@@ -1,7 +1,7 @@
/**
* @description Contact related operations
*/
-@RestResource(urlMapping='/Contact/*')
+@RestResource(UrlMapping='/Contact/*')
global with sharing class SampleRestResourceWithInnerClass {
/**
* @description This is a sample HTTP Get method
diff --git a/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithInnerClass.cls-meta.xml b/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithInnerClass.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithInnerClass.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithoutApexDocs.cls b/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithoutApexDocs.cls
index c0981306..0742b893 100644
--- a/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithoutApexDocs.cls
+++ b/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithoutApexDocs.cls
@@ -1,7 +1,7 @@
/**
* @description Order related operations
*/
-@RestResource(urlMapping='/Order/*')
+@RestResource(UrlMapping='/Order/*')
global with sharing class SampleRestResourceWithoutApexDocs {
@HttpGet
global static String doGet(String param1, Reference1 param2) {
diff --git a/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithoutApexDocs.cls-meta.xml b/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithoutApexDocs.cls-meta.xml
new file mode 100644
index 00000000..998805a8
--- /dev/null
+++ b/examples/open-api/force-app/main/default/classes/rest/SampleRestResourceWithoutApexDocs.cls-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 62.0
+ Active
+
diff --git a/imgs/integration.png b/imgs/integration.png
new file mode 100644
index 00000000..b6558d32
Binary files /dev/null and b/imgs/integration.png differ
diff --git a/imgs/redocly.png b/imgs/redocly.png
new file mode 100644
index 00000000..d0a27ae1
Binary files /dev/null and b/imgs/redocly.png differ
diff --git a/imgs/vitepress-dark.png b/imgs/vitepress-dark.png
new file mode 100644
index 00000000..ec473a32
Binary files /dev/null and b/imgs/vitepress-dark.png differ
diff --git a/imgs/vitepress-light.png b/imgs/vitepress-light.png
new file mode 100644
index 00000000..a69b5e54
Binary files /dev/null and b/imgs/vitepress-light.png differ
diff --git a/package-lock.json b/package-lock.json
index 4cb39f91..73c8a990 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@cparra/apexdocs",
- "version": "3.3.1",
+ "version": "3.3.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@cparra/apexdocs",
- "version": "3.3.1",
+ "version": "3.3.2",
"license": "MIT",
"dependencies": {
"@cparra/apex-reflection": "2.15.0",
diff --git a/package.json b/package.json
index 01e49836..fb7e56ac 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@cparra/apexdocs",
- "version": "3.3.1",
+ "version": "3.3.2",
"description": "Library with CLI capabilities to generate documentation for Salesforce Apex classes.",
"keywords": [
"apex",
diff --git a/src/core/openapi/__tests__/open-api-docs-processor.spec.ts b/src/core/openapi/__tests__/open-api-docs-processor.spec.ts
index 3951b896..22ad5fec 100644
--- a/src/core/openapi/__tests__/open-api-docs-processor.spec.ts
+++ b/src/core/openapi/__tests__/open-api-docs-processor.spec.ts
@@ -24,13 +24,13 @@ it('should add a path based on the @UrlResource annotation on the class', functi
const processor = new OpenApiDocsProcessor(noLogger);
processor.onProcess(classMirror);
- expect(processor.openApiModel.paths).toHaveProperty('Account/');
+ expect(processor.openApiModel.paths).toHaveProperty('/Account/');
});
it('should respect slashes', function () {
const annotationElementValue = {
key: 'urlMapping',
- value: "'v1/Account/*'",
+ value: "'/v1/Account/*'",
};
const classMirror = new ClassMirrorBuilder()
.addAnnotation(new AnnotationBuilder().addElementValue(annotationElementValue).build())
@@ -39,7 +39,7 @@ it('should respect slashes', function () {
const processor = new OpenApiDocsProcessor(noLogger);
processor.onProcess(classMirror);
- expect(processor.openApiModel.paths).toHaveProperty('v1/Account/');
+ expect(processor.openApiModel.paths).toHaveProperty('/v1/Account/');
});
it('should contain a path with a description when the class has an ApexDoc comment', function () {
@@ -55,5 +55,5 @@ it('should contain a path with a description when the class has an ApexDoc comme
const processor = new OpenApiDocsProcessor(noLogger);
processor.onProcess(classMirror);
- expect(processor.openApiModel.paths['Account/'].description).toBe('My Description');
+ expect(processor.openApiModel.paths['/Account/'].description).toBe('My Description');
});
diff --git a/src/core/openapi/open-api-docs-processor.ts b/src/core/openapi/open-api-docs-processor.ts
index 5f076348..4dca32f9 100644
--- a/src/core/openapi/open-api-docs-processor.ts
+++ b/src/core/openapi/open-api-docs-processor.ts
@@ -84,10 +84,10 @@ export class OpenApiDocsProcessor {
return null;
}
- let endpointPath = urlMapping.value.replaceAll('"', '').replaceAll("'", '').replaceAll('/*', '/');
- if (endpointPath.startsWith('/')) {
- endpointPath = endpointPath.substring(1);
- }
- return endpointPath;
+ // The OpenApi path needs to start with a leading slash, but
+ // Salesforce @RestResource annotations already require a leading slash,
+ // so no need to check for it.
+ // See URL Guidelines: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation_rest_resource.htm
+ return urlMapping.value.replaceAll('"', '').replaceAll("'", '').replaceAll('/*', '/');
}
}