ship-em 0.2.2 → 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 +165 -54
- package/dist/lib.js +65 -26
- 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
|
}
|
|
@@ -1599,8 +1627,7 @@ var CloudflarePages = class {
|
|
|
1599
1627
|
this.client = axios.create({
|
|
1600
1628
|
baseURL: CF_API_BASE,
|
|
1601
1629
|
headers: {
|
|
1602
|
-
Authorization: `Bearer ${apiToken}
|
|
1603
|
-
"Content-Type": "application/json"
|
|
1630
|
+
Authorization: `Bearer ${apiToken}`
|
|
1604
1631
|
},
|
|
1605
1632
|
timeout: CF_REQUEST_TIMEOUT_MS
|
|
1606
1633
|
});
|
|
@@ -1681,7 +1708,7 @@ var CloudflarePages = class {
|
|
|
1681
1708
|
let totalBytes = 0;
|
|
1682
1709
|
const fileMap = /* @__PURE__ */ new Map();
|
|
1683
1710
|
for (const filePath of filePaths) {
|
|
1684
|
-
const content =
|
|
1711
|
+
const content = readFileSync6(filePath);
|
|
1685
1712
|
const hash = createHash("sha256").update(content).digest("hex");
|
|
1686
1713
|
const urlPath = "/" + relative(fullOutputPath, filePath).replace(/\\/g, "/");
|
|
1687
1714
|
fileMap.set(urlPath, { hash, content });
|
|
@@ -1696,14 +1723,20 @@ var CloudflarePages = class {
|
|
|
1696
1723
|
for (const [urlPath, { hash }] of fileMap) {
|
|
1697
1724
|
manifest[urlPath] = hash;
|
|
1698
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
|
+
}
|
|
1699
1729
|
const deploySpinner = ui.spinner("Creating deployment...");
|
|
1700
1730
|
let jwt;
|
|
1701
1731
|
let requiredFiles;
|
|
1702
1732
|
let deployment;
|
|
1703
1733
|
try {
|
|
1734
|
+
const deployFormData = new FormData();
|
|
1735
|
+
deployFormData.append("manifest", JSON.stringify(manifest));
|
|
1736
|
+
deployFormData.append("branch", "main");
|
|
1704
1737
|
const res = await this.client.post(
|
|
1705
1738
|
`/accounts/${this.accountId}/pages/projects/${projectName}/deployments`,
|
|
1706
|
-
|
|
1739
|
+
deployFormData,
|
|
1707
1740
|
{ timeout: CF_REQUEST_TIMEOUT_MS }
|
|
1708
1741
|
);
|
|
1709
1742
|
if (!res.data.success) {
|
|
@@ -1757,7 +1790,7 @@ var CloudflarePages = class {
|
|
|
1757
1790
|
let totalBytes = 0;
|
|
1758
1791
|
const fileMap = /* @__PURE__ */ new Map();
|
|
1759
1792
|
for (const filePath of filePaths) {
|
|
1760
|
-
const content =
|
|
1793
|
+
const content = readFileSync6(filePath);
|
|
1761
1794
|
const hash = createHash("sha256").update(content).digest("hex");
|
|
1762
1795
|
const urlPath = "/" + relative(fullOutputPath, filePath).replace(/\\/g, "/");
|
|
1763
1796
|
fileMap.set(urlPath, { hash, content });
|
|
@@ -1772,14 +1805,20 @@ var CloudflarePages = class {
|
|
|
1772
1805
|
for (const [urlPath, { hash }] of fileMap) {
|
|
1773
1806
|
manifest[urlPath] = hash;
|
|
1774
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
|
+
}
|
|
1775
1811
|
const deploySpinner = ui.spinner(`Creating preview deployment (branch: ${branch})...`);
|
|
1776
1812
|
let jwt;
|
|
1777
1813
|
let requiredFiles;
|
|
1778
1814
|
let deployment;
|
|
1779
1815
|
try {
|
|
1816
|
+
const deployFormData = new FormData();
|
|
1817
|
+
deployFormData.append("manifest", JSON.stringify(manifest));
|
|
1818
|
+
deployFormData.append("branch", branch);
|
|
1780
1819
|
const res = await this.client.post(
|
|
1781
1820
|
`/accounts/${this.accountId}/pages/projects/${projectName}/deployments`,
|
|
1782
|
-
|
|
1821
|
+
deployFormData,
|
|
1783
1822
|
{ timeout: CF_REQUEST_TIMEOUT_MS }
|
|
1784
1823
|
);
|
|
1785
1824
|
if (!res.data.success) {
|
|
@@ -1943,7 +1982,7 @@ function sanitizeProjectName(name) {
|
|
|
1943
1982
|
}
|
|
1944
1983
|
|
|
1945
1984
|
// src/deploy/badge.ts
|
|
1946
|
-
import { readdirSync as readdirSync4, readFileSync as
|
|
1985
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
1947
1986
|
import { join as join7 } from "path";
|
|
1948
1987
|
var BADGE_HTML = `<!-- Shipped with Shipem -->
|
|
1949
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>`;
|
|
@@ -1953,14 +1992,14 @@ function injectBadge(outputDir) {
|
|
|
1953
1992
|
const files = readdirSync4(outputDir).filter((f) => f.endsWith(".html"));
|
|
1954
1993
|
for (const file of files) {
|
|
1955
1994
|
const filePath = join7(outputDir, file);
|
|
1956
|
-
const content =
|
|
1995
|
+
const content = readFileSync7(filePath, "utf-8");
|
|
1957
1996
|
if (content.includes("shipem-badge")) continue;
|
|
1958
1997
|
if (content.includes("</body>")) {
|
|
1959
|
-
|
|
1998
|
+
writeFileSync4(filePath, content.replace("</body>", `${BADGE_HTML}
|
|
1960
1999
|
</body>`), "utf-8");
|
|
1961
2000
|
injected++;
|
|
1962
2001
|
} else if (content.includes("</html>")) {
|
|
1963
|
-
|
|
2002
|
+
writeFileSync4(filePath, content.replace("</html>", `${BADGE_HTML}
|
|
1964
2003
|
</html>`), "utf-8");
|
|
1965
2004
|
injected++;
|
|
1966
2005
|
}
|
|
@@ -2294,6 +2333,10 @@ async function deployCommand(options) {
|
|
|
2294
2333
|
}
|
|
2295
2334
|
}
|
|
2296
2335
|
phases.push({ name: "Scan", durationMs: Date.now() - phaseStart });
|
|
2336
|
+
if (projectConfig.framework === "nextjs") {
|
|
2337
|
+
patchNextConfigForStaticExport(cwd);
|
|
2338
|
+
patchNextLayoutFonts(cwd);
|
|
2339
|
+
}
|
|
2297
2340
|
if (!options.skipBuild && !options.turbo) {
|
|
2298
2341
|
phaseStart = Date.now();
|
|
2299
2342
|
ui.section("Building...");
|
|
@@ -2549,13 +2592,13 @@ async function deployCommand(options) {
|
|
|
2549
2592
|
}, cwd);
|
|
2550
2593
|
const gitignorePath = join8(cwd, ".gitignore");
|
|
2551
2594
|
if (existsSync6(gitignorePath)) {
|
|
2552
|
-
const gitignoreContent =
|
|
2595
|
+
const gitignoreContent = readFileSync8(gitignorePath, "utf-8");
|
|
2553
2596
|
const lines = gitignoreContent.split("\n").map((l) => l.trim());
|
|
2554
2597
|
if (!lines.includes("shipem.json")) {
|
|
2555
2598
|
appendFileSync(gitignorePath, "\n# Shipem config\nshipem.json\n");
|
|
2556
2599
|
}
|
|
2557
2600
|
} else {
|
|
2558
|
-
|
|
2601
|
+
writeFileSync5(gitignorePath, "# Shipem config\nshipem.json\n");
|
|
2559
2602
|
}
|
|
2560
2603
|
ui.deployBoxEnhanced(
|
|
2561
2604
|
projectConfig.name,
|
|
@@ -2567,9 +2610,77 @@ async function deployCommand(options) {
|
|
|
2567
2610
|
isAnonymous
|
|
2568
2611
|
);
|
|
2569
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
|
+
}
|
|
2570
2681
|
|
|
2571
2682
|
// src/commands/env.ts
|
|
2572
|
-
import { existsSync as existsSync7, readFileSync as
|
|
2683
|
+
import { existsSync as existsSync7, readFileSync as readFileSync9, readdirSync as readdirSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
2573
2684
|
import { join as join9 } from "path";
|
|
2574
2685
|
var SERVICE_LINKS = {
|
|
2575
2686
|
SUPABASE_URL: { name: "Supabase", url: "https://app.supabase.com/project/_/settings/api" },
|
|
@@ -2609,7 +2720,7 @@ function scanSourceForEnvVars(cwd) {
|
|
|
2609
2720
|
scanDir(fullPath, depth + 1);
|
|
2610
2721
|
} else if (/\.(ts|tsx|js|jsx|mjs|mts|vue|svelte|astro)$/.test(entry.name)) {
|
|
2611
2722
|
try {
|
|
2612
|
-
const content =
|
|
2723
|
+
const content = readFileSync9(fullPath, "utf-8");
|
|
2613
2724
|
for (const pat of patterns) {
|
|
2614
2725
|
pat.lastIndex = 0;
|
|
2615
2726
|
let match;
|
|
@@ -2648,7 +2759,7 @@ function readEnvFile(cwd) {
|
|
|
2648
2759
|
}
|
|
2649
2760
|
function readFile2(path) {
|
|
2650
2761
|
try {
|
|
2651
|
-
return
|
|
2762
|
+
return readFileSync9(path, "utf-8");
|
|
2652
2763
|
} catch {
|
|
2653
2764
|
return null;
|
|
2654
2765
|
}
|
|
@@ -2713,7 +2824,7 @@ async function envCommand() {
|
|
|
2713
2824
|
const comment = service ? ` # ${service.name}` : "";
|
|
2714
2825
|
return `${v}=${comment}`;
|
|
2715
2826
|
}).join("\n") + "\n";
|
|
2716
|
-
|
|
2827
|
+
writeFileSync6(envExamplePath, content, "utf-8");
|
|
2717
2828
|
ui.success("Generated .env.example from detected variables");
|
|
2718
2829
|
console.log("");
|
|
2719
2830
|
} else if (existsSync7(envExamplePath)) {
|
|
@@ -2734,7 +2845,7 @@ async function envCommand() {
|
|
|
2734
2845
|
// src/commands/init.ts
|
|
2735
2846
|
import inquirer2 from "inquirer";
|
|
2736
2847
|
import { execa as execa3 } from "execa";
|
|
2737
|
-
import { existsSync as existsSync8, writeFileSync as
|
|
2848
|
+
import { existsSync as existsSync8, writeFileSync as writeFileSync7, mkdirSync } from "fs";
|
|
2738
2849
|
import { join as join10 } from "path";
|
|
2739
2850
|
import chalk6 from "chalk";
|
|
2740
2851
|
|
|
@@ -2928,7 +3039,7 @@ async function initCommand(options = {}) {
|
|
|
2928
3039
|
}
|
|
2929
3040
|
const projectDir = join10(process.cwd(), projectName);
|
|
2930
3041
|
if (existsSync8(projectDir)) {
|
|
2931
|
-
|
|
3042
|
+
writeFileSync7(
|
|
2932
3043
|
join10(projectDir, "shipem.json"),
|
|
2933
3044
|
JSON.stringify({ project: { name: projectName, framework: matched.value } }, null, 2) + "\n",
|
|
2934
3045
|
"utf-8"
|
|
@@ -3009,7 +3120,7 @@ async function scaffoldFromTemplate(tpl, options) {
|
|
|
3009
3120
|
}
|
|
3010
3121
|
const projectDir = join10(process.cwd(), projectName);
|
|
3011
3122
|
if (existsSync8(projectDir)) {
|
|
3012
|
-
|
|
3123
|
+
writeFileSync7(
|
|
3013
3124
|
join10(projectDir, "shipem.json"),
|
|
3014
3125
|
JSON.stringify({ project: { name: projectName, framework: tpl.framework } }, null, 2) + "\n",
|
|
3015
3126
|
"utf-8"
|
|
@@ -3025,7 +3136,7 @@ function createMinimalProject(projectName, description) {
|
|
|
3025
3136
|
const dir = join10(process.cwd(), projectName);
|
|
3026
3137
|
if (!existsSync8(dir)) {
|
|
3027
3138
|
mkdirSync(dir, { recursive: true });
|
|
3028
|
-
|
|
3139
|
+
writeFileSync7(join10(dir, "index.html"), `<!DOCTYPE html>
|
|
3029
3140
|
<html lang="en">
|
|
3030
3141
|
<head>
|
|
3031
3142
|
<meta charset="UTF-8">
|
|
@@ -3051,7 +3162,7 @@ function createMinimalProject(projectName, description) {
|
|
|
3051
3162
|
function createStaticPortfolio(projectName) {
|
|
3052
3163
|
const dir = join10(process.cwd(), projectName);
|
|
3053
3164
|
mkdirSync(dir, { recursive: true });
|
|
3054
|
-
|
|
3165
|
+
writeFileSync7(join10(dir, "index.html"), `<!DOCTYPE html>
|
|
3055
3166
|
<html lang="en">
|
|
3056
3167
|
<head>
|
|
3057
3168
|
<meta charset="UTF-8">
|
|
@@ -3088,7 +3199,7 @@ function createStaticPortfolio(projectName) {
|
|
|
3088
3199
|
</footer>
|
|
3089
3200
|
</body>
|
|
3090
3201
|
</html>`, "utf-8");
|
|
3091
|
-
|
|
3202
|
+
writeFileSync7(join10(dir, "style.css"), `* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
3092
3203
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0a0a0a; color: #ededed; min-height: 100vh; }
|
|
3093
3204
|
header { text-align: center; padding: 4rem 1rem 2rem; }
|
|
3094
3205
|
header h1 { font-size: 2.5rem; margin-bottom: 0.5rem; }
|
|
@@ -3527,7 +3638,7 @@ async function watchCommand() {
|
|
|
3527
3638
|
import chalk11 from "chalk";
|
|
3528
3639
|
|
|
3529
3640
|
// src/memory/index.ts
|
|
3530
|
-
import { readFileSync as
|
|
3641
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync2, existsSync as existsSync9 } from "fs";
|
|
3531
3642
|
import { join as join12 } from "path";
|
|
3532
3643
|
import { homedir } from "os";
|
|
3533
3644
|
var SHIPEM_HOME = join12(homedir(), ".shipem");
|
|
@@ -3543,14 +3654,14 @@ function ensureDir() {
|
|
|
3543
3654
|
function readJson2(path, fallback) {
|
|
3544
3655
|
try {
|
|
3545
3656
|
if (!existsSync9(path)) return fallback;
|
|
3546
|
-
return JSON.parse(
|
|
3657
|
+
return JSON.parse(readFileSync10(path, "utf-8"));
|
|
3547
3658
|
} catch {
|
|
3548
3659
|
return fallback;
|
|
3549
3660
|
}
|
|
3550
3661
|
}
|
|
3551
3662
|
function writeJson(path, data) {
|
|
3552
3663
|
ensureDir();
|
|
3553
|
-
|
|
3664
|
+
writeFileSync8(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
3554
3665
|
}
|
|
3555
3666
|
function getMemory() {
|
|
3556
3667
|
return readJson2(MEMORY_PATH, {
|
|
@@ -3645,7 +3756,7 @@ async function configCommand(action, key, value) {
|
|
|
3645
3756
|
// src/commands/monitor.ts
|
|
3646
3757
|
import axios5 from "axios";
|
|
3647
3758
|
import chalk12 from "chalk";
|
|
3648
|
-
import { writeFileSync as
|
|
3759
|
+
import { writeFileSync as writeFileSync9, readFileSync as readFileSync11, existsSync as existsSync10, unlinkSync } from "fs";
|
|
3649
3760
|
import { join as join13 } from "path";
|
|
3650
3761
|
import { tmpdir as tmpdir2 } from "os";
|
|
3651
3762
|
|
|
@@ -3724,7 +3835,7 @@ async function monitorCommand(options = {}) {
|
|
|
3724
3835
|
if (options.stop) {
|
|
3725
3836
|
if (existsSync10(PID_FILE)) {
|
|
3726
3837
|
try {
|
|
3727
|
-
const pid = parseInt(
|
|
3838
|
+
const pid = parseInt(readFileSync11(PID_FILE, "utf-8").trim(), 10);
|
|
3728
3839
|
process.kill(pid, "SIGTERM");
|
|
3729
3840
|
unlinkSync(PID_FILE);
|
|
3730
3841
|
ui.success("Monitor daemon stopped.");
|
|
@@ -3798,11 +3909,11 @@ async function monitorCommand(options = {}) {
|
|
|
3798
3909
|
});
|
|
3799
3910
|
}
|
|
3800
3911
|
function writePidFile() {
|
|
3801
|
-
|
|
3912
|
+
writeFileSync9(PID_FILE, String(process.pid), "utf-8");
|
|
3802
3913
|
}
|
|
3803
3914
|
|
|
3804
3915
|
// src/commands/hooks.ts
|
|
3805
|
-
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";
|
|
3806
3917
|
import { join as join14 } from "path";
|
|
3807
3918
|
var HOOK_MARKER = "# shipem-auto-deploy";
|
|
3808
3919
|
var HOOK_CONTENT = `#!/bin/sh
|
|
@@ -3816,7 +3927,7 @@ function getHookPath(cwd) {
|
|
|
3816
3927
|
}
|
|
3817
3928
|
function isShipemHook(path) {
|
|
3818
3929
|
if (!existsSync11(path)) return false;
|
|
3819
|
-
const content =
|
|
3930
|
+
const content = readFileSync12(path, "utf-8");
|
|
3820
3931
|
return content.includes(HOOK_MARKER);
|
|
3821
3932
|
}
|
|
3822
3933
|
async function hooksCommand(action) {
|
|
@@ -3856,7 +3967,7 @@ async function hooksCommand(action) {
|
|
|
3856
3967
|
if (!existsSync11(hooksDir)) {
|
|
3857
3968
|
mkdirSync3(hooksDir, { recursive: true });
|
|
3858
3969
|
}
|
|
3859
|
-
|
|
3970
|
+
writeFileSync10(hookPath, HOOK_CONTENT, "utf-8");
|
|
3860
3971
|
chmodSync(hookPath, 493);
|
|
3861
3972
|
ui.success("Installed post-commit hook \u2192 auto-deploy on commit");
|
|
3862
3973
|
ui.dim(`Remove with: shipem hooks remove`);
|
|
@@ -4009,7 +4120,7 @@ async function previewCommand(options = {}) {
|
|
|
4009
4120
|
}
|
|
4010
4121
|
|
|
4011
4122
|
// src/index.ts
|
|
4012
|
-
import { readFileSync as
|
|
4123
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
4013
4124
|
import { fileURLToPath } from "url";
|
|
4014
4125
|
import { dirname, join as join15 } from "path";
|
|
4015
4126
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
@@ -4017,7 +4128,7 @@ var __dirname2 = dirname(__filename2);
|
|
|
4017
4128
|
var version = "0.1.0";
|
|
4018
4129
|
try {
|
|
4019
4130
|
const pkg = JSON.parse(
|
|
4020
|
-
|
|
4131
|
+
readFileSync13(join15(__dirname2, "../package.json"), "utf-8")
|
|
4021
4132
|
);
|
|
4022
4133
|
version = pkg.version;
|
|
4023
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
|
}
|
|
@@ -1139,8 +1167,7 @@ var CloudflarePages = class {
|
|
|
1139
1167
|
this.client = axios.create({
|
|
1140
1168
|
baseURL: CF_API_BASE,
|
|
1141
1169
|
headers: {
|
|
1142
|
-
Authorization: `Bearer ${apiToken}
|
|
1143
|
-
"Content-Type": "application/json"
|
|
1170
|
+
Authorization: `Bearer ${apiToken}`
|
|
1144
1171
|
},
|
|
1145
1172
|
timeout: CF_REQUEST_TIMEOUT_MS
|
|
1146
1173
|
});
|
|
@@ -1221,7 +1248,7 @@ var CloudflarePages = class {
|
|
|
1221
1248
|
let totalBytes = 0;
|
|
1222
1249
|
const fileMap = /* @__PURE__ */ new Map();
|
|
1223
1250
|
for (const filePath of filePaths) {
|
|
1224
|
-
const content =
|
|
1251
|
+
const content = readFileSync5(filePath);
|
|
1225
1252
|
const hash = createHash("sha256").update(content).digest("hex");
|
|
1226
1253
|
const urlPath = "/" + relative(fullOutputPath, filePath).replace(/\\/g, "/");
|
|
1227
1254
|
fileMap.set(urlPath, { hash, content });
|
|
@@ -1236,14 +1263,20 @@ var CloudflarePages = class {
|
|
|
1236
1263
|
for (const [urlPath, { hash }] of fileMap) {
|
|
1237
1264
|
manifest[urlPath] = hash;
|
|
1238
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
|
+
}
|
|
1239
1269
|
const deploySpinner = ui.spinner("Creating deployment...");
|
|
1240
1270
|
let jwt;
|
|
1241
1271
|
let requiredFiles;
|
|
1242
1272
|
let deployment;
|
|
1243
1273
|
try {
|
|
1274
|
+
const deployFormData = new FormData();
|
|
1275
|
+
deployFormData.append("manifest", JSON.stringify(manifest));
|
|
1276
|
+
deployFormData.append("branch", "main");
|
|
1244
1277
|
const res = await this.client.post(
|
|
1245
1278
|
`/accounts/${this.accountId}/pages/projects/${projectName}/deployments`,
|
|
1246
|
-
|
|
1279
|
+
deployFormData,
|
|
1247
1280
|
{ timeout: CF_REQUEST_TIMEOUT_MS }
|
|
1248
1281
|
);
|
|
1249
1282
|
if (!res.data.success) {
|
|
@@ -1297,7 +1330,7 @@ var CloudflarePages = class {
|
|
|
1297
1330
|
let totalBytes = 0;
|
|
1298
1331
|
const fileMap = /* @__PURE__ */ new Map();
|
|
1299
1332
|
for (const filePath of filePaths) {
|
|
1300
|
-
const content =
|
|
1333
|
+
const content = readFileSync5(filePath);
|
|
1301
1334
|
const hash = createHash("sha256").update(content).digest("hex");
|
|
1302
1335
|
const urlPath = "/" + relative(fullOutputPath, filePath).replace(/\\/g, "/");
|
|
1303
1336
|
fileMap.set(urlPath, { hash, content });
|
|
@@ -1312,14 +1345,20 @@ var CloudflarePages = class {
|
|
|
1312
1345
|
for (const [urlPath, { hash }] of fileMap) {
|
|
1313
1346
|
manifest[urlPath] = hash;
|
|
1314
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
|
+
}
|
|
1315
1351
|
const deploySpinner = ui.spinner(`Creating preview deployment (branch: ${branch})...`);
|
|
1316
1352
|
let jwt;
|
|
1317
1353
|
let requiredFiles;
|
|
1318
1354
|
let deployment;
|
|
1319
1355
|
try {
|
|
1356
|
+
const deployFormData = new FormData();
|
|
1357
|
+
deployFormData.append("manifest", JSON.stringify(manifest));
|
|
1358
|
+
deployFormData.append("branch", branch);
|
|
1320
1359
|
const res = await this.client.post(
|
|
1321
1360
|
`/accounts/${this.accountId}/pages/projects/${projectName}/deployments`,
|
|
1322
|
-
|
|
1361
|
+
deployFormData,
|
|
1323
1362
|
{ timeout: CF_REQUEST_TIMEOUT_MS }
|
|
1324
1363
|
);
|
|
1325
1364
|
if (!res.data.success) {
|
|
@@ -1483,7 +1522,7 @@ function sanitizeProjectName(name) {
|
|
|
1483
1522
|
}
|
|
1484
1523
|
|
|
1485
1524
|
// src/commands/fix.ts
|
|
1486
|
-
import { existsSync as existsSync5, readFileSync as
|
|
1525
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync3, readdirSync as readdirSync3 } from "fs";
|
|
1487
1526
|
import { join as join6 } from "path";
|
|
1488
1527
|
import { execa as execa2 } from "execa";
|
|
1489
1528
|
function findMissingModules(errorOutput) {
|
|
@@ -1560,12 +1599,12 @@ function applyTsConfigFixes(cwd, fixes) {
|
|
|
1560
1599
|
const tsconfigPath = join6(cwd, "tsconfig.json");
|
|
1561
1600
|
if (!existsSync5(tsconfigPath)) return false;
|
|
1562
1601
|
try {
|
|
1563
|
-
const content =
|
|
1602
|
+
const content = readFileSync6(tsconfigPath, "utf-8");
|
|
1564
1603
|
const stripped = content.replace(/\/\/[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
1565
1604
|
const tsconfig = JSON.parse(stripped);
|
|
1566
1605
|
if (!tsconfig.compilerOptions) tsconfig.compilerOptions = {};
|
|
1567
1606
|
Object.assign(tsconfig.compilerOptions, fixes);
|
|
1568
|
-
|
|
1607
|
+
writeFileSync3(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
|
|
1569
1608
|
return true;
|
|
1570
1609
|
} catch {
|
|
1571
1610
|
return false;
|
|
@@ -1588,7 +1627,7 @@ function findMissingEnvVars(cwd) {
|
|
|
1588
1627
|
scanDir(fullPath, depth + 1);
|
|
1589
1628
|
} else if (/\.(ts|tsx|js|jsx|mjs|mts|vue|svelte|astro)$/.test(entry.name)) {
|
|
1590
1629
|
try {
|
|
1591
|
-
const content =
|
|
1630
|
+
const content = readFileSync6(fullPath, "utf-8");
|
|
1592
1631
|
for (const pat of patterns) {
|
|
1593
1632
|
pat.lastIndex = 0;
|
|
1594
1633
|
let match;
|
|
@@ -1617,7 +1656,7 @@ function generateEnvExample(cwd, vars) {
|
|
|
1617
1656
|
const envExamplePath = join6(cwd, ".env.example");
|
|
1618
1657
|
if (existsSync5(envExamplePath)) return false;
|
|
1619
1658
|
const content = vars.map((v) => `${v}=`).join("\n") + "\n";
|
|
1620
|
-
|
|
1659
|
+
writeFileSync3(envExamplePath, content, "utf-8");
|
|
1621
1660
|
return true;
|
|
1622
1661
|
}
|
|
1623
1662
|
function detectFrameworkConfigFixes(errorOutput, cwd, framework) {
|
|
@@ -1626,7 +1665,7 @@ function detectFrameworkConfigFixes(errorOutput, cwd, framework) {
|
|
|
1626
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;
|
|
1627
1666
|
if (nextConfigPath && errorOutput.includes("output")) {
|
|
1628
1667
|
try {
|
|
1629
|
-
const content =
|
|
1668
|
+
const content = readFileSync6(nextConfigPath, "utf-8");
|
|
1630
1669
|
if (!content.includes("output")) {
|
|
1631
1670
|
fixes.push(`Add output: 'export' to ${nextConfigPath.split("/").pop()}`);
|
|
1632
1671
|
}
|
|
@@ -1637,7 +1676,7 @@ function detectFrameworkConfigFixes(errorOutput, cwd, framework) {
|
|
|
1637
1676
|
if ((framework === "vite-react" || framework === "vite-vue" || framework === "vite-svelte") && errorOutput.includes("plugin")) {
|
|
1638
1677
|
const pkgPath = join6(cwd, "package.json");
|
|
1639
1678
|
try {
|
|
1640
|
-
const pkg = JSON.parse(
|
|
1679
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
1641
1680
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1642
1681
|
if (framework === "vite-react" && !deps["@vitejs/plugin-react"]) {
|
|
1643
1682
|
fixes.push("Install @vitejs/plugin-react");
|
|
@@ -1710,7 +1749,7 @@ async function runFixHeuristics(errorOutput, cwd, config) {
|
|
|
1710
1749
|
}
|
|
1711
1750
|
|
|
1712
1751
|
// src/commands/env.ts
|
|
1713
|
-
import { existsSync as existsSync6, readFileSync as
|
|
1752
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
1714
1753
|
import { join as join7 } from "path";
|
|
1715
1754
|
function scanSourceForEnvVars(cwd) {
|
|
1716
1755
|
const found = /* @__PURE__ */ new Set();
|
|
@@ -1730,7 +1769,7 @@ function scanSourceForEnvVars(cwd) {
|
|
|
1730
1769
|
scanDir(fullPath, depth + 1);
|
|
1731
1770
|
} else if (/\.(ts|tsx|js|jsx|mjs|mts|vue|svelte|astro)$/.test(entry.name)) {
|
|
1732
1771
|
try {
|
|
1733
|
-
const content =
|
|
1772
|
+
const content = readFileSync7(fullPath, "utf-8");
|
|
1734
1773
|
for (const pat of patterns) {
|
|
1735
1774
|
pat.lastIndex = 0;
|
|
1736
1775
|
let match;
|
|
@@ -1769,7 +1808,7 @@ function readEnvFile(cwd) {
|
|
|
1769
1808
|
}
|
|
1770
1809
|
function readFile2(path) {
|
|
1771
1810
|
try {
|
|
1772
|
-
return
|
|
1811
|
+
return readFileSync7(path, "utf-8");
|
|
1773
1812
|
} catch {
|
|
1774
1813
|
return null;
|
|
1775
1814
|
}
|