UNPKG

mdast-util-caml

Version:

Converts a `micromark` token stream into an `mdast` syntax tree.

556 lines (524 loc) 18.6 kB
// mdast-util-caml v0.0.9-md - https://github.com/wikibonsai/remark-caml.git import { merge } from 'lodash-es'; import { selectAll } from 'unist-util-select'; import * as wikirefs from 'wikirefs'; import { resolve } from 'caml-mkdn'; import { safe } from 'mdast-util-to-markdown/lib/util/safe.js'; function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } } function _arrayLikeToArray$1(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _unsupportedIterableToArray$1(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$1(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray$1(arr, i) || _nonIterableRest(); } function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } // required options function initAttrBox(tree, opts) { // opts var defaults = { attrs: { render: true, title: 'Attributes' }, cssNames: { attr: 'attr', attrbox: 'attrbox', attrboxTitle: 'attrbox-title', // [[wikiattrs]]-related wiki: 'wiki', invalid: 'invalid', // types reftype: 'reftype__', doctype: 'doctype__' } }; var fullOpts = merge(defaults, opts); // todo: should i be making node assertions...? // https://github.com/syntax-tree/unist-util-is#isnode-test-index-parent-context // extract attr data nodes var attrDataNodes = selectAll('attrbox-data', tree); // extract attr data var attrData = attrDataNodes.reduce(function (acc, node) { Object.entries(node.data.items).forEach(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), key = _ref2[0], value = _ref2[1]; if (acc[key]) { acc[key] = acc[key].concat(value); } else { acc[key] = value; } }); return acc; }, {}); // build attrbox and return return Object.keys(attrData).length > 0 ? buildAttrBox(attrData, fullOpts) : undefined; } // html properties are meant to build an element like this: // // <aside class='attrbox'> // <span class='attrbox-title'>Attributes</span> // <dl> // <dt>key</dt> // <dd><span class='attr type key'>val a</span></dd> // <dd><span class='attr type key'>val b</span></dd> // <dd><span class='attr type key'>val c</span></dd> // ... // </dl> // </aside> function buildAttrBox(attrData, fullOpts) { // init var attrbox = { type: 'attrbox', children: [], data: { items: {}, hName: 'aside', hProperties: { className: [fullOpts.cssNames.attrbox] } } }; var attrboxTitle = { type: 'attrbox-title', children: [{ type: 'text', value: fullOpts.attrs.title }], data: { hName: 'span', hProperties: { className: [fullOpts.cssNames.attrboxTitle] } } }; var attrBoxListNode = { type: 'attrbox-list', children: [], data: { hName: 'dl' } }; // construct attrbox.children.push(attrboxTitle); attrbox.children.push(attrBoxListNode); // if we have attr items, process them if (Object.keys(attrData).length > 0) { // copy item data attrbox.data.items = _objectSpread({}, attrData); // build item hProperties from item data for (var _i = 0, _Object$entries = Object.entries(attrData); _i < _Object$entries.length; _i++) { var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), attrtype = _Object$entries$_i[0], items = _Object$entries$_i[1]; addAttrKey(attrbox, attrtype); var _iterator = _createForOfIteratorHelper(items), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var item = _step.value; if (item.type === 'wiki') { addWikiAttrVal(attrbox, attrtype, item, fullOpts); } else { addAttrVal(attrbox, attrtype, item, fullOpts); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } } return attrbox; } function addAttrKey(attrbox, key, fullOpts) { var keyTxt = key ? key.trim() : undefined; var keyNode = { type: 'attr-key', children: [{ type: 'text', value: keyTxt ? keyTxt : 'key error' }], data: { hName: 'dt' } }; // add to attrbox-list attrbox.children[1].children.push(keyNode); } function addAttrVal(attrbox, key, val, fullOpts) { var valNode = { type: 'attr-val', children: [{ type: val.type, children: [{ type: 'text', value: val.string }], data: { hName: 'span', hProperties: { className: [fullOpts.cssNames.attr, val.type ? val.type.trim().toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '') : 'valtype-error', key ? key.trim().toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '') : 'key-error'] } } }], data: { hName: 'dd' } }; // add to attrbox-list attrbox.children[1].children.push(valNode); } // todo: link to mdast-util-wikirefs instance // this interface is to help cut down on interdependencies between caml + wikirefs function addWikiAttrVal(attrbox, attrtype, wikiVal, // wikiVal: WikiAttrData, fullOpts // fullOpts: DefaultsWikiRefs & DefaultsWikiAttrs, ) { var wikiNode; // let wikiNode: WikiAttrNode; var valNode = { type: 'attr-val', children: [], data: { hName: 'dd' } }; // invalid if (!wikiVal.htmlHref) { wikiNode = { type: 'wikiattr', children: [{ type: 'text', value: wikirefs.CONST.MARKER.OPEN + wikiVal.filename + wikirefs.CONST.MARKER.CLOSE }], data: { hName: 'a', hProperties: { className: [fullOpts.cssNames.attr, fullOpts.cssNames.wiki, fullOpts.cssNames.invalid] } } }; // valid } else { wikiNode = { type: 'wikiattr', children: [{ type: 'text', value: wikiVal.htmlText ? wikiVal.htmlText : wikiVal.filename }], data: { hName: 'a', hProperties: { className: wikiVal.doctype.length > 0 // with doctype ? [fullOpts.cssNames.attr, fullOpts.cssNames.wiki, attrtype ? fullOpts.cssNames.reftype + attrtype.trim().toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '') : fullOpts.cssNames.reftype + 'attrtype-error', fullOpts.cssNames.doctype + wikiVal.doctype.trim().toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '')] // without doctype : [fullOpts.cssNames.attr, fullOpts.cssNames.wiki, attrtype ? fullOpts.cssNames.reftype + attrtype.trim().toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '') : fullOpts.cssNames.reftype + 'attrtype-error'], href: wikiVal.baseUrl + wikiVal.htmlHref, dataHref: wikiVal.baseUrl + wikiVal.htmlHref } } }; } // add wikiattr to attr-val valNode.children.push(wikiNode); // add to attrbox-list attrbox.children[1].children.push(valNode); } // required options function fromMarkdownCaml(opts) { // note: enter/exit keys should match a token name return { enter: { attrBox: enterAttrBox }, exit: { attrKey: exitAttrKey, attrVal: exitAttrVal, attrBox: exitAttrBox } }; function enterAttrBox(token) { var attrBoxDataNode = { type: 'attrbox-data', data: { items: {} } }; // is accessible via 'this.stack' (see below) this.enter(attrBoxDataNode, token); // current key var curKey = this.getData('curKey'); if (curKey === undefined) { this.setData('curKey', ''); } } function exitAttrKey(token) { var key = this.sliceSerialize(token).trim(); var current = top(this.stack); if (current.data && current.data.items && !Object.keys(current.data.items).includes(key)) { current.data.items[key] = []; } this.setData('curKey', key); } function exitAttrVal(token) { var stringValue = this.sliceSerialize(token); var item = resolve(stringValue); var current = top(this.stack); var curKey = this.getData('curKey'); if (current.data && current.data.items) { if (curKey !== undefined) { current.data.items[curKey].push(item); } } } // note: leaving this here to close the token function exitAttrBox(token) { this.exit(token); return; } // util function top(stack) { return Array.from(stack)[Array.from(stack).length - 1]; } } function toMarkdownCaml() { var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; // opts var defaults = { attrs: { enable: true, toMarkdown: { format: 'none', listKind: 'mkdn', prefixed: true } } }; var fullOpts = merge(defaults, opts); // 'defaults' ensure 'fullOpts.attrs' will be populated above var format = fullOpts.attrs.toMarkdown.format; var listKind = fullOpts.attrs.toMarkdown.listKind; var prefixed = fullOpts.attrs.toMarkdown.prefixed; return { // TODO: I don't fully understand what this does, but I did my // best to fill it in based on what I saw in other mdast utils // (e.g. https://github.com/syntax-tree/mdast-util-math/blob/main/index.js#L135) unsafe: [{ character: '[', inConstruct: ['phrasing', 'label', 'reference'] }, { character: ']', inConstruct: ['label', 'reference'] }], handlers: { // as of (2021-05-07), the typings for Handle do not reflect // that the handler will be passed nodes of a specific type // note: name should match 'Node.type' // attrbox: handleNode as Handle, // note: 'case-conversion' <-> caseConversion seems to not be working... 'attrbox-data': handler, attrboxData: handler } }; function handler(node, _, context) { var exit = context.enter('attrbox'); var value = buildMkdn(node.data.items, context); exit(); return value; } // note: this handler will generate markdown from the content's top-level attrbox // function handleNode( // node: (AttrBoxNode), // _: Uni.Parent | null | undefined, // context: Context, // ): string { // attrboxFound = true; // const exit = context.enter('attrbox' as ConstructName); // const value: string = buildMkdn(node.data.items, context); // exit(); // return value; // } function buildMkdn(attrs, context) { // init vars / build string value var value = ''; if (prefixed) { value += ':'; if (format === 'pad') { value += ' '; } } for (var _i = 0, _Object$entries = Object.entries(attrs); _i < _Object$entries.length; _i++) { var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), key = _Object$entries$_i[0], items = _Object$entries$_i[1]; var vals = items; // key /* eslint-disable indent */ var nodeKey = safe(context, key, { before: ':', after: ':' }); /* eslint-enable indent */ if (nodeKey) { value += nodeKey; if (format === 'pad') { value += ' '; } value += '::'; if (format === 'pad') { value += ' '; } } // values / items for (var i = 0; i < items.length; i++) { // todo: link to mdast-util-wikirefs instance // this interface is to help cut down on interdependencies between caml + wikirefs // [[wikilink]] (taken from mdast-util-wikirefs) if (vals[i].type === 'wiki') { var isLastItem = i === vals.length - 1; /* eslint-disable indent */ var nodeFileName = safe(context, // todo: 'context' -> 'state' (see: https://github.com/syntax-tree/mdast-util-to-markdown/commit/e812c7954f8b8ea5dd68476c856cbfd7cc4c442b) vals[i].filename, { before: '[', after: ']' }); /* eslint-enable indent */ // single item or list comma-separated if (vals.length === 1 || listKind === 'comma') { value += "[[".concat(nodeFileName, "]]"); if (!isLastItem) { value += ','; if (format === 'pad') { value += ' '; } } } // multiple items, list mkdn-separated if (vals.length > 1 && listKind === 'mkdn') { value += "\n- [[".concat(nodeFileName, "]]"); } // add last newline after last item if (isLastItem) { value += '\n'; } // primitive } else { var camlItem = vals[i]; var _isLastItem = i === vals.length - 1; /* eslint-disable indent */ var nodeValue = safe(context, camlItem.string, { before: '[', after: ']' }); /* eslint-enable indent */ // single item or list comma-separated if (vals.length === 1 || listKind === 'comma') { value += nodeValue; if (!_isLastItem) { value += ','; if (format === 'pad') { value += ' '; } } } // multiple items, list mkdn-separated if (vals.length > 1 && listKind === 'mkdn') { value += "\n- ".concat(nodeValue); } // add last newline after last item if (_isLastItem) { value += '\n'; } } } } return value; } } export { fromMarkdownCaml, initAttrBox, toMarkdownCaml }; //# sourceMappingURL=index.js.map