Full Code of balupton/buildr.npm for AI

master fbf44577d849
10 files
43.1 KB
12.8k tokens
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.

Copied to clipboard!