Skip to content

Allow to override the app:// prefix in RewriteFrames integration #3406

@matbour

Description

@matbour

Currently, when you are using the RewriteFrames integration, in my case to handle TypeScript source maps, you end you with a filename starting with app://. Here is an example:

app:// prefix demonstration

I would like to change this prefix to something else, like : project://. After some research, I found that the app:// is hardcoded here, in the default iteratee:

const base = this._root ? relative(this._root, filename) : basename(filename);
frame.filename = `app:///${base}`;
}

Of course, it's possible to pass a custom iteratee into the RewriteFrames constructor, like so:

new RewriteFrames({
  root: global.__rootdir__,
  iteratee: function (frame: StackFrame) {
    if (!frame.filename) {
      return frame;
    }
    // Check if the frame filename begins with `/` or a Windows-style prefix such as `C:\`
    const isWindowsFrame = /^[A-Z]:\\/.test(frame.filename);
    const startsWithSlash = /^\//.test(frame.filename);
    if (isWindowsFrame || startsWithSlash) {
      const filename = isWindowsFrame
        ? frame.filename
            .replace(/^[A-Z]:/, '') // remove Windows-style prefix
            .replace(/\\/g, '/') // replace all `\\` instances with `/`
        : frame.filename;
      const base = global.__rootdir__ ? relative(global.__rootdir__, filename) : basename(filename);
      frame.filename = `myprefix:///${base}`; // <--- custom prefix
    }
    return frame;
  },
});

but it means that the developer has to update the callback after each @sentry/inetgration upgrade, if he wants to keep the library default behavior.

I think that it would be convenient to add a prefix key to the RewriteFrames constructor and a _prefix property set to app:// by default. The patched version could look like this:

export class RewriteFrames implements Integration {
  private readonly _prefix: string = 'app://';

  // Rest of rewriteframes.ts

  public constructor(options: { root?: string; iteratee?: StackFrameIteratee } = {}) {
    if (options.root) {
      this._root = options.root;
    }
    if (options.iteratee) {
      this._iteratee = options.iteratee;
    }
    if (options.prefix) {
      this._prefix = options.prefix;
    }
  }

  // Rest of rewriteframes.ts

  private readonly _iteratee: StackFrameIteratee = (frame: StackFrame) => {
    if (!frame.filename) {
      return frame;
    }
    // Check if the frame filename begins with `/` or a Windows-style prefix such as `C:\`
    const isWindowsFrame = /^[A-Z]:\\/.test(frame.filename);
    const startsWithSlash = /^\//.test(frame.filename);
    if (isWindowsFrame || startsWithSlash) {
      const filename = isWindowsFrame
        ? frame.filename
            .replace(/^[A-Z]:/, '') // remove Windows-style prefix
            .replace(/\\/g, '/') // replace all `\\` instances with `/`
        : frame.filename;
      const base = this._root ? relative(this._root, filename) : basename(filename);
      frame.filename = `${this._prefix}:///${base}`;
    }
    return frame;
  };

  // Rest of rewriteframes.ts
}

If this feature is coherent with the project, I would be happy to submit a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions