-
Notifications
You must be signed in to change notification settings - Fork 109
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
Support preexisisting inline source maps. #364
Conversation
src/tsickle_compiler_host.ts
Outdated
|
||
return this.getCanonicalFileName(path.resolve(fileDir, sourceFileName)); | ||
sourceFile.text = sm.removeInlineSourceMap(sourceFile.text); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not generally safe to mutate the TypeScript AST. We've run into very hard to track bugs because of it before. Can you extract the source map on load?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved into the initial load.
src/tsickle_compiler_host.ts
Outdated
@@ -125,6 +126,7 @@ export class TsickleCompilerHost implements ts.CompilerHost { | |||
} | |||
|
|||
const sourceFile = this.runConfiguration.oldProgram.getSourceFile(fileName); | |||
this.stripAndStoreExistingSourceMap(sourceFile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this need to happen on every getSourceFile? Shouldn't we only do this when loading the file from disk the very first time?
Also: how often does getSourceFile
get called? We shove megabytes through this, running a regexp over and over again on all inputs could get quite costly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved into the first load.
return getInlineSourceMapCount(source) > 0; | ||
} | ||
|
||
export function getInlineSourceMapCount(source: string): number { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is only used in tests, is it really important to expose this and test it separately?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't part of the public api and I'd like to keep all the source map functions together.
src/source_map_utils.ts
Outdated
} | ||
|
||
export function extractInlineSourceMap(source: string): string { | ||
const result = INLINE_SOURCE_MAP_REGEX.exec(source)!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we've hard issues before where source code would contain:
// #sourceMapURL...
more().code();
// #sourceMap...
I.e., you should take the last source map, not the first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be missing something, but this still returns the first matching //# sourceMap
, and the clobbering code below still replaces the first one, where it should be the last one, doesn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You were right! I was reusing the same regex object, which moves the pointer in the string forward each time, rendering my test that this was happening invalid.
src/source_map_utils.ts
Outdated
const match = source.match(INLINE_SOURCE_MAP_REGEX); | ||
if (match) { | ||
// The INLINE_SOURCE_MAP_REGEX has a capture group, but we only want to count real matches | ||
return match.length / 2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure this really counts the matches?
> m = 'a a a a a a a'.match(/(a)/m).length
2
Maybe you're missing a g
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was missing a g
, thanks!
test/e2e_source_map_test.ts
Outdated
}`); | ||
|
||
const closurizeSources = new Map<string, string>(); | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drop the extra block?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/source_map_utils.ts
Outdated
`//# sourceMappingURL=data:application/json;base64,${encodedSourceMap}`); | ||
} else { | ||
return `${source} | ||
//# sourceMappingURL=data:application/json;base64,${encodedSourceMap}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would be clearer to read if you just used \n
in the string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is better, yes.
e1db360
to
ea90d59
Compare
src/tsickle_compiler_host.ts
Outdated
import * as ts from 'typescript'; | ||
|
||
import {convertDecorators} from './decorator-annotator'; | ||
import {processES5} from './es5processor'; | ||
import {ModulesManifest} from './modules_manifest'; | ||
import * as sm from './source_map_utils'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Go rule around short names is that the more local and contextual the name (e.g. the iterator for a "for" loop), the more likely you are to know the name in context (e.g. "i"). For a global name like this that appears across the file, a longer name like sourceMapUtils
is more appropriate.
(The JS/TS style rule is to just always use long names.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough, but why is typescript imported as ts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hah, that's a good point! I blame @mprobst
src/tsickle_compiler_host.ts
Outdated
@@ -96,13 +96,16 @@ export class TsickleCompilerHost implements ts.CompilerHost { | |||
/** externs.js files produced by tsickle, if any. */ | |||
public externs: {[fileName: string]: string} = {}; | |||
|
|||
private sourceFileToPreexistingSourceMapMap = new Map<ts.SourceFile, SourceMapGenerator>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can drop one "map" suffix here, because it's implied in the type. (E.g. the other maps below don't have Map at the end.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/source_map_utils_test.ts
Outdated
|
||
import {expect} from 'chai'; | ||
|
||
import * as sm from '../src/source_map_utils'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment here about module name, but the short name is okay-er here because it's a short file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/source_map_utils.ts
Outdated
|
||
export function sourceMapTextToGenerator(sourceMapText: string): SourceMapGenerator { | ||
const sourceMapJson: any = sourceMapText; | ||
return SourceMapGenerator.fromSourceMap(this.sourceMapTextToConsumer(sourceMapJson)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the idea behind this function and the one above it? They seem to just wrap another function?
I think the use of this
here accidentally compiles but it shouldn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They wrap another function (and do the cast to any). When they were inlined, it was getting hard to read. And the this
is intentional - we're calling another function on the same object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which object? I think this function is a top-level function, not a method (?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, my bad. I thought this was still where I had originally written it, despite you calling attention to it. Fixed.
src/source_map_utils.ts
Outdated
|
||
export function getInlineSourceMapCount(source: string): number { | ||
const match = source.match(INLINE_SOURCE_MAP_REGEX); | ||
if (match) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think it'd be more idiomatic to write return match ? match.length : 0;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/source_map_utils.ts
Outdated
return source; | ||
} | ||
|
||
export function setInlineSourceMap(source: string, sourceMap: string): string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add some docs and mention that this clobbers pre-existing maps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/source_map_utils.ts
Outdated
} | ||
|
||
export function extractInlineSourceMap(source: string): string { | ||
const result = INLINE_SOURCE_MAP_REGEX.exec(source)!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be missing something, but this still returns the first matching //# sourceMap
, and the clobbering code below still replaces the first one, where it should be the last one, doesn't it?
I'd claim that "ts" is an obvious abbreviation in this context, whereas
"sm" is not?
|
ea90d59
to
08d31e3
Compare
src/source_map_utils.ts
Outdated
@@ -0,0 +1,77 @@ | |||
import {SourceMapConsumer, SourceMapGenerator} from 'source-map'; | |||
|
|||
function getInlineSourceMapRegex(): RegExp { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
document why this is not using a constant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/source_map_utils.ts
Outdated
const inlineSourceMapRegex = getInlineSourceMapRegex(); | ||
let previousResult: RegExpExecArray|null = null; | ||
let result: RegExpExecArray|null = null; | ||
do { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment why this is looping
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/source_map_utils.ts
Outdated
} | ||
|
||
export function removeInlineSourceMap(source: string): string { | ||
if (containsInlineSourceMap(source)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you double check if you need the if() here? and below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not needed here, needed below.
If tsickle is passed sources that include inline source maps (eg ts 'sources' produced by ngc), compose those source maps with the source maps produced by running tsickle.
08d31e3
to
c5460b2
Compare
If tsickle is passed sources that include inline source maps (eg ts 'sources' produced by ngc), compose those source maps with the source maps produced by running tsickle.
If tsickle is passed sources that include inline source maps (eg ts 'sources' produced by ngc), compose those source maps with the source maps produced by running tsickle.