zephyr-agent
Version:
Zephyr plugin agent
139 lines (138 loc) • 6.03 kB
JavaScript
import "node:module";
import { relative, resolve } from "node:path";
import { assertZephyrBuildTarget } from "zephyr-edge-contract";
import { ZephyrEngine } from "../zephyr-engine/index.mjs";
import { ZeErrors, ZephyrError } from "./errors/index.mjs";
import { buildAssetsMapMock } from "./transformers/ze-build-assets-map.mjs";
import { zeBuildDashData } from "./transformers/ze-build-dash-data.mjs";
import { readDirRecursiveWithContents } from "./utils/read-dir-recursive.mjs";
const DEFAULT_DEPLOY_TARGET = 'web';
const DEFAULT_DEPLOY_SSR = true;
const SKIP_DEPLOY_PATTERNS = [
/\.map$/i,
/node_modules\//i,
/\.git\//i,
/\.DS_Store$/i,
/^\.zephyr\//i,
/thumbs\.db$/i
];
const DEPLOY_ENTRYPOINT_CANDIDATES = [
'server/index.js',
'server/index.mjs',
'server/server.js',
'server/server.mjs',
'server/_worker.js',
'server/_worker.mjs',
'index.mjs',
'index.js'
];
function normalizePath(filePath) {
return filePath.replace(/\\/g, '/').replace(/^\.?\//, '');
}
function normalizeBaseURL(baseURL) {
let normalized = baseURL.trim().replace(/\\/g, '/');
while(normalized.startsWith('./'))normalized = normalized.slice(2);
while(normalized.startsWith('/'))normalized = normalized.slice(1);
while(normalized.endsWith('/'))normalized = normalized.slice(0, -1);
return normalized;
}
function resolveAssetPath(file, outputDir, publicDir, baseURL = '/') {
const relativePath = normalizePath(file.relativePath);
if (!publicDir) return relativePath;
const fullPath = normalizePath(file.fullPath);
const publicRoot = normalizePath(publicDir).replace(/\/+$/, '');
const outputRoot = normalizePath(outputDir).replace(/\/+$/, '');
const publicRelativeRoot = normalizePath(relative(outputRoot, publicRoot)).replace(/\/+$/, '');
const isWithinPublicAlias = !!publicRelativeRoot && relativePath.startsWith(`${publicRelativeRoot}/`);
if (fullPath.startsWith(`${publicRoot}/`) || isWithinPublicAlias) {
const staticRelative = fullPath.startsWith(`${publicRoot}/`) ? fullPath.slice(publicRoot.length + 1) : relativePath.slice(publicRelativeRoot.length + 1);
if (!staticRelative) return relativePath;
const basePath = normalizeBaseURL(baseURL);
return basePath ? `client/${basePath}/${staticRelative}` : `client/${staticRelative}`;
}
return relativePath;
}
function shouldSkipDeployAsset(filePath) {
return SKIP_DEPLOY_PATTERNS.some((pattern)=>pattern.test(filePath));
}
function resolveDeployEntrypoint(assets) {
for (const candidate of DEPLOY_ENTRYPOINT_CANDIDATES)if (Object.prototype.hasOwnProperty.call(assets, candidate)) return candidate;
}
function getLegacyModuleFederationConfig(mfConfigs) {
const config = mfConfigs?.length === 1 ? mfConfigs[0] : void 0;
return config && 'string' == typeof config.name && 'string' == typeof config.filename ? config : void 0;
}
async function uploadOutputToZephyr(opts) {
const target = opts.target ?? DEFAULT_DEPLOY_TARGET;
assertZephyrBuildTarget(target, 'uploadOutputToZephyr({ target })');
const outputDir = resolve(opts.outputDir);
const publicDir = opts.publicDir ? resolve(opts.publicDir) : void 0;
const files = await readDirRecursiveWithContents(outputDir);
const assets = {};
const assetSources = new Map();
for (const file of files){
const relativePath = resolveAssetPath(file, outputDir, publicDir, opts.baseURL);
if (shouldSkipDeployAsset(relativePath)) continue;
const existingSource = assetSources.get(relativePath);
if (existingSource) throw new ZephyrError(ZeErrors.ERR_DEPLOY_LOCAL_BUILD, {
message: `uploadOutputToZephyr found conflicting output files for snapshot path "${relativePath}": "${existingSource}" and "${file.fullPath}".`
});
assets[relativePath] = {
content: file.content
};
assetSources.set(relativePath, file.fullPath);
}
if (0 === Object.keys(assets).length) throw new ZephyrError(ZeErrors.ERR_ASSETS_NOT_FOUND);
const ssr = opts.ssr ?? DEFAULT_DEPLOY_SSR;
const entrypoint = resolveDeployEntrypoint(assets);
if (ssr && !entrypoint) throw new ZephyrError(ZeErrors.ERR_SSR_ENTRYPOINT_NOT_FOUND, {
outputDir,
candidates: DEPLOY_ENTRYPOINT_CANDIDATES.join(', ')
});
const assetsMap = buildAssetsMapMock(assets, (asset)=>asset.content, ()=>'buffer');
const zephyrEngine = await ZephyrEngine.create({
builder: opts.builder ?? 'unknown',
context: opts.rootDir,
target
});
let buildInProgress = true;
try {
zephyrEngine.env.target = target;
zephyrEngine.env.ssr = ssr;
const buildStats = await zeBuildDashData(zephyrEngine);
const mfConfig = getLegacyModuleFederationConfig(opts.mfConfigs);
const buildStatsWithFederation = opts.federation ? {
...buildStats,
federation: opts.federation
} : buildStats;
let deploymentUrl = null;
await zephyrEngine.upload_assets({
assetsMap,
buildStats: buildStatsWithFederation,
...mfConfig ? {
mfConfig
} : {},
...opts.mfConfigs ? {
mfConfigs: opts.mfConfigs
} : {},
snapshotType: ssr ? 'ssr' : 'csr',
entrypoint: ssr ? entrypoint : void 0,
hooks: {
onDeployComplete: async (deploymentInfo)=>{
deploymentUrl = deploymentInfo.url;
await opts.hooks?.onDeployComplete?.(deploymentInfo);
}
}
});
buildInProgress = false;
await zephyrEngine.build_finished();
return {
deploymentUrl,
entrypoint
};
} finally{
if (buildInProgress && false !== zephyrEngine.hasActiveBuild) zephyrEngine.build_failed();
}
}
export { uploadOutputToZephyr };
//# sourceMappingURL=upload-output-to-zephyr.mjs.map