@magentrix-corp/magentrix-cli 1.2.0 → 1.3.0
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 +282 -2
- package/actions/autopublish.js +9 -48
- package/actions/iris/buildStage.js +330 -0
- package/actions/iris/delete.js +211 -0
- package/actions/iris/dev.js +338 -0
- package/actions/iris/index.js +6 -0
- package/actions/iris/link.js +377 -0
- package/actions/iris/recover.js +228 -0
- package/actions/publish.js +258 -15
- package/actions/pull.js +520 -327
- package/actions/setup.js +62 -15
- package/bin/magentrix.js +43 -1
- package/package.json +2 -1
- package/utils/autopublishLock.js +77 -0
- package/utils/cli/helpers/compare.js +4 -5
- package/utils/cli/helpers/ensureApiKey.js +28 -22
- package/utils/cli/helpers/ensureInstanceUrl.js +35 -27
- package/utils/cli/writeRecords.js +13 -2
- package/utils/config.js +76 -0
- package/utils/iris/backup.js +201 -0
- package/utils/iris/builder.js +304 -0
- package/utils/iris/config-reader.js +296 -0
- package/utils/iris/deleteHelper.js +102 -0
- package/utils/iris/linker.js +490 -0
- package/utils/iris/validator.js +281 -0
- package/utils/iris/zipper.js +239 -0
- package/utils/logger.js +13 -5
- package/utils/magentrix/api/auth.js +45 -6
- package/utils/magentrix/api/iris.js +235 -0
- package/utils/permissionError.js +70 -0
- package/utils/progress.js +87 -1
- package/utils/updateFileBase.js +14 -2
- package/vars/global.js +1 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { createIrisZip } from './zipper.js';
|
|
4
|
+
import { extractIrisZip } from './zipper.js';
|
|
5
|
+
|
|
6
|
+
const BACKUP_DIR = '.magentrix/iris-backups';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Create a backup of an Iris app before deletion.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} appPath - Path to the Iris app folder (e.g., src/iris-apps/my-app)
|
|
12
|
+
* @param {object} metadata - Additional metadata to save
|
|
13
|
+
* @param {string} metadata.slug - App slug
|
|
14
|
+
* @param {string} metadata.appName - App display name
|
|
15
|
+
* @param {object} [metadata.linkedProject] - Linked Vue project info (if any)
|
|
16
|
+
* @returns {Promise<{success: boolean, backupPath: string | null, error: string | null}>}
|
|
17
|
+
*/
|
|
18
|
+
export async function backupIrisApp(appPath, metadata) {
|
|
19
|
+
try {
|
|
20
|
+
const { slug } = metadata;
|
|
21
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
22
|
+
const backupName = `${slug}-${timestamp}`;
|
|
23
|
+
const backupPath = path.join(process.cwd(), BACKUP_DIR, backupName);
|
|
24
|
+
|
|
25
|
+
// Create backup directory
|
|
26
|
+
fs.mkdirSync(backupPath, { recursive: true });
|
|
27
|
+
|
|
28
|
+
// Create ZIP of app files
|
|
29
|
+
const zipBuffer = await createIrisZip(appPath, slug);
|
|
30
|
+
const zipPath = path.join(backupPath, `${slug}.zip`);
|
|
31
|
+
fs.writeFileSync(zipPath, zipBuffer);
|
|
32
|
+
|
|
33
|
+
// Save metadata
|
|
34
|
+
const metadataPath = path.join(backupPath, 'metadata.json');
|
|
35
|
+
fs.writeFileSync(metadataPath, JSON.stringify({
|
|
36
|
+
...metadata,
|
|
37
|
+
deletedAt: new Date().toISOString(),
|
|
38
|
+
backupName,
|
|
39
|
+
originalPath: appPath
|
|
40
|
+
}, null, 2));
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
success: true,
|
|
44
|
+
backupPath,
|
|
45
|
+
error: null
|
|
46
|
+
};
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
backupPath: null,
|
|
51
|
+
error: error.message
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* List all available Iris app backups.
|
|
58
|
+
*
|
|
59
|
+
* @returns {Array<{
|
|
60
|
+
* backupName: string,
|
|
61
|
+
* slug: string,
|
|
62
|
+
* appName: string,
|
|
63
|
+
* deletedAt: string,
|
|
64
|
+
* linkedProject: object | null,
|
|
65
|
+
* backupPath: string
|
|
66
|
+
* }>}
|
|
67
|
+
*/
|
|
68
|
+
export function listBackups() {
|
|
69
|
+
const backupsDir = path.join(process.cwd(), BACKUP_DIR);
|
|
70
|
+
|
|
71
|
+
if (!fs.existsSync(backupsDir)) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const backups = [];
|
|
76
|
+
const entries = fs.readdirSync(backupsDir, { withFileTypes: true });
|
|
77
|
+
|
|
78
|
+
for (const entry of entries) {
|
|
79
|
+
if (!entry.isDirectory()) continue;
|
|
80
|
+
|
|
81
|
+
const backupPath = path.join(backupsDir, entry.name);
|
|
82
|
+
const metadataPath = path.join(backupPath, 'metadata.json');
|
|
83
|
+
|
|
84
|
+
if (!fs.existsSync(metadataPath)) continue;
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8'));
|
|
88
|
+
backups.push({
|
|
89
|
+
...metadata,
|
|
90
|
+
backupPath
|
|
91
|
+
});
|
|
92
|
+
} catch {
|
|
93
|
+
// Skip invalid backups
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Sort by deletedAt (newest first)
|
|
98
|
+
return backups.sort((a, b) =>
|
|
99
|
+
new Date(b.deletedAt).getTime() - new Date(a.deletedAt).getTime()
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Restore an Iris app from backup.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} backupPath - Path to the backup folder
|
|
107
|
+
* @param {object} options - Recovery options
|
|
108
|
+
* @param {boolean} options.restoreLink - Whether to restore the linked project (default: true)
|
|
109
|
+
* @param {boolean} options.restoreLocal - Whether to restore local files (default: true)
|
|
110
|
+
* @returns {Promise<{
|
|
111
|
+
* success: boolean,
|
|
112
|
+
* restoredFiles: boolean,
|
|
113
|
+
* restoredLink: boolean,
|
|
114
|
+
* linkedProjectPathExists: boolean,
|
|
115
|
+
* warnings: string[],
|
|
116
|
+
* error: string | null,
|
|
117
|
+
* isPermissionError: boolean,
|
|
118
|
+
* targetPath: string | null
|
|
119
|
+
* }>}
|
|
120
|
+
*/
|
|
121
|
+
export async function restoreIrisApp(backupPath, options = {}) {
|
|
122
|
+
const { restoreLink = true, restoreLocal = true } = options;
|
|
123
|
+
const result = {
|
|
124
|
+
success: false,
|
|
125
|
+
restoredFiles: false,
|
|
126
|
+
restoredLink: false,
|
|
127
|
+
linkedProjectPathExists: true,
|
|
128
|
+
warnings: [],
|
|
129
|
+
error: null,
|
|
130
|
+
isPermissionError: false,
|
|
131
|
+
targetPath: null
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
// Read metadata
|
|
136
|
+
const metadataPath = path.join(backupPath, 'metadata.json');
|
|
137
|
+
if (!fs.existsSync(metadataPath)) {
|
|
138
|
+
result.error = 'Backup metadata not found';
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8'));
|
|
143
|
+
const { slug, linkedProject } = metadata;
|
|
144
|
+
|
|
145
|
+
// Restore local files
|
|
146
|
+
if (restoreLocal) {
|
|
147
|
+
const zipPath = path.join(backupPath, `${slug}.zip`);
|
|
148
|
+
if (!fs.existsSync(zipPath)) {
|
|
149
|
+
result.error = 'Backup ZIP file not found';
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const zipBuffer = fs.readFileSync(zipPath);
|
|
154
|
+
const targetDir = path.join(process.cwd(), 'src', 'iris-apps');
|
|
155
|
+
const appTargetDir = path.join(targetDir, slug);
|
|
156
|
+
result.targetPath = appTargetDir;
|
|
157
|
+
|
|
158
|
+
// Ensure target directory exists
|
|
159
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
160
|
+
|
|
161
|
+
// Extract backup
|
|
162
|
+
await extractIrisZip(zipBuffer, targetDir);
|
|
163
|
+
result.restoredFiles = true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Restore linked project
|
|
167
|
+
if (restoreLink && linkedProject) {
|
|
168
|
+
// Check if the linked project path still exists
|
|
169
|
+
if (!fs.existsSync(linkedProject.path)) {
|
|
170
|
+
result.linkedProjectPathExists = false;
|
|
171
|
+
result.warnings.push(
|
|
172
|
+
`Linked Vue project no longer exists at: ${linkedProject.path}`
|
|
173
|
+
);
|
|
174
|
+
} else {
|
|
175
|
+
result.restoredLink = true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
result.success = true;
|
|
180
|
+
return result;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
result.error = error.message;
|
|
183
|
+
result.isPermissionError = error.code === 'EACCES' || error.code === 'EPERM';
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Delete a backup folder.
|
|
190
|
+
*
|
|
191
|
+
* @param {string} backupPath - Path to the backup folder
|
|
192
|
+
*/
|
|
193
|
+
export function deleteBackup(backupPath) {
|
|
194
|
+
try {
|
|
195
|
+
if (fs.existsSync(backupPath)) {
|
|
196
|
+
fs.rmSync(backupPath, { recursive: true, force: true });
|
|
197
|
+
}
|
|
198
|
+
} catch {
|
|
199
|
+
// Ignore errors
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync, mkdirSync, cpSync, rmSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { validateIrisBuild } from './validator.js';
|
|
5
|
+
import { updateLastBuild } from './linker.js';
|
|
6
|
+
import { EXPORT_ROOT, IRIS_APPS_DIR } from '../../vars/global.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build a Vue project using npm run build.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} projectPath - Path to the Vue project
|
|
12
|
+
* @param {Object} options - Build options
|
|
13
|
+
* @param {boolean} options.silent - Suppress build output
|
|
14
|
+
* @returns {Promise<{
|
|
15
|
+
* success: boolean,
|
|
16
|
+
* output: string,
|
|
17
|
+
* error: string | null,
|
|
18
|
+
* distPath: string | null
|
|
19
|
+
* }>}
|
|
20
|
+
*/
|
|
21
|
+
export async function buildVueProject(projectPath, options = {}) {
|
|
22
|
+
const { silent = false } = options;
|
|
23
|
+
const resolvedPath = resolve(projectPath);
|
|
24
|
+
|
|
25
|
+
// Check for package.json
|
|
26
|
+
if (!existsSync(join(resolvedPath, 'package.json'))) {
|
|
27
|
+
return {
|
|
28
|
+
success: false,
|
|
29
|
+
output: '',
|
|
30
|
+
error: `No package.json found in ${resolvedPath}`,
|
|
31
|
+
distPath: null
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Check for node_modules
|
|
36
|
+
if (!existsSync(join(resolvedPath, 'node_modules'))) {
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
output: '',
|
|
40
|
+
error: `No node_modules found. Run 'npm install' in ${resolvedPath} first.`,
|
|
41
|
+
distPath: null
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return new Promise((resolvePromise) => {
|
|
46
|
+
const output = [];
|
|
47
|
+
const errorOutput = [];
|
|
48
|
+
|
|
49
|
+
// Determine npm command based on platform
|
|
50
|
+
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
51
|
+
|
|
52
|
+
const child = spawn(npmCmd, ['run', 'build'], {
|
|
53
|
+
cwd: resolvedPath,
|
|
54
|
+
shell: false,
|
|
55
|
+
env: { ...process.env, FORCE_COLOR: '1' }
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
child.stdout.on('data', (data) => {
|
|
59
|
+
const text = data.toString();
|
|
60
|
+
output.push(text);
|
|
61
|
+
if (!silent) {
|
|
62
|
+
process.stdout.write(text);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
child.stderr.on('data', (data) => {
|
|
67
|
+
const text = data.toString();
|
|
68
|
+
errorOutput.push(text);
|
|
69
|
+
if (!silent) {
|
|
70
|
+
process.stderr.write(text);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
child.on('close', (code) => {
|
|
75
|
+
const fullOutput = output.join('');
|
|
76
|
+
const fullError = errorOutput.join('');
|
|
77
|
+
|
|
78
|
+
if (code !== 0) {
|
|
79
|
+
resolvePromise({
|
|
80
|
+
success: false,
|
|
81
|
+
output: fullOutput,
|
|
82
|
+
error: fullError || `Build process exited with code ${code}`,
|
|
83
|
+
distPath: null
|
|
84
|
+
});
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Find dist directory (Vue typically outputs to 'dist')
|
|
89
|
+
const possibleDistPaths = ['dist', 'build', 'output'];
|
|
90
|
+
let distPath = null;
|
|
91
|
+
|
|
92
|
+
for (const distDir of possibleDistPaths) {
|
|
93
|
+
const fullPath = join(resolvedPath, distDir);
|
|
94
|
+
if (existsSync(fullPath)) {
|
|
95
|
+
distPath = fullPath;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!distPath) {
|
|
101
|
+
resolvePromise({
|
|
102
|
+
success: false,
|
|
103
|
+
output: fullOutput,
|
|
104
|
+
error: 'Build completed but no dist/build directory found',
|
|
105
|
+
distPath: null
|
|
106
|
+
});
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
resolvePromise({
|
|
111
|
+
success: true,
|
|
112
|
+
output: fullOutput,
|
|
113
|
+
error: null,
|
|
114
|
+
distPath
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
child.on('error', (err) => {
|
|
119
|
+
resolvePromise({
|
|
120
|
+
success: false,
|
|
121
|
+
output: output.join(''),
|
|
122
|
+
error: `Failed to start build process: ${err.message}`,
|
|
123
|
+
distPath: null
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Stage build output to the CLI project's iris-apps directory.
|
|
131
|
+
*
|
|
132
|
+
* @param {string} distPath - Path to the build output (dist directory)
|
|
133
|
+
* @param {string} slug - The app slug (folder name)
|
|
134
|
+
* @param {string} cliProjectPath - Path to the CLI project root (defaults to CWD)
|
|
135
|
+
* @returns {{
|
|
136
|
+
* success: boolean,
|
|
137
|
+
* stagedPath: string | null,
|
|
138
|
+
* error: string | null,
|
|
139
|
+
* fileCount: number
|
|
140
|
+
* }}
|
|
141
|
+
*/
|
|
142
|
+
export function stageToCliProject(distPath, slug, cliProjectPath = process.cwd()) {
|
|
143
|
+
const resolvedDistPath = resolve(distPath);
|
|
144
|
+
const irisAppsDir = join(cliProjectPath, EXPORT_ROOT, IRIS_APPS_DIR);
|
|
145
|
+
const targetDir = join(irisAppsDir, slug);
|
|
146
|
+
|
|
147
|
+
// Validate dist path exists
|
|
148
|
+
if (!existsSync(resolvedDistPath)) {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
stagedPath: null,
|
|
152
|
+
error: `Dist path does not exist: ${resolvedDistPath}`,
|
|
153
|
+
fileCount: 0
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Validate build output
|
|
158
|
+
const validation = validateIrisBuild(resolvedDistPath);
|
|
159
|
+
if (!validation.valid) {
|
|
160
|
+
return {
|
|
161
|
+
success: false,
|
|
162
|
+
stagedPath: null,
|
|
163
|
+
error: `Invalid build output:\n${validation.errors.join('\n')}`,
|
|
164
|
+
fileCount: 0
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Ensure iris-apps directory exists
|
|
169
|
+
if (!existsSync(irisAppsDir)) {
|
|
170
|
+
mkdirSync(irisAppsDir, { recursive: true });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Remove existing app directory if it exists
|
|
174
|
+
if (existsSync(targetDir)) {
|
|
175
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Copy dist contents to target
|
|
179
|
+
try {
|
|
180
|
+
cpSync(resolvedDistPath, targetDir, { recursive: true });
|
|
181
|
+
} catch (err) {
|
|
182
|
+
return {
|
|
183
|
+
success: false,
|
|
184
|
+
stagedPath: null,
|
|
185
|
+
error: `Failed to copy build output: ${err.message}`,
|
|
186
|
+
fileCount: 0
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Count files copied
|
|
191
|
+
const fileCount = countFiles(targetDir);
|
|
192
|
+
|
|
193
|
+
// Update last build timestamp for linked project
|
|
194
|
+
updateLastBuild(slug);
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
success: true,
|
|
198
|
+
stagedPath: targetDir,
|
|
199
|
+
error: null,
|
|
200
|
+
fileCount
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Count files recursively in a directory.
|
|
206
|
+
*
|
|
207
|
+
* @param {string} dir - Directory to count files in
|
|
208
|
+
* @returns {number} - Number of files
|
|
209
|
+
*/
|
|
210
|
+
function countFiles(dir) {
|
|
211
|
+
if (!existsSync(dir)) return 0;
|
|
212
|
+
|
|
213
|
+
let count = 0;
|
|
214
|
+
const items = readdirSync(dir, { withFileTypes: true });
|
|
215
|
+
|
|
216
|
+
for (const item of items) {
|
|
217
|
+
if (item.isDirectory()) {
|
|
218
|
+
count += countFiles(join(dir, item.name));
|
|
219
|
+
} else {
|
|
220
|
+
count++;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return count;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Find an existing dist directory in a Vue project.
|
|
229
|
+
*
|
|
230
|
+
* @param {string} projectPath - Path to the Vue project
|
|
231
|
+
* @returns {string | null} - Path to dist directory or null if not found
|
|
232
|
+
*/
|
|
233
|
+
export function findDistDirectory(projectPath) {
|
|
234
|
+
const possibleDistPaths = ['dist', 'build', 'output'];
|
|
235
|
+
|
|
236
|
+
for (const distDir of possibleDistPaths) {
|
|
237
|
+
const fullPath = join(projectPath, distDir);
|
|
238
|
+
if (existsSync(fullPath)) {
|
|
239
|
+
return fullPath;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Format build error for display.
|
|
248
|
+
*
|
|
249
|
+
* @param {string} projectPath - Path to the Vue project
|
|
250
|
+
* @param {string} errorOutput - Error output from build
|
|
251
|
+
* @returns {string} - Formatted error message
|
|
252
|
+
*/
|
|
253
|
+
export function formatBuildError(projectPath, errorOutput) {
|
|
254
|
+
return `Build Failed
|
|
255
|
+
────────────────────────────────────────────────────
|
|
256
|
+
|
|
257
|
+
The Vue.js build process failed. This is a project issue, not a CLI issue.
|
|
258
|
+
|
|
259
|
+
Error output:
|
|
260
|
+
${errorOutput.split('\n').map(line => ` ${line}`).join('\n')}
|
|
261
|
+
|
|
262
|
+
To fix this:
|
|
263
|
+
1. Run 'npm run build' manually in your Vue project:
|
|
264
|
+
cd ${projectPath}
|
|
265
|
+
npm run build
|
|
266
|
+
|
|
267
|
+
2. Fix any compilation errors shown above
|
|
268
|
+
|
|
269
|
+
3. Run 'magentrix vue-build-stage' again
|
|
270
|
+
|
|
271
|
+
Alternatively, use --skip-build to stage an existing dist/ folder.`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Format validation error for display.
|
|
276
|
+
*
|
|
277
|
+
* @param {string} distPath - Path to the dist directory
|
|
278
|
+
* @param {string[]} errors - Validation errors
|
|
279
|
+
* @returns {string} - Formatted error message
|
|
280
|
+
*/
|
|
281
|
+
export function formatValidationError(distPath, errors) {
|
|
282
|
+
return `Invalid Build Output
|
|
283
|
+
────────────────────────────────────────────────────
|
|
284
|
+
|
|
285
|
+
The build output at ${distPath} is missing required files.
|
|
286
|
+
|
|
287
|
+
Missing:
|
|
288
|
+
${errors.map(e => ` ✗ ${e}`).join('\n')}
|
|
289
|
+
|
|
290
|
+
Required files for an Iris app:
|
|
291
|
+
- remoteEntry.js (Module Federation entry point)
|
|
292
|
+
- assets/hostInit*.js (Host initialization script)
|
|
293
|
+
- assets/main*.js (Main entry point)
|
|
294
|
+
- assets/index*.js (Index entry)
|
|
295
|
+
|
|
296
|
+
This usually means:
|
|
297
|
+
1. The project is not configured as a Module Federation remote
|
|
298
|
+
2. The build did not complete successfully
|
|
299
|
+
3. The dist directory contains an old or incorrect build
|
|
300
|
+
|
|
301
|
+
Try rebuilding the project:
|
|
302
|
+
cd ${distPath.replace('/dist', '')}
|
|
303
|
+
npm run build`;
|
|
304
|
+
}
|