nextdesk 0.1.5 → 0.1.6
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/nextdesk-runtime +0 -0
- package/dist/commands/build.js +6 -6
- package/dist/commands/init.js +4 -5
- package/dist/utils/nextjs.js +103 -4
- package/package.json +5 -1
- package/src/commands/build.ts +0 -127
- package/src/commands/dev.ts +0 -94
- package/src/commands/init.ts +0 -113
- package/src/index.ts +0 -30
- package/src/utils/nextjs.ts +0 -70
- package/tsconfig.json +0 -15
|
Binary file
|
package/dist/commands/build.js
CHANGED
|
@@ -45,17 +45,17 @@ async function build(options) {
|
|
|
45
45
|
copySpinner.fail(`Failed to copy Next.js build: ${err.message}`);
|
|
46
46
|
process.exit(1);
|
|
47
47
|
}
|
|
48
|
-
// Step 3:
|
|
49
|
-
const runtimeSpinner = (0, ora_1.default)('
|
|
48
|
+
// Step 3: Get runtime binary (downloads from GitHub if not available locally)
|
|
49
|
+
const runtimeSpinner = (0, ora_1.default)('Getting runtime...').start();
|
|
50
50
|
try {
|
|
51
|
-
const runtime = (0, nextjs_1.
|
|
52
|
-
const outputBinary = path_1.default.join(outputDir, appName);
|
|
51
|
+
const runtime = await (0, nextjs_1.getRuntimeBinaryAsync)();
|
|
52
|
+
const outputBinary = path_1.default.join(outputDir, appName + (process.platform === 'win32' ? '.exe' : ''));
|
|
53
53
|
fs_1.default.copyFileSync(runtime, outputBinary);
|
|
54
54
|
fs_1.default.chmodSync(outputBinary, 0o755);
|
|
55
|
-
runtimeSpinner.succeed('Runtime
|
|
55
|
+
runtimeSpinner.succeed('Runtime ready');
|
|
56
56
|
}
|
|
57
57
|
catch (err) {
|
|
58
|
-
runtimeSpinner.fail(`Failed to
|
|
58
|
+
runtimeSpinner.fail(`Failed to get runtime: ${err.message}`);
|
|
59
59
|
process.exit(1);
|
|
60
60
|
}
|
|
61
61
|
// Step 4: Create run script
|
package/dist/commands/init.js
CHANGED
|
@@ -35,14 +35,13 @@ async function init(options) {
|
|
|
35
35
|
}
|
|
36
36
|
// Try to find and copy runtime binary - look from various common locations
|
|
37
37
|
const possiblePaths = [
|
|
38
|
+
// From npm package (when installed via npx)
|
|
39
|
+
path_1.default.join(cwd, 'node_modules/nextdesk/bin/nextdesk-runtime'),
|
|
40
|
+
// Development paths
|
|
38
41
|
path_1.default.join(cwd, '../mydesk-poc/target/release/mydesk-poc'),
|
|
39
42
|
path_1.default.join(process.env.HOME || '', 'Projects/Personal/mydesk-poc/target/release/mydesk-poc'),
|
|
40
43
|
'/home/yoni/Desktop/Projects/Personal/mydesk-poc/target/release/mydesk-poc',
|
|
41
44
|
];
|
|
42
|
-
// Also check if running from node_modules (npx)
|
|
43
|
-
if (process.env.npm_package_name?.startsWith('nextdesk')) {
|
|
44
|
-
possiblePaths.unshift(path_1.default.join(cwd, '../../mydesk-poc/target/release/mydesk-poc'));
|
|
45
|
-
}
|
|
46
45
|
let runtimeCopied = false;
|
|
47
46
|
for (const src of possiblePaths) {
|
|
48
47
|
if (fs_1.default.existsSync(src)) {
|
|
@@ -55,7 +54,7 @@ async function init(options) {
|
|
|
55
54
|
}
|
|
56
55
|
}
|
|
57
56
|
if (!runtimeCopied) {
|
|
58
|
-
spinner.warn('Runtime binary not found');
|
|
57
|
+
spinner.warn('Runtime binary not found - install nextdesk globally or from source');
|
|
59
58
|
}
|
|
60
59
|
// Create nextdesk.config.js
|
|
61
60
|
const configContent = `export default {
|
package/dist/utils/nextjs.js
CHANGED
|
@@ -6,11 +6,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.isNextJsProject = isNextJsProject;
|
|
7
7
|
exports.getPackageJson = getPackageJson;
|
|
8
8
|
exports.getAppName = getAppName;
|
|
9
|
+
exports.downloadRuntimeBinary = downloadRuntimeBinary;
|
|
9
10
|
exports.waitForServer = waitForServer;
|
|
10
11
|
exports.getRuntimeBinary = getRuntimeBinary;
|
|
12
|
+
exports.getRuntimeBinaryAsync = getRuntimeBinaryAsync;
|
|
11
13
|
const fs_1 = __importDefault(require("fs"));
|
|
12
14
|
const path_1 = __importDefault(require("path"));
|
|
13
15
|
const http_1 = __importDefault(require("http"));
|
|
16
|
+
const https_1 = __importDefault(require("https"));
|
|
17
|
+
const GITHUB_REPO = 'yoniwitz/nextdesk';
|
|
14
18
|
function isNextJsProject(cwd = process.cwd()) {
|
|
15
19
|
return (fs_1.default.existsSync(path_1.default.join(cwd, 'next.config.js')) ||
|
|
16
20
|
fs_1.default.existsSync(path_1.default.join(cwd, 'next.config.ts')) ||
|
|
@@ -26,6 +30,89 @@ function getAppName(cwd = process.cwd()) {
|
|
|
26
30
|
const pkg = getPackageJson(cwd);
|
|
27
31
|
return pkg?.name ?? path_1.default.basename(cwd);
|
|
28
32
|
}
|
|
33
|
+
function getPlatformBinaryName() {
|
|
34
|
+
const platform = process.platform;
|
|
35
|
+
const arch = process.arch;
|
|
36
|
+
if (platform === 'linux')
|
|
37
|
+
return 'x86_64-unknown-linux-gnu';
|
|
38
|
+
if (platform === 'darwin') {
|
|
39
|
+
return arch === 'arm64' ? 'aarch64-apple-darwin' : 'x86_64-apple-darwin';
|
|
40
|
+
}
|
|
41
|
+
if (platform === 'win32')
|
|
42
|
+
return 'x86_64-pc-windows-msvc';
|
|
43
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
44
|
+
}
|
|
45
|
+
function getExtension() {
|
|
46
|
+
return process.platform === 'win32' ? '.exe' : '';
|
|
47
|
+
}
|
|
48
|
+
async function downloadFile(url, destPath) {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const protocol = url.startsWith('https') ? https_1.default : http_1.default;
|
|
51
|
+
const request = protocol.get(url, (response) => {
|
|
52
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
53
|
+
const redirectUrl = response.headers.location;
|
|
54
|
+
if (redirectUrl) {
|
|
55
|
+
downloadFile(redirectUrl, destPath).then(resolve).catch(reject);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (response.statusCode !== 200) {
|
|
60
|
+
reject(new Error(`Failed to download: ${response.statusCode}`));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const file = fs_1.default.createWriteStream(destPath);
|
|
64
|
+
response.pipe(file);
|
|
65
|
+
file.on('finish', () => {
|
|
66
|
+
file.close();
|
|
67
|
+
resolve();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
request.on('error', reject);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async function getLatestRelease() {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
https_1.default.get(`https://api.github.com/repos/${GITHUB_REPO}/releases/latest`, {
|
|
76
|
+
headers: { 'User-Agent': 'nextdesk' }
|
|
77
|
+
}, (res) => {
|
|
78
|
+
let data = '';
|
|
79
|
+
res.on('data', chunk => data += chunk);
|
|
80
|
+
res.on('end', () => {
|
|
81
|
+
try {
|
|
82
|
+
resolve(JSON.parse(data));
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
reject(e);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}).on('error', reject);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async function downloadRuntimeBinary(cwd) {
|
|
92
|
+
const targetDir = path_1.default.join(cwd, '.nextdesk', 'bin');
|
|
93
|
+
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
94
|
+
const binaryName = `mydesk-poc${getExtension()}`;
|
|
95
|
+
const targetPath = path_1.default.join(targetDir, `nextdesk-runtime${getExtension()}`);
|
|
96
|
+
if (fs_1.default.existsSync(targetPath)) {
|
|
97
|
+
return targetPath;
|
|
98
|
+
}
|
|
99
|
+
console.log('Downloading NextDesk runtime...');
|
|
100
|
+
try {
|
|
101
|
+
const release = await getLatestRelease();
|
|
102
|
+
const platformTarget = getPlatformBinaryName();
|
|
103
|
+
const asset = release.assets.find(a => a.name.includes(platformTarget));
|
|
104
|
+
if (!asset) {
|
|
105
|
+
throw new Error(`No binary available for ${platformTarget}. Please build from source.`);
|
|
106
|
+
}
|
|
107
|
+
await downloadFile(asset.browser_download_url, targetPath);
|
|
108
|
+
fs_1.default.chmodSync(targetPath, 0o755);
|
|
109
|
+
console.log('Runtime downloaded successfully!');
|
|
110
|
+
return targetPath;
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
throw new Error(`Failed to download runtime: ${err.message}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
29
116
|
function waitForServer(url, timeout = 60000) {
|
|
30
117
|
return new Promise((resolve, reject) => {
|
|
31
118
|
const start = Date.now();
|
|
@@ -51,21 +138,33 @@ function waitForServer(url, timeout = 60000) {
|
|
|
51
138
|
}
|
|
52
139
|
function getRuntimeBinary() {
|
|
53
140
|
const cwd = process.cwd();
|
|
54
|
-
// Priority 1: Check in project's .nextdesk/bin/ (after init)
|
|
55
141
|
const localBinary = path_1.default.join(cwd, '.nextdesk/bin/nextdesk-runtime');
|
|
56
142
|
if (fs_1.default.existsSync(localBinary))
|
|
57
143
|
return localBinary;
|
|
58
|
-
// Priority 2: Check in node_modules/.nextdesk/bin/
|
|
59
144
|
const nodeModulesBinary = path_1.default.join(cwd, 'node_modules/.nextdesk/bin/nextdesk-runtime');
|
|
60
145
|
if (fs_1.default.existsSync(nodeModulesBinary))
|
|
61
146
|
return nodeModulesBinary;
|
|
62
|
-
// Priority 3: Check parent's mydesk-poc (development)
|
|
63
147
|
const devBinary = path_1.default.join(cwd, '../mydesk-poc/target/release/mydesk-poc');
|
|
64
148
|
if (fs_1.default.existsSync(devBinary))
|
|
65
149
|
return devBinary;
|
|
66
|
-
// Priority 4: Check hardcoded path from home
|
|
67
150
|
const homeBinary = path_1.default.join(process.env.HOME || '', 'Projects/Personal/mydesk-poc/target/release/mydesk-poc');
|
|
68
151
|
if (fs_1.default.existsSync(homeBinary))
|
|
69
152
|
return homeBinary;
|
|
70
153
|
throw new Error('NextDesk runtime not found. Run `npx nextdesk init` to set it up.');
|
|
71
154
|
}
|
|
155
|
+
async function getRuntimeBinaryAsync() {
|
|
156
|
+
const cwd = process.cwd();
|
|
157
|
+
const localBinary = path_1.default.join(cwd, '.nextdesk/bin/nextdesk-runtime');
|
|
158
|
+
if (fs_1.default.existsSync(localBinary))
|
|
159
|
+
return localBinary;
|
|
160
|
+
const nodeModulesBinary = path_1.default.join(cwd, 'node_modules/.nextdesk/bin/nextdesk-runtime');
|
|
161
|
+
if (fs_1.default.existsSync(nodeModulesBinary))
|
|
162
|
+
return nodeModulesBinary;
|
|
163
|
+
const devBinary = path_1.default.join(cwd, '../mydesk-poc/target/release/mydesk-poc');
|
|
164
|
+
if (fs_1.default.existsSync(devBinary))
|
|
165
|
+
return devBinary;
|
|
166
|
+
const homeBinary = path_1.default.join(process.env.HOME || '', 'Projects/Personal/mydesk-poc/target/release/mydesk-poc');
|
|
167
|
+
if (fs_1.default.existsSync(homeBinary))
|
|
168
|
+
return homeBinary;
|
|
169
|
+
return downloadRuntimeBinary(cwd);
|
|
170
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nextdesk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Turn your Next.js app into a 5MB desktop app in 30 seconds",
|
|
5
5
|
"bin": {
|
|
6
6
|
"nextdesk": "./dist/index.js"
|
|
7
7
|
},
|
|
8
|
+
"files": [
|
|
9
|
+
"dist/",
|
|
10
|
+
"bin/"
|
|
11
|
+
],
|
|
8
12
|
"scripts": {
|
|
9
13
|
"build": "tsc && chmod +x dist/index.js",
|
|
10
14
|
"dev": "tsc --watch",
|
package/src/commands/build.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk'
|
|
2
|
-
import ora from 'ora'
|
|
3
|
-
import execa from 'execa'
|
|
4
|
-
import fs from 'fs'
|
|
5
|
-
import path from 'path'
|
|
6
|
-
import { isNextJsProject, getAppName, getRuntimeBinary } from '../utils/nextjs'
|
|
7
|
-
|
|
8
|
-
interface BuildOptions {
|
|
9
|
-
target: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export async function build(options: BuildOptions) {
|
|
13
|
-
const cwd = process.cwd()
|
|
14
|
-
|
|
15
|
-
if (!isNextJsProject(cwd)) {
|
|
16
|
-
console.error(chalk.red('✗ Not a Next.js project. Make sure next.config.js exists.'))
|
|
17
|
-
process.exit(1)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const appName = getAppName(cwd)
|
|
21
|
-
const outputDir = path.join(cwd, 'dist-desktop')
|
|
22
|
-
|
|
23
|
-
console.log(chalk.bold(`\n📦 Building ${appName} for desktop...\n`))
|
|
24
|
-
|
|
25
|
-
// Clean output directory
|
|
26
|
-
if (fs.existsSync(outputDir)) {
|
|
27
|
-
fs.rmSync(outputDir, { recursive: true })
|
|
28
|
-
}
|
|
29
|
-
fs.mkdirSync(outputDir, { recursive: true })
|
|
30
|
-
|
|
31
|
-
// Step 1: Build Next.js app
|
|
32
|
-
const nextSpinner = ora('Building Next.js app...').start()
|
|
33
|
-
try {
|
|
34
|
-
await execa('npm', ['run', 'build'], { cwd, stdio: 'pipe' })
|
|
35
|
-
nextSpinner.succeed('Next.js app built')
|
|
36
|
-
} catch (err: any) {
|
|
37
|
-
nextSpinner.fail(`Next.js build failed: ${err.message}`)
|
|
38
|
-
process.exit(1)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Step 2: Copy Next.js output
|
|
42
|
-
const copySpinner = ora('Copying Next.js build...').start()
|
|
43
|
-
try {
|
|
44
|
-
const nextOut = path.join(cwd, '.next')
|
|
45
|
-
fs.cpSync(nextOut, path.join(outputDir, '.next'), { recursive: true })
|
|
46
|
-
copySpinner.succeed('Next.js build copied')
|
|
47
|
-
} catch (err: any) {
|
|
48
|
-
copySpinner.fail(`Failed to copy Next.js build: ${err.message}`)
|
|
49
|
-
process.exit(1)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Step 3: Copy runtime binary
|
|
53
|
-
const runtimeSpinner = ora('Copying runtime...').start()
|
|
54
|
-
try {
|
|
55
|
-
const runtime = getRuntimeBinary()
|
|
56
|
-
const outputBinary = path.join(outputDir, appName)
|
|
57
|
-
fs.copyFileSync(runtime, outputBinary)
|
|
58
|
-
fs.chmodSync(outputBinary, 0o755)
|
|
59
|
-
runtimeSpinner.succeed('Runtime copied')
|
|
60
|
-
} catch (err: any) {
|
|
61
|
-
runtimeSpinner.fail(`Failed to copy runtime: ${err.message}`)
|
|
62
|
-
process.exit(1)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Step 4: Create run script
|
|
66
|
-
const scriptSpinner = ora('Creating run script...').start()
|
|
67
|
-
try {
|
|
68
|
-
const runScript = `#!/bin/bash
|
|
69
|
-
APP_NAME="${appName}"
|
|
70
|
-
PORT=3000
|
|
71
|
-
|
|
72
|
-
# Start Next.js production server in background
|
|
73
|
-
echo "Starting $APP_NAME server..."
|
|
74
|
-
npm run start &
|
|
75
|
-
|
|
76
|
-
# Wait for server to be ready
|
|
77
|
-
sleep 3
|
|
78
|
-
|
|
79
|
-
# Launch desktop app
|
|
80
|
-
cd "$(dirname "$0")"
|
|
81
|
-
"./$APP_NAME" "http://localhost:$PORT" "$APP_NAME"
|
|
82
|
-
`
|
|
83
|
-
fs.writeFileSync(path.join(outputDir, 'run.sh'), runScript)
|
|
84
|
-
fs.chmodSync(path.join(outputDir, 'run.sh'), 0o755)
|
|
85
|
-
|
|
86
|
-
// Also create Windows batch file
|
|
87
|
-
const runBat = `@echo off
|
|
88
|
-
set APP_NAME=${appName}
|
|
89
|
-
echo Starting %APP_NAME% server...
|
|
90
|
-
start /b npm run start
|
|
91
|
-
timeout /t 3 /nobreak >nul
|
|
92
|
-
"%~dp0%APP_NAME%.exe" "http://localhost:3000" "%APP_NAME%"
|
|
93
|
-
`
|
|
94
|
-
fs.writeFileSync(path.join(outputDir, 'run.bat'), runBat)
|
|
95
|
-
|
|
96
|
-
scriptSpinner.succeed('Run scripts created')
|
|
97
|
-
} catch (err: any) {
|
|
98
|
-
scriptSpinner.fail(`Failed to create scripts: ${err.message}`)
|
|
99
|
-
process.exit(1)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Step 5: Copy package.json for npm install in output
|
|
103
|
-
const depsSpinner = ora('Setting up dependencies...').start()
|
|
104
|
-
try {
|
|
105
|
-
const packageJson = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8'))
|
|
106
|
-
fs.writeFileSync(path.join(outputDir, 'package.json'), JSON.stringify({
|
|
107
|
-
name: appName,
|
|
108
|
-
version: packageJson.version || '1.0.0',
|
|
109
|
-
private: true,
|
|
110
|
-
scripts: {
|
|
111
|
-
start: 'next start'
|
|
112
|
-
}
|
|
113
|
-
}, null, 2))
|
|
114
|
-
depsSpinner.succeed('Dependencies configured')
|
|
115
|
-
} catch (err: any) {
|
|
116
|
-
depsSpinner.fail(`Failed to setup dependencies: ${err.message}`)
|
|
117
|
-
process.exit(1)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
console.log(chalk.bold('\n✨ Build complete!\n'))
|
|
121
|
-
console.log(chalk.gray(` Output: ${chalk.cyan(outputDir)}`))
|
|
122
|
-
console.log(chalk.gray(` To run:`))
|
|
123
|
-
console.log(chalk.cyan(` cd ${outputDir}`))
|
|
124
|
-
console.log(chalk.cyan(` npm install`))
|
|
125
|
-
console.log(chalk.cyan(` ./run.sh # Linux/Mac`))
|
|
126
|
-
console.log(chalk.cyan(` run.bat # Windows\n`))
|
|
127
|
-
}
|
package/src/commands/dev.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk'
|
|
2
|
-
import ora from 'ora'
|
|
3
|
-
import execa from 'execa'
|
|
4
|
-
import { isNextJsProject, waitForServer, getRuntimeBinary, getAppName } from '../utils/nextjs'
|
|
5
|
-
|
|
6
|
-
interface DevOptions {
|
|
7
|
-
port: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export async function dev(options: DevOptions) {
|
|
11
|
-
const port = options.port
|
|
12
|
-
const url = `http://localhost:${port}`
|
|
13
|
-
const cwd = process.cwd()
|
|
14
|
-
|
|
15
|
-
// Check if Next.js project
|
|
16
|
-
if (!isNextJsProject(cwd)) {
|
|
17
|
-
console.error(chalk.red('✗ Not a Next.js project. Make sure next.config.js exists.'))
|
|
18
|
-
process.exit(1)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const appName = getAppName(cwd)
|
|
22
|
-
console.log(chalk.bold(`\n🚀 NextDesk — ${appName}\n`))
|
|
23
|
-
|
|
24
|
-
// Start Next.js dev server
|
|
25
|
-
const nextSpinner = ora('Starting Next.js dev server...').start()
|
|
26
|
-
|
|
27
|
-
const nextProcess = execa('npm', ['run', 'dev'], {
|
|
28
|
-
cwd,
|
|
29
|
-
env: { ...process.env, PORT: port },
|
|
30
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
nextProcess.stdout?.on('data', (data: Buffer) => {
|
|
34
|
-
const line = data.toString()
|
|
35
|
-
if (line.includes('Ready') || line.includes('ready')) {
|
|
36
|
-
nextSpinner.succeed(`Next.js ready on ${chalk.cyan(url)}`)
|
|
37
|
-
}
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
nextProcess.stderr?.on('data', (data: Buffer) => {
|
|
41
|
-
const line = data.toString()
|
|
42
|
-
if (line.toLowerCase().includes('error')) {
|
|
43
|
-
console.error(chalk.red(line.trim()))
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
// Wait for server to be ready
|
|
48
|
-
try {
|
|
49
|
-
await waitForServer(url)
|
|
50
|
-
} catch (err) {
|
|
51
|
-
nextSpinner.fail('Next.js dev server failed to start')
|
|
52
|
-
nextProcess.kill()
|
|
53
|
-
process.exit(1)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Launch desktop window
|
|
57
|
-
const windowSpinner = ora('Opening desktop window...').start()
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
const runtime = getRuntimeBinary()
|
|
61
|
-
|
|
62
|
-
const runtimeProcess = execa(runtime, [], {
|
|
63
|
-
env: {
|
|
64
|
-
...process.env,
|
|
65
|
-
MYDESK_URL: url,
|
|
66
|
-
MYDESK_TITLE: appName,
|
|
67
|
-
GDK_BACKEND: 'x11',
|
|
68
|
-
WAYLAND_DISPLAY: '',
|
|
69
|
-
},
|
|
70
|
-
stdio: 'inherit',
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
windowSpinner.succeed(`${chalk.green('✨ Ready!')} Desktop window opened`)
|
|
74
|
-
console.log(chalk.gray(`\n App: ${chalk.cyan(url)}`))
|
|
75
|
-
console.log(chalk.gray(` Runtime: ${runtime}\n`))
|
|
76
|
-
|
|
77
|
-
// Clean shutdown
|
|
78
|
-
const cleanup = () => {
|
|
79
|
-
nextProcess.kill()
|
|
80
|
-
runtimeProcess.kill()
|
|
81
|
-
process.exit(0)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
process.on('SIGINT', cleanup)
|
|
85
|
-
process.on('SIGTERM', cleanup)
|
|
86
|
-
|
|
87
|
-
await runtimeProcess
|
|
88
|
-
|
|
89
|
-
} catch (err: any) {
|
|
90
|
-
windowSpinner.fail(`Failed to open desktop window: ${err.message}`)
|
|
91
|
-
nextProcess.kill()
|
|
92
|
-
process.exit(1)
|
|
93
|
-
}
|
|
94
|
-
}
|
package/src/commands/init.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk'
|
|
2
|
-
import ora from 'ora'
|
|
3
|
-
import execa from 'execa'
|
|
4
|
-
import fs from 'fs'
|
|
5
|
-
import path from 'path'
|
|
6
|
-
|
|
7
|
-
interface InitOptions {
|
|
8
|
-
name?: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export async function init(options: InitOptions) {
|
|
12
|
-
const cwd = process.cwd()
|
|
13
|
-
const appName = options.name || path.basename(cwd)
|
|
14
|
-
|
|
15
|
-
console.log(chalk.bold(`\n🚀 Initializing NextDesk project: ${appName}\n`))
|
|
16
|
-
|
|
17
|
-
const spinner = ora('Setting up project...').start()
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
// Check if Next.js project exists
|
|
21
|
-
const hasNextConfig = fs.existsSync(path.join(cwd, 'next.config.js')) ||
|
|
22
|
-
fs.existsSync(path.join(cwd, 'next.config.mjs')) ||
|
|
23
|
-
fs.existsSync(path.join(cwd, 'next.config.ts'))
|
|
24
|
-
|
|
25
|
-
if (!hasNextConfig) {
|
|
26
|
-
spinner.info('No Next.js project found. Creating one...')
|
|
27
|
-
|
|
28
|
-
// Create a basic Next.js app
|
|
29
|
-
await execa('npx', ['create-next-app@latest', '.', '--typescript', '--tailwind', '--eslint', '--app', '--src-dir', '--no-import-alias'], {
|
|
30
|
-
cwd,
|
|
31
|
-
stdio: 'inherit'
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Create .nextdesk directory with runtime
|
|
36
|
-
const nextdeskDir = path.join(cwd, '.nextdesk')
|
|
37
|
-
const binDir = path.join(nextdeskDir, 'bin')
|
|
38
|
-
if (!fs.existsSync(binDir)) {
|
|
39
|
-
fs.mkdirSync(binDir, { recursive: true })
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Try to find and copy runtime binary - look from various common locations
|
|
43
|
-
const possiblePaths = [
|
|
44
|
-
path.join(cwd, '../mydesk-poc/target/release/mydesk-poc'),
|
|
45
|
-
path.join(process.env.HOME || '', 'Projects/Personal/mydesk-poc/target/release/mydesk-poc'),
|
|
46
|
-
'/home/yoni/Desktop/Projects/Personal/mydesk-poc/target/release/mydesk-poc',
|
|
47
|
-
]
|
|
48
|
-
|
|
49
|
-
// Also check if running from node_modules (npx)
|
|
50
|
-
if (process.env.npm_package_name?.startsWith('nextdesk')) {
|
|
51
|
-
possiblePaths.unshift(path.join(cwd, '../../mydesk-poc/target/release/mydesk-poc'))
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
let runtimeCopied = false
|
|
55
|
-
for (const src of possiblePaths) {
|
|
56
|
-
if (fs.existsSync(src)) {
|
|
57
|
-
const dst = path.join(binDir, 'nextdesk-runtime')
|
|
58
|
-
fs.copyFileSync(src, dst)
|
|
59
|
-
fs.chmodSync(dst, 0o755)
|
|
60
|
-
spinner.succeed('Runtime binary copied')
|
|
61
|
-
runtimeCopied = true
|
|
62
|
-
break
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (!runtimeCopied) {
|
|
67
|
-
spinner.warn('Runtime binary not found')
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Create nextdesk.config.js
|
|
71
|
-
const configContent = `export default {
|
|
72
|
-
name: "${appName}",
|
|
73
|
-
version: "1.0.0",
|
|
74
|
-
window: {
|
|
75
|
-
width: 900,
|
|
76
|
-
height: 640,
|
|
77
|
-
title: "${appName}",
|
|
78
|
-
},
|
|
79
|
-
}
|
|
80
|
-
`
|
|
81
|
-
fs.writeFileSync(path.join(cwd, 'nextdesk.config.js'), configContent)
|
|
82
|
-
|
|
83
|
-
// Update package.json scripts
|
|
84
|
-
const packageJsonPath = path.join(cwd, 'package.json')
|
|
85
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
86
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
|
|
87
|
-
|
|
88
|
-
if (!packageJson.scripts) {
|
|
89
|
-
packageJson.scripts = {}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (!packageJson.scripts['nextdesk:dev']) {
|
|
93
|
-
packageJson.scripts['nextdesk:dev'] = 'npx nextdesk dev'
|
|
94
|
-
}
|
|
95
|
-
if (!packageJson.scripts['nextdesk:build']) {
|
|
96
|
-
packageJson.scripts['nextdesk:build'] = 'npx nextdesk build'
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n')
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
spinner.succeed('NextDesk initialized!')
|
|
103
|
-
|
|
104
|
-
console.log(chalk.bold('\n✨ All set!\n'))
|
|
105
|
-
console.log(chalk.gray(' Run these commands:'))
|
|
106
|
-
console.log(chalk.cyan(' npm run nextdesk:dev # Start desktop app in dev mode'))
|
|
107
|
-
console.log(chalk.cyan(' npm run nextdesk:build # Build desktop app for production\n'))
|
|
108
|
-
|
|
109
|
-
} catch (err: any) {
|
|
110
|
-
spinner.fail(`Initialization failed: ${err.message}`)
|
|
111
|
-
process.exit(1)
|
|
112
|
-
}
|
|
113
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { program } from 'commander'
|
|
3
|
-
import { dev } from './commands/dev'
|
|
4
|
-
import { build } from './commands/build'
|
|
5
|
-
import { init } from './commands/init'
|
|
6
|
-
|
|
7
|
-
program
|
|
8
|
-
.name('mydesk')
|
|
9
|
-
.description('Turn your Next.js app into a desktop app in 30 seconds')
|
|
10
|
-
.version('0.1.0')
|
|
11
|
-
|
|
12
|
-
program
|
|
13
|
-
.command('init')
|
|
14
|
-
.description('Initialize MyDesk in your Next.js project')
|
|
15
|
-
.option('-n, --name <name>', 'App name')
|
|
16
|
-
.action(init)
|
|
17
|
-
|
|
18
|
-
program
|
|
19
|
-
.command('dev')
|
|
20
|
-
.description('Start your Next.js app as a desktop app')
|
|
21
|
-
.option('-p, --port <port>', 'Next.js dev server port', '3000')
|
|
22
|
-
.action(dev)
|
|
23
|
-
|
|
24
|
-
program
|
|
25
|
-
.command('build')
|
|
26
|
-
.description('Build your app for production')
|
|
27
|
-
.option('-t, --target <target>', 'Target platform (windows, macos, linux)', process.platform)
|
|
28
|
-
.action(build)
|
|
29
|
-
|
|
30
|
-
program.parse()
|
package/src/utils/nextjs.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
import http from 'http'
|
|
4
|
-
|
|
5
|
-
export function isNextJsProject(cwd: string = process.cwd()): boolean {
|
|
6
|
-
return (
|
|
7
|
-
fs.existsSync(path.join(cwd, 'next.config.js')) ||
|
|
8
|
-
fs.existsSync(path.join(cwd, 'next.config.ts')) ||
|
|
9
|
-
fs.existsSync(path.join(cwd, 'next.config.mjs'))
|
|
10
|
-
)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function getPackageJson(cwd: string = process.cwd()): Record<string, any> | null {
|
|
14
|
-
const pkgPath = path.join(cwd, 'package.json')
|
|
15
|
-
if (!fs.existsSync(pkgPath)) return null
|
|
16
|
-
return JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function getAppName(cwd: string = process.cwd()): string {
|
|
20
|
-
const pkg = getPackageJson(cwd)
|
|
21
|
-
return pkg?.name ?? path.basename(cwd)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function waitForServer(url: string, timeout = 60000): Promise<void> {
|
|
25
|
-
return new Promise((resolve, reject) => {
|
|
26
|
-
const start = Date.now()
|
|
27
|
-
|
|
28
|
-
const check = () => {
|
|
29
|
-
http.get(url, (res) => {
|
|
30
|
-
if (res.statusCode && res.statusCode < 500) {
|
|
31
|
-
resolve()
|
|
32
|
-
} else {
|
|
33
|
-
retry()
|
|
34
|
-
}
|
|
35
|
-
}).on('error', retry)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const retry = () => {
|
|
39
|
-
if (Date.now() - start > timeout) {
|
|
40
|
-
reject(new Error(`Server at ${url} did not start within ${timeout}ms`))
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
setTimeout(check, 500)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
check()
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function getRuntimeBinary(): string {
|
|
51
|
-
const cwd = process.cwd()
|
|
52
|
-
|
|
53
|
-
// Priority 1: Check in project's .nextdesk/bin/ (after init)
|
|
54
|
-
const localBinary = path.join(cwd, '.nextdesk/bin/nextdesk-runtime')
|
|
55
|
-
if (fs.existsSync(localBinary)) return localBinary
|
|
56
|
-
|
|
57
|
-
// Priority 2: Check in node_modules/.nextdesk/bin/
|
|
58
|
-
const nodeModulesBinary = path.join(cwd, 'node_modules/.nextdesk/bin/nextdesk-runtime')
|
|
59
|
-
if (fs.existsSync(nodeModulesBinary)) return nodeModulesBinary
|
|
60
|
-
|
|
61
|
-
// Priority 3: Check parent's mydesk-poc (development)
|
|
62
|
-
const devBinary = path.join(cwd, '../mydesk-poc/target/release/mydesk-poc')
|
|
63
|
-
if (fs.existsSync(devBinary)) return devBinary
|
|
64
|
-
|
|
65
|
-
// Priority 4: Check hardcoded path from home
|
|
66
|
-
const homeBinary = path.join(process.env.HOME || '', 'Projects/Personal/mydesk-poc/target/release/mydesk-poc')
|
|
67
|
-
if (fs.existsSync(homeBinary)) return homeBinary
|
|
68
|
-
|
|
69
|
-
throw new Error('NextDesk runtime not found. Run `npx nextdesk init` to set it up.')
|
|
70
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"lib": ["ES2020"],
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"rootDir": "./src",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"resolveJsonModule": true
|
|
12
|
-
},
|
|
13
|
-
"include": ["src/**/*"],
|
|
14
|
-
"exclude": ["node_modules", "dist"]
|
|
15
|
-
}
|