mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-06 11:54:44 -06:00
WIP --annotateTransforms switch to add transformer diagnostics to Source Maps
This commit is contained in:
parent
2c12b14999
commit
0946a7f6ed
@ -318,6 +318,13 @@ namespace ts {
|
||||
description: Diagnostics.Set_the_language_of_the_messaging_from_TypeScript_This_does_not_affect_emit,
|
||||
defaultValueDescription: Diagnostics.Platform_specific
|
||||
},
|
||||
{
|
||||
name: "annotateTransforms",
|
||||
type: "boolean",
|
||||
affectsEmit: true,
|
||||
category: Diagnostics.Compiler_Diagnostics,
|
||||
defaultValueDescription: false
|
||||
}
|
||||
];
|
||||
|
||||
/* @internal */
|
||||
|
||||
@ -384,7 +384,7 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
// Transform the source files
|
||||
const transform = transformNodes(resolver, host, factory, compilerOptions, [sourceFileOrBundle], scriptTransformers, /*allowDtsFiles*/ false);
|
||||
const transform = transformNodes(resolver, host, /*factoryIn*/ undefined, compilerOptions, [sourceFileOrBundle], scriptTransformers, /*allowDtsFiles*/ false);
|
||||
|
||||
const printerOptions: PrinterOptions = {
|
||||
removeComments: compilerOptions.removeComments,
|
||||
@ -397,7 +397,8 @@ namespace ts {
|
||||
inlineSources: compilerOptions.inlineSources,
|
||||
extendedDiagnostics: compilerOptions.extendedDiagnostics,
|
||||
writeBundleFileInfo: !!bundleBuildInfo,
|
||||
relativeToBuildInfo
|
||||
relativeToBuildInfo,
|
||||
annotateTransforms: compilerOptions.annotateTransforms
|
||||
};
|
||||
|
||||
// Create a printer to print the nodes
|
||||
@ -457,7 +458,8 @@ namespace ts {
|
||||
onlyPrintJsDocStyle: true,
|
||||
writeBundleFileInfo: !!bundleBuildInfo,
|
||||
recordInternalSection: !!bundleBuildInfo,
|
||||
relativeToBuildInfo
|
||||
relativeToBuildInfo,
|
||||
annotateTransforms: compilerOptions.annotateTransforms
|
||||
};
|
||||
|
||||
const declarationPrinter = createPrinter(printerOptions, {
|
||||
@ -877,6 +879,7 @@ namespace ts {
|
||||
Substitution,
|
||||
Comments,
|
||||
SourceMaps,
|
||||
TransformerAnnotations,
|
||||
Emit,
|
||||
}
|
||||
|
||||
@ -897,6 +900,7 @@ namespace ts {
|
||||
const extendedDiagnostics = !!printerOptions.extendedDiagnostics;
|
||||
const newLine = getNewLineCharacter(printerOptions);
|
||||
const moduleKind = getEmitModuleKind(printerOptions);
|
||||
const annotateTransforms = printerOptions.annotateTransforms;
|
||||
const bundledHelpers = new Map<string, boolean>();
|
||||
|
||||
let currentSourceFile: SourceFile | undefined;
|
||||
@ -1293,6 +1297,11 @@ namespace ts {
|
||||
return pipelineEmitWithSourceMaps;
|
||||
}
|
||||
// falls through
|
||||
case PipelinePhase.TransformerAnnotations:
|
||||
if (annotateTransforms && sourceMapGenerator) {
|
||||
return pipelineEmitWithTransformerAnnotations;
|
||||
}
|
||||
// falls through
|
||||
case PipelinePhase.Emit:
|
||||
return pipelineEmitWithHint;
|
||||
default:
|
||||
@ -5885,6 +5894,22 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function pipelineEmitWithTransformerAnnotations(hint: EmitHint, node: Node) {
|
||||
const pipelinePhase = getNextPipelinePhase(PipelinePhase.TransformerAnnotations, hint, node);
|
||||
const transformerNames = annotateTransforms && sourceMapGenerator && getTransformerNames(node);
|
||||
if (transformerNames) {
|
||||
// Add leading and trailing annotations for the node. While this only emits transformer annotations
|
||||
// currently, the format for annotations is intentionally generic to allow for other kinds of annotations
|
||||
// in the future
|
||||
sourceMapGenerator!.addAnnotation(writer.getLine(), writer.getColumn(), "typescript.transformers", { kind: "start", transformers: transformerNames });
|
||||
pipelinePhase(hint, node);
|
||||
sourceMapGenerator!.addAnnotation(writer.getLine(), writer.getColumn(), "typescript.transformers", { kind: "end" });
|
||||
}
|
||||
else {
|
||||
pipelinePhase(hint, node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips trivia such as comments and white-space that can be optionally overridden by the source-map source
|
||||
*/
|
||||
@ -6047,4 +6072,44 @@ namespace ts {
|
||||
typeof parenthesizerRule === "object" ? emitListItemWithParenthesizerRuleSelector :
|
||||
emitListItemWithParenthesizerRule;
|
||||
}
|
||||
|
||||
function getTransformerNames(node: Node | undefined) {
|
||||
let transformerNames: string[] | undefined;
|
||||
let lastTransformerName: string | undefined;
|
||||
while (node) {
|
||||
const transformerName = getTransformerName(node.transformer);
|
||||
if (transformerName && transformerName !== lastTransformerName) {
|
||||
transformerNames = append(transformerNames, transformerName);
|
||||
lastTransformerName = transformerName;
|
||||
}
|
||||
node = node.original;
|
||||
}
|
||||
return transformerNames;
|
||||
}
|
||||
|
||||
function getTransformerName(transformer: TransformerFactory<Node> | undefined) {
|
||||
switch (transformer) {
|
||||
case transformTypeScript: return "ts";
|
||||
case transformESNext: return "esnext";
|
||||
case transformES2021: return "es2021";
|
||||
case transformES2020: return "es2020";
|
||||
case transformES2019: return "es2019";
|
||||
case transformES2018: return "es2018";
|
||||
case transformES2017: return "es2017";
|
||||
case transformES2016: return "es2016";
|
||||
case transformES2015: return "es2015";
|
||||
case transformES5: return "es5";
|
||||
case transformClassFields: return "classFields";
|
||||
case transformLegacyDecorators: return "legacyDecorators";
|
||||
case transformGenerators: return "generators";
|
||||
case transformJsx: return "jsx";
|
||||
case transformECMAScriptModule: return "esmodule";
|
||||
case transformModule: return "module";
|
||||
case transformSystemModule: return "system";
|
||||
case transformNodeModule: return "node";
|
||||
case transformDeclarations: return "declarations";
|
||||
case undefined: return;
|
||||
default: return transformer.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,12 +39,27 @@ namespace ts {
|
||||
let hasPendingSource = false;
|
||||
let hasPendingName = false;
|
||||
|
||||
const singleAnnotationCharCodes: number[] = [];
|
||||
let singleAnnotation = "";
|
||||
const annotationsCharCodes: number[] = [];
|
||||
let annotations = "";
|
||||
let lastAnnotatedGeneratedLine = 0;
|
||||
let lastAnnotatedGeneratedCharacter = 0;
|
||||
let lastAnnotationsNameIndex = 0;
|
||||
let lastAnnotations: SourceMapAnnotation[] | undefined;
|
||||
let hasLastAnnotation = false;
|
||||
let pendingAnnotatedGeneratedLine = 0;
|
||||
let pendingAnnotatedGeneratedCharacter = 0;
|
||||
let pendingAnnotations: SourceMapAnnotation[] | undefined;
|
||||
let hasPendingAnnotation = false;
|
||||
|
||||
return {
|
||||
getSources: () => rawSources,
|
||||
addSource,
|
||||
setSourceContent,
|
||||
addName,
|
||||
addMapping,
|
||||
addAnnotation,
|
||||
appendSourceMap,
|
||||
toJSON,
|
||||
toString: () => JSON.stringify(toJSON())
|
||||
@ -142,6 +157,186 @@ namespace ts {
|
||||
exit();
|
||||
}
|
||||
|
||||
function canEncodeAnnotation(annotation: unknown) {
|
||||
switch (typeof annotation) {
|
||||
case "number": return isFinite(annotation);
|
||||
case "string": return true;
|
||||
case "boolean": return true;
|
||||
case "object": return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
function encodeSingleAnnotation(annotation: unknown) {
|
||||
let lastNameIndex = 0;
|
||||
return encodeSingleAnnotationWorker(annotation);
|
||||
|
||||
function encodeSingleAnnotationWorker(annotation: unknown) {
|
||||
switch (typeof annotation) {
|
||||
case "number":
|
||||
appendSingleAnnotationCharCode(CharacterCodes.hash);
|
||||
appendBase64VLQ(appendSingleAnnotationCharCode, annotation);
|
||||
break;
|
||||
case "string":
|
||||
appendSingleAnnotationCharCode(CharacterCodes.at);
|
||||
const nameIndex = addName(annotation);
|
||||
appendBase64VLQ(appendSingleAnnotationCharCode, nameIndex - lastNameIndex);
|
||||
lastNameIndex = nameIndex;
|
||||
break;
|
||||
case "boolean":
|
||||
appendSingleAnnotationCharCode(annotation ? CharacterCodes.t : CharacterCodes.f);
|
||||
break;
|
||||
case "object":
|
||||
if (!annotation) {
|
||||
appendSingleAnnotationCharCode(CharacterCodes.exclamation);
|
||||
break;
|
||||
}
|
||||
if (isArray(annotation)) {
|
||||
appendSingleAnnotationCharCode(CharacterCodes.openBracket);
|
||||
for (let i = 0; i < annotation.length; i++) {
|
||||
if (i > 0) {
|
||||
appendSingleAnnotationCharCode(CharacterCodes.comma);
|
||||
}
|
||||
if (canEncodeAnnotation(annotation[i])) {
|
||||
encodeSingleAnnotationWorker(annotation[i]);
|
||||
}
|
||||
}
|
||||
appendSingleAnnotationCharCode(CharacterCodes.closeBracket);
|
||||
break;
|
||||
}
|
||||
appendSingleAnnotationCharCode(CharacterCodes.openBrace);
|
||||
let hasWrittenProperty = false;
|
||||
for (const key in annotation) {
|
||||
const value = (annotation as any)[key];
|
||||
if (canEncodeAnnotation(key) && canEncodeAnnotation(value)) {
|
||||
if (hasWrittenProperty) {
|
||||
appendSingleAnnotationCharCode(CharacterCodes.comma);
|
||||
}
|
||||
encodeSingleAnnotationWorker(key);
|
||||
appendSingleAnnotationCharCode(CharacterCodes.colon);
|
||||
encodeSingleAnnotationWorker(value);
|
||||
hasWrittenProperty = true;
|
||||
}
|
||||
}
|
||||
appendSingleAnnotationCharCode(CharacterCodes.closeBrace);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isNewAnnotatedGeneratedPosition(generatedLine: number, generatedCharacter: number) {
|
||||
return !hasPendingAnnotation
|
||||
|| pendingAnnotatedGeneratedLine !== generatedLine
|
||||
|| pendingAnnotatedGeneratedCharacter !== generatedCharacter;
|
||||
}
|
||||
|
||||
function shouldCommitPendingAnnotation() {
|
||||
return !hasLastAnnotation
|
||||
|| lastAnnotatedGeneratedLine !== pendingAnnotatedGeneratedLine
|
||||
|| lastAnnotatedGeneratedCharacter !== pendingAnnotatedGeneratedCharacter
|
||||
|| lastAnnotations !== pendingAnnotations;
|
||||
}
|
||||
|
||||
function flushSingleAnnotationBuffer(): void {
|
||||
if (singleAnnotationCharCodes.length > 0) {
|
||||
singleAnnotation += String.fromCharCode.apply(undefined, singleAnnotationCharCodes);
|
||||
singleAnnotationCharCodes.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function appendSingleAnnotationCharCode(charCode: number) {
|
||||
singleAnnotationCharCodes.push(charCode);
|
||||
// String.fromCharCode accepts its arguments on the stack, so we have to chunk the input,
|
||||
// otherwise we can get stack overflows for large source maps
|
||||
if (singleAnnotationCharCodes.length >= 1024) {
|
||||
flushSingleAnnotationBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
function flushAnnotationsBuffer(): void {
|
||||
if (annotationsCharCodes.length > 0) {
|
||||
annotations += String.fromCharCode.apply(undefined, annotationsCharCodes);
|
||||
annotationsCharCodes.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function appendAnnotationsCharCode(charCode: number) {
|
||||
annotationsCharCodes.push(charCode);
|
||||
// String.fromCharCode accepts its arguments on the stack, so we have to chunk the input,
|
||||
// otherwise we can get stack overflows for large source maps
|
||||
if (annotationsCharCodes.length >= 1024) {
|
||||
flushAnnotationsBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
function commitPendingAnnotations() {
|
||||
if (!hasPendingAnnotation || !shouldCommitPendingAnnotation()) {
|
||||
return;
|
||||
}
|
||||
|
||||
enter();
|
||||
|
||||
// Line/Comma delimiters
|
||||
if (lastAnnotatedGeneratedLine < pendingAnnotatedGeneratedLine) {
|
||||
// Emit line delimiters
|
||||
do {
|
||||
appendAnnotationsCharCode(CharacterCodes.semicolon);
|
||||
lastAnnotatedGeneratedLine++;
|
||||
}
|
||||
while (lastAnnotatedGeneratedLine < pendingAnnotatedGeneratedLine);
|
||||
// Only need to set this once
|
||||
lastAnnotatedGeneratedCharacter = 0;
|
||||
}
|
||||
else {
|
||||
Debug.assertEqual(lastAnnotatedGeneratedLine, pendingAnnotatedGeneratedLine, "generatedLine cannot backtrack");
|
||||
// Emit comma to separate the entry
|
||||
if (hasLast) {
|
||||
appendAnnotationsCharCode(CharacterCodes.comma);
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Relative generated character
|
||||
appendBase64VLQ(appendAnnotationsCharCode, pendingAnnotatedGeneratedCharacter - lastAnnotatedGeneratedCharacter);
|
||||
lastAnnotatedGeneratedCharacter = pendingAnnotatedGeneratedCharacter;
|
||||
|
||||
// 2. Annotations
|
||||
if (canEncodeAnnotation(pendingAnnotations)) {
|
||||
encodeSingleAnnotation(pendingAnnotations);
|
||||
flushSingleAnnotationBuffer();
|
||||
|
||||
const annotationsNameIndex = addName(singleAnnotation);
|
||||
singleAnnotation = "";
|
||||
|
||||
appendBase64VLQ(appendAnnotationsCharCode, annotationsNameIndex - lastAnnotationsNameIndex);
|
||||
lastAnnotationsNameIndex = annotationsNameIndex;
|
||||
}
|
||||
|
||||
lastAnnotations = pendingAnnotations;
|
||||
pendingAnnotations = undefined;
|
||||
|
||||
hasLastAnnotation = false;
|
||||
exit();
|
||||
}
|
||||
|
||||
function addAnnotation(generatedLine: number, generatedCharacter: number, annotationName: string, annotationValue: unknown) {
|
||||
Debug.assert(generatedLine >= pendingAnnotatedGeneratedLine, "generatedLine cannot backtrack");
|
||||
Debug.assert(generatedCharacter >= 0, "generatedCharacter cannot be negative");
|
||||
enter();
|
||||
const annotation: SourceMapAnnotation = { name: annotationName, value: annotationValue };
|
||||
if (isNewAnnotatedGeneratedPosition(generatedLine, generatedCharacter)) {
|
||||
commitPendingAnnotations();
|
||||
pendingAnnotatedGeneratedLine = generatedLine;
|
||||
pendingAnnotatedGeneratedCharacter = generatedCharacter;
|
||||
pendingAnnotations = [annotation];
|
||||
hasPendingAnnotation = true;
|
||||
}
|
||||
else {
|
||||
pendingAnnotations ??= [];
|
||||
pendingAnnotations.push(annotation);
|
||||
}
|
||||
exit();
|
||||
}
|
||||
|
||||
function appendSourceMap(generatedLine: number, generatedCharacter: number, map: RawSourceMap, sourceMapPath: string, start?: LineAndCharacter, end?: LineAndCharacter) {
|
||||
Debug.assert(generatedLine >= pendingGeneratedLine, "generatedLine cannot backtrack");
|
||||
Debug.assert(generatedCharacter >= 0, "generatedCharacter cannot be negative");
|
||||
@ -247,25 +442,25 @@ namespace ts {
|
||||
}
|
||||
|
||||
// 1. Relative generated character
|
||||
appendBase64VLQ(pendingGeneratedCharacter - lastGeneratedCharacter);
|
||||
appendBase64VLQ(appendMappingCharCode, pendingGeneratedCharacter - lastGeneratedCharacter);
|
||||
lastGeneratedCharacter = pendingGeneratedCharacter;
|
||||
|
||||
if (hasPendingSource) {
|
||||
// 2. Relative sourceIndex
|
||||
appendBase64VLQ(pendingSourceIndex - lastSourceIndex);
|
||||
appendBase64VLQ(appendMappingCharCode, pendingSourceIndex - lastSourceIndex);
|
||||
lastSourceIndex = pendingSourceIndex;
|
||||
|
||||
// 3. Relative source line
|
||||
appendBase64VLQ(pendingSourceLine - lastSourceLine);
|
||||
appendBase64VLQ(appendMappingCharCode, pendingSourceLine - lastSourceLine);
|
||||
lastSourceLine = pendingSourceLine;
|
||||
|
||||
// 4. Relative source character
|
||||
appendBase64VLQ(pendingSourceCharacter - lastSourceCharacter);
|
||||
appendBase64VLQ(appendMappingCharCode, pendingSourceCharacter - lastSourceCharacter);
|
||||
lastSourceCharacter = pendingSourceCharacter;
|
||||
|
||||
if (hasPendingName) {
|
||||
// 5. Relative nameIndex
|
||||
appendBase64VLQ(pendingNameIndex - lastNameIndex);
|
||||
appendBase64VLQ(appendMappingCharCode, pendingNameIndex - lastNameIndex);
|
||||
lastNameIndex = pendingNameIndex;
|
||||
}
|
||||
}
|
||||
@ -284,7 +479,7 @@ namespace ts {
|
||||
function toJSON(): RawSourceMap {
|
||||
commitPendingMapping();
|
||||
flushMappingBuffer();
|
||||
return {
|
||||
const sourceMap: RawSourceMap = {
|
||||
version: 3,
|
||||
file,
|
||||
sourceRoot,
|
||||
@ -293,9 +488,15 @@ namespace ts {
|
||||
mappings,
|
||||
sourcesContent,
|
||||
};
|
||||
if (hasPendingAnnotation || hasLastAnnotation) {
|
||||
commitPendingAnnotations();
|
||||
flushAnnotationsBuffer();
|
||||
sourceMap.x_ms_ts_annotations = annotations;
|
||||
}
|
||||
return sourceMap;
|
||||
}
|
||||
|
||||
function appendBase64VLQ(inValue: number): void {
|
||||
function appendBase64VLQ(appendCharCode: (charCode: number) => void, inValue: number): void {
|
||||
// Add a new least significant bit that has the sign of the value.
|
||||
// if negative number the least significant bit that gets added to the number has value 1
|
||||
// else least significant bit value that gets added is 0
|
||||
@ -316,7 +517,7 @@ namespace ts {
|
||||
// There are still more digits to decode, set the msb (6th bit)
|
||||
currentDigit = currentDigit | 32;
|
||||
}
|
||||
appendMappingCharCode(base64FormatEncode(currentDigit));
|
||||
appendCharCode(base64FormatEncode(currentDigit));
|
||||
} while (inValue > 0);
|
||||
}
|
||||
}
|
||||
@ -408,9 +609,37 @@ namespace ts {
|
||||
sourceCharacter: number;
|
||||
}
|
||||
|
||||
export interface CharCodeReader {
|
||||
readonly length: number;
|
||||
pos: number;
|
||||
peek(): number;
|
||||
read(): number;
|
||||
}
|
||||
|
||||
export function createCharCodeReader(text: string): CharCodeReader {
|
||||
const length = text.length;
|
||||
return {
|
||||
length,
|
||||
pos: 0,
|
||||
peek() {
|
||||
const pos = this.pos;
|
||||
return pos < length ? text.charCodeAt(pos) : -1;
|
||||
},
|
||||
read() {
|
||||
const pos = this.pos;
|
||||
if (pos < length) {
|
||||
const ch = text.charCodeAt(pos);
|
||||
this.pos++;
|
||||
return ch;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function decodeMappings(mappings: string): MappingsDecoder {
|
||||
const reader = createCharCodeReader(mappings);
|
||||
let done = false;
|
||||
let pos = 0;
|
||||
let generatedLine = 0;
|
||||
let generatedCharacter = 0;
|
||||
let sourceIndex = 0;
|
||||
@ -420,53 +649,53 @@ namespace ts {
|
||||
let error: string | undefined;
|
||||
|
||||
return {
|
||||
get pos() { return pos; },
|
||||
get pos() { return reader.pos; },
|
||||
get error() { return error; },
|
||||
get state() { return captureMapping(/*hasSource*/ true, /*hasName*/ true); },
|
||||
next() {
|
||||
while (!done && pos < mappings.length) {
|
||||
const ch = mappings.charCodeAt(pos);
|
||||
while (!done && reader.pos < reader.length) {
|
||||
const ch = reader.peek();
|
||||
if (ch === CharacterCodes.semicolon) {
|
||||
// new line
|
||||
generatedLine++;
|
||||
generatedCharacter = 0;
|
||||
pos++;
|
||||
reader.read();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === CharacterCodes.comma) {
|
||||
// Next entry is on same line - no action needed
|
||||
pos++;
|
||||
reader.read();
|
||||
continue;
|
||||
}
|
||||
|
||||
let hasSource = false;
|
||||
let hasName = false;
|
||||
|
||||
generatedCharacter += base64VLQFormatDecode();
|
||||
generatedCharacter += base64VLQFormatDecode(reader, setError);
|
||||
if (hasReportedError()) return stopIterating();
|
||||
if (generatedCharacter < 0) return setErrorAndStopIterating("Invalid generatedCharacter found");
|
||||
|
||||
if (!isSourceMappingSegmentEnd()) {
|
||||
hasSource = true;
|
||||
|
||||
sourceIndex += base64VLQFormatDecode();
|
||||
sourceIndex += base64VLQFormatDecode(reader, setError);
|
||||
if (hasReportedError()) return stopIterating();
|
||||
if (sourceIndex < 0) return setErrorAndStopIterating("Invalid sourceIndex found");
|
||||
if (isSourceMappingSegmentEnd()) return setErrorAndStopIterating("Unsupported Format: No entries after sourceIndex");
|
||||
|
||||
sourceLine += base64VLQFormatDecode();
|
||||
sourceLine += base64VLQFormatDecode(reader, setError);
|
||||
if (hasReportedError()) return stopIterating();
|
||||
if (sourceLine < 0) return setErrorAndStopIterating("Invalid sourceLine found");
|
||||
if (isSourceMappingSegmentEnd()) return setErrorAndStopIterating("Unsupported Format: No entries after sourceLine");
|
||||
|
||||
sourceCharacter += base64VLQFormatDecode();
|
||||
sourceCharacter += base64VLQFormatDecode(reader, setError);
|
||||
if (hasReportedError()) return stopIterating();
|
||||
if (sourceCharacter < 0) return setErrorAndStopIterating("Invalid sourceCharacter found");
|
||||
|
||||
if (!isSourceMappingSegmentEnd()) {
|
||||
hasName = true;
|
||||
nameIndex += base64VLQFormatDecode();
|
||||
nameIndex += base64VLQFormatDecode(reader, setError);
|
||||
if (hasReportedError()) return stopIterating();
|
||||
if (nameIndex < 0) return setErrorAndStopIterating("Invalid nameIndex found");
|
||||
|
||||
@ -515,44 +744,45 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isSourceMappingSegmentEnd() {
|
||||
return (pos === mappings.length ||
|
||||
mappings.charCodeAt(pos) === CharacterCodes.comma ||
|
||||
mappings.charCodeAt(pos) === CharacterCodes.semicolon);
|
||||
return (reader.pos === reader.length ||
|
||||
reader.peek() === CharacterCodes.comma ||
|
||||
reader.peek() === CharacterCodes.semicolon);
|
||||
}
|
||||
|
||||
function base64VLQFormatDecode(): number {
|
||||
let moreDigits = true;
|
||||
let shiftCount = 0;
|
||||
let value = 0;
|
||||
}
|
||||
|
||||
for (; moreDigits; pos++) {
|
||||
if (pos >= mappings.length) return setError("Error in decoding base64VLQFormatDecode, past the mapping string"), -1;
|
||||
export function base64VLQFormatDecode(reader: CharCodeReader, reportError: (error: string) => void): number {
|
||||
let moreDigits = true;
|
||||
let shiftCount = 0;
|
||||
let value = 0;
|
||||
|
||||
// 6 digit number
|
||||
const currentByte = base64FormatDecode(mappings.charCodeAt(pos));
|
||||
if (currentByte === -1) return setError("Invalid character in VLQ"), -1;
|
||||
for (; moreDigits; reader.read()) {
|
||||
if (reader.pos >= reader.length) return reportError("Error in decoding base64VLQFormatDecode, past the mapping string"), -1;
|
||||
|
||||
// If msb is set, we still have more bits to continue
|
||||
moreDigits = (currentByte & 32) !== 0;
|
||||
// 6 digit number
|
||||
const currentByte = base64FormatDecode(reader.peek());
|
||||
if (currentByte === -1) return reportError("Invalid character in VLQ"), -1;
|
||||
|
||||
// least significant 5 bits are the next msbs in the final value.
|
||||
value = value | ((currentByte & 31) << shiftCount);
|
||||
shiftCount += 5;
|
||||
}
|
||||
// If msb is set, we still have more bits to continue
|
||||
moreDigits = (currentByte & 32) !== 0;
|
||||
|
||||
// Least significant bit if 1 represents negative and rest of the msb is actual absolute value
|
||||
if ((value & 1) === 0) {
|
||||
// + number
|
||||
value = value >> 1;
|
||||
}
|
||||
else {
|
||||
// - number
|
||||
value = value >> 1;
|
||||
value = -value;
|
||||
}
|
||||
|
||||
return value;
|
||||
// least significant 5 bits are the next msbs in the final value.
|
||||
value = value | ((currentByte & 31) << shiftCount);
|
||||
shiftCount += 5;
|
||||
}
|
||||
|
||||
// Least significant bit if 1 represents negative and rest of the msb is actual absolute value
|
||||
if ((value & 1) === 0) {
|
||||
// + number
|
||||
value = value >> 1;
|
||||
}
|
||||
else {
|
||||
// - number
|
||||
value = value >> 1;
|
||||
value = -value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function sameMapping<T extends Mapping>(left: T, right: T) {
|
||||
|
||||
146
src/compiler/sourcemapPublic.ts
Normal file
146
src/compiler/sourcemapPublic.ts
Normal file
@ -0,0 +1,146 @@
|
||||
namespace ts {
|
||||
export interface DecodedSourceMapAnnotation {
|
||||
generatedLine: number;
|
||||
generatedCharacter: number;
|
||||
annotations: SourceMapAnnotation[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the custom `x_ms_ts_annotations` field of a SourceMap.
|
||||
*/
|
||||
export function decodeSourceMapAnnotations(annotations: string, names: readonly string[]): Iterator<DecodedSourceMapAnnotation> {
|
||||
const reader = createCharCodeReader(annotations);
|
||||
let done = false;
|
||||
let generatedLine = 0;
|
||||
let generatedCharacter = 0;
|
||||
let nameIndex = 0;
|
||||
|
||||
return {
|
||||
next() {
|
||||
debugger;
|
||||
while (!done && reader.pos < reader.length) {
|
||||
const ch = reader.peek();
|
||||
if (ch === CharacterCodes.semicolon) {
|
||||
generatedLine++;
|
||||
generatedCharacter = 0;
|
||||
reader.read();
|
||||
continue;
|
||||
}
|
||||
if (ch === CharacterCodes.comma) {
|
||||
reader.read();
|
||||
continue;
|
||||
}
|
||||
|
||||
generatedCharacter += base64VLQFormatDecode(reader, noop);
|
||||
if (generatedCharacter < 0) break;
|
||||
if (isSegmentEnd()) continue;
|
||||
|
||||
nameIndex += base64VLQFormatDecode(reader, noop);
|
||||
const name = elementAt(names, nameIndex);
|
||||
if (name) {
|
||||
const annotations = decodeAnnotations(createCharCodeReader(name));
|
||||
if (annotations) {
|
||||
return { value: { generatedLine, generatedCharacter, annotations }, done };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done = true;
|
||||
return { value: undefined!, done: true } as { value: never, done: true };
|
||||
}
|
||||
};
|
||||
|
||||
function isSegmentEnd() {
|
||||
return (reader.pos === reader.length ||
|
||||
reader.peek() === CharacterCodes.comma ||
|
||||
reader.peek() === CharacterCodes.semicolon);
|
||||
}
|
||||
|
||||
function decodeAnnotations(reader: CharCodeReader): SourceMapAnnotation[] | undefined {
|
||||
let nameIndex = 0;
|
||||
const annotations = decodeAnnotationWorker();
|
||||
if (Array.isArray(annotations) && annotations.every(isSourceMapAnnotation)) {
|
||||
return annotations;
|
||||
}
|
||||
|
||||
function decodeAnnotationWorker(): unknown {
|
||||
if (reader.pos < reader.length) {
|
||||
let ch = reader.peek();
|
||||
if (ch === CharacterCodes.hash) {
|
||||
reader.read();
|
||||
return base64VLQFormatDecode(reader, noop);
|
||||
}
|
||||
else if (ch === CharacterCodes.at) {
|
||||
reader.read();
|
||||
nameIndex += base64VLQFormatDecode(reader, noop);
|
||||
return elementAt(names, nameIndex);
|
||||
}
|
||||
else if (ch === CharacterCodes.t || ch === CharacterCodes.f) {
|
||||
reader.read();
|
||||
return ch === CharacterCodes.t;
|
||||
}
|
||||
else if (ch === CharacterCodes.exclamation) {
|
||||
reader.read();
|
||||
return null; // eslint-disable-line no-null/no-null
|
||||
}
|
||||
else if (ch === CharacterCodes.openBracket) {
|
||||
const array: unknown[] = [];
|
||||
reader.read();
|
||||
while (reader.pos < reader.length) {
|
||||
ch = reader.peek();
|
||||
if (ch === CharacterCodes.closeBracket) {
|
||||
reader.read();
|
||||
return array;
|
||||
}
|
||||
if (array.length > 0) {
|
||||
if (ch !== CharacterCodes.comma) {
|
||||
return;
|
||||
}
|
||||
reader.read();
|
||||
}
|
||||
array.push(decodeAnnotationWorker());
|
||||
}
|
||||
}
|
||||
else if (ch === CharacterCodes.openBrace) {
|
||||
const obj: any = {};
|
||||
let hasReadProperty = false;
|
||||
reader.read();
|
||||
while (reader.pos < reader.length) {
|
||||
ch = reader.peek();
|
||||
if (ch === CharacterCodes.closeBrace) {
|
||||
reader.read();
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (hasReadProperty) {
|
||||
if (ch !== CharacterCodes.comma) {
|
||||
return;
|
||||
}
|
||||
reader.read();
|
||||
if (reader.pos >= reader.length) return;
|
||||
}
|
||||
|
||||
const key = decodeAnnotationWorker();
|
||||
if (typeof key !== "string") return;
|
||||
if (reader.pos >= reader.length) return;
|
||||
|
||||
ch = reader.peek();
|
||||
if (ch !== CharacterCodes.colon) return;
|
||||
|
||||
reader.read();
|
||||
if (reader.pos >= reader.length) return;
|
||||
|
||||
const value = decodeAnnotationWorker();
|
||||
obj[key] = value;
|
||||
hasReadProperty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isSourceMapAnnotation(value: unknown): value is SourceMapAnnotation {
|
||||
return typeof value === "object" && !!value && typeof (value as SourceMapAnnotation).name === "string";
|
||||
}
|
||||
}
|
||||
@ -147,12 +147,13 @@ namespace ts {
|
||||
*
|
||||
* @param resolver The emit resolver provided by the checker.
|
||||
* @param host The emit host object used to interact with the file system.
|
||||
* @param factoryIn The `NodeFactory` to use to create new nodes.
|
||||
* @param options Compiler options to surface in the `TransformationContext`.
|
||||
* @param nodes An array of nodes to transform.
|
||||
* @param transforms An array of `TransformerFactory` callbacks.
|
||||
* @param allowDtsFiles A value indicating whether to allow the transformation of .d.ts files.
|
||||
*/
|
||||
export function transformNodes<T extends Node>(resolver: EmitResolver | undefined, host: EmitHost | undefined, factory: NodeFactory, options: CompilerOptions, nodes: readonly T[], transformers: readonly TransformerFactory<T>[], allowDtsFiles: boolean): TransformationResult<T> {
|
||||
export function transformNodes<T extends Node>(resolver: EmitResolver | undefined, host: EmitHost | undefined, factoryIn: NodeFactory | undefined, options: CompilerOptions, nodes: readonly T[], transformers: readonly TransformerFactory<T>[], allowDtsFiles: boolean): TransformationResult<T> {
|
||||
const enabledSyntaxKindFeatures = new Array<SyntaxKindFeatureFlags>(SyntaxKind.Count);
|
||||
let lexicalEnvironmentVariableDeclarations: VariableDeclaration[];
|
||||
let lexicalEnvironmentFunctionDeclarations: FunctionDeclaration[];
|
||||
@ -171,12 +172,13 @@ namespace ts {
|
||||
let onSubstituteNode: TransformationContext["onSubstituteNode"] = noEmitSubstitution;
|
||||
let onEmitNode: TransformationContext["onEmitNode"] = noEmitNotification;
|
||||
let state = TransformationState.Uninitialized;
|
||||
const shouldAttachTransformer = options.annotateTransforms || Debug.isDebugging;
|
||||
const diagnostics: DiagnosticWithLocation[] = [];
|
||||
|
||||
// The transformation context is provided to each transformer as part of transformer
|
||||
// initialization.
|
||||
const context: TransformationContext = {
|
||||
factory,
|
||||
factory: factoryIn ?? factory,
|
||||
getCompilerOptions: () => options,
|
||||
getEmitResolver: () => resolver!, // TODO: GH#18217
|
||||
getEmitHost: () => host!, // TODO: GH#18217
|
||||
@ -224,7 +226,7 @@ namespace ts {
|
||||
performance.mark("beforeTransform");
|
||||
|
||||
// Chain together and initialize each transformer.
|
||||
const transformersWithContext = transformers.map(t => t(context));
|
||||
const transformersWithContext = transformers.map(createTransformerWithContext);
|
||||
const transformation = (node: T): T => {
|
||||
for (const transform of transformersWithContext) {
|
||||
node = transform(node);
|
||||
@ -258,6 +260,41 @@ namespace ts {
|
||||
diagnostics
|
||||
};
|
||||
|
||||
function createTransformerWithContext(t: TransformerFactory<T>) {
|
||||
if (factoryIn || !shouldAttachTransformer) {
|
||||
return t(context);
|
||||
}
|
||||
|
||||
const baseFactory = factory.baseFactory;
|
||||
const perTransformerBaseFactory: BaseNodeFactory = {
|
||||
createBaseSourceFileNode: kind => attachTransformer(baseFactory.createBaseSourceFileNode(kind), t),
|
||||
createBaseIdentifierNode: kind => attachTransformer(baseFactory.createBaseIdentifierNode(kind), t),
|
||||
createBasePrivateIdentifierNode: kind => attachTransformer(baseFactory.createBasePrivateIdentifierNode(kind), t),
|
||||
createBaseTokenNode: kind => attachTransformer(baseFactory.createBaseTokenNode(kind), t),
|
||||
createBaseNode: kind => attachTransformer(baseFactory.createBaseNode(kind), t),
|
||||
};
|
||||
|
||||
const perTransformerFactory = createNodeFactory(NodeFactoryFlags.NoIndentationOnFreshPropertyAccess, perTransformerBaseFactory);
|
||||
const perTransformerContext: TransformationContext = Object.create(context, {
|
||||
factory: {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: perTransformerFactory
|
||||
},
|
||||
getEmitHelperFactory: {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: memoize(() => createEmitHelperFactory(perTransformerContext))
|
||||
}
|
||||
});
|
||||
return t(perTransformerContext);
|
||||
}
|
||||
|
||||
function attachTransformer<TNode extends Node>(node: TNode, transformer: TransformerFactory<T>) {
|
||||
node.transformer = transformer;
|
||||
return node;
|
||||
}
|
||||
|
||||
function transformRoot(node: T) {
|
||||
return node && (!isSourceFile(node) || !node.isDeclarationFile) ? transformation(node) : node;
|
||||
}
|
||||
|
||||
@ -43,6 +43,7 @@
|
||||
"checker.ts",
|
||||
"visitorPublic.ts",
|
||||
"sourcemap.ts",
|
||||
"sourcemapPublic.ts",
|
||||
"transformers/utilities.ts",
|
||||
"transformers/destructuring.ts",
|
||||
"transformers/taggedTemplate.ts",
|
||||
|
||||
@ -885,6 +885,7 @@ namespace ts {
|
||||
/* @internal */ emitNode?: EmitNode; // Associated EmitNode (initialized by transforms)
|
||||
/* @internal */ contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution
|
||||
/* @internal */ inferenceContext?: InferenceContext; // Inference context for contextual type
|
||||
/* @internal */ transformer?: TransformerFactory<Node>; // Source transformer that created the node
|
||||
}
|
||||
|
||||
export interface JSDocContainer {
|
||||
@ -6669,6 +6670,7 @@ namespace ts {
|
||||
esModuleInterop?: boolean;
|
||||
/* @internal */ showConfig?: boolean;
|
||||
useDefineForClassFields?: boolean;
|
||||
annotateTransforms?: boolean;
|
||||
|
||||
[option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined;
|
||||
}
|
||||
@ -8786,6 +8788,7 @@ namespace ts {
|
||||
/*@internal*/ stripInternal?: boolean;
|
||||
/*@internal*/ preserveSourceNewlines?: boolean;
|
||||
/*@internal*/ terminateUnterminatedLiterals?: boolean;
|
||||
/*@internal*/ annotateTransforms?: boolean;
|
||||
/*@internal*/ relativeToBuildInfo?: (path: string) => string;
|
||||
}
|
||||
|
||||
@ -8798,6 +8801,8 @@ namespace ts {
|
||||
sourcesContent?: (string | null)[] | null;
|
||||
mappings: string;
|
||||
names?: string[] | null;
|
||||
[key: `x_${string}`]: unknown;
|
||||
x_ms_ts_annotations?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -8830,6 +8835,10 @@ namespace ts {
|
||||
* Appends a source map.
|
||||
*/
|
||||
appendSourceMap(generatedLine: number, generatedCharacter: number, sourceMap: RawSourceMap, sourceMapPath: string, start?: LineAndCharacter, end?: LineAndCharacter): void;
|
||||
/**
|
||||
* Adds a JSON annotation at the specified line and character.
|
||||
*/
|
||||
addAnnotation(generatedLine: number, generatedCharacter: number, annotationName: string, annotationValue: unknown): void;
|
||||
/**
|
||||
* Gets the source map as a `RawSourceMap` object.
|
||||
*/
|
||||
@ -8840,6 +8849,11 @@ namespace ts {
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
export interface SourceMapAnnotation {
|
||||
name: string;
|
||||
value: unknown;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface DocumentPositionMapperHost {
|
||||
getSourceFileLike(fileName: string): SourceFileLike | undefined;
|
||||
|
||||
@ -3088,6 +3088,7 @@ declare namespace ts {
|
||||
typeRoots?: string[];
|
||||
esModuleInterop?: boolean;
|
||||
useDefineForClassFields?: boolean;
|
||||
annotateTransforms?: boolean;
|
||||
[option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined;
|
||||
}
|
||||
export interface WatchOptions {
|
||||
@ -4052,6 +4053,10 @@ declare namespace ts {
|
||||
omitTrailingSemicolon?: boolean;
|
||||
noEmitHelpers?: boolean;
|
||||
}
|
||||
export interface SourceMapAnnotation {
|
||||
name: string;
|
||||
value: unknown;
|
||||
}
|
||||
export interface GetEffectiveTypeRootsHost {
|
||||
directoryExists?(directoryName: string): boolean;
|
||||
getCurrentDirectory?(): string;
|
||||
@ -5141,6 +5146,17 @@ declare namespace ts {
|
||||
*/
|
||||
function visitEachChild<T extends Node>(node: T | undefined, visitor: Visitor, context: TransformationContext, nodesVisitor?: typeof visitNodes, tokenVisitor?: Visitor): T | undefined;
|
||||
}
|
||||
declare namespace ts {
|
||||
interface DecodedSourceMapAnnotation {
|
||||
generatedLine: number;
|
||||
generatedCharacter: number;
|
||||
annotations: SourceMapAnnotation[];
|
||||
}
|
||||
/**
|
||||
* Decodes the custom `x_ms_ts_annotations` field of a SourceMap.
|
||||
*/
|
||||
function decodeSourceMapAnnotations(annotations: string, names: readonly string[]): Iterator<DecodedSourceMapAnnotation>;
|
||||
}
|
||||
declare namespace ts {
|
||||
function getTsBuildInfoEmitOutputFilePath(options: CompilerOptions): string | undefined;
|
||||
function getOutputFileNames(commandLine: ParsedCommandLine, inputFileName: string, ignoreCase: boolean): readonly string[];
|
||||
|
||||
16
tests/baselines/reference/api/typescript.d.ts
vendored
16
tests/baselines/reference/api/typescript.d.ts
vendored
@ -3088,6 +3088,7 @@ declare namespace ts {
|
||||
typeRoots?: string[];
|
||||
esModuleInterop?: boolean;
|
||||
useDefineForClassFields?: boolean;
|
||||
annotateTransforms?: boolean;
|
||||
[option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined;
|
||||
}
|
||||
export interface WatchOptions {
|
||||
@ -4052,6 +4053,10 @@ declare namespace ts {
|
||||
omitTrailingSemicolon?: boolean;
|
||||
noEmitHelpers?: boolean;
|
||||
}
|
||||
export interface SourceMapAnnotation {
|
||||
name: string;
|
||||
value: unknown;
|
||||
}
|
||||
export interface GetEffectiveTypeRootsHost {
|
||||
directoryExists?(directoryName: string): boolean;
|
||||
getCurrentDirectory?(): string;
|
||||
@ -5141,6 +5146,17 @@ declare namespace ts {
|
||||
*/
|
||||
function visitEachChild<T extends Node>(node: T | undefined, visitor: Visitor, context: TransformationContext, nodesVisitor?: typeof visitNodes, tokenVisitor?: Visitor): T | undefined;
|
||||
}
|
||||
declare namespace ts {
|
||||
interface DecodedSourceMapAnnotation {
|
||||
generatedLine: number;
|
||||
generatedCharacter: number;
|
||||
annotations: SourceMapAnnotation[];
|
||||
}
|
||||
/**
|
||||
* Decodes the custom `x_ms_ts_annotations` field of a SourceMap.
|
||||
*/
|
||||
function decodeSourceMapAnnotations(annotations: string, names: readonly string[]): Iterator<DecodedSourceMapAnnotation>;
|
||||
}
|
||||
declare namespace ts {
|
||||
function getTsBuildInfoEmitOutputFilePath(options: CompilerOptions): string | undefined;
|
||||
function getOutputFileNames(commandLine: ParsedCommandLine, inputFileName: string, ignoreCase: boolean): readonly string[];
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"annotateTransforms": true
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user