From 4ee031aa6231e721c6893d1685a7fff09872af95 Mon Sep 17 00:00:00 2001 From: Rob Palmer Date: Tue, 14 Jan 2025 13:17:58 +0000 Subject: [PATCH] Range Mappings: Allow multi-line ranges This update changes the semantics of the Range Mapping such that it applies to character sequences that include newlines. The encoding format/syntax is unchanged. This is a purely semantic change. It is motivated by cases where most or all of a file contents could be expressed via a single range mapping, e.g. to concisely produce an identity range mapping. Source: https://issues.chromium.org/issues/364917746 --- proposals/range-mappings.md | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/proposals/range-mappings.md b/proposals/range-mappings.md index 663ec50..649cb37 100644 --- a/proposals/range-mappings.md +++ b/proposals/range-mappings.md @@ -15,9 +15,9 @@ Tobias Koppers ## Motiviation Currently mappings map locations in the source code to other locations in the source code. -This works well when trying to access a location that is defined in the SourceMap, but one looses percision when accessing locations that are not directly defined in the SourceMap. +This works well when trying to access a location that is defined in the SourceMap, but one loses precision when accessing locations that are not directly defined in the SourceMap. In these cases tools usually fallback to next lower column that is actually mapped in the SourceMap. -So we are either loosing information or we need many mappings in the SourceMap to cover all possible locations. +So we are either losing information or we need many mappings in the SourceMap to cover all possible locations. These information problem is especially problematic when applying a SourceMap to another SourceMap. Here we can only use locations that are specified in both SourceMaps. We have to be lucky that locations match up. @@ -26,7 +26,7 @@ Here we can only use locations that are specified in both SourceMaps. We have to As an example let's look at a build process when a TypeScript file is converted to JavaScript first and that is minified afterwards. -The TypeScript to JavaScript transformation is mostly keeping code identical, but removing type annotations. +A simplistic TypeScript to JavaScript transformation such as SWC's [`strip_types`](https://play.swc.rs/?version=1.10.7&code=H4sIAAAAAAAAA0WMQQqDMBBF93OKv6wgPYBpu5HewAvEQTA0Tcpkggvx7k2E4OrD%2B4%2FH3qaE0epjemEn4Jdn7xjb6tJnkTQg5O%2B8iLkutc4PmAwVxDEklcwa5cYxB21%2B37TurAJagvdWxROnba6r6gXXqfSgg0hXiRveIqXemT8eTB9GqwAAAA%3D%3D&config=H4sIAAAAAAAAA1VPOw7DIAzdOQXy3KFi6NA79BCIOhERAYQdqSjK3QsJpM1mv4%2Ff8yqkhIkMPOVaxrJEnQjTuReEsmf9KQhwjkgm2chw6yxTpQbtCHdoOxhgnUbk6kJSd6WaA1wIhN3RsNl6O%2BT%2FTBPmmJDoKqxS7UeH10TRUmEO72Un2y%2B179HgAT9RDzsPg6VXd3JaUGxfBMLf3xcBAAA%3D&strip-types=) keeps the runtime code identical, whilst removing type annotations. Theoretically only a few SourceMap mappings are needs, as most code stays identical. Minifying is a bigger transformation of the code, which one it's own would result in a lot of SourceMap mappings to be generated. @@ -40,7 +40,7 @@ The TypeScript SourceMap would behave identical to a SourceMap mapping every sin ## Proposal Add a boolean flag for each mapping to convert it into a "range mapping". -For a range mapping, tools should assume that every char that follows the mapping (until the next mapping), is mapped to the specified original location plus the offset in the generated code. +For a range mapping, tools should assume that every char (including newlines) that follows the mapping (until the next mapping), is mapped to the specified original location plus the offset in the generated code. This means all chars in the generated code that is covered by the range mapping, are mapped char by char to the same range in the original code. (Usually this only makes sense when generated and original are identical for that range) @@ -49,14 +49,16 @@ This means all chars in the generated code that is covered by the range mapping, Generated Code: ``` js -console.log("hello world"); +console.log( +"hello world"); ``` Original Code: ``` js - // Copyright 2023 - console.log("hello world"); +// Copyright 2023 + console.log( +"hello world"); ``` With a normal mapping: @@ -64,12 +66,13 @@ With a normal mapping: ``` Source Map: Generate Line 1 Column 0 -> Original Line 2 Column 2 +Generate Line 2 Column 0 -> Original Line 3 Column 0 ``` ``` js -console.log("hello world"); -^ ^ ^ -| | + maps to Original Line 2 Column 2 +console.log(\n"hello world"); +^ ^ ^ +| | + maps to Original Line 3 Column 0 | + maps to Original Line 2 Column 2 + maps to Original Line 2 Column 2 ``` @@ -82,9 +85,9 @@ Generate Line 1 Column 0 -> Original Line 2 Column 2 (range mapping) ``` ``` js -console.log("hello world"); -^ ^ ^ -| | + maps to Original Line 2 Column 14 +console.log(\n"hello world"); +^ ^ ^ +| | + maps to Original Line 3 Column 0 | + maps to Original Line 2 Column 10 + maps to Original Line 2 Column 2 ``` @@ -92,7 +95,7 @@ console.log("hello world"); ### Encoding To avoid a breaking change to the `mappings` field, a new field named `rangeMappings` is added. -It contains encoded data per line in the generated code. +It contains encoded data per-line in the generated code. Each line is separated by `;`. The data contains a bit per mapping in that line. When the bit is set, the mapping is a range mapping, otherwise it is a normal mapping. @@ -111,5 +114,5 @@ Line 1: 0b000000 0b000000 0b000001 => the 13th mapping is a range mapping Line 3: 0b100000 => the 6th mapping is a range mapping ``` -> Note: The per line encoding is chosen to make it easier to generate SourceMap line by line. +> Note: The per-line encoding is chosen to make it easier to generate SourceMap line by line. > It also looks similar to the `mappings` field, so should allow good compression.