Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ab7cb27
Add compiler option to enable declaration sourcemaps
weswigham Feb 15, 2018
91cc19c
Transparent goto definition for sourcemapped declaration files
weswigham Feb 17, 2018
c291828
Post-rebase touchups
weswigham Mar 16, 2018
2b26a27
Rename API methods
weswigham Mar 16, 2018
ec44da5
Fix lints
weswigham Mar 16, 2018
ffb7f85
Fix typo in name XD
weswigham Mar 16, 2018
589ac28
Log sourcemap decode errors
weswigham Mar 16, 2018
5584a2d
Share the cache more, but also invalidate it more
weswigham Mar 16, 2018
238ba98
Remove todo
weswigham Mar 16, 2018
ff158cf
Enable mapping on go to implementation as well
weswigham Mar 17, 2018
b9f6149
Allow fourslash to test declaration maps mroe easily
weswigham Mar 19, 2018
53f72de
more test
weswigham Mar 19, 2018
29c732e
Merge branch 'master' into decl-maps
weswigham Mar 19, 2018
6070108
Handle sourceRoot
weswigham Mar 19, 2018
0a63553
Add tests documenting current behavior with other sourcemapping flags
weswigham Mar 19, 2018
d8480b2
Ignore inline options for declaration file maps, simplify dispatch in…
weswigham Mar 20, 2018
ca88d5e
Change program diagnostic
weswigham Mar 20, 2018
2b231d7
Fix nit
weswigham Mar 20, 2018
fed6132
Use charCodeAt
weswigham Mar 20, 2018
509e4f3
Rename internal methods + veriables
weswigham Mar 20, 2018
0c44ba1
Avoid filter
weswigham Mar 20, 2018
c8620e3
span -> position
weswigham Mar 20, 2018
5687e10
Use character codes
weswigham Mar 20, 2018
e64e73d
Dont parse our sourcemap names until we need to start using them
weswigham Mar 20, 2018
47e701a
zero-index parsed positions
weswigham Mar 20, 2018
6a467ba
Handle sourceMappingURL comments, including base64 encoded ones
weswigham Mar 20, 2018
f2c8d3f
Unittest b64 decoder, make mroe robust to handle unicode properly
weswigham Mar 20, 2018
bd9cccf
Fix lint
weswigham Mar 20, 2018
c7a5296
declarationMaps -> declarationMap
weswigham Mar 20, 2018
f6e81f2
Merge branch 'master' into decl-maps
weswigham Mar 24, 2018
d0a5e28
Even more feedback
weswigham Mar 24, 2018
943dc12
USE Mroe lenient combined regexp
weswigham Mar 26, 2018
265cc1a
only match base64 characters
weswigham Mar 26, 2018
e34a6bd
Fix nit
weswigham Mar 26, 2018
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
Prev Previous commit
Next Next commit
Handle sourceMappingURL comments, including base64 encoded ones
  • Loading branch information
weswigham committed Mar 20, 2018
commit 6a467ba0415597bd8b7ce993b1ba620f74a077d9
6 changes: 6 additions & 0 deletions src/compiler/sys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ namespace ts {
clearTimeout?(timeoutId: any): void;
clearScreen?(): void;
/*@internal*/ setBlocking?(): void;
base64decode?(input: string): string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we also add an optional sys member for base64encode?

}

export interface FileWatcher {
Expand Down Expand Up @@ -528,6 +529,8 @@ namespace ts {
_crypto = undefined;
}

const Buffer: typeof global.Buffer = require("buffer").Buffer;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Despite the fact that we ephemerally get the node.d.ts types due to the fact we're using them along with ts-node for our gulpfile, we don't depend on actual node types anywhere else in compiler. Yes, this makes this less type-safe but I don't necessary want to take the dependency for just one declaration in the entire project.


const nodeVersion = getNodeMajorVersion();
const isNode4OrLater = nodeVersion >= 4;

Expand Down Expand Up @@ -620,6 +623,9 @@ namespace ts {
if (process.stdout && process.stdout._handle && process.stdout._handle.setBlocking) {
process.stdout._handle.setBlocking(true);
}
},
base64decode: input => {
return Buffer.from(input, "base64").toString("utf8");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Buffer.from was added in NodeJS v5.10.0. We should feature test for Buffer.from and either don't add base64decode if it isn't present, or fall back to the deprecated new Buffer(string, encoding) form that was used prior to NodeJS v6.0.0.

}
};
return nodeSystem;
Expand Down
36 changes: 36 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3463,6 +3463,42 @@ namespace ts {
return result;
}

export function base64decode(host: { base64decode?(input: string): string }, input: string): string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're inconsistent with naming between base64decode and convertToBase64 above it. Please choose a more consistent name.

if (host.base64decode) {
return host.base64decode(input);
}
let result = "";
const length = input.length;
let i = 0;
while (i < length) {
// Stop decoding once padding characters are present
if (input.charCodeAt(i) === base64Digits.charCodeAt(64)) {
break;
}
// convert 4 input digits into three characters, ignoring padding characters at the end
const ch1 = base64Digits.indexOf(input[i]);
const ch2 = base64Digits.indexOf(input[i + 1]);
const ch3 = base64Digits.indexOf(input[i + 2]);
const ch4 = base64Digits.indexOf(input[i + 3]);

const code1 = (ch1 & 0B00111111 << 2) | (ch2 & 0B00000011);
const code2 = (ch2 & 0B00001111 << 4) | (ch3 & 0B00001111);
const code3 = (ch3 & 0B00000011 << 6) | (ch4 & 0B00111111);

if (code2 === 0 && ch3 !== 0) { // code2 decoded to zero, but ch3 was padding - elide code2 and code3
result += String.fromCharCode(code1);
}
else if (code3 === 0 && ch4 !== 0) { // code3 decoded to zero, but ch4 was padding, elide code3
result += `${String.fromCharCode(code1)}${String.fromCharCode(code2)}`;
}
else {
result += `${String.fromCharCode(code1)}${String.fromCharCode(code2)}${String.fromCharCode(code3)}`;
}
i += 4;
}
return result;
}

const carriageReturnLineFeed = "\r\n";
const lineFeed = "\n";
export function getNewLineCharacter(options: CompilerOptions | PrinterOptions, getNewLine?: () => string): string {
Expand Down
61 changes: 49 additions & 12 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1573,22 +1573,29 @@ namespace ts {
return checker.getSymbolAtLocation(node);
}

function getSourceMapper(fileName: string, file: { sourceMapper?: sourcemaps.SourceMapper }) {
if (!host.readFile || !host.fileExists) {
return file.sourceMapper = sourcemaps.identitySourceMapper;
}
if (file.sourceMapper) {
return file.sourceMapper;

const sourceMapCommentRE = /^\/\/[@#] sourceMappingURL=(.+)$/gm;
const dataURLRE = /^data:/;
const base64URLRE = /^data:application\/json;charset=utf-8;base64,(.+)$/;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"URLRE" is a lot of sequential uppercase letters. I'd prefer a name like base64UrlRegExp.

function scanForSourcemapURL(fileName: string) {
const mappedFile = sourcemappedFileCache.get(toPath(fileName, currentDirectory, getCanonicalFileName));
if (!mappedFile) {
return;
}
// TODO (weswigham): Read sourcemappingurl from last line of .d.ts if present
const mapFileName = fileName + ".map";
if (!host.fileExists(mapFileName)) {
return file.sourceMapper = sourcemaps.identitySourceMapper;
const starts = getLineStarts(mappedFile);
for (let index = starts.length - 1; index--; index >= 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have your condition and incrementer swapped.

sourceMapCommentRE.lastIndex = starts[index];
const comment = sourceMapCommentRE.exec(mappedFile.text);
if (comment) {
return comment[1];
}
}
const doc = host.readFile(mapFileName);
}

function convertDocumentToSourceMapper(file: { sourceMapper?: sourcemaps.SourceMapper }, contents: string, mapFileName: string) {
let maps: sourcemaps.SourceMapData;
try {
maps = JSON.parse(doc);
maps = JSON.parse(contents);
}
catch {
// swallow error
Expand All @@ -1605,6 +1612,36 @@ namespace ts {
}, mapFileName, maps, program, sourcemappedFileCache);
}

function getSourceMapper(fileName: string, file: { sourceMapper?: sourcemaps.SourceMapper }) {
if (!host.readFile || !host.fileExists) {
return file.sourceMapper = sourcemaps.identitySourceMapper;
}
if (file.sourceMapper) {
return file.sourceMapper;
}
let mapFileName = scanForSourcemapURL(fileName);
if (mapFileName && dataURLRE.exec(mapFileName)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dataURLRE seems unnecessary. The same thing could be accomplished with a single RegExp.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm doing this to avoid bothering to look for a file at a data: URL we couldn't understand. It's likely the rest of the path machinery may handle arbitrary data URLs OK-ish... but do we want it to?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant was that your regexp could be written like this /^data:(?:application\/json;charset=utf-8;base64,(.+)$)?/. Then you can exec the regexp once. If the result is non-null, its at least a data URL, but if it has a matches[1] then it's a valid base64 data URL.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it might be better to be more forgiving in the regexp with respect to case sensitivity to the value of charset, since UTF-8 is just as acceptable as utf-8 (and is entirely optional). I'd recommend something only slightly more lenient like this:

/^data:(?:application\/json(?:;charset=[uU][tT][fF]-8)?;base64,(.+)$)?/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if we wanted to be picky we could restrict the capturing group to ([A-Za-z0-9+\/=]+) (rather than (.+)).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we care if a different charset is supplied (like "US-ASCII" or "UTF-16"), or do we want to only allow UTF8?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want to be in the game of supporting multiple encodings, and our builtin impl (ignoring what the platform can provide) only handles utf8.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, though I do believe leveraging a single RegExp and possibly restricting the allowed values for the capturing group would be worthwhile.

const b64EncodedMatch = base64URLRE.exec(mapFileName);
if (b64EncodedMatch) {
const base64Object = b64EncodedMatch[1];
return convertDocumentToSourceMapper(file, base64decode(sys, base64Object), fileName);
}
mapFileName = undefined;
}
const possibleMapLocations: string[] = [];
if (mapFileName) {
possibleMapLocations.push(mapFileName);
}
possibleMapLocations.push(fileName + ".map");
for (const location of possibleMapLocations) {
const mapPath = toPath(location, getDirectoryPath(fileName), getCanonicalFileName);
if (host.fileExists(mapPath)) {
return convertDocumentToSourceMapper(file, host.readFile(mapPath), mapPath);
}
}
return file.sourceMapper = sourcemaps.identitySourceMapper;
}

function makeGetTargetOfMappedPosition<TIn>(
extract: (original: TIn) => sourcemaps.SourceMappableLocation,
create: (result: sourcemaps.SourceMappableLocation, original: TIn) => TIn
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 @@ -2893,6 +2893,7 @@ declare namespace ts {
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
clearTimeout?(timeoutId: any): void;
clearScreen?(): void;
base64decode?(input: string): string;
}
interface FileWatcher {
close(): void;
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 @@ -2893,6 +2893,7 @@ declare namespace ts {
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
clearTimeout?(timeoutId: any): void;
clearScreen?(): void;
base64decode?(input: string): string;
}
interface FileWatcher {
close(): void;
Expand Down