Skip to content

Commit 537fbef

Browse files
authored
Fix column offsets for source mapped code (#197)
1 parent 9fb149d commit 537fbef

7 files changed

Lines changed: 85 additions & 18 deletions

File tree

.changeset/seven-fireants-shop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'pleasantest': patch
3+
---
4+
5+
Fix column offsets when esbuild is disabled

src/index.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -371,17 +371,18 @@ const createTab = async ({
371371

372372
const { SourceMapConsumer } = await import('source-map');
373373
const consumer = await new SourceMapConsumer(map as any);
374-
const sourceLocation = consumer.originalPositionFor({ line, column });
374+
const sourceLocation = consumer.originalPositionFor({
375+
line,
376+
column: column - 1, // Source-map uses zero-based column numbers
377+
});
375378
consumer.destroy();
376-
if (sourceLocation.line === null || sourceLocation.column === null)
377-
return stackItem.raw;
378-
const mappedColumn = sourceLocation.column + 1;
379-
const mappedLine = sourceLocation.line;
380-
const mappedPath = sourceLocation.source || url.pathname;
381379
return printStackLine(
382-
mappedPath,
383-
mappedLine,
384-
mappedColumn,
380+
join(process.cwd(), url.pathname),
381+
sourceLocation.line ?? line,
382+
sourceLocation.column === null
383+
? column
384+
: // Convert back from zero-based column to 1-based
385+
sourceLocation.column + 1,
385386
stackItem.name,
386387
);
387388
});

src/module-server/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export const createModuleServer = async ({
6565
const requestCache = new Map<string, SourceDescription>();
6666
const middleware: polka.Middleware[] = [
6767
indexHTMLMiddleware,
68-
jsMiddleware({ root, plugins, requestCache }),
68+
await jsMiddleware({ root, plugins, requestCache }),
6969
cssMiddleware({ root }),
7070
staticMiddleware({ root }),
7171
];

src/module-server/middleware/js.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ const getResolveCacheKey = (spec: string, from: string) =>
2929

3030
// Minimal version of https://github.com/preactjs/wmr/blob/main/packages/wmr/src/wmr-middleware.js
3131

32-
export const jsMiddleware = ({
32+
export const jsMiddleware = async ({
3333
root,
3434
plugins,
3535
requestCache,
36-
}: JSMiddlewareOpts): polka.Middleware => {
36+
}: JSMiddlewareOpts): Promise<polka.Middleware> => {
3737
interface ResolveCacheEntry {
3838
buildId: number;
3939
resolved: PartialResolvedId;
@@ -58,7 +58,8 @@ export const jsMiddleware = ({
5858

5959
const rollupPlugins = createPluginContainer(plugins);
6060

61-
rollupPlugins.buildStart();
61+
await rollupPlugins.options();
62+
await rollupPlugins.buildStart();
6263

6364
return async (req, res, next) => {
6465
const buildId =

src/module-server/plugins/esbuild-plugin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const esbuildPlugin = (
2424
...esbuildOptions,
2525
})
2626
.catch((error) => {
27+
if (!('errors' in error)) throw error;
2728
const err = error.errors[0];
2829
this.error(err.text, {
2930
line: err.location.line,

src/module-server/rollup-plugin-container.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
- relative resolution fixed to handle '.' for ctx.resolveId
3737
- Source map handling added to transform hook
3838
- Error handling (with code frame, using source maps) added to transform hook
39+
- Stubbed out options hook was added
3940
*/
4041

4142
import { resolve, dirname } from 'path';
@@ -212,11 +213,9 @@ export const createPluginContainer = (plugins: Plugin[]) => {
212213
) {
213214
let code = originalCode;
214215
// TODO: if any of the transforms is missing sourcemaps, then there should be no source maps emitted
215-
const sourceMaps: (DecodedSourceMap | RawSourceMap)[] = [];
216-
if (inputMap)
217-
sourceMaps.push(
218-
typeof inputMap === 'string' ? JSON.parse(inputMap) : inputMap,
219-
);
216+
const sourceMaps: (DecodedSourceMap | RawSourceMap)[] = inputMap
217+
? [typeof inputMap === 'string' ? JSON.parse(inputMap) : inputMap]
218+
: [];
220219
for (plugin of plugins) {
221220
if (!plugin.transform) continue;
222221
try {
@@ -273,6 +272,14 @@ export const createPluginContainer = (plugins: Plugin[]) => {
273272
};
274273
},
275274

275+
async options() {
276+
for (plugin of plugins) {
277+
// Since we don't have "input options", we just pass {}
278+
// This hook must be called for @rollup/plugin-babel
279+
await plugin.options?.call(ctx as any, {});
280+
}
281+
},
282+
276283
async load(id: string): Promise<LoadResult> {
277284
for (plugin of plugins) {
278285
if (!plugin.load) continue;

tests/utils/runJS.test.tsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { PleasantestContext, PleasantestUtils } from 'pleasantest';
33
import { printErrorFrames } from '../test-utils';
44
import vuePlugin from 'rollup-plugin-vue';
55
import aliasPlugin from '@rollup/plugin-alias';
6+
import babel from '@rollup/plugin-babel';
67
import ansiRegex from 'ansi-regex';
78

89
const createHeading = async ({
@@ -192,6 +193,23 @@ test(
192193
),
193194
);
194195

196+
test(
197+
'Line/column offsets for source-mapped runtime error is correct even with esbuild disabled',
198+
withBrowser({ moduleServer: { esbuild: false } }, async ({ utils }) => {
199+
const error = await utils
200+
.runJS('console.log(nothing)')
201+
.catch((error) => error);
202+
expect(await printErrorFrames(error)).toMatchInlineSnapshot(`
203+
"ReferenceError: nothing is not defined
204+
-------------------------------------------------------
205+
tests/utils/runJS.test.tsx
206+
207+
.runJS('console.log(nothing)')
208+
^"
209+
`);
210+
}),
211+
);
212+
195213
test(
196214
'allows importing .tsx file, and errors from imported file are source mapped',
197215
withBrowser(async ({ utils, page }) => {
@@ -467,3 +485,37 @@ test(
467485
},
468486
),
469487
);
488+
489+
test(
490+
'@rollup/plugin-babel works',
491+
withBrowser(
492+
{
493+
moduleServer: {
494+
esbuild: false,
495+
plugins: [
496+
babel({
497+
extensions: ['.js', '.ts', '.tsx', '.mjs'],
498+
babelHelpers: 'bundled',
499+
presets: ['@babel/preset-typescript'],
500+
}),
501+
],
502+
},
503+
},
504+
async ({ utils }) => {
505+
await utils.runJS("const foo: string = 'hello'");
506+
507+
// Check that source map from babel works correctly
508+
const error = await utils
509+
.runJS('console.log(nothing)')
510+
.catch((error) => error);
511+
expect(await printErrorFrames(error)).toMatchInlineSnapshot(`
512+
"ReferenceError: nothing is not defined
513+
-------------------------------------------------------
514+
tests/utils/runJS.test.tsx
515+
516+
.runJS('console.log(nothing)')
517+
^"
518+
`);
519+
},
520+
),
521+
);

0 commit comments

Comments
 (0)