Webpack external require module with complex name - angularjs

I'm working on an Angular application that integrates into a bigger one. We use RequireJs and I'm migrating to Webpack. My code depends on an external requirejs module in the format:
var x = require('foo/foo/bar');
Edit: the module is actually a script located in serverRoot/foo/foo/bar.js.
In my webpack.config.js I'm telling webpack to not bother with that module, because it's provided externally:
externals: {
'foo/foo/bar': 'foo/foo/bar'
}
This generates the following code by webpack:
/* 15 */
/***/ function(module, exports) {
module.exports = foo/foo/bar;
/***/ },
Which obviously raises an error.
So... how can I make it work?

The external library or target should expose a var to the global context, for example, using external jquery library:
externals: {
// require("jquery") is external and available
// on the global var jQuery
"jquery": "jQuery"
}
so in you case, if require('foo/foo/bar') would expose a globalFoo variable, you could set the externals:
externals: {
"globalFoo": "foo/foo/bar"
}

Related

Using Webpack on an existing angular 1.x application

I have an existing, very large, angular 1.x application which runs today ES5 code.
Almost all of the application runs on the same module. My main module is defined in the file "dashboardApp.js".
I want to start using ES6 with modules per component as the app is component structured. For it to run in develpment, I want to start using Webpack.
I tried adding Webpack so I added all the needed npm dependencies and added the following webpack.config.js
var webpack = require('webpack');
module.exports = {
entry: '../app/dashboardApp.js',
output:{
path: __dirname + '/../dst/dist',
filename: 'my.bundle.js'
},
module:{
rules: [{
test: /\.js$/,
loader: 'babel-loader',
exclude: /(node_modules|bower_components)/
}]
}
};
Also, I added to package.json the following property:
"scripts": {
"build": "webpack --config webpack.config.js"
},
and was able to successfully run build and create my.bundle.js. However, when trying to load the app using just the my.bundle.js script, I got an exception:
Uncaught Error: [$injector:modulerr] Failed to instantiate module dashboardApp due to:
Error: [$injector:unpr] Unknown provider: myConsts
myConsts is an angular constant which was included before using Webpack by loading the script and hence my question:
Whats needed in order to transform an existing angular 1.x app that used to load all scripts explicitly to be one Webpack generated script app. What changes I need to do in all my files, that are all defined on the same module, in order to be included in the generated file. I understand that webpack is a module bundler, but I lack the understanding on what I need to do in order to make the old app work with Webpack. Do I need to transform all the files to ES6 module import/export syntax? How does Webpack knows what files to load when the old angular syntax (1 controller/service/constant... per file when all on the same module)? What does it do given the entry point.
Thanks
If your app is using requirejs, then you could achieve it using webpack2. Just configure it properly using rules and aliases. My app too uses requirejs and I successfully managed to replace Grunt with webpack2 after a lot of struggle.
Below is the webpack.config.js file:
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
let basePath = path.join(__dirname, '/');
let config = {
// Entry, file to be bundled
entry: {
'main': basePath + '/src/main.js',
},
devtool: 'source-map',
output: {
// Output directory
path: basePath + '/dist/',
library: '[name]',
// [hash:6] with add a SHA based on file changes if the env is build
filename: env === EnvEnum.BUILD ? '[name]-[hash:6].min.js' : '[name].min.js',
libraryTarget: 'amd',
umdNamedDefine: true
},
module: {
rules: [{
test: /(\.js)$/,
exclude: /(node_modules|bower_components)/,
use: {
// babel-loader to convert ES6 code to ES5 + amdCleaning requirejs code into simple JS code, taking care of modules to load as desired
loader: 'babel-loader',
options: {
presets: ['es2015'],
plugins: []
}
}
}, { test: /jQuery/, loader: 'expose-loader?$' },
{ test: /application/, loader: 'expose-loader?application' },
{ test: /base64/, loader: 'exports-loader?Base64' }
]
},
resolve: {
alias: {
'jQuery': 'bower_components/jquery/dist/jquery.min',
'application': 'main',
'base64': 'vendor/base64'
},
modules: [
// Files path which will be referenced while bundling
'src/**/*.js',
'src/bower_components',
path.resolve('./src')
],
extensions: ['.js'] // File types
},
plugins: [
]
};
module.exports = config;
Let me know if you have any more queries. I still remember how hard I had to try to make things work. WIll be happy to help you!
Putting this here in case anyone else runs into this problem. Essentially what webpack is trying to do is build a dependency graph. Meaning there is an entry point, and then webpack will look at that file and see what it depends on by seeing if there are any imports or require statements in it. It will then travel to the dependency file and bundle that while also looking for more dependencies and so on. In this way, it knows what things need to be loaded before others.
It sounds like you didn't alter your source code to import or require any of the module's dependencies, so Webpack simply built that one file you pointed it to instead of all of the files of your app.
Lets say ModuleA depends on ModuleB and ModuleC.
in ModuleA.js, you'll import (or require) moduleB as well as ModuleC.
In both ModuleB and ModuleC, you'll need to export them and make sure your exporting the .name property from the module since AngularJS wants strings for its dependencies.
The tricky thing about using AngularJS with Webpack, is that Angular has its own Module system which is different from the commonJS pattern or ESModules, so its a bit of an odd combination.
Softvar's solution above works because he told webpack what to bundle when defining his modules under the resolve property. If all of your sub modules are exported, another solution to bundling all of your angular files into one parent module to export, is like this, where the file is index.js and webpack looks here as its entry point:
const modules = [];
function importAll(webpackContext) {
// the webpackContext parameter is a function returned after invoking require.context() that has
// access to all of the resolved paths defined in the require.context call.
// The keys will be an array of all of the resolved module paths returned from the initial
// require.context invocation within the importAll invocation a number of lines below this declaration.
webpackContext.keys()
// this will fetch each module itself and give us access to all of the exports from that module.
// Since we are exporting the angular modules as the default export from all of our index files,
// we are just pushing the default property into the modules array. In this case the default property
// is the string name of the angular module.
.forEach(modulePath => modules.push( webpackContext(modulePath).default) );
}
// recurse through all sub directories in ./src and find the path for each index.js file.
importAll(require.context("./src/", true, /index\.js$/));
// take all of the module's name strings and spread them out as module dependencies.
// export the single module all glued together.
export default angular.module("YOUR_MODULE_NAME", [...modules]).name;

How to reference a NodeJS module w/ Webpack

When using SystemJS for writing an Angular2 app I can do a
// map tells the System loader where to look for things
var map = {
'app': 'app', // 'dist',
'#angular': 'node_modules/#angular',
'rxjs': 'node_modules/rxjs'
};
to tell angular where to find things when I call a import {} from. In this case if I wanted to use RxJS now I could import { Observable } from 'rxjs/Rx'.
But what would the equivalent of this be if I were using Webpack?
Webpack does that automatically. Unless you are using code splitting.
Suppose you have:
Module A:
import {fn} "moduleB"
Module B:
export default fn
Webpack will include all the dependencies of module A (your entry), so you will be able to use fn.
Now if you use code splitting, you will need to use require.ensure
e.g:
require.ensure(["module-a", "module-b"], function(require) {
var a = require("module-a");
// ...
});

How to use webpack with js that isn't exported

I'm trying to figure out how to include this javascript with webpack but I'm struggling. I'm installing an angular module called angular-colorthief via npm. I've installed it and included the module in my webpack config file:
if(TEST) {
config.entry = {};
} else {
config.entry = {
app: './client/app/app.js',
polyfills: './client/polyfills.js',
vendor: [
'angular',
'angular-colorthief',
'angular-animate',
'angular-aria',
'angular-cookies',
'angular-resource',
'angular-route',
'angular-sanitize',
'angular-socket-io',
'angular-material',
'lodash'
]
};
}
The problem is that inside of the angular-colorthief directory is another js which the angular module depends on which is called color-thief.js. This file does not export anything. The angular-colorthief module requires it this way:
use strict';
require("./color-thief");
angular.module('ngColorThief', [])
.provider('$colorThief', [function () {
When I run my app I keep getting an error that ColorThief is undefined because although this script is being included in my vendor bundle, it's not available when the angular module runs. Can anyone help me to fix this? I've tried installing the exports-loader module but I'm unsure how to use it to solve this problem. This is the loader I tried to add but this didn't work.
{
include: require.resolve("./color-thief"),
loader: "exports?ColorThief!node_modules/angular-colorthief/color-thief.js"
}
So I'm not certain if this is the correct solution but it solved my problem. If someone could tell me a better way I would definitely appreciate that. For anyone else, here is what I ended up adding to my loaders.
{
include: require.resolve(path.resolve(__dirname, 'node_modules/angular-colorthief/color-thief.js')),
loader: "exports?ColorThief"
}, {
include: require.resolve(path.resolve(__dirname, 'node_modules/angular-colorthief/angular-colorthief.js')),
loader: "imports?ColorThief=./color-thief.js"
}

How do I use an exported module in Angular1 using Webpack

I have the following coffeescript...
LogicThing =
doSomething: (obj) ->
console.log(obj)
...
if module?
module.exports =
LogicThing : LogicThing
Then in another file I use this by
LogicThing.doSomething({name: object})
Then in my webpack I make sure it gets loaded first...
var files = glob.sync([
'./src/main/coffee/utilities/LogicThing.coffee',
'./src/main/coffee/**/*Module.coffee',
'./src/main/coffee/**/*.coffee'
]);
module.exports = {
context: __dirname,
entry: {
polyfills: './src/main/typescripts/polyfills.ts',
vendor: './src/main/typescripts/vendor.ts',
resources: './src/main/typescripts/resources.ts',
app: files
},
...
}
Problem is once I compile everything using webpack I get...
ReferenceError: LogicThing is not defined
When I try to add it to the webpack like this...
plugins: [
new webpack.ProvidePlugin({
...
LogicThing: 'LogicThing'
}),
...
]
But then when I call webpack I get...
Module not found: Error: Cannot resolve module 'LogicThing' in
How do I handle this situation?
FYI
"coffee-script": "^1.10.0",
Update
Per comments I tried adding this to the other file...
require("../LogicThing")
To the top of my file but I still get...
ReferenceError: LogicThing is not defined

How to config minified React+ReactDOM with Webpack

After too many unsuccessful trials my question is: What is the proper way to setup Webpack so that:
Use react.min.js + react-dom.min.js - not the npm installed sources
Don't parse/com them again, just bundle with my own components.
"React" and "ReactDOM" variables can be used from all .jsx files.
The tutorials and guides I found didn't work - or maybe I did some errors. Usually I got error in browser developer tools about missing variable React.
My aim is just to save parsing/bundling time. Now I parse React from scratch every time I bundle my app. And it takes tens of seconds on a slowish computer. In watch mode it is faster, but I find I'm doing unnecessary work.
Any ideas with recent React versions?
Assuming you have a webpack.config.js that looks something like this:
module.exports = {
entry: "./entry.js",
output: {
path: __dirname,
filename: "bundle.js"
},
module: {
loaders: [
...
]
}
};
You just need to specify React and ReactDOM as external dependencies (from the docs):
module.exports = {
entry: "./entry.js",
output: {
path: __dirname,
filename: "bundle.js"
},
module: {
loaders: [
...
]
},
externals: {
// "node/npm module name": "name of exported library variable"
"react": "React",
"react-dom": "ReactDOM"
}
};
The key point about the externals section is that the key is the name of the module you want to reference, and the value is the name of the variable that the library exposes when used in a <script> tag.
In this example, using the following two script tags:
<script src="https://fb.me/react-0.14.6.js"></script>
<script src="https://fb.me/react-dom-0.14.6.js"></script>
results in two top-level variables being created: React and ReactDOM.
With the above externals configuration, anytime in your source code you have a require('react'), it will return the value of the global variable React instead of bundling react with your output.
However, in order to do this the page that includes your bundle must include the referenced libraries (in this case react and react-dom) before including your bundle.
Hope that helps!
*edit*
Okay I see what you're trying to do. The webpack configuration option you want is module.noParse.
This disables parsing by webpack. Therefore you cannot use dependencies. This may be useful for prepackaged libraries.
For example:
{
module: {
noParse: [
/XModule[\\\/]file\.js$/,
path.join(__dirname, "web_modules", "XModule2")
]
}
}
So you'd have your react.min.js, react-dom.min.js, and jquery.min.js files in some folder (say ./prebuilt), and then you'd require them like any other local module:
var react = require('./prebuilt/react.min');
And the entry in webpack.config.js would look something like this (untested):
{
module: {
noParse: [
/prebuilt[\\\/].*\.js$/
]
}
}
The [\\\/] mess is for matching paths on both Windows and OSX/Linux.

Resources