I'm trying to refactor client javascript in my nodejs/express application using CommonJS + Webpack modules.
With Webpack I build a 'bundle.js' made of two js files where I defined the two modules following CommongJS syntax. But on window.onload of the page I get a nice 'TypeError: moveTo.logHelloWorld is not a function" error.
Where am I wrong?
Webpack configuration:
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: ['./public/js/common.js', './public/js/moveTo.js'],
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.min.js'
},
plugins: process.env.NODE_ENV === 'production' ? [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin()
] : []
}
moveTo.js
var moveTo = {};
console.log("moveTo.js loaded...");
moveTo.logHelloWorld = function(){
console.console.log("Hello World logHelloWorld() from moveTo client Javascript!");
};
module.exports = moveTo;
moveTo.handlebars
<div>MoveTo</div>
<script type="text/javascript">
window.onload = function(){
console.log("ON LOAD...");
moveTo.logHelloWorld();
}
</script>
and finally in the main template I included the bundle.js built with Webpack:
<script src="/bundle.min.js" type="text/javascript"></script>
And this is the error I got:
UPDATE: How to use multiple client js files
Suppose I would like to add another test.js file with its own module like so:
test.js
var test = {};
console.log("test.js loaded...");
test.logHelloWorld = function(){
console.log("Hello World logHelloWorld() from TEST client Javascript!");
};
module.exports = test;
And then in Webpack config:
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: ['./public/js/common.js', './public/js/moveTo.js', './public/js/test.js'],
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.min.js',
library: ["moveTo", "test"] //This is not working.
},
plugins: process.env.NODE_ENV === 'production' ? [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin()
] : []
}
How may I get this to work?
UPDATE:
This is what i get using the suggested webpack configuration
#LAST UPDATE#
Apparently there is no way to have a single bundle.js and get it to work with different javascript files. This is the only thing I reached so far with the precious help of #hazardous:
webpack.config.js
var webpack = require('webpack');
var path = require('path');
// ['./public/js/common.js', './public/js/moveTo.js', './public/js/test.js'],
module.exports = {
entry: './public/js/t3toolbox.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.min.js',
library: ["t3toolbox", "[name]"]
},
plugins: process.env.NODE_ENV === 'production' ? [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin()
] : []
}
t3toolbox.js
var t3toolbox = {}; // Global App NameSpace
var test = require('./test.js');
var moveTo = require('./moveTo.js');
t3toolbox.moveTo = moveTo;
t3toolbox.test = test;
module.exports = t3toolbox;
moveTo.js
var moveTo = {};
console.log("moveTo.js loaded...");
moveTo.logHelloWorld = function(){
console.log("Hello World logHelloWorld() from MOVETO client Javascript!");
};
module.exports = moveTo;
test.js
var test = {};
console.log("test.js loaded...");
test.logHelloWorld = function(){
console.log("Hello World logHelloWorld() from TEST client Javascript!");
};
module.exports = test;
moveTo.handlebars
moveTo
<script type="text/javascript">
window.onload = function(){
console.log("ON LOAD...");
t3toolbox.main.moveTo.logHelloWorld();
t3toolbox.main.test.logHelloWorld();
}
</script>
As you can see you still have to use the main after t3toolbox. I never accomplished the configuration where you end up with
var mylibrary = {
moveTO: {/*...*/},
test: {/*...*/}
}
It always end up with:
var mylibrary = {
main: {
moveTo: {/*...*/},
test: {/*...*/}
}
}
Try adding library to output configuration, like so -
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: ['./public/js/common.js', './public/js/moveTo.js'],
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.min.js',
library: 'moveTo'
},
plugins: process.env.NODE_ENV === 'production' ? [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin()
] : []
}
If your library has several exported entities, use this -
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: ['./public/js/common.js', './public/js/moveTo.js'],
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.min.js',
library: ["mylibrary", "[name]"]
},
plugins: process.env.NODE_ENV === 'production' ? [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin()
] : []
}
This example is based on https://github.com/webpack/webpack/blob/master/examples/multi-part-library/webpack.config.js#L10.
This example configuration will create an export of this form:
var mylibrary : {
"common": {/* exports from common */},
"moveTo": {/* exports from moveTo */}
}
So you can use them in your code using mylibrary.common, mylibrary.moveTo etc.
If you don't want to club all of your imports in "mylibrary", you can change the library to -
library:"[name]"
This will create separate var moveTo = ... and var test = ... exports.
Related
I am building custom gutenberg blocks using npm, webpack and #wordpress/scripts. Everything was fine until I tried to use block.json file. To use block.json file I need block.asset.php file in the build directory because that's the way WordPress core is coded... (https://github.com/WordPress/gutenberg/issues/40447)
And now my problem is that running npm run build does not generate .asset.php file and I do not know why. When I register blocks using wp_enqueue_script or when I manually create an empty .asset.php it works fine.
My webpack.config.js now looks like this:
const defaultConfig = require("#wordpress/scripts/config/webpack.config");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const path = require('path');
module.exports = {
...defaultConfig,
entry: {
'cgbms-section-block': './src/section-block.js',
'cgbms-article-block': './src/article-block.js',
'cgbms-article-header-block': './src/article-header-block.js',
'cgbms-category-block': './src/category-block.js',
'cgbms-category-block-edit': './src/category-block-edit.js',
'cgbms-card-block': './src/card-block.js',
'style-front': './src/css/style-front.scss',
'style-editor': './src/css/style-editor.scss',
},
output: {
path: path.join(__dirname, './build/'),
filename: './blocks/[name].js'
},
module: {
...defaultConfig.module,
rules: [
...defaultConfig.module.rules,
]
},
plugins: [
new MiniCssExtractPlugin({
filename: './css/[name].css'
})
],
externals: {
'#wordpress/blocks': 'wp.blocks',
'#wordpress/block-editor': 'wp.blockEditor'
},
}
Okay so solution is actually really simple.
I think I had to import default plugins config:
...defaultConfig.plugins
So my whole webpack.config.js is now:
const defaultConfig = require("#wordpress/scripts/config/webpack.config");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const path = require('path');
module.exports = {
...defaultConfig,
entry: {
'cgbms-section-block': './src/section-block.js',
'cgbms-article-block': './src/article-block.js',
'cgbms-article-header-block': './src/article-header-block.js',
'cgbms-category-block': './src/category-block.js',
'cgbms-category-block-edit': './src/category-block-edit.js',
'cgbms-card-block': './src/card-block.js',
'style-front': './src/css/style-front.scss',
'style-editor': './src/css/style-editor.scss',
},
output: {
path: path.join(__dirname, './build/'),
filename: './blocks/[name].js'
},
module: {
...defaultConfig.module,
rules: [
...defaultConfig.module.rules,
]
},
plugins: [
...defaultConfig.plugins,
new MiniCssExtractPlugin({
filename: './css/[name].css'
})
]
}
as you can see I also removed externals block.
I try to create a Javascript library with OOP style and make it can run with the browser. I am a newbie to modern javascript.
My Example Code:
mylib.js
function MyLib() {
if (!(this instanceof MyLib)) {
return new MyLib(options)
}
this.init(options);
return this
}
MyLib.prototype.init = function (options) {
this.debug = options.debug
console.log(options)
}
.... ///
export default MyLib
index.js
import MyLib from './mylib.js';
index.html
<script src="dist/mylib.js"></secript>
<script>
let mylib = new({
debug: true
});
</script>
webpack.config.js
const webpack = require('webpack')
const path = require('path')
module.exports = {
entry: {
mylib: './lib/index.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
},
mode: process.env.MINIFY_BUILD ? 'production' : 'development',
plugins: [
new webpack.DefinePlugin({
'procrss.env.NODE_ENV': 'production'
})
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules\/(?!(lit-element|lit-html)\/).*/,
loader: 'babel-loader'
}
]
}
}
So after all, I got an error message like below
Uncaught ReferenceError: MyLib is not defined
Please help or suggest to me.
So, I'm trying to get this application setup so I can start coding it. But everytime I build the application; webpack automatically adds auto/file.js to the script tags, but it should really be: file.js. So it's adding the auto/ part by itself. I've checked every webpack config file, and I cannot understand why it adds the auto/ prefix to my scripts.
Also like to mention this is a ElectronJS project.
Here are my configurations for webpack.
webpack.config.js
const mainConfig = require("./webpack.main.config");
const rendererConfig = require("./webpack.renderer.config");
const config = [mainConfig, rendererConfig];
module.exports = config;
webpack.base.config.js
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const config = {
plugins: [
new UglifyJsPlugin({
test: /\.js($|\?)/i,
sourceMap: true,
uglifyOptions: {
compress: true
}
})
]
};
module.exports = config;
webpack.main.config.js
const path = require("path");
const merge = require("webpack-merge");
const base = require("./webpack.base.config");
const buildPath = path.resolve(__dirname, "./dist");
const main = merge(base, {
entry: "./main.js",
output: {
filename: "main.js",
path: buildPath
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: "babel-loader"
},
]
},
node: {
__dirname: false,
__filename: false
},
target: "electron-main"
});
module.exports = main;
webpack.renderer.config.js (this is where i think the problem is happening)
const path = require("path");
const merge = require("webpack-merge");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const base = require("./webpack.base.config");
const buildPath = path.resolve(__dirname, "./dist");
const renderer = merge(base, {
entry: "./src/renderer.js",
output: {
filename: "renderer.js",
path: buildPath
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
})
],
target: "electron-renderer",
});
module.exports = renderer;
And after the build, when I open the index.html file from the dist directory, the script tag is like this: <script src="auto/renderer.js"></script> when it should just be <script src="renderer.js"></script>
What could be causing this? Is there any configuration I am missing here?
Thanks in advance!
Solved it by updating webpack.
I have a class defined in an index.js file like this
const BLOG = BLOG || {};
BLOG.ComponentFactory = class {
}
window.BLOG = BLOG;
Then, in a file init.js in a bundle, I try to access that var, the file is like this
const BLOG = BLOG || {};
BLOG.init = function() {
var c = new BLOG.ComponentFactory()
}
I get BLOG.ComponentFactory is not a constructor and I cannot understand why. Is the BLOG definition inside the file init.js masking the global var?
There is something strange: using Chrome debugger in the init function, I see Blog.ComponentFactory defined inside "Global", but if I add to the properties to watch, I see Blog.ComponentFactory = undefined
I'd like to understand what's happening.
I need to defin BLOG as a global var as I'm using ES6 together with old javascritpt code.
EDIT: if I use the following code all works (init.js)
const BLOG = BLOG || {};
BLOG.init = function() {
var c = new window.BLOG.ComponentFactory()
}
So, it seems the local const BLOG is masking the global BLOG var, but I need to define BLOG because otherwise I get BLOG is undefined. So, how do I solve the problem?
EDIT2: my webpack config (the problem is in the bundling, the vars are defined inside functions which are generated while bundling)
const webpack = require('webpack');
const path = require('path');
var config = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
]
},
// workaround for
// ERROR in ./node_modules/wpcom/build/lib/site.media.js
// Module not found: Error: Can't resolve 'fs' in '/node_modules/wpcom/build/lib'
node: {
fs: 'empty'
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
};
var aConfig = Object.assign({}, config, {
name: "a",
entry: "./WebContent/js/blog/index.js",
output: {
path: __dirname + '/WebContent/js/blogW',
filename: "bundle.js",
publicPath: 'http://localhost:8080/js/blogW'
},
devServer: {
historyApiFallback: true,
contentBase: './WebContent',
publicPath: "http://localhost:8080/js/blogW",
hot: true
}
});
module.exports = [
aConfig
];
I want to create multiple entry points for a website, which is pretty easily done in Webpack using an object for the entry property, like here.
But as the site grows (and it inevitably will) having to add each entry point seems cumbersome and prone to error. So I'd like to simply point at a directory and say "here are all the entry points."
So I've cooked this up:
var path = require('path');
var fs = require('fs');
var entryDir = path.resolve(__dirname, '../source/js');
var entries = fs.readdirSync(entryDir);
var entryMap = {};
entries.forEach(function(entry){
var stat = fs.statSync(entryDir + '/' + entry);
if (stat && !stat.isDirectory()) {
var name = entry.substr(0, entry.length -1);
entryMap[name] = entryDir + '/' + entry;
}
});
module.exports = {
entry: entryMap,
output: {
path: path.resolve(__dirname, '../static/js'),
filename: "[name]"
},
...
This works fine, but is there a feature or configuration option in Webpack that would handle this for me?
I think glob is the right way to go here (AFAIK webpack wont do this for you). This is what I ended up with, it will find all files in a directory and create an entry with a name matching the file:
var glob = require('glob');
var path = require('path');
module.exports = {
entry: glob.sync('../source/js/**.js').reduce(function(obj, el){
obj[path.parse(el).name] = el;
return obj
},{}),
output: {
path: path.resolve(__dirname, '../static/js'),
filename: "[name]"
},
...
adapt the search path to meet your specific needs. It might also be useful to pass in {cwd: someRoot} as the second argument to sync if you have a special scripts directory which will make this the new root of relative path searches.
In my opinion, only a little Node skill is needed, and it doesn't have to be that complicated.
const webpack = require('webpack');
const path = require('path');
const fs = require('fs');
const fileNames = fs.readdirSync('./src').reduce((acc, v) => ({ ...acc, [v]: `./src/${v}` }), {});
const config = {
entry: fileNames,
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name]',
},
};
module.exports = config;
I have used Glob for this.
var path = require('path');
var glob = require('glob');
module.exports = {
entry: { 'app' : glob.sync('./scripts/**/*.ts*') },
output: {
path: path.join(__dirname, '/wwwroot/dist'),
filename: '[name].bundle.js',
sourceMapFilename: '[name].map'
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/,
}
]
},
resolve: {
extensions: [".ts", ".js"]
}
};