-
Notifications
You must be signed in to change notification settings - Fork 45
Add TypeScript support #83
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
Merged
Merged
Changes from 6 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
60fae7e
Add TypeScript support
81ae07d
block files test
91e0287
type-aware rules test
fead372
less bloated expected.json
d3cf462
DEM UNDERSCOREZZ
dd6220a
fix a bunch of indentation
Conduitry e4f9a85
more snake case
Conduitry a2ff579
simplify code
996824d
variable name, code style
d5b8020
line match bugfix
b3b1d61
adjust readme
Conduitry d86cd24
bundle sourcemap-codec
Conduitry f285706
tweak/tidy
Conduitry File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
# 3.1.0 (Unreleased) | ||
|
||
- Add TypeScript support | ||
|
||
# 3.0.0 | ||
|
||
- Breaking change: Node 10+ is now required | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
import { decode } from 'sourcemap-codec'; | ||
|
||
class GeneratedFragmentMapper { | ||
constructor( | ||
generated_code, | ||
tag_info, | ||
) { | ||
this.generated_code = generated_code; | ||
this.tag_info = tag_info; | ||
} | ||
|
||
get_position_relative_to_fragment(positionRelativeToFile) { | ||
const fragment_offset = this.offset_in_fragment(offset_at(positionRelativeToFile, this.generated_code)); | ||
return position_at(fragment_offset, this.tag_info.generated_content); | ||
} | ||
|
||
offset_in_fragment(offset) { | ||
return offset - this.tag_info.generated_start | ||
} | ||
} | ||
|
||
class OriginalFragmentMapper { | ||
constructor( | ||
original_code, | ||
tag_info, | ||
) { | ||
this.original_code = original_code; | ||
this.tag_info = tag_info; | ||
} | ||
|
||
get_position_relative_to_file(positionRelativeToFragment) { | ||
const parent_offset = this.offset_in_parent(offset_at(positionRelativeToFragment, this.tag_info.original_content)); | ||
return position_at(parent_offset, this.original_code); | ||
} | ||
|
||
offset_in_parent(offset) { | ||
return this.tag_info.original_start + offset; | ||
} | ||
} | ||
|
||
class SourceMapper { | ||
constructor(raw_source_map) { | ||
this.raw_source_map = raw_source_map; | ||
} | ||
|
||
getOriginalPosition(generated_position) { | ||
if (generated_position.line < 0) { | ||
return { line: -1, column: -1 }; | ||
} | ||
|
||
// Lazy-load | ||
if (!this.decoded) { | ||
this.decoded = decode(JSON.parse(this.raw_source_map).mappings); | ||
} | ||
|
||
let line = generated_position.line; | ||
let column = generated_position.column; | ||
|
||
let line_match = this.decoded[generated_position.line]; | ||
while (line >= 0 && (!line_match || !line_match.length)) { | ||
line -= 1; | ||
line_match = this.decoded[generated_position]; | ||
if (line_match && line_match.length) { | ||
return { | ||
line: line_match[line_match.length - 1][2], | ||
column: line_match[line_match.length - 1][3] | ||
}; | ||
} | ||
} | ||
|
||
if (line < 0) { | ||
return { line: -1, column: -1 }; | ||
} | ||
|
||
const column_match = line_match.find((col, idx) => | ||
idx + 1 === line_match.length || | ||
(col[0] <= column && line_match[idx + 1][0] > column) | ||
); | ||
|
||
return { | ||
line: column_match[2], | ||
column: column_match[3], | ||
}; | ||
} | ||
} | ||
|
||
export class DocumentMapper { | ||
constructor(original_code, generated_code, diffs) { | ||
this.original_code = original_code; | ||
this.generated_code = generated_code; | ||
this.diffs = diffs; | ||
this.mappers = diffs.map(diff => { | ||
return { | ||
start: diff.generated_start, | ||
end: diff.generated_end, | ||
diff: diff.diff, | ||
generated_fragment_mapper: new GeneratedFragmentMapper(generated_code, diff), | ||
source_mapper: new SourceMapper(diff.map), | ||
original_fragment_mapper: new OriginalFragmentMapper(original_code, diff) | ||
} | ||
}); | ||
} | ||
|
||
get_original_position(generated_position) { | ||
generated_position = { line: generated_position.line - 1, column: generated_position.column }; | ||
const offset = offset_at(generated_position, this.generated_code); | ||
let original_offset = offset; | ||
for (const mapper of this.mappers) { | ||
if (offset >= mapper.start && offset <= mapper.end) { | ||
return this.map(mapper, generated_position); | ||
} | ||
if (offset > mapper.end) { | ||
original_offset -= mapper.diff; | ||
} | ||
} | ||
const original_position = position_at(original_offset, this.original_code); | ||
return this.to_ESLint_position(original_position); | ||
} | ||
|
||
map(mapper, generatedPosition) { | ||
// Map the position to be relative to the transpiled fragment | ||
const position_in_transpiled_fragment = mapper.generated_fragment_mapper.get_position_relative_to_fragment( | ||
generatedPosition | ||
); | ||
// Map the position, using the sourcemap, to the original position in the source fragment | ||
const position_in_original_fragment = mapper.source_mapper.getOriginalPosition( | ||
position_in_transpiled_fragment | ||
); | ||
// Map the position to be in the original fragment's parent | ||
const original_position = mapper.original_fragment_mapper.get_position_relative_to_file(position_in_original_fragment); | ||
return this.to_ESLint_position(original_position); | ||
} | ||
|
||
to_ESLint_position(position) { | ||
// ESLint line/column is 1-based | ||
return { line: position.line + 1, column: position.column + 1 }; | ||
} | ||
|
||
} | ||
|
||
/** | ||
* Get the offset of the line and character position | ||
* @param position Line and character position | ||
* @param text The text for which the offset should be retrived | ||
*/ | ||
function offset_at(position, text) { | ||
const line_offsets = get_line_offsets(text); | ||
|
||
if (position.line >= line_offsets.length) { | ||
return text.length; | ||
} else if (position.line < 0) { | ||
return 0; | ||
} | ||
|
||
const line_offset = line_offsets[position.line]; | ||
const next_line_offset = | ||
position.line + 1 < line_offsets.length ? line_offsets[position.line + 1] : text.length; | ||
|
||
return clamp(next_line_offset, line_offset, line_offset + position.column); | ||
} | ||
|
||
function position_at(offset, text) { | ||
offset = clamp(offset, 0, text.length); | ||
|
||
const line_offsets = get_line_offsets(text); | ||
let low = 0; | ||
let high = line_offsets.length; | ||
if (high === 0) { | ||
return { line: 0, column: offset }; | ||
} | ||
|
||
while (low < high) { | ||
const mid = Math.floor((low + high) / 2); | ||
if (line_offsets[mid] > offset) { | ||
high = mid; | ||
} else { | ||
low = mid + 1; | ||
} | ||
} | ||
|
||
// low is the least x for which the line offset is larger than the current offset | ||
// or array.length if no line offset is larger than the current offset | ||
const line = low - 1; | ||
return { line, column: offset - line_offsets[line] }; | ||
} | ||
|
||
function get_line_offsets(text) { | ||
const line_offsets = []; | ||
let is_line_start = true; | ||
|
||
for (let i = 0; i < text.length; i++) { | ||
if (is_line_start) { | ||
line_offsets.push(i); | ||
is_line_start = false; | ||
} | ||
const ch = text.charAt(i); | ||
is_line_start = ch === '\r' || ch === '\n'; | ||
if (ch === '\r' && i + 1 < text.length && text.charAt(i + 1) === '\n') { | ||
i++; | ||
} | ||
} | ||
|
||
if (is_line_start && text.length > 0) { | ||
line_offsets.push(text.length); | ||
} | ||
|
||
return line_offsets; | ||
} | ||
|
||
function clamp(num, min, max) { | ||
return Math.max(min, Math.min(max, num)); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.