Socket
Socket
Sign inDemoInstall

@markdoc/markdoc

Package Overview
Dependencies
Maintainers
3
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@markdoc/markdoc - npm Package Compare versions

Comparing version 0.1.13 to 0.2.0

6

dist/index.d.ts
import Ast from './src/ast';
import __EXPERIMENTAL__format from './src/formatter';
import format from './src/formatter';
import functions from './src/functions';

@@ -33,3 +33,3 @@ import * as nodes from './src/schema';

};
export { nodes, tags, functions, globalAttributes, transforms, renderers, Ast, Tag, Tokenizer, parseTags, transformer, validator, truthy, __EXPERIMENTAL__format, };
export { nodes, tags, functions, globalAttributes, transforms, renderers, Ast, Tag, Tokenizer, parseTags, transformer, validator, truthy, format, };
export default class Markdoc {

@@ -124,3 +124,3 @@ static nodes: typeof nodes;

static truthy: typeof truthy;
static __EXPERIMENTAL__format: typeof __EXPERIMENTAL__format;
static format: typeof format;
config: Readonly<Partial<{

@@ -127,0 +127,0 @@ nodes: Partial<Record<import("./src/types").NodeType, import("./src/types").Schema<Readonly<Partial<any>>, string>>>;

@@ -1,7 +0,18 @@

// src/renderers/react/shared.ts
// src/tag.ts
var Tag = class {
constructor(name = "div", attributes = {}, children = []) {
this.$$mdtype = "Tag";
this.name = name;
this.attributes = attributes;
this.children = children;
}
};
Tag.isTag = (tag) => {
return "$$mdtype" in tag && tag.$$mdtype === "Tag";
};
// src/renderers/react/react.ts
function tagName(name, components) {
return typeof name !== "string" ? "Fragment" : name[0] !== name[0].toUpperCase() ? name : components instanceof Function ? components(name) : components[name];
return typeof name !== "string" ? name : name[0] !== name[0].toUpperCase() ? name : components instanceof Function ? components(name) : components[name];
}
// src/renderers/react/react.ts
function dynamic(node, React, { components = {} } = {}) {

@@ -25,3 +36,3 @@ function deepRender(value) {

return React.createElement(React.Fragment, null, ...node2.map(render));
if (node2 === null || typeof node2 !== "object")
if (node2 === null || typeof node2 !== "object" || !Tag.isTag(node2))
return node2;

@@ -28,0 +39,0 @@ const {

@@ -0,3 +1,3 @@

import { RenderableTreeNodes } from '../../types';
import type { createElement, Fragment, ReactNode } from 'react';
import type { RenderableTreeNodes } from '../../types';
declare type ReactShape = Readonly<{

@@ -4,0 +4,0 @@ createElement: typeof createElement;

@@ -1,3 +0,3 @@

import type { RenderableTreeNodes } from '../../types';
import { RenderableTreeNodes } from '../../types';
export default function reactStatic(node: RenderableTreeNodes): string;
//# sourceMappingURL=static.d.ts.map
import type { RenderableTreeNode } from './types';
export default class Tag<N extends string = string, A extends Record<string, any> = Record<string, any>> {
readonly $$mdtype: "Tag";
static isTag: (tag: any) => tag is Tag<string, Record<string, any>>;
name: N;

@@ -5,0 +6,0 @@ attributes: A;

@@ -1,2 +0,2 @@

import type { Schema } from '../types';
import { Schema } from '../types';
export declare function truthy(value: any): boolean;

@@ -3,0 +3,0 @@ export declare const tagIf: Schema;

@@ -54,3 +54,3 @@ import type Func from './ast/function';

export declare type Primitive = null | boolean | number | string;
export declare type RenderableTreeNode = Tag | string | null;
export declare type RenderableTreeNode = Tag | Scalar;
export declare type RenderableTreeNodes = RenderableTreeNode | RenderableTreeNode[];

@@ -57,0 +57,0 @@ export declare type Scalar = Primitive | Scalar[] | {

@@ -5,3 +5,3 @@ import type Token from 'markdown-it/lib/token';

export declare const IDENTIFIER_REGEX: RegExp;
export declare function isIdentifier(s: string): boolean;
export declare function isIdentifier(s: any): s is string;
export declare function isPromise(a: any): a is Promise<any>;

@@ -8,0 +8,0 @@ export declare function findTagEnd(content: string, start?: number): number | null;

import Ast from './src/ast';
import __EXPERIMENTAL__format from './src/formatter';
import format from './src/formatter';
import functions from './src/functions';

@@ -137,3 +137,3 @@ import parser from './src/parser';

truthy,
__EXPERIMENTAL__format,
format,
};

@@ -159,3 +159,3 @@

static truthy = truthy;
static __EXPERIMENTAL__format = __EXPERIMENTAL__format;
static format = format;

@@ -162,0 +162,0 @@ config;

{
"name": "@markdoc/markdoc",
"author": "Ryan Paul",
"version": "0.1.13",
"version": "0.2.0",
"description": "A text markup language for documentation",

@@ -35,6 +35,13 @@ "main": "dist/index.js",

},
"optionalDependencies": {
"@types/markdown-it": "12.2.3"
},
"peerDependencies": {
"@types/markdown-it": "*",
"@types/react": "*"
"@types/react": "*",
"react": "*"
},
"peerDependenciesMeta": {
"@types/react": {"optional": true},
"react": {"optional": true}
},
"devDependencies": {

@@ -41,0 +48,0 @@ "@types/jasmine": "^3.10.2",

@@ -57,10 +57,4 @@ <h1 align="center">

When using TypeScript, install Markdoc with:
This is the minimal `tsconfig.json` required to use Markdoc in your TypeScript project:
```sh
npm install @markdoc/markdoc @types/react @types/markdown-it
```
Additionally, this is the minimal `tsconfig.json` required to use Markdoc in your TypeScript project:
```json

@@ -76,2 +70,10 @@ {

### React
If you are using React, install Markdoc with:
```sh
npm install @markdoc/markdoc react @types/react
```
## Contributing

@@ -78,0 +80,0 @@

@@ -136,6 +136,37 @@ import { diff } from 'jest-diff';

`;
check(source, source);
stable(source);
stable(source);
});
it('escape markdown content', () => {
const source = `
\\* Asterisk
**/docs/\\***
~~**a \\_sentence\\_ with \\_underscores**~~
- Item with [brackets]
\`\`\`
\\*\\_[\\[]
\`\`\`
{% table %}
- **[Link](https://example.com?q=()**
- **[Link](https://example.com?q=\\()**
- ![Image](https://example.com?q=()
- ![Image](https://example.com?q=\\()
{% /table %}
paragraph 1
&nbsp;
paragraph 2
`;
stable(source);
});
it('complex attributes', () => {

@@ -168,3 +199,3 @@ const source = `{% if $gates["<string_key>"].test["@var"] id="id with space" class="class with space" /%}`;

{% $user.name %}
{% key x=$user.name new=$flag /%}
{% key x=$user.name y=$flag z=$array[5] /%}
`;

@@ -180,3 +211,3 @@ const expected = `

{% key x=$user.name new=$flag /%}
{% key x=$user.name y=$flag z=$array[5] /%}
`;

@@ -257,3 +288,3 @@

`;
check(source, source);
stable(source);

@@ -269,3 +300,3 @@ const inlineParent = `### {% image src="/src" alt="A very long alt text to test if the tag wraps or not" /%}

`;
check(source, source, { maxTagOpeningWidth: Infinity });
stable(source, { maxTagOpeningWidth: Infinity });
});

@@ -477,3 +508,3 @@

check(source, source);
stable(source);
});

@@ -613,3 +644,3 @@

check(source, source);
stable(source);
});

@@ -632,2 +663,34 @@

});
it('complex lists', () => {
const source = `
* **One {% colspan=1 %}**
* **Two {% colspan=2 %}**
* **Three {% colspan=3 %}**
`;
const expected = `
- **One**{% colspan=1 %}
- **Two**{% colspan=2 %}
- **Three**{% colspan=3 %}
`;
check(source, expected);
stable(expected);
});
it('nested fences', () => {
const source = `
${'`'.repeat(4)}
${'`'.repeat(3)}
Fence within a fence
${'`'.repeat(3)}
${'`'.repeat(4)}
`;
stable(source);
});
});

@@ -32,2 +32,6 @@ import Ast from './ast';

function* formatInline(g: Generator<string>) {
yield [...g].join('').trim();
}
function* formatTableRow(items: Array<string>) {

@@ -97,3 +101,4 @@ yield `| ${items.join(' | ')} |`;

if (i === 0) return p;
if (isIdentifier(String(p))) return '.' + p;
if (isIdentifier(p)) return '.' + p;
if (typeof p === 'number') return `[${p}]`;
return `["${p}"]`;

@@ -122,2 +127,10 @@ })

function* escapeMarkdownCharacters(s: string, characters: RegExp) {
yield s
.replace(characters, '\\$&')
// TODO keep &nbsp; as entity in the AST?
// Non-breaking space (0xA0)
.replace(new RegExp('\xa0', 'g'), '&nbsp;');
}
function* formatNode(n: Node, o: Options = {}) {

@@ -163,3 +176,5 @@ const no = { ...o, parent: n };

yield '(';
yield* formatValue(n.attributes.src, no);
yield* typeof n.attributes.src === 'string'
? escapeMarkdownCharacters(n.attributes.src, /[()]/)
: formatValue(n.attributes.src, no);
if (n.attributes.title) {

@@ -176,3 +191,5 @@ yield SPACE + `"${n.attributes.title}"`;

yield '(';
yield* formatValue(n.attributes.href, no);
yield* typeof n.attributes.href === 'string'
? escapeMarkdownCharacters(n.attributes.href, /[()]/g)
: formatValue(n.attributes.href, no);
if (n.attributes.title) {

@@ -185,5 +202,8 @@ yield SPACE + `"${n.attributes.title}"`;

case 'text': {
if (Ast.isAst(n.attributes.content)) yield OPEN + SPACE;
yield* formatValue(n.attributes.content, no);
if (Ast.isAst(n.attributes.content)) yield SPACE + CLOSE;
const { content } = n.attributes;
if (Ast.isAst(content)) yield OPEN + SPACE;
yield* typeof content === 'string'
? escapeMarkdownCharacters(content, /[_*~]/g)
: formatValue(content, no);
if (Ast.isAst(content)) yield SPACE + CLOSE;
break;

@@ -208,3 +228,12 @@ }

yield indent;
yield '```';
const innerFence = n.attributes.content.match(/`{3,}/g) || [];
const innerFenceLength = innerFence
.map((s: string) => s.length)
.reduce(max, 0);
const boundary = '`'.repeat(innerFenceLength ? innerFenceLength + 1 : 3);
yield boundary;
if (n.attributes.language) yield n.attributes.language;

@@ -217,3 +246,3 @@ if (n.annotations.length) yield SPACE;

yield n.attributes.content.split(NL).join(NL + indent); // yield* formatChildren(n, no);
yield '```';
yield boundary;
yield NL;

@@ -274,3 +303,3 @@ break;

yield '**';
yield* formatChildren(n, no);
yield* formatInline(formatChildren(n, no));
yield '**';

@@ -281,3 +310,3 @@ break;

yield '_';
yield* formatChildren(n, no);
yield* formatInline(formatChildren(n, no));
yield '_';

@@ -288,3 +317,3 @@ break;

yield '`';
yield* formatValue(n.attributes.content, no);
yield* formatInline(formatValue(n.attributes.content, no));
yield '`';

@@ -295,3 +324,3 @@ break;

yield '~~';
yield* formatChildren(n, no);
yield* formatInline(formatChildren(n, no));
yield '~~';

@@ -298,0 +327,0 @@ break;

import render from './html';
import { RenderableTreeNode } from '../types';
import Tag from '../tag';

@@ -9,3 +10,3 @@ function tag(

) {
return { name, attributes, children };
return new Tag(name, attributes, children);
}

@@ -15,3 +16,3 @@

it('rendering a tag', function () {
const example = render(tag('h1', null, ['test'])).trim();
const example = render(tag('h1', undefined, ['test'])).trim();
expect(example).toEqual('<h1>test</h1>');

@@ -21,3 +22,3 @@ });

it('rendering string child nodes', function () {
const example = tag('h1', null, ['test ', '1']);
const example = tag('h1', undefined, ['test ', '1']);
expect(render(example)).toEqual('<h1>test 1</h1>');

@@ -27,3 +28,3 @@ });

it('rendering nested tags', function () {
const example = tag('div', null, [tag('p', null, ['test'])]);
const example = tag('div', undefined, [tag('p', undefined, ['test'])]);

@@ -34,3 +35,6 @@ expect(render(example)).toEqual('<div><p>test</p></div>');

it('rendering parallel tags', function () {
const example = [tag('p', null, ['foo']), tag('p', null, ['bar'])];
const example = [
tag('p', undefined, ['foo']),
tag('p', undefined, ['bar']),
];

@@ -41,3 +45,3 @@ expect(render(example)).toEqual('<p>foo</p><p>bar</p>');

it('rendering a tag with an invalid child', function () {
const example = tag('div', null, ['test', { foo: 'bar' }]);
const example = tag('div', undefined, ['test', { foo: 'bar' }]);

@@ -44,0 +48,0 @@ expect(render(example)).toEqual('<div>test</div>');

import MarkdownIt from 'markdown-it';
import Tag from '../tag';
import type { RenderableTreeNodes } from '../types';

@@ -26,7 +27,7 @@ const { escapeHtml } = MarkdownIt().utils;

if (typeof node === 'string' || typeof node === 'number')
return escapeHtml(node);
return escapeHtml(String(node));
if (Array.isArray(node)) return node.map(render).join('');
if (node === null || typeof node !== 'object') return '';
if (node === null || typeof node !== 'object' || !Tag.isTag(node)) return '';

@@ -33,0 +34,0 @@ const { name, attributes, children = [] } = node;

import dynamic from './react';
import renderStatic from './static';
import Tag from '../../tag';

@@ -55,3 +56,3 @@ const React = {

const components = { Foo: 'bar' };
const example = { name: 'Foo', children: ['test'] };
const example = new Tag('Foo', undefined, ['test']);
const output = dynamic(example, React, { components });

@@ -77,7 +78,3 @@ expect(output).toDeepEqualSubset({

it('with a class attribute', function () {
const example = {
name: 'h1',
attributes: { class: 'foo bar' },
children: ['test'],
};
const example = new Tag('h1', { class: 'foo bar' }, ['test']);

@@ -106,7 +103,3 @@ const output = dynamic(example, React);

it('rendering a fenced code block', function () {
const example = {
name: 'pre',
attributes: { class: 'code code-ruby' },
children: ['test'],
};
const example = new Tag('pre', { class: 'code code-ruby' }, ['test']);

@@ -170,3 +163,3 @@ const output = dynamic(example, React);

const components = { Foo: 'bar' };
const example = { name: 'Foo', children: ['test'] };
const example = new Tag('Foo', undefined, ['test']);
const code = renderStatic(example);

@@ -173,0 +166,0 @@ const output = eval(code)({ components });

@@ -1,5 +0,6 @@

import { tagName } from './shared';
import type { createElement, Fragment, ReactNode } from 'react';
import type { RenderableTreeNodes, Scalar } from '../../types';
import Tag from '../../tag';
import { RenderableTreeNodes, Scalar } from '../../types';
import type { createElement, ComponentType, Fragment, ReactNode } from 'react';
type ReactShape = Readonly<{

@@ -10,2 +11,17 @@ createElement: typeof createElement;

type Component = ComponentType<unknown>;
function tagName(
name: string,
components: Record<string, Component> | ((string: string) => Component)
): string | Component {
return typeof name !== 'string'
? name // This can be an object, e.g. when React.forwardRef is used
: name[0] !== name[0].toUpperCase()
? name
: components instanceof Function
? components(name)
: components[name];
}
export default function dynamic(

@@ -34,3 +50,4 @@ node: RenderableTreeNodes,

if (node === null || typeof node !== 'object') return node;
if (node === null || typeof node !== 'object' || !Tag.isTag(node))
return node;

@@ -37,0 +54,0 @@ const {

@@ -1,4 +0,21 @@

import { tagName } from './shared';
import type { RenderableTreeNode, RenderableTreeNodes } from '../../types';
import Tag from '../../tag';
import { RenderableTreeNode, RenderableTreeNodes } from '../../types';
import type { ComponentType } from 'react';
type Component = ComponentType<unknown>;
function tagName(
name: string,
components: Record<string, Component> | ((string: string) => Component)
): string | Component {
return typeof name !== 'string'
? 'Fragment'
: name[0] !== name[0].toUpperCase()
? name
: components instanceof Function
? components(name)
: components[name];
}
function renderArray(children: RenderableTreeNode[]): string {

@@ -29,3 +46,4 @@ return children.map(render).join(', ');

if (node === null || typeof node !== 'object') return JSON.stringify(node);
if (node === null || typeof node !== 'object' || !Tag.isTag(node))
return JSON.stringify(node);

@@ -32,0 +50,0 @@ const {

@@ -9,2 +9,6 @@ import type { RenderableTreeNode } from './types';

static isTag = (tag: any): tag is Tag => {
return '$$mdtype' in tag && tag.$$mdtype === 'Tag';
};
name: N;

@@ -11,0 +15,0 @@ attributes: A;

import { isPromise } from '../utils';
import type { Node, RenderableTreeNode, Schema, Value } from '../types';
import {
MaybePromise,
Node,
RenderableTreeNode,
RenderableTreeNodes,
Schema,
Value,
} from '../types';

@@ -37,3 +44,5 @@ type Condition = { condition: Value; children: Node[] };

if (truthy(condition)) {
const nodes = children.flatMap((child) => child.transform(config));
const nodes = children.flatMap<MaybePromise<RenderableTreeNodes>>(
(child) => child.transform(config)
);
if (nodes.some(isPromise)) {

@@ -40,0 +49,0 @@ return Promise.all(nodes).then((nodes) => nodes.flat());

@@ -5,3 +5,11 @@ import Tag from './tag';

import { isPromise } from './utils';
import type { Config, Node, NodeType, Schema, Transformer } from './types';
import type {
Config,
MaybePromise,
Node,
NodeType,
RenderableTreeNodes,
Schema,
Transformer,
} from './types';

@@ -47,3 +55,5 @@ type AttributesSchema = Schema['attributes'];

children(node: Node, config: Config = {}) {
const children = node.children.flatMap((child) => this.node(child, config));
const children = node.children.flatMap<MaybePromise<RenderableTreeNodes>>(
(child) => this.node(child, config)
);
if (children.some(isPromise)) {

@@ -50,0 +60,0 @@ return Promise.all(children);

@@ -96,3 +96,3 @@ import type Func from './ast/function';

export type RenderableTreeNode = Tag | string | null;
export type RenderableTreeNode = Tag | Scalar;
export type RenderableTreeNodes = RenderableTreeNode | RenderableTreeNode[];

@@ -99,0 +99,0 @@

@@ -18,4 +18,4 @@ import { parse, SyntaxError } from './grammar/tag';

export function isIdentifier(s: string): boolean {
return IDENTIFIER_REGEX.test(s);
export function isIdentifier(s: any): s is string {
return typeof s === 'string' && IDENTIFIER_REGEX.test(s);
}

@@ -22,0 +22,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc