matehat / brewer.js Goto Github PK
View Code? Open in Web Editor NEWYour asset management best friend
Your asset management best friend
This bug concerns the automated actualization of compiled files from a set of LESS files referencing each other. It was noticed that if fileA
"imports" fileB
using the normal @import
LESS directive, the output is correct, but the brewer make
and brewer watch
commands don't associate changes from fileB
to the need to recompile fileA
, which works nicely for other parsers.
The problem is indeed that we detect the importation tree and verify modification time based on our own # import
directive, made up for coffeescript/javascript/etc family (because they don't have this concept; the closest is commonjs modules). There is no workaround, unless we change the way imports are handled for CSS-like languages.
Is it possible for this to concat files that use different compilers?
For instance:
application.js
#import ["common.coffee","jquery.js"]
Ideally, the parser could identify the files provided and precompile them before bundling them.
Could we maybe add this to the File class to identify if there should be a precompile event prior to bundling?
Or is this already implemented and I'm just not seeing it?
It just seems like the Brewfile has to explicitly define which folders process coffee, and which ones process js, etc.?
Does it sort dependencies in order of appearance in imports amongst multiple concatenated files?
Small QoL change that would allow for out of the box syntax highlighting in text editors, such as Vim. Brake tool should search for project's configuration file in the following order: [Brewfile, brewfile, brewfile.coffee], stopping upon finding a match.
Any info on this?
As was reported by @compressed in #24:
One other question I have now in regards to the import line. I was playing around with importing some JS files from bootstrap. Does the import abide by the ordering of the files in the import? For instance, in my application.js (bundle) I have this:
// import ["jquery.js","bootstrap-tooltip.js","bootstrap-popover.js"]
but when I look at build/js/application.js (after running brake all) the code appears in the opposite order (popover first, then tooltip, then jquery last). Is that expected? I'd want JQuery first, followed by tooltip and then popover.
A nice enhancement to the project, given we're going for the "tasking" features (see last section) for 0.5, would be that Brewer.js be able to "serve" assets. Also, a local webserver could also provide a graphical interface to manage Brewer.js projects. There should therefore be a system Node.js daemon (and child worker processes that handle serving files and the graphical web interface for Brewer.js)
Deployment tasks (pushing to S3, (S)FTP, or Git) could be added to enhance the workflow.
Different options could be available for using and manipulating templates:
.brewer_templates
directory in his/her home folder.Templates should be prepared for different kind of projects that would benefit from Brewer.js (web site, static web site, Node.js project /w documentation).
Interesting additions would be :
Also, when specifying paths to assets, it would be nice to allow globs in addition to folders.
Every directive specified in a Brewfile could be viewed as expressions of structure or of "things to do", i.e. tasks. css
, js
barely express structure. less
, stylus
, coffeescript
, etc. express structure, but also tasks (compilation). bundler
express mainly tasks, just like some of the new directives from the ideas given here, such as serve
, deploy
, etc. Keeping this in mind, one should be able to organize tasks into higher level ones, using a task
directive.
// in coffee/home.coffee
// import ["jquery", "chosen/chosen.jquery.js"]
The chosen/chosen.jquery.js file can't be imported on windowsos
Is not nodejs can not access file with '' as path separator on windows os ?
As a temporary solution, i change src/lib/file.js:168 to
return match[1].replace(/\//, '\\\\') + recurse(_data.slice(match[0].length + match.index));
We should reduce the number of classes and just increase the diversity of the vocabulary.
Package
classWe need a Project
class. The Package
class existed only to accommodate de distinction between JavaScript and CSS bundles, and their distinctive needs. If we make the bundling process a separate thing, packages can be removed, and there would be only sources and projects.
Sources can provide source files and encapsulate their own compilation process to provide different representations of source files. This concept of source is the same as before. Staying functionally compatible, though the nomenclature would need revision, the separate entity that generate bundled (and potentially compressed) assets could be thought of as a particular case of sources. It just interacts more tightly with the project to find files that match an access path and a type to aggregate bundled assets. Vendor libraries are also just sources, only more static.
New kinds of sources and operations could include :
The Brewfile is currently used to specify only the structure of a project. It could also be used to specify tasks and task-groups, such as what other build systems out there do (make, cake, rake).
Brewer.js Formulae are a easy way, for users of Brewer.js, to install vendor libraries
into their project. Formulae can easily be added to this directory and immediately
available to Brewer.js users, by forking the project, adding a formula and making a pull
request to merge in your new-born formula.
A formula file can include one or more formulae, tightly related to each other. For example,
the jquery.coffee
formula file contains a jquery
formula and a jquery-dev
formula.This is the directive that starts off the definition of a formula
formula '<formula_name>', ->
Within the body of this formula definition, the following methods can be used:
Required. This tells Brewer.js at what URL it can download the library. This can be in one of three
forms. The first is through the use of a function that takes a single argument, a version. This version
is an object that can be coerced to its string representation. It also contain its major
, minor
,
patch
and tag
components (assuming [major].[minor].[patch]-[tag]
)
@urls (v) ->
"https://ajax.googleapis.com/ajax/libs/jquery/#{v}/jquery.js"
The second form is through an object, with keys being either versions, version ranges or the 'latest'
keyword. The values can be a URL string or a function, like above:
@urls
"latest": "https://github.com/jquery/jquery/tarball/master"
"1.X.X": (v) ->
v = if v.patch is '0' then "#{v.major}.#{v.minor}" else v
"https://github.com/jquery/jquery/tarball/#{v}"
The last, and the simplest, is a single URL string in an anonymous function:
@urls -> 'https://raw.github.com/douglascrockford/JSON-js/master/json2.js'
In fact, this is the same form as the first. We just simply didn't take the version into account.
Required. This tells Brewer.js how it should interact with a staged vendor library. A vendor
library could contain, like in the case of bootstrap, a mix of javascript and less files. For that case,
a formula for bootstrap could contain the following snippet:
@exports ->
@javascript './js'
@less './less', {prefix: 'styles'}
@main 'less', './less/bootstrap.less'
This shows a few characteristics of an @exports
directive. First, @exports
is followed by an anonymous
function, whose body will specify the individual directories to include, using the @dir
directive. This
function receives a single argument: the installed version. This allows the function body to vary its
the structure of the vendor library according to the version installed.
The @main
directive is used to specify a main source file (for a particular type) for the vendor library. It
has the form @main(<type>, <relative path>)
. For the case above, it adds a shorter access path
of simply bootstrap
, pointing to the file located at ./less/bootstrap.less
. @less
, @javascript
(and
friends) have the form of @<source_type>(<relative path>, [<options>])
. They are meant to look a lot like
the sources found in a package. Each of those will be added as sources in the project packages, thus
the resemblance. The trailing options
object can provide the following values:
prefix
This value will be prepended when calculating the access path of files contained in the directory. For
example, using the snippet above, assuming the @exports
directive is contain in a bootstrap
formula, the
file alerts.less
found in the ./less
directory would have an access path of bootstrap/styles/alerts
.
main
This value is simply a path pointing to a file (relative to the path of the directory, not to that of the
vendor library). If prefix
was provided, like in the case above it, it specifies what file should be
pointed to by the access path of <project name>/<prefix>
. Otherwise, it specifies the file pointed by
<project name>
. Without the prefix
option, this is equivalent to the @main
directive.
For some really simple formulae, the @exports
directive can be of another form, which is:
@exports <type>, <option>
# equivalent to
@exports ->
@<type> '.', <option>
This implies that the whole vendor library is added as a single source, of the type provided, with the given
options.
This is the directive that specify how to install the vendor library. It takes a single argument: an anonymous
function called when installing the library. This function receives the installed version as its only argument.
In the function body, a couple of methods are made available:
@stage(<path>, [<new name>])
This method takes the path provided as the first argument and puts it in the staged vendor library, under a
new name if <new name>
is provided.
@deflate(<path>, <compression>, <callback>)
This method takes a path and decompresses it using the compression provided (can be either zip
, tar
,
tar.gz
or tar.bz2
). When it finishes, it invokes the callback with the list of files extracted as
argument.
More methods will be made available to the installer body as the needs emerge.
What versions are available, if applicable. This can be in the form of an object, mapping versions to md5
checksums, for better reliability. The versions must comply to semantic versioning:
@versions '1.6.4', '1.7.1'
@versions
'1.6.4': 'be5cda8fa534e4db49425efbbf36c565'
'1.7.1': '273e017fd0bef143258516bdee173a1e'
This helps keep track of homepages for the installed vendor libraries. It has the form of @homepage(<url>)
This function can be in either of the forms allowed by @urls
. It is used to keep
track of where to find proper documentation for the installed vendor libraries.
Feel free to glimpse through the existing example to get a better understanding of formulae, and how you can
roll your own! Current work is going on in the 0.4 branch (previously the formula branch, but it has been merged in view of the upcoming 0.4 release).
In this method, of the Package
class,
https://github.com/matehat/brewer.js/blob/master/src/package.coffee#L175
We should definitely change the strategy to one that's based on ReadStream
and WriteStream
rather than fs.readFileSync
.
Hi!
Installation goes fine but when running "brake init", it throws the following stack trace:
$ brake init
Info Writing Brewfile
Info Making initial folder structure
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
TypeError: Cannot read property 'prototype' of undefined
at /usr/local/lib/node_modules/brewer/lib/extensions/iced-coffee-script.js:4:199
at /usr/local/lib/node_modules/brewer/lib/extensions/iced-coffee-script.js:12:5
at Object.<anonymous> (/usr/local/lib/node_modules/brewer/lib/extensions/iced-coffee-script.js:58:4)
at Object.<anonymous> (/usr/local/lib/node_modules/brewer/lib/extensions/iced-coffee-script.js:62:4)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
at Module.load (module.js:348:31)
at Function._load (module.js:308:12)
at Module.require (module.js:354:17)
at require (module.js:370:17)
$ npm --versions
{ node: '0.6.15',
v8: '3.6.6.24',
ares: '1.7.5-DEV',
uv: '0.6',
openssl: '0.9.8k',
npm: '1.1.16' }
$ uname -a
Linux laptop 2.6.32-41-generic #89-Ubuntu SMP Fri Apr 27 22:18:56 UTC 2012 x86_64 GNU/Linux
Hi, this looks like a really awesome project, just what I was looking for!
However, I seem to be having an issue running even the most simple example (css or js).
I set up a repo here: https://github.com/compressed/test-brewer.
When I run "brake all" in the command line this is what I get:
[master][~/Documents/web/test-brewer] brake all
Info Finished making styles package
which proceeds to make a vendor directory and that's it. No .cache folder or any output in the build dir. Can you help point me to what I'm missing?
Thanks!
Version info:
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├── [email protected] extraneous
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├─┬ [email protected]
│ │ │ ├── [email protected]
│ │ │ └─┬ [email protected]
│ │ │ └── [email protected]
│ │ ├─┬ [email protected]
│ │ │ ├── [email protected]
│ │ │ ├── [email protected]
│ │ │ ├── [email protected]
│ │ │ └─┬ [email protected]
│ │ │ └── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ └─┬ [email protected]
│ └─┬ [email protected]
│ └── [email protected]
When bundle A imports file B, and file B imports file C, changing C doesn't seem to trigger recompilation of bundle A.
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: EMFILE, too many open files 'assets/src/_cache/common.js'
at Object.openSync (fs.js:230:18)
at Object.readFileSync (fs.js:120:15)
at Object.checksumSync (/usr/local/lib/node_modules/brewer/lib/util.js:17:19)
at File.stamp (/usr/local/lib/node_modules/brewer/lib/file.js:128:35)
at File.watch (/usr/local/lib/node_modules/brewer/lib/file.js:99:14)
at /usr/local/lib/node_modules/brewer/lib/package.js:226:35
at Array.0 (/usr/local/lib/node_modules/brewer/lib/package.js:229:12)
at EventEmitter._tickCallback (node.js:192:40)
I know this isn't "sprockets" but it would be nice to be able to use it similar to sprockets since it's Node instead of Ruby...
Ideally, if I could setup a specific asset like in Rails Asset Pipeline like "app/assets/javascripts/application.js" and "app/assets/stylesheets/stylesheets/application.css" and put some base @import directives in it, then those files could also contain @import directives and it would branch out from there.
Then, I'd like a place to specify the final package file + minified as such ./public/assets/javascripts/application.js / application.min.js and ./public/assets/stylesheets/application.css / application.min.css respectively.
I'm not barking orders, I'd love it if I could help implement some of these things.
If these don't seem like reasonable expectations of this particular bundler then I understand! :X
Related to #3, but in a more general sense, the whole mecanism for importation and bundling (a concept only relevant to bundled web assets) should be reworked, I think to be more robust and factored out of the main Package
class to a more specific class, maybe something like AssetPackage
.
I've got a project containing only coffeescript files. Whenever I edit the files in Vim (in this case the file "Condition.coffee"), the 'watch' command appears to try and monitor all of vim's transient save files. As a result, brake tries to work with files that have vanished since it first discovered them. The result is that the file count goes up and then a crash occurs.
Info Watching project /Users/zack/bugdemo
Compiled coffee/Condition.coffee
Info Watching 13 files
Info coffee/Condition.coffee removed
Info coffee/Condition.coffee removed
Compiled coffee/Condition.coffee
Compiled coffee/Condition.coffee
Info Watching 14 files
Info Watching 26 files
Info coffee/Condition.coffee removed
Info coffee/Condition.coffee removed
events.js:66
throw arguments[1]; // Unhandled 'error' event
^
Error: ENOENT, lstat 'coffee/Condition.coffee~'
This behavior does not occur when I use sublime text 2 to edit the code.
OS X 10.7
Node 0.8.9
brake 0.3.7
I've been mixing up brewer
for brew
, the command for Homebrew. That's a sign the two are way too close.
Just ran an install on my OSX 10.8.2 machine and after the install "completes" running brake from the command line doesn't work (brake: command not found). You have to run brake using the full path which is something like /usr/local/share/npm/bin/brake
Also, when you run /usr/local/share/npm/bin/brake watch it does not add any new files even if you do a brake clean on the directory. In my case I was trying to add javascript with various names and all access permissions to no avail. The brake watch command did notice file changes, just not new files.
Versions:
npm 1.20
node v0.8.17
brake watch
should only build the uncompressed output. When several large files are produced, compression slows down the automatic reloading experience.
I was able to use 'npm install -g uglify-js@1' and get the previous version which appears to be fine. Here's the error generated by using v2.x:
/nodejs/lib/node_modules/brewer/lib/extensions/javascript.js:36
gen_code = uglify.gen_code, ast_squeeze = uglify.ast_squeeze, ast_mang
^
TypeError: Cannot read property 'gen_code' of undefined
at JavascriptPackage.compressFile.compress (/nodejs/lib/node_modules/brewer/lib/extensions/javascript.js:36:26)
at File.project (/nodejs/lib/node_modules/brewer/lib/file.js:89:16)
at File.read (/nodejs/lib/node_modules/brewer/lib/file.js:283:16)
at fs.readFile (fs.js:176:14)
at Object.oncomplete (fs.js:297:15)
When building documentation assets and compressing CSS, we get that message
/Users/mathieudamours/development/node/brewer/node_modules/ncss/lib/parser.js:275
if (KEYWORDS.indexOf(t) >= 0) q(t);
^
RangeError: Maximum call stack size exceeded
Combine (merge) & minify files to one "big" file.
Specify list of files list in Brewfile and merge it defined one. Eg.:
javascript 'package_name', ->
@options {build: './build', compress: true}
@bundles 'home', 'products'
@coffeescript './coffee'
@js './vendor'
@package 'home'
@package 'products'
# OR
@package 'home', 'products'
Create file ./build/package_name.min.js
with merged home
and product
.
Maybe add support "package" directory but:
However, if you provide directories, how do you deal with the ordering of files that are found within? If files depend on one another, you probably need them inserted in the concatenated file in a certain order, so you would need a mechanism to specify the order of files found in a given dir. Obviously, you wouldn't need this when manually providing files since you provide the order just by doing so.
javascript 'scripts', {build: './build/js'} ->
@bundles 'home', 'products', 'jquery.pjax'
@coffeescript './coffee', {output: './js'}
@js './vendor'
/usr/lib/node_modules/brewer/lib/file.js:156
if ((match = _data.match(regexp)) == null) return '';
^
TypeError: Cannot call method 'match' of undefined
at /usr/lib/node_modules/brewer/lib/file.js:156:30
at File.readImportedPaths (/usr/lib/node_modules/brewer/lib/file.js:159:25)
at File.importedPaths (/usr/lib/node_modules/brewer/lib/file.js:181:36)
at File.<anonymous> (/usr/lib/node_modules/brewer/lib/file.js:191:23)
at File.imports (/usr/lib/node_modules/brewer/lib/file.js:198:12)
at /usr/lib/node_modules/brewer/lib/file.js:223:21
at /usr/lib/node_modules/brewer/lib/file.js:211:13
at File.dependOnImports (/usr/lib/node_modules/brewer/lib/file.js:231:14)
at JavascriptPackage.<anonymous> (/usr/lib/node_modules/brewer/lib/package.js:101:18)
at JavascriptPackage.emit (events.js:67:17)
First i use 'brake scripts' on windows os, i received this:
path.js:204
throw new TypeError('Arguments to path.join must be strings');
^
TypeError: Arguments to path.join must be strings
at f (path.js:204:15)
at Object.filter (native)
at exports.join (path.js:209:40)
at Walker. (C:\Users***\npm\node_modules\brewer\lib\source.js:116:17)
at Walker.EventEmitter.emit (events.js:98:17)
at C:\Users***\npm\node_modules\brewer\node_modules\walker\lib\walker.js:98:12
at Object.oncomplete (fs.js:93:15)
source.js:116 is
walker.on('file', function(root, stat) {
var fpath;
fpath = join(root.slice(rpath.length + 1), stat.name);
I console.log stat.name, found stat has no [name] attribute, i use Windows 7(x86)
then, i delete the stat.name arg, found that it can also work well
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.