UNPKG

ts-switch-case

Version:

A TypeScript-first switch-case utility with object-based and chainable syntax

178 lines (176 loc) 5.89 kB
// src/utils.ts function logCyclicError(obj, path, context) { console.warn("[Cyclic Reference]", { timestamp: (/* @__PURE__ */ new Date()).toISOString(), context, path: path.join(" -> "), objectType: obj?.constructor?.name || "unknown" }); } function isCyclic(obj, currentPath = /* @__PURE__ */ new WeakSet(), pathArray = [], path = ["root"]) { if (obj && typeof obj === "object") { if (currentPath.has(obj)) { return true; } currentPath.add(obj); pathArray.push(obj); for (const key in obj) { const newPath = /* @__PURE__ */ new WeakSet(); pathArray.forEach((item) => newPath.add(item)); if (isCyclic(obj[key], newPath, [...pathArray], [...path, key])) { return true; } } } return false; } // src/switchCase.ts function switchCase(value, discriminatorOrCases, casesOrDefault, defaultHandler) { if (discriminatorOrCases === void 0) { const cases = []; let defaultFn; const builder = { case(match, handler) { cases.push({ match, handler }); return this; }, default(handler) { defaultFn = handler; return this; }, exhaustive() { return this.run(); }, run() { for (const { match, handler } of cases) { const isMatch = typeof match === "function" ? match(value) : match === value; if (isMatch) { return typeof handler === "function" ? handler(value) : handler; } } if (defaultFn) { return defaultFn(value); } throw new Error(`No matching case for value: ${JSON.stringify(value)}`); } }; return builder; } if (typeof discriminatorOrCases === "string" || typeof discriminatorOrCases === "number" || typeof discriminatorOrCases === "symbol") { let createDiscriminatedUnionBuilder2 = function(value2, discriminator2) { const handlers = /* @__PURE__ */ new Map(); let defaultFn; function caseFn(valueKey, handler) { handlers.set( valueKey, handler ); return builder; } function defaultFnSetter(handler) { defaultFn = handler; return builder; } function run() { const discriminatorValue = value2[discriminator2]; if (handlers.has(discriminatorValue)) { return handlers.get(discriminatorValue)(value2); } if (defaultFn) { return defaultFn(value2); } throw new Error( `No matching case for ${String(discriminator2)}: ${String(discriminatorValue)}` ); } const builder = { case: caseFn, default: defaultFnSetter, run }; return builder; }; var createDiscriminatedUnionBuilder = createDiscriminatedUnionBuilder2; if (casesOrDefault !== void 0) { const discriminator2 = discriminatorOrCases; const cases = casesOrDefault; const discriminatorValue = String(value[discriminator2]); const handlerOrValue = cases[discriminatorValue]; if (handlerOrValue !== void 0) { const result = typeof handlerOrValue === "function" ? handlerOrValue(value) : handlerOrValue; if (isCyclic(result)) logCyclicError(casesOrDefault, ["root"], "Cycle detected"); return result; } if (defaultHandler) { const result = defaultHandler(value); if (isCyclic(result)) logCyclicError(casesOrDefault, ["root"], "Cycle detected"); return result; } throw new Error( `No matching case for ${String(discriminator2)}: ${discriminatorValue}` ); } const discriminator = discriminatorOrCases; return createDiscriminatedUnionBuilder2(value, discriminator); } if (Array.isArray(discriminatorOrCases)) { if (isCyclic(discriminatorOrCases)) { logCyclicError(discriminatorOrCases, ["root"], "Cycle detected"); } const cases = discriminatorOrCases; const defaultFn = casesOrDefault; for (const { match, handler } of cases) { if (match(value)) { return typeof handler === "function" ? handler(value) : handler; } } if (defaultFn) { return defaultFn(value); } throw new Error(`No matching case for value: ${JSON.stringify(value)}`); } if (typeof discriminatorOrCases === "object") { if (isCyclic(discriminatorOrCases)) throw new Error( "Cyclic cases detected. See ts-switch-case README for handling cycles (e.g., sanitizeNode for React)." ); const cases = discriminatorOrCases; const defaultFn = casesOrDefault; const literalHandler = cases[String(value)]; if (literalHandler !== void 0) { const result = typeof literalHandler === "function" ? literalHandler() : literalHandler; if (isCyclic(result)) logCyclicError(discriminatorOrCases, ["root"], "Cycle detected"); return result; } for (const key of Object.keys(cases)) { const c = cases[key]; if (typeof c !== "function") { const match = typeof c.match === "function" ? c.match(value) : c.match === value; if (match) { const result = typeof c.handler === "function" ? c.handler(value) : c.handler; if (isCyclic(result)) logCyclicError(discriminatorOrCases, ["root"], "Cycle detected"); return result; } } } if (defaultFn) { const result = defaultFn(value); if (isCyclic(result)) logCyclicError(discriminatorOrCases, ["root"], "Cycle detected"); return result; } throw new Error(`No matching case for value: ${JSON.stringify(value)}`); } throw new Error("Invalid arguments to switchCase"); } function assertUnreachable(value) { throw new Error(`Unreachable case: ${value}`); } export { assertUnreachable, switchCase };