@bootmap/wpep-cli 1.0.7 → 1.0.9
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/package.json +1 -1
- package/src/commands/deploy.js +82 -30
package/package.json
CHANGED
package/src/commands/deploy.js
CHANGED
|
@@ -47,49 +47,80 @@ export default async function deployCommand(options) {
|
|
|
47
47
|
|
|
48
48
|
console.log(chalk.blue.bold(`\nDeploying to ${url} ...\n`));
|
|
49
49
|
|
|
50
|
+
// ── Auto-detect framework and patch config before building ──────────────
|
|
51
|
+
let nextConfigPath = null;
|
|
52
|
+
let nextConfigBackup = null;
|
|
53
|
+
|
|
54
|
+
const configCandidates = ['next.config.ts', 'next.config.mjs', 'next.config.js'];
|
|
55
|
+
for (const c of configCandidates) {
|
|
56
|
+
const p = path.join(process.cwd(), c);
|
|
57
|
+
if (fs.existsSync(p)) {
|
|
58
|
+
nextConfigPath = p;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// If it's a Next.js project, inject output: "export" automatically
|
|
64
|
+
if (nextConfigPath) {
|
|
65
|
+
const originalContent = fs.readFileSync(nextConfigPath, 'utf8');
|
|
66
|
+
|
|
67
|
+
if (!originalContent.includes('output:') && !originalContent.includes("output :")) {
|
|
68
|
+
nextConfigBackup = originalContent;
|
|
69
|
+
console.log(chalk.yellow('⚡ Detected Next.js project without static export configured.'));
|
|
70
|
+
console.log(chalk.yellow(' Auto-injecting { output: "export" } for this deployment...\n'));
|
|
71
|
+
|
|
72
|
+
// Inject output: "export" right after the opening brace of the config object
|
|
73
|
+
const patched = originalContent.replace(
|
|
74
|
+
/(\bconst\s+\w+\s*(?::\s*\w+)?\s*=\s*\{)/,
|
|
75
|
+
'$1\n output: "export",'
|
|
76
|
+
);
|
|
77
|
+
fs.writeFileSync(nextConfigPath, patched, 'utf8');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
50
81
|
if (options.build !== false) {
|
|
51
82
|
console.log(chalk.gray('Running npm run build...'));
|
|
52
83
|
try {
|
|
53
84
|
execSync('npm run build', { stdio: 'inherit' });
|
|
54
85
|
} catch (e) {
|
|
86
|
+
// Restore original config before throwing
|
|
87
|
+
if (nextConfigBackup && nextConfigPath) {
|
|
88
|
+
fs.writeFileSync(nextConfigPath, nextConfigBackup, 'utf8');
|
|
89
|
+
console.log(chalk.gray('Restored original next.config after failed build.'));
|
|
90
|
+
}
|
|
55
91
|
throw new Error('Build failed. See output above.');
|
|
56
92
|
}
|
|
57
93
|
}
|
|
58
94
|
|
|
95
|
+
// Restore the original next.config so git stays clean
|
|
96
|
+
if (nextConfigBackup && nextConfigPath) {
|
|
97
|
+
fs.writeFileSync(nextConfigPath, nextConfigBackup, 'utf8');
|
|
98
|
+
console.log(chalk.gray('Restored original next.config after build.\n'));
|
|
99
|
+
}
|
|
100
|
+
|
|
59
101
|
let outputDir = options.dir || 'wpep-build';
|
|
60
102
|
const targetDir = path.join(process.cwd(), 'wpep-build');
|
|
61
103
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
for (const d of possibleDirs) {
|
|
67
|
-
if (fs.existsSync(path.join(process.cwd(), d))) {
|
|
68
|
-
foundDir = d;
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
104
|
+
// Always clear previous wpep-build so we deploy fresh output
|
|
105
|
+
if (fs.existsSync(targetDir)) {
|
|
106
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
107
|
+
}
|
|
72
108
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
console.log(chalk.yellow.bold('--- How to fix your Next.js App ---'));
|
|
81
|
-
console.log(chalk.yellow('To actually make your deployment work, you MUST configure Next.js to export static HTML.'));
|
|
82
|
-
console.log(chalk.yellow('Open your next.config.ts (or .js) and add `output: "export"` inside your config object:\n'));
|
|
83
|
-
|
|
84
|
-
console.log(chalk.cyan('const nextConfig: NextConfig = {'));
|
|
85
|
-
console.log(chalk.green(' output: "export",'));
|
|
86
|
-
console.log(chalk.cyan(' // ... rest of your config'));
|
|
87
|
-
console.log(chalk.cyan('};\n'));
|
|
88
|
-
|
|
89
|
-
process.exit(1);
|
|
109
|
+
// Auto-detect build output and copy to wpep-build
|
|
110
|
+
const possibleDirs = ['out', 'dist', 'build'];
|
|
111
|
+
let foundDir = null;
|
|
112
|
+
for (const d of possibleDirs) {
|
|
113
|
+
if (fs.existsSync(path.join(process.cwd(), d))) {
|
|
114
|
+
foundDir = d;
|
|
115
|
+
break;
|
|
90
116
|
}
|
|
91
117
|
}
|
|
92
118
|
|
|
119
|
+
if (foundDir) {
|
|
120
|
+
console.log(chalk.gray(`Auto-detected build in "${foundDir}". Copying to "wpep-build"...`));
|
|
121
|
+
fs.cpSync(path.join(process.cwd(), foundDir), targetDir, { recursive: true });
|
|
122
|
+
}
|
|
123
|
+
|
|
93
124
|
outputDir = 'wpep-build';
|
|
94
125
|
const outDir = path.join(process.cwd(), outputDir);
|
|
95
126
|
|
|
@@ -110,6 +141,7 @@ export default async function deployCommand(options) {
|
|
|
110
141
|
spinner.text = `Creating deployment session for ${allFiles.length} files...`;
|
|
111
142
|
|
|
112
143
|
let deployId;
|
|
144
|
+
let remoteManifest = null;
|
|
113
145
|
try {
|
|
114
146
|
const createRes = await fetch(`${url}/wp-json/wpep/v1/deployments/create`, {
|
|
115
147
|
method: 'POST',
|
|
@@ -126,6 +158,7 @@ export default async function deployCommand(options) {
|
|
|
126
158
|
throw new Error(`Invalid response from server: ${JSON.stringify(createData)}`);
|
|
127
159
|
}
|
|
128
160
|
deployId = createData.deployment_id;
|
|
161
|
+
remoteManifest = createData.remote_manifest;
|
|
129
162
|
} catch (e) {
|
|
130
163
|
spinner.fail('Failed to create deployment session.');
|
|
131
164
|
throw e;
|
|
@@ -133,9 +166,27 @@ export default async function deployCommand(options) {
|
|
|
133
166
|
|
|
134
167
|
spinner.succeed(`Created deployment session: ${chalk.bold(deployId)}`);
|
|
135
168
|
|
|
136
|
-
|
|
169
|
+
// Build a set of unchanged files by comparing hashes with the remote manifest
|
|
170
|
+
const unchangedFiles = new Set();
|
|
171
|
+
if (remoteManifest && remoteManifest.files) {
|
|
172
|
+
for (const [filePath, localHash] of Object.entries(filesList)) {
|
|
173
|
+
const remoteMeta = remoteManifest.files[filePath];
|
|
174
|
+
if (remoteMeta && remoteMeta === localHash) {
|
|
175
|
+
unchangedFiles.add(filePath);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const changedFiles = allFiles.filter(f => !unchangedFiles.has(path.relative(outDir, f)));
|
|
181
|
+
const skippedCount = allFiles.length - changedFiles.length;
|
|
182
|
+
|
|
183
|
+
if (skippedCount > 0) {
|
|
184
|
+
console.log(chalk.green(`\n⚡ ${skippedCount} unchanged files will be copied server-side (not re-uploaded).`));
|
|
185
|
+
}
|
|
186
|
+
console.log(chalk.gray(`Uploading ${changedFiles.length} changed files...\n`));
|
|
187
|
+
|
|
137
188
|
let uploaded = 0;
|
|
138
|
-
for (const file of
|
|
189
|
+
for (const file of changedFiles) {
|
|
139
190
|
const relativePath = path.relative(outDir, file);
|
|
140
191
|
const data = fs.readFileSync(file);
|
|
141
192
|
const base64Data = data.toString('base64');
|
|
@@ -175,5 +226,6 @@ export default async function deployCommand(options) {
|
|
|
175
226
|
throw e;
|
|
176
227
|
}
|
|
177
228
|
|
|
178
|
-
console.log(chalk.green.bold(`\n🎉 Success! Your site is live at ${url}
|
|
229
|
+
console.log(chalk.green.bold(`\n🎉 Success! Your site is live at ${url}`));
|
|
230
|
+
console.log(chalk.gray(` ${uploaded} files uploaded, ${skippedCount} files copied from previous deployment.\n`));
|
|
179
231
|
}
|