11cb0ef41Sopenharmony_ci// Flags: --enable-source-maps
21cb0ef41Sopenharmony_ci'use strict';
31cb0ef41Sopenharmony_ci
41cb0ef41Sopenharmony_ciconst common = require('../common');
51cb0ef41Sopenharmony_ciconst assert = require('assert');
61cb0ef41Sopenharmony_ciconst { findSourceMap, SourceMap } = require('module');
71cb0ef41Sopenharmony_ciconst { readFileSync } = require('fs');
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ci// It should throw with invalid args.
101cb0ef41Sopenharmony_ci{
111cb0ef41Sopenharmony_ci  [1, true, 'foo'].forEach((invalidArg) =>
121cb0ef41Sopenharmony_ci    assert.throws(
131cb0ef41Sopenharmony_ci      () => new SourceMap(invalidArg),
141cb0ef41Sopenharmony_ci      {
151cb0ef41Sopenharmony_ci        code: 'ERR_INVALID_ARG_TYPE',
161cb0ef41Sopenharmony_ci        name: 'TypeError',
171cb0ef41Sopenharmony_ci        message: 'The "payload" argument must be of type object.' +
181cb0ef41Sopenharmony_ci               common.invalidArgTypeHelper(invalidArg)
191cb0ef41Sopenharmony_ci      }
201cb0ef41Sopenharmony_ci    )
211cb0ef41Sopenharmony_ci  );
221cb0ef41Sopenharmony_ci}
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_ci// `findSourceMap()` should return undefined when no source map is found.
251cb0ef41Sopenharmony_ci{
261cb0ef41Sopenharmony_ci  const files = [
271cb0ef41Sopenharmony_ci    __filename,
281cb0ef41Sopenharmony_ci    '',
291cb0ef41Sopenharmony_ci    'invalid-file',
301cb0ef41Sopenharmony_ci  ];
311cb0ef41Sopenharmony_ci  for (const file of files) {
321cb0ef41Sopenharmony_ci    const sourceMap = findSourceMap(file);
331cb0ef41Sopenharmony_ci    assert.strictEqual(sourceMap, undefined);
341cb0ef41Sopenharmony_ci  }
351cb0ef41Sopenharmony_ci}
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ci// findSourceMap() can lookup source-maps based on URIs, in the
381cb0ef41Sopenharmony_ci// non-exceptional case.
391cb0ef41Sopenharmony_ci{
401cb0ef41Sopenharmony_ci  require('../fixtures/source-map/disk-relative-path.js');
411cb0ef41Sopenharmony_ci  const sourceMap = findSourceMap(
421cb0ef41Sopenharmony_ci    require.resolve('../fixtures/source-map/disk-relative-path.js')
431cb0ef41Sopenharmony_ci  );
441cb0ef41Sopenharmony_ci  const {
451cb0ef41Sopenharmony_ci    originalLine,
461cb0ef41Sopenharmony_ci    originalColumn,
471cb0ef41Sopenharmony_ci    originalSource
481cb0ef41Sopenharmony_ci  } = sourceMap.findEntry(0, 29);
491cb0ef41Sopenharmony_ci  assert.strictEqual(originalLine, 2);
501cb0ef41Sopenharmony_ci  assert.strictEqual(originalColumn, 4);
511cb0ef41Sopenharmony_ci  assert(originalSource.endsWith('disk.js'));
521cb0ef41Sopenharmony_ci  const {
531cb0ef41Sopenharmony_ci    fileName,
541cb0ef41Sopenharmony_ci    lineNumber,
551cb0ef41Sopenharmony_ci    columnNumber,
561cb0ef41Sopenharmony_ci  } = sourceMap.findOrigin(1, 30);
571cb0ef41Sopenharmony_ci  assert.strictEqual(fileName, originalSource);
581cb0ef41Sopenharmony_ci  assert.strictEqual(lineNumber, 3);
591cb0ef41Sopenharmony_ci  assert.strictEqual(columnNumber, 6);
601cb0ef41Sopenharmony_ci}
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci// findSourceMap() can be used in Error.prepareStackTrace() to lookup
631cb0ef41Sopenharmony_ci// source-map attached to error.
641cb0ef41Sopenharmony_ci{
651cb0ef41Sopenharmony_ci  let callSite;
661cb0ef41Sopenharmony_ci  let sourceMap;
671cb0ef41Sopenharmony_ci  Error.prepareStackTrace = (error, trace) => {
681cb0ef41Sopenharmony_ci    const throwingRequireCallSite = trace[0];
691cb0ef41Sopenharmony_ci    if (throwingRequireCallSite.getFileName().endsWith('typescript-throw.js')) {
701cb0ef41Sopenharmony_ci      sourceMap = findSourceMap(throwingRequireCallSite.getFileName());
711cb0ef41Sopenharmony_ci      callSite = throwingRequireCallSite;
721cb0ef41Sopenharmony_ci    }
731cb0ef41Sopenharmony_ci  };
741cb0ef41Sopenharmony_ci  try {
751cb0ef41Sopenharmony_ci    // Require a file that throws an exception, and has a source map.
761cb0ef41Sopenharmony_ci    require('../fixtures/source-map/typescript-throw.js');
771cb0ef41Sopenharmony_ci  } catch (err) {
781cb0ef41Sopenharmony_ci    // eslint-disable-next-line no-unused-expressions
791cb0ef41Sopenharmony_ci    err.stack; // Force prepareStackTrace() to be called.
801cb0ef41Sopenharmony_ci  }
811cb0ef41Sopenharmony_ci  assert(callSite);
821cb0ef41Sopenharmony_ci  assert(sourceMap);
831cb0ef41Sopenharmony_ci  const {
841cb0ef41Sopenharmony_ci    generatedLine,
851cb0ef41Sopenharmony_ci    generatedColumn,
861cb0ef41Sopenharmony_ci    originalLine,
871cb0ef41Sopenharmony_ci    originalColumn,
881cb0ef41Sopenharmony_ci    originalSource
891cb0ef41Sopenharmony_ci  } = sourceMap.findEntry(
901cb0ef41Sopenharmony_ci    callSite.getLineNumber() - 1,
911cb0ef41Sopenharmony_ci    callSite.getColumnNumber() - 1
921cb0ef41Sopenharmony_ci  );
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ci  assert.strictEqual(generatedLine, 19);
951cb0ef41Sopenharmony_ci  assert.strictEqual(generatedColumn, 14);
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci  assert.strictEqual(originalLine, 17);
981cb0ef41Sopenharmony_ci  assert.strictEqual(originalColumn, 10);
991cb0ef41Sopenharmony_ci  assert(originalSource.endsWith('typescript-throw.ts'));
1001cb0ef41Sopenharmony_ci
1011cb0ef41Sopenharmony_ci  const {
1021cb0ef41Sopenharmony_ci    fileName,
1031cb0ef41Sopenharmony_ci    lineNumber,
1041cb0ef41Sopenharmony_ci    columnNumber,
1051cb0ef41Sopenharmony_ci  } = sourceMap.findOrigin(
1061cb0ef41Sopenharmony_ci    callSite.getLineNumber(),
1071cb0ef41Sopenharmony_ci    callSite.getColumnNumber()
1081cb0ef41Sopenharmony_ci  );
1091cb0ef41Sopenharmony_ci  assert.strictEqual(fileName, originalSource);
1101cb0ef41Sopenharmony_ci  assert.strictEqual(lineNumber, 18);
1111cb0ef41Sopenharmony_ci  assert.strictEqual(columnNumber, 11);
1121cb0ef41Sopenharmony_ci}
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci// SourceMap can be instantiated with Source Map V3 object as payload.
1151cb0ef41Sopenharmony_ci{
1161cb0ef41Sopenharmony_ci  const payload = JSON.parse(readFileSync(
1171cb0ef41Sopenharmony_ci    require.resolve('../fixtures/source-map/disk.map'), 'utf8'
1181cb0ef41Sopenharmony_ci  ));
1191cb0ef41Sopenharmony_ci  const sourceMap = new SourceMap(payload);
1201cb0ef41Sopenharmony_ci  const {
1211cb0ef41Sopenharmony_ci    originalLine,
1221cb0ef41Sopenharmony_ci    originalColumn,
1231cb0ef41Sopenharmony_ci    originalSource
1241cb0ef41Sopenharmony_ci  } = sourceMap.findEntry(0, 29);
1251cb0ef41Sopenharmony_ci  assert.strictEqual(originalLine, 2);
1261cb0ef41Sopenharmony_ci  assert.strictEqual(originalColumn, 4);
1271cb0ef41Sopenharmony_ci  assert(originalSource.endsWith('disk.js'));
1281cb0ef41Sopenharmony_ci  // The stored payload should be a clone:
1291cb0ef41Sopenharmony_ci  assert.strictEqual(payload.mappings, sourceMap.payload.mappings);
1301cb0ef41Sopenharmony_ci  assert.notStrictEqual(payload, sourceMap.payload);
1311cb0ef41Sopenharmony_ci  assert.strictEqual(payload.sources[0], sourceMap.payload.sources[0]);
1321cb0ef41Sopenharmony_ci  assert.notStrictEqual(payload.sources, sourceMap.payload.sources);
1331cb0ef41Sopenharmony_ci}
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci// findEntry() and findOrigin() must return empty object instead of
1361cb0ef41Sopenharmony_ci// error when receiving a malformed mappings.
1371cb0ef41Sopenharmony_ci{
1381cb0ef41Sopenharmony_ci  const payload = JSON.parse(readFileSync(
1391cb0ef41Sopenharmony_ci    require.resolve('../fixtures/source-map/disk.map'), 'utf8'
1401cb0ef41Sopenharmony_ci  ));
1411cb0ef41Sopenharmony_ci  payload.mappings = ';;;;;;;;;';
1421cb0ef41Sopenharmony_ci
1431cb0ef41Sopenharmony_ci  const sourceMap = new SourceMap(payload);
1441cb0ef41Sopenharmony_ci  const result = sourceMap.findEntry(0, 5);
1451cb0ef41Sopenharmony_ci  assert.strictEqual(typeof result, 'object');
1461cb0ef41Sopenharmony_ci  assert.strictEqual(Object.keys(result).length, 0);
1471cb0ef41Sopenharmony_ci  const origin = sourceMap.findOrigin(0, 5);
1481cb0ef41Sopenharmony_ci  assert.strictEqual(typeof origin, 'object');
1491cb0ef41Sopenharmony_ci  assert.strictEqual(Object.keys(origin).length, 0);
1501cb0ef41Sopenharmony_ci}
1511cb0ef41Sopenharmony_ci
1521cb0ef41Sopenharmony_ci// SourceMap can be instantiated with Index Source Map V3 object as payload.
1531cb0ef41Sopenharmony_ci{
1541cb0ef41Sopenharmony_ci  const payload = JSON.parse(readFileSync(
1551cb0ef41Sopenharmony_ci    require.resolve('../fixtures/source-map/disk-index.map'), 'utf8'
1561cb0ef41Sopenharmony_ci  ));
1571cb0ef41Sopenharmony_ci  const sourceMap = new SourceMap(payload);
1581cb0ef41Sopenharmony_ci  const {
1591cb0ef41Sopenharmony_ci    originalLine,
1601cb0ef41Sopenharmony_ci    originalColumn,
1611cb0ef41Sopenharmony_ci    originalSource
1621cb0ef41Sopenharmony_ci  } = sourceMap.findEntry(0, 29);
1631cb0ef41Sopenharmony_ci  assert.strictEqual(originalLine, 2);
1641cb0ef41Sopenharmony_ci  assert.strictEqual(originalColumn, 4);
1651cb0ef41Sopenharmony_ci  assert(originalSource.endsWith('section.js'));
1661cb0ef41Sopenharmony_ci  // The stored payload should be a clone:
1671cb0ef41Sopenharmony_ci  assert.strictEqual(payload.mappings, sourceMap.payload.mappings);
1681cb0ef41Sopenharmony_ci  assert.notStrictEqual(payload, sourceMap.payload);
1691cb0ef41Sopenharmony_ci  assert.strictEqual(payload.sources[0], sourceMap.payload.sources[0]);
1701cb0ef41Sopenharmony_ci  assert.notStrictEqual(payload.sources, sourceMap.payload.sources);
1711cb0ef41Sopenharmony_ci}
1721cb0ef41Sopenharmony_ci
1731cb0ef41Sopenharmony_ci// Test various known decodings to ensure decodeVLQ works correctly.
1741cb0ef41Sopenharmony_ci{
1751cb0ef41Sopenharmony_ci  function makeMinimalMap(column) {
1761cb0ef41Sopenharmony_ci    return {
1771cb0ef41Sopenharmony_ci      sources: ['test.js'],
1781cb0ef41Sopenharmony_ci      // Mapping from the 0th line, 0th column of the output file to the 0th
1791cb0ef41Sopenharmony_ci      // source file, 0th line, ${column}th column.
1801cb0ef41Sopenharmony_ci      mappings: `AAA${column}`,
1811cb0ef41Sopenharmony_ci    };
1821cb0ef41Sopenharmony_ci  }
1831cb0ef41Sopenharmony_ci  const knownDecodings = {
1841cb0ef41Sopenharmony_ci    'A': 0,
1851cb0ef41Sopenharmony_ci    'B': -2147483648,
1861cb0ef41Sopenharmony_ci    'C': 1,
1871cb0ef41Sopenharmony_ci    'D': -1,
1881cb0ef41Sopenharmony_ci    'E': 2,
1891cb0ef41Sopenharmony_ci    'F': -2,
1901cb0ef41Sopenharmony_ci
1911cb0ef41Sopenharmony_ci    // 2^31 - 1, maximum values
1921cb0ef41Sopenharmony_ci    '+/////D': 2147483647,
1931cb0ef41Sopenharmony_ci    '8/////D': 2147483646,
1941cb0ef41Sopenharmony_ci    '6/////D': 2147483645,
1951cb0ef41Sopenharmony_ci    '4/////D': 2147483644,
1961cb0ef41Sopenharmony_ci    '2/////D': 2147483643,
1971cb0ef41Sopenharmony_ci    '0/////D': 2147483642,
1981cb0ef41Sopenharmony_ci
1991cb0ef41Sopenharmony_ci    // -2^31 + 1, minimum values
2001cb0ef41Sopenharmony_ci    '//////D': -2147483647,
2011cb0ef41Sopenharmony_ci    '9/////D': -2147483646,
2021cb0ef41Sopenharmony_ci    '7/////D': -2147483645,
2031cb0ef41Sopenharmony_ci    '5/////D': -2147483644,
2041cb0ef41Sopenharmony_ci    '3/////D': -2147483643,
2051cb0ef41Sopenharmony_ci    '1/////D': -2147483642,
2061cb0ef41Sopenharmony_ci  };
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_ci  for (const column in knownDecodings) {
2091cb0ef41Sopenharmony_ci    const sourceMap = new SourceMap(makeMinimalMap(column));
2101cb0ef41Sopenharmony_ci    const { originalColumn } = sourceMap.findEntry(0, 0);
2111cb0ef41Sopenharmony_ci    assert.strictEqual(originalColumn, knownDecodings[column]);
2121cb0ef41Sopenharmony_ci  }
2131cb0ef41Sopenharmony_ci}
2141cb0ef41Sopenharmony_ci
2151cb0ef41Sopenharmony_ci// Test that generated columns are sorted when a negative offset is
2161cb0ef41Sopenharmony_ci// observed, see: https://github.com/mozilla/source-map/pull/92
2171cb0ef41Sopenharmony_ci{
2181cb0ef41Sopenharmony_ci  function makeMinimalMap(generatedColumns, originalColumns) {
2191cb0ef41Sopenharmony_ci    return {
2201cb0ef41Sopenharmony_ci      sources: ['test.js'],
2211cb0ef41Sopenharmony_ci      // Mapping from the 0th line, ${g}th column of the output file to the 0th
2221cb0ef41Sopenharmony_ci      // source file, 0th line, ${column}th column.
2231cb0ef41Sopenharmony_ci      mappings: generatedColumns.map((g, i) => `${g}AA${originalColumns[i]}`)
2241cb0ef41Sopenharmony_ci        .join(',')
2251cb0ef41Sopenharmony_ci    };
2261cb0ef41Sopenharmony_ci  }
2271cb0ef41Sopenharmony_ci  // U = 10
2281cb0ef41Sopenharmony_ci  // F = -2
2291cb0ef41Sopenharmony_ci  // A = 0
2301cb0ef41Sopenharmony_ci  // E = 2
2311cb0ef41Sopenharmony_ci  const sourceMap = new SourceMap(makeMinimalMap(
2321cb0ef41Sopenharmony_ci    ['U', 'F', 'F'],
2331cb0ef41Sopenharmony_ci    ['A', 'E', 'E']
2341cb0ef41Sopenharmony_ci  ));
2351cb0ef41Sopenharmony_ci  assert.strictEqual(sourceMap.findEntry(0, 6).originalColumn, 4);
2361cb0ef41Sopenharmony_ci  assert.strictEqual(sourceMap.findEntry(0, 8).originalColumn, 2);
2371cb0ef41Sopenharmony_ci  assert.strictEqual(sourceMap.findEntry(0, 10).originalColumn, 0);
2381cb0ef41Sopenharmony_ci}
239