Skip to content

Commit ef068f0

Browse files
committed
first release
0 parents  commit ef068f0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+14020
-0
lines changed

Diff for: .editorconfig

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 2
6+
end_of_line = lf
7+
charset = utf-8
8+
trim_trailing_whitespace = true
9+
insert_final_newline = true

Diff for: .eslintignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
templates/

Diff for: .eslintrc.js

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module.exports = {
2+
env: {
3+
browser: true,
4+
es2021: true
5+
},
6+
extends: ['airbnb-base', 'prettier'],
7+
parser: '@typescript-eslint/parser',
8+
parserOptions: {
9+
ecmaVersion: 12,
10+
sourceType: 'module'
11+
},
12+
plugins: ['@typescript-eslint'],
13+
rules: {
14+
'import/extensions': [
15+
'error',
16+
'always',
17+
{
18+
ts: 'never'
19+
}
20+
],
21+
'no-console': 0,
22+
'no-use-before-define': 'off',
23+
'no-unused-vars': 'warn',
24+
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
25+
'import/prefer-default-export': 0,
26+
'import/newline-after-import': 0,
27+
'no-shadow': 1,
28+
'prefer-const': 1,
29+
'prefer-spread': 1,
30+
'no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }]
31+
},
32+
settings: {
33+
'import/resolver': {
34+
node: {
35+
extensions: ['.js', '.jsx', '.ts', '.tsx']
36+
}
37+
}
38+
}
39+
}

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

Diff for: .husky/.gitignore

Whitespace-only changes.

Diff for: .husky/pre-commit

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/sh
2+
. "$(dirname "$0")/_/husky.sh"
3+
4+
npx --no-install lint-staged

Diff for: .prettierrc

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"singleQuote": true,
3+
"jsxSingleQuote": true,
4+
"semi": false,
5+
"tabWidth": 2,
6+
"printWidth": 120,
7+
"bracketSpacing": true,
8+
"jsxBracketSameLine": false,
9+
"arrowParens": "always",
10+
"trailingComma": "none",
11+
"endOfLine": "auto"
12+
}

Diff for: LICENSE

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2015 gatsbyjs
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.
22+

Diff for: README.md

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# esbuild Create React App [![NPM](https://img.shields.io/npm/v/esbuild-create-react-app.svg)](https://www.npmjs.com/package/esbuild-create-react-app) ![npm bundle size](https://img.shields.io/bundlephobia/min/esbuild-create-react-app) ![GitHub](https://img.shields.io/github/license/awran5/esbuild-create-react-app)
2+
3+
<img alt="Logo" align="right" src="./assets/logo.png" width="20%" />
4+
5+
A minimal replacement for create-react-app using a truly blazing fast [esbuild](https://esbuild.github.io/) bundler. Up and running in less than 1 minute with almost zero configuration needed.
6+
<br />
7+
8+
---
9+
10+
## What is inside?
11+
12+
<img alt="file structure" align="right" src="./assets/files.png" width="20%">
13+
14+
Truly minimal React app with almost zero configuration needed.
15+
16+
- [TypeScript](https://www.typescriptlang.org/)
17+
- [esbuild](https://esbuild.github.io/)
18+
- [Eslint](https://eslint.org/) with [airbnb](https://github.com/airbnb/javascript) Style Guide
19+
- [Prettier](https://prettier.io/)
20+
- [Husky](https://github.com/typicode/husky)
21+
- [lint-staged](https://github.com/okonet/lint-staged)
22+
- [live-server](https://github.com/tapio/live-server)
23+
24+
## Overview
25+
26+
<p align='center'>
27+
<img src='./assets/overview.gif' width='800' alt='app overview'>
28+
</p>
29+
30+
## Install
31+
32+
```bash
33+
npx esbuild-create-react-app my-app
34+
35+
cd my-app
36+
37+
yarn start | npm run start
38+
```
39+
40+
## Available Templates
41+
42+
- Default [JS template](https://github.com/awran5/esbuild-create-react-app/tree/main/templates/javascript)
43+
- [Typescript template](https://github.com/awran5/esbuild-create-react-app/tree/main/templates/typescript)
44+
45+
### License
46+
47+
MIT © [awran5](https://github.com/awran5/)

Diff for: asset/files.png

16.3 KB
Loading

Diff for: asset/logo.png

3.83 KB
Loading

Diff for: asset/overview.gif

232 KB
Loading

Diff for: bin/functions.js

+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
"use strict";
2+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4+
return new (P || (P = Promise))(function (resolve, reject) {
5+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8+
step((generator = generator.apply(thisArg, _arguments || [])).next());
9+
});
10+
};
11+
var __importDefault = (this && this.__importDefault) || function (mod) {
12+
return (mod && mod.__esModule) ? mod : { "default": mod };
13+
};
14+
Object.defineProperty(exports, "__esModule", { value: true });
15+
const util_1 = require("util");
16+
const child_process_1 = require("child_process");
17+
const fs_1 = require("fs");
18+
const chalk_1 = __importDefault(require("chalk"));
19+
const path_1 = __importDefault(require("path"));
20+
const inquirer_1 = __importDefault(require("inquirer"));
21+
const listr2_1 = require("listr2");
22+
const ora_1 = __importDefault(require("ora"));
23+
const asyncExec = util_1.promisify(child_process_1.exec);
24+
function getProjectName() {
25+
return __awaiter(this, void 0, void 0, function* () {
26+
const args = process.argv.slice(2, process.argv.length);
27+
const name = args[0];
28+
const acceptedName = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(name);
29+
if (!name) {
30+
console.log(`\n${chalk_1.default.bgRed(` Error! `)} Please specify a project name e.g. ${chalk_1.default.cyan(`npx react-esbuild-app`)} ${chalk_1.default.green('my-app')}\n`);
31+
process.exit();
32+
}
33+
if (!acceptedName) {
34+
console.log(`\n${chalk_1.default.bgRed(` npm naming restrictions! `)} Please specify a project name with lowercase characters separated by hyphen (-) e.g. ${chalk_1.default.green('my-app')}\n`);
35+
process.exit();
36+
}
37+
return name;
38+
});
39+
}
40+
function removeProject(projectName) {
41+
return __awaiter(this, void 0, void 0, function* () {
42+
const spinner = ora_1.default();
43+
return new Promise((resolve, reject) => {
44+
spinner.start(`Removing ${chalk_1.default.cyan(projectName)}, please wait`);
45+
fs_1.rmdir(projectName, { recursive: true }, (error) => {
46+
if (error) {
47+
reject(new Error(`Directory ${chalk_1.default.red(projectName)} can not be removed. Please close any open files and try again`));
48+
}
49+
spinner.succeed('Done');
50+
resolve(true);
51+
});
52+
});
53+
});
54+
}
55+
function isProjectExists(projectName) {
56+
return __awaiter(this, void 0, void 0, function* () {
57+
if (fs_1.existsSync(projectName)) {
58+
return new Promise((resolve, reject) => {
59+
inquirer_1.default
60+
.prompt([
61+
{
62+
type: 'confirm',
63+
name: 'exists',
64+
message: `Project ${chalk_1.default.cyan(projectName)} is already exists. Do you want to remove it? (just hit enter for NO)?`,
65+
default: false
66+
}
67+
])
68+
.then((answer) => answer.exists && resolve(removeProject(projectName)))
69+
.catch((err) => reject(err));
70+
});
71+
}
72+
return false;
73+
});
74+
}
75+
function getSelectedTemplate() {
76+
return __awaiter(this, void 0, void 0, function* () {
77+
const templates = fs_1.readdirSync(`${__dirname}/../templates`, { withFileTypes: true });
78+
const templatesDirectories = templates.filter((folder) => folder.isDirectory()).map((template) => template.name);
79+
return new Promise((resolve, reject) => {
80+
inquirer_1.default
81+
.prompt({
82+
type: 'list',
83+
name: 'selected',
84+
message: 'Welcome! To get started please choose a template',
85+
choices: [...templatesDirectories]
86+
})
87+
.then((answer) => resolve(answer.selected))
88+
.catch((err) => reject(err));
89+
});
90+
});
91+
}
92+
function copyFolderSync(source, destination) {
93+
return __awaiter(this, void 0, void 0, function* () {
94+
fs_1.mkdirSync(destination);
95+
fs_1.readdirSync(source).map((file) => {
96+
const srcPath = path_1.default.join(source, file);
97+
const destPath = path_1.default.join(destination, file);
98+
return fs_1.lstatSync(srcPath).isFile() ? fs_1.copyFileSync(srcPath, destPath) : copyFolderSync(srcPath, destPath);
99+
});
100+
});
101+
}
102+
function updatePackageJson(projectName) {
103+
return __awaiter(this, void 0, void 0, function* () {
104+
const author = (process.env.USERPROFILE && process.env.USERPROFILE.split(path_1.default.sep)[2]) || 'package-author';
105+
const packageJsonPath = `${projectName}/package.json`;
106+
const oldPackageJson = JSON.parse(fs_1.readFileSync(packageJsonPath, 'utf8'));
107+
const newPackageJson = Object.assign(Object.assign({}, oldPackageJson), { name: projectName, author });
108+
fs_1.writeFileSync(packageJsonPath, JSON.stringify(newPackageJson, null, 2), 'utf8');
109+
});
110+
}
111+
function installDependencies(projectName) {
112+
return __awaiter(this, void 0, void 0, function* () {
113+
let pm = 'npm i';
114+
try {
115+
yield asyncExec('yarnpkg --version');
116+
pm = 'yarn';
117+
}
118+
catch (e) {
119+
pm = 'npm i';
120+
}
121+
const options = { cwd: projectName };
122+
yield asyncExec(pm, options);
123+
yield asyncExec(`git init && git add . && git commit -m "initial commit"`, options);
124+
});
125+
}
126+
function successfulMessage(projectName) {
127+
return __awaiter(this, void 0, void 0, function* () {
128+
console.log(`\n${chalk_1.default.black.bgGreen(' SUCCESS! ')}\n\nAll dependencies are installed already, to get started, just navigate to your ${chalk_1.default.cyan(projectName)} project and type ${chalk_1.default.cyan('npm run start')} or ${chalk_1.default.cyan('yarn start')}\n\n${chalk_1.default.yellow('Happy Coding!')}\n\nQuestions? Feedback? Please let me know!
129+
${chalk_1.default.green('https://github.com/awran5/esbuild-create-react-app/issues')}`);
130+
});
131+
}
132+
function createProject() {
133+
return __awaiter(this, void 0, void 0, function* () {
134+
// Read project name from args
135+
const projectName = yield getProjectName();
136+
// Check if project exists
137+
yield isProjectExists(projectName);
138+
// Greeting Message
139+
console.log(`${chalk_1.default.greenBright(`
140+
:::::::::: :::::::: :::::::: ::::::::: :::
141+
:+: :+: :+: :+: :+: :+: :+: :+: :+:
142+
+:+ +:+ +:+ +:+ +:+ +:+ +:+
143+
+#++:++# +#++:++#++ +#+ +#++:++#: +#++:++#++:
144+
+#+ +#+ +#+ +#+ +#+ +#+ +#+
145+
#+# #+# #+# #+# #+# #+# #+# #+# #+#
146+
########## ######## ######## ### ### ### ### `)}\n`);
147+
// Read templates
148+
const template = yield getSelectedTemplate();
149+
const tasks = new listr2_1.Listr([
150+
{
151+
title: `[1/4] 🚀 Creating a new project in ${chalk_1.default.cyan(projectName)}`,
152+
task: () => copyFolderSync(path_1.default.join(`${__dirname}/../templates`, template), projectName)
153+
},
154+
{
155+
title: '[2/4] 🎁 Modifying package.json...',
156+
task: () => updatePackageJson(projectName)
157+
},
158+
{
159+
title: '[3/4] 🍳 Installing dependencies...',
160+
task: () => installDependencies(projectName)
161+
},
162+
{
163+
title: '[4/4] ✨ All done...',
164+
task: () => new Promise((resolve) => setTimeout(resolve, 500))
165+
}
166+
]);
167+
// Run and show successful message
168+
tasks
169+
.run()
170+
.then(() => __awaiter(this, void 0, void 0, function* () { return successfulMessage(projectName); }))
171+
.catch(console.error);
172+
});
173+
}
174+
exports.default = createProject;

Diff for: bin/index.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env node
2+
"use strict";
3+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5+
return new (P || (P = Promise))(function (resolve, reject) {
6+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9+
step((generator = generator.apply(thisArg, _arguments || [])).next());
10+
});
11+
};
12+
var __importDefault = (this && this.__importDefault) || function (mod) {
13+
return (mod && mod.__esModule) ? mod : { "default": mod };
14+
};
15+
Object.defineProperty(exports, "__esModule", { value: true });
16+
const functions_1 = __importDefault(require("./functions"));
17+
(() => __awaiter(void 0, void 0, void 0, function* () {
18+
try {
19+
yield functions_1.default();
20+
}
21+
catch (error) {
22+
console.log(error);
23+
}
24+
}))();

0 commit comments

Comments
 (0)