casemorph 0.1.0 → 0.1.1

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 CHANGED
@@ -5,7 +5,7 @@
5
5
  [![npm version](https://img.shields.io/npm/v/casemorph)](https://www.npmjs.com/package/casemorph)
6
6
  [![bundle size](https://img.shields.io/bundlephobia/minzip/casemorph)](https://bundlephobia.com/package/casemorph)
7
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org/)
8
- [![CI](https://img.shields.io/github/actions/workflow/status/best-package/casemorph/ci.yml)](https://github.com/best-package/casemorph/actions)
8
+ [![CI](https://img.shields.io/github/actions/workflow/status/Ludovic-Blondon/casemorph/ci.yml)](https://github.com/Ludovic-Blondon/casemorph/actions)
9
9
  [![license](https://img.shields.io/npm/l/casemorph)](./LICENSE)
10
10
 
11
11
  ## The Problem
@@ -170,7 +170,7 @@ The naive approach is faster for simple snake→camel on flat objects because it
170
170
  ## Contributing
171
171
 
172
172
  ```bash
173
- git clone https://github.com/best-package/casemorph.git
173
+ git clone https://github.com/Ludovic-Blondon/casemorph.git
174
174
  cd casemorph
175
175
  npm install
176
176
  npm test # run tests
package/dist/index.cjs CHANGED
@@ -19,7 +19,10 @@ function joinKebab(words) {
19
19
  return words.join("-");
20
20
  }
21
21
  function joinPascal(words) {
22
- return words.reduce((acc, w) => acc + w.charAt(0).toUpperCase() + w.slice(1), "");
22
+ return words.reduce(
23
+ (acc, w) => acc + w.charAt(0).toUpperCase() + w.slice(1),
24
+ ""
25
+ );
23
26
  }
24
27
  function joinConstant(words) {
25
28
  return words.map((w) => w.toUpperCase()).join("_");
@@ -56,19 +59,34 @@ function constantCase(str) {
56
59
  return joinConstant(splitWords(str));
57
60
  }
58
61
  function toCamelCase(data) {
59
- return deepTransformKeys(data, (k) => joinCamel(splitWords(k)));
62
+ return deepTransformKeys(
63
+ data,
64
+ (k) => joinCamel(splitWords(k))
65
+ );
60
66
  }
61
67
  function toSnakeCase(data) {
62
- return deepTransformKeys(data, (k) => joinSnake(splitWords(k)));
68
+ return deepTransformKeys(
69
+ data,
70
+ (k) => joinSnake(splitWords(k))
71
+ );
63
72
  }
64
73
  function toKebabCase(data) {
65
- return deepTransformKeys(data, (k) => joinKebab(splitWords(k)));
74
+ return deepTransformKeys(
75
+ data,
76
+ (k) => joinKebab(splitWords(k))
77
+ );
66
78
  }
67
79
  function toPascalCase(data) {
68
- return deepTransformKeys(data, (k) => joinPascal(splitWords(k)));
80
+ return deepTransformKeys(
81
+ data,
82
+ (k) => joinPascal(splitWords(k))
83
+ );
69
84
  }
70
85
  function toConstantCase(data) {
71
- return deepTransformKeys(data, (k) => joinConstant(splitWords(k)));
86
+ return deepTransformKeys(
87
+ data,
88
+ (k) => joinConstant(splitWords(k))
89
+ );
72
90
  }
73
91
 
74
92
  exports.camelCase = camelCase;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/transform.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n\tCamelCase,\n\tCamelCaseKeys,\n\tConstantCase,\n\tConstantCaseKeys,\n\tKebabCase,\n\tKebabCaseKeys,\n\tPascalCase,\n\tPascalCaseKeys,\n\tSnakeCase,\n\tSnakeCaseKeys,\n} from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Word splitting — mirrors the type-level SplitWords<S>\n// ---------------------------------------------------------------------------\n\n/** Boundary between a lowercase letter (or digit) and an uppercase letter. */\nconst LOWER_TO_UPPER = /([a-z\\d])([A-Z])/g;\n\n/** Boundary between an acronym and the start of the next word. */\nconst ACRONYM_BOUNDARY = /([A-Z]+)([A-Z][a-z])/g;\n\n/** Any explicit separator (underscore, hyphen, whitespace). */\nconst SEPARATORS = /[_\\-\\s]+/;\n\n/**\n * Split any casing convention into lowercase word fragments.\n *\n * Handles snake_case, kebab-case, CONSTANT_CASE, camelCase, PascalCase,\n * and acronyms like `\"XMLHttpRequest\"` → `[\"xml\", \"http\", \"request\"]`.\n */\nfunction splitWords(str: string): string[] {\n\tif (!str) return [];\n\treturn str\n\t\t.replace(LOWER_TO_UPPER, \"$1\\x00$2\")\n\t\t.replace(ACRONYM_BOUNDARY, \"$1\\x00$2\")\n\t\t.split(SEPARATORS)\n\t\t.join(\"\\x00\")\n\t\t.split(\"\\x00\")\n\t\t.filter(Boolean)\n\t\t.map((w) => w.toLowerCase());\n}\n\n// ---------------------------------------------------------------------------\n// Join helpers — one per target format\n// ---------------------------------------------------------------------------\n\nfunction joinCamel(words: string[]): string {\n\tif (words.length === 0) return \"\";\n\treturn (\n\t\twords[0] + words.slice(1).reduce((acc, w) => acc + w.charAt(0).toUpperCase() + w.slice(1), \"\")\n\t);\n}\n\nfunction joinSnake(words: string[]): string {\n\treturn words.join(\"_\");\n}\n\nfunction joinKebab(words: string[]): string {\n\treturn words.join(\"-\");\n}\n\nfunction joinPascal(words: string[]): string {\n\treturn words.reduce((acc, w) => acc + w.charAt(0).toUpperCase() + w.slice(1), \"\");\n}\n\nfunction joinConstant(words: string[]): string {\n\treturn words.map((w) => w.toUpperCase()).join(\"_\");\n}\n\n// ---------------------------------------------------------------------------\n// Deep key transformation\n// ---------------------------------------------------------------------------\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst proto = Object.getPrototypeOf(value) as unknown;\n\treturn proto === Object.prototype || proto === null;\n}\n\nfunction deepTransformKeys(data: unknown, transform: (key: string) => string): unknown {\n\tif (Array.isArray(data)) {\n\t\treturn data.map((item) => deepTransformKeys(item, transform));\n\t}\n\tif (!isPlainObject(data)) return data;\n\n\tconst out: Record<string, unknown> = {};\n\tfor (const key of Object.keys(data)) {\n\t\tout[transform(key)] = deepTransformKeys(data[key], transform);\n\t}\n\treturn out;\n}\n\n// ---------------------------------------------------------------------------\n// String-level transforms (public)\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a string to camelCase.\n *\n * @example\n * camelCase('user_name') // 'userName'\n * camelCase('XMLParser') // 'xmlParser'\n * camelCase('content-type') // 'contentType'\n */\nexport function camelCase<S extends string>(str: S): CamelCase<S> {\n\treturn joinCamel(splitWords(str)) as CamelCase<S>;\n}\n\n/**\n * Convert a string to snake_case.\n *\n * @example\n * snakeCase('userName') // 'user_name'\n * snakeCase('XMLParser') // 'xml_parser'\n * snakeCase('content-type') // 'content_type'\n */\nexport function snakeCase<S extends string>(str: S): SnakeCase<S> {\n\treturn joinSnake(splitWords(str)) as SnakeCase<S>;\n}\n\n/**\n * Convert a string to kebab-case.\n *\n * @example\n * kebabCase('userName') // 'user-name'\n * kebabCase('XMLParser') // 'xml-parser'\n * kebabCase('user_name') // 'user-name'\n */\nexport function kebabCase<S extends string>(str: S): KebabCase<S> {\n\treturn joinKebab(splitWords(str)) as KebabCase<S>;\n}\n\n/**\n * Convert a string to PascalCase.\n *\n * @example\n * pascalCase('user_name') // 'UserName'\n * pascalCase('XMLParser') // 'XmlParser'\n * pascalCase('kebab-case') // 'KebabCase'\n */\nexport function pascalCase<S extends string>(str: S): PascalCase<S> {\n\treturn joinPascal(splitWords(str)) as PascalCase<S>;\n}\n\n/**\n * Convert a string to CONSTANT_CASE.\n *\n * @example\n * constantCase('userName') // 'USER_NAME'\n * constantCase('XMLParser') // 'XML_PARSER'\n * constantCase('kebab-case') // 'KEBAB_CASE'\n */\nexport function constantCase<S extends string>(str: S): ConstantCase<S> {\n\treturn joinConstant(splitWords(str)) as ConstantCase<S>;\n}\n\n// ---------------------------------------------------------------------------\n// Object-level transforms (public)\n// ---------------------------------------------------------------------------\n\n/**\n * Deeply transform all keys of an object (or array of objects) to camelCase.\n * Primitives, Dates, RegExps, Maps, Sets, and other non-plain objects are preserved.\n *\n * @example\n * const result = toCamelCase({ user_name: 'Alice', billing_address: { zip_code: '75001' } })\n * result.userName // 'Alice'\n * result.billingAddress.zipCode // '75001'\n */\nexport function toCamelCase<T>(data: T): CamelCaseKeys<T> {\n\treturn deepTransformKeys(data, (k) => joinCamel(splitWords(k))) as CamelCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to snake_case.\n *\n * @example\n * const result = toSnakeCase({ userName: 'Alice', billingAddress: { zipCode: '75001' } })\n * result.user_name // 'Alice'\n * result.billing_address.zip_code // '75001'\n */\nexport function toSnakeCase<T>(data: T): SnakeCaseKeys<T> {\n\treturn deepTransformKeys(data, (k) => joinSnake(splitWords(k))) as SnakeCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to kebab-case.\n *\n * @example\n * const result = toKebabCase({ userName: 'Alice' })\n * result['user-name'] // 'Alice'\n */\nexport function toKebabCase<T>(data: T): KebabCaseKeys<T> {\n\treturn deepTransformKeys(data, (k) => joinKebab(splitWords(k))) as KebabCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to PascalCase.\n *\n * @example\n * const result = toPascalCase({ user_name: 'Alice' })\n * result.UserName // 'Alice'\n */\nexport function toPascalCase<T>(data: T): PascalCaseKeys<T> {\n\treturn deepTransformKeys(data, (k) => joinPascal(splitWords(k))) as PascalCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to CONSTANT_CASE.\n *\n * @example\n * const result = toConstantCase({ userName: 'Alice' })\n * result.USER_NAME // 'Alice'\n */\nexport function toConstantCase<T>(data: T): ConstantCaseKeys<T> {\n\treturn deepTransformKeys(data, (k) => joinConstant(splitWords(k))) as ConstantCaseKeys<T>;\n}\n"]}
1
+ {"version":3,"sources":["../src/transform.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n CamelCase,\n CamelCaseKeys,\n ConstantCase,\n ConstantCaseKeys,\n KebabCase,\n KebabCaseKeys,\n PascalCase,\n PascalCaseKeys,\n SnakeCase,\n SnakeCaseKeys,\n} from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Word splitting — mirrors the type-level SplitWords<S>\n// ---------------------------------------------------------------------------\n\n/** Boundary between a lowercase letter (or digit) and an uppercase letter. */\nconst LOWER_TO_UPPER = /([a-z\\d])([A-Z])/g;\n\n/** Boundary between an acronym and the start of the next word. */\nconst ACRONYM_BOUNDARY = /([A-Z]+)([A-Z][a-z])/g;\n\n/** Any explicit separator (underscore, hyphen, whitespace). */\nconst SEPARATORS = /[_\\-\\s]+/;\n\n/**\n * Split any casing convention into lowercase word fragments.\n *\n * Handles snake_case, kebab-case, CONSTANT_CASE, camelCase, PascalCase,\n * and acronyms like `\"XMLHttpRequest\"` → `[\"xml\", \"http\", \"request\"]`.\n */\nfunction splitWords(str: string): string[] {\n if (!str) return [];\n return str\n .replace(LOWER_TO_UPPER, \"$1\\x00$2\")\n .replace(ACRONYM_BOUNDARY, \"$1\\x00$2\")\n .split(SEPARATORS)\n .join(\"\\x00\")\n .split(\"\\x00\")\n .filter(Boolean)\n .map((w) => w.toLowerCase());\n}\n\n// ---------------------------------------------------------------------------\n// Join helpers — one per target format\n// ---------------------------------------------------------------------------\n\nfunction joinCamel(words: string[]): string {\n if (words.length === 0) return \"\";\n return (\n words[0] +\n words\n .slice(1)\n .reduce((acc, w) => acc + w.charAt(0).toUpperCase() + w.slice(1), \"\")\n );\n}\n\nfunction joinSnake(words: string[]): string {\n return words.join(\"_\");\n}\n\nfunction joinKebab(words: string[]): string {\n return words.join(\"-\");\n}\n\nfunction joinPascal(words: string[]): string {\n return words.reduce(\n (acc, w) => acc + w.charAt(0).toUpperCase() + w.slice(1),\n \"\",\n );\n}\n\nfunction joinConstant(words: string[]): string {\n return words.map((w) => w.toUpperCase()).join(\"_\");\n}\n\n// ---------------------------------------------------------------------------\n// Deep key transformation\n// ---------------------------------------------------------------------------\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null) return false;\n const proto = Object.getPrototypeOf(value) as unknown;\n return proto === Object.prototype || proto === null;\n}\n\nfunction deepTransformKeys(\n data: unknown,\n transform: (key: string) => string,\n): unknown {\n if (Array.isArray(data)) {\n return data.map((item) => deepTransformKeys(item, transform));\n }\n if (!isPlainObject(data)) return data;\n\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(data)) {\n out[transform(key)] = deepTransformKeys(data[key], transform);\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// String-level transforms (public)\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a string to camelCase.\n *\n * @example\n * camelCase('user_name') // 'userName'\n * camelCase('XMLParser') // 'xmlParser'\n * camelCase('content-type') // 'contentType'\n */\nexport function camelCase<S extends string>(str: S): CamelCase<S> {\n return joinCamel(splitWords(str)) as CamelCase<S>;\n}\n\n/**\n * Convert a string to snake_case.\n *\n * @example\n * snakeCase('userName') // 'user_name'\n * snakeCase('XMLParser') // 'xml_parser'\n * snakeCase('content-type') // 'content_type'\n */\nexport function snakeCase<S extends string>(str: S): SnakeCase<S> {\n return joinSnake(splitWords(str)) as SnakeCase<S>;\n}\n\n/**\n * Convert a string to kebab-case.\n *\n * @example\n * kebabCase('userName') // 'user-name'\n * kebabCase('XMLParser') // 'xml-parser'\n * kebabCase('user_name') // 'user-name'\n */\nexport function kebabCase<S extends string>(str: S): KebabCase<S> {\n return joinKebab(splitWords(str)) as KebabCase<S>;\n}\n\n/**\n * Convert a string to PascalCase.\n *\n * @example\n * pascalCase('user_name') // 'UserName'\n * pascalCase('XMLParser') // 'XmlParser'\n * pascalCase('kebab-case') // 'KebabCase'\n */\nexport function pascalCase<S extends string>(str: S): PascalCase<S> {\n return joinPascal(splitWords(str)) as PascalCase<S>;\n}\n\n/**\n * Convert a string to CONSTANT_CASE.\n *\n * @example\n * constantCase('userName') // 'USER_NAME'\n * constantCase('XMLParser') // 'XML_PARSER'\n * constantCase('kebab-case') // 'KEBAB_CASE'\n */\nexport function constantCase<S extends string>(str: S): ConstantCase<S> {\n return joinConstant(splitWords(str)) as ConstantCase<S>;\n}\n\n// ---------------------------------------------------------------------------\n// Object-level transforms (public)\n// ---------------------------------------------------------------------------\n\n/**\n * Deeply transform all keys of an object (or array of objects) to camelCase.\n * Primitives, Dates, RegExps, Maps, Sets, and other non-plain objects are preserved.\n *\n * @example\n * const result = toCamelCase({ user_name: 'Alice', billing_address: { zip_code: '75001' } })\n * result.userName // 'Alice'\n * result.billingAddress.zipCode // '75001'\n */\nexport function toCamelCase<T>(data: T): CamelCaseKeys<T> {\n return deepTransformKeys(data, (k) =>\n joinCamel(splitWords(k)),\n ) as CamelCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to snake_case.\n *\n * @example\n * const result = toSnakeCase({ userName: 'Alice', billingAddress: { zipCode: '75001' } })\n * result.user_name // 'Alice'\n * result.billing_address.zip_code // '75001'\n */\nexport function toSnakeCase<T>(data: T): SnakeCaseKeys<T> {\n return deepTransformKeys(data, (k) =>\n joinSnake(splitWords(k)),\n ) as SnakeCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to kebab-case.\n *\n * @example\n * const result = toKebabCase({ userName: 'Alice' })\n * result['user-name'] // 'Alice'\n */\nexport function toKebabCase<T>(data: T): KebabCaseKeys<T> {\n return deepTransformKeys(data, (k) =>\n joinKebab(splitWords(k)),\n ) as KebabCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to PascalCase.\n *\n * @example\n * const result = toPascalCase({ user_name: 'Alice' })\n * result.UserName // 'Alice'\n */\nexport function toPascalCase<T>(data: T): PascalCaseKeys<T> {\n return deepTransformKeys(data, (k) =>\n joinPascal(splitWords(k)),\n ) as PascalCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to CONSTANT_CASE.\n *\n * @example\n * const result = toConstantCase({ userName: 'Alice' })\n * result.USER_NAME // 'Alice'\n */\nexport function toConstantCase<T>(data: T): ConstantCaseKeys<T> {\n return deepTransformKeys(data, (k) =>\n joinConstant(splitWords(k)),\n ) as ConstantCaseKeys<T>;\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -21,10 +21,7 @@ type JoinPascal<W extends readonly string[]> = W extends readonly [
21
21
  infer F extends string,
22
22
  ...infer R extends string[]
23
23
  ] ? `${Capitalize<Lowercase<F>>}${JoinPascal<R>}` : "";
24
- type JoinSep<W extends readonly string[], S extends string> = W extends readonly [
25
- infer F extends string,
26
- ...infer R extends string[]
27
- ] ? R extends readonly [string, ...string[]] ? `${Lowercase<F>}${S}${JoinSep<R, S>}` : Lowercase<F> : "";
24
+ type JoinSep<W extends readonly string[], S extends string> = W extends readonly [infer F extends string, ...infer R extends string[]] ? R extends readonly [string, ...string[]] ? `${Lowercase<F>}${S}${JoinSep<R, S>}` : Lowercase<F> : "";
28
25
  type JoinConstant<W extends readonly string[]> = W extends readonly [
29
26
  infer F extends string,
30
27
  ...infer R extends string[]
package/dist/index.d.ts CHANGED
@@ -21,10 +21,7 @@ type JoinPascal<W extends readonly string[]> = W extends readonly [
21
21
  infer F extends string,
22
22
  ...infer R extends string[]
23
23
  ] ? `${Capitalize<Lowercase<F>>}${JoinPascal<R>}` : "";
24
- type JoinSep<W extends readonly string[], S extends string> = W extends readonly [
25
- infer F extends string,
26
- ...infer R extends string[]
27
- ] ? R extends readonly [string, ...string[]] ? `${Lowercase<F>}${S}${JoinSep<R, S>}` : Lowercase<F> : "";
24
+ type JoinSep<W extends readonly string[], S extends string> = W extends readonly [infer F extends string, ...infer R extends string[]] ? R extends readonly [string, ...string[]] ? `${Lowercase<F>}${S}${JoinSep<R, S>}` : Lowercase<F> : "";
28
25
  type JoinConstant<W extends readonly string[]> = W extends readonly [
29
26
  infer F extends string,
30
27
  ...infer R extends string[]
package/dist/index.js CHANGED
@@ -17,7 +17,10 @@ function joinKebab(words) {
17
17
  return words.join("-");
18
18
  }
19
19
  function joinPascal(words) {
20
- return words.reduce((acc, w) => acc + w.charAt(0).toUpperCase() + w.slice(1), "");
20
+ return words.reduce(
21
+ (acc, w) => acc + w.charAt(0).toUpperCase() + w.slice(1),
22
+ ""
23
+ );
21
24
  }
22
25
  function joinConstant(words) {
23
26
  return words.map((w) => w.toUpperCase()).join("_");
@@ -54,19 +57,34 @@ function constantCase(str) {
54
57
  return joinConstant(splitWords(str));
55
58
  }
56
59
  function toCamelCase(data) {
57
- return deepTransformKeys(data, (k) => joinCamel(splitWords(k)));
60
+ return deepTransformKeys(
61
+ data,
62
+ (k) => joinCamel(splitWords(k))
63
+ );
58
64
  }
59
65
  function toSnakeCase(data) {
60
- return deepTransformKeys(data, (k) => joinSnake(splitWords(k)));
66
+ return deepTransformKeys(
67
+ data,
68
+ (k) => joinSnake(splitWords(k))
69
+ );
61
70
  }
62
71
  function toKebabCase(data) {
63
- return deepTransformKeys(data, (k) => joinKebab(splitWords(k)));
72
+ return deepTransformKeys(
73
+ data,
74
+ (k) => joinKebab(splitWords(k))
75
+ );
64
76
  }
65
77
  function toPascalCase(data) {
66
- return deepTransformKeys(data, (k) => joinPascal(splitWords(k)));
78
+ return deepTransformKeys(
79
+ data,
80
+ (k) => joinPascal(splitWords(k))
81
+ );
67
82
  }
68
83
  function toConstantCase(data) {
69
- return deepTransformKeys(data, (k) => joinConstant(splitWords(k)));
84
+ return deepTransformKeys(
85
+ data,
86
+ (k) => joinConstant(splitWords(k))
87
+ );
70
88
  }
71
89
 
72
90
  export { camelCase, constantCase, kebabCase, pascalCase, snakeCase, toCamelCase, toConstantCase, toKebabCase, toPascalCase, toSnakeCase };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/transform.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n\tCamelCase,\n\tCamelCaseKeys,\n\tConstantCase,\n\tConstantCaseKeys,\n\tKebabCase,\n\tKebabCaseKeys,\n\tPascalCase,\n\tPascalCaseKeys,\n\tSnakeCase,\n\tSnakeCaseKeys,\n} from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Word splitting — mirrors the type-level SplitWords<S>\n// ---------------------------------------------------------------------------\n\n/** Boundary between a lowercase letter (or digit) and an uppercase letter. */\nconst LOWER_TO_UPPER = /([a-z\\d])([A-Z])/g;\n\n/** Boundary between an acronym and the start of the next word. */\nconst ACRONYM_BOUNDARY = /([A-Z]+)([A-Z][a-z])/g;\n\n/** Any explicit separator (underscore, hyphen, whitespace). */\nconst SEPARATORS = /[_\\-\\s]+/;\n\n/**\n * Split any casing convention into lowercase word fragments.\n *\n * Handles snake_case, kebab-case, CONSTANT_CASE, camelCase, PascalCase,\n * and acronyms like `\"XMLHttpRequest\"` → `[\"xml\", \"http\", \"request\"]`.\n */\nfunction splitWords(str: string): string[] {\n\tif (!str) return [];\n\treturn str\n\t\t.replace(LOWER_TO_UPPER, \"$1\\x00$2\")\n\t\t.replace(ACRONYM_BOUNDARY, \"$1\\x00$2\")\n\t\t.split(SEPARATORS)\n\t\t.join(\"\\x00\")\n\t\t.split(\"\\x00\")\n\t\t.filter(Boolean)\n\t\t.map((w) => w.toLowerCase());\n}\n\n// ---------------------------------------------------------------------------\n// Join helpers — one per target format\n// ---------------------------------------------------------------------------\n\nfunction joinCamel(words: string[]): string {\n\tif (words.length === 0) return \"\";\n\treturn (\n\t\twords[0] + words.slice(1).reduce((acc, w) => acc + w.charAt(0).toUpperCase() + w.slice(1), \"\")\n\t);\n}\n\nfunction joinSnake(words: string[]): string {\n\treturn words.join(\"_\");\n}\n\nfunction joinKebab(words: string[]): string {\n\treturn words.join(\"-\");\n}\n\nfunction joinPascal(words: string[]): string {\n\treturn words.reduce((acc, w) => acc + w.charAt(0).toUpperCase() + w.slice(1), \"\");\n}\n\nfunction joinConstant(words: string[]): string {\n\treturn words.map((w) => w.toUpperCase()).join(\"_\");\n}\n\n// ---------------------------------------------------------------------------\n// Deep key transformation\n// ---------------------------------------------------------------------------\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst proto = Object.getPrototypeOf(value) as unknown;\n\treturn proto === Object.prototype || proto === null;\n}\n\nfunction deepTransformKeys(data: unknown, transform: (key: string) => string): unknown {\n\tif (Array.isArray(data)) {\n\t\treturn data.map((item) => deepTransformKeys(item, transform));\n\t}\n\tif (!isPlainObject(data)) return data;\n\n\tconst out: Record<string, unknown> = {};\n\tfor (const key of Object.keys(data)) {\n\t\tout[transform(key)] = deepTransformKeys(data[key], transform);\n\t}\n\treturn out;\n}\n\n// ---------------------------------------------------------------------------\n// String-level transforms (public)\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a string to camelCase.\n *\n * @example\n * camelCase('user_name') // 'userName'\n * camelCase('XMLParser') // 'xmlParser'\n * camelCase('content-type') // 'contentType'\n */\nexport function camelCase<S extends string>(str: S): CamelCase<S> {\n\treturn joinCamel(splitWords(str)) as CamelCase<S>;\n}\n\n/**\n * Convert a string to snake_case.\n *\n * @example\n * snakeCase('userName') // 'user_name'\n * snakeCase('XMLParser') // 'xml_parser'\n * snakeCase('content-type') // 'content_type'\n */\nexport function snakeCase<S extends string>(str: S): SnakeCase<S> {\n\treturn joinSnake(splitWords(str)) as SnakeCase<S>;\n}\n\n/**\n * Convert a string to kebab-case.\n *\n * @example\n * kebabCase('userName') // 'user-name'\n * kebabCase('XMLParser') // 'xml-parser'\n * kebabCase('user_name') // 'user-name'\n */\nexport function kebabCase<S extends string>(str: S): KebabCase<S> {\n\treturn joinKebab(splitWords(str)) as KebabCase<S>;\n}\n\n/**\n * Convert a string to PascalCase.\n *\n * @example\n * pascalCase('user_name') // 'UserName'\n * pascalCase('XMLParser') // 'XmlParser'\n * pascalCase('kebab-case') // 'KebabCase'\n */\nexport function pascalCase<S extends string>(str: S): PascalCase<S> {\n\treturn joinPascal(splitWords(str)) as PascalCase<S>;\n}\n\n/**\n * Convert a string to CONSTANT_CASE.\n *\n * @example\n * constantCase('userName') // 'USER_NAME'\n * constantCase('XMLParser') // 'XML_PARSER'\n * constantCase('kebab-case') // 'KEBAB_CASE'\n */\nexport function constantCase<S extends string>(str: S): ConstantCase<S> {\n\treturn joinConstant(splitWords(str)) as ConstantCase<S>;\n}\n\n// ---------------------------------------------------------------------------\n// Object-level transforms (public)\n// ---------------------------------------------------------------------------\n\n/**\n * Deeply transform all keys of an object (or array of objects) to camelCase.\n * Primitives, Dates, RegExps, Maps, Sets, and other non-plain objects are preserved.\n *\n * @example\n * const result = toCamelCase({ user_name: 'Alice', billing_address: { zip_code: '75001' } })\n * result.userName // 'Alice'\n * result.billingAddress.zipCode // '75001'\n */\nexport function toCamelCase<T>(data: T): CamelCaseKeys<T> {\n\treturn deepTransformKeys(data, (k) => joinCamel(splitWords(k))) as CamelCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to snake_case.\n *\n * @example\n * const result = toSnakeCase({ userName: 'Alice', billingAddress: { zipCode: '75001' } })\n * result.user_name // 'Alice'\n * result.billing_address.zip_code // '75001'\n */\nexport function toSnakeCase<T>(data: T): SnakeCaseKeys<T> {\n\treturn deepTransformKeys(data, (k) => joinSnake(splitWords(k))) as SnakeCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to kebab-case.\n *\n * @example\n * const result = toKebabCase({ userName: 'Alice' })\n * result['user-name'] // 'Alice'\n */\nexport function toKebabCase<T>(data: T): KebabCaseKeys<T> {\n\treturn deepTransformKeys(data, (k) => joinKebab(splitWords(k))) as KebabCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to PascalCase.\n *\n * @example\n * const result = toPascalCase({ user_name: 'Alice' })\n * result.UserName // 'Alice'\n */\nexport function toPascalCase<T>(data: T): PascalCaseKeys<T> {\n\treturn deepTransformKeys(data, (k) => joinPascal(splitWords(k))) as PascalCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to CONSTANT_CASE.\n *\n * @example\n * const result = toConstantCase({ userName: 'Alice' })\n * result.USER_NAME // 'Alice'\n */\nexport function toConstantCase<T>(data: T): ConstantCaseKeys<T> {\n\treturn deepTransformKeys(data, (k) => joinConstant(splitWords(k))) as ConstantCaseKeys<T>;\n}\n"]}
1
+ {"version":3,"sources":["../src/transform.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n CamelCase,\n CamelCaseKeys,\n ConstantCase,\n ConstantCaseKeys,\n KebabCase,\n KebabCaseKeys,\n PascalCase,\n PascalCaseKeys,\n SnakeCase,\n SnakeCaseKeys,\n} from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Word splitting — mirrors the type-level SplitWords<S>\n// ---------------------------------------------------------------------------\n\n/** Boundary between a lowercase letter (or digit) and an uppercase letter. */\nconst LOWER_TO_UPPER = /([a-z\\d])([A-Z])/g;\n\n/** Boundary between an acronym and the start of the next word. */\nconst ACRONYM_BOUNDARY = /([A-Z]+)([A-Z][a-z])/g;\n\n/** Any explicit separator (underscore, hyphen, whitespace). */\nconst SEPARATORS = /[_\\-\\s]+/;\n\n/**\n * Split any casing convention into lowercase word fragments.\n *\n * Handles snake_case, kebab-case, CONSTANT_CASE, camelCase, PascalCase,\n * and acronyms like `\"XMLHttpRequest\"` → `[\"xml\", \"http\", \"request\"]`.\n */\nfunction splitWords(str: string): string[] {\n if (!str) return [];\n return str\n .replace(LOWER_TO_UPPER, \"$1\\x00$2\")\n .replace(ACRONYM_BOUNDARY, \"$1\\x00$2\")\n .split(SEPARATORS)\n .join(\"\\x00\")\n .split(\"\\x00\")\n .filter(Boolean)\n .map((w) => w.toLowerCase());\n}\n\n// ---------------------------------------------------------------------------\n// Join helpers — one per target format\n// ---------------------------------------------------------------------------\n\nfunction joinCamel(words: string[]): string {\n if (words.length === 0) return \"\";\n return (\n words[0] +\n words\n .slice(1)\n .reduce((acc, w) => acc + w.charAt(0).toUpperCase() + w.slice(1), \"\")\n );\n}\n\nfunction joinSnake(words: string[]): string {\n return words.join(\"_\");\n}\n\nfunction joinKebab(words: string[]): string {\n return words.join(\"-\");\n}\n\nfunction joinPascal(words: string[]): string {\n return words.reduce(\n (acc, w) => acc + w.charAt(0).toUpperCase() + w.slice(1),\n \"\",\n );\n}\n\nfunction joinConstant(words: string[]): string {\n return words.map((w) => w.toUpperCase()).join(\"_\");\n}\n\n// ---------------------------------------------------------------------------\n// Deep key transformation\n// ---------------------------------------------------------------------------\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null) return false;\n const proto = Object.getPrototypeOf(value) as unknown;\n return proto === Object.prototype || proto === null;\n}\n\nfunction deepTransformKeys(\n data: unknown,\n transform: (key: string) => string,\n): unknown {\n if (Array.isArray(data)) {\n return data.map((item) => deepTransformKeys(item, transform));\n }\n if (!isPlainObject(data)) return data;\n\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(data)) {\n out[transform(key)] = deepTransformKeys(data[key], transform);\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// String-level transforms (public)\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a string to camelCase.\n *\n * @example\n * camelCase('user_name') // 'userName'\n * camelCase('XMLParser') // 'xmlParser'\n * camelCase('content-type') // 'contentType'\n */\nexport function camelCase<S extends string>(str: S): CamelCase<S> {\n return joinCamel(splitWords(str)) as CamelCase<S>;\n}\n\n/**\n * Convert a string to snake_case.\n *\n * @example\n * snakeCase('userName') // 'user_name'\n * snakeCase('XMLParser') // 'xml_parser'\n * snakeCase('content-type') // 'content_type'\n */\nexport function snakeCase<S extends string>(str: S): SnakeCase<S> {\n return joinSnake(splitWords(str)) as SnakeCase<S>;\n}\n\n/**\n * Convert a string to kebab-case.\n *\n * @example\n * kebabCase('userName') // 'user-name'\n * kebabCase('XMLParser') // 'xml-parser'\n * kebabCase('user_name') // 'user-name'\n */\nexport function kebabCase<S extends string>(str: S): KebabCase<S> {\n return joinKebab(splitWords(str)) as KebabCase<S>;\n}\n\n/**\n * Convert a string to PascalCase.\n *\n * @example\n * pascalCase('user_name') // 'UserName'\n * pascalCase('XMLParser') // 'XmlParser'\n * pascalCase('kebab-case') // 'KebabCase'\n */\nexport function pascalCase<S extends string>(str: S): PascalCase<S> {\n return joinPascal(splitWords(str)) as PascalCase<S>;\n}\n\n/**\n * Convert a string to CONSTANT_CASE.\n *\n * @example\n * constantCase('userName') // 'USER_NAME'\n * constantCase('XMLParser') // 'XML_PARSER'\n * constantCase('kebab-case') // 'KEBAB_CASE'\n */\nexport function constantCase<S extends string>(str: S): ConstantCase<S> {\n return joinConstant(splitWords(str)) as ConstantCase<S>;\n}\n\n// ---------------------------------------------------------------------------\n// Object-level transforms (public)\n// ---------------------------------------------------------------------------\n\n/**\n * Deeply transform all keys of an object (or array of objects) to camelCase.\n * Primitives, Dates, RegExps, Maps, Sets, and other non-plain objects are preserved.\n *\n * @example\n * const result = toCamelCase({ user_name: 'Alice', billing_address: { zip_code: '75001' } })\n * result.userName // 'Alice'\n * result.billingAddress.zipCode // '75001'\n */\nexport function toCamelCase<T>(data: T): CamelCaseKeys<T> {\n return deepTransformKeys(data, (k) =>\n joinCamel(splitWords(k)),\n ) as CamelCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to snake_case.\n *\n * @example\n * const result = toSnakeCase({ userName: 'Alice', billingAddress: { zipCode: '75001' } })\n * result.user_name // 'Alice'\n * result.billing_address.zip_code // '75001'\n */\nexport function toSnakeCase<T>(data: T): SnakeCaseKeys<T> {\n return deepTransformKeys(data, (k) =>\n joinSnake(splitWords(k)),\n ) as SnakeCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to kebab-case.\n *\n * @example\n * const result = toKebabCase({ userName: 'Alice' })\n * result['user-name'] // 'Alice'\n */\nexport function toKebabCase<T>(data: T): KebabCaseKeys<T> {\n return deepTransformKeys(data, (k) =>\n joinKebab(splitWords(k)),\n ) as KebabCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to PascalCase.\n *\n * @example\n * const result = toPascalCase({ user_name: 'Alice' })\n * result.UserName // 'Alice'\n */\nexport function toPascalCase<T>(data: T): PascalCaseKeys<T> {\n return deepTransformKeys(data, (k) =>\n joinPascal(splitWords(k)),\n ) as PascalCaseKeys<T>;\n}\n\n/**\n * Deeply transform all keys of an object (or array of objects) to CONSTANT_CASE.\n *\n * @example\n * const result = toConstantCase({ userName: 'Alice' })\n * result.USER_NAME // 'Alice'\n */\nexport function toConstantCase<T>(data: T): ConstantCaseKeys<T> {\n return deepTransformKeys(data, (k) =>\n joinConstant(splitWords(k)),\n ) as ConstantCaseKeys<T>;\n}\n"]}
package/package.json CHANGED
@@ -1,77 +1,77 @@
1
1
  {
2
- "name": "casemorph",
3
- "version": "0.1.0",
4
- "description": "Type-safe deep case transformation for objects — snake_case, camelCase, kebab-case, PascalCase, CONSTANT_CASE with full TypeScript inference",
5
- "author": "Ludovic Blondon",
6
- "license": "MIT",
7
- "type": "module",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js",
12
- "require": "./dist/index.cjs"
13
- }
14
- },
15
- "main": "./dist/index.cjs",
16
- "module": "./dist/index.js",
17
- "types": "./dist/index.d.ts",
18
- "files": ["dist", "README.md", "LICENSE", "CHANGELOG.md"],
19
- "sideEffects": false,
20
- "engines": {
21
- "node": ">=20"
22
- },
23
- "keywords": [
24
- "typescript",
25
- "camelcase",
26
- "snake-case",
27
- "kebab-case",
28
- "pascal-case",
29
- "case-conversion",
30
- "deep-transform",
31
- "type-safe",
32
- "object-keys",
33
- "naming-convention",
34
- "camelcase-keys",
35
- "snakecase-keys"
36
- ],
37
- "scripts": {
38
- "build": "tsup",
39
- "dev": "tsup --watch",
40
- "test": "vitest run",
41
- "test:watch": "vitest",
42
- "test:coverage": "vitest run --coverage",
43
- "test:types": "vitest run --typecheck.only",
44
- "lint": "biome lint .",
45
- "format": "biome format --write .",
46
- "format:check": "biome format .",
47
- "check": "biome check .",
48
- "check:fix": "biome check --fix .",
49
- "typecheck": "tsc --noEmit",
50
- "bench": "tsx benchmarks/bench.ts",
51
- "size": "size-limit",
52
- "prepublishOnly": "npm run check && npm run typecheck && npm run test && npm run build",
53
- "ci": "npm run check && npm run typecheck && npm run test:coverage && npm run build && npm run size"
54
- },
55
- "repository": {
56
- "type": "git",
57
- "url": "https://github.com/best-package/casemorph.git"
58
- },
59
- "size-limit": [
60
- {
61
- "path": "dist/index.js",
62
- "limit": "2 KB"
63
- }
64
- ],
65
- "devDependencies": {
66
- "@biomejs/biome": "^1",
67
- "@size-limit/preset-small-lib": "^11",
68
- "@vitest/coverage-v8": "^4",
69
- "expect-type": "^1",
70
- "size-limit": "^11",
71
- "tinybench": "^3",
72
- "tsup": "^8",
73
- "tsx": "^4",
74
- "typescript": "~5.8",
75
- "vitest": "^4"
76
- }
2
+ "name": "casemorph",
3
+ "version": "0.1.1",
4
+ "description": "Type-safe deep case transformation for objects — snake_case, camelCase, kebab-case, PascalCase, CONSTANT_CASE with full TypeScript inference",
5
+ "author": "Ludovic Blondon",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "main": "./dist/index.cjs",
16
+ "module": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "files": ["dist", "README.md", "LICENSE", "CHANGELOG.md"],
19
+ "sideEffects": false,
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "keywords": [
24
+ "typescript",
25
+ "camelcase",
26
+ "snake-case",
27
+ "kebab-case",
28
+ "pascal-case",
29
+ "case-conversion",
30
+ "deep-transform",
31
+ "type-safe",
32
+ "object-keys",
33
+ "naming-convention",
34
+ "camelcase-keys",
35
+ "snakecase-keys"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsup",
39
+ "dev": "tsup --watch",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest",
42
+ "test:coverage": "vitest run --coverage",
43
+ "test:types": "vitest run --typecheck.only",
44
+ "lint": "biome lint .",
45
+ "format": "biome format --write .",
46
+ "format:check": "biome format .",
47
+ "check": "biome check .",
48
+ "check:fix": "biome check --fix .",
49
+ "typecheck": "tsc --noEmit",
50
+ "bench": "tsx benchmarks/bench.ts",
51
+ "size": "size-limit",
52
+ "prepublishOnly": "npm run check && npm run typecheck && npm run test && npm run build",
53
+ "ci": "npm run check && npm run typecheck && npm run test:coverage && npm run build && npm run size"
54
+ },
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "https://github.com/Ludovic-Blondon/casemorph.git"
58
+ },
59
+ "size-limit": [
60
+ {
61
+ "path": "dist/index.js",
62
+ "limit": "2 KB"
63
+ }
64
+ ],
65
+ "devDependencies": {
66
+ "@biomejs/biome": "^1",
67
+ "@size-limit/preset-small-lib": "^11",
68
+ "@vitest/coverage-v8": "^4",
69
+ "expect-type": "^1",
70
+ "size-limit": "^11",
71
+ "tinybench": "^3",
72
+ "tsup": "^8",
73
+ "tsx": "^4",
74
+ "typescript": "~5.8",
75
+ "vitest": "^4"
76
+ }
77
77
  }