Repository: balupton/buildr.npm
Branch: master
Commit: fbf44577d849
Files: 10
Total size: 43.1 KB
Directory structure:
gitextract_2gp6xz3k/
├── .gitignore
├── .npmignore
├── Cakefile
├── LICENSE.txt
├── README.md
├── bin/
│ └── buildr.coffee
├── lib/
│ ├── buildr.coffee
│ └── templates/
│ ├── srcLoader.coffee
│ └── srcLoaderHeader.coffee
└── package.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules/
bin/buildr.js
lib/buildr.js
buildr-*.tgz
================================================
FILE: .npmignore
================================================
node_modules/
bin/buildr.coffee
lib/buildr.coffee
.gitignore
Cakefile
buildr-*.tgz
================================================
FILE: Cakefile
================================================
fs = require 'fs'
CoffeeScript = require 'coffee-script'
task 'build', 'compile source files to javascript', (options) ->
compile 'lib/buildr.coffee', false
compile 'bin/buildr.coffee', true
task 'clean', 'delete compiled output', (options) ->
fs.exists 'lib/buildr.js', (exists) ->
fs.unlink 'lib/buildr.js' if exists
fs.exists 'bin/buildr.js', (exists) ->
fs.unlink 'bin/buildr.js' if exists
compile = (sourceFile, needShebang) ->
fs.readFile sourceFile, 'utf8', (error, coffeeCode) ->
throw error if error
javaScript = CoffeeScript.compile(coffeeCode)
javaScript = '#!/usr/bin/env node\n' + javaScript if needShebang
outFile = sourceFile.replace '\.coffee', '.js'
fs.writeFile outFile, javaScript, 'utf8', () ->
console.log 'built ' + outFile + ' successfully.'
================================================
FILE: LICENSE.txt
================================================
Copyright (c) 2011, Benjamin Lupton
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# Welcome to Buildr
The (Java|Coffee)Script and (CSS|Less) (Builder|Bundler|Packer|Minifier|Merger|Checker)
## Install
1. [Install Node.js](http://bevry.me/node/install)
1. Install dependencies for image compression
- On OSX
ruby -e "$(curl -fsSLk https://gist.github.com/raw/323731/install_homebrew.rb)"
brew install gifsicle libjpeg optipng pngcrush
- On Apt Linux
sudo apt-get update && sudo apt-get install gifsicle libjpeg-progs optipng pngcrush
- On Yum Linux
sudo yum -y install gifsicle libjpeg-progs optipng pngcrush
- Windows
> Hahahahaha
## Configure
Before you use Buildr, you must specify some configuration for it. The available configuration is:
``` coffeescript
{
# Options
name: null # (name to be outputted in log messages) String or null
log: true # (log status updates to console?) true or false
watch: false # (automatically rebuild on file change?) true or false
# Handlers
buildHandler: false # (fired when build completed) function or false
rebuildHandler: false # (fired when rebuild completed) function or false
successHandler: false # (fired when (re)build completed successfully) function or false
# Paths
srcPath: false # String
outPath: false # String or false
# Checking
checkScripts: true # Array or true or false
checkStyles: true # Array or true or false
jshintOptions: false # Object or false
csslintOptions: false # Object or false
# Compression (requires outPath)
compressScripts: true # Array or true or false
compressStyles: true # Array or true or false
compressImages: true # Array or true or false
# Order
scriptsOrder: false # Array or false
stylesOrder: false # Array or false
# Bundling (requires Order)
bundleScriptPath: false # String or false
bundleStylePath: false # String or false
deleteBundledFiles: true # (requires outPath) true or false
# Loaders (requires Order)
srcLoaderHeader: false # String or false
srcLoaderPath: false # String or false
}
```
The above values are the default values for those options. The settings which are set to `true` will auto-detect the files for you.
### Options
There are currently two options available, the `log` and `watch` options.
- The `log` option when enabled will output all status messages, by default this is enabled.
- The `watch` option when enabled will allow buildr to run in the background watching for changes in our `srcPath`, if a change is detected then our project is automatically rebuilt for us, by default this is disabled.
### Handlers
There are two handlers you can configure, they are the `buildHandler` and the `rebuildHandler`.
- The `buildHandler` is fired after our project has been built.
- The `rebuildHandler` is fired after our project has been rebuilt. Our project is rebuilt when we utilise the `watch: true` config option, which scans for changes in the background and automatically rebuilds our project on change. If this isn't specified, then the `buildHandler` will automatically be used as the `rebuildHandler`.
They are both passed a single argument called `err` which is either an `Error` instance, or `false` if no error occurred. They both also have default values, so you don't need to specify them if you don't want to.
### Checking
To pass your scripts through jshint and your styles through csslint, you'd want the following configuration:
``` coffeescript
{
# Paths
srcPath: 'src' # String
# Checking
checkScripts: true # Array or true or false
checkStyles: true # Array or true or false
jshintOptions: false # Object or false
csslintOptions: false # Object or false
}
```
### Compression
To copy your `src` directory to an `out` directory, then compile and compress all your styles and scripts in the `out` directory, you'd want the following configuration:
``` coffeescript
{
# Paths
srcPath: 'src' # String
outPath: 'out' # String or false
# Compression (without outPath only the generated bundle files are compressed)
compressScripts: true # Array or true or false
compressStyles: true # Array or true or false
compressImages: true # Array or true or false
}
```
If your `outPath` is the same as your `srcPath` then the only files which will be compressed are the generated bundle files.
### Bundling
To bundle all your style files into one file called `out/bundled.css` and all your script files into one file called `out/bundled.js`, you'd want the following configuration:
``` coffeescript
{
# Paths
srcPath: 'src' # String
outPath: 'out' # String or false
# Order
scriptsOrder: [
'script1.js'
'script2.coffee'
] # Array or false
stylesOrder: [
'style1.css'
'style2.less'
] # Array or false
# Bundling (requires Order)
bundleScriptPath: false # String or false
bundleStylePath: false # String or false
deleteBundledFiles: true # (requires outPath) true or false
}
```
### Loaders
To generate a source loader file called `src/loader.js` which will load in all your source styles and scripts into the page, you can use the following:
``` coffeescript
{
# Paths
srcPath: 'src' # String
# Order
scriptsOrder: [
'script1.js'
'script2.coffee'
] # Array or false
stylesOrder: [
'style1.css'
'style2.less'
] # Array or false
# Loaders (requires Order)
srcLoaderHeader: '''
# Prepare
myprojectEl = document.getElementById('myproject-include')
myprojectBaseUrl = myprojectEl.src.replace(/\\?.*$/,'').replace(/loader\\.js$/, '').replace(/\\/+$/, '')+'/'
# Load in with Buildr
myprojectBuildr = new window.Buildr {
baseUrl: myprojectBaseUrl
beforeEl: myprojectEl
serverCompilation: window.serverCompilation or false
scripts: scripts
styles: styles
}
myprojectBuildr.load()
''' # note, all \ in this are escaped due to it being in a string
srcLoaderPath: 'src/myproject.loader.js' # String or false
}
```
Then include into your page with the following html:
``` html
<script id="myproject-include" src="../../loader.js"></script>
```
This is incredibly useful for developing apps which have lots of files, as instead of updating all your demo page's html with the new script and style files all the time, you just include the loader.
### Combining
You can feel free to combine any of the configurations above to get something which checks, compiles, compresses, bundles, and generates loaders too. Though compression and bundling is dependent on having an `outPath` which is different from your `srcPath`.
## Run
### As a Command Line Tool
Within your application folder
1. Install Buildr Globally
npm -g install buildr
2. Stick your configuration in `buildr.cson`
3. Run the global buildr
buildr
You may specify the filename for configuring by passing -f <filename> or --file <filename> on the command-line.
### As a Module
Within your application folder
1. Install Buildr Locally
npm install buildr
2. Code `buildr.coffee`
``` coffeescript
buildr = require 'buildr'
config = {} # your configuration
myBuildr = buildr.createInstance(config)
myBuildr.process (err) ->
throw err if err
console.log 'Building completed'
```
3. Run your buildr file
coffee buildr.coffee
## License
Licensed under the [MIT License](http://creativecommons.org/licenses/MIT/)
Copyright 2011 [Benjamin Arthur Lupton](http://balupton.com)
## History
### Changelog
- v0.8.7 March 24, 2013
- Fix issue 34
- Fix syntax error in CLI script which prevents error reporting.
- Copy source files recursively again (reverting v0.8.5 fix) which is safe because we switched to rimraf (to fix issue 34).
- v0.8.6 March 17, 2013
- Replace deprecated path.exists call with fs.exists.
- Fix build issue with Cakefile.
- Fix issue 32 and 33
- v0.8.5 November 11, 2012
- Fix problem with copying of hidden directories like .svn/
- v0.8.4 November 5, 2012
- Fix bug 31: Log level debug is always used, regardless of configuration.
- v0.8.3 October 28, 2012
- Feature request #13: specify .cson file at command line
- Feature request #15: Macro preprocessor
- Fix bugs 8, 19, 20, 21, 23, 25, 27, 28, 30
- Use cake to build JavaScript, making buildr easier to run
- Updated dependencies to fix several bugs
- v0.8 September 27, 2011
- Fixed concurrency support
- Fixed compression under certain configurations
- Added [Caterpillar](https://github.com/balupton/caterpillar.npm) for awesome console logging
- v0.7 August 22, 2011
- Added `watch`, `buildHandler` and `rebuildHandler` options
- v0.6 July 21, 2011
- v0.6.0 July 21, 2011
- Added javascript, image and css compression
- Added jshint and csslint checks
- v0.6.6 August 16, 2011
- Fixed relative paths between outPath and bundledPaths
- v0.5 July 9, 2011
- Added srcLoader compilation
- v0.4 July 1, 2011
- Extremely Simplified
- Only supports bundling of js|coffee and css|less files currently
- v0.3 May 31, 2011
- Exploration into better architectures
- v0.2 April 2, 2011
- Initial Release
- v0.1 March 23, 2011
- Initial Commit
### Todo
- Needs auto file finding for bundling/orders
- Needs no-config version
- Needs unit tests
================================================
FILE: bin/buildr.coffee
================================================
#!/usr/bin/env coffee
# Requires
cson = require 'cson'
fs = require 'fs'
path = require 'path'
buildr = require __dirname+'/../lib/buildr'
optimist = require 'optimist'
# Argument parsing
argv = optimist.options('file', {
alias: 'f',
default: 'buildr.cson'
}).argv;
filename = path.resolve argv.file
#Preprocessors
preprocessors = [
(data) ->
new_data = data
p = /##def\s+(\w+)\s*"(.*?)"/g
while m = p.exec data
console.log "--> " + m[0]
new_data = new_data.replace RegExp(m[1], 'g'), m[2]
new_data
]
data = ''
fs.exists filename, (exists) ->
if exists
fs.readFile filename, (err,d) ->
throw err if err
data = d.toString()
console.log "Preprocessing configuration file..."
for p in preprocessors
data = p data
console.log "Preprocessed."
# Parse the config file
cson.parse data, (err,config) ->
throw err if err
myBuildr = buildr.createInstance(config)
myBuildr.process (err) ->
throw err if err
else
console.error "Configuration file not found: #{filename}"
================================================
FILE: lib/buildr.coffee
================================================
# Requires
fs = require 'fs'
path = require 'path'
util = require 'bal-util'
coffee = require 'coffee-script'
less = require 'less'
pulverizr = require 'pulverizr'
csslint = require('csslint').CSSLint
jshint = require('jshint').JSHINT
uglify = require 'uglify-js'
jsp = uglify.parser
pro = uglify.uglify
cwd = process.cwd()
watchTree = false
caterpillar = require 'caterpillar'
rimraf = require 'rimraf'
# =====================================
# Prototypes
# Checks if an array contains a value
Array::has or= (value2) ->
for value1 in @
if value1 is value2
return true
return false
# =====================================
# Buildr
# Define
class Buildr
# Configuration
config: {
# Options
name: null # (name to be outputted in log messages) String or null
log: true # (log status updates to console?) true or false
watch: false # (automatically rebuild on file change?) true or false
# Handlers
buildHandler: false # (fired when build completed) function or false
rebuildHandler: false # (fired when rebuild completed) function or false
successHandler: false # (fired when (re)build completed successfully) function or false
# Paths
srcPath: false # String
outPath: false # String or false
# Checking
checkScripts: false # Array or true or false
checkStyles: false # Array or true or false
jshintOptions: false # Object or false
csslintOptions: false # Object or false
# Compression (without outPath only the generated bundle files are compressed)
compressScripts: false # Array or true or false
compressStyles: false # Array or true or false
compressImages: false # Array or true or false
# Order
scriptsOrder: false # Array or false
stylesOrder: false # Array or false
# Bundling (requires Order)
bundleScriptPath: false # String or false
bundleStylePath: false # String or false
deleteBundledFiles: true # (requires outPath) true or false
# Loaders (requires Order)
srcLoaderHeader: false # String or false
srcLoaderPath: false # String or false
}
# Files to clean
filesToClean: []
# Error
errors: []
# Watching
watching: false
# Processing
processing: false
# Logger
logger: false
# Constructor
constructor: (options={}) ->
# Prepare
@filesToClean = []
@errors = []
# Apply configuration
tmp = {}
tmp[key] = value for key,value of @config
tmp[key] = value for key,value of options
@config = tmp
# Handlers
@config.buildHandler or= (err) =>
if err
@log 'error', err
throw err
@log 'info', 'Building completed'
@config.successHandler.call(@) if @config.successHandler
@config.rebuildHandler or= (err) =>
if err
@log 'error', err
throw err
@log 'info', 'ReBuilding completed'
@config.successHandler.call(@) if @config.successHandler
# Logger
if @config.log is true then @config.log = 6
@logger or= @config.logger or new caterpillar.Logger
level: @config.log or 6
transports:
level: @config.log or 6
formatter:
module: module
# Completed
true
# =====================================
# Actions
# Log
log: (args...) =>
if @config.name
type = args.shift()
args.unshift "[#{@config.name}]"
args.unshift type
@logger.log.apply(@logger,args)
# Watch
watch: (next) ->
# Check
if @watching
return
else
@watching = true
# Requires
watchTree = require 'watch-tree-maintained' unless watchTree
# Prepare
buildr = @
log = @log
next or= @config.rebuildHandler or @config.buildHandler
# Log
log 'debug', 'Setting up watching...'
# Watch the src directory
watcher = watchTree.watchTree @config.srcPath
watcher.on 'fileDeleted', (path) ->
buildr.process ->
log 'info', 'Rebuilt due to file delete at '+(new Date()).toLocaleString()
next.apply(next,arguments)
watcher.on 'fileCreated', (path,stat) ->
buildr.process ->
log 'info', 'Rebuilt due to file create at '+(new Date()).toLocaleString()
next.apply(next,arguments)
watcher.on 'fileModified', (path,stat) ->
buildr.process ->
log 'info', 'Rebuilt due to file change at '+(new Date()).toLocaleString()
next.apply(next,arguments)
# Log
log 'debug', 'Watching setup'
# Next
next false
# Process
process: (next) ->
# Prepare
log = @log
next or= @config.buildHandler
# Check
if @processing
log 'info', 'Processing postponed'
return
@processing = true
# Log
log 'info', 'Processing started'
# Watch
@watch() if @config.watch
# Check configuration
@checkConfiguration (err) =>
return next err if err
# Check files
@checkFiles (err) =>
return next err if err
# Copy srcPath to outPath
@cpSrcToOut (err) =>
return next err if err
# Generate files
@generateFiles (err) =>
return next err if err
# Clean outPath
@cleanOutPath (err) =>
return next err if err
# Compress outPath
@compressFiles (err) =>
@processing = false
log 'info', 'Processing finished'
next err
# ---------------------------------
# Check Configuration
# Check Configuration
# next(err)
checkConfiguration: (next) ->
# Check
return next new Error('srcPath is required') unless @config.srcPath
# Prepare
log = @log
tasks = new util.Group (err) =>
log 'debug', 'Checked configuration'
next err
tasks.total = 6
log 'debug', 'Checking configuration'
# Ensure
@config.outPath or= @config.srcPath
# Expand srcPath
@config.srcPath = path.resolve @config.srcPath
tasks.complete null
# Expand outPath
@config.outPath = path.resolve @config.outPath
tasks.complete null
# Expand bundleScriptPath
if @config.bundleScriptPath
@config.bundleScriptPath = path.resolve @config.bundleScriptPath
tasks.complete null
else
tasks.complete false
# Expand bundleStylePath
if @config.bundleStylePath
@config.bundleStylePaths = path.resolve @config.bundleStylePath
tasks.complete null
else
tasks.complete false
# Expand srcLoaderPath
if @config.srcLoaderPath
@config.srcLoaderPath = path.resolve @config.srcLoaderPath
tasks.complete null
else
tasks.complete false
# Adjust Atomic Options
if @config.srcPath is @config.outPath
@config.deleteBundledFiles = false
if @config.compressScripts
@config.compressScripts =
if @config.bundleScriptPath
[@config.bundleScriptPath]
else
false
if @config.compressStyles
@config.compressStyles =
if @config.bundleStylePath
[@config.bundleStylePath]
else
false
if @config.compressImages
@config.compressImages = false
# Auto find files?
# Not yet implemented
if @config.bundleScripts is true
@config.bundleScripts = false
if @config.bundleStyles is true
@config.bundleStyles = false
# Finish
tasks.complete false
# ---------------------------------
# Check Files
# Check files
# next(err)
checkFiles: (next,config) ->
# Prepare
log = @log
config or= @config
return next false unless config.checkScripts or config.checkStyles
log 'debug', 'Check files'
# Handle
@forFilesInDirectory(
# Directory
config.srcPath
# Callback
(fileFullPath,fileRelativePath,next) =>
# Render
@checkFile fileFullPath, next
# Next
(err) ->
return next err if err
log 'debug', 'Checked files'
next err
)
# Completed
true
# ---------------------------------
# Copy srcPath to outPath
# Copy srcPath to outPath
# next(err)
cpSrcToOut: (next,config) ->
# Prepare
log = @log
config or= @config
return next false if config.outPath is config.srcPath
log 'debug', "Copying #{config.srcPath} to #{config.outPath}"
# Remove outPath
rimraf config.outPath, (err) ->
return next err if err
# Copy srcPath to outPath
util.cpdir config.srcPath, config.outPath, (err) ->
# Next
log 'debug', "Copied #{config.srcPath} to #{config.outPath}"
next err
# Completed
true
# ---------------------------------
# Generate Files
# Generate files
# next(err)
generateFiles: (next) ->
# Prepare
log = @log
tasks = new util.Group (err) ->
log 'debug', 'Generated files'
next err
tasks.total += 3
log 'debug', 'Generating files'
# Generate src loader file
@generateSrcLoaderFile tasks.completer()
# Generate bundled script file
@generateBundledScriptFile tasks.completer()
# Generate bundle style file
@generateBundledStyleFile tasks.completer()
# Completed
true
# Generate src loader file
# next(err)
generateSrcLoaderFile: (next,config) ->
# Check
config or= @config
return next false unless config.srcLoaderPath
# Log
log = @log
log 'debug', "Generating #{config.srcLoaderPath}"
# Prepare
templates = {}
srcLoaderData = ''
srcLoaderPath = config.srcLoaderPath
loadedInTemplates = null
# Loaded in Templates
templateTasks = new util.Group (err) =>
# Check
next err if err
# Stringify scripts
srcLoaderData += "scripts = [\n"
for script in config.scriptsOrder
srcLoaderData += "\t'#{script}'\n"
srcLoaderData += "\]\n\n"
# Stringify styles
srcLoaderData += "styles = [\n"
for style in config.stylesOrder
srcLoaderData += "\t'#{style}'\n"
srcLoaderData += "\]\n\n"
# Append Templates
srcLoaderData += templates.srcLoader+"\n\n"+templates.srcLoaderHeader
# Write in coffee first for debugging
fs.writeFile srcLoaderPath, srcLoaderData, (err) ->
# Check
return next err if err
# Compile Script
srcLoaderData = coffee.compile(srcLoaderData)
# Now write in javascript
fs.writeFile srcLoaderPath, srcLoaderData, (err) ->
# Check
return next err if err
# Log
log 'debug', "Generated #{config.srcLoaderPath}"
# Good
next false
# Total Template Tasks
templateTasks.total = if config.srcLoaderHeader then 1 else 2
# Load srcLoader Template
fs.readFile __dirname+'/templates/srcLoader.coffee', (err,data) ->
return templateTasks.exit err if err
templates.srcLoader = data.toString()
templateTasks.complete err
# Load srcLoaderHeader Template
if config.srcLoaderHeader
templates.srcLoaderHeader = config.srcLoaderHeader
else
fs.readFile __dirname+'/templates/srcLoaderHeader.coffee', (err,data) ->
return templateTasks.exit err if err
templates.srcLoaderHeader = data.toString()
templateTasks.complete err
# Completed
true
# Generate out style file
# next(err)
generateBundledStyleFile: (next,config) ->
# Check
log = @log
config or= @config
return next false unless config.bundleStylePath
# Log
log 'debug', "Generating #{config.bundleStylePath}"
# Prepare
source = ''
# Cycle
@useOrScan(
# Files
config.stylesOrder
# Directory
@config.outPath
# Callback
(fileFullPath, fileRelativePath, next) =>
# Ensure .less file exists
extension = path.extname(fileRelativePath)
switch extension
# CSS
when '.css'
# Determine less path
_fileRelativePath = fileRelativePath
_fileFullPath = fileFullPath
fileRelativePath = _fileRelativePath.substring(0,_fileRelativePath.length-extension.length)+'.less'
fileFullPath = _fileFullPath.substring(0,_fileFullPath.length-extension.length)+'.less'
# Amend clean files
if config.deleteBundledFiles
@filesToClean.push _fileFullPath
@filesToClean.push fileFullPath
# Get relative bundled path
bundledRelativePath = path.relative path.dirname(path.resolve(config.bundleStylePath)), fileFullPath
# Check if less path exists
fs.exists fileFullPath, (exists) ->
# It does
if exists
# Append source
source += """@import "#{bundledRelativePath}";\n"""
next false
# It doesn't
else
# Create it
fs.readFile _fileFullPath, (err, data) ->
# Escape Microsoft filter syntax so it will get past the less compiler.
# See issue 32 https://github.com/balupton/buildr.npm/issues/32
data = data.toString().replace(/filter: ([^~"])(.*)$/gm, "filter: ~\"$1$2\"")
fs.writeFile fileFullPath, data, (err) ->
return next err if err
# Append source
source += """@import "#{bundledRelativePath}";\n"""
next false
# Less
when '.less'
# Amend clean files
if config.deleteBundledFiles
@filesToClean.push fileFullPath
# Get relative bundled path
bundledRelativePath = path.relative path.dirname(path.resolve(config.bundleStylePath)), fileFullPath
# Append source
source += """@import "#{bundledRelativePath}";\n"""
next false
# Something else
else
next false
# Next
(err) =>
return next err if err
# Log
log 'debug', "Compiling #{config.bundleStylePath}"
# Compile file
@compileStyleData(
# File Path
config.bundleStylePath
# Source
source
# Next
(err,result) ->
return next err if err
# Write
fs.writeFile config.bundleStylePath, result, (err) ->
# Log
log 'debug', "Generated #{config.bundleStylePath}"
# Forward
next err, result
)
)
# Completed
true
# Generate out script file
# next(err)
generateBundledScriptFile: (next,config) ->
# Check
log = @log
config or= @config
return next false unless config.bundleScriptPath
# Log
log 'debug', "Generating #{config.bundleScriptPath}"
# Prepare
results = {}
# Cycle
@useOrScan(
# Files
config.scriptsOrder
# Directory
config.outPath
# Callback
(fileFullPath,fileRelativePath,next) =>
# Ensure valid extension
extension = path.extname(fileRelativePath)
switch extension
# Script
when '.js','.coffee'
# Render
@compileScriptFile(
# File path
fileFullPath
# Next
(err,result) =>
return next err if err
results[fileRelativePath] = result
if config.deleteBundledFiles
@filesToClean.push fileFullPath
next err
# Write file
false
)
# Else
else
next false
# Next
(err) ->
return next err if err
# Prepare
result = ''
# Cycle Array
if config.scriptsOrder.has?
for fileRelativePath in config.scriptsOrder
return next new Error("The file #{fileRelativePath} failed to compile") unless results[fileRelativePath]?
result += results[fileRelativePath]
# Write file
fs.writeFile config.bundleScriptPath, result, (err) ->
# Log
log 'debug', "Generated #{config.bundleScriptPath}"
# Forward
next err
)
# Completed
true
# ---------------------------------
# Clean outPath
# Clean outPath
# next(err)
cleanOutPath: (next) ->
# Check
return next false unless (@filesToClean||[]).length
# Prepare
log = @log
tasks = new util.Group (err) ->
log 'debug', 'Cleaned outPath'
next err
tasks.total += @filesToClean.length
log 'debug', 'Cleaning outPath'
# Delete files to clean
while @filesToClean.length > 0
# Be sure to clean up the array as we go through it because this object gets reused in watch mode.
fileFullPath = @filesToClean.shift()
log 'debug', "Cleaning #{fileFullPath}"
fs.unlink fileFullPath, tasks.completer()
# Completed
true
# ---------------------------------
# Compress Files
# Compress files
# next(err)
compressFiles: (next,config) ->
# Prepare
config or= @config
return next false unless config.compressScripts or config.compressStyles or config.compressImages
# Prepare
log = @log
tasks = new util.Group (err) ->
log 'debug', 'Compressed files'
next err
tasks.total += 1
log 'debug', 'Compressing files'
# Bundled Files
if config.compressScripts is true
if config.bundleScriptPath
++tasks.total
@compressFile config.bundleScriptPath, tasks.completer()
if config.bundleStylePath
++tasks.total
@compressFile config.bundleStylePath, tasks.completer()
# Handle
@useOrScan(
# Array
config.compressScripts
# Directory
config.outPath
# Callback
(fileFullPath,fileRelativePath,next) =>
# Render
@compressFile fileFullPath, next
# Next
tasks.completer()
)
# Completed
true
# =====================================
# Helpers
# For each file in an array
# callback(fileFullPath,fileRelativePath,next)
# next(err)
forFilesInArray: (files,parentPath,callback,next) ->
# Check
return next false unless (files||[]).length
# Prepare
log = @log
tasks = new util.Group (err) =>
next err
tasks.total += files.length
# Cycle
for fileRelativePath in files
# Expand filePath
callback path.resolve(parentPath, fileRelativePath),fileRelativePath, tasks.completer()
# Completed
true
# For each file in a directory
# callback(fileFullPath,fileRelativePath,next)
# next(err)
forFilesInDirectory: (parentPath,callback,next) ->
# Scan for files
util.scandir(
# Path
parentPath
# File Action
# next(err)
callback
# Dir Action
false
# Next
next
)
# Completed
true
# Use or scan
# callback(fileFullPath,fileRelativePath,next)
# next(err)
useOrScan: (files,parentPath,callback,next) ->
# Handle
if files is true
@forFilesInDirectory(
# Directory
parentPath
# Callback
callback
# Next
next
)
else if files and files.length
@forFilesInArray(
# Files
files
# Directory
parentPath
# Callback
callback
# Next
next
)
else
next false
# Completed
true
# =====================================
# Files
# Compile the file
# next(err)
compileFile: (fileFullPath,next) ->
# Prepare
extension = path.extname fileFullPath
# Handle
switch extension
when '.coffee'
@compileScriptFile fileFullPath, next
when '.less'
@compileStyleFile fileFullPath, next
else
next false
# Completed
true
# Compress the file
# next(err)
compressFile: (fileFullPath,next,config) ->
# Prepare
config or= @config
extension = path.extname fileFullPath
# Handle
switch extension
# Scripts
when '.js'
if config.compressScripts is true or config.compressScripts.has? and config.compressScripts.has(fileFullPath)
@compressScriptFile fileFullPath, next
else
next false
# Styles
when '.css'
if config.compressStyles is true or config.compressStyles.has? and config.compressStyles.has(fileFullPath)
@compressStyleFile fileFullPath, next
else
next false
# Images
when '.gif','.jpg','.jpeg','.png','.tiff','.bmp'
if config.compressImages is true or config.compressImages.has? and config.compressImages.has(fileFullPath)
@compressImageFile fileFullPath, next
else
next false
# Other
else
next false
# Completed
true
# Check the file
# next(err)
checkFile: (fileFullPath,next,config) ->
# Prepare
config or= @config
extension = path.extname fileFullPath
# Handle
switch extension
when '.js'
if config.checkScripts is true or config.checkScripts.has? and config.checkScripts.has(fileFullPath)
@checkScriptFile fileFullPath, next
else
next false
when '.css'
if config.checkStyles is true or config.checkStyles.has? and config.checkStyles.has(fileFullPath)
@checkStyleFile fileFullPath, next
else
next false
else
next false
# Completed
true
# =====================================
# Image Files
# ---------------------------------
# Compress
# Compress Image File
# next(err)
compressImageFile: (fileFullPath,next) ->
# Prepare
log = @log
# Log
log 'debug', "Compressing #{fileFullPath}"
# Attempt
try
# Compress
pulverizr.compress fileFullPath, quiet: true
# Log
log 'debug', "Compressed #{fileFullPath}"
# Forward
next false
# Error
catch err
# Forward
next err
# Complete
true
# =====================================
# Style Files
# ---------------------------------
# Compile
# Compile Style File
# next(err,result)
compileStyleData: (fileFullPath,src,next) ->
# Prepare
log = @log
result = ''
options =
paths: [path.dirname(fileFullPath)]
optimization: 1
filename: fileFullPath
# Compile
new (less.Parser)(options).parse src, (err, tree) ->
if err
log 'debug', err
next new Error('Less compilation failed'), result
else
try
# Compile
result = tree.toCSS compress: 0
# Write
next false, result
catch err
next err, result
# Completed
true
# Compile Style File
# next(err,result)
compileStyleFile: (fileFullPath,next,write=true) ->
# Prepare
log = @log
# Log
# log 'debug', "Compiling #{fileFullPath}"
# Read
fs.readFile fileFullPath, (err,data) =>
return next err if err
# Compile
@compileStyleData fileFullPath, data.toString(), (err,result) ->
return next err, result if err or !write
# Write
fs.writeFile fileFullPath, result, (err) ->
return next err if err
# Log
# log 'debug', "Compiled #{fileFullPath}"
# Forward
next err, result
# Completed
true
# ---------------------------------
# Compress
# Compress Style File
# next(err,result)
compressStyleData: (fileFullPath,src,next) ->
# Prepare
log = @log
result = ''
options =
paths: [path.dirname(fileFullPath)]
optimization: 1
filename: fileFullPath
# Compress
new (less.Parser)(options).parse src, (err, tree) ->
if err
log 'debug', err
next new Error('Less compilation failed'), result
else
try
# Compress
result = tree.toCSS compress: 1
# Write
next false, result
catch err
next err, result
# Completed
true
# Compress Style File
# next(err,result)
compressStyleFile: (fileFullPath,next,write=true) ->
# Prepare
log = @log
# Log
log 'debug', "Compressing #{fileFullPath}"
# Read
fs.readFile fileFullPath, (err,data) =>
return next err if err
# Compress
@compressStyleData fileFullPath, data.toString(), (err,result) ->
return next err, result if err or !write
# Write
fs.writeFile fileFullPath, result, (err) ->
return next err if err
# Log
log 'debug', "Compressed #{fileFullPath}"
# Forward
next err, result
# Completed
true
# ---------------------------------
# Check
# Check Style Data
# next(err,errord)
checkStyleData: (fileFullPath,src,next,config) ->
# Prepare
log = @log
config or= @config
errord = false
# Peform checks
result = csslint.verify src, config.csslintOptions||{}
formatId = 'text'
# Check for errors
unless result.messages.length
return next false, false
# Log the errors
for message in result.messages
continue unless message and message.type is 'error'
# Errord
errord = true
# Output
if errord
log 'error', csslint.getFormatter(formatId).formatResults(result, fileFullPath, formatId)
# Forward
next false, errord
# Check Style File
# next(err,errord)
checkStyleFile: (fileFullPath,next) ->
# Prepare
log = @log
# Log
log 'debug', "Checking #{fileFullPath}"
# Read
fs.readFile fileFullPath, (err,data) =>
# Error
return next err, false if err
# Check
@checkStyleData fileFullPath, data.toString(), (err,errord) ->
return next err if err
# Log
log 'debug', "Checked #{fileFullPath}"
# Forward
return next err, errord
# Completed
true
# =====================================
# Script Files
# ---------------------------------
# Compile
# Compile Script Data
# next(err,result)
compileScriptData: (extension,src,next) ->
# Prepare
result = false
# Compile
try
switch extension
when '.coffee'
result = coffee.compile src
when '.js'
result = src
else
throw new Error('Unknown script type: '+extension)
catch err
next err
# Forward
next false, result
# Compile Script File
# next(err,result)
compileScriptFile: (fileFullPath,next,write=true) ->
# Prepare
log = @log
# Log
# log 'debug', "Compiling #{fileFullPath}"
# Read
fs.readFile fileFullPath, (err,data) =>
return next err if err
# Compile
@compileScriptData path.extname(fileFullPath), data.toString(), (err,result) ->
return next err, result if err or !write
# Write
fs.writeFile fileFullPath, result, (err) ->
return next err if err
# Log
# log 'debug', "Compiled #{fileFullPath}"
# Forward
next err, result
# Completed
true
# ---------------------------------
# Compress
# Compress Script Data
# next(err,result)
compressScriptData: (src,next) ->
# Compress
ast = jsp.parse(src) # parse code and get the initial AST
ast = pro.ast_mangle(ast) # get a new AST with mangled names
ast = pro.ast_squeeze(ast) # get an AST with compression optimizations
out = pro.gen_code(ast) # compressed code here
# Forward
return next false, out
# Compress Script File
# next(err,result)
compressScriptFile: (fileFullPath,next,write=true) ->
# Prepare
log = @log
# Log
log 'debug', "Compressing #{fileFullPath}"
# Read
fs.readFile fileFullPath, (err,data) =>
return next err if err
# Compile
@compressScriptData data.toString(), (err,result) ->
return next err, result if err or !write
# Write
fs.writeFile fileFullPath, result, (err) ->
return next err if err
# Log
log 'debug', "Compressed #{fileFullPath}"
# Forward
next err, result
# Completed
true
# ---------------------------------
# Check
# Check Script Data
# next(err,errord)
checkScriptData: (fileFullPath,src,next,config) ->
# Prepare
log = @log
config or= @config
errord = false
# Peform checks
jshint src, config.jshintOptions||{}
result = jshint.data()
result.errors or= []
# Check for errors
unless result.errors.length
return next false, false
# Log the file
log 'error', "\n#{fileFullPath}:"
# Log the errors
for error in result.errors
continue unless error and error.raw
# Errord
errord = true
# Log
message = error.raw.replace(/\.$/,'').replace /\{([a-z])\}/, (a,b) ->
error[b] or a
evidence =
if error.evidence
"\n\t" + error.evidence.replace(/^\s+/, '')
else
''
log 'warn', "\tLine #{error.line}: #{message} #{evidence}\n"
# Forward
next false, errord
# Check Script File
# next(err,errord)
checkScriptFile: (fileFullPath,next) ->
# Prepare
log = @log
# Log
log 'debug', "Checking #{fileFullPath}"
# Read
fs.readFile fileFullPath, (err,data) =>
# Error
return next err, false if err
# Check
@checkScriptData fileFullPath, data.toString(), (err,errord) ->
return next err if err
# Log
log 'debug', "Checked #{fileFullPath}"
# Forward
return next err, errord
# Completed
true
# =====================================
# Export
module.exports =
createInstance: (options) ->
return new Buildr(options)
================================================
FILE: lib/templates/srcLoader.coffee
================================================
# Buildr
window.Buildr ?= class
# Options
scripts: null
styles: null
baseUrl: null
appendEl: null
beforeEl: null
serverCompilation: null
# Construct a new Buildr instance
constructor: ({scripts,styles,appendEl,baseUrl,beforeEl,serverCompilation}) ->
@scripts = scripts or []
@styles = styles or []
@appendEl = appendEl or document.head or document.getElementsByTagName('head')[0]
@baseUrl = baseUrl or @getRootUrl()
@beforeEl = beforeEl or document.head.lastChild
@serverCompilation = serverCompilation or false
# Get the root url of our page
getRootUrl: ->
# Prepare
host = (document.location.hostname||document.location.host)
protocol = document.location.protocol
rootUrl = "#{protocol}//#{host}"
# Port
if document.location.port
rootUrl += ':'+document.location.port
rootUrl += '/'
# Return
rootUrl
# Load Styles and Scripts
load: (next) ->
me = @
me.loadStyle ->
me.loadScript ->
next() if next
# Script Loader
loadScriptIndex: 0
loadScript: (next) ->
# Prepare
me = @
scriptSrc = @baseUrl + @scripts[@loadScriptIndex]
scriptSrc += '?js' if @serverCompilation
scriptLoaded = ->
if @readyState? and @readyState isnt 'complete'
return
if @src? and @src isnt scriptSrc
return
++me.loadScriptIndex
me.loadScript next
# Exists
if @scripts[@loadScriptIndex]?
scriptEl = document.createElement('script')
scriptEl.src = scriptSrc
if /\.coffee$/.test(scriptSrc)
scriptEl.type = 'text/coffeescript'
else
scriptEl.onreadystatechange = scriptLoaded
scriptEl.onload = scriptLoaded
scriptEl.onerror = scriptLoaded
@appendEl.appendChild scriptEl, @beforeEl.nextSibling
@beforeEl = scriptEl
if /\.coffee$/.test(scriptSrc)
scriptLoaded()
# Completed
else
next()
# Return
true
# Style Loader
loadStyleIndex: 0
loadStyle: (next) ->
# Prepare
me = @
styleHref = @baseUrl + @styles[@loadStyleIndex]
styleHref += '?css' if @serverCompilation
styleLoaded = ->
++me.loadStyleIndex
me.loadStyle next
# Exists
if @styles[@loadStyleIndex]?
styleEl = document.createElement('link')
styleEl.href = styleHref
styleEl.media = 'screen'
if /\.less$/.test(styleHref)
styleEl.rel = 'stylesheet/less'
else
styleEl.rel = 'stylesheet'
styleEl.type = 'text/css'
#styleEl.onreadystatechange = styleLoaded
#styleEl.onload = styleLoaded
#styleEl.onerror = styleLoaded
@appendEl.insertBefore styleEl, @beforeEl.nextSibling
@beforeEl = styleEl
styleLoaded()
# Completed
else
next()
# Return
true
================================================
FILE: lib/templates/srcLoaderHeader.coffee
================================================
# Load in with Buildr
myBuildr = new window.Buildr {
scripts: scripts
styles: styles
}
myBuildr.load()
================================================
FILE: package.json
================================================
{
"name": "buildr",
"version": "0.8.7",
"description": "The (Java|Coffee)Script and (CSS|Less) (Builder|Bundler|Packer|Minifier|Merger|Checker)",
"homepage": "https://github.com/balupton/buildr.npm",
"keywords": [
"javascript",
"coffee",
"lesscss",
"less",
"css",
"builder",
"package",
"compile",
"compress",
"minify",
"bundle",
"merge",
"lint"
],
"author": {
"name": "Benjamin Lupton",
"email": "b@lupton.cc",
"web": "http://balupton.com"
},
"maintainers": [
{
"name": "Benjamin Lupton",
"email": "b@lupton.cc",
"web": "http://balupton.com"
},
{
"name": "Brandon Ramirez",
"email": "brandon.s.ramirez@gmail.com",
"web": "http://www.brandonsramirez.com/"
}
],
"contributors": [
{
"name": "Benjamin Lupton",
"email": "b@lupton.cc",
"web": "http://balupton.com"
},
{
"name": "Brandon Ramirez",
"email": "brandon.s.ramirez@gmail.com",
"web": "http://www.brandonsramirez.com/"
}
],
"bugs": {
"web": "https://github.com/balupton/buildr.npm/issues"
},
"licenses": [
{
"type": "MIT",
"url": "http://creativecommons.org/licenses/MIT/"
}
],
"repository" : {
"type" : "git",
"url" : "http://github.com/balupton/buildr.npm.git"
},
"dependencies": {
"less": "1.3.x",
"coffee-script": "1.4.x",
"bal-util": "1.x",
"jshint": "0.9.x",
"csslint": "0.9.x",
"uglify-js": "1.3.x",
"pulverizr": "0.7.x",
"cson": ">=1.4",
"watch-tree-maintained": "0.1.x",
"caterpillar": "1.x",
"optimist": ">=0.3",
"rimraf": "2.1.4"
},
"engines" : {
"node": ">=0.4.0"
},
"scripts": {
"prepublish": "cake clean build"
},
"directories": {
"lib": "lib"
},
"bin": {
"buildr": "./bin/buildr.js"
},
"main": "./lib/buildr.js"
}
gitextract_2gp6xz3k/ ├── .gitignore ├── .npmignore ├── Cakefile ├── LICENSE.txt ├── README.md ├── bin/ │ └── buildr.coffee ├── lib/ │ ├── buildr.coffee │ └── templates/ │ ├── srcLoader.coffee │ └── srcLoaderHeader.coffee └── package.json
Condensed preview — 10 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (51K chars).
[
{
"path": ".gitignore",
"chars": 55,
"preview": "node_modules/\nbin/buildr.js\nlib/buildr.js\nbuildr-*.tgz\n"
},
{
"path": ".npmignore",
"chars": 83,
"preview": "node_modules/\nbin/buildr.coffee\nlib/buildr.coffee\n.gitignore\nCakefile\nbuildr-*.tgz\n"
},
{
"path": "Cakefile",
"chars": 815,
"preview": "fs = require 'fs'\nCoffeeScript = require 'coffee-script'\n\ntask 'build', 'compile source files to javascript', (options)..."
},
{
"path": "LICENSE.txt",
"chars": 1060,
"preview": "Copyright (c) 2011, Benjamin Lupton\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of thi..."
},
{
"path": "README.md",
"chars": 9086,
"preview": "# Welcome to Buildr\n\nThe (Java|Coffee)Script and (CSS|Less) (Builder|Bundler|Packer|Minifier|Merger|Checker)\n\n\n## Instal..."
},
{
"path": "bin/buildr.coffee",
"chars": 1221,
"preview": "#!/usr/bin/env coffee\n\n# Requires\ncson = require 'cson'\nfs = require 'fs'\npath = require 'path'\nbuildr = require __dirna..."
},
{
"path": "lib/buildr.coffee",
"chars": 27371,
"preview": "# Requires\nfs = require 'fs'\npath = require 'path'\nutil = require 'bal-util'\ncoffee = require 'coffee-script'\nless = req..."
},
{
"path": "lib/templates/srcLoader.coffee",
"chars": 2593,
"preview": "# Buildr\nwindow.Buildr ?= class\n\t# Options\n\tscripts: null\n\tstyles: null\n\tbaseUrl: null\n\tappendEl: null\n\tbeforeEl: null..."
},
{
"path": "lib/templates/srcLoaderHeader.coffee",
"chars": 105,
"preview": "# Load in with Buildr\nmyBuildr = new window.Buildr {\n\tscripts: scripts\n\tstyles: styles\n}\nmyBuildr.load()\n"
},
{
"path": "package.json",
"chars": 1744,
"preview": "{\n\t\"name\": \"buildr\",\n\t\"version\": \"0.8.7\",\n\t\"description\": \"The (Java|Coffee)Script and (CSS|Less) (Builder|Bundler|Packe..."
}
]
About this extraction
This page contains the full source code of the balupton/buildr.npm GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 10 files (43.1 KB), approximately 12.8k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.