Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
409 lines (349 sloc) 14.5 KB
_.mixin (require 'underscore.string').exports()
l = new _B.Logger 'uRequire/ModuleGeneratorTemplates'
isTrueOrFileMatch = require '../config/isTrueOrFileMatch'
Template = require './Template'
# Templates for
#
# * UMD module based on https://github.com/umdjs/umd/blob/master/returnExportsGlobal.js
#
# * AMD module `define([...], function(...){return theModule})
#
# * nodejs module `module.exports = theModule`
#
# @param @module {Module} with
# {
# bundle: the bundle object, containing all config & modules
#
# path: where the module is, within bundle
#
# name: the name, if it exists.
#
# kind: type of the original module : 'nodejs' or 'AMD'
#
# defineArrayDeps: Array of deps, as needed in AMD, in filerelative format (eg '../PersonView' for 'views/PersonView') + all `require('dep')`
#
# nodeDeps: Array for file-relative dependencies, as required by node (eg '../PersonView')
#
# parameters: Array of parameter names, as declared on the original AMD, minus those exceeding arrayDeps
#
# flags:
#
# rootExports: Array with names 'root' variable(s) to export on the browser side (or false/undefined)
#
# noConflict: if true, inject a noConflict() method on this module, that reclaims all rootExports to their original value and returns this module.
#
# factoryBody: The code that returns the module (the body of the function that's the last param of `define()`) or the whole body of the commonjs `require()` module.
#
# preDefineIIFEBody: The code with an IIFE, before the `define()` call (i.e coffeescripts __extends etc)
#
# webRootMap: path of where to map '/' when running on node, relative to bundleRoot (starting with '.'), absolute OS path otherwise.
# }
class ModuleGeneratorTemplates extends Template
scope: 'module'
constructor: (@module) -> super
Object.defineProperties @::,
bundle: get: -> @module.bundle
build: get: -> @bundle.build
isCombined: get:-> @build.template.name is 'combined'
namePrint: get:-> if @module.name then "'#{@module.name}', " else ""
isInjectExportsModule: get:->
(@module.kind is 'nodejs') or
(
@module.isInjectExportsModule and
#not @isCombined and # why not needed in combined ?
not ( # skip if already there
(@module.defineArrayDeps?[0]?.isEqual? 'exports') and
(@module.parameters?[0] is 'exports') and
(@module.defineArrayDeps?[1]?.isEqual? 'module') and
(@module.parameters?[1] is 'module')
)
)
injectExportsModuleParamsPrint: get:->
if @isInjectExportsModule then ', exports, module' else ''
# parameters of the factory method, eg 'require, _, personModel' ###
parametersPrint: get:-> """
require#{@injectExportsModuleParamsPrint}#{
(", #{par}" for par in @module.parameters).join ''}
"""
defineArrayDepsPrint: get:->
# to work around https://github.com/jrburke/r.js/issues/747 & https://github.com/jrburke/requirejs/issues/467
# always keep an array to disable requirejs / r.js scan when any isNode deps is present
# even if defineArrayDeps is empty
allowEmptyDefineDepsArray =
_.isEmpty(@module.defineArrayDeps) and @isScanAllow and
(not _.some @module.ext_requireDeps, (d) -> d.isNode)
( if allowEmptyDefineDepsArray
""
else
if @isInjectExportsModule
"['require', 'exports', 'module'"
else
"['require'"
) +
( for dep in @module.defineArrayDeps
", #{dep.name(quote:true)}" # quote: single quotes if literal, no quotes otherwise (if untrusted)
).join('') +
( if allowEmptyDefineDepsArray then '' else '], ')
# On combined allow either
# * per Module if filespec is used
# * for whole template only if true was used
useStrictModule: get:->
if @module.isUseStrict and not
(@isCombined and _B.isTrue(@build.useString))
"'use strict';"
else
''
runtimeInfo: get: ->
if @module.isRuntimeInfo or (@isRootExports and @exportRootCheck)
Template::runtimeInfo
else
''
isRootExports: get: ->
(not ( @module.isRootExports_ignore or
_.isEmpty(@module.flags.rootExports) or
_.isEmpty(@build.rootExports.runtimes)
)) and not (
(@build.template.name in ['UMD', 'UMDplain']) and
(not @build.noLoaderUMD) and
('AMD' not in @build.rootExports.runtimes) and
('node' not in @build.rootExports.runtimes)
)
exportRootCheck: get: ->
checks = []
checks.push '!__isAMD' if 'AMD' not in @build.rootExports.runtimes
checks.push '!__isNode' if 'node' not in @build.rootExports.runtimes
checks.push '!(__isWeb && !__isAMD)' if 'script' not in @build.rootExports.runtimes
checks.join ' && '
### private ###
_rootExportsNoConflict: (rootName='root', returnModule=true) ->
@deb(10, "*** START *** rootExports & noConflict() : exporting module '#{@module.path}' to root='#{rootName}' & attaching `noConflict()`.") +
( if @module.flags.noConflict
( for expVar, i in @module.flags.rootExports
"#{if i is 0 then 'var ' else ' '}__old__#{_.underscored expVar}#{i} = #{rootName}['#{expVar}']"
).join(',\n') + ';\n'
else
''
) +
(if expCheck = @exportRootCheck then "if (#{expCheck}) {" else '' ) +
(for expVar in @module.flags.rootExports
"#{rootName}['#{expVar}'] = __umodule__"
).join(';\n') + ';\n' +
( if @module.flags.noConflict
"\n__umodule__.noConflict = " +
@__function(
(for expVar, i in @module.flags.rootExports
"#{rootName}['#{expVar}'] = __old__#{_.underscored expVar}#{i}"
).join(';\n') + ';' +
"\nreturn __umodule__;"
) + ';'
else
''
) + '\n' +
(if expCheck then "}" else '' ) +
(if returnModule then "return __umodule__;" else '') +
@deb(10, "*** END *** rootExports & noConflict()")
Object.defineProperties @::,
factoryBodyAMD: get:-> @factoryBodyAll 'AMD'
factoryBodyNodejs: get:-> @factoryBodyAll 'nodejs'
_moduleExports_ModuleFactoryBody: get: ->
"module.exports = #{@__functionIIFE @module.factoryBody};"
factoryBodyAll: (toKind = do-> throw new Error "factoryBodyAll requires `toKind` in ['AMD', 'nodejs']" ) ->
@sp(
'useStrictModule',
('bundle.commonCode' if not @isCombined),
('module.mergedCode' if not @isCombined),
'module.beforeBody',
(
if (toKind is 'nodejs') and (@module.kind is 'AMD')
[ '_moduleExports_ModuleFactoryBody',
"body of original '#{@module.kind}' module, assigned to module.exports"]
else
[ 'module.factoryBody',
"body of original '#{@module.kind}' module"]
),
'module.afterBody'
) +
( if (toKind is 'AMD') and (@module.kind is 'nodejs')
@deb(20, 'returning nodejs `exports.module` as AMD factory result.') +
"\nreturn module.exports;"
else ''
)
_genFullBody: (fullBody) ->
fullBody = @sp(
'runtimeInfo'
[ 'module.preDefineIIFEBody',
'statements/declarations before define(), enclosed in an IIFE (function(){})().']
) + fullBody
#return
@uRequireBanner +
( if @module.isBare
fullBody
else
if @module.isGlobalWindow
@__functionIIFE fullBody, 'window', @globalSelector, 'global', @globalSelector
else
@__functionIIFE fullBody
) + ';'
###
A Simple `define(['dep'], function(dep){...body...}})`,
without any common stuff that are not needed for 'combined' template.
`combined` template in AlmondOptimizationTemplate, merges/adds them only once.
###
_AMD_plain_define: ->
'define('+ @namePrint + @defineArrayDepsPrint +
@__function( #our factory function (body)
if not @isRootExports
@sp('factoryBodyAMD')
else
"var __umodule__ = " +
@__functionIIFE(
@sp('factoryBodyAMD'),
@parametersPrint,
@parametersPrint
) + ";\n" +
@_rootExportsNoConflict 'window'
,
@parametersPrint # our factory function (declaration params)
) +
')'
###
`combined` templates is actually defined in AlmondOptimizationTemplate.coffee,
rendered through Bundle.coffee.
When 'combined' is used, each `Module` in `Bundle` is converted as a `_AMD_plain_define`
###
combined: -> @_AMD_plain_define()
### AMD template
Runs only on WEB/AMD/RequireJs (and hopefully in node through uRequire'd *driven* RequireJS).
###
AMD: -> @_genFullBody @_AMD_plain_define()
nodejs: ->
prCnt = 0
nodeDeps = @module.nodeDeps
@_genFullBody( # load AMD deps 1st, before kicking off common dynamic code
# deps with a variable (parameter in AMD)
( if _.some(nodeDeps,
(dep, depIdx)=> (not dep.isSystem) and (@module?.parameters or [])[depIdx]) # has a dep with param
"\nvar "
else
''
) +
(for param, pi in @module.parameters when not (dep = nodeDeps[pi]).isSystem
(if prCnt++ is 0 then '' else ' ') +
param + " = require(" + dep.name(quote:true) + ")"
).join(',\n') + (if prCnt is 0 then '' else ';') +
# deps without a variable (parameter in AMD) - simple require
(for dep in nodeDeps[@module.parameters.length..]
"\nrequire(" + dep.name(quote:true) + ");"
).join('') +
@sp('factoryBodyNodejs') +
if @isRootExports
"var __umodule__ = module.exports;\n" +
@_rootExportsNoConflict('global', false)
else ''
)
###
UMD template - runs AS-IS on both Web/AMD and nodejs (having 'npm install urequire').
* Uses `NodeRequirer` to perform `require`s.
###
UMDplain: -> @UMD false
# todo: revise this
UMD: (isNodeRequirer=true) ->
replaceParamsPrint = (paramsPrint) ->
paramsPrint.
replace('require', '_require').
replace('exports', '_exports').
replace('module', '_module')
nr = if isNodeRequirer then "nr." else ""
define = """
define(#{@namePrint}#{@defineArrayDepsPrint}#{
if not @isRootExports
'factory'
else
@__function(
"return rootExport(window, factory(#{@parametersPrint}));",
@parametersPrint
)
});
"""
@_genFullBody(
@__functionIIFE(
(if @isRootExports
"var rootExport = #{@__function @_rootExportsNoConflict(), 'root, __umodule__'};\n"
else '') +
"""
if (typeof exports === 'object') {#{
if isNodeRequirer
"\n var nr = new (require('urequire').NodeRequirer) ('#{@module.path}', module, require, __dirname, '#{@module.webRootMap}', #{@build.template.debugLevel});"
else ''}
module.exports = #{
if @isRootExports then 'rootExport(global, ' else ''
}factory(#{nr}require#{@injectExportsModuleParamsPrint}#{
(for nDep in @module.nodeDeps
if nDep.isSystem
', ' + nDep.name()
else
", #{nr}require(#{nDep.name(quote:true)})"
).join ''})#{if @isRootExports then ')' else ''};
} else
""" +
(
if @module.isNoLoaderUMD or @module.isWarnNoLoaderUMD
" if (typeof define === 'function' && define.amd) { #{define} }"
else
" #{define}"
) +
if @module.isNoLoaderUMD
" else {\n" +
(
if not _.isEmpty badDeps = _.filter(@module.defineArrayDeps,
(dep) -> dep.type in ['bundle', 'external', 'notFoundInBundle', 'nodeLocal']) # ok types [ 'untrusted', 'system', 'local' ]
'throw new Error("UMD with bundle or external deps runs only with an AMD or CommonJS loader.\\n' +
"Can`t load these deps: " + _.map(badDeps, (d) -> "'#{d.name()}' (#{d.type})" ).join("', '") + "\");"
else
(
if not _.isEmpty @module.defineArrayDeps
"""
var modNameVars = {#{
_.map( @module.defineArrayDeps, (dep)=>
"'" + dep.name() + "': " +
JSON.stringify @bundle.all_depsVars[dep.name(relative: 'bundle')]
).join(',')
}},
_require = function(modyle) {
if (modNameVars[modyle])
for (var _i = 0; _i < modNameVars[modyle].length; _i++)
if (window.hasOwnProperty(modNameVars[modyle][_i]))
return window[modNameVars[modyle][_i]];
var msg = "uRequire: Running UMD module as plain <script>, failed to `require('" + modyle + "')`:";
if (modNameVars[modyle] && modNameVars[modyle].length)
msg = msg + "it`s not exported on `window` as any of these vars: " + JSON.stringify(modNameVars[modyle]);
else
msg = msg + "WITHOUT an AMD or CommonJS loader & " +
"no identifier (i.e varName or param name) associated with dependency '"+modyle+"' in the bundle of '#{@module.path}'.";
throw new Error(msg);
},
"""
else
"""
var _require = function(modyle){
throw new Error("uRequire: Loading UMD module as <script>, failed to `require('" + modyle + "')`: reason unexpected !");
},
"""
) + " _exports = {}, _module = {exports: _exports};\n" +
if not @isRootExports
"factory(#{replaceParamsPrint @parametersPrint});"
else
"rootExport(window, factory(#{replaceParamsPrint @parametersPrint}));"
) + ";\n}";
else
if @module.isWarnNoLoaderUMD
" else throw new Error('uRequire: Loading UMD module as <script>, without `build.noLoaderUMD`');"
else ''
,
# parameter + value to our IIFE
'factory'
,
@__function( @sp('factoryBodyAMD'), @parametersPrint )
) + ';'
)
module.exports = ModuleGeneratorTemplates
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.