Include CSV-JS with webpack (dynamic require error) - javascript

I am moderately new to webpack and have been struggling with this for a while, and need some help.
This repo: github.com/gkindel/CSV-JS
I cannot get to be built with webpack. It throws the error:
WARNING in ./bower_components/CSV-JS/csv.js
Critical dependencies:
203:20-27 require function is used in a way in which dependencies cannot be statically extracted
Here is how I try to use it:
// CSV-JS used by angular ui-grid CSV importer
window.CSV = require('../bower_components/CSV-JS');
The line it is failing on looks like this:
CSV.stream = function () {
var s = new require('stream').Transform({objectMode: true});
...
I am pretty sure ui-grid does not use this function in the library, but webpack has to parse the entire file. I am guessing this is functionality to use in a node environment.
Anyway, anyone have a way of loading this dependency? Or perhaps an alternative library? (not sure if this is possible though, because ui-grid depends on the CSV.parse() function).

Related

Require third party RequireJS modules with Webpack

I'm working on an application that needs to pull in the ReadiumJS library, which uses AMD modules. The app itself is written in es6 w/ webpack and babel. I've gotten the vendor bundle working correctly, and it's pulling in the built Readium file, but when I try to require any of the modules Webpack says it can't resolve them. Anyone ever do this before with Webpack and RequireJS? Here's some info that may help - not sure what else to include as this is my first time really using Webpack..
Folder Structure
/readium-src
/readium-js
/ *** all readium-specific files and build output (have to pull down repo and build locally)
/node_modules
/src
/app.js -> main entry for my app
/webpack.config.babel.js
webpack.config.js entries
entry: {
vendorJs: [
'jquery',
'angular',
'../readium-src/readium-js/build-output/_single-bundle/readium-js_all.js',
'bootstrap/js/alert.js' //bootstrap js example
],
appJs: './app.js'
}
Trying to require it in app.js
var readiumSharedGlobals = require('readium_shared_js/globals');
I never really got into using RequireJS, so really struggling to understand how to consume that type of module along side other types of modules with webpack. Any help greatly appreciated :)
Update
If I change my app.js to use this instead:
window.rqReadium = require('../readium-src/readium-js/build-output/_single-bundle/readium-js_all.js');
Then it appears to try to load all the modules, but I get a strange error:
Uncaught Error: No IPv6
At this point, I'm unsure of
Should I have to require the entire path like that?
Is this error something from webpack, requirejs, or Readium? Tried debugging, but couldn't find anything useful...
UPDATE 8/12/2016
I think this is related to an issue with a library that Readium is depending on: https://github.com/medialize/URI.js/issues/118
However, I'm still not clear on how to correctly import AMD modules with webpack. Here's what I mean:
Let's say I have an amd module defined in moneyService.amd.js like this:
define('myMoneyService', ['jquery'], function($) {
//contrived simple example...
return function getDollaz() { console.log('$$$'); }
});
Then, in a sibling file, app.js, I want to pull in that file.
//this works
var getDollaz = require('./moneyService.amd.js');
//this works
require(['./moneyService.amd.js'], function(getDollaz) { getDollaz(); }
//this does not
require(['myMoneyService' /*require by its ID vs file name*/], function(getDollaz) {
getDollaz();
}
So, if we cannot require named modules, how would we work with a third party lib's dist file that has all the modules bundled into a single file?
Ok, so there's a repo out there for an Electron ePub reader using Readium, and it's using webpack: https://github.com/clebeaupin/readium-electron This shows a great way to handle pulling in RequireJS modules with webpack.
One super awesome thing I found is that you can specify output.library and output.libraryTarget and webpack will transpose from one module format to another... freaking awesome! So, I can import the requirejs module, set output library and libraryTarget to 'readium-js' and 'commonjs2' respectively, then inside my application code I can do import Readium from 'readium-js';

How to bundle isomorphic commonJS code with webpack

I have a project that uses nodeJS module format (commonJS) and should also (in parts) run in the browser.
I do have non-isomorphic code paths where I conditionally include modules:
var impl;
try {
// in node, use node-impl
impl = require('../node-impl');
} catch (err) {
// running in browser, use browser-impl
impl = require('../browser-impl');
}
Now, I want to use webpack to create a bundle that runs in the browser. I therefore need to defined the external (nodeJS specific) modules as external in the webpack.config.js so that they don't get included in the bundle:
external: {
'../node-impl': true
}
I verified that the '../node-impl' code is actually not included in bundle, but the emitted code looks like this:
/***/ },
/* 33 */
/***/ function(module, exports) {
module.exports = ../node-impl;
/***/ },
This is syntactically wrong JS and the browser will throw a syntax error there.
How is this scenario properly handled with webpack.js? Be aware that I do not wish to use webpack for running with nodeJS, only the browser bundles should be created with webpack.
// Your actual situation:
var impl;
try {
impl = require('../node-impl');
} catch(e) {
impl = require('../browser-impl');
}
You need to refactor this snippet to:
var impl = require('../node-impl');
After this rework, your code is able to work only in a node js environment, that's is good because we are going to mock this request when bundling for browsers...
// webpack.browser.config.js
module.exports = {
resolve: {
alias: {
'../node-impl': '../browser-impl'
}
}
};
Webpack - Resolve.Alias
Or using a package.json#browser, or https://webpack.github.io/docs/configuration.html#resolve-packagealias
I don't think this is the intended purpose of the externals config. Per the docs,
Specify dependencies that shouldn’t be resolved by webpack, but should become dependencies of the resulting bundle. The kind of the dependency depends on output.libraryTarget.
So you're telling webpack that your build requires that module, but not to bundle it in. It's leaving it out, but attempting to require it.
There are probably a couple of ways to do what you want. Worth mentioning that you can easily have webpack produce multiple builds, with different/shared config, from a single config file, which opens up a lot of possibilities. But the way I'd suggest is to use the DefinePlugin to define a boolean 'constant' that represents the execution context (e.g. IN_BROWSER = true). Check that constant in your conditional around the require(). Webpack's parser isn't that smart but it can evaluate boolean variables, so it will resolve the conditional correctly and only require the appropriate module. (Using a non-boolean, like CONTEXT = 'browser' is too confusing for webpack, and it will parse each require statement.) You can then use the Uglify plugin to remove the 'dead code' in the conditional so it doesn't bloat your production build.
With the help of #Hitmands I could come up with a solution that is still not perfect, but fits my needs. I introduce a fictious nonexistingmodule and declare it as external; I then declare the node-impl specific modules to be available as nonexistingmodule:
externals: {
'nonexistingmodule': true,
'../node-impl': 'nonexistingmodule'
}
This way I can keep the try/catch pattern to load specific implementations and it will still run on node. In the browser the loading of nonexistingmodule fails, and the browser-impl module is loaded - just as I intended.
I am a big fan of "don't refactor your code to match a tool", so I am going with this solution.

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.

RequireJS - Package backbone-related modules for re-use in other Rails/JS applications

I am building a web application based on Rails and, on the client-side Backbone.js. For structuring my Coffeescript-Code, I used RequireJS and requirejs-rails. Each of my Backbone classes lives in its own RequireJS module.
I recently refactored a lot of code into some base classes and want to package them somehow to be able to easily reuse them in other projects (Rails and/or Javascript/Coffeescript, possible even without RequireJS) and share it on GitHub as a separate project from my Rails application. I read the RequireJS documentation on packages, but it doesn't go into the details very much. So this is what I would like to do:
Move my shared code into its own package, so views/base_collection_view becomes commons/views/base_collection_views and so on
Include the package into my requirejs-rails setup in my rails applications, and provide a compiled my-commons.js file to use within non-requirejs setups (I guess the latter would be done using almond fairly easily once I figured out how to layout the package)
A full example of a reusable RequireJS-package would really help me a lot at this point, along with some ideas how this could be transfered to requirejs-rails.
Not sure about requirejs-rails, but with RequireJS it's pretty easy.
define(['dep1', 'dep2'] , function ($, otherLibrary) {
return function () {
// your module code
};
});
Where 'dep1' and 'dep2' are other RequireJS modules that your module depends on. You can depend on as few or as many as you like. The var names you pass to the actual function ($ and otherLibrary in this example) are the names that those libraries will be assigned to within your module.
Anyone using RequireJS will be able to require your module this way, based on how the file is named and the folders it's in.
For instance, if this file was called "my-super-lib.js" inside of the libs directory, another module could just pass libs/my-super-lib to its dependency array and everything will be set up.
Update: just remembered you mentioned coffeescript. Same idea, but to be clear:
define ['dep1', 'dep2'], ($, otherLibrary) ->
() ->
// your module code
If you're into that. ;)

Categories

Resources