feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+19
View File
@@ -0,0 +1,19 @@
The MIT License (MIT)
Copyright (c) Microsoft Corporation
Copyright (c) 2023-PRESENT Anthony Fu <https://github.com/antfu>
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.
+32
View File
@@ -0,0 +1,32 @@
<img src="https://twoslash.netlify.app/logo.svg" alt="Twoslash Logo" width="150">
# Twoslash
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![bundle][bundle-src]][bundle-href]
[![JSDocs][jsdocs-src]][jsdocs-href]
[![License][license-src]][license-href]
[📚 Documentation](https://twoslash.netlify.app/) | [⚙️ Migration Guide](https://twoslash.netlify.app/guide/migrate)
A markup format for TypeScript code, ideal for creating self-contained code samples which let the TypeScript compiler do the extra leg-work. Inspired by the [fourslash test system](https://github.com/orta/typescript-notes/blob/master/systems/testing/fourslash.md).
This project is the successor of [`@typescript/twoslash`](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/ts-twoslasher).
## License
MIT License © [Orta Therox](https://github.com/orta), [Anthony Fu](https://github.com/antfu), Microsoft Corporation
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/twoslash?style=flat&colorA=161514&colorB=EAB836
[npm-version-href]: https://npmjs.com/package/twoslash
[npm-downloads-src]: https://img.shields.io/npm/dm/twoslash?style=flat&colorA=161514&colorB=E66041
[npm-downloads-href]: https://npmjs.com/package/twoslash
[bundle-src]: https://img.shields.io/bundlephobia/minzip/twoslash?style=flat&colorA=161514&colorB=45627B&label=minzip
[bundle-href]: https://bundlephobia.com/result?p=twoslash
[license-src]: https://img.shields.io/github/license/twoslashes/twoslash.svg?style=flat&colorA=161514&colorB=45627B
[license-href]: https://github.com/twoslashes/twoslash/blob/main/LICENSE
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-161514?style=flat&colorA=161514&colorB=EAB836
[jsdocs-href]: https://www.jsdocs.io/package/twoslash
+502
View File
@@ -0,0 +1,502 @@
'use strict';
const vfs = require('@typescript/vfs');
const twoslashProtocol = require('twoslash-protocol');
const fallback = require('./shared/twoslash.9a66b1ce.cjs');
const types = require('twoslash-protocol/types');
function validateCodeForErrors(relevantErrors, handbookOptions, vfsRoot) {
const unspecifiedErrors = relevantErrors.filter((e) => e.code && !handbookOptions.errors.includes(e.code));
const errorsFound = Array.from(new Set(unspecifiedErrors.map((e) => e.code))).join(" ");
if (unspecifiedErrors.length) {
const errorsToShow = new Set(relevantErrors.map((e) => e.code));
const codeToAdd = `// @errors: ${Array.from(errorsToShow).join(" ")}`;
const missing = handbookOptions.errors.length ? `
The existing annotation specified ${handbookOptions.errors.join(" ")}` : `
Expected: ${codeToAdd}`;
const filesToErrors = {};
const noFiles = [];
unspecifiedErrors.forEach((d) => {
const fileRef = d.filename?.replace(vfsRoot, "");
if (!fileRef) {
noFiles.push(d);
} else {
const existing = filesToErrors[fileRef];
if (existing)
existing.push(d);
else
filesToErrors[fileRef] = [d];
}
});
const showDiagnostics = (title, diags) => {
return `${title}
${diags.map((e) => `[${e.code}] ${e.start} - ${e.text}`).join("\n ")}`;
};
const innerDiags = [];
if (noFiles.length)
innerDiags.push(showDiagnostics("Ambient Errors", noFiles));
Object.keys(filesToErrors).forEach((filepath) => {
innerDiags.push(showDiagnostics(filepath, filesToErrors[filepath]));
});
const allMessages = innerDiags.join("\n\n");
const newErr = new fallback.TwoslashError(
`Errors were thrown in the sample, but not included in an error tag`,
`These errors were not marked as being expected: ${errorsFound}. ${missing}`,
`Compiler Errors:
${allMessages}`
);
throw newErr;
}
}
function createTwoslasher(createOptions = {}) {
const ts = createOptions.tsModule;
const tsOptionDeclarations = ts.optionDeclarations;
const useFS = !!createOptions.fsMap;
const _root = createOptions.vfsRoot.replace(/\\/g, "/");
const vfs$1 = createOptions.fsMap || /* @__PURE__ */ new Map();
const system = useFS ? vfs.createSystem(vfs$1) : createCacheableFSBackedSystem(vfs$1, _root, ts, createOptions.tsLibDirectory, createOptions.fsCache);
const fsRoot = useFS ? "/" : `${_root}/`;
const cache = createOptions.cache === false ? void 0 : createOptions.cache instanceof Map ? createOptions.cache : /* @__PURE__ */ new Map();
function getEnv(compilerOptions) {
if (!cache)
return vfs.createVirtualTypeScriptEnvironment(system, [], ts, compilerOptions, createOptions.customTransformers);
const key = fallback.getObjectHash(compilerOptions);
if (!cache?.has(key)) {
const env = vfs.createVirtualTypeScriptEnvironment(system, [], ts, compilerOptions, createOptions.customTransformers);
cache?.set(key, env);
return env;
}
return cache.get(key);
}
function twoslasher2(code, extension = "ts", options = {}) {
const meta = {
extension: fallback.typesToExtension(extension),
compilerOptions: {
...fallback.defaultCompilerOptions,
baseUrl: fsRoot,
...createOptions.compilerOptions,
...options.compilerOptions
},
handbookOptions: {
...fallback.defaultHandbookOptions,
...createOptions.handbookOptions,
...options.handbookOptions
},
removals: [],
flagNotations: [],
virtualFiles: [],
positionQueries: options.positionQueries || [],
positionCompletions: options.positionCompletions || [],
positionHighlights: options.positionHighlights || []
};
const {
customTags = createOptions.customTags || [],
shouldGetHoverInfo = createOptions.shouldGetHoverInfo || (() => true),
filterNode = createOptions.filterNode,
extraFiles = createOptions.extraFiles || {}
} = options;
const defaultFilename = `index.${meta.extension}`;
let nodes = [];
const isInRemoval = (index) => index >= code.length || index < 0 || twoslashProtocol.isInRanges(index, meta.removals, false);
meta.flagNotations = fallback.findFlagNotations(code, customTags, tsOptionDeclarations);
for (const flag of meta.flagNotations) {
switch (flag.type) {
case "unknown":
continue;
case "compilerOptions":
meta.compilerOptions[flag.name] = flag.value;
break;
case "handbookOptions":
meta.handbookOptions[flag.name] = flag.value;
break;
case "tag":
nodes.push({
type: "tag",
name: flag.name,
start: flag.end,
length: 0,
text: flag.value
});
break;
}
meta.removals.push([flag.start, flag.end]);
}
if (!meta.handbookOptions.noErrorValidation) {
const unknownFlags = meta.flagNotations.filter((i) => i.type === "unknown");
if (unknownFlags.length) {
throw new fallback.TwoslashError(
`Unknown inline compiler flags`,
`The following flags are either valid TSConfig nor handbook options:
${unknownFlags.map((i) => `@${i.name}`).join(", ")}`,
`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.`
);
}
}
const env = getEnv(meta.compilerOptions);
const ls = env.languageService;
const pc = twoslashProtocol.createPositionConverter(code);
fallback.findCutNotations(code, meta);
fallback.findQueryMarkers(code, meta, pc);
const supportedFileTyes = ["js", "jsx", "ts", "tsx"];
meta.virtualFiles = fallback.splitFiles(code, defaultFilename, fsRoot);
const identifiersMap = /* @__PURE__ */ new Map();
function getIdentifiersOfFile(file) {
if (!identifiersMap.has(file.filename)) {
const source = env.getSourceFile(file.filepath);
identifiersMap.set(file.filename, fallback.getIdentifierTextSpans(ts, source, file.offset - (file.prepend?.length || 0)));
}
return identifiersMap.get(file.filename);
}
function getFileAtPosition(pos) {
return meta.virtualFiles.find((i) => twoslashProtocol.isInRange(pos, [i.offset, i.offset + i.content.length]));
}
function getQuickInfo(file, start, target) {
const quickInfo = ls.getQuickInfoAtPosition(file.filepath, getOffsetInFile(start, file));
if (quickInfo && quickInfo.displayParts) {
const text = quickInfo.displayParts.map((dp) => dp.text).join("");
const docs = quickInfo.documentation?.map((d) => d.text).join("\n") || void 0;
const tags = quickInfo.tags?.map((t) => [t.name, t.text?.map((i) => i.text).join("")]);
return {
type: "hover",
text,
docs,
tags,
start,
length: target.length,
target
};
}
}
Object.entries(extraFiles).forEach(([filename, content]) => {
if (!meta.virtualFiles.find((i) => i.filename === filename)) {
env.createFile(
fsRoot + filename,
typeof content === "string" ? content : (content.prepend || "") + (content.append || "")
);
}
});
for (const file of meta.virtualFiles) {
if (supportedFileTyes.includes(file.extension) || file.extension === "json" && meta.compilerOptions.resolveJsonModule) {
file.supportLsp = true;
const extra = extraFiles[file.filename];
if (extra && typeof extra !== "string") {
file.append = extra.append;
file.prepend = extra.prepend;
}
env.createFile(file.filepath, getFileContent(file));
getIdentifiersOfFile(file);
}
}
function getOffsetInFile(offset, file) {
return offset - file.offset + (file.prepend?.length || 0);
}
function getFileContent(file) {
return (file.prepend || "") + file.content + (file.append || "");
}
if (!meta.handbookOptions.showEmit) {
for (const file of meta.virtualFiles) {
if (!file.supportLsp)
continue;
if (!meta.handbookOptions.noStaticSemanticInfo) {
const identifiers = getIdentifiersOfFile(file);
for (const [start, _end, target] of identifiers) {
if (isInRemoval(start))
continue;
if (!shouldGetHoverInfo(target, start, file.filename))
continue;
const node = getQuickInfo(file, start, target);
if (node)
nodes.push(node);
}
}
}
for (const query of meta.positionQueries) {
if (isInRemoval(query)) {
throw new fallback.TwoslashError(
`Invalid quick info query`,
`The request on line ${pc.indexToPos(query).line + 2} for quickinfo via ^? is in a removal range.`,
`This is likely that the positioning is off.`
);
}
const file = getFileAtPosition(query);
const identifiers = getIdentifiersOfFile(file);
const id = identifiers.find((i) => twoslashProtocol.isInRange(query, i));
let node;
if (id)
node = getQuickInfo(file, id[0], id[2]);
if (node) {
node.type = "query";
nodes.push(node);
} else {
const pos = pc.indexToPos(query);
throw new fallback.TwoslashError(
`Invalid quick info query`,
`The request on line ${pos.line + 2} in ${file.filename} for quickinfo via ^? returned nothing from the compiler.`,
`This is likely that the positioning is off.`
);
}
}
for (const highlight of meta.positionHighlights) {
nodes.push({
type: "highlight",
start: highlight[0],
length: highlight[1] - highlight[0],
text: highlight[2]
});
}
for (const target of meta.positionCompletions) {
const file = getFileAtPosition(target);
if (isInRemoval(target) || !file) {
throw new fallback.TwoslashError(
`Invalid completion query`,
`The request on line ${pc.indexToPos(target).line + 2} for completions via ^| is in a removal range.`,
`This is likely that the positioning is off.`
);
}
let prefix = code.slice(0, target).match(/[$\w]+$/)?.[0] || "";
prefix = prefix.split(".").pop();
let completions = [];
if (prefix) {
const result = ls.getCompletionsAtPosition(file.filepath, getOffsetInFile(target, file) - 1, {
triggerKind: 1,
includeCompletionsForModuleExports: false
});
completions = result?.entries ?? [];
prefix = completions[0]?.replacementSpan && code.slice(
completions[0].replacementSpan.start,
target
) || prefix;
completions = completions.filter((i) => i.name.startsWith(prefix));
} else {
prefix = code[target - 1];
if (prefix) {
const result = ls.getCompletionsAtPosition(file.filepath, getOffsetInFile(target, file), {
triggerKind: 2,
triggerCharacter: prefix,
includeCompletionsForModuleExports: false
});
completions = result?.entries ?? [];
if (completions[0]?.replacementSpan?.length) {
prefix = code.slice(
completions[0].replacementSpan.start,
target
) || prefix;
const newCompletions = completions.filter((i) => i.name.startsWith(prefix));
if (newCompletions.length)
completions = newCompletions;
}
}
}
if (!completions?.length && !meta.handbookOptions.noErrorValidation) {
const pos = pc.indexToPos(target);
throw new fallback.TwoslashError(
`Invalid completion query`,
`The request on line ${pos.line} in ${file.filename} for completions via ^| returned no completions from the compiler. (prefix: ${prefix})`,
`This is likely that the positioning is off.`
);
}
nodes.push({
type: "completion",
start: target,
length: 0,
completions,
completionsPrefix: prefix
});
}
}
let errorNodes = [];
for (const file of meta.virtualFiles) {
if (!file.supportLsp)
continue;
if (meta.handbookOptions.noErrors !== true) {
env.updateFile(file.filepath, getFileContent(file));
const diagnostics = [
...ls.getSemanticDiagnostics(file.filepath),
...ls.getSyntacticDiagnostics(file.filepath)
];
const ignores = Array.isArray(meta.handbookOptions.noErrors) ? meta.handbookOptions.noErrors : [];
for (const diagnostic of diagnostics) {
if (diagnostic.file?.fileName !== file.filepath)
continue;
if (ignores.includes(diagnostic.code))
continue;
const start = diagnostic.start + file.offset - (file.prepend?.length || 0);
if (meta.handbookOptions.noErrorsCutted && isInRemoval(start))
continue;
errorNodes.push({
type: "error",
start,
length: diagnostic.length,
code: diagnostic.code,
filename: file.filename,
id: `err-${diagnostic.code}-${start}-${diagnostic.length}`,
text: ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"),
level: diagnosticCategoryToErrorLevel(diagnostic.category)
});
}
}
}
if (filterNode) {
nodes = nodes.filter(filterNode);
errorNodes = errorNodes.filter(filterNode);
}
nodes.push(...errorNodes);
if (!meta.handbookOptions.noErrorValidation && errorNodes.length)
validateCodeForErrors(errorNodes, meta.handbookOptions, fsRoot);
let outputCode = code;
if (meta.handbookOptions.showEmit) {
if (meta.handbookOptions.keepNotations) {
throw new fallback.TwoslashError(
`Option 'showEmit' cannot be used with 'keepNotations'`,
"With `showEmit` enabled, the output will always be the emitted code",
"Remove either option to continue"
);
}
if (!meta.handbookOptions.keepNotations) {
const { code: removedCode } = twoslashProtocol.removeCodeRanges(outputCode, meta.removals);
const files = fallback.splitFiles(removedCode, defaultFilename, fsRoot);
for (const file of files)
env.updateFile(file.filepath, getFileContent(file));
}
const emitFilename = meta.handbookOptions.showEmittedFile ? meta.handbookOptions.showEmittedFile : meta.compilerOptions.jsx === 1 ? "index.jsx" : "index.js";
let emitSource = meta.virtualFiles.find((i) => fallback.removeTsExtension(i.filename) === fallback.removeTsExtension(emitFilename))?.filename;
if (!emitSource && !meta.compilerOptions.outFile) {
const allFiles = meta.virtualFiles.map((i) => i.filename).join(", ");
throw new fallback.TwoslashError(
`Could not find source file to show the emit for`,
`Cannot find the corresponding **source** file: '${emitFilename}'`,
`Looked for: ${emitSource} in the vfs - which contains: ${allFiles}`
);
}
if (meta.compilerOptions.outFile)
emitSource = meta.virtualFiles[0].filename;
const output = ls.getEmitOutput(fsRoot + emitSource);
const outfile = output.outputFiles.find((o) => o.name === fsRoot + emitFilename || o.name === emitFilename);
if (!outfile) {
const allFiles = output.outputFiles.map((o) => o.name).join(", ");
throw new fallback.TwoslashError(
`Cannot find the output file in the Twoslash VFS`,
`Looking for ${emitFilename} in the Twoslash vfs after compiling`,
`Looked for" ${fsRoot + emitFilename} in the vfs - which contains ${allFiles}.`
);
}
outputCode = outfile.text;
meta.extension = fallback.typesToExtension(fallback.getExtension(outfile.name));
meta.removals.length = 0;
nodes.length = 0;
}
if (!meta.handbookOptions.keepNotations) {
const removed = twoslashProtocol.removeCodeRanges(outputCode, meta.removals, nodes);
outputCode = removed.code;
nodes = removed.nodes;
meta.removals = removed.removals;
}
const indexToPos = outputCode === code ? pc.indexToPos : twoslashProtocol.createPositionConverter(outputCode).indexToPos;
const resolvedNodes = twoslashProtocol.resolveNodePositions(nodes, indexToPos);
for (const file of meta.virtualFiles)
env.createFile(file.filepath, "");
for (const file of Object.keys(extraFiles))
env.createFile(fsRoot + file, "");
return {
code: outputCode,
nodes: resolvedNodes,
meta,
get queries() {
return this.nodes.filter((i) => i.type === "query");
},
get completions() {
return this.nodes.filter((i) => i.type === "completion");
},
get errors() {
return this.nodes.filter((i) => i.type === "error");
},
get highlights() {
return this.nodes.filter((i) => i.type === "highlight");
},
get hovers() {
return this.nodes.filter((i) => i.type === "hover");
},
get tags() {
return this.nodes.filter((i) => i.type === "tag");
}
};
}
twoslasher2.getCacheMap = () => {
return cache;
};
return twoslasher2;
}
function createCacheableFSBackedSystem(vfs$1, root, ts, tsLibDirectory, enableFsCache = true) {
function withCache(fn) {
const cache = /* @__PURE__ */ new Map();
return (key) => {
const cached = cache.get(key);
if (cached !== void 0)
return cached;
const result = fn(key);
cache.set(key, result);
return result;
};
}
const cachedReadFile = withCache(ts.sys.readFile);
const cachedTs = enableFsCache ? {
...ts,
sys: {
...ts.sys,
directoryExists: withCache(ts.sys.directoryExists),
fileExists: withCache(ts.sys.fileExists),
...ts.sys.realpath ? { realpath: withCache(ts.sys.realpath) } : {},
readFile(path, encoding) {
if (encoding === void 0)
return cachedReadFile(path);
return ts.sys.readFile(path, encoding);
}
}
} : ts;
return {
...vfs.createFSBackedSystem(vfs$1, root, cachedTs, tsLibDirectory),
// To work with non-hoisted packages structure
realpath(path) {
if (vfs$1.has(path))
return path;
return cachedTs.sys.realpath?.(path) || path;
}
};
}
function twoslasher(code, lang, opts) {
return createTwoslasher({
...opts,
cache: false
})(code, lang);
}
function diagnosticCategoryToErrorLevel(e) {
switch (e) {
case 0:
return "warning";
case 1:
return "error";
case 2:
return "suggestion";
case 3:
return "message";
default:
return void 0;
}
}
exports.TwoslashError = fallback.TwoslashError;
exports.defaultCompilerOptions = fallback.defaultCompilerOptions;
exports.defaultHandbookOptions = fallback.defaultHandbookOptions;
exports.findCutNotations = fallback.findCutNotations;
exports.findFlagNotations = fallback.findFlagNotations;
exports.findQueryMarkers = fallback.findQueryMarkers;
exports.getObjectHash = fallback.getObjectHash;
exports.removeTwoslashNotations = fallback.removeTwoslashNotations;
exports.createTwoslasher = createTwoslasher;
exports.twoslasher = twoslasher;
exports.validateCodeForErrors = validateCodeForErrors;
Object.keys(types).forEach(function (k) {
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = types[k];
});
+20
View File
@@ -0,0 +1,20 @@
import { m as CreateTwoslashOptions, c as TwoslashInstance, b as TwoslashOptions, a as TwoslashReturn } from './shared/twoslash.662cb7fb.cjs';
export { C as CompilerOptionDeclaration, E as ExtraFiles, H as HandbookOptions, P as ParsedFlagNotation, l as TS, j as TwoslashError, T as TwoslashExecuteOptions, k as TwoslashFunction, n as TwoslashReturnMeta, V as VirtualFile, h as defaultCompilerOptions, i as defaultHandbookOptions, f as findCutNotations, d as findFlagNotations, e as findQueryMarkers, g as getObjectHash, v as validateCodeForErrors } from './shared/twoslash.662cb7fb.cjs';
export * from 'twoslash-protocol/types';
export { removeTwoslashNotations } from './fallback.cjs';
import '@typescript/vfs';
import 'typescript';
import 'twoslash-protocol';
/**
* Create a Twoslash instance with cached TS environments
*/
declare function createTwoslasher(createOptions?: CreateTwoslashOptions): TwoslashInstance;
/**
* Run Twoslash on a string of code
*
* It's recommended to use `createTwoslash` for better performance on multiple runs
*/
declare function twoslasher(code: string, lang?: string, opts?: Partial<TwoslashOptions>): TwoslashReturn;
export { CreateTwoslashOptions, TwoslashInstance, TwoslashOptions, TwoslashReturn, createTwoslasher, twoslasher };
+20
View File
@@ -0,0 +1,20 @@
import { m as CreateTwoslashOptions, c as TwoslashInstance, b as TwoslashOptions, a as TwoslashReturn } from './shared/twoslash.662cb7fb.mjs';
export { C as CompilerOptionDeclaration, E as ExtraFiles, H as HandbookOptions, P as ParsedFlagNotation, l as TS, j as TwoslashError, T as TwoslashExecuteOptions, k as TwoslashFunction, n as TwoslashReturnMeta, V as VirtualFile, h as defaultCompilerOptions, i as defaultHandbookOptions, f as findCutNotations, d as findFlagNotations, e as findQueryMarkers, g as getObjectHash, v as validateCodeForErrors } from './shared/twoslash.662cb7fb.mjs';
export * from 'twoslash-protocol/types';
export { removeTwoslashNotations } from './fallback.mjs';
import '@typescript/vfs';
import 'typescript';
import 'twoslash-protocol';
/**
* Create a Twoslash instance with cached TS environments
*/
declare function createTwoslasher(createOptions?: CreateTwoslashOptions): TwoslashInstance;
/**
* Run Twoslash on a string of code
*
* It's recommended to use `createTwoslash` for better performance on multiple runs
*/
declare function twoslasher(code: string, lang?: string, opts?: Partial<TwoslashOptions>): TwoslashReturn;
export { CreateTwoslashOptions, TwoslashInstance, TwoslashOptions, TwoslashReturn, createTwoslasher, twoslasher };
+20
View File
@@ -0,0 +1,20 @@
import { m as CreateTwoslashOptions, c as TwoslashInstance, b as TwoslashOptions, a as TwoslashReturn } from './shared/twoslash.662cb7fb.js';
export { C as CompilerOptionDeclaration, E as ExtraFiles, H as HandbookOptions, P as ParsedFlagNotation, l as TS, j as TwoslashError, T as TwoslashExecuteOptions, k as TwoslashFunction, n as TwoslashReturnMeta, V as VirtualFile, h as defaultCompilerOptions, i as defaultHandbookOptions, f as findCutNotations, d as findFlagNotations, e as findQueryMarkers, g as getObjectHash, v as validateCodeForErrors } from './shared/twoslash.662cb7fb.js';
export * from 'twoslash-protocol/types';
export { removeTwoslashNotations } from './fallback.js';
import '@typescript/vfs';
import 'typescript';
import 'twoslash-protocol';
/**
* Create a Twoslash instance with cached TS environments
*/
declare function createTwoslasher(createOptions?: CreateTwoslashOptions): TwoslashInstance;
/**
* Run Twoslash on a string of code
*
* It's recommended to use `createTwoslash` for better performance on multiple runs
*/
declare function twoslasher(code: string, lang?: string, opts?: Partial<TwoslashOptions>): TwoslashReturn;
export { CreateTwoslashOptions, TwoslashInstance, TwoslashOptions, TwoslashReturn, createTwoslasher, twoslasher };
+488
View File
@@ -0,0 +1,488 @@
import { createSystem, createFSBackedSystem, createVirtualTypeScriptEnvironment } from '@typescript/vfs';
import { createPositionConverter, isInRange, removeCodeRanges, resolveNodePositions, isInRanges } from 'twoslash-protocol';
import { T as TwoslashError, t as typesToExtension, d as defaultCompilerOptions, c as defaultHandbookOptions, a as findFlagNotations, f as findCutNotations, b as findQueryMarkers, s as splitFiles, e as removeTsExtension, h as getExtension, g as getObjectHash, i as getIdentifierTextSpans } from './shared/twoslash.a8564bda.mjs';
export { r as removeTwoslashNotations } from './shared/twoslash.a8564bda.mjs';
export * from 'twoslash-protocol/types';
function validateCodeForErrors(relevantErrors, handbookOptions, vfsRoot) {
const unspecifiedErrors = relevantErrors.filter((e) => e.code && !handbookOptions.errors.includes(e.code));
const errorsFound = Array.from(new Set(unspecifiedErrors.map((e) => e.code))).join(" ");
if (unspecifiedErrors.length) {
const errorsToShow = new Set(relevantErrors.map((e) => e.code));
const codeToAdd = `// @errors: ${Array.from(errorsToShow).join(" ")}`;
const missing = handbookOptions.errors.length ? `
The existing annotation specified ${handbookOptions.errors.join(" ")}` : `
Expected: ${codeToAdd}`;
const filesToErrors = {};
const noFiles = [];
unspecifiedErrors.forEach((d) => {
const fileRef = d.filename?.replace(vfsRoot, "");
if (!fileRef) {
noFiles.push(d);
} else {
const existing = filesToErrors[fileRef];
if (existing)
existing.push(d);
else
filesToErrors[fileRef] = [d];
}
});
const showDiagnostics = (title, diags) => {
return `${title}
${diags.map((e) => `[${e.code}] ${e.start} - ${e.text}`).join("\n ")}`;
};
const innerDiags = [];
if (noFiles.length)
innerDiags.push(showDiagnostics("Ambient Errors", noFiles));
Object.keys(filesToErrors).forEach((filepath) => {
innerDiags.push(showDiagnostics(filepath, filesToErrors[filepath]));
});
const allMessages = innerDiags.join("\n\n");
const newErr = new TwoslashError(
`Errors were thrown in the sample, but not included in an error tag`,
`These errors were not marked as being expected: ${errorsFound}. ${missing}`,
`Compiler Errors:
${allMessages}`
);
throw newErr;
}
}
function createTwoslasher(createOptions = {}) {
const ts = createOptions.tsModule;
const tsOptionDeclarations = ts.optionDeclarations;
const useFS = !!createOptions.fsMap;
const _root = createOptions.vfsRoot.replace(/\\/g, "/");
const vfs = createOptions.fsMap || /* @__PURE__ */ new Map();
const system = useFS ? createSystem(vfs) : createCacheableFSBackedSystem(vfs, _root, ts, createOptions.tsLibDirectory, createOptions.fsCache);
const fsRoot = useFS ? "/" : `${_root}/`;
const cache = createOptions.cache === false ? void 0 : createOptions.cache instanceof Map ? createOptions.cache : /* @__PURE__ */ new Map();
function getEnv(compilerOptions) {
if (!cache)
return createVirtualTypeScriptEnvironment(system, [], ts, compilerOptions, createOptions.customTransformers);
const key = getObjectHash(compilerOptions);
if (!cache?.has(key)) {
const env = createVirtualTypeScriptEnvironment(system, [], ts, compilerOptions, createOptions.customTransformers);
cache?.set(key, env);
return env;
}
return cache.get(key);
}
function twoslasher2(code, extension = "ts", options = {}) {
const meta = {
extension: typesToExtension(extension),
compilerOptions: {
...defaultCompilerOptions,
baseUrl: fsRoot,
...createOptions.compilerOptions,
...options.compilerOptions
},
handbookOptions: {
...defaultHandbookOptions,
...createOptions.handbookOptions,
...options.handbookOptions
},
removals: [],
flagNotations: [],
virtualFiles: [],
positionQueries: options.positionQueries || [],
positionCompletions: options.positionCompletions || [],
positionHighlights: options.positionHighlights || []
};
const {
customTags = createOptions.customTags || [],
shouldGetHoverInfo = createOptions.shouldGetHoverInfo || (() => true),
filterNode = createOptions.filterNode,
extraFiles = createOptions.extraFiles || {}
} = options;
const defaultFilename = `index.${meta.extension}`;
let nodes = [];
const isInRemoval = (index) => index >= code.length || index < 0 || isInRanges(index, meta.removals, false);
meta.flagNotations = findFlagNotations(code, customTags, tsOptionDeclarations);
for (const flag of meta.flagNotations) {
switch (flag.type) {
case "unknown":
continue;
case "compilerOptions":
meta.compilerOptions[flag.name] = flag.value;
break;
case "handbookOptions":
meta.handbookOptions[flag.name] = flag.value;
break;
case "tag":
nodes.push({
type: "tag",
name: flag.name,
start: flag.end,
length: 0,
text: flag.value
});
break;
}
meta.removals.push([flag.start, flag.end]);
}
if (!meta.handbookOptions.noErrorValidation) {
const unknownFlags = meta.flagNotations.filter((i) => i.type === "unknown");
if (unknownFlags.length) {
throw new TwoslashError(
`Unknown inline compiler flags`,
`The following flags are either valid TSConfig nor handbook options:
${unknownFlags.map((i) => `@${i.name}`).join(", ")}`,
`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.`
);
}
}
const env = getEnv(meta.compilerOptions);
const ls = env.languageService;
const pc = createPositionConverter(code);
findCutNotations(code, meta);
findQueryMarkers(code, meta, pc);
const supportedFileTyes = ["js", "jsx", "ts", "tsx"];
meta.virtualFiles = splitFiles(code, defaultFilename, fsRoot);
const identifiersMap = /* @__PURE__ */ new Map();
function getIdentifiersOfFile(file) {
if (!identifiersMap.has(file.filename)) {
const source = env.getSourceFile(file.filepath);
identifiersMap.set(file.filename, getIdentifierTextSpans(ts, source, file.offset - (file.prepend?.length || 0)));
}
return identifiersMap.get(file.filename);
}
function getFileAtPosition(pos) {
return meta.virtualFiles.find((i) => isInRange(pos, [i.offset, i.offset + i.content.length]));
}
function getQuickInfo(file, start, target) {
const quickInfo = ls.getQuickInfoAtPosition(file.filepath, getOffsetInFile(start, file));
if (quickInfo && quickInfo.displayParts) {
const text = quickInfo.displayParts.map((dp) => dp.text).join("");
const docs = quickInfo.documentation?.map((d) => d.text).join("\n") || void 0;
const tags = quickInfo.tags?.map((t) => [t.name, t.text?.map((i) => i.text).join("")]);
return {
type: "hover",
text,
docs,
tags,
start,
length: target.length,
target
};
}
}
Object.entries(extraFiles).forEach(([filename, content]) => {
if (!meta.virtualFiles.find((i) => i.filename === filename)) {
env.createFile(
fsRoot + filename,
typeof content === "string" ? content : (content.prepend || "") + (content.append || "")
);
}
});
for (const file of meta.virtualFiles) {
if (supportedFileTyes.includes(file.extension) || file.extension === "json" && meta.compilerOptions.resolveJsonModule) {
file.supportLsp = true;
const extra = extraFiles[file.filename];
if (extra && typeof extra !== "string") {
file.append = extra.append;
file.prepend = extra.prepend;
}
env.createFile(file.filepath, getFileContent(file));
getIdentifiersOfFile(file);
}
}
function getOffsetInFile(offset, file) {
return offset - file.offset + (file.prepend?.length || 0);
}
function getFileContent(file) {
return (file.prepend || "") + file.content + (file.append || "");
}
if (!meta.handbookOptions.showEmit) {
for (const file of meta.virtualFiles) {
if (!file.supportLsp)
continue;
if (!meta.handbookOptions.noStaticSemanticInfo) {
const identifiers = getIdentifiersOfFile(file);
for (const [start, _end, target] of identifiers) {
if (isInRemoval(start))
continue;
if (!shouldGetHoverInfo(target, start, file.filename))
continue;
const node = getQuickInfo(file, start, target);
if (node)
nodes.push(node);
}
}
}
for (const query of meta.positionQueries) {
if (isInRemoval(query)) {
throw new TwoslashError(
`Invalid quick info query`,
`The request on line ${pc.indexToPos(query).line + 2} for quickinfo via ^? is in a removal range.`,
`This is likely that the positioning is off.`
);
}
const file = getFileAtPosition(query);
const identifiers = getIdentifiersOfFile(file);
const id = identifiers.find((i) => isInRange(query, i));
let node;
if (id)
node = getQuickInfo(file, id[0], id[2]);
if (node) {
node.type = "query";
nodes.push(node);
} else {
const pos = pc.indexToPos(query);
throw new TwoslashError(
`Invalid quick info query`,
`The request on line ${pos.line + 2} in ${file.filename} for quickinfo via ^? returned nothing from the compiler.`,
`This is likely that the positioning is off.`
);
}
}
for (const highlight of meta.positionHighlights) {
nodes.push({
type: "highlight",
start: highlight[0],
length: highlight[1] - highlight[0],
text: highlight[2]
});
}
for (const target of meta.positionCompletions) {
const file = getFileAtPosition(target);
if (isInRemoval(target) || !file) {
throw new TwoslashError(
`Invalid completion query`,
`The request on line ${pc.indexToPos(target).line + 2} for completions via ^| is in a removal range.`,
`This is likely that the positioning is off.`
);
}
let prefix = code.slice(0, target).match(/[$\w]+$/)?.[0] || "";
prefix = prefix.split(".").pop();
let completions = [];
if (prefix) {
const result = ls.getCompletionsAtPosition(file.filepath, getOffsetInFile(target, file) - 1, {
triggerKind: 1,
includeCompletionsForModuleExports: false
});
completions = result?.entries ?? [];
prefix = completions[0]?.replacementSpan && code.slice(
completions[0].replacementSpan.start,
target
) || prefix;
completions = completions.filter((i) => i.name.startsWith(prefix));
} else {
prefix = code[target - 1];
if (prefix) {
const result = ls.getCompletionsAtPosition(file.filepath, getOffsetInFile(target, file), {
triggerKind: 2,
triggerCharacter: prefix,
includeCompletionsForModuleExports: false
});
completions = result?.entries ?? [];
if (completions[0]?.replacementSpan?.length) {
prefix = code.slice(
completions[0].replacementSpan.start,
target
) || prefix;
const newCompletions = completions.filter((i) => i.name.startsWith(prefix));
if (newCompletions.length)
completions = newCompletions;
}
}
}
if (!completions?.length && !meta.handbookOptions.noErrorValidation) {
const pos = pc.indexToPos(target);
throw new TwoslashError(
`Invalid completion query`,
`The request on line ${pos.line} in ${file.filename} for completions via ^| returned no completions from the compiler. (prefix: ${prefix})`,
`This is likely that the positioning is off.`
);
}
nodes.push({
type: "completion",
start: target,
length: 0,
completions,
completionsPrefix: prefix
});
}
}
let errorNodes = [];
for (const file of meta.virtualFiles) {
if (!file.supportLsp)
continue;
if (meta.handbookOptions.noErrors !== true) {
env.updateFile(file.filepath, getFileContent(file));
const diagnostics = [
...ls.getSemanticDiagnostics(file.filepath),
...ls.getSyntacticDiagnostics(file.filepath)
];
const ignores = Array.isArray(meta.handbookOptions.noErrors) ? meta.handbookOptions.noErrors : [];
for (const diagnostic of diagnostics) {
if (diagnostic.file?.fileName !== file.filepath)
continue;
if (ignores.includes(diagnostic.code))
continue;
const start = diagnostic.start + file.offset - (file.prepend?.length || 0);
if (meta.handbookOptions.noErrorsCutted && isInRemoval(start))
continue;
errorNodes.push({
type: "error",
start,
length: diagnostic.length,
code: diagnostic.code,
filename: file.filename,
id: `err-${diagnostic.code}-${start}-${diagnostic.length}`,
text: ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"),
level: diagnosticCategoryToErrorLevel(diagnostic.category)
});
}
}
}
if (filterNode) {
nodes = nodes.filter(filterNode);
errorNodes = errorNodes.filter(filterNode);
}
nodes.push(...errorNodes);
if (!meta.handbookOptions.noErrorValidation && errorNodes.length)
validateCodeForErrors(errorNodes, meta.handbookOptions, fsRoot);
let outputCode = code;
if (meta.handbookOptions.showEmit) {
if (meta.handbookOptions.keepNotations) {
throw new TwoslashError(
`Option 'showEmit' cannot be used with 'keepNotations'`,
"With `showEmit` enabled, the output will always be the emitted code",
"Remove either option to continue"
);
}
if (!meta.handbookOptions.keepNotations) {
const { code: removedCode } = removeCodeRanges(outputCode, meta.removals);
const files = splitFiles(removedCode, defaultFilename, fsRoot);
for (const file of files)
env.updateFile(file.filepath, getFileContent(file));
}
const emitFilename = meta.handbookOptions.showEmittedFile ? meta.handbookOptions.showEmittedFile : meta.compilerOptions.jsx === 1 ? "index.jsx" : "index.js";
let emitSource = meta.virtualFiles.find((i) => removeTsExtension(i.filename) === removeTsExtension(emitFilename))?.filename;
if (!emitSource && !meta.compilerOptions.outFile) {
const allFiles = meta.virtualFiles.map((i) => i.filename).join(", ");
throw new TwoslashError(
`Could not find source file to show the emit for`,
`Cannot find the corresponding **source** file: '${emitFilename}'`,
`Looked for: ${emitSource} in the vfs - which contains: ${allFiles}`
);
}
if (meta.compilerOptions.outFile)
emitSource = meta.virtualFiles[0].filename;
const output = ls.getEmitOutput(fsRoot + emitSource);
const outfile = output.outputFiles.find((o) => o.name === fsRoot + emitFilename || o.name === emitFilename);
if (!outfile) {
const allFiles = output.outputFiles.map((o) => o.name).join(", ");
throw new TwoslashError(
`Cannot find the output file in the Twoslash VFS`,
`Looking for ${emitFilename} in the Twoslash vfs after compiling`,
`Looked for" ${fsRoot + emitFilename} in the vfs - which contains ${allFiles}.`
);
}
outputCode = outfile.text;
meta.extension = typesToExtension(getExtension(outfile.name));
meta.removals.length = 0;
nodes.length = 0;
}
if (!meta.handbookOptions.keepNotations) {
const removed = removeCodeRanges(outputCode, meta.removals, nodes);
outputCode = removed.code;
nodes = removed.nodes;
meta.removals = removed.removals;
}
const indexToPos = outputCode === code ? pc.indexToPos : createPositionConverter(outputCode).indexToPos;
const resolvedNodes = resolveNodePositions(nodes, indexToPos);
for (const file of meta.virtualFiles)
env.createFile(file.filepath, "");
for (const file of Object.keys(extraFiles))
env.createFile(fsRoot + file, "");
return {
code: outputCode,
nodes: resolvedNodes,
meta,
get queries() {
return this.nodes.filter((i) => i.type === "query");
},
get completions() {
return this.nodes.filter((i) => i.type === "completion");
},
get errors() {
return this.nodes.filter((i) => i.type === "error");
},
get highlights() {
return this.nodes.filter((i) => i.type === "highlight");
},
get hovers() {
return this.nodes.filter((i) => i.type === "hover");
},
get tags() {
return this.nodes.filter((i) => i.type === "tag");
}
};
}
twoslasher2.getCacheMap = () => {
return cache;
};
return twoslasher2;
}
function createCacheableFSBackedSystem(vfs, root, ts, tsLibDirectory, enableFsCache = true) {
function withCache(fn) {
const cache = /* @__PURE__ */ new Map();
return (key) => {
const cached = cache.get(key);
if (cached !== void 0)
return cached;
const result = fn(key);
cache.set(key, result);
return result;
};
}
const cachedReadFile = withCache(ts.sys.readFile);
const cachedTs = enableFsCache ? {
...ts,
sys: {
...ts.sys,
directoryExists: withCache(ts.sys.directoryExists),
fileExists: withCache(ts.sys.fileExists),
...ts.sys.realpath ? { realpath: withCache(ts.sys.realpath) } : {},
readFile(path, encoding) {
if (encoding === void 0)
return cachedReadFile(path);
return ts.sys.readFile(path, encoding);
}
}
} : ts;
return {
...createFSBackedSystem(vfs, root, cachedTs, tsLibDirectory),
// To work with non-hoisted packages structure
realpath(path) {
if (vfs.has(path))
return path;
return cachedTs.sys.realpath?.(path) || path;
}
};
}
function twoslasher(code, lang, opts) {
return createTwoslasher({
...opts,
cache: false
})(code, lang);
}
function diagnosticCategoryToErrorLevel(e) {
switch (e) {
case 0:
return "warning";
case 1:
return "error";
case 2:
return "suggestion";
case 3:
return "message";
default:
return void 0;
}
}
export { TwoslashError, createTwoslasher, defaultCompilerOptions, defaultHandbookOptions, findCutNotations, findFlagNotations, findQueryMarkers, getObjectHash, twoslasher, validateCodeForErrors };
+8
View File
@@ -0,0 +1,8 @@
'use strict';
require('twoslash-protocol');
const fallback = require('./shared/twoslash.9a66b1ce.cjs');
exports.removeTwoslashNotations = fallback.removeTwoslashNotations;
+9
View File
@@ -0,0 +1,9 @@
/**
* A fallback function to strip out twoslash annotations from a string and does nothing else.
*
* This function does not returns the meta information about the removals.
* It's designed to be used as a fallback when Twoslash fails.
*/
declare function removeTwoslashNotations(code: string, customTags?: string[]): string;
export { removeTwoslashNotations };
+9
View File
@@ -0,0 +1,9 @@
/**
* A fallback function to strip out twoslash annotations from a string and does nothing else.
*
* This function does not returns the meta information about the removals.
* It's designed to be used as a fallback when Twoslash fails.
*/
declare function removeTwoslashNotations(code: string, customTags?: string[]): string;
export { removeTwoslashNotations };
+9
View File
@@ -0,0 +1,9 @@
/**
* A fallback function to strip out twoslash annotations from a string and does nothing else.
*
* This function does not returns the meta information about the removals.
* It's designed to be used as a fallback when Twoslash fails.
*/
declare function removeTwoslashNotations(code: string, customTags?: string[]): string;
export { removeTwoslashNotations };
+2
View File
@@ -0,0 +1,2 @@
import 'twoslash-protocol';
export { r as removeTwoslashNotations } from './shared/twoslash.a8564bda.mjs';
+136
View File
@@ -0,0 +1,136 @@
'use strict';
const ts = require('typescript');
const core = require('./core.cjs');
const fallback = require('./shared/twoslash.9a66b1ce.cjs');
const types = require('twoslash-protocol/types');
require('@typescript/vfs');
require('twoslash-protocol');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
const ts__default = /*#__PURE__*/_interopDefaultCompat(ts);
function convertLegacyOptions(opts) {
return {
...opts,
handbookOptions: opts.handbookOptions || opts.defaultOptions,
compilerOptions: opts.compilerOptions || opts.defaultCompilerOptions
};
}
function convertLegacyReturn(result) {
return {
code: result.code,
extension: result.meta.extension,
staticQuickInfos: result.hovers.map((i) => ({
text: i.text,
docs: i.docs || "",
start: i.start,
length: i.length,
line: i.line,
character: i.character,
targetString: i.target
})),
tags: result.tags.map((t) => ({
name: t.name,
line: t.line,
annotation: t.text
})),
highlights: result.highlights.map((h) => ({
kind: "highlight",
// it's a bit confusing that `offset` and `start` are flipped
offset: h.start,
start: h.character,
length: h.length,
line: h.line,
text: h.text || ""
})),
queries: [
...result.queries.map((q) => ({
kind: "query",
docs: q.docs || "",
offset: q.character,
start: q.start,
length: q.length,
line: q.line + 1,
text: q.text
})),
...result.completions.map((q) => ({
kind: "completions",
offset: q.character,
start: q.start,
length: q.length,
line: q.line + 1,
completions: q.completions,
completionsPrefix: q.completionsPrefix
}))
].sort((a, b) => a.start - b.start),
errors: result.errors.map((e) => ({
id: e.id ?? "",
code: e.code,
start: e.start,
length: e.length,
line: e.line,
character: e.character,
renderedMessage: e.text,
category: errorLevelToCategory(e.level)
})),
playgroundURL: ""
};
}
function errorLevelToCategory(level) {
switch (level) {
case "warning":
return 0;
case "suggestion":
return 2;
case "message":
return 3;
case "error":
return 1;
}
return 1;
}
const cwd = typeof process !== "undefined" && typeof process.cwd === "function" ? process.cwd() : "";
function createTwoslasher(opts) {
return core.createTwoslasher({
vfsRoot: cwd,
tsModule: ts__default,
...opts
});
}
function twoslasher(code, lang, opts) {
return core.twoslasher(code, lang, {
vfsRoot: cwd,
tsModule: ts__default,
...opts
});
}
function twoslasherLegacy(code, lang, opts) {
return convertLegacyReturn(
core.twoslasher(code, lang, convertLegacyOptions({
vfsRoot: cwd,
tsModule: ts__default,
...opts
}))
);
}
exports.validateCodeForErrors = core.validateCodeForErrors;
exports.TwoslashError = fallback.TwoslashError;
exports.defaultCompilerOptions = fallback.defaultCompilerOptions;
exports.defaultHandbookOptions = fallback.defaultHandbookOptions;
exports.findCutNotations = fallback.findCutNotations;
exports.findFlagNotations = fallback.findFlagNotations;
exports.findQueryMarkers = fallback.findQueryMarkers;
exports.getObjectHash = fallback.getObjectHash;
exports.removeTwoslashNotations = fallback.removeTwoslashNotations;
exports.convertLegacyOptions = convertLegacyOptions;
exports.convertLegacyReturn = convertLegacyReturn;
exports.createTwoslasher = createTwoslasher;
exports.twoslasher = twoslasher;
exports.twoslasherLegacy = twoslasherLegacy;
Object.keys(types).forEach(function (k) {
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = types[k];
});
+119
View File
@@ -0,0 +1,119 @@
import { T as TwoslashExecuteOptions, H as HandbookOptions, a as TwoslashReturn, b as TwoslashOptions, c as TwoslashInstance } from './shared/twoslash.662cb7fb.cjs';
export { C as CompilerOptionDeclaration, m as CreateTwoslashOptions, E as ExtraFiles, P as ParsedFlagNotation, l as TS, j as TwoslashError, k as TwoslashFunction, n as TwoslashReturnMeta, V as VirtualFile, h as defaultCompilerOptions, i as defaultHandbookOptions, f as findCutNotations, d as findFlagNotations, e as findQueryMarkers, g as getObjectHash, v as validateCodeForErrors } from './shared/twoslash.662cb7fb.cjs';
export * from 'twoslash-protocol/types';
export { removeTwoslashNotations } from './fallback.cjs';
import * as typescript from 'typescript';
import { CompilerOptions } from 'typescript';
import '@typescript/vfs';
import 'twoslash-protocol';
interface TwoslashOptionsLegacy extends TwoslashExecuteOptions {
/**
* @deprecated, use `handbookOptions` instead
*/
defaultOptions?: Partial<HandbookOptions>;
/**
* @deprecated, use `compilerOptions` instead
*/
defaultCompilerOptions?: CompilerOptions;
}
interface TwoslashReturnLegacy {
/** The output code, could be TypeScript, but could also be a JS/JSON/d.ts */
code: string;
/** The new extension type for the code, potentially changed if they've requested emitted results */
extension: string;
/** Requests to highlight a particular part of the code */
highlights: {
kind: 'highlight';
/** The index of the text in the file */
start: number;
/** What line is the highlighted identifier on? */
line: number;
/** At what index in the line does the caret represent */
offset: number;
/** The text of the token which is highlighted */
text?: string;
/** The length of the token */
length: number;
}[];
/** An array of LSP responses identifiers in the sample */
staticQuickInfos: {
/** The string content of the node this represents (mainly for debugging) */
targetString: string;
/** The base LSP response (the type) */
text: string;
/** Attached JSDoc info */
docs: string | undefined;
/** The index of the text in the file */
start: number;
/** how long the identifier */
length: number;
/** line number where this is found */
line: number;
/** The character on the line */
character: number;
}[];
/** Requests to use the LSP to get info for a particular symbol in the source */
queries: {
kind: 'query' | 'completions';
/** What line is the highlighted identifier on? */
line: number;
/** At what index in the line does the caret represent */
offset: number;
/** The text of the node which is highlighted */
text?: string;
/** Any attached JSDocs */
docs?: string | undefined;
/** The node start which the query indicates */
start: number;
/** The length of the node */
length: number;
/** Results for completions at a particular point */
completions?: typescript.CompletionEntry[];
completionsPrefix?: string;
}[];
/** The extracted twoslash commands for any custom tags passed in via customTags */
tags: {
/** What was the name of the tag */
name: string;
/** Where was it located in the original source file */
line: number;
/** What was the text after the `// @tag: ` string (optional because you could do // @tag on it's own line without the ':') */
annotation?: string;
}[];
/** Diagnostic error messages which came up when creating the program */
errors: {
renderedMessage: string;
id: string;
category: 0 | 1 | 2 | 3;
code: number;
start: number | undefined;
length: number | undefined;
line: number | undefined;
character: number | undefined;
}[];
/** The URL for this sample in the playground */
playgroundURL: string;
}
declare function convertLegacyOptions<T extends TwoslashOptionsLegacy>(opts: T): Omit<T, 'defaultOptions' | 'defaultCompilerOptions'>;
/**
* Covert the new return type to the old one
*/
declare function convertLegacyReturn(result: TwoslashReturn): TwoslashReturnLegacy;
/**
* Create a Twoslash instance with cached TS environments
*/
declare function createTwoslasher(opts?: TwoslashOptions): TwoslashInstance;
/**
* Get type results from a code sample
*/
declare function twoslasher(code: string, lang: string, opts?: TwoslashOptions): TwoslashReturn;
/**
* Compatability wrapper to align with `@typescript/twoslash`'s input/output
*
* @deprecated migrate to `twoslasher` instead
*/
declare function twoslasherLegacy(code: string, lang: string, opts?: TwoslashOptionsLegacy): TwoslashReturnLegacy;
export { HandbookOptions, TwoslashExecuteOptions, TwoslashInstance, TwoslashOptions, type TwoslashOptionsLegacy, TwoslashReturn, type TwoslashReturnLegacy, convertLegacyOptions, convertLegacyReturn, createTwoslasher, twoslasher, twoslasherLegacy };
+119
View File
@@ -0,0 +1,119 @@
import { T as TwoslashExecuteOptions, H as HandbookOptions, a as TwoslashReturn, b as TwoslashOptions, c as TwoslashInstance } from './shared/twoslash.662cb7fb.mjs';
export { C as CompilerOptionDeclaration, m as CreateTwoslashOptions, E as ExtraFiles, P as ParsedFlagNotation, l as TS, j as TwoslashError, k as TwoslashFunction, n as TwoslashReturnMeta, V as VirtualFile, h as defaultCompilerOptions, i as defaultHandbookOptions, f as findCutNotations, d as findFlagNotations, e as findQueryMarkers, g as getObjectHash, v as validateCodeForErrors } from './shared/twoslash.662cb7fb.mjs';
export * from 'twoslash-protocol/types';
export { removeTwoslashNotations } from './fallback.mjs';
import * as typescript from 'typescript';
import { CompilerOptions } from 'typescript';
import '@typescript/vfs';
import 'twoslash-protocol';
interface TwoslashOptionsLegacy extends TwoslashExecuteOptions {
/**
* @deprecated, use `handbookOptions` instead
*/
defaultOptions?: Partial<HandbookOptions>;
/**
* @deprecated, use `compilerOptions` instead
*/
defaultCompilerOptions?: CompilerOptions;
}
interface TwoslashReturnLegacy {
/** The output code, could be TypeScript, but could also be a JS/JSON/d.ts */
code: string;
/** The new extension type for the code, potentially changed if they've requested emitted results */
extension: string;
/** Requests to highlight a particular part of the code */
highlights: {
kind: 'highlight';
/** The index of the text in the file */
start: number;
/** What line is the highlighted identifier on? */
line: number;
/** At what index in the line does the caret represent */
offset: number;
/** The text of the token which is highlighted */
text?: string;
/** The length of the token */
length: number;
}[];
/** An array of LSP responses identifiers in the sample */
staticQuickInfos: {
/** The string content of the node this represents (mainly for debugging) */
targetString: string;
/** The base LSP response (the type) */
text: string;
/** Attached JSDoc info */
docs: string | undefined;
/** The index of the text in the file */
start: number;
/** how long the identifier */
length: number;
/** line number where this is found */
line: number;
/** The character on the line */
character: number;
}[];
/** Requests to use the LSP to get info for a particular symbol in the source */
queries: {
kind: 'query' | 'completions';
/** What line is the highlighted identifier on? */
line: number;
/** At what index in the line does the caret represent */
offset: number;
/** The text of the node which is highlighted */
text?: string;
/** Any attached JSDocs */
docs?: string | undefined;
/** The node start which the query indicates */
start: number;
/** The length of the node */
length: number;
/** Results for completions at a particular point */
completions?: typescript.CompletionEntry[];
completionsPrefix?: string;
}[];
/** The extracted twoslash commands for any custom tags passed in via customTags */
tags: {
/** What was the name of the tag */
name: string;
/** Where was it located in the original source file */
line: number;
/** What was the text after the `// @tag: ` string (optional because you could do // @tag on it's own line without the ':') */
annotation?: string;
}[];
/** Diagnostic error messages which came up when creating the program */
errors: {
renderedMessage: string;
id: string;
category: 0 | 1 | 2 | 3;
code: number;
start: number | undefined;
length: number | undefined;
line: number | undefined;
character: number | undefined;
}[];
/** The URL for this sample in the playground */
playgroundURL: string;
}
declare function convertLegacyOptions<T extends TwoslashOptionsLegacy>(opts: T): Omit<T, 'defaultOptions' | 'defaultCompilerOptions'>;
/**
* Covert the new return type to the old one
*/
declare function convertLegacyReturn(result: TwoslashReturn): TwoslashReturnLegacy;
/**
* Create a Twoslash instance with cached TS environments
*/
declare function createTwoslasher(opts?: TwoslashOptions): TwoslashInstance;
/**
* Get type results from a code sample
*/
declare function twoslasher(code: string, lang: string, opts?: TwoslashOptions): TwoslashReturn;
/**
* Compatability wrapper to align with `@typescript/twoslash`'s input/output
*
* @deprecated migrate to `twoslasher` instead
*/
declare function twoslasherLegacy(code: string, lang: string, opts?: TwoslashOptionsLegacy): TwoslashReturnLegacy;
export { HandbookOptions, TwoslashExecuteOptions, TwoslashInstance, TwoslashOptions, type TwoslashOptionsLegacy, TwoslashReturn, type TwoslashReturnLegacy, convertLegacyOptions, convertLegacyReturn, createTwoslasher, twoslasher, twoslasherLegacy };
+119
View File
@@ -0,0 +1,119 @@
import { T as TwoslashExecuteOptions, H as HandbookOptions, a as TwoslashReturn, b as TwoslashOptions, c as TwoslashInstance } from './shared/twoslash.662cb7fb.js';
export { C as CompilerOptionDeclaration, m as CreateTwoslashOptions, E as ExtraFiles, P as ParsedFlagNotation, l as TS, j as TwoslashError, k as TwoslashFunction, n as TwoslashReturnMeta, V as VirtualFile, h as defaultCompilerOptions, i as defaultHandbookOptions, f as findCutNotations, d as findFlagNotations, e as findQueryMarkers, g as getObjectHash, v as validateCodeForErrors } from './shared/twoslash.662cb7fb.js';
export * from 'twoslash-protocol/types';
export { removeTwoslashNotations } from './fallback.js';
import * as typescript from 'typescript';
import { CompilerOptions } from 'typescript';
import '@typescript/vfs';
import 'twoslash-protocol';
interface TwoslashOptionsLegacy extends TwoslashExecuteOptions {
/**
* @deprecated, use `handbookOptions` instead
*/
defaultOptions?: Partial<HandbookOptions>;
/**
* @deprecated, use `compilerOptions` instead
*/
defaultCompilerOptions?: CompilerOptions;
}
interface TwoslashReturnLegacy {
/** The output code, could be TypeScript, but could also be a JS/JSON/d.ts */
code: string;
/** The new extension type for the code, potentially changed if they've requested emitted results */
extension: string;
/** Requests to highlight a particular part of the code */
highlights: {
kind: 'highlight';
/** The index of the text in the file */
start: number;
/** What line is the highlighted identifier on? */
line: number;
/** At what index in the line does the caret represent */
offset: number;
/** The text of the token which is highlighted */
text?: string;
/** The length of the token */
length: number;
}[];
/** An array of LSP responses identifiers in the sample */
staticQuickInfos: {
/** The string content of the node this represents (mainly for debugging) */
targetString: string;
/** The base LSP response (the type) */
text: string;
/** Attached JSDoc info */
docs: string | undefined;
/** The index of the text in the file */
start: number;
/** how long the identifier */
length: number;
/** line number where this is found */
line: number;
/** The character on the line */
character: number;
}[];
/** Requests to use the LSP to get info for a particular symbol in the source */
queries: {
kind: 'query' | 'completions';
/** What line is the highlighted identifier on? */
line: number;
/** At what index in the line does the caret represent */
offset: number;
/** The text of the node which is highlighted */
text?: string;
/** Any attached JSDocs */
docs?: string | undefined;
/** The node start which the query indicates */
start: number;
/** The length of the node */
length: number;
/** Results for completions at a particular point */
completions?: typescript.CompletionEntry[];
completionsPrefix?: string;
}[];
/** The extracted twoslash commands for any custom tags passed in via customTags */
tags: {
/** What was the name of the tag */
name: string;
/** Where was it located in the original source file */
line: number;
/** What was the text after the `// @tag: ` string (optional because you could do // @tag on it's own line without the ':') */
annotation?: string;
}[];
/** Diagnostic error messages which came up when creating the program */
errors: {
renderedMessage: string;
id: string;
category: 0 | 1 | 2 | 3;
code: number;
start: number | undefined;
length: number | undefined;
line: number | undefined;
character: number | undefined;
}[];
/** The URL for this sample in the playground */
playgroundURL: string;
}
declare function convertLegacyOptions<T extends TwoslashOptionsLegacy>(opts: T): Omit<T, 'defaultOptions' | 'defaultCompilerOptions'>;
/**
* Covert the new return type to the old one
*/
declare function convertLegacyReturn(result: TwoslashReturn): TwoslashReturnLegacy;
/**
* Create a Twoslash instance with cached TS environments
*/
declare function createTwoslasher(opts?: TwoslashOptions): TwoslashInstance;
/**
* Get type results from a code sample
*/
declare function twoslasher(code: string, lang: string, opts?: TwoslashOptions): TwoslashReturn;
/**
* Compatability wrapper to align with `@typescript/twoslash`'s input/output
*
* @deprecated migrate to `twoslasher` instead
*/
declare function twoslasherLegacy(code: string, lang: string, opts?: TwoslashOptionsLegacy): TwoslashReturnLegacy;
export { HandbookOptions, TwoslashExecuteOptions, TwoslashInstance, TwoslashOptions, type TwoslashOptionsLegacy, TwoslashReturn, type TwoslashReturnLegacy, convertLegacyOptions, convertLegacyReturn, createTwoslasher, twoslasher, twoslasherLegacy };
+115
View File
@@ -0,0 +1,115 @@
import ts from 'typescript';
import { createTwoslasher as createTwoslasher$1, twoslasher as twoslasher$1 } from './core.mjs';
export { validateCodeForErrors } from './core.mjs';
export { T as TwoslashError, d as defaultCompilerOptions, c as defaultHandbookOptions, f as findCutNotations, a as findFlagNotations, b as findQueryMarkers, g as getObjectHash, r as removeTwoslashNotations } from './shared/twoslash.a8564bda.mjs';
export * from 'twoslash-protocol/types';
import '@typescript/vfs';
import 'twoslash-protocol';
function convertLegacyOptions(opts) {
return {
...opts,
handbookOptions: opts.handbookOptions || opts.defaultOptions,
compilerOptions: opts.compilerOptions || opts.defaultCompilerOptions
};
}
function convertLegacyReturn(result) {
return {
code: result.code,
extension: result.meta.extension,
staticQuickInfos: result.hovers.map((i) => ({
text: i.text,
docs: i.docs || "",
start: i.start,
length: i.length,
line: i.line,
character: i.character,
targetString: i.target
})),
tags: result.tags.map((t) => ({
name: t.name,
line: t.line,
annotation: t.text
})),
highlights: result.highlights.map((h) => ({
kind: "highlight",
// it's a bit confusing that `offset` and `start` are flipped
offset: h.start,
start: h.character,
length: h.length,
line: h.line,
text: h.text || ""
})),
queries: [
...result.queries.map((q) => ({
kind: "query",
docs: q.docs || "",
offset: q.character,
start: q.start,
length: q.length,
line: q.line + 1,
text: q.text
})),
...result.completions.map((q) => ({
kind: "completions",
offset: q.character,
start: q.start,
length: q.length,
line: q.line + 1,
completions: q.completions,
completionsPrefix: q.completionsPrefix
}))
].sort((a, b) => a.start - b.start),
errors: result.errors.map((e) => ({
id: e.id ?? "",
code: e.code,
start: e.start,
length: e.length,
line: e.line,
character: e.character,
renderedMessage: e.text,
category: errorLevelToCategory(e.level)
})),
playgroundURL: ""
};
}
function errorLevelToCategory(level) {
switch (level) {
case "warning":
return 0;
case "suggestion":
return 2;
case "message":
return 3;
case "error":
return 1;
}
return 1;
}
const cwd = typeof process !== "undefined" && typeof process.cwd === "function" ? process.cwd() : "";
function createTwoslasher(opts) {
return createTwoslasher$1({
vfsRoot: cwd,
tsModule: ts,
...opts
});
}
function twoslasher(code, lang, opts) {
return twoslasher$1(code, lang, {
vfsRoot: cwd,
tsModule: ts,
...opts
});
}
function twoslasherLegacy(code, lang, opts) {
return convertLegacyReturn(
twoslasher$1(code, lang, convertLegacyOptions({
vfsRoot: cwd,
tsModule: ts,
...opts
}))
);
}
export { convertLegacyOptions, convertLegacyReturn, createTwoslasher, twoslasher, twoslasherLegacy };
+228
View File
@@ -0,0 +1,228 @@
import { VirtualTypeScriptEnvironment } from '@typescript/vfs';
import * as typescript from 'typescript';
import { CompilerOptions, CustomTransformers } from 'typescript';
import { TwoslashGenericResult, NodeQuery, NodeCompletion, NodeError, NodeHighlight, NodeHover, NodeTag, Range, NodeWithoutPosition, createPositionConverter, NodeErrorWithoutPosition } from 'twoslash-protocol';
/**
* Available inline flags which are not compiler flags
*/
interface HandbookOptions {
/**
* An array of TS error codes, which you write as space separated - this is so the tool can know about unexpected errors
*/
errors: number[];
/**
* Suppress errors for diagnostics and display
*
* Setting true to suppress all errors, or an array of error codes to suppress
*/
noErrors: boolean | number[];
/**
* Declare that you don't need to validate that errors have corresponding annotations, defaults to false
*/
noErrorValidation: boolean;
/**
* Whether to disable the pre-cache of LSP calls for interesting identifiers, defaults to false
*/
noStaticSemanticInfo: boolean;
/**
* Shows the JS equivalent of the TypeScript code instead
*/
showEmit: boolean;
/**
* Must be used with showEmit, lets you choose the file to present instead of the source - defaults to index.js which
* means when you just use `showEmit` above it shows the transpiled JS.
*/
showEmittedFile?: string;
/**
* Do not remove twoslash notations from output code, the nodes will have the position of the input code.
* @default false
*/
keepNotations: boolean;
/**
* Do not check errors in the cutted code.
* @default false
*/
noErrorsCutted: boolean;
}
interface TwoslashReturn extends TwoslashGenericResult {
/**
* The meta information the twoslash run
*/
meta: TwoslashReturnMeta;
get queries(): NodeQuery[];
get completions(): NodeCompletion[];
get errors(): NodeError[];
get highlights(): NodeHighlight[];
get hovers(): NodeHover[];
get tags(): NodeTag[];
}
interface TwoslashReturnMeta {
/**
* The new extension type for the code, potentially changed if they've requested emitted results
*/
extension: string;
/**
* Ranges of text which should be removed from the output
*/
removals: Range[];
/**
* Resolved compiler options
*/
compilerOptions: CompilerOptions;
/**
* Resolved handbook options
*/
handbookOptions: HandbookOptions;
/**
* Flags which were parsed from the code
*/
flagNotations: ParsedFlagNotation[];
/**
* The virtual files which were created
*/
virtualFiles: VirtualFile[];
/**
* Positions of queries in the code
*/
positionQueries: number[];
/**
* Positions of completions in the code
*/
positionCompletions: number[];
/**
* Positions of errors in the code
*/
positionHighlights: [start: number, end: number, text?: string][];
}
interface ParsedFlagNotation {
type: 'compilerOptions' | 'handbookOptions' | 'tag' | 'unknown';
name: string;
value: any;
start: number;
end: number;
}
interface VirtualFile {
offset: number;
filename: string;
filepath: string;
content: string;
extension: string;
supportLsp?: boolean;
prepend?: string;
append?: string;
}
type TS = typeof typescript;
interface CompilerOptionDeclaration {
name: string;
type: 'list' | 'boolean' | 'number' | 'string' | 'object' | Map<string, any>;
element?: CompilerOptionDeclaration;
}
/**
* Options for the `twoslasher` function
*/
interface TwoslashOptions extends CreateTwoslashOptions, TwoslashExecuteOptions {
}
/**
* Options for twoslash instance
*/
interface TwoslashExecuteOptions extends Partial<Pick<TwoslashReturnMeta, 'positionQueries' | 'positionCompletions' | 'positionHighlights'>> {
/**
* Allows setting any of the handbook options from outside the function, useful if you don't want LSP identifiers
*/
handbookOptions?: Partial<HandbookOptions>;
/**
* Allows setting any of the compiler options from outside the function
*/
compilerOptions?: CompilerOptions;
/**
* A set of known `// @[tags]` tags to extract and not treat as a comment
*/
customTags?: string[];
/**
* A custom hook to filter out hover info for certain identifiers
*/
shouldGetHoverInfo?: (identifier: string, start: number, filename: string) => boolean;
/**
* A custom predicate to filter out nodes for further processing
*/
filterNode?: (node: NodeWithoutPosition) => boolean;
/**
* Extra files to to added to the virtual file system, or prepended/appended to existing files
*/
extraFiles?: ExtraFiles;
}
type ExtraFiles = Record<string, string | {
prepend?: string;
append?: string;
}>;
interface CreateTwoslashOptions extends TwoslashExecuteOptions {
/**
* Allows applying custom transformers to the emit result, only useful with the showEmit output
*/
customTransformers?: CustomTransformers;
/**
* An optional copy of the TypeScript import, if missing it will be require'd.
*/
tsModule?: TS;
/**
* Absolute path to the directory to look up built-in TypeScript .d.ts files.
*/
tsLibDirectory?: string;
/**
* An optional Map object which is passed into @typescript/vfs - if you are using twoslash on the
* web then you'll need this to set up your lib *.d.ts files. If missing, it will use your fs.
*/
fsMap?: Map<string, string>;
/**
* The cwd for the folder which the virtual fs should be overlaid on top of when using local fs, opts to process.cwd() if not present
*/
vfsRoot?: string;
/**
* Cache the ts envs based on compiler options, defaults to true
*/
cache?: boolean | Map<string, VirtualTypeScriptEnvironment>;
/**
* Cache file system requests
*
* @default true
*/
fsCache?: boolean;
}
type TwoslashFunction = (code: string, extension?: string, options?: TwoslashExecuteOptions) => TwoslashReturn;
interface TwoslashInstance {
/**
* Run Twoslash on a string of code, with a particular extension
*/
(code: string, extension?: string, options?: TwoslashExecuteOptions): TwoslashReturn;
/**
* Get the internal cache map
*/
getCacheMap: () => Map<string, VirtualTypeScriptEnvironment> | undefined;
}
declare const defaultCompilerOptions: CompilerOptions;
declare const defaultHandbookOptions: HandbookOptions;
declare class TwoslashError extends Error {
title: string;
description: string;
recommendation: string;
code: string | undefined;
constructor(title: string, description: string, recommendation: string, code?: string | undefined);
}
declare function getObjectHash(obj: any): string;
declare function findFlagNotations(code: string, customTags: string[], tsOptionDeclarations: CompilerOptionDeclaration[]): ParsedFlagNotation[];
declare function findCutNotations(code: string, meta: Pick<TwoslashReturnMeta, 'removals'>): Range[];
declare function findQueryMarkers(code: string, meta: Pick<TwoslashReturnMeta, 'positionQueries' | 'positionCompletions' | 'positionHighlights' | 'removals'>, pc: ReturnType<typeof createPositionConverter>): Pick<TwoslashReturnMeta, "positionQueries" | "positionCompletions" | "positionHighlights" | "removals">;
/** To ensure that errors are matched up right */
declare function validateCodeForErrors(relevantErrors: NodeErrorWithoutPosition[], handbookOptions: {
errors: number[];
}, vfsRoot: string): void;
export { type CompilerOptionDeclaration as C, type ExtraFiles as E, type HandbookOptions as H, type ParsedFlagNotation as P, type TwoslashExecuteOptions as T, type VirtualFile as V, type TwoslashReturn as a, type TwoslashOptions as b, type TwoslashInstance as c, findFlagNotations as d, findQueryMarkers as e, findCutNotations as f, getObjectHash as g, defaultCompilerOptions as h, defaultHandbookOptions as i, TwoslashError as j, type TwoslashFunction as k, type TS as l, type CreateTwoslashOptions as m, type TwoslashReturnMeta as n, validateCodeForErrors as v };
+228
View File
@@ -0,0 +1,228 @@
import { VirtualTypeScriptEnvironment } from '@typescript/vfs';
import * as typescript from 'typescript';
import { CompilerOptions, CustomTransformers } from 'typescript';
import { TwoslashGenericResult, NodeQuery, NodeCompletion, NodeError, NodeHighlight, NodeHover, NodeTag, Range, NodeWithoutPosition, createPositionConverter, NodeErrorWithoutPosition } from 'twoslash-protocol';
/**
* Available inline flags which are not compiler flags
*/
interface HandbookOptions {
/**
* An array of TS error codes, which you write as space separated - this is so the tool can know about unexpected errors
*/
errors: number[];
/**
* Suppress errors for diagnostics and display
*
* Setting true to suppress all errors, or an array of error codes to suppress
*/
noErrors: boolean | number[];
/**
* Declare that you don't need to validate that errors have corresponding annotations, defaults to false
*/
noErrorValidation: boolean;
/**
* Whether to disable the pre-cache of LSP calls for interesting identifiers, defaults to false
*/
noStaticSemanticInfo: boolean;
/**
* Shows the JS equivalent of the TypeScript code instead
*/
showEmit: boolean;
/**
* Must be used with showEmit, lets you choose the file to present instead of the source - defaults to index.js which
* means when you just use `showEmit` above it shows the transpiled JS.
*/
showEmittedFile?: string;
/**
* Do not remove twoslash notations from output code, the nodes will have the position of the input code.
* @default false
*/
keepNotations: boolean;
/**
* Do not check errors in the cutted code.
* @default false
*/
noErrorsCutted: boolean;
}
interface TwoslashReturn extends TwoslashGenericResult {
/**
* The meta information the twoslash run
*/
meta: TwoslashReturnMeta;
get queries(): NodeQuery[];
get completions(): NodeCompletion[];
get errors(): NodeError[];
get highlights(): NodeHighlight[];
get hovers(): NodeHover[];
get tags(): NodeTag[];
}
interface TwoslashReturnMeta {
/**
* The new extension type for the code, potentially changed if they've requested emitted results
*/
extension: string;
/**
* Ranges of text which should be removed from the output
*/
removals: Range[];
/**
* Resolved compiler options
*/
compilerOptions: CompilerOptions;
/**
* Resolved handbook options
*/
handbookOptions: HandbookOptions;
/**
* Flags which were parsed from the code
*/
flagNotations: ParsedFlagNotation[];
/**
* The virtual files which were created
*/
virtualFiles: VirtualFile[];
/**
* Positions of queries in the code
*/
positionQueries: number[];
/**
* Positions of completions in the code
*/
positionCompletions: number[];
/**
* Positions of errors in the code
*/
positionHighlights: [start: number, end: number, text?: string][];
}
interface ParsedFlagNotation {
type: 'compilerOptions' | 'handbookOptions' | 'tag' | 'unknown';
name: string;
value: any;
start: number;
end: number;
}
interface VirtualFile {
offset: number;
filename: string;
filepath: string;
content: string;
extension: string;
supportLsp?: boolean;
prepend?: string;
append?: string;
}
type TS = typeof typescript;
interface CompilerOptionDeclaration {
name: string;
type: 'list' | 'boolean' | 'number' | 'string' | 'object' | Map<string, any>;
element?: CompilerOptionDeclaration;
}
/**
* Options for the `twoslasher` function
*/
interface TwoslashOptions extends CreateTwoslashOptions, TwoslashExecuteOptions {
}
/**
* Options for twoslash instance
*/
interface TwoslashExecuteOptions extends Partial<Pick<TwoslashReturnMeta, 'positionQueries' | 'positionCompletions' | 'positionHighlights'>> {
/**
* Allows setting any of the handbook options from outside the function, useful if you don't want LSP identifiers
*/
handbookOptions?: Partial<HandbookOptions>;
/**
* Allows setting any of the compiler options from outside the function
*/
compilerOptions?: CompilerOptions;
/**
* A set of known `// @[tags]` tags to extract and not treat as a comment
*/
customTags?: string[];
/**
* A custom hook to filter out hover info for certain identifiers
*/
shouldGetHoverInfo?: (identifier: string, start: number, filename: string) => boolean;
/**
* A custom predicate to filter out nodes for further processing
*/
filterNode?: (node: NodeWithoutPosition) => boolean;
/**
* Extra files to to added to the virtual file system, or prepended/appended to existing files
*/
extraFiles?: ExtraFiles;
}
type ExtraFiles = Record<string, string | {
prepend?: string;
append?: string;
}>;
interface CreateTwoslashOptions extends TwoslashExecuteOptions {
/**
* Allows applying custom transformers to the emit result, only useful with the showEmit output
*/
customTransformers?: CustomTransformers;
/**
* An optional copy of the TypeScript import, if missing it will be require'd.
*/
tsModule?: TS;
/**
* Absolute path to the directory to look up built-in TypeScript .d.ts files.
*/
tsLibDirectory?: string;
/**
* An optional Map object which is passed into @typescript/vfs - if you are using twoslash on the
* web then you'll need this to set up your lib *.d.ts files. If missing, it will use your fs.
*/
fsMap?: Map<string, string>;
/**
* The cwd for the folder which the virtual fs should be overlaid on top of when using local fs, opts to process.cwd() if not present
*/
vfsRoot?: string;
/**
* Cache the ts envs based on compiler options, defaults to true
*/
cache?: boolean | Map<string, VirtualTypeScriptEnvironment>;
/**
* Cache file system requests
*
* @default true
*/
fsCache?: boolean;
}
type TwoslashFunction = (code: string, extension?: string, options?: TwoslashExecuteOptions) => TwoslashReturn;
interface TwoslashInstance {
/**
* Run Twoslash on a string of code, with a particular extension
*/
(code: string, extension?: string, options?: TwoslashExecuteOptions): TwoslashReturn;
/**
* Get the internal cache map
*/
getCacheMap: () => Map<string, VirtualTypeScriptEnvironment> | undefined;
}
declare const defaultCompilerOptions: CompilerOptions;
declare const defaultHandbookOptions: HandbookOptions;
declare class TwoslashError extends Error {
title: string;
description: string;
recommendation: string;
code: string | undefined;
constructor(title: string, description: string, recommendation: string, code?: string | undefined);
}
declare function getObjectHash(obj: any): string;
declare function findFlagNotations(code: string, customTags: string[], tsOptionDeclarations: CompilerOptionDeclaration[]): ParsedFlagNotation[];
declare function findCutNotations(code: string, meta: Pick<TwoslashReturnMeta, 'removals'>): Range[];
declare function findQueryMarkers(code: string, meta: Pick<TwoslashReturnMeta, 'positionQueries' | 'positionCompletions' | 'positionHighlights' | 'removals'>, pc: ReturnType<typeof createPositionConverter>): Pick<TwoslashReturnMeta, "positionQueries" | "positionCompletions" | "positionHighlights" | "removals">;
/** To ensure that errors are matched up right */
declare function validateCodeForErrors(relevantErrors: NodeErrorWithoutPosition[], handbookOptions: {
errors: number[];
}, vfsRoot: string): void;
export { type CompilerOptionDeclaration as C, type ExtraFiles as E, type HandbookOptions as H, type ParsedFlagNotation as P, type TwoslashExecuteOptions as T, type VirtualFile as V, type TwoslashReturn as a, type TwoslashOptions as b, type TwoslashInstance as c, findFlagNotations as d, findQueryMarkers as e, findCutNotations as f, getObjectHash as g, defaultCompilerOptions as h, defaultHandbookOptions as i, TwoslashError as j, type TwoslashFunction as k, type TS as l, type CreateTwoslashOptions as m, type TwoslashReturnMeta as n, validateCodeForErrors as v };
+228
View File
@@ -0,0 +1,228 @@
import { VirtualTypeScriptEnvironment } from '@typescript/vfs';
import * as typescript from 'typescript';
import { CompilerOptions, CustomTransformers } from 'typescript';
import { TwoslashGenericResult, NodeQuery, NodeCompletion, NodeError, NodeHighlight, NodeHover, NodeTag, Range, NodeWithoutPosition, createPositionConverter, NodeErrorWithoutPosition } from 'twoslash-protocol';
/**
* Available inline flags which are not compiler flags
*/
interface HandbookOptions {
/**
* An array of TS error codes, which you write as space separated - this is so the tool can know about unexpected errors
*/
errors: number[];
/**
* Suppress errors for diagnostics and display
*
* Setting true to suppress all errors, or an array of error codes to suppress
*/
noErrors: boolean | number[];
/**
* Declare that you don't need to validate that errors have corresponding annotations, defaults to false
*/
noErrorValidation: boolean;
/**
* Whether to disable the pre-cache of LSP calls for interesting identifiers, defaults to false
*/
noStaticSemanticInfo: boolean;
/**
* Shows the JS equivalent of the TypeScript code instead
*/
showEmit: boolean;
/**
* Must be used with showEmit, lets you choose the file to present instead of the source - defaults to index.js which
* means when you just use `showEmit` above it shows the transpiled JS.
*/
showEmittedFile?: string;
/**
* Do not remove twoslash notations from output code, the nodes will have the position of the input code.
* @default false
*/
keepNotations: boolean;
/**
* Do not check errors in the cutted code.
* @default false
*/
noErrorsCutted: boolean;
}
interface TwoslashReturn extends TwoslashGenericResult {
/**
* The meta information the twoslash run
*/
meta: TwoslashReturnMeta;
get queries(): NodeQuery[];
get completions(): NodeCompletion[];
get errors(): NodeError[];
get highlights(): NodeHighlight[];
get hovers(): NodeHover[];
get tags(): NodeTag[];
}
interface TwoslashReturnMeta {
/**
* The new extension type for the code, potentially changed if they've requested emitted results
*/
extension: string;
/**
* Ranges of text which should be removed from the output
*/
removals: Range[];
/**
* Resolved compiler options
*/
compilerOptions: CompilerOptions;
/**
* Resolved handbook options
*/
handbookOptions: HandbookOptions;
/**
* Flags which were parsed from the code
*/
flagNotations: ParsedFlagNotation[];
/**
* The virtual files which were created
*/
virtualFiles: VirtualFile[];
/**
* Positions of queries in the code
*/
positionQueries: number[];
/**
* Positions of completions in the code
*/
positionCompletions: number[];
/**
* Positions of errors in the code
*/
positionHighlights: [start: number, end: number, text?: string][];
}
interface ParsedFlagNotation {
type: 'compilerOptions' | 'handbookOptions' | 'tag' | 'unknown';
name: string;
value: any;
start: number;
end: number;
}
interface VirtualFile {
offset: number;
filename: string;
filepath: string;
content: string;
extension: string;
supportLsp?: boolean;
prepend?: string;
append?: string;
}
type TS = typeof typescript;
interface CompilerOptionDeclaration {
name: string;
type: 'list' | 'boolean' | 'number' | 'string' | 'object' | Map<string, any>;
element?: CompilerOptionDeclaration;
}
/**
* Options for the `twoslasher` function
*/
interface TwoslashOptions extends CreateTwoslashOptions, TwoslashExecuteOptions {
}
/**
* Options for twoslash instance
*/
interface TwoslashExecuteOptions extends Partial<Pick<TwoslashReturnMeta, 'positionQueries' | 'positionCompletions' | 'positionHighlights'>> {
/**
* Allows setting any of the handbook options from outside the function, useful if you don't want LSP identifiers
*/
handbookOptions?: Partial<HandbookOptions>;
/**
* Allows setting any of the compiler options from outside the function
*/
compilerOptions?: CompilerOptions;
/**
* A set of known `// @[tags]` tags to extract and not treat as a comment
*/
customTags?: string[];
/**
* A custom hook to filter out hover info for certain identifiers
*/
shouldGetHoverInfo?: (identifier: string, start: number, filename: string) => boolean;
/**
* A custom predicate to filter out nodes for further processing
*/
filterNode?: (node: NodeWithoutPosition) => boolean;
/**
* Extra files to to added to the virtual file system, or prepended/appended to existing files
*/
extraFiles?: ExtraFiles;
}
type ExtraFiles = Record<string, string | {
prepend?: string;
append?: string;
}>;
interface CreateTwoslashOptions extends TwoslashExecuteOptions {
/**
* Allows applying custom transformers to the emit result, only useful with the showEmit output
*/
customTransformers?: CustomTransformers;
/**
* An optional copy of the TypeScript import, if missing it will be require'd.
*/
tsModule?: TS;
/**
* Absolute path to the directory to look up built-in TypeScript .d.ts files.
*/
tsLibDirectory?: string;
/**
* An optional Map object which is passed into @typescript/vfs - if you are using twoslash on the
* web then you'll need this to set up your lib *.d.ts files. If missing, it will use your fs.
*/
fsMap?: Map<string, string>;
/**
* The cwd for the folder which the virtual fs should be overlaid on top of when using local fs, opts to process.cwd() if not present
*/
vfsRoot?: string;
/**
* Cache the ts envs based on compiler options, defaults to true
*/
cache?: boolean | Map<string, VirtualTypeScriptEnvironment>;
/**
* Cache file system requests
*
* @default true
*/
fsCache?: boolean;
}
type TwoslashFunction = (code: string, extension?: string, options?: TwoslashExecuteOptions) => TwoslashReturn;
interface TwoslashInstance {
/**
* Run Twoslash on a string of code, with a particular extension
*/
(code: string, extension?: string, options?: TwoslashExecuteOptions): TwoslashReturn;
/**
* Get the internal cache map
*/
getCacheMap: () => Map<string, VirtualTypeScriptEnvironment> | undefined;
}
declare const defaultCompilerOptions: CompilerOptions;
declare const defaultHandbookOptions: HandbookOptions;
declare class TwoslashError extends Error {
title: string;
description: string;
recommendation: string;
code: string | undefined;
constructor(title: string, description: string, recommendation: string, code?: string | undefined);
}
declare function getObjectHash(obj: any): string;
declare function findFlagNotations(code: string, customTags: string[], tsOptionDeclarations: CompilerOptionDeclaration[]): ParsedFlagNotation[];
declare function findCutNotations(code: string, meta: Pick<TwoslashReturnMeta, 'removals'>): Range[];
declare function findQueryMarkers(code: string, meta: Pick<TwoslashReturnMeta, 'positionQueries' | 'positionCompletions' | 'positionHighlights' | 'removals'>, pc: ReturnType<typeof createPositionConverter>): Pick<TwoslashReturnMeta, "positionQueries" | "positionCompletions" | "positionHighlights" | "removals">;
/** To ensure that errors are matched up right */
declare function validateCodeForErrors(relevantErrors: NodeErrorWithoutPosition[], handbookOptions: {
errors: number[];
}, vfsRoot: string): void;
export { type CompilerOptionDeclaration as C, type ExtraFiles as E, type HandbookOptions as H, type ParsedFlagNotation as P, type TwoslashExecuteOptions as T, type VirtualFile as V, type TwoslashReturn as a, type TwoslashOptions as b, type TwoslashInstance as c, findFlagNotations as d, findQueryMarkers as e, findCutNotations as f, getObjectHash as g, defaultCompilerOptions as h, defaultHandbookOptions as i, TwoslashError as j, type TwoslashFunction as k, type TS as l, type CreateTwoslashOptions as m, type TwoslashReturnMeta as n, validateCodeForErrors as v };
+842
View File
@@ -0,0 +1,842 @@
'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;
+828
View File
@@ -0,0 +1,828 @@
import { removeCodeRanges } from '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 removeCodeRanges(code, meta.removals).code;
}
export { TwoslashError as T, findFlagNotations as a, findQueryMarkers as b, defaultHandbookOptions as c, defaultCompilerOptions as d, removeTsExtension as e, findCutNotations as f, getObjectHash as g, getExtension as h, getIdentifierTextSpans as i, removeTwoslashNotations as r, splitFiles as s, typesToExtension as t };
+69
View File
@@ -0,0 +1,69 @@
{
"name": "twoslash",
"type": "module",
"version": "0.2.12",
"description": "Markup for generating rich type information in your documentations ahead of time",
"author": "TypeScript team",
"license": "MIT",
"homepage": "https://github.com/twoslashes/twoslash",
"repository": {
"url": "https://github.com/twoslashes/twoslash",
"type": "git",
"directory": "packages/twoslash"
},
"bugs": {
"url": "https://github.com/twoslashes/twoslash/issues"
},
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./core": {
"import": "./dist/core.mjs",
"require": "./dist/core.cjs"
},
"./fallback": {
"import": "./dist/fallback.mjs",
"require": "./dist/fallback.cjs"
}
},
"main": "dist/index.mjs",
"module": "dist/index.mjs",
"types": "dist/index.d.mts",
"typesVersions": {
"*": {
"./core": [
"./dist/core.d.mts"
],
"./fallback": [
"./dist/fallback.d.mts"
],
"*": [
"./dist/index.d.mts"
]
}
},
"files": [
"dist"
],
"peerDependencies": {
"typescript": "*"
},
"dependencies": {
"@typescript/vfs": "^1.6.0",
"twoslash-protocol": "0.2.12"
},
"devDependencies": {
"ohash": "^1.1.4"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
},
"lint-staged": {
"*": "eslint --fix"
},
"scripts": {
"build": "unbuild"
}
}