npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

webpack-addons-demo

v1.6.1

Published

Example webpack-addon

Downloads

12

Readme

webpack-addons-demo

Welcome to an demonstration of the new webpack --init feature! To view what we are building today, simply do webpack --init webpack-addons-demo. This demo is to show you how to build your own webpack scaffold. Let's start by creating an file named generator.js.

Part 1

First, we have to define our skeleton. In order for the CLI to find our options, we've got to define some properties in the constructor first.

generator.js
const Generator = require('yeoman-generator');

module.exports = class WebpackGenerator extends Generator {
	constructor(args, opts) {
		super(args, opts);
		opts.env.configuration = {
			dev: {}
		}
	}
};

As you can see, the configuration object has to have one property you name, it can be anything. A good practise is to name the underlying property with the name you want to give your webpack.config.js file, to better indicate what values each file has.

PART 1-A

In order for us to interact with the users, we make good use of the prompting method yeoman has. In this method we can get various of answers from the user, like asking for entry points or plugins. You can either manually create each object representing a question, or you can make good use of our utilities from webpack-addons. I'm in a good mood today, so I'm going to build a configuration if the user chooses Pengwings.

const Generator = require('yeoman-generator');
const List = require('webpack-addons').List;

module.exports = class WebpackGenerator extends Generator {
	constructor(args, opts) {
		super(args, opts);
		opts.env.configuration = {
			dev: {}
		};
	}

	prompting() {
		return this.prompt([
			List('confirm', 'Welcome to the demo scaffold! Are you ready?', ['Yes', 'No', 'Pengwings'])
		]).then (answer => {
			if(answer['confirm'] === 'Pengwings') {

			}
		});
	}
};

PART 1-B

So far, we've made an interaction with the user. If you were coding along, great! So how do we proceed from here? Let's try to build a simple webpack config that has an entry point, an output, and a context property. For this, we need to create a webpackOptions property on our dev object. This is where entry, output and context is gonna be hooked up, later resulting in a webpack.config.js.

Hint: Define the webpackOptions property in the constructor to make your addon as most clean as possible!
const Generator = require('yeoman-generator');
const List = require('webpack-addons').List;

module.exports = class WebpackGenerator extends Generator {
	constructor(args, opts) {
		super(args, opts);
		opts.env.configuration = {
			dev: {
				webpackOptions: {}
			}
		};
	}

	prompting() {
		return this.prompt([
			List('confirm', 'Welcome to the demo scaffold! Are you ready?', ['Yes', 'No', 'Pengwings'])
		]).then (answer => {
			if(answer['confirm'] === 'Pengwings') {

			}
		});
	}
};

Part 2-A

Congrats, you've now created the base of an webpack-addon! Let's add some stuff to our future configuration file! I'm going to follow good convention, and extract my config into another file, named dev-config.js. As this is just regular JavaScript, we can make the module a function, and supply our entry as a parameter for us to build up a configuration file from.

dev-config.js
module.exports = function createDevConfig(answer) {
	let devConfig = {};
};
generator.js
const Generator = require('yeoman-generator');
const List = require('webpack-addons').List;
const createDevConfig = require('./dev-config');

module.exports = class WebpackGenerator extends Generator {
	constructor(args, opts) {
		super(args, opts);
		opts.env.configuration = {
			dev: {
				webpackOptions: {}
			}
		};
	}

	prompting() {
		return this.prompt([
			List('confirm', 'Welcome to the demo scaffold! Are you ready?', ['Yes', 'No', 'Pengwings'])
		]).then (answer => {
			if(answer['confirm'] === 'Pengwings') {
				this.options.env.configuration.dev.webpackOptions = createDevConfig(answer);
			}
		});
	}
};

Sweet! We've now abstracted some part of the code that's probably gonna be really big. We can almost call ourselves engineers now! Let's go ahead and add another question, like asking for an entry point.

const Generator = require('yeoman-generator');
const List = require('webpack-addons').List;
const Input = require('webpack-addons').Input;
const createDevConfig = require('./dev-config');

module.exports = class WebpackGenerator extends Generator {
	constructor(args, opts) {
		super(args, opts);
		opts.env.configuration = {
			dev: {
				webpackOptions: {}
			}
		};
	}

	prompting() {
		return this.prompt([
			List('confirm', 'Welcome to the demo scaffold! Are you ready?', ['Yes', 'No', 'Pengwings']),
			Input('entry', 'What is the entry point in your app?')
		]).then (answer => {
			if(answer['confirm'] === 'Pengwings') {
				this.options.env.configuration.dev.webpackOptions = createDevConfig(answer);
			}
		});
	}
};

These answers aren't well known on their own for us, so let's go ahead and create our config.

Part 3-A

Let's start by looking at dev-config.js. We have some answers, now we want to use them to build up an config. How do we do that? Evidently, we should mount our values on the variable we've declared, with some properties we want to build up. We also want to use the answers for the entry prop. I've also taken the liberty to add an output property that has a filename.

Important: With string values, you need to wrap your strings once again. This is because we can declare some other functionality, using only " ", while " 'Mystring' " resolves to a string.
dev-config.js
module.exports = function createDevConfig(answer) {
	let entryProp = answer.entry ? ( "'" + answer.entry + "'") : "'index.js'";
	let devConfig = {
		entry: entryProp,
		output: {
			filename: "'[name].js'"
		}
	};
	return devConfig;
};

Try running webpack --init webpack-addons-demo, and now you're seeing your first scaffold!

Part 3-B

Cool. Now we've got an initial scaffold. Let's add the rest of our options! For the context, let's say we've got a path.join we want to make use of. For this, you use a single quote string.

module.exports = function createDevConfig(answer) {
	let entryProp = answer.entry ? ( "'" + answer.entry + "'") : "'index.js'";
	let devConfig = {
		entry: entryProp,
		output: {
			filename: "'[name].js'"
		},
		context: 'path.join(__dirname, "src")'
	};
	return devConfig;
};

Part 4-A

We're ready to add an plugin, as a last piece of the puzzle. For this, I'm going to create an utility for commonsChunk, based on the input from the user. I'll start by adding another question in our prompt.

const Generator = require('yeoman-generator');
const List = require('webpack-addons').List;
const Input = require('webpack-addons').Input;
const createDevConfig = require('./dev-config');

module.exports = class WebpackGenerator extends Generator {
	constructor(args, opts) {
		super(args, opts);
		opts.env.configuration = {
			dev: {
				webpackOptions: {}
			}
		};
	}

	prompting() {
		return this.prompt([
			List('confirm', 'Welcome to the demo scaffold! Are you ready?', ['Yes', 'No', 'Pengwings']),
			Input('entry', 'What is the entry point in your app?'),
			Input('plugin', 'What do you want to name your commonsChunk?')
		]).then (answer => {
			if(answer['confirm'] === 'Pengwings') {
				this.options.env.configuration.dev.webpackOptions = createDevConfig(answer);
			}
		});
	}
};

Part 4-B

Let's go ahead and create our utility. We've got an utility for this in webpack-addons, so if you do const createCommonsChunkPlugin = require('webpack-addons').commonChunksPluginCreate;, you get the same thing. I'm doing this for demonstration purposes, so that you can better know how to compose an addon in good faith. First, I'm going to create a create-chunk.js, followed up by an import in dev-config.js.

dev-config.js
const createCommonsChunkPlugin = require('./commons-chunk');

module.exports = function createDevConfig(answer) {
	let entryProp = answer.entry ? ( "'" + answer.entry + "'") : "'index.js'";
	let devConfig = {
		entry: entryProp,
		output: {
			filename: "'[name].js'"
		},
		context: 'path.join(__dirname, "src")',
		plugins: [
			createCommonsChunkPlugin(answer.plugin)
		]
	};
	return devConfig;
};
commons-chunk.js
module.exports = function createCommonsChunkPlugin(chunk) {

};

Part 4-C

Now, we've got to create a string with our chunk. This is how it looks.

module.exports = function createCommonsChunkPlugin(chunk) {
	return (
		"new webpack.optimize.CommonsChunkPlugin({name:" + "'" + chunk + "'" +
		",filename:" + "'" + chunk + "-[hash].min.js'})"
	);
};

Sweet! We've now created a scaffold with entry, output, context and a plugin. If you're curious on the API, check the API for more info on how to scaffold with regexps, module, or other!

Part 5-A

In order for webpack to compile correctly, we've got to import path. For this, we've got to define something called topScope. This is where our code before module.exports are going, where you can add everything from imports, variables, to functions. The syntax is the same as with the plugins, except that the topScope property expects an array. In topScope you can define whatever you want.

generator.js
const Generator = require('yeoman-generator');
const List = require('webpack-addons').List;
const Input = require('webpack-addons').Input;
const createDevConfig = require('./dev-config');

module.exports = class WebpackGenerator extends Generator {
	constructor(args, opts) {
		super(args, opts);
		opts.env.configuration = {
			dev: {
				webpackOptions: {}
			}
		};
	}

	prompting() {
		return this.prompt([
			List('confirm', 'Welcome to the demo scaffold! Are you ready?', ['Yes', 'No', 'Pengwings']),
			Input('entry', 'What is the entry point in your app?'),
			Input('plugin', 'What do you want to name your commonsChunk?')
		]).then (answer => {
			if(answer['confirm'] === 'Pengwings') {
				this.options.env.configuration.dev.webpackOptions = createDevConfig(answer);
				this.options.env.configuration.dev.topScope = [
					'const path = require("path")',
					'const webpack = require("webpack")'
				];
			}
		});
	}
};

Part 5-B

You also might want to name your config file something you've got strong personal attachments to, like my love for penguins. To do so, you can do the following.

const Generator = require('yeoman-generator');
const List = require('webpack-addons').List;
const Input = require('webpack-addons').Input;
const createDevConfig = require('./dev-config');

module.exports = class WebpackGenerator extends Generator {
	constructor(args, opts) {
		super(args, opts);
		opts.env.configuration = {
			dev: {
				webpackOptions: {}
			}
		};
	}

	prompting() {
		return this.prompt([
			List('confirm', 'Welcome to the demo scaffold! Are you ready?', ['Yes', 'No', 'Pengwings']),
			Input('entry', 'What is the entry point in your app?'),
			Input('plugin', 'What do you want to name your commonsChunk?')
		]).then (answer => {
			if(answer['confirm'] === 'Pengwings') {
				this.options.env.configuration.dev.webpackOptions = createDevConfig(answer);
				this.options.env.configuration.dev.topScope = [
					'const path = require("path")',
					'const webpack = require("webpack")'
				];
				this.options.env.configuration.dev.configName = 'pengwings';
			}
		});
	}
};

Part 5-C

To write the actual configuration, webpack-cli creates a .yo-rc.json file for it to parse the ast. For the CLI to understand how to parse the configuration, we need to write to the .yo-rc.json. This is done using the writing lifecycle method built in by yeoman.

const Generator = require('yeoman-generator');
const List = require('webpack-addons').List;
const Input = require('webpack-addons').Input;
const createDevConfig = require('./dev-config');

module.exports = class WebpackGenerator extends Generator {
	constructor(args, opts) {
		super(args, opts);
		opts.env.configuration = {
			dev: {
				webpackOptions: {}
			}
		};
	}

	prompting() {
		return this.prompt([
			List('confirm', 'Welcome to the demo scaffold! Are you ready?', ['Yes', 'No', 'Pengwings']),
			Input('entry', 'What is the entry point in your app?'),
			Input('plugin', 'What do you want to name your commonsChunk?')
		]).then (answer => {
			if(answer['confirm'] === 'Pengwings') {
				this.options.env.configuration.dev.webpackOptions = createDevConfig(answer);
				this.options.env.configuration.dev.topScope = [
					'const path = require("path")',
					'const webpack = require("webpack")'
				];
				this.options.env.configuration.dev.configName = 'pengwings';
			}
		});
	}
	writing() {
		this.config.set('configuration', this.options.env.configuration);
	}
};

Congrats on your first scaffold! If you need help, submit an issue, or reach out to me on Twitter! You can also check the CLI repo.