ship-em 0.2.3 → 0.2.5

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
@@ -1,4 +1,4 @@
1
- # shipem
1
+ # shipem 🐑
2
2
 
3
3
  **One command. Your app is live.**
4
4
 
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import chalk4 from "chalk";
9
9
  import axios2 from "axios";
10
10
  import FormData2 from "form-data";
11
11
  import { createReadStream } from "fs";
12
- import { rmSync, existsSync as existsSync6, appendFileSync, writeFileSync as writeFileSync4, readFileSync as readFileSync7, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
12
+ import { rmSync, existsSync as existsSync6, appendFileSync, writeFileSync as writeFileSync5, readFileSync as readFileSync8, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
13
13
  import { join as join8 } from "path";
14
14
  import { tmpdir } from "os";
15
15
  import { create as tarCreate } from "tar";
@@ -68,7 +68,7 @@ var ui = {
68
68
  );
69
69
  console.log("");
70
70
  console.log(
71
- ` ${brand.gray("Your AI built it.")} ${brand.blue.bold("We'll ship it.")}`
71
+ ` \u{1F411} ${brand.gray("Your AI built it.")} ${brand.blue.bold("We'll ship it.")}`
72
72
  );
73
73
  console.log("");
74
74
  },
@@ -546,7 +546,7 @@ function scanProjectInternal(cwd) {
546
546
  return {
547
547
  framework: "nextjs",
548
548
  buildCommand: pkg.scripts?.build ?? "npm run build",
549
- outputDirectory: ".next",
549
+ outputDirectory: "out",
550
550
  installCommand: detectPackageManager(cwd),
551
551
  serverType,
552
552
  deployTarget: "cloudflare-pages",
@@ -794,9 +794,34 @@ function findWorkspacePackages(cwd) {
794
794
 
795
795
  // src/build/builder.ts
796
796
  import { execa } from "execa";
797
- import { existsSync as existsSync2 } from "fs";
797
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
798
798
  import { join as join2 } from "path";
799
799
  import chalk2 from "chalk";
800
+ function patchNextConfig(cwd) {
801
+ const candidates = ["next.config.ts", "next.config.mjs", "next.config.js"];
802
+ for (const filename of candidates) {
803
+ const configPath = join2(cwd, filename);
804
+ if (existsSync2(configPath)) {
805
+ const content = readFileSync2(configPath, "utf-8");
806
+ if (content.includes("output")) return;
807
+ const patched = content.replace(
808
+ /const nextConfig[^=]*=\s*\{/,
809
+ (match) => match + "\n output: 'export',"
810
+ );
811
+ if (patched !== content) {
812
+ writeFileSync(configPath, patched, "utf-8");
813
+ ui.dim(" \u2139\uFE0F Added output: 'export' to " + filename + " for static deployment");
814
+ return;
815
+ }
816
+ }
817
+ }
818
+ writeFileSync(
819
+ join2(cwd, "next.config.js"),
820
+ "/** @type {import('next').NextConfig} */\nconst nextConfig = { output: 'export' };\nmodule.exports = nextConfig;\n",
821
+ "utf-8"
822
+ );
823
+ ui.dim(" \u2139\uFE0F Created next.config.js with output: 'export' for static deployment");
824
+ }
800
825
  async function buildProject(config, cwd = process.cwd()) {
801
826
  const start = Date.now();
802
827
  if (config.installCommand) {
@@ -804,8 +829,8 @@ async function buildProject(config, cwd = process.cwd()) {
804
829
  const outputLines = [];
805
830
  let lineCount = 0;
806
831
  try {
807
- const [installBin, ...installArgs] = config.installCommand.split(" ");
808
- const installProc = execa(installBin, installArgs, {
832
+ const installProc = execa(config.installCommand, {
833
+ shell: true,
809
834
  cwd,
810
835
  env: { ...process.env, CI: "true" },
811
836
  timeout: 5 * 60 * 1e3,
@@ -850,6 +875,9 @@ async function buildProject(config, cwd = process.cwd()) {
850
875
  }
851
876
  console.log("");
852
877
  }
878
+ if (config.framework === "nextjs") {
879
+ patchNextConfig(cwd);
880
+ }
853
881
  if (config.buildCommand) {
854
882
  const buildSpinner = ui.spinner("Building...");
855
883
  const outputLines = [];
@@ -865,8 +893,8 @@ async function buildProject(config, cwd = process.cwd()) {
865
893
  }
866
894
  }
867
895
  try {
868
- const [finalBin, ...finalArgs] = config.buildCommand.split(" ");
869
- const buildProc = execa(finalBin, finalArgs, {
896
+ const buildProc = execa(config.buildCommand, {
897
+ shell: true,
870
898
  cwd,
871
899
  env: buildEnv,
872
900
  timeout: 10 * 60 * 1e3,
@@ -944,7 +972,7 @@ function parseErrorMessage(raw, outputLines) {
944
972
 
945
973
  // src/config.ts
946
974
  import Conf from "conf";
947
- import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
975
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "fs";
948
976
  import { join as join3 } from "path";
949
977
  var SHIPEM_API_URL = process.env.SHIPEM_API_URL ?? "https://api.shipem.dev";
950
978
  var globalConf = new Conf({
@@ -985,7 +1013,7 @@ function readProjectConfig(cwd = process.cwd()) {
985
1013
  return {};
986
1014
  }
987
1015
  try {
988
- const raw = readFileSync2(configPath, "utf-8");
1016
+ const raw = readFileSync3(configPath, "utf-8");
989
1017
  const config = JSON.parse(raw);
990
1018
  warnIfConfigContainsSecrets(config, configPath);
991
1019
  return config;
@@ -995,7 +1023,7 @@ function readProjectConfig(cwd = process.cwd()) {
995
1023
  }
996
1024
  function writeProjectConfig(config, cwd = process.cwd()) {
997
1025
  const configPath = join3(cwd, SHIPIT_CONFIG_FILE);
998
- writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
1026
+ writeFileSync2(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
999
1027
  }
1000
1028
  var SECRET_KEY_PATTERN = /token|secret|key|password|credential|api_?key/i;
1001
1029
  function warnIfConfigContainsSecrets(config, configPath) {
@@ -1162,7 +1190,7 @@ async function loginCommand(opts = {}) {
1162
1190
  }
1163
1191
 
1164
1192
  // src/commands/fix.ts
1165
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, readdirSync as readdirSync2 } from "fs";
1193
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3, readdirSync as readdirSync2 } from "fs";
1166
1194
  import { join as join4 } from "path";
1167
1195
  import { execa as execa2 } from "execa";
1168
1196
  function findMissingModules(errorOutput) {
@@ -1239,12 +1267,12 @@ function applyTsConfigFixes(cwd, fixes) {
1239
1267
  const tsconfigPath = join4(cwd, "tsconfig.json");
1240
1268
  if (!existsSync4(tsconfigPath)) return false;
1241
1269
  try {
1242
- const content = readFileSync3(tsconfigPath, "utf-8");
1270
+ const content = readFileSync4(tsconfigPath, "utf-8");
1243
1271
  const stripped = content.replace(/\/\/[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
1244
1272
  const tsconfig = JSON.parse(stripped);
1245
1273
  if (!tsconfig.compilerOptions) tsconfig.compilerOptions = {};
1246
1274
  Object.assign(tsconfig.compilerOptions, fixes);
1247
- writeFileSync2(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
1275
+ writeFileSync3(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
1248
1276
  return true;
1249
1277
  } catch {
1250
1278
  return false;
@@ -1267,7 +1295,7 @@ function findMissingEnvVars(cwd) {
1267
1295
  scanDir(fullPath, depth + 1);
1268
1296
  } else if (/\.(ts|tsx|js|jsx|mjs|mts|vue|svelte|astro)$/.test(entry.name)) {
1269
1297
  try {
1270
- const content = readFileSync3(fullPath, "utf-8");
1298
+ const content = readFileSync4(fullPath, "utf-8");
1271
1299
  for (const pat of patterns) {
1272
1300
  pat.lastIndex = 0;
1273
1301
  let match;
@@ -1296,7 +1324,7 @@ function generateEnvExample(cwd, vars) {
1296
1324
  const envExamplePath = join4(cwd, ".env.example");
1297
1325
  if (existsSync4(envExamplePath)) return false;
1298
1326
  const content = vars.map((v) => `${v}=`).join("\n") + "\n";
1299
- writeFileSync2(envExamplePath, content, "utf-8");
1327
+ writeFileSync3(envExamplePath, content, "utf-8");
1300
1328
  return true;
1301
1329
  }
1302
1330
  function detectFrameworkConfigFixes(errorOutput, cwd, framework) {
@@ -1305,7 +1333,7 @@ function detectFrameworkConfigFixes(errorOutput, cwd, framework) {
1305
1333
  const nextConfigPath = existsSync4(join4(cwd, "next.config.mjs")) ? join4(cwd, "next.config.mjs") : existsSync4(join4(cwd, "next.config.js")) ? join4(cwd, "next.config.js") : null;
1306
1334
  if (nextConfigPath && errorOutput.includes("output")) {
1307
1335
  try {
1308
- const content = readFileSync3(nextConfigPath, "utf-8");
1336
+ const content = readFileSync4(nextConfigPath, "utf-8");
1309
1337
  if (!content.includes("output")) {
1310
1338
  fixes.push(`Add output: 'export' to ${nextConfigPath.split("/").pop()}`);
1311
1339
  }
@@ -1316,7 +1344,7 @@ function detectFrameworkConfigFixes(errorOutput, cwd, framework) {
1316
1344
  if ((framework === "vite-react" || framework === "vite-vue" || framework === "vite-svelte") && errorOutput.includes("plugin")) {
1317
1345
  const pkgPath = join4(cwd, "package.json");
1318
1346
  try {
1319
- const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
1347
+ const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
1320
1348
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
1321
1349
  if (framework === "vite-react" && !deps["@vitejs/plugin-react"]) {
1322
1350
  fixes.push("Install @vitejs/plugin-react");
@@ -1488,11 +1516,11 @@ async function tryInlineFix(errorOutput, cwd, config) {
1488
1516
  // src/deploy/cloudflare.ts
1489
1517
  import axios from "axios";
1490
1518
  import { createHash } from "crypto";
1491
- import { readdirSync as readdirSync3, statSync as statSync2, readFileSync as readFileSync5 } from "fs";
1519
+ import { readdirSync as readdirSync3, statSync as statSync2, readFileSync as readFileSync6 } from "fs";
1492
1520
  import { join as join6, relative } from "path";
1493
1521
 
1494
1522
  // src/deploy/exclude.ts
1495
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
1523
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
1496
1524
  import { join as join5 } from "path";
1497
1525
  import chalk3 from "chalk";
1498
1526
  var DEFAULT_PATTERNS = [
@@ -1547,7 +1575,7 @@ function matchesIgnoreLine(relPath, line) {
1547
1575
  function loadIgnoreLines(filePath) {
1548
1576
  if (!existsSync5(filePath)) return [];
1549
1577
  try {
1550
- return readFileSync4(filePath, "utf-8").split("\n");
1578
+ return readFileSync5(filePath, "utf-8").split("\n");
1551
1579
  } catch {
1552
1580
  return [];
1553
1581
  }
@@ -1680,7 +1708,7 @@ var CloudflarePages = class {
1680
1708
  let totalBytes = 0;
1681
1709
  const fileMap = /* @__PURE__ */ new Map();
1682
1710
  for (const filePath of filePaths) {
1683
- const content = readFileSync5(filePath);
1711
+ const content = readFileSync6(filePath);
1684
1712
  const hash = createHash("sha256").update(content).digest("hex");
1685
1713
  const urlPath = "/" + relative(fullOutputPath, filePath).replace(/\\/g, "/");
1686
1714
  fileMap.set(urlPath, { hash, content });
@@ -1695,6 +1723,9 @@ var CloudflarePages = class {
1695
1723
  for (const [urlPath, { hash }] of fileMap) {
1696
1724
  manifest[urlPath] = hash;
1697
1725
  }
1726
+ if (Object.keys(manifest).length === 0) {
1727
+ throw new DeployError(`Output directory '${outputDir}' contains no files to deploy. Check your build output.`);
1728
+ }
1698
1729
  const deploySpinner = ui.spinner("Creating deployment...");
1699
1730
  let jwt;
1700
1731
  let requiredFiles;
@@ -1759,7 +1790,7 @@ var CloudflarePages = class {
1759
1790
  let totalBytes = 0;
1760
1791
  const fileMap = /* @__PURE__ */ new Map();
1761
1792
  for (const filePath of filePaths) {
1762
- const content = readFileSync5(filePath);
1793
+ const content = readFileSync6(filePath);
1763
1794
  const hash = createHash("sha256").update(content).digest("hex");
1764
1795
  const urlPath = "/" + relative(fullOutputPath, filePath).replace(/\\/g, "/");
1765
1796
  fileMap.set(urlPath, { hash, content });
@@ -1774,6 +1805,9 @@ var CloudflarePages = class {
1774
1805
  for (const [urlPath, { hash }] of fileMap) {
1775
1806
  manifest[urlPath] = hash;
1776
1807
  }
1808
+ if (Object.keys(manifest).length === 0) {
1809
+ throw new DeployError(`Output directory '${outputDir}' contains no files to deploy. Check your build output.`);
1810
+ }
1777
1811
  const deploySpinner = ui.spinner(`Creating preview deployment (branch: ${branch})...`);
1778
1812
  let jwt;
1779
1813
  let requiredFiles;
@@ -1948,7 +1982,7 @@ function sanitizeProjectName(name) {
1948
1982
  }
1949
1983
 
1950
1984
  // src/deploy/badge.ts
1951
- import { readdirSync as readdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
1985
+ import { readdirSync as readdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
1952
1986
  import { join as join7 } from "path";
1953
1987
  var BADGE_HTML = `<!-- Shipped with Shipem -->
1954
1988
  <div id="shipem-badge" style="position:fixed;bottom:12px;right:12px;z-index:9999;font-family:-apple-system,BlinkMacSystemFont,sans-serif;font-size:12px;background:rgba(13,17,23,0.9);color:#94A3B8;padding:6px 12px;border-radius:20px;border:1px solid rgba(59,130,246,0.3);text-decoration:none;display:flex;align-items:center;gap:6px;backdrop-filter:blur(8px);transition:opacity 0.2s"><a href="https://shipem.dev" target="_blank" rel="noopener" style="color:#94A3B8;text-decoration:none;display:flex;align-items:center;gap:6px">Shipped with <span style="color:#3B82F6">\u26A1</span> Shipem</a></div>`;
@@ -1958,14 +1992,14 @@ function injectBadge(outputDir) {
1958
1992
  const files = readdirSync4(outputDir).filter((f) => f.endsWith(".html"));
1959
1993
  for (const file of files) {
1960
1994
  const filePath = join7(outputDir, file);
1961
- const content = readFileSync6(filePath, "utf-8");
1995
+ const content = readFileSync7(filePath, "utf-8");
1962
1996
  if (content.includes("shipem-badge")) continue;
1963
1997
  if (content.includes("</body>")) {
1964
- writeFileSync3(filePath, content.replace("</body>", `${BADGE_HTML}
1998
+ writeFileSync4(filePath, content.replace("</body>", `${BADGE_HTML}
1965
1999
  </body>`), "utf-8");
1966
2000
  injected++;
1967
2001
  } else if (content.includes("</html>")) {
1968
- writeFileSync3(filePath, content.replace("</html>", `${BADGE_HTML}
2002
+ writeFileSync4(filePath, content.replace("</html>", `${BADGE_HTML}
1969
2003
  </html>`), "utf-8");
1970
2004
  injected++;
1971
2005
  }
@@ -2120,24 +2154,19 @@ async function deployCommand(options) {
2120
2154
  console.log("");
2121
2155
  }
2122
2156
  if (isAnonymous) {
2123
- console.log(` ${chalk4.dim("To deploy, you need either Cloudflare credentials or a Shipem login.")}`);
2124
- console.log("");
2125
- console.log(` ${chalk4.cyan("Option 1:")} Set Cloudflare credentials (free account)`);
2126
- console.log(` ${chalk4.dim("export CLOUDFLARE_API_TOKEN=your_token")}`);
2127
- console.log(` ${chalk4.dim("export CLOUDFLARE_ACCOUNT_ID=your_account_id")}`);
2128
- console.log(` ${chalk4.dim("Get these from: https://dash.cloudflare.com/profile/api-tokens")}`);
2157
+ console.log(` ${chalk4.bold("Deploy your app in seconds")} \u2014 no Cloudflare account needed. \u{1F411}`);
2129
2158
  console.log("");
2130
- console.log(` ${chalk4.cyan("Option 2:")} Log in to Shipem`);
2131
- console.log(` ${chalk4.dim("npx ship-em login")}`);
2159
+ console.log(` Log in with GitHub to get started. It's free.`);
2160
+ console.log(` ${chalk4.dim("\u2192 https://shipem.dev")}`);
2132
2161
  console.log("");
2133
2162
  const { action } = await inquirer.prompt([
2134
2163
  {
2135
2164
  type: "list",
2136
2165
  name: "action",
2137
- message: "What would you like to do?",
2166
+ message: "Ready to ship?",
2138
2167
  choices: [
2139
- { name: "Log in with GitHub (sets up deployment)", value: "login" },
2140
- { name: "Quit (I'll set up Cloudflare credentials)", value: "quit" }
2168
+ { name: "Log in with GitHub (free, takes 10 seconds)", value: "login" },
2169
+ { name: "Cancel", value: "quit" }
2141
2170
  ]
2142
2171
  }
2143
2172
  ]);
@@ -2299,6 +2328,10 @@ async function deployCommand(options) {
2299
2328
  }
2300
2329
  }
2301
2330
  phases.push({ name: "Scan", durationMs: Date.now() - phaseStart });
2331
+ if (projectConfig.framework === "nextjs") {
2332
+ patchNextConfigForStaticExport(cwd);
2333
+ patchNextLayoutFonts(cwd);
2334
+ }
2302
2335
  if (!options.skipBuild && !options.turbo) {
2303
2336
  phaseStart = Date.now();
2304
2337
  ui.section("Building...");
@@ -2554,13 +2587,13 @@ async function deployCommand(options) {
2554
2587
  }, cwd);
2555
2588
  const gitignorePath = join8(cwd, ".gitignore");
2556
2589
  if (existsSync6(gitignorePath)) {
2557
- const gitignoreContent = readFileSync7(gitignorePath, "utf-8");
2590
+ const gitignoreContent = readFileSync8(gitignorePath, "utf-8");
2558
2591
  const lines = gitignoreContent.split("\n").map((l) => l.trim());
2559
2592
  if (!lines.includes("shipem.json")) {
2560
2593
  appendFileSync(gitignorePath, "\n# Shipem config\nshipem.json\n");
2561
2594
  }
2562
2595
  } else {
2563
- writeFileSync4(gitignorePath, "# Shipem config\nshipem.json\n");
2596
+ writeFileSync5(gitignorePath, "# Shipem config\nshipem.json\n");
2564
2597
  }
2565
2598
  ui.deployBoxEnhanced(
2566
2599
  projectConfig.name,
@@ -2572,9 +2605,77 @@ async function deployCommand(options) {
2572
2605
  isAnonymous
2573
2606
  );
2574
2607
  }
2608
+ function patchNextConfigForStaticExport(cwd) {
2609
+ const tsConfig = join8(cwd, "next.config.ts");
2610
+ const jsConfig = join8(cwd, "next.config.js");
2611
+ const mjsConfig = join8(cwd, "next.config.mjs");
2612
+ for (const configPath of [tsConfig, jsConfig, mjsConfig]) {
2613
+ if (existsSync6(configPath)) {
2614
+ const content = readFileSync8(configPath, "utf-8");
2615
+ if (/output\s*:\s*['"]export['"]/.test(content)) {
2616
+ return;
2617
+ }
2618
+ }
2619
+ }
2620
+ if (existsSync6(tsConfig)) {
2621
+ const content = readFileSync8(tsConfig, "utf-8");
2622
+ const patched = content.replace(
2623
+ /({[\s\S]*?)(})\s*(?:satisfies|as)\s/,
2624
+ "$1 output: 'export',\n$2 satisfies "
2625
+ );
2626
+ if (patched === content) {
2627
+ const fallback = content.replace(/(\{)/, "$1\n output: 'export',");
2628
+ writeFileSync5(tsConfig, fallback);
2629
+ } else {
2630
+ writeFileSync5(tsConfig, patched);
2631
+ }
2632
+ console.log(` \u2139\uFE0F Added output: 'export' to next.config.ts for static deployment`);
2633
+ } else if (existsSync6(mjsConfig)) {
2634
+ const content = readFileSync8(mjsConfig, "utf-8");
2635
+ const patched = content.replace(/(\{)/, "$1\n output: 'export',");
2636
+ writeFileSync5(mjsConfig, patched);
2637
+ console.log(` \u2139\uFE0F Added output: 'export' to next.config.mjs for static deployment`);
2638
+ } else if (existsSync6(jsConfig)) {
2639
+ const content = readFileSync8(jsConfig, "utf-8");
2640
+ const patched = content.replace(/(\{)/, "$1\n output: 'export',");
2641
+ writeFileSync5(jsConfig, patched);
2642
+ console.log(` \u2139\uFE0F Added output: 'export' to next.config.js for static deployment`);
2643
+ } else {
2644
+ writeFileSync5(jsConfig, `/** @type {import('next').NextConfig} */
2645
+ module.exports = { output: 'export' };
2646
+ `);
2647
+ console.log(` \u2139\uFE0F Created next.config.js with output: 'export' for static deployment`);
2648
+ }
2649
+ }
2650
+ function patchNextLayoutFonts(cwd) {
2651
+ const layoutPaths = [
2652
+ join8(cwd, "app/layout.tsx"),
2653
+ join8(cwd, "app/layout.jsx"),
2654
+ join8(cwd, "src/app/layout.tsx"),
2655
+ join8(cwd, "src/app/layout.jsx")
2656
+ ];
2657
+ for (const layoutPath of layoutPaths) {
2658
+ if (!existsSync6(layoutPath)) continue;
2659
+ const content = readFileSync8(layoutPath, "utf-8");
2660
+ if (!/import.*next\/font/.test(content)) continue;
2661
+ let patched = content;
2662
+ patched = patched.replace(/import\s+\{[^}]*\}\s+from\s+['"]next\/font\/[^'"]+['"];?\n?/g, "");
2663
+ patched = patched.replace(/const\s+\w+\s*=\s*\w+\(\s*\{[^}]*\}\s*\)\s*;?\n?/g, "");
2664
+ patched = patched.replace(/\$\{\w+\.className\}/g, "");
2665
+ patched = patched.replace(/className=\{`([^`]*)`\}/g, (match, inner) => {
2666
+ const cleaned = inner.trim();
2667
+ return cleaned ? `className={\`${cleaned}\`}` : "";
2668
+ });
2669
+ patched = patched.replace(/className=\{\w+\.className\}/g, "");
2670
+ if (patched !== content) {
2671
+ writeFileSync5(layoutPath, patched);
2672
+ console.log(` \u2139\uFE0F Removed next/font imports from ${layoutPath.replace(cwd + "/", "")} for static export`);
2673
+ }
2674
+ }
2675
+ }
2575
2676
 
2576
2677
  // src/commands/env.ts
2577
- import { existsSync as existsSync7, readFileSync as readFileSync8, readdirSync as readdirSync6, writeFileSync as writeFileSync5 } from "fs";
2678
+ import { existsSync as existsSync7, readFileSync as readFileSync9, readdirSync as readdirSync6, writeFileSync as writeFileSync6 } from "fs";
2578
2679
  import { join as join9 } from "path";
2579
2680
  var SERVICE_LINKS = {
2580
2681
  SUPABASE_URL: { name: "Supabase", url: "https://app.supabase.com/project/_/settings/api" },
@@ -2614,7 +2715,7 @@ function scanSourceForEnvVars(cwd) {
2614
2715
  scanDir(fullPath, depth + 1);
2615
2716
  } else if (/\.(ts|tsx|js|jsx|mjs|mts|vue|svelte|astro)$/.test(entry.name)) {
2616
2717
  try {
2617
- const content = readFileSync8(fullPath, "utf-8");
2718
+ const content = readFileSync9(fullPath, "utf-8");
2618
2719
  for (const pat of patterns) {
2619
2720
  pat.lastIndex = 0;
2620
2721
  let match;
@@ -2653,7 +2754,7 @@ function readEnvFile(cwd) {
2653
2754
  }
2654
2755
  function readFile2(path) {
2655
2756
  try {
2656
- return readFileSync8(path, "utf-8");
2757
+ return readFileSync9(path, "utf-8");
2657
2758
  } catch {
2658
2759
  return null;
2659
2760
  }
@@ -2718,7 +2819,7 @@ async function envCommand() {
2718
2819
  const comment = service ? ` # ${service.name}` : "";
2719
2820
  return `${v}=${comment}`;
2720
2821
  }).join("\n") + "\n";
2721
- writeFileSync5(envExamplePath, content, "utf-8");
2822
+ writeFileSync6(envExamplePath, content, "utf-8");
2722
2823
  ui.success("Generated .env.example from detected variables");
2723
2824
  console.log("");
2724
2825
  } else if (existsSync7(envExamplePath)) {
@@ -2739,7 +2840,7 @@ async function envCommand() {
2739
2840
  // src/commands/init.ts
2740
2841
  import inquirer2 from "inquirer";
2741
2842
  import { execa as execa3 } from "execa";
2742
- import { existsSync as existsSync8, writeFileSync as writeFileSync6, mkdirSync } from "fs";
2843
+ import { existsSync as existsSync8, writeFileSync as writeFileSync7, mkdirSync } from "fs";
2743
2844
  import { join as join10 } from "path";
2744
2845
  import chalk6 from "chalk";
2745
2846
 
@@ -2933,7 +3034,7 @@ async function initCommand(options = {}) {
2933
3034
  }
2934
3035
  const projectDir = join10(process.cwd(), projectName);
2935
3036
  if (existsSync8(projectDir)) {
2936
- writeFileSync6(
3037
+ writeFileSync7(
2937
3038
  join10(projectDir, "shipem.json"),
2938
3039
  JSON.stringify({ project: { name: projectName, framework: matched.value } }, null, 2) + "\n",
2939
3040
  "utf-8"
@@ -3014,7 +3115,7 @@ async function scaffoldFromTemplate(tpl, options) {
3014
3115
  }
3015
3116
  const projectDir = join10(process.cwd(), projectName);
3016
3117
  if (existsSync8(projectDir)) {
3017
- writeFileSync6(
3118
+ writeFileSync7(
3018
3119
  join10(projectDir, "shipem.json"),
3019
3120
  JSON.stringify({ project: { name: projectName, framework: tpl.framework } }, null, 2) + "\n",
3020
3121
  "utf-8"
@@ -3030,7 +3131,7 @@ function createMinimalProject(projectName, description) {
3030
3131
  const dir = join10(process.cwd(), projectName);
3031
3132
  if (!existsSync8(dir)) {
3032
3133
  mkdirSync(dir, { recursive: true });
3033
- writeFileSync6(join10(dir, "index.html"), `<!DOCTYPE html>
3134
+ writeFileSync7(join10(dir, "index.html"), `<!DOCTYPE html>
3034
3135
  <html lang="en">
3035
3136
  <head>
3036
3137
  <meta charset="UTF-8">
@@ -3056,7 +3157,7 @@ function createMinimalProject(projectName, description) {
3056
3157
  function createStaticPortfolio(projectName) {
3057
3158
  const dir = join10(process.cwd(), projectName);
3058
3159
  mkdirSync(dir, { recursive: true });
3059
- writeFileSync6(join10(dir, "index.html"), `<!DOCTYPE html>
3160
+ writeFileSync7(join10(dir, "index.html"), `<!DOCTYPE html>
3060
3161
  <html lang="en">
3061
3162
  <head>
3062
3163
  <meta charset="UTF-8">
@@ -3093,7 +3194,7 @@ function createStaticPortfolio(projectName) {
3093
3194
  </footer>
3094
3195
  </body>
3095
3196
  </html>`, "utf-8");
3096
- writeFileSync6(join10(dir, "style.css"), `* { margin: 0; padding: 0; box-sizing: border-box; }
3197
+ writeFileSync7(join10(dir, "style.css"), `* { margin: 0; padding: 0; box-sizing: border-box; }
3097
3198
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0a0a0a; color: #ededed; min-height: 100vh; }
3098
3199
  header { text-align: center; padding: 4rem 1rem 2rem; }
3099
3200
  header h1 { font-size: 2.5rem; margin-bottom: 0.5rem; }
@@ -3532,7 +3633,7 @@ async function watchCommand() {
3532
3633
  import chalk11 from "chalk";
3533
3634
 
3534
3635
  // src/memory/index.ts
3535
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync2, existsSync as existsSync9 } from "fs";
3636
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync2, existsSync as existsSync9 } from "fs";
3536
3637
  import { join as join12 } from "path";
3537
3638
  import { homedir } from "os";
3538
3639
  var SHIPEM_HOME = join12(homedir(), ".shipem");
@@ -3548,14 +3649,14 @@ function ensureDir() {
3548
3649
  function readJson2(path, fallback) {
3549
3650
  try {
3550
3651
  if (!existsSync9(path)) return fallback;
3551
- return JSON.parse(readFileSync9(path, "utf-8"));
3652
+ return JSON.parse(readFileSync10(path, "utf-8"));
3552
3653
  } catch {
3553
3654
  return fallback;
3554
3655
  }
3555
3656
  }
3556
3657
  function writeJson(path, data) {
3557
3658
  ensureDir();
3558
- writeFileSync7(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
3659
+ writeFileSync8(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
3559
3660
  }
3560
3661
  function getMemory() {
3561
3662
  return readJson2(MEMORY_PATH, {
@@ -3650,7 +3751,7 @@ async function configCommand(action, key, value) {
3650
3751
  // src/commands/monitor.ts
3651
3752
  import axios5 from "axios";
3652
3753
  import chalk12 from "chalk";
3653
- import { writeFileSync as writeFileSync8, readFileSync as readFileSync10, existsSync as existsSync10, unlinkSync } from "fs";
3754
+ import { writeFileSync as writeFileSync9, readFileSync as readFileSync11, existsSync as existsSync10, unlinkSync } from "fs";
3654
3755
  import { join as join13 } from "path";
3655
3756
  import { tmpdir as tmpdir2 } from "os";
3656
3757
 
@@ -3729,7 +3830,7 @@ async function monitorCommand(options = {}) {
3729
3830
  if (options.stop) {
3730
3831
  if (existsSync10(PID_FILE)) {
3731
3832
  try {
3732
- const pid = parseInt(readFileSync10(PID_FILE, "utf-8").trim(), 10);
3833
+ const pid = parseInt(readFileSync11(PID_FILE, "utf-8").trim(), 10);
3733
3834
  process.kill(pid, "SIGTERM");
3734
3835
  unlinkSync(PID_FILE);
3735
3836
  ui.success("Monitor daemon stopped.");
@@ -3803,11 +3904,11 @@ async function monitorCommand(options = {}) {
3803
3904
  });
3804
3905
  }
3805
3906
  function writePidFile() {
3806
- writeFileSync8(PID_FILE, String(process.pid), "utf-8");
3907
+ writeFileSync9(PID_FILE, String(process.pid), "utf-8");
3807
3908
  }
3808
3909
 
3809
3910
  // src/commands/hooks.ts
3810
- import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync9, unlinkSync as unlinkSync2, chmodSync, mkdirSync as mkdirSync3 } from "fs";
3911
+ import { existsSync as existsSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync10, unlinkSync as unlinkSync2, chmodSync, mkdirSync as mkdirSync3 } from "fs";
3811
3912
  import { join as join14 } from "path";
3812
3913
  var HOOK_MARKER = "# shipem-auto-deploy";
3813
3914
  var HOOK_CONTENT = `#!/bin/sh
@@ -3821,7 +3922,7 @@ function getHookPath(cwd) {
3821
3922
  }
3822
3923
  function isShipemHook(path) {
3823
3924
  if (!existsSync11(path)) return false;
3824
- const content = readFileSync11(path, "utf-8");
3925
+ const content = readFileSync12(path, "utf-8");
3825
3926
  return content.includes(HOOK_MARKER);
3826
3927
  }
3827
3928
  async function hooksCommand(action) {
@@ -3861,7 +3962,7 @@ async function hooksCommand(action) {
3861
3962
  if (!existsSync11(hooksDir)) {
3862
3963
  mkdirSync3(hooksDir, { recursive: true });
3863
3964
  }
3864
- writeFileSync9(hookPath, HOOK_CONTENT, "utf-8");
3965
+ writeFileSync10(hookPath, HOOK_CONTENT, "utf-8");
3865
3966
  chmodSync(hookPath, 493);
3866
3967
  ui.success("Installed post-commit hook \u2192 auto-deploy on commit");
3867
3968
  ui.dim(`Remove with: shipem hooks remove`);
@@ -4014,7 +4115,7 @@ async function previewCommand(options = {}) {
4014
4115
  }
4015
4116
 
4016
4117
  // src/index.ts
4017
- import { readFileSync as readFileSync12 } from "fs";
4118
+ import { readFileSync as readFileSync13 } from "fs";
4018
4119
  import { fileURLToPath } from "url";
4019
4120
  import { dirname, join as join15 } from "path";
4020
4121
  var __filename2 = fileURLToPath(import.meta.url);
@@ -4022,7 +4123,7 @@ var __dirname2 = dirname(__filename2);
4022
4123
  var version = "0.1.0";
4023
4124
  try {
4024
4125
  const pkg = JSON.parse(
4025
- readFileSync12(join15(__dirname2, "../package.json"), "utf-8")
4126
+ readFileSync13(join15(__dirname2, "../package.json"), "utf-8")
4026
4127
  );
4027
4128
  version = pkg.version;
4028
4129
  } catch {
package/dist/lib.js CHANGED
@@ -137,7 +137,7 @@ function scanProjectInternal(cwd) {
137
137
  return {
138
138
  framework: "nextjs",
139
139
  buildCommand: pkg.scripts?.build ?? "npm run build",
140
- outputDirectory: ".next",
140
+ outputDirectory: "out",
141
141
  installCommand: detectPackageManager(cwd),
142
142
  serverType,
143
143
  deployTarget: "cloudflare-pages",
@@ -450,7 +450,7 @@ function warnIfConfigContainsSecrets(config, configPath) {
450
450
 
451
451
  // src/build/builder.ts
452
452
  import { execa } from "execa";
453
- import { existsSync as existsSync3 } from "fs";
453
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
454
454
  import { join as join3 } from "path";
455
455
  import chalk2 from "chalk";
456
456
 
@@ -508,7 +508,7 @@ var ui = {
508
508
  );
509
509
  console.log("");
510
510
  console.log(
511
- ` ${brand.gray("Your AI built it.")} ${brand.blue.bold("We'll ship it.")}`
511
+ ` \u{1F411} ${brand.gray("Your AI built it.")} ${brand.blue.bold("We'll ship it.")}`
512
512
  );
513
513
  console.log("");
514
514
  },
@@ -848,6 +848,31 @@ ${url}`);
848
848
  };
849
849
 
850
850
  // src/build/builder.ts
851
+ function patchNextConfig(cwd) {
852
+ const candidates = ["next.config.ts", "next.config.mjs", "next.config.js"];
853
+ for (const filename of candidates) {
854
+ const configPath = join3(cwd, filename);
855
+ if (existsSync3(configPath)) {
856
+ const content = readFileSync3(configPath, "utf-8");
857
+ if (content.includes("output")) return;
858
+ const patched = content.replace(
859
+ /const nextConfig[^=]*=\s*\{/,
860
+ (match) => match + "\n output: 'export',"
861
+ );
862
+ if (patched !== content) {
863
+ writeFileSync2(configPath, patched, "utf-8");
864
+ ui.dim(" \u2139\uFE0F Added output: 'export' to " + filename + " for static deployment");
865
+ return;
866
+ }
867
+ }
868
+ }
869
+ writeFileSync2(
870
+ join3(cwd, "next.config.js"),
871
+ "/** @type {import('next').NextConfig} */\nconst nextConfig = { output: 'export' };\nmodule.exports = nextConfig;\n",
872
+ "utf-8"
873
+ );
874
+ ui.dim(" \u2139\uFE0F Created next.config.js with output: 'export' for static deployment");
875
+ }
851
876
  async function buildProject(config, cwd = process.cwd()) {
852
877
  const start = Date.now();
853
878
  if (config.installCommand) {
@@ -855,8 +880,8 @@ async function buildProject(config, cwd = process.cwd()) {
855
880
  const outputLines = [];
856
881
  let lineCount = 0;
857
882
  try {
858
- const [installBin, ...installArgs] = config.installCommand.split(" ");
859
- const installProc = execa(installBin, installArgs, {
883
+ const installProc = execa(config.installCommand, {
884
+ shell: true,
860
885
  cwd,
861
886
  env: { ...process.env, CI: "true" },
862
887
  timeout: 5 * 60 * 1e3,
@@ -901,6 +926,9 @@ async function buildProject(config, cwd = process.cwd()) {
901
926
  }
902
927
  console.log("");
903
928
  }
929
+ if (config.framework === "nextjs") {
930
+ patchNextConfig(cwd);
931
+ }
904
932
  if (config.buildCommand) {
905
933
  const buildSpinner = ui.spinner("Building...");
906
934
  const outputLines = [];
@@ -916,8 +944,8 @@ async function buildProject(config, cwd = process.cwd()) {
916
944
  }
917
945
  }
918
946
  try {
919
- const [finalBin, ...finalArgs] = config.buildCommand.split(" ");
920
- const buildProc = execa(finalBin, finalArgs, {
947
+ const buildProc = execa(config.buildCommand, {
948
+ shell: true,
921
949
  cwd,
922
950
  env: buildEnv,
923
951
  timeout: 10 * 60 * 1e3,
@@ -996,11 +1024,11 @@ function parseErrorMessage(raw, outputLines) {
996
1024
  // src/deploy/cloudflare.ts
997
1025
  import axios from "axios";
998
1026
  import { createHash } from "crypto";
999
- import { readdirSync as readdirSync2, statSync as statSync2, readFileSync as readFileSync4 } from "fs";
1027
+ import { readdirSync as readdirSync2, statSync as statSync2, readFileSync as readFileSync5 } from "fs";
1000
1028
  import { join as join5, relative } from "path";
1001
1029
 
1002
1030
  // src/deploy/exclude.ts
1003
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
1031
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
1004
1032
  import { join as join4 } from "path";
1005
1033
  import chalk3 from "chalk";
1006
1034
  var DEFAULT_PATTERNS = [
@@ -1055,7 +1083,7 @@ function matchesIgnoreLine(relPath, line) {
1055
1083
  function loadIgnoreLines(filePath) {
1056
1084
  if (!existsSync4(filePath)) return [];
1057
1085
  try {
1058
- return readFileSync3(filePath, "utf-8").split("\n");
1086
+ return readFileSync4(filePath, "utf-8").split("\n");
1059
1087
  } catch {
1060
1088
  return [];
1061
1089
  }
@@ -1220,7 +1248,7 @@ var CloudflarePages = class {
1220
1248
  let totalBytes = 0;
1221
1249
  const fileMap = /* @__PURE__ */ new Map();
1222
1250
  for (const filePath of filePaths) {
1223
- const content = readFileSync4(filePath);
1251
+ const content = readFileSync5(filePath);
1224
1252
  const hash = createHash("sha256").update(content).digest("hex");
1225
1253
  const urlPath = "/" + relative(fullOutputPath, filePath).replace(/\\/g, "/");
1226
1254
  fileMap.set(urlPath, { hash, content });
@@ -1235,6 +1263,9 @@ var CloudflarePages = class {
1235
1263
  for (const [urlPath, { hash }] of fileMap) {
1236
1264
  manifest[urlPath] = hash;
1237
1265
  }
1266
+ if (Object.keys(manifest).length === 0) {
1267
+ throw new DeployError(`Output directory '${outputDir}' contains no files to deploy. Check your build output.`);
1268
+ }
1238
1269
  const deploySpinner = ui.spinner("Creating deployment...");
1239
1270
  let jwt;
1240
1271
  let requiredFiles;
@@ -1299,7 +1330,7 @@ var CloudflarePages = class {
1299
1330
  let totalBytes = 0;
1300
1331
  const fileMap = /* @__PURE__ */ new Map();
1301
1332
  for (const filePath of filePaths) {
1302
- const content = readFileSync4(filePath);
1333
+ const content = readFileSync5(filePath);
1303
1334
  const hash = createHash("sha256").update(content).digest("hex");
1304
1335
  const urlPath = "/" + relative(fullOutputPath, filePath).replace(/\\/g, "/");
1305
1336
  fileMap.set(urlPath, { hash, content });
@@ -1314,6 +1345,9 @@ var CloudflarePages = class {
1314
1345
  for (const [urlPath, { hash }] of fileMap) {
1315
1346
  manifest[urlPath] = hash;
1316
1347
  }
1348
+ if (Object.keys(manifest).length === 0) {
1349
+ throw new DeployError(`Output directory '${outputDir}' contains no files to deploy. Check your build output.`);
1350
+ }
1317
1351
  const deploySpinner = ui.spinner(`Creating preview deployment (branch: ${branch})...`);
1318
1352
  let jwt;
1319
1353
  let requiredFiles;
@@ -1488,7 +1522,7 @@ function sanitizeProjectName(name) {
1488
1522
  }
1489
1523
 
1490
1524
  // src/commands/fix.ts
1491
- import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync2, readdirSync as readdirSync3 } from "fs";
1525
+ import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync3, readdirSync as readdirSync3 } from "fs";
1492
1526
  import { join as join6 } from "path";
1493
1527
  import { execa as execa2 } from "execa";
1494
1528
  function findMissingModules(errorOutput) {
@@ -1565,12 +1599,12 @@ function applyTsConfigFixes(cwd, fixes) {
1565
1599
  const tsconfigPath = join6(cwd, "tsconfig.json");
1566
1600
  if (!existsSync5(tsconfigPath)) return false;
1567
1601
  try {
1568
- const content = readFileSync5(tsconfigPath, "utf-8");
1602
+ const content = readFileSync6(tsconfigPath, "utf-8");
1569
1603
  const stripped = content.replace(/\/\/[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
1570
1604
  const tsconfig = JSON.parse(stripped);
1571
1605
  if (!tsconfig.compilerOptions) tsconfig.compilerOptions = {};
1572
1606
  Object.assign(tsconfig.compilerOptions, fixes);
1573
- writeFileSync2(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
1607
+ writeFileSync3(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
1574
1608
  return true;
1575
1609
  } catch {
1576
1610
  return false;
@@ -1593,7 +1627,7 @@ function findMissingEnvVars(cwd) {
1593
1627
  scanDir(fullPath, depth + 1);
1594
1628
  } else if (/\.(ts|tsx|js|jsx|mjs|mts|vue|svelte|astro)$/.test(entry.name)) {
1595
1629
  try {
1596
- const content = readFileSync5(fullPath, "utf-8");
1630
+ const content = readFileSync6(fullPath, "utf-8");
1597
1631
  for (const pat of patterns) {
1598
1632
  pat.lastIndex = 0;
1599
1633
  let match;
@@ -1622,7 +1656,7 @@ function generateEnvExample(cwd, vars) {
1622
1656
  const envExamplePath = join6(cwd, ".env.example");
1623
1657
  if (existsSync5(envExamplePath)) return false;
1624
1658
  const content = vars.map((v) => `${v}=`).join("\n") + "\n";
1625
- writeFileSync2(envExamplePath, content, "utf-8");
1659
+ writeFileSync3(envExamplePath, content, "utf-8");
1626
1660
  return true;
1627
1661
  }
1628
1662
  function detectFrameworkConfigFixes(errorOutput, cwd, framework) {
@@ -1631,7 +1665,7 @@ function detectFrameworkConfigFixes(errorOutput, cwd, framework) {
1631
1665
  const nextConfigPath = existsSync5(join6(cwd, "next.config.mjs")) ? join6(cwd, "next.config.mjs") : existsSync5(join6(cwd, "next.config.js")) ? join6(cwd, "next.config.js") : null;
1632
1666
  if (nextConfigPath && errorOutput.includes("output")) {
1633
1667
  try {
1634
- const content = readFileSync5(nextConfigPath, "utf-8");
1668
+ const content = readFileSync6(nextConfigPath, "utf-8");
1635
1669
  if (!content.includes("output")) {
1636
1670
  fixes.push(`Add output: 'export' to ${nextConfigPath.split("/").pop()}`);
1637
1671
  }
@@ -1642,7 +1676,7 @@ function detectFrameworkConfigFixes(errorOutput, cwd, framework) {
1642
1676
  if ((framework === "vite-react" || framework === "vite-vue" || framework === "vite-svelte") && errorOutput.includes("plugin")) {
1643
1677
  const pkgPath = join6(cwd, "package.json");
1644
1678
  try {
1645
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
1679
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
1646
1680
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
1647
1681
  if (framework === "vite-react" && !deps["@vitejs/plugin-react"]) {
1648
1682
  fixes.push("Install @vitejs/plugin-react");
@@ -1715,7 +1749,7 @@ async function runFixHeuristics(errorOutput, cwd, config) {
1715
1749
  }
1716
1750
 
1717
1751
  // src/commands/env.ts
1718
- import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync as readdirSync4, writeFileSync as writeFileSync3 } from "fs";
1752
+ import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync4, writeFileSync as writeFileSync4 } from "fs";
1719
1753
  import { join as join7 } from "path";
1720
1754
  function scanSourceForEnvVars(cwd) {
1721
1755
  const found = /* @__PURE__ */ new Set();
@@ -1735,7 +1769,7 @@ function scanSourceForEnvVars(cwd) {
1735
1769
  scanDir(fullPath, depth + 1);
1736
1770
  } else if (/\.(ts|tsx|js|jsx|mjs|mts|vue|svelte|astro)$/.test(entry.name)) {
1737
1771
  try {
1738
- const content = readFileSync6(fullPath, "utf-8");
1772
+ const content = readFileSync7(fullPath, "utf-8");
1739
1773
  for (const pat of patterns) {
1740
1774
  pat.lastIndex = 0;
1741
1775
  let match;
@@ -1774,7 +1808,7 @@ function readEnvFile(cwd) {
1774
1808
  }
1775
1809
  function readFile2(path) {
1776
1810
  try {
1777
- return readFileSync6(path, "utf-8");
1811
+ return readFileSync7(path, "utf-8");
1778
1812
  } catch {
1779
1813
  return null;
1780
1814
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ship-em",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "One-command deployment for apps built by AI coding tools",
5
5
  "type": "module",
6
6
  "bin": {