recast/test/mapping.ts

208 lines
5.8 KiB
TypeScript

import assert from "assert";
import sourceMap from "source-map";
import * as recast from "../main";
import * as types from "ast-types";
const n = types.namedTypes;
const b = types.builders;
const NodePath = types.NodePath;
import { fromString } from "../lib/lines";
import { parse } from "../lib/parser";
import { Printer } from "../lib/printer";
import { EOL as eol } from "os";
describe("source maps", function () {
it("should generate correct mappings", function () {
const code = ["function foo(bar) {", " return 1 + bar;", "}"].join(eol);
fromString(code);
const ast = parse(code, {
sourceFileName: "source.js",
});
const path = new NodePath(ast);
const returnPath = path.get("program", "body", 0, "body", "body", 0);
n.ReturnStatement.assert(returnPath.value);
const leftPath = returnPath.get("argument", "left");
const leftValue = leftPath.value;
const rightPath = returnPath.get("argument", "right");
leftPath.replace(rightPath.value);
rightPath.replace(leftValue);
const sourceRoot = "path/to/source/root";
const printed = new Printer({
sourceMapName: "source.map.json",
sourceRoot: sourceRoot,
}).print(ast);
assert.ok(printed.map);
assert.strictEqual(printed.map.file, "source.map.json");
assert.strictEqual(printed.map.sourceRoot, sourceRoot);
const smc = new sourceMap.SourceMapConsumer(printed.map);
function check(
origLine: any,
origCol: any,
genLine: any,
genCol: any,
lastColumn: any,
) {
assert.deepEqual(
smc.originalPositionFor({
line: genLine,
column: genCol,
}),
{
source: sourceRoot + "/source.js",
line: origLine,
column: origCol,
name: null,
},
);
assert.deepEqual(
smc.generatedPositionFor({
source: sourceRoot + "/source.js",
line: origLine,
column: origCol,
}),
{
line: genLine,
column: genCol,
lastColumn: lastColumn,
},
);
}
check(1, 0, 1, 0, null); // function
check(1, 18, 1, 18, null); // {
check(2, 13, 2, 9, null); // bar
check(2, 9, 2, 15, null); // 1
check(3, 0, 3, 0, null); // }
});
it("should compose with inputSourceMap", function () {
function addUseStrict(ast: any) {
return recast.visit(ast, {
visitFunction: function (path) {
path
.get("body", "body")
.unshift(b.expressionStatement(b.literal("use strict")));
this.traverse(path);
},
});
}
function stripConsole(ast: any) {
return recast.visit(ast, {
visitCallExpression: function (path) {
const node = path.value;
if (
n.MemberExpression.check(node.callee) &&
n.Identifier.check(node.callee.object) &&
node.callee.object.name === "console"
) {
n.ExpressionStatement.assert(path.parent.node);
path.parent.replace();
return false;
}
return;
},
});
}
const code = [
"function add(a, b) {",
" var sum = a + b;",
" console.log(a, b);",
" return sum;",
"}",
].join(eol);
const ast = parse(code, {
sourceFileName: "original.js",
});
const useStrictResult = new Printer({
sourceMapName: "useStrict.map.json",
}).print(addUseStrict(ast));
const useStrictAst = parse(useStrictResult.code, {
sourceFileName: "useStrict.js",
});
const oneStepResult = new Printer({
sourceMapName: "oneStep.map.json",
}).print(stripConsole(ast));
const twoStepResult = new Printer({
sourceMapName: "twoStep.map.json",
inputSourceMap: useStrictResult.map,
}).print(stripConsole(useStrictAst));
assert.strictEqual(oneStepResult.code, twoStepResult.code);
const smc1 = new sourceMap.SourceMapConsumer(oneStepResult.map);
const smc2 = new sourceMap.SourceMapConsumer(twoStepResult.map);
smc1.eachMapping(function (mapping) {
const pos = {
line: mapping.generatedLine,
column: mapping.generatedColumn,
};
const orig1 = smc1.originalPositionFor(pos);
const orig2 = smc2.originalPositionFor(pos);
// The composition of the source maps generated separately from
// the two transforms should be equivalent to the source map
// generated from the composition of the two transforms.
assert.deepEqual(orig1, orig2);
// Make sure the two-step source map refers back to the original
// source instead of the intermediate source.
assert.strictEqual(orig2.source, "original.js");
});
});
it("should work when a child node becomes null", function () {
// https://github.com/facebook/regenerator/issues/103
const code = ["for (var i = 0; false; i++)", " log(i);"].join(eol);
const ast = parse(code);
const path = new NodePath(ast);
const updatePath = path.get("program", "body", 0, "update");
n.UpdateExpression.assert(updatePath.value);
updatePath.replace(null);
const printed = new Printer().print(ast);
assert.strictEqual(
printed.code,
["for (var i = 0; false; )", " log(i);"].join(eol),
);
});
it("should tolerate programs that become empty", function () {
const source = "foo();";
const ast = recast.parse(source, {
sourceFileName: "foo.js",
});
assert.strictEqual(ast.program.body.length, 1);
ast.program.body.length = 0;
const result = recast.print(ast, {
sourceMapName: "foo.map.json",
});
assert.strictEqual(result.map.file, "foo.map.json");
assert.deepEqual(result.map.sources, []);
assert.deepEqual(result.map.names, []);
assert.strictEqual(result.map.mappings, "");
});
});