> = [],
44 | ) {}
45 |
46 | /**
47 | * Adds the plugin to Documentalist. Returns a new instance of Documentalist
48 | * with a template type that includes the data from the plugin. This way the
49 | * `documentFiles` and `documentGlobs` methods will return an object that is
50 | * already typed to include each plugin's output.
51 | *
52 | * The plugin is applied to all files whose absolute path matches the
53 | * supplied pattern.
54 | *
55 | * @param pattern - A regexp pattern or a file extension string like "js"
56 | * @param plugin - The plugin implementation
57 | * @returns A new instance of `Documentalist` with an extended type
58 | */
59 | public use(pattern: RegExp | string, plugin: Plugin
): Documentalist {
60 | if (typeof pattern === "string") {
61 | pattern = new RegExp(`${pattern}$`);
62 | }
63 |
64 | const newPlugins = [...this.plugins, { pattern, plugin }] as Array>;
65 | return new Documentalist(this.options, newPlugins);
66 | }
67 |
68 | /**
69 | * Returns a new instance of Documentalist with no plugins.
70 | */
71 | public clearPlugins(): Documentalist<{}> {
72 | return new Documentalist<{}>(this.options, []);
73 | }
74 |
75 | /**
76 | * Finds all files matching the provided variadic glob expressions and then
77 | * runs `documentFiles` with them, emitting all the documentation data.
78 | */
79 | public async documentGlobs(...filesGlobs: string[]) {
80 | const files = this.expandGlobs(filesGlobs);
81 | return this.documentFiles(files);
82 | }
83 |
84 | /**
85 | * Iterates over all plugins, passing all matching files to each in turn.
86 | * The output of each plugin is merged to produce the resulting
87 | * documentation object.
88 | *
89 | * The return type `T` is a union of each plugin's data type.
90 | */
91 | public async documentFiles(files: File[]) {
92 | const compiler = new CompilerImpl(this.options);
93 | // need an empty object of correct type that we can merge into
94 | // tslint:disable-next-line:no-object-literal-type-assertion
95 | const documentation = {} as T;
96 | for (const { pattern, plugin } of this.plugins) {
97 | const pluginFiles = files.filter(f => pattern.test(f.path));
98 | const pluginDocumentation = await plugin.compile(pluginFiles, compiler);
99 | this.mergeInto(documentation, pluginDocumentation);
100 | }
101 | return documentation;
102 | }
103 |
104 | /**
105 | * Expands an array of globs and flatten to a single array of files.
106 | */
107 | private expandGlobs(filesGlobs: string[]): File[] {
108 | return filesGlobs
109 | .map(filesGlob => glob.sync(filesGlob))
110 | .reduce((a, b) => a.concat(b))
111 | .map(fileName => {
112 | const absolutePath = path.resolve(fileName);
113 | return {
114 | path: absolutePath,
115 | read: () => fs.readFileSync(absolutePath, "utf8"),
116 | };
117 | });
118 | }
119 |
120 | /**
121 | * Shallow-merges keys form source into destination object (modifying it in the process).
122 | */
123 | private mergeInto(destination: T, source: T) {
124 | for (const key in source) {
125 | if (source[key] != null) {
126 | if (destination[key] != null) {
127 | console.warn(`WARNING: Duplicate plugin key "${key}". Your plugins are overwriting each other.`);
128 | }
129 | destination[key] = source[key];
130 | }
131 | }
132 | return destination;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/packages/compiler/src/compilerImpl.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Palantir Technologies, Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { Block, Compiler, HeadingTag, StringOrTag } from "@documentalist/client";
18 | import * as yaml from "js-yaml";
19 | import { marked, MarkedOptions } from "marked";
20 | import { relative } from "path";
21 |
22 | /**
23 | * Matches the triple-dash metadata block on the first line of markdown file.
24 | * The first capture group contains YAML content.
25 | */
26 | const METADATA_REGEX = /^---\n?((?:.|\n)*)\n---\n/;
27 |
28 | /**
29 | * Splits text content for lines that begin with `@tagName`.
30 | */
31 | const TAG_REGEX = /^@(\S+)(?:\s+([^\n]+))?$/;
32 | const TAG_SPLIT_REGEX = /^(@\S+(?:\s+[^\n]+)?)$/gm;
33 |
34 | export interface CompilerOptions {
35 | /** Options for markdown rendering. See https://github.com/chjj/marked#options-1. */
36 | markdown?: MarkedOptions;
37 |
38 | /**
39 | * Reserved @tags that should be preserved in the contents string.
40 | * A common use case is allowing specific code constructs, like `@Decorator` names.
41 | * Do not include the `@` prefix in the strings.
42 | */
43 | reservedTags?: string[];
44 |
45 | /**
46 | * Base directory for generating relative `sourcePath`s.
47 | *
48 | * This option _does not affect_ glob expansion, only the generation of
49 | * `sourcePath` in plugin data.
50 | * @default process.cwd()
51 | */
52 | sourceBaseDir?: string;
53 | }
54 |
55 | export class CompilerImpl implements Compiler {
56 | public constructor(private options: CompilerOptions) {}
57 |
58 | public objectify(array: T[], getKey: (item: T) => string) {
59 | return array.reduce<{ [key: string]: T }>((obj, item) => {
60 | obj[getKey(item)] = item;
61 | return obj;
62 | }, {});
63 | }
64 |
65 | public relativePath = (path: string) => {
66 | const { sourceBaseDir = process.cwd() } = this.options;
67 | return relative(sourceBaseDir, path);
68 | };
69 |
70 | public renderBlock = (blockContent: string, reservedTagWords = this.options.reservedTags): Block => {
71 | const { contentsRaw, metadata } = this.extractMetadata(blockContent.trim());
72 | const contents = this.renderContents(contentsRaw, reservedTagWords);
73 | return { contents, contentsRaw, metadata };
74 | };
75 |
76 | public renderMarkdown = (markdown: string) => marked(markdown, this.options.markdown);
77 |
78 | /**
79 | * Converts the content string into an array of `ContentNode`s. If the
80 | * `contents` option is `html`, the string nodes will also be rendered with
81 | * markdown.
82 | */
83 | private renderContents(content: string, reservedTagWords?: string[]) {
84 | const splitContents = this.parseTags(content, reservedTagWords);
85 | return splitContents
86 | .map(node => (typeof node === "string" ? this.renderMarkdown(node) : node))
87 | .filter(node => node !== "");
88 | }
89 |
90 | /**
91 | * Extracts optional YAML frontmatter metadata block from the beginning of a
92 | * markdown file and parses it to a JS object.
93 | */
94 | private extractMetadata(text: string) {
95 | const match = METADATA_REGEX.exec(text);
96 | if (match === null) {
97 | return { contentsRaw: text, metadata: {} };
98 | }
99 |
100 | const contentsRaw = text.substr(match[0].length);
101 | const yamlObject: any | undefined = yaml.load(match[1]);
102 |
103 | return { contentsRaw, metadata: yamlObject ?? {} };
104 | }
105 |
106 | /**
107 | * Splits the content string when it encounters a line that begins with a
108 | * `@tag`. You may prevent this splitting by specifying an array of reserved
109 | * tag names.
110 | */
111 | private parseTags(content: string, reservedWords: string[] = []) {
112 | // using reduce so we can squash consecutive strings (<= 1 entry per iteration)
113 | return content.split(TAG_SPLIT_REGEX).reduce((arr, str) => {
114 | const match = TAG_REGEX.exec(str);
115 | if (match === null || reservedWords.indexOf(match[1]) >= 0) {
116 | if (typeof arr[arr.length - 1] === "string") {
117 | // merge consecutive strings to avoid breaking up code blocks
118 | arr[arr.length - 1] += str;
119 | } else {
120 | arr.push(str);
121 | }
122 | } else {
123 | const tag = match[1];
124 | const value = match[2];
125 | if (/#+/.test(tag)) {
126 | // NOTE: not enough information to populate `route` field yet
127 | const heading: HeadingTag = {
128 | level: tag.length,
129 | route: "",
130 | tag: "heading",
131 | value,
132 | };
133 | arr.push(heading);
134 | } else {
135 | arr.push({ tag, value });
136 | }
137 | }
138 | return arr;
139 | }, []);
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/packages/compiler/src/plugins/markdown.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Palantir Technologies, Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import {
18 | Block,
19 | Compiler,
20 | File,
21 | HeadingNode,
22 | isHeadingTag,
23 | isPageNode,
24 | MarkdownPluginData,
25 | PageData,
26 | PageNode,
27 | Plugin,
28 | slugify,
29 | } from "@documentalist/client";
30 | import * as path from "path";
31 | import { PageMap } from "../page";
32 |
33 | export interface MarkdownPluginOptions {
34 | /**
35 | * Page reference that lists the nav roots.
36 | * @default "_nav"
37 | */
38 | navPage: string;
39 | }
40 |
41 | /**
42 | * The `MarkdownPlugin` parses and renders markdown pages and produces a navigation tree of all documents.
43 | * This plugin traces `@page` and `@#+` "heading" tags to discover pages (given a single starting `navPage`)
44 | * and build up a tree representation of those pages.
45 | *
46 | * @see PageData (rendered markdown page)
47 | * @see PageNode (node in navigation tree)
48 | */
49 | export class MarkdownPlugin implements Plugin {
50 | private options: MarkdownPluginOptions;
51 |
52 | public constructor(options: Partial = {}) {
53 | this.options = {
54 | navPage: "_nav",
55 | ...options,
56 | };
57 | }
58 |
59 | /**
60 | * Reads the given set of markdown files and adds their data to the internal storage.
61 | * Returns a plain object mapping page references to their data.
62 | */
63 | public compile(markdownFiles: File[], compiler: Compiler): MarkdownPluginData {
64 | const pageMap = this.buildPageStore(markdownFiles, compiler);
65 | // now that we have all known pages, we can resolve @include tags.
66 | this.resolveIncludeTags(pageMap);
67 | // generate navigation tree after all pages loaded and processed.
68 | const { navPage } = this.options;
69 | if (pageMap.get(navPage) == null) {
70 | throw new Error(`Error generating page map: options.navPage "${navPage}" does not exist.`);
71 | }
72 | const nav = pageMap.toTree(navPage).children.filter(isPageNode);
73 | // use nav tree to fill in `route` for all pages and headings.
74 | this.resolveRoutes(pageMap, nav);
75 | // generate object at the end, after `route` has been computed throughout.
76 | const pages = pageMap.toObject();
77 | return { nav, pages };
78 | }
79 |
80 | private blockToPage(sourcePath: string, block: Block): PageData {
81 | const reference = getReference(sourcePath, block);
82 | return {
83 | reference,
84 | route: reference,
85 | sourcePath,
86 | title: getTitle(block),
87 | ...block,
88 | };
89 | }
90 |
91 | /** Convert each file to PageData and populate store. */
92 | private buildPageStore(markdownFiles: File[], { relativePath, renderBlock }: Compiler) {
93 | const pageMap = new PageMap();
94 | for (const file of markdownFiles) {
95 | const block = renderBlock(file.read());
96 | const page = this.blockToPage(relativePath(file.path), block);
97 | pageMap.set(page.reference, page);
98 | }
99 | return pageMap;
100 | }
101 |
102 | /**
103 | * Computes `route` for the given `node` based on its parent.
104 | * If node is a page, then it also computes `route` for each heading and recurses through child
105 | * pages.
106 | */
107 | private recurseRoute(pageMap: PageMap, node: PageNode | HeadingNode, parent?: PageNode) {
108 | // compute route for page and heading NODES (from nav tree)
109 | const baseRoute = parent === undefined ? [] : [parent.route];
110 | const route = isPageNode(node)
111 | ? baseRoute.concat(node.reference).join("/")
112 | : baseRoute.concat(slugify(node.title)).join(".");
113 | node.route = route;
114 |
115 | if (isPageNode(node)) {
116 | // node is a page, so it must exist in PageMap.
117 | const page = pageMap.get(node.reference)!;
118 | page.route = route;
119 |
120 | page.contents.forEach(content => {
121 | // inject `route` field into heading TAGS (from page contents)
122 | if (isHeadingTag(content)) {
123 | // h1 tags do not get nested as they are used as page title
124 | content.route = content.level > 1 ? [route, slugify(content.value)].join(".") : route;
125 | }
126 | });
127 | node.children.forEach(child => this.recurseRoute(pageMap, child, node));
128 | }
129 | }
130 |
131 | private resolveRoutes(pageMap: PageMap, nav: PageNode[]) {
132 | for (const page of nav) {
133 | // walk the nav tree and compute `route` property for each resource.
134 | this.recurseRoute(pageMap, page);
135 | }
136 | }
137 |
138 | /** Iterates `contents` array and inlines any `@include page` tags. */
139 | private resolveIncludeTags(pageStore: PageMap) {
140 | for (const page of pageStore.pages()) {
141 | // using `reduce` so we can add one or many entries for each node
142 | page.contents = page.contents.reduce((array, content) => {
143 | if (typeof content === "string" || content.tag !== "include") {
144 | return array.concat(content);
145 | }
146 | // inline @include page
147 | const pageToInclude = pageStore.get(content.value);
148 | if (pageToInclude === undefined) {
149 | throw new Error(`Unknown @include reference '${content.value}' in '${page.reference}'`);
150 | }
151 | return array.concat(pageToInclude.contents);
152 | }, []);
153 | }
154 | }
155 | }
156 |
157 | function getReference(absolutePath: string, { metadata }: Block) {
158 | if (metadata.reference != null) {
159 | return metadata.reference;
160 | }
161 | return path.basename(absolutePath, path.extname(absolutePath));
162 | }
163 |
164 | function getTitle(block: Block) {
165 | if (block.metadata.title != null) {
166 | return block.metadata.title;
167 | }
168 |
169 | const first = block.contents[0];
170 | if (isHeadingTag(first)) {
171 | return first.value;
172 | }
173 |
174 | return "(untitled)";
175 | }
176 |
--------------------------------------------------------------------------------
/packages/client/src/typescript.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Palantir Technologies, Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { Block } from "./compiler";
18 |
19 | /** Enumeration describing the various kinds of member supported by this plugin. */
20 | export enum Kind {
21 | Class = "class",
22 | Constructor = "constructor",
23 | Enum = "enum",
24 | EnumMember = "enum member",
25 | Interface = "interface",
26 | Method = "method",
27 | Parameter = "parameter",
28 | Signature = "signature",
29 | Property = "property",
30 | TypeAlias = "type alias",
31 | Accessor = "accessor",
32 | }
33 |
34 | /** Compiler flags about this member. */
35 | export interface TsFlags {
36 | /** This flag supports an optional message, typically used to include a version number. */
37 | isDeprecated?: boolean | string;
38 | isExported?: boolean;
39 | isExternal?: boolean;
40 | isOptional?: boolean;
41 | isPrivate?: boolean;
42 | isProtected?: boolean;
43 | isPublic?: boolean;
44 | isRest?: boolean;
45 | isStatic?: boolean;
46 | }
47 |
48 | /** Base type for all typescript documentation members. */
49 | export interface TsDocBase {
50 | /** Type brand indicating kind of member; type guards will reveal further information about it. */
51 | kind: K;
52 |
53 | /** Compiled documentation: `contents` field contains an array of markdown strings or `@tag value` objects. */
54 | documentation?: Block;
55 |
56 | /** Original file name in which this member originated, relative to current working directory. */
57 | fileName?: string;
58 |
59 | flags?: TsFlags;
60 |
61 | /** Name of this member in code, also used as its identifiers in the data store. */
62 | name: string;
63 |
64 | /**
65 | * Absolute URL pointing to source file in repository, including line number.
66 | * If `gitBranch` option is provided to the `TypescriptPlugin`, the URL will reference that branch.
67 | * Otherwise, it will reference the current commit hash.
68 | * @see ITypescriptPluginOptions.gitBranch
69 | */
70 | sourceUrl?: string;
71 | }
72 |
73 | /**
74 | * Common type for a callable member, something that can be invoked.
75 | * @see TsConstructor
76 | * @see TsMethod
77 | */
78 | export interface TsCallable {
79 | /** Type name from which this method was inherited. Typically takes the form `Interface.member`. */
80 | inheritedFrom?: string;
81 | /** A method has at least one signature, which describes the parameters and return type and contains documentation. */
82 | signatures: TsSignature[];
83 | }
84 |
85 | /** Re-usable interface for Typescript members that support a notion of "default value." */
86 | export interface TsDefaultValue {
87 | /** The default value of this property, from an initializer or an `@default` tag. */
88 | defaultValue?: string;
89 | }
90 |
91 | /** Re-usable interface for Typescript members that look like objects. */
92 | export interface TsObjectDefinition {
93 | /** List of type strings that this definition `extends`. */
94 | extends?: string[];
95 | /** List of type names that this definition `implements`. */
96 | implements?: string[];
97 | /** Index signature for this object, if declared. */
98 | indexSignature?: TsSignature;
99 | /** Property members of this definition. */
100 | properties: TsProperty[];
101 | /** Method members of this definiton. */
102 | methods: TsMethod[];
103 | }
104 |
105 | /**
106 | * Documentation for a class constructor. See `signatures` array for actual callable signatures and rendered docs.
107 | * @see TsClass
108 | */
109 | export interface TsConstructor extends TsDocBase, TsCallable {
110 | kind: Kind.Constructor;
111 | }
112 |
113 | export interface TsAccessor extends TsDocBase {
114 | kind: Kind.Accessor;
115 | /** If a set signature is defined and documented for this accessor, this will contain its documentation. */
116 | getDocumentation: Block | undefined;
117 | /** If a get signature is defined and documented for this accessor, this will contain its documentation. */
118 | setDocumentation: Block | undefined;
119 | /** Type of the accessor. */
120 | type: string;
121 | }
122 |
123 | /** Documentation for a method. See `signatures` array for actual callable signatures and rendered docs. */
124 | export interface TsMethod extends TsDocBase, TsCallable {
125 | kind: Kind.Method;
126 | }
127 |
128 | /**
129 | * Documentation for a single signature, including parameters, return type, and full type string.
130 | * Signatures are used for methods and constructors on classes or interfaces, and for index signatures on objects.
131 | */
132 | export interface TsSignature extends TsDocBase {
133 | kind: Kind.Signature;
134 | /** Signatures do not have flags of their own. Flags can be found on the parent and on each parameter. */
135 | flags: undefined;
136 | /** Signature parameters, each with their own docs and data. */
137 | parameters: TsParameter[];
138 | /** Return type of the signature. */
139 | returnType: string;
140 | /** Fully qualified type string describing this method, including parameters and return type. */
141 | type: string;
142 | }
143 |
144 | /** Documentation for a single parameter to a signature. */
145 | export interface TsParameter extends TsDocBase, TsDefaultValue {
146 | kind: Kind.Parameter;
147 | /** Fully qualified type string describing this parameter. */
148 | type: string;
149 | /** Parameters do not have their own URL; see the containing signature. */
150 | sourceUrl: undefined;
151 | }
152 |
153 | /** Documentation for a property of an object, which may have a default value. */
154 | export interface TsProperty extends TsDocBase, TsDefaultValue {
155 | kind: Kind.Property;
156 | /** Type name from which this property was inherited. Typically takes the form `Interface.member`. */
157 | inheritedFrom?: string;
158 | /** Type string describing this property. */
159 | type: string;
160 | }
161 |
162 | /** Documentation for an `interface` definition. */
163 | export interface TsInterface extends TsDocBase, TsObjectDefinition {
164 | kind: Kind.Interface;
165 | }
166 |
167 | /** Documentation for a `class` definition. */
168 | export interface TsClass extends TsDocBase, TsObjectDefinition {
169 | kind: Kind.Class;
170 | /** Constructor signature of this class. Note the special name here, as `constructor` is a JavaScript keyword. */
171 | constructorType: TsConstructor;
172 | accessors: TsAccessor[];
173 | }
174 | /** A member of an `enum` definition. An enum member will have a `defaultValue` if it was declared with an initializer. */
175 | export interface TsEnumMember extends TsDocBase, TsDefaultValue {
176 | kind: Kind.EnumMember;
177 | }
178 |
179 | /** Documentation for an `enum` definition. */
180 | export interface TsEnum extends TsDocBase {
181 | kind: Kind.Enum;
182 | /** Enumeration members. */
183 | members: TsEnumMember[];
184 | }
185 |
186 | /** A type alias, defined using `export type {name} = {type}.` The `type` property will contain the full type alias as a string. */
187 | export interface TsTypeAlias extends TsDocBase {
188 | kind: Kind.TypeAlias;
189 | /** Type string for which this member is an alias. */
190 | type: string;
191 | }
192 |
193 | export type TsDocEntry = TsClass | TsInterface | TsEnum | TsMethod | TsTypeAlias;
194 |
195 | /**
196 | * The `TypescriptPlugin` exports a `typescript` key that contains a map of member name to
197 | * `class` or `interface` definition.
198 | *
199 | * Only classes and interfaces are provided at this root level, but each member contains full
200 | * information about its children, such as methods (and signatures and parameters) and properties.
201 | */
202 | export interface TypescriptPluginData {
203 | typescript: {
204 | [name: string]: TsDocEntry;
205 | };
206 | }
207 |
208 | function typeguard(kind: Kind) {
209 | return (data: any): data is T => data != null && (data as T).kind === kind;
210 | }
211 |
212 | export const isTsClass = typeguard(Kind.Class);
213 | export const isTsConstructor = typeguard(Kind.Constructor);
214 | export const isTsEnum = typeguard(Kind.Enum);
215 | export const isTsEnumMember = typeguard(Kind.EnumMember);
216 | export const isTsInterface = typeguard(Kind.Interface);
217 | export const isTsMethod = typeguard(Kind.Method);
218 | export const isTsParameter = typeguard(Kind.Parameter);
219 | export const isTsProperty = typeguard(Kind.Property);
220 | export const isTsSignature = typeguard(Kind.Signature);
221 | export const isTsTypeAlias = typeguard(Kind.TypeAlias);
222 |
--------------------------------------------------------------------------------
/packages/compiler/src/plugins/typescript/typescriptPlugin.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Palantir Technologies, Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import type { Compiler, File, Plugin, TsDocEntry, TypescriptPluginData } from "@documentalist/client";
18 | import { readFileSync } from "fs";
19 | import { dirname } from "path";
20 | import { tsconfigResolverSync } from "tsconfig-resolver";
21 | import { Application, LogLevel, TSConfigReader, TypeDocOptions, TypeDocReader } from "typedoc";
22 | import * as ts from "typescript";
23 | import { Visitor } from "./visitor";
24 |
25 | export interface TypescriptPluginOptions {
26 | /**
27 | * List of entry point modules.
28 | * @default ["src/index.ts"]
29 | */
30 | entryPoints?: TypeDocOptions["entryPoints"];
31 |
32 | /**
33 | * Array of patterns (string or RegExp) to exclude members by name.
34 | * Strings will be converted to regular expressions through `string.match(pattern)`.
35 | *
36 | * Note that excluded members will still be parsed by the compiler, so they can be referenced
37 | * by other symbols, but they will not appear in the output data.
38 | */
39 | excludeNames?: Array;
40 |
41 | /**
42 | * Array of patterns (string or RegExp) to exclude members based on file path.
43 | * See `excludeNames` above for usage notes.
44 | */
45 | excludePaths?: Array;
46 |
47 | /**
48 | * Specify the branch name to use when generating source file URLs.
49 | * If omitted, the current commit hash will be used.
50 | * @see ITsDocBase.url
51 | */
52 | gitBranch?: string;
53 |
54 | /**
55 | * Enable parsing of `.d.ts` files.
56 | * @default false
57 | */
58 | includeDeclarations?: boolean;
59 |
60 | /**
61 | * Whether files in `node_modules` should be included in the TypeScript
62 | * compilation context. This is disabled by default because it typically
63 | * results in an explosion of data size due to including all types from _all
64 | * installed packages_, the vast majority of which are not useful for
65 | * documenting your own APIs.
66 | *
67 | * Enable at your own risk, and consider using the `excludeNames` and
68 | * `excludePaths` options above to filter the output data.
69 | * @default false
70 | */
71 | includeNodeModules?: boolean;
72 |
73 | /**
74 | * Whether `private` fields should be included in the data.
75 | * This is disabled by default as `private` fields typically do not need to be publicly documented.
76 | * @default false
77 | */
78 | includePrivateMembers?: boolean;
79 |
80 | /** Path to tsconfig file. */
81 | tsconfigPath?: string;
82 |
83 | /**
84 | * If enabled, logs messages and compiler errors to the console.
85 | * Note that compiler errors are ignored by Typedoc so they do not affect docs generation.
86 | * @default false
87 | */
88 | verbose?: boolean;
89 | }
90 |
91 | export class TypescriptPlugin implements Plugin {
92 | private typedocOptions: Partial;
93 |
94 | /*
95 | * Maps of tsconfig.json paths to their TS programs and TypeDoc apps, respectively.
96 | *
97 | * These are necesary to support compilation of a list of files which may belong to separate TypeScript projects,
98 | * a situation which occurs frequently in a monorepo.
99 | */
100 | private tsPrograms: Map = new Map();
101 | private typedocApps: Map = new Map();
102 |
103 | public constructor(private options: TypescriptPluginOptions = {}) {
104 | const {
105 | entryPoints = ["src/index.ts"],
106 | includeDeclarations = false,
107 | includeNodeModules = false,
108 | includePrivateMembers = false,
109 | verbose = false,
110 | } = options;
111 |
112 | this.typedocOptions = {
113 | commentStyle: "jsdoc",
114 | entryPointStrategy: "expand",
115 | entryPoints,
116 | exclude: [
117 | includeNodeModules ? undefined : "**/node_modules/**",
118 | includeDeclarations ? undefined : "**/*.d.ts",
119 | ].filter(Boolean) as string[],
120 | excludePrivate: !includePrivateMembers,
121 | gitRevision: options.gitBranch,
122 | logLevel: verbose ? LogLevel.Verbose : LogLevel.Error,
123 | skipErrorChecking: false,
124 | };
125 | }
126 |
127 | private async initializeTypedocAppAndTsProgram(tsconfig: string, entryPoints: string[]) {
128 | const options = {
129 | ...this.typedocOptions,
130 | entryPoints,
131 | tsconfig,
132 | };
133 | const app = await Application.bootstrapWithPlugins(options, [new TypeDocReader(), new TSConfigReader()]);
134 |
135 | this.typedocApps.set(tsconfig, app);
136 |
137 | const { config } = ts.readConfigFile(tsconfig, path => readFileSync(path, { encoding: "utf-8" }));
138 | const program = ts.createProgram(entryPoints, config);
139 | this.tsPrograms.set(tsconfig, program);
140 |
141 | return app;
142 | }
143 |
144 | public async compile(files: File[], compiler: Compiler): Promise {
145 | // List of existing projects which contain some of the files to compile
146 | const existingProjectsToCompile: string[] = [];
147 |
148 | // Map of (tsconfig path -> list of files to compile)
149 | const newProjectsToCreate: Record = {};
150 |
151 | for (const file of files) {
152 | let hasExistingProject = false;
153 |
154 | // attempt to load an existing project which contains this file
155 | for (const [tsconfigPath, program] of this.tsPrograms.entries()) {
156 | if (program.getRootFileNames().includes(file.path)) {
157 | existingProjectsToCompile.push(tsconfigPath);
158 | hasExistingProject = true;
159 | }
160 | }
161 |
162 | // if we don't have one, keep track of it in the new projects we must create
163 | if (!hasExistingProject) {
164 | const tsconfigPath = this.resolveClosestTsconfig(file);
165 | if (tsconfigPath !== undefined) {
166 | if (newProjectsToCreate[tsconfigPath] !== undefined) {
167 | newProjectsToCreate[tsconfigPath].push(file.path);
168 | } else {
169 | newProjectsToCreate[tsconfigPath] = [file.path];
170 | }
171 | }
172 | }
173 | }
174 |
175 | const output: Record = {};
176 |
177 | for (const projectPath of existingProjectsToCompile) {
178 | const app = this.typedocApps.get(projectPath);
179 | if (app === undefined) {
180 | throw new Error(`[Documentalist] could not find TypeDoc application for project at ${projectPath}`);
181 | }
182 |
183 | const docs = await this.getDocumentationOutput(compiler, app);
184 | for (const [key, value] of Object.entries(docs)) {
185 | output[key] = value;
186 | }
187 | }
188 |
189 | for (const [projectPath, files] of Object.entries(newProjectsToCreate)) {
190 | const app = await this.initializeTypedocAppAndTsProgram(projectPath, files);
191 | const docs = await this.getDocumentationOutput(compiler, app);
192 | for (const [key, value] of Object.entries(docs)) {
193 | output[key] = value;
194 | }
195 | }
196 |
197 | return { typescript: output };
198 | }
199 |
200 | private async getDocumentationOutput(compiler: Compiler, app: Application) {
201 | const visitor = new Visitor(compiler, this.options);
202 | const project = await app.convert();
203 | if (project === undefined) {
204 | throw new Error(
205 | `[Documentalist] unable to generate typescript documentation for project at ${app.options.getValue(
206 | "tsconfig",
207 | )}`,
208 | );
209 | }
210 |
211 | return compiler.objectify(visitor.visitProject(project), i => i.name);
212 | }
213 |
214 | private resolveClosestTsconfig(file: File) {
215 | const { path, reason } = tsconfigResolverSync({ cwd: dirname(file.path) });
216 |
217 | switch (reason) {
218 | case "invalid-config":
219 | console.error(
220 | `[Documentalist] invalid tsconfig resolved for ${file.path}, skipping documentation of this file`,
221 | );
222 | return undefined;
223 | case "not-found":
224 | console.error(
225 | `[Documentalist] unable to find any relevant tsconfig for ${file.path}, skipping documentation of this file`,
226 | );
227 | return undefined;
228 | default:
229 | return path;
230 | }
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
--------------------------------------------------------------------------------
/packages/client/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
--------------------------------------------------------------------------------
/packages/compiler/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
--------------------------------------------------------------------------------
/packages/compiler/src/plugins/typescript/visitor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Palantir Technologies, Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import {
18 | Compiler,
19 | Kind,
20 | TsAccessor,
21 | TsClass,
22 | TsConstructor,
23 | TsDocBase,
24 | TsEnum,
25 | TsEnumMember,
26 | TsFlags,
27 | TsInterface,
28 | TsMethod,
29 | TsParameter,
30 | TsProperty,
31 | TsSignature,
32 | TsTypeAlias,
33 | } from "@documentalist/client";
34 | import { relative } from "path";
35 | import {
36 | Comment,
37 | DeclarationReflection,
38 | ParameterReflection,
39 | ProjectReflection,
40 | Reflection,
41 | ReflectionKind,
42 | SignatureReflection,
43 | } from "typedoc";
44 | import { TypescriptPluginOptions } from "./typescriptPlugin";
45 | import { resolveSignature, resolveTypeString } from "./typestring";
46 |
47 | export class Visitor {
48 | public constructor(
49 | private compiler: Compiler,
50 | private options: TypescriptPluginOptions,
51 | ) {}
52 |
53 | public visitProject(project: ProjectReflection) {
54 | const { excludePaths = [] } = this.options;
55 | // get top-level members of typedoc project
56 | return [
57 | ...this.visitChildren(project.getReflectionsByKind(ReflectionKind.Class), this.visitClass),
58 | ...this.visitChildren(project.getReflectionsByKind(ReflectionKind.Enum), this.visitEnum),
59 | ...this.visitChildren(project.getReflectionsByKind(ReflectionKind.Function), this.visitMethod),
60 | ...this.visitChildren(project.getReflectionsByKind(ReflectionKind.Interface), this.visitInterface),
61 | ...this.visitChildren(project.getReflectionsByKind(ReflectionKind.TypeAlias), def => ({
62 | ...this.makeDocEntry(def, Kind.TypeAlias),
63 | type: resolveTypeString(def.type),
64 | })),
65 | ].filter(
66 | // remove members excluded by path option
67 | ref => isNotExcluded(excludePaths, ref.fileName),
68 | );
69 | }
70 |
71 | private makeDocEntry(ref: Reflection, kind: K): TsDocBase {
72 | let comment = ref.comment;
73 |
74 | if (comment === undefined && ref.isDeclaration()) {
75 | // special case for interface properties which have function signatures - we need to go one level deeper
76 | // to access the comment
77 | ref.type?.visit({
78 | reflection: reflectionType => {
79 | if (reflectionType.declaration.signatures !== undefined) {
80 | comment = reflectionType.declaration.signatures[0].comment;
81 | }
82 | },
83 | });
84 | }
85 |
86 | return {
87 | documentation: this.renderComment(comment),
88 | fileName: getSourceFileName(ref),
89 | flags: getFlags(ref),
90 | kind,
91 | name: ref.name,
92 | sourceUrl: getSourceUrl(ref),
93 | };
94 | }
95 |
96 | private visitClass = (def: DeclarationReflection): TsClass => ({
97 | ...this.visitInterface(def),
98 | accessors: this.visitChildren(def.getChildrenByKind(ReflectionKind.Accessor), this.visitAccessor),
99 | constructorType: this.visitChildren(
100 | def.getChildrenByKind(ReflectionKind.Constructor),
101 | this.visitConstructor,
102 | )[0],
103 | kind: Kind.Class,
104 | });
105 |
106 | private visitInterface = (def: DeclarationReflection): TsInterface => ({
107 | ...this.makeDocEntry(def, Kind.Interface),
108 | extends: def.extendedTypes?.map(resolveTypeString),
109 | implements: def.implementedTypes?.map(resolveTypeString),
110 | indexSignature: def.indexSignature && this.visTsignature(def.indexSignature),
111 | methods: this.visitChildren(def.getChildrenByKind(ReflectionKind.Method), this.visitMethod, sortStaticFirst),
112 | properties: this.visitChildren(
113 | def.getChildrenByKind(ReflectionKind.Property),
114 | this.visitProperty,
115 | sortStaticFirst,
116 | ),
117 | });
118 |
119 | private visitConstructor = (def: DeclarationReflection): TsConstructor => ({
120 | ...this.visitMethod(def),
121 | kind: Kind.Constructor,
122 | });
123 |
124 | private visitEnum = (def: DeclarationReflection): TsEnum => ({
125 | ...this.makeDocEntry(def, Kind.Enum),
126 | members: this.visitChildren(def.getChildrenByKind(ReflectionKind.EnumMember), m => ({
127 | ...this.makeDocEntry(m, Kind.EnumMember),
128 | defaultValue: getDefaultValue(m),
129 | })),
130 | });
131 |
132 | private visitProperty = (def: DeclarationReflection): TsProperty => ({
133 | ...this.makeDocEntry(def, Kind.Property),
134 | defaultValue: getDefaultValue(def),
135 | inheritedFrom: def.inheritedFrom && resolveTypeString(def.inheritedFrom),
136 | type: resolveTypeString(def.type),
137 | });
138 |
139 | private visitMethod = (def: DeclarationReflection): TsMethod => ({
140 | ...this.makeDocEntry(def, Kind.Method),
141 | inheritedFrom: def.inheritedFrom && resolveTypeString(def.inheritedFrom),
142 | signatures: def.signatures !== undefined ? def.signatures.map(sig => this.visTsignature(sig)) : [],
143 | });
144 |
145 | private visTsignature = (sig: SignatureReflection): TsSignature => ({
146 | ...this.makeDocEntry(sig, Kind.Signature),
147 | flags: undefined,
148 | parameters: (sig.parameters || []).map(param => this.visitParameter(param)),
149 | returnType: resolveTypeString(sig.type),
150 | type: resolveSignature(sig),
151 | });
152 |
153 | private visitParameter = (param: ParameterReflection): TsParameter => ({
154 | ...this.makeDocEntry(param, Kind.Parameter),
155 | defaultValue: getDefaultValue(param),
156 | sourceUrl: undefined,
157 | type: resolveTypeString(param.type),
158 | });
159 |
160 | private visitAccessor = (param: DeclarationReflection): TsAccessor => {
161 | let type: string;
162 | let getDocumentation;
163 | let setDocumentation;
164 |
165 | if (param.getSignature) {
166 | type = resolveTypeString(param.getSignature.type);
167 | } else if (param.setSignature?.parameters && param.setSignature?.parameters[0] !== undefined) {
168 | type = resolveTypeString(param.setSignature.parameters[0].type);
169 | } else {
170 | throw Error("Accessor did neither define get nor set signature.");
171 | }
172 |
173 | if (param.getSignature) {
174 | getDocumentation = this.renderComment(param.getSignature.comment);
175 | }
176 | if (param.setSignature) {
177 | setDocumentation = this.renderComment(param.setSignature.comment);
178 | }
179 |
180 | return {
181 | ...this.makeDocEntry(param, Kind.Accessor),
182 | getDocumentation,
183 | setDocumentation,
184 | type,
185 | };
186 | };
187 |
188 | /** VisTs each child that passes the filter condition (based on options). */
189 | private visitChildren(
190 | children: Reflection[],
191 | visitor: (def: DeclarationReflection) => T,
192 | comparator?: (a: T, b: T) => number,
193 | ): T[] {
194 | const { excludeNames = [], excludePaths = [] } = this.options;
195 | return children
196 | .map(visitor)
197 | .filter(doc => isNotExcluded(excludeNames, doc.name) && isNotExcluded(excludePaths, doc.fileName))
198 | .sort(comparator);
199 | }
200 |
201 | /**
202 | * Converts a typedoc comment object to a rendered `Block`.
203 | */
204 | private renderComment(comment: Comment | undefined) {
205 | if (comment === undefined) {
206 | return;
207 | }
208 |
209 | let documentation = "";
210 | documentation += comment.summary.map(part => part.text).join("\n");
211 |
212 | const blockTags = comment.blockTags.filter(tag => tag.tag !== "@default" && tag.tag !== "@deprecated");
213 | if (blockTags.length > 0) {
214 | documentation += "\n\n";
215 | documentation += blockTags.map(tag => `${tag.tag} ${tag.content}`).join("\n");
216 | }
217 |
218 | return this.compiler.renderBlock(documentation);
219 | }
220 | }
221 |
222 | function getCommentTagValue(comment: Comment | undefined, tagName: string) {
223 | const maybeTag = comment?.getTag(`@${tagName}`);
224 | return maybeTag?.content.map(part => part.text.trim()).join("\n");
225 | }
226 |
227 | function getDefaultValue(ref: ParameterReflection | DeclarationReflection): string | undefined {
228 | // N.B. TypeDoc no longer sets defaultValue for enum members as of v0.23, see https://typedoc.org/guides/changelog/#v0.23.0-(2022-06-26)
229 | // Also, we typically expect enum member values to only have literal types, so we can just use the type value.
230 | if (ref.kind === ReflectionKind.EnumMember && ref.type?.type === "literal") {
231 | return ref.type?.value?.toString();
232 | }
233 |
234 | return ref.defaultValue ?? getCommentTagValue(ref.comment, "default");
235 | }
236 |
237 | function getSourceFileName(reflection: Reflection): string | undefined {
238 | if (reflection.isDeclaration() || isSignatureReflection(reflection)) {
239 | if (reflection.sources !== undefined) {
240 | const { fullFileName } = reflection.sources[0];
241 | // fullFileName relative to cwd, so it can be saved in a snapshot (machine-independent)
242 | return fullFileName && relative(process.cwd(), fullFileName);
243 | }
244 | }
245 | return undefined;
246 | }
247 |
248 | function isSignatureReflection(reflection: Reflection): reflection is SignatureReflection {
249 | return reflection.variant === "signature";
250 | }
251 |
252 | function getSourceUrl(reflection: Reflection): string | undefined {
253 | if (reflection.isDeclaration() || isSignatureReflection(reflection)) {
254 | if (reflection.sources !== undefined) {
255 | return reflection.sources[0]?.url;
256 | }
257 | }
258 | return undefined;
259 | }
260 |
261 | function getFlags(ref: Reflection): TsFlags | undefined {
262 | if (ref === undefined || ref.flags === undefined) {
263 | return undefined;
264 | }
265 | const isDeprecated = getIsDeprecated(ref);
266 | const { isExternal, isOptional, isPrivate, isProtected, isPublic, isRest, isStatic } = ref.flags;
267 | return {
268 | isDeprecated,
269 | isExternal,
270 | isOptional,
271 | isPrivate,
272 | isProtected,
273 | isPublic,
274 | isRest,
275 | isStatic,
276 | };
277 | }
278 |
279 | function getIsDeprecated(ref: Reflection) {
280 | const deprecatedTagValue = getCommentTagValue(ref.comment, "deprecated");
281 | const deprecatedModifier = ref.comment?.hasModifier("@deprecated");
282 | return deprecatedModifier || deprecatedTagValue !== undefined;
283 | }
284 |
285 | /** Returns true if value does not match all patterns. */
286 | function isNotExcluded(patterns: Array, value?: string) {
287 | return value === undefined || patterns.every(p => value.match(p) == null);
288 | }
289 |
290 | /** Sorts static members (`flags.isStatic`) before non-static members. */
291 | function sortStaticFirst({ flags: aFlags = {} }: T, { flags: bFlags = {} }: T) {
292 | if (aFlags.isStatic && bFlags.isStatic) {
293 | return 0;
294 | } else if (aFlags.isStatic) {
295 | return -1;
296 | } else if (bFlags.isStatic) {
297 | return 1;
298 | }
299 | return 0;
300 | }
301 |
--------------------------------------------------------------------------------