typedoc-plugin-markdown
typedoc-plugin-markdown copied to clipboard
Request: Single file from multiple file
jsdoc2md optionally produce single markdown page from multiple modules/files by concatenating them. Also let developer use a handlebars template for that single page.
This is super useful to generate a README.md file for npm and github from code automatically.
Below is a simplified example handlebars template I use for that purpose:
<!-- DO NOT EDIT README.md (It will be overridden by README.hbs) -->
# {{changeCase "title" (package "name")}}
<!-- Badges from shield.io -->
> {{package "description"}}
>
> {{#each (package "shields")}}[![{{this.name}}][{{this.name}}]][{{this.name}}-url]{{/each}}
{{#each (package "shields")}}
[{{this.name}}]: {{this.image}}
[{{this.name}}-url]: {{this.url}}
{{/each}}
<!-- START doctoc -->
<!-- END doctoc -->
Some manual content I want to include in my README.
Below main part is added by jsdoc2md
# API
{{>main~}}
All those can be achieved programmatically with typedoc-plugin-markdown too, but it is helpful to have this feature built-in, because npm & github README.md file generation may be used by lots of people.
EDIT:
Why this is useful?
Sometimes, especially for small modules, it's enough to have a single README.md file for the whole documentation, and there is no need to have extra web sites/github pages or wiki pages. For example:
# Install
# Synopsis
# Usage
# API
jsdoc2md generated API comes here.
Today, I published concat-md for creating single file from multiple markdown files. Although it is not typedoc-plugin-markdown specific, I developed it primarily to use with typedoc-plugin-markdown.
It does not simply concat files, it adds necessary titles, and changes level of existing titles.
$ typedoc --plugin typedoc-plugin-markdown --mode file --out docs
$ npx concat-md --decrease-title-levels --dir-name-as-title docs > README.md
Above command;
- Concatenates markdown files,
- Adds directory names as title,
- Decreases level of existing titles,
- Outputs to README.md.
- Converts links to generated file.
This is an initial release, but I think it is OK to be used in projects.
@tgreyuk, I will appreciate if you can give it a try and provide some feedback.
@ozum this looks pretty cool. sorry been a bit busy on some other stuff but I will test this out properly when i have a bit of time.
@ozum I'm loving my new autogenerated API documentation - thanks!
@tgreyuk, thanks.
Also thanks to suggestion of @Vinnl, concat-md now converts links too.
@tgreyuk, I also appreciate if you mention md-concat in README with an example if you find it useful for typedoc-plugin-markdown.
Any news about this? I really dislike writing READMEs, mainly because I JsDoc my code already and I hate to copy the stuff there to the README, and having to manually update the README is a pain for me.
I would like to just have a template README, write some specific texts, and everything else would be filled automagically, so I could just focus on what I like, that is coding.
Another current issue I am having, is the same as the https://github.com/tgreyuk/typedoc-plugin-markdown/issues/109. typedoc-plugin-markdown seems good, but the generated .md isn't really good to be used in a README as it takes too much space. My package is a React component, and I just want a table with the properties of the component. It's just ~10 props, but for me is already a mental pain having to copy/paste and having to maintain it through the time, specially because I have ~8 npm packages (not popular at all) and I plan to have many more, as I really like to do it.
I thought of creating from scratch basically another TypeDoc to do what I want using AST, but that would take more time that I am willing to spend on it. Seems to be a better option to use this package and do some transformations to achieve what I want. For now, I will write a quick .js to convert the generated .md to a table.
I already have a template generator for my projects that also creates a basic README. I just need that automatic README content filler so I can really automate my production line.
@SrBrahma Thanks .. makes sense - I have been meaning to have a look at tabulating interface props for a while. Currently refactoring a few things which will make it easier to incorporate this. Will report back in due course.
Thanks for the quick answer! I will as soon as I do my quick toTable.js post it here as it may help you or at least give you some ideas. I think I will just use regex to get the info I want from the .md.
Done!! Probably it's just a dirty workaround that won't work on other cases that includes other JSDoc tags. In the entry file, differently from the original code typings, I made a prop required and other without the default tag, to handle and test different cases.
Entry example/test file, generated by typedoc-plugin-markdown
# Interface: ShadowI
## Properties
### startColor
• **startColor**: `string`
The color of the shadow when it's right next to the given content, leaving it.
Accepts alpha channel.
___
### finalColor
• `Optional` **finalColor**: `string`
The color of the shadow at the maximum distance from the content.
**`default`** '#0000', transparent.
___
### distance
• `Optional` **distance**: `number`
How far the shadow will go.
___
### containerViewStyle
• **containerViewStyle**: `StyleProp`<ViewStyle\>
The style of the view that contains the shadow and the children.
**`default`** undefined
___
### radius
• `Optional` **radius**: `number` \| { `default?`: `number` ; `topLeft?`: `number` ; `topRight?`: `number` ; `bottomLeft?`: `number` ; `bottomRight?`: `number` }
The radius of each corner of your child component. Passing a number will apply it to all corners.
If passing an object, undefined corners will have the radius of the `default` property if it's defined, else 0.
If undefined, as it's by default, and if getChildRadius, it will attemp to get the child radius style. Else, 0.
**`default`** undefined
___
### getChildRadius
• `Optional` **getChildRadius**: `boolean`
If it should try to get the radius from the child if `radius` prop is undefined. It will get the values for each
corner, like `borderTopLeftRadius`, and also `borderRadius`. If a specific corner isn't defined, `borderRadius` value is used.
If `borderRadius` isn't defined or < 0, 0 will be used.
**`default`** true
___
### sides
• `Optional` **sides**: `Side`[]
The sides of your content that will have the shadows drawn. Doesn't include corners.
**`default`** ['left', 'right', 'top', 'bottom']
___
### corners
• `Optional` **corners**: `Corner`[]
The corners that will have the shadows drawn.
**`default`** ['topLeft', 'topRight', 'bottomLeft', 'bottomRight']
___
### offset
• `Optional` **offset**: [x: string \| number, y: string \| nu</details>mber]
Moves the shadow. Negative x moves it to the left, negative y moves it up.
Accepts 'x%' values, in relation to the child's size.
Read paintInside property description for related configuration.
**`default`** [0, 0]
___
### paintInside
• `Optional` **paintInside**: `boolean`
If the shadow should be applied inside the external shadows, below the child.
You may want this as true when using offset or if your child have some transparency.
**`default`** false
Converter TS file
/* eslint-disable @typescript-eslint/prefer-regexp-exec */
import fs from 'fs';
const fileContent = fs.readFileSync('docs/interfaces/shadowi.md').toString();
// Match everything between ### and (___ or end of string)
const matches = [...fileContent.matchAll(/###.+?(?=(___)|$)/sg)]; // s flag is . matches newline
if (!matches.length) {
throw new Error('No matches!');
}
// let i = 0;
const properties: {
name?: string;
type?: string;
/** undefined means it's required. */
defaultVal?: string;
description?: string;
}[] = [];
for (const match of matches) {
// console.log(`Match #${i++}`);
const content = match[0];
// console.log(content);
const name = content.match(/### (.+)?/)?.[1];
const hasOptionalTag: boolean = !!content.match(/• `Optional`/);
const defaultVal = content.match(/\*\*`default`\*\* (.+)/)?.[1] || (hasOptionalTag ? 'undefined' : undefined);
const type = content.match(/• .+?: (.+)/)?.[1];
// It's after the type and before the (default or or EOS)
const description = content.match(/•.+?\n([\s\S]+?)(?=(\*\*`default`\*\*)|$)/)?.[1].trim();
// console.log(name, defaultVal, hasOptionalTag, type);
properties.push({
name, defaultVal, type, description
});
// console.log();
}
let resultString =
`| Property | Type | Default | Description
| --- | --- | --- | ---\n`;
function ensureWrappingBackticks(value: string): string {
return (value[0] === '`' ? value : `\`${value}\``);
}
// The type already includes wrapping backticks
for (const prop of properties) {
// Remove existing backticks (that sometimes are weirdly placed) and wrap it all with backticks
const type = ensureWrappingBackticks((prop.type ?? '?').replace(/`/g, ''));
const defaultVal = prop.defaultVal ? `${ensureWrappingBackticks(prop.defaultVal)}` : '**required**';
// Replace new lines with <br/> tag
const description = prop.description ? prop.description.replace(/\n/g, '<br/>')
: '-'; // '-' instead of a blank/undefined description
// Ensuring backtick because my [x: string | number, y: string | number] wasn't backticked by some reason.
resultString += `| **${prop.name}** | ${type} | ${defaultVal} | ${description}\n`;
}
fs.writeFileSync('result.md', resultString);
The result!!
| Property | Type | Default | Description |
|---|---|---|---|
| startColor | string |
required | The color of the shadow when it's right next to the given content, leaving it. Accepts alpha channel. |
| finalColor | string |
'#0000', transparent. |
The color of the shadow at the maximum distance from the content. |
| distance | number |
undefined |
How far the shadow will go. |
| containerViewStyle | StyleProp<ViewStyle\> |
undefined |
The style of the view that contains the shadow and the children. |
| radius | number | { default?: number ; topLeft?: number ; topRight?: number ; bottomLeft?: number ; bottomRight?: number } |
undefined |
The radius of each corner of your child component. Passing a number will apply it to all corners. If passing an object, undefined corners will have the radius of the default property if it's defined, else 0.If undefined, as it's by default, and if getChildRadius, it will attemp to get the child radius style. Else, 0. |
| getChildRadius | boolean |
true |
If it should try to get the radius from the child if radius prop is undefined. It will get the values for eachcorner, like borderTopLeftRadius, and also borderRadius. If a specific corner isn't defined, borderRadius value is used.If borderRadius isn't defined or < 0, 0 will be used. |
| sides | Side[] |
['left', 'right', 'top', 'bottom'] |
The sides of your content that will have the shadows drawn. Doesn't include corners. |
| corners | Corner[] |
['topLeft', 'topRight', 'bottomLeft', 'bottomRight'] |
The corners that will have the shadows drawn. |
| offset | [x: string | number, y: string | number] |
[0, 0] |
Moves the shadow. Negative x moves it to the left, negative y moves it up. Accepts 'x%' values, in relation to the child's size. Read paintInside property description for related configuration. |
| paintInside | boolean |
false |
If the shadow should be applied inside the external shadows, below the child. You may want this as true when using offset or if your child have some transparency. |
<!-- DO NOT EDIT README.md (It will be overridden by README.hbs) --> # {{changeCase "title" (package "name")}} <!-- Badges from shield.io --> > {{package "description"}} > > {{#each (package "shields")}}[![{{this.name}}][{{this.name}}]][{{this.name}}-url]{{/each}} {{#each (package "shields")}} [{{this.name}}]: {{this.image}} [{{this.name}}-url]: {{this.url}} {{/each}} <!-- START doctoc --> <!-- END doctoc --> Some manual content I want to include in my README. Below main part is added by jsdoc2md # API {{>main~}}
@tgreyuk using that handlebars system, I made https://github.com/SrBrahma/react-native-shadow-2/tree/wip/resources/README. My handlebars only populates the {{shadowProperties}} table for now. Maybe we could make a typedoc2readme or typedoc2md? Writing for eg {{table interface@shadowi}} would get docs/interfaces/shadowi.md content, transform it into a table, and populate the corresponding field in .hbs. I don't know about jsdoc2md, maybe we could use its same syntax and maybe some of its parsers or do it as we like.
I am interested in doing it. In fact, my proof of concept is already done in that link, it would just require some further generalizations for a basic and useful release version. I don't think it would be possible a fully automatic README as it would probably get really messy, but using handlebars and typedocs automations would simplify and make the maintenance and the development of projects way easier.
Do you have any interest on it?