Research
Security News
Threat Actor Exposes Playbook for Exploiting npm to Build Blockchain-Powered Botnets
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.
Metal.js is a JavaScript library for building from simple widgets to full scale applications.
Even though it's powerful, Metal.js is very small, being only around 9kb after compressed with gzip. It's also really well tested, currently with 99% coverage of unit tests, besides having great performance.
Metal.js's main classes are Attribute and Component. Component actually extends from Attribute, thus containing all its features. The main difference between the two is that Component's extra features are related to rendering. So you could just use Attribute directly if your module doesn't do any rendering. But if your module does need rendering logic though, then Component will work better for you.
One thing that can be really useful for a developer when building a component, is to separate the rendering logic from the business logic. This can be achieved on Metal.js by modules built on top of Component, that integrate with template engines. Metal.js already provides an implementation called SoyComponent that integrates with Soy Templates, also referred to as Closure Templates.
bower install metal
. The code will be available at bower_components/metal
.With the code already available, you can use Metal.js by just importing the desired module on your js file and calling what you wish on it. For example:
import core from './bower_components/metal/src/core';
// You can now call any function from Metal.js's core module.
core.isString('Hello World');
Note that Metal.js is written in ES6 (a.k.a ECMAScript 2015), so you can also use ES6 on your code like we did on the example. Since ES6 isn't fully implemented on browsers yet though, either a polyfill or a build process is necessary before using Metal.js on a website. See the Build Tasks section for more details.
Having to supply the relative path to bower_components is not cool and, besides that, it may cause problems when a module doing that is imported later as a bower dependency of another project.
Knowing that, Metal.js allows the use of aliases to refer to bower dependencies. It basically allows importing dependencies by just using a prefix instead of the whole path to the bower folder location. Note that this will only work when using Metal.js's build tools or adding a similar logic to your build process yourself (though we provide a helper function on Metal.js's npm package).
With aliases, the previous example can be rewritten like this:
import core from 'bower:metal/src/core';
The Attribute class provides a way of defining attributes for the classes that extend it, as well as watching these attributes for value changes.
The following example is a class that extends from Attribute and defines an attribute named foo
on itself:
import Attribute from '../bower_components/metal/src/attribute/Attribute';
class MyAttributes extends Attribute {
constructor(opt_config) {
super(opt_config);
}
}
MyAttributes.ATTRS = {
foo: {
value: 'Initial value'
}
}
If you're familiar with YUI, you'll notice that this is very similar to how attributes are defined there. You basically just need to list all attributes you'll be using on the ATTRS static variable, and provide their configuration options, like initial value and validator. For a list of all valid options, take a look at Attribute's docs.
You can access or change an object's attributes in the same way you'd access or change any object property.
var obj = new MyAttributes();
console.log(obj.foo); // Prints 'Initial value'
obj.foo = 'New value';
console.log(obj.foo); // Prints 'New value'
You can also listen to attribute value changes by listening to the appropriate event.
obj.on('fooChanged', function(event) {
// event.prevVal has the previous value.
// event.newVal has the new value.
});
To see all features of the Attribute class, take a look at its unit tests.
This section will explain how to build rich widgets on Metal.js, by taking advantage of the SoyComponent class. By using SoyComponent, you'll be able to easily separate business logic from rendering logic, as it provides an integration with soy templates.
Building a widget with SoyComponent is simple, you just need to create two files: one with your soy templates, and the other with your JavaScript logic.
So, for example, let's say we want to create a widget called MyWidget, that has a body and a footer with content. The JavaScript file would look like this:
import SoyComponent from '../bower_components/metal/src/soy/SoyComponent';
import from './myWidget.soy.js';
class MyWidget extends SoyComponent {
constructor(opt_config) {
super(opt_config);
}
}
MyWidget.ATTRS = {
bodyContent: {
value: 'Initial body content.'
},
footerContent: {
value: SoyComponent.sanitizeHtml('<footer>Initial footer content.</footer>')
}
};
This file just defines a class named MyWidget, makes it extend from SoyComponent, imports the compiled soy templates and defines two attributes. Note that html strings need to be properly sanitized, otherwise they will be escaped by default before rendering.
Now we just need a soy file for MyWidget's rendering logic. It would look like this:
{namespace Templates.MyWidget}
/**
* This renders the component's whole content.
*/
{template .content}
{delcall MyWidget.body data="all" /}
{delcall MyWidget.footer data="all" /}
{/template}
/**
* This renders the body part of the component.
* @param bodyContent
*/
{template .body}
<p>{$bodyContent}</p>
{/template}
/**
* This renders the footer part of the component.
* @param footerContent
*/
{template .footer}
<footer>{$footerContent}</footer>
{/template}
Looking at that you can see that it's just a basic soy file that defines some templates. For this soy file to work well with SoyComponent its namespace just needs to be in the format: Templates.{name of widget}
.
Note that, on the soy file, we have divided the main template into subtemplates, one for the body content and one for the footer. This is not necessary, but can be really helpful, as SoyComponent will handle these as special parts of the widget, automatically rerendering them when one of the attributes listed as params of a template changes. In MyWidget's case this means that whenever the value of the bodyContent
attribute is changed, the body
template will be called, and that part of the widget will be updated, even though there is no JavaScript code on MyWidget to handle this logic. The same goes for the footerContent
attribute and the footer
template.
SoyComponent's logic for updating the widget's contents automatically is very smart, so it won't cause a rerender unless it's necessary. So if a change causes a template to be called again, but the resulting HTML from the template is the same that was rendered for the last time, it will be ignored. This is done by compressing and caching the hash code of a template's results when it's called, and later using it to compare with new results to decide if a new content should be rendered or not.
Finally, to render an instance of MyWidget, just call render
, passing any attribute values that you want to initialize:
new MyWidget({headerContent: 'My Header'}).render(parentElement);
For a more complete and working example, take a look at the metal-boilerplate repo. Among other things, it lists all optional lifecycle functions that can be implemented for SoyComponent.
Since we want to be able to separate business logic from rendering, it'd be really useful to be able to reference components on the template files. That would make it easier to correctly place the child component at the right position inside the parent, and would make the template more complete so it would be able to render the whole component by itself (see Decorate).
This can already be done with SoyComponent. For example, let's say we have the Modal and Button components, and the modal wants to render buttons on its footer. Inside modal.soy we'd see the following:
{template .footer}
{delcall Button}
{param id: 'ok' /}
{param label: 'Ok' /}
{/delcall}
{delcall Button}
{param id: 'cancel' /}
{param label: 'Cancel' /}
{/delcall}
{/template}
When Modal is rendered, the two specified buttons will be rendered as well. Also, the button instances can be accessed from the components
property inside the modal instance, indexed by their ids:
modal.components.ok // The instance for the 'Ok' button
modal.components.cancel // The instance for the 'Cancel' button
Another feature Metal.js has that can be very useful is the ability to declare events inside templates, directly on the desired element. Besides being simple and intuitive, this feature allows Metal.js to handle attaching events itself, and so this can be done in the best way possible, with delegates for example, without the user having worry about that at all.
By using SoyComponent, for example, you can add inline listeners like this:
{template .button}
<button data-onclick="handleClick"></button>
{/template}
Then, you just need to define a handleClick
method on your component, and it will be called whenever the event is triggered.
Progressive enhancement is a feature that is very important for a lot of people. Knowing about this, Metal.js is prepared to deal with content that already comes rendered from the server. In that case, instead of calling render
the developer can call decorate
instead. This will skip the rendering phase of the component, running only the code that enhances it with JavaScript behavior instead.
For SoyComponent, this means that the template won't be rendered, but the attached
lifecycle method will still be called.
It's important to note that building components with SoyComponent also helps with progressive enhancement in another way: by providing a faithful template that can be run by the server without having to duplicate the rendering code or run JavaScript at all.
Metal.js was built from the first with performance in mind. We've run performance tests to compare it with other libraries and got really good results that show the benefits of using it.
In one of the tests we made, we built a simple list widget on three different libraries: Metal.js, YUI and React. We then measured the time it took to render those widgets with 1000 items each on three different situations:
The chart below shows the results we obtained on Safari:
In this previous test, the list widget was built on all three libraries as a single component that renders each list item itself. We also did another similar test, with a list widget that was built using nested components instead, on which the list component renders other components that represent list items. Since YUI doesn't have this concept of nested components, this test was only done for Metal.js and React.
Once again, the chart below shows the results we obtained on Safari:
Metal.js comes together with a set of gulp tasks designed to help develop with it. To learn more about them and use them, take a look at: https://github.com/metal/metal-tasks.
FAQs
Core functions from Metal.js, with utilities for dealing with arrays, objects and others.
The npm package metal receives a total of 1,404 weekly downloads. As such, metal popularity was classified as popular.
We found that metal demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 6 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.
Security News
NVD’s backlog surpasses 20,000 CVEs as analysis slows and NIST announces new system updates to address ongoing delays.
Security News
Research
A malicious npm package disguised as a WhatsApp client is exploiting authentication flows with a remote kill switch to exfiltrate data and destroy files.