monocart-coverage-reports 2.10.3 → 2.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,6 +45,7 @@
45
45
  - [VSCode](#vscode)
46
46
  - [Jest](#jest)
47
47
  - [Vitest](#vitest)
48
+ - [Node Test Runner](#node-test-runner)
48
49
  - [Puppeteer](#puppeteer)
49
50
  - [Cypress](#cypress)
50
51
  - [WebdriverIO](#webdriverio)
@@ -294,6 +295,7 @@ const [jsCoverage, cssCoverage] = await Promise.all([
294
295
  const coverageData = [... jsCoverage, ... cssCoverage];
295
296
 
296
297
  ```
298
+ Collect coverage with `@playwright/test` [`Automatic fixtures`](https://playwright.dev/docs/test-fixtures#automatic-fixtures), see example: [fixtures.ts](https://github.com/cenfun/playwright-coverage/blob/main/fixtures.ts)
297
299
  For more examples, see [./test/test-v8.js](./test/test-v8.js), and [anonymous](./test/test-anonymous.js), [css](./test/test-css.js)
298
300
 
299
301
 
@@ -987,6 +989,9 @@ c8 --experimental-monocart --reporter=v8 --reporter=console-details node foo.js
987
989
  - [vitest-monocart-coverage](https://github.com/cenfun/vitest-monocart-coverage) - Vitest custom provider module for coverage reports
988
990
  - [merge-code-coverage-vitest](https://github.com/cenfun/merge-code-coverage-vitest) - Example for merging code coverage (Vitest unit + Playwright e2e sharding)
989
991
 
992
+ ### [Node Test Runner](https://nodejs.org/docs/latest/api/test.html)
993
+ - [node-monocart-coverage](https://github.com/cenfun/node-monocart-coverage) - Custom reporter for Node test runner for coverage
994
+
990
995
  ### [Puppeteer](https://github.com/puppeteer/puppeteer/)
991
996
  - [jest-puppeteer-coverage](https://github.com/cenfun/jest-puppeteer-coverage) - Example for Jest puppeteer coverage
992
997
  - [maplibre-gl-js](https://github.com/maplibre/maplibre-gl-js) - Example for Jest (unit) + Puppeteer (e2e) + Codecov
package/README.zh-Hans.md CHANGED
@@ -45,6 +45,7 @@
45
45
  - [VSCode](#vscode)
46
46
  - [Jest](#jest)
47
47
  - [Vitest](#vitest)
48
+ - [Node Test Runner](#node-test-runner)
48
49
  - [Puppeteer](#puppeteer)
49
50
  - [Cypress](#cypress)
50
51
  - [WebdriverIO](#webdriverio)
@@ -117,10 +118,11 @@ mcr node my-app.js -r v8,console-details
117
118
  ![](./assets/v8.gif)
118
119
 
119
120
  - `v8-json`
120
- - 保存 `CoverageResults` 到一个json文件
121
121
  - 保存 `CoverageResults` 到一个json文件 (默认是 [`coverage-report.json`](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/coverage-report.json))
122
122
  - 用于VSCode扩展来显示原生V8代码覆盖率: [Monocart Coverage for VSCode](https://github.com/cenfun/monocart-coverage-vscode)
123
123
 
124
+ ![](./assets/mcv.gif)
125
+
124
126
  > 内置Istanbul报告 (V8和Istanbul格式数据都支持):
125
127
 
126
128
  - `clover`
@@ -295,6 +297,7 @@ const [jsCoverage, cssCoverage] = await Promise.all([
295
297
  const coverageData = [... jsCoverage, ... cssCoverage];
296
298
 
297
299
  ```
300
+ 使用 `@playwright/test` 的 [`Automatic fixtures`](https://playwright.dev/docs/test-fixtures#automatic-fixtures)收集覆盖率数据, 见例子: [fixtures.ts](https://github.com/cenfun/playwright-coverage/blob/main/fixtures.ts)
298
301
  参见例子 [./test/test-v8.js](./test/test-v8.js), and [anonymous](./test/test-anonymous.js), [css](./test/test-css.js)
299
302
 
300
303
 
@@ -990,6 +993,9 @@ c8 --experimental-monocart --reporter=v8 --reporter=console-details node foo.js
990
993
  - [vitest-monocart-coverage](https://github.com/cenfun/vitest-monocart-coverage) - Vitest custom provider module for coverage reports
991
994
  - [merge-code-coverage-vitest](https://github.com/cenfun/merge-code-coverage-vitest) - Example for merging code coverage (Vitest unit + Playwright e2e sharding)
992
995
 
996
+ ### [Node Test Runner](https://nodejs.org/docs/latest/api/test.html)
997
+ - [node-monocart-coverage](https://github.com/cenfun/node-monocart-coverage) - Custom reporter for Node test runner for coverage
998
+
993
999
  ### [Puppeteer](https://github.com/puppeteer/puppeteer/)
994
1000
  - [jest-puppeteer-coverage](https://github.com/cenfun/jest-puppeteer-coverage) - Example for Jest puppeteer coverage
995
1001
  - [maplibre-gl-js](https://github.com/maplibre/maplibre-gl-js) - Example for Jest (unit) + Puppeteer (e2e) + Codecov
@@ -1044,7 +1050,7 @@ const coverageOptions = {
1044
1050
  ]
1045
1051
  };
1046
1052
  ```
1047
- - Github actions example:
1053
+ - Github actions:
1048
1054
  ```yml
1049
1055
  - name: Codecov
1050
1056
  uses: codecov/codecov-action@v4
@@ -1080,7 +1086,7 @@ const coverageOptions = {
1080
1086
  lcov: true
1081
1087
  };
1082
1088
  ```
1083
- - Github actions example:
1089
+ - Github actions:
1084
1090
  ```yml
1085
1091
  - name: Coveralls
1086
1092
  uses: coverallsapp/github-action@v2
@@ -13,6 +13,8 @@ const findOriginalRange = require('./find-original-range.js');
13
13
  const { getJsAstInfo, getCssAstInfo } = require('./ast.js');
14
14
  const { getIgnoredRanges } = require('./ignore.js');
15
15
 
16
+ const { getUntestedList } = require('./untested.js');
17
+
16
18
  const {
17
19
  sortRanges, dedupeCountRanges, mergeRangesWith
18
20
  } = require('../utils/dedupe.js');
@@ -868,8 +870,8 @@ const decodeSourceMappings = (state, originalDecodedMap) => {
868
870
 
869
871
  const allDecodedMappings = [];
870
872
  decodedList.forEach((segments, generatedLine) => {
871
- let info = null;
872
- segments.forEach((segment) => {
873
+ const lastIndex = segments.length - 1;
874
+ segments.forEach((segment, i) => {
873
875
 
874
876
  // const COLUMN = 0;
875
877
  // const SOURCES_INDEX = 1;
@@ -889,7 +891,7 @@ const decodeSourceMappings = (state, originalDecodedMap) => {
889
891
  column: generatedColumn
890
892
  });
891
893
 
892
- info = {
894
+ const info = {
893
895
  generatedOffset,
894
896
  generatedLine,
895
897
  generatedColumn,
@@ -900,12 +902,21 @@ const decodeSourceMappings = (state, originalDecodedMap) => {
900
902
  nameIndex
901
903
  };
902
904
 
905
+ // first and last column
906
+ if (i === 0) {
907
+ info.first = true;
908
+ }
909
+ if (i === lastIndex) {
910
+ info.last = true;
911
+ }
912
+
903
913
  allDecodedMappings.push(info);
904
914
 
905
915
  originalDecodedMap.get(sourceIndex).push(info);
906
916
 
907
917
  });
908
918
 
919
+
909
920
  });
910
921
 
911
922
  // defaults to sort by generated offset, not need sort
@@ -1324,66 +1335,215 @@ const initJsCoverageList = (item) => {
1324
1335
  return coverageList;
1325
1336
  };
1326
1337
 
1327
- // ========================================================================================================
1328
-
1329
- const isCoveredInRanges = (range, uncoveredBytes) => {
1330
- if (!uncoveredBytes.length) {
1331
- return true;
1338
+ const logConvertTime = (msg, time_start, untested) => {
1339
+ if (untested) {
1340
+ return;
1332
1341
  }
1333
- for (const item of uncoveredBytes) {
1334
- if (range.start >= item.start && range.end <= item.end) {
1335
- return false;
1342
+ Util.logTime(msg, time_start);
1343
+ };
1344
+
1345
+ const convertCoverages = (list, options, untested) => {
1346
+ const stateList = [];
1347
+
1348
+ for (const item of list) {
1349
+ // console.log([item.id]);
1350
+
1351
+ const time_start_ast = Date.now();
1352
+
1353
+ const {
1354
+ type, source, fake, sourcePath
1355
+ } = item;
1356
+
1357
+ // for source file, type could be ts or vue as extname, but js = true
1358
+ const js = type === 'js';
1359
+ item.js = js;
1360
+
1361
+ // source mapping
1362
+ const locator = new Locator(source);
1363
+ const maxContentLength = source.length;
1364
+
1365
+ // ============================
1366
+ // move sourceMap
1367
+ const sourceMap = item.sourceMap;
1368
+ if (sourceMap) {
1369
+ delete item.sourceMap;
1370
+ }
1371
+
1372
+ // ============================
1373
+ // move functions and ranges to coverageList
1374
+ let coverageList = [];
1375
+ let astInfo;
1376
+ if (js) {
1377
+ coverageList = initJsCoverageList(item);
1378
+ // remove original functions
1379
+ if (!Util.isDebug()) {
1380
+ delete item.functions;
1381
+ }
1382
+ astInfo = getJsAstInfo(item, coverageList);
1383
+ } else {
1384
+ // convent css covered ranges to rules ranges and include uncovered ranges
1385
+ astInfo = getCssAstInfo(item, coverageList);
1386
+ // remove original ranges
1387
+ if (!Util.isDebug()) {
1388
+ delete item.ranges;
1389
+ }
1336
1390
  }
1391
+
1392
+ logConvertTime(` ┌ [convert] parsed ast: ${sourcePath} (${EC.cyan(Util.BSF(maxContentLength))})`, time_start_ast, untested);
1393
+
1394
+ // console.log(sourcePath, astInfo.statements.length);
1395
+ // ============================
1396
+
1397
+ const time_start_unpack = Date.now();
1398
+
1399
+ // current file and it's sources from sourceMap
1400
+ // see const originalState
1401
+ const state = {
1402
+ js,
1403
+ type,
1404
+ source,
1405
+ fake,
1406
+ sourcePath,
1407
+ sourceMap,
1408
+ locator,
1409
+ maxContentLength,
1410
+ decodedMappings: [],
1411
+ rangeCache: new Map(),
1412
+ diffCache: new Map(),
1413
+
1414
+ // alignTextList: [],
1415
+
1416
+ // coverage info
1417
+ bytes: [],
1418
+ functions: [],
1419
+ branches: [],
1420
+ statements: [],
1421
+ astInfo,
1422
+ // for sub source files
1423
+ coverageList,
1424
+ originalList: [],
1425
+ // coverage data
1426
+ v8Data: item
1427
+ };
1428
+
1429
+ unpackDistFile(item, state, options);
1430
+ const unpackedFiles = EC.cyan(`${state.originalList.length} files`);
1431
+
1432
+ stateList.push(state);
1433
+
1434
+ logConvertTime(` ┌ [convert] unpacked sourcemap: ${sourcePath} (${unpackedFiles})`, time_start_unpack, untested);
1435
+
1337
1436
  }
1338
- return true;
1437
+
1438
+ return stateList;
1439
+ };
1440
+
1441
+ // ========================================================================================================
1442
+
1443
+ const isUncoveredRange = (range, key, uncoveredGroups) => {
1444
+
1445
+ let uncovered = true;
1446
+ uncoveredGroups.forEach((group) => {
1447
+ const { groupMap, state } = group;
1448
+
1449
+ // ========================================================
1450
+ // self key
1451
+ if (groupMap.has(key)) {
1452
+ return;
1453
+ }
1454
+
1455
+ // ========================================================
1456
+ // in all group
1457
+ for (const item of groupMap.values()) {
1458
+ if (range.start >= item.start && range.end <= item.end) {
1459
+ return;
1460
+ }
1461
+ }
1462
+
1463
+ // ========================================================
1464
+ // no sourcemap mappings in range
1465
+ // check original range in decodedMappings
1466
+ const { decodedMappings } = state;
1467
+ const item = decodedMappings.find((it) => it.originalOffset >= range.start && it.originalOffset <= range.end);
1468
+ if (!item) {
1469
+ return;
1470
+ }
1471
+
1472
+ // ========================================================
1473
+ uncovered = false;
1474
+
1475
+ });
1476
+
1477
+ return uncovered;
1478
+
1339
1479
  };
1340
1480
 
1341
1481
  const mergeV8Data = (state, stateList) => {
1342
1482
  // console.log(stateList);
1343
1483
 
1344
- // const sourcePath = state.sourcePath;
1345
- // console.log(sourcePath);
1346
- // if (sourcePath.endsWith('scroll_zoom.ts')) {
1347
- // console.log('merge v8 data ===================================', sourcePath);
1348
- // console.log(stateList.map((it) => it.bytes));
1349
- // console.log(stateList.map((it) => it.functions));
1350
- // console.log(stateList.map((it) => it.branches.map((b) => [`${b.start}-${b.end}`, JSON.stringify(b.locations.map((l) => l.count))])));
1484
+ // let debug = false;
1485
+ // if (state.sourcePath.endsWith('counter.tsx')) {
1486
+ // debug = true;
1487
+ // console.log('merge v8 data ===================================', state.sourcePath);
1488
+ // console.log('bytes before ============', stateList.map((it) => it.bytes));
1489
+ // // console.log('statements ==============', stateList.map((it) => it.statements));
1490
+ // // console.log('functions ==============', stateList.map((it) => it.functions));
1491
+ // // console.log('branches ======================', stateList.map((it) => it.branches.map((b) => [`${b.start}-${b.end}`, JSON.stringify(b.locations.map((l) => l.count))])));
1351
1492
  // }
1352
1493
 
1353
1494
  // ===========================================================
1354
1495
  // bytes
1355
1496
  const mergedBytes = [];
1356
- const uncoveredList = [];
1497
+
1498
+ const coveredMap = new Map();
1499
+ const uncoveredMap = new Map();
1500
+ const uncoveredGroups = [];
1357
1501
  stateList.forEach((st) => {
1502
+ const groupMap = new Map();
1358
1503
  const bytes = dedupeCountRanges(st.bytes);
1359
- const uncoveredBytes = [];
1360
1504
  bytes.forEach((range) => {
1505
+ const key = `${range.start}_${range.end}`;
1361
1506
  if (range.count) {
1362
1507
  mergedBytes.push(range);
1508
+ coveredMap.set(key, true);
1363
1509
  } else {
1364
- uncoveredBytes.push(range);
1510
+ uncoveredMap.set(key, range);
1511
+ groupMap.set(key, range);
1365
1512
  }
1366
1513
  });
1367
- uncoveredList.push(uncoveredBytes);
1514
+
1515
+ uncoveredGroups.push({
1516
+ groupMap,
1517
+ state: st
1518
+ });
1368
1519
  });
1369
- // just remove uncovered range
1370
- uncoveredList.forEach((currentBytes) => {
1371
- currentBytes.forEach((range) => {
1372
- for (const targetBytes of uncoveredList) {
1373
- if (targetBytes === currentBytes) {
1374
- continue;
1375
- }
1376
- if (isCoveredInRanges(range, targetBytes)) {
1377
- return;
1378
- }
1379
- }
1520
+
1521
+ // if (debug) {
1522
+ // console.log('coveredMap ============', coveredMap);
1523
+ // console.log('uncoveredMap ============', uncoveredMap);
1524
+ // console.log('uncoveredGroups ============', uncoveredGroups);
1525
+ // }
1526
+
1527
+ uncoveredMap.forEach((range, key) => {
1528
+
1529
+ // in covered range
1530
+ if (coveredMap.has(key)) {
1531
+ return;
1532
+ }
1533
+
1534
+ if (isUncoveredRange(range, key, uncoveredGroups)) {
1380
1535
  mergedBytes.push(range);
1381
- });
1536
+ }
1537
+
1382
1538
  });
1383
1539
 
1384
1540
  // will be dedupeCountRanges in collectFileCoverage
1385
1541
  state.bytes = mergedBytes;
1386
1542
 
1543
+ // if (debug) {
1544
+ // console.log('bytes after ============', mergedBytes);
1545
+ // }
1546
+
1387
1547
  // ===========================================================
1388
1548
  // functions
1389
1549
  const allFunctions = stateList.map((it) => it.functions).flat();
@@ -1423,6 +1583,20 @@ const mergeV8Data = (state, stateList) => {
1423
1583
  const mergedFunctions = mergeRangesWith(allFunctions, functionComparer, functionHandler);
1424
1584
  state.functions = mergedFunctions;
1425
1585
 
1586
+ // ===========================================================
1587
+ // statements
1588
+ const allStatements = stateList.map((it) => it.statements).flat();
1589
+ const statementComparer = (lastRange, range) => {
1590
+ // exact matched because the statement range is generated from ast
1591
+ return lastRange.start === range.start && lastRange.end === range.end;
1592
+ };
1593
+ const statementHandler = (lastRange, range) => {
1594
+ // merge statements count
1595
+ lastRange.count += range.count;
1596
+ };
1597
+ const mergedStatements = mergeRangesWith(allStatements, statementComparer, statementHandler);
1598
+ state.statements = mergedStatements;
1599
+
1426
1600
  // ===========================================================
1427
1601
  // branches
1428
1602
  const allBranches = stateList.map((it) => it.branches).flat();
@@ -1449,6 +1623,43 @@ const mergeV8Data = (state, stateList) => {
1449
1623
  // }
1450
1624
 
1451
1625
 
1626
+ };
1627
+
1628
+ const addUntestedFiles = async (stateList, options) => {
1629
+
1630
+ const time_start_untested = Date.now();
1631
+
1632
+ const testedMap = new Map();
1633
+ stateList.forEach((state) => {
1634
+ const { v8Data, originalList } = state;
1635
+ // dedupe dist file if not debug
1636
+ if (!v8Data.dedupe) {
1637
+ testedMap.set(state.sourcePath, true);
1638
+ }
1639
+ originalList.forEach((originalState) => {
1640
+ testedMap.set(originalState.sourcePath, true);
1641
+ });
1642
+ });
1643
+
1644
+ // console.log('testedMap', testedMap);
1645
+
1646
+ const untestedList = await getUntestedList(testedMap, options, 'v8');
1647
+ if (!untestedList) {
1648
+ return;
1649
+ }
1650
+
1651
+ const untestedStateList = convertCoverages(untestedList, options, true);
1652
+
1653
+ // console.log(untestedStateList);
1654
+ untestedStateList.forEach((state) => {
1655
+ stateList.push(state);
1656
+ });
1657
+
1658
+ // console.log('untestedList', untestedList);
1659
+
1660
+ Util.logTime(` ┌ [convert] added untested files: ${EC.yellow(untestedList.length)}`, time_start_untested);
1661
+
1662
+
1452
1663
  };
1453
1664
 
1454
1665
  const generateV8DataList = (stateList, options) => {
@@ -1527,109 +1738,17 @@ const generateV8DataList = (stateList, options) => {
1527
1738
  };
1528
1739
  };
1529
1740
 
1530
- const convertV8List = (v8list, options) => {
1531
-
1532
- const stateList = [];
1533
-
1534
- for (const item of v8list) {
1535
- // console.log([item.id]);
1536
-
1537
- const time_start_ast = Date.now();
1538
-
1539
- const {
1540
- type, source, fake, sourcePath
1541
- } = item;
1542
-
1543
- // for source file, type could be ts or vue as extname, but js = true
1544
- const js = type === 'js';
1545
- item.js = js;
1546
-
1547
- // source mapping
1548
- const locator = new Locator(source);
1549
- const maxContentLength = source.length;
1550
-
1551
- // ============================
1552
- // move sourceMap
1553
- const sourceMap = item.sourceMap;
1554
- if (sourceMap) {
1555
- delete item.sourceMap;
1556
- }
1557
-
1558
- // ============================
1559
- // move functions and ranges to coverageList
1560
- let coverageList = [];
1561
- let astInfo;
1562
- if (js) {
1563
- coverageList = initJsCoverageList(item);
1564
- // remove original functions
1565
- if (!Util.isDebug()) {
1566
- delete item.functions;
1567
- }
1568
- astInfo = getJsAstInfo(item, coverageList);
1569
- } else {
1570
- // convent css covered ranges to rules ranges and include uncovered ranges
1571
- astInfo = getCssAstInfo(item, coverageList);
1572
- // remove original ranges
1573
- if (!Util.isDebug()) {
1574
- delete item.ranges;
1575
- }
1576
- }
1741
+ const convertV8List = async (v8list, options) => {
1577
1742
 
1578
- Util.logTime(` ┌ [convert] parsed ast: ${sourcePath} (${EC.cyan(Util.BSF(maxContentLength))})`, time_start_ast);
1579
-
1580
- // console.log(sourcePath, astInfo.statements.length);
1581
- // ============================
1743
+ // for tested files
1744
+ const stateList = convertCoverages(v8list, options);
1582
1745
 
1583
- const time_start_unpack = Date.now();
1584
-
1585
- // current file and it's sources from sourceMap
1586
- // see const originalState
1587
- const state = {
1588
- js,
1589
- type,
1590
- source,
1591
- fake,
1592
- sourcePath,
1593
- sourceMap,
1594
- locator,
1595
- maxContentLength,
1596
- decodedMappings: [],
1597
- rangeCache: new Map(),
1598
- diffCache: new Map(),
1599
-
1600
- // alignTextList: [],
1601
-
1602
- // coverage info
1603
- bytes: [],
1604
- functions: [],
1605
- branches: [],
1606
- statements: [],
1607
- astInfo,
1608
- // for sub source files
1609
- coverageList,
1610
- originalList: [],
1611
- // coverage data
1612
- v8Data: item
1613
- };
1614
-
1615
- unpackDistFile(item, state, options);
1616
- const unpackedFiles = EC.cyan(`${state.originalList.length} files`);
1617
-
1618
- stateList.push(state);
1619
-
1620
- Util.logTime(` ┌ [convert] unpacked sourcemap: ${sourcePath} (${unpackedFiles})`, time_start_unpack);
1621
-
1622
- }
1746
+ // empty coverage handler
1747
+ await addUntestedFiles(stateList, options);
1623
1748
 
1624
1749
  const time_start_convert = Date.now();
1625
-
1626
1750
  const dataList = generateV8DataList(stateList, options);
1627
1751
  const dataFiles = EC.cyan(`${dataList.v8DataList.length} files`);
1628
-
1629
- // stateList.forEach((st, i) => {
1630
- // Util.writeFileSync(`align-text-${i}.js`, `window.gridData = ${JSON.stringify(st.alignTextList, null, 4)}`);
1631
- // });
1632
-
1633
1752
  Util.logTime(` ┌ [convert] converted data list (${dataFiles})`, time_start_convert);
1634
1753
 
1635
1754
  return dataList;
@@ -407,16 +407,16 @@ const getFixedPosition = (info, direction) => {
407
407
 
408
408
  // ========================================================================================================
409
409
 
410
- const getOriginalEndOffset = (m, cache) => {
410
+ const getOriginalEndOffset = (m, originalState) => {
411
411
  if (!m.originalEndOffset) {
412
- const line = cache.originalState.locator.getLine(m.originalLine + 1);
412
+ const line = originalState.locator.getLine(m.originalLine + 1);
413
413
  m.originalEndOffset = line ? line.end : m.originalOffset;
414
414
  }
415
415
  return m.originalEndOffset;
416
416
  };
417
417
 
418
- const getOriginalText = (m1, m2, cache) => {
419
- const originalLocator = cache.originalState.locator;
418
+ const getOriginalText = (m1, m2, originalState, startNextLine) => {
419
+ const originalLocator = originalState.locator;
420
420
 
421
421
  const o1 = m1.originalOffset;
422
422
  const o2 = m2.originalOffset;
@@ -442,10 +442,22 @@ const getOriginalText = (m1, m2, cache) => {
442
442
  };
443
443
  }
444
444
 
445
+ // start from next line
446
+ if (startNextLine) {
447
+ const nextLine = originalState.locator.getLine(m1.originalLine + 2);
448
+ if (nextLine) {
449
+ // console.log(nextLine);
450
+ return {
451
+ originalOffset: nextLine.start + nextLine.indent,
452
+ originalText: nextLine.text.slice(nextLine.indent)
453
+ };
454
+ }
455
+ }
456
+
445
457
  // could be multiple lines for original text
446
458
  return {
447
459
  originalOffset: o1,
448
- originalText: originalLocator.getSlice(o1, getOriginalEndOffset(m1, cache))
460
+ originalText: originalLocator.getSlice(o1, getOriginalEndOffset(m1, originalState))
449
461
  };
450
462
  }
451
463
 
@@ -454,7 +466,7 @@ const getOriginalText = (m1, m2, cache) => {
454
466
  if (o1 === o2) {
455
467
  return {
456
468
  originalOffset: o1,
457
- originalText: originalLocator.getSlice(o1, getOriginalEndOffset(m1, cache))
469
+ originalText: originalLocator.getSlice(o1, getOriginalEndOffset(m1, originalState))
458
470
  };
459
471
  }
460
472
 
@@ -477,7 +489,7 @@ const getOriginalText = (m1, m2, cache) => {
477
489
  // should be wrong, ignore m2
478
490
  return {
479
491
  originalOffset: o1,
480
- originalText: originalLocator.getSlice(o1, getOriginalEndOffset(m1, cache))
492
+ originalText: originalLocator.getSlice(o1, getOriginalEndOffset(m1, originalState))
481
493
  };
482
494
 
483
495
  };
@@ -553,8 +565,19 @@ const getFixedOriginalStart = (start, mappings, state, cache, options) => {
553
565
  };
554
566
  }
555
567
 
568
+ // skip last mapping
569
+ let startNextLine = false;
570
+ if (m1.last) {
571
+ const locStart = state.locator.offsetToLocation(start);
572
+ // 1-base
573
+ const lineIndex = locStart.line - 1;
574
+ if (lineIndex > m1.generatedLine) {
575
+ startNextLine = true;
576
+ // console.log(originalState.sourcePath, start, m1, m2);
577
+ }
578
+ }
556
579
 
557
- const { originalText, originalOffset } = getOriginalText(m1, m2, cache);
580
+ const { originalText, originalOffset } = getOriginalText(m1, m2, originalState, startNextLine);
558
581
  const { generatedText, generatedPos } = getGeneratedText(m1, m2, state, start);
559
582
  const direction = 'start';
560
583
 
@@ -592,7 +615,7 @@ const getFixedOriginalEnd = (end, mappings, state, cache) => {
592
615
 
593
616
  // cross file until e1 line end (not file end)
594
617
  if (crossEnd) {
595
- const originalEndOffset = getOriginalEndOffset(m1, cache);
618
+ const originalEndOffset = getOriginalEndOffset(m1, originalState);
596
619
  return {
597
620
  originalEnd: originalEndOffset
598
621
  };
@@ -608,7 +631,7 @@ const getFixedOriginalEnd = (end, mappings, state, cache) => {
608
631
  }
609
632
 
610
633
 
611
- const { originalText, originalOffset } = getOriginalText(m1, m2, cache);
634
+ const { originalText, originalOffset } = getOriginalText(m1, m2, originalState);
612
635
  const { generatedText, generatedPos } = getGeneratedText(m1, m2, state, end);
613
636
  const direction = 'end';
614
637