Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prototype implementation of Source Maps v4 #1

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,16 @@ namespace ts {
defaultValueDescription: false,
description: Diagnostics.Create_source_map_files_for_emitted_JavaScript_files,
},
{
name: "sourceMapVersion",
type: "number",
affectsEmit: true,
showInSimplifiedHelpView: false,
category: Diagnostics.Emit,
defaultValueDescription: 3,
description: Diagnostics.Specifies_the_version_3_or_4_of_source_maps_that_should_be_generated_by_compiler_Requires_the_sourceMap_option_to_be_enabled,
defaultInitValue: 3,
},
{
name: "outFile",
type: "string",
Expand Down Expand Up @@ -3611,6 +3621,10 @@ namespace ts {


function getDefaultValueForOption(option: CommandLineOption) {
if ("defaultInitValue" in option && option.defaultInitValue !== undefined) {
return option.defaultInitValue;
}

switch (option.type) {
case "number":
return 1;
Expand Down
10 changes: 9 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4161,7 +4161,7 @@
"category": "Message",
"code": 6041
},

"Generates corresponding '.map' file.": {
"category": "Message",
"code": 6043
Expand Down Expand Up @@ -7236,5 +7236,13 @@
"A 'return' statement cannot be used inside a class static block.": {
"category": "Error",
"code": 18041
},
"Specifies the version (3 or 4) of source maps that should be generated by compiler. Requires the sourceMap option to be enabled.": {
"category": "Message",
"code": 18042
},
"If the 'sourceMapVersion' option is specified, it must be set to 3 or 4.": {
"category": "Error",
"code": 18043
}
}
321 changes: 321 additions & 0 deletions src/compiler/emitter.ts

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3307,6 +3307,16 @@ namespace ts {
}
}

if (options.sourceMapVersion) {
if (!options.sourceMap && !options.inlineSourceMap) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "sourceMapVersion", "sourceMap", "inlineSourceMap");
}

if (options.sourceMapVersion !== 3 && options.sourceMapVersion !== 4) {
createDiagnosticForOptionName(Diagnostics.If_the_sourceMapVersion_option_is_specified_it_must_be_set_to_3_or_4, "sourceMapVersion");
}
}

if (options.composite) {
if (options.declaration === false) {
createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_declaration_emit, "declaration");
Expand Down
123 changes: 123 additions & 0 deletions src/compiler/sourcemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ namespace ts {
let nameToNameIndexMap: ESMap<string, number> | undefined;
const mappingCharCodes: number[] = [];
let mappings = "";
const scopeNames: string[] = [];
let scopeNamesToNameIndexMap: ESMap<string, number> | undefined;
const scopeMappingCharCodes: number[] = [];
let scopes = "";

const version = host.getCompilerOptions().sourceMapVersion;

// Last recorded and encoded mappings
let lastGeneratedLine = 0;
Expand All @@ -39,13 +45,21 @@ namespace ts {
let hasPendingSource = false;
let hasPendingName = false;

let lastScopeSourceIndex = 0;
let lastScopeStartingLine = 0;
let lastScopeNameIndex = 0;
let hasScopeMappings = false;

return {
getSources: () => rawSources,
addSource,
setSourceContent,
addName,
addMapping,
appendSourceMap,
addScopeName,
addScopeMapping,
shouldCollectScopeNames: () => !!version && version >= 4,
toJSON,
toString: () => JSON.stringify(toJSON())
};
Expand Down Expand Up @@ -96,6 +110,22 @@ namespace ts {
return nameIndex;
}

function addScopeName(name: string): number {
if (version !== 4) {
return 0;
}
enter();
if (!scopeNamesToNameIndexMap) scopeNamesToNameIndexMap = new Map();
let nameIndex = scopeNamesToNameIndexMap.get(name);
if (nameIndex === undefined) {
nameIndex = scopeNames.length;
scopeNames.push(name);
scopeNamesToNameIndexMap.set(name, nameIndex);
}
exit();
return nameIndex;
}

function isNewGeneratedPosition(generatedLine: number, generatedCharacter: number) {
return !hasPending
|| pendingGeneratedLine !== generatedLine
Expand Down Expand Up @@ -142,6 +172,42 @@ namespace ts {
exit();
}

function addScopeMapping(sourceIndex: number, start: LineAndCharacter, end: LineAndCharacter, scopeNameIndex: number) {
if (version !== 4) {
return;
}
enter();

if (hasScopeMappings) {
appendScopeMappingCharCode(CharacterCodes.comma);
}

// 1. Relative source index
appendBase64VLQForScopeMapping(sourceIndex - lastScopeSourceIndex);
lastScopeSourceIndex = sourceIndex;

// 2. Relative starting line
appendBase64VLQForScopeMapping(start.line - lastScopeStartingLine);
lastScopeStartingLine = start.line;

// 3. Starting column, absolute
appendBase64VLQForScopeMapping(start.character);

// 4. Ending line, relative to STARTING LINE
appendBase64VLQForScopeMapping(end.line - start.line);

// 5. Ending column, absolute
appendBase64VLQForScopeMapping(end.character);

// 6. Relative index into scopeNames array
appendBase64VLQForScopeMapping(scopeNameIndex - lastScopeNameIndex);
lastScopeNameIndex = scopeNameIndex;

hasScopeMappings = true;

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");
Expand Down Expand Up @@ -211,6 +277,15 @@ namespace ts {
|| lastNameIndex !== pendingNameIndex;
}

function appendScopeMappingCharCode(charCode: number) {
scopeMappingCharCodes.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 (scopeMappingCharCodes.length >= 1024) {
flushScopeMappingBuffer();
}
}

function appendMappingCharCode(charCode: number) {
mappingCharCodes.push(charCode);
// String.fromCharCode accepts its arguments on the stack, so we have to chunk the input,
Expand Down Expand Up @@ -274,6 +349,13 @@ namespace ts {
exit();
}

function flushScopeMappingBuffer(): void {
if (scopeMappingCharCodes.length > 0) {
scopes += String.fromCharCode.apply(undefined, scopeMappingCharCodes);
scopeMappingCharCodes.length = 0;
}
}

function flushMappingBuffer(): void {
if (mappingCharCodes.length > 0) {
mappings += String.fromCharCode.apply(undefined, mappingCharCodes);
Expand All @@ -284,6 +366,22 @@ namespace ts {
function toJSON(): RawSourceMap {
commitPendingMapping();
flushMappingBuffer();
flushScopeMappingBuffer();

if (version === 4) {
return {
version,
file,
sourceRoot,
sources,
names,
mappings,
sourcesContent,
scopeNames,
scopes,
};
}

return {
version: 3,
file,
Expand All @@ -295,6 +393,31 @@ namespace ts {
};
}

function appendBase64VLQForScopeMapping(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
// eg. -1 changes to binary : 01 [1] => 3
// +1 changes to binary : 01 [0] => 2
if (inValue < 0) {
inValue = ((-inValue) << 1) + 1;
}
else {
inValue = inValue << 1;
}

// Encode 5 bits at a time starting from least significant bits
do {
let currentDigit = inValue & 31; // 11111
inValue = inValue >> 5;
if (inValue > 0) {
// There are still more digits to decode, set the msb (6th bit)
currentDigit = currentDigit | 32;
}
appendScopeMappingCharCode(base64FormatEncode(currentDigit));
} while (inValue > 0);
}

function appendBase64VLQ(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
Expand Down
38 changes: 37 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6145,6 +6145,7 @@ namespace ts {
skipLibCheck?: boolean;
skipDefaultLibCheck?: boolean;
sourceMap?: boolean;
sourceMapVersion?: 3 | 4;
sourceRoot?: string;
strict?: boolean;
strictFunctionTypes?: boolean; // Always combine with strict property
Expand Down Expand Up @@ -6351,30 +6352,35 @@ namespace ts {
affectsProgramStructure?: true; // true if program should be reconstructed from root files if option changes and does not affect module resolution as affectsModuleResolution indirectly means program needs to reconstructed
transpileOptionValue?: boolean | undefined; // If set this means that the option should be set to this value when transpiling
extraValidation?: (value: CompilerOptionsValue) => [DiagnosticMessage, ...string[]] | undefined; // Additional validation to be performed for the value to be valid
defaultInitValue?: string | number | boolean; // A custom default value to be used when generating tsconfig.json
}

/* @internal */
export interface CommandLineOptionOfStringType extends CommandLineOptionBase {
type: "string";
defaultValueDescription?: string | undefined | DiagnosticMessage;
defaultInitValue?: string | undefined;
}

/* @internal */
export interface CommandLineOptionOfNumberType extends CommandLineOptionBase {
type: "number";
defaultValueDescription: number | undefined | DiagnosticMessage;
defaultInitValue?: number | undefined;
}

/* @internal */
export interface CommandLineOptionOfBooleanType extends CommandLineOptionBase {
type: "boolean";
defaultValueDescription: boolean | undefined | DiagnosticMessage;
defaultInitValue?: boolean | undefined;
}

/* @internal */
export interface CommandLineOptionOfCustomType extends CommandLineOptionBase {
type: ESMap<string, number | string>; // an object literal mapping named values to actual values
defaultValueDescription: number | string | undefined | DiagnosticMessage;
defaultInitValue?: number | string | undefined;
}

/* @internal */
Expand Down Expand Up @@ -8232,7 +8238,7 @@ namespace ts {
}

/* @internal */
export interface RawSourceMap {
export interface RawSourceMapV3 {
version: 3;
file: string;
sourceRoot?: string | null;
Expand All @@ -8242,6 +8248,22 @@ namespace ts {
names?: string[] | null;
}

/* @internal */
export interface RawSourceMapV4 {
version: 4;
file: string;
sourceRoot?: string | null;
sources: string[];
sourcesContent?: (string | null)[] | null;
mappings: string;
names?: string[] | null;
scopeNames: string[];
scopes: string;
}

/* @internal */
export type RawSourceMap = RawSourceMapV3 | RawSourceMapV4;

/**
* Generates a source map.
*/
Expand Down Expand Up @@ -8272,6 +8294,20 @@ namespace ts {
* Appends a source map.
*/
appendSourceMap(generatedLine: number, generatedCharacter: number, sourceMap: RawSourceMap, sourceMapPath: string, start?: LineAndCharacter, end?: LineAndCharacter): void;
/**
* Appends a 'scope name,' a concept in V4 of Source Maps which represents the original-source name of a function scope.
*/
addScopeName(name: string): number;
/**
* Appends a 'scope mapping,' a concept in V4 of Source Maps which associates a runtime name of a function to the original source name.
*/
addScopeMapping(sourceIndex: number, start: LineAndCharacter, end: LineAndCharacter, scopeNameIndex: number): void;
/**
* Whether the compiler should collect scope names during compilation. This should be true for version 4+ of
* source maps, and false for version 3. This is exposed because collecting scope names might negatively
* impact the performance of compilation.
*/
shouldCollectScopeNames(): boolean;
/**
* Gets the source map as a `RawSourceMap` object.
*/
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2978,6 +2978,7 @@ declare namespace ts {
skipLibCheck?: boolean;
skipDefaultLibCheck?: boolean;
sourceMap?: boolean;
sourceMapVersion?: 3 | 4;
sourceRoot?: string;
strict?: boolean;
strictFunctionTypes?: boolean;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2978,6 +2978,7 @@ declare namespace ts {
skipLibCheck?: boolean;
skipDefaultLibCheck?: boolean;
sourceMap?: boolean;
sourceMapVersion?: 3 | 4;
sourceRoot?: string;
strict?: boolean;
strictFunctionTypes?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "sourceMapVersion": 3, /* Specifies the version (3 or 4) of source maps that should be generated by compiler. Requires the sourceMap option to be enabled. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "sourceMapVersion": 3, /* Specifies the version (3 or 4) of source maps that should be generated by compiler. Requires the sourceMap option to be enabled. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "sourceMapVersion": 3, /* Specifies the version (3 or 4) of source maps that should be generated by compiler. Requires the sourceMap option to be enabled. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "sourceMapVersion": 3, /* Specifies the version (3 or 4) of source maps that should be generated by compiler. Requires the sourceMap option to be enabled. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
Expand Down
Loading