janela 0.14.2 → 0.14.3

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 CHANGED
@@ -43,10 +43,15 @@ Or start from a frontend framework:
43
43
 
44
44
  ```bash
45
45
  janela init my-app --template vue # or react | svelte | solid | vanilla
46
- cd my-app && npm install
46
+ cd my-app # deps are already installed
47
47
  janela dev # Vite dev server + HMR, in a native window
48
48
  ```
49
49
 
50
+ `init` installs the template's dependencies with whichever package manager
51
+ ran it — `pnpm janela init` uses pnpm — so `janela dev` works straight
52
+ afterwards. Pass `--no-install` to skip that, and if the install fails the
53
+ project is still written; the message names the one command to retry.
54
+
50
55
  A `--template` a globally installed CLI has never heard of is now an error
51
56
  rather than a silent fallback, and `janela --version` says which one you are
52
57
  running — worth checking first if a scaffolded project comes out looking
package/bin/janela.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // janela — the CLI (the tauri-cli analogue).
3
3
  //
4
- // janela init <name> scaffold a new project
4
+ // janela init <name> scaffold a new project (and install its dependencies)
5
5
  // janela build compile the project to a native binary (+ .app on macOS)
6
6
  // janela dev build, then run the binary with logs in the terminal
7
7
  // janela --version which janela this is
@@ -21,8 +21,8 @@ import { dirname, join, relative, resolve, sep } from "node:path";
21
21
  import { fileURLToPath } from "node:url";
22
22
  import {
23
23
  ANDROID_ABI, ANDROID_TARGET_SDK, androidConf, ffiManifest, iosConf,
24
- libraryProfile, mimeFor, NAME_RE, patchPeSubsystem, PeError,
25
- rewriteHostSpecifier, suggestName,
24
+ installCommand, libraryProfile, mimeFor, NAME_RE, packageManager as pmFor,
25
+ patchPeSubsystem, PeError, rewriteHostSpecifier, suggestName,
26
26
  } from "./lib.mjs";
27
27
 
28
28
  const KIT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
@@ -107,8 +107,9 @@ function viteCommand(root) {
107
107
  if (existsSync(shim)) return { argv: [shim], shell: process.platform === "win32" };
108
108
 
109
109
  fail(
110
- "this project has a vite config but no local vite — run your package manager's " +
111
- "install first (npm install / pnpm install)",
110
+ "this project has a vite config but no local vite — its dependencies are not " +
111
+ `installed yet:\n ${installCommand(pmFor(process.env)).join(" ")}\n` +
112
+ " (janela init installs them for you unless you pass --no-install)",
112
113
  );
113
114
  }
114
115
 
@@ -1160,7 +1161,29 @@ function copyTemplate(from, to, name) {
1160
1161
 
1161
1162
  // Best-effort repair of a rejected name, so the error can suggest something
1162
1163
  // that would have worked instead of only stating the rule.
1163
- function init(name, template) {
1164
+ /**
1165
+ * Install a scaffolded project's dependencies.
1166
+ *
1167
+ * Returns true on success. A failure is NOT fatal: the project is already
1168
+ * written, and the useful thing to do is name the one command to retry rather
1169
+ * than exit non-zero on a tree that is fine. Offline, an unreachable registry
1170
+ * and missing auth all land here.
1171
+ *
1172
+ * Windows needs a shell: npm/pnpm/yarn are .cmd shims, and Node has refused to
1173
+ * spawn those directly since the CVE-2024-27980 fix.
1174
+ */
1175
+ function installDeps(dir, pm) {
1176
+ const cmd = installCommand(pm);
1177
+ console.log(`janela: installing dependencies with ${pm}…`);
1178
+ const r = spawnSync(cmd[0], cmd.slice(1), {
1179
+ stdio: "inherit",
1180
+ cwd: dir,
1181
+ shell: process.platform === "win32",
1182
+ });
1183
+ return r.status === 0;
1184
+ }
1185
+
1186
+ function init(name, template, { install = true } = {}) {
1164
1187
  if (!name) {
1165
1188
  fail(
1166
1189
  "no project name given.\n" +
@@ -1216,8 +1239,27 @@ function init(name, template) {
1216
1239
  writeFileSync(join(dir, ".gitignore"), ".janela/\nnode_modules/\ndist/\n");
1217
1240
  writeFileSync(join(dir, "package.json"), JSON.stringify(pkg, null, 2) + "\n");
1218
1241
 
1219
- const install = template === "vanilla" ? "" : "npm install && ";
1220
- console.log(`janela: created ${name}/ (${template}) next: cd ${name} && ${install}janela dev`);
1242
+ // The no-framework template has no dependencies to install: it needs no
1243
+ // frontend toolchain, and `janela dev` works on it straight out of init.
1244
+ const hasDeps = template !== "vanilla";
1245
+ if (!hasDeps || !install) {
1246
+ // Only a skipped install on a template that HAS dependencies leaves the
1247
+ // caller something to run.
1248
+ const manual = hasDeps ? `${installCommand(pmFor(process.env)).join(" ")} && ` : "";
1249
+ console.log(`janela: created ${name}/ (${template}) — next: cd ${name} && ${manual}janela dev`);
1250
+ return;
1251
+ }
1252
+
1253
+ const pm = pmFor(process.env);
1254
+ if (installDeps(dir, pm)) {
1255
+ console.log(`janela: created ${name}/ (${template}) — next: cd ${name} && janela dev`);
1256
+ } else {
1257
+ console.log(
1258
+ `janela: created ${name}/ (${template}), but installing its dependencies failed.\n` +
1259
+ ` The project is written and fine — retry the install and you are set:\n` +
1260
+ ` cd ${name} && ${installCommand(pm).join(" ")} && janela dev`,
1261
+ );
1262
+ }
1221
1263
  }
1222
1264
 
1223
1265
  // ---- dev --------------------------------------------------------------------
@@ -1349,9 +1391,11 @@ function assertPositionals(max) {
1349
1391
 
1350
1392
  switch (cmd) {
1351
1393
  case "init":
1352
- assertKnownFlags(["template"]);
1394
+ assertKnownFlags(["template", "no-install"]);
1353
1395
  assertPositionals(1);
1354
- init(positionals()[0], flag("template", "vanilla"));
1396
+ init(positionals()[0], flag("template", "vanilla"), {
1397
+ install: !argv.includes("--no-install"),
1398
+ });
1355
1399
  break;
1356
1400
  case "build":
1357
1401
  assertKnownFlags(["target"]);
@@ -1371,7 +1415,7 @@ switch (cmd) {
1371
1415
  default:
1372
1416
  console.log(
1373
1417
  `janela ${VERSION}\n\n` +
1374
- "usage: janela init <name> [--template vanilla|vue|react|svelte|solid]\n" +
1418
+ "usage: janela init <name> [--template vanilla|vue|react|svelte|solid] [--no-install]\n" +
1375
1419
  " janela build [--target desktop|ios|android]\n" +
1376
1420
  " janela dev [--target desktop|ios|android]\n" +
1377
1421
  " janela --version",
package/bin/lib.mjs CHANGED
@@ -345,3 +345,30 @@ export function patchPeSubsystem(input) {
345
345
  buf.writeUInt16LE(IMAGE_SUBSYSTEM_WINDOWS_GUI, subOff);
346
346
  return { patched: true, buf };
347
347
  }
348
+
349
+ /**
350
+ * Which package manager to install a scaffolded project with.
351
+ *
352
+ * `npm_config_user_agent` is set by whichever manager ran janela, so
353
+ * `pnpm janela init` installs with pnpm. Run as a plain global binary it is
354
+ * unset, and npm is the only manager guaranteed to exist alongside node.
355
+ *
356
+ * Takes the environment rather than reading process.env, so it is testable.
357
+ */
358
+ export function packageManager(env = {}) {
359
+ const ua = env.npm_config_user_agent ?? "";
360
+ for (const pm of ["pnpm", "yarn", "bun"]) if (ua.startsWith(`${pm}/`)) return pm;
361
+ return "npm";
362
+ }
363
+
364
+ /**
365
+ * The install invocation for a manager.
366
+ *
367
+ * yarn's is the bare command. npm gets --no-fund --no-audit: neither is useful
368
+ * on a freshly scaffolded tree, and the audit is the slowest part of it.
369
+ */
370
+ export function installCommand(pm) {
371
+ if (pm === "yarn") return ["yarn"];
372
+ if (pm === "npm") return ["npm", "install", "--no-fund", "--no-audit"];
373
+ return [pm, "install"];
374
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "janela",
3
- "version": "0.14.2",
3
+ "version": "0.14.3",
4
4
  "description": "Desktop, iOS and Android apps in pure TypeScript, compiled to native. No Rust, no Node, no Electron.",
5
5
  "type": "module",
6
6
  "bin": {