@diplodoc/folding-headings-extension 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,308 @@
1
- export { transform } from './transform';
2
- export { TokenType } from './const';
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/plugin/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ TokenType: () => TokenType,
24
+ transform: () => transform
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/common/const.ts
29
+ var DATA_KEY = "heading-section";
30
+ var SectionAttr = {
31
+ DataId: "data-diplodoc-id",
32
+ DataKey: "data-diplodoc-key"
33
+ };
34
+ var SectionCN = {
35
+ Section: "heading-section",
36
+ Content: "heading-section-content"
37
+ };
38
+
39
+ // src/plugin/const.ts
40
+ var ENV_FLAG_NAME = "has-folding-headings";
41
+ var TokenType = {
42
+ Heading: "heading",
43
+ HeadingOpen: "heading_open",
44
+ HeadingClose: "heading_close",
45
+ Section: "heading_section",
46
+ SectionOpen: "heading_section_open",
47
+ SectionClose: "heading_section_close",
48
+ Content: "heading_section_content",
49
+ ContentOpen: "heading_section_content_open",
50
+ ContentClose: "heading_section_content_close"
51
+ };
52
+
53
+ // src/plugin/headingBlockRule.ts
54
+ var headingBlockRule = (state, startLine, _endLine, silent) => {
55
+ const { isSpace } = state.md.utils;
56
+ let pos = state.bMarks[startLine] + state.tShift[startLine];
57
+ let max = state.eMarks[startLine];
58
+ if (state.sCount[startLine] - state.blkIndent >= 4) {
59
+ return false;
60
+ }
61
+ let ch = state.src.charCodeAt(pos);
62
+ if (ch !== 35 || pos >= max) {
63
+ return false;
64
+ }
65
+ let level = 1;
66
+ ch = state.src.charCodeAt(++pos);
67
+ while (ch === 35 && pos < max && level <= 6) {
68
+ level++;
69
+ ch = state.src.charCodeAt(++pos);
70
+ }
71
+ let folding = false;
72
+ if (ch === 43) {
73
+ folding = true;
74
+ ch = state.src.charCodeAt(++pos);
75
+ }
76
+ if (level > 6 || pos < max && !isSpace(ch)) {
77
+ return false;
78
+ }
79
+ if (silent) {
80
+ return true;
81
+ }
82
+ max = state.skipSpacesBack(max, pos);
83
+ const tmp = state.skipCharsBack(max, 35, pos);
84
+ if (tmp > pos && isSpace(state.src.charCodeAt(tmp - 1))) {
85
+ max = tmp;
86
+ }
87
+ state.line = startLine + 1;
88
+ let token = state.push(TokenType.HeadingOpen, "h" + String(level), 1);
89
+ token.markup = "########".slice(0, level) + (folding ? "+" : "");
90
+ token.map = [startLine, state.line];
91
+ if (folding) {
92
+ token.meta ||= {};
93
+ token.meta.folding = true;
94
+ }
95
+ token = state.push("inline", "", 0);
96
+ token.content = state.src.slice(pos, max).trim();
97
+ token.map = [startLine, state.line];
98
+ token.children = [];
99
+ token = state.push(TokenType.HeadingClose, "h" + String(level), -1);
100
+ token.markup = "########".slice(0, level) + (folding ? "+" : "");
101
+ if (folding) {
102
+ token.meta ||= {};
103
+ token.meta.folding = true;
104
+ }
105
+ return true;
106
+ };
107
+
108
+ // src/plugin/sectionsCoreRule.ts
109
+ var import_utils = require("@diplodoc/utils");
110
+
111
+ // src/plugin/utils.ts
112
+ function headingLevel(header) {
113
+ return parseInt(header.charAt(1), 10);
114
+ }
115
+ function last(arr) {
116
+ return arr[arr.length - 1];
117
+ }
118
+ function hidden(box, field, value) {
119
+ if (!(field in box)) {
120
+ Object.defineProperty(box, field, {
121
+ enumerable: false,
122
+ value
123
+ });
124
+ }
125
+ return box;
126
+ }
127
+ function copyRuntime({ runtime, output }, cache) {
128
+ const PATH_TO_RUNTIME = "../runtime";
129
+ const { join, resolve } = dynrequire("node:path");
130
+ const runtimeFiles = {
131
+ "index.js": runtime.script,
132
+ "index.css": runtime.style
133
+ };
134
+ for (const [originFile, outputFile] of Object.entries(runtimeFiles)) {
135
+ const file = join(PATH_TO_RUNTIME, originFile);
136
+ if (!cache.has(file)) {
137
+ cache.add(file);
138
+ copy(resolve(__dirname, file), join(output, outputFile));
139
+ }
140
+ }
141
+ }
142
+ function copy(from, to) {
143
+ const { mkdirSync, copyFileSync } = dynrequire("node:fs");
144
+ const { dirname } = dynrequire("node:path");
145
+ mkdirSync(dirname(to), { recursive: true });
146
+ copyFileSync(from, to);
147
+ }
148
+ function dynrequire(module) {
149
+ return eval(`require('${module}')`);
150
+ }
151
+
152
+ // src/plugin/sectionsCoreRule.ts
153
+ function createSectionsCoreRule(externalGenerateID) {
154
+ const generateID = externalGenerateID ?? (0, import_utils.createIDGeneratorByStrategy)("random");
155
+ return (state) => {
156
+ const Token = state.Token;
157
+ const tokens = [];
158
+ const sections = [];
159
+ let nestedLevel = 0;
160
+ for (const token of state.tokens) {
161
+ if (token.type.search(TokenType.Heading) !== 0) {
162
+ nestedLevel += token.nesting;
163
+ }
164
+ if (last(sections) && nestedLevel < last(sections).nesting) {
165
+ closeSectionsToCurrentNesting(nestedLevel);
166
+ }
167
+ const hasFolding = token.markup.endsWith("#+");
168
+ if (token.type === TokenType.HeadingOpen) {
169
+ const section = {
170
+ header: headingLevel(token.tag),
171
+ nesting: nestedLevel
172
+ };
173
+ closeSections(section);
174
+ if (hasFolding) {
175
+ tokens.push(openSection());
176
+ sections.push(section);
177
+ }
178
+ }
179
+ tokens.push(token);
180
+ if (token.type === TokenType.HeadingClose && hasFolding) {
181
+ tokens.push(openContent());
182
+ }
183
+ }
184
+ closeAllSections();
185
+ if (state.tokens.length !== tokens.length) {
186
+ state.env ??= {};
187
+ state.env[ENV_FLAG_NAME] = true;
188
+ }
189
+ state.tokens = tokens;
190
+ function openSection() {
191
+ const t = new Token(TokenType.SectionOpen, "section", 1);
192
+ t.attrPush(["class", SectionCN.Section]);
193
+ t.attrPush([SectionAttr.DataKey, DATA_KEY]);
194
+ t.attrPush([SectionAttr.DataId, generateID(DATA_KEY)]);
195
+ t.block = true;
196
+ return t;
197
+ }
198
+ function closeSection() {
199
+ const t = new Token(TokenType.SectionClose, "section", -1);
200
+ t.block = true;
201
+ return t;
202
+ }
203
+ function openContent() {
204
+ const t = new Token(TokenType.ContentOpen, "div", 1);
205
+ t.attrPush(["class", SectionCN.Content]);
206
+ t.block = true;
207
+ return t;
208
+ }
209
+ function closeContent() {
210
+ const t = new Token(TokenType.ContentClose, "div", -1);
211
+ t.block = true;
212
+ return t;
213
+ }
214
+ function closeSections(section) {
215
+ while (last(sections) && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
216
+ section.header <= last(sections).header && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
217
+ section.nesting <= last(sections).nesting) {
218
+ sections.pop();
219
+ tokens.push(closeContent());
220
+ tokens.push(closeSection());
221
+ }
222
+ }
223
+ function closeSectionsToCurrentNesting(nesting) {
224
+ while (last(sections) && nesting < last(sections).nesting) {
225
+ sections.pop();
226
+ tokens.push(closeContent());
227
+ tokens.push(closeSection());
228
+ }
229
+ }
230
+ function closeAllSections() {
231
+ while (sections.pop()) {
232
+ tokens.push(closeContent());
233
+ tokens.push(closeSection());
234
+ }
235
+ }
236
+ };
237
+ }
238
+
239
+ // src/plugin/plugin.ts
240
+ function createFoldingHeadingPlugin(generateID) {
241
+ return (md) => {
242
+ md.block.ruler.at("heading", headingBlockRule, {
243
+ alt: ["paragraph", "reference", "blockquote"]
244
+ });
245
+ md.core.ruler.push("heading_sections", createSectionsCoreRule(generateID));
246
+ };
247
+ }
248
+
249
+ // src/plugin/transform.ts
250
+ var registerTransform = (md, {
251
+ runtime,
252
+ bundle,
253
+ output,
254
+ generateID
255
+ }) => {
256
+ md.use(createFoldingHeadingPlugin(generateID));
257
+ md.core.ruler.push("heading_sections_after", ({ env }) => {
258
+ hidden(env, "bundled", /* @__PURE__ */ new Set());
259
+ if (env?.[ENV_FLAG_NAME]) {
260
+ env.meta = env.meta || {};
261
+ env.meta.script = env.meta.script || [];
262
+ env.meta.script.push(runtime.script);
263
+ env.meta.style = env.meta.style || [];
264
+ env.meta.style.push(runtime.style);
265
+ if (bundle) {
266
+ copyRuntime({ runtime, output }, env.bundled);
267
+ }
268
+ }
269
+ });
270
+ };
271
+ function transform(options = {}) {
272
+ const { bundle = true } = options;
273
+ if (bundle && typeof options.runtime === "string") {
274
+ throw new TypeError("Option `runtime` should be record when `bundle` is enabled.");
275
+ }
276
+ const runtime = typeof options.runtime === "string" ? { script: options.runtime, style: options.runtime } : options.runtime || {
277
+ script: "_assets/folding-headings-extension.js",
278
+ style: "_assets/folding-headings-extension.css"
279
+ };
280
+ const plugin = function(md, { output = ".", generateID } = {}) {
281
+ registerTransform(md, {
282
+ runtime,
283
+ bundle,
284
+ output,
285
+ generateID
286
+ });
287
+ };
288
+ Object.assign(plugin, {
289
+ collect(input, { destRoot = "." }) {
290
+ const MdIt = dynrequire("markdown-it");
291
+ const md = new MdIt().use((md2) => {
292
+ registerTransform(md2, {
293
+ runtime,
294
+ bundle,
295
+ output: destRoot
296
+ });
297
+ });
298
+ md.parse(input, {});
299
+ }
300
+ });
301
+ return plugin;
302
+ }
303
+ // Annotate the CommonJS export names for ESM import in node:
304
+ 0 && (module.exports = {
305
+ TokenType,
306
+ transform
307
+ });
308
+ //# sourceMappingURL=index.js.map
@@ -1,6 +1,2 @@
1
- import { GLOBAL_KEY } from './const';
2
- import { HeadingSectionContoller } from './controller';
3
- import './styles/index.scss';
4
- if (typeof window !== 'undefined' && typeof document !== 'undefined' && !window[GLOBAL_KEY]) {
5
- window[GLOBAL_KEY] = new HeadingSectionContoller(document);
6
- }
1
+ "use strict";(()=>{var c="heading-section",o={DataId:"data-diplodoc-id",DataKey:"data-diplodoc-key"};var a="heading_sections",I={Heading:".yfm .heading-section > h1,h2,h3,h4,h5,h6",Section:".yfm .heading-section",Content:".yfm .heading-section-content"},i={Open:"open"};var A=g=>{let e=g.composedPath();return Array.isArray(e)&&e.length>0?e[0]:g.target},C=g=>{let e=A(g);return!e||!e.matches};var n=class{__doc;constructor(e){this.__doc=e,this.__doc.addEventListener("click",this._onDocClick)}open(e){for(let t of this._findSections(e))t.classList.add(i.Open)}toggle(e){for(let t of this._findSections(e))t.classList.toggle(i.Open)}close(e){for(let t of this._findSections(e))t.classList.remove(i.Open)}destroy(){this.__doc.removeEventListener("click",this._onDocClick)}_findSections(e){return Array.from(this.__doc.querySelectorAll(`section[${o.DataKey}=${c}][${o.DataId}=${e}]`))}_onDocClick=e=>{if(C(e))return;let t=this._findHeading(e);t&&this._toogleSection(t)};_findHeading(e){let t=A(e);return this._matchHeading(t)?t:e.composedPath?.()?.find(this._matchHeading)}_matchHeading=e=>e instanceof HTMLElement?e?.matches?.(I.Heading)&&e.parentElement?.matches(I.Section):!1;_toogleSection(e){let t=e.parentElement;t instanceof HTMLElement&&t.classList.toggle(i.Open)}};typeof window<"u"&&typeof document<"u"&&!window[a]&&(window[a]=new n(document));})();
2
+ //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diplodoc/folding-headings-extension",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Folding headings extension for Diplodoc platform ",
5
5
  "main": "build/plugin/index.js",
6
6
  "types": "build/plugin/index.d.ts",
@@ -48,13 +48,13 @@
48
48
  "lock": "npm install --no-workspaces --package-lock-only --ignore-scripts"
49
49
  },
50
50
  "devDependencies": {
51
- "@diplodoc/utils": "^2.2.2",
51
+ "@diplodoc/infra": "2.2.1",
52
+ "@diplodoc/utils": "^2.3.5",
52
53
  "@types/markdown-it": "^13.0.9",
53
54
  "@vitest/coverage-v8": "^3.2.4",
54
55
  "markdown-it": "^13.0.0",
55
56
  "npm-run-all": "^4.1.5",
56
57
  "typescript": "^5.3.3",
57
- "vitest": "^3.2.4",
58
- "@diplodoc/infra": "2.2.1"
58
+ "vitest": "^3.2.4"
59
59
  }
60
60
  }
@@ -1,9 +0,0 @@
1
- export const DATA_KEY = 'heading-section';
2
- export const SectionAttr = {
3
- DataId: 'data-diplodoc-id',
4
- DataKey: 'data-diplodoc-key',
5
- };
6
- export const SectionCN = {
7
- Section: 'heading-section',
8
- Content: 'heading-section-content',
9
- };
@@ -1,13 +0,0 @@
1
- export { SectionCN, DATA_KEY, SectionAttr } from '../common/const';
2
- export const ENV_FLAG_NAME = 'has-folding-headings';
3
- export const TokenType = {
4
- Heading: 'heading',
5
- HeadingOpen: 'heading_open',
6
- HeadingClose: 'heading_close',
7
- Section: 'heading_section',
8
- SectionOpen: 'heading_section_open',
9
- SectionClose: 'heading_section_close',
10
- Content: 'heading_section_content',
11
- ContentOpen: 'heading_section_content_open',
12
- ContentClose: 'heading_section_content_close',
13
- };
@@ -1,59 +0,0 @@
1
- import { TokenType } from './const';
2
- // copied from https://github.com/markdown-it/markdown-it/blob/14.1.0/lib/rules_block/heading.mjs
3
- // modified: support syntax ###+ for folding headings
4
- export const headingBlockRule = (state, startLine, _endLine, silent) => {
5
- const { isSpace } = state.md.utils;
6
- let pos = state.bMarks[startLine] + state.tShift[startLine];
7
- let max = state.eMarks[startLine];
8
- // if it's indented more than 3 spaces, it should be a code block
9
- if (state.sCount[startLine] - state.blkIndent >= 4) {
10
- return false;
11
- }
12
- let ch = state.src.charCodeAt(pos);
13
- if (ch !== 0x23 /* # */ || pos >= max) {
14
- return false;
15
- }
16
- // count heading level
17
- let level = 1;
18
- ch = state.src.charCodeAt(++pos);
19
- while (ch === 0x23 /* # */ && pos < max && level <= 6) {
20
- level++;
21
- ch = state.src.charCodeAt(++pos);
22
- }
23
- let folding = false;
24
- if (ch === 0x2b /* + */) {
25
- folding = true;
26
- ch = state.src.charCodeAt(++pos);
27
- }
28
- if (level > 6 || (pos < max && !isSpace(ch))) {
29
- return false;
30
- }
31
- if (silent) {
32
- return true;
33
- }
34
- // Let's cut tails like ' ### ' from the end of string
35
- max = state.skipSpacesBack(max, pos);
36
- const tmp = state.skipCharsBack(max, 0x23, pos); // #
37
- if (tmp > pos && isSpace(state.src.charCodeAt(tmp - 1))) {
38
- max = tmp;
39
- }
40
- state.line = startLine + 1;
41
- let token = state.push(TokenType.HeadingOpen, 'h' + String(level), 1);
42
- token.markup = '########'.slice(0, level) + (folding ? '+' : '');
43
- token.map = [startLine, state.line];
44
- if (folding) {
45
- token.meta ||= {};
46
- token.meta.folding = true;
47
- }
48
- token = state.push('inline', '', 0);
49
- token.content = state.src.slice(pos, max).trim();
50
- token.map = [startLine, state.line];
51
- token.children = [];
52
- token = state.push(TokenType.HeadingClose, 'h' + String(level), -1);
53
- token.markup = '########'.slice(0, level) + (folding ? '+' : '');
54
- if (folding) {
55
- token.meta ||= {};
56
- token.meta.folding = true;
57
- }
58
- return true;
59
- };
@@ -1,19 +0,0 @@
1
- import { headingBlockRule } from './headingBlockRule';
2
- import { createSectionsCoreRule } from './sectionsCoreRule';
3
- /**
4
- * Creates a folding-heading plugin with an optional external ID generator.
5
- * When `generateID` is provided, all generated section IDs share the same counter
6
- * as other plugins, ensuring deterministic output regardless of --merge-includes.
7
- * When omitted, the plugin uses its internal fallback generator.
8
- *
9
- * @param generateID - Optional external ID generator shared with the rest of the transform pipeline.
10
- * @returns Markdown-it plugin that registers folding heading parsing and section wrapping rules.
11
- */
12
- export function createFoldingHeadingPlugin(generateID) {
13
- return (md) => {
14
- md.block.ruler.at('heading', headingBlockRule, {
15
- alt: ['paragraph', 'reference', 'blockquote'],
16
- });
17
- md.core.ruler.push('heading_sections', createSectionsCoreRule(generateID));
18
- };
19
- }
@@ -1,104 +0,0 @@
1
- import { createIDGeneratorByStrategy } from '@diplodoc/utils';
2
- import { DATA_KEY, ENV_FLAG_NAME, SectionAttr, SectionCN, TokenType } from './const';
3
- import { headingLevel, last } from './utils';
4
- /**
5
- * Creates a core rule that wraps folding headings in `<section>` elements with unique IDs.
6
- * @param externalGenerateID - Optional external ID generator (e.g. from MarkdownItPluginOpts).
7
- * When provided, ensures all plugins share the same per-file generator.
8
- * When omitted, uses random fallback behavior.
9
- * @returns Markdown-it core rule that injects section wrapper tokens around folding headings.
10
- */
11
- export function createSectionsCoreRule(externalGenerateID) {
12
- // Use the external generator if provided, otherwise use random fallback.
13
- // Random IDs are the default behavior for production builds.
14
- const generateID = externalGenerateID ?? createIDGeneratorByStrategy('random');
15
- return (state) => {
16
- const Token = state.Token;
17
- const tokens = []; // output
18
- const sections = [];
19
- let nestedLevel = 0;
20
- for (const token of state.tokens) {
21
- // record level of nesting
22
- if (token.type.search(TokenType.Heading) !== 0) {
23
- nestedLevel += token.nesting;
24
- }
25
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
26
- if (last(sections) && nestedLevel < last(sections).nesting) {
27
- closeSectionsToCurrentNesting(nestedLevel);
28
- }
29
- const hasFolding = token.markup.endsWith('#+');
30
- // add sections before headers
31
- if (token.type === TokenType.HeadingOpen) {
32
- const section = {
33
- header: headingLevel(token.tag),
34
- nesting: nestedLevel,
35
- };
36
- closeSections(section);
37
- if (hasFolding) {
38
- tokens.push(openSection());
39
- sections.push(section);
40
- }
41
- }
42
- tokens.push(token);
43
- if (token.type === TokenType.HeadingClose && hasFolding) {
44
- tokens.push(openContent());
45
- }
46
- }
47
- // end for every token
48
- closeAllSections();
49
- if (state.tokens.length !== tokens.length) {
50
- state.env ??= {};
51
- state.env[ENV_FLAG_NAME] = true;
52
- }
53
- state.tokens = tokens;
54
- function openSection() {
55
- const t = new Token(TokenType.SectionOpen, 'section', 1);
56
- t.attrPush(['class', SectionCN.Section]);
57
- t.attrPush([SectionAttr.DataKey, DATA_KEY]);
58
- t.attrPush([SectionAttr.DataId, generateID(DATA_KEY)]);
59
- t.block = true;
60
- return t;
61
- }
62
- function closeSection() {
63
- const t = new Token(TokenType.SectionClose, 'section', -1);
64
- t.block = true;
65
- return t;
66
- }
67
- function openContent() {
68
- const t = new Token(TokenType.ContentOpen, 'div', 1);
69
- t.attrPush(['class', SectionCN.Content]);
70
- t.block = true;
71
- return t;
72
- }
73
- function closeContent() {
74
- const t = new Token(TokenType.ContentClose, 'div', -1);
75
- t.block = true;
76
- return t;
77
- }
78
- function closeSections(section) {
79
- while (last(sections) &&
80
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
81
- section.header <= last(sections).header &&
82
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
83
- section.nesting <= last(sections).nesting) {
84
- sections.pop();
85
- tokens.push(closeContent());
86
- tokens.push(closeSection());
87
- }
88
- }
89
- function closeSectionsToCurrentNesting(nesting) {
90
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
91
- while (last(sections) && nesting < last(sections).nesting) {
92
- sections.pop();
93
- tokens.push(closeContent());
94
- tokens.push(closeSection());
95
- }
96
- }
97
- function closeAllSections() {
98
- while (sections.pop()) {
99
- tokens.push(closeContent());
100
- tokens.push(closeSection());
101
- }
102
- }
103
- };
104
- }
@@ -1,53 +0,0 @@
1
- import { ENV_FLAG_NAME } from './const';
2
- import { createFoldingHeadingPlugin } from './plugin';
3
- import { copyRuntime, dynrequire, hidden } from './utils';
4
- const registerTransform = (md, { runtime, bundle, output, generateID, }) => {
5
- md.use(createFoldingHeadingPlugin(generateID));
6
- md.core.ruler.push('heading_sections_after', ({ env }) => {
7
- hidden(env, 'bundled', new Set());
8
- if (env?.[ENV_FLAG_NAME]) {
9
- env.meta = env.meta || {};
10
- env.meta.script = env.meta.script || [];
11
- env.meta.script.push(runtime.script);
12
- env.meta.style = env.meta.style || [];
13
- env.meta.style.push(runtime.style);
14
- if (bundle) {
15
- copyRuntime({ runtime, output }, env.bundled);
16
- }
17
- }
18
- });
19
- };
20
- export function transform(options = {}) {
21
- const { bundle = true } = options;
22
- if (bundle && typeof options.runtime === 'string') {
23
- throw new TypeError('Option `runtime` should be record when `bundle` is enabled.');
24
- }
25
- const runtime = typeof options.runtime === 'string'
26
- ? { script: options.runtime, style: options.runtime }
27
- : options.runtime || {
28
- script: '_assets/folding-headings-extension.js',
29
- style: '_assets/folding-headings-extension.css',
30
- };
31
- const plugin = function (md, { output = '.', generateID } = {}) {
32
- registerTransform(md, {
33
- runtime,
34
- bundle,
35
- output,
36
- generateID,
37
- });
38
- };
39
- Object.assign(plugin, {
40
- collect(input, { destRoot = '.' }) {
41
- const MdIt = dynrequire('markdown-it');
42
- const md = new MdIt().use((md) => {
43
- registerTransform(md, {
44
- runtime,
45
- bundle,
46
- output: destRoot,
47
- });
48
- });
49
- md.parse(input, {});
50
- },
51
- });
52
- return plugin;
53
- }
@@ -1,44 +0,0 @@
1
- export function headingLevel(header) {
2
- return parseInt(header.charAt(1), 10);
3
- }
4
- export function last(arr) {
5
- return arr[arr.length - 1];
6
- }
7
- export function hidden(box, field, value) {
8
- if (!(field in box)) {
9
- Object.defineProperty(box, field, {
10
- enumerable: false,
11
- value: value,
12
- });
13
- }
14
- return box;
15
- }
16
- export function copyRuntime({ runtime, output }, cache) {
17
- const PATH_TO_RUNTIME = '../runtime';
18
- const { join, resolve } = dynrequire('node:path');
19
- const runtimeFiles = {
20
- 'index.js': runtime.script,
21
- 'index.css': runtime.style,
22
- };
23
- for (const [originFile, outputFile] of Object.entries(runtimeFiles)) {
24
- const file = join(PATH_TO_RUNTIME, originFile);
25
- if (!cache.has(file)) {
26
- cache.add(file);
27
- copy(resolve(__dirname, file), join(output, outputFile));
28
- }
29
- }
30
- }
31
- export function copy(from, to) {
32
- const { mkdirSync, copyFileSync } = dynrequire('node:fs');
33
- const { dirname } = dynrequire('node:path');
34
- mkdirSync(dirname(to), { recursive: true });
35
- copyFileSync(from, to);
36
- }
37
- /*
38
- * Runtime require hidden for builders.
39
- * Used for nodejs api
40
- */
41
- export function dynrequire(module) {
42
- // eslint-disable-next-line no-eval
43
- return eval(`require('${module}')`);
44
- }
@@ -1,10 +0,0 @@
1
- export { SectionAttr, DATA_KEY } from '../common/const';
2
- export const GLOBAL_KEY = 'heading_sections';
3
- export const Selector = {
4
- Heading: '.yfm .heading-section > h1,h2,h3,h4,h5,h6',
5
- Section: '.yfm .heading-section',
6
- Content: '.yfm .heading-section-content',
7
- };
8
- export const ClassName = {
9
- Open: 'open',
10
- };
@@ -1,55 +0,0 @@
1
- import { ClassName, DATA_KEY, SectionAttr, Selector } from './const';
2
- import { getEventTarget, isCustom } from './utils';
3
- export class HeadingSectionContoller {
4
- __doc;
5
- constructor(doc) {
6
- this.__doc = doc;
7
- this.__doc.addEventListener('click', this._onDocClick);
8
- }
9
- open(id) {
10
- for (const elem of this._findSections(id)) {
11
- elem.classList.add(ClassName.Open);
12
- }
13
- }
14
- toggle(id) {
15
- for (const elem of this._findSections(id)) {
16
- elem.classList.toggle(ClassName.Open);
17
- }
18
- }
19
- close(id) {
20
- for (const elem of this._findSections(id)) {
21
- elem.classList.remove(ClassName.Open);
22
- }
23
- }
24
- destroy() {
25
- this.__doc.removeEventListener('click', this._onDocClick);
26
- }
27
- _findSections(id) {
28
- return Array.from(this.__doc.querySelectorAll(`section[${SectionAttr.DataKey}=${DATA_KEY}][${SectionAttr.DataId}=${id}]`));
29
- }
30
- _onDocClick = (event) => {
31
- if (isCustom(event))
32
- return;
33
- const heading = this._findHeading(event);
34
- if (heading)
35
- this._toogleSection(heading);
36
- };
37
- _findHeading(event) {
38
- const target = getEventTarget(event);
39
- if (this._matchHeading(target))
40
- return target;
41
- const path = event.composedPath?.();
42
- return path?.find(this._matchHeading);
43
- }
44
- _matchHeading = (target) => {
45
- if (!(target instanceof HTMLElement))
46
- return false;
47
- return (target?.matches?.(Selector.Heading) && target.parentElement?.matches(Selector.Section));
48
- };
49
- _toogleSection(heading) {
50
- const section = heading.parentElement;
51
- if (section instanceof HTMLElement) {
52
- section.classList.toggle(ClassName.Open);
53
- }
54
- }
55
- }
@@ -1,8 +0,0 @@
1
- export const getEventTarget = (event) => {
2
- const path = event.composedPath();
3
- return Array.isArray(path) && path.length > 0 ? path[0] : event.target;
4
- };
5
- export const isCustom = (event) => {
6
- const target = getEventTarget(event);
7
- return !target || !target.matches;
8
- };