layero 0.7.2 → 0.7.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.
Files changed (2) hide show
  1. package/dist/detect.js +89 -0
  2. package/package.json +1 -1
package/dist/detect.js CHANGED
@@ -137,6 +137,25 @@ export async function detectProject(cwd) {
137
137
  }),
138
138
  };
139
139
  }
140
+ // Remix / React Router v7 before SvelteKit/Vite — RR7 ships Vite
141
+ // internally, so the Vite-dep check would otherwise win. Mirrors the
142
+ // builder's RemixFramework + backend `remix` table entry (output
143
+ // build/client). Was missing from the CLI entirely — same dual-detector
144
+ // gap class as Angular: a Remix repo deployed via the CLI fell through
145
+ // to the static fallback and shipped raw sources.
146
+ if (hasDep(pkg, "@remix-run/dev") ||
147
+ hasDep(pkg, "@remix-run/react") ||
148
+ hasDep(pkg, "@remix-run/node") ||
149
+ hasDep(pkg, "@react-router/dev") ||
150
+ hasDep(pkg, "@react-router/node") ||
151
+ (await fileExists(cwd, "react-router.config.ts", "react-router.config.js"))) {
152
+ return {
153
+ framework_hint: "remix",
154
+ build_cmd: buildCmd(pkg, "npx react-router build"),
155
+ output_dir: "build/client",
156
+ confident: true,
157
+ };
158
+ }
140
159
  if (hasDep(pkg, "@sveltejs/kit") || (await fileExists(cwd, "svelte.config.js"))) {
141
160
  // SvelteKit needs an adapter. adapter-static = SPA path; anything
142
161
  // else (adapter-node, adapter-auto, …) is server-side. Mirrors
@@ -254,6 +273,22 @@ export async function detectProject(cwd) {
254
273
  confident: true,
255
274
  };
256
275
  }
276
+ // Angular after Vite — mirrors builder ALL order. Angular ships its
277
+ // own CLI (`ng build`) and writes to `dist/{project}/` (Angular <17)
278
+ // or `dist/{project}/browser/` (17+ `application` builder), NOT bare
279
+ // `dist`. Until this branch existed the CLI fell through to the
280
+ // static fallback (`output_dir='.'`), so the first deploy of an
281
+ // Angular repo shipped the raw sources — the dist/<project> S3 404
282
+ // incident. `angularOutputDir` parses angular.json to pin the path
283
+ // (lockstep with `frameworks/angular.py:extract_output_dir`).
284
+ if (hasDep(pkg, "@angular/core") || (await fileExists(cwd, "angular.json"))) {
285
+ return {
286
+ framework_hint: "angular",
287
+ build_cmd: buildCmd(pkg, "npx ng build"),
288
+ output_dir: await angularOutputDir(cwd),
289
+ confident: true,
290
+ };
291
+ }
257
292
  if (hasDep(pkg, "react-scripts")) {
258
293
  return {
259
294
  framework_hint: "cra",
@@ -363,3 +398,57 @@ async function hasHugoConfigMarker(cwd) {
363
398
  }
364
399
  return false;
365
400
  }
401
+ // Resolve Angular's real build output directory from angular.json.
402
+ // Lockstep with `core/builder/src/frameworks/angular.py:extract_output_dir`
403
+ // — same dual-detector-drift class as Next.js static-export, so the two
404
+ // implementations must agree (parity fixtures in
405
+ // core/tests/fixtures/framework-detect/angular-*).
406
+ //
407
+ // Resolution:
408
+ // 1. defaultProject if set & present, else first project in `projects`
409
+ // 2. project.architect.build.options.outputPath, else the CLI default
410
+ // `dist/{projectName}`
411
+ // 3. strip leading `./`
412
+ // 4. append `/browser` for the Angular 17+ `application` builder
413
+ // (builder id ends with `:application`) — it writes the served
414
+ // assets one level deeper. We also probe disk in case the repo was
415
+ // built locally before `layero deploy`.
416
+ // Returns the framework default `dist` on any parse error — matches
417
+ // AngularFramework.default_output_dir, and the builder re-derives the
418
+ // exact path at upload time anyway.
419
+ async function angularOutputDir(cwd) {
420
+ const FALLBACK = "dist";
421
+ let cfg;
422
+ try {
423
+ cfg = JSON.parse(await fs.readFile(path.join(cwd, "angular.json"), "utf-8"));
424
+ }
425
+ catch {
426
+ return FALLBACK;
427
+ }
428
+ const projects = cfg?.projects;
429
+ if (!projects || typeof projects !== "object")
430
+ return FALLBACK;
431
+ const firstName = Object.keys(projects)[0];
432
+ if (!firstName)
433
+ return FALLBACK;
434
+ const defaultName = cfg.defaultProject;
435
+ const projectName = defaultName && projects[defaultName] ? defaultName : firstName;
436
+ const buildCfg = projects[projectName]?.architect?.build;
437
+ if (!buildCfg || typeof buildCfg !== "object")
438
+ return FALLBACK;
439
+ const opts = (buildCfg.options ?? {});
440
+ const rawOut = typeof opts === "object" && opts ? opts.outputPath : undefined;
441
+ let out = typeof rawOut === "string" && rawOut.trim() ? rawOut.trim() : `dist/${projectName}`;
442
+ if (out.startsWith("./"))
443
+ out = out.slice(2);
444
+ const builderId = typeof buildCfg.builder === "string" ? buildCfg.builder : "";
445
+ const isApplicationBuilder = builderId.endsWith(":application");
446
+ let browserExists = false;
447
+ try {
448
+ browserExists = (await fs.stat(path.join(cwd, out, "browser"))).isDirectory();
449
+ }
450
+ catch {
451
+ // not built locally — rely on the builder-id signal below
452
+ }
453
+ return browserExists || isApplicationBuilder ? `${out}/browser` : out;
454
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "layero",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "Layero CLI — publish a local site with one command. No git, no GitHub, agent-friendly (Cursor, Claude Code).",
5
5
  "license": "MIT",
6
6
  "type": "module",