UNPKG

noflo

Version:

Flow-Based Programming environment for JavaScript

189 lines (163 loc) 6.18 kB
# NoFlo - Flow-Based Programming for JavaScript # (c) 2013-2016 TheGrid (Rituwall Inc.) # (c) 2013 Henri Bergius, Nemein # NoFlo may be freely distributed under the MIT license # # This is the Node.js version of the ComponentLoader. {_} = require 'underscore' path = require 'path' fs = require 'fs' loader = require '../ComponentLoader' internalSocket = require '../InternalSocket' utils = require '../Utils' nofloGraph = require '../Graph' manifest = require 'fbp-manifest' # We allow components to be un-compiled CoffeeScript CoffeeScript = require 'coffee-script' if typeof CoffeeScript.register != 'undefined' CoffeeScript.register() babel = require 'babel-core' class ComponentLoader extends loader.ComponentLoader writeCache: (options, modules, callback) -> filePath = path.resolve @baseDir, options.manifest fs.writeFile filePath, JSON.stringify(modules, null, 2), encoding: 'utf-8' , callback readCache: (options, callback) -> options.discover = false manifest.load.load @baseDir, options, callback prepareManifestOptions: -> options = {} options.runtimes = @options.runtimes or [] options.runtimes.push 'noflo' if options.runtimes.indexOf('noflo') is -1 options.recursive = if typeof @options.recursive is 'undefined' then true else @options.recursive options.manifest = 'fbp.json' unless options.manifest options readModules: (modules, callback) -> compatible = modules.filter (m) -> m.runtime in ['noflo', 'noflo-nodejs'] for m in compatible @libraryIcons[m.name] = m.icon if m.icon if m.noflo?.loader loaderPath = path.resolve @baseDir, m.base, m.noflo.loader @componentLoaders.push loaderPath for c in m.components @components["#{m.name}/#{c.name}"] = path.resolve @baseDir, c.path # Inject subgraph component if path.extname(__filename) is '.js' @components.Graph = path.resolve __dirname, '../../components/Graph.js' else @components.Graph = path.resolve __dirname, '../../components/Graph.coffee' return callback null unless @componentLoaders.length done = _.after @componentLoaders.length, callback @componentLoaders.forEach (loaderPath) => loader = require loaderPath @registerLoader loader, (err) -> return callback err if err done null listComponents: (callback) -> if @processing @once 'ready', => callback null, @components return return callback null, @components if @components @ready = false @processing = true manifestOptions = @prepareManifestOptions() if @options.cache and not @failedCache @readCache manifestOptions, (err, modules) => if err @failedCache = true @processing = false return callback err unless @options.discover return @listComponents callback @components = {} @readModules modules.modules, (err) => @ready = true @processing = false @emit 'ready', true callback null, @components if callback return @components = {} manifest.list.list @baseDir, manifestOptions, (err, modules) => return callback err if err @readModules modules, (err) => @processing = false return callback err if err @ready = true @emit 'ready', true return callback null, @components unless @options.cache @writeCache manifestOptions, modules, (err) => return callback err if err callback null, @components setSource: (packageId, name, source, language, callback) -> unless @ready @listComponents (err) => return callback err if err @setSource packageId, name, source, language, callback return Module = require 'module' if language is 'coffeescript' try source = CoffeeScript.compile source, bare: true catch e return callback e else if language in ['es6', 'es2015'] try source = babel.transform(source).code catch e return callback e try # Use the Node.js module API to evaluate in the correct directory context modulePath = path.resolve @baseDir, "./components/#{name}.js" moduleImpl = new Module modulePath, module moduleImpl.paths = Module._nodeModulePaths path.dirname modulePath moduleImpl.filename = modulePath moduleImpl._compile source, modulePath implementation = moduleImpl.exports catch e return callback e unless implementation or implementation.getComponent return callback new Error 'Provided source failed to create a runnable component' @registerComponent packageId, name, implementation, -> callback null getSource: (name, callback) -> unless @ready @listComponents (err) => return callback err if err @getSource name, callback return component = @components[name] unless component # Try an alias for componentName of @components if componentName.split('/')[1] is name component = @components[componentName] name = componentName break unless component return callback new Error "Component #{name} not installed" if typeof component isnt 'string' return callback new Error "Can't provide source for #{name}. Not a file" nameParts = name.split '/' if nameParts.length is 1 nameParts[1] = nameParts[0] nameParts[0] = '' if @isGraph component nofloGraph.loadFile component, (err, graph) -> return callback err if err return callback new Error 'Unable to load graph' unless graph callback null, name: nameParts[1] library: nameParts[0] code: JSON.stringify graph.toJSON() language: 'json' return fs.readFile component, 'utf-8', (err, code) -> return callback err if err callback null, name: nameParts[1] library: nameParts[0] language: utils.guessLanguageFromFilename component code: code exports.ComponentLoader = ComponentLoader