Why don't relative imports with AMD work in requirejs? - javascript

So, if you have a folder:
- demo/js/foo/foo.js
- demo/js/foo/bar/a.js
- demo/js/foo/bar/b.js
Then defining an AMD module's as:
foo.js: define(['./bar/a.js', './bar/b.js'], function(a, b) { console.log(a, b); });
a.js: define([], function() { return {a:'a'}; });
b.js: define([], function() { return {b:'b'}; });
Then if you import the module like this:
require(['./foo/foo'], function() { ... }
Gives:
"NetworkError: 404 Not Found - http://localhost:3005/demo/foo/bar/a.js"
"NetworkError: 404 Not Found - http://localhost:3005/demo/foo/bar/b.js"
Error: Script error for: foo/bar/a.js http://requirejs.org/docs/errors.html#scripterror
Error: Script error for: foo/bar/b.js http://requirejs.org/docs/errors.html#scripterror
Why doesn't this work?
I've read some obscure posts in the requirejs forum saying this is 'working as intended' because 'imports are names, they are not relative paths'. ...and that you should resolve this issue using the 'map' function.
...
Right! Well, I won't go in to how obvious this behavior is, or how vastly unhelpful threads like https://groups.google.com/forum/#!searchin/requirejs/relative/requirejs/Zmh7EV5fR2M/4OM-ss5g3DEJ are; let's just get to the point.
How are you supposed to make this work?
Obviously if you import an arbitrary module using bower, and it lives in say, js/lib/obscure.js/dist/obscure.js you need to setup your config:
require.config({
paths: {
obscure: 'lib/obscure.js/dist/obscure'
}
});
...but this seems to mean that all 'amd importable' modules end up being massive amalgamations of one simple file with all the modules in it.
Using baseUrl is not a solution, because, as above, you expect to have multiple isolated 'islands' of javascript which are installed as modules, which 1) need to refer to each other, but 2) also need to be able to refer to their own internal, relative modules.
Seems extremely weird.
Once again, how are you supposed to make this work in the non-trivial case?
Edit:
Surely you say, you're doing it wrong and just not telling us all the things you're doing. Well, see for yourself. An exact copy of this not working is now here on github: https://github.com/shadowmint/requirejs-example

Relative imports do work.
The problem with your code is that you list your module names together with the .js extension. You should never specify a module name with the .js extension. If you do put the extension, you're essentially telling RequireJS "I already know that the module I want is at the end of the path I'm giving you; don't mess with this path" so your RequireJS configuration won't affect how the path you put in your dependencies is resolved.

Related

how to import a standalone module build with browserify?

My question is how do I achieve importing a standalone module obtained with browserify, inside another javascript file. Here are some specific details:
I created a standalone module with browserify like this:
browserify module.js --s module_name > output.js
The file module.js contains at the end the line
module.exports = module_name;
Inside a file "use_module.js", I can now use the module module_name, with this code in some html file:
<sctipt src="output.js">
<sctipt src="use_module.js">
But what I would like is to specify just the script "use_module.js" in my html file, and somehow import "output.js" inside "use_module.js" directly in the javascript code. I tried two different approaches :
I tried javascript import module_name from './output.js' inside use_module.js, but then it cannot find the module module_name. Somehow I'm not sure module.exports is the same as export, neither with what browserify does with my module.exports anyway. (I'm very confused about all that).
I tried with require(./output.js) and then re-using browserify, but I then get a lot of weird errors with browserify, about tons of missing modules. Maybe I should specify some module informations in a file next to "output.js", that browserify can process, but I don't know really.
I understood what was failing in both approaches :
As I thought, import works only with export and not modules.exports. Also I read that browserify cannot work with the import/export syntax which is still too new.
More interestingly : a browserified file contains plenty of require(...) calls, which refers to a function created by browserify, and not to the CommonJS require keyword. Of course browserify does not know the difference, and thus look for tons a module to import a second time. All that needs to be done is to rename the word "require" into something else, and browserify can be applied a second time...
I however do not find my answer completely satisfactory : there must be a proper way to do this...

Require file on runtime & unit test issue

I use require.js to load files at runtime like following
This is working as expected when I run the file in the right context(I mean when the call is coming from the right path.)
module1.js
define(["otherModule"], function(otherModule) {
working!!!
....
Now I want to create some unit test to this file (module1) from
other context (from folder of tests which is found in diffrent location in the project) and I get error
require.js:145 Uncaught Error: Script error for: otherModule
Since it tries to run the get on this path during the Unit Test
which is located in diffrent project structure ...
https://app/path1/path2/path3/otherModule.js
And in runtime which works (from different context) it find it in the path
https://app/path1/path2/path3/path4/path5/otherModule.js
There is additional path4 & path5 in the request that works,
How should I solve it to work on both cases (UT/Runtime) ?
http://requirejs.org
I think you should be able to get it working by applying a RequireJS configuration file, so that the module name is abstracted from its path:
E.g. in the test context, call something like this as initialization step:
require.config({
baseUrl: "/path1/path2/path3"
});
Alternatively, you can also remap single modules like so (this can also be used to inject a different implementation of a specific module for testing etc.):
require.config({
paths: {
"otherModule": "/path1/path2/path3/otherModule"
}
});
See here: http://requirejs.org/docs/api.html#config

RequireJS: Cannot find module 'domReady'

I have quite an annoying, but probably simple, problem that I just cannot figure out.
In a TypeScript file I have defined the following line:
import test1 = require('domReady');
This "domReady" module is defined in a main.js file that is loaded as the entry point for RequireJS. The definition is as followed:
require.config({
paths: {
'domReady': '../domReady',
}
However... in my TypeScript file I simply get a "cannot find module 'domReady'" and it is driving me insane, as I have double checked the pathing to the file and it is indeed in the correct location with the correct name. Additionally, I am fairly certain that the domReady.js file IS AMD compatible, so it should define an external module just fine! (domReady GitHub Link).
I seriously can't understand why the module can't be found in the import statement. Does anyone have any ideas to what the problem may be?
EDIT 1
The directory structure is as follows:
.
+--App
| +--main.js
| +--dashboard.js
+--domReady.js
The import statement takes place in the "dashboard.js" file, and the config for require.js happens in "main.js".
In order for TypeScript to find a module, you must actually provide module information to TypeScript.
TypeScript doesn’t yet support AMD-style paths configuration, it doesn’t ever use calls within your JavaScript code (like require.config()) to configure itself, and it won’t treat JavaScript files on disk as modules when compiling. So right now, you aren’t doing anything to actually give the compiler the information it needs to successfully process the import statement.
For your code to compile without error, you have to explicitly declare an ambient declaration for the module you’re importing within the compiler, in a separate d.ts file:
// in domReady.d.ts
declare module 'domReady' {
function domReady(callback: () => any): void;
export = domReady;
}
Then, include this d.ts in the list of files you pass to the compiler:
tsc domReady.d.ts App/main.ts App/dashboard.ts
Any other third party JavaScript code that you import also needs ambient declarations to compile successfully; DefinitelyTyped provides d.ts files for many of these.
I've had problems before when the path key and the directory name or file name are the same (in your case, domReady). Might not work, but worth giving it a quick try, i.e.
either
'domReadyModule': '../domReady',
require('domReadyModule')
or rename domReady.js to e.g. domReady-1.0.js and use
'domReady': '../domReady-1.0',
require('domReady')
If that doesn't work, I'd check the relative directories between main.js and the file that is doing the require, or else try importing another library in the same fashion, or finally compare with other libraries that you are importing successfully.
Good luck, hope you resolve the problem!

How to make sure a module is loaded in NodeJS

This is a problem I faced more than one. Here is an example file structure:
app.js
folder
-- index.js
-- module1.js
-- module2.js
From app.js, the entry point of my application, I require folder/index.js. This file itself is just a loader who requires all the other files in the directory. module1.js and module2.js both define some methods I want to use eventually. They are never directly required by app.js since index.js takes care of that and adds common helper utilities and applies some transformations to these modules.
All works well if I then use some methods defined in those files from app.js after I required them. But the problem comes when a method defined in module2.js wants to use a method defined in method1.js (or vice-versa). Sometimes it will work, sometimes not (in folders with multiple files I didn't get to find out when precisely it works and when it doesn't yet).
If, from module2.js I require ./index.js and then use methods in it defined by module1.js, I sometimes get Cannot read property 'myMethod' of undefined. I assume it has to do with the order the modules are required by index.js, but I can't seem to find a solution to this common problem (other than duplicating code or not using methods of these other modules).
Is there a common solution to this problem? I tried doing something like this :
var modules = require(./index.js);
exports.myMethod = function() {
if(!modules.module1 || !modules.module1.myOtherMethod) {
modules = require('./index.js');
}
modules.module1.myOtherMethod();
};
But it doesn't to do anything, modules.module1 is still undefined.
It just sounds like module should require module2. That's the solution to module1 needing to call methods in module2.
If you're worried about many calls to require, don't. That's standard practice in every programming language I've used (in particular look at the import statements in any Java program. Java is verbose, I know, but this is correct practice.)
It's definitely bad practice to look at code in one module and have no idea where anything comes from, because the require statements are missing.
You have a circular dependency problem. Try moving some of the common functions to a third file and have module1 and module2 require it, or make sure that one of them requires the other in one way only.
Never ever require a file that requires the current file back.

Require.js optimizer and variables in paths

I have a problem getting r.js to work the way we need it to.
I have the following problem: We have 2 domains (e.g. foo.de and bar.de) and different environments. Depending on what environment and what domain they are running on they need to load a different file from their origin servers. My initial solution was this:
// channelDomain and environmentPath get defined above this script
require.config({
paths: {
'fooscript': channelDomain+environmentPath
}
}
Testing this in the browser unoptimized works exactly as it should but the nightly build complained with:
[Error: Error: The config in mainConfigFile /absolute/path/app/public/js/main.js
cannot be used because it cannot be evaluated correctly while running in the
optimizer. Try only using a config that is also valid JSON, or do not use
mainConfigFile and instead copy the config values needed into a build file or
command line arguments given to the optimizer.
Source error from parsing: /absolute/path/app/public/js/main.js: ReferenceError:
channelDomain is not defined
I tried doing lots of things but I'm running out of ideas. I tried doing the empty: thing in the build file but that didn't work either. I'd be glad if someone could point me into the right direction.
Use two require.config in the same file. The optimizer will only read the first one, as James says here https://github.com/jrburke/r.js/issues/270#issuecomment-13112859, and it will work in the browser after optimization.
So at the end you will have something like this in main.js:
require.config({
//only configurations needed for the transpiler's optimization
});
require.config({
paths: {
'fooscript': channelDomain+environmentPath
}
});

Categories

Resources