I just started to use webpack and am trying to load jquery synchronously
Here is my main.js
var $ = require('jquery');
require('javascript/index.js');
require('less/index.less');
and here is my webpack.config
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var path = require('path');
module.exports = {
entry: './assets/javascript/main.js',
output: {
path: './assets',
filename: '/javascript/bundle.js'
},
module : {
loaders : [
{
test: /\.css/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},
{
test: /\.less$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader")
}
]
},
plugins: [
new ExtractTextPlugin("/css/[name].css")
],
resolve : {
root: path.resolve('./assets'),
extensions: ['', '.js', '.less']
}
};
my index.js looks like this
$(document).ready(function () {
var body = $('body');
var backgrounds = new Array(
'url(./../images/bg1.jpg)' ,
'url(./../images/bg2.jpg)' ,
'url(./../images/bg3.jpg)' ,
'url(./../images/bg4.jpg)'
);
var current = 0;
function nextBackground() {
console.log("Changing bg");
current++;
current = current % backgrounds.length;
body.css('background-image', backgrounds[current]);
}
setInterval(nextBackground, 1000);
body.css('background-image', backgrounds[0]);
});
and on execution throws the error
Uncaught ReferenceError: $ is not defined
I really don't understand this error since if I look into the generated bundle.js Jquery clearly is getting defined.
I already tried to add this to my resolve:
resolve : {
root: path.resolve('./assets'),
extensions: ['', '.js', '.less'],
alias: {
jquery: "jquery"
}
}
but the error is still persistent
Edit: Here is a snipped of the created bundle.js
var $ = __webpack_require__(2);
__webpack_require__(3);
__webpack_require__(4);
According to your code, you need to add this to your index.js
var $ = require('jquery');
That's because when you used the webpack to build your code, each files(e.g index.js) would be wrap into a function which is defined by webpack.
So all the variables defined in your main.js are not accessable to index.js, coz they are now in different function which are not sharing the same scope.
You can either expose jquery to global(window) use the expose-loader or you need to require the jquery manually.
Hope this can solve your problem. : )
What you are looking for is the ProvidePlugin:
Automatically loaded modules. Module (value) is loaded when the identifier (key) is used as free variable in a module. The identifier is filled with the exports of the loaded module.
For example:
Add this plugin to your config:
new webpack.ProvidePlugin({
$: "jquery"
})
Somewhere in your code:
// in a module
$("#item") // <= just works
// $ is automatically set to the exports of module "jquery"
Make sure you got jquery installed via NPM
Related
I am using the below package on an angular project:
https://github.com/pablojim/highcharts-ng
A requirement is that it needs highcharts as a global dependency, in which it tells you to add a script tag into your html:
<script src="http://code.highcharts.com/highcharts.src.js"></script>
Rather than add the above script tag into my HTML I would like to make it global via Webpack.
I have installed highcharts via npm and have tried using the ProvidePlugin and the noParse methods described here (to no avail): https://webpack.js.org/guides/shimming/#scripts-loader
For the ProvidePlugin option I used:
new webpack.ProvidePlugin({
Highcharts: "highcharts",
})
for noParse:
noParse: [
/[\/\\]node_modules[\/\\]highcharts[\/\\]highcharts\.js$/,
],
Neither worked, meaning when highcharts-ng tried to work, it gets an error because it cannot create a new Highcharts:
TypeError: Cannot read property 'Chart' of undefined
// from highcharts-ng which throws above error
chart = new Highcharts[chartType](mergedOptions, func);
Here is my angular module
import angular from 'angular'
import highchartsNg from 'highcharts-ng'
import { ReportsData } from './reports.data'
import { reportsWidget } from './reports-widget/component'
export const ReportsModule = angular
.module('reports', [
highchartsNg,
])
.factory('ReportsData', ReportsData)
.component('reportsWidget', reportsWidget)
.name
My webpack config:
var webpack = require('webpack')
module.exports = {
context: __dirname + '/app/modules',
entry: {
vendor: ['angular', 'highcharts-ng'],
modules: './modules.js',
},
output: {
path: __dirname + '/.tmp/modules',
filename: '[name].bundle.js',
},
plugins: [
// info: https://webpack.js.org/guides/code-splitting-libraries/
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor', 'manifest'],
}),
],
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: { presets: ['es2015'] },
},
],
},
],
},
}
Use expose-loader and write require('expose-loader?Highcharts!highcharts'); somewhere before the first usage of Highcharts global variable. This will require highcharts and save expose it as window.Highcharts in the browser.
Under the hood webpack makes it available for you as:
/* 0 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {module.exports = global["Highcharts"] = __webpack_require__(1);
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)))
/***/ }
I'm using webpack to concatenate JS libraries and JS files of my own. Currently the setup is like this
var wpStream = require('webpack-stream')';
var files = ['jquery.js', 'angular.js', 'app.js', 'controller.js', 'etc.js'];
gulp.task('pack', function(){
return gulp.src(files)
.pipe(wpStream({
output:{filename: 'bundle.js'}
}).pipe(gulp.dest('dist'));
});
This works well. My file is created and it has all of the content specified in my files array. However on page load, I get the error jQuery is not defined. Additionally, in my console when I type jQuery there is not jQuery, but rather jQuery111206520785381790835. And when I append a dot to see the list of methods, there's just the normal object methods (hasOwnProperty, toString, etc).
How do I access jQuery? What has webpack done with it?
You have to make jQuery globally accessible with webpack ProvidePlugin
var webpack = require('webpack');
var wpStream = require('webpack-stream');
var path = require('path');
var files = ['app.js', 'controller.js', 'etc.js'];
gulp.task('pack', function () {
return gulp.src(files)
.pipe(wpStream(
{
output: {
filename: 'bundle.js'
},
plugins: [
new webpack.ProvidePlugin({
angular: "angular",
$: "jquery",
jQuery: "jquery"
})
],
resolve: {
root: path.resolve('./vendor'), // directory that contains jquery.js and angular.js
extensions: ['', '.js']
}
}
).pipe(gulp.dest('dist'));
});
Currently we're using Webpack for our Module loader, and Gulp for everything else (sass -> css, and the dev/production build process)
I want to wrap the webpack stuff into gulp, so all I have to do is type gulp and it starts, watches and runs webpack and the rest of what our gulp is setup to do.
So I found webpack-stream and implemented it.
gulp.task('webpack', function() {
return gulp.src('entry.js')
.pipe(webpack({
watch: true,
module: {
loaders: [
{ test: /\.css$/, loader: 'style!css' },
],
},
}))
.pipe(gulp.dest('dist/bundle.js'));
});
The problem is that it generates a random character name for the .js file, how are we suppose to use that in our app?
From the github repo:
The above will compile src/entry.js into assets with webpack into dist/ with the output filename of [hash].js (webpack generated hash of the build).
How do you rename these files? Also the new gulp task generates a new file everytime I save an edit:
I can't use c2212af8f732662acc64.js I need it to be named bundle.js or something else normal.
Our Webpack config:
var webpack = require('webpack');
var PROD = JSON.parse(process.env.PROD_DEV || '0');
// http://stackoverflow.com/questions/25956937/how-to-build-minified-and-uncompressed-bundle-with-webpack
module.exports = {
entry: "./entry.js",
devtool: "source-map",
output: {
devtoolLineToLine: true,
sourceMapFilename: "app/assets/js/bundle.js.map",
pathinfo: true,
path: __dirname,
filename: PROD ? "app/assets/js/bundle.min.js" : "app/assets/js/bundle.js"
},
module: {
loaders: [
{ test: /\.css$/, loader: "style!css" }
]
},
plugins: PROD ? [
new webpack.optimize.UglifyJsPlugin({minimize: true})
] : []
};
There was a comment to Leon Gaban's answer as to what his webpack.config.js looked like. Rather than answer that within a comment, I'm providing it here so it formats better.
Per the docs for webpack-stream, "You can pass webpack options in with the first argument"...
So, I did the following to force webpack to use the same output name each time (for me, I used bundle.js):
gulp.task('webpack', ['babelify'],
() => {
return gulp.src('Scripts/index-app.js')
.pipe(webpack({output: {filename: 'bundle.js'} }))
.pipe(debug({ title: 'webpack:' }))
.pipe(gulp.dest('Scripts/'));
});
The key being the options inside webpack(), which are:
{output: {filename: 'bundle.js'} }
As recommended in docs you should use the vinyl-named package on the pipe before webpack-stream. This way you can use a more cleaner Webpack configuration. The following is the task definition i use myself:
'use strict';
const gulp = require('gulp'),
named = require('vinyl-named'),
webpack = require('webpack-stream');
gulp.task('webpack', function () {
gulp.src(['./src/vendor.js', './src/bootstrap.js', './src/**/*.spec.js'])
.pipe(named())
.pipe(webpack({
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
query: {
presets: ['es2015', 'angular2']
}
}
]
}
}))
.pipe(gulp.dest('./build'))
});
The only problem i'm facing with this task definition is that the subfolder are loosed. For example ./src/components/application.spec.js will produce ./build/application.spec.js instead of ./build/components/application.spec.js.
Ah I read on a bit further and figured it out:
gulp.task('webpack', function() {
return gulp.src('entry.js')
.pipe(webpack( require('./webpack.config.js') ))
.pipe(gulp.dest('app/assets/js'));
});
^ here I can just pass in my actual webpack.config and it will use the paths I have already set in there. In my case I just removed app/assets/js since I have that path in now gulp instead.
Still no earthly idea though, why with the first task I created, it generates random hash filenames?
Rather than giving your javascript a fixed filename, a better solution would be to use gulp-inject and insert the generated hash filename into a script tag. This means you don't have to worry about cache expiry on the compiled javascript (which is why the hash filename is being used in the first place).
const inject = require('gulp-inject');
gulp.task('webpack', function() {
const index = './src/index.html';
const scripts = gulp.src('entry.js')
.pipe(webpack( require('./webpack.config.js') ))
.pipe(gulp.dest('dist/js'));
return target
.pipe(inject(scripts))
.pipe(gulp.dest('dist/'));
});
and of course you need the inject section in your src/index.html:
<!DOCTYPE html>
<html>
<head>
<title>index page</title>
</head>
<body>
<!-- inject:js -->
<!-- endinject -->
</body>
</html>
I a trying to generate an HTML coverage report, but it does not contain the output I expect. Maybe I'm wrong here, but it should show only those lines and methods covered which are called from the spec file, right?
Somehow it does not.
Update:
I created an repository to provide a working example, outlining the problem:
https://github.com/gearsdigital/stunning-octo-train
This is my (test) project setup. I'm able to push it to a GitHub repo if needed as I don't know how to setup JSFiddle to run this code.
TL;DR
There is a process to generate an HTML coverage report. This report shows code as covered which is obviously not covered because there is no test available.
karma.conf.js:
var webpack = require('webpack');
var path = require('path');
// Reference webpack.config.js, don't repeat it!
var webpackConfig = require('./webpack.config.js');
// The entry point from the referenced Webpack configuration has to be
// removed or tests will fail in weird and inscrutable ways.
// Easy enough, just define an empty entry object (null won't work).
webpackConfig.entry = {};
webpackConfig.module = {
preLoaders: [
{
test: /\.js$/,
// files within these directories should be excluded
// for babel processing
exclude: /(node_modules)/,
loaders: ['babel?cacheDirectory']
},
{
test: /\.js$/,
include: /(src\/js)/,
exclude: /(vendor)/,
loaders: ['isparta']
}
]
};
/**
* Karma configuration
* #param config
*/
module.exports = function (config) {
config.set({
browsers: ['PhantomJS'],
coverageReporter: {
dir: 'test-results',
reporters: [
{type: 'text-summary'},
{type: 'html', subdir: 'coverage'}
]
},
files: [
'webpack.test.config.js'
],
frameworks: [
'jasmine'
],
preprocessors: {
'webpack.test.config.js': ['webpack']
},
reporters: ['spec', 'coverage'],
webpack: webpackConfig
});
};
webpack.config.js:
var webpack = require('webpack');
var path = require('path');
module.exports = {
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
]
};
webpack.test.config.js:
// make sure the file name regexp matches your test files.
var testsContext = require.context('./tests', true, /\.spec\.js$/);
testsContext.keys().forEach(testsContext);
// make sure the file name regexp matches your test files.
var srcContext = require.context('./src/js', true, /\.js$/);
srcContext.keys().forEach(srcContext);
bootstrap.js:
import {Calculator} from './modules/Calculator';
let c = new Calculator();
c.add(1,2); // 3
Calculator.js:
export class Calculator {
add(op1, op2) {
return op1 + op2;
}
sub(op1, op2) {
if (typeof op1 !== 'number') {
return false;
}
return op1 - op2;
}
mul(op1, op2) {
return op1 * op2;
}
div(op1, op2) {
return op1 / op2;
}
}
bootstrap.spec.js:
import {Calculator} from '../src/js/modules/Calculator';
describe('Calculator', function () {
it('should return 10', function () {
expect(true).toBe(false);
});
});
Generated report:
I expect add() to be uncovered, as it's not called in any test but in bootstrap.js.
Project structure:
src/js/bootstrap.js gets loaded; which means these lines get executed.
import {Calculator} from './modules/Calculator';
let c = new Calculator();
c.add(1,2); // 3
I am guessing this block is the culprit:
{
test: /\.js$/,
include: /(src\/js)/,
exclude: /(vendor)/,
loaders: ['isparta']
}
Shouldn't my code be only exercised during tests?
The short answer is no.
Anything that is not located inside closures / function / methods may get covered when you import the module.
// this will get coverage; because it is top-level code outside a function block
var dummy = null;
export class Calculator {
add(op1, op2) {
// this will get coverage only if you call explicitly the function.
return op1 + op2;
}
}
// this will get coverage.
var c = new Calculator();
// and now, Calculator.add will get coverage.
c.add(1,2); // 3
Okay so I'm not familiar with Webpack or Babel, but I strongly suspect that since you include all your files in Karma configuration, code get executed no matter what's in the tests. I've checked out your GitHub's example (kudos on doing this btw, it's easier to debug), and if I comment out the lines 3 and 4 of cart.js, the coverage report doesn't show utilities.js as 'ran'.
Long story short : code that's not encapsulated into a function gets run by including the files in Karma, obviously. Also, don't trust Karma when there's no tests at all (like, literally 0 it), it's unreliable in this specific case.
I am using webpack to bundle/transform jsx.
From the command line I'm running "webpack --watch". This creates my bundle without errors. Here's my webpack config and Application.js
'use strict';
var webpack = require('webpack');
module.exports = {
resolve: {
extensions: ['', '.js']
},
devtool: 'eval',
entry: './client.js',
output: {
path: __dirname+'/build/js',
filename: 'client.js'
},
module: {
loaders: [
{ test: /\.css$/, loader: 'style!css' },
{ test: /\.js$/, loader: 'jsx-loader?harmony' }
]
}
};
var React = require('react'),
classSet = require('react/addons'),
Nav = require('./Nav.js'),
Home = require('./Home.js'),
Recipe = require('./Recipe.js'),
RecipeArchive = require('./RecipeArchive.js'),
About = require('./About.js'),
Timestamp = require('./Timestamp.js'),
RouterMixin = require('flux-router-component').RouterMixin;
var Application = React.createClass({
mixins: [RouterMixin],
getInitialState: function () {
this.store = this.props.context.getStore('ApplicationStore');
return this.store.getState();
},
componentDidMount: function () {
var self = this;
self._changeEventListener = function () {
var state = self.store.getState();
self.setState(state);
};
self.store.on('change', self._changeEventListener);
},
componentWillUnmount: function () {
var self = this;
self.store.removeListener('change', self._changeEventListener);
self._changeEventListener = null;
},
render: function () {
return (
<div>test</div>
);
}
});
module.exports = Application;
Then I'm running my node server which throws an error.
node server.js
results in:
/Users//Documents/routing/components/Application.js:39
<div>test</div>
^
SyntaxError: Unexpected token <
How should I be running my project to allow me to include jsx/harmony in my .js files?
UPDATE: Solution
As Brandon pointed out, I needed to use node-jsx for transforms in Node. At the top I've of my server.js file I've added and alls working:
require('node-jsx').install({ extension: '.js', harmony: true });
Webpack just creates a client-side bundle; when you run your Node app, it doesn't use webpack to load the files. Prevously you could use something like node-jsx to require JSX files in Node.js, but that project has since been deprecated in favor of Babel. In particular, you can use babel-register to require JSX files in Node.