@cparra/apexdocs 3.0.0-alpha.6 → 3.0.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/generate.js +159 -85
- package/package.json +1 -2
- package/src/application/Apexdocs.ts +10 -6
- package/src/application/__tests__/apex-file-reader.spec.ts +25 -25
- package/src/application/apex-file-reader.ts +32 -19
- package/src/application/file-system.ts +46 -10
- package/src/application/file-writer.ts +2 -2
- package/src/application/generators/markdown.ts +27 -12
- package/src/util/logger.ts +0 -13
package/dist/cli/generate.js
CHANGED
|
@@ -13,7 +13,6 @@ var Handlebars = require('handlebars');
|
|
|
13
13
|
var defaults = require('../defaults-DGKfeZq-.js');
|
|
14
14
|
var fs = require('fs');
|
|
15
15
|
var chalk = require('chalk');
|
|
16
|
-
var ora = require('ora');
|
|
17
16
|
var cosmiconfig = require('cosmiconfig');
|
|
18
17
|
var yargs = require('yargs');
|
|
19
18
|
var cosmiconfigTypescriptLoader = require('cosmiconfig-typescript-loader');
|
|
@@ -1526,7 +1525,7 @@ var __spreadValues$7 = (a, b) => {
|
|
|
1526
1525
|
return a;
|
|
1527
1526
|
};
|
|
1528
1527
|
var __spreadProps$7 = (a, b) => __defProps$7(a, __getOwnPropDescs$7(b));
|
|
1529
|
-
var __async$
|
|
1528
|
+
var __async$4 = (__this, __arguments, generator) => {
|
|
1530
1529
|
return new Promise((resolve, reject) => {
|
|
1531
1530
|
var fulfilled = (value) => {
|
|
1532
1531
|
try {
|
|
@@ -1578,7 +1577,7 @@ function generateDocs(apexBundles, config) {
|
|
|
1578
1577
|
}
|
|
1579
1578
|
function transformReferenceHook(config) {
|
|
1580
1579
|
function _execute(references, parsedFiles, transformReference) {
|
|
1581
|
-
return __async$
|
|
1580
|
+
return __async$4(this, null, function* () {
|
|
1582
1581
|
return {
|
|
1583
1582
|
references: yield execTransformReferenceHook(Object.values(references), transformReference),
|
|
1584
1583
|
parsedFiles
|
|
@@ -1599,8 +1598,8 @@ function transformDocumentationBundleHook(config) {
|
|
|
1599
1598
|
function passThroughHook(value) {
|
|
1600
1599
|
return value;
|
|
1601
1600
|
}
|
|
1602
|
-
const execTransformReferenceHook = (_0, ..._1) => __async$
|
|
1603
|
-
const hooked = references.map((reference) => __async$
|
|
1601
|
+
const execTransformReferenceHook = (_0, ..._1) => __async$4(void 0, [_0, ..._1], function* (references, hook = passThroughHook) {
|
|
1602
|
+
const hooked = references.map((reference) => __async$4(void 0, null, function* () {
|
|
1604
1603
|
const hookedResult = yield hook(reference);
|
|
1605
1604
|
return __spreadValues$7(__spreadValues$7({}, reference), hookedResult);
|
|
1606
1605
|
}));
|
|
@@ -1610,24 +1609,24 @@ const execTransformReferenceHook = (_0, ..._1) => __async$3(void 0, [_0, ..._1],
|
|
|
1610
1609
|
return acc;
|
|
1611
1610
|
}, {});
|
|
1612
1611
|
});
|
|
1613
|
-
const documentationBundleHook = (bundle, config) => __async$
|
|
1612
|
+
const documentationBundleHook = (bundle, config) => __async$4(void 0, null, function* () {
|
|
1614
1613
|
return {
|
|
1615
1614
|
referenceGuide: yield transformReferenceGuide(bundle.referenceGuide, config.transformReferenceGuide),
|
|
1616
1615
|
docs: yield transformDocs(bundle.docs, config.transformDocs, config.transformDocPage)
|
|
1617
1616
|
};
|
|
1618
1617
|
});
|
|
1619
|
-
const transformReferenceGuide = (_0, ..._1) => __async$
|
|
1618
|
+
const transformReferenceGuide = (_0, ..._1) => __async$4(void 0, [_0, ..._1], function* (referenceGuide, hook = passThroughHook) {
|
|
1620
1619
|
const result = yield hook(referenceGuide);
|
|
1621
1620
|
if (isSkip(result)) {
|
|
1622
1621
|
return result;
|
|
1623
1622
|
}
|
|
1624
1623
|
return __spreadValues$7(__spreadValues$7({}, referenceGuide), yield hook(referenceGuide));
|
|
1625
1624
|
});
|
|
1626
|
-
const transformDocs = (_0, ..._1) => __async$
|
|
1625
|
+
const transformDocs = (_0, ..._1) => __async$4(void 0, [_0, ..._1], function* (docs, transformDocsHook = passThroughHook, transformDocPageHook = passThroughHook) {
|
|
1627
1626
|
const transformed = yield transformDocsHook(docs);
|
|
1628
1627
|
return Promise.all(transformed.map((doc) => transformDocPage(doc, transformDocPageHook)));
|
|
1629
1628
|
});
|
|
1630
|
-
const transformDocPage = (_0, ..._1) => __async$
|
|
1629
|
+
const transformDocPage = (_0, ..._1) => __async$4(void 0, [_0, ..._1], function* (doc, hook = passThroughHook) {
|
|
1631
1630
|
return __spreadValues$7(__spreadValues$7({}, doc), yield hook(doc));
|
|
1632
1631
|
});
|
|
1633
1632
|
function postHookCompile(bundle) {
|
|
@@ -1690,7 +1689,7 @@ class FileWriter {
|
|
|
1690
1689
|
const { outputDocPath, content } = this.getTargetLocation(file, outputDir);
|
|
1691
1690
|
fs__namespace.mkdirSync(path__namespace.dirname(outputDocPath), { recursive: true });
|
|
1692
1691
|
fs__namespace.writeFileSync(outputDocPath, content, "utf8");
|
|
1693
|
-
onWriteCallback(file);
|
|
1692
|
+
onWriteCallback == null ? void 0 : onWriteCallback(file);
|
|
1694
1693
|
});
|
|
1695
1694
|
}
|
|
1696
1695
|
static getTargetLocation(file, outputDir) {
|
|
@@ -1734,17 +1733,8 @@ class Logger {
|
|
|
1734
1733
|
static getChalkFn(color) {
|
|
1735
1734
|
return color === "green" ? chalk.green : chalk.red;
|
|
1736
1735
|
}
|
|
1737
|
-
// TODO: This isn't really working and I don't think its worth it. Let's just completely remove the ora dependency.
|
|
1738
|
-
static startSpinner(text) {
|
|
1739
|
-
this.spinner.text = text;
|
|
1740
|
-
this.spinner.start();
|
|
1741
|
-
}
|
|
1742
|
-
static succeedSpinner(text) {
|
|
1743
|
-
this.spinner.succeed(text);
|
|
1744
|
-
}
|
|
1745
1736
|
}
|
|
1746
1737
|
Logger.currentFrame = 0;
|
|
1747
|
-
Logger.spinner = ora();
|
|
1748
1738
|
|
|
1749
1739
|
const referenceGuideTemplate = `
|
|
1750
1740
|
# Apex Reference Guide
|
|
@@ -1780,45 +1770,35 @@ var __spreadValues$5 = (a, b) => {
|
|
|
1780
1770
|
return a;
|
|
1781
1771
|
};
|
|
1782
1772
|
var __spreadProps$5 = (a, b) => __defProps$5(a, __getOwnPropDescs$5(b));
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1773
|
+
class FileWritingError {
|
|
1774
|
+
constructor(message, error) {
|
|
1775
|
+
this.message = message;
|
|
1776
|
+
this.error = error;
|
|
1777
|
+
this._tag = "FileWritingError";
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
function generate(bundles, config) {
|
|
1781
|
+
return _function.pipe(
|
|
1782
|
+
generateDocumentationBundle(bundles, config),
|
|
1783
|
+
TE__namespace.flatMap((files) => writeFilesToSystem(files, config.targetDir)),
|
|
1784
|
+
TE__namespace.mapLeft((error) => {
|
|
1785
|
+
if (error._tag === "HookError") {
|
|
1786
|
+
Logger.error("Error(s) occurred while processing hooks. Please review the following issues:");
|
|
1787
|
+
Logger.error(error.error);
|
|
1788
|
+
return;
|
|
1790
1789
|
}
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
} catch (e) {
|
|
1796
|
-
reject(e);
|
|
1790
|
+
if (error._tag === "FileWritingError") {
|
|
1791
|
+
Logger.error(error.message);
|
|
1792
|
+
Logger.error(error.error);
|
|
1793
|
+
return;
|
|
1797
1794
|
}
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
return _function.pipe(
|
|
1806
|
-
generateDocumentationBundle(bundles, config),
|
|
1807
|
-
TE__namespace.map((files) => writeFilesToSystem(files, config.targetDir)),
|
|
1808
|
-
TE__namespace.mapLeft((error) => {
|
|
1809
|
-
if (error._tag === "HookError") {
|
|
1810
|
-
Logger.error("Error(s) occurred while processing hooks. Please review the following issues:");
|
|
1811
|
-
Logger.error(error.error);
|
|
1812
|
-
return;
|
|
1813
|
-
}
|
|
1814
|
-
const errorMessages = [
|
|
1815
|
-
"Error(s) occurred while parsing files. Please review the following issues:",
|
|
1816
|
-
...error.errors.map(formatReflectionError)
|
|
1817
|
-
].join("\n");
|
|
1818
|
-
Logger.error(errorMessages);
|
|
1819
|
-
})
|
|
1820
|
-
)();
|
|
1821
|
-
});
|
|
1795
|
+
const errorMessages = [
|
|
1796
|
+
"Error(s) occurred while parsing files. Please review the following issues:",
|
|
1797
|
+
...error.errors.map(formatReflectionError)
|
|
1798
|
+
].join("\n");
|
|
1799
|
+
Logger.error(errorMessages);
|
|
1800
|
+
})
|
|
1801
|
+
)();
|
|
1822
1802
|
}
|
|
1823
1803
|
function generateDocumentationBundle(bundles, config) {
|
|
1824
1804
|
return generateDocs(bundles, __spreadProps$5(__spreadValues$5({}, config), {
|
|
@@ -1826,12 +1806,15 @@ function generateDocumentationBundle(bundles, config) {
|
|
|
1826
1806
|
}));
|
|
1827
1807
|
}
|
|
1828
1808
|
function writeFilesToSystem(files, outputDir) {
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1809
|
+
try {
|
|
1810
|
+
FileWriter.write(
|
|
1811
|
+
[files.referenceGuide, ...files.docs].filter((file) => !isSkip(file)),
|
|
1812
|
+
outputDir
|
|
1813
|
+
);
|
|
1814
|
+
return TE__namespace.right(void 0);
|
|
1815
|
+
} catch (error) {
|
|
1816
|
+
return TE__namespace.left(new FileWritingError("An error occurred while writing files to the system.", error));
|
|
1817
|
+
}
|
|
1835
1818
|
}
|
|
1836
1819
|
function formatReflectionError(error) {
|
|
1837
1820
|
return `Source file: ${error.file}
|
|
@@ -2814,47 +2797,134 @@ function filterByScopes(manifest) {
|
|
|
2814
2797
|
return filteredTypes;
|
|
2815
2798
|
}
|
|
2816
2799
|
|
|
2800
|
+
var __async$3 = (__this, __arguments, generator) => {
|
|
2801
|
+
return new Promise((resolve, reject) => {
|
|
2802
|
+
var fulfilled = (value) => {
|
|
2803
|
+
try {
|
|
2804
|
+
step(generator.next(value));
|
|
2805
|
+
} catch (e) {
|
|
2806
|
+
reject(e);
|
|
2807
|
+
}
|
|
2808
|
+
};
|
|
2809
|
+
var rejected = (value) => {
|
|
2810
|
+
try {
|
|
2811
|
+
step(generator.throw(value));
|
|
2812
|
+
} catch (e) {
|
|
2813
|
+
reject(e);
|
|
2814
|
+
}
|
|
2815
|
+
};
|
|
2816
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
2817
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
2818
|
+
});
|
|
2819
|
+
};
|
|
2817
2820
|
const APEX_FILE_EXTENSION = ".cls";
|
|
2818
2821
|
class ApexFileReader {
|
|
2819
2822
|
/**
|
|
2820
2823
|
* Reads from .cls files and returns their raw body.
|
|
2821
2824
|
*/
|
|
2822
2825
|
static processFiles(fileSystem, rootPath, includeMetadata) {
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
const
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2826
|
+
return __async$3(this, null, function* () {
|
|
2827
|
+
const filePaths = yield this.getFilePaths(fileSystem, rootPath);
|
|
2828
|
+
const apexFilePaths = filePaths.filter((filePath) => this.isApexFile(filePath));
|
|
2829
|
+
const filePromises = apexFilePaths.map((filePath) => this.processFile(fileSystem, filePath, includeMetadata));
|
|
2830
|
+
return Promise.all(filePromises);
|
|
2831
|
+
});
|
|
2832
|
+
}
|
|
2833
|
+
static getFilePaths(fileSystem, rootPath) {
|
|
2834
|
+
return __async$3(this, null, function* () {
|
|
2835
|
+
const directoryContents = yield fileSystem.readDirectory(rootPath);
|
|
2836
|
+
const paths = [];
|
|
2837
|
+
for (const filePath of directoryContents) {
|
|
2838
|
+
const currentPath = fileSystem.joinPath(rootPath, filePath);
|
|
2839
|
+
if (yield fileSystem.isDirectory(currentPath)) {
|
|
2840
|
+
paths.push(...yield this.getFilePaths(fileSystem, currentPath));
|
|
2841
|
+
}
|
|
2842
|
+
paths.push(currentPath);
|
|
2832
2843
|
}
|
|
2833
|
-
|
|
2834
|
-
|
|
2844
|
+
return paths;
|
|
2845
|
+
});
|
|
2846
|
+
}
|
|
2847
|
+
static processFile(fileSystem, filePath, includeMetadata) {
|
|
2848
|
+
return __async$3(this, null, function* () {
|
|
2849
|
+
const rawTypeContent = yield fileSystem.readFile(filePath);
|
|
2850
|
+
const metadataPath = `${filePath}-meta.xml`;
|
|
2835
2851
|
let rawMetadataContent = null;
|
|
2836
2852
|
if (includeMetadata) {
|
|
2837
|
-
rawMetadataContent = fileSystem.exists(metadataPath) ? fileSystem.readFile(metadataPath) : null;
|
|
2853
|
+
rawMetadataContent = fileSystem.exists(metadataPath) ? yield fileSystem.readFile(metadataPath) : null;
|
|
2838
2854
|
}
|
|
2839
|
-
|
|
2855
|
+
return { filePath, content: rawTypeContent, metadataContent: rawMetadataContent };
|
|
2840
2856
|
});
|
|
2841
|
-
return bundles;
|
|
2842
2857
|
}
|
|
2843
2858
|
static isApexFile(currentFile) {
|
|
2844
2859
|
return currentFile.endsWith(APEX_FILE_EXTENSION);
|
|
2845
2860
|
}
|
|
2846
2861
|
}
|
|
2847
2862
|
|
|
2863
|
+
var __async$2 = (__this, __arguments, generator) => {
|
|
2864
|
+
return new Promise((resolve, reject) => {
|
|
2865
|
+
var fulfilled = (value) => {
|
|
2866
|
+
try {
|
|
2867
|
+
step(generator.next(value));
|
|
2868
|
+
} catch (e) {
|
|
2869
|
+
reject(e);
|
|
2870
|
+
}
|
|
2871
|
+
};
|
|
2872
|
+
var rejected = (value) => {
|
|
2873
|
+
try {
|
|
2874
|
+
step(generator.throw(value));
|
|
2875
|
+
} catch (e) {
|
|
2876
|
+
reject(e);
|
|
2877
|
+
}
|
|
2878
|
+
};
|
|
2879
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
2880
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
2881
|
+
});
|
|
2882
|
+
};
|
|
2883
|
+
function stat(path2) {
|
|
2884
|
+
return new Promise((resolve, reject) => {
|
|
2885
|
+
fs__namespace.stat(path2, (err, stats) => {
|
|
2886
|
+
if (err) {
|
|
2887
|
+
reject(err);
|
|
2888
|
+
} else {
|
|
2889
|
+
resolve(stats);
|
|
2890
|
+
}
|
|
2891
|
+
});
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
function readdir(path2) {
|
|
2895
|
+
return new Promise((resolve, reject) => {
|
|
2896
|
+
fs__namespace.readdir(path2, (err, files) => {
|
|
2897
|
+
if (err) {
|
|
2898
|
+
reject(err);
|
|
2899
|
+
} else {
|
|
2900
|
+
resolve(files);
|
|
2901
|
+
}
|
|
2902
|
+
});
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2905
|
+
function readFile(path2) {
|
|
2906
|
+
return new Promise((resolve, reject) => {
|
|
2907
|
+
fs__namespace.readFile(path2, (err, data) => {
|
|
2908
|
+
if (err) {
|
|
2909
|
+
reject(err);
|
|
2910
|
+
} else {
|
|
2911
|
+
resolve(data.toString());
|
|
2912
|
+
}
|
|
2913
|
+
});
|
|
2914
|
+
});
|
|
2915
|
+
}
|
|
2848
2916
|
class DefaultFileSystem {
|
|
2849
2917
|
isDirectory(pathToRead) {
|
|
2850
|
-
return
|
|
2918
|
+
return __async$2(this, null, function* () {
|
|
2919
|
+
const stats = yield stat(pathToRead);
|
|
2920
|
+
return stats.isDirectory();
|
|
2921
|
+
});
|
|
2851
2922
|
}
|
|
2852
2923
|
readDirectory(sourceDirectory) {
|
|
2853
|
-
return
|
|
2924
|
+
return readdir(sourceDirectory);
|
|
2854
2925
|
}
|
|
2855
2926
|
readFile(pathToRead) {
|
|
2856
|
-
|
|
2857
|
-
return rawFile.toString();
|
|
2927
|
+
return readFile(pathToRead);
|
|
2858
2928
|
}
|
|
2859
2929
|
joinPath(...paths) {
|
|
2860
2930
|
return path__namespace.join(...paths);
|
|
@@ -2890,10 +2960,13 @@ class Apexdocs {
|
|
|
2890
2960
|
*/
|
|
2891
2961
|
static generate(config) {
|
|
2892
2962
|
return __async$1(this, null, function* () {
|
|
2893
|
-
Logger.logSingle(
|
|
2894
|
-
Logger.startSpinner("Processing files...");
|
|
2963
|
+
Logger.logSingle(`Generating ${config.targetGenerator} documentation...`);
|
|
2895
2964
|
try {
|
|
2896
|
-
const fileBodies = ApexFileReader.processFiles(
|
|
2965
|
+
const fileBodies = yield ApexFileReader.processFiles(
|
|
2966
|
+
new DefaultFileSystem(),
|
|
2967
|
+
config.sourceDir,
|
|
2968
|
+
config.includeMetadata
|
|
2969
|
+
);
|
|
2897
2970
|
switch (config.targetGenerator) {
|
|
2898
2971
|
case "markdown":
|
|
2899
2972
|
yield generate(fileBodies, config);
|
|
@@ -2902,8 +2975,9 @@ class Apexdocs {
|
|
|
2902
2975
|
openApi(fileBodies, config);
|
|
2903
2976
|
break;
|
|
2904
2977
|
}
|
|
2905
|
-
|
|
2906
|
-
|
|
2978
|
+
Logger.logSingle("\u2714\uFE0F Documentation generated successfully!");
|
|
2979
|
+
} catch (error) {
|
|
2980
|
+
Logger.logSingle("\u274C An error occurred while generating the documentation.", "red");
|
|
2907
2981
|
}
|
|
2908
2982
|
});
|
|
2909
2983
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cparra/apexdocs",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.8",
|
|
4
4
|
"description": "Library with CLI capabilities to generate documentation for Salesforce Apex classes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"apex",
|
|
@@ -68,7 +68,6 @@
|
|
|
68
68
|
"fp-ts": "^2.16.8",
|
|
69
69
|
"handlebars": "^4.7.8",
|
|
70
70
|
"js-yaml": "^4.1.0",
|
|
71
|
-
"ora": "^5.4.1",
|
|
72
71
|
"type-fest": "^4.23.0",
|
|
73
72
|
"yargs": "^17.7.2"
|
|
74
73
|
},
|
|
@@ -14,11 +14,14 @@ export class Apexdocs {
|
|
|
14
14
|
* Generates documentation out of Apex source files.
|
|
15
15
|
*/
|
|
16
16
|
static async generate(config: UserDefinedConfig): Promise<void> {
|
|
17
|
-
Logger.logSingle(
|
|
18
|
-
Logger.startSpinner('Processing files...');
|
|
17
|
+
Logger.logSingle(`Generating ${config.targetGenerator} documentation...`);
|
|
19
18
|
|
|
20
19
|
try {
|
|
21
|
-
const fileBodies = ApexFileReader.processFiles(
|
|
20
|
+
const fileBodies = await ApexFileReader.processFiles(
|
|
21
|
+
new DefaultFileSystem(),
|
|
22
|
+
config.sourceDir,
|
|
23
|
+
config.includeMetadata,
|
|
24
|
+
);
|
|
22
25
|
|
|
23
26
|
switch (config.targetGenerator) {
|
|
24
27
|
case 'markdown':
|
|
@@ -28,9 +31,10 @@ export class Apexdocs {
|
|
|
28
31
|
openApi(fileBodies, config);
|
|
29
32
|
break;
|
|
30
33
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
|
|
35
|
+
Logger.logSingle('✔️ Documentation generated successfully!');
|
|
36
|
+
} catch (error) {
|
|
37
|
+
Logger.logSingle('❌ An error occurred while generating the documentation.', 'red');
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
40
|
}
|
|
@@ -18,24 +18,24 @@ describe('File Reader', () => {
|
|
|
18
18
|
} as SettingsConfig);
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
it('returns an empty list when there are no files in the directory', () => {
|
|
22
|
-
const result = ApexFileReader.processFiles(
|
|
21
|
+
it('returns an empty list when there are no files in the directory', async () => {
|
|
22
|
+
const result = await ApexFileReader.processFiles(
|
|
23
23
|
{
|
|
24
24
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
25
|
-
isDirectory(_: string): boolean {
|
|
26
|
-
return false;
|
|
25
|
+
isDirectory(_: string): Promise<boolean> {
|
|
26
|
+
return Promise.resolve(false);
|
|
27
27
|
},
|
|
28
28
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
29
29
|
joinPath(_: string): string {
|
|
30
30
|
return '';
|
|
31
31
|
},
|
|
32
32
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
33
|
-
readDirectory(_: string): string[] {
|
|
34
|
-
return [];
|
|
33
|
+
readDirectory(_: string): Promise<string[]> {
|
|
34
|
+
return Promise.resolve([]);
|
|
35
35
|
},
|
|
36
36
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
37
|
-
readFile(_: string): string {
|
|
38
|
-
return '';
|
|
37
|
+
readFile(_: string): Promise<string> {
|
|
38
|
+
return Promise.resolve('');
|
|
39
39
|
},
|
|
40
40
|
exists(): boolean {
|
|
41
41
|
return true;
|
|
@@ -47,20 +47,20 @@ describe('File Reader', () => {
|
|
|
47
47
|
expect(result.length).toBe(0);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
it('returns an empty list when there are no Apex files in the directory', () => {
|
|
51
|
-
const result = ApexFileReader.processFiles(
|
|
50
|
+
it('returns an empty list when there are no Apex files in the directory', async () => {
|
|
51
|
+
const result = await ApexFileReader.processFiles(
|
|
52
52
|
{
|
|
53
|
-
isDirectory(): boolean {
|
|
54
|
-
return false;
|
|
53
|
+
isDirectory(): Promise<boolean> {
|
|
54
|
+
return Promise.resolve(false);
|
|
55
55
|
},
|
|
56
56
|
joinPath(): string {
|
|
57
57
|
return '';
|
|
58
58
|
},
|
|
59
|
-
readDirectory(): string[] {
|
|
60
|
-
return ['SomeFile.md'];
|
|
59
|
+
readDirectory(): Promise<string[]> {
|
|
60
|
+
return Promise.resolve(['SomeFile.md']);
|
|
61
61
|
},
|
|
62
|
-
readFile(): string {
|
|
63
|
-
return '';
|
|
62
|
+
readFile(): Promise<string> {
|
|
63
|
+
return Promise.resolve('');
|
|
64
64
|
},
|
|
65
65
|
exists(): boolean {
|
|
66
66
|
return true;
|
|
@@ -72,24 +72,24 @@ describe('File Reader', () => {
|
|
|
72
72
|
expect(result.length).toBe(0);
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
it('returns the file contents for an Apex file', () => {
|
|
76
|
-
const result = ApexFileReader.processFiles(
|
|
75
|
+
it('returns the file contents for an Apex file', async () => {
|
|
76
|
+
const result = await ApexFileReader.processFiles(
|
|
77
77
|
{
|
|
78
78
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
79
|
-
isDirectory(_: string): boolean {
|
|
80
|
-
return false;
|
|
79
|
+
isDirectory(_: string): Promise<boolean> {
|
|
80
|
+
return Promise.resolve(false);
|
|
81
81
|
},
|
|
82
82
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
83
83
|
joinPath(_: string): string {
|
|
84
|
-
return '';
|
|
84
|
+
return 'SomeApexFile.cls';
|
|
85
85
|
},
|
|
86
86
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
87
|
-
readDirectory(_: string): string[] {
|
|
88
|
-
return ['SomeApexFile.cls'];
|
|
87
|
+
readDirectory(_: string): Promise<string[]> {
|
|
88
|
+
return Promise.resolve(['SomeApexFile.cls']);
|
|
89
89
|
},
|
|
90
90
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
91
|
-
readFile(_: string): string {
|
|
92
|
-
return 'public class MyClass{}';
|
|
91
|
+
readFile(_: string): Promise<string> {
|
|
92
|
+
return Promise.resolve('public class MyClass{}');
|
|
93
93
|
},
|
|
94
94
|
exists(): boolean {
|
|
95
95
|
return true;
|
|
@@ -10,30 +10,43 @@ export class ApexFileReader {
|
|
|
10
10
|
/**
|
|
11
11
|
* Reads from .cls files and returns their raw body.
|
|
12
12
|
*/
|
|
13
|
-
static processFiles(
|
|
14
|
-
|
|
13
|
+
static async processFiles(
|
|
14
|
+
fileSystem: FileSystem,
|
|
15
|
+
rootPath: string,
|
|
16
|
+
includeMetadata: boolean,
|
|
17
|
+
): Promise<UnparsedSourceFile[]> {
|
|
18
|
+
const filePaths = await this.getFilePaths(fileSystem, rootPath);
|
|
19
|
+
const apexFilePaths = filePaths.filter((filePath) => this.isApexFile(filePath));
|
|
20
|
+
const filePromises = apexFilePaths.map((filePath) => this.processFile(fileSystem, filePath, includeMetadata));
|
|
21
|
+
return Promise.all(filePromises);
|
|
22
|
+
}
|
|
15
23
|
|
|
16
|
-
|
|
17
|
-
directoryContents.
|
|
24
|
+
private static async getFilePaths(fileSystem: FileSystem, rootPath: string): Promise<string[]> {
|
|
25
|
+
const directoryContents = await fileSystem.readDirectory(rootPath);
|
|
26
|
+
const paths: string[] = [];
|
|
27
|
+
for (const filePath of directoryContents) {
|
|
18
28
|
const currentPath = fileSystem.joinPath(rootPath, filePath);
|
|
19
|
-
if (fileSystem.isDirectory(currentPath)) {
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (!this.isApexFile(filePath)) {
|
|
24
|
-
return;
|
|
29
|
+
if (await fileSystem.isDirectory(currentPath)) {
|
|
30
|
+
paths.push(...(await this.getFilePaths(fileSystem, currentPath)));
|
|
25
31
|
}
|
|
32
|
+
paths.push(currentPath);
|
|
33
|
+
}
|
|
34
|
+
return paths;
|
|
35
|
+
}
|
|
26
36
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
private static async processFile(
|
|
38
|
+
fileSystem: FileSystem,
|
|
39
|
+
filePath: string,
|
|
40
|
+
includeMetadata: boolean,
|
|
41
|
+
): Promise<UnparsedSourceFile> {
|
|
42
|
+
const rawTypeContent = await fileSystem.readFile(filePath);
|
|
43
|
+
const metadataPath = `${filePath}-meta.xml`;
|
|
44
|
+
let rawMetadataContent = null;
|
|
45
|
+
if (includeMetadata) {
|
|
46
|
+
rawMetadataContent = fileSystem.exists(metadataPath) ? await fileSystem.readFile(metadataPath) : null;
|
|
47
|
+
}
|
|
33
48
|
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
return bundles;
|
|
49
|
+
return { filePath, content: rawTypeContent, metadataContent: rawMetadataContent };
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
private static isApexFile(currentFile: string): boolean {
|
|
@@ -2,25 +2,61 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
|
|
4
4
|
export interface FileSystem {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
readFile: (path: string) => string
|
|
5
|
+
isDirectory: (path: string) => Promise<boolean>;
|
|
6
|
+
readDirectory: (sourceDirectory: string) => Promise<string[]>;
|
|
7
|
+
readFile: (path: string) => Promise<string>;
|
|
8
8
|
joinPath: (...paths: string[]) => string;
|
|
9
9
|
exists: (path: string) => boolean;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
function stat(path: string): Promise<fs.Stats> {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
fs.stat(path, (err, stats) => {
|
|
15
|
+
if (err) {
|
|
16
|
+
reject(err);
|
|
17
|
+
} else {
|
|
18
|
+
resolve(stats);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function readdir(path: string): Promise<string[]> {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
fs.readdir(path, (err, files) => {
|
|
27
|
+
if (err) {
|
|
28
|
+
reject(err);
|
|
29
|
+
} else {
|
|
30
|
+
resolve(files);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readFile(path: string): Promise<string> {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
fs.readFile(path, (err, data) => {
|
|
39
|
+
if (err) {
|
|
40
|
+
reject(err);
|
|
41
|
+
} else {
|
|
42
|
+
resolve(data.toString());
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
12
48
|
export class DefaultFileSystem implements FileSystem {
|
|
13
|
-
isDirectory(pathToRead: string): boolean {
|
|
14
|
-
|
|
49
|
+
async isDirectory(pathToRead: string): Promise<boolean> {
|
|
50
|
+
const stats = await stat(pathToRead);
|
|
51
|
+
return stats.isDirectory();
|
|
15
52
|
}
|
|
16
53
|
|
|
17
|
-
readDirectory(sourceDirectory: string): string[] {
|
|
18
|
-
return
|
|
54
|
+
readDirectory(sourceDirectory: string): Promise<string[]> {
|
|
55
|
+
return readdir(sourceDirectory);
|
|
19
56
|
}
|
|
20
57
|
|
|
21
|
-
readFile(pathToRead: string): string {
|
|
22
|
-
|
|
23
|
-
return rawFile.toString();
|
|
58
|
+
readFile(pathToRead: string): Promise<string> {
|
|
59
|
+
return readFile(pathToRead);
|
|
24
60
|
}
|
|
25
61
|
|
|
26
62
|
joinPath(...paths: string[]): string {
|
|
@@ -3,12 +3,12 @@ import * as path from 'path';
|
|
|
3
3
|
import { PageData } from '../core/shared/types';
|
|
4
4
|
|
|
5
5
|
export class FileWriter {
|
|
6
|
-
static write(files: PageData[], outputDir: string, onWriteCallback
|
|
6
|
+
static write(files: PageData[], outputDir: string, onWriteCallback?: (file: PageData) => void) {
|
|
7
7
|
files.forEach((file) => {
|
|
8
8
|
const { outputDocPath, content } = this.getTargetLocation(file, outputDir);
|
|
9
9
|
fs.mkdirSync(path.dirname(outputDocPath), { recursive: true });
|
|
10
10
|
fs.writeFileSync(outputDocPath, content, 'utf8');
|
|
11
|
-
onWriteCallback(file);
|
|
11
|
+
onWriteCallback?.(file);
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -13,10 +13,18 @@ import { referenceGuideTemplate } from '../../core/markdown/templates/reference-
|
|
|
13
13
|
import * as TE from 'fp-ts/TaskEither';
|
|
14
14
|
import { isSkip } from '../../core/shared/utils';
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
class FileWritingError {
|
|
17
|
+
readonly _tag = 'FileWritingError';
|
|
18
|
+
constructor(
|
|
19
|
+
public message: string,
|
|
20
|
+
public error: unknown,
|
|
21
|
+
) {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default function generate(bundles: UnparsedSourceFile[], config: UserDefinedMarkdownConfig) {
|
|
17
25
|
return pipe(
|
|
18
26
|
generateDocumentationBundle(bundles, config),
|
|
19
|
-
TE.
|
|
27
|
+
TE.flatMap((files) => writeFilesToSystem(files, config.targetDir)),
|
|
20
28
|
TE.mapLeft((error) => {
|
|
21
29
|
if (error._tag === 'HookError') {
|
|
22
30
|
Logger.error('Error(s) occurred while processing hooks. Please review the following issues:');
|
|
@@ -24,6 +32,12 @@ export default async function generate(bundles: UnparsedSourceFile[], config: Us
|
|
|
24
32
|
return;
|
|
25
33
|
}
|
|
26
34
|
|
|
35
|
+
if (error._tag === 'FileWritingError') {
|
|
36
|
+
Logger.error(error.message);
|
|
37
|
+
Logger.error(error.error);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
27
41
|
const errorMessages = [
|
|
28
42
|
'Error(s) occurred while parsing files. Please review the following issues:',
|
|
29
43
|
...error.errors.map(formatReflectionError),
|
|
@@ -42,16 +56,17 @@ function generateDocumentationBundle(bundles: UnparsedSourceFile[], config: User
|
|
|
42
56
|
}
|
|
43
57
|
|
|
44
58
|
function writeFilesToSystem(files: PostHookDocumentationBundle, outputDir: string) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
try {
|
|
60
|
+
FileWriter.write(
|
|
61
|
+
[files.referenceGuide, ...files.docs]
|
|
62
|
+
// Filter out any files that should be skipped
|
|
63
|
+
.filter((file) => !isSkip(file)) as PageData[],
|
|
64
|
+
outputDir,
|
|
65
|
+
);
|
|
66
|
+
return TE.right(undefined);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
return TE.left(new FileWritingError('An error occurred while writing files to the system.', error));
|
|
69
|
+
}
|
|
55
70
|
}
|
|
56
71
|
|
|
57
72
|
function formatReflectionError(error: ReflectionError) {
|
package/src/util/logger.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import ora from 'ora';
|
|
3
2
|
/**
|
|
4
3
|
* Logs messages to the console.
|
|
5
4
|
*/
|
|
6
5
|
export class Logger {
|
|
7
6
|
static currentFrame = 0;
|
|
8
|
-
static spinner = ora();
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
9
|
* Logs a message with optional arguments.
|
|
@@ -43,15 +41,4 @@ export class Logger {
|
|
|
43
41
|
private static getChalkFn(color: 'green' | 'red') {
|
|
44
42
|
return color === 'green' ? chalk.green : chalk.red;
|
|
45
43
|
}
|
|
46
|
-
|
|
47
|
-
// TODO: This isn't really working and I don't think its worth it. Let's just completely remove the ora dependency.
|
|
48
|
-
|
|
49
|
-
public static startSpinner(text: string) {
|
|
50
|
-
this.spinner.text = text;
|
|
51
|
-
this.spinner.start();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public static succeedSpinner(text: string) {
|
|
55
|
-
this.spinner.succeed(text);
|
|
56
|
-
}
|
|
57
44
|
}
|