├── .gitignore
├── bin
├── handlers
│ ├── sourceFiles
│ │ ├── style.tpl
│ │ ├── entry.tpl
│ │ ├── entry.x.tpl
│ │ ├── tasks
│ │ │ └── imports.js
│ │ └── sourceFilesHandler.js
│ ├── views
│ │ ├── html.tpl
│ │ ├── tasks
│ │ │ ├── name.js
│ │ │ └── resources.js
│ │ └── viewsHandler.js
│ ├── index.js
│ ├── webpackConfig
│ │ ├── tasks
│ │ │ ├── plugins.js
│ │ │ ├── extensions.js
│ │ │ ├── entry.js
│ │ │ └── rules.js
│ │ ├── webpackConfigHandler.js
│ │ └── webpack.config.tpl
│ ├── dotFiles
│ │ └── dotFilesHandler.js
│ ├── tsConfig
│ │ └── tsConfigHandler.js
│ └── packageJson
│ │ └── packageJsonHandler.js
├── params.js
├── index.js
├── utils.js
├── createWpapp.js
├── appGenerator.js
└── setup.js
├── package.json
├── LICENCE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | .idea
3 | *.log
4 | node_modules
5 | package-lock.json
--------------------------------------------------------------------------------
/bin/handlers/sourceFiles/style.tpl:
--------------------------------------------------------------------------------
1 | html, body {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
--------------------------------------------------------------------------------
/bin/handlers/sourceFiles/entry.tpl:
--------------------------------------------------------------------------------
1 | <@=ENTRY.IMPORTS=@>
2 |
3 | const $root = document.getElementById("root");
4 |
5 | $root.innerHTML = `
It Works!
`;
6 |
--------------------------------------------------------------------------------
/bin/handlers/sourceFiles/entry.x.tpl:
--------------------------------------------------------------------------------
1 | <@=ENTRY.IMPORTS=@>
2 |
3 | const $root = document.getElementById("root");
4 |
5 | render(
6 |
7 | It Works!
8 |
9 | , $root
10 | );
11 |
--------------------------------------------------------------------------------
/bin/params.js:
--------------------------------------------------------------------------------
1 | const { getGitConfigValue } = require("./utils");
2 |
3 | module.exports = {
4 | APP_PATH: process.cwd(),
5 | GIT_USER_NAME: getGitConfigValue("user.name"),
6 | GIT_USER_EMAIL: getGitConfigValue("user.email")
7 | }
--------------------------------------------------------------------------------
/bin/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const chalk = require("chalk");
4 | const { isEmptyDir } = require("./utils");
5 | const { APP_PATH } = require("./params");
6 |
7 | if (!isEmptyDir(APP_PATH)) {
8 | console.log(chalk.red("Please run on an empty folder!"));
9 | process.exit(1);
10 | }
11 |
12 | require("./createWpapp");
--------------------------------------------------------------------------------
/bin/handlers/views/html.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <@=APP.NAME=@>
7 |
8 | <@=APP.RESOURCES=@>
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/bin/handlers/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | dotFilesHandler: require("./dotFiles/dotFilesHandler"),
3 | packageJsonHandler: require("./packageJson/packageJsonHandler"),
4 | sourceFilesHandler: require("./sourceFiles/sourceFilesHandler"),
5 | viewsHandler: require("./views/viewsHandler"),
6 | webpackConfigHandler: require("./webpackConfig/webpackConfigHandler"),
7 | tsConfigHandler: require("./tsConfig/tsConfigHandler")
8 | }
--------------------------------------------------------------------------------
/bin/handlers/views/tasks/name.js:
--------------------------------------------------------------------------------
1 | const { Transform } = require("stream");
2 |
3 | module.exports = function (params) {
4 |
5 | const stream = Transform({ objectMode: true });
6 | const KEY = "<@=APP.NAME=@>";
7 |
8 | stream._transform = function (file, encoding, cb) {
9 |
10 | file = file.toString();
11 |
12 | this.push(file.replace(new RegExp(KEY, "g"), params.app_name));
13 |
14 | cb();
15 | }
16 |
17 | return stream;
18 | }
--------------------------------------------------------------------------------
/bin/handlers/views/tasks/resources.js:
--------------------------------------------------------------------------------
1 | const { Transform } = require("stream");
2 |
3 | module.exports = function (params) {
4 |
5 | const stream = Transform({ objectMode: true });
6 | const KEY = "<@=APP.RESOURCES=@>";
7 |
8 | stream._transform = function (file, encoding, cb) {
9 |
10 | file = file.toString();
11 |
12 | const resources = [];
13 |
14 | if (params.use_css) {
15 | resources.push(``);
16 | }
17 |
18 | this.push(file.replace(new RegExp(KEY, "g"), resources.join('\n')));
19 |
20 | cb();
21 | }
22 |
23 | return stream;
24 | }
--------------------------------------------------------------------------------
/bin/handlers/webpackConfig/tasks/plugins.js:
--------------------------------------------------------------------------------
1 |
2 | const { Transform } = require("stream");
3 |
4 | module.exports = function (params) {
5 |
6 | const stream = Transform({ objectMode: true });
7 | const KEY = "<@=PLUGINS=@>";
8 |
9 | stream._transform = function (file, encoding, cb) {
10 |
11 | file = file.toString();
12 |
13 | const plugins = [];
14 |
15 | if (params.use_css) {
16 | plugins.push(`new ExtractTextPlugin("bundle.css", { allChunks: true })`);
17 | }
18 |
19 | this.push(file.replace(new RegExp(KEY, "g"), plugins.join(", ")));
20 |
21 | cb();
22 | }
23 |
24 | return stream;
25 | }
--------------------------------------------------------------------------------
/bin/handlers/webpackConfig/tasks/extensions.js:
--------------------------------------------------------------------------------
1 | const { Transform } = require("stream");
2 |
3 | module.exports = function (params) {
4 |
5 | const stream = Transform({ objectMode: true });
6 | const KEY = "<@=RESOLVE.EXTENSIONS=@>";
7 |
8 | stream._transform = function (file, encoding, cb) {
9 |
10 | file = file.toString();
11 |
12 | let extensions = [".js"];
13 |
14 | if (params.type === "typescript") {
15 | extensions.push(".ts");
16 |
17 | if (params.use_react) {
18 | extensions.push(".tsx");
19 | }
20 | }
21 |
22 | this.push(file.replace(new RegExp(KEY, "g"), JSON.stringify(extensions)));
23 |
24 | cb();
25 | }
26 |
27 | return stream;
28 | }
--------------------------------------------------------------------------------
/bin/handlers/webpackConfig/tasks/entry.js:
--------------------------------------------------------------------------------
1 | const { Transform } = require("stream");
2 |
3 | module.exports = function (params) {
4 |
5 | const stream = Transform({ objectMode: true });
6 | const KEY = "<@=ENTRY.FILE=@>";
7 |
8 | stream._transform = function (file, encoding, cb) {
9 |
10 | file = file.toString();
11 |
12 | let entry;
13 |
14 | if (params.type === "typescript") {
15 | entry = "app.ts";
16 |
17 | if (params.use_react) {
18 | entry = "app.tsx";
19 | }
20 |
21 | } else if (params.type === "javascript") {
22 | entry = "app.js";
23 | } else {
24 | entry = "";
25 | }
26 |
27 | this.push(file.replace(new RegExp(KEY, "g"), JSON.stringify(entry)));
28 |
29 | cb();
30 | }
31 |
32 | return stream;
33 | }
--------------------------------------------------------------------------------
/bin/handlers/views/viewsHandler.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const fse = require("fs-extra");
3 | const path = require("path");
4 |
5 | const name = require("./tasks/name");
6 | const resources = require("./tasks/resources");
7 |
8 | module.exports = function (appPath, params) {
9 |
10 | const templatePath = path.resolve(__dirname, "html.tpl");
11 | const baseDestPath = path.resolve(appPath, "views");
12 | const destPath = path.resolve(baseDestPath, "index.html");
13 |
14 | fse.ensureDirSync(baseDestPath);
15 |
16 | return new Promise((resolve, reject) => {
17 | let stream = fs.createReadStream(templatePath)
18 | .pipe(name(params))
19 | .pipe(resources(params))
20 | .pipe(fs.createWriteStream(destPath));
21 |
22 | stream.on("finish", resolve);
23 | stream.on("error", reject);
24 | });
25 | }
--------------------------------------------------------------------------------
/bin/utils.js:
--------------------------------------------------------------------------------
1 | const { spawnSync, execSync } = require("child_process");
2 | const fs = require("fs");
3 |
4 | const clearNewLineFromBuffer = exports.clearNewLineFromBuffer = function (buffer) {
5 | return buffer.toString().replace(/\n/g, "");
6 | }
7 |
8 | const getGitConfigValue = exports.getGitConfigValue = function (info) {
9 | return clearNewLineFromBuffer(spawnSync("git", ["config", info]).stdout);
10 | }
11 |
12 | const getENVParamValue = exports.getENVParamValue = function (param) {
13 | return clearNewLineFromBuffer(execSync(`echo $${param}`));
14 | }
15 |
16 | const isEmptyDir = exports.isEmptyDir = function (path) {
17 | return fs.existsSync(path) && fs.readdirSync(path).length === 0;
18 | }
19 |
20 | const isValidHTTPPort = exports.isValidHTTPPort = function (port) {
21 | const parsed = +port;
22 | return parsed >= 1 && parsed <= 65535 && port === parsed.toString();
23 | }
--------------------------------------------------------------------------------
/bin/handlers/webpackConfig/webpackConfigHandler.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 |
4 | const entry = require("./tasks/entry");
5 | const extensions = require("./tasks/extensions");
6 | const rules = require("./tasks/rules");
7 | const plugins = require("./tasks/plugins");
8 |
9 | module.exports = function (appPath, params) {
10 |
11 | const templatePath = path.resolve(__dirname, "webpack.config.tpl");
12 | const destPath = path.resolve(appPath, "webpack.config.js");
13 |
14 | return new Promise((resolve, reject) => {
15 | let stream = fs.createReadStream(templatePath)
16 | .pipe(entry(params))
17 | .pipe(extensions(params))
18 | .pipe(rules(params))
19 | .pipe(plugins(params))
20 | .pipe(fs.createWriteStream(destPath));
21 |
22 | stream.on("finish", resolve);
23 | stream.on("error", reject);
24 | });
25 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-wpapp",
3 | "version": "0.2.0",
4 | "description": "Boost your workflow with a simple and configurable webpack project generator",
5 | "bin": {
6 | "create-wpapp": "./bin/index.js"
7 | },
8 | "keywords": [
9 | "webpack",
10 | "bootstrap",
11 | "project",
12 | "starter",
13 | "generator"
14 | ],
15 | "author": {
16 | "name": "Udi Talias",
17 | "email": "udi.talias@gmail.com",
18 | "url": "https://github.com/uditalias"
19 | },
20 | "license": "MIT",
21 | "bugs": {
22 | "url": "https://github.com/uditalias/create-wpapp/issues"
23 | },
24 | "homepage": "https://github.com/uditalias/create-wpapp#readme",
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/uditalias/create-wpapp.git"
28 | },
29 | "dependencies": {
30 | "chalk": "2.3.0",
31 | "fs-extra": "^4.0.2",
32 | "inquirer": "3.3.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/bin/handlers/dotFiles/dotFilesHandler.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 |
4 | const GIT_IGNORE = [
5 | ".vscode",
6 | ".idea",
7 | ".DS_Store",
8 | "dist",
9 | "node_modules",
10 | "package-lock.json"
11 | ];
12 |
13 | module.exports = function (appPath, params) {
14 |
15 | fs.writeFileSync(
16 | path.resolve(appPath, ".gitignore"),
17 | GIT_IGNORE.join("\n")
18 | );
19 |
20 | if (params.type === "javascript") {
21 | const babelRc = {
22 | presets: [
23 | "es2015",
24 | "stage-0"
25 | ],
26 | plugins: [
27 | "transform-runtime"
28 | ]
29 | };
30 |
31 | if (params.use_react) {
32 | babelRc.presets.push("react");
33 | }
34 |
35 | fs.writeFileSync(
36 | path.resolve(appPath, ".babelrc"),
37 | JSON.stringify(babelRc, null, 4)
38 | );
39 | }
40 |
41 | return Promise.resolve();
42 | }
--------------------------------------------------------------------------------
/bin/handlers/sourceFiles/tasks/imports.js:
--------------------------------------------------------------------------------
1 | const { Transform } = require("stream");
2 |
3 | module.exports = function (params) {
4 |
5 | const stream = Transform({ objectMode: true });
6 | const KEY = "<@=ENTRY.IMPORTS=@>";
7 |
8 | stream._transform = function (file, encoding, cb) {
9 |
10 | file = file.toString();
11 |
12 | let imports = [];
13 |
14 | if (params.use_css) {
15 | imports.push(`import "style/main.${params.use_scss ? "scss" : "css"}"`);
16 | }
17 |
18 | if (params.use_react) {
19 | if (params.type === "typescript") {
20 | imports.push(`import * as React from "react"`);
21 | } else if (params.type === "javascript") {
22 | imports.push(`import React from "react"`);
23 | }
24 |
25 | imports.push(`import { render } from "react-dom"`);
26 | }
27 |
28 | this.push(file.replace(new RegExp(KEY, "g"), imports.join("\n")));
29 |
30 | cb();
31 | }
32 |
33 | return stream;
34 | }
--------------------------------------------------------------------------------
/bin/createWpapp.js:
--------------------------------------------------------------------------------
1 | const inquirer = require('inquirer');
2 | const chalk = require("chalk");
3 | const AppGenerator = require("./appGenerator");
4 | const setup = require("./setup");
5 | const { APP_PATH } = require("./params");
6 |
7 | let generator = null;
8 |
9 | /**
10 | * Get all user inputs from setup and create the app generator
11 | *
12 | * @param {any} answers
13 | */
14 | function createApp(answers) {
15 |
16 | console.log(`Creating a new webpack app in ${chalk.green(APP_PATH)}...`);
17 |
18 | generator = new AppGenerator(answers, APP_PATH);
19 |
20 | generator.on("finish", onFinish);
21 |
22 | generator.generate();
23 | }
24 |
25 | function onFinish(err) {
26 | if (err) {
27 | console.log(chalk.red("Failed to create webpack app!"));
28 | console.log(chalk.red(err.stack));
29 |
30 | process.exit(1);
31 | }
32 |
33 | console.log(chalk.green("All Done."));
34 | console.log(`Run ${chalk.green(`npm start`)} to start the dev server`);
35 |
36 | process.exit(0);
37 | }
38 |
39 |
40 | inquirer.prompt(setup).then(createApp);
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Udi Talias
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/bin/handlers/tsConfig/tsConfigHandler.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 |
4 | module.exports = function (appPath, params) {
5 |
6 | if (params.type !== "typescript") {
7 | return Promise.resolve();
8 | }
9 |
10 | const tsConfig = {
11 | compilerOptions: {
12 | baseUrl: "src/",
13 | target: "es5",
14 | module: "commonjs",
15 | allowJs: true,
16 | allowSyntheticDefaultImports: true,
17 | importHelpers: true,
18 | sourceMap: true,
19 | moduleResolution: "node",
20 | lib: [
21 | "es6",
22 | "es7",
23 | "dom"
24 | ],
25 | types: [
26 | "webpack-env"
27 | ],
28 | noUnusedLocals: false,
29 | strictNullChecks: false,
30 | removeComments: false
31 | },
32 | include: [
33 | "src/**/*"
34 | ],
35 | exclude: [
36 | "node_modules"
37 | ],
38 | compileOnSave: false
39 | };
40 |
41 | if (params.use_react) {
42 | tsConfig.compilerOptions.types.push("react");
43 | tsConfig.compilerOptions.types.push("react-dom");
44 | tsConfig.compilerOptions.jsx = "react";
45 | }
46 |
47 | fs.writeFileSync(
48 | path.resolve(appPath, "tsconfig.json"),
49 | JSON.stringify(tsConfig, null, 4)
50 | );
51 |
52 | return Promise.resolve();
53 | }
--------------------------------------------------------------------------------
/bin/handlers/webpackConfig/webpack.config.tpl:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const packageConfig = require('./package.json');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const path = require('path');
5 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
6 | const contextPath = path.join(__dirname, './src');
7 | const appEnv = process.env.NODE_ENV || 'development';
8 | const isProduction = appEnv == 'production';
9 | const isDevelopment = appEnv == 'development';
10 | const destinationDir = (isProduction) ? `./dist` : `./build`;
11 |
12 | module.exports = {
13 |
14 | context: contextPath,
15 |
16 | devtool: "#source-map",
17 |
18 | entry: {
19 | app: <@=ENTRY.FILE=@>
20 | },
21 |
22 | resolve: {
23 | extensions: <@=RESOLVE.EXTENSIONS=@>,
24 |
25 | modules: [contextPath, "node_modules"]
26 | },
27 |
28 | output: {
29 | path: path.resolve(__dirname, destinationDir),
30 | filename: 'bundle.js'
31 | },
32 |
33 | module: {
34 | rules: <@=MODULE.RULES=@>
35 | },
36 |
37 | plugins: [
38 |
39 | new HtmlWebpackPlugin({
40 | inject: false,
41 | template: '../views/index.html'
42 | }),
43 |
44 | new webpack.NoEmitOnErrorsPlugin(),
45 |
46 | new webpack.ProvidePlugin({
47 | 'Promise': 'es6-promise'
48 | }),
49 |
50 | new webpack.DefinePlugin({
51 | 'process.env.NODE_ENV': JSON.stringify(appEnv),
52 | 'process.env.VERSION': JSON.stringify(packageConfig.version)
53 | }),
54 |
55 | <@=PLUGINS=@>
56 | ]
57 | };
58 |
--------------------------------------------------------------------------------
/bin/handlers/webpackConfig/tasks/rules.js:
--------------------------------------------------------------------------------
1 | const { Transform } = require("stream");
2 |
3 | module.exports = function (params) {
4 |
5 | const stream = Transform({ objectMode: true });
6 | const KEY = "<@=MODULE.RULES=@>";
7 |
8 | stream._transform = function (file, encoding, cb) {
9 |
10 | file = file.toString();
11 |
12 | let rules = [];
13 |
14 | if (params.type === "typescript") {
15 |
16 | const ext = params.use_react ? "tsx" : "ts";
17 |
18 | rules.push(`{
19 | test: /\.${ext}?$/,
20 | use: ['ts-loader'],
21 | exclude: /node_modules/
22 | }`);
23 |
24 | } else if (params.type === "javascript") {
25 |
26 | rules.push(`{
27 | test: /\.js?$/,
28 | use: ['babel-loader'],
29 | exclude: /node_modules/
30 | }`);
31 |
32 | }
33 |
34 | if (params.use_css) {
35 |
36 | let ext = "css";
37 |
38 | const loaders = [
39 | "css-loader"
40 | ];
41 |
42 | if (params.use_scss) {
43 | ext = "scss"
44 | loaders.push("sass-loader");
45 | }
46 |
47 | rules.push(`{
48 | test: /\.${ext}$/,
49 | use: ExtractTextPlugin.extract({
50 | fallback: 'style-loader',
51 | use: ${JSON.stringify(loaders)}
52 | })
53 | }`);
54 |
55 | }
56 |
57 | this.push(file.replace(new RegExp(KEY, "g"), `[${rules.join(", ")}]`));
58 |
59 | cb();
60 | }
61 |
62 | return stream;
63 | }
--------------------------------------------------------------------------------
/bin/handlers/sourceFiles/sourceFilesHandler.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const fse = require("fs-extra");
3 | const path = require("path");
4 |
5 | const imports = require("./tasks/imports");
6 |
7 | module.exports = function (appPath, params) {
8 |
9 | const baseDestPath = path.resolve(appPath, "src");
10 | const entryTemplatePath = path.resolve(__dirname, (
11 | params.use_react ? "entry.x.tpl" : "entry.tpl"
12 | ));
13 |
14 | fse.ensureDirSync(baseDestPath);
15 |
16 | let entryExt = "js";
17 |
18 | if (params.type === "typescript") {
19 | entryExt = "ts";
20 |
21 | if (params.use_react) {
22 | entryExt = "tsx";
23 | }
24 | }
25 |
26 | const entryDestPath = path.resolve(baseDestPath, `app.${entryExt}`);
27 |
28 | const tasks = [];
29 |
30 | if (params.use_css) {
31 | const styleDestPath = path.resolve(appPath, "src/style");
32 |
33 | fse.ensureDirSync(styleDestPath);
34 |
35 | tasks.push(new Promise((resolve, reject) => {
36 | let ext = params.use_scss ? "scss" : "css";
37 |
38 | let styleStream = fs.createReadStream(path.resolve(__dirname, "style.tpl"))
39 | .pipe(fs.createWriteStream(path.resolve(styleDestPath, `main.${ext}`)));
40 |
41 | styleStream.on("error", reject);
42 | styleStream.on("finish", resolve);
43 | }));
44 | }
45 |
46 | tasks.push(new Promise((resolve, reject) => {
47 | const entryStream = fs.createReadStream(entryTemplatePath)
48 | .pipe(imports(params))
49 | .pipe(fs.createWriteStream(entryDestPath));
50 |
51 | entryStream.on("error", reject);
52 | entryStream.on("finish", resolve);
53 | }));
54 |
55 | return Promise.all(tasks);
56 | }
--------------------------------------------------------------------------------
/bin/appGenerator.js:
--------------------------------------------------------------------------------
1 | const { EventEmitter } = require("events");
2 | const handlers = require("./handlers");
3 | const fse = require('fs-extra')
4 |
5 | /**
6 | * Creates a webpack app at given `appPath` with user `params`
7 | *
8 | * @class AppGenerator
9 | * @extends {EventEmitter}
10 | */
11 | class AppGenerator extends EventEmitter {
12 |
13 | /**
14 | * Creates an instance of AppGenerator.
15 | * @param {any} params - user params from setup process
16 | * @param {any} appPath - destination app path
17 | * @memberof AppGenerator
18 | */
19 | constructor(params, appPath) {
20 | super();
21 |
22 | this._params = params;
23 | this._appPath = appPath;
24 | }
25 |
26 | /**
27 | * Process all handlers to create app files
28 | * and installs app dependencies
29 | *
30 | * @memberof AppGenerator
31 | */
32 | generate() {
33 | const tasks = [];
34 |
35 | for (let handlerName in handlers) {
36 | tasks.push(handlers[handlerName](this._appPath, this._params));
37 | }
38 |
39 | Promise.all(tasks)
40 | .then(this._tasksSuccess.bind(this))
41 | .catch(this._tasksError.bind(this));
42 | }
43 |
44 | /**
45 | * Fires the `finish` event with no error when all
46 | * handlers finished successfully
47 | *
48 | * @memberof AppGenerator
49 | */
50 | _tasksSuccess() {
51 | this.emit("finish", null);
52 | }
53 |
54 | /**
55 | * Fires the `finish` event with error when one
56 | * of the handlers throws one
57 | *
58 | * @param {any} err
59 | * @memberof AppGenerator
60 | */
61 | _tasksError(err) {
62 |
63 | this._cleanup();
64 |
65 | this.emit("finish", err);
66 | }
67 |
68 | /**
69 | * Clears the `appPath` from files and folders
70 | * when the process has failed
71 | *
72 | * @memberof AppGenerator
73 | */
74 | _cleanup() {
75 | fse.emptyDirSync(this._appPath);
76 | }
77 | }
78 |
79 | module.exports = AppGenerator;
--------------------------------------------------------------------------------
/bin/setup.js:
--------------------------------------------------------------------------------
1 | const {
2 | GIT_USER_NAME,
3 | GIT_USER_EMAIL
4 | } = require("./params");
5 | const { isValidHTTPPort } = require("./utils");
6 |
7 | module.exports = [
8 | {
9 | type: "input",
10 | name: "app_name",
11 | message: "What is your app name?",
12 | default: "webpack-starter",
13 | validate: function (input) {
14 | const done = this.async();
15 |
16 | if (~input.indexOf(' ')) {
17 | done("No spaces please");
18 | return;
19 | }
20 |
21 | done(null, true);
22 | }
23 | },
24 | {
25 | type: "input",
26 | name: "app_port",
27 | message: "Select dev server port:",
28 | default: "3001",
29 | validate: function (input) {
30 | const done = this.async();
31 |
32 | if (!isValidHTTPPort(input)) {
33 | done("Port is not valid");
34 | return;
35 | }
36 |
37 | done(null, true);
38 | }
39 | },
40 | {
41 | type: "list",
42 | name: "type",
43 | message: "Choose app type:",
44 | choices: [
45 | { name: "TypeScript", value: "typescript" },
46 | { name: "ES2015", value: "javascript" }
47 | ]
48 | },
49 | {
50 | type: "confirm",
51 | name: "use_react",
52 | message: "Are you going to use React in your app?",
53 | default: false
54 | },
55 | {
56 | type: "confirm",
57 | name: "use_css",
58 | message: "Are you going to use css in your app?",
59 | default: true
60 | },
61 | {
62 | type: "confirm",
63 | name: "use_scss",
64 | message: "Do you want to use SCSS preprocessor?",
65 | default: true,
66 | when: function (answers) {
67 | return answers.use_css;
68 | }
69 | },
70 | {
71 | type: "input",
72 | name: "user_name",
73 | message: "What is your name?",
74 | default: GIT_USER_NAME
75 | },
76 | {
77 | type: "input",
78 | name: "user_email",
79 | message: "What is your email?",
80 | default: GIT_USER_EMAIL
81 | }
82 | ];
--------------------------------------------------------------------------------
/bin/handlers/packageJson/packageJsonHandler.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 | const { spawnSync } = require("child_process");
4 |
5 | const BABEL_DEV_DEPS = [
6 | "babel-core",
7 | "babel-loader",
8 | "babel-plugin-transform-runtime",
9 | "babel-preset-es2015",
10 | "babel-preset-stage-0",
11 | "babel-runtime",
12 | ];
13 |
14 | const TYPESCRIPT_DEV_DEPS = [
15 | "typescript",
16 | "tslib",
17 | "ts-loader",
18 | "@types/webpack-env"
19 | ];
20 |
21 | const CSS_DEV_DEPS = [
22 | "css-loader",
23 | "style-loader"
24 | ];
25 |
26 | const SASS_DEV_DEPS = [
27 | "node-sass",
28 | "sass-loader"
29 | ];
30 |
31 | const COMMON_DEV_DEPS = [
32 | "es6-promise",
33 | "extract-text-webpack-plugin",
34 | "html-webpack-plugin",
35 | "webpack",
36 | "webpack-dev-server"
37 | ];
38 |
39 | const REACT_DEV_DEPS = [
40 | "react",
41 | "react-dom",
42 | "prop-types"
43 | ];
44 |
45 | const REACT_TYPESCRIPT_DEV_DEPS = [
46 | "@types/react",
47 | "@types/react-dom"
48 | ]
49 |
50 | module.exports = function (appPath, params) {
51 |
52 | const package = {
53 | name: params.app_name,
54 | version: "1.0.0",
55 | description: "",
56 | author: {
57 | name: params.user_name,
58 | email: params.user_email
59 | },
60 | scripts: {
61 | start: `NODE_ENV=development webpack-dev-server --hot --progress --port ${params.app_port}`,
62 | build: "NODE_ENV=production webpack -p"
63 | },
64 | license: "ISC",
65 | devDependencies: {}
66 | };
67 |
68 | fs.writeFileSync(
69 | path.resolve(appPath, "package.json"),
70 | JSON.stringify(package, null, 4)
71 | );
72 |
73 | let devDependencies = [].concat(COMMON_DEV_DEPS);
74 |
75 | switch (params.type) {
76 | case "javascript":
77 | devDependencies.push(...BABEL_DEV_DEPS);
78 |
79 | if (params.use_react) {
80 | devDependencies.push("babel-preset-react");
81 | }
82 |
83 | break;
84 | case "typescript":
85 | devDependencies.push(...TYPESCRIPT_DEV_DEPS);
86 |
87 | if (params.use_react) {
88 | devDependencies.push(...REACT_TYPESCRIPT_DEV_DEPS);
89 | }
90 | break;
91 | }
92 |
93 | if (params.use_react) {
94 | devDependencies.push(...REACT_DEV_DEPS);
95 | }
96 |
97 | if (params.use_css) {
98 | devDependencies.push(...CSS_DEV_DEPS);
99 | }
100 |
101 | if (params.use_scss) {
102 | devDependencies.push(...SASS_DEV_DEPS);
103 | }
104 |
105 | if (devDependencies.length > 0) {
106 | spawnSync("npm", ["install", "--save-dev"].concat(devDependencies), { stdio: "inherit" });
107 | }
108 |
109 | return Promise.resolve();
110 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # create-wpapp
2 | ## Boost your workflow with a simple and configurable webpack project generator
3 |
4 | ### Disclaimer
5 | This is not a replacement for `create-react-app` by any means! It's just a way to startup a webpack project with basic configuration in seconds.
6 |
7 | ### Why?
8 | A new UI framework just came out, a new library, a new tool.. you want to test it but you need to config webpack and install lots of stuff in order to start.. 😤
9 |
10 | There's lots of webpack app generators, but this one won't make you cry.
11 |
12 | Like `create-react-app` which is awesome but very opinionated... have you tried adding something to the `webpack.config.js` file? I know... you must `eject` everything in order to do so.
13 |
14 | What if all I want to do is just write my code, maybe add a webpack plugin or a loader so I can run it in my browser?
15 |
16 | - You want `babel` to transform your ES6 code? OK!
17 | - You want TypeScript instead? OK!
18 | - You want `react`? OK!
19 | - You want to style your app with css? OK!
20 | - You want to work with SCSS preprocessor? OK!!!
21 |
22 | I put all my opinions aside and focused on simplicity, and guess what? you don't need to eject anything in order to edit/add/remove something so you can just start writing your code! **THAT'S - IT!**
23 |
24 |
25 | ### Install?
26 |
27 | `npm install -g create-wpapp`
28 |
29 | ### How does it work?
30 |
31 | ```
32 | $ mkdir project
33 | $ cd project
34 | $ create-wpapp
35 |
36 | ? What is your app name? webpack-starter
37 | ? Select dev server port: 3001
38 | ? Choose app type: TypeScript
39 | ? Are you going to use React in your app? Yes
40 | ? Are you going to use css in your app? Yes
41 | ? Do you want to use SCSS preprocessor? Yes
42 | ? What is your name? Udi Talias
43 | ? What is your email? udi.talias@gmail.com
44 | Creating a new webpack app in /Users/udidu/project...
45 | ...
46 | All Done.
47 | Run npm start to start the dev server
48 | ```
49 |
50 | This will create a webpack project with dev server hmr in the following structure:
51 |
52 | ```
53 | /project
54 | |- node_modules
55 | |- src
56 | |- style
57 | |- main.{css,scss} // Based on your selection
58 | |- app.{js,ts,tsx} // Based on your selection
59 | |- views
60 | |- index.html
61 | |- .gitignore
62 | |- package.json
63 | |- webpack.config.js
64 | |- tsconfig.json // For a TypeScript project
65 |
66 | ```
67 |
68 | Just run `npm start` to start the dev server.
69 |
70 |
71 | ### Contribute?
72 |
73 | Just fork, do what you want and [create a pull request](https://github.com/uditalias/create-wpapp/compare?expand=1).
74 |
75 | ### Issue?
76 |
77 | Just [open one](https://github.com/uditalias/create-wpapp/issues/new).
78 |
79 |
80 |
81 | This tool will evolve from time to time... I know it's very simple but tests and more options will be added to the setup process so you can choose (or not) what you want in your **dead simple webpack project!**
82 |
83 |
84 |
85 |
86 | ---
87 | ## License
88 |
89 | Licensed under the MIT License (Basically - do anything you want license). See [License](LICENSE) for more details.
90 |
--------------------------------------------------------------------------------