Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 118x 118x 322x 322x 322x 322x 322x 322x 322x 305x 305x 305x 1x 1x 304x 305x 322x 322x 322x 322x 322x 322x 305x 305x 305x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 472x 472x 472x 472x 2x 2x 472x 472x 265x 265x 264x 264x 265x 472x 472x 36x 36x 36x 36x 36x 472x 472x 4x 4x 4x 4x 4x 472x 472x 472x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 94x 94x 94x 94x 94x 94x 94x 94x 159x 159x 94x 94x 31x 31x 31x 31x 31x 31x 31x 31x 31x 94x 94x 94x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 322x 13x 11x 11x 2x 5x 7x 7x 3x 3x 4x 7x 7x 2x 1x 1x 1x 1x 2x 2x 2x 7x 7x 1x 1x 7x 2x 2x 13x | /**
* @file sourceMapTrace.js
* @description Builds the source-location sidecar that `avenx trace view` uses.
*
* A trace records the *source text* of every action and computed it ran, which
* is already more than a compiled framework can offer. What it cannot know at
* runtime is where that text came from — the bundle is one concatenated script
* with no file boundaries left in it.
*
* The compiler does know, so it writes the answer beside the bundle rather
* than inside it. `bundle.trace.json` is emitted next to `bundle.js` and is not
* referenced by it: an application that never records a trace downloads
* nothing extra, and a deployment that does not want the file simply does not
* upload it.
* @module lib/compiler/sourceMapTrace
*/
import path from 'path';
import { TRACE_VERSION } from '../core/trace/schema.js';
/**
* The file name the sidecar is written under, given a bundle name.
* @param {string} outputName - The configured bundle name, e.g. `bundle`.
* @returns {string} The sidecar file name.
*/
export function sidecarFileName(outputName) {
return `${outputName}.trace.json`;
}
/**
* Finds the 1-based line a pattern first occurs on.
* @param {string} source - The file contents.
* @param {RegExp} pattern - What to look for.
* @returns {number|null} The line number, or null when absent.
*/
function lineOf(source, pattern) {
const match = pattern.exec(source);
if (!match) {
return null;
}
return source.slice(0, match.index).split('\n').length;
}
/**
* Escapes a name for use inside a regular expression.
* @param {string} name - The declared name.
* @returns {string} The escaped name.
*/
function escape(name) {
return String(name).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/**
* Collects the source locations of one component's declarations.
* @param {object} unit - What the parser extracted.
* @param {string} unit.name - The component class name.
* @param {string} unit.filePath - Absolute path to the source file.
* @param {string} unit.rootDir - The project root, for relative paths.
* @param {string} unit.content - The file contents.
* @param {object} [unit.computed] - Computed definitions by name.
* @param {object} [unit.methods] - Action bodies by name.
* @param {object} [unit.resources] - Resource definitions by name.
* @param {Set<string>} [unit.contracts] - Declared contracts.
* @returns {object} The component's sidecar entry.
*/
export function collectLocations(unit) {
const relative = path.relative(unit.rootDir, unit.filePath).split(path.sep).join('/');
const entry = { file: relative, actions: {}, computed: {}, resources: {} };
if (unit.contracts && unit.contracts.size > 0) {
entry.contracts = Array.from(unit.contracts);
}
for (const name of Object.keys(unit.methods || {})) {
const line = lineOf(unit.content, new RegExp(`<action\\s+[^>]*name\\s*=\\s*["']${escape(name)}["']`));
if (line !== null) {
entry.actions[name] = { line };
}
}
for (const name of Object.keys(unit.computed || {})) {
const line = lineOf(unit.content, new RegExp(`<computed\\s+[^>]*name\\s*=\\s*["']${escape(name)}["']`));
if (line !== null) {
entry.computed[name] = { line, expression: unit.computed[name] };
}
}
for (const name of Object.keys(unit.resources || {})) {
const line = lineOf(unit.content, new RegExp(`<resource\\s+[^>]*name\\s*=\\s*["']${escape(name)}["']`));
if (line !== null) {
entry.resources[name] = { line };
}
}
return entry;
}
/**
* Assembles the sidecar document.
* @param {Map<string, object>} components - Entries keyed by class name.
* @param {Map<string, object>} [bridges] - Bridge descriptors. The compiler keys
* these by absolute path, so the descriptor's own `name` is used for the
* sidecar rather than the map key — a trace records bridge names, not paths.
* @param {string} [rootDir] - The project root, for relative bridge paths.
* @returns {object} The sidecar, ready to serialize.
*/
export function buildSidecar(components, bridges = new Map(), rootDir = process.cwd()) {
const sidecar = {
traceVersion: TRACE_VERSION,
generatedAt: new Date().toISOString(),
components: {},
bridges: {},
};
for (const [name, entry] of components) {
sidecar.components[name] = entry;
}
for (const [key, descriptor] of bridges) {
if (!descriptor || !descriptor.filePath) {
continue;
}
const name = descriptor.name || key;
sidecar.bridges[name] = {
file: path.relative(rootDir, descriptor.filePath).split(path.sep).join('/'),
actions: descriptor.actions || [],
getters: descriptor.getters || [],
state: descriptor.stateKeys || [],
};
}
return sidecar;
}
/**
* Annotates a trace's nodes with the source locations the sidecar holds.
*
* Applied when a trace is read, not when it is recorded: a trace stays a
* record of what happened, and the mapping from that to a file and a line is a
* property of the build it came from.
* @param {object} trace - The trace to annotate. Mutated in place.
* @param {object|null} sidecar - The sidecar, or null to leave the trace alone.
* @returns {object} The same trace.
*/
export function annotateTrace(trace, sidecar) {
if (!sidecar || !trace || !Array.isArray(trace.nodes)) {
return trace;
}
for (const node of trace.nodes) {
const owner = node.component || node.bridge;
if (!owner) {
continue;
}
const component = sidecar.components && sidecar.components[owner];
if (component) {
if (node.type === 'action' && component.actions[node.name]) {
node.loc = { file: component.file, line: component.actions[node.name].line };
} else if (node.type === 'computed' && component.computed[node.name]) {
node.loc = { file: component.file, line: component.computed[node.name].line };
} else if (node.type === 'resource' && component.resources[node.name]) {
node.loc = { file: component.file, line: component.resources[node.name].line };
} else if (!node.loc) {
node.loc = { file: component.file };
}
continue;
}
const bridge = sidecar.bridges && sidecar.bridges[owner];
if (bridge) {
node.loc = { file: bridge.file };
}
}
return trace;
}
|