Skip to content

Commit

Permalink
Merge pull request #28 from BenoitZugmeyer/char-position
Browse files Browse the repository at this point in the history
Char position
  • Loading branch information
BenoitZugmeyer authored Sep 17, 2024
2 parents 4e8593d + 90fef71 commit 997e641
Show file tree
Hide file tree
Showing 13 changed files with 205 additions and 65 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# in-situ

`in-situ` is a simple CLI application taking a JavaScript file URL and a line/column position inside
`in-situ` is a simple CLI application taking a JavaScript file URL and a line/column location inside
it. It will download the JavaScript file, beautify it, and print the context around the given
position.
location.

## Installation

Expand All @@ -17,10 +17,12 @@ Note: instead of installing it, you can use `npx in-situ` to run it directly.
## Usage

```
Usage: in-situ [options] <URL:LINE:COLUMN>
Usage: in-situ [options] <LOCATION>
Download, beautify and print lines from a minified JavaScript source
The <LOCATION> argument is an URL or a file path to a JavaScript bundle, followed by a location formatted as ':line:column' or ':osition'. Example: 'https://example.com/bundle.js:10:20' or './bundle.js:123'.
Options:
-A, --after-context <num> print <num> lines of trailing context after the selected line
-B, --before-context <num> print <num> lines of leading context before the selected line
Expand Down
12 changes: 6 additions & 6 deletions src/applyBeautify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,18 @@ export default async function applyBeautify(
}

const map = new TraceMap(uglifyResult.map as string);
const position = generatedPositionFor(map, {
line: source.position.line,
column: source.position.column,
const location = generatedPositionFor(map, {
line: source.location.line,
column: source.location.column,
source: map.sources[0]!,
});
if (position.line === null || position.column === null) {
throw new CLIError("Failed to map position using beautify");
if (location.line === null || location.column === null) {
throw new CLIError("Failed to map location using beautify");
}
return {
type: "resolved",
fileName: undefined,
content: uglifyResult.code!,
position,
location,
};
}
12 changes: 6 additions & 6 deletions src/applySourceMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ import log from "./log.ts";
import type { ApplyResult, InputSource } from "./types.ts";

export default async function applySourceMap({
position,
location,
readResult,
}: InputSource): Promise<ApplyResult> {
const sourceMapContent = await readSourceMap(readResult);
if (!sourceMapContent) return { type: "unresolved" };

const map = new TraceMap(sourceMapContent);

const mappedPosition = originalPositionFor(map, position);
if (!mappedPosition.source) {
log.error("Failed to resolve the position with source map");
const mappedLocation = originalPositionFor(map, location);
if (!mappedLocation.source) {
log.error("Failed to resolve the location with source map");
return { type: "unresolved" };
}

const fileName = mappedPosition.source;
const fileName = mappedLocation.source;
const content = sourceContentFor(map, fileName);
if (!content) {
log.error("Source map doesn't include the source content");
Expand All @@ -36,7 +36,7 @@ export default async function applySourceMap({
return {
type: "resolved",
content,
position: mappedPosition,
location: mappedLocation,
fileName,
};
}
13 changes: 9 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import printContext from "./printContext.ts";
import log from "./log.ts";
import read from "./read.ts";
import type { ApplyResult, Configuration } from "./types.ts";
import resolveGridLocation from "./resolveGridLocation.ts";

main().catch((e) => {
if (e instanceof CLIError) {
Expand Down Expand Up @@ -43,10 +44,12 @@ async function main() {
export function commandHelp() {
const pkg = getPackageInfos();
let message = `\
Usage: ${pkg.name} [options] <URL:LINE:COLUMN>
Usage: ${pkg.name} [options] <LOCATION>
${pkg.description}
The <LOCATION> argument is an URL or a file path to a JavaScript bundle, followed by a location formatted as ':line:column' or ':osition'. Example: 'https://example.com/bundle.js:10:20' or './bundle.js:123'.
Options:`;
for (const [name, option] of Object.entries(OPTIONS)) {
let names = "";
Expand All @@ -69,7 +72,7 @@ function commanVersion() {
async function commandContext({
debug,
sourceURL,
position,
location,
beforeContext,
afterContext,
useSourceMap,
Expand All @@ -84,15 +87,17 @@ async function commandContext({
throw new CLIError(`Failed to fetch source code: ${e}`);
}

location = resolveGridLocation(location, readResult.content);

let applyResult: ApplyResult = {
type: "unresolved",
};
if (useSourceMap) {
applyResult = await applySourceMap({ readResult, position });
applyResult = await applySourceMap({ readResult, location });
}
if (applyResult.type !== "resolved") {
// TODO: use filename from sourcemaps? is that an actual use case?
applyResult = await applyBeautify({ readResult, position });
applyResult = await applyBeautify({ readResult, location });
}
if (applyResult.type !== "resolved") {
throw new CLIError("Failed to apply source map or beautify");
Expand Down
53 changes: 41 additions & 12 deletions src/parseArguments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe("parseArguments", () => {
t.assert.throws(
() => parseArguments([]),
new CLIError(
"Missing positional argument URL:LINE:COLUMN. Use --help for documentation.",
"Missing positional argument LOCATION. Use --help for documentation.",
),
);
});
Expand Down Expand Up @@ -44,24 +44,53 @@ describe("parseArguments", () => {
configuration: {
debug: false,
sourceURL: "https://foo.com",
position: { line: 1, column: 1 },
location: { line: 1, column: 1 },
useSourceMap: true,
beforeContext: 5,
afterContext: 5,
},
});
});

test("URL and position", (t: TestContext) => {
const parsedArguments = parseArguments([`https://foo.com:42:12`]);
t.assert.strictEqual(parsedArguments.command, "context");
t.assert.strictEqual(
parsedArguments.configuration.sourceURL,
"https://foo.com",
);
t.assert.deepStrictEqual(parsedArguments.configuration.position, {
line: 42,
column: 12,
describe("location", () => {
test("URL:LINE:COLUMN", (t: TestContext) => {
const parsedArguments = parseArguments([`https://foo.com:42:12`]);
t.assert.strictEqual(parsedArguments.command, "context");
t.assert.strictEqual(
parsedArguments.configuration.sourceURL,
"https://foo.com",
);
t.assert.deepStrictEqual(parsedArguments.configuration.location, {
line: 42,
column: 12,
});
});
test("URL:POSITION", (t: TestContext) => {
const parsedArguments = parseArguments([`https://foo.com:42`]);
t.assert.strictEqual(parsedArguments.command, "context");
t.assert.strictEqual(
parsedArguments.configuration.sourceURL,
"https://foo.com",
);
t.assert.deepStrictEqual(parsedArguments.configuration.location, {
position: 42,
});
});
test("invalid location", (t: TestContext) => {
t.assert.throws(
() => parseArguments([`https://foo.com:42:`]),
new CLIError(
"Invalid positional argument https://foo.com:42:. Use --help for documentation.",
),
);
});
test("missing location", (t: TestContext) => {
t.assert.throws(
() => parseArguments([`https://foo.com`]),
new CLIError(
"Invalid positional argument https://foo.com. Use --help for documentation.",
),
);
});
});

Expand Down
25 changes: 8 additions & 17 deletions src/parseArguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,38 +89,29 @@ export default function parseArguments(args: string[]): ParseArgumentsResult {
const arg = positionals[0];
if (!arg) {
throw new CLIError(
"Missing positional argument URL:LINE:COLUMN. Use --help for documentation.",
"Missing positional argument LOCATION. Use --help for documentation.",
);
}

const matches = /^(.*):(\d+):(\d+)$/.exec(arg);
const matches = /^(.*?):(\d+)(?::(\d+))?$/.exec(arg);
if (!matches) {
throw new CLIError(
`Invalid positional argument ${arg}. Use --help for documentation.`,
);
}

const [, sourceURL, line, column] = matches;

const beforeContext = parseInteger(
values["before-context"] !== undefined
? values["before-context"]
: values.context!,
);
const afterContext = parseInteger(
values["after-context"] !== undefined
? values["after-context"]
: values.context!,
);
const [, sourceURL, lineOrPosition, column] = matches;

return {
command: "context",
configuration: {
debug: values.debug!,
sourceURL,
position: { line: Number(line), column: Number(column) },
beforeContext,
afterContext,
location: column
? { line: Number(lineOrPosition), column: Number(column) }
: { position: Number(lineOrPosition) },
beforeContext: parseInteger(values["before-context"] ?? values.context),
afterContext: parseInteger(values["after-context"] ?? values.context),
useSourceMap: !values["no-source-map"]!,
},
};
Expand Down
18 changes: 9 additions & 9 deletions src/printContext.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ test("prints simple source context", (t: TestContext) => {
type: "resolved",
fileName: undefined,
content: "a",
position: { line: 1, column: 0 },
location: { line: 1, column: 0 },
}),
"a\n^",
);
Expand All @@ -22,7 +22,7 @@ test("context limit", (t: TestContext) => {
type: "resolved",
fileName: undefined,
content: "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm",
position: { line: 7, column: 0 },
location: { line: 7, column: 0 },
}),
"b\nc\nd\ne\nf\ng\n^\nh\ni\nj\nk\nl",
);
Expand All @@ -32,7 +32,7 @@ test("context limit", (t: TestContext) => {
type: "resolved",
fileName: undefined,
content: "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm",
position: { line: 7, column: 0 },
location: { line: 7, column: 0 },
},
{ beforeContext: 0 },
),
Expand All @@ -44,7 +44,7 @@ test("context limit", (t: TestContext) => {
type: "resolved",
fileName: undefined,
content: "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm",
position: { line: 7, column: 0 },
location: { line: 7, column: 0 },
},
{ afterContext: 0 },
),
Expand All @@ -56,7 +56,7 @@ test("context limit", (t: TestContext) => {
type: "resolved",
fileName: undefined,
content: "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm",
position: { line: 7, column: 0 },
location: { line: 7, column: 0 },
},
{ beforeContext: 0, afterContext: 0 },
),
Expand All @@ -70,7 +70,7 @@ test("lastColumn", (t: TestContext) => {
type: "resolved",
fileName: undefined,
content: "abcdefghi",
position: { line: 1, column: 1, lastColumn: 4 },
location: { line: 1, column: 1, lastColumn: 4 },
}),
"abcdefghi\n ^^^",
);
Expand All @@ -82,7 +82,7 @@ test("tab", (t: TestContext) => {
type: "resolved",
fileName: undefined,
content: "\t\tabcdefghi",
position: { line: 1, column: 2 },
location: { line: 1, column: 2 },
}),
"\t\tabcdefghi\n ^",
);
Expand All @@ -94,7 +94,7 @@ test("wide character before cursor", (t: TestContext) => {
type: "resolved",
fileName: undefined,
content: "杨abcdefghi",
position: { line: 1, column: 1 },
location: { line: 1, column: 1 },
}),
"杨abcdefghi\n ^",
);
Expand All @@ -106,7 +106,7 @@ test("wide character at cursor", (t: TestContext) => {
type: "resolved",
fileName: undefined,
content: "abc杨defghi",
position: { line: 1, column: 3 },
location: { line: 1, column: 3 },
}),
"abc杨defghi\n ^^",
);
Expand Down
2 changes: 1 addition & 1 deletion src/printContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function formatContext(
{
content,
fileName,
position: { line, column, lastColumn },
location: { line, column, lastColumn },
}: ResolvedApplyResult,
{
shouldHighlight = false,
Expand Down
Loading

0 comments on commit 997e641

Please sign in to comment.