2026-05-31 13:54:31 +02:00

843 lines
22 KiB
JavaScript

'use strict';
const twoslashProtocol = require('twoslash-protocol');
const defaultCompilerOptions = {
strict: true,
module: 99,
target: 99,
allowJs: true,
skipDefaultLibCheck: true,
skipLibCheck: true,
moduleDetection: 3
};
const defaultHandbookOptions = {
errors: [],
noErrors: false,
noErrorsCutted: false,
noErrorValidation: false,
noStaticSemanticInfo: false,
showEmit: false,
showEmittedFile: void 0,
keepNotations: false
};
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
class TwoslashError extends Error {
constructor(title, description, recommendation, code) {
let message = `
## ${title}
${description}
`;
if (recommendation)
message += `
${recommendation}`;
if (code)
message += `
${code}`;
super(message);
__publicField(this, "title");
__publicField(this, "description");
__publicField(this, "recommendation");
__publicField(this, "code");
this.title = title;
this.description = description;
this.recommendation = recommendation;
this.code = code;
}
}
const defaults = Object.freeze({
ignoreUnknown: false,
respectType: false,
respectFunctionNames: false,
respectFunctionProperties: false,
unorderedObjects: true,
unorderedArrays: false,
unorderedSets: false,
excludeKeys: void 0,
excludeValues: void 0,
replacer: void 0
});
function objectHash(object, options) {
if (options) {
options = { ...defaults, ...options };
} else {
options = defaults;
}
const hasher = createHasher(options);
hasher.dispatch(object);
return hasher.toString();
}
const defaultPrototypesKeys = Object.freeze([
"prototype",
"__proto__",
"constructor"
]);
function createHasher(options) {
let buff = "";
let context = /* @__PURE__ */ new Map();
const write = (str) => {
buff += str;
};
return {
toString() {
return buff;
},
getContext() {
return context;
},
dispatch(value) {
if (options.replacer) {
value = options.replacer(value);
}
const type = value === null ? "null" : typeof value;
return this[type](value);
},
object(object) {
if (object && typeof object.toJSON === "function") {
return this.object(object.toJSON());
}
const objString = Object.prototype.toString.call(object);
let objType = "";
const objectLength = objString.length;
if (objectLength < 10) {
objType = "unknown:[" + objString + "]";
} else {
objType = objString.slice(8, objectLength - 1);
}
objType = objType.toLowerCase();
let objectNumber = null;
if ((objectNumber = context.get(object)) === void 0) {
context.set(object, context.size);
} else {
return this.dispatch("[CIRCULAR:" + objectNumber + "]");
}
if (typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(object)) {
write("buffer:");
return write(object.toString("utf8"));
}
if (objType !== "object" && objType !== "function" && objType !== "asyncfunction") {
if (this[objType]) {
this[objType](object);
} else if (!options.ignoreUnknown) {
this.unkown(object, objType);
}
} else {
let keys = Object.keys(object);
if (options.unorderedObjects) {
keys = keys.sort();
}
let extraKeys = [];
if (options.respectType !== false && !isNativeFunction(object)) {
extraKeys = defaultPrototypesKeys;
}
if (options.excludeKeys) {
keys = keys.filter((key) => {
return !options.excludeKeys(key);
});
extraKeys = extraKeys.filter((key) => {
return !options.excludeKeys(key);
});
}
write("object:" + (keys.length + extraKeys.length) + ":");
const dispatchForKey = (key) => {
this.dispatch(key);
write(":");
if (!options.excludeValues) {
this.dispatch(object[key]);
}
write(",");
};
for (const key of keys) {
dispatchForKey(key);
}
for (const key of extraKeys) {
dispatchForKey(key);
}
}
},
array(arr, unordered) {
unordered = unordered === void 0 ? options.unorderedArrays !== false : unordered;
write("array:" + arr.length + ":");
if (!unordered || arr.length <= 1) {
for (const entry of arr) {
this.dispatch(entry);
}
return;
}
const contextAdditions = /* @__PURE__ */ new Map();
const entries = arr.map((entry) => {
const hasher = createHasher(options);
hasher.dispatch(entry);
for (const [key, value] of hasher.getContext()) {
contextAdditions.set(key, value);
}
return hasher.toString();
});
context = contextAdditions;
entries.sort();
return this.array(entries, false);
},
date(date) {
return write("date:" + date.toJSON());
},
symbol(sym) {
return write("symbol:" + sym.toString());
},
unkown(value, type) {
write(type);
if (!value) {
return;
}
write(":");
if (value && typeof value.entries === "function") {
return this.array(
Array.from(value.entries()),
true
/* ordered */
);
}
},
error(err) {
return write("error:" + err.toString());
},
boolean(bool) {
return write("bool:" + bool);
},
string(string) {
write("string:" + string.length + ":");
write(string);
},
function(fn) {
write("fn:");
if (isNativeFunction(fn)) {
this.dispatch("[native]");
} else {
this.dispatch(fn.toString());
}
if (options.respectFunctionNames !== false) {
this.dispatch("function-name:" + String(fn.name));
}
if (options.respectFunctionProperties) {
this.object(fn);
}
},
number(number) {
return write("number:" + number);
},
xml(xml) {
return write("xml:" + xml.toString());
},
null() {
return write("Null");
},
undefined() {
return write("Undefined");
},
regexp(regex) {
return write("regex:" + regex.toString());
},
uint8array(arr) {
write("uint8array:");
return this.dispatch(Array.prototype.slice.call(arr));
},
uint8clampedarray(arr) {
write("uint8clampedarray:");
return this.dispatch(Array.prototype.slice.call(arr));
},
int8array(arr) {
write("int8array:");
return this.dispatch(Array.prototype.slice.call(arr));
},
uint16array(arr) {
write("uint16array:");
return this.dispatch(Array.prototype.slice.call(arr));
},
int16array(arr) {
write("int16array:");
return this.dispatch(Array.prototype.slice.call(arr));
},
uint32array(arr) {
write("uint32array:");
return this.dispatch(Array.prototype.slice.call(arr));
},
int32array(arr) {
write("int32array:");
return this.dispatch(Array.prototype.slice.call(arr));
},
float32array(arr) {
write("float32array:");
return this.dispatch(Array.prototype.slice.call(arr));
},
float64array(arr) {
write("float64array:");
return this.dispatch(Array.prototype.slice.call(arr));
},
arraybuffer(arr) {
write("arraybuffer:");
return this.dispatch(new Uint8Array(arr));
},
url(url) {
return write("url:" + url.toString());
},
map(map) {
write("map:");
const arr = [...map];
return this.array(arr, options.unorderedSets !== false);
},
set(set) {
write("set:");
const arr = [...set];
return this.array(arr, options.unorderedSets !== false);
},
file(file) {
write("file:");
return this.dispatch([file.name, file.size, file.type, file.lastModfied]);
},
blob() {
if (options.ignoreUnknown) {
return write("[blob]");
}
throw new Error(
'Hashing Blob objects is currently not supported\nUse "options.replacer" or "options.ignoreUnknown"\n'
);
},
domwindow() {
return write("domwindow");
},
bigint(number) {
return write("bigint:" + number.toString());
},
/* Node.js standard native objects */
process() {
return write("process");
},
timer() {
return write("timer");
},
pipe() {
return write("pipe");
},
tcp() {
return write("tcp");
},
udp() {
return write("udp");
},
tty() {
return write("tty");
},
statwatcher() {
return write("statwatcher");
},
securecontext() {
return write("securecontext");
},
connection() {
return write("connection");
},
zlib() {
return write("zlib");
},
context() {
return write("context");
},
nodescript() {
return write("nodescript");
},
httpparser() {
return write("httpparser");
},
dataview() {
return write("dataview");
},
signal() {
return write("signal");
},
fsevent() {
return write("fsevent");
},
tlswrap() {
return write("tlswrap");
}
};
}
const nativeFunc = "[native code] }";
const nativeFuncLength = nativeFunc.length;
function isNativeFunction(f) {
if (typeof f !== "function") {
return false;
}
return Function.prototype.toString.call(f).slice(-nativeFuncLength) === nativeFunc;
}
const reConfigBoolean = /^\/\/\s?@(\w+)$/gm;
const reConfigValue = /^\/\/\s?@(\w+):\s?(.+)$/gm;
const reAnnonateMarkers = /^\s*\/\/\s*\^(\?|\||\^+)( .*)?$/gm;
const reCutBefore = /^[\t\v\f ]*\/\/\s?---cut(-before)?---\r?\n/gm;
const reCutAfter = /^[\t\v\f ]*\/\/\s?---cut-after---$/gm;
const reCutStart = /^[\t\v\f ]*\/\/\s?---cut-start---$/gm;
const reCutEnd = /^[\t\v\f ]*\/\/\s?---cut-end---\r?\n/gm;
const reFilenamesMakers = /^[\t\v\f ]*\/\/\s?@filename: (.+)$/gm;
function getObjectHash(obj) {
return objectHash(obj);
}
function parsePrimitive(value, type) {
if (typeof value === type)
return value;
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 typesToExtension(types) {
const map = {
js: "js",
javascript: "js",
ts: "ts",
typescript: "ts",
tsx: "tsx",
jsx: "jsx",
json: "json",
jsn: "json",
map: "json",
mts: "ts",
cts: "ts",
mjs: "js",
cjs: "js"
};
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, fileOffset) {
const textSpans = [];
checkChildren(sourceFile);
return textSpans;
function checkChildren(node) {
ts.forEachChild(node, (child) => {
if (ts.isIdentifier(child)) {
const text = child.getText(sourceFile);
const start = child.getStart(sourceFile, false) + fileOffset;
const end = start + text.length;
textSpans.push([start, end, text]);
}
checkChildren(child);
});
}
}
function getOptionValueFromMap(name, key, optMap) {
const result = optMap.get(key.toLowerCase());
if (result === void 0) {
const 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 splitFiles(code, defaultFileName, root) {
const matches = Array.from(code.matchAll(reFilenamesMakers));
const allFilenames = matches.map((match) => match[1].trimEnd());
let currentFileName = allFilenames.includes(defaultFileName) ? "__index__.ts" : defaultFileName;
const files = [];
let index = 0;
for (const match of matches) {
const offset = match.index;
const content = code.slice(index, offset);
if (content) {
files.push({
offset: index,
filename: currentFileName,
filepath: root + currentFileName,
content,
extension: getExtension(currentFileName)
});
}
currentFileName = match[1].trimEnd();
index = offset;
}
if (index < code.length) {
const content = code.slice(index);
files.push({
offset: index,
filename: currentFileName,
filepath: root + currentFileName,
content,
extension: getExtension(currentFileName)
});
}
return files;
}
function getExtension(fileName) {
return fileName.split(".").pop();
}
function parseFlag(name, value, start, end, customTags, tsOptionDeclarations) {
if (customTags.includes(name)) {
return {
type: "tag",
name,
value,
start,
end
};
}
const compilerDecl = tsOptionDeclarations.find((d) => d.name.toLocaleLowerCase() === name.toLocaleLowerCase());
if (compilerDecl) {
switch (compilerDecl.type) {
case "number":
case "string":
case "boolean":
return {
type: "compilerOptions",
name: compilerDecl.name,
value: parsePrimitive(value, compilerDecl.type),
start,
end
};
case "list": {
const elementType = compilerDecl.element.type;
const strings = value.split(",");
const resolved = typeof elementType === "string" ? strings.map((v) => parsePrimitive(v, elementType)) : strings.map((v) => getOptionValueFromMap(compilerDecl.name, v, elementType));
return {
type: "compilerOptions",
name: compilerDecl.name,
value: resolved,
start,
end
};
}
case "object":
return {
type: "compilerOptions",
name: compilerDecl.name,
value: JSON.parse(value),
start,
end
};
default: {
return {
type: "compilerOptions",
name: compilerDecl.name,
value: getOptionValueFromMap(compilerDecl.name, value, compilerDecl.type),
start,
end
};
}
}
}
if (Object.keys(defaultHandbookOptions).includes(name)) {
if (name === "errors" && typeof value === "string")
value = value.split(" ").map(Number);
if (name === "noErrors" && typeof value === "string") {
if (value === "true")
value = true;
else if (value === "false")
value = false;
else
value = value.split(" ").map(Number);
}
return {
type: "handbookOptions",
name,
value,
start,
end
};
}
return {
type: "unknown",
name,
value,
start,
end
};
}
function findFlagNotations(code, customTags, tsOptionDeclarations) {
const flagNotations = [];
Array.from(code.matchAll(reConfigBoolean)).forEach((match) => {
const index = match.index;
const name = match[1];
flagNotations.push(
parseFlag(name, true, index, index + match[0].length + 1, customTags, tsOptionDeclarations)
);
});
Array.from(code.matchAll(reConfigValue)).forEach((match) => {
const name = match[1];
if (name === "filename")
return;
const index = match.index;
const value = match[2];
flagNotations.push(
parseFlag(name, value, index, index + match[0].length + 1, customTags, tsOptionDeclarations)
);
});
return flagNotations;
}
function findCutNotations(code, meta) {
const removals = [];
const cutBefore = [...code.matchAll(reCutBefore)];
const cutAfter = [...code.matchAll(reCutAfter)];
const cutStart = [...code.matchAll(reCutStart)];
const cutEnd = [...code.matchAll(reCutEnd)];
if (cutBefore.length) {
const last = cutBefore[cutBefore.length - 1];
removals.push([0, last.index + last[0].length]);
}
if (cutAfter.length) {
const first = cutAfter[0];
removals.push([first.index, code.length]);
}
if (cutStart.length !== cutEnd.length) {
throw new TwoslashError(
`Mismatched cut markers`,
`You have ${cutStart.length} cut-starts and ${cutEnd.length} cut-ends`,
`Make sure you have a matching pair for each.`
);
}
for (let i = 0; i < cutStart.length; i++) {
const start = cutStart[i];
const end = cutEnd[i];
if (start.index > end.index) {
throw new TwoslashError(
`Mismatched cut markers`,
`You have a cut-start at ${start.index} which is after the cut-end at ${end.index}`,
`Make sure you have a matching pair for each.`
);
}
removals.push([start.index, end.index + end[0].length]);
}
if (meta)
meta.removals.push(...removals);
return removals;
}
function findQueryMarkers(code, meta, pc) {
if (code.includes("//")) {
const linesQuery = /* @__PURE__ */ new Set();
Array.from(code.matchAll(reAnnonateMarkers)).forEach((match) => {
const type = match[1];
const index = match.index;
meta.removals.push([index, index + match[0].length + 1]);
const markerIndex = match[0].indexOf("^");
const pos = pc.indexToPos(index + markerIndex);
let targetLine = pos.line - 1;
while (linesQuery.has(targetLine) && targetLine >= 0)
targetLine -= 1;
const targetIndex = pc.posToIndex(targetLine, pos.character);
if (type === "?") {
meta.positionQueries.push(targetIndex);
} else if (type === "|") {
meta.positionCompletions.push(targetIndex);
} else {
const markerLength = match[0].lastIndexOf("^") - markerIndex + 1;
meta.positionHighlights.push([
targetIndex,
targetIndex + markerLength,
match[2]?.trim()
]);
}
linesQuery.add(pos.line);
});
}
return meta;
}
function removeTsExtension(filename) {
const sansMapOrDTS = filename.replace(/\.map$/, "").replace(/\.d\.ts$/, ".ts").replace(/\.map$/, "");
return sansMapOrDTS.replace(/\.[^/.]+$/, "");
}
const flagKeys = [
"all",
"allowArbitraryExtensions",
"allowImportingTsExtensions",
"allowJs",
"allowSyntheticDefaultImports",
"allowUmdGlobalAccess",
"allowUnreachableCode",
"allowUnusedLabels",
"alwaysStrict",
"assumeChangesOnlyAffectDirectDependencies",
"baseUrl",
"build",
"charset",
"checkJs",
"composite",
"customConditions",
"declaration",
"declarationDir",
"declarationMap",
"diagnostics",
"disableReferencedProjectLoad",
"disableSizeLimit",
"disableSolutionSearching",
"disableSourceOfProjectReferenceRedirect",
"downlevelIteration",
"emitBOM",
"emitDeclarationOnly",
"emitDecoratorMetadata",
"errors",
"esModuleInterop",
"exactOptionalPropertyTypes",
"experimentalDecorators",
"explainFiles",
"extendedDiagnostics",
"forceConsistentCasingInFileNames",
"generateCpuProfile",
"generateTrace",
"help",
"help",
"ignoreDeprecations",
"importHelpers",
"importsNotUsedAsValues",
"incremental",
"init",
"inlineSourceMap",
"inlineSources",
"isolatedModules",
"jsx",
"jsxFactory",
"jsxFragmentFactory",
"jsxImportSource",
"keepNotations",
"keyofStringsOnly",
"lib",
"listEmittedFiles",
"listFiles",
"listFilesOnly",
"locale",
"mapRoot",
"maxNodeModuleJsDepth",
"module",
"moduleDetection",
"moduleResolution",
"moduleSuffixes",
"newLine",
"noEmit",
"noEmitHelpers",
"noEmitOnError",
"noErrorTruncation",
"noErrorValidation",
"noErrors",
"noErrorsCutted",
"noFallthroughCasesInSwitch",
"noImplicitAny",
"noImplicitOverride",
"noImplicitReturns",
"noImplicitThis",
"noImplicitUseStrict",
"noLib",
"noPropertyAccessFromIndexSignature",
"noResolve",
"noStaticSemanticInfo",
"noStrictGenericChecks",
"noUncheckedIndexedAccess",
"noUnusedLocals",
"noUnusedParameters",
"out",
"outDir",
"outFile",
"paths",
"plugins",
"preserveConstEnums",
"preserveSymlinks",
"preserveValueImports",
"preserveWatchOutput",
"pretty",
"project",
"reactNamespace",
"removeComments",
"resolveJsonModule",
"resolvePackageJsonExports",
"resolvePackageJsonImports",
"rootDir",
"rootDirs",
"showConfig",
"showEmit",
"showEmittedFile",
"skipDefaultLibCheck",
"skipLibCheck",
"sourceMap",
"sourceRoot",
"strict",
"strictBindCallApply",
"strictFunctionTypes",
"strictNullChecks",
"strictPropertyInitialization",
"stripInternal",
"suppressExcessPropertyErrors",
"suppressImplicitAnyIndexErrors",
"target",
"traceResolution",
"tsBuildInfoFile",
"typeRoots",
"types",
"useDefineForClassFields",
"useUnknownInCatchVariables",
"verbatimModuleSyntax",
"version",
"watch"
];
function removeTwoslashNotations(code, customTags) {
const meta = {
removals: []
};
const tags = [
...customTags ?? [],
...flagKeys
];
Array.from(code.matchAll(reConfigBoolean)).forEach((match) => {
if (!tags.includes(match[1]))
return;
meta.removals.push([match.index, match.index + match[0].length + 1]);
});
Array.from(code.matchAll(reConfigValue)).forEach((match) => {
if (!tags.includes(match[1]))
return;
meta.removals.push([match.index, match.index + match[0].length + 1]);
});
findCutNotations(code, meta);
Array.from(code.matchAll(reAnnonateMarkers)).forEach((match) => {
const index = match.index;
meta.removals.push([index, index + match[0].length + 1]);
});
return twoslashProtocol.removeCodeRanges(code, meta.removals).code;
}
exports.TwoslashError = TwoslashError;
exports.defaultCompilerOptions = defaultCompilerOptions;
exports.defaultHandbookOptions = defaultHandbookOptions;
exports.findCutNotations = findCutNotations;
exports.findFlagNotations = findFlagNotations;
exports.findQueryMarkers = findQueryMarkers;
exports.getExtension = getExtension;
exports.getIdentifierTextSpans = getIdentifierTextSpans;
exports.getObjectHash = getObjectHash;
exports.removeTsExtension = removeTsExtension;
exports.removeTwoslashNotations = removeTwoslashNotations;
exports.splitFiles = splitFiles;
exports.typesToExtension = typesToExtension;