This code defines a system for inserting debugging information into JavaScript code by modifying its Abstract Syntax Tree (AST) and injecting calls to a logging function.
npm run import -- "inspect every statement"
var importer = require('../Core')
var {transpile} = importer.import("transpile code")
var {selectAst, makeExpr} = importer.import("select code tree")
var {htmlToTree} = importer.import("html to tree")
var STATEMENTS = `//*[contains(@type, "Declaration")]
|//*[contains(@type, "Statement")]`
var NEAR_IDENTIFIERS = `
./Identifier/@name
|./*/Identifier/@name
|./*/*/Identifier/@name
|./*/*/*/Identifier/@name
|./parent::*/Identifier/@name
|./parent::*/*/Identifier/@name
|./parent::*/*/*/Identifier/@name
|./parent::*/*/*/*/Identifier/@name
|./parent::*/parent::*/Identifier/@name
|./parent::*/parent::*/*/Identifier/@name
|./parent::*/parent::*/*/*/Identifier/@name
|./parent::*/parent::*/*/*/*/Identifier/@name
`
function inspectCallback(ctx) {
console.log(JSON.stringify(ctx))
}
function inspectTemplate(ctx) {
// code inserted in to transpiled module
inspectCallback(ctx)
}
function insertInspect(filename, code, ctx) {
var inspect = makeExpr(inspectTemplate)
// replace line with the line number from original range
var range = JSON.parse(ctx.getAttribute('range'))
var line = code.substr(0, range[0]).split('\n').length
// replace the ctx with nearby identifiers
// TODO: 'replace' transpiler command
var replaceCtx = selectAst(`.//Identifier[@name="ctx"]`, inspect)
var nearbyIdentifiers = selectAst([NEAR_IDENTIFIERS], ctx)
var nearbyCtx = makeExpr(`{
${nearbyIdentifiers.join(',')},
//ctx: ${JSON.stringify(htmlToTree(ctx))},
type: "${ctx.getAttribute('type')}",
line: ${line},
filename: "${filename}"
}`)
nearbyCtx.setAttribute('parent-attr', 'arguments')
replaceCtx.replaceWith(nearbyCtx)
// insert into parent statement body
// TODO: make this a transpile operation because it contains node calls
var parent = selectAst(`./parent::*`, ctx)
Array.from(inspect.childNodes).forEach(n => {
if(n.setAttribute) {
n.setAttribute('parent-attr', 'body')
}
parent.insertBefore(n, ctx)
})
}
function transpileInspect(code, filename) {
return transpile([
[STATEMENTS, insertInspect.bind(null, filename, code)]
], code)
}
module.exports = {
inspectCallback,
inspectTemplate,
transpileInspect,
}
const { importCoreFunctions } = require('../Core');
/**
* Import necessary functions from the 'Core' module.
*/
const { transpile, selectAst, makeExpr, htmlToTree } = importCoreFunctions([
'transpile code',
'select code tree',
'html to tree',
]);
/**
* XPath expressions for selecting specific nodes in the code tree.
*/
const STATEMENTS = `//*[contains(@type, "Declaration") or contains(@type, "Statement")]`;
const NEAR_IDENTIFIERS = `
./Identifier/@name
|./*/Identifier/@name
|./*/*/Identifier/@name
|./*/*/*/Identifier/@name
|./parent::*/Identifier/@name
|./parent::*/*/Identifier/@name
|./parent::*/*/*/Identifier/@name
|./parent::*/*/*/*/Identifier/@name
|./parent::*/parent::*/Identifier/@name
|./parent::*/parent::*/*/Identifier/@name
|./parent::*/parent::*/*/*/Identifier/@name
|./parent::*/parent::*/*/*/*/Identifier/@name
`;
/**
* Inspect callback function.
*
* @param {Object} ctx - The context object.
*/
function inspectCallback(ctx) {
console.log(JSON.stringify(ctx));
}
/**
* Inspect template function.
*
* @param {Object} ctx - The context object.
*/
function inspectTemplate(ctx) {
// Code inserted into the transpiled module.
inspectCallback(ctx);
}
/**
* Inserts inspection code into the given code snippet.
*
* @param {string} filename - The filename of the code snippet.
* @param {string} code - The code snippet.
* @param {Element} ctx - The context element.
*/
function insertInspect(filename, code, ctx) {
const inspect = makeExpr(inspectTemplate);
// Get the line number from the original range.
const range = JSON.parse(ctx.getAttribute('range'));
const line = code
.substr(0, range[0])
.split('\n')
.length;
// Get nearby identifiers.
const nearbyIdentifiers = selectAst(NEAR_IDENTIFIERS, ctx).map((id) => id.getAttribute('name'));
// Create the nearby context object.
const nearbyCtx = makeExpr({
type: ctx.getAttribute('type'),
line,
filename,
...nearbyIdentifiers.reduce((acc, id) => ({...acc, [id]: true }), {}),
});
// Replace the context element with the nearby context element.
const replaceCtx = selectAst(`.//Identifier[@name="ctx"]`, inspect);
replaceCtx.replaceWith(nearbyCtx);
// Insert the inspection code into the parent statement body.
const parent = selectAst('./parent::*/*', ctx);
Array.from(inspect.childNodes).forEach((n) => {
if (n.setAttribute) {
n.setAttribute('parent-attr', 'body');
}
parent.insertBefore(n, ctx);
});
}
/**
* Transpiles the code snippet and inserts inspection code.
*
* @param {string} code - The code snippet.
* @param {string} filename - The filename of the code snippet.
* @returns {string} The transpiled code.
*/
function transpileInspect(code, filename) {
return transpile([['/Statements', insertInspect.bind(null, filename, code)]], code);
}
module.exports = {
inspectCallback,
inspectTemplate,
transpileInspect,
};
This code defines a function transpileInspect
that aims to modify JavaScript code by inserting debugging information into it.
Here's a breakdown:
Imports:
importer
: A custom module used to import other functions.transpile
: Function to transpile code (likely JavaScript).selectAst
: Function to select parts of an Abstract Syntax Tree (AST).htmlToTree
: Function to convert HTML to an AST.Constants:
STATEMENTS
: XPath expression to select statement nodes in an AST.NEAR_IDENTIFIERS
: XPath expression to select identifiers (variable names) near a given node.Helper Functions:
inspectCallback
: Logs the context of a given AST node to the console.inspectTemplate
: A template function that calls inspectCallback
with the provided context.insertInspect
Function:
inspect
using the inspectTemplate
.selectAst
and NEAR_IDENTIFIERS
.nearbyCtx
that includes the nearby identifiers, context information, and the line number.nearbyCtx
.inspect
expression into the parent statement of the context node.transpileInspect
Function (Incomplete):
insertInspect
logic at appropriate points. The code snippet provided is incomplete.In essence, this code provides a mechanism to instrument JavaScript code with debugging information by inserting calls to a logging function at specific points within the code. The insertInspect
function handles the logic of finding the relevant context and constructing the debugging information.