UNPKG

mdx-prompt

Version:

Use MDX to render high quality LLM prompts

1,939 lines (1,732 loc) 2.02 MB
import fs from 'node:fs'; import React from 'react'; import require$$0, { jsx as jsx$1, jsxs } from 'react/jsx-runtime'; import require$$1 from 'react/jsx-dev-runtime'; import { renderToStaticMarkup } from 'react-dom/server'; function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var jsxRuntime = {}; var hasRequiredJsxRuntime; function requireJsxRuntime () { if (hasRequiredJsxRuntime) return jsxRuntime; hasRequiredJsxRuntime = 1; /** * Copyright (c) HashiCorp, Inc. * SPDX-License-Identifier: MPL-2.0 */ Object.defineProperty(jsxRuntime, "__esModule", { value: true }); /** * Allow jsx-runtime to be successfully imported from either React 17 or React 18. * * Inspired by the approach here: https://github.com/contentlayerdev/contentlayer/blob/main/packages/next-contentlayer/src/hooks/jsx-runtime.cjs */ if (process.env.NODE_ENV === 'production') { jsxRuntime.jsxRuntime = require$$0; } else { jsxRuntime.jsxRuntime = require$$1; } return jsxRuntime; } var jsxRuntimeExports = requireJsxRuntime(); /** * @typedef {import('unist').Node} Node * @typedef {import('unist').Point} Point * @typedef {import('unist').Position} Position */ /** * @typedef NodeLike * @property {string} type * @property {PositionLike | null | undefined} [position] * * @typedef PointLike * @property {number | null | undefined} [line] * @property {number | null | undefined} [column] * @property {number | null | undefined} [offset] * * @typedef PositionLike * @property {PointLike | null | undefined} [start] * @property {PointLike | null | undefined} [end] */ /** * Serialize the positional info of a point, position (start and end points), * or node. * * @param {Node | NodeLike | Point | PointLike | Position | PositionLike | null | undefined} [value] * Node, position, or point. * @returns {string} * Pretty printed positional info of a node (`string`). * * In the format of a range `ls:cs-le:ce` (when given `node` or `position`) * or a point `l:c` (when given `point`), where `l` stands for line, `c` for * column, `s` for `start`, and `e` for end. * An empty string (`''`) is returned if the given value is neither `node`, * `position`, nor `point`. */ function stringifyPosition(value) { // Nothing. if (!value || typeof value !== 'object') { return '' } // Node. if ('position' in value || 'type' in value) { return position$2(value.position) } // Position. if ('start' in value || 'end' in value) { return position$2(value) } // Point. if ('line' in value || 'column' in value) { return point$3(value) } // ? return '' } /** * @param {Point | PointLike | null | undefined} point * @returns {string} */ function point$3(point) { return index(point && point.line) + ':' + index(point && point.column) } /** * @param {Position | PositionLike | null | undefined} pos * @returns {string} */ function position$2(pos) { return point$3(pos && pos.start) + '-' + point$3(pos && pos.end) } /** * @param {number | null | undefined} value * @returns {number} */ function index(value) { return value && typeof value === 'number' ? value : 1 } /** * @typedef {import('unist').Node} Node * @typedef {import('unist').Point} Point * @typedef {import('unist').Position} Position */ /** * Message. */ class VFileMessage extends Error { /** * Create a message for `reason`. * * > 🪦 **Note**: also has obsolete signatures. * * @overload * @param {string} reason * @param {Options | null | undefined} [options] * @returns * * @overload * @param {string} reason * @param {Node | NodeLike | null | undefined} parent * @param {string | null | undefined} [origin] * @returns * * @overload * @param {string} reason * @param {Point | Position | null | undefined} place * @param {string | null | undefined} [origin] * @returns * * @overload * @param {string} reason * @param {string | null | undefined} [origin] * @returns * * @overload * @param {Error | VFileMessage} cause * @param {Node | NodeLike | null | undefined} parent * @param {string | null | undefined} [origin] * @returns * * @overload * @param {Error | VFileMessage} cause * @param {Point | Position | null | undefined} place * @param {string | null | undefined} [origin] * @returns * * @overload * @param {Error | VFileMessage} cause * @param {string | null | undefined} [origin] * @returns * * @param {Error | VFileMessage | string} causeOrReason * Reason for message, should use markdown. * @param {Node | NodeLike | Options | Point | Position | string | null | undefined} [optionsOrParentOrPlace] * Configuration (optional). * @param {string | null | undefined} [origin] * Place in code where the message originates (example: * `'my-package:my-rule'` or `'my-rule'`). * @returns * Instance of `VFileMessage`. */ // eslint-disable-next-line complexity constructor(causeOrReason, optionsOrParentOrPlace, origin) { super(); if (typeof optionsOrParentOrPlace === 'string') { origin = optionsOrParentOrPlace; optionsOrParentOrPlace = undefined; } /** @type {string} */ let reason = ''; /** @type {Options} */ let options = {}; let legacyCause = false; if (optionsOrParentOrPlace) { // Point. if ( 'line' in optionsOrParentOrPlace && 'column' in optionsOrParentOrPlace ) { options = {place: optionsOrParentOrPlace}; } // Position. else if ( 'start' in optionsOrParentOrPlace && 'end' in optionsOrParentOrPlace ) { options = {place: optionsOrParentOrPlace}; } // Node. else if ('type' in optionsOrParentOrPlace) { options = { ancestors: [optionsOrParentOrPlace], place: optionsOrParentOrPlace.position }; } // Options. else { options = {...optionsOrParentOrPlace}; } } if (typeof causeOrReason === 'string') { reason = causeOrReason; } // Error. else if (!options.cause && causeOrReason) { legacyCause = true; reason = causeOrReason.message; options.cause = causeOrReason; } if (!options.ruleId && !options.source && typeof origin === 'string') { const index = origin.indexOf(':'); if (index === -1) { options.ruleId = origin; } else { options.source = origin.slice(0, index); options.ruleId = origin.slice(index + 1); } } if (!options.place && options.ancestors && options.ancestors) { const parent = options.ancestors[options.ancestors.length - 1]; if (parent) { options.place = parent.position; } } const start = options.place && 'start' in options.place ? options.place.start : options.place; /* eslint-disable no-unused-expressions */ /** * Stack of ancestor nodes surrounding the message. * * @type {Array<Node> | undefined} */ this.ancestors = options.ancestors || undefined; /** * Original error cause of the message. * * @type {Error | undefined} */ this.cause = options.cause || undefined; /** * Starting column of message. * * @type {number | undefined} */ this.column = start ? start.column : undefined; /** * State of problem. * * * `true` — error, file not usable * * `false` — warning, change may be needed * * `undefined` — change likely not needed * * @type {boolean | null | undefined} */ this.fatal = undefined; /** * Path of a file (used throughout the `VFile` ecosystem). * * @type {string | undefined} */ this.file; // Field from `Error`. /** * Reason for message. * * @type {string} */ this.message = reason; /** * Starting line of error. * * @type {number | undefined} */ this.line = start ? start.line : undefined; // Field from `Error`. /** * Serialized positional info of message. * * On normal errors, this would be something like `ParseError`, buit in * `VFile` messages we use this space to show where an error happened. */ this.name = stringifyPosition(options.place) || '1:1'; /** * Place of message. * * @type {Point | Position | undefined} */ this.place = options.place || undefined; /** * Reason for message, should use markdown. * * @type {string} */ this.reason = this.message; /** * Category of message (example: `'my-rule'`). * * @type {string | undefined} */ this.ruleId = options.ruleId || undefined; /** * Namespace of message (example: `'my-package'`). * * @type {string | undefined} */ this.source = options.source || undefined; // Field from `Error`. /** * Stack of message. * * This is used by normal errors to show where something happened in * programming code, irrelevant for `VFile` messages, * * @type {string} */ this.stack = legacyCause && options.cause && typeof options.cause.stack === 'string' ? options.cause.stack : ''; // The following fields are “well known”. // Not standard. // Feel free to add other non-standard fields to your messages. /** * Specify the source value that’s being reported, which is deemed * incorrect. * * @type {string | undefined} */ this.actual; /** * Suggest acceptable values that can be used instead of `actual`. * * @type {Array<string> | undefined} */ this.expected; /** * Long form description of the message (you should use markdown). * * @type {string | undefined} */ this.note; /** * Link to docs for the message. * * > 👉 **Note**: this must be an absolute URL that can be passed as `x` * > to `new URL(x)`. * * @type {string | undefined} */ this.url; /* eslint-enable no-unused-expressions */ } } VFileMessage.prototype.file = ''; VFileMessage.prototype.name = ''; VFileMessage.prototype.reason = ''; VFileMessage.prototype.message = ''; VFileMessage.prototype.stack = ''; VFileMessage.prototype.column = undefined; VFileMessage.prototype.line = undefined; VFileMessage.prototype.ancestors = undefined; VFileMessage.prototype.cause = undefined; VFileMessage.prototype.fatal = undefined; VFileMessage.prototype.place = undefined; VFileMessage.prototype.ruleId = undefined; VFileMessage.prototype.source = undefined; // A derivative work based on: // <https://github.com/browserify/path-browserify>. // Which is licensed: // // MIT License // // Copyright (c) 2013 James Halliday // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // A derivative work based on: // // Parts of that are extracted from Node’s internal `path` module: // <https://github.com/nodejs/node/blob/master/lib/path.js>. // Which is licensed: // // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. const minpath = {basename, dirname, extname, join, sep: '/'}; /* eslint-disable max-depth, complexity */ /** * Get the basename from a path. * * @param {string} path * File path. * @param {string | null | undefined} [extname] * Extension to strip. * @returns {string} * Stem or basename. */ function basename(path, extname) { if (extname !== undefined && typeof extname !== 'string') { throw new TypeError('"ext" argument must be a string') } assertPath$1(path); let start = 0; let end = -1; let index = path.length; /** @type {boolean | undefined} */ let seenNonSlash; if ( extname === undefined || extname.length === 0 || extname.length > path.length ) { while (index--) { if (path.codePointAt(index) === 47 /* `/` */) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now. if (seenNonSlash) { start = index + 1; break } } else if (end < 0) { // We saw the first non-path separator, mark this as the end of our // path component. seenNonSlash = true; end = index + 1; } } return end < 0 ? '' : path.slice(start, end) } if (extname === path) { return '' } let firstNonSlashEnd = -1; let extnameIndex = extname.length - 1; while (index--) { if (path.codePointAt(index) === 47 /* `/` */) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now. if (seenNonSlash) { start = index + 1; break } } else { if (firstNonSlashEnd < 0) { // We saw the first non-path separator, remember this index in case // we need it if the extension ends up not matching. seenNonSlash = true; firstNonSlashEnd = index + 1; } if (extnameIndex > -1) { // Try to match the explicit extension. if (path.codePointAt(index) === extname.codePointAt(extnameIndex--)) { if (extnameIndex < 0) { // We matched the extension, so mark this as the end of our path // component end = index; } } else { // Extension does not match, so our result is the entire path // component extnameIndex = -1; end = firstNonSlashEnd; } } } } if (start === end) { end = firstNonSlashEnd; } else if (end < 0) { end = path.length; } return path.slice(start, end) } /** * Get the dirname from a path. * * @param {string} path * File path. * @returns {string} * File path. */ function dirname(path) { assertPath$1(path); if (path.length === 0) { return '.' } let end = -1; let index = path.length; /** @type {boolean | undefined} */ let unmatchedSlash; // Prefix `--` is important to not run on `0`. while (--index) { if (path.codePointAt(index) === 47 /* `/` */) { if (unmatchedSlash) { end = index; break } } else if (!unmatchedSlash) { // We saw the first non-path separator unmatchedSlash = true; } } return end < 0 ? path.codePointAt(0) === 47 /* `/` */ ? '/' : '.' : end === 1 && path.codePointAt(0) === 47 /* `/` */ ? '//' : path.slice(0, end) } /** * Get an extname from a path. * * @param {string} path * File path. * @returns {string} * Extname. */ function extname(path) { assertPath$1(path); let index = path.length; let end = -1; let startPart = 0; let startDot = -1; // Track the state of characters (if any) we see before our first dot and // after any path separator we find. let preDotState = 0; /** @type {boolean | undefined} */ let unmatchedSlash; while (index--) { const code = path.codePointAt(index); if (code === 47 /* `/` */) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now. if (unmatchedSlash) { startPart = index + 1; break } continue } if (end < 0) { // We saw the first non-path separator, mark this as the end of our // extension. unmatchedSlash = true; end = index + 1; } if (code === 46 /* `.` */) { // If this is our first dot, mark it as the start of our extension. if (startDot < 0) { startDot = index; } else if (preDotState !== 1) { preDotState = 1; } } else if (startDot > -1) { // We saw a non-dot and non-path separator before our dot, so we should // have a good chance at having a non-empty extension. preDotState = -1; } } if ( startDot < 0 || end < 0 || // We saw a non-dot character immediately before the dot. preDotState === 0 || // The (right-most) trimmed path component is exactly `..`. (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) ) { return '' } return path.slice(startDot, end) } /** * Join segments from a path. * * @param {Array<string>} segments * Path segments. * @returns {string} * File path. */ function join(...segments) { let index = -1; /** @type {string | undefined} */ let joined; while (++index < segments.length) { assertPath$1(segments[index]); if (segments[index]) { joined = joined === undefined ? segments[index] : joined + '/' + segments[index]; } } return joined === undefined ? '.' : normalize$1(joined) } /** * Normalize a basic file path. * * @param {string} path * File path. * @returns {string} * File path. */ // Note: `normalize` is not exposed as `path.normalize`, so some code is // manually removed from it. function normalize$1(path) { assertPath$1(path); const absolute = path.codePointAt(0) === 47; /* `/` */ // Normalize the path according to POSIX rules. let value = normalizeString(path, !absolute); if (value.length === 0 && !absolute) { value = '.'; } if (value.length > 0 && path.codePointAt(path.length - 1) === 47 /* / */) { value += '/'; } return absolute ? '/' + value : value } /** * Resolve `.` and `..` elements in a path with directory names. * * @param {string} path * File path. * @param {boolean} allowAboveRoot * Whether `..` can move above root. * @returns {string} * File path. */ function normalizeString(path, allowAboveRoot) { let result = ''; let lastSegmentLength = 0; let lastSlash = -1; let dots = 0; let index = -1; /** @type {number | undefined} */ let code; /** @type {number} */ let lastSlashIndex; while (++index <= path.length) { if (index < path.length) { code = path.codePointAt(index); } else if (code === 47 /* `/` */) { break } else { code = 47; /* `/` */ } if (code === 47 /* `/` */) { if (lastSlash === index - 1 || dots === 1) ; else if (lastSlash !== index - 1 && dots === 2) { if ( result.length < 2 || lastSegmentLength !== 2 || result.codePointAt(result.length - 1) !== 46 /* `.` */ || result.codePointAt(result.length - 2) !== 46 /* `.` */ ) { if (result.length > 2) { lastSlashIndex = result.lastIndexOf('/'); if (lastSlashIndex !== result.length - 1) { if (lastSlashIndex < 0) { result = ''; lastSegmentLength = 0; } else { result = result.slice(0, lastSlashIndex); lastSegmentLength = result.length - 1 - result.lastIndexOf('/'); } lastSlash = index; dots = 0; continue } } else if (result.length > 0) { result = ''; lastSegmentLength = 0; lastSlash = index; dots = 0; continue } } if (allowAboveRoot) { result = result.length > 0 ? result + '/..' : '..'; lastSegmentLength = 2; } } else { if (result.length > 0) { result += '/' + path.slice(lastSlash + 1, index); } else { result = path.slice(lastSlash + 1, index); } lastSegmentLength = index - lastSlash - 1; } lastSlash = index; dots = 0; } else if (code === 46 /* `.` */ && dots > -1) { dots++; } else { dots = -1; } } return result } /** * Make sure `path` is a string. * * @param {string} path * File path. * @returns {asserts path is string} * Nothing. */ function assertPath$1(path) { if (typeof path !== 'string') { throw new TypeError( 'Path must be a string. Received ' + JSON.stringify(path) ) } } /* eslint-enable max-depth, complexity */ // Somewhat based on: // <https://github.com/defunctzombie/node-process/blob/master/browser.js>. // But I don’t think one tiny line of code can be copyrighted. 😅 const minproc = {cwd}; function cwd() { return '/' } /** * Checks if a value has the shape of a WHATWG URL object. * * Using a symbol or instanceof would not be able to recognize URL objects * coming from other implementations (e.g. in Electron), so instead we are * checking some well known properties for a lack of a better test. * * We use `href` and `protocol` as they are the only properties that are * easy to retrieve and calculate due to the lazy nature of the getters. * * We check for auth attribute to distinguish legacy url instance with * WHATWG URL instance. * * @param {unknown} fileUrlOrPath * File path or URL. * @returns {fileUrlOrPath is URL} * Whether it’s a URL. */ // From: <https://github.com/nodejs/node/blob/6a3403c/lib/internal/url.js#L720> function isUrl(fileUrlOrPath) { return Boolean( fileUrlOrPath !== null && typeof fileUrlOrPath === 'object' && 'href' in fileUrlOrPath && fileUrlOrPath.href && 'protocol' in fileUrlOrPath && fileUrlOrPath.protocol && // @ts-expect-error: indexing is fine. fileUrlOrPath.auth === undefined ) } // See: <https://github.com/nodejs/node/blob/6a3403c/lib/internal/url.js> /** * @param {URL | string} path * File URL. * @returns {string} * File URL. */ function urlToPath(path) { if (typeof path === 'string') { path = new URL(path); } else if (!isUrl(path)) { /** @type {NodeJS.ErrnoException} */ const error = new TypeError( 'The "path" argument must be of type string or an instance of URL. Received `' + path + '`' ); error.code = 'ERR_INVALID_ARG_TYPE'; throw error } if (path.protocol !== 'file:') { /** @type {NodeJS.ErrnoException} */ const error = new TypeError('The URL must be of scheme file'); error.code = 'ERR_INVALID_URL_SCHEME'; throw error } return getPathFromURLPosix(path) } /** * Get a path from a POSIX URL. * * @param {URL} url * URL. * @returns {string} * File path. */ function getPathFromURLPosix(url) { if (url.hostname !== '') { /** @type {NodeJS.ErrnoException} */ const error = new TypeError( 'File URL host must be "localhost" or empty on darwin' ); error.code = 'ERR_INVALID_FILE_URL_HOST'; throw error } const pathname = url.pathname; let index = -1; while (++index < pathname.length) { if ( pathname.codePointAt(index) === 37 /* `%` */ && pathname.codePointAt(index + 1) === 50 /* `2` */ ) { const third = pathname.codePointAt(index + 2); if (third === 70 /* `F` */ || third === 102 /* `f` */) { /** @type {NodeJS.ErrnoException} */ const error = new TypeError( 'File URL path must not include encoded / characters' ); error.code = 'ERR_INVALID_FILE_URL_PATH'; throw error } } } return decodeURIComponent(pathname) } /** * @import {Node, Point, Position} from 'unist' * @import {Options as MessageOptions} from 'vfile-message' * @import {Compatible, Data, Map, Options, Value} from 'vfile' */ /** * Order of setting (least specific to most), we need this because otherwise * `{stem: 'a', path: '~/b.js'}` would throw, as a path is needed before a * stem can be set. */ const order = /** @type {const} */ ([ 'history', 'path', 'basename', 'stem', 'extname', 'dirname' ]); class VFile { /** * Create a new virtual file. * * `options` is treated as: * * * `string` or `Uint8Array` — `{value: options}` * * `URL` — `{path: options}` * * `VFile` — shallow copies its data over to the new file * * `object` — all fields are shallow copied over to the new file * * Path related fields are set in the following order (least specific to * most specific): `history`, `path`, `basename`, `stem`, `extname`, * `dirname`. * * You cannot set `dirname` or `extname` without setting either `history`, * `path`, `basename`, or `stem` too. * * @param {Compatible | null | undefined} [value] * File value. * @returns * New instance. */ constructor(value) { /** @type {Options | VFile} */ let options; if (!value) { options = {}; } else if (isUrl(value)) { options = {path: value}; } else if (typeof value === 'string' || isUint8Array$1(value)) { options = {value}; } else { options = value; } /* eslint-disable no-unused-expressions */ /** * Base of `path` (default: `process.cwd()` or `'/'` in browsers). * * @type {string} */ // Prevent calling `cwd` (which could be expensive) if it’s not needed; // the empty string will be overridden in the next block. this.cwd = 'cwd' in options ? '' : minproc.cwd(); /** * Place to store custom info (default: `{}`). * * It’s OK to store custom data directly on the file but moving it to * `data` is recommended. * * @type {Data} */ this.data = {}; /** * List of file paths the file moved between. * * The first is the original path and the last is the current path. * * @type {Array<string>} */ this.history = []; /** * List of messages associated with the file. * * @type {Array<VFileMessage>} */ this.messages = []; /** * Raw value. * * @type {Value} */ this.value; // The below are non-standard, they are “well-known”. // As in, used in several tools. /** * Source map. * * This type is equivalent to the `RawSourceMap` type from the `source-map` * module. * * @type {Map | null | undefined} */ this.map; /** * Custom, non-string, compiled, representation. * * This is used by unified to store non-string results. * One example is when turning markdown into React nodes. * * @type {unknown} */ this.result; /** * Whether a file was saved to disk. * * This is used by vfile reporters. * * @type {boolean} */ this.stored; /* eslint-enable no-unused-expressions */ // Set path related properties in the correct order. let index = -1; while (++index < order.length) { const field = order[index]; // Note: we specifically use `in` instead of `hasOwnProperty` to accept // `vfile`s too. if ( field in options && options[field] !== undefined && options[field] !== null ) { // @ts-expect-error: TS doesn’t understand basic reality. this[field] = field === 'history' ? [...options[field]] : options[field]; } } /** @type {string} */ let field; // Set non-path related properties. for (field in options) { // @ts-expect-error: fine to set other things. if (!order.includes(field)) { // @ts-expect-error: fine to set other things. this[field] = options[field]; } } } /** * Get the basename (including extname) (example: `'index.min.js'`). * * @returns {string | undefined} * Basename. */ get basename() { return typeof this.path === 'string' ? minpath.basename(this.path) : undefined } /** * Set basename (including extname) (`'index.min.js'`). * * Cannot contain path separators (`'/'` on unix, macOS, and browsers, `'\'` * on windows). * Cannot be nullified (use `file.path = file.dirname` instead). * * @param {string} basename * Basename. * @returns {undefined} * Nothing. */ set basename(basename) { assertNonEmpty(basename, 'basename'); assertPart(basename, 'basename'); this.path = minpath.join(this.dirname || '', basename); } /** * Get the parent path (example: `'~'`). * * @returns {string | undefined} * Dirname. */ get dirname() { return typeof this.path === 'string' ? minpath.dirname(this.path) : undefined } /** * Set the parent path (example: `'~'`). * * Cannot be set if there’s no `path` yet. * * @param {string | undefined} dirname * Dirname. * @returns {undefined} * Nothing. */ set dirname(dirname) { assertPath(this.basename, 'dirname'); this.path = minpath.join(dirname || '', this.basename); } /** * Get the extname (including dot) (example: `'.js'`). * * @returns {string | undefined} * Extname. */ get extname() { return typeof this.path === 'string' ? minpath.extname(this.path) : undefined } /** * Set the extname (including dot) (example: `'.js'`). * * Cannot contain path separators (`'/'` on unix, macOS, and browsers, `'\'` * on windows). * Cannot be set if there’s no `path` yet. * * @param {string | undefined} extname * Extname. * @returns {undefined} * Nothing. */ set extname(extname) { assertPart(extname, 'extname'); assertPath(this.dirname, 'extname'); if (extname) { if (extname.codePointAt(0) !== 46 /* `.` */) { throw new Error('`extname` must start with `.`') } if (extname.includes('.', 1)) { throw new Error('`extname` cannot contain multiple dots') } } this.path = minpath.join(this.dirname, this.stem + (extname || '')); } /** * Get the full path (example: `'~/index.min.js'`). * * @returns {string} * Path. */ get path() { return this.history[this.history.length - 1] } /** * Set the full path (example: `'~/index.min.js'`). * * Cannot be nullified. * You can set a file URL (a `URL` object with a `file:` protocol) which will * be turned into a path with `url.fileURLToPath`. * * @param {URL | string} path * Path. * @returns {undefined} * Nothing. */ set path(path) { if (isUrl(path)) { path = urlToPath(path); } assertNonEmpty(path, 'path'); if (this.path !== path) { this.history.push(path); } } /** * Get the stem (basename w/o extname) (example: `'index.min'`). * * @returns {string | undefined} * Stem. */ get stem() { return typeof this.path === 'string' ? minpath.basename(this.path, this.extname) : undefined } /** * Set the stem (basename w/o extname) (example: `'index.min'`). * * Cannot contain path separators (`'/'` on unix, macOS, and browsers, `'\'` * on windows). * Cannot be nullified (use `file.path = file.dirname` instead). * * @param {string} stem * Stem. * @returns {undefined} * Nothing. */ set stem(stem) { assertNonEmpty(stem, 'stem'); assertPart(stem, 'stem'); this.path = minpath.join(this.dirname || '', stem + (this.extname || '')); } // Normal prototypal methods. /** * Create a fatal message for `reason` associated with the file. * * The `fatal` field of the message is set to `true` (error; file not usable) * and the `file` field is set to the current file path. * The message is added to the `messages` field on `file`. * * > 🪦 **Note**: also has obsolete signatures. * * @overload * @param {string} reason * @param {MessageOptions | null | undefined} [options] * @returns {never} * * @overload * @param {string} reason * @param {Node | NodeLike | null | undefined} parent * @param {string | null | undefined} [origin] * @returns {never} * * @overload * @param {string} reason * @param {Point | Position | null | undefined} place * @param {string | null | undefined} [origin] * @returns {never} * * @overload * @param {string} reason * @param {string | null | undefined} [origin] * @returns {never} * * @overload * @param {Error | VFileMessage} cause * @param {Node | NodeLike | null | undefined} parent * @param {string | null | undefined} [origin] * @returns {never} * * @overload * @param {Error | VFileMessage} cause * @param {Point | Position | null | undefined} place * @param {string | null | undefined} [origin] * @returns {never} * * @overload * @param {Error | VFileMessage} cause * @param {string | null | undefined} [origin] * @returns {never} * * @param {Error | VFileMessage | string} causeOrReason * Reason for message, should use markdown. * @param {Node | NodeLike | MessageOptions | Point | Position | string | null | undefined} [optionsOrParentOrPlace] * Configuration (optional). * @param {string | null | undefined} [origin] * Place in code where the message originates (example: * `'my-package:my-rule'` or `'my-rule'`). * @returns {never} * Never. * @throws {VFileMessage} * Message. */ fail(causeOrReason, optionsOrParentOrPlace, origin) { // @ts-expect-error: the overloads are fine. const message = this.message(causeOrReason, optionsOrParentOrPlace, origin); message.fatal = true; throw message } /** * Create an info message for `reason` associated with the file. * * The `fatal` field of the message is set to `undefined` (info; change * likely not needed) and the `file` field is set to the current file path. * The message is added to the `messages` field on `file`. * * > 🪦 **Note**: also has obsolete signatures. * * @overload * @param {string} reason * @param {MessageOptions | null | undefined} [options] * @returns {VFileMessage} * * @overload * @param {string} reason * @param {Node | NodeLike | null | undefined} parent * @param {string | null | undefined} [origin] * @returns {VFileMessage} * * @overload * @param {string} reason * @param {Point | Position | null | undefined} place * @param {string | null | undefined} [origin] * @returns {VFileMessage} * * @overload * @param {string} reason * @param {string | null | undefined} [origin] * @returns {VFileMessage} * * @overload * @param {Error | VFileMessage} cause * @param {Node | NodeLike | null | undefined} parent * @param {string | null | undefined} [origin] * @returns {VFileMessage} * * @overload * @param {Error | VFileMessage} cause * @param {Point | Position | null | undefined} place * @param {string | null | undefined} [origin] * @returns {VFileMessage} * * @overload * @param {Error | VFileMessage} cause * @param {string | null | undefined} [origin] * @returns {VFileMessage} * * @param {Error | VFileMessage | string} causeOrReason * Reason for message, should use markdown. * @param {Node | NodeLike | MessageOptions | Point | Position | string | null | undefined} [optionsOrParentOrPlace] * Configuration (optional). * @param {string | null | undefined} [origin] * Place in code where the message originates (example: * `'my-package:my-rule'` or `'my-rule'`). * @returns {VFileMessage} * Message. */ info(causeOrReason, optionsOrParentOrPlace, origin) { // @ts-expect-error: the overloads are fine. const message = this.message(causeOrReason, optionsOrParentOrPlace, origin); message.fatal = undefined; return message } /** * Create a message for `reason` associated with the file. * * The `fatal` field of the message is set to `false` (warning; change may be * needed) and the `file` field is set to the current file path. * The message is added to the `messages` field on `file`. * * > 🪦 **Note**: also has obsolete signatures. * * @overload * @param {string} reason * @param {MessageOptions | null | undefined} [options] * @returns {VFileMessage} * * @overload * @param {string} reason * @param {Node | NodeLike | null | undefined} parent * @param {string | null | undefined} [origin] * @returns {VFileMessage} * * @overload * @param {string} reason * @param {Point | Position | null | undefined} place * @param {string | null | undefined} [origin] * @returns {VFileMessage} * * @overload * @param {string} reason * @param {string | null | undefined} [origin] * @returns {VFileMessage} * * @overload * @param {Error | VFileMessage} cause * @param {Node | NodeLike | null | undefined} parent * @param {string | null | undefined} [origin] * @returns {VFileMessage} * * @overload * @param {Error | VFileMessage} cause * @param {Point | Position | null | undefined} place * @param {string | null | undefined} [origin] * @returns {VFileMessage} * * @overload * @param {Error | VFileMessage} cause * @param {string | null | undefined} [origin] * @returns {VFileMessage} * * @param {Error | VFileMessage | string} causeOrReason * Reason for message, should use markdown. * @param {Node | NodeLike | MessageOptions | Point | Position | string | null | undefined} [optionsOrParentOrPlace] * Configuration (optional). * @param {string | null | undefined} [origin] * Place in code where the message originates (example: * `'my-package:my-rule'` or `'my-rule'`). * @returns {VFileMessage} * Message. */ message(causeOrReason, optionsOrParentOrPlace, origin) { const message = new VFileMessage( // @ts-expect-error: the overloads are fine. causeOrReason, optionsOrParentOrPlace, origin ); if (this.path) { message.name = this.path + ':' + message.name; message.file = this.path; } message.fatal = false; this.messages.push(message); return message } /** * Serialize the file. * * > **Note**: which encodings are supported depends on the engine. * > For info on Node.js, see: * > <https://nodejs.org/api/util.html#whatwg-supported-encodings>. * * @param {string | null | undefined} [encoding='utf8'] * Character encoding to understand `value` as when it’s a `Uint8Array` * (default: `'utf-8'`). * @returns {string} * Serialized file. */ toString(encoding) { if (this.value === undefined) { return '' } if (typeof this.value === 'string') { return this.value } const decoder = new TextDecoder(encoding || undefined); return decoder.decode(this.value) } } /** * Assert that `part` is not a path (as in, does not contain `path.sep`). * * @param {string | null | undefined} part * File path part. * @param {string} name * Part name. * @returns {undefined} * Nothing. */ function assertPart(part, name) { if (part && part.includes(minpath.sep)) { throw new Error( '`' + name + '` cannot be a path: did not expect `' + minpath.sep + '`' ) } } /** * Assert that `part` is not empty. * * @param {string | undefined} part * Thing. * @param {string} name * Part name. * @returns {asserts part is string} * Nothing. */ function assertNonEmpty(part, name) { if (!part) { throw new Error('`' + name + '` cannot be empty') } } /** * Assert `path` exists. * * @param {string | undefined} path * Path. * @param {string} name * Dependency name. * @returns {asserts path is string} * Nothing. */ function assertPath(path, name) { if (!path) { throw new Error('Setting `' + name + '` requires `path` to be set too') } } /** * Assert `value` is an `Uint8Array`. * * @param {unknown} value * thing. * @returns {value is Uint8Array} * Whether `value` is an `Uint8Array`. */ function isUint8Array$1(value) { return Boolean( value && typeof value === 'object' && 'byteLength' in value && 'byteOffset' in value ) } // TODO: Load the JSON file when Node.js supports that. const markdownExtension = [ 'md', 'markdown', 'mdown', 'mkdn', 'mkd', 'mdwn', 'mkdown', 'ron', ]; const md = markdownExtension.map(function (d) { return '.' + d }); /** * @import {Compatible} from 'vfile' * @import {CompileOptions} from '../compile.js' * @import {ProcessorOptions} from '../core.js' */ /** * Create a file and options from a given `vfileCompatible` and options that * might contain `format: 'detect'`. * * @param {Readonly<Compatible>} vfileCompatible * File. * @param {Readonly<CompileOptions> | null | undefined} [options] * Configuration (optional). * @returns {{file: VFile, options: ProcessorOptions}} * File and options. */ function resolveFileAndOptions(vfileCompatible, options) { const file = looksLikeAVFile$1(vfileCompatible) ? vfileCompatible : new VFile(vfileCompatible); const {format, ...rest} = options; return { file, options: { format: format === 'md' || format === 'mdx' ? format : file.extname && (rest.mdExtensions || md).includes(file.extname) ? 'md' : 'mdx', ...rest } } } /** * @param {Readonly<Compatible> | null | undefined} [value] * Thing. * @returns {value is VFile} * Check. */ function looksLikeAVFile$1(value) { return Boolean( value && typeof value === 'object' && 'message' in value && 'messages' in value ) } function ok$1() {} /** * @typedef Options * Configuration. * @property {boolean | null | undefined} [jsx=false] * Support JSX identifiers (default: `false`). */ const startRe = /[$_\p{ID_Start}]/u; const contRe = /[$_\u{200C}\u{200D}\p{ID_Continue}]/u; const contReJsx = /[-$_\u{200C}\u{200D}\p{ID_Continue}]/u; const nameRe = /^[$_\p{ID_Start}][$_\u{200C}\u{200D}\p{ID_Continue}]*$/u; const nameReJsx = /^[$_\p{ID_Start}][-$_\u{200C}\u{200D}\p{ID_Continue}]*$/u; /** @type {Options} */ const emptyOptions$4 = {}; /** * Checks if the given code point can start an identifier. * * @param {number | undefined} code * Code point to check. * @returns {boolean} * Whether `code` can start an identifier. */ // Note: `undefined` is supported so you can pass the result from `''.codePointAt`. function start(code) { return code ? startRe.test(String.fromCodePoint(code)) : false } /** * Checks if the given code point can continue an identifier. * * @param {number | undefined} code * Code point to check. * @param {Options | null | undefined} [options] * Configuration (optional). * @returns {boolean} * Whether `code` can continue an identifier. */ // Note: `undefined` is supported so you can pass the result from `''.codePointAt`. function cont(code, options) { const settings = options || emptyOptions$4; const re = settings.jsx ? contReJsx : contRe; return code ? re.test(String.fromCodePoint(code)) : false } /** * Checks if the given value is a valid identifier name. * * @param {string} name * Identifier to check. * @param {Options | null | undefined} [options] * Configuration (optional). * @returns {boolean} * Whether `name` can be an identifier. */ function name(name, options) { const settings = options || emptyOptions$4; const re = settings.jsx ? nameReJsx : nameRe; return re.test(name) } // @ts-check /** @typedef { import('estree').BaseNode} BaseNode */ /** @typedef {{ skip: () => void; remove: () => void; replace: (node: BaseNode) => void; }} WalkerContext */ class WalkerBase { constructor() { /** @type {boolean} */ this.should_skip = false; /** @type {boolean} */ this.should_remove = false; /** @type {BaseNode | null} */ this.replacement = null; /** @type {WalkerContext} */ this.context = { skip: () => (this.should_skip = true), remove: () => (this.should_remove = true), replace: (node) => (this.replacement = node) }; } /** * * @param {any} parent * @param {string} prop * @param {number} index * @param {BaseNode} node */ replace(parent, prop, index, node) { if (parent) { if (index !== null) { parent[prop][index] = node; } else { parent[prop] = node; } } } /** * * @param {any} parent * @param {string} prop * @param {number} index */ remove(parent, prop, index) { if (parent) { if (index !== null) { parent[prop].splice(index, 1); } else { delete parent[prop]; } } } } // @ts-check /** @typedef { import('estree').BaseNode} BaseNode */ /** @typedef { import('./walker.js').WalkerContext} WalkerContext */ /** @typedef {( * this: WalkerContext, * node: BaseNode, * parent: BaseNode, * key: string, * index: number * ) => void} SyncHandler */ class SyncWalker extends WalkerBase { /** * * @param {SyncHandler} enter * @param {SyncHandler} leave */ constructor(enter, leave) { super(); /** @type {SyncHandler} */ this.enter = enter; /** @type {SyncHandler} */ this.leave = leave; } /** * * @param {BaseNode} node * @param {BaseNode} parent * @param {string} [prop] * @param {number} [index] * @returns {BaseNode} */ visit(node, parent, prop, index) { if (node) { if (this.enter) { const _should_skip = this.should_skip; const _should_remove = this.should_remove; const _replacement = this.replacement; this.should_skip = false; this.should_remove = false; this.replacement = null; this.enter.call(this.context, node, parent, prop, index); if (this.replacement) { node = this.replacement; this.replace(parent, prop, index, node); } if (this.should_remove) { this.remove(parent, prop, index); } const skipped = this.should_skip; const removed = this.should_remove; this.should_skip = _should_skip; this.should_remove = _should_remove; this.replacement = _replacement; if (skipped) return node; if (removed) return null; } for (const key in node) { const value = node[key]; if (typeof value !== "object") { continue; } else if (Array.isArray(value)) { for (let i = 0; i < value.length; i += 1) { if (value[i] !== null && typeof value[i].type === 'string') { if (!this.visit(value[i], node, key, i)) { // removed i--; } } } } else if (value !== null && typeof value.type === "string") { this.visit(value, node, key, null); } } if (this.leave) { const _replacement = this.replacement; const _should_remove = this.should_remove; this.replacement = null; this.should_remove = false; this.leave.call(this.context, node, parent, prop, index); if (this.replacement) { node = this.replacement; this.replace(parent, prop, index, node); } if (this.should_remove) { this.remove(parent, prop, index); } const removed = this.should_remove; this.replacement = _replacement; this.should_remove = _should_remove; if (removed) return null; } } return node; } } // @ts-check /** @typedef { import('estree').BaseNode} BaseNode */ /** @typedef { import('./sync.js').SyncHandler} SyncHandler */ /** @typedef { import('./async.js').AsyncHandler} AsyncHandler */ /** * * @param {BaseNode} ast * @param {{ * enter?: SyncHandler * leave?: SyncHandler * }} walker * @returns {BaseNode} */ function walk$1(ast, { enter, leave }) { const instance = new SyncWalker(enter, leave); return instance.visit(ast, null); } /** * @typedef {import('estree-jsx').Expression} Expression * @typedef {import('estree-jsx').Identifier} Identifier * @typedef {import('estree-jsx').ImportSpecifier} ImportSpecifier * @typedef {import('estree-jsx').JSXAttribute} JSXAttribute * @typedef {import('estree-jsx').JSXIdentifier} JSXIdentifier * @typedef