create-webpack-starter 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +51 -0
- package/bin/index.js +3 -0
- package/dist/cli.js +57 -0
- package/dist/copier.js +14 -0
- package/dist/index.js +119 -0
- package/dist/installer.js +19 -0
- package/dist/logger.js +12 -0
- package/dist/package-merger.js +24 -0
- package/dist/template-loader.js +29 -0
- package/dist/templates.js +10 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Razerspine
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# create-webpack-starter
|
|
2
|
+
|
|
3
|
+
Create a modern webpack project using ready-to-use templates.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx create-webpack-starter my-app
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
With options:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx create-webpack-starter my-app \
|
|
15
|
+
--template pug-scss-ts \
|
|
16
|
+
--no-install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Options
|
|
20
|
+
|
|
21
|
+
| Option | Description |
|
|
22
|
+
| ------------------- | ------------------------------- |
|
|
23
|
+
| `--template <name>` | Skip prompt and select template |
|
|
24
|
+
| `--no-install` | Skip dependency installation |
|
|
25
|
+
| `--dry-run` | Show what would be done |
|
|
26
|
+
|
|
27
|
+
## Available templates
|
|
28
|
+
* pug-less-js
|
|
29
|
+
* pug-less-ts
|
|
30
|
+
* pug-scss-js
|
|
31
|
+
* pug-scss-ts
|
|
32
|
+
|
|
33
|
+
## Requirement
|
|
34
|
+
* Node.js >= 18
|
|
35
|
+
* npm/ pnpm/ yarn
|
|
36
|
+
|
|
37
|
+
## How it works
|
|
38
|
+
1. CLI copies the selected template
|
|
39
|
+
2. Template files are written to the target directory
|
|
40
|
+
3. Dependencies are installed (unless disabled)
|
|
41
|
+
4. Project is ready to use
|
|
42
|
+
|
|
43
|
+
## What you get
|
|
44
|
+
- Preconfigured webpack setup
|
|
45
|
+
- Pug templates
|
|
46
|
+
- SCSS / Less
|
|
47
|
+
- JavaScript or TypeScript
|
|
48
|
+
- Production-ready build
|
|
49
|
+
|
|
50
|
+
## 📄 License
|
|
51
|
+
This project is licensed under the ISC License.
|
package/bin/index.js
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getCliContext = getCliContext;
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
9
|
+
const templates_1 = require("./templates");
|
|
10
|
+
async function getCliContext() {
|
|
11
|
+
const program = new commander_1.Command();
|
|
12
|
+
program
|
|
13
|
+
.argument('[project-name]', 'Project name')
|
|
14
|
+
.option('-t, --template <template>', 'Template name')
|
|
15
|
+
.option('--no-install', 'Skip npm install')
|
|
16
|
+
.option('--dry-run', 'Do not write files');
|
|
17
|
+
program.parse(process.argv);
|
|
18
|
+
const options = program.opts();
|
|
19
|
+
let projectName = program.args[0];
|
|
20
|
+
// --- ASK PROJECT NAME IF NOT PROVIDED
|
|
21
|
+
if (!projectName) {
|
|
22
|
+
const answer = await inquirer_1.default.prompt([
|
|
23
|
+
{
|
|
24
|
+
type: 'input',
|
|
25
|
+
name: 'projectName',
|
|
26
|
+
message: 'Project name:',
|
|
27
|
+
validate: v => !!v || 'Project name is required'
|
|
28
|
+
}
|
|
29
|
+
]);
|
|
30
|
+
projectName = answer.projectName;
|
|
31
|
+
}
|
|
32
|
+
let template = options.template;
|
|
33
|
+
// --- ASK TEMPLATE IF NOT PROVIDED
|
|
34
|
+
if (!template) {
|
|
35
|
+
const answer = await inquirer_1.default.prompt([
|
|
36
|
+
{
|
|
37
|
+
type: 'list',
|
|
38
|
+
name: 'template',
|
|
39
|
+
message: 'Choose a template:',
|
|
40
|
+
choices: Object.values(templates_1.templates).map(t => ({
|
|
41
|
+
name: t.meta.description,
|
|
42
|
+
value: t.key
|
|
43
|
+
}))
|
|
44
|
+
}
|
|
45
|
+
]);
|
|
46
|
+
template = answer.template;
|
|
47
|
+
}
|
|
48
|
+
if (!template || !templates_1.templates[template]) {
|
|
49
|
+
throw new Error(`Unknown template: ${template}`);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
projectName,
|
|
53
|
+
template,
|
|
54
|
+
noInstall: options.install === false,
|
|
55
|
+
dryRun: Boolean(options.dryRun)
|
|
56
|
+
};
|
|
57
|
+
}
|
package/dist/copier.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.copyTemplate = copyTemplate;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
/**
|
|
9
|
+
* Copy template directory into target directory.
|
|
10
|
+
* Assumes targetDir does NOT exist.
|
|
11
|
+
*/
|
|
12
|
+
async function copyTemplate(templatePath, targetDir) {
|
|
13
|
+
await fs_extra_1.default.copy(templatePath, targetDir);
|
|
14
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
11
|
+
const cli_1 = require("./cli");
|
|
12
|
+
const templates_1 = require("./templates");
|
|
13
|
+
const copier_1 = require("./copier");
|
|
14
|
+
const installer_1 = require("./installer");
|
|
15
|
+
const logger_1 = require("./logger");
|
|
16
|
+
const package_merger_1 = require("./package-merger");
|
|
17
|
+
process.on('unhandledRejection', (err) => {
|
|
18
|
+
if (err?.isTtyError || err?.name === 'ExitPromptError') {
|
|
19
|
+
logger_1.log.info('Cancelled by user');
|
|
20
|
+
process.exit(130);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
async function run() {
|
|
24
|
+
const spinner = (0, ora_1.default)();
|
|
25
|
+
try {
|
|
26
|
+
const { projectName, template: templateKey, noInstall, dryRun } = await (0, cli_1.getCliContext)();
|
|
27
|
+
const template = templates_1.templates[templateKey];
|
|
28
|
+
if (!template) {
|
|
29
|
+
throw new Error(`Unknown template: ${templateKey}`);
|
|
30
|
+
}
|
|
31
|
+
if (!template.filesPath) {
|
|
32
|
+
throw new Error(`Template '${templateKey}' has no filesPath`);
|
|
33
|
+
}
|
|
34
|
+
const targetDir = path_1.default.resolve(process.cwd(), projectName);
|
|
35
|
+
logger_1.log.info(`Creating project: ${projectName}`);
|
|
36
|
+
logger_1.log.info(`Template: ${templateKey}`);
|
|
37
|
+
// --- Copy template (SAFE)
|
|
38
|
+
if (dryRun) {
|
|
39
|
+
if (fs_extra_1.default.existsSync(targetDir)) {
|
|
40
|
+
spinner.warn(`[dry-run] Directory "${projectName}" already exists`);
|
|
41
|
+
}
|
|
42
|
+
spinner.info('[dry-run] Template would be copied');
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// ⛔ overwrite decision happens ONLY here
|
|
46
|
+
if (fs_extra_1.default.existsSync(targetDir)) {
|
|
47
|
+
spinner.stop();
|
|
48
|
+
const { overwrite } = await inquirer_1.default.prompt([
|
|
49
|
+
{
|
|
50
|
+
type: 'confirm',
|
|
51
|
+
name: 'overwrite',
|
|
52
|
+
message: `Directory "${projectName}" already exists. Overwrite?`,
|
|
53
|
+
default: false
|
|
54
|
+
}
|
|
55
|
+
]);
|
|
56
|
+
if (!overwrite) {
|
|
57
|
+
logger_1.log.info('Cancelled by user');
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
spinner.start('Cleaning target directory...');
|
|
61
|
+
await fs_extra_1.default.remove(targetDir);
|
|
62
|
+
spinner.succeed('Directory cleaned');
|
|
63
|
+
}
|
|
64
|
+
spinner.start('Copying template...');
|
|
65
|
+
await (0, copier_1.copyTemplate)(template.filesPath, targetDir);
|
|
66
|
+
spinner.succeed('Template copied');
|
|
67
|
+
}
|
|
68
|
+
// --- Merge dependencies
|
|
69
|
+
if (!dryRun && template.meta) {
|
|
70
|
+
const { dependencies, devDependencies } = template.meta;
|
|
71
|
+
if ((dependencies && Object.keys(dependencies).length > 0) ||
|
|
72
|
+
(devDependencies && Object.keys(devDependencies).length > 0)) {
|
|
73
|
+
spinner.start('Merging template dependencies...');
|
|
74
|
+
await (0, package_merger_1.mergePackageJson)(targetDir, {
|
|
75
|
+
dependencies,
|
|
76
|
+
devDependencies
|
|
77
|
+
});
|
|
78
|
+
spinner.succeed('Dependencies merged');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// --- Script cleanup (JS vs TS)
|
|
82
|
+
if (!dryRun && template.meta?.features?.script) {
|
|
83
|
+
const pkgPath = path_1.default.join(targetDir, 'package.json');
|
|
84
|
+
const pkg = await fs_extra_1.default.readJson(pkgPath);
|
|
85
|
+
if (template.meta.features.script === 'ts') {
|
|
86
|
+
delete pkg.devDependencies?.['@babel/core'];
|
|
87
|
+
delete pkg.devDependencies?.['@babel/preset-env'];
|
|
88
|
+
delete pkg.devDependencies?.['babel-loader'];
|
|
89
|
+
await fs_extra_1.default.remove(path_1.default.join(targetDir, '.babelrc'));
|
|
90
|
+
await fs_extra_1.default.remove(path_1.default.join(targetDir, 'babel.config.js'));
|
|
91
|
+
}
|
|
92
|
+
if (template.meta.features.script === 'js') {
|
|
93
|
+
delete pkg.devDependencies?.['typescript'];
|
|
94
|
+
delete pkg.devDependencies?.['ts-loader'];
|
|
95
|
+
await fs_extra_1.default.remove(path_1.default.join(targetDir, 'tsconfig.json'));
|
|
96
|
+
}
|
|
97
|
+
await fs_extra_1.default.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
98
|
+
}
|
|
99
|
+
// --- Install deps
|
|
100
|
+
if (noInstall) {
|
|
101
|
+
spinner.info('Skipping install');
|
|
102
|
+
}
|
|
103
|
+
else if (dryRun) {
|
|
104
|
+
spinner.info('[dry-run] Would install dependencies');
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
spinner.start('Installing dependencies...');
|
|
108
|
+
await (0, installer_1.installDeps)(targetDir);
|
|
109
|
+
spinner.succeed('Dependencies installed');
|
|
110
|
+
}
|
|
111
|
+
logger_1.log.success('Done!');
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
spinner.stop();
|
|
115
|
+
console.error('❌ Error:', err?.message || err);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
run().then();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.installDeps = installDeps;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
function installDeps(cwd) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const child = (0, child_process_1.spawn)('npm', ['install'], {
|
|
8
|
+
cwd,
|
|
9
|
+
stdio: 'inherit',
|
|
10
|
+
shell: true
|
|
11
|
+
});
|
|
12
|
+
child.on('close', code => {
|
|
13
|
+
if (code === 0)
|
|
14
|
+
resolve();
|
|
15
|
+
else
|
|
16
|
+
reject(new Error('npm install failed'));
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.log = void 0;
|
|
7
|
+
const kleur_1 = __importDefault(require("kleur"));
|
|
8
|
+
exports.log = {
|
|
9
|
+
info: (msg) => console.log(kleur_1.default.cyan(msg)),
|
|
10
|
+
success: (msg) => console.log(kleur_1.default.green(msg)),
|
|
11
|
+
error: (msg) => console.error(kleur_1.default.red(msg))
|
|
12
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.mergePackageJson = mergePackageJson;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
async function mergePackageJson(targetDir, templateDeps) {
|
|
10
|
+
const packageJsonPath = path_1.default.join(targetDir, 'package.json');
|
|
11
|
+
if (!await fs_extra_1.default.pathExists(packageJsonPath)) {
|
|
12
|
+
throw new Error('package.json not found in generated project');
|
|
13
|
+
}
|
|
14
|
+
const pkg = await fs_extra_1.default.readJson(packageJsonPath);
|
|
15
|
+
pkg.dependencies = {
|
|
16
|
+
...(pkg.dependencies || {}),
|
|
17
|
+
...(templateDeps.dependencies || {})
|
|
18
|
+
};
|
|
19
|
+
pkg.devDependencies = {
|
|
20
|
+
...(pkg.devDependencies || {}),
|
|
21
|
+
...(templateDeps.devDependencies || {})
|
|
22
|
+
};
|
|
23
|
+
await fs_extra_1.default.writeJson(packageJsonPath, pkg, { spaces: 2 });
|
|
24
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadTemplates = loadTemplates;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
function loadTemplates(templatesRoot) {
|
|
10
|
+
const entries = fs_1.default.readdirSync(templatesRoot, { withFileTypes: true });
|
|
11
|
+
const result = {};
|
|
12
|
+
for (const entry of entries) {
|
|
13
|
+
if (!entry.isDirectory())
|
|
14
|
+
continue;
|
|
15
|
+
const templateDir = path_1.default.join(templatesRoot, entry.name);
|
|
16
|
+
const metaPath = path_1.default.join(templateDir, 'template.json');
|
|
17
|
+
const filesPath = path_1.default.join(templateDir, 'files');
|
|
18
|
+
if (!fs_1.default.existsSync(metaPath) || !fs_1.default.existsSync(filesPath))
|
|
19
|
+
continue;
|
|
20
|
+
const meta = JSON.parse(fs_1.default.readFileSync(metaPath, 'utf-8'));
|
|
21
|
+
result[entry.name] = {
|
|
22
|
+
key: entry.name,
|
|
23
|
+
meta,
|
|
24
|
+
path: templateDir,
|
|
25
|
+
filesPath
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.templates = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const template_loader_1 = require("./template-loader");
|
|
9
|
+
const templatesRoot = path_1.default.resolve(__dirname, '../../../packages/templates');
|
|
10
|
+
exports.templates = (0, template_loader_1.loadTemplates)(templatesRoot);
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-webpack-starter",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Create a webpack starter with Pug, SCSS, TS and more",
|
|
5
|
+
"bin": {
|
|
6
|
+
"create-webpack-starter": "bin/index.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin",
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "ts-node src/index.ts",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"webpack",
|
|
23
|
+
"starter",
|
|
24
|
+
"cli",
|
|
25
|
+
"pug",
|
|
26
|
+
"scss",
|
|
27
|
+
"less",
|
|
28
|
+
"javascript",
|
|
29
|
+
"typescript"
|
|
30
|
+
],
|
|
31
|
+
"author": "Razerspine",
|
|
32
|
+
"license": "ISC",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"commander": "^12.0.0",
|
|
35
|
+
"fs-extra": "^11.2.0",
|
|
36
|
+
"inquirer": "^9.3.0",
|
|
37
|
+
"kleur": "^4.1.5",
|
|
38
|
+
"ora": "^9.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/fs-extra": "^11.0.4",
|
|
42
|
+
"@types/inquirer": "^9.0.9",
|
|
43
|
+
"ts-node": "^10.9.2",
|
|
44
|
+
"typescript": "^5.9.3"
|
|
45
|
+
}
|
|
46
|
+
}
|