ship-em 0.2.3 → 0.2.4
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 +1 -1
- package/dist/index.js +156 -50
- package/dist/lib.js +56 -22
- package/package.json +1 -1
package/README.md
CHANGED
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
|
|
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: "
|
|
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
|
|
808
|
-
|
|
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
|
|
869
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
1995
|
+
const content = readFileSync7(filePath, "utf-8");
|
|
1962
1996
|
if (content.includes("shipem-badge")) continue;
|
|
1963
1997
|
if (content.includes("</body>")) {
|
|
1964
|
-
|
|
1998
|
+
writeFileSync4(filePath, content.replace("</body>", `${BADGE_HTML}
|
|
1965
1999
|
</body>`), "utf-8");
|
|
1966
2000
|
injected++;
|
|
1967
2001
|
} else if (content.includes("</html>")) {
|
|
1968
|
-
|
|
2002
|
+
writeFileSync4(filePath, content.replace("</html>", `${BADGE_HTML}
|
|
1969
2003
|
</html>`), "utf-8");
|
|
1970
2004
|
injected++;
|
|
1971
2005
|
}
|
|
@@ -2299,6 +2333,10 @@ async function deployCommand(options) {
|
|
|
2299
2333
|
}
|
|
2300
2334
|
}
|
|
2301
2335
|
phases.push({ name: "Scan", durationMs: Date.now() - phaseStart });
|
|
2336
|
+
if (projectConfig.framework === "nextjs") {
|
|
2337
|
+
patchNextConfigForStaticExport(cwd);
|
|
2338
|
+
patchNextLayoutFonts(cwd);
|
|
2339
|
+
}
|
|
2302
2340
|
if (!options.skipBuild && !options.turbo) {
|
|
2303
2341
|
phaseStart = Date.now();
|
|
2304
2342
|
ui.section("Building...");
|
|
@@ -2554,13 +2592,13 @@ async function deployCommand(options) {
|
|
|
2554
2592
|
}, cwd);
|
|
2555
2593
|
const gitignorePath = join8(cwd, ".gitignore");
|
|
2556
2594
|
if (existsSync6(gitignorePath)) {
|
|
2557
|
-
const gitignoreContent =
|
|
2595
|
+
const gitignoreContent = readFileSync8(gitignorePath, "utf-8");
|
|
2558
2596
|
const lines = gitignoreContent.split("\n").map((l) => l.trim());
|
|
2559
2597
|
if (!lines.includes("shipem.json")) {
|
|
2560
2598
|
appendFileSync(gitignorePath, "\n# Shipem config\nshipem.json\n");
|
|
2561
2599
|
}
|
|
2562
2600
|
} else {
|
|
2563
|
-
|
|
2601
|
+
writeFileSync5(gitignorePath, "# Shipem config\nshipem.json\n");
|
|
2564
2602
|
}
|
|
2565
2603
|
ui.deployBoxEnhanced(
|
|
2566
2604
|
projectConfig.name,
|
|
@@ -2572,9 +2610,77 @@ async function deployCommand(options) {
|
|
|
2572
2610
|
isAnonymous
|
|
2573
2611
|
);
|
|
2574
2612
|
}
|
|
2613
|
+
function patchNextConfigForStaticExport(cwd) {
|
|
2614
|
+
const tsConfig = join8(cwd, "next.config.ts");
|
|
2615
|
+
const jsConfig = join8(cwd, "next.config.js");
|
|
2616
|
+
const mjsConfig = join8(cwd, "next.config.mjs");
|
|
2617
|
+
for (const configPath of [tsConfig, jsConfig, mjsConfig]) {
|
|
2618
|
+
if (existsSync6(configPath)) {
|
|
2619
|
+
const content = readFileSync8(configPath, "utf-8");
|
|
2620
|
+
if (/output\s*:\s*['"]export['"]/.test(content)) {
|
|
2621
|
+
return;
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
if (existsSync6(tsConfig)) {
|
|
2626
|
+
const content = readFileSync8(tsConfig, "utf-8");
|
|
2627
|
+
const patched = content.replace(
|
|
2628
|
+
/({[\s\S]*?)(})\s*(?:satisfies|as)\s/,
|
|
2629
|
+
"$1 output: 'export',\n$2 satisfies "
|
|
2630
|
+
);
|
|
2631
|
+
if (patched === content) {
|
|
2632
|
+
const fallback = content.replace(/(\{)/, "$1\n output: 'export',");
|
|
2633
|
+
writeFileSync5(tsConfig, fallback);
|
|
2634
|
+
} else {
|
|
2635
|
+
writeFileSync5(tsConfig, patched);
|
|
2636
|
+
}
|
|
2637
|
+
console.log(` \u2139\uFE0F Added output: 'export' to next.config.ts for static deployment`);
|
|
2638
|
+
} else if (existsSync6(mjsConfig)) {
|
|
2639
|
+
const content = readFileSync8(mjsConfig, "utf-8");
|
|
2640
|
+
const patched = content.replace(/(\{)/, "$1\n output: 'export',");
|
|
2641
|
+
writeFileSync5(mjsConfig, patched);
|
|
2642
|
+
console.log(` \u2139\uFE0F Added output: 'export' to next.config.mjs for static deployment`);
|
|
2643
|
+
} else if (existsSync6(jsConfig)) {
|
|
2644
|
+
const content = readFileSync8(jsConfig, "utf-8");
|
|
2645
|
+
const patched = content.replace(/(\{)/, "$1\n output: 'export',");
|
|
2646
|
+
writeFileSync5(jsConfig, patched);
|
|
2647
|
+
console.log(` \u2139\uFE0F Added output: 'export' to next.config.js for static deployment`);
|
|
2648
|
+
} else {
|
|
2649
|
+
writeFileSync5(jsConfig, `/** @type {import('next').NextConfig} */
|
|
2650
|
+
module.exports = { output: 'export' };
|
|
2651
|
+
`);
|
|
2652
|
+
console.log(` \u2139\uFE0F Created next.config.js with output: 'export' for static deployment`);
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
function patchNextLayoutFonts(cwd) {
|
|
2656
|
+
const layoutPaths = [
|
|
2657
|
+
join8(cwd, "app/layout.tsx"),
|
|
2658
|
+
join8(cwd, "app/layout.jsx"),
|
|
2659
|
+
join8(cwd, "src/app/layout.tsx"),
|
|
2660
|
+
join8(cwd, "src/app/layout.jsx")
|
|
2661
|
+
];
|
|
2662
|
+
for (const layoutPath of layoutPaths) {
|
|
2663
|
+
if (!existsSync6(layoutPath)) continue;
|
|
2664
|
+
const content = readFileSync8(layoutPath, "utf-8");
|
|
2665
|
+
if (!/import.*next\/font/.test(content)) continue;
|
|
2666
|
+
let patched = content;
|
|
2667
|
+
patched = patched.replace(/import\s+\{[^}]*\}\s+from\s+['"]next\/font\/[^'"]+['"];?\n?/g, "");
|
|
2668
|
+
patched = patched.replace(/const\s+\w+\s*=\s*\w+\(\s*\{[^}]*\}\s*\)\s*;?\n?/g, "");
|
|
2669
|
+
patched = patched.replace(/\$\{\w+\.className\}/g, "");
|
|
2670
|
+
patched = patched.replace(/className=\{`([^`]*)`\}/g, (match, inner) => {
|
|
2671
|
+
const cleaned = inner.trim();
|
|
2672
|
+
return cleaned ? `className={\`${cleaned}\`}` : "";
|
|
2673
|
+
});
|
|
2674
|
+
patched = patched.replace(/className=\{\w+\.className\}/g, "");
|
|
2675
|
+
if (patched !== content) {
|
|
2676
|
+
writeFileSync5(layoutPath, patched);
|
|
2677
|
+
console.log(` \u2139\uFE0F Removed next/font imports from ${layoutPath.replace(cwd + "/", "")} for static export`);
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2575
2681
|
|
|
2576
2682
|
// src/commands/env.ts
|
|
2577
|
-
import { existsSync as existsSync7, readFileSync as
|
|
2683
|
+
import { existsSync as existsSync7, readFileSync as readFileSync9, readdirSync as readdirSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
2578
2684
|
import { join as join9 } from "path";
|
|
2579
2685
|
var SERVICE_LINKS = {
|
|
2580
2686
|
SUPABASE_URL: { name: "Supabase", url: "https://app.supabase.com/project/_/settings/api" },
|
|
@@ -2614,7 +2720,7 @@ function scanSourceForEnvVars(cwd) {
|
|
|
2614
2720
|
scanDir(fullPath, depth + 1);
|
|
2615
2721
|
} else if (/\.(ts|tsx|js|jsx|mjs|mts|vue|svelte|astro)$/.test(entry.name)) {
|
|
2616
2722
|
try {
|
|
2617
|
-
const content =
|
|
2723
|
+
const content = readFileSync9(fullPath, "utf-8");
|
|
2618
2724
|
for (const pat of patterns) {
|
|
2619
2725
|
pat.lastIndex = 0;
|
|
2620
2726
|
let match;
|
|
@@ -2653,7 +2759,7 @@ function readEnvFile(cwd) {
|
|
|
2653
2759
|
}
|
|
2654
2760
|
function readFile2(path) {
|
|
2655
2761
|
try {
|
|
2656
|
-
return
|
|
2762
|
+
return readFileSync9(path, "utf-8");
|
|
2657
2763
|
} catch {
|
|
2658
2764
|
return null;
|
|
2659
2765
|
}
|
|
@@ -2718,7 +2824,7 @@ async function envCommand() {
|
|
|
2718
2824
|
const comment = service ? ` # ${service.name}` : "";
|
|
2719
2825
|
return `${v}=${comment}`;
|
|
2720
2826
|
}).join("\n") + "\n";
|
|
2721
|
-
|
|
2827
|
+
writeFileSync6(envExamplePath, content, "utf-8");
|
|
2722
2828
|
ui.success("Generated .env.example from detected variables");
|
|
2723
2829
|
console.log("");
|
|
2724
2830
|
} else if (existsSync7(envExamplePath)) {
|
|
@@ -2739,7 +2845,7 @@ async function envCommand() {
|
|
|
2739
2845
|
// src/commands/init.ts
|
|
2740
2846
|
import inquirer2 from "inquirer";
|
|
2741
2847
|
import { execa as execa3 } from "execa";
|
|
2742
|
-
import { existsSync as existsSync8, writeFileSync as
|
|
2848
|
+
import { existsSync as existsSync8, writeFileSync as writeFileSync7, mkdirSync } from "fs";
|
|
2743
2849
|
import { join as join10 } from "path";
|
|
2744
2850
|
import chalk6 from "chalk";
|
|
2745
2851
|
|
|
@@ -2933,7 +3039,7 @@ async function initCommand(options = {}) {
|
|
|
2933
3039
|
}
|
|
2934
3040
|
const projectDir = join10(process.cwd(), projectName);
|
|
2935
3041
|
if (existsSync8(projectDir)) {
|
|
2936
|
-
|
|
3042
|
+
writeFileSync7(
|
|
2937
3043
|
join10(projectDir, "shipem.json"),
|
|
2938
3044
|
JSON.stringify({ project: { name: projectName, framework: matched.value } }, null, 2) + "\n",
|
|
2939
3045
|
"utf-8"
|
|
@@ -3014,7 +3120,7 @@ async function scaffoldFromTemplate(tpl, options) {
|
|
|
3014
3120
|
}
|
|
3015
3121
|
const projectDir = join10(process.cwd(), projectName);
|
|
3016
3122
|
if (existsSync8(projectDir)) {
|
|
3017
|
-
|
|
3123
|
+
writeFileSync7(
|
|
3018
3124
|
join10(projectDir, "shipem.json"),
|
|
3019
3125
|
JSON.stringify({ project: { name: projectName, framework: tpl.framework } }, null, 2) + "\n",
|
|
3020
3126
|
"utf-8"
|
|
@@ -3030,7 +3136,7 @@ function createMinimalProject(projectName, description) {
|
|
|
3030
3136
|
const dir = join10(process.cwd(), projectName);
|
|
3031
3137
|
if (!existsSync8(dir)) {
|
|
3032
3138
|
mkdirSync(dir, { recursive: true });
|
|
3033
|
-
|
|
3139
|
+
writeFileSync7(join10(dir, "index.html"), `<!DOCTYPE html>
|
|
3034
3140
|
<html lang="en">
|
|
3035
3141
|
<head>
|
|
3036
3142
|
<meta charset="UTF-8">
|
|
@@ -3056,7 +3162,7 @@ function createMinimalProject(projectName, description) {
|
|
|
3056
3162
|
function createStaticPortfolio(projectName) {
|
|
3057
3163
|
const dir = join10(process.cwd(), projectName);
|
|
3058
3164
|
mkdirSync(dir, { recursive: true });
|
|
3059
|
-
|
|
3165
|
+
writeFileSync7(join10(dir, "index.html"), `<!DOCTYPE html>
|
|
3060
3166
|
<html lang="en">
|
|
3061
3167
|
<head>
|
|
3062
3168
|
<meta charset="UTF-8">
|
|
@@ -3093,7 +3199,7 @@ function createStaticPortfolio(projectName) {
|
|
|
3093
3199
|
</footer>
|
|
3094
3200
|
</body>
|
|
3095
3201
|
</html>`, "utf-8");
|
|
3096
|
-
|
|
3202
|
+
writeFileSync7(join10(dir, "style.css"), `* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
3097
3203
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0a0a0a; color: #ededed; min-height: 100vh; }
|
|
3098
3204
|
header { text-align: center; padding: 4rem 1rem 2rem; }
|
|
3099
3205
|
header h1 { font-size: 2.5rem; margin-bottom: 0.5rem; }
|
|
@@ -3532,7 +3638,7 @@ async function watchCommand() {
|
|
|
3532
3638
|
import chalk11 from "chalk";
|
|
3533
3639
|
|
|
3534
3640
|
// src/memory/index.ts
|
|
3535
|
-
import { readFileSync as
|
|
3641
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync2, existsSync as existsSync9 } from "fs";
|
|
3536
3642
|
import { join as join12 } from "path";
|
|
3537
3643
|
import { homedir } from "os";
|
|
3538
3644
|
var SHIPEM_HOME = join12(homedir(), ".shipem");
|
|
@@ -3548,14 +3654,14 @@ function ensureDir() {
|
|
|
3548
3654
|
function readJson2(path, fallback) {
|
|
3549
3655
|
try {
|
|
3550
3656
|
if (!existsSync9(path)) return fallback;
|
|
3551
|
-
return JSON.parse(
|
|
3657
|
+
return JSON.parse(readFileSync10(path, "utf-8"));
|
|
3552
3658
|
} catch {
|
|
3553
3659
|
return fallback;
|
|
3554
3660
|
}
|
|
3555
3661
|
}
|
|
3556
3662
|
function writeJson(path, data) {
|
|
3557
3663
|
ensureDir();
|
|
3558
|
-
|
|
3664
|
+
writeFileSync8(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
3559
3665
|
}
|
|
3560
3666
|
function getMemory() {
|
|
3561
3667
|
return readJson2(MEMORY_PATH, {
|
|
@@ -3650,7 +3756,7 @@ async function configCommand(action, key, value) {
|
|
|
3650
3756
|
// src/commands/monitor.ts
|
|
3651
3757
|
import axios5 from "axios";
|
|
3652
3758
|
import chalk12 from "chalk";
|
|
3653
|
-
import { writeFileSync as
|
|
3759
|
+
import { writeFileSync as writeFileSync9, readFileSync as readFileSync11, existsSync as existsSync10, unlinkSync } from "fs";
|
|
3654
3760
|
import { join as join13 } from "path";
|
|
3655
3761
|
import { tmpdir as tmpdir2 } from "os";
|
|
3656
3762
|
|
|
@@ -3729,7 +3835,7 @@ async function monitorCommand(options = {}) {
|
|
|
3729
3835
|
if (options.stop) {
|
|
3730
3836
|
if (existsSync10(PID_FILE)) {
|
|
3731
3837
|
try {
|
|
3732
|
-
const pid = parseInt(
|
|
3838
|
+
const pid = parseInt(readFileSync11(PID_FILE, "utf-8").trim(), 10);
|
|
3733
3839
|
process.kill(pid, "SIGTERM");
|
|
3734
3840
|
unlinkSync(PID_FILE);
|
|
3735
3841
|
ui.success("Monitor daemon stopped.");
|
|
@@ -3803,11 +3909,11 @@ async function monitorCommand(options = {}) {
|
|
|
3803
3909
|
});
|
|
3804
3910
|
}
|
|
3805
3911
|
function writePidFile() {
|
|
3806
|
-
|
|
3912
|
+
writeFileSync9(PID_FILE, String(process.pid), "utf-8");
|
|
3807
3913
|
}
|
|
3808
3914
|
|
|
3809
3915
|
// src/commands/hooks.ts
|
|
3810
|
-
import { existsSync as existsSync11, readFileSync as
|
|
3916
|
+
import { existsSync as existsSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync10, unlinkSync as unlinkSync2, chmodSync, mkdirSync as mkdirSync3 } from "fs";
|
|
3811
3917
|
import { join as join14 } from "path";
|
|
3812
3918
|
var HOOK_MARKER = "# shipem-auto-deploy";
|
|
3813
3919
|
var HOOK_CONTENT = `#!/bin/sh
|
|
@@ -3821,7 +3927,7 @@ function getHookPath(cwd) {
|
|
|
3821
3927
|
}
|
|
3822
3928
|
function isShipemHook(path) {
|
|
3823
3929
|
if (!existsSync11(path)) return false;
|
|
3824
|
-
const content =
|
|
3930
|
+
const content = readFileSync12(path, "utf-8");
|
|
3825
3931
|
return content.includes(HOOK_MARKER);
|
|
3826
3932
|
}
|
|
3827
3933
|
async function hooksCommand(action) {
|
|
@@ -3861,7 +3967,7 @@ async function hooksCommand(action) {
|
|
|
3861
3967
|
if (!existsSync11(hooksDir)) {
|
|
3862
3968
|
mkdirSync3(hooksDir, { recursive: true });
|
|
3863
3969
|
}
|
|
3864
|
-
|
|
3970
|
+
writeFileSync10(hookPath, HOOK_CONTENT, "utf-8");
|
|
3865
3971
|
chmodSync(hookPath, 493);
|
|
3866
3972
|
ui.success("Installed post-commit hook \u2192 auto-deploy on commit");
|
|
3867
3973
|
ui.dim(`Remove with: shipem hooks remove`);
|
|
@@ -4014,7 +4120,7 @@ async function previewCommand(options = {}) {
|
|
|
4014
4120
|
}
|
|
4015
4121
|
|
|
4016
4122
|
// src/index.ts
|
|
4017
|
-
import { readFileSync as
|
|
4123
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
4018
4124
|
import { fileURLToPath } from "url";
|
|
4019
4125
|
import { dirname, join as join15 } from "path";
|
|
4020
4126
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
@@ -4022,7 +4128,7 @@ var __dirname2 = dirname(__filename2);
|
|
|
4022
4128
|
var version = "0.1.0";
|
|
4023
4129
|
try {
|
|
4024
4130
|
const pkg = JSON.parse(
|
|
4025
|
-
|
|
4131
|
+
readFileSync13(join15(__dirname2, "../package.json"), "utf-8")
|
|
4026
4132
|
);
|
|
4027
4133
|
version = pkg.version;
|
|
4028
4134
|
} 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: "
|
|
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
|
|
859
|
-
|
|
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
|
|
920
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
1811
|
+
return readFileSync7(path, "utf-8");
|
|
1778
1812
|
} catch {
|
|
1779
1813
|
return null;
|
|
1780
1814
|
}
|