I am trying to require a file with browserify using variables passed into a function:
var playersOptions = {
name: 'players',
ajax: 'team-overview',
route: {
name: 'overview',
path: 'playersOverview',
url: 'playersoverview'
}
};
var BackboneView = require(playersOptions.route.path);
//Error: Uncaught Error: Cannot find module 'playersOverview'
var BackboneView = require('playersOverview');
//Requires the file without any problems.
I am confused as to why this would fail? How can it not find the module when both are strings?
Browserify has to be able to statically analyze all of the require statements at build time so it can know what files it needs to include in the bundle. That requires that require can only be used with a string literal in the source code.
Instead of passing the name of a module around to require later, just pass the module itself:
var playersOptions = {
name: 'players',
ajax: 'team-overview',
route: {
name: 'overview',
module: require('playersOverview'),
url: 'playersoverview'
}
};
var BackboneView = playersOptions.route.module;
Even if this Browserify limitation wasn't present (eg. if you were using node.js directly), it's still a good idea to avoid passing module names to be required later because the require call could break if the module name passed to it had a path relative to the caller's directory and was passed into code in a file inside a different directory.
Related
For example, I use AMD definition in my project, and use "webpack" for project building. It's possible to create some loader which will take a dependencies in array format?
define(
[
'mySuperLoader![./path/dependency-1, ./path/dependency-2, ...]'
],
function() {
// ... some logic here
}
)
Project example: gitHub
If you want to port the load-plugin's behavior to webpack, you need to do this:
1. Create a custom resolver
This is because mySuperLoader![./path/dependency-1, ./path/dependency-2, ...] does not point to a single file. When webpack tries to load a file, it first:
resolves the file path
loads the file content
matches and resolves all loaders
passes the file content to the loader chain
Since [./path/dependency-1, ./path/dependency-2, ...] is not a proper file path, there is some work to do. It is even not a proper JSON.
So, our first goal is to turn this into mySuperLoader!some/random/file?["./path/dependency-1", "./path/dependency-2", ...]. This is usually done by creating a custom resolver:
// webpack.config.js
var customResolverPlugin = {
apply: function (resolver) {
resolver.plugin("resolve", function (context, request) {
const matchLoadRequest = /^\[(.+)]$/.exec(request.path);
if (matchLoadRequest) {
request.query = '?' + JSON.stringify(
matchLoadRequest[1]
.split(", ")
);
request.path = __filename;
}
});
}
};
module.exports = {
...
plugins: [
{
apply: function (compiler) {
compiler.resolvers.normal.apply(customResolverPlugin);
}
}
]
};
Notice request.path = __filename;? We just need to give webpack an existing file so that it does not throw an error. We will generate all the content anyway. Probably not the most elegant solution, but it works.
2. Create our own load-loader (yeah!)
// loadLoader.js
const path = require("path");
function loadLoader() {
return JSON.parse(this.request.match(/\?(.+?)$/)[1])
.map(module =>
`exports['${path.basename(module, '.js')}'] = require('${module}');`
)
.join('\n');
}
module.exports = loadLoader;
This loader parses the request's query we have re-written with our custom resolver and creates a CommonJS module that looks like this
exports['dependency-1'] = require('path/to/dependency-1');
exports['dependency-2'] = require('path/to/dependency-2');
3. Alias our own load-loader
// webpack.config.js
...
resolveLoader: {
alias: {
load: require.resolve('./loadLoader.js')
}
},
4. Configure root
Since /path/to/dependency-1 is root-relative, we need to add the root to the webpack config
// webpack.config.js
resolve: {
root: '/absolute/path/to/root' // usually just __dirname
},
This is neither a beautiful nor an ideal solution, but should work as a makeshift until you've ported your modules.
I don't think that you should use a loader for that. Why don't you just write:
require("./path/dependency-1");
require("./path/dependency-2");
require("./path/dependency-3");
It accomplishes the same thing, is much more expressive and requires no extra code/loader/hack/configuration.
If you're still not satisfied, you might be interested in webpack contexts which allow you to require a bulk of files that match a given filter. So, if you write
require("./template/" + name + ".jade");
webpack includes all modules that could be accessed by this expression without accessing parent directories. It's basically the same like writing
require("./table.jade");
require("./table-row.jade");
require("./directory/folder.jade")
You can also create contexts manually like this
var myRequire = require.context(
"./template", // search inside this directory
false, // false excludes sub-directories
/\.jade$/ // use this regex to filter files
);
var table = myRequire("./table.jade");
Script A
;(function(g){
define('hotness', [], function() {
function f() {;
console.log('gotem');
}
return f;
});
define('otherModule', [], function() {});
})(this);
Script B
define([
'ScriptA',
], function() {
var hotness = require('hotness')
});
Error: Module name "hotness" has not been loaded yet for context: _
What is the recommended way to require in a definition from ScriptA in ScriptB?
I've also tried:
Script B Alt
define([
'require',
'ScriptA',
'hotness'
], function(require) {
var hotness = require('hotness')
});
Which gives me
Error loading resource ... /hotness.js: No such file or directory
Error: Script error for: hotness
EDIT:
It's important to note that ScriptA stands as is and will not be modified. The question is how can I get at the named module in ScriptA from ScriptB.
EDIT2:
I have no control over the HTML or any other aspect of the page. I must do everything within ScriptB.
EDIT3:
I have an example of something that works but it looks like a horrible antipattern work-around so I didn't even want to mention it:
define(['require'], function(require) {
// why use window? IDK.
// this is just verbatim what is "working" for someone else
window.require(['scriptA'], function(sA) {
//sA never actually used
window.require([
'otherModule'
], function(oM) {
var hotness = require('hotness'),
You should use a bundles configuration. Given what you've described works, you should use:
bundles: {
'scriptA': ['hotness', 'otherModule']
}
This essentially tells RequireJS "when you want to find hotness or otherModule then load scriptA because they are defined there". I use this every now and then to load modules from bundles that have been generated with r.js.
Other than this Joseph the Dreamer is correct that you should not in the same module mix the AMD and CommonJS methods of getting dependencies.
Your ScriptA uses named modules. They must be loaded after RequireJS but prior to any of the dependents. RequireJS does not recommend named modules unless you know what you're doing (like load a library like a regular script, and register it as a module at the same time).
Error: Module name "hotness" has not been loaded yet for context: _
This is a generic RequireJS error, when the module is loaded from the server, but somehow RequireJS can't provide it to the dependent. Usually happens when the module has syntax errors or when there's a circular dependency, among others.
ScriptB also has problems. It's trying to use both AMD style (array dependencies) and CommonJS style (explicit require). Just use one of the two syntaxes. Note that in the CommonJS format, it needs the module to have a first argument named require to trigger CommonJS format of writing.
define(['hotness'], function(hotness) {
// use hotness
});
// or
define(function(require){
var hotness = require('hotness');
// use hotness
});
Error loading resource ... /hotness.js: No such file or directory
Be sure to set a base url. It can be implicit (based on the data-main location) or explicit (using require.config). Module names are usually paths + filenames relative to the base url.
It is advised that you use one file per module, containing a module definition that has no name (the file name becomes the name of the module) and resides somewhere under the set baseUrl.
I am quite new to nodeJS.
I am using the nodeJS module node-workflow
Basically, this module is an orchestrator that takes a custom javascript script (=workflow definition), then serialize it and store it in a REDIS db (for example), and execute on-demand later on by the node-workflow module.
A workflow definition is composed of task, like this:
var my_external_module = require('my_external_module');
var workflow = module.exports = {
name: 'Workflow Test',
chain: [{
name: 'TASK 1',
timeout: 30,
retry: 1,
body: function(job, cb) {
// Execute external function
my_external_module.hello("Monkey");
return cb(null)
},
},
...
First I put my function my_external_module.hello() in a .js file beside the workflow script.
When I run the node-workflow module I get the following error:
Error initializing runner:
[ReferenceError: my_external_module is not defined]
So I have created a module my_external_module,
and in: ./node_modules/my_external_module/index.js
module.exports = {
hello: function(name) {
console.log("Hello, " + name);
}
};
When I run the node-workflow module I get the same error:
Error initializing runner:
[ReferenceError: my_external_module is not defined]
It seems that the require(...) shall stands in one of the .js files of the node-workflow module, so I would have to hack one of the files of the module, but it is a bit dirty.
Is there something I missed?
Or is there a way to define a $PATH like in Python in order to my function to be accessible from everywhere.
You have to require it as:
var my_external_module = require('./my_external_module');
Notice the ./ this means Node should search for a file like ./my_external_module.js
If you omit the ./ Node looks at the installed modules on (usually) node_modules directory
I use WebJars to manage my JavaScript dependencies which means that most of the libraries I use are outside the Require.js base path. The config is created automatically and it works for most libraries. I only have problems with those libraries that call require() to load some inner dependencies (usually just external files) - for example when.js.
This is the require.js config generated for when library:
requirejs.config({"paths":{"when":["/webjars/when-node/3.5.2/when","when"]}});
This file loads properly. But the problem is that it tries to load further files:
...
var timed = require('./lib/decorators/timed');
var array = require('./lib/decorators/array');
var flow = require('./lib/decorators/flow');
var fold = require('./lib/decorators/fold');
...
I would expect require.js to use the location of when.js to determine the correct locations of the other required files, i. e.:
/webjars/when-node/3.5.2/lib/decorators/timed.js
But unfortunately require.js uses instead the location of the main.js file as the base path which obviously results in a lot of 404 errors and the application crashes.
How can I tell require.js to look in the correct subdirectory?
So according to the RequireJS documentation this seems to be the correct behavior. Unlike CommonJS, every require() call is resolved using the base path (it is relative to the base path, not to the location of the file it is called in).
The only way to get around this (as far as I know) is to configure the dependency as a package. The package location is then used for path resolution instead of the general base path. For the mentioned when package, the configuration should look something like this:
requirejs.config({
packages: [
{ name: 'when', location: '/path/to/when', main: 'when' }
]
});
To fix this problem automatically for all my current or future dependencies, I've replaced the Scala Play Framework loading script with my own solution which loads all WebJars as separate packages.
var require = {
callback : function () {
var packages = [];
var shim = {};
[
#for(webJarJson <- org.webjars.RequireJS.getSetupJson(routes.WebJarAssets.at("").url).values()) {
#Html(webJarJson.toString),
}
].forEach(function (webjar) {
if (webjar.paths) {
for (var name in webjar.paths) {
if (webjar.paths.hasOwnProperty(name)) {
packages.push({
name: name,
location: webjar.paths[name][0].replace(/\/[^\/]+$/, ""),
main: webjar.paths[name][1]
});
}
}
}
if (webjar.shim) {
for (var name in webjar.shim) {
if (webjar.shim.hasOwnProperty(name)) {
shim[name] = webjar.shim[name];
}
}
}
});
requirejs.config({
packages: packages,
shim: shim
});
}
}
Here is my main.js
requirejs.config({
baseUrl: '/js',
paths: {
jquery: 'jquery',
ckeditor: 'ckeditor/ckeditor',
juiAutocomplete: 'jquery-ui-1.10.4.custom',
tags: 'bootstrap-tokenfield',
createPost: 'createPost',
domReady: 'domReady',
test: 'dropUpload'
},
shim: {
createPost: {
deps: ['domReady!']
}
},
deps: ['require'],
callback: function(require) {
'use strice';
var moduleName = location.pathname.replace(/\//g, '-').replace(/^-/, '');
console.log('moduleName is: ' + moduleName);
console.log('yes is: ' + require.config);
}
});
In the callback, I'd like to access the paths which is defined in the requirejs.config() above. If it is possible, how to do it?
My purpose is to see if a module path is defined(exists). If so, then load the module script. If not checked, then a loading error will generate in the console.
Here are the available methods in requirejs by this console command. I can't find a way to access the paths I defined in requirejs.config(). Is this the right direction?
for (var i in requirejs) {console.log(i);}
config
nextTick
version
jsExtRegExp
isBrowser
s
toUrl
undef
defined
specified
onError
createNode
load
exec
undef
There is no public API to get the whole RequireJS configuration from inside a module. You can have a config section in your configuration, which modules may access.
However, the problem you describe trying to solve does not require you to read the configuration. Calling require the normal way will load the module. If the module can't be loaded, it will generate an error on the console. Presumably you also want your code to know whether the loading was successful or not. You can do it with an errback:
require(['foo'], function (foo) {
// Whatever you'd like to do with foo on success.
}, function (err) {
// Whatever you'd like to do on error.
});
If for some reason you must read the config directly then it is located at requirejs.s.contexts.<context name>.config where <context name> is the name of the RequireJS context. The default context is named _ so the configuration for it would be requirejs.s.contexts._.config. However, this is not part of the public API and can change at any time.