{"version":3,"sources":["jsdelivr-header.js","/npm/structured-clone/index.ts","/npm/@unified-latex/libs/parse-macro-substitutions.ts","/npm/@unified-latex/libs/newcommand.ts","/npm/@unified-latex/libs/list-newcommands.ts","/npm/@unified-latex/libs/expand-macros.ts"],"names":[],"mappings":"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","file":"/npm/@unified-latex/unified-latex-util-macros@1.8.3/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/@unified-latex/unified-latex-util-macros@1.8.3/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","// globalThis polyfill from https://mathiasbynens.be/notes/globalthis\n(function () {\n    if (typeof globalThis === \"object\") {\n        return;\n    }\n    Object.defineProperty(Object.prototype, \"__magic__\", {\n        get: function () {\n            return this;\n        },\n        configurable: true, // This makes it possible to `delete` the getter later.\n    });\n    __magic__.globalThis = __magic__; // lolwat\n    delete Object.prototype.__magic__;\n})();\n\nconst clone =\n    typeof globalThis.structuredClone === \"function\"\n        ? globalThis.structuredClone\n        : (obj: any) => JSON.parse(JSON.stringify(obj));\n\n/**\n * Wrapper around the built-in structured clone. Uses `JSON.parse(JSON.stringify(...))`\n * as a fallback.\n */\nexport function structuredClone<T>(obj: T): T {\n    return clone(obj);\n}\n\ndeclare global {\n    const __magic__: any;\n    interface Object {\n        __magic__?: any;\n    }\n}\n","import { MacroSubstitutionPegParser } from \"@unified-latex/unified-latex-util-pegjs\";\nimport * as Ast from \"@unified-latex/unified-latex-types\";\nimport { match } from \"@unified-latex/unified-latex-util-match\";\nimport { decorateArrayForPegjs } from \"@unified-latex/unified-latex-util-pegjs\";\n\n// The types returned by the grammar\n\nexport interface HashNumber {\n    type: \"hash_number\";\n    number: number;\n}\n\nexport function createMatchers() {\n    return {\n        isHash: (node: Ast.Node) => match.string(node, \"#\"),\n        isNumber: (node: Ast.Node) =>\n            match.string(node) && 0 < +node.content.charAt(0),\n        splitNumber: (node: Ast.String) => {\n            const number = +node.content.charAt(0);\n            if (node.content.length > 1) {\n                return {\n                    number,\n                    rest: { type: \"string\", content: node.content.slice(1) },\n                };\n            }\n            return { number };\n        },\n    };\n}\n\n/**\n * Parse for macro substitutions. For example, in \"\\foo{#1}\", the `#1`\n * is recognized as a `HashNumber` (`{type: \"hash_number\"}`). Double hashes\n * are automatically replaced with their single-hash substitutions.\n *\n * The resulting AST is ready for substitutions to be applied to it.\n */\nexport function parseMacroSubstitutions(\n    ast: Ast.Node[]\n): (Ast.Node | HashNumber)[] {\n    if (!Array.isArray(ast)) {\n        throw new Error(\"You must pass an array of nodes\");\n    }\n    // We need to at functions to `nodes` so that it imitates\n    // a Javascript string. Because we're mutating, make a copy first\n    ast = decorateArrayForPegjs([...ast]);\n    // matchers are passed in via the second argument (the `options` argument)\n    // so they are available from within the Pegjs grammar.\n    return MacroSubstitutionPegParser.parse(ast, createMatchers());\n}\n","import { structuredClone } from \"@unified-latex/structured-clone\";\nimport * as Ast from \"@unified-latex/unified-latex-types\";\nimport { printRaw } from \"@unified-latex/unified-latex-util-print-raw\";\nimport { replaceNode } from \"@unified-latex/unified-latex-util-replace\";\nimport { visit } from \"@unified-latex/unified-latex-util-visit\";\nimport { getNamedArgsContent } from \"@unified-latex/unified-latex-util-arguments\";\nimport { parseMinimal } from \"@unified-latex/unified-latex-util-parse\";\nimport {\n    HashNumber,\n    parseMacroSubstitutions,\n} from \"./parse-macro-substitutions\";\n\nexport const LATEX_NEWCOMMAND = new Set([\n    \"newcommand\",\n    \"renewcommand\",\n    \"providecommand\",\n]);\nexport const XPARSE_NEWCOMMAND = new Set([\n    \"NewDocumentCommand\",\n    \"RenewDocumentCommand\",\n    \"ProvideDocumentCommand\",\n    \"DeclareDocumentCommand\",\n    \"NewExpandableDocumentCommand\",\n    \"RenewExpandableDocumentCommand\",\n    \"ProvideExpandableDocumentCommand\",\n    \"DeclareExpandableDocumentCommand\",\n]);\n\nconst NEWCOMMAND_ARGUMENTS_REG = [\n    \"starred\",\n    \"name\",\n    \"numArgs\",\n    \"default\",\n    \"body\",\n] as const;\nconst NEWCOMMAND_ARGUMENTS_BEAMER = [\n    \"starred\",\n    null,\n    \"name\",\n    \"numArgs\",\n    \"default\",\n    \"body\",\n] as const;\ntype NewcommandNamedArgs = Record<\n    (typeof NEWCOMMAND_ARGUMENTS_REG)[number],\n    Ast.Node[] | null\n>;\n\n/**\n * Get the named arguments for a `\\newcommand` macro.\n */\nfunction getNewcommandNamedArgs(node: Ast.Macro): NewcommandNamedArgs {\n    if (!Array.isArray(node.args)) {\n        throw new Error(\n            `Found a '\\\\newcommand' macro without any arguments \"${JSON.stringify(\n                node\n            )}\"`\n        );\n    }\n    const argNames =\n        node.args.length === NEWCOMMAND_ARGUMENTS_BEAMER.length\n            ? NEWCOMMAND_ARGUMENTS_BEAMER\n            : NEWCOMMAND_ARGUMENTS_REG;\n    return getNamedArgsContent(node, argNames) as NewcommandNamedArgs;\n}\n\n/**\n * Compute the xparse argument signature of the `\\newcommand`/`\\renewcommand`/etc. macro.\n */\nexport function newcommandMacroToSpec(node: Ast.Macro): string {\n    if (LATEX_NEWCOMMAND.has(node.content)) {\n        // The signature is `s m o o m`. The signature of the defined macro\n        // is completely dependent on the optional args. E.g. the macro defined by\n        // \\newcommand*{\\foo}[4][x]{\\bar}\n        // has 5 arguments with the first one optional\n        if (!node.args) {\n            console.warn(\n                String.raw`Found a '\\newcommand' macro that doesn't have any args`,\n                node\n            );\n            return \"\";\n        }\n        const namedArgs = getNewcommandNamedArgs(node);\n        if (namedArgs.numArgs == null) {\n            return \"\";\n        }\n        let numArgsForSig = +printRaw(namedArgs.numArgs);\n        let sigOptionalArg: string[] = [];\n        // `namedArgs.default` determines the default value of the initial optional argument.\n        // If it is present, we need to change the signature.\n        if (namedArgs.default != null) {\n            numArgsForSig--;\n            sigOptionalArg = [`O{${printRaw(namedArgs.default)}}`];\n        }\n        return [\n            ...sigOptionalArg,\n            ...Array.from({ length: numArgsForSig }).map((_) => \"m\"),\n        ].join(\" \");\n    }\n    if (XPARSE_NEWCOMMAND.has(node.content)) {\n        if (!node.args?.length) {\n            console.warn(\n                String.raw`Found a '\\NewDocumentCommand' macro that doesn't have any args`,\n                node\n            );\n            return \"\";\n        }\n        const macroSpec = printRaw(node.args[1]?.content);\n        return macroSpec.trim();\n    }\n\n    return \"\";\n}\n\n/**\n * Trims whitespace and removes the leading `\\` from a macro name.\n */\nfunction normalizeCommandName(str: string): string {\n    str = str.trim();\n    return str.startsWith(\"\\\\\") ? str.slice(1) : str;\n}\n\n/**\n * Get the name of the macro defined with `\\newcommand`/`\\renewcommand`/etc..\n */\nexport function newcommandMacroToName(node: Ast.Macro): string {\n    if (LATEX_NEWCOMMAND.has(node.content)) {\n        // These commands all have a similar structure. E.g.:\n        // \\newcommand{\\foo}[4][x]{\\bar}\n        if (!node.args?.length) {\n            return \"\";\n        }\n        const namedArgs = getNewcommandNamedArgs(node);\n        const definedName = namedArgs.name;\n        if (!definedName) {\n            console.warn(\"Could not find macro name defined in\", node);\n            return \"\";\n        }\n        return normalizeCommandName(printRaw(definedName));\n    }\n    if (XPARSE_NEWCOMMAND.has(node.content)) {\n        if (!node.args?.length) {\n            return \"\";\n        }\n        const definedName = node.args[0]?.content[0];\n        if (!definedName) {\n            console.warn(\"Could not find macro name defined in\", node);\n            return \"\";\n        }\n        return normalizeCommandName(printRaw(node.args[0].content));\n    }\n\n    return \"\";\n}\n\n/**\n * Returns the AST that should be used for substitution. E.g.,\n * `\\newcommand{\\foo}{\\bar{#1}}` would return `\\bar{#1}`.\n */\nexport function newcommandMacroToSubstitutionAst(node: Ast.Macro): Ast.Node[] {\n    if (LATEX_NEWCOMMAND.has(node.content)) {\n        // These commands all have a similar structure. E.g.:\n        // \\newcommand{\\foo}[4][x]{\\bar}\n        if (!node.args?.length) {\n            return [];\n        }\n        const namedArgs = getNewcommandNamedArgs(node);\n        const substitution = namedArgs.body;\n        if (!substitution) {\n            console.warn(\"Could not find macro name defined in\", node);\n            return [];\n        }\n        return substitution;\n    }\n    if (XPARSE_NEWCOMMAND.has(node.content)) {\n        if (!node.args?.length) {\n            return [];\n        }\n        return node.args[2]?.content || [];\n    }\n\n    return [];\n}\n\ntype ExpanderArg = {\n    content: Ast.Node[];\n    /**\n     * The hash numbers that are used in this argument.\n     */\n    hashNumbers: number[];\n};\n\n/**\n * A macro can have arguments `#1`...`#9`. This function returns an array of\n * `Ast.Node` that can be used as the default arguments for a macro if none are provided.\n */\nfunction defaultExpanderArgs(): ExpanderArg[] {\n    return Array.from({ length: 10 }).map((_, i) => ({\n        hashNumbers: [],\n        content: [{ type: \"string\", content: `#${i + 1}` }],\n    }));\n}\n\n/**\n * A factory function. Given a macro definition, creates a function that accepts\n * the macro's arguments and outputs an Ast with the contents substituted (i.e.,\n * it expands the macro).\n */\nexport function createMacroExpander(\n    substitution: Ast.Node[]\n): (macro: Ast.Macro) => Ast.Node[] {\n    const cachedSubstitutionTree = structuredClone(substitution);\n    const hashNumbers = parseHashNumbers(cachedSubstitutionTree);\n\n    return (macro: Ast.Macro) => {\n        // We can short-circuit if there are no substitutions to do.\n        if (hashNumbers.length === 0) {\n            return structuredClone(cachedSubstitutionTree);\n        }\n\n        const cachedSubstitutions: ExpanderArg[] = defaultExpanderArgs().map(\n            (expanderArg, i) => {\n                const number = i + 1;\n                if (!hashNumbers.includes(number)) {\n                    // If we're here, the hash number `i+1` has not been requested for use in a substitution.\n                    // It is safe to return the default (it will never be used)\n                    return expanderArg;\n                }\n                const arg = macro.args?.[i];\n                const defaultArg = arg?._renderInfo?.defaultArg;\n                if (!arg || (isEmptyArg(arg) && defaultArg != null)) {\n                    const content = cachedParse(defaultArg!);\n                    const hashNumbers = parseHashNumbers(content);\n                    return {\n                        content,\n                        hashNumbers,\n                    };\n                }\n                return { content: arg.content, hashNumbers: [] };\n            }\n        );\n\n        // Since we are only allowed `#1`...`#9`, it can take at most 10 iterations to expand in the worst case.\n        // If we expand further than that, there is a circular reference.\n        let numTimesExpanded = 0;\n        while (\n            expandCachedSubstitutions(cachedSubstitutions) &&\n            numTimesExpanded < cachedSubstitutions.length\n        ) {\n            numTimesExpanded++;\n        }\n\n        // By this point we have expanded substitutions as much as possible.\n        // If there are any remaining hash numbers, they come from circular references.\n        for (const expanderArg of cachedSubstitutions) {\n            if (expanderArg.hashNumbers.length > 0) {\n                expanderArg.content = [\n                    // `xparse` seems to use `-No Value-` here.\n                    { type: \"string\", content: `-Circular-` },\n                ];\n            }\n        }\n\n        const retTree = structuredClone(cachedSubstitutionTree);\n        replaceNode(retTree, (node) => {\n            const hashNumOrNode = node as Ast.Node | HashNumber;\n            if (hashNumOrNode.type !== \"hash_number\") {\n                return;\n            }\n            return cachedSubstitutions[hashNumOrNode.number - 1].content;\n        });\n        return retTree;\n    };\n}\n\n/**\n * Is the argument empty? This occurs when optional arguments are not provided.\n */\nfunction isEmptyArg(arg: Ast.Argument) {\n    return (\n        arg.openMark === \"\" && arg.closeMark === \"\" && arg.content.length === 0\n    );\n}\n\n/**\n * Parses `tree` for hash numbers (e.g. `#1` or `##`, etc.). Hash numbers are replaced\n * with a `{type: \"hash_number\"}` node. This mutates `tree`.\n * @param tree\n * @returns A list containing all hash numbers that were found.\n */\nfunction parseHashNumbers(tree: Ast.Node[]) {\n    let hashNumbers = new Set<number>();\n    visit(\n        tree,\n        (nodes) => {\n            const parsed = parseMacroSubstitutions(nodes);\n            for (const node of parsed) {\n                if (node.type === \"hash_number\") {\n                    hashNumbers.add(node.number);\n                }\n            }\n            // Replace the nodes with the parsed version\n            nodes.length = 0;\n            nodes.push(...(parsed as Ast.Node[]));\n        },\n        {\n            includeArrays: true,\n            test: Array.isArray,\n        }\n    );\n    return Array.from(hashNumbers);\n}\n\n/**\n * Get a list of all hash numbers referenced within `tree`.\n */\nfunction hashNumbersReferenced(tree: Ast.Node[]) {\n    let hashNumbers = new Set<number>();\n    visit(tree, (node) => {\n        const n = node as any as Ast.Node | HashNumber;\n        if (n.type === \"hash_number\") {\n            hashNumbers.add(n.number);\n        }\n    });\n    return Array.from(hashNumbers);\n}\n\nconst parseCache = new Map<string, Ast.Node[]>();\n/**\n * Parse `source` and cache the result. Multiple on the same string will not reparse.\n */\nfunction cachedParse(source: string): Ast.Node[] {\n    const cached = parseCache.get(source);\n    if (cached) {\n        return structuredClone(cached);\n    }\n    const parsed = parseMinimal(source).content;\n    parseCache.set(source, structuredClone(parsed));\n    return parsed;\n}\n\n/**\n * Perform one step in the expansion of `expanderArgs`. The result may still have `type: \"hash_number\"` nodes.\n */\nfunction expandCachedSubstitutions(expanderArgs: ExpanderArg[]) {\n    let didExpand = false;\n    for (const expanderArg of expanderArgs) {\n        if (expanderArg.hashNumbers.length === 0) {\n            continue;\n        }\n        replaceNode(expanderArg.content, (node) => {\n            const hashNumOrNode = node as Ast.Node | HashNumber;\n            if (hashNumOrNode.type !== \"hash_number\") {\n                return;\n            }\n            didExpand = true;\n            return expanderArgs[hashNumOrNode.number - 1].content;\n        });\n        expanderArg.hashNumbers = hashNumbersReferenced(expanderArg.content);\n    }\n    return didExpand;\n}\n","import * as Ast from \"@unified-latex/unified-latex-types\";\nimport { match } from \"@unified-latex/unified-latex-util-match\";\nimport { visit } from \"@unified-latex/unified-latex-util-visit\";\nimport {\n    LATEX_NEWCOMMAND,\n    newcommandMacroToName,\n    newcommandMacroToSpec,\n    newcommandMacroToSubstitutionAst,\n    XPARSE_NEWCOMMAND,\n} from \"./newcommand\";\n\ntype NewCommandSpec = {\n    name: string;\n    signature: string;\n    body: Ast.Node[];\n    definition: Ast.Macro;\n};\n\nexport const newcommandMatcher = match.createMacroMatcher([\n    ...LATEX_NEWCOMMAND,\n    ...XPARSE_NEWCOMMAND,\n]);\n\n/**\n * List all new commands defined in `tree`. This lists commands defined LaTeX-style with\n * `\\newcommand` etc., and defined with xparse-style `\\NewDocumentCommand` etc. It does\n * **not** find commands defined via `\\def` (it is too difficult to parse the argument\n * signature of commands defined with `\\def`).\n */\nexport function listNewcommands(tree: Ast.Ast): NewCommandSpec[] {\n    const ret: NewCommandSpec[] = [];\n    visit(\n        tree,\n        (node) => {\n            const name = newcommandMacroToName(node);\n            const signature = newcommandMacroToSpec(node);\n            const body = newcommandMacroToSubstitutionAst(node);\n\n            ret.push({ name, signature, body, definition: node });\n        },\n        { test: newcommandMatcher }\n    );\n    return ret;\n}\n","import * as Ast from \"@unified-latex/unified-latex-types\";\nimport { match } from \"@unified-latex/unified-latex-util-match\";\nimport { replaceNode } from \"@unified-latex/unified-latex-util-replace\";\nimport { newcommandMatcher } from \"./list-newcommands\";\nimport { createMacroExpander } from \"./newcommand\";\n\n/**\n * Expands macros in `ast` as specified by `macros`.\n * Each macro in `macros` should provide the substitution AST (i.e., the AST with the #1, etc.\n * in it). This function assumes that the appropriate arguments have already been attached\n * to each macro specified. If the macro doesn't have it's arguments attached, its\n * contents will be wholesale replaced with its substitution AST.\n */\nexport function expandMacros(\n    tree: Ast.Ast,\n    macros: { name: string; body: Ast.Node[] }[]\n) {\n    const expanderCache = new Map(\n        macros.map((spec) => [spec.name, createMacroExpander(spec.body)])\n    );\n    replaceNode(tree, (node) => {\n        if (!match.anyMacro(node)) {\n            return;\n        }\n        const macroName = node.content;\n        const expander = expanderCache.get(macroName);\n        if (!expander) {\n            return;\n        }\n\n        return expander(node);\n    });\n}\n\n/**\n * Expands macros in `ast` as specified by `macros`, but do not expand any macros\n * that appear in the context of a macro definition. For example, expanding `\\foo` to `X` in\n * ```\n * \\newcommand{\\foo}{Y}\n * \\foo\n * ```\n * would result in\n * ```\n * \\newcommand{\\foo}{Y}\n * X\n * ```\n * If `expandMacros(...)` were used, macros would be expanded in all contexts and the result\n * would be\n * ```\n * \\newcommand{X}{Y}\n * X\n * ```\n *\n * Each macro in `macros` should provide the substitution AST (i.e., the AST with the #1, etc.\n * in it). This function assumes that the appropriate arguments have already been attached\n * to each macro specified. If the macro doesn't have it's arguments attached, its\n * contents will be wholesale replaced with its substitution AST.\n */\nexport function expandMacrosExcludingDefinitions(\n    tree: Ast.Ast,\n    macros: { name: string; body: Ast.Node[] }[]\n) {\n    const expanderCache = new Map(\n        macros.map((spec) => [spec.name, createMacroExpander(spec.body)])\n    );\n    replaceNode(tree, (node, info) => {\n        if (!match.anyMacro(node)) {\n            return;\n        }\n        const macroName = node.content;\n        const expander = expanderCache.get(macroName);\n        if (!expander) {\n            return;\n        }\n\n        // We don't want to substitute if we are a child of a newcommand\n        if (info.parents.some((n) => newcommandMatcher(n))) {\n            return;\n        }\n\n        return expander(node);\n    });\n}\n"]}