diff --git a/README.md b/README.md
index 5f2618f..48f0bd7 100644
--- a/README.md
+++ b/README.md
@@ -166,10 +166,10 @@ node scripts/main.js
cd examples/foo && npm i && npm run build && cd ../..
export INPUT_ENTRY_POINTS="
- file: src/index.ts
- docsReporter: api-extractor
+ docsReporter: typedoc
docsGenerator: typedoc-markdown
- file: examples/foo/index.ts
- docsReporter: api-extractor
+ docsReporter: typedoc
docsGenerator: typedoc-markdown
"
node scripts/main.js
@@ -182,7 +182,7 @@ that you have in your local environment without needing to setup and wait for a
real GH action execution.
```sh
-docker build -f Dockerfile . --tag tbdocs-app:latest
+docker build -f Dockerfile . --tag tbdocs:latest
# now from the repo you want to analyze & generate docs
# below is an example of running it from the root of tbdex-js repo
diff --git a/badges/coverage.svg b/badges/coverage.svg
index 11c80b3..40f1648 100644
--- a/badges/coverage.svg
+++ b/badges/coverage.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index b3e21a7..88bdc81 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,7 +18,7 @@
"simple-git": "^3.20.0",
"ts-is-present": "^1.2.2",
"tsconfck": "^2.1.2",
- "typedoc": "^0.25.2",
+ "typedoc": "^0.25.7",
"typedoc-plugin-markdown": "^3.16.0",
"yaml": "^2.3.3"
},
@@ -6158,9 +6158,9 @@
}
},
"node_modules/jsonc-parser": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
- "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w=="
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
+ "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA=="
},
"node_modules/jsonfile": {
"version": "4.0.0",
@@ -7603,9 +7603,9 @@
}
},
"node_modules/shiki": {
- "version": "0.14.4",
- "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.4.tgz",
- "integrity": "sha512-IXCRip2IQzKwxArNNq1S+On4KPML3Yyn8Zzs/xRgcgOWIr8ntIK3IKzjFPfjy/7kt9ZMjc+FItfqHRBg8b6tNQ==",
+ "version": "0.14.7",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz",
+ "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==",
"dependencies": {
"ansi-sequence-parser": "^1.1.0",
"jsonc-parser": "^3.2.0",
@@ -8192,14 +8192,14 @@
}
},
"node_modules/typedoc": {
- "version": "0.25.2",
- "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.2.tgz",
- "integrity": "sha512-286F7BeATBiWe/qC4PCOCKlSTwfnsLbC/4cZ68oGBbvAqb9vV33quEOXx7q176OXotD+JdEerdQ1OZGJ818lnA==",
+ "version": "0.25.7",
+ "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.7.tgz",
+ "integrity": "sha512-m6A6JjQRg39p2ZVRIN3NKXgrN8vzlHhOS+r9ymUYtcUP/TIQPvWSq7YgE5ZjASfv5Vd5BW5xrir6Gm2XNNcOow==",
"dependencies": {
"lunr": "^2.3.9",
"marked": "^4.3.0",
"minimatch": "^9.0.3",
- "shiki": "^0.14.1"
+ "shiki": "^0.14.7"
},
"bin": {
"typedoc": "bin/typedoc"
@@ -8208,7 +8208,7 @@
"node": ">= 16"
},
"peerDependencies": {
- "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x"
+ "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x"
}
},
"node_modules/typedoc-plugin-markdown": {
@@ -13189,9 +13189,9 @@
}
},
"jsonc-parser": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
- "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w=="
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
+ "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA=="
},
"jsonfile": {
"version": "4.0.0",
@@ -14224,9 +14224,9 @@
"dev": true
},
"shiki": {
- "version": "0.14.4",
- "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.4.tgz",
- "integrity": "sha512-IXCRip2IQzKwxArNNq1S+On4KPML3Yyn8Zzs/xRgcgOWIr8ntIK3IKzjFPfjy/7kt9ZMjc+FItfqHRBg8b6tNQ==",
+ "version": "0.14.7",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz",
+ "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==",
"requires": {
"ansi-sequence-parser": "^1.1.0",
"jsonc-parser": "^3.2.0",
@@ -14642,14 +14642,14 @@
}
},
"typedoc": {
- "version": "0.25.2",
- "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.2.tgz",
- "integrity": "sha512-286F7BeATBiWe/qC4PCOCKlSTwfnsLbC/4cZ68oGBbvAqb9vV33quEOXx7q176OXotD+JdEerdQ1OZGJ818lnA==",
+ "version": "0.25.7",
+ "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.7.tgz",
+ "integrity": "sha512-m6A6JjQRg39p2ZVRIN3NKXgrN8vzlHhOS+r9ymUYtcUP/TIQPvWSq7YgE5ZjASfv5Vd5BW5xrir6Gm2XNNcOow==",
"requires": {
"lunr": "^2.3.9",
"marked": "^4.3.0",
"minimatch": "^9.0.3",
- "shiki": "^0.14.1"
+ "shiki": "^0.14.7"
},
"dependencies": {
"brace-expansion": {
diff --git a/package.json b/package.json
index 7ef7c8a..935b528 100644
--- a/package.json
+++ b/package.json
@@ -78,7 +78,7 @@
"simple-git": "^3.20.0",
"ts-is-present": "^1.2.2",
"tsconfck": "^2.1.2",
- "typedoc": "^0.25.2",
+ "typedoc": "^0.25.7",
"typedoc-plugin-markdown": "^3.16.0",
"yaml": "^2.3.3"
},
diff --git a/src/docs-generator/typedoc.ts b/src/docs-generator/typedoc.ts
index 65887ae..4200300 100644
--- a/src/docs-generator/typedoc.ts
+++ b/src/docs-generator/typedoc.ts
@@ -27,9 +27,15 @@ export const generateTypedoc = async (
// Set project path if not set before by the doc-reporter
if (!entryPoint.projectPath) {
- const entryPointDir = path.dirname(entryPointFile)
- const packageJsonPath = lookupFile(entryPointDir, 'package.json')
- entryPoint.projectPath = packageJsonPath
+ const entryPointFileFullPath = path.dirname(
+ path.join(process.cwd(), entryPointFile)
+ )
+ const packageJsonFullPath = lookupFile(
+ 'package.json',
+ entryPointFileFullPath
+ )
+ const projectPath = path.dirname(packageJsonFullPath)
+ entryPoint.projectPath = projectPath
}
const { tsconfigFile } = await loadTsconfigProps(entryPoint.projectPath)
diff --git a/src/docs-report/index.ts b/src/docs-report/index.ts
index 6fbe48e..b7dc0ed 100644
--- a/src/docs-report/index.ts
+++ b/src/docs-report/index.ts
@@ -1,6 +1,7 @@
import { FilesDiffsMap, isSourceInChangedScope } from '../utils'
import { EntryPoint } from '../interfaces'
import { generateApiExtractorReport } from './api-extractor'
+import { generateTypedocReport } from './typedoc'
import { DocsReport } from './interfaces'
export * from './report-markdown'
@@ -26,6 +27,8 @@ const generateReport = async (
ignoreMessages?: string[]
): Promise => {
switch (entryPoint.docsReporter) {
+ case 'typedoc':
+ return generateTypedocReport(entryPoint, ignoreMessages)
case 'api-extractor':
return generateApiExtractorReport(entryPoint, ignoreMessages)
default:
diff --git a/src/docs-report/interfaces.ts b/src/docs-report/interfaces.ts
index 8d1e161..570f3d0 100644
--- a/src/docs-report/interfaces.ts
+++ b/src/docs-report/interfaces.ts
@@ -11,7 +11,7 @@
*
* @public
**/
-export type DocsReporterType = 'api-extractor'
+export type DocsReporterType = 'api-extractor' | 'typedoc'
/**
* Object containing the result of running the docs reporter.
diff --git a/src/docs-report/typedoc.ts b/src/docs-report/typedoc.ts
new file mode 100644
index 0000000..5223cfc
--- /dev/null
+++ b/src/docs-report/typedoc.ts
@@ -0,0 +1,225 @@
+import path from 'path'
+import ts from 'typescript'
+
+import { DocsReport, MessageLevel } from './interfaces'
+import { loadTsconfigProps, lookupFile } from '../utils'
+import { EntryPoint } from '../interfaces'
+import {
+ Application,
+ LogLevel,
+ Logger,
+ MinimalSourceFile,
+ TypeDocOptions,
+ ValidationOptions
+} from 'typedoc'
+
+export const generateTypedocReport = async (
+ entryPoint: EntryPoint,
+ ignoreMessages?: string[]
+): Promise => {
+ console.info('ignoreMessages', ignoreMessages)
+ const entryPointFile = entryPoint.file
+ const typedocValidationConfig = initializeConfig(entryPointFile)
+
+ const entryPointFileFullPath = path.dirname(
+ path.join(process.cwd(), entryPointFile)
+ )
+ const packageJsonFullPath = lookupFile('package.json', entryPointFileFullPath)
+ const projectPath = path.dirname(packageJsonFullPath)
+
+ entryPoint.projectPath = projectPath
+
+ const { tsconfigFile } = await loadTsconfigProps(entryPoint.projectPath)
+
+ const generatorConfig: Partial = {
+ tsconfig: tsconfigFile,
+ entryPoints: [entryPointFile],
+ validation: typedocValidationConfig
+ // skipErrorChecking: true,
+ // disableSources: true,
+ // readme: entryPoint.readmeFile || 'none',
+ // includeVersion: true
+ }
+
+ console.info('>>> Typedoc Report entryPoint', entryPointFile, generatorConfig)
+ const generatorApp = await Application.bootstrapWithPlugins(generatorConfig)
+
+ const projectReflection = await generatorApp.convert()
+ if (!projectReflection) {
+ throw new Error('Typedoc Report: Failed to generate project reflection')
+ }
+
+ const logger = new TypedocReportLogger()
+ generatorApp.logger = logger
+
+ generatorApp.validate(projectReflection)
+ return logger.getReport()
+}
+
+const initializeConfig = (entryPointFile: string): ValidationOptions => {
+ console.info('>>> Typedoc Report initializeConfig', entryPointFile)
+ // TODO: allow dynamic config input from tbdocs action inputs
+ return {
+ notExported: true,
+ invalidLink: true,
+ notDocumented: true
+ }
+}
+
+class TypedocReportLogger extends Logger {
+ private report: DocsReport = {
+ reporter: 'typedoc',
+ errorsCount: 0,
+ warningsCount: 0,
+ messages: []
+ }
+
+ getReport(): DocsReport {
+ return this.report
+ }
+
+ addContext(
+ message: string,
+ level: LogLevel,
+ ...args: [ts.Node?] | [number, MinimalSourceFile]
+ ): string {
+ const messageLevel = {
+ [LogLevel.Error]: 'error',
+ [LogLevel.Warn]: 'warning',
+ [LogLevel.Info]: 'info',
+ [LogLevel.Verbose]: 'verbose',
+ [LogLevel.None]: 'none'
+ }[level] as MessageLevel
+
+ let pos
+ let file
+
+ console.info('typedoc-context args >>>', { message, level, args })
+ if (typeof args[0] == 'undefined') {
+ pos = 0
+ file = undefined
+ } else if (typeof args[0] !== 'number') {
+ pos = args[0].getStart(args[0].getSourceFile(), false)
+ file = args[0].getSourceFile()
+ } else {
+ pos = args[0]
+ file = args[1]
+ }
+
+ let sourceFilePath
+ let sourceFileLine
+ let sourceFileColumn
+ let context
+ let text = message
+ if (file) {
+ const { line, character } = file.getLineAndCharacterOfPosition(pos)
+ sourceFilePath = file.fileName
+ sourceFileLine = line
+ sourceFileColumn = character
+
+ const location = `${sourceFilePath}:${line + 1}:${character}`
+
+ const start = file.text.lastIndexOf('\n', pos) + 1
+ let end = file.text.indexOf('\n', start)
+ if (end === -1) end = file.text.length
+
+ const prefix = `${location} - [${level}]`
+
+ context = `${line + 1} ${file.text.substring(start, end)}`
+ text = `${prefix} ${message}\n\n${context}\n`
+
+ sourceFilePath
+ } else {
+ const [rawMessage, fileName] = parseMessageFileLocation(message)
+ if (rawMessage && fileName) {
+ sourceFilePath = fileName
+ text = rawMessage
+ }
+ }
+
+ this.reportMessage(
+ messageLevel,
+ text,
+ sourceFilePath,
+ sourceFileLine,
+ sourceFileColumn,
+ context
+ )
+
+ return text
+ }
+
+ private reportMessage(
+ messageLevel: MessageLevel,
+ text: string,
+ sourceFilePath?: string,
+ sourceFileLine?: number,
+ sourceFileColumn?: number,
+ context?: string
+ ): void {
+ // count errors and warnings
+ switch (messageLevel) {
+ case 'error':
+ this.report.errorsCount += 1
+ break
+ case 'warning':
+ this.report.warningsCount += 1
+ break
+ case 'info':
+ break
+ default:
+ return
+ }
+
+ this.report.messages.push({
+ level: messageLevel,
+ category: 'extractor',
+ messageId: getTypedocMessageId(text),
+ text,
+ sourceFilePath,
+ sourceFileLine,
+ sourceFileColumn,
+ context
+ })
+ }
+}
+
+const getTypedocMessageId = (text: string): string => {
+ // exports validations
+ if (text.includes('missing export')) {
+ return 'typedoc:missing-export'
+ } else if (text.includes('intentionally not exported')) {
+ return 'typedoc:unintentional-export'
+ } else if (text.includes('not included in the documentation')) {
+ return 'typedoc:missing-reference'
+
+ // links validations
+ } else if (text.includes('resolve link')) {
+ return 'typedoc:invalid-link'
+
+ // docs validation
+ } else if (text.includes('not have any documentation')) {
+ return 'typedoc:missing-docs'
+
+ // unknown
+ } else {
+ return 'typedoc:generic'
+ }
+}
+
+const parseMessageFileLocation = (text: string): [string?, string?] => {
+ // Regular expression to capture the message and file path
+ const regex = /^(.+?), defined in (.*), (.+?)$/
+ const match = text.match(regex)
+
+ if (match) {
+ // Extracted parts from the log
+ const message = `${match[1]} ${match[3]}`
+ const file = match[2]
+
+ // Creating the object with the extracted information
+ return [message, file]
+ }
+
+ return [undefined, undefined]
+}