Skip to content
This repository has been archived by the owner on Jan 22, 2024. It is now read-only.

The CSS Loader Plugin

Chuck Dumont edited this page Jul 1, 2015 · 8 revisions

The Aggregator sample application includes a CSS loader plugin that can be used in conjunction with the Aggregator to load CSS resources. The plugin delegates to the text plugin for the resource load, which allows it to be used with any AMD loader, with or without the Aggregator. When used with the Aggregator, the text plugin is implemented by the Aggregator, allowing text modules, including CSS, to be aggregated together with JavaScript modules, minimizing the number of HTTP requests needed to load the application. The CSS plugin receives the CSS text from the text plugin, rewrites relative URLs to make them relative to the page, and dynamically inserts a style element containing the module text into the page's <head> element.

Starting with version 1.2.4, the CSS loader plugin has two modes of operation, controlled by the css-inject-api feature. The default (legacy) behavior, if the feature is not defined or if has('css-inject-api') returns false, is for the plugin to inject style elements into the DOM at the time that the plugin's load() method is called by the loader. This guarantees that the styles will be injected in the order that they appear in a module's dependency list, but the order of injection for styles that are loaded by different modules is undefined.

If has('css-inject-api') is true, then styles are not injected into the DOM by the plugin (unless installAutoInjectHooks() is called at application startup - see below). Instead, the application calls the plugin's inject() method at the start of require() or define() callback for each module that uses the CSS loader plugin to declare CSS dependencies.

define(['app/foo', 'js/css!app/styles/foo.css'], function(foo) {
   // Inject the CSS
   require('js/css').inject.apply(this, arguments);

The inject() method iterates over the arguments passed and injects into the DOM any style elements that have not previously been injected. This mode provides for more predictable order of injection of styles since the order for styles injected from different modules corresponds to the define order of the JavaScript modules doing the injecting.

Note that in the example above, the style element for app/styles/foo.css is not declared in the formal parameters for the define callback, but it is included in the arguments array that is passed to inject(). Note also the use of the synchronous form of require() which avoids the need to declare a dependency on the plugin module itself in order to obtain a reference, making the code needed to do the injection very generic and boiler-plate.

###installAutoInjectHooks()###

When using css-inject-api, the task of calling inject() at the beginning of every require() or define() callback can be automated by calling installAutoInjectHooks() during application startup. This method installs intercepts for the global require() and define() functions. The intercepts hook the callback functions passed to require() and define() for the purpose of automatically invoking the inject() method before the callback function is invoked, thereby eliminating the need for the boiler-plate code described above. These hooks also watch for context require() instances in dependency lists and install intercepts for the context require() callbacks as well.

The only caveat is that installAutoInjectHooks() must be called before any require() or define() functions that use the CSS plugin to load CSS, or that reference a context require() which uses the CSS plugin to load CSS, are called, otherwise the inject api will not automatically be invoked for those cases.

###PostCSS###

The plugin provides support for invoking PostCSS plugins on the loaded CSS. This support is complementary to the server-side PostCSS support provided by the Aggregator, and is provided for those cases where you want to run your application without aggregation. The PostCSS support in the loader plugin is enabled when the feature dojo-combo-api is not true (i.e. the Aggregator is not in use) and the postcss feature is true (i.e. !has('dojo-combo-api') && has('postcss')), and the postcss loader config property is defined either in the global require or dojoConfig objects. You also need to define a mapping for the module id postcss in your loader config. The Aggregator hosts an implementation of PostCSS at <servlet-alias>/postcss/postcss.js.

The postcss loader config property has the same format as the postcss server-side AMD config property, except that the module ids used to reference the plugins are defined by the client-side loader config rather than the server-side AMD config.