quapp 1.0.0 → 1.0.1

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server.js +114 -70
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quapp",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
package/server.js CHANGED
@@ -1,11 +1,20 @@
1
+ #!/usr/bin/env node
2
+
1
3
  import { spawn } from "child_process";
2
4
  import os from "os";
3
5
  import qrcode from "qrcode-terminal";
4
- import fs from "fs";
6
+ import fs from "fs/promises";
7
+ import { existsSync } from "fs";
5
8
  import path from "path";
6
9
  import open from "open";
10
+ import { fileURLToPath } from "url";
11
+ import { spawnSync } from "child_process";
12
+
13
+ // __dirname replacement in ES modules
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
7
16
 
8
- // Load config
17
+ // Default config
9
18
  let config = {
10
19
  server: {
11
20
  qr: true,
@@ -15,30 +24,35 @@ let config = {
15
24
  https: false,
16
25
  openBrowser: false,
17
26
  autoRetry: true,
18
- strictPort: false
19
- }
27
+ strictPort: false,
28
+ },
20
29
  };
21
30
 
22
- try {
23
- const configPath = path.resolve("quapp.config.json");
24
- if (fs.existsSync(configPath)) {
25
- const userConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
26
- config = {
27
- ...config,
28
- ...userConfig,
29
- server: { ...config.server, ...userConfig.server }
30
- };
31
+ // Load user config
32
+ const loadUserConfig = async () => {
33
+ try {
34
+ const configPath = path.resolve("quapp.config.json");
35
+ if (existsSync(configPath)) {
36
+ const data = await fs.readFile(configPath, "utf-8");
37
+ const userConfig = JSON.parse(data);
38
+ config = {
39
+ ...config,
40
+ ...userConfig,
41
+ server: { ...config.server, ...userConfig.server },
42
+ };
43
+ }
44
+ } catch (err) {
45
+ console.warn("⚠️ Failed to read quapp.config.json. Using default config.");
46
+ console.warn(err.message);
31
47
  }
32
- } catch (err) {
33
- console.warn("⚠️ Failed to read quapp.config.json. Using default config.");
34
- }
48
+ };
35
49
 
36
- // Get local or LAN IP
37
- function getIP(networkType = "local") {
50
+ // Get IP address
51
+ const getIP = (networkType = "local") => {
38
52
  if (networkType === "private") {
39
53
  const interfaces = os.networkInterfaces();
40
54
  for (const key in interfaces) {
41
- for (const iface of interfaces[key] || []) {
55
+ for (const iface of interfaces[key] ?? []) {
42
56
  if (!iface.internal && iface.family === "IPv4") {
43
57
  return iface.address;
44
58
  }
@@ -46,83 +60,113 @@ function getIP(networkType = "local") {
46
60
  }
47
61
  }
48
62
  return "localhost";
63
+ };
64
+
65
+ // Check if Vite is installed
66
+
67
+
68
+ function isViteInstalled() {
69
+ const result = spawnSync(
70
+ process.platform === "win32" ? "npx.cmd" : "npx",
71
+ ["vite", "--version"],
72
+ { stdio: "ignore" }
73
+ );
74
+ return result.status === 0;
49
75
  }
50
76
 
51
- // Start Vite with fallback logic
52
- function startVite(port, attempt = 0) {
77
+
78
+
79
+ // Install Vite
80
+ const installVite = () => {
81
+ return new Promise((resolve, reject) => {
82
+ console.log("📦 Installing Vite...");
83
+ const install = spawn("npm", ["install", "vite", "-D"], {
84
+ stdio: "inherit",
85
+ shell: true,
86
+ });
87
+
88
+ install.on("exit", (code) => {
89
+ if (code === 0) {
90
+ console.log("✅ Vite installed.");
91
+ resolve();
92
+ } else {
93
+ reject(new Error("Failed to install Vite"));
94
+ }
95
+ });
96
+ });
97
+ };
98
+
99
+ // Start Vite server
100
+ const startVite = (port, attempt = 0) => {
53
101
  const host = config.server.network === "private" ? getIP("private") : "localhost";
54
- const url = `${config.server.https ? "https" : "http"}://${host}:${port}`;
102
+ const protocol = config.server.https ? "https" : "http";
103
+ const url = `${protocol}://${host}:${port}`;
55
104
 
56
105
  const viteArgs = [
57
106
  "--host",
58
107
  host,
59
108
  "--port",
60
109
  port,
61
- // 🛠 Automatically enable strictPort if autoRetry is false
62
110
  ...(config.server.strictPort || !config.server.autoRetry ? ["--strictPort"] : []),
63
- ...(config.server.https ? ["--https"] : [])
111
+ ...(config.server.https ? ["--https"] : []),
64
112
  ];
65
113
 
66
- const viteProcess = spawn("vite", viteArgs, { shell: true });
67
-
68
- let shown = false;
69
-
70
- viteProcess.stdout.on("data", (data) => {
71
- const output = data.toString();
114
+ const viteBinary = path.resolve(
115
+ "node_modules",
116
+ ".bin",
117
+ process.platform === "win32" ? "vite.cmd" : "vite"
118
+ );
72
119
 
73
- const portMatch = output.match(/http[s]?:\/\/.*:(\d+)/);
74
- if (portMatch && !shown) {
75
- const usedPort = parseInt(portMatch[1]);
76
- const finalUrl = `${config.server.https ? "https" : "http"}://${host}:${usedPort}`;
120
+ if (!existsSync(viteBinary)) {
121
+ console.error("❌ vite binary not found. Try running `npm install vite`.");
122
+ process.exit(1);
123
+ }
77
124
 
78
- console.log(`\n\n🌍 Access your app from LAN at: ${finalUrl}`);
79
- if (config.server.qr) {
80
- console.log(`\n📱 Scan the QR code below to open on any device:\n`);
81
- qrcode.generate(finalUrl, { small: true });
82
- }
125
+ const vite = spawn(viteBinary, viteArgs, { shell: true });
83
126
 
84
- if (config.server.openBrowser) {
85
- open(finalUrl); // ✅ Uses external open package
86
- }
127
+ vite.stdout.on("data", (data) => process.stdout.write(data));
128
+ vite.stderr.on("data", (data) => process.stderr.write(data));
87
129
 
88
- shown = true;
130
+ setTimeout(() => {
131
+ console.log(`\n🌍 Access your app from LAN at: ${url}`);
132
+ if (config.server.qr) {
133
+ console.log(`\n📱 Scan the QR code below:\n`);
134
+ qrcode.generate(url, { small: true });
89
135
  }
136
+ if (config.server.openBrowser) {
137
+ open(url);
138
+ }
139
+ }, 1500);
90
140
 
91
- process.stdout.write(data);
92
- });
93
-
94
- viteProcess.stderr.on("data", (data) => {
95
- process.stderr.write(data);
96
- });
97
-
98
- viteProcess.on("exit", (code) => {
141
+ vite.on("exit", (code) => {
99
142
  if (
100
143
  code !== 0 &&
101
144
  config.server.fallbackPort &&
102
145
  config.server.autoRetry &&
103
146
  attempt < 10
104
147
  ) {
105
- console.log(`⚠️ Port ${port} might be in use. Retrying on port ${port + 1}...`);
148
+ console.log(`⚠️ Port ${port} in use. Trying port ${port + 1}...`);
106
149
  startVite(port + 1, attempt + 1);
107
- } else {
108
- console.log(`❌ Vite exited with code ${code}`);
150
+ } else if (code !== 0) {
151
+ console.error(`❌ Vite exited with code ${code}`);
109
152
  }
110
153
  });
111
- }
112
- // Check if Vite is installed
113
- function checkViteInstalled() {
114
- try {
115
- require.resolve("vite");
116
- return true;
117
- } catch (err) {
118
- return false;
154
+ };
155
+
156
+ // Main
157
+ const main = async () => {
158
+ await loadUserConfig();
159
+
160
+ if (!isViteInstalled()) {
161
+ try {
162
+ await installVite();
163
+ } catch (err) {
164
+ console.error("❌ Failed to install Vite automatically. Please install it manually.");
165
+ process.exit(1);
166
+ }
119
167
  }
120
- }
121
- // Check if Vite is installed
122
- if (checkViteInstalled()) {
123
- console.error("❌ Vite is not installed. Please install it globally or in your project.");
124
- process.exit(1);
125
- }
126
168
 
127
- // Kickoff
128
- startVite(config.server.port);
169
+ startVite(config.server.port);
170
+ };
171
+
172
+ main();