import { createSystem, createFSBackedSystem, createVirtualTypeScriptEnvironment } from '@typescript/vfs'; function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); } function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; } function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } it = o[Symbol.iterator](); return it.next.bind(it); } function parsePrimitive(value, type) { switch (type) { case "number": return +value; case "string": return value; case "boolean": return value.toLowerCase() === "true" || value.length === 0; } throw new TwoslashError("Unknown primitive value in compiler flag", "The only recognized primitives are number, string and boolean. Got " + type + " with " + value + ".", "This is likely a typo."); } function cleanMarkdownEscaped(code) { code = code.replace(/¨D/g, "$"); code = code.replace(/¨T/g, "~"); return code; } function typesToExtension(types) { var map = { js: "js", javascript: "js", ts: "ts", typescript: "ts", tsx: "tsx", jsx: "jsx", json: "json", jsn: "json" }; if (map[types]) return map[types]; throw new TwoslashError("Unknown TypeScript extension given to Twoslash", "Received " + types + " but Twoslash only accepts: " + Object.keys(map) + " ", ""); } function getIdentifierTextSpans(ts, sourceFile) { var textSpans = []; checkChildren(sourceFile); return textSpans; function checkChildren(node) { ts.forEachChild(node, function (child) { if (ts.isIdentifier(child)) { var start = child.getStart(sourceFile, false); textSpans.push({ span: ts.createTextSpan(start, child.end - start), text: child.getText(sourceFile) }); } checkChildren(child); }); } } /** Came from https://ourcodeworld.com/articles/read/223/how-to-retrieve-the-closest-word-in-a-string-with-a-given-index-in-javascript */ function getClosestWord(str, pos) { // Make copies str = String(str); pos = Number(pos) >>> 0; // Search for the word's beginning and end. var left = str.slice(0, pos + 1).search(/\S+$/), right = str.slice(pos).search(/\s/); // The last word in the string is a special case. if (right < 0) { return { word: str.slice(left), startPos: left }; } // Return the word, using the located bounds to extract it from the string. return { word: str.slice(left, right + pos), startPos: left }; } /** To ensure that errors are matched up right */ function validateCodeForErrors(relevantErrors, handbookOptions, extension, originalCode, vfsRoot) { var inErrsButNotFoundInTheHeader = relevantErrors.filter(function (e) { return !handbookOptions.errors.includes(e.code); }); var errorsFound = Array.from(new Set(inErrsButNotFoundInTheHeader.map(function (e) { return e.code; }))).join(" "); if (inErrsButNotFoundInTheHeader.length) { var errorsToShow = new Set(relevantErrors.map(function (e) { return e.code; })); var codeToAdd = "// @errors: " + Array.from(errorsToShow).join(" "); var missing = handbookOptions.errors.length ? "\nThe existing annotation specified " + handbookOptions.errors.join(" ") : "\nExpected: " + codeToAdd; // These get filled by below var filesToErrors = {}; var noFiles = []; inErrsButNotFoundInTheHeader.forEach(function (d) { var _d$file; var fileRef = ((_d$file = d.file) == null ? void 0 : _d$file.fileName) && d.file.fileName.replace(vfsRoot, ""); if (!fileRef) noFiles.push(d);else { var existing = filesToErrors[fileRef]; if (existing) existing.push(d);else filesToErrors[fileRef] = [d]; } }); var showDiagnostics = function showDiagnostics(title, diags) { return title + "\n " + diags.map(function (e) { var msg = typeof e.messageText === "string" ? e.messageText : e.messageText.messageText; return "[" + e.code + "] " + e.start + " - " + msg; }).join("\n "); }; var innerDiags = []; if (noFiles.length) { innerDiags.push(showDiagnostics("Ambient Errors", noFiles)); } Object.keys(filesToErrors).forEach(function (filepath) { innerDiags.push(showDiagnostics(filepath, filesToErrors[filepath])); }); var allMessages = innerDiags.join("\n\n"); var newErr = new TwoslashError("Errors were thrown in the sample, but not included in an errors tag", "These errors were not marked as being expected: " + errorsFound + ". " + missing, "Compiler Errors:\n\n" + allMessages); newErr.code = "## Code\n\n'''" + extension + "\n" + originalCode + "\n'''"; throw newErr; } } /** Mainly to warn myself, I've lost a good few minutes to this before */ function validateInput(code) { if (code.includes("// @errors ")) { throw new TwoslashError("You have '// @errors ' (with a space)", "You want '// @errors: ' (with a colon)", "This is a pretty common typo"); } if (code.includes("// @filename ")) { throw new TwoslashError("You have '// @filename ' (with a space)", "You want '// @filename: ' (with a colon)", "This is a pretty common typo"); } } var hasLocalStorage = false; try { hasLocalStorage = typeof localStorage !== "undefined"; } catch (error) {} var hasProcess = typeof process !== "undefined"; var shouldDebug = hasLocalStorage && /*#__PURE__*/localStorage.getItem("DEBUG") || hasProcess && process.env.DEBUG; var log = shouldDebug ? console.log : function (_message) { return ""; }; var TwoslashError = /*#__PURE__*/function (_Error) { _inheritsLoose(TwoslashError, _Error); function TwoslashError(title, description, recommendation, code) { var _this; var message = "\n## " + title + "\n\n" + description + "\n"; if (recommendation) { message += "\n" + recommendation; } if (code) { message += "\n" + code; } _this = _Error.call(this, message) || this; _this.title = void 0; _this.description = void 0; _this.recommendation = void 0; _this.code = void 0; _this.title = title; _this.description = description; _this.recommendation = recommendation; _this.code = code; return _this; } return TwoslashError; }( /*#__PURE__*/_wrapNativeSuper(Error)); function filterHighlightLines(codeLines) { var highlights = []; var queries = []; var nextContentOffset = 0; var contentOffset = 0; var removedLines = 0; var _loop = function _loop(_i) { var line = codeLines[_i]; var moveForward = function moveForward() { contentOffset = nextContentOffset; nextContentOffset += line.length + 1; }; var stripLine = function stripLine(logDesc) { log("Removing line " + _i + " for " + logDesc); removedLines++; codeLines.splice(_i, 1); _i--; }; // We only need to run regexes over lines with comments if (!line.includes("//")) { moveForward(); } else { var highlightMatch = /^\s*\/\/\s*\^+( .+)?$/.exec(line); var queryMatch = /^\s*\/\/\s*\^\?\s*$/.exec(line); // https://regex101.com/r/2yDsRk/1 var removePrettierIgnoreMatch = /^\s*\/\/ prettier-ignore$/.exec(line); var completionsQuery = /^\s*\/\/\s*\^\|$/.exec(line); if (queryMatch !== null) { var start = line.indexOf("^"); queries.push({ kind: "query", offset: start, text: undefined, docs: undefined, line: _i + removedLines - 1 }); stripLine("having a query"); } else if (highlightMatch !== null) { var _start = line.indexOf("^"); var length = line.lastIndexOf("^") - _start + 1; var description = highlightMatch[1] ? highlightMatch[1].trim() : ""; highlights.push({ kind: "highlight", offset: _start + contentOffset, length: length, text: description, line: _i + removedLines - 1, start: _start }); stripLine("having a highlight"); } else if (removePrettierIgnoreMatch !== null) { stripLine("being a prettier ignore"); } else if (completionsQuery !== null) { var _start2 = line.indexOf("^"); // prettier-ignore queries.push({ kind: "completion", offset: _start2, text: undefined, docs: undefined, line: _i + removedLines - 1 }); stripLine("having a completion query"); } else { moveForward(); } } i = _i; }; for (var i = 0; i < codeLines.length; i++) { _loop(i); } return { highlights: highlights, queries: queries }; } function getOptionValueFromMap(name, key, optMap) { var result = optMap.get(key.toLowerCase()); log("Get " + name + " mapped option: " + key + " => " + result); if (result === undefined) { var keys = Array.from(optMap.keys()); throw new TwoslashError("Invalid inline compiler value", "Got " + key + " for " + name + " but it is not a supported value by the TS compiler.", "Allowed values: " + keys.join(",")); } return result; } function setOption(name, value, opts, ts) { log("Setting " + name + " to " + value); var _loop2 = function _loop2() { var opt = _step.value; if (opt.name.toLowerCase() === name.toLowerCase()) { switch (opt.type) { case "number": case "string": case "boolean": opts[opt.name] = parsePrimitive(value, opt.type); break; case "list": var elementType = opt.element.type; var strings = value.split(","); if (typeof elementType === "string") { opts[opt.name] = strings.map(function (v) { return parsePrimitive(v, elementType); }); } else { opts[opt.name] = strings.map(function (v) { return getOptionValueFromMap(opt.name, v, elementType); }); } break; default: // It's a map! var optMap = opt.type; opts[opt.name] = getOptionValueFromMap(opt.name, value, optMap); break; } return { v: void 0 }; } }; for (var _iterator = _createForOfIteratorHelperLoose(ts.optionDeclarations), _step; !(_step = _iterator()).done;) { var _ret = _loop2(); if (typeof _ret === "object") return _ret.v; } throw new TwoslashError("Invalid inline compiler flag", "There isn't a TypeScript compiler flag called '" + name + "'.", "This is likely a typo, you can check all the compiler flags in the TSConfig reference, or check the additional Twoslash flags in the npm page for @typescript/twoslash."); } var booleanConfigRegexp = /^\/\/\s?@(\w+)$/; // https://regex101.com/r/8B2Wwh/1 var valuedConfigRegexp = /^\/\/\s?@(\w+):\s?(.+)$/; function filterCompilerOptions(codeLines, defaultCompilerOptions, ts) { var options = _extends({}, defaultCompilerOptions); for (var _i2 = 0; _i2 < codeLines.length;) { var match = void 0; if (match = booleanConfigRegexp.exec(codeLines[_i2])) { options[match[1]] = true; setOption(match[1], "true", options, ts); } else if (match = valuedConfigRegexp.exec(codeLines[_i2])) { // Skip a filename tag, which should propagate through this stage if (match[1] === "filename") { _i2++; continue; } setOption(match[1], match[2], options, ts); } else { _i2++; continue; } codeLines.splice(_i2, 1); } return options; } function filterCustomTags(codeLines, customTags) { var tags = []; for (var _i3 = 0; _i3 < codeLines.length;) { var match = void 0; if (match = valuedConfigRegexp.exec(codeLines[_i3])) { if (customTags.includes(match[1])) { tags.push({ name: match[1], line: _i3, annotation: codeLines[_i3].split("@" + match[1] + ": ")[1] }); codeLines.splice(_i3, 1); } } _i3++; } return tags; } // Keys in this object are used to filter out handbook options // before compiler options are set. var defaultHandbookOptions = { errors: [], noErrors: false, showEmit: false, showEmittedFile: undefined, noStaticSemanticInfo: false, emit: false, noErrorValidation: false }; function filterHandbookOptions(codeLines) { var options = _extends({}, defaultHandbookOptions); for (var _i4 = 0; _i4 < codeLines.length; _i4++) { var match = void 0; if (match = booleanConfigRegexp.exec(codeLines[_i4])) { if (match[1] in options) { options[match[1]] = true; log("Setting options." + match[1] + " to true"); codeLines.splice(_i4, 1); _i4--; } } else if (match = valuedConfigRegexp.exec(codeLines[_i4])) { if (match[1] in options) { options[match[1]] = match[2]; log("Setting options." + match[1] + " to " + match[2]); codeLines.splice(_i4, 1); _i4--; } } } // Edge case the errors object to turn it into a string array if ("errors" in options && typeof options.errors === "string") { options.errors = options.errors.split(" ").map(Number); log("Setting options.error to ", options.errors); } return options; } /** * Runs the checker against a TypeScript/JavaScript code sample returning potentially * difference code, and a set of annotations around how it works. * * @param code The twoslash markup'd code * @param extension For example: "ts", "tsx", "typescript", "javascript" or "js". * @param options Additional options for twoslash */ function twoslasher(code, extension, options) { var _options$tsModule, _options$lzstringModu, _options$defaultCompi; if (options === void 0) { options = {}; } var ts = (_options$tsModule = options.tsModule) != null ? _options$tsModule : require("typescript"); var lzstring = (_options$lzstringModu = options.lzstringModule) != null ? _options$lzstringModu : require("lz-string"); var originalCode = code; var safeExtension = typesToExtension(extension); var defaultFileName = "index." + safeExtension; log("\n\nLooking at code: \n```" + safeExtension + "\n" + code + "\n```\n"); var defaultCompilerOptions = _extends({ strict: true, target: ts.ScriptTarget.ES2016, allowJs: true }, (_options$defaultCompi = options.defaultCompilerOptions) != null ? _options$defaultCompi : {}); validateInput(code); code = cleanMarkdownEscaped(code); // NOTE: codeLines is mutated by the below functions: var codeLines = code.split(/\r\n?|\n/g); var tags = options.customTags ? filterCustomTags(codeLines, options.customTags) : []; var handbookOptions = _extends({}, filterHandbookOptions(codeLines), options.defaultOptions); var compilerOptions = filterCompilerOptions(codeLines, defaultCompilerOptions, ts); // Handle special casing the lookup for when using jsx preserve which creates .jsx files if (!handbookOptions.showEmittedFile) { handbookOptions.showEmittedFile = compilerOptions.jsx && compilerOptions.jsx === ts.JsxEmit.Preserve ? "index.jsx" : "index.js"; } var getRoot = function getRoot() { var pa = "pa"; var path = require(pa + "th"); var rootPath = options.vfsRoot || process.cwd(); return rootPath.split(path.sep).join(path.posix.sep); }; // In a browser we want to DI everything, in node we can use local infra var useFS = !!options.fsMap; var vfs = useFS && options.fsMap ? options.fsMap : new Map(); var system = useFS ? createSystem(vfs) : createFSBackedSystem(vfs, getRoot(), ts); var fsRoot = useFS ? "/" : getRoot() + "/"; var env = createVirtualTypeScriptEnvironment(system, [], ts, compilerOptions, options.customTransformers); var ls = env.languageService; code = codeLines.join("\n"); var partialQueries = []; var queries = []; var highlights = []; var nameContent = splitTwoslashCodeInfoFiles(code, defaultFileName, fsRoot); var sourceFiles = ["js", "jsx", "ts", "tsx"]; /** All of the referenced files in the markup */ var filenames = nameContent.map(function (nc) { return nc[0]; }); var _loop3 = function _loop3() { var file = _step2.value; var filename = file[0], codeLines = file[1]; var filetype = filename.split(".").pop() || ""; // Only run the LSP-y things on source files var allowJSON = compilerOptions.resolveJsonModule && filetype === "json"; if (!sourceFiles.includes(filetype) && !allowJSON) { return "continue"; } // Create the file in the vfs var newFileCode = codeLines.join("\n"); env.createFile(filename, newFileCode); var updates = filterHighlightLines(codeLines); highlights = highlights.concat(updates.highlights); // ------ Do the LSP lookup for the queries var lspedQueries = updates.queries.map(function (q, i) { var sourceFile = env.getSourceFile(filename); var position = ts.getPositionOfLineAndCharacter(sourceFile, q.line, q.offset); switch (q.kind) { case "query": { var quickInfo = ls.getQuickInfoAtPosition(filename, position); // prettier-ignore var text; var docs; if (quickInfo && quickInfo.displayParts) { text = quickInfo.displayParts.map(function (dp) { return dp.text; }).join(""); docs = quickInfo.documentation ? quickInfo.documentation.map(function (d) { return d.text; }).join("
") : undefined; } else { throw new TwoslashError("Invalid QuickInfo query", "The request on line " + q.line + " in " + filename + " for quickinfo via ^? returned no from the compiler.", "This is likely that the x positioning is off."); } var queryResult = { kind: "query", text: text, docs: docs, line: q.line - i, offset: q.offset, file: filename }; return queryResult; } case "completion": { var completions = ls.getCompletionsAtPosition(filename, position - 1, {}); if (!completions && !handbookOptions.noErrorValidation) { throw new TwoslashError("Invalid completion query", "The request on line " + q.line + " in " + filename + " for completions via ^| returned no completions from the compiler.", "This is likely that the positioning is off."); } var word = getClosestWord(sourceFile.text, position - 1); var prefix = sourceFile.text.slice(word.startPos, position); var lastDot = prefix.split(".").pop() || ""; var _queryResult = { kind: "completions", completions: (completions == null ? void 0 : completions.entries) || [], completionPrefix: lastDot, line: q.line - i, offset: q.offset, file: filename }; return _queryResult; } } }); partialQueries = partialQueries.concat(lspedQueries); // Sets the file in the compiler as being without the comments var newEditedFileCode = codeLines.join("\n"); env.updateFile(filename, newEditedFileCode); }; for (var _iterator2 = _createForOfIteratorHelperLoose(nameContent), _step2; !(_step2 = _iterator2()).done;) { var _ret2 = _loop3(); if (_ret2 === "continue") continue; } // We need to also strip the highlights + queries from the main file which is shown to people var allCodeLines = code.split(/\r\n?|\n/g); filterHighlightLines(allCodeLines); code = allCodeLines.join("\n"); // Lets fs changes propagate back up to the fsMap if (handbookOptions.emit) { filenames.forEach(function (f) { var filetype = f.split(".").pop() || ""; if (!sourceFiles.includes(filetype)) return; var output = ls.getEmitOutput(f); output.outputFiles.forEach(function (output) { system.writeFile(output.name, output.text); }); }); } // Code should now be safe to compile, so we're going to split it into different files var errs = []; // Let because of a filter when cutting var staticQuickInfos = []; // Iterate through the declared files and grab errors and LSP quickinfos // const declaredFiles = Object.keys(fileMap) filenames.forEach(function (file) { var filetype = file.split(".").pop() || ""; // Only run the LSP-y things on source files if (!sourceFiles.includes(filetype)) { return; } if (!handbookOptions.noErrors) { errs = errs.concat(ls.getSemanticDiagnostics(file), ls.getSyntacticDiagnostics(file)); } var source = env.sys.readFile(file); var sourceFile = env.getSourceFile(file); if (!sourceFile) { throw new TwoslashError("Could not find a TypeScript sourcefile for '" + file + "' in the Twoslash vfs", "It's a little hard to provide useful advice on this error. Maybe you imported something which the compiler doesn't think is a source file?", ""); } // Get all of the interesting quick info popover if (!handbookOptions.showEmit) { var fileContentStartIndexInModifiedFile = code.indexOf(source) == -1 ? 0 : code.indexOf(source); var linesAbove = code.slice(0, fileContentStartIndexInModifiedFile).split("\n").length - 1; // Get all interesting identifiers in the file, so we can show hover info for it var identifiers = handbookOptions.noStaticSemanticInfo ? [] : getIdentifierTextSpans(ts, sourceFile); for (var _iterator3 = _createForOfIteratorHelperLoose(identifiers), _step3; !(_step3 = _iterator3()).done;) { var identifier = _step3.value; var span = identifier.span; var quickInfo = ls.getQuickInfoAtPosition(file, span.start); if (quickInfo && quickInfo.displayParts) { var text = quickInfo.displayParts.map(function (dp) { return dp.text; }).join(""); var targetString = identifier.text; var docs = quickInfo.documentation ? quickInfo.documentation.map(function (d) { return d.text; }).join("\n") : undefined; // Get the position of the var position = span.start + fileContentStartIndexInModifiedFile; // Use TypeScript to pull out line/char from the original code at the position + any previous offset var burnerSourceFile = ts.createSourceFile("_.ts", code, ts.ScriptTarget.ES2015); var _ts$getLineAndCharact = ts.getLineAndCharacterOfPosition(burnerSourceFile, position), line = _ts$getLineAndCharact.line, character = _ts$getLineAndCharact.character; staticQuickInfos.push({ text: text, docs: docs, start: position, length: span.length, line: line, character: character, targetString: targetString }); } } // Offset the queries for this file because they are based on the line for that one // specific file, and not the global twoslash document. This has to be done here because // in the above loops, the code for queries/highlights/etc hasn't been stripped yet. partialQueries.filter(function (q) { return q.file === file; }).forEach(function (q) { var pos = ts.getPositionOfLineAndCharacter(sourceFile, q.line, q.offset) + fileContentStartIndexInModifiedFile; switch (q.kind) { case "query": { queries.push({ docs: q.docs, kind: "query", start: pos + fileContentStartIndexInModifiedFile, length: q.text.length, text: q.text, offset: q.offset, line: q.line + linesAbove + 1 }); break; } case "completions": { queries.push({ completions: q.completions, kind: "completions", start: pos + fileContentStartIndexInModifiedFile, completionsPrefix: q.completionPrefix, length: 1, offset: q.offset, line: q.line + linesAbove + 1 }); } } }); } }); var relevantErrors = errs.filter(function (e) { return e.file && filenames.includes(e.file.fileName); }); // A validator that error codes are mentioned, so we can know if something has broken in the future if (!handbookOptions.noErrorValidation && relevantErrors.length) { validateCodeForErrors(relevantErrors, handbookOptions, extension, originalCode, fsRoot); } var errors = []; // We can't pass the ts.DiagnosticResult out directly (it can't be JSON.stringified) for (var _iterator4 = _createForOfIteratorHelperLoose(relevantErrors), _step4; !(_step4 = _iterator4()).done;) { var err = _step4.value; var codeWhereErrorLives = env.sys.readFile(err.file.fileName); var fileContentStartIndexInModifiedFile = code.indexOf(codeWhereErrorLives); var renderedMessage = ts.flattenDiagnosticMessageText(err.messageText, "\n"); var id = "err-" + err.code + "-" + err.start + "-" + err.length; var _ts$getLineAndCharact2 = ts.getLineAndCharacterOfPosition(err.file, err.start), line = _ts$getLineAndCharact2.line, character = _ts$getLineAndCharact2.character; errors.push({ category: err.category, code: err.code, length: err.length, start: err.start ? err.start + fileContentStartIndexInModifiedFile : undefined, line: line, character: character, renderedMessage: renderedMessage, id: id }); } // Handle emitting files if (handbookOptions.showEmit) { // Get the file which created the file we want to show: var emitFilename = handbookOptions.showEmittedFile || defaultFileName; var emitSourceFilename = fsRoot + emitFilename.replace(".jsx", "").replace(".js", "").replace(".d.ts", "").replace(".map", ""); var emitSource = filenames.find(function (f) { return f === emitSourceFilename + ".ts" || f === emitSourceFilename + ".tsx"; }); if (!emitSource && !compilerOptions.outFile) { var allFiles = filenames.join(", "); // prettier-ignore throw new TwoslashError("Could not find source file to show the emit for", "Cannot find the corresponding **source** file " + emitFilename + " for completions via ^| returned no quickinfo from the compiler.", "Looked for: " + emitSourceFilename + " in the vfs - which contains: " + allFiles); } // Allow outfile, in which case you need any file. if (compilerOptions.outFile) { emitSource = filenames[0]; } var output = ls.getEmitOutput(emitSource); var file = output.outputFiles.find(function (o) { return o.name === fsRoot + handbookOptions.showEmittedFile || o.name === handbookOptions.showEmittedFile; }); if (!file) { var _allFiles = output.outputFiles.map(function (o) { return o.name; }).join(", "); throw new TwoslashError("Cannot find the output file in the Twoslash VFS", "Looking for " + handbookOptions.showEmittedFile + " in the Twoslash vfs after compiling", "Looked for\" " + (fsRoot + handbookOptions.showEmittedFile) + " in the vfs - which contains " + _allFiles + "."); } code = file.text; extension = file.name.split(".").pop(); // Remove highlights and queries, because it won't work across transpiles, // though I guess source-mapping could handle the transition highlights = []; partialQueries = []; staticQuickInfos = []; } var zippedCode = lzstring.compressToEncodedURIComponent(originalCode); var playgroundURL = "https://www.typescriptlang.org/play/#code/" + zippedCode; // Cutting happens last, and it means editing the lines and character index of all // the type annotations which are attached to a location var cutString = "// ---cut---\n"; if (code.includes(cutString)) { // Get the place it is, then find the end and the start of the next line var cutIndex = code.indexOf(cutString) + cutString.length; var lineOffset = code.substr(0, cutIndex).split("\n").length - 1; // Kills the code shown code = code.split(cutString).pop(); // For any type of metadata shipped, it will need to be shifted to // fit in with the new positions after the cut staticQuickInfos.forEach(function (info) { info.start -= cutIndex; info.line -= lineOffset; }); staticQuickInfos = staticQuickInfos.filter(function (s) { return s.start > -1; }); errors.forEach(function (err) { if (err.start) err.start -= cutIndex; if (err.line) err.line -= lineOffset; }); errors = errors.filter(function (e) { return e.start && e.start > -1; }); highlights.forEach(function (highlight) { highlight.start -= cutIndex; highlight.line -= lineOffset; }); highlights = highlights.filter(function (e) { return e.start > -1; }); queries.forEach(function (q) { return q.line -= lineOffset; }); queries = queries.filter(function (q) { return q.line > -1; }); tags.forEach(function (q) { return q.line -= lineOffset; }); tags = tags.filter(function (q) { return q.line > -1; }); } var cutAfterString = "// ---cut-after---\n"; if (code.includes(cutAfterString)) { // Get the place it is, then find the end and the start of the next line var _cutIndex = code.indexOf(cutAfterString) + cutAfterString.length; var _lineOffset = code.substr(0, _cutIndex).split("\n").length - 1; // Kills the code shown, removing any whitespace on the end code = code.split(cutAfterString).shift().trimEnd(); // Cut any metadata after the cutAfterString staticQuickInfos = staticQuickInfos.filter(function (s) { return s.line < _lineOffset; }); errors = errors.filter(function (e) { return e.line && e.line < _lineOffset; }); highlights = highlights.filter(function (e) { return e.line < _lineOffset; }); queries = queries.filter(function (q) { return q.line < _lineOffset; }); tags = tags.filter(function (q) { return q.line < _lineOffset; }); } return { code: code, extension: extension, highlights: highlights, queries: queries, staticQuickInfos: staticQuickInfos, errors: errors, playgroundURL: playgroundURL, tags: tags }; } var splitTwoslashCodeInfoFiles = function splitTwoslashCodeInfoFiles(code, defaultFileName, root) { var lines = code.split(/\r\n?|\n/g); var nameForFile = code.includes("@filename: " + defaultFileName) ? "global.ts" : defaultFileName; var currentFileContent = []; var fileMap = []; for (var _iterator5 = _createForOfIteratorHelperLoose(lines), _step5; !(_step5 = _iterator5()).done;) { var line = _step5.value; if (line.includes("// @filename: ")) { fileMap.push([root + nameForFile, currentFileContent]); nameForFile = line.split("// @filename: ")[1].trim(); currentFileContent = []; } else { currentFileContent.push(line); } } fileMap.push([root + nameForFile, currentFileContent]); // Basically, strip these: // ["index.ts", []] // ["index.ts", [""]] var nameContent = fileMap.filter(function (n) { return n[1].length > 0 && (n[1].length > 1 || n[1][0] !== ""); }); return nameContent; }; export { TwoslashError, twoslasher }; //# sourceMappingURL=twoslash.esm.js.map