Skip to content

Commit

Permalink
runtime: add a position mapping function
Browse files Browse the repository at this point in the history
  • Loading branch information
josephjclark committed Dec 18, 2024
1 parent 7293c22 commit fba35fa
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 21 deletions.
6 changes: 5 additions & 1 deletion packages/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@
"author": "Open Function Group <[email protected]>",
"license": "ISC",
"devDependencies": {
"@openfn/compiler": "workspace:^",
"@openfn/language-common": "2.0.0-rc3",
"@openfn/lexicon": "workspace:^",
"@types/mock-fs": "^4.13.1",
"@types/node": "^18.15.13",
"@types/semver": "^7.5.0",
"ava": "5.3.1",
"mock-fs": "^5.4.1",
"recast": "^0.21.5",
"ts-node": "^10.9.1",
"tslib": "^2.4.0",
"tsup": "^7.2.0",
"typescript": "^5.1.6"
Expand All @@ -45,6 +48,7 @@
"dependencies": {
"@openfn/logger": "workspace:*",
"fast-safe-stringify": "^2.1.1",
"semver": "^7.5.4"
"semver": "^7.5.4",
"source-map": "^0.7.4"
}
}
11 changes: 4 additions & 7 deletions packages/runtime/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,26 @@ import type { LinkerOptions } from './modules/linker';
import executePlan from './execute/plan';
import { defaultState, parseRegex, clone } from './util/index';

// TODO we should be able to get a proper typing for this from somewherewhere
type SourceMap = any;

export type Options = {
logger?: Logger;
jobLogger?: Logger;

// Treat state as immutable (likely to break in legacy jobs)
immutableState?: boolean;

// TODO currently unused
// Ensure that all incoming jobs are sandboxed / loaded as text
// In practice this means throwing if someone tries to pass live js
forceSandbox?: boolean;

linker?: LinkerOptions;

callbacks?: ExecutionCallbacks;

// inject globals into the environment
// TODO leaving this here for now, but maybe its actually on the xplan?
globals?: any;

statePropsToRemove?: string[];

defaultRunTimeoutMs?: number;
sourceMap?: SourceMap;
};

type RawOptions = Omit<Options, 'linker'> & {
Expand Down
18 changes: 18 additions & 0 deletions packages/runtime/src/util/get-mapped-position.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// TODO rename the file to source mapping
// 1 function to get the position
// 1 function to extract the line from the source

import { SourceMapConsumer } from 'source-map';

const getMappedPosition = async (map, line, column) => {
const smc = await new SourceMapConsumer(map);
const pos = smc.originalPositionFor({
line,
column,
});

//return { line, col, src };
return pos;
};

export default getMappedPosition;
10 changes: 8 additions & 2 deletions packages/runtime/test/errors.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import test from 'ava';
import path from 'node:path';
import type { WorkflowOptions } from '@openfn/lexicon';
import compile from '@openfn/compiler';

import run from '../src/runtime';

Expand Down Expand Up @@ -47,13 +48,18 @@ test('crash on runtime error with SyntaxError', async (t) => {
t.is(error.message, 'SyntaxError: Invalid or unexpected token');
});

test('crash on runtime error with ReferenceError', async (t) => {
test.only('crash on runtime error with ReferenceError', async (t) => {
const expression = 'export default [(s) => x]';

// compile the code so we get a source map
const { code, map } = compile(expression, { name: 'src' });
console.log({ code, map });

let error: any;
try {
await run(expression);
await run(code, { map });
} catch (e) {
console.log(e);
error = e;
}
t.log(error);
Expand Down
43 changes: 43 additions & 0 deletions packages/runtime/test/util/get-mapped-position.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import test from 'ava';
import recast from 'recast';
import getMappedPosition from '../../src/util/get-mapped-position';

const b = recast.types.builders;

// compile an expression into a function
const compile = (src: string) => {
const ast = recast.parse(src, {
sourceFileName: 'src.js',
});

// take the expression and wrap it in a function declaration
const [{ expression }] = ast.program.body;
const fn = b.functionDeclaration(
b.identifier('fn'),
[],
b.blockStatement([b.returnStatement(expression)])
);

ast.program.body.push(fn);

return recast.print(fn, {
sourceMapName: 'src.map.js',
});
};

test('should return a sourcemapped position', async (t) => {
// Compile this simple expression into a function
const { code, map } = compile('x + 1');

// Now work out where x is
const lines = code.split('\n');
const line = 2; // line is 1 based
const column = lines[1].indexOf('x');

// now get the uncompiled position for x
const result = await getMappedPosition(map, line, column);

// now check the position
t.is(result.line, 1);
t.is(result.column, 0);
});
Loading

0 comments on commit fba35fa

Please sign in to comment.