In version 15 we've migrated to ES modules and dropped support for CommonJS. You still can use CommonJS if you want with the help of bundlers like esbuild, Webpack or Rollup.
There's a few breaking changes in this release: Tonic.version, Tonic.SPREAD, Tonic.ESC, Tonic.AsyncFunctionGenerator, AsyncFunction and Tonic.MAP are getters now. This change shouldn't affect most of developers.
Bundle size was reduced as we don't bundle package.json anymore and set the version on a build step.
We enabled escaping on strings returned by render()
If your render() method returns a 'string' or uses a plain
template tag like "return `<div></div>`" then this will
be escaped and rendered as text content.
You will have to update your render() {} methods to use
the this.html method for rendering.
We've renamed the method Tonic.raw => Tonic.unsafeRawString.
This makes it clear that the method is unsafe and exposes your
application to security issues.
We strongly recommend replacing all uses of Tonic.raw with
this.html instead. For the use case of repeating templates
you can pass an array of TonicTemplate objects returned
from this.html into another this.html.
If you truly do need an unsafeRawString that is assigned as
raw HTML we recommend that you use Tonic.escape() to build
that string and review it very carefully, add a comment explaining
it too.
We renamed a field from isTonicRaw to isTonicTemplate on
the TonicRaw / TonicTemplate class. This is unlikely to break
your app.
We made a breaking change where the id attribute is mandatory
for any tonic components that access this.id or this.state.
Previously id attribute was not mandatory but the component
was in a half broken state since the this.state was not
stored upon re-render.
Now tonic warns you that the component is stateful and that the
id is mandatory as its the primary key by which we store the
state and get the previous state upon re-render.
Basically this.state is semi-broken without an id attribute.
You will get exceptions and you will have to refactor; for example
- <app-search-filter-combo>
+ <app-search-filter-combo id="app-search-filter-combo">
render () {
- return `
+ return this.html`
<header>New Folder</header>
<main>
<tonic-input
+ id="new-folder"
name="new-folder"
label="Name"Basically just add the id attributes.
The implementation of HTML escaping changed between v10 and v11
of @socketsupply/tonic.
The main takeaway is that v10 had a potential XSS injection as
we only escaped strings that exist on this.props.someKey,
we've now changed the implementation to sanitize all strings
that are passed into this.html`<div>${someStr}<div>`;.
This breaks some existing patterns that are common in applications like the following
class Comp extends Tonic {
renderLabel () {
return `<label>${this.props.label}</label>`
}
render () {
return this.html`
<div>
<header>Some header</header>
${this.renderLabel()}
</div>
`
}
}In this case the HTML returned from this.renderLabel() is now
being escaped which is probably not what you meant.
You will have to patch the code to use this.html for the
implementation of renderLabel() like
renderLabel () {
return this.html`<label>${this.props.label}</label>`
}Or to call Tonic.raw() manually like
render () {
return this.html`
<div>
<header>Some header</header>
${Tonic.raw(this.renderLabel())}
</div>
`
}If you want to quickly find all occurences of the above patterns you can run the following git grep on your codebase.
git grep -C10 '${' | grep ')}'The fix is to add this.html calls in various places.
We have updated @socketsupply/components and you will have to
update to version 7.4.0 as well
npm install @socketsupply/components@^7.4.0 -ESThere are other situations in which the increased escaping from
Tonic.escape() like for example escaping the " character if
you dynamically generate optional attributes
Like:
class Icon extends Tonic {
render () {
return this.html`<svg ${tabAttr} styles="icon">
<use
width="${size}"
${fill ? `fill="${fill}" color="${fill}"` : ''}
height="${size}">
</svg>`
}
}In the above example we do fill ? `fill="${fill}"` : '' which
leads to " getting escaped to " and leads to the value
of use.getAttribute('fill') to be "${fill}" instead of ${fill}
Here is a regex you can use to find the one-liner use cases.
git grep -E '`(.+)="'
When building dynamic attribute lists Tonic has a spread feature
in the this.html() function you can use instead to make it easier.
For example, you can refactor the above Icon class to:
class Icon extends Tonic {
render () {
return this.html`<svg ${tabAttr} styles="icon">
<use ...${{
width: size,
fill,
color: fill,
height: size
}}>
</svg>`
}
}Here we use ...${{ ... }} to expand an object of attributes to
attribute key value pairs in the HTML. You can also pull out the attrs
into a reference if you prefer, like:
class Icon extends Tonic {
render () {
const useAttrs = {
width: size,
fill,
color: fill,
height: size
}
return this.html`<svg ${tabAttr} styles="icon">
<use ...${useAttrs}>
</svg>`
}
}