UNPKG

docusaurus-twoslash

Version:

A Docusaurus plugin that adds TypeScript Twoslash integration for enhanced code blocks with type information

491 lines (481 loc) 17.3 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // node_modules/unist-util-is/lib/index.js function anyFactory(tests) { const checks = []; let index = -1; while (++index < tests.length) { checks[index] = convert(tests[index]); } return castFactory(any); function any(...parameters) { let index2 = -1; while (++index2 < checks.length) { if (checks[index2].apply(this, parameters)) return true; } return false; } } function propsFactory(check) { const checkAsRecord = ( /** @type {Record<string, unknown>} */ check ); return castFactory(all); function all(node) { const nodeAsRecord = ( /** @type {Record<string, unknown>} */ /** @type {unknown} */ node ); let key; for (key in check) { if (nodeAsRecord[key] !== checkAsRecord[key]) return false; } return true; } } function typeFactory(check) { return castFactory(type); function type(node) { return node && node.type === check; } } function castFactory(testFunction) { return check; function check(value, index, parent) { return Boolean( looksLikeANode(value) && testFunction.call( this, value, typeof index === "number" ? index : void 0, parent || void 0 ) ); } } function ok() { return true; } function looksLikeANode(value) { return value !== null && typeof value === "object" && "type" in value; } var convert; var init_lib = __esm({ "node_modules/unist-util-is/lib/index.js"() { convert = // Note: overloads in JSDoc can’t yet use different `@template`s. /** * @type {( * (<Condition extends string>(test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & {type: Condition}) & * (<Condition extends Props>(test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & Condition) & * (<Condition extends TestFunction>(test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & Predicate<Condition, Node>) & * ((test?: null | undefined) => (node?: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node) & * ((test?: Test) => Check) * )} */ /** * @param {Test} [test] * @returns {Check} */ function(test) { if (test === null || test === void 0) { return ok; } if (typeof test === "function") { return castFactory(test); } if (typeof test === "object") { return Array.isArray(test) ? anyFactory(test) : propsFactory(test); } if (typeof test === "string") { return typeFactory(test); } throw new Error("Expected function, string, or object as test"); }; } }); // node_modules/unist-util-is/index.js var init_unist_util_is = __esm({ "node_modules/unist-util-is/index.js"() { init_lib(); } }); // node_modules/unist-util-visit-parents/lib/color.node.js function color(d) { return "\x1B[33m" + d + "\x1B[39m"; } var init_color_node = __esm({ "node_modules/unist-util-visit-parents/lib/color.node.js"() { } }); // node_modules/unist-util-visit-parents/lib/index.js function visitParents(tree, test, visitor, reverse) { let check; if (typeof test === "function" && typeof visitor !== "function") { reverse = visitor; visitor = test; } else { check = test; } const is2 = convert(check); const step = reverse ? -1 : 1; factory(tree, void 0, [])(); function factory(node, index, parents) { const value = ( /** @type {Record<string, unknown>} */ node && typeof node === "object" ? node : {} ); if (typeof value.type === "string") { const name = ( // `hast` typeof value.tagName === "string" ? value.tagName : ( // `xast` typeof value.name === "string" ? value.name : void 0 ) ); Object.defineProperty(visit2, "name", { value: "node (" + color(node.type + (name ? "<" + name + ">" : "")) + ")" }); } return visit2; function visit2() { let result = empty; let subresult; let offset; let grandparents; if (!test || is2(node, index, parents[parents.length - 1] || void 0)) { result = toResult(visitor(node, parents)); if (result[0] === EXIT) { return result; } } if ("children" in node && node.children) { const nodeAsParent = ( /** @type {UnistParent} */ node ); if (nodeAsParent.children && result[0] !== SKIP) { offset = (reverse ? nodeAsParent.children.length : -1) + step; grandparents = parents.concat(nodeAsParent); while (offset > -1 && offset < nodeAsParent.children.length) { const child = nodeAsParent.children[offset]; subresult = factory(child, offset, grandparents)(); if (subresult[0] === EXIT) { return subresult; } offset = typeof subresult[1] === "number" ? subresult[1] : offset + step; } } } return result; } } } function toResult(value) { if (Array.isArray(value)) { return value; } if (typeof value === "number") { return [CONTINUE, value]; } return value === null || value === void 0 ? empty : [value]; } var empty, CONTINUE, EXIT, SKIP; var init_lib2 = __esm({ "node_modules/unist-util-visit-parents/lib/index.js"() { init_unist_util_is(); init_color_node(); empty = []; CONTINUE = true; EXIT = false; SKIP = "skip"; } }); // node_modules/unist-util-visit-parents/index.js var init_unist_util_visit_parents = __esm({ "node_modules/unist-util-visit-parents/index.js"() { init_lib2(); } }); // node_modules/unist-util-visit/lib/index.js function visit(tree, testOrVisitor, visitorOrReverse, maybeReverse) { let reverse; let test; let visitor; if (typeof testOrVisitor === "function" && typeof visitorOrReverse !== "function") { test = void 0; visitor = testOrVisitor; reverse = visitorOrReverse; } else { test = testOrVisitor; visitor = visitorOrReverse; reverse = maybeReverse; } visitParents(tree, test, overload, reverse); function overload(node, parents) { const parent = parents[parents.length - 1]; const index = parent ? parent.children.indexOf(node) : void 0; return visitor(node, index, parent); } } var init_lib3 = __esm({ "node_modules/unist-util-visit/lib/index.js"() { init_unist_util_visit_parents(); init_unist_util_visit_parents(); } }); // node_modules/unist-util-visit/index.js var unist_util_visit_exports = {}; __export(unist_util_visit_exports, { CONTINUE: () => CONTINUE, EXIT: () => EXIT, SKIP: () => SKIP, visit: () => visit }); var init_unist_util_visit = __esm({ "node_modules/unist-util-visit/index.js"() { init_lib3(); } }); // src/remark/remarkTwoslash.js var require_remarkTwoslash = __commonJS({ "src/remark/remarkTwoslash.js"(exports2, module2) { "use strict"; var { visit: visit2 } = (init_unist_util_visit(), __toCommonJS(unist_util_visit_exports)); function remarkTwoslash2(options = {}) { const { typescript: { compilerOptions: customCompilerOptions = {} } = {}, themes = ["typescript", "javascript", "jsx", "tsx"], cache = true, includeDefaultLib = true } = options; console.log("\u{1F527} Remark Twoslash plugin initialized with themes:", themes); let twoslashRunner; let isInitialized = false; async function initializeTwoslash() { if (isInitialized) return; try { const ts = require("typescript"); const { twoslasher } = require("@typescript/twoslash"); twoslashRunner = twoslasher; isInitialized = true; console.log("\u2705 Twoslash TypeScript runner initialized successfully"); } catch (error) { console.warn("\u26A0\uFE0F Twoslash initialization failed, using fallback:", error.message); twoslashRunner = { runTwoSlash: (code) => ({ code, queries: [], errors: [], staticQuickInfos: [], highlights: [] }) }; isInitialized = true; } } return async function transformer(ast) { var _a; await initializeTwoslash(); const codeblocks = []; visit2(ast, "code", (node, index, parent) => { const { lang, meta, value } = node; console.log(`\u{1F50D} Found code block: lang="${lang}", meta="${meta}", value length=${(value == null ? void 0 : value.length) || 0}, meta type=${typeof meta}`); const hasTwoslash = meta && typeof meta === "string" && meta.includes("twoslash") || lang && typeof lang === "string" && lang.includes("twoslash") || value && typeof value === "string" && value.includes("// ^?"); const actualLang = lang && typeof lang === "string" ? lang.replace(/\s+twoslash.*$/, "").trim() : lang; if (lang === "typescript" || lang === "javascript" || lang === "jsx" || lang === "tsx" || actualLang === "typescript" || actualLang === "javascript") { console.log(`\u{1F3AF} TypeScript-related block found! lang="${lang}", actualLang="${actualLang}", meta="${meta}", hasTwoslash=${hasTwoslash}`); if (hasTwoslash) { console.log(`\u{1F389} TWOSLASH BLOCK DETECTED! lang="${lang}", meta="${meta}"`); } else { console.log(`\u274C No twoslash detected: meta="${meta}" (type: ${typeof meta}), lang="${lang}"`); } } if (hasTwoslash && value && themes.includes(actualLang)) { console.log(`\u2728 Processing Twoslash block: actualLang="${actualLang}", originalLang="${lang}", meta="${meta}"`); codeblocks.push({ node, index, parent, lang: actualLang, meta: meta || "twoslash", value }); } }); console.log(`\u{1F4CA} Found ${codeblocks.length} Twoslash code blocks to process`); let allCodeBlocks = []; visit2(ast, "code", (node) => { allCodeBlocks.push({ lang: node.lang, meta: node.meta, value: node.value ? node.value.substring(0, 50) + "..." : "no value" }); }); console.log("\u{1F50D} ALL CODE BLOCKS FOUND:", JSON.stringify(allCodeBlocks.slice(0, 10), null, 2)); for (const { node, lang, meta, value } of codeblocks) { try { const compilerOptions = { allowJs: true, target: "esnext", module: "esnext", lib: ["esnext", "dom"], moduleResolution: "node", strict: false, esModuleInterop: true, skipLibCheck: true, declaration: false, allowSyntheticDefaultImports: true, isolatedModules: false, noEmit: true, ...customCompilerOptions }; const lines = value.split("\n"); const expectedErrors = []; for (const line of lines) { const trimmed = line.trim(); if (trimmed.startsWith("// @errors:")) { const errorsMatch = trimmed.match(/\/\/ @errors:\s*(.+)$/); if (errorsMatch) { expectedErrors.push(...errorsMatch[1].split(",").map((s) => parseInt(s.trim()))); } } } let processedCode; if (twoslashRunner && typeof twoslashRunner === "function") { processedCode = twoslashRunner(value, lang || "ts", { compilerOptions, expectedErrors }); } else if (twoslashRunner == null ? void 0 : twoslashRunner.runTwoSlash) { processedCode = twoslashRunner.runTwoSlash(value, `index.${lang || "ts"}`, { compilerOptions, expectedErrors }); } else { processedCode = { code: value, queries: [], errors: [], staticQuickInfos: [], highlights: [] }; } console.log(`\u{1F3AF} Processed code block with ${((_a = processedCode.queries) == null ? void 0 : _a.length) || 0} queries`); const enhancedMeta = `${meta || ""} twoslash-processed`.trim(); node.meta = enhancedMeta; const twoslashData = { code: processedCode.code || value, queries: processedCode.queries || [], errors: processedCode.errors || [], staticQuickInfos: processedCode.staticQuickInfos || [], highlights: processedCode.highlights || [], lang: lang || "typescript" }; node.data = node.data || {}; node.data.twoslash = twoslashData; node.data.hProperties = node.data.hProperties || {}; node.data.hProperties.twoslash = twoslashData; node.data.hProperties["data-twoslash"] = JSON.stringify(twoslashData); node.data.hProperties["data-twoslash-queries"] = twoslashData.queries.length; node.data.hProperties["data-twoslash-errors"] = twoslashData.errors.length; node.data.hProperties["data-twoslash-processed"] = "true"; node.twoslash = twoslashData; Object.assign(node, { "data-twoslash": JSON.stringify(twoslashData), "data-twoslash-queries": twoslashData.queries.length, "data-twoslash-errors": twoslashData.errors.length }); console.log(`\u2705 Successfully enhanced node with Twoslash data`); } catch (error) { console.warn(`\u26A0\uFE0F Failed to process twoslash for ${lang}:`, error.message); node.meta = `${meta || ""} twoslash-error`.trim(); node.data = node.data || {}; node.data.twoslash = { error: error.message, code: value, queries: [], errors: [], staticQuickInfos: [], highlights: [], lang: lang || "typescript" }; node.data.hProperties = node.data.hProperties || {}; node.data.hProperties.twoslash = node.data.twoslash; node.twoslash = node.data.twoslash; } } }; } module2.exports = remarkTwoslash2; } }); // src/index.js var path = require("path"); var remarkTwoslash = require_remarkTwoslash(); function docusaurusTwoslashPlugin(context, options = {}) { const { typescript = {}, themes = ["typescript", "javascript", "jsx", "tsx"], cache = true, includeDefaultLib = true } = options; console.log("\u{1F680} Docusaurus Twoslash Plugin loaded with options:", { themes, cache }); return { name: "docusaurus-twoslash", getThemePath() { return path.resolve(__dirname, "theme"); }, getTypeScriptThemePath() { return path.resolve(__dirname, "theme"); }, async contentLoaded({ content, actions, allContent }) { const { addRoute } = actions; }, async loadContent() { return null; }, // This method configures ALL markdown processing in Docusaurus configureMarkdownProcessor(processor) { console.log("\u{1F4DD} Configuring markdown processor with Twoslash"); return processor.use([remarkTwoslash, { typescript, themes, cache, includeDefaultLib }]); }, getClientModules() { return [ path.resolve(__dirname, "theme/styles.css") ]; }, injectHtmlTags() { return { headTags: [], preBodyTags: [], postBodyTags: [] }; } }; } docusaurusTwoslashPlugin.remarkPlugin = remarkTwoslash; module.exports = docusaurusTwoslashPlugin;