Skip to content

Commit 5673660

Browse files
committed
Merge pull request #2484 from Microsoft/inlineSourceMaps
Inline source maps
2 parents 2fc90d0 + 32409f9 commit 5673660

29 files changed

+756
-29
lines changed

Jakefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ var harnessSources = [
127127
"services/documentRegistry.ts",
128128
"services/preProcessFile.ts",
129129
"services/patternMatcher.ts",
130-
"versionCache.ts"
130+
"versionCache.ts",
131+
"convertToBase64.ts"
131132
].map(function (f) {
132133
return path.join(unittestsDirectory, f);
133134
})).concat([

src/compiler/commandLineParser.ts

+8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ module ts {
3030
type: "boolean",
3131
description: Diagnostics.Print_this_message,
3232
},
33+
{
34+
name: "inlineSourceMap",
35+
type: "boolean",
36+
},
37+
{
38+
name: "inlineSources",
39+
type: "boolean",
40+
},
3341
{
3442
name: "listFiles",
3543
type: "boolean",

src/compiler/diagnosticInformationMap.generated.ts

+4
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,10 @@ module ts {
454454
Option_noEmitOnError_cannot_be_specified_with_option_separateCompilation: { code: 5045, category: DiagnosticCategory.Error, key: "Option 'noEmitOnError' cannot be specified with option 'separateCompilation'." },
455455
Option_out_cannot_be_specified_with_option_separateCompilation: { code: 5046, category: DiagnosticCategory.Error, key: "Option 'out' cannot be specified with option 'separateCompilation'." },
456456
Option_separateCompilation_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES6_or_higher: { code: 5047, category: DiagnosticCategory.Error, key: "Option 'separateCompilation' can only be used when either option'--module' is provided or option 'target' is 'ES6' or higher." },
457+
Option_sourceMap_cannot_be_specified_with_option_inlineSourceMap: { code: 5048, category: DiagnosticCategory.Error, key: "Option 'sourceMap' cannot be specified with option 'inlineSourceMap'." },
458+
Option_sourceRoot_cannot_be_specified_with_option_inlineSourceMap: { code: 5049, category: DiagnosticCategory.Error, key: "Option 'sourceRoot' cannot be specified with option 'inlineSourceMap'." },
459+
Option_mapRoot_cannot_be_specified_with_option_inlineSourceMap: { code: 5050, category: DiagnosticCategory.Error, key: "Option 'mapRoot' cannot be specified with option 'inlineSourceMap'." },
460+
Option_inlineSources_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided: { code: 5051, category: DiagnosticCategory.Error, key: "Option 'inlineSources' can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided." },
457461
Concatenate_and_emit_output_to_single_file: { code: 6001, category: DiagnosticCategory.Message, key: "Concatenate and emit output to single file." },
458462
Generates_corresponding_d_ts_file: { code: 6002, category: DiagnosticCategory.Message, key: "Generates corresponding '.d.ts' file." },
459463
Specifies_the_location_where_debugger_should_locate_map_files_instead_of_generated_locations: { code: 6003, category: DiagnosticCategory.Message, key: "Specifies the location where debugger should locate map files instead of generated locations." },

src/compiler/diagnosticMessages.json

+17
Original file line numberDiff line numberDiff line change
@@ -1804,6 +1804,23 @@
18041804
"category": "Error",
18051805
"code": 5047
18061806
},
1807+
"Option 'sourceMap' cannot be specified with option 'inlineSourceMap'.": {
1808+
"category": "Error",
1809+
"code": 5048
1810+
},
1811+
"Option 'sourceRoot' cannot be specified with option 'inlineSourceMap'.": {
1812+
"category": "Error",
1813+
"code": 5049
1814+
},
1815+
"Option 'mapRoot' cannot be specified with option 'inlineSourceMap'.": {
1816+
"category": "Error",
1817+
"code": 5050
1818+
},
1819+
"Option 'inlineSources' can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided.": {
1820+
"category": "Error",
1821+
"code": 5051
1822+
},
1823+
18071824
"Concatenate and emit output to single file.": {
18081825
"category": "Message",
18091826
"code": 6001

src/compiler/emitter.ts

+45-17
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
5757

5858
let compilerOptions = host.getCompilerOptions();
5959
let languageVersion = compilerOptions.target || ScriptTarget.ES3;
60-
let sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap ? [] : undefined;
60+
let sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined;
6161
let diagnostics: Diagnostic[] = [];
6262
let newLine = host.getNewLine();
6363

@@ -181,7 +181,7 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
181181
/** Sourcemap data that will get encoded */
182182
let sourceMapData: SourceMapData;
183183

184-
if (compilerOptions.sourceMap) {
184+
if (compilerOptions.sourceMap || compilerOptions.inlineSourceMap) {
185185
initializeEmitterWithSourceMaps();
186186
}
187187

@@ -506,6 +506,13 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
506506

507507
// The one that can be used from program to get the actual source file
508508
sourceMapData.inputSourceFileNames.push(node.fileName);
509+
510+
if (compilerOptions.inlineSources) {
511+
if (!sourceMapData.sourceMapSourcesContent) {
512+
sourceMapData.sourceMapSourcesContent = [];
513+
}
514+
sourceMapData.sourceMapSourcesContent.push(node.text);
515+
}
509516
}
510517

511518
function recordScopeNameOfNode(node: Node, scopeName?: string) {
@@ -577,19 +584,25 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
577584
recordSourceMapSpan(comment.end);
578585
}
579586

580-
function serializeSourceMapContents(version: number, file: string, sourceRoot: string, sources: string[], names: string[], mappings: string) {
587+
function serializeSourceMapContents(version: number, file: string, sourceRoot: string, sources: string[], names: string[], mappings: string, sourcesContent?: string[]) {
581588
if (typeof JSON !== "undefined") {
582-
return JSON.stringify({
583-
version: version,
584-
file: file,
585-
sourceRoot: sourceRoot,
586-
sources: sources,
587-
names: names,
588-
mappings: mappings
589-
});
589+
let map: any = {
590+
version,
591+
file,
592+
sourceRoot,
593+
sources,
594+
names,
595+
mappings
596+
};
597+
598+
if (sourcesContent !== undefined) {
599+
map.sourcesContent = sourcesContent;
600+
}
601+
602+
return JSON.stringify(map);
590603
}
591604

592-
return "{\"version\":" + version + ",\"file\":\"" + escapeString(file) + "\",\"sourceRoot\":\"" + escapeString(sourceRoot) + "\",\"sources\":[" + serializeStringArray(sources) + "],\"names\":[" + serializeStringArray(names) + "],\"mappings\":\"" + escapeString(mappings) + "\"}";
605+
return "{\"version\":" + version + ",\"file\":\"" + escapeString(file) + "\",\"sourceRoot\":\"" + escapeString(sourceRoot) + "\",\"sources\":[" + serializeStringArray(sources) + "],\"names\":[" + serializeStringArray(names) + "],\"mappings\":\"" + escapeString(mappings) + "\" " + (sourcesContent !== undefined ? ",\"sourcesContent\":[" + serializeStringArray(sourcesContent) + "]" : "") + "}";
593606

594607
function serializeStringArray(list: string[]): string {
595608
let output = "";
@@ -604,19 +617,33 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
604617
}
605618

606619
function writeJavaScriptAndSourceMapFile(emitOutput: string, writeByteOrderMark: boolean) {
607-
// Write source map file
608620
encodeLastRecordedSourceMapSpan();
609-
writeFile(host, diagnostics, sourceMapData.sourceMapFilePath, serializeSourceMapContents(
621+
622+
let sourceMapText = serializeSourceMapContents(
610623
3,
611624
sourceMapData.sourceMapFile,
612625
sourceMapData.sourceMapSourceRoot,
613626
sourceMapData.sourceMapSources,
614627
sourceMapData.sourceMapNames,
615-
sourceMapData.sourceMapMappings), /*writeByteOrderMark*/ false);
628+
sourceMapData.sourceMapMappings,
629+
sourceMapData.sourceMapSourcesContent);
630+
616631
sourceMapDataList.push(sourceMapData);
617632

633+
let sourceMapUrl: string;
634+
if (compilerOptions.inlineSourceMap) {
635+
// Encode the sourceMap into the sourceMap url
636+
let base64SourceMapText = convertToBase64(sourceMapText);
637+
sourceMapUrl = `//# sourceMappingURL=data:application/json;base64,${base64SourceMapText}`;
638+
}
639+
else {
640+
// Write source map file
641+
writeFile(host, diagnostics, sourceMapData.sourceMapFilePath, sourceMapText, /*writeByteOrderMark*/ false);
642+
sourceMapUrl = `//# sourceMappingURL=${sourceMapData.jsSourceMappingURL}`;
643+
}
644+
618645
// Write sourcemap url to the js file and write the js file
619-
writeJavaScriptFile(emitOutput + "//# sourceMappingURL=" + sourceMapData.jsSourceMappingURL, writeByteOrderMark);
646+
writeJavaScriptFile(emitOutput + sourceMapUrl, writeByteOrderMark);
620647
}
621648

622649
// Initialize source map data
@@ -630,6 +657,7 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
630657
inputSourceFileNames: [],
631658
sourceMapNames: [],
632659
sourceMapMappings: "",
660+
sourceMapSourcesContent: undefined,
633661
sourceMapDecodedMappings: []
634662
};
635663

@@ -874,7 +902,7 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
874902
function emitLiteral(node: LiteralExpression) {
875903
let text = getLiteralText(node);
876904

877-
if (compilerOptions.sourceMap && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
905+
if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
878906
writer.writeLiteral(text);
879907
}
880908
// For versions below ES6, emit binary & octal literals in their canonical decimal form.

src/compiler/program.ts

+19
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,25 @@ module ts {
534534
}
535535
}
536536

537+
if (options.inlineSourceMap) {
538+
if (options.sourceMap) {
539+
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_sourceMap_cannot_be_specified_with_option_inlineSourceMap));
540+
}
541+
if (options.mapRoot) {
542+
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_mapRoot_cannot_be_specified_with_option_inlineSourceMap));
543+
}
544+
if (options.sourceRoot) {
545+
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_sourceRoot_cannot_be_specified_with_option_inlineSourceMap));
546+
}
547+
}
548+
549+
550+
if (options.inlineSources) {
551+
if (!options.sourceMap && !options.inlineSourceMap) {
552+
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_inlineSources_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided));
553+
}
554+
}
555+
537556
if (!options.sourceMap && (options.mapRoot || options.sourceRoot)) {
538557
// Error to specify --mapRoot or --sourceRoot without mapSourceFiles
539558
if (options.mapRoot) {

src/compiler/types.ts

+11-8
Original file line numberDiff line numberDiff line change
@@ -1096,14 +1096,15 @@ module ts {
10961096
}
10971097

10981098
export interface SourceMapData {
1099-
sourceMapFilePath: string; // Where the sourcemap file is written
1100-
jsSourceMappingURL: string; // source map URL written in the .js file
1101-
sourceMapFile: string; // Source map's file field - .js file name
1102-
sourceMapSourceRoot: string; // Source map's sourceRoot field - location where the sources will be present if not ""
1103-
sourceMapSources: string[]; // Source map's sources field - list of sources that can be indexed in this source map
1104-
inputSourceFileNames: string[]; // Input source file (which one can use on program to get the file), 1:1 mapping with the sourceMapSources list
1105-
sourceMapNames?: string[]; // Source map's names field - list of names that can be indexed in this source map
1106-
sourceMapMappings: string; // Source map's mapping field - encoded source map spans
1099+
sourceMapFilePath: string; // Where the sourcemap file is written
1100+
jsSourceMappingURL: string; // source map URL written in the .js file
1101+
sourceMapFile: string; // Source map's file field - .js file name
1102+
sourceMapSourceRoot: string; // Source map's sourceRoot field - location where the sources will be present if not ""
1103+
sourceMapSources: string[]; // Source map's sources field - list of sources that can be indexed in this source map
1104+
sourceMapSourcesContent?: string[]; // Source map's sourcesContent field - list of the sources' text to be embedded in the source map
1105+
inputSourceFileNames: string[]; // Input source file (which one can use on program to get the file), 1:1 mapping with the sourceMapSources list
1106+
sourceMapNames?: string[]; // Source map's names field - list of names that can be indexed in this source map
1107+
sourceMapMappings: string; // Source map's mapping field - encoded source map spans
11071108
sourceMapDecodedMappings: SourceMapSpan[]; // Raw source map spans that were encoded into the sourceMapMappings
11081109
}
11091110

@@ -1647,6 +1648,8 @@ module ts {
16471648
diagnostics?: boolean;
16481649
emitBOM?: boolean;
16491650
help?: boolean;
1651+
inlineSourceMap?: boolean;
1652+
inlineSources?: boolean;
16501653
listFiles?: boolean;
16511654
locale?: string;
16521655
mapRoot?: string;

src/compiler/utilities.ts

+77
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,83 @@ module ts {
16951695
export function getLocalSymbolForExportDefault(symbol: Symbol) {
16961696
return symbol && symbol.valueDeclaration && (symbol.valueDeclaration.flags & NodeFlags.Default) ? symbol.valueDeclaration.localSymbol : undefined;
16971697
}
1698+
1699+
/**
1700+
* Replace each instance of non-ascii characters by one, two, three, or four escape sequences
1701+
* representing the UTF-8 encoding of the character, and return the expanded char code list.
1702+
*/
1703+
function getExpandedCharCodes(input: string): number[] {
1704+
let output: number[] = [];
1705+
let length = input.length;
1706+
let leadSurrogate: number = undefined;
1707+
1708+
for (let i = 0; i < length; i++) {
1709+
let charCode = input.charCodeAt(i);
1710+
1711+
// handel utf8
1712+
if (charCode < 0x80) {
1713+
output.push(charCode);
1714+
}
1715+
else if (charCode < 0x800) {
1716+
output.push((charCode >> 6) | 0B11000000);
1717+
output.push((charCode & 0B00111111) | 0B10000000);
1718+
}
1719+
else if (charCode < 0x10000) {
1720+
output.push((charCode >> 12) | 0B11100000);
1721+
output.push(((charCode >> 6) & 0B00111111) | 0B10000000);
1722+
output.push((charCode & 0B00111111) | 0B10000000);
1723+
}
1724+
else if (charCode < 0x20000) {
1725+
output.push((charCode >> 18) | 0B11110000);
1726+
output.push(((charCode >> 12) & 0B00111111) | 0B10000000);
1727+
output.push(((charCode >> 6) & 0B00111111) | 0B10000000);
1728+
output.push((charCode & 0B00111111) | 0B10000000);
1729+
}
1730+
else {
1731+
Debug.assert(false, "Unexpected code point");
1732+
}
1733+
}
1734+
1735+
return output;
1736+
}
1737+
1738+
const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
1739+
1740+
/**
1741+
* Converts a string to a base-64 encoded ASCII string.
1742+
*/
1743+
export function convertToBase64(input: string): string {
1744+
var result = "";
1745+
let charCodes = getExpandedCharCodes(input);
1746+
let i = 0;
1747+
let length = charCodes.length;
1748+
let byte1: number, byte2: number, byte3: number, byte4: number;
1749+
1750+
while (i < length) {
1751+
// Convert every 6-bits in the input 3 character points
1752+
// into a base64 digit
1753+
byte1 = charCodes[i] >> 2;
1754+
byte2 = (charCodes[i] & 0B00000011) << 4 | charCodes[i + 1] >> 4;
1755+
byte3 = (charCodes[i + 1] & 0B00001111) << 2 | charCodes[i + 2] >> 6;
1756+
byte4 = charCodes[i + 2] & 0B00111111;
1757+
1758+
// We are out of characters in the input, set the extra
1759+
// digits to 64 (padding character).
1760+
if (i + 1 >= length) {
1761+
byte3 = byte4 = 64;
1762+
}
1763+
else if (i + 2 >= length) {
1764+
byte4 = 64;
1765+
}
1766+
1767+
// Write to the ouput
1768+
result += base64Digits.charAt(byte1) + base64Digits.charAt(byte2) + base64Digits.charAt(byte3) + base64Digits.charAt(byte4);
1769+
1770+
i += 3;
1771+
}
1772+
1773+
return result;
1774+
}
16981775
}
16991776

17001777
module ts {

src/harness/compilerRunner.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ class CompilerBaselineRunner extends RunnerBase {
159159

160160
// Source maps?
161161
it('Correct sourcemap content for ' + fileName, () => {
162-
if (options.sourceMap) {
162+
if (options.sourceMap || options.inlineSourceMap) {
163163
Harness.Baseline.runBaseline('Correct sourcemap content for ' + fileName, justName.replace(/\.ts$/, '.sourcemap.txt'), () => {
164164
var record = result.getSourceMapRecord();
165165
if (options.noEmitOnError && result.errors.length !== 0 && record === undefined) {
@@ -228,7 +228,13 @@ class CompilerBaselineRunner extends RunnerBase {
228228
});
229229

230230
it('Correct Sourcemap output for ' + fileName, () => {
231-
if (options.sourceMap) {
231+
if (options.inlineSourceMap) {
232+
if (result.sourceMaps.length > 0) {
233+
throw new Error('No sourcemap files should be generated if inlineSourceMaps was set.');
234+
}
235+
return null;
236+
}
237+
else if (options.sourceMap) {
232238
if (result.sourceMaps.length !== result.files.length) {
233239
throw new Error('Number of sourcemap files should be same as js files.');
234240
}

0 commit comments

Comments
 (0)