documentalist
Advanced tools
Comparing version 0.0.5 to 0.0.6
47
cli.js
@@ -15,30 +15,41 @@ #!/usr/bin/env node | ||
const glob = require("glob"); | ||
const { Documentalist } = require("./dist/"); | ||
const { Documentalist, KssPlugin, MarkdownPlugin, TypescriptPlugin } = require("./dist/"); | ||
const argv = yargs | ||
.alias("v", "version") | ||
.version(require("./package.json").version) | ||
.usage("$0 [options] <files>") | ||
.option("md", { | ||
default: true, | ||
desc: "use MarkdownPlugin for .md files", | ||
type: "boolean", | ||
}) | ||
.option("ts", { | ||
default: true, | ||
desc: "use TypescriptPlugin for .tsx? files", | ||
type: "boolean", | ||
}) | ||
.option("css", { | ||
desc: "use KssPlugin for .(css|less|scss) files", | ||
type: "boolean", | ||
}) | ||
.demandCommand(1, "Requires at least one file") | ||
// TODO: how to specify plugin on CLI? | ||
// .option("--use [pattern:plugin]", "Use a plugin to process files matching the pattern") | ||
.argv; | ||
// ensure `use` option is always an array | ||
const plugins = [].concat(argv.use) | ||
.filter((use) => use !== undefined) | ||
.map((/** @type {string} */ use) => { | ||
const [pattern, plugin] = use.split(":"); | ||
if (plugin === undefined) { | ||
console.error("invalid --use option '%s'", use); | ||
process.exit(1); | ||
} | ||
return { pattern, plugin }; | ||
}); | ||
let docs = Documentalist.create(); | ||
const docs = Documentalist.create(); | ||
plugins.forEach(({ pattern, plugin }) => { | ||
docs.use(new RegExp(pattern), plugin); | ||
}); | ||
if (argv.md) { | ||
docs = docs.use(".md", new MarkdownPlugin()); | ||
} | ||
if (argv.ts) { | ||
docs = docs.use(/\.tsx?$/, new TypescriptPlugin({ | ||
excludePaths: ["__tests__"], | ||
})); | ||
} | ||
if (argv.css) { | ||
docs = docs.use(/\.(css|less|s[ac]ss)$/, new KssPlugin()); | ||
} | ||
docs.documentGlobs(...argv._) | ||
.then((data) => JSON.stringify(data, null, 2)) | ||
.then(console.log, console.error); |
@@ -71,5 +71,6 @@ /** | ||
const { contents } = API.renderBlock(FILE, ["interface"]); | ||
expect(contents).toHaveLength(3); | ||
// reserved @tag is emitted as separate string cuz it's still split by regex | ||
expect(contents[1]).toEqual("<p>@interface IButtonProps</p>\n"); | ||
// only one string (reserved @word does not get split to its own entry) | ||
expect(contents).toHaveLength(1); | ||
// @tag value comes out exactly as written in source, on its own line | ||
expect(contents[0].split("\n")).toContain("@interface IButtonProps"); | ||
}); | ||
@@ -76,0 +77,0 @@ }); |
@@ -18,2 +18,3 @@ /** | ||
const index_1 = require("../index"); | ||
const markdown_1 = require("../plugins/markdown"); | ||
const TEST_MARKDOWN = `--- | ||
@@ -42,4 +43,6 @@ key: value | ||
describe("Plugins", () => { | ||
const dm = index_1.Documentalist.create() | ||
.use(".md", new markdown_1.MarkdownPlugin()); | ||
it("can document Markdown files", () => __awaiter(this, void 0, void 0, function* () { | ||
const docs = yield index_1.Documentalist.create().documentFiles(TEST_FILES); | ||
const docs = yield dm.documentFiles(TEST_FILES); | ||
const page = docs.pages["test"]; | ||
@@ -46,0 +49,0 @@ expect(page).toBeDefined(); |
/// <reference types="marked" /> | ||
import { IBlock, ICompiler } from "./plugins/plugin"; | ||
export interface ICompilerOptions { | ||
/** Options for markdown rendering. See https://github.com/chjj/marked#options-1. */ | ||
markdown?: MarkedOptions; | ||
/** | ||
* Reserved @tags that should be preserved in the contents string. | ||
* A common use case is allowing specific code constructs, like `@Decorator` names. | ||
* Do not include the `@` prefix in the strings. | ||
*/ | ||
reservedTags?: string[]; | ||
} | ||
export declare class Compiler implements ICompiler { | ||
private markedOptions; | ||
constructor(markedOptions: MarkedOptions); | ||
private options; | ||
constructor(options: ICompilerOptions); | ||
objectify<T>(array: T[], getKey: (item: T) => string): { | ||
[key: string]: T; | ||
}; | ||
renderBlock: (blockContent: string, reservedTagWords?: string[]) => IBlock; | ||
renderBlock: (blockContent: string, reservedTagWords?: string[] | undefined) => IBlock; | ||
renderMarkdown: (markdown: string) => string; | ||
@@ -16,3 +26,3 @@ /** | ||
*/ | ||
private renderContents(content, reservedTagWords); | ||
private renderContents(content, reservedTagWords?); | ||
/** | ||
@@ -28,3 +38,3 @@ * Extracts optional YAML frontmatter metadata block from the beginning of a | ||
*/ | ||
private parseTags(content, reservedWords); | ||
private parseTags(content, reservedWords?); | ||
} |
@@ -15,16 +15,6 @@ "use strict"; | ||
const TAG_SPLIT_REGEX = /^(@\S+(?:\s+[^\n]+)?)$/gm; | ||
/** | ||
* Ignored `@tag` names. Some languages already support `@tags`, so to separate | ||
* Documentalist tags, we use these default reserved words to avoid conflicts. | ||
* | ||
* Plugins may define their own reserved words when calling the `renderBlock` | ||
* method. | ||
*/ | ||
const RESERVED_WORDS = [ | ||
"import", | ||
]; | ||
class Compiler { | ||
constructor(markedOptions) { | ||
this.markedOptions = markedOptions; | ||
this.renderBlock = (blockContent, reservedTagWords = RESERVED_WORDS) => { | ||
constructor(options) { | ||
this.options = options; | ||
this.renderBlock = (blockContent, reservedTagWords = this.options.reservedTags) => { | ||
const { contentsRaw, metadata } = this.extractMetadata(blockContent); | ||
@@ -34,3 +24,3 @@ const contents = this.renderContents(contentsRaw, reservedTagWords); | ||
}; | ||
this.renderMarkdown = (markdown) => marked(markdown, this.markedOptions); | ||
this.renderMarkdown = (markdown) => marked(markdown, this.options.markdown); | ||
} | ||
@@ -71,20 +61,30 @@ objectify(array, getKey) { | ||
*/ | ||
parseTags(content, reservedWords) { | ||
return content.split(TAG_SPLIT_REGEX).map((str) => { | ||
parseTags(content, reservedWords = []) { | ||
// using reduce so we can squash consecutive strings (<= 1 entry per iteration) | ||
return content.split(TAG_SPLIT_REGEX).reduce((arr, str) => { | ||
const match = TAG_REGEX.exec(str); | ||
if (match === null || reservedWords.indexOf(match[1]) >= 0) { | ||
return str; | ||
if (typeof arr[arr.length - 1] === "string") { | ||
// merge consecutive strings to avoid breaking up code blocks | ||
arr[arr.length - 1] += str; | ||
} | ||
else { | ||
arr.push(str); | ||
} | ||
} | ||
const tag = match[1]; | ||
const value = match[2]; | ||
if (/#+/.test(tag)) { | ||
// NOTE: not enough information to populate `route` field yet | ||
return { tag: "heading", value, level: tag.length }; | ||
} | ||
else { | ||
return { tag, value }; | ||
const tag = match[1]; | ||
const value = match[2]; | ||
if (/#+/.test(tag)) { | ||
// NOTE: not enough information to populate `route` field yet | ||
arr.push({ tag: "heading", value, level: tag.length }); | ||
} | ||
else { | ||
arr.push({ tag, value }); | ||
} | ||
} | ||
}); | ||
return arr; | ||
}, []); | ||
} | ||
} | ||
exports.Compiler = Compiler; |
@@ -1,3 +0,3 @@ | ||
/// <reference types="marked" /> | ||
import { IFile, IMarkdownPluginData, IPlugin, ITypescriptPluginData } from "./plugins"; | ||
import { ICompilerOptions } from "./compiler"; | ||
import { IFile, IPlugin } from "./plugins"; | ||
export interface IApi<T> { | ||
@@ -47,6 +47,6 @@ /** | ||
export declare class Documentalist<T> implements IApi<T> { | ||
private options; | ||
private plugins; | ||
private markedOptions; | ||
static create(markedOptions?: MarkedOptions): IApi<IMarkdownPluginData & ITypescriptPluginData>; | ||
constructor(plugins?: Array<IPluginEntry<T>>, markedOptions?: MarkedOptions); | ||
static create(options?: ICompilerOptions): IApi<{}>; | ||
constructor(options?: ICompilerOptions, plugins?: Array<IPluginEntry<T>>); | ||
use<P>(pattern: RegExp | string, plugin: IPlugin<P>): IApi<T & P>; | ||
@@ -53,0 +53,0 @@ clearPlugins(): IApi<{}>; |
@@ -21,12 +21,9 @@ /** | ||
const compiler_1 = require("./compiler"); | ||
const plugins_1 = require("./plugins"); | ||
class Documentalist { | ||
constructor(plugins = [], markedOptions = {}) { | ||
constructor(options = {}, plugins = []) { | ||
this.options = options; | ||
this.plugins = plugins; | ||
this.markedOptions = markedOptions; | ||
} | ||
static create(markedOptions) { | ||
return new Documentalist([], markedOptions) | ||
.use(/\.md$/, new plugins_1.MarkdownPlugin()) | ||
.use(/\.tsx?$/, new plugins_1.TypescriptPlugin()); | ||
static create(options) { | ||
return new Documentalist(options, []); | ||
} | ||
@@ -38,6 +35,6 @@ use(pattern, plugin) { | ||
const newPlugins = [...this.plugins, { pattern, plugin }]; | ||
return new Documentalist(newPlugins, this.markedOptions); | ||
return new Documentalist(this.options, newPlugins); | ||
} | ||
clearPlugins() { | ||
return new Documentalist([], this.markedOptions); | ||
return new Documentalist(this.options, []); | ||
} | ||
@@ -52,3 +49,3 @@ documentGlobs(...filesGlobs) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const compiler = new compiler_1.Compiler(this.markedOptions); | ||
const compiler = new compiler_1.Compiler(this.options); | ||
const documentation = {}; | ||
@@ -55,0 +52,0 @@ for (const { pattern, plugin } of this.plugins) { |
@@ -7,3 +7,3 @@ /** | ||
*/ | ||
import { IJsDocTags } from "ts-quick-docs"; | ||
import { IDocumentationOptions, IJsDocTags } from "ts-quick-docs"; | ||
import { IBlock, ICompiler, IFile, IPlugin } from "./plugin"; | ||
@@ -30,2 +30,23 @@ export interface ITsDocEntry { | ||
export declare class TypescriptPlugin implements IPlugin<ITypescriptPluginData> { | ||
/** | ||
* Options to `ts-quick-docs`, mostly for customizing which symbols appear in the output. | ||
*/ | ||
private options; | ||
/** | ||
* Compiler options for Typescript program used to "read" your typings. | ||
* (This is distinct from whatever options you need to build your typings.) | ||
* If omitted, the default compiler options are used. | ||
*/ | ||
private compilerOptions; | ||
constructor( | ||
/** | ||
* Options to `ts-quick-docs`, mostly for customizing which symbols appear in the output. | ||
*/ | ||
options?: IDocumentationOptions, | ||
/** | ||
* Compiler options for Typescript program used to "read" your typings. | ||
* (This is distinct from whatever options you need to build your typings.) | ||
* If omitted, the default compiler options are used. | ||
*/ | ||
compilerOptions?: any); | ||
compile(files: IFile[], {renderBlock, objectify}: ICompiler): { | ||
@@ -32,0 +53,0 @@ ts: { |
@@ -11,4 +11,20 @@ /** | ||
class TypescriptPlugin { | ||
constructor( | ||
/** | ||
* Options to `ts-quick-docs`, mostly for customizing which symbols appear in the output. | ||
*/ | ||
options = {}, | ||
/** | ||
* Compiler options for Typescript program used to "read" your typings. | ||
* (This is distinct from whatever options you need to build your typings.) | ||
* If omitted, the default compiler options are used. | ||
*/ | ||
// HACK: using any to avoid duplicate typings issue with ts.CompilerOptions | ||
compilerOptions = {}) { | ||
this.options = options; | ||
this.compilerOptions = compilerOptions; | ||
} | ||
compile(files, { renderBlock, objectify }) { | ||
const entries = ts_quick_docs_1.default.fromFiles(files.map((f) => f.path), {}).map((entry) => (Object.assign({}, entry, { documentation: renderBlock(entry.documentation), properties: entry.properties.map((prop) => (Object.assign({}, prop, { documentation: renderBlock(prop.documentation) }))) }))); | ||
const entries = ts_quick_docs_1.default.fromFiles(files.map((f) => f.path), this.compilerOptions, this.options) | ||
.map((entry) => (Object.assign({}, entry, { documentation: renderBlock(entry.documentation), properties: entry.properties.map((prop) => (Object.assign({}, prop, { documentation: renderBlock(prop.documentation) }))) }))); | ||
const ts = objectify(entries, (e) => e.name); | ||
@@ -15,0 +31,0 @@ return { ts }; |
{ | ||
"name": "documentalist", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"description": "documentation for the rest of us", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -5,2 +5,5 @@ # Documentalist | ||
[![npm](https://img.shields.io/npm/v/documentalist.svg)](https://www.npmjs.com/package/documentalist) | ||
[![CircleCI](https://circleci.com/gh/palantir/documentalist.svg?style=shield&circle-token=1dbd27fe833e64bafb3e8de8ee111a2aee9bb79d)](https://circleci.com/gh/palantir/documentalist) | ||
## Documentalism 101 | ||
@@ -16,10 +19,13 @@ | ||
- `.ts`, `.tsx` files for JSDoc comments on interfaces in TypeScript source code | ||
- `.css`, `.scss` files for comments on CSS selectors | ||
- `.css`, `.less`, `.scss` files for comments on CSS selectors | ||
With the JavaScript API, nothing comes for free. All plugins must be registered with `.use()`. | ||
```js | ||
const { Documentalist } = require("documentalist"); | ||
const { Documentalist, MarkdownPlugin } = require("documentalist"); | ||
const { writeFileSync } = require("fs"); | ||
const dm = new Documentalist(); | ||
const docs = dm.documentGlobs("src/**/*"); | ||
const docs = new Documentalist() | ||
.use(".md", new MarkdownPlugin()) | ||
.documentGlobs("src/**/*"); | ||
@@ -29,2 +35,9 @@ writeFileSync("docs.json", JSON.stringify(docs, null, 2)); | ||
With the CLI, the Markdown and Typescript plugins are enabled by default. | ||
The CSS plugin can be enabled with `--css`. | ||
```sh | ||
documentalist "src/**/*" --css --no-ts > docs.json | ||
``` | ||
# License | ||
@@ -31,0 +44,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
54826
1252
44