frontend-hamroun 1.1.58 → 1.1.59

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/bin/cli.js CHANGED
@@ -1,87 +1,122 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { Command } from 'commander';
4
- import inquirer from 'inquirer';
5
- import fs from 'fs-extra';
4
+ import { spawn } from 'child_process';
5
+ import sirv from 'sirv';
6
+ import http from 'http';
6
7
  import path from 'path';
7
8
  import { fileURLToPath } from 'url';
9
+ import fs from 'fs-extra';
8
10
  import chalk from 'chalk';
9
- import { createSpinner } from 'nanospinner';
10
-
11
- const __filename = fileURLToPath(import.meta.url);
12
- const __dirname = path.dirname(__filename);
13
-
14
- async function init() {
15
- const program = new Command();
16
11
 
17
- program
18
- .name('create-frontend-app')
19
- .description('Create a new Frontend Hamroun application')
20
- .argument('[name]', 'Project name')
21
- .action(async (name) => {
22
- const projectName = name || await askProjectName();
23
- await createProject(projectName);
12
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
+ const program = new Command();
14
+
15
+ // Create development server
16
+ async function createDevServer(root) {
17
+ const server = http.createServer(sirv(root, {
18
+ dev: true,
19
+ single: true
20
+ }));
21
+
22
+ return new Promise((resolve) => {
23
+ server.listen(3000, () => {
24
+ console.log(chalk.green('✓'), 'Dev server running at:', chalk.cyan('http://localhost:3000'));
25
+ resolve(server);
24
26
  });
25
-
26
- program.parse();
27
- }
28
-
29
- async function askProjectName() {
30
- const { projectName } = await inquirer.prompt([{
31
- type: 'input',
32
- name: 'projectName',
33
- message: 'What is your project named?',
34
- default: 'my-frontend-app'
35
- }]);
36
- return projectName;
37
- }
38
-
39
- async function askProjectType() {
40
- const { template } = await inquirer.prompt([{
41
- type: 'list',
42
- name: 'template',
43
- message: 'Select project type:',
44
- choices: [
45
- { name: 'Client Side App', value: 'basic-app' },
46
- { name: 'Server Side Rendered App', value: 'ssr-template' }
47
- ]
48
- }]);
49
- return template;
27
+ });
50
28
  }
51
29
 
52
- async function createProject(projectName) {
53
- const spinner = createSpinner('Creating project...').start();
54
-
55
- try {
56
- const template = await askProjectType();
57
- const templateDir = path.join(__dirname, '..', 'templates', template);
58
- const targetDir = path.join(process.cwd(), projectName);
59
-
60
- // Create project directory
61
- await fs.ensureDir(targetDir);
62
-
63
- // Copy template files
64
- await fs.copy(templateDir, targetDir);
65
-
66
- // Update package.json
67
- const pkgPath = path.join(targetDir, 'package.json');
68
- const pkg = await fs.readJson(pkgPath);
69
- pkg.name = projectName;
70
- await fs.writeJson(pkgPath, pkg, { spaces: 2 });
71
-
72
- spinner.success({ text: `Project ${chalk.green(projectName)} created successfully!` });
73
-
74
- // Show next steps
75
- console.log('\nNext steps:');
76
- console.log(chalk.cyan(` cd ${projectName}`));
77
- console.log(chalk.cyan(' npm install'));
78
- console.log(chalk.cyan(' npm run dev'));
79
-
80
- } catch (error) {
81
- spinner.error({ text: 'Failed to create project' });
82
- console.error(chalk.red(error));
83
- process.exit(1);
30
+ // Build configuration
31
+ async function buildProject(root, isDev = false) {
32
+ const esbuild = await import('esbuild');
33
+
34
+ const config = {
35
+ entryPoints: [path.join(root, 'src/main.tsx')],
36
+ bundle: true,
37
+ outfile: path.join(root, 'dist/bundle.js'),
38
+ loader: { '.tsx': 'tsx', '.ts': 'ts' },
39
+ jsxFactory: '_jsx',
40
+ jsxFragment: '_Fragment',
41
+ platform: 'browser',
42
+ target: 'es2020',
43
+ minify: !isDev,
44
+ sourcemap: isDev,
45
+ define: {
46
+ 'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production')
47
+ },
48
+ inject: [path.join(root, 'src/jsx-shim.js')],
49
+ };
50
+
51
+ if (isDev) {
52
+ const ctx = await esbuild.context(config);
53
+ await ctx.watch();
54
+ console.log(chalk.green('✓'), 'Watching for changes...');
55
+ return ctx;
56
+ } else {
57
+ await esbuild.build(config);
58
+ console.log(chalk.green('✓'), 'Build complete');
84
59
  }
85
60
  }
86
61
 
87
- init().catch(console.error);
62
+ // Commands
63
+ program
64
+ .command('dev')
65
+ .description('Start development server')
66
+ .action(async () => {
67
+ try {
68
+ const root = process.cwd();
69
+ const [server, buildCtx] = await Promise.all([
70
+ createDevServer(root),
71
+ buildProject(root, true)
72
+ ]);
73
+
74
+ process.once('SIGINT', () => {
75
+ server.close();
76
+ buildCtx.dispose();
77
+ process.exit(0);
78
+ });
79
+ } catch (error) {
80
+ console.error(chalk.red('Error:'), error.message);
81
+ process.exit(1);
82
+ }
83
+ });
84
+
85
+ program
86
+ .command('build')
87
+ .description('Build for production')
88
+ .action(async () => {
89
+ try {
90
+ const root = process.cwd();
91
+ await buildProject(root, false);
92
+ } catch (error) {
93
+ console.error(chalk.red('Error:'), error.message);
94
+ process.exit(1);
95
+ }
96
+ });
97
+
98
+ program
99
+ .command('create <project-name>')
100
+ .description('Create a new project')
101
+ .action(async (name) => {
102
+ const targetDir = path.join(process.cwd(), name);
103
+
104
+ try {
105
+ // Copy template files
106
+ await fs.copy(
107
+ path.join(__dirname, '../templates/basic-app'),
108
+ targetDir
109
+ );
110
+
111
+ console.log(chalk.green('✓'), 'Created project:', chalk.cyan(name));
112
+ console.log('\nNext steps:');
113
+ console.log(` cd ${name}`);
114
+ console.log(' npm install');
115
+ console.log(' npm run dev');
116
+ } catch (error) {
117
+ console.error(chalk.red('Error:'), error.message);
118
+ process.exit(1);
119
+ }
120
+ });
121
+
122
+ program.parse(process.argv);
package/bin/cli.ts ADDED
@@ -0,0 +1,40 @@
1
+ import { Command } from 'commander';
2
+ import { startDevServer } from './dev-server';
3
+ import { createBuildConfig } from './esbuild.config';
4
+
5
+ const program = new Command();
6
+
7
+ program
8
+ .command('dev')
9
+ .description('Start development server')
10
+ .action(async () => {
11
+ try {
12
+ const projectRoot = process.cwd();
13
+ const { esbuild, server } = startDevServer(projectRoot);
14
+
15
+ process.on('SIGINT', () => {
16
+ esbuild.kill();
17
+ server.close();
18
+ process.exit(0);
19
+ });
20
+ } catch (error) {
21
+ console.error('Failed to start dev server:', error);
22
+ process.exit(1);
23
+ }
24
+ });
25
+
26
+ program
27
+ .command('build')
28
+ .description('Build for production')
29
+ .action(async () => {
30
+ try {
31
+ const projectRoot = process.cwd();
32
+ process.env.NODE_ENV = 'production';
33
+ await createBuildConfig(projectRoot);
34
+ } catch (error) {
35
+ console.error('Build failed:', error);
36
+ process.exit(1);
37
+ }
38
+ });
39
+
40
+ program.parse(process.argv);
@@ -0,0 +1,34 @@
1
+ import { spawn } from 'child_process';
2
+ import sirv from 'sirv';
3
+ import http from 'http';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+
9
+ export function startDevServer(projectRoot: string) {
10
+ // Start esbuild in watch mode
11
+ const esbuild = spawn('node', [path.join(__dirname, 'esbuild.config.js'), '--watch'], {
12
+ stdio: 'inherit',
13
+ cwd: projectRoot
14
+ });
15
+
16
+ // Create static server
17
+ const serve = sirv(projectRoot, {
18
+ dev: true,
19
+ single: true
20
+ });
21
+
22
+ const server = http.createServer(serve);
23
+ server.listen(3000);
24
+ console.log('✓ Dev server running at http://localhost:3000');
25
+
26
+ // Handle cleanup
27
+ process.on('SIGTERM', () => {
28
+ esbuild.kill();
29
+ server.close();
30
+ process.exit();
31
+ });
32
+
33
+ return { esbuild, server };
34
+ }
@@ -0,0 +1,39 @@
1
+ import * as esbuild from 'esbuild';
2
+ import path from 'path';
3
+
4
+ export async function createBuildConfig(projectRoot: string, options: { watch?: boolean } = {}) {
5
+ const config: esbuild.BuildOptions = {
6
+ entryPoints: [path.join(projectRoot, 'src/main.tsx')],
7
+ bundle: true,
8
+ outfile: path.join(projectRoot, 'dist/bundle.js'),
9
+ loader: { '.tsx': 'tsx', '.ts': 'ts' },
10
+ jsxFactory: 'createElement',
11
+ jsxFragment: 'Fragment',
12
+ platform: 'browser',
13
+ target: 'es2020',
14
+ minify: process.env.NODE_ENV === 'production',
15
+ sourcemap: true,
16
+ define: {
17
+ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
18
+ },
19
+ plugins: [{
20
+ name: 'frontend-hamroun',
21
+ setup(build) {
22
+ // Handle framework imports
23
+ build.onResolve({ filter: /^frontend-hamroun$/ }, args => ({
24
+ path: path.resolve(projectRoot, 'node_modules/frontend-hamroun/dist/index.mjs')
25
+ }));
26
+ }
27
+ }]
28
+ };
29
+
30
+ if (options.watch) {
31
+ const ctx = await esbuild.context(config);
32
+ await ctx.watch();
33
+ console.log('✓ Watching for changes...');
34
+ return ctx;
35
+ } else {
36
+ await esbuild.build(config);
37
+ console.log('✓ Build complete');
38
+ }
39
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontend-hamroun",
3
- "version": "1.1.58",
3
+ "version": "1.1.59",
4
4
  "description": "A lightweight frontend framework with hooks and virtual DOM",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -61,8 +61,10 @@
61
61
  "dependencies": {
62
62
  "chalk": "^5.3.0",
63
63
  "commander": "^11.0.0",
64
+ "esbuild": "^0.19.0",
64
65
  "fs-extra": "^11.1.1",
65
66
  "inquirer": "^9.2.10",
66
- "nanospinner": "^1.1.0"
67
+ "nanospinner": "^1.1.0",
68
+ "sirv": "^2.0.4"
67
69
  }
68
70
  }
@@ -4,8 +4,8 @@
4
4
  "version": "0.0.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
- "dev": "node server.js",
8
- "build": "node esbuild.config.js",
7
+ "dev": "npx frontend-hamroun dev",
8
+ "build": "npx frontend-hamroun build",
9
9
  "lint": "eslint src --ext ts,tsx"
10
10
  },
11
11
  "dependencies": {