Ember.js: Where is `Ember.Handlebars.Utils.escapeExpression` in the @ember module system?

Created on 13 Jul 2018  ·  18Comments  ·  Source: emberjs/ember.js

Similar to the last comment in #9874, how does one access Ember.Handlebars.Utils.escapeExpression in a modern way, without importing all of ember? I need to escape HTML in my app (because I am decorating some user-input with markup) before marking it as htmlSafe() and can't find any way of doing so without writing my own escaping function or importing some kind of addon.

Documentation

Most helpful comment

We need this.

Right now you need to either/or:

@rwjblue said:

Closing this for now. If we need to add these API's to HTMLBars in the future, it will be simple.

What about now? Something possible that I should know about?

All 18 comments

@peabnuts123 Have you found an answer yet? Did asking this on the ember community slack or forum help you get any answers to your question?

I found some dialog on this post, https://medium.com/@bradleyscollins/how-does-one-access-ember-handlebars-utils-escapeexpression-ebbe33093186

No, I never did find a satisfactory answer, though I did not post in the Slack or Forum.

I currently have the following in my component

import Ember from 'ember';
const htmlEscape = Ember.Handlebars.Utils.escapeExpression;

so that in the future I may import htmlEscape properly and remove these lines

Yea, nice links - I had found the same thread in my travels. I think the idea for the future is to import it from Handlebars directly e.g.

import { htmlEscape } from "Handlebars";

or similar. This has the unfortunate downside of relying on Handlebars to export these functionalities themselves via the module API, which I think we're just kind of "waiting on" at this point

Trying to upgrade an addon to a more recent version of Ember I stumbled upon this issue.

Since we cannot rely on global Ember variable and Handlebars is not a valid module, how are we supposed to migrate this particular piece of code?

import Ember from 'ember';
const htmlEscape = Ember.Handlebars.Utils.escapeExpression;

We need this.

Right now you need to either/or:

@rwjblue said:

Closing this for now. If we need to add these API's to HTMLBars in the future, it will be simple.

What about now? Something possible that I should know about?

Is there a resolution on this? It feels like a quintessential util for people who need to process text.

@rwjblue Any thoughts? :)

Folks please see the comment that closed the related PR:

After discussing with the core team today- we've decided to not move forward with this PR because Ember doesn't use it internally, and hasn't for quite some time now- it was always an artifact from Handlebars itself.

The work for the module RFC removed non-Ember re-exports, and authors were advised to import what they need directly from any particular non-Ember module (e.g., rsvp or jQuery). This is still our position.

If you feel like you still have a use case, please submit an RFC.

I am happy with this position / this has been my understanding throughout this issue.

If people find this issue in the future still wanting to access this function - you will need access it from the Handlebars package directly. However, I think they still have not structured their package as a module i.e. you may need to import all of Handlebars and then use Handlebars.Utils.escapeExpression.

import Handlebars from 'handlebars';
import { htmlSafe } from '@ember/template';

let userInput = getUserInput(); // Something unsafe, e.g. user-controlled text
let escapedInput = Handlebars.Utils.escapeExpression(userInput); // Escape any HTML in this string so that it will render as <, & etc.
let alteredHtml = escapedInput.replace(/hello/g, '<span class="highlight">hello</span>'); // Add some HTML to the text
let safeHtml = htmlSafe(alteredHtml);

// safeHtml is now safe to render in the DOM

@MelSumner

Ember doesn't use it internally

So I understand this issue is 100% upstream. Out of curiosity, is there something similar'ish that can be done using only Ember utils?

@Redsandro

Something like this should work:

// your-app/utils/misc.js

import { isHTMLSafe } from '@ember/template';

const ESCAPE = {
  '&': '&amp;',
  '<': '&lt;',
  '>': '&gt;',
  '"': '&quot;',
  "'": '&#x27;',
  '`': '&#x60;',
  '=': '&#x3D;',
};

const BAD_CHARS = /[&<>"'`=]/g;
const POSSIBLE = /[&<>"'`=]/;

let escapeChar = function(chr) {
  return ESCAPE[chr];
};

// borrowed from Handlebars.utils.escapeexpression
// https://github.com/emberjs/ember.js/pull/18791
let escapeExpression = function(string) {
  if (typeof string !== 'string') {
    // don't escape SafeStrings, since they're already safe
    if (string && isHTMLSafe(string)) {
      return string.toHTML(); // private API, since SafeString isn't public
    } else if (string === null) {
      return '';
    } else if (!string) {
      return String(string);
    }

    // Force a string conversion as this will be done by the append regardless and
    // the regex test will do this transparently behind the scenes, causing issues if
    // an object's to string has escaped characters in it.
    string = String(string);
  }

  if (!POSSIBLE.test(string)) {
    return string;
  }

  return string.replace(BAD_CHARS, escapeChar);
};

export { escapeExpression as escape };

What about using a dedicated package for this?

  • sanitize-html supports escaping disallowed tags

  • dompurify (very similar to sanitize-html) allows you to keep some tags or attributes, or strip out undesired html while leaving the content in place, among many other things. I use dompurify in emberobserver to only allow certain tags to be rendered in addon READMEs and to ensure all links have the correct rel applied.

@kategengler Those are also good suggestions, but I don't think they interop with SafeString in Ember. Not needed for all use-cases though.

SafeString is a string marked as safe by the user, it doesn’t do any escaping itself, and it doesn’t have any expectations of what has been escaped, if anything. You can use any escaping library you like. As @MelSumner said, Ember doesn’t use any escaping functions internally anymore.

@pzuraq Yeah, but wouldn't the escaping library need to know if a string is a safe-string or not? To know whether it should, or should not, escape them?

And it seems to me like the SafeString class itself, plus the method toHTML are private.

This isn't a big problem + I don't mind using private APIs (or perhaps them not being private is my misunderstanding), but it seems to me like making them public would be helpful in the aforementioned regard.

@sandstrom I don't think that the htmlSafe API is meant to be unwrapped ever, and definitely not mutated. My understanding was that it was meant to only be used at the _edge_ of your state, so once you're actually ready to put a string into the DOM somewhere.

I'll double check to see about that. FWIW, there have also been discussions of having a new htmlSafe API, one that has some other benefits (for instance, you probably want to be able to do (eq '' someHtmlSafeString) type checks), so maybe the ability to unwrap and rewrap strings could be added in the future if I'm correct about the current API.

@pzuraq Alright, I'm happy with that! 😄

This isn't by any means a big issue in our application, so it's all good.

Have a great weekend & thanks for all the awsome work you (and others) are doing on Ember! 🏅

(for reference, here is my PR about making escapeExpression public https://github.com/emberjs/ember.js/pull/18791)

Was this page helpful?
0 / 5 - 0 ratings