stoix 0.1.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/README.md +147 -0
- package/dist/bin/index.d.ts +2 -0
- package/dist/bin/index.js +119 -0
- package/package.json +45 -0
- package/template/.env +2 -0
- package/template/.env.example +2 -0
- package/template/gitignore +6 -0
- package/template/index.html +13 -0
- package/template/package-lock.json +3383 -0
- package/template/package.json +34 -0
- package/template/pages/index.tsx +61 -0
- package/template/public/favicon.svg +4 -0
- package/template/server/routes/example.ts +28 -0
- package/template/server/server.ts +78 -0
- package/template/src/App.tsx +18 -0
- package/template/src/main.tsx +17 -0
- package/template/src/styles.css +198 -0
- package/template/stoix.config.ts +19 -0
- package/template/tsconfig.json +31 -0
- package/template/tsconfig.server.json +16 -0
- package/template/vite-env.d.ts +1 -0
- package/template/vite.config.ts +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Stoix
|
|
2
|
+
|
|
3
|
+
Stoix is a TypeScript-first framework starter that scaffolds a Node + Express + React app with Vite.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx stoix create my-app
|
|
9
|
+
cd my-app
|
|
10
|
+
npm run dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
App URL: `http://localhost:3000`
|
|
14
|
+
|
|
15
|
+
## CLI
|
|
16
|
+
|
|
17
|
+
### Create a project
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx stoix create <project-name>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Help and version
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx stoix --help
|
|
27
|
+
npx stoix --version
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Project names must start with a letter and may contain letters, numbers, `.`, `_`, and `-`.
|
|
31
|
+
|
|
32
|
+
## What Stoix Generates
|
|
33
|
+
|
|
34
|
+
- Express server with auto-loaded API route modules
|
|
35
|
+
- React 18 client with Vite HMR in development
|
|
36
|
+
- Shared TypeScript setup across server, client, and config
|
|
37
|
+
- Production build pipeline for client and server output
|
|
38
|
+
|
|
39
|
+
## Generated Project Structure
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
my-app/
|
|
43
|
+
├── package.json # Dependencies and scripts
|
|
44
|
+
├── stoix.config.ts # Stoix framework configuration
|
|
45
|
+
├── tsconfig.json # TypeScript config (shared)
|
|
46
|
+
├── tsconfig.server.json # TypeScript config (server)
|
|
47
|
+
├── vite.config.ts # Vite build configuration
|
|
48
|
+
├── vite-env.d.ts # Vite environment types
|
|
49
|
+
├── index.html # HTML entry point
|
|
50
|
+
├── .env # Environment variables
|
|
51
|
+
├── server/ # Express server
|
|
52
|
+
│ ├── server.ts # Server entry point
|
|
53
|
+
│ └── routes/ # Auto-loaded API routes
|
|
54
|
+
│ └── example.ts # Example API route
|
|
55
|
+
├── src/ # React client source
|
|
56
|
+
│ ├── App.tsx # Root React component
|
|
57
|
+
│ ├── main.tsx # Client entry point
|
|
58
|
+
│ └── styles.css # Global styles
|
|
59
|
+
├── pages/ # SSR pages (if enabled)
|
|
60
|
+
│ └── index.tsx # Home page
|
|
61
|
+
└── public/ # Static assets
|
|
62
|
+
└── favicon.svg # Favicon
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Generated Scripts
|
|
66
|
+
|
|
67
|
+
| Script | Description |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `npm run dev` | Runs the Express server with Vite middleware in development (`tsx server/server.ts`). |
|
|
70
|
+
| `npm run build` | Runs TS checks, builds client assets with Vite, then compiles server output. |
|
|
71
|
+
| `npm start` | Starts the production server from compiled files. |
|
|
72
|
+
| `npm run typecheck` | Runs TypeScript checks without emit. |
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
Edit `stoix.config.ts`:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
export interface StoixConfig {
|
|
80
|
+
port: number;
|
|
81
|
+
framework: 'react';
|
|
82
|
+
server: {
|
|
83
|
+
apiPrefix: string;
|
|
84
|
+
cors: string | string[] | false;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Default values:
|
|
90
|
+
|
|
91
|
+
- `port`: `3000`
|
|
92
|
+
- `server.apiPrefix`: `"/api"`
|
|
93
|
+
- `server.cors`: `false`
|
|
94
|
+
|
|
95
|
+
## API Routes
|
|
96
|
+
|
|
97
|
+
Any file in `server/routes/` that default-exports an Express router is auto-mounted at:
|
|
98
|
+
|
|
99
|
+
`<apiPrefix>/<path-to-file>`
|
|
100
|
+
|
|
101
|
+
Examples:
|
|
102
|
+
|
|
103
|
+
- `server/routes/users.ts` -> `/api/users`
|
|
104
|
+
- `server/routes/auth/me.ts` -> `/api/auth/me`
|
|
105
|
+
- `server/routes/user/profile.ts` -> `/api/user/profile`
|
|
106
|
+
- `server/routes/try/get.ts` -> `/api/try/get`
|
|
107
|
+
- `server/routes/auth/index.ts` -> `/api/auth`
|
|
108
|
+
|
|
109
|
+
## Environment Variables
|
|
110
|
+
|
|
111
|
+
`template/.env` includes:
|
|
112
|
+
|
|
113
|
+
```env
|
|
114
|
+
PORT=3000
|
|
115
|
+
NODE_ENV=development
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`PORT` overrides the port in `stoix.config.ts`.
|
|
119
|
+
|
|
120
|
+
## Development and Production Flow
|
|
121
|
+
|
|
122
|
+
- Development: Express runs first, then mounts Vite as middleware for client HMR.
|
|
123
|
+
- Production: Express serves static files from `dist/client` and handles SPA fallback to `index.html`.
|
|
124
|
+
|
|
125
|
+
## Framework Development (this repo)
|
|
126
|
+
|
|
127
|
+
From this repository:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npm run build
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`prepack` runs:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npm run clean && npm run build
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
This removes template build artifacts (`template/node_modules`, `template/dist`, `template/.vite`) before packaging.
|
|
140
|
+
|
|
141
|
+
## Current Limitations
|
|
142
|
+
|
|
143
|
+
- Route modules are expected to `export default` an Express router.
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const STOIX_VERSION = fs.readJsonSync(path.resolve(__dirname, '..', '..', 'package.json')).version || '0.1.0';
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
const command = args[0];
|
|
12
|
+
const projectName = args[1];
|
|
13
|
+
function printBanner() {
|
|
14
|
+
console.log(chalk.cyan(`
|
|
15
|
+
===================================
|
|
16
|
+
Stoix v${STOIX_VERSION}
|
|
17
|
+
Node + Express + React + Vite
|
|
18
|
+
===================================
|
|
19
|
+
`));
|
|
20
|
+
}
|
|
21
|
+
function printUsage() {
|
|
22
|
+
printBanner();
|
|
23
|
+
console.log(`${chalk.bold('Usage:')}
|
|
24
|
+
npx stoix create <project-name>
|
|
25
|
+
|
|
26
|
+
${chalk.bold('Commands:')}
|
|
27
|
+
create <name> Scaffold a new Stoix project
|
|
28
|
+
|
|
29
|
+
${chalk.bold('Examples:')}
|
|
30
|
+
npx stoix create my-app
|
|
31
|
+
npx stoix create blog-api
|
|
32
|
+
`);
|
|
33
|
+
}
|
|
34
|
+
function validateProjectName(name) {
|
|
35
|
+
const validPattern = /^[a-zA-Z][a-zA-Z0-9._-]*$/;
|
|
36
|
+
return validPattern.test(name);
|
|
37
|
+
}
|
|
38
|
+
async function createProject(name) {
|
|
39
|
+
const targetDir = path.resolve(process.cwd(), name);
|
|
40
|
+
if (fs.existsSync(targetDir)) {
|
|
41
|
+
console.error(chalk.red(`\n Error: Directory "${name}" already exists.\n`));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
printBanner();
|
|
45
|
+
console.log(chalk.cyan(` Creating Stoix project: ${chalk.bold(name)}\n`));
|
|
46
|
+
// Resolve template directory (relative to compiled dist/bin/index.js -> ../../template)
|
|
47
|
+
const templateDir = path.resolve(__dirname, '..', '..', 'template');
|
|
48
|
+
if (!fs.existsSync(templateDir)) {
|
|
49
|
+
console.error(chalk.red(' Error: Template directory not found.'));
|
|
50
|
+
console.error(chalk.dim(` Expected at: ${templateDir}`));
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
fs.copySync(templateDir, targetDir, { overwrite: false });
|
|
54
|
+
console.log(chalk.green(' + Project files created'));
|
|
55
|
+
const gitignoreSrc = path.join(targetDir, 'gitignore');
|
|
56
|
+
const gitignoreDest = path.join(targetDir, '.gitignore');
|
|
57
|
+
if (fs.existsSync(gitignoreSrc)) {
|
|
58
|
+
fs.renameSync(gitignoreSrc, gitignoreDest);
|
|
59
|
+
}
|
|
60
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
61
|
+
const pkg = fs.readJsonSync(pkgPath);
|
|
62
|
+
pkg.name = name;
|
|
63
|
+
fs.writeJsonSync(pkgPath, pkg, { spaces: 2 });
|
|
64
|
+
console.log(chalk.green(' + package.json configured'));
|
|
65
|
+
console.log(chalk.cyan('\n Installing dependencies...\n'));
|
|
66
|
+
let dependenciesInstalled = true;
|
|
67
|
+
try {
|
|
68
|
+
execSync('npm install', {
|
|
69
|
+
cwd: targetDir,
|
|
70
|
+
stdio: 'inherit',
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
dependenciesInstalled = false;
|
|
75
|
+
console.error(chalk.yellow('\n Warning: npm install failed. Run it manually.'));
|
|
76
|
+
}
|
|
77
|
+
const statusLine = dependenciesInstalled
|
|
78
|
+
? chalk.green.bold(' + Stoix project is ready!')
|
|
79
|
+
: chalk.yellow.bold(' ! Stoix project files are ready (dependencies not installed)');
|
|
80
|
+
const installStep = dependenciesInstalled ? '' : ` ${chalk.cyan('npm install')}\n`;
|
|
81
|
+
console.log(`
|
|
82
|
+
${statusLine}
|
|
83
|
+
|
|
84
|
+
${chalk.bold(' Next steps:')}
|
|
85
|
+
${chalk.cyan(`cd ${name}`)}
|
|
86
|
+
${installStep} ${chalk.cyan('npm run dev')}
|
|
87
|
+
|
|
88
|
+
${chalk.dim(' Stoix app will run at http://localhost:3000')}
|
|
89
|
+
`);
|
|
90
|
+
}
|
|
91
|
+
async function main() {
|
|
92
|
+
if (!command || command === '--help' || command === '-h') {
|
|
93
|
+
printUsage();
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
96
|
+
if (command === '--version' || command === '-v') {
|
|
97
|
+
console.log(`stoix v${STOIX_VERSION}`);
|
|
98
|
+
process.exit(0);
|
|
99
|
+
}
|
|
100
|
+
if (command !== 'create') {
|
|
101
|
+
console.error(chalk.red(`\n Unknown command: "${command}"\n`));
|
|
102
|
+
printUsage();
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
if (!projectName) {
|
|
106
|
+
console.error(chalk.red('\n Error: Please provide a project name.\n'));
|
|
107
|
+
console.log(chalk.dim(' npx stoix create <project-name>\n'));
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
if (!validateProjectName(projectName)) {
|
|
111
|
+
console.error(chalk.red('\n Error: Project name must start with a letter and contain only letters, numbers, hyphens, dots, or underscores.\n'));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
await createProject(projectName);
|
|
115
|
+
}
|
|
116
|
+
main().catch((err) => {
|
|
117
|
+
console.error(chalk.red('\n Error:'), err.message);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stoix",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Stoix — A Node + Express + React framework with Vite, fully written in TypeScript",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"stoix": "dist/bin/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"clean": "node -e \"const fs=require('fs');['template/node_modules','template/dist','template/.vite'].forEach(d=>{if(fs.existsSync(d)){fs.rmSync(d,{recursive:true,force:true});console.log('removed',d)}})\"",
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepack": "npm run clean && npm run build"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"template",
|
|
17
|
+
"!template/node_modules",
|
|
18
|
+
"!template/dist",
|
|
19
|
+
"!template/.vite"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"stoix",
|
|
23
|
+
"react",
|
|
24
|
+
"express",
|
|
25
|
+
"vite",
|
|
26
|
+
"typescript",
|
|
27
|
+
"framework",
|
|
28
|
+
"cli",
|
|
29
|
+
"scaffolding"
|
|
30
|
+
],
|
|
31
|
+
"author": "Patrick",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"chalk": "^5.3.0",
|
|
38
|
+
"fs-extra": "^11.2.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/fs-extra": "^11.0.4",
|
|
42
|
+
"@types/node": "^20.11.0",
|
|
43
|
+
"typescript": "^5.3.3"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/template/.env
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Stoix App</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|