Skip to content

Commit

Permalink
chore(jsdoc): add doc to parts of doc gen code
Browse files Browse the repository at this point in the history
this commit adds JSDoc to a portion of the codebase that is related to
the generation of documentation
  • Loading branch information
rwaskiewicz committed Sep 29, 2022
1 parent 241e8f1 commit 1317cdf
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 9 deletions.
5 changes: 5 additions & 0 deletions src/compiler/build/build-ctx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ export class BuildContext implements d.BuildCtx {
}
}

/**
* Generate a timestamp of the format `YYYY-MM-DDThh:mm:ss`, using the number of seconds that have elapsed since
* January 01, 1970, and the time this function was called
* @returns the generated timestamp
*/
export const getBuildTimestamp = () => {
const d = new Date();

Expand Down
87 changes: 79 additions & 8 deletions src/compiler/docs/generate-doc-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import { typescriptVersion, version } from '../../version';
import { getBuildTimestamp } from '../build/build-ctx';
import { AUTO_GENERATE_COMMENT } from './constants';

/**
* Generate metadata that will be used to generate any given documentation-related output target(s)
* @param config the configuration associated with the current Stencil task run
* @param compilerCtx the current compiler context
* @param buildCtx the build context for the current Stencil task run
* @returns the generated metadata
*/
export const generateDocData = async (
config: d.ValidatedConfig,
compilerCtx: d.CompilerCtx,
Expand All @@ -23,6 +30,13 @@ export const generateDocData = async (
};
};

/**
* Derive the metadata for each Stencil component
* @param config the configuration associated with the current Stencil task run
* @param compilerCtx the current compiler context
* @param buildCtx the build context for the current Stencil task run
* @returns the derived metadata
*/
const getDocsComponents = async (
config: d.ValidatedConfig,
compilerCtx: d.CompilerCtx,
Expand All @@ -37,8 +51,8 @@ const getDocsComponents = async (
const readme = await getUserReadmeContent(compilerCtx, readmePath);
const usage = await generateUsages(compilerCtx, usagesDir);
return moduleFile.cmps
.filter((cmp) => !cmp.internal && !cmp.isCollectionDependency)
.map((cmp) => ({
.filter((cmp: d.ComponentCompilerMeta) => !cmp.internal && !cmp.isCollectionDependency)
.map((cmp: d.ComponentCompilerMeta) => ({
dirPath,
filePath: relative(config.rootDir, filePath),
fileName: basename(filePath),
Expand Down Expand Up @@ -69,9 +83,12 @@ const getDocsComponents = async (
return sortBy(flatOne(results), (cmp) => cmp.tag);
};

const buildDocsDepGraph = (cmp: d.ComponentCompilerMeta, cmps: d.ComponentCompilerMeta[]) => {
const buildDocsDepGraph = (
cmp: d.ComponentCompilerMeta,
cmps: d.ComponentCompilerMeta[]
): d.JsonDocsDependencyGraph => {
const dependencies: d.JsonDocsDependencyGraph = {};
function walk(tagName: string) {
function walk(tagName: string): void {
if (!dependencies[tagName]) {
const cmp = cmps.find((c) => c.tagName === tagName);
const deps = cmp.directDependencies;
Expand All @@ -94,6 +111,11 @@ const buildDocsDepGraph = (cmp: d.ComponentCompilerMeta, cmps: d.ComponentCompil
return dependencies;
};

/**
* Determines the encapsulation string to use, based on the provided compiler metadata
* @param cmp the metadata for a single component
* @returns the encapsulation level, expressed as a string
*/
const getDocsEncapsulation = (cmp: d.ComponentCompilerMeta): 'shadow' | 'scoped' | 'none' => {
if (cmp.encapsulation === 'shadow') {
return 'shadow';
Expand Down Expand Up @@ -251,7 +273,13 @@ const getDocsListeners = (listeners: d.ComponentCompilerListener[]): d.JsonDocsL
}));
};

const getDocsDeprecationText = (tags: d.JsonDocsTag[]) => {
/**
* Get the text associated with a `@deprecated` tag, if one exists
* @param tags the tags associated with a JSDoc block on a node in the AST
* @returns the text associated with the first found `@deprecated` tag. If a `@deprecated` tag exists but does not
* have associated text, an empty string is returned. If no such tag is found, return `undefined`
*/
const getDocsDeprecationText = (tags: d.JsonDocsTag[]): string | undefined => {
const deprecation = tags.find((t) => t.name === 'deprecated');
if (deprecation) {
return deprecation.text || '';
Expand Down Expand Up @@ -284,9 +312,20 @@ export const getNameText = (name: string, tags: d.JsonDocsTag[]) => {
});
};

const getUserReadmeContent = async (compilerCtx: d.CompilerCtx, readmePath: string) => {
/**
* Attempts to read a pre-existing README.md file from disk, returning any content generated by the user.
*
* For simplicity's sake, it is assumed that all user-generated content will fall before {@link AUTO_GENERATE_COMMENT}
*
* @param compilerCtx the current compiler context
* @param readmePath the path to the README file to read
* @returns the user generated content that occurs before {@link AUTO_GENERATE_COMMENT}. If no user generated content
* exists, or if there was an issue reading the file, return `undefined`
*/
const getUserReadmeContent = async (compilerCtx: d.CompilerCtx, readmePath: string): Promise<string | undefined> => {
try {
const existingContent = await compilerCtx.fs.readFile(readmePath);
// subtract one to get everything up to, but not including the auto generated comment
const userContentIndex = existingContent.indexOf(AUTO_GENERATE_COMMENT) - 1;
if (userContentIndex >= 0) {
return existingContent.substring(0, userContentIndex);
Expand All @@ -295,31 +334,63 @@ const getUserReadmeContent = async (compilerCtx: d.CompilerCtx, readmePath: stri
return undefined;
};

const generateDocs = (readme: string, jsdoc: d.CompilerJsDoc) => {
/**
* Generate documentation for a given component based on the provided JSDoc and README contents
* @param readme the contents of a component's README file, without any autogenerated contents
* @param jsdoc the JSDoc associated with the component's declaration
* @returns the generated documentation
*/
const generateDocs = (readme: string, jsdoc: d.CompilerJsDoc): string => {
const docs = jsdoc.text;
if (docs !== '' || !readme) {
// just return the existing docs if they exist. these would have been captured earlier in the compilation process.
// if they don't exist, and there's no README to process, return an empty string.
return docs;
}

/**
* Parse the README, storing the first section of content.
* Content is defined as the area between two non-consecutive lines that start with a '#':
* ```
* # Header 1
* This is some content
* # Header 2
* This is more content
* # Header 3
* Again, content
* ```
* In the example above, this chunk of code is designed to capture "This is some content"
*/
let isContent = false;
const lines = readme.split('\n');
const contentLines = [];
for (const line of lines) {
const isHeader = line.startsWith('#');
if (isHeader && isContent) {
// we were actively parsing content, but found a new header, break out
break;
}
if (!isHeader && !isContent) {
// we've found content for the first time, set this sentinel to `true`
isContent = true;
}
if (isContent) {
// we're actively parsing the first found block of content, add it to our list for later
contentLines.push(line);
}
}
return contentLines.join('\n').trim();
};

const generateUsages = async (compilerCtx: d.CompilerCtx, usagesDir: string) => {
/**
* This function is responsible for reading the contents of all markdown files in a provided `usage` directory and
* returning their contents
* @param compilerCtx the current compiler context
* @param usagesDir the directory to read usages markdown files from
* @returns an object that maps the filename containing the usage example, to the file's contents. If an error occurs,
* an empty object is returned.
*/
const generateUsages = async (compilerCtx: d.CompilerCtx, usagesDir: string): Promise<d.JsonDocsUsage> => {
const rtn: d.JsonDocsUsage = {};

try {
Expand Down
12 changes: 11 additions & 1 deletion src/compiler/output-targets/output-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,17 @@ import {
isOutputTargetDocsVscode,
} from './output-utils';

export const outputDocs = async (config: d.ValidatedConfig, compilerCtx: d.CompilerCtx, buildCtx: d.BuildCtx) => {
/**
* Generate documentation-related output targets
* @param config the configuration associated with the current Stencil task run
* @param compilerCtx the current compiler context
* @param buildCtx the build context for the current Stencil task run
*/
export const outputDocs = async (
config: d.ValidatedConfig,
compilerCtx: d.CompilerCtx,
buildCtx: d.BuildCtx
): Promise<void> => {
if (!config.buildDocs) {
return;
}
Expand Down
18 changes: 18 additions & 0 deletions src/declarations/stencil-private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -935,13 +935,31 @@ export interface ComponentCompilerState {
name: string;
}

/**
* Representation of JSDoc that is pulled off a node in the AST
*/
export interface CompilerJsDoc {
/**
* The text associated with the JSDoc
*/
text: string;
/**
* Tags included in the JSDoc
*/
tags: CompilerJsDocTagInfo[];
}

/**
* Representation of a tag that exists in a JSDoc
*/
export interface CompilerJsDocTagInfo {
/**
* The name of the tag - e.g. `@deprecated`
*/
name: string;
/**
* Additional text that is associated with the tag - e.g. `@deprecated use v2 of this API`
*/
text?: string;
}

Expand Down
20 changes: 20 additions & 0 deletions src/declarations/stencil-public-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ export interface JsonDocsValue {
type: string;
}

/**
* A mapping of file names to their contents.
*
* This type is meant to be used when reading one or more usage markdown files associated with a component. For the
* given directory structure:
* ```
* src/components/my-component
* ├── my-component.tsx
* └── usage
* ├── bar.md
* └── foo.md
* ```
* an instance of this type would include the name of the markdown file, mapped to its contents:
* ```ts
* {
* 'bar': STRING_CONTENTS_OF_BAR.MD
* 'foo': STRING_CONTENTS_OF_FOO.MD
* }
* ```
*/
export interface JsonDocsUsage {
[key: string]: string;
}
Expand Down

0 comments on commit 1317cdf

Please sign in to comment.