Skip to content

Commit

Permalink
feat(YfmFile): support directive syntax (#503)
Browse files Browse the repository at this point in the history
  • Loading branch information
d3m1d0v authored Nov 28, 2024
1 parent 5186fab commit 0340dce
Show file tree
Hide file tree
Showing 7 changed files with 325 additions and 43 deletions.
5 changes: 4 additions & 1 deletion demo/defaults/md-plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ export function getPlugins({
directiveSyntax: directiveSyntax?.mdPluginValueFor('yfmCut'),
}),
deflist,
yfmFile({bundle: false}),
yfmFile({
bundle: false,
directiveSyntax: directiveSyntax?.mdPluginValueFor('yfmFile'),
}),
(md) => md.use(imsize, {enableInlineStyling: true}),
meta,
monospace,
Expand Down
7 changes: 1 addition & 6 deletions src/extensions/yfm/YfmCut/YfmCut.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import dd from 'ts-dedent';

import {parseDOM} from '../../../../tests/parse-dom';
import {createMarkupChecker} from '../../../../tests/sameMarkup';
import {DirectiveContext} from '../../../../tests/utils';
import {ExtensionsManager} from '../../../core';
import {DirectiveSyntaxContext, type DirectiveSyntaxOption} from '../../../utils/directive';
import {BaseNode, BaseSchemaSpecs} from '../../base/specs';
import {
BlockquoteSpecs,
Expand All @@ -18,11 +18,6 @@ import {

import {CutAttr, CutNode, YfmCutSpecs} from './YfmCutSpecs';

class DirectiveContext extends DirectiveSyntaxContext {
setOption(option: DirectiveSyntaxOption | undefined) {
this.option = option;
}
}
const directiveContext = new DirectiveContext(undefined);

function buildDeps() {
Expand Down
235 changes: 213 additions & 22 deletions src/extensions/yfm/YfmFile/YfmFile.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import {builders} from 'prosemirror-test-builder';
import dd from 'ts-dedent';

import type {DirectiveSyntaxValue} from '../../../';
import {parseDOM} from '../../../../tests/parse-dom';
import {createMarkupChecker} from '../../../../tests/sameMarkup';
import {DirectiveContext} from '../../../../tests/utils';
import {ExtensionsManager} from '../../../core';
import {BaseNode, BaseSchemaSpecs} from '../../base/specs';

import {YfmFileSpecs, yfmFileNodeName} from './YfmFileSpecs';
import {YfmFileAttr, YfmFileSpecs, yfmFileNodeName} from './YfmFileSpecs';

const {
schema,
markupParser: parser,
serializer,
} = new ExtensionsManager({
extensions: (builder) => builder.use(BaseSchemaSpecs, {}).use(YfmFileSpecs),
}).buildDeps();
function buildDeps(directiveSyntax: DirectiveSyntaxValue | undefined) {
return new ExtensionsManager({
options: {mdOpts: {preset: 'zero'}},
extensions: (builder) => {
builder.context.set('directiveSyntax', new DirectiveContext(directiveSyntax));
builder.use(BaseSchemaSpecs, {}).use(YfmFileSpecs);
},
}).buildDeps();
}

function buildCheckers(directiveSyntax: DirectiveSyntaxValue) {
const {markupParser, serializer} = buildDeps(directiveSyntax);
return createMarkupChecker({parser: markupParser, serializer});
}

const {schema, markupParser: parser, serializer} = buildDeps(undefined);

const {same} = createMarkupChecker({parser, serializer});

Expand All @@ -24,13 +36,14 @@ const {doc, p, file} = builders<'doc' | 'p' | 'file'>(schema, {
});

const defaultAttrs = {
href: 'path/to/readme',
download: 'readme.md',
hreflang: null,
referrerpolicy: null,
rel: null,
target: null,
type: null,
[YfmFileAttr.Link]: 'path/to/readme',
[YfmFileAttr.Name]: 'readme.md',
[YfmFileAttr.Lang]: null,
[YfmFileAttr.ReferrerPolicy]: null,
[YfmFileAttr.Rel]: null,
[YfmFileAttr.Target]: null,
[YfmFileAttr.Type]: null,
[YfmFileAttr.Markup]: '{% file ',
};

describe('YFM File extension', () => {
Expand Down Expand Up @@ -65,13 +78,14 @@ describe('YFM File extension', () => {
doc(
p(
file({
href: 'path/to/readme',
download: 'readme.md',
hreflang: 'ru',
referrerpolicy: 'origin',
rel: 'help',
target: '_top',
type: 'text/markdown',
[YfmFileAttr.Link]: 'path/to/readme',
[YfmFileAttr.Name]: 'readme.md',
[YfmFileAttr.Lang]: 'ru',
[YfmFileAttr.ReferrerPolicy]: 'origin',
[YfmFileAttr.Rel]: 'help',
[YfmFileAttr.Target]: '_top',
[YfmFileAttr.Type]: 'text/markdown',
[YfmFileAttr.Markup]: '{% file ',
}),
),
),
Expand All @@ -93,4 +107,181 @@ describe('YFM File extension', () => {
),
);
});

describe('directiveSyntax', () => {
const MARKUP = {
CurlySyntax: dd`
{% file src="path/to/readme.md" name="README.md" lang="ru" referrerpolicy="origin" rel="help" target="_top" type="text/markdown" %}
`,

DirectiveSyntax: dd`
:file[README.md](path/to/readme.md){referrerpolicy="origin" rel="help" target="_top" type="text/markdown" hreflang="ru"}
`,
};

const ATTRS = {
[YfmFileAttr.Name]: 'README.md',
[YfmFileAttr.Link]: 'path/to/readme.md',
[YfmFileAttr.ReferrerPolicy]: 'origin',
[YfmFileAttr.Rel]: 'help',
[YfmFileAttr.Target]: '_top',
[YfmFileAttr.Type]: 'text/markdown',
[YfmFileAttr.Lang]: 'ru',
};

const PM_DOC = {
CurlyFile: doc(
p(
file({
...ATTRS,
[YfmFileAttr.Markup]: '{% file ',
}),
),
),
UnknownFile: doc(p(file({...ATTRS}))),
DirectiveFile: doc(
p(
file({
...ATTRS,
[YfmFileAttr.Markup]: ':file',
}),
),
),
};

describe('directiveSyntax:disabled', () => {
it('should parse curly syntax', () => {
const {parse} = buildCheckers('disabled');
parse(MARKUP.CurlySyntax, PM_DOC.CurlyFile, {json: true});
});

it('should not parse directive syntax', () => {
const {parse} = buildCheckers('disabled');
parse(MARKUP.DirectiveSyntax, doc(p(MARKUP.DirectiveSyntax)), {json: true});
});

it('should preserve curly syntax', () => {
const {serialize} = buildCheckers('disabled');
serialize(PM_DOC.CurlyFile, MARKUP.CurlySyntax);
});

it('should serialize block with unknown markup to curly syntax', () => {
const {serialize} = buildCheckers('disabled');
serialize(PM_DOC.UnknownFile, MARKUP.CurlySyntax);
});

it('should preserve directive syntax', () => {
const {serialize} = buildCheckers('disabled');
serialize(PM_DOC.DirectiveFile, MARKUP.DirectiveSyntax);
});
});

describe('directiveSyntax:enabled', () => {
it('should parse curly syntax', () => {
const {parse} = buildCheckers('enabled');
parse(MARKUP.CurlySyntax, PM_DOC.CurlyFile, {json: true});
});

it('should parse directive syntax', () => {
const {parse} = buildCheckers('enabled');
parse(MARKUP.DirectiveSyntax, PM_DOC.DirectiveFile, {json: true});
});

it('should preserve curly syntax', () => {
const {serialize} = buildCheckers('enabled');
serialize(PM_DOC.CurlyFile, MARKUP.CurlySyntax);
});

it('should serialize block with unknown markup to curly syntax', () => {
const {serialize} = buildCheckers('enabled');
serialize(PM_DOC.UnknownFile, MARKUP.CurlySyntax);
});

it('should preserve directive syntax', () => {
const {serialize} = buildCheckers('enabled');
serialize(PM_DOC.DirectiveFile, MARKUP.DirectiveSyntax);
});
});

describe('directiveSyntax:preserve', () => {
it('should parse curly syntax', () => {
const {parse} = buildCheckers('preserve');
parse(MARKUP.CurlySyntax, PM_DOC.CurlyFile, {json: true});
});

it('should parse directive syntax', () => {
const {parse} = buildCheckers('preserve');
parse(MARKUP.DirectiveSyntax, PM_DOC.DirectiveFile, {json: true});
});

it('should preserve curly syntax', () => {
const {serialize} = buildCheckers('preserve');
serialize(PM_DOC.CurlyFile, MARKUP.CurlySyntax);
});

it('should serialize block with unknown markup to directive syntax', () => {
const {serialize} = buildCheckers('preserve');
serialize(PM_DOC.UnknownFile, MARKUP.DirectiveSyntax);
});

it('should preserve directive syntax', () => {
const {serialize} = buildCheckers('preserve');
serialize(PM_DOC.DirectiveFile, MARKUP.DirectiveSyntax);
});
});

describe('directiveSyntax:overwrite', () => {
it('should parse curly syntax', () => {
const {parse} = buildCheckers('overwrite');
parse(MARKUP.CurlySyntax, PM_DOC.CurlyFile, {json: true});
});

it('should parse directive syntax', () => {
const {parse} = buildCheckers('overwrite');
parse(MARKUP.DirectiveSyntax, PM_DOC.DirectiveFile, {json: true});
});

it('should overwrite curly to directive syntax', () => {
const {serialize} = buildCheckers('overwrite');
serialize(PM_DOC.CurlyFile, MARKUP.DirectiveSyntax);
});

it('should serialize block with unknown markup to directive syntax', () => {
const {serialize} = buildCheckers('overwrite');
serialize(PM_DOC.UnknownFile, MARKUP.DirectiveSyntax);
});

it('should preserve directive syntax', () => {
const {serialize} = buildCheckers('overwrite');
serialize(PM_DOC.DirectiveFile, MARKUP.DirectiveSyntax);
});
});

describe('directiveSyntax:only', () => {
it('should not parse curly syntax', () => {
const {parse} = buildCheckers('only');
parse(MARKUP.CurlySyntax, doc(p(MARKUP.CurlySyntax)), {json: true});
});

it('should parse directive syntax', () => {
const {parse} = buildCheckers('only');
parse(MARKUP.DirectiveSyntax, PM_DOC.DirectiveFile, {json: true});
});

it('should overwrite curly to directive syntax', () => {
const {serialize} = buildCheckers('only');
serialize(PM_DOC.CurlyFile, MARKUP.DirectiveSyntax);
});

it('should serialize block with unknown markup to directive syntax', () => {
const {serialize} = buildCheckers('only');
serialize(PM_DOC.UnknownFile, MARKUP.DirectiveSyntax);
});

it('should preserve directive syntax', () => {
const {serialize} = buildCheckers('only');
serialize(PM_DOC.DirectiveFile, MARKUP.DirectiveSyntax);
});
});
});
});
24 changes: 23 additions & 1 deletion src/extensions/yfm/YfmFile/YfmFileSpecs/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,32 @@ import {
FILE_REQUIRED_ATTRS,
FILE_TOKEN,
FILE_TO_LINK_ATTRS_MAP,
FileHtmlAttr,
FileSpecialAttr,
} from '@diplodoc/file-extension';
import type {AttributeSpec} from 'prosemirror-model';

export const yfmFileNodeName = FILE_TOKEN;

export const YfmFileAttr = {
Markup: 'data-markup',
Name: FileHtmlAttr.Download,
Link: FileHtmlAttr.Href,
ReferrerPolicy: FileHtmlAttr.ReferrerPolicy,
Rel: FileHtmlAttr.Rel,
Target: FileHtmlAttr.Target,
Type: FileHtmlAttr.Type,
Lang: FileHtmlAttr.HrefLang,
} as const;

export const YFM_FILE_DIRECTIVE_ATTRS: readonly string[] = [
YfmFileAttr.ReferrerPolicy,
YfmFileAttr.Rel,
YfmFileAttr.Target,
YfmFileAttr.Type,
YfmFileAttr.Lang,
];

export const KNOWN_ATTRS: readonly string[] = FILE_KNOWN_ATTRS.map((attrName) => {
if (attrName in FILE_TO_LINK_ATTRS_MAP)
return FILE_TO_LINK_ATTRS_MAP[attrName as FileSpecialAttr];
Expand All @@ -21,7 +41,9 @@ export const REQUIRED_ATTRS = FILE_REQUIRED_ATTRS.map((attrName) => {
return attrName;
});

export const fileNodeAttrsSpec: Record<string, AttributeSpec> = {};
export const fileNodeAttrsSpec: Record<string, AttributeSpec> = {
[YfmFileAttr.Markup]: {default: null},
};
for (const attrName of KNOWN_ATTRS) {
const attrSpec: AttributeSpec = (fileNodeAttrsSpec[attrName] = {});
if (!REQUIRED_ATTRS.includes(attrName)) {
Expand Down
Loading

0 comments on commit 0340dce

Please sign in to comment.