UNPKG

browser-build

Version:

Makes commonjs modules available in the browser via window.require('module-name')

250 lines (222 loc) 9.26 kB
require 'sugar' utility = require "./utility" watcher = require "./watcher" np = require "path" fs = require 'fs' log = (config, message) -> console.log message unless config.silent normalize = (file) -> file.replace /\\/g, "\/" getInputKeyValueForFile = (config, file) -> # normalize file name to / format file = normalize file for name, input of config.input when Object.isString input if file.startsWith input return [name,input] throw new Error "input not found in config.input: #{file}" getInputDirectory = (config, file) -> getInputKeyValueForFile(config, file)[1] getBaseModuleName = (config, file) -> getInputKeyValueForFile(config, file)[0] getOutputModuleDirectory = (config) -> np.join config.output.directory, "modules" getOutputFile = (config, file, id) -> file = np.normalize np.join(getOutputModuleDirectory(config), id) + ".js" deleteFile = (file) -> if fs.existsSync file fs.unlinkSync file buildBrowserTestFile = (config) -> type = config.output.test return unless type? testFolder = np.join(__dirname, "../test", type) if not fs.existsSync testFolder throw new Error "Test files for #{type} not found, expected at #{testFolder}" # copy this folder to the output test folder outputFolder = np.join config.output.directory, "test" # copy our test files to the output folder utility.copy testFolder, outputFolder copySourceMap = (config, inputFile, outputFile, deleteOutput = false) -> mapInput = inputFile.replace /\.js$/, ".map" mapOutput = outputFile.replace /\.js$/, ".map" if fs.existsSync mapInput try map = JSON.parse utility.read mapInput catch e # this can happen sometimes if we're reading # while this file is getting written by coffee -c map = sourceRoot: "." sources: [] # modify the sourceRoot and sources # to expect the source to be in the same directory # find and copy each source to the output directory for source, index in map.sources source = np.join np.dirname(mapInput), map.sourceRoot, source copyTo = np.join np.dirname(mapOutput), shortName = source.split(/[\/\\]/g).pop() if deleteOutput deleteFile copyTo else if not fs.existsSync source # console.warn "Source not found: #{mapInput} -> #{source}" continue utility.copy source, copyTo # also change the source reference to the shortName map.sources[index] = shortName # finally, make the source root the current directory map.sourceRoot = "." # then write out the sourceMap inputFile if deleteOutput deleteFile mapInput deleteFile mapOutput else utility.write mapOutput, JSON.stringify map, null, ' ' buildMap = {} buildFile = (config, file, id) -> # check build map to avoid duplicates previousFile = buildMap[id] if previousFile? and previousFile isnt file return buildMap[id] = file outputFile = getOutputFile config, file, id if not fs.existsSync file # delete source map files copySourceMap config, file, outputFile, true deleteFile outputFile return input = utility.read file output = "(function(){require.register('#{id}',function(module,exports,require){#{input}\n})})()" utility.write outputFile, output log config, "Wrapped #{outputFile}" copySourceMap config, file, outputFile buildIncludes = (config) -> list = utility.list getOutputModuleDirectory(config), {include:".js"} list = list.map (x) -> normalize np.relative(config.output.directory, x) # sort by shallowest directory, then alphabetical list = list.sort (a, b) -> aa = a.split '/' bb = b.split '/' if aa.length != bb.length return aa.length - bb.length for aitem, index in aa bitem = bb[index] compare = aitem.localeCompare bitem if compare != 0 return compare return 0 script = "" # figure out the include base relative to the web root. webroot = config.output.webroot ? config.output.directory base = np.relative(webroot, config.output.directory).replace(/\\/g, '\/') base = "/#{base}/".replace(/\/+/, '/') for file in list script += """document.writeln("<script src='#{base}#{file}'></script>");\n""" includeFile = getDebugIncludeFile config utility.write includeFile, script log config, "Created #{includeFile}" # also build the manifest manifest = getDebugManifestFile config manifestParent = np.dirname manifest manifestList = list.map (x) -> normalize np.relative manifestParent, np.join(config.output.directory, x) utility.write getDebugManifestFile(config), JSON.stringify(manifestList, null, " ") # also build the test file buildBrowserTestFile config getDebugIncludeFile = (config) -> np.join config.output.directory, "debug.js" getDebugManifestFile = (config) -> np.join config.output.directory, "modules/manifest.json" copyRequire = (config) -> # copy the require from our source to the output directory source = np.join __dirname, 'require.js' # in case we're running from .coffee sources if not fs.existsSync source source = np.join __dirname, '../lib/require.js' target = np.join getOutputModuleDirectory(config), 'require.js' utility.copy source, target log config, "Copied #{target}" check = (config) -> config.input ?= {"": true} resolve = (root, module, source) -> paths = [ root np.join(root, "node_modules") np.join(root, "../node_modules") np.join(root, "../../node_modules") ] if process.env.NODE_PATH? paths = paths.concat process.env.NODE_PATH.split np.delimiter for path in paths main = np.join path, module + "/package.json" if fs.existsSync main json = eval "(#{utility.read main})" module = json.name if json.dependencies? for name of json.dependencies fixOptions name, true, np.dirname(main), main main = np.join np.dirname(main), (json.main ? "index.js") return [module,main] main = np.join path, module + ".js" if fs.existsSync main return [module,main] main = np.join path, module + "/index.js" if fs.existsSync main return [module,main] throw new Error "module not found: " + module + ", source: " + source fixOptions = (name, options, root, source) -> # remove original value delete config.input[name] if Object.isString options options = name: name main: 'index.js' directory: np.normalize options else if options is true [name,main] = resolve root, name, source options = name: name main: np.basename main directory: np.dirname main # set new value config.input[name] = options for key, value of config.input fixOptions key, value, '.', 'config' return buildCommon = (config) -> check config copyRequire config getModuleId = (inputConfig, file) -> relative = np.relative inputConfig.directory, file if relative is inputConfig.main relative = "index.js" # remove extension relative = relative.slice 0, -".js".length normalize np.join inputConfig.name, relative exclude = (file) -> if /WEB-INF/.test file return true return false exports.build = (config, callback) -> buildCommon config for name, input of config.input list = utility.list input.directory, {include: ".js"} for file in list when not exclude file id = getModuleId input, file buildFile config, file, id buildIncludes config callback?() watchInput = (config, input) -> watcher.watchDirectory input.directory, {include: ".js", initial:false}, (file, curr, prev, change) -> if exclude file return id = getModuleId input, file buildFile config, file, id if change is "deleted" or change is "created" buildIncludes config else # touch the manifest file so others can fs.watch it utility.touch getDebugManifestFile config exports.watch = (config) -> exports.build config for name, input of config.input watchInput config, input # re-export utility and watcher exports.utility = utility exports.watcher = watcher