UNPKG

19.3 kBMarkdownView Raw
1# Lumbar #
2
3Lumbar is a js-build tool that takes a _general codebase_ and list of _platforms_ to generate modular _platform specific applications_.
4
5## Introduction ##
6
7You can think of lumbar as a conditional compiler that targets platforms. However, it doesn’t rely on variables in your source code. There’s no #ifdef or #endifs. Rather you can include and exclude files by associating them with a platform. It uses a json file, [lumbar.json](#lumbar.json), to describe project meta-data.
8
9* It allows you to define multiple routers in your code.
10* It pulls in your mustache or handlebars templates.
11* It pulls in your stylus styles and generates css files.
12* It outputs standalone javascript and css files.
13* It wraps your code in the correct scope (module pattern), or not.
14
15Lumbar works well with [Backbone](http://documentcloud.github.com/backbone/) allowing for grouping of routers, models, views, and other application code into stand alone modular javascript and css files which can be lazy loaded when the route is encountered.
16
17Best of all, if what’s included out of the box doesn’t satisfy your needs, then you should be able to build a plugin relatively easily to support it. Lumbar was built around a [plugin architecture](#plugins) which makes it very extensible.
18
19## High Level Overview ##
20
21Lumbar is modeled and built around [platforms](#platforms). Platforms are defined by you, to fit your representations. When a platform flag is present on a resource, such as a javascript file, then that resource will be included for that platform’s output only.
22
23The next big term are [module(s)](#modules). A module is a grouping of multiple resources, such as static files, stylesheets, templates, and routes, into a logical unit. Lumbar will process all the resources in each module and output them appropriately for each platform.
24
25Following module(s) are [package(s)](#packages). When platform(s) are processed one by one, they are output based on rules found in the packages. Therefore, a package gives more flexibility on how to output files. Theoretically, you could have one package that referenced all your platform(s).
26
27## lumbar.json ##
28
29The main configuration file for lumbar is by default named *lumbar.json*. It is a JSON formatted document that has six main sections. They are [application](#application), [platforms](#platforms), [packages](#packages), [modules](#modules), [templates](#templates), and [styles](#styles).
30
31Each section of lumbar.json is discussed in more detail below. There is an example lumbar.json included in the [thorax-example](https://github.com/walmartlabs/thorax-example) project.
32
33The configuration in this file may also be split into multiple files via [libraries and mixins](#libraries).
34
35### Platforms ###
36
37 "platforms": [ "android", "iphone", "ipad", "web" ],
38
39Platforms define the target environments that a particular lumbar build may target. This could range from separate builds targeted to different browsing environments (I.e. embedded webview vs. full browser) all the way up to builds targeted for specific business units.
40
41Platforms are defined through the `platforms` field in the lumbar.json file. This is an array of names, each of which produce a subdirectory in the build that contains all of the resources needed by that platform.
42
43### Packages ###
44
45 "packages": {
46 "web": {
47 "platforms": [ "web" ],
48 "combine": false
49 },
50 "native-hello-world": {
51 "platforms": [ "android", "iphone", "ipad" ],
52 "modules": [ "base", "hello-world" ],
53 "combine": true
54 }
55 },
56
57Where platforms specify what you are going to build for, packages define what will be in each platform, at a macro level. This allows for creating applications for specific environments that are optimized subsets of the larger codebase. For example a native+web application may have a package for all modules that is utilized for web users and a package containing only a subset of modules that are combined into a single HTTP request for users that are coming from the native implementation.
58
59Packages are defined as objects on the `packages` object. Each package may define `platforms` and `modules` keys which will limit the platforms and modules that are associated with that package to a given set. If these fields are omitted then all platforms and modules are involved with the package.
60
61The platforms list can be viewed as the platforms that the package will deliver on and the modules list can be viewed as what resources will be included in the package.
62
63The resources in a package may also be optionally combined into a single file via the `combine` attribute. When combined the generated file will have the same name as the package name.
64
65### Modules ###
66
67 "modules": {
68 "base": {
69 "scripts": [
70 {"src": "js/lib/zepto.js", "global": true},
71 {"src": "js/lib/underscore.js", "global": true},
72 {"src": "js/lib/backbone.js", "global": true},
73 {"src": "js/lib/handlebars.js", "global": true},
74 {"src": "js/lib/thorax.js", "global": true},
75 {"src": "js/lib/script.js", "global": true},
76 {"src": "js/lib/lumbar-loader.js", "platform": "web"},
77 {"src": "js/lib/lumbar-loader-events.js", "platform": "web"},
78 {"src": "js/lib/lumbar-loader-standard.js", "platform": "web"},
79 {"src": "js/lib/lumbar-loader-backbone.js", "platform": "web"},
80 "js/init.js",
81 "js/router.js",
82 "js/model.js",
83 "js/collection.js",
84 "js/view.js",
85 {"src": "js/bridge.js", "platforms": ["iphone", "ipad", "android"]},
86 {"src": "js/bridge-android.js", "platform": "android"},
87 {"src": "js/bridge-ios.js", "platforms": ["ipad","iphone"]},
88 {"module-map": true}
89 ],
90 "styles": [
91 "styles/base.styl"
92 ],
93 "static": [
94 {"src": "static/#{platform}/index.html", "dest": "index.html"}
95 ]
96 },
97 "hello-world": {
98 "routes": {
99 "": "index",
100 "hello": "index"
101 },
102 "scripts": [
103 "js/views/hello-world",
104 "js/routers/hello-world.js"
105 ],
106 "styles": [
107 "styles/hello-world.styl"
108 ]
109 }
110 },
111
112Next, lets discuss the `modules` section of the *lumbar.json* file. This section lets you define logical groupings of code, static files, stylesheets, and templates (mustache / handlebars).
113
114A module's content is primarily defined by the `scripts`, `styles` and `static` fields which define arrays of resources that will be included within the module. For the `scripts` and `styles` entries all resources will be combined into a single script and single style file at build time. `static` resources will be copied to the build target on build. Each of these fields are optional and omitting a given field will prevent final output of that given file.
115
116#### Resources ####
117
118While left somewhat vague as plugins can provide their own definition of what a resource is, at the core level it is content that will be output in the resulting module. In general a resource is a file on the file system that may be transformed by plugins, but in some cases plugins define their own resources. The [module-map](plugins/module-map.md) plugin for example defines its own resource type that outputs the collective routes and associated modules for the entire package.
119
120File resources can be a simple string with relative path and or an object that offers more granularity for the file. If it's a simple filename then an entry like, **init.js** would be appropriate. If additional requirements are needed such as limiting to specific platforms or scope then an object with the `src` field
121set to the relative path should be used, along with any plugin specific filter values. The
122[conditional plugin](plugins/conditional.md) for example can define conditional inclusion via contstructs like this following:
123
124 {"src": "lumber-loader-localstorage.js", "env" :"production"},
125 {"src": "lumber-loader-storage.js", "env": "dev"}
126
127The core implementation provides platform-specific filtering by specifying the `platform` or `platforms` fields on the resource object; the singular form being a string reference to a platform and the plural being an array of platform names we want init.js to be included with. If we were targeting the ipad and iphone platforms our entry would look like this:
128
129 { "src": "init.js", "platforms": ["ipad", "iphone"] }
130
131#### Scoping ####
132
133By default, Lumbar creates a private scope for all generated modules using the javascript module pattern. This behavior is controlled by the [scope plugin](plugins/scope.md) and may be adjusted as necessary.
134
135The scopes are somewhat akin to CommonJS modules, generating a `module` instance variable which acts as a general input from plugins and the `exports` or `module.exports` variable allowing for the module to expose functionality to the outside world.
136
137The output of a module can be customized with a template which will receive `scope` (the current module scope) and `name` (the current application name) as variables.
138
139To specify this in your lumbar file set:
140
141 {
142 "scope": {
143 "template": "templates/module.template"
144 }
145 }
146
147The template can be a handlebars template string or a path to a file that ends in `.handlebars`. This template **must** contain `{{yield}}`. The built in module template looks more or less like:
148
149 {{{scope}}} = (function() {
150 var module = {exports: {}};
151 var exports = module. exports;
152 {{yield}}
153 return module.exports;
154 }).call(this);
155
156#### Routes ####
157
158Modules may optionally define Backbone routes that it manages via the `routes` field. When paired with [lumbar-loader](https://github.com/walmartlabs/lumbar-loader) the given module will be loaded on demand when the user navigates to a route serviced by that module.
159
160Any routes defined for the module are available to javascript code via the `module.routes` field.
161
162### Application ###
163
164 "application": {
165 "name": "Example",
166 "module": "base"
167 },
168
169The `application` section defines the module that serves as the root namespace for all other modules. This is generally the module that provides the core framework for the application while other modules implement specific subsections of the application functionality.
170
171On this object, the `name` key defines the javascript name that other modules may access the module's exports from. (Note that code running within this module must address itself using the `exports` object directly until it has been fully initialized).
172
173All other modules will namespace themselves based on this name. With the example above a module named home would name itself `Example.home`.
174
175There’s also a `module` key used to identify the module that will be loaded into the root namespace. Without explicit loading this is the only one that can be relied on when running code in any other modules. This is a special module that kicks off everything and presumably contains the core library code for your application.
176
177For example, if we listed two modules for our package, say "base" and "home" we would get two files called *home.js* and *base.js*. If we chose "base" as the value for module then our *base.js* file would declare `Example` and *home.js* would use it. If we chose "home" as the value for module then our *base.js* file would use `Example` while *home.js* would declare it.
178
179### Templates ###
180
181 "templates": {
182 "js/views/hello-world/index.js": [
183 "templates/hello-world/index.handlebars"
184 ]
185 },
186
187Lumbar also support inclusion of templates via the `templates` field. Each entry in this object will be matched against the files that are included in a particular module. When a match occurs all templates will be included in the associated module (not more than once).
188
189These templates may also be precompiled and have other optimizations applied to them. See the [template plugin](plugins/template.md) documentation for additional information.
190
191### Styles ###
192
193 "styles": {
194 "pixelDensity": {
195 "android": [ 1, 1.5 ],
196 "iphone": [ 1, 2 ],
197 "web": [ 1, 2 ]
198 },
199 "useNib": true,
200 "includes": [
201 "styles/include/global.styl"
202 ]
203 }
204
205Finally the `styles` section controls how CSS is generated and included within the generated content. When combined with [stylus](http://learnboost.github.com/stylus/), lumbar can allow for things like:
206
207 * Platform conditional styles via stylus conditionals and `$platform` variables
208 * Data URI inlined images
209 * Pixel density targeted stylesheets
210
211For more information see the [stylus plugin](plugins/stylus.md).
212
213## Libraries ###
214
215Lumbar allows for a projects configuration to be split into multiple lumbar config files via libraries. This allows multiple projects to reuse behaviors without having to duplicate their
216configuration. For example the lumbar-loader files can be referenced via a library like so:
217
218
219 "modules": {
220 "loader": {
221 "mixins": [
222 {"name": "lumbar-loader", "env": "dev"},
223 {"name": "lumbar-localstorage-loader", "env": "production"}
224 ]
225 }
226 },
227 "libraries": [
228 "components/lumbar-loader"
229 ]
230
231Libraries have two methods of operation, global config and module mixins. The former allows libraries to define common configuration such as common `stylus` include files and the later allows for the addition of specific behaviors to specific modules.
232
233Global config changes are simply defined in the root of the library `lumbar.json` file. When the library is loaded any settings defined here will be imported into the project. Note that each plugin implements its own inheritance scheme here. Some plugins might treat specific definitions as additive and others might treat them as overrides.
234
235They are discussed in detail [here](libraries.md).
236
237## Plugins ##
238
239Lumbar offers many options for customization and extensibly through its Plugin Architecture.
240
241### Core Plugins ###
242
243The bulk of Lumbar's core functionality is implemented through core plugins, such as the [stylus](plugins/stylus.md), [template](plugins/template.md), and [scope](plugins/scope.md) plugins. Documentation on all of the core plugins is available [here](plugins/).
244
245By default each of these plugins are utilized for all builds. If this is not desired then the `ignoreCorePlugins` field may be specified on the build options. When doing this select core plugins may still be used by referencing them by name. See [Using 3rd Party Plugins](#plugins-using) below for details.
246
247### Using 3rd Party Plugins ###
248
2493rd party (and core plugins in `ignoreCorePlugins` mode) may be defined through
250
251* The command line
252 Plugins may be defined on the command line with the `--use` parameter. This should be the plugin module name (global or relative to the current working directory) or an explicit path the the module defining the plugin. For plugins that support option passing, the `--with` argument may be used immediately after the associated plugin to pass arbitrary JSON objects to the plugin.
253
254* The project configuration
255 Plugins may also be defined in the plugin configuration via the `plugins` field. This field should be an array of either name values or `{path: pluginPath}` objects. Path objects will load global node.js modules, explicit paths, or modules in the *node_modules* directory in the same directory as the configuration file.
256
257* The Lumbar API
258 Clients that are interfacing directly with the lumbar API may explicitly define plugins via the `Lumbar.use` API. This API registers a particular plugin with a given name that can be referenced with through the `options` parameter or the project configuration.
259
260 API clients may also pass plugins through the `plugins` field on the `options` object. This field should be an array containing either the name, `{path: pluginPath}` object or the plugin instance. Path objects are loaded in the same manner as plugins defined in the configuration file.
261
262### Implementing Custom Plugins ###
263
264Read more about our architecture [here](plugins.md).
265
266## Command Line ##
267
268 lumbar build [--package name] [--config file] [--minimize] [--use path [--with options]] lumbarFile outputDir
269 Build out the package(s).
270 lumbar watch [--package name] [--config file] [--minimize] [--use path [--with options]] lumbarFile outputDir
271 Start watching the files for changes and rebuild as necessary.
272
273 --package: represents the name of a corresponding package listed
274 under 'packages' in lumbarFile.
275
276 --minimize: to shrink the resultant file.
277
278 --config: is the name and path to the lumbar config file, if
279 not given then lumbar.json is assumed.
280
281 --use: path to your plugin to load
282
283 --with: an optional json config object to pass to your plugin.
284
285 lumbarFile: is the name and path to the lumbar config file, if
286 not given then lumbar.json is assumed.
287
288 outputDir: Required. Designates where the files will be placed.
289
290## FAQ ##
291
2921. Does lumbar.json have to be in the root our our application?
293
294 No, not necessarily. The root is the current working directory that you are running _lumbar_ from.
295
296 However, the files mentioned in lumbar.json would have to be relative to its location. So if you dropped lumbar into a sub directory, you would have to ../ all files from the root. You would also have to run lumbar from the sub directory.
297
298 So to keep it simple, keep lumbar.json in the root.
299
3001. Is there a bootstrap for Lumbar?
301
302 The [thorax-seed](https://github.com/walmartlabs/thorax-seed) project contains all of the content necessary to setup a Lumbar (and Thorax) project. This may be freely copied and used as a basis for new projects.
303
3041. What do I do about **EMFILE** errors?
305
306 For larger projects watch mode may run into issues relating to too many open files depending on the size of the project and environment settings. If **EMFILE** errors are encountered while running in watch mode there are two options:
307
308 1. Increate the maximum number of open files. On most systems this can be achieved via the `ulimit -n <value>` command.
309 1. Use an outside recompile method such as an IDE or general execution.
310
3111. Does Lumbar provide any options for long expires resources?
312
313 The [lumbar long expires](https://github.com/walmartlabs/lumbar-long-expires) plugin allows for naming objects with arbitrary cache buster tokens, such as git SHA values. For example:
314
315> ./android/7c18fda/index.html
316> ./android/7c18fda/native-hello-world.css
317> ./android/7c18fda/native-hello-world.js
318> ./android/7c18fda/native-hello-world@1.5x.css
319> ./ipad/7c18fda/index.html
320> ./ipad/7c18fda/native-hello-world.css
321> ./ipad/7c18fda/native-hello-world.js
322> ./iphone/7c18fda/index.html
323> ./iphone/7c18fda/native-hello-world.css
324> ./iphone/7c18fda/native-hello-world.js
325> ./iphone/7c18fda/native-hello-world@2x.css
326> ./web/7c18fda/base.css
327> ./web/7c18fda/base.js
328> ./web/7c18fda/base@2x.css
329> ./web/7c18fda/hello-world.css
330> ./web/7c18fda/hello-world.js
331> ./web/7c18fda/hello-world@2x.css
332> ./web/7c18fda/index.html