UNPKG

@wix/cli

Version:

CLI tool for building Wix sites and applications

818 lines (801 loc) 22.3 kB
import { createRequire as _createRequire } from 'node:module'; const require = _createRequire(import.meta.url); import { loadProjectGridAppContent } from "./chunk-P2RXCVYQ.js"; import { syncTypesWithAuth } from "./chunk-N337BL64.js"; import { createLogger } from "./chunk-T4IHVPO4.js"; import { httpRequest } from "./chunk-A27LWQHN.js"; import { createI18nT } from "./chunk-6MV3GIRQ.js"; import { authenticate } from "./chunk-Y7XOML5T.js"; import { saveProjectModel } from "./chunk-TGN4KGPQ.js"; import "./chunk-BWQNSFZR.js"; import "./chunk-TMLDHPS6.js"; import { createVeloCLIServer, debounceQueue, require_chokidar } from "./chunk-N5N7VOSM.js"; import { require_normalize_path } from "./chunk-V23RKBE2.js"; import "./chunk-GQE6PKJU.js"; import { updateFiles } from "./chunk-IZ7XSGRJ.js"; import { AppType, GridAppLayout as GridAppLayout2, createApp, pagesDiff } from "./chunk-WYTLYOOV.js"; import { GridAppLayout } from "./chunk-6RDHKDZ4.js"; import { TunnelClient, launchTunnel } from "./chunk-QF7GRNRC.js"; import "./chunk-GO3JYNZE.js"; import "./chunk-MB2DJ4WP.js"; import "./chunk-RWTQFIPN.js"; import "./chunk-W54PG6O5.js"; import "./chunk-NPIBHDAX.js"; import "./chunk-HWVLWVTF.js"; import "./chunk-Q5A6FJW3.js"; import { createHttpClient, isHttpError, openBrowser, pLimit } from "./chunk-PLPLA7N3.js"; import { ErrorViewer, extractHttpError, writeCrashReport } from "./chunk-EH3J5VW4.js"; import { wixCliCliMessageDisplayed, wixCliCodeHasBeenChanged, wixCliFileFolderCreatedRenamedDeleted, wixCliReadyForReloadAfterCodeChange, wixCliUserResponseToCliMessage } from "./chunk-CCOOZLEQ.js"; import "./chunk-75K4NCRA.js"; import "./chunk-KJ37XZQA.js"; import { renderSync } from "./chunk-OAVLQEAG.js"; import { require_react } from "./chunk-5A5B2WR4.js"; import "./chunk-SQ3KPTIH.js"; import "./chunk-Z4MHKCET.js"; import "./chunk-C4SUTB4O.js"; import "./chunk-PLXWATBI.js"; import { getDebugLogFilePath, getPagesFolder, getSourceFolder, getTypesDir } from "./chunk-TKDDSH55.js"; import "./chunk-62BYZXT7.js"; import { getTestOverrides } from "./chunk-WYHHEOWO.js"; import { outputFile, pathExists } from "./chunk-P4ZPHTFX.js"; import "./chunk-ZXYGJZOO.js"; import { CliError, CliErrorCode } from "./chunk-D5VYILRO.js"; import { __toESM, init_esm_shims } from "./chunk-4EFJZ3GQ.js"; // ../cli-site-old/src/dev/index.ts init_esm_shims(); // ../cli-site-old/src/dev/dev.ts init_esm_shims(); // ../../node_modules/exit-hook/index.js init_esm_shims(); import process from "node:process"; var asyncCallbacks = /* @__PURE__ */ new Set(); var callbacks = /* @__PURE__ */ new Set(); var isCalled = false; var isRegistered = false; async function exit(shouldManuallyExit, isSynchronous, signal) { if (isCalled) { return; } isCalled = true; if (asyncCallbacks.size > 0 && isSynchronous) { console.error([ "SYNCHRONOUS TERMINATION NOTICE:", "When explicitly exiting the process via process.exit or via a parent process,", "asynchronous tasks in your exitHooks will not run. Either remove these tasks,", "use gracefulExit() instead of process.exit(), or ensure your parent process", "sends a SIGINT to the process running this code." ].join(" ")); } const exitCode = 128 + signal; const done = (force = false) => { if (force === true || shouldManuallyExit === true) { process.exit(exitCode); } }; for (const callback of callbacks) { callback(exitCode); } if (isSynchronous) { done(); return; } const promises = []; let forceAfter = 0; for (const [callback, wait] of asyncCallbacks) { forceAfter = Math.max(forceAfter, wait); promises.push(Promise.resolve(callback(exitCode))); } const asyncTimer = setTimeout(() => { done(true); }, forceAfter); await Promise.all(promises); clearTimeout(asyncTimer); done(); } function addHook(options) { const { onExit, wait, isSynchronous } = options; const asyncCallbackConfig = [onExit, wait]; if (isSynchronous) { callbacks.add(onExit); } else { asyncCallbacks.add(asyncCallbackConfig); } if (!isRegistered) { isRegistered = true; process.once("beforeExit", exit.bind(void 0, true, false, -128)); process.once("SIGINT", exit.bind(void 0, true, false, 2)); process.once("SIGTERM", exit.bind(void 0, true, false, 15)); process.once("exit", exit.bind(void 0, false, true, 0)); process.on("message", (message) => { if (message === "shutdown") { exit(true, true, -128); } }); } return () => { if (isSynchronous) { callbacks.delete(onExit); } else { asyncCallbacks.delete(asyncCallbackConfig); } }; } function asyncExitHook(onExit, options = {}) { if (typeof onExit !== "function") { throw new TypeError("onExit must be a function"); } if (!(typeof options.wait === "number" && options.wait > 0)) { throw new TypeError("wait must be set to a positive numeric value"); } return addHook({ onExit, wait: options.wait, isSynchronous: false }); } // ../cli-site-old/src/dev/dev.ts var import_react = __toESM(require_react(), 1); // ../cli-site-old/src/environment.ts init_esm_shims(); var DEV_EDITOR_BASE_URL = "https://wix.com/editor/"; function getDevEditorBaseUrl() { return getTestOverrides().devEditorBaseUrl ?? DEV_EDITOR_BASE_URL; } // ../cli-site-old/src/gridapp/createGridApp.ts init_esm_shims(); // ../cli-site-old/src/gridapp/error-handlers/maxFileSizeErrorHandler.ts init_esm_shims(); function catchFilesMaxSizeError(err, files = []) { if (!files.length) { return; } const httpError = extractHttpError(err); if (!httpError) { return; } const filesMaxSizeRawViolations = httpError.response?.data?.details?.validationError?.fieldViolations?.filter( (v) => v.violatedRule === "MAX_LENGTH" ); if (!filesMaxSizeRawViolations?.length) { return; } const maxSizeViolationsData = tryExtractMaxSizeViolationMetadata( filesMaxSizeRawViolations ); throw new CliError({ code: CliErrorCode.GridAppFilesMaxLengthExceeded({ maxSizeViolationsData, filePaths: files.map((f) => f.path ?? "[unknown file path]") }), cause: httpError }); } function tryExtractMaxSizeViolationMetadata(filesMaxSizeViolations) { const fileIndexRegex = /\[(?<fileIndex>\d+)\].content/; const expectedAndReceivedSizeRegex = /has size (?<receivedSize>\d+), expected (?<expectedSize>\d+) or less/; const violationsData = { expectedSize: void 0, files: [] }; for (const filesMaxSizeViolation of filesMaxSizeViolations) { const fileIndexMatchResult = fileIndexRegex.exec( filesMaxSizeViolation.field ); const expectedAndReceivedSizeMatchResult = expectedAndReceivedSizeRegex.exec(filesMaxSizeViolation.description); if (!(fileIndexMatchResult?.groups?.fileIndex && expectedAndReceivedSizeMatchResult?.groups?.receivedSize && expectedAndReceivedSizeMatchResult.groups.expectedSize)) { return null; } violationsData.expectedSize ??= Math.trunc( parseInt(expectedAndReceivedSizeMatchResult.groups.expectedSize, 10) / 1e3 ); violationsData.files.push({ receivedSize: Math.trunc( parseInt(expectedAndReceivedSizeMatchResult.groups.receivedSize, 10) / 1e3 ), fileIndex: parseInt(fileIndexMatchResult.groups.fileIndex, 10) }); } return violationsData; } // ../cli-site-old/src/gridapp/createGridApp.ts async function createAppWithParams(params, opts) { let app; try { const response = await httpRequest( { type: "code", authState: opts.authState }, createApp(params) ); app = response.data.app; } catch (err) { catchFilesMaxSizeError(err, params.content?.files); throw err; } if (!app?.id) { throw new CliError({ code: CliErrorCode.InvalidCreateAppResponse(), info: { app }, cause: null }); } return app.id; } async function createMutableGridApp(projectFolder, opts) { const content = await loadProjectGridAppContent(projectFolder); return createAppWithParams( { app: { mutable: true, appType: AppType.VELO_ISOLATED }, content }, opts ); } // ../cli-site-old/src/gridapp/startGridAppUpdater.ts init_esm_shims(); import { readFile } from "node:fs/promises"; import { join } from "node:path"; var import_chokidar = __toESM(require_chokidar(), 1); var import_normalize_path = __toESM(require_normalize_path(), 1); var WATCH_FS_EVENTS_DEBOUNCE_TIME = 1e3; var getSectionName = (path) => { if (path.startsWith("src/pages")) { return "public"; } if (path.startsWith("src/backend")) { return "backend"; } return "other"; }; function startGridAppUpdater(model, gridAppId, opts, logger, biEvents) { const { projectFolder } = model; const limit = pLimit(1); const watcher = (0, import_chokidar.watch)(getSourceFolder(projectFolder), { disableGlobbing: true, cwd: projectFolder, ignoreInitial: true }).on( "all", debounceQueue(async (events) => { await limit(async () => { const logSynced = logger.logSyncingLocalCode(); const { added, changed, removed } = normalizeChokidarBatchedEvents(events); const addedArray = Array.from(added.values()).map((path) => ({ path, type: "create" })); const changedArray = Array.from(changed.values()).map((path) => ({ path, type: "update" })); const removedArray = Array.from(removed.values()).map((path) => ({ path, type: "delete" })); for (const val of [...addedArray, ...changedArray, ...removedArray]) { biEvents.file({ model, action: val.type, path: val.path, oldPath: "", sectionName: getSectionName(val.path) }); if (val.type === "update") { biEvents.codeChanged(model, val.path); } } try { await writeFiles({ filesToUpdate: await Promise.all( addedArray.concat(changedArray).map(async (val) => ({ path: (0, import_normalize_path.default)(val.path), content: await readFile( join(projectFolder, val.path), "utf8" ) })) ), removedArray, gridAppId, authState: opts.authState }); const message = logSynced.success(); biEvents.messageDisplayed({ message, model }); biEvents.readyForReloadAfterCodeChanged(model); } catch (e) { logSynced.fail(); const error = new CliError({ code: CliErrorCode.GridAppFailedToUpdateFiles(), cause: e, info: { added, changed, removed } }); opts.onError(error); } }); }, WATCH_FS_EVENTS_DEBOUNCE_TIME) ).on("error", (e) => { const error = new CliError({ code: CliErrorCode.FailedToWatchFiles(), cause: e }); opts.onError(error); }); return { close: () => watcher.close() }; } function normalizeChokidarBatchedEvents(events) { return events.reduce( (result, [eventType, filePath]) => { switch (eventType) { case "add": { result.added.add(filePath); break; } case "change": { result.changed.add(filePath); break; } case "unlink": { result.added.delete(filePath); result.changed.delete(filePath); result.removed.add(filePath); break; } case "unlinkDir": { const filePathAsDir = filePath.endsWith("/") ? filePath : `${filePath}/`; for (const filePath2 of result.added) { if (filePath2.startsWith(filePathAsDir)) { result.added.delete(filePath2); } } for (const filePath2 of result.changed) { if (filePath2.startsWith(filePathAsDir)) { result.changed.delete(filePath2); } } result.removed.add(filePath); break; } case "addDir": { break; } } return result; }, { added: /* @__PURE__ */ new Set(), changed: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() } ); } async function writeFiles({ filesToUpdate, removedArray, gridAppId, authState }) { try { await httpRequest( { type: "code", authState }, updateFiles({ gridAppId, layout: GridAppLayout.GITHUB, filesToUpdate, ignoreForbiddenPaths: true, pathsToDelete: removedArray.map((val) => val.path) }) ); } catch (err) { catchFilesMaxSizeError(err, filesToUpdate); throw err; } } // ../cli-site-old/src/key-watcher.ts init_esm_shims(); import readline from "node:readline"; import { stdin } from "node:process"; var KeyWatcher = class { actions = /* @__PURE__ */ new Map(); watchExitProcess() { return new Promise((resolve) => { this.on("c", (key) => { if (key.ctrl) { resolve(); } }); }); } on(key, callback) { this.actions.set(key, callback); } watch() { const rl = readline.createInterface(stdin); readline.emitKeypressEvents(stdin, rl); if (stdin.isTTY) { stdin.setRawMode(true); stdin.setEncoding("utf8"); } stdin.on("keypress", this.handleKeyPress); return { ctrlCPromise: this.watchExitProcess() }; } unwatch() { if (stdin.isTTY) { stdin.setRawMode(false); } stdin.off("keypress", this.handleKeyPress); stdin.pause(); this.actions.clear(); } handleKeyPress = (_, key) => { this.actions.get(key.name)?.(key); }; }; // ../cli-site-old/src/bi.ts init_esm_shims(); import { randomUUID } from "node:crypto"; var sitesBiEvents = (biLogger) => ({ file: ({ model, action, path, sectionName, oldPath }) => { void biLogger.report( wixCliFileFolderCreatedRenamedDeleted({ action, path, sectionName, oldPath, siteRevision: Number(model.revision) }) ); }, codeChanged: (model, fileName) => { void biLogger.report( wixCliCodeHasBeenChanged({ fileName, siteRevision: Number(model.revision) }) ); }, readyForReloadAfterCodeChanged: (model) => { void biLogger.report( wixCliReadyForReloadAfterCodeChange({ siteRevision: Number(model.revision) }) ); }, messageDisplayed: ({ message, model }) => { void biLogger.report( wixCliCliMessageDisplayed({ message, messageId: randomUUID(), siteRevision: Number(model.revision) }) ); }, userResponse: ({ model, action, message }) => { void biLogger.report( wixCliUserResponseToCliMessage({ action, message, messageId: randomUUID(), siteRevision: Number(model.revision) }) ); } }); // ../cli-site-old/src/dev/sync-all-to-revision.ts init_esm_shims(); import { rm as rm2 } from "node:fs/promises"; // ../cli-site-old/src/sources/index.ts init_esm_shims(); import { readdir } from "node:fs/promises"; // ../cli-site-old/src/sources/pages-diff.ts init_esm_shims(); import { join as join2 } from "node:path"; import { rm, rename } from "node:fs/promises"; async function createPages(projectFolder, pages) { await Promise.all( pages.map((page) => { if (!page.pageId?.path || !page.content) { return Promise.resolve(); } return outputFile(join2(projectFolder, page.pageId.path), page.content); }) ); } async function renamePages(projectFolder, pages) { await Promise.all( pages.map(async (page) => { if (!page.sourcePath?.path || !page.targetPath?.path) { return; } const sourcePath = join2(projectFolder, page.sourcePath.path); const targetPath = join2(projectFolder, page.targetPath.path); if (await pathExists(targetPath)) { return rm(sourcePath, { force: true }); } return rename(sourcePath, targetPath); }) ); } async function deletePages(projectFolder, pages) { await Promise.all( pages.map((page) => { if (!page.path) { return Promise.resolve(); } return rm(join2(projectFolder, page.path), { force: true }); }) ); } // ../cli-site-old/src/sources/index.ts async function syncPages(model, opts) { const pagesFolder = getPagesFolder(model.projectFolder); if (!await pathExists(pagesFolder)) { throw new CliError({ code: CliErrorCode.NoPagesDirectoryFound({ srcDir: getSourceFolder(model.projectFolder) }), cause: null }); } const params = { layout: GridAppLayout2.GITHUB, sourcePagesPaths: await readdir(pagesFolder), targetRevision: model.revision }; const { data } = await httpRequest( { authState: opts.authState, type: "code" }, pagesDiff(params) ).catch((e) => { throw new CliError({ code: CliErrorCode.FailedToGetPagesDiff(), cause: e, info: { ...params } }); }); await Promise.all([ createPages(model.projectFolder, data.pagesDiff?.created ?? []), renamePages(model.projectFolder, data.pagesDiff?.renamed ?? []), deletePages(model.projectFolder, data.pagesDiff?.deleted ?? []) ]); } // ../cli-site-old/src/dev/sync-all-to-revision.ts async function syncAllToRevision(model, opts, logger) { const logTypesSynced = logger.logSyncingTypesToRevision(model.revision); try { await syncTypesWithAuth(model, { authState: opts.authState }); logTypesSynced.success(); } catch (error) { writeCrashReport(getDebugLogFilePath(model.projectFolder), error); logTypesSynced.fail(); await rm2(getTypesDir(model.projectFolder), { recursive: true, force: true }).catch((e) => { writeCrashReport(getDebugLogFilePath(model.projectFolder), e); logger.logRecoverableFailureToRemoveTypesDir(e); }); } const logPagesSynced = logger.logSyncingPagesToRevision(model.revision); try { await syncPages(model, { authState: opts.authState }); logPagesSynced.success(); } catch (error) { logPagesSynced.fail(); throw error; } } // ../cli-site-old/src/dev/dev.ts async function dev(initialModel, { biLogger, errorReporter }, { https = false, tunnel: useTunnel = false }) { let model = initialModel; const authState = await authenticate({ metaSiteId: model.metaSiteId, biLogger, errorReporter }); const t = createI18nT(); const logger = createLogger({ t }); const biEvents = sitesBiEvents(biLogger); const server = createVeloCLIServer({ https }); let gridAppId; try { gridAppId = await createMutableGridApp(model.projectFolder, { authState }); } catch (error) { const message = isHttpError(error) ? error.response?.data?.message : void 0; throw new CliError({ code: CliErrorCode.FailedToCreateGridApp({ error: message }), cause: error }); } await syncAllToRevision(model, { authState }, logger); const { close } = startGridAppUpdater( model, gridAppId, { authState, onError(error) { logger.logNewLine(); errorReporter.reportError(error); writeCrashReport(getDebugLogFilePath(model.projectFolder), error); renderSync((0, import_react.createElement)(ErrorViewer, { error })); } }, logger, biEvents ); const onServerDisconnected = (devEditorUrl) => { const message = logger.logNoOpenedTab(devEditorUrl); biEvents.messageDisplayed({ model, message }); }; const onSyncDevEditorChanges = async (revision) => { if (revision !== model.revision) { const newModel = { ...model, revision }; await saveProjectModel(newModel); const message = logger.logUpdatedLocalCodeToRevision({ revision: newModel.revision }); biEvents.messageDisplayed({ message, model }); await syncAllToRevision(newModel, { authState }, logger); model = newModel; } }; const openLocalEditor = async (serverPort) => { biEvents.userResponse({ action: "Browser opened", message: "Opening the Local Editor", model }); const devEditorUrl = getDevEditorUrl(serverPort); if (https) { await openBrowser(new URL(`https://localhost:${serverPort}`).href); } else { await openBrowser(devEditorUrl); } }; let keyWatcher; let tunnel; const getDevEditorUrl = (serverPort) => { const url = new URL(model.metaSiteId, getDevEditorBaseUrl()); if (tunnel) { url.searchParams.append("localEditorDestination", tunnel.domain); } else { url.searchParams.append("localPort", serverPort.toString()); } url.searchParams.append("secureSocket", https.toString()); return url.toString(); }; try { const { port: serverPort } = await server.serve(gridAppId, { getDevEditorUrl }); if (useTunnel) { const httpClient = createHttpClient({ type: "backoffice", getAppToken: authState.getAccessToken }); const tunnelClient = new TunnelClient(httpClient); tunnel = await launchTunnel(serverPort, tunnelClient); } const devEditorUrl = getDevEditorUrl(serverPort); server.on("disconnected", () => onServerDisconnected(devEditorUrl)); server.on("syncDevEditorChanges", onSyncDevEditorChanges); keyWatcher = new KeyWatcher(); logger.logOpeningBrowser(https); await openLocalEditor(serverPort); const { ctrlCPromise } = keyWatcher.watch(); keyWatcher.on("e", () => openLocalEditor(serverPort)); asyncExitHook( async () => { await server.close(); tunnel?.close(); }, { wait: 500 } ); await ctrlCPromise; } finally { keyWatcher?.unwatch(); await Promise.all([server.close(), close(), tunnel?.close()]); } } export { dev }; //# sourceMappingURL=dev-6JFQUCHE.js.map