Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
assetify is an open-source client-side asset manager for Node.JS web applications.
There are two distinct stages when working with assetify to manage static assets. The first step is processing the sources you developed, parsing LESS stylesheets, CoffeeScript files, bundling, minifying, etc. The second step involves serving the resulting files.
With assetify, you work on your sources, and then generate output that is meant to be consumed by your users. assetify
is middleware-based, and you can extend it with plugins of your own.
Here is a sample folder structure for your public static assets:
assetify
will take your directory structure and produce a build result, which you'll then use to serve your static assets. Let's walk through a basic setup.
We start off by installing assetify
$ npm install assetify --save
This is a simple configuration file:
{
assets: {
source: __dirname + '/public',
bin: __dirname + '/public/.bin',
js: [
'/js/app.js',
'/js/foo.js'
],
css: [
'/css/reset.css'
'/css/layout.less',
'/css/design.less'
]
}
}
Let me explain these properties.
The source
directory is the base directory where your static assets are.
Don't worry, assetify supports referencing assets outside of this directory, but it will be used as the base directory
for relatively referenced assets.
The bin
directory is used to place the results of processing your static assets.
First of all: assets.css
works in the same way as assets.js
, some plugins run exclusively in the appropriate set of assets, though, so it's important to place them in the correct array. More on plugins later.
Strings. Strings are syntactic sugar for an object like this: { file: '/the/string' }
. So what kind of properties can these objects have?
{
"file": "/js/app.js",
"glob": "/js/service/*.js",
"inline": false,
"src": "alert('foo');",
"ext": "//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js",
"test": "window.jQuery",
"profile": "owner"
}
We obviously shouldn't be setting all these properties at once, let's go over them.
The path to the asset, relative to assets.bin
. This path can contain ..
jumps to parent directories.
To specify files outside the source
directory more conveniently, simply specify the file
value as an absolute path. assetify
will attempt to
located the file as if it were relative to source
, when that fails it will assume the file
value is absolute.
assetify
concedes that the order you output your assets in is important, and it will respect the order you
define for your assets to be exposed.
This property isn't actually in assetify, but it comes bundled with grunt-assetify, which is a grunt
task that simplifies the compilation step.
As you might have guessed, if you're familiar with grunt
, the glob
property is special in that it allows us to set a globbing pattern. When it's expanded, resulting objects will have file
set to each matching file, and all the other properties will be preserved.
Typically, files will be written to disk, and script tags will reference those files. However, if inline === true
, the script will be inlined. Resulting in HTML such as:
<script>
alert("foo");
</script>
Also, assets that are dynamically generated will be inlined. More on these later.
Usually, you'll be using file
to set the source code of your assets. If you need to provide the asset source code directly, you can use this property instead.
Sometimes, we want to reference assets in a CDN. We can do this using ext
. Generally, its recommended that you provide a fallback in case the CDN asset fails to load, you can do that by providing a test using the test property, for example, if window.jQuery
isn't set after we try to load jQuery from Google's CDN, we fall back to the local copy of jQuery (which should be set with file
).
Last but not least, profile
. This property allows us to set up different asset groups. You can specify a String with a single profile, or an array with multiple profiles. e.g: ['anon', 'registered']
.
The purpose of this property is to save time for our users by not making them download assets they are not going to need.
Now that you know how to configure your assets hash, here's how you compile your assets.
This step can be simplified with the aid of grunt-assetify.
var assetify = require('assetify').instance();
assetify.compile(options, function(err){
console.log('assetify compilation done');
});
Compilation will generate all the required files that we will need to serve our static assets later on. Additionally, the assetify compiler will output an assetify.json
file that will contain some metadata that will be used to let the middleware know how to behave.
Remember how I mentioned assetify is middleware-based? Well, if you don't add anything else, your "compilation" isn't doing anything, it will just be a glorified copy. The thing is, once you've set up the asset options, adding functionality is really easy. Let me teach you by example.
var assetify = require('assetify').instance();
assetify.use(assetify.plugins.less);
assetify.use(assetify.plugins.minifyCSS);
assetify.use(assetify.plugins.minifyJS);
assetify.use(assetify.plugins.bundle);
assetify.compile(options, function(err){
console.log('assetify compilation done');
});
Just like that, your assets will be bundled together, your LESS stylesheets will be properly pre-processed, and everything will be minified.
Here is a list of all the compilation plugins that come bundled with assetify.
Instead of resulting in one output file as a result for each input file, you'll be getting one file as a result for each profile. If no profiles were specified, then a default, 'all'
profile, will be used.
Your LESS CSS stylesheet files will be pre-processed
Your SASS CSS (and SCSS) stylesheet files will be pre-processed
Your CoffeeScript JS files will be pre-processed
Your jsn JS files will be pre-processed
Your CSS will be minified.
Your JS will be minified
By default, images in the source
directory won't be forwarded to the compilation folder. If you want to keep all your public-facing static assets in one place, then you can forward images to the output directory.
plugins.forward
is a function you need to call to get the plugin. The first parameter takes an object and it's optional. It defaults to:
{
"extnames": [".ico", ".png", ".gif", ".jpg", ".jpeg"]
}
If you want to add more forwarded extensions, you could set concat
to true. It doesn't have to be limited to image extensions, they can be anything.
var fwd = assetify.plugins.forward({ extnames: ['.woff', '.otf', '.ttf'] }, true);
assetify.use(fwd);
Now you can very simply use NODE_ENV
to figure out whether you want to minify and bundle or not. It gets better, using grunt-assetify will make the compile step even easier to configure, by letting you tell it whether you're in production or not, and using a sensible defaults-based approach.
Plugins don't just run in whatever order you pick, they do so within the event they are bound to. Events will be executed in series, not asynchronously. Each plugin will also run in series. This is required due to the nature of the compiler, and the fact that a plugin, such as the bundling plugin, might alter the very list of assets that are being processed.
This is kind of an implementation detail, but you should know this if you want to roll your own plugins.
'afterReadFile'
After reading input files, this event will be raised. At this point, language pre-processor plugins will run. Such as LESS, CoffeeScript, and jsn pre-processors. If you are including a custom pre-processor, this is the best fit.
'beforeBundle'
The beforeBundle
event is emitted once all plugins from the previous step have completed. The bundle plugin runs in this step.
'afterBundle'
Directly after the bundling step, afterBundle
plugins will trigger. This step includes the packed plugins for minification, and the 'forward'
plugin.
'afterOutput'
The 'afterOutput'
step is here in case we want to run any plugins after the output has been generated and written to disk. None of the distributed plugins run in this step, but you might want to develop a plugin to perform some task in this step.
'beforeRender'
This step is special in that it runs whenever asset HTML tags are going to be emitted, typically before an incoming request is going to render them. This step is used by an special plugin I'll describe in short.
Often you'll want to add assets in different places in your program - for example if you had a large site that consisted of multiple modules, you may want to allow each module to add their own assets, instead of the main module being responsible for its submodule's assets.
This behaviour can be acheived through the use of assetify.addFiles()
.
Assuming the same assetify
instance, the following two blocks are equivalent:
var assetify = require('assetify').instance();
assetify.addFiles({
css: [ '/stylesheets/style.css' ]
});
assetify.addFiles({
js: [ '/javascript/directives/ui-bootstrap-tpls-0.4.0.js' ]
});
assetify.use(assetify.plugins.bundle);
assetify.compile({
assets: {
source: path.join(rootDirectory, 'public'),
bin: path.join(rootDirectory, 'public', '.bin')
}
}, function(error) {
callback(error, app, rootDirectory);
});
var assetify = require('assetify').instance();
assetify.use(assetify.plugins.bundle);
assetify.compile({
assets: {
css: [ '/stylesheets/style.css' ],
js: [ '/javascript/directives/ui-bootstrap-tpls-0.4.0.js' ]
source: path.join(rootDirectory, 'public'),
bin: path.join(rootDirectory, 'public', '.bin')
}
}, function(error) {
callback(error, app, rootDirectory);
});
The second piece of the assetify puzzle is serving the assets, to facilitate this, we've provided a middleware you can tack onto
connect
(orexpress
), very easily.
Here is an example of how to integrate with express
.
var express = require('express'),
app = express(),
bin = __dirname + '/public/.bin',
assetify = require('assetify').instance();
assetify(app, express, bin);
// routing, etc
We're basically passing assetify three things: the app
, so that we can tack a middleware onto it; express
, so we know how to use a few more configuration options, and bin
, which is the same bin
as the one we used in the compilation step.
Your middleware won't do much. It will, however, set up a local variable in your res
objects, called assetify
. This object will have a very small API.
Alternatively, if you don't want to have this middleware set up on every route, you can use assetify(bin)
, instead. This will load the serialized data needed to make the plugin run. After that, you can load up the middleware only in the routes where you need it, using app.use(assetify.middleware)
. This will expose the API for each particular request.
This will emit all the CSS style tags you need in your view, in the order you chose, and using the assets that have been previously compiled through assetify
. The profile
can be omitted.
In a Jade view:
html
head
!=assetify.css.emit()
body
p Awesome!
Dynamically adding assets to particular requests is supported. The code will be inlined in the appropriate asset tag in the response. If before
is true, then the asset will be added before any statically compiled assets, rather than last.
This API is repeated for JavaScript assets, in assetify.js
.
The crucial take-away here is that we can compile and serve in two completely separate steps, and it would still work. This enables us to compile using grunt-assetify in a grunt
task, and then run our app just doing node app
.
Why are we passing express
to create this middleware? Well, there are actually a few more options we can use with assetify
. These should also be passed to the options we've described at the beginning.
{
compress: true,
serve: true,
fingerprint: true,
expires: /^\/img\//i,
explicit: false,
assets: {
favicon: '/icon.ico',
host: 'http://localhost:3000'
}
}
Let's go over each of these, too!
Whether to use the connect.compress()
middleware.
If this is enabled, then we're going to use static-asset to produce fingerprints for our assets, sending them far into the future with expires headers. These fingerprints will only be set for assets you declare explicitly, and not for forwarded assets, since image references aren't in control of assetify.
This option defaults to true
. When serve
is disabled, static assets won't be served by assetify. We use connect.static
to serve assets, but you might want to serve them yourself. In this case, you can disable serve
. Note that in doing so, you'll be turning off the fingerprint option as well.
If this is set, then the regular expression provided will be used to give an Expires
header to any matching request. The favicon
will also receive this treatment. Note that assets registered through assetify don't need this, and fingerprint is the preferred method for serving those. However, if you've chosen to disable serve
, then you could use this option alternatively.
expires
is intended to be used for images and any other requests that might match assets we forwarded using the forward
plugin.
When this option is true
, assets will be prefixed with a custom extension, for example, file.assetify.js
and file.assetify.min.js
, rather than file.js
and file.min.js
. This enables us to place the output files in the same directory as where the sources are.
This setting can also be set to a string to use as the custom extension. If treated as a boolean value, then 'assetify'
will be used.
A favicon to use with connect.favicon(file)
.
Sometimes, it's convenient to have asset reference the absolute URL rather than a relative URL, in these cases, we can provide a host name using this property.
That's about it when it comes to using assetify
.
assetify
You can include your own plugins using the following API:
assetify.use(key, eventName, plugin);
The key
can be either omitted, or filter our plugin to only run for the 'js'
or 'css'
pipe.
The eventName
is one of the event steps mentioned in the Plugin Precedence section.
Your plugin. It will receive four parameters. The assets
will contain the list of assets that are currently being processed. You can manipulate them in any way. Keep in mind that if you modify the array, the next plugin will get your changes too.
The config
object is the options hash that was passed to assetify when invoking the compile
function. The context
variable you'll receive will contain the key
, and if you are attaching to the beforeRender
event, you'll receive the http
context property as well.
Once you are done doing your job, you should invoke the done
callback, so processing can continue. If you pass an argument, it will be treated as an error.
There's pretty much all you'll ever need to know about assetify
. Let me know if you have any questions, issues, feature requests, concerns or suggestions!
FAQs
Node client-side asset manager tool
The npm package assetify receives a total of 6 weekly downloads. As such, assetify popularity was classified as not popular.
We found that assetify demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 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 malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.