Skip to content

Commit

Permalink
fix(common): Fix handling of input source maps (#6561)
Browse files Browse the repository at this point in the history
**Related issue:**

 - Closes #4578.
 - Closes #6244.
 - vercel/next.js#39878.

Co-authored-by: Justin Ridgewell <justin@ridgewell.name>
  • Loading branch information
kdy1 and jridgewell committed Dec 2, 2022
1 parent d00b291 commit 4af52c7
Show file tree
Hide file tree
Showing 13 changed files with 315 additions and 111 deletions.
26 changes: 9 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/swc/Cargo.toml
Expand Up @@ -98,6 +98,7 @@ swc_plugin_runner = { version = "0.78.1", path = "../swc_plugin_runner", optiona
swc_timer = { version = "0.17.17", path = "../swc_timer" }
swc_visit = { version = "0.5.2", path = "../swc_visit" }
tracing = "0.1.32"
url = "2.3.1"

[dependencies.napi-derive]
default-features = false
Expand Down
26 changes: 21 additions & 5 deletions crates/swc/src/lib.rs
Expand Up @@ -158,6 +158,7 @@ use swc_ecma_visit::{noop_visit_type, FoldWith, Visit, VisitMutWith, VisitWith};
pub use swc_error_reporters::handler::{try_with_handler, HandlerOpts};
pub use swc_node_comments::SwcComments;
use swc_timer::timer;
use url::Url;

pub use crate::builder::PassBuilder;
use crate::config::{
Expand Down Expand Up @@ -328,20 +329,35 @@ impl Compiler {
}
InputSourceMap::Str(ref s) => {
if s == "inline" {
const NEEDLE: &str = "sourceMappingURL=";
// Load inline source map by simple string
// operations
let s = "sourceMappingURL=data:application/json;base64,";
let idx = fm.src.rfind(s);
let idx = fm.src.rfind(NEEDLE);
let idx = match idx {
None => bail!(
"failed to parse inline source map: `sourceMappingURL` not found"
),
Some(v) => v,
};
let encoded = &fm.src[idx + s.len()..];
let data_url = fm.src[idx + NEEDLE.len()..].trim();
let url = Url::parse(data_url).with_context(|| {
format!("failed to parse inline source map url\n{}", data_url)
})?;

let res = base64::decode(encoded.as_bytes())
.context("failed to decode base64-encoded source map")?;
let idx = match url.path().find("base64,") {
Some(v) => v,
None => {
bail!("failed to parse inline source map: not base64: {:?}", url)
}
};

let content = url.path()[idx + "base64,".len()..].trim();

let res = base64::decode_config(
content.as_bytes(),
base64::Config::new(base64::CharacterSet::Standard, true),
)
.context("failed to decode base64-encoded source map")?;

Ok(Some(sourcemap::SourceMap::from_slice(&res).context(
"failed to read input source map from inlined base64 encoded string",
Expand Down
14 changes: 14 additions & 0 deletions crates/swc/tests/fixture/next-39878/input/.swcrc
@@ -0,0 +1,14 @@
{
"env": {
"targets": {
"chrome": "95"
}
},
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
}
},
"sourceMaps": true
}
63 changes: 63 additions & 0 deletions crates/swc/tests/fixture/next-39878/input/box-model.ts
@@ -0,0 +1,63 @@
import type { UIStore, UIThunkAction } from "ui/actions";
import { isInspectorSelected } from "ui/reducers/app";
import { AppStartListening } from "ui/setup/listenerMiddleware";
import { getBoundingRectAsync, getComputedStyleAsync } from "ui/suspense/styleCaches";

import { nodeSelected } from "../../markup/reducers/markup";
import { LAYOUT_NUMERIC_FIELDS, Layout, layoutUpdated } from "../reducers/box-model";

export function setupBoxModel(store: UIStore, startAppListening: AppStartListening) {
// Any time a new node is selected in the "Markup" panel,
// try to update the box model layout data
startAppListening({
actionCreator: nodeSelected,
effect: async (action, listenerApi) => {
const { extra, getState, dispatch } = listenerApi;
const { ThreadFront, protocolClient, replayClient } = extra;
const state = getState();
const { selectedNode, tree } = state.markup;

if (!isInspectorSelected(state) || !selectedNode || !ThreadFront.currentPause?.pauseId) {
return;
}

const nodeInfo = tree.entities[selectedNode];

if (!nodeInfo) {
return;
}

const [bounds, style] = await Promise.all([
getBoundingRectAsync(
protocolClient,
ThreadFront.sessionId!,
ThreadFront.currentPause.pauseId,
selectedNode
),
getComputedStyleAsync(
protocolClient,
ThreadFront.sessionId!,
ThreadFront.currentPause.pauseId,
selectedNode
),
]);

if (!bounds || !style) {
return;
}

const layout = {
width: parseFloat(bounds.width.toPrecision(6)),
height: parseFloat(bounds.height.toPrecision(6)),
autoMargins: {},
} as Layout;

for (const prop of LAYOUT_NUMERIC_FIELDS) {
layout[prop] = style.get(prop)!;
}

// Update the redux store with the latest layout properties and update the box model view.
dispatch(layoutUpdated(layout));
},
});
}
52 changes: 52 additions & 0 deletions crates/swc/tests/fixture/next-39878/output/box-model.map
@@ -0,0 +1,52 @@
{
"mappings": "AACA,SAASA,mBAAmB,QAAQ,kBAAkB;AAEtD,SAASC,oBAAoB,EAAEC,qBAAqB,QAAQ,0BAA0B;AAEtF,SAASC,YAAY,QAAQ,+BAA+B;AAC5D,SAASC,qBAAqB,EAAUC,aAAa,QAAQ,wBAAwB;AAErF,OAAO,SAASC,cAAcC,KAAc,EAAEC,iBAAoC,EAAE;IAClF,yDAAyD;IACzD,0CAA0C;IAC1CA,kBAAkB;QAChBC,eAAeN;QACfO,QAAQ,OAAOC,QAAQC,cAAgB;YACrC,MAAM,EAAEC,MAAK,EAAEC,SAAQ,EAAEC,SAAQ,EAAE,GAAGH;YACtC,MAAM,EAAEI,YAAW,EAAEC,eAAc,EAAEC,aAAY,EAAE,GAAGL;YACtD,MAAMM,QAAQL;YACd,MAAM,EAAEM,aAAY,EAAEC,KAAI,EAAE,GAAGF,MAAMG,MAAM;YAE3C,IAAI,CAACtB,oBAAoBmB,UAAU,CAACC,gBAAgB,CAACJ,YAAYO,YAAY,EAAEC,SAAS;gBACtF;YACF,CAAC;YAED,MAAMC,WAAWJ,KAAKK,QAAQ,CAACN,aAAa;YAE5C,IAAI,CAACK,UAAU;gBACb;YACF,CAAC;YAED,MAAM,CAACE,QAAQC,MAAM,GAAG,MAAMC,QAAQC,GAAG,CAAC;gBACxC7B,qBACEgB,gBACAD,YAAYe,SAAS,EACrBf,YAAYO,YAAY,CAACC,OAAO,EAChCJ;gBAEFlB,sBACEe,gBACAD,YAAYe,SAAS,EACrBf,YAAYO,YAAY,CAACC,OAAO,EAChCJ;aAEH;YAED,IAAI,CAACO,UAAU,CAACC,OAAO;gBACrB;YACF,CAAC;YAED,MAAMI,SAAS;gBACbC,OAAOC,WAAWP,OAAOM,KAAK,CAACE,WAAW,CAAC;gBAC3CC,QAAQF,WAAWP,OAAOS,MAAM,CAACD,WAAW,CAAC;gBAC7CE,aAAa,CAAC;YAChB;YAEA,KAAK,MAAMC,QAAQlC,sBAAuB;gBACxC4B,MAAM,CAACM,KAAK,GAAGV,MAAMW,GAAG,CAACD;YAC3B;YAEA,0FAA0F;YAC1FvB,SAASV,cAAc2B;QACzB;IACF;AACF,CAAC",
"names": [
"isInspectorSelected",
"getBoundingRectAsync",
"getComputedStyleAsync",
"nodeSelected",
"LAYOUT_NUMERIC_FIELDS",
"layoutUpdated",
"setupBoxModel",
"store",
"startAppListening",
"actionCreator",
"effect",
"action",
"listenerApi",
"extra",
"getState",
"dispatch",
"ThreadFront",
"protocolClient",
"replayClient",
"state",
"selectedNode",
"tree",
"markup",
"currentPause",
"pauseId",
"nodeInfo",
"entities",
"bounds",
"style",
"Promise",
"all",
"sessionId",
"layout",
"width",
"parseFloat",
"toPrecision",
"height",
"autoMargins",
"prop",
"get"
],
"sources": [
"../../input/box-model.ts"
],
"sourcesContent": [
"import type { UIStore, UIThunkAction } from \"ui/actions\";\nimport { isInspectorSelected } from \"ui/reducers/app\";\nimport { AppStartListening } from \"ui/setup/listenerMiddleware\";\nimport { getBoundingRectAsync, getComputedStyleAsync } from \"ui/suspense/styleCaches\";\n\nimport { nodeSelected } from \"../../markup/reducers/markup\";\nimport { LAYOUT_NUMERIC_FIELDS, Layout, layoutUpdated } from \"../reducers/box-model\";\n\nexport function setupBoxModel(store: UIStore, startAppListening: AppStartListening) {\n // Any time a new node is selected in the \"Markup\" panel,\n // try to update the box model layout data\n startAppListening({\n actionCreator: nodeSelected,\n effect: async (action, listenerApi) => {\n const { extra, getState, dispatch } = listenerApi;\n const { ThreadFront, protocolClient, replayClient } = extra;\n const state = getState();\n const { selectedNode, tree } = state.markup;\n\n if (!isInspectorSelected(state) || !selectedNode || !ThreadFront.currentPause?.pauseId) {\n return;\n }\n\n const nodeInfo = tree.entities[selectedNode];\n\n if (!nodeInfo) {\n return;\n }\n\n const [bounds, style] = await Promise.all([\n getBoundingRectAsync(\n protocolClient,\n ThreadFront.sessionId!,\n ThreadFront.currentPause.pauseId,\n selectedNode\n ),\n getComputedStyleAsync(\n protocolClient,\n ThreadFront.sessionId!,\n ThreadFront.currentPause.pauseId,\n selectedNode\n ),\n ]);\n\n if (!bounds || !style) {\n return;\n }\n\n const layout = {\n width: parseFloat(bounds.width.toPrecision(6)),\n height: parseFloat(bounds.height.toPrecision(6)),\n autoMargins: {},\n } as Layout;\n\n for (const prop of LAYOUT_NUMERIC_FIELDS) {\n layout[prop] = style.get(prop)!;\n }\n\n // Update the redux store with the latest layout properties and update the box model view.\n dispatch(layoutUpdated(layout));\n },\n });\n}\n"
],
"version": 3
}
41 changes: 41 additions & 0 deletions crates/swc/tests/fixture/next-39878/output/box-model.ts
@@ -0,0 +1,41 @@
import { isInspectorSelected } from "ui/reducers/app";
import { getBoundingRectAsync, getComputedStyleAsync } from "ui/suspense/styleCaches";
import { nodeSelected } from "../../markup/reducers/markup";
import { LAYOUT_NUMERIC_FIELDS, layoutUpdated } from "../reducers/box-model";
export function setupBoxModel(store, startAppListening) {
// Any time a new node is selected in the "Markup" panel,
// try to update the box model layout data
startAppListening({
actionCreator: nodeSelected,
effect: async (action, listenerApi)=>{
const { extra , getState , dispatch } = listenerApi;
const { ThreadFront , protocolClient , replayClient } = extra;
const state = getState();
const { selectedNode , tree } = state.markup;
if (!isInspectorSelected(state) || !selectedNode || !ThreadFront.currentPause?.pauseId) {
return;
}
const nodeInfo = tree.entities[selectedNode];
if (!nodeInfo) {
return;
}
const [bounds, style] = await Promise.all([
getBoundingRectAsync(protocolClient, ThreadFront.sessionId, ThreadFront.currentPause.pauseId, selectedNode),
getComputedStyleAsync(protocolClient, ThreadFront.sessionId, ThreadFront.currentPause.pauseId, selectedNode)
]);
if (!bounds || !style) {
return;
}
const layout = {
width: parseFloat(bounds.width.toPrecision(6)),
height: parseFloat(bounds.height.toPrecision(6)),
autoMargins: {}
};
for (const prop of LAYOUT_NUMERIC_FIELDS){
layout[prop] = style.get(prop);
}
// Update the redux store with the latest layout properties and update the box model view.
dispatch(layoutUpdated(layout));
}
});
}

1 comment on commit 4af52c7

@github-actions
Copy link

Choose a reason for hiding this comment

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

Benchmark

Benchmark suite Current: 4af52c7 Previous: cdf0d8a Ratio
es/full/bugs-1 336470 ns/iter (± 26017) 347600 ns/iter (± 21107) 0.97
es/full/minify/libraries/antd 1977533977 ns/iter (± 22733268) 2036833240 ns/iter (± 65299279) 0.97
es/full/minify/libraries/d3 440415931 ns/iter (± 18969435) 449921640 ns/iter (± 15545822) 0.98
es/full/minify/libraries/echarts 1727554504 ns/iter (± 44917915) 1747869817 ns/iter (± 284198529) 0.99
es/full/minify/libraries/jquery 111171313 ns/iter (± 2980819) 114051503 ns/iter (± 2348462) 0.97
es/full/minify/libraries/lodash 140959178 ns/iter (± 2959741) 131359407 ns/iter (± 5068345) 1.07
es/full/minify/libraries/moment 70889602 ns/iter (± 1540179) 66839836 ns/iter (± 2236533) 1.06
es/full/minify/libraries/react 22869963 ns/iter (± 546241) 22065996 ns/iter (± 609807) 1.04
es/full/minify/libraries/terser 336309217 ns/iter (± 21053548) 342266503 ns/iter (± 15979735) 0.98
es/full/minify/libraries/three 616913334 ns/iter (± 8726645) 625347510 ns/iter (± 13892819) 0.99
es/full/minify/libraries/typescript 3668020802 ns/iter (± 29183046) 3659254464 ns/iter (± 77550761) 1.00
es/full/minify/libraries/victory 903282720 ns/iter (± 21075307) 909940842 ns/iter (± 27053033) 0.99
es/full/minify/libraries/vue 168089590 ns/iter (± 8536610) 172808513 ns/iter (± 13676562) 0.97
es/full/codegen/es3 33481 ns/iter (± 579) 34457 ns/iter (± 2655) 0.97
es/full/codegen/es5 33410 ns/iter (± 526) 34829 ns/iter (± 1764) 0.96
es/full/codegen/es2015 33423 ns/iter (± 463) 34404 ns/iter (± 1268) 0.97
es/full/codegen/es2016 33390 ns/iter (± 977) 35214 ns/iter (± 927) 0.95
es/full/codegen/es2017 32839 ns/iter (± 1105) 34752 ns/iter (± 713) 0.94
es/full/codegen/es2018 33307 ns/iter (± 1154) 34071 ns/iter (± 950) 0.98
es/full/codegen/es2019 33400 ns/iter (± 511) 34559 ns/iter (± 1412) 0.97
es/full/codegen/es2020 33420 ns/iter (± 342) 34200 ns/iter (± 1638) 0.98
es/full/all/es3 188046506 ns/iter (± 8881474) 206436493 ns/iter (± 28503818) 0.91
es/full/all/es5 179533046 ns/iter (± 8181022) 209892739 ns/iter (± 18569666) 0.86
es/full/all/es2015 144321531 ns/iter (± 8234440) 152457874 ns/iter (± 17148838) 0.95
es/full/all/es2016 143821601 ns/iter (± 20042934) 155336471 ns/iter (± 23154947) 0.93
es/full/all/es2017 141150194 ns/iter (± 4526075) 156618860 ns/iter (± 16557551) 0.90
es/full/all/es2018 139321767 ns/iter (± 9122253) 148978570 ns/iter (± 14036603) 0.94
es/full/all/es2019 150396187 ns/iter (± 10414466) 156612369 ns/iter (± 17188799) 0.96
es/full/all/es2020 145464943 ns/iter (± 13307571) 150456177 ns/iter (± 14219881) 0.97
es/full/parser 708816 ns/iter (± 41381) 741042 ns/iter (± 38352) 0.96
es/full/base/fixer 25966 ns/iter (± 935) 26725 ns/iter (± 1304) 0.97
es/full/base/resolver_and_hygiene 91685 ns/iter (± 3209) 93473 ns/iter (± 5180) 0.98
serialization of ast node 209 ns/iter (± 5) 211 ns/iter (± 8) 0.99
serialization of serde 218 ns/iter (± 10) 222 ns/iter (± 5) 0.98

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.