diff --git a/package-lock.json b/package-lock.json index 1738739..8c3cf6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "@diplodoc/cut-extension", "version": "0.3.2", "license": "MIT", + "dependencies": { + "@diplodoc/directive": "^0.1.0" + }, "devDependencies": { "@diplodoc/lint": "^1.2.0", "@diplodoc/tsconfig": "^1.0.2", @@ -589,6 +592,14 @@ "postcss-selector-parser": "^6.0.13" } }, + "node_modules/@diplodoc/directive": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@diplodoc/directive/-/directive-0.1.0.tgz", + "integrity": "sha512-wQ4RoCoCjbWD9r2YdVPlgTW8+a0JNmsh4OmRLq4EA5PJ1SLjUKqSP9HkHb4LjRINLFtac6SsbXtQ8Y/zUiYazA==", + "dependencies": { + "markdown-it-directive": "2.0.2" + } + }, "node_modules/@diplodoc/lint": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@diplodoc/lint/-/lint-1.2.0.tgz", @@ -1431,14 +1442,12 @@ "node_modules/@types/linkify-it": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", - "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", - "dev": true + "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==" }, "node_modules/@types/markdown-it": { "version": "13.0.9", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.9.tgz", "integrity": "sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw==", - "dev": true, "license": "MIT", "dependencies": { "@types/linkify-it": "^3", @@ -1448,8 +1457,7 @@ "node_modules/@types/mdurl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", - "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", - "dev": true + "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==" }, "node_modules/@types/minimist": { "version": "1.2.5", @@ -1822,8 +1830,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/aria-query": { "version": "5.1.3", @@ -5248,7 +5255,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", - "dev": true, "dependencies": { "uc.micro": "^1.0.1" } @@ -5791,7 +5797,6 @@ "version": "13.0.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.2.tgz", "integrity": "sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==", - "dev": true, "dependencies": { "argparse": "^2.0.1", "entities": "~3.0.1", @@ -5803,11 +5808,19 @@ "markdown-it": "bin/markdown-it.js" } }, + "node_modules/markdown-it-directive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/markdown-it-directive/-/markdown-it-directive-2.0.2.tgz", + "integrity": "sha512-UceeGZwl9bjnuEJWW/4UnJIFfcJ3NYmEXSTrffq6N/77E5wpbAYyopsuH/H4R8Ccuwge9E7otyuvHP5bULkccg==", + "peerDependencies": { + "@types/markdown-it": "^12.0.0 || ^13.0.0", + "markdown-it": "^12.0.0 || ^13.0.0" + } + }, "node_modules/markdown-it/node_modules/entities": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true, "engines": { "node": ">=0.12" }, @@ -5836,8 +5849,7 @@ "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" }, "node_modules/memorystream": { "version": "0.3.1", @@ -8615,8 +8627,7 @@ "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" }, "node_modules/unbox-primitive": { "version": "1.0.2", diff --git a/package.json b/package.json index 47548c3..5aae867 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,9 @@ "pre-commit": "lint update && lint-staged", "prepare": "husky" }, + "dependencies": { + "@diplodoc/directive": "^0.1.0" + }, "devDependencies": { "@diplodoc/lint": "^1.2.0", "@diplodoc/tsconfig": "^1.0.2", diff --git a/src/plugin/directive.ts b/src/plugin/directive.ts new file mode 100644 index 0000000..ef83e81 --- /dev/null +++ b/src/plugin/directive.ts @@ -0,0 +1,41 @@ +import type MarkdownIt from 'markdown-it'; + +import {directiveParser, registerContainerDirective} from '@diplodoc/directive'; + +import {ClassNames, ENV_FLAG_NAME, TokenType} from './const'; + +export const cutDirective: MarkdownIt.PluginSimple = (md) => { + md.use(directiveParser()); + + registerContainerDirective(md, { + name: 'cut', + match(_params, state) { + state.env ??= {}; + state.env[ENV_FLAG_NAME] = true; + + return true; + }, + container: { + tag: 'details', + token: TokenType.Cut, + attrs: { + class: ClassNames.Cut, + }, + }, + inlineContent: { + required: false, + tag: 'summary', + token: TokenType.Title, + attrs: { + class: ClassNames.Title, + }, + }, + content: { + tag: 'div', + token: TokenType.Content, + attrs: { + class: ClassNames.Content, + }, + }, + }); +}; diff --git a/src/plugin/plugin.ts b/src/plugin/plugin.ts index a5c1980..a18d860 100644 --- a/src/plugin/plugin.ts +++ b/src/plugin/plugin.ts @@ -34,6 +34,7 @@ export const cutPlugin: MarkdownIt.PluginSimple = (md) => { const newOpenToken = new state.Token(TokenType.CutOpen, 'details', 1); newOpenToken.attrSet('class', ClassNames.Cut); newOpenToken.map = tokens[i].map; + newOpenToken.markup = '{%'; attrsParser.apply(newOpenToken); diff --git a/src/plugin/transform.ts b/src/plugin/transform.ts index 6bf3b68..fb54743 100644 --- a/src/plugin/transform.ts +++ b/src/plugin/transform.ts @@ -3,6 +3,7 @@ import type MarkdownIt from 'markdown-it'; import {cutPlugin} from './plugin'; import {type Runtime, copyRuntime, dynrequire, hidden} from './utils'; import {ENV_FLAG_NAME} from './const'; +import {cutDirective} from './directive'; export type TransformOptions = { runtime?: @@ -12,10 +13,21 @@ export type TransformOptions = { style: string; }; bundle?: boolean; + /** + * Enables directive syntax of yfm-cut. + * + * - 'disabled' – directive syntax is disabled. + * - 'enabled' – directive syntax is enabled; old syntax is also enabled. + * - 'only' – enabled only directive syntax; old syntax is disabled. + * + * @default 'disabled' + */ + directiveSyntax?: 'disabled' | 'enabled' | 'only'; }; -type NormalizedPluginOptions = Omit & { +type NormalizedPluginOptions = Omit & { runtime: Runtime; + directiveSyntax: NonNullable; }; const registerTransform = ( @@ -24,11 +36,17 @@ const registerTransform = ( runtime, bundle, output, - }: Pick & { + directiveSyntax, + }: Pick & { output: string; }, ) => { - md.use(cutPlugin); + if (directiveSyntax === 'disabled' || directiveSyntax === 'enabled') { + md.use(cutPlugin); + } + if (directiveSyntax === 'enabled' || directiveSyntax === 'only') { + md.use(cutDirective); + } md.core.ruler.push('yfm_cut_after', ({env}) => { hidden(env, 'bundled', new Set()); @@ -57,6 +75,7 @@ export function transform(options: Partial = {}) { throw new TypeError('Option `runtime` should be record when `bundle` is enabled.'); } + const directiveSyntax = options.directiveSyntax || 'disabled'; const runtime: Runtime = typeof options.runtime === 'string' ? {script: options.runtime, style: options.runtime} @@ -70,6 +89,7 @@ export function transform(options: Partial = {}) { {output = '.'} = {}, ) { registerTransform(md, { + directiveSyntax, runtime, bundle, output, @@ -81,6 +101,7 @@ export function transform(options: Partial = {}) { const MdIt = dynrequire('markdown-it'); const md = new MdIt().use((md: MarkdownIt) => { registerTransform(md, { + directiveSyntax, runtime, bundle, output: destRoot, diff --git a/tests/package-lock.json b/tests/package-lock.json index 0e6b646..6a1829f 100644 --- a/tests/package-lock.json +++ b/tests/package-lock.json @@ -9,6 +9,7 @@ "devDependencies": { "@diplodoc/transform": "^4.28.2", "@types/jest": "^29.5.12", + "@types/markdown-it": "^13.0.9", "esbuild-jest": "^0.5.0", "highlight.js": "^11.10.0", "jest": "^29.7.0", @@ -1608,6 +1609,28 @@ "parse5": "^7.0.0" } }, + "node_modules/@types/linkify-it": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", + "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "13.0.9", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.9.tgz", + "integrity": "sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw==", + "dev": true, + "dependencies": { + "@types/linkify-it": "^3", + "@types/mdurl": "^1" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", + "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", + "dev": true + }, "node_modules/@types/node": { "version": "22.5.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.1.tgz", diff --git a/tests/package.json b/tests/package.json index a339390..f2f405c 100644 --- a/tests/package.json +++ b/tests/package.json @@ -2,11 +2,13 @@ "name": "@diplodoc/tests", "private": true, "scripts": { - "test": "jest" + "test": "jest", + "test:watch": "jest --watch" }, "devDependencies": { "@diplodoc/transform": "^4.28.2", "@types/jest": "^29.5.12", + "@types/markdown-it": "^13.0.9", "esbuild-jest": "^0.5.0", "highlight.js": "^11.10.0", "jest": "^29.7.0", diff --git a/tests/src/__snapshots__/plugin.test.ts.snap b/tests/src/__snapshots__/plugin.test.ts.snap index f4ad13f..6a0761f 100644 --- a/tests/src/__snapshots__/plugin.test.ts.snap +++ b/tests/src/__snapshots__/plugin.test.ts.snap @@ -1,6 +1,69 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Cut extension - plugin should close all tags correctly and insert two p tags 1`] = ` +exports[`Cut extension - plugin Options: directiveSyntax should render both cuts 1`] = ` +
+ + Old cut + +
+

+ Old cut content +

+
+
+
+ + Directive cut + +
+

+ Directive cut content +

+
+
+`; + +exports[`Cut extension - plugin Options: directiveSyntax should render only new (directive) cut 1`] = ` +

+

+

+ Old cut content +

+

+

+
+ + Directive cut + +
+

+ Directive cut content +

+
+
+`; + +exports[`Cut extension - plugin Options: directiveSyntax should render only old (curly) cut 1`] = ` +
+ + Old cut + +
+

+ Old cut content +

+
+
+

+ :::cut[Directive cut] +
+ Directive cut content +
+ ::: +

+`; + +exports[`Cut extension - plugin curly syntax should close all tags correctly and insert two p tags 1`] = `
  • @@ -20,7 +83,7 @@ exports[`Cut extension - plugin should close all tags correctly and insert two p
`; -exports[`Cut extension - plugin should close all tags correctly when given a bullet-list with several items 1`] = ` +exports[`Cut extension - plugin curly syntax should close all tags correctly when given a bullet-list with several items 1`] = `
  • @@ -61,7 +124,272 @@ exports[`Cut extension - plugin should close all tags correctly when given a bul
`; -exports[`Cut extension - plugin should render nested cuts 1`] = ` +exports[`Cut extension - plugin curly syntax should parse markup with cut to token stream 1`] = ` +[ + Token { + "attrs": [ + [ + "class", + "yfm-cut", + ], + ], + "block": false, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": [ + 2, + 3, + ], + "markup": "{%", + "meta": null, + "nesting": 1, + "tag": "details", + "type": "yfm_cut_open", + }, + Token { + "attrs": [ + [ + "class", + "yfm-cut-title", + ], + ], + "block": false, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "", + "meta": null, + "nesting": 1, + "tag": "summary", + "type": "yfm_cut_title_open", + }, + Token { + "attrs": null, + "block": false, + "children": [ + Token { + "attrs": null, + "block": false, + "children": null, + "content": "Cut ", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "", + "meta": null, + "nesting": 0, + "tag": "", + "type": "text", + }, + Token { + "attrs": null, + "block": false, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "_", + "meta": null, + "nesting": 1, + "tag": "em", + "type": "em_open", + }, + Token { + "attrs": null, + "block": false, + "children": null, + "content": "title", + "hidden": false, + "info": "", + "level": 1, + "map": null, + "markup": "", + "meta": null, + "nesting": 0, + "tag": "", + "type": "text", + }, + Token { + "attrs": null, + "block": false, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "_", + "meta": null, + "nesting": -1, + "tag": "em", + "type": "em_close", + }, + ], + "content": "Cut _title_", + "hidden": false, + "info": "", + "level": 0, + "map": [ + 0, + 1, + ], + "markup": "", + "meta": null, + "nesting": 0, + "tag": "", + "type": "inline", + }, + Token { + "attrs": null, + "block": false, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "", + "meta": null, + "nesting": -1, + "tag": "summary", + "type": "yfm_cut_title_close", + }, + Token { + "attrs": [ + [ + "class", + "yfm-cut-content", + ], + ], + "block": false, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": [ + 3, + 4, + ], + "markup": "", + "meta": null, + "nesting": 1, + "tag": "div", + "type": "yfm_cut_content_open", + }, + Token { + "attrs": null, + "block": true, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": [ + 4, + 5, + ], + "markup": "", + "meta": null, + "nesting": 1, + "tag": "p", + "type": "paragraph_open", + }, + Token { + "attrs": null, + "block": true, + "children": [ + Token { + "attrs": null, + "block": false, + "children": null, + "content": "Cut content", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "", + "meta": null, + "nesting": 0, + "tag": "", + "type": "text", + }, + ], + "content": "Cut content", + "hidden": false, + "info": "", + "level": 1, + "map": [ + 4, + 5, + ], + "markup": "", + "meta": null, + "nesting": 0, + "tag": "", + "type": "inline", + }, + Token { + "attrs": null, + "block": true, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "", + "meta": null, + "nesting": -1, + "tag": "p", + "type": "paragraph_close", + }, + Token { + "attrs": null, + "block": false, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "", + "meta": null, + "nesting": -1, + "tag": "div", + "type": "yfm_cut_content_close", + }, + Token { + "attrs": null, + "block": false, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": [ + 6, + 7, + ], + "markup": "", + "meta": null, + "nesting": -1, + "tag": "details", + "type": "yfm_cut_close", + }, +] +`; + +exports[`Cut extension - plugin curly syntax should render nested cuts 1`] = `
Outer title @@ -84,7 +412,7 @@ exports[`Cut extension - plugin should render nested cuts 1`] = `
`; -exports[`Cut extension - plugin should render siblings cuts 1`] = ` +exports[`Cut extension - plugin curly syntax should render siblings cuts 1`] = `
Cut title 1 @@ -107,7 +435,7 @@ exports[`Cut extension - plugin should render siblings cuts 1`] = `
`; -exports[`Cut extension - plugin should render simple cut 1`] = ` +exports[`Cut extension - plugin curly syntax should render simple cut 1`] = `
Cut title @@ -120,7 +448,7 @@ exports[`Cut extension - plugin should render simple cut 1`] = `
`; -exports[`Cut extension - plugin should render simple cut with code in it 1`] = ` +exports[`Cut extension - plugin curly syntax should render simple cut with code in it 1`] = `
Cut title @@ -135,7 +463,7 @@ exports[`Cut extension - plugin should render simple cut with code in it 1`] = `
`; -exports[`Cut extension - plugin should render title with format 1`] = ` +exports[`Cut extension - plugin curly syntax should render title with format 1`] = `
@@ -149,3 +477,334 @@ exports[`Cut extension - plugin should render title with format 1`] = `
`; + +exports[`Cut extension - plugin directive syntax should not render leaf cut 1`] = ` +

+ ::cut[Title] +

+`; + +exports[`Cut extension - plugin directive syntax should parse markup with cut to token stream 1`] = ` +[ + Token { + "attrs": [ + [ + "class", + "yfm-cut", + ], + ], + "block": true, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": [ + 2, + 5, + ], + "markup": ":::cut", + "meta": null, + "nesting": 1, + "tag": "details", + "type": "yfm_cut_open", + }, + Token { + "attrs": [ + [ + "class", + "yfm-cut-title", + ], + ], + "block": true, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 1, + "map": null, + "markup": "", + "meta": null, + "nesting": 1, + "tag": "summary", + "type": "yfm_cut_title_open", + }, + Token { + "attrs": null, + "block": true, + "children": [ + Token { + "attrs": null, + "block": false, + "children": null, + "content": "Cut ", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "", + "meta": null, + "nesting": 0, + "tag": "", + "type": "text", + }, + Token { + "attrs": null, + "block": false, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "_", + "meta": null, + "nesting": 1, + "tag": "em", + "type": "em_open", + }, + Token { + "attrs": null, + "block": false, + "children": null, + "content": "title", + "hidden": false, + "info": "", + "level": 1, + "map": null, + "markup": "", + "meta": null, + "nesting": 0, + "tag": "", + "type": "text", + }, + Token { + "attrs": null, + "block": false, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "_", + "meta": null, + "nesting": -1, + "tag": "em", + "type": "em_close", + }, + ], + "content": "Cut _title_", + "hidden": false, + "info": "", + "level": 2, + "map": [ + 2, + 3, + ], + "markup": "", + "meta": null, + "nesting": 0, + "tag": "", + "type": "inline", + }, + Token { + "attrs": null, + "block": true, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 1, + "map": null, + "markup": "", + "meta": null, + "nesting": -1, + "tag": "summary", + "type": "yfm_cut_title_close", + }, + Token { + "attrs": [ + [ + "class", + "yfm-cut-content", + ], + ], + "block": true, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 1, + "map": [ + 3, + 4, + ], + "markup": "", + "meta": null, + "nesting": 1, + "tag": "div", + "type": "yfm_cut_content_open", + }, + Token { + "attrs": null, + "block": true, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 2, + "map": [ + 3, + 4, + ], + "markup": "", + "meta": null, + "nesting": 1, + "tag": "p", + "type": "paragraph_open", + }, + Token { + "attrs": null, + "block": true, + "children": [ + Token { + "attrs": null, + "block": false, + "children": null, + "content": "Cut content", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "", + "meta": null, + "nesting": 0, + "tag": "", + "type": "text", + }, + ], + "content": "Cut content", + "hidden": false, + "info": "", + "level": 3, + "map": [ + 3, + 4, + ], + "markup": "", + "meta": null, + "nesting": 0, + "tag": "", + "type": "inline", + }, + Token { + "attrs": null, + "block": true, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 2, + "map": null, + "markup": "", + "meta": null, + "nesting": -1, + "tag": "p", + "type": "paragraph_close", + }, + Token { + "attrs": null, + "block": true, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 1, + "map": null, + "markup": "", + "meta": null, + "nesting": -1, + "tag": "div", + "type": "yfm_cut_content_close", + }, + Token { + "attrs": null, + "block": true, + "children": null, + "content": "", + "hidden": false, + "info": "", + "level": 0, + "map": null, + "markup": "", + "meta": null, + "nesting": -1, + "tag": "details", + "type": "yfm_cut_close", + }, +] +`; + +exports[`Cut extension - plugin directive syntax should render cut with empty title 1`] = ` +
+ + +
+

+ Cut content +

+
+
+`; + +exports[`Cut extension - plugin directive syntax should render cut with inline markup in title 1`] = ` +
+ + + Strong cut title + + +
+

+ Content we want to hide +

+
+
+`; + +exports[`Cut extension - plugin directive syntax should render directive cut 1`] = ` +
+ + Cut title + +
+

+ Cut content +

+
+
+`; + +exports[`Cut extension - plugin directive syntax should render nested cut 1`] = ` +
+ + Outer title + +
+

+ Outer content +

+
+ + Inner title + +
+

+ Inner content +

+
+
+
+
+`; diff --git a/tests/src/plugin.test.ts b/tests/src/plugin.test.ts index 136ea5d..3b8b38b 100644 --- a/tests/src/plugin.test.ts +++ b/tests/src/plugin.test.ts @@ -1,5 +1,6 @@ import transform from '@diplodoc/transform'; -import dedent from 'ts-dedent'; +import dd from 'ts-dedent'; +import MarkdownIt from 'markdown-it'; import * as cutExtension from '../../src/plugin'; @@ -19,170 +20,335 @@ const meta = (text: string, opts?: cutExtension.TransformOptions) => { return result.meta; }; +const parse = (text: string, opts?: cutExtension.TransformOptions) => { + const md = new MarkdownIt().use(cutExtension.transform({bundle: false, ...opts})); + return md.parse(text, {}); +}; + describe('Cut extension - plugin', () => { - it('should render simple cut', () => { - expect( - html(dedent` - {% cut "Cut title" %} + describe('curly syntax', () => { + it('should render simple cut', () => { + expect( + html(dd` + {% cut "Cut title" %} - Cut content + Cut content - {% endcut %} - `), - ).toMatchSnapshot(); - }); + {% endcut %} + `), + ).toMatchSnapshot(); + }); - it('should render simple cut with code in it', () => { - expect( - html(dedent` - {% cut "Cut title" %} + it('should render simple cut with code in it', () => { + expect( + html(dd` + {% cut "Cut title" %} - \`\`\` - Code - \`\`\` + \`\`\` + Code + \`\`\` - {% endcut %} - `), - ).toMatchSnapshot(); - }); + {% endcut %} + `), + ).toMatchSnapshot(); + }); - it('should render siblings cuts', () => { - expect( - html(dedent` - {% cut "Cut title 1" %} + it('should render siblings cuts', () => { + expect( + html(dd` + {% cut "Cut title 1" %} - Cut content 1 + Cut content 1 - {% endcut %} + {% endcut %} - {% cut "Cut title 2" %} + {% cut "Cut title 2" %} - Cut content 2 + Cut content 2 - {% endcut %} - `), - ).toMatchSnapshot(); - }); + {% endcut %} + `), + ).toMatchSnapshot(); + }); - it('should render nested cuts', () => { - expect( - html(dedent` - {% cut "Outer title" %} + it('should render nested cuts', () => { + expect( + html(dd` + {% cut "Outer title" %} - Outer content + Outer content - {% cut "Inner title" %} + {% cut "Inner title" %} - Inner content + Inner content - {% endcut %} + {% endcut %} - {% endcut %} - `), - ).toMatchSnapshot(); - }); + {% endcut %} + `), + ).toMatchSnapshot(); + }); - it('should render title with format', () => { - expect( - html(dedent` - {% cut "**Strong cut title**" %} + it('should render title with format', () => { + expect( + html(dd` + {% cut "**Strong cut title**" %} - Content we want to hide + Content we want to hide - {% endcut %} - `), - ).toMatchSnapshot(); - }); + {% endcut %} + `), + ).toMatchSnapshot(); + }); - it('should close all tags correctly and insert two p tags', () => { - expect( - html(dedent` - * {% cut "Cut 1" %} + it('should close all tags correctly and insert two p tags', () => { + expect( + html(dd` + * {% cut "Cut 1" %} - Some text + Some text - Some text + Some text - {% endcut %} - `), - ).toMatchSnapshot(); - }); + {% endcut %} + `), + ).toMatchSnapshot(); + }); - it('should close all tags correctly when given a bullet-list with several items', () => { - expect( - html(dedent` - * {% cut "Cut 1" %} + it('should close all tags correctly when given a bullet-list with several items', () => { + expect( + html(dd` + * {% cut "Cut 1" %} - Some text + Some text - {% endcut %} + {% endcut %} - * {% cut "Cut 2" %} + * {% cut "Cut 2" %} - Some text + Some text - {% endcut %} + {% endcut %} - * {% cut "Cut 3" %} + * {% cut "Cut 3" %} - Some text + Some text - {% endcut %} - `), - ).toMatchSnapshot(); - }); + {% endcut %} + `), + ).toMatchSnapshot(); + }); - it('should dont add assets to meta if no yfm-cut is found', () => { - expect(meta('paragraph')).toBeUndefined(); - }); + it('should dont add assets to meta if no yfm-cut is found', () => { + expect(meta('paragraph')).toBeUndefined(); + }); - it('should add default assets to meta', () => { - expect( - meta(dedent` - {% cut "Cut title" %} + it('should add default assets to meta', () => { + expect( + meta(dd` + {% cut "Cut title" %} - Cut content + Cut content - {% endcut %} - `), - ).toStrictEqual({ - script: ['_assets/cut-extension.js'], - style: ['_assets/cut-extension.css'], + {% endcut %} + `), + ).toStrictEqual({ + script: ['_assets/cut-extension.js'], + style: ['_assets/cut-extension.css'], + }); + }); + + it('should add custom assets to meta', () => { + expect( + meta( + dd` + {% cut "Cut title" %} + + Cut content + + {% endcut %} + `, + {runtime: 'yfm-cut'}, + ), + ).toStrictEqual({ + script: ['yfm-cut'], + style: ['yfm-cut'], + }); + }); + + it('should add custom assets to meta 2', () => { + expect( + meta( + dd` + {% cut "Cut title" %} + + Cut content + + {% endcut %} + `, + { + runtime: {script: 'yfm-cut.script', style: 'yfm-cut.style'}, + }, + ), + ).toStrictEqual({script: ['yfm-cut.script'], style: ['yfm-cut.style']}); + }); + + it('should parse markup with cut to token stream', () => { + expect( + parse(dd` + + + {% cut "Cut _title_" %} + + Cut content + + {% endcut %} + + + `), + ).toMatchSnapshot(); }); }); - it('should add custom assets to meta', () => { - expect( - meta( - dedent` - {% cut "Cut title" %} + describe('directive syntax', () => { + it('should render directive cut', () => { + expect( + html( + dd` + :::cut[Cut title] + Cut content + ::: + `, + {directiveSyntax: 'only'}, + ), + ).toMatchSnapshot(); + }); + + it('should render nested cut', () => { + expect( + html( + dd` + :::cut[Outer title] + Outer content + + :::cut[Inner title] + Inner content + ::: + ::: + `, + {directiveSyntax: 'only'}, + ), + ).toMatchSnapshot(); + }); - Cut content + it('should render cut with inline markup in title', () => { + expect( + html( + dd` + :::cut[**Strong cut title**] + Content we want to hide + ::: + `, + {directiveSyntax: 'only'}, + ), + ).toMatchSnapshot(); + }); - {% endcut %} - `, - {runtime: 'yfm-cut'}, - ), - ).toStrictEqual({ - script: ['yfm-cut'], - style: ['yfm-cut'], + it('should dont add assets to meta if no yfm-cut is found', () => { + expect(meta('paragraph', {directiveSyntax: 'only'})).toBeUndefined(); + }); + + it('should add assets to meta', () => { + expect( + meta( + dd` + :::cut[Cut title] + Cut content + ::: + `, + {directiveSyntax: 'only'}, + ), + ).toStrictEqual({ + script: ['_assets/cut-extension.js'], + style: ['_assets/cut-extension.css'], + }); + }); + + it('should parse markup with cut to token stream', () => { + expect( + parse( + dd` + + + :::cut[Cut _title_] + Cut content + ::: + + + `, + {directiveSyntax: 'only'}, + ), + ).toMatchSnapshot(); + }); + + it('should render cut with empty title', () => { + expect( + html( + dd` + :::cut + Cut content + :::`, + {directiveSyntax: 'only'}, + ), + ).toMatchSnapshot(); + }); + + it('should not render leaf cut', () => { + expect( + html( + dd` + ::cut[Title] + `, + {directiveSyntax: 'only'}, + ), + ).toMatchSnapshot(); + }); + + it('should not add assets with leaf cut', () => { + expect( + meta( + dd` + ::cut[Title] + `, + {directiveSyntax: 'only'}, + ), + ).toBeUndefined(); }); }); - it('should add custom assets to meta 2', () => { - expect( - meta( - dedent` - {% cut "Cut title" %} + describe('Options: directiveSyntax', () => { + const markup = dd` + {% cut "Old cut" %} - Cut content + Old cut content {% endcut %} - `, - { - runtime: {script: 'yfm-cut.script', style: 'yfm-cut.style'}, - }, - ), - ).toStrictEqual({script: ['yfm-cut.script'], style: ['yfm-cut.style']}); + + :::cut[Directive cut] + Directive cut content + ::: + `; + + it('should render only old (curly) cut', () => { + expect(html(markup, {directiveSyntax: 'disabled'})).toMatchSnapshot(); + }); + + it('should render only new (directive) cut', () => { + expect(html(markup, {directiveSyntax: 'only'})).toMatchSnapshot(); + }); + + it('should render both cuts', () => { + expect(html(markup, {directiveSyntax: 'enabled'})).toMatchSnapshot(); + }); }); }); diff --git a/tests/tsconfig.json b/tests/tsconfig.json new file mode 100644 index 0000000..35c7338 --- /dev/null +++ b/tests/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@diplodoc/tsconfig", + "compilerOptions": { + "target": "es2019", + "declaration": true + }, + "include": ["src"], + "exclude": [] +}