WebStorm Dynamically Created Node.js Modules + Intellisense - javascript

So we have a lot of modules in our own internal Node.js code that kind of work like this:
module.exports = function(opts) {
// do some stuff with opts
return {
moduleMethod0: ...,
moduleMethod1: ...
};
}
The issue with this is that WebStorm can no longer auto-complete our modules when we use this pattern!
Is there a way to help WebStorm out here?
Edit:
It seems like WebStorm can work with JSDoc stuff. So that's a good start! I think I can actually figure this out on my own. All I gotta do is find the module and give it all the JSDoc stuff right there.
We have a helper file that instantiates our target module with the opts and then passes that around as its module export. I'll just add the JSDoc stuff there and hopefully it'll just work.

Related

Require JS files dynamically on runtime using webpack

I am trying to port a library from grunt/requirejs to webpack and stumbled upon a problem, that might be a game-breaker for this endeavor.
The library I try to port has a function, that loads and evaluates multiple modules - based on their filenames that we get from a config file - into our app. The code looks like this (coffee):
loadModules = (arrayOfFilePaths) ->
new Promise (resolve) ->
require arrayOfFilePaths, (ms...) ->
for module in ms
module ModuleAPI
resolve()
The require here needs to be called on runtime and behave like it did with requireJS. Webpack seems to only care about what happens in the "build-process".
Is this something that webpack fundamentally doesn't care about? If so, can I still use requireJS with it? What is a good solution to load assets dynamically during runtime?
edit: loadModule can load modules, that are not present on the build-time of this library. They will be provided by the app, that implements my library.
So I found that my requirement to have some files loaded on runtime, that are only available on "app-compile-time" and not on "library-compile-time" is not easily possible with webpack.
I will change the mechanism, so that my library doesn't require the files anymore, but needs to be passed the required modules. Somewhat tells me, this is gonna be the better API anyways.
edit to clarify:
Basically, instead of:
// in my library
load = (path_to_file) ->
(require path_to_file).do_something()
// in my app (using the 'compiled' libary)
cool_library.load("file_that_exists_in_my_app")
I do this:
// in my library
load = (module) ->
module.do_something()
// in my app (using the 'compiled' libary)
module = require("file_that_exists_in_my_app")
cool_library.load(module)
The first code worked in require.js but not in webpack.
In hindsight i feel its pretty wrong to have a 3rd-party-library load files at runtime anyway.
There is concept named context (http://webpack.github.io/docs/context.html), it allows to make dynamic requires.
Also there is a possibility to define code split points: http://webpack.github.io/docs/code-splitting.html
function loadInContext(filename) {
return new Promise(function(resolve){
require(['./'+filename], resolve);
})
}
function loadModules(namesInContext){
return Promise.all(namesInContext.map(loadInContext));
}
And use it like following:
loadModules(arrayOfFiles).then(function(){
modules.forEach(function(module){
module(moduleAPI);
})
});
But likely it is not what you need - you will have a lot of chunks instead of one bundle with all required modules, and likely it would not be optimal..
It is better to define module requires in you config file, and include it to your build:
// modulesConfig.js
module.exports = [
require(...),
....
]
// run.js
require('modulesConfig').forEach(function(module){
module(moduleAPI);
})
You can also try using a library such as this: https://github.com/Venryx/webpack-runtime-require
Disclaimer: I'm its developer. I wrote it because I was also frustrated with the inability to freely access module contents at runtime. (in my case, for testing from the console)

Defining an imported external module with WebPack + TypeScript

I'm trying to get WebPack set up with TypeScript and some external libraries, but having some trouble.
I'm importing the src version of jQuery in order to let WebPack choose the required modules (this was needed to get the Boostrap-SASS loader working), so am importing the library like this in my TS:
import jquery = require( 'jquery/src/jquery.js' );
When compiling, this throws the error cannot find external module 'jquery/src/jquery'. If I create a manual .d.ts file to define the library, like so:
declare module "jquery/src/jquery"{}
then the compiler works, but warns:
cannot invoke an expression whose type lacks a call signature
Alternatively, if I change the import line to
var jquery = require( 'jquery/src/jquery' );
(which I think is a CommonJS style import?) then it all compiles (and runs) fine. So, questions:
Is there a better way of requiring/including the source files using WebPack?
What's the correct way to define the module so it doesn't lack a call sig?
Should I be worried about using var instead of import or just roll with it if it's working?
I've only just picked up WebPack so it's entirely possible I'm doing something stupid. Please correct me if so!
Edit:
As I was thinking about how to phrase the question this came to me:
declare module "jquery/src/jquery" { export = $; }
which seems to let me use "import..." - but is that a good way to handle this?
Edit 2:
In reply to #basarat's answer below (my comment got a bit long to be read without line breaks):
I'm already using the jquery def from DefinitelyTyped, but it doesn't work by itself because the module I'm requiring is "jquery/src/jquery" and not just "jquery". If I use "jquery" then the compiled dist version of jquery is loaded, which doesn't work with the Bootstrap loader.
So the declare module "jquery/src/jquery" { export = $; } is, I think, extending the existing definition but using the path that I need to load it from.
import jquery = require( 'jquery/src/jquery.js' );
Use the definitely typed definition for jquery instead : https://github.com/borisyankov/DefinitelyTyped/blob/master/jquery/jquery.d.ts#L3158-L3160 which already allows you to do import jquery = require('jquery').
To answer my own question, this is what I ended up doing.
NB I don't know if this is the "correct" way of achieving this (I suspect not), so I'm open to anyone with a better answer!
In my TS definitions file tsd.d.ts I have the Definitely Typed jQuery definition:
/// <reference path="jquery/jquery.d.ts" />
but on its own this wasn't enough for the compiler to pick up the jQuery source files from within the node_modules folder.
So further down tsd.d.ts I've added:
declare module "jquery/src/jquery"
{
export = $;
}
The compiler can now locate the jQuery source files.
I also had to edit one of the jQuery source files for the WebPack compiler to play nicely with it: selector.js doesn't have a factory function included by default, and WebPack requires one to be present.
There's a loader that says it fixes this but I couldn't get it to work, so I just changed the code in selector.js to:
define([ "./selector-sizzle" ], function(){});
This all feels a little bit hacky... but it got me over this particular hurdle.

Node JS fs module inside browser

I have a scenario where I want to export data to CSV from the client-side. I will have a textbox/area or whatever where the user can input data and then ideally with one click, a local CSV file will be updated with that data.
This is easily achievable with NodeJS with server interaction, and its core modules (specifically fs module), but apparently not so much from a browser.
I found out that certain node modules (for example underscore), support RequireJS's method of making a particular module work in the browser. So for underscore I did this:
methods.js
define(['underscore'],function(_) {
var Methods = {
doSomething: function() {
var x = _.size({one: 1, two: 2, three: 3, xuz: 3});
alert(x);
}
};
return Methods;
});
common.js
requirejs.config({
baseURL: 'node_modules',
paths: {
underscore: 'underscore/underscore',
}
});
require(['methods'], function(y){
y.doSomething();
});
index.html
<script data-main="common" src="require.js"></script>
<script>
require(['common'], function() {
require(['methods.js']);
});
</script>
The above works fine and will show an alert: 4.
But when I try the same with the fs module it won't work. It will show this error:
Module name "util" has not been loaded yet for context: _. Use require([])
As far as I can understand, this is because fs requires several other modules, one of them being util.
So I proceeded to add all these modules to the RequireJS config. But still no luck, so I specifically tested util module by itself as this doesn't seem to require other modules to work.
And now I am stuck on this error: Uncaught ReferenceError: exports is not defined
I tried modularizing this util module by encapsulating the whole module source code in this:
define([], function() {})
But it didn't work either... I've also tried copying underscore's model but still no luck.
So I was wondering if anyone managed to use util & fs modules (or any other core NodeJS modules) within the browser with libraries such as RequireJS or Browserify.
That's right, exports is node-specific JS (used to make part of your module available outside the module) and isn't supported by web-browsers. Even though NodeJS is technically JS, there are client specific properties (like the window property for browsers, and exports for NodeJS apps) that can't be interchanged.
That said, here's a client side JS answer to the CSV problem.
Your best bet (and probably only one) is to use HTML5 FileSystem API. I am not aware of any other browser feature that would allow to work with files on client computer except maybe Flash and similar solution.
I am bit confused by your browserify tag since you are not obviously using Browserify. That would fix your issue with "exports is not defined".

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

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.

Where do I put my scripts in a Yeoman project when using RequireJS?

So I am just getting started with Yeoman. I just setup a basic project and I am not sure where to put my scripts. Should I just put stuff in the provided app.js? Or should I make my own files and add them to some build script somewhere or require them somewhere? Any advice is appreciated.
I also don't understand the purpose of this in the app.js file:
define([], function() {
return 'Hello from Yeoman!';
});
Okay, so you opted to use RequireJS for your project. RequireJS allows you to break your code down in modules - not only improving the tidiness of your code (reducing spaghetti code), but also de-cluttering the global namespace.
If you look in your main.js file, you'll notice the following:
require(['app'], function(app) {
//use app here
console.log(app);
});
What this does is include app.js into your main javascript file and runs it.
Now, where i think you're getting confused, is with the new syntax RequireJS introduces - but essentially each file forms a closure and has its own scope.
Imagine the following:
If you weren't using RequireJs to separate your files, and all the code was in one single file. It would look like this:
function app () {
return 'Hello from Yeoman!';
}
console.log(app());
You can write whatever you want inside app.js as long as it's wrapped with the required RequireJS syntax:
define([], function() {
//Start writing your code here
});
And that'll be included in main.js and compiled down into one file for production.
The docs for Yeoman are a little sparse at the moment, so i'd recommend you head over to the RequireJS site and read through their documentation if you're keen on writing modular JavaScript.
You can always choose not to include RequireJS when initialising a new Yeoman project if you don't want/need this functionality.
Hope that helps :)

Categories

Resources