wikiparser-node
Version: 
A Node.js parser for MediaWiki markup with AST
389 lines (388 loc) • 17.5 kB
JavaScript
"use strict";
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
    var useValue = arguments.length > 2;
    for (var i = 0; i < initializers.length; i++) {
        value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
    }
    return useValue ? value : void 0;
};
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
    function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
    var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
    var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
    var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
    var _, done = false;
    for (var i = decorators.length - 1; i >= 0; i--) {
        var context = {};
        for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
        for (var p in contextIn.access) context.access[p] = contextIn.access[p];
        context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
        var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
        if (kind === "accessor") {
            if (result === void 0) continue;
            if (result === null || typeof result !== "object") throw new TypeError("Object expected");
            if (_ = accept(result.get)) descriptor.get = _;
            if (_ = accept(result.set)) descriptor.set = _;
            if (_ = accept(result.init)) initializers.unshift(_);
        }
        else if (_ = accept(result)) {
            if (kind === "field") initializers.unshift(_);
            else descriptor[key] = _;
        }
    }
    if (target) Object.defineProperty(target, contextIn.name, descriptor);
    done = true;
};
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
    if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
    return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTd = exports.TdToken = void 0;
const lint_1 = require("../../util/lint");
const constants_1 = require("../../util/constants");
const rect_1 = require("../../lib/rect");
const cached_1 = require("../../mixin/cached");
const index_1 = __importDefault(require("../../index"));
const index_2 = require("../index");
const base_1 = require("./base");
/* NOT FOR BROWSER */
const debug_1 = require("../../util/debug");
const string_1 = require("../../util/string");
const fixed_1 = require("../../mixin/fixed");
/* NOT FOR BROWSER END */
/**
 * `<td>`, `<th>` or `<caption>`
 *
 * `<td>`、`<th>`或`<caption>`
 * @classdesc `{childNodes: [SyntaxToken, AttributesToken, Token]}`
 */
let TdToken = (() => {
    let _classDecorators = [fixed_1.fixedToken];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = base_1.TableBaseToken;
    let _instanceExtraInitializers = [];
    let _private_getSyntax_decorators;
    let _private_getSyntax_descriptor;
    let _toHtmlInternal_decorators;
    var TdToken = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _private_getSyntax_decorators = [(0, cached_1.cached)(false)];
            _toHtmlInternal_decorators = [(0, cached_1.cached)()];
            __esDecorate(this, _private_getSyntax_descriptor = { value: __setFunctionName(function () {
                    const syntax = this.firstChild.text(), 
                    /* NOT FOR BROWSER */
                    esc = syntax.includes('{{'), 
                    /* NOT FOR BROWSER END */
                    char = syntax.slice(-1);
                    let subtype = 'td';
                    if (char === '!') {
                        subtype = 'th';
                    }
                    else if (char === '+') {
                        subtype = 'caption';
                    }
                    if (this.isIndependent()) {
                        return {
                            subtype,
                            /* NOT FOR BROWSER */
                            escape: esc,
                            correction: false,
                        };
                    }
                    const { previousSibling } = this;
                    /* NOT FOR BROWSER */
                    if (!(previousSibling instanceof TdToken)) {
                        return { subtype, escape: esc, correction: true };
                    }
                    /* NOT FOR BROWSER END */
                    // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
                    const result = { ...previousSibling.#getSyntax() };
                    /* NOT FOR BROWSER */
                    const str = previousSibling.lastChild.toString();
                    result.escape ||= esc;
                    result.correction = str.includes('\n') && debug_1.Shadow.run(() => new index_2.Token(str, this.getAttribute('config'))
                        .parseOnce(0, this.getAttribute('include'))
                        .parseOnce()
                        .parseOnce()
                        .toString()
                        .includes('\n'));
                    if (subtype === 'th' && result.subtype !== 'th') {
                        result.subtype = 'th';
                        result.correction = true;
                    }
                    /* NOT FOR BROWSER END */
                    return result;
                }, "#getSyntax") }, _private_getSyntax_decorators, { kind: "method", name: "#getSyntax", static: false, private: true, access: { has: obj => #getSyntax in obj, get: obj => obj.#getSyntax }, metadata: _metadata }, null, _instanceExtraInitializers);
            __esDecorate(this, null, _toHtmlInternal_decorators, { kind: "method", name: "toHtmlInternal", static: false, private: false, access: { has: obj => "toHtmlInternal" in obj, get: obj => obj.toHtmlInternal }, metadata: _metadata }, null, _instanceExtraInitializers);
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            TdToken = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
        #innerSyntax = (__runInitializers(this, _instanceExtraInitializers), '');
        /* NOT FOR BROWSER END */
        get type() {
            return 'td';
        }
        /** rowspan */
        get rowspan() {
            return this.getAttr('rowspan');
        }
        /** colspan */
        get colspan() {
            return this.getAttr('colspan');
        }
        /** cell type / 单元格类型 */
        get subtype() {
            return this.#getSyntax().subtype;
        }
        /* NOT FOR BROWSER */
        set subtype(subtype) {
            this.setSyntax(subtype);
        }
        set rowspan(rowspan) {
            this.setAttr('rowspan', rowspan);
        }
        set colspan(colspan) {
            this.setAttr('colspan', colspan);
        }
        /** inner wikitext / 内部wikitext */
        get innerText() {
            return this.lastChild.text().trim();
        }
        set innerText(text) {
            this.lastChild.replaceChildren(...index_1.default.parse(text, true, undefined, this.getAttribute('config')).childNodes);
        }
        /* NOT FOR BROWSER END */
        /**
         * @param syntax 单元格语法
         * @param inner 内部wikitext
         */
        constructor(syntax, inner, config, accum = []) {
            let innerSyntax = /\||\0\d+!\x7F/u.exec(inner ?? ''), attr = innerSyntax ? inner.slice(0, innerSyntax.index) : '';
            if (/\[\[|-\{/u.test(attr)) {
                innerSyntax = null;
                attr = '';
            }
            super(/^(?:\n[^\S\n]*(?:[|!]|\|\+|\{\{\s*!\s*\}\}\+?)|(?:\||\{\{\s*!\s*\}\}){2}|!!|\{\{\s*!!\s*\}\})$/u, syntax, 'td', attr, config, accum, { SyntaxToken: 0, AttributesToken: 1, Token: 2 });
            if (innerSyntax) {
                [this.#innerSyntax] = innerSyntax;
            }
            const innerToken = new index_2.Token(inner?.slice((innerSyntax?.index ?? NaN) + this.#innerSyntax.length), config, accum);
            innerToken.type = 'td-inner';
            innerToken.setAttribute('stage', 4);
            this.insertAt(innerToken);
        }
        /** 表格语法信息 */
        get #getSyntax() { return _private_getSyntax_descriptor.value; }
        /** @private */
        afterBuild() {
            if (this.#innerSyntax.includes('\0')) {
                this.#innerSyntax = this.buildFromStr(this.#innerSyntax, constants_1.BuildMethod.String);
            }
            super.afterBuild();
        }
        /** @private */
        toString(skip) {
            /* NOT FOR BROWSER */
            this.#correct();
            /* NOT FOR BROWSER END */
            const { childNodes: [syntax, attr, inner] } = this;
            return syntax.toString(skip) + attr.toString(skip) + this.#innerSyntax + inner.toString(skip);
        }
        /** @private */
        text() {
            /* NOT FOR BROWSER */
            this.#correct();
            /* NOT FOR BROWSER END */
            const { childNodes: [syntax, attr, inner] } = this;
            return syntax.text() + attr.text() + this.#innerSyntax + inner.text();
        }
        /** @private */
        getGaps(i) {
            if (i === 1) {
                /* NOT FOR BROWSER */
                this.#correct();
                /* NOT FOR BROWSER END */
                return this.#innerSyntax.length;
            }
            return 0;
        }
        /** @private */
        lint(start = this.getAbsoluteIndex(), re) {
            LINT: { // eslint-disable-line no-unused-labels
                const errors = super.lint(start, re), rect = new rect_1.BoundingRect(this, start + this.getRelativeIndex(this.length - 1)), rule = 'pipe-like', { lintConfig } = index_1.default, { computeEditInfo, fix } = lintConfig, severities = ['td', 'double'].map(key => lintConfig.getSeverity(rule, key));
                for (const child of this.lastChild.childNodes) {
                    if (child.type === 'text') {
                        const { data } = child;
                        if (data.includes('|')) {
                            const double = data.includes('||'), s = severities[double ? 1 : 0];
                            if (s) {
                                const e = (0, lint_1.generateForChild)(child, rect, rule, 'pipe-in-table', s);
                                if (computeEditInfo || fix) {
                                    if (double) {
                                        const syntax = { caption: '|+', td: '|', th: '!' }[this.subtype];
                                        e.fix = (0, lint_1.fixBy)(e, 'newline', data.replace(/\|\|/gu, `\n${syntax}`));
                                    }
                                    else if (computeEditInfo) {
                                        e.suggestions = [(0, lint_1.fixByPipe)(e.startIndex, data)];
                                    }
                                }
                                errors.push(e);
                            }
                        }
                    }
                }
                return errors;
            }
        }
        /**
         * Check if the cell is at the beginning of a line
         *
         * 是否位于行首
         */
        isIndependent() {
            return this.firstChild.text().startsWith('\n');
        }
        getAttr(key) {
            const value = super.getAttr(key);
            key = (0, string_1.trimLc)(key);
            return (key === 'rowspan' || key === 'colspan' ? parseInt(value) || 1 : value);
        }
        escape() {
            LSP: { // eslint-disable-line no-unused-labels
                super.escape();
                if (this.childNodes[1].toString()) {
                    this.#innerSyntax ||= '{{!}}';
                }
                if (this.#innerSyntax === '|') {
                    this.#innerSyntax = '{{!}}';
                }
            }
        }
        /** @private */
        print() {
            const { childNodes: [syntax, attr, inner] } = this;
            return `<span class="wpb-td">${syntax.print()}${attr.print()}${this.#innerSyntax}${inner.print()}</span>`;
        }
        /** @private */
        json(_, start = this.getAbsoluteIndex()) {
            const json = super.json(undefined, start);
            LSP: { // eslint-disable-line no-unused-labels
                const { rowspan, colspan } = this;
                Object.assign(json, { subtype: this.subtype }, rowspan !== 1 && { rowspan }, colspan !== 1 && { colspan });
                return json;
            }
        }
        /* NOT FOR BROWSER */
        cloneNode() {
            const token = super.cloneNode();
            token.setAttribute('innerSyntax', this.#innerSyntax);
            return token;
        }
        /** @private */
        setAttribute(key, value) {
            if (key === 'innerSyntax') {
                this.#innerSyntax = (value ?? '');
            }
            else {
                super.setAttribute(key, value);
            }
        }
        /** @private */
        setSyntax(syntax, esc) {
            const aliases = { td: '\n|', th: '\n!', caption: '\n|+' };
            super.setSyntax(aliases[syntax] ?? syntax, esc);
        }
        /** 修复\<td\>语法 */
        #correct() {
            if (this.childNodes[1].toString()) {
                this.#innerSyntax ||= '|';
            }
            const { subtype, escape, correction } = this.#getSyntax();
            if (correction) {
                this.setSyntax(subtype, escape);
            }
        }
        /**
         * Move to a new line
         *
         * 改为独占一行
         */
        independence() {
            if (!this.isIndependent()) {
                const { subtype, escape } = this.#getSyntax();
                this.setSyntax(subtype, escape);
            }
        }
        getAttrs() {
            const attr = super.getAttrs();
            if ('rowspan' in attr) {
                attr.rowspan = parseInt(attr.rowspan);
            }
            if ('colspan' in attr) {
                attr.colspan = parseInt(attr.colspan);
            }
            return attr;
        }
        setAttr(keyOrProp, value) {
            if (typeof keyOrProp !== 'string') {
                for (const [key, val] of Object.entries(keyOrProp)) {
                    this.setAttr(key, val);
                }
                return;
            }
            const key = (0, string_1.trimLc)(keyOrProp);
            let v;
            if (typeof value === 'number' && (key === 'rowspan' || key === 'colspan')) {
                v = value === 1 ? false : String(value);
            }
            else {
                v = String(value);
            }
            super.setAttr(key, v);
            if (!this.childNodes[1].toString()) {
                this.#innerSyntax = '';
            }
        }
        /** @private */
        toHtmlInternal(opt) {
            const { subtype, childNodes: [, attr, inner], nextSibling } = this, notEOL = nextSibling?.toString().startsWith('\n') === false, lf = opt?.nowrap ? ' ' : '\n';
            let html = inner.toHtmlInternal(opt).replace(/^[^\S\n]*/u, '');
            if (notEOL) {
                html = html.replace(/(?<=[\S\n])[^\S\n]*$/u, '');
            }
            return `${lf}<${subtype}${attr.toHtmlInternal()}>${subtype === 'caption' ? (0, string_1.newline)(html) : html + (notEOL ? '' : lf)}</${subtype}>`;
        }
    };
    return TdToken = _classThis;
})();
exports.TdToken = TdToken;
/* NOT FOR BROWSER */
/**
 * 创建新的单元格
 * @param inner 内部wikitext
 * @param subtype 单元格类型
 * @param attr 单元格属性
 * @param include 是否嵌入
 * @param config
 */
const createTd = (inner, subtype = 'td', attr = {}, include, config) => {
    const innerToken = typeof inner === 'string' ? index_1.default.parse(inner, include, undefined, config) : inner, 
    // @ts-expect-error abstract class
    token = debug_1.Shadow.run(() => new TdToken('\n|', undefined, config));
    token.setSyntax(subtype);
    token.lastChild.safeReplaceWith(innerToken);
    token.setAttr(attr);
    return token;
};
exports.createTd = createTd;
constants_1.classes['TdToken'] = __filename;