@nativescript/cloudflare-cli 0.1.0-beta.7 → 0.1.0-beta.9
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 +25 -24
- package/dist/bin.js +92 -36
- package/package.json +43 -43
package/README.md
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
# @nativescript/cloudflare-cli
|
|
2
|
-
|
|
3
|
-
```sh
|
|
4
|
-
npx @nativescript/cloudflare-cli live # a phone app: opens the IDE and prints a QR code for NativeScript Preview
|
|
5
|
-
npx @nativescript/cloudflare-cli live --
|
|
6
|
-
npx @nativescript/cloudflare-cli live --
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
npx ns-edge
|
|
19
|
-
npx ns-edge
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
# @nativescript/cloudflare-cli
|
|
2
|
+
|
|
3
|
+
```sh
|
|
4
|
+
npx @nativescript/cloudflare-cli live # a phone app: opens the IDE and prints a QR code for NativeScript Preview
|
|
5
|
+
npx @nativescript/cloudflare-cli live --react # the same with a framework: --ng (Angular), --react, --vue, --svelte or --solid
|
|
6
|
+
npx @nativescript/cloudflare-cli live --mac # a Mac app: opens the IDE and runs the app on this Mac, streamed into the IDE
|
|
7
|
+
npx @nativescript/cloudflare-cli live --electron # an Electron app with native views, run the same way
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
`live` creates a project on preview.nativescript.org (`--server` or `NS_EDGE_SERVER` for another edge), saves it to `.ns-edge.json` and opens its IDE. The framework flags are `ns create`'s (`--ng`/`--angular`, `--react`, `--vue`, `--svelte`, `--solid`, `--ts`); without one a phone project is plain TypeScript with XML views. Run it again in the same folder to reopen that project (no flag needed), or add `--new` for another; a flag for a different kind of project than the folder's asks for `--new`. While `live` runs a Mac app it restarts the app if it crashes, unless it crashes three times in a minute. Without a create token the project is free: a few hosted agent turns a day, no backend, deleted after 30 days unused. `--no-open` skips the browser; with `--mac` or `--electron`, `--allow-eval` lets the agent run code on this Mac.
|
|
11
|
+
|
|
12
|
+
## Mirror a local project
|
|
13
|
+
|
|
14
|
+
Mirror a NativeScript project into an ns-edge project from a terminal or a Vite dev server.
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm i -D @nativescript/cloudflare-cli@beta
|
|
18
|
+
npx ns-edge create --server https://preview.nativescript.org --token <create token> # --template vue for nativescript-vue
|
|
19
|
+
npx ns-edge watch # push, then every save
|
|
20
|
+
npx ns-edge qr # print the pairing QR code again
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Also: `ns-edge push` uploads `app/` and `server/` once, `ns-edge pull` downloads the project's files.
|
|
24
|
+
|
|
25
|
+
`ns-edge create` writes `.ns-edge.json` (project id and keys; keep it out of git). In a Vite config, `@nativescript/cloudflare-cli/vite` mirrors `app/` and `server/` on every save.
|
package/dist/bin.js
CHANGED
|
@@ -2,21 +2,42 @@
|
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { existsSync, watch } from 'node:fs';
|
|
4
4
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
5
|
-
import { join, sep } from 'node:path';
|
|
5
|
+
import { dirname, join, sep } from 'node:path';
|
|
6
6
|
import { createInterface } from 'node:readline/promises';
|
|
7
7
|
import QRCode from 'qrcode';
|
|
8
8
|
import { applyChanges, collect, createProject, projectUrl, report, SYNCED } from './sync.js';
|
|
9
9
|
const CONFIG = '.ns-edge.json';
|
|
10
10
|
const DEBOUNCE_MS = 120;
|
|
11
11
|
const LIVE_SERVER = process.env.NS_EDGE_SERVER ?? 'https://preview.nativescript.org';
|
|
12
|
-
/** The hosts `live --mac`, `--electron` and `--expo` run; bump them with each host release this CLI is tested against. */
|
|
12
|
+
/** The hosts `live --desktop` (or `--mac`, `--windows`), `--electron` and `--expo` run; bump them with each host release this CLI is tested against. */
|
|
13
13
|
const DESKTOP_HOSTS = {
|
|
14
|
-
macos: { spec: '@nativescript/cloudflare-desktop@0.1.0-alpha.
|
|
15
|
-
electron: { spec: '@nativescript/cloudflare-electron@0.1.0-alpha.
|
|
16
|
-
expo: { spec: '@nativescript/cloudflare-expo-desktop@0.1.0-alpha.
|
|
14
|
+
macos: { spec: '@nativescript/cloudflare-desktop@0.1.0-alpha.6', bin: 'ns-edge-desktop' },
|
|
15
|
+
electron: { spec: '@nativescript/cloudflare-electron@0.1.0-alpha.3', bin: 'ns-edge-electron' },
|
|
16
|
+
expo: { spec: '@nativescript/cloudflare-expo-desktop@0.1.0-alpha.1', bin: 'ns-edge-expo-desktop' },
|
|
17
17
|
};
|
|
18
18
|
const DESKTOP_TEMPLATES = new Set(['macos', 'electron', 'expo']);
|
|
19
|
-
|
|
19
|
+
/** The macos template is the desktop one: it runs on a Mac with AppKit and on Windows with WinUI, so all three flags pick it. */
|
|
20
|
+
const DESKTOP_FLAGS = { macos: ['desktop', 'mac', 'windows'], electron: ['electron'], expo: ['expo'] };
|
|
21
|
+
/** The NativeScript CLI's `ns create` framework flags, so `live` and `create` take the same ones. */
|
|
22
|
+
const FRAMEWORK_FLAGS = {
|
|
23
|
+
angular: ['ng', 'angular'],
|
|
24
|
+
react: ['react', 'reactjs'],
|
|
25
|
+
vue: ['vue', 'vuejs'],
|
|
26
|
+
svelte: ['svelte'],
|
|
27
|
+
solid: ['solid', 'solidjs'],
|
|
28
|
+
core: ['ts', 'tsc', 'typescript'],
|
|
29
|
+
};
|
|
30
|
+
const TEMPLATE_KINDS = {
|
|
31
|
+
core: 'a phone',
|
|
32
|
+
angular: 'an Angular phone',
|
|
33
|
+
react: 'a React phone',
|
|
34
|
+
vue: 'a Vue phone',
|
|
35
|
+
svelte: 'a Svelte phone',
|
|
36
|
+
solid: 'a Solid phone',
|
|
37
|
+
macos: 'a desktop',
|
|
38
|
+
electron: 'an Electron',
|
|
39
|
+
expo: 'an Expo Desktop',
|
|
40
|
+
};
|
|
20
41
|
const MAX_CRASHES = 3;
|
|
21
42
|
const CRASH_WINDOW_MS = 60_000;
|
|
22
43
|
const RESTART_DELAY_MS = 1000;
|
|
@@ -32,6 +53,22 @@ const flag = (name) => {
|
|
|
32
53
|
return i >= 0 ? args[i + 1] : undefined;
|
|
33
54
|
};
|
|
34
55
|
const has = (name) => args.includes(`--${name}`);
|
|
56
|
+
/** The template --template, a framework flag or a desktop flag asks for, if any; asking for two is an error. */
|
|
57
|
+
function requestedTemplate() {
|
|
58
|
+
const asked = new Set();
|
|
59
|
+
const named = flag('template');
|
|
60
|
+
if (named)
|
|
61
|
+
asked.add(named);
|
|
62
|
+
for (const [template, names] of Object.entries(FRAMEWORK_FLAGS))
|
|
63
|
+
if (names.some((name) => has(name)))
|
|
64
|
+
asked.add(template);
|
|
65
|
+
for (const [template, names] of Object.entries(DESKTOP_FLAGS))
|
|
66
|
+
if (names.some((name) => has(name)))
|
|
67
|
+
asked.add(template);
|
|
68
|
+
if (asked.size > 1)
|
|
69
|
+
throw new Error(`Pick one kind of project: this asked for ${[...asked].join(' and ')}.`);
|
|
70
|
+
return [...asked][0];
|
|
71
|
+
}
|
|
35
72
|
const root = process.cwd();
|
|
36
73
|
async function loadRef() {
|
|
37
74
|
let ref;
|
|
@@ -80,14 +117,31 @@ function openInBrowser(url) {
|
|
|
80
117
|
const [command, ...rest] = process.platform === 'darwin' ? ['open', url] : process.platform === 'win32' ? ['cmd', '/c', 'start', '""', url.replace(/&/g, '^&')] : ['xdg-open', url];
|
|
81
118
|
spawn(command, rest, { stdio: 'ignore', detached: true }).on('error', () => { }).unref();
|
|
82
119
|
}
|
|
120
|
+
const COMPUTER = process.platform === 'win32' ? 'PC' : 'Mac';
|
|
121
|
+
/**
|
|
122
|
+
* How to run npx. On Windows it's npx.cmd, which Node won't spawn without a
|
|
123
|
+
* shell (and a shell would re-parse the arguments), so npm's own npx-cli.js
|
|
124
|
+
* runs with this Node instead when it's where Node's installer puts it.
|
|
125
|
+
*/
|
|
126
|
+
function npx() {
|
|
127
|
+
if (process.platform !== 'win32')
|
|
128
|
+
return ['npx'];
|
|
129
|
+
const cli = join(dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npx-cli.js');
|
|
130
|
+
return existsSync(cli) ? [process.execPath, cli] : ['npx.cmd'];
|
|
131
|
+
}
|
|
83
132
|
/**
|
|
84
|
-
* Runs the
|
|
85
|
-
* crashes is started again, since nothing in the browser can reach this
|
|
86
|
-
* do it, unless it keeps crashing. The host says what joining allows.
|
|
133
|
+
* Runs the desktop host for a desktop project until it quits or Ctrl+C. A host
|
|
134
|
+
* that crashes is started again, since nothing in the browser can reach this
|
|
135
|
+
* computer to do it, unless it keeps crashing. The host says what joining allows.
|
|
87
136
|
*/
|
|
88
137
|
async function runDesktopHost(ref) {
|
|
89
|
-
|
|
90
|
-
|
|
138
|
+
const mac = process.platform === 'darwin' && process.arch === 'arm64';
|
|
139
|
+
const windows = process.platform === 'win32' && (process.arch === 'x64' || process.arch === 'arm64');
|
|
140
|
+
if (ref.template === 'expo' && !mac && !(windows && process.arch === 'x64')) {
|
|
141
|
+
throw new Error('Expo Desktop projects run on an Apple Silicon Mac (macOS 14.4 or later) or a Windows x64 PC. Run the same command on one.');
|
|
142
|
+
}
|
|
143
|
+
if (!mac && !windows) {
|
|
144
|
+
throw new Error('Desktop projects run on an Apple Silicon Mac (macOS 14.4 or later) or a Windows 10 (1903 or later) or Windows 11 PC. Run the same command there.');
|
|
91
145
|
}
|
|
92
146
|
const device = `${projectUrl(ref)}#${ref.deviceKey}`;
|
|
93
147
|
const extra = has('allow-eval') ? ['--allow-eval'] : [];
|
|
@@ -104,7 +158,8 @@ async function runDesktopHost(ref) {
|
|
|
104
158
|
}
|
|
105
159
|
const crashes = [];
|
|
106
160
|
for (;;) {
|
|
107
|
-
|
|
161
|
+
const [command, ...prefix] = npx();
|
|
162
|
+
child = spawn(command, [...prefix, '-y', '--package', spec, '--', host.bin, device, '--yes', ...extra], { stdio: 'inherit' });
|
|
108
163
|
const exit = await new Promise((resolve, reject) => {
|
|
109
164
|
child.on('error', reject);
|
|
110
165
|
child.on('exit', (code, signal) => resolve({ code, signal }));
|
|
@@ -117,9 +172,9 @@ async function runDesktopHost(ref) {
|
|
|
117
172
|
crashes.shift();
|
|
118
173
|
const why = exit.signal ?? `code ${exit.code}`;
|
|
119
174
|
if (crashes.length > MAX_CRASHES) {
|
|
120
|
-
throw new Error(`The
|
|
175
|
+
throw new Error(`The desktop host crashed ${crashes.length} times in a minute (last: ${why}), so it wasn't restarted. Fix the error above, then run this command again.`);
|
|
121
176
|
}
|
|
122
|
-
console.log(`\nThe
|
|
177
|
+
console.log(`\nThe desktop host stopped (${why}). Starting it again…\n`);
|
|
123
178
|
await new Promise((r) => setTimeout(r, RESTART_DELAY_MS));
|
|
124
179
|
}
|
|
125
180
|
}
|
|
@@ -127,17 +182,16 @@ const commands = {
|
|
|
127
182
|
async live() {
|
|
128
183
|
for (const later of ['ios', 'android', 'all']) {
|
|
129
184
|
if (has(later))
|
|
130
|
-
throw new Error(`--${later} isn't available yet. Run \`live\` for a phone app, or \`live --
|
|
185
|
+
throw new Error(`--${later} isn't available yet. Run \`live\` for a phone app, or \`live --desktop\`, \`--electron\` or \`--expo\` for a desktop app.`);
|
|
131
186
|
}
|
|
132
187
|
let ref = has('new') ? null : await savedProject();
|
|
133
|
-
|
|
134
|
-
|
|
188
|
+
const requested = requestedTemplate();
|
|
189
|
+
// Without a template flag, this folder's project runs as whatever it was made as.
|
|
190
|
+
const template = requested ?? ref?.template ?? 'core';
|
|
135
191
|
const desktop = DESKTOP_TEMPLATES.has(template);
|
|
136
|
-
if (ref &&
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (ref && desktop && ref.template !== template) {
|
|
140
|
-
throw new Error(`This folder's project (${CONFIG}) is a ${ref.template} project. Add --new to replace it, or run this in another folder.`);
|
|
192
|
+
if (ref && requested && (ref.template ?? 'core') !== requested) {
|
|
193
|
+
const current = TEMPLATE_KINDS[ref.template ?? 'core'] ?? `a ${ref.template}`;
|
|
194
|
+
throw new Error(`This folder's project (${CONFIG}) is ${current} project. Add --new to start ${TEMPLATE_KINDS[requested] ?? `a ${requested}`} one, or run this in another folder.`);
|
|
141
195
|
}
|
|
142
196
|
if (ref) {
|
|
143
197
|
console.log(`Opening this folder's project ${ref.id} (${CONFIG})`);
|
|
@@ -145,8 +199,7 @@ const commands = {
|
|
|
145
199
|
else {
|
|
146
200
|
ref = await createProject(flag('server') ?? LIVE_SERVER, flag('token') ?? process.env.NS_EDGE_CREATE_TOKEN, template);
|
|
147
201
|
await writeFile(join(root, CONFIG), JSON.stringify(ref, null, 2) + '\n');
|
|
148
|
-
|
|
149
|
-
console.log(`Created ${kind} project, saved to ${CONFIG} (it holds the project's keys: keep it private and out of git).`);
|
|
202
|
+
console.log(`Created ${TEMPLATE_KINDS[template] ?? `a ${template}`} project, saved to ${CONFIG} (it holds the project's keys: keep it private and out of git).`);
|
|
150
203
|
if (ref.free) {
|
|
151
204
|
console.log(`It's a free project: ${ref.free.agentTurnsPerDay} hosted agent turn${ref.free.agentTurnsPerDay === 1 ? '' : 's'} a day, deleted after ${ref.free.idleDays} days unused. ` +
|
|
152
205
|
'For an unlimited agent, add your own API key in the agent panel or connect Claude Code from Connect agent.');
|
|
@@ -158,7 +211,7 @@ const commands = {
|
|
|
158
211
|
openInBrowser(ide);
|
|
159
212
|
if (desktop) {
|
|
160
213
|
const first = template === 'electron' ? ' (the first run downloads Electron)' : template === 'expo' ? ' (the first run downloads the Expo Desktop shell)' : '';
|
|
161
|
-
console.log(`Starting the app on this
|
|
214
|
+
console.log(`Starting the app on this ${COMPUTER}${first}. Its window streams to the IDE's Device pane. Press Ctrl+C to stop.\n`);
|
|
162
215
|
return runDesktopHost(ref);
|
|
163
216
|
}
|
|
164
217
|
const device = `${projectUrl(ref)}#${ref.deviceKey}`;
|
|
@@ -168,7 +221,7 @@ const commands = {
|
|
|
168
221
|
},
|
|
169
222
|
async create() {
|
|
170
223
|
const server = flag('server') ?? 'http://localhost:5173';
|
|
171
|
-
const ref = await createProject(server, flag('token') ?? process.env.NS_EDGE_CREATE_TOKEN,
|
|
224
|
+
const ref = await createProject(server, flag('token') ?? process.env.NS_EDGE_CREATE_TOKEN, requestedTemplate());
|
|
172
225
|
await writeFile(join(root, CONFIG), JSON.stringify(ref, null, 2) + '\n');
|
|
173
226
|
console.log(`Created project ${ref.id} (saved to ${CONFIG}; it holds the project keys, keep it out of git)`);
|
|
174
227
|
await printPairing(ref);
|
|
@@ -236,16 +289,19 @@ const commands = {
|
|
|
236
289
|
await printPairing(await loadRef());
|
|
237
290
|
},
|
|
238
291
|
async help() {
|
|
239
|
-
console.log(`ns-edge <command>
|
|
240
|
-
|
|
241
|
-
live [--
|
|
242
|
-
create a project (or reopen this folder's), open its IDE, and print a QR code
|
|
243
|
-
for your phone; --
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
292
|
+
console.log(`ns-edge <command>
|
|
293
|
+
|
|
294
|
+
live [--ng|--react|--vue|--svelte|--solid|--desktop|--electron|--expo] [--new] [--no-open] [--allow-eval]
|
|
295
|
+
create a project (or reopen this folder's), open its IDE, and print a QR code
|
|
296
|
+
for your phone; --ng, --react, --vue, --svelte or --solid picks the framework
|
|
297
|
+
(plain TypeScript with XML views otherwise); --desktop (or --mac, --windows)
|
|
298
|
+
or --electron runs it on this Mac or Windows PC instead, with that OS's own
|
|
299
|
+
controls; --expo runs an Expo Desktop app on this Mac or Windows x64 PC
|
|
300
|
+
create [--server <url>] [--token <t>] [--ng|--react|--vue|--svelte|--solid|--template <name>]
|
|
301
|
+
create an edge project and save .ns-edge.json
|
|
302
|
+
push upload app/ and server/ in one transaction
|
|
303
|
+
watch push, then stream every save to the edge
|
|
304
|
+
pull download the project's files here
|
|
249
305
|
qr print the pairing QR code`);
|
|
250
306
|
},
|
|
251
307
|
};
|
package/package.json
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@nativescript/cloudflare-cli",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
4
|
-
"description": "Mirror a NativeScript project into an ns-edge project from a terminal or a Vite dev server",
|
|
5
|
-
"license": "Apache-2.0",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"bin": {
|
|
8
|
-
"ns-edge": "./dist/bin.js"
|
|
9
|
-
},
|
|
10
|
-
"exports": {
|
|
11
|
-
".": "./dist/sync.js",
|
|
12
|
-
"./vite": "./dist/vite.js"
|
|
13
|
-
},
|
|
14
|
-
"scripts": {
|
|
15
|
-
"build": "tsc -p .",
|
|
16
|
-
"typecheck": "tsc -p . --noEmit",
|
|
17
|
-
"prepack": "npm run build"
|
|
18
|
-
},
|
|
19
|
-
"dependencies": {
|
|
20
|
-
"qrcode": "^1.5.4"
|
|
21
|
-
},
|
|
22
|
-
"devDependencies": {
|
|
23
|
-
"@types/node": "^24.0.0",
|
|
24
|
-
"@types/qrcode": "^1.5.6",
|
|
25
|
-
"typescript": "^6.0.0",
|
|
26
|
-
"vite": "^8.3.5"
|
|
27
|
-
},
|
|
28
|
-
"peerDependencies": {
|
|
29
|
-
"vite": ">=6"
|
|
30
|
-
},
|
|
31
|
-
"peerDependenciesMeta": {
|
|
32
|
-
"vite": {
|
|
33
|
-
"optional": true
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
"files": [
|
|
37
|
-
"dist"
|
|
38
|
-
],
|
|
39
|
-
"publishConfig": {
|
|
40
|
-
"access": "public",
|
|
41
|
-
"tag": "beta"
|
|
42
|
-
}
|
|
43
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@nativescript/cloudflare-cli",
|
|
3
|
+
"version": "0.1.0-beta.9",
|
|
4
|
+
"description": "Mirror a NativeScript project into an ns-edge project from a terminal or a Vite dev server",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ns-edge": "./dist/bin.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./dist/sync.js",
|
|
12
|
+
"./vite": "./dist/vite.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc -p .",
|
|
16
|
+
"typecheck": "tsc -p . --noEmit",
|
|
17
|
+
"prepack": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"qrcode": "^1.5.4"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^24.0.0",
|
|
24
|
+
"@types/qrcode": "^1.5.6",
|
|
25
|
+
"typescript": "^6.0.0",
|
|
26
|
+
"vite": "^8.3.5"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"vite": ">=6"
|
|
30
|
+
},
|
|
31
|
+
"peerDependenciesMeta": {
|
|
32
|
+
"vite": {
|
|
33
|
+
"optional": true
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public",
|
|
41
|
+
"tag": "beta"
|
|
42
|
+
}
|
|
43
|
+
}
|