@skmtc/core 0.0.828 โ†’ 0.0.830

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
@@ -1,89 +1,116 @@
1
- [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-green.svg)](https://opensource.org/licenses/Apache-2.0)
2
- [![Deno](https://img.shields.io/badge/Deno-2.5+-green.svg)](https://deno.land/)
3
- [![Discord](https://img.shields.io/badge/Discord-join%20chat-1dce73.svg)](https://discord.com/invite/Mg88C8Xu5Y)
4
-
5
1
  <p align="center">
6
- <img src="https://raw.githubusercontent.com/skmtc/skmtc/refs/heads/main/skmtc.svg" />
2
+ <img src="./skmtc.svg" />
7
3
  </p>
8
4
 
9
- ## Problems We Solve
10
-
11
- **Stop writing the same code over and over.** Generate TypeScript types, API clients, validation schemas, and more directly from your OpenAPI specifications.
5
+ **Skmtc is a declarative code generation framework**. It lets you generate TypeScript code from OpenAPI schemas. Specify the exact output you want using string templates and get it in seconds.
12
6
 
13
- ### Manual Type Definitions
14
- **Pain:** Hours spent manually writing TypeScript interfaces from OpenAPI docs, fighting typos and sync issues.
15
- **Solution:** Generate accurate types with perfect synchronization.
7
+ ## โœจ Features
16
8
 
17
- ### Schema Evolution Management
18
- **Pain:** API changes break frontend types, validation, mocks, and docs across dozens of files.
19
- **Solution:** Single regeneration command updates all artifacts simultaneously.
9
+ - ๐ŸŽ๏ธ **Fast** - Generates 550k+ tokens per second
10
+ - ๐Ÿงต **Easy to edit code generators** - Outputs specified using string templates, not ASTs
11
+ - ๐Ÿฅž **Stackable generators** - Combine multiple generators to generate deep functionality
12
+ - ๐Ÿ—„๏ธ **Use your own code conventions** - Full control over naming and file structure
20
13
 
21
- ### Type-Unsafe Runtime API Integration
22
- **Pain:** No compile-time guarantees that API calls match backend schemas.
23
- **Solution:** End-to-end type safety from schema through clients to UI components.
14
+ ## ๐Ÿš€ Quick Start
24
15
 
25
- ## Quick Start
16
+ ```bash
17
+ # Run directly with npx
18
+ npx skmtc
26
19
 
27
- **Learning Outcome:** Generate TypeScript types from an OpenAPI schema in 5 minutes.
20
+ ```
28
21
 
29
- ### Install
22
+ ### Running code generators
30
23
 
31
24
  ```bash
32
- # Import in your Deno project
33
- import { toArtifacts } from "jsr:@skmtc/core"
25
+ npx skmtc generate @skmtc/supabase-backend https://petstore3.swagger.io/api/v3/openapi.json
26
+ # Generated 9 files (507 lines, 3,383 tokens) in 9ms
27
+
28
+ npx skmtc generate @skmtc/supabase-react-client https://raw.githubusercontent.com/cloudflare/api-schemas/refs/heads/main/openapi.json
29
+ # Generated 6,797 files (104,752 lines, 1,635,227 tokens) in 2,969ms
34
30
  ```
35
31
 
36
- ### Generate Types
32
+ ### Example generator code
37
33
 
38
34
  ```typescript
39
- import { toArtifacts } from "@skmtc/core"
40
- import typescriptGenerator from "@skmtc/gen-typescript"
41
-
42
- // Your OpenAPI v3 schema
43
- const schema = {
44
- openapi: "3.0.0",
45
- info: { title: "API", version: "1.0.0" },
46
- components: {
47
- schemas: {
48
- User: {
49
- type: "object",
50
- properties: {
51
- id: { type: "string" },
52
- name: { type: "string" }
53
- }
54
- }
55
- }
35
+ import { ZodInsertable } from '@skmtc/gen-zod'
36
+
37
+ class ZodFetch extends BaseTransformer {
38
+ zodName: string;
39
+
40
+ constructor({context, operation, settings}){
41
+ super({context, operation, settings})
42
+
43
+ // Generate Zod schema for API response and insert it into current file
44
+ const response = operation.toSuccessResponse()?.resolve().toSchema()
45
+ const zodResponse = this.insert(ZodInsertable, response)
46
+
47
+ // Grab Zod schema name to use in output code
48
+ this.zodName = zodResponse.identifier.name
56
49
  }
57
- }
58
50
 
59
- // Generate TypeScript types
60
- const artifacts = await toArtifacts({
61
- schema,
62
- generators: [typescriptGenerator]
63
- })
51
+ // Define code output
52
+ toString(){
53
+ return `() => {
54
+ const res = await fetch('${this.operation.path}')
55
+ const data = await res.json()
64
56
 
65
- // Write generated files
66
- for (const [path, content] of artifacts) {
67
- await Deno.writeTextFile(path, content)
57
+ return ${zodName}.parse(data)
58
+ }`
59
+ }
68
60
  }
69
61
  ```
70
62
 
71
- ### Result
63
+ ## ๐Ÿ“ฆ Available Generators
72
64
 
73
- ```typescript
74
- // @/types/user.generated.ts
75
- export interface User {
76
- id?: string
77
- name?: string
78
- }
79
- ```
65
+ Choose from our growing collection of generators, combone them or create your own:
80
66
 
81
- ## What's Next
67
+ - **Tanstack Query** - React Query hooks with Zod validation
68
+ - **MSW** - Mock Service Worker handlers from OpenAPI examples
69
+ - **Zod Schemas** - Runtime validation schemas
70
+ - **TypeScript Types** - Pure type definitions
71
+ - **Supabase/Hono Functions** - Edge function handlers
72
+ - See full list at https://github.com/skmtc/skmtc-generators
82
73
 
83
- - **[Getting Started โ†’](docs/getting-started/installation.md)** - Complete setup guide
84
- - **[How-To Guides โ†’](docs/how-to/)** - Task-oriented tutorials
85
- - **[API Reference โ†’](docs/api/)** - Complete technical documentation
74
+ ## โ“ FAQ
86
75
 
87
- ## License
76
+ ### **What OpenAPI versions are supported?**
77
+ Skmtc supports OpenAPI v3.0. Swagger 2.0 and OpenAPI v3.1 are automatically converted to OpenAPI v3.0.
88
78
 
89
- [Apache-2.0](LICENSE)
79
+ ### **Can I customize the generated code?**
80
+ Yes! Each Transformer specifies its output using plain string templates, which means you can
81
+ edit them as would you edit any other code.
82
+
83
+ ### **Can I use this with my existing React app?**
84
+ Yes! Skmtc generates standalone code that integrates with any React application. The generated components work with your existing setup.
85
+
86
+ ### **How does this compare to OpenAPI Generator?**
87
+ Skmtc is the only code generation framework that provides full control over the generated code. You are not limited by library-specific settings and you do not need to write complex AST code.
88
+
89
+ ### **Does it work with Next.js/Remix/Vite?**
90
+ Yes! The generated code is framework-agnostic TypeScript that works with any build tool or library.
91
+
92
+ ## ๐Ÿค Contributing
93
+
94
+ We welcome contributions! Check out our [Contributing Guide](CONTRIBUTING.md) to get started.
95
+
96
+ <!-- ## ๐Ÿ“š Documentation
97
+
98
+ - [Full Documentation](https://docs.skmtc.dev)
99
+ - [API Reference](https://docs.skmtc.dev/api)
100
+ - [Custom Generators Guide](https://docs.skmtc.dev/generators)
101
+ - [Examples](https://github.com/skmtc/skmtc/tree/main/examples) -->
102
+
103
+ ## ๐Ÿ›Ÿ Support
104
+
105
+ - [GitHub Issues](https://github.com/skmtc/skmtc/issues) - Bug reports and feature requests
106
+ - [Discord Community](https://discord.com/invite/Mg88C8Xu5Y) - Get help and share your experience
107
+
108
+ ## ๐Ÿ“„ License
109
+
110
+ Apache 2.0 ยฉ [Skmtc Contributors](LICENSE.md)
111
+
112
+ ---
113
+
114
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-green.svg)](https://opensource.org/licenses/Apache-2.0)
115
+ [![Deno](https://img.shields.io/badge/Deno-2.5+-green.svg)](https://deno.land/)
116
+ [![Discord](https://img.shields.io/badge/Discord-join%20chat-1dce73.svg)](https://discord.com/invite/Mg88C8Xu5Y)
@@ -77,6 +77,8 @@ export declare const decapitalize: (str: string) => string;
77
77
  type CamelCaseOptions = {
78
78
  /** Whether to capitalize the first character (PascalCase) */
79
79
  upperFirst?: boolean;
80
+ /** Whether to retain underscores in place when converting strings */
81
+ retainUnderscores?: boolean;
80
82
  };
81
83
  /**
82
84
  * Converts a string to camelCase, removing non-alphanumeric characters.
@@ -88,6 +90,7 @@ type CamelCaseOptions = {
88
90
  * @param str - The string to convert to camelCase
89
91
  * @param options - Options to control the conversion
90
92
  * @param options.upperFirst - Whether to capitalize the first character (default: false)
93
+ * @param options.retainUnderscores - Whether to retain underscores in place (default: false)
91
94
  * @returns The camelCase version of the input string
92
95
  *
93
96
  * @example Basic usage
@@ -110,7 +113,15 @@ type CamelCaseOptions = {
110
113
  * camelCase('XML_HTTP_Request'); // 'xmlHttpRequest'
111
114
  * camelCase('2fa-enabled'); // '2faEnabled'
112
115
  * ```
116
+ *
117
+ * @example Retaining underscores (retainUnderscores)
118
+ * ```typescript
119
+ * camelCase('user_name', { retainUnderscores: true }); // 'user_name'
120
+ * camelCase('api-key_token', { retainUnderscores: true }); // 'apiKey_token'
121
+ * camelCase('get_user_by_id', { retainUnderscores: true }); // 'get_user_by_id'
122
+ * camelCase('user_profile', { upperFirst: true, retainUnderscores: true }); // 'User_profile'
123
+ * ```
113
124
  */
114
- export declare const camelCase: (str: string, { upperFirst }?: CamelCaseOptions) => string;
125
+ export declare const camelCase: (str: string, { upperFirst, retainUnderscores }?: CamelCaseOptions) => string;
115
126
  export {};
116
127
  //# sourceMappingURL=strings.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/helpers/strings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH;;;;;;;;;;;;GAYG;AACH,OAAO,sBAAsB,CAAC;AAE9B,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,MAExC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,MAE1C,CAAA;AAED;;GAEG;AACH,KAAK,gBAAgB,GAAG;IACtB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,EAAE,iBAAgB,gBAAqB,KAAG,MAgB9E,CAAA"}
1
+ {"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/helpers/strings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH;;;;;;;;;;;;GAYG;AACH,OAAO,sBAAsB,CAAC;AAE9B,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,MAExC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,MAE1C,CAAA;AAED;;GAEG;AACH,KAAK,gBAAgB,GAAG;IACtB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,EAAE,oCAAmC,gBAAqB,KAAG,MA+BjG,CAAA"}
@@ -85,6 +85,7 @@ export const decapitalize = (str) => {
85
85
  * @param str - The string to convert to camelCase
86
86
  * @param options - Options to control the conversion
87
87
  * @param options.upperFirst - Whether to capitalize the first character (default: false)
88
+ * @param options.retainUnderscores - Whether to retain underscores in place (default: false)
88
89
  * @returns The camelCase version of the input string
89
90
  *
90
91
  * @example Basic usage
@@ -107,16 +108,38 @@ export const decapitalize = (str) => {
107
108
  * camelCase('XML_HTTP_Request'); // 'xmlHttpRequest'
108
109
  * camelCase('2fa-enabled'); // '2faEnabled'
109
110
  * ```
111
+ *
112
+ * @example Retaining underscores (retainUnderscores)
113
+ * ```typescript
114
+ * camelCase('user_name', { retainUnderscores: true }); // 'user_name'
115
+ * camelCase('api-key_token', { retainUnderscores: true }); // 'apiKey_token'
116
+ * camelCase('get_user_by_id', { retainUnderscores: true }); // 'get_user_by_id'
117
+ * camelCase('user_profile', { upperFirst: true, retainUnderscores: true }); // 'User_profile'
118
+ * ```
110
119
  */
111
- export const camelCase = (str, { upperFirst } = {}) => {
112
- let doCapitalize = Boolean(upperFirst);
113
- const camelCased = str.replace(/[^a-zA-Z0-9]+([a-zA-Z0-9]+)/g, (_, matched) => {
114
- if (doCapitalize) {
115
- return capitalize(matched);
120
+ export const camelCase = (str, { upperFirst, retainUnderscores } = {}) => {
121
+ // Check if string starts with a delimiter
122
+ const startsWithDelimiter = retainUnderscores
123
+ ? /^[^a-zA-Z0-9_]/.test(str) // Non-alphanumeric except underscore
124
+ : /^[^a-zA-Z0-9]/.test(str); // Non-alphanumeric including underscore
125
+ let isFirstMatch = true;
126
+ // Determine delimiter pattern based on retainUnderscores
127
+ const delimiterPattern = retainUnderscores
128
+ ? /[^a-zA-Z0-9_]+([a-zA-Z0-9_]+)/g // Treat underscore as regular character
129
+ : /[^a-zA-Z0-9]+([a-zA-Z0-9]+)/g; // Treat underscore as delimiter
130
+ const camelCased = str.replace(delimiterPattern, (_, matched) => {
131
+ // If string starts with delimiter and this is the first match:
132
+ // - Capitalize only if upperFirst is true
133
+ // Otherwise, always capitalize (word comes after delimiter)
134
+ if (isFirstMatch && startsWithDelimiter && !upperFirst) {
135
+ isFirstMatch = false;
136
+ return matched;
116
137
  }
117
- doCapitalize = true;
118
- return matched;
138
+ isFirstMatch = false;
139
+ return capitalize(matched);
119
140
  });
120
- const cleaned = camelCased.replaceAll(/[^a-z0-9]*/gi, '');
141
+ // Clean non-alphanumeric characters (preserving underscores if retainUnderscores)
142
+ const cleanPattern = retainUnderscores ? /[^a-zA-Z0-9_]/g : /[^a-zA-Z0-9]/g;
143
+ const cleaned = camelCased.replace(cleanPattern, '');
121
144
  return upperFirst ? capitalize(cleaned) : cleaned;
122
145
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skmtc/core",
3
- "version": "0.0.828",
3
+ "version": "0.0.830",
4
4
  "description": "Skmtc is a declarative code generation framework",
5
5
  "keywords": [
6
6
  "openapi",
@@ -77,6 +77,8 @@ export declare const decapitalize: (str: string) => string;
77
77
  type CamelCaseOptions = {
78
78
  /** Whether to capitalize the first character (PascalCase) */
79
79
  upperFirst?: boolean;
80
+ /** Whether to retain underscores in place when converting strings */
81
+ retainUnderscores?: boolean;
80
82
  };
81
83
  /**
82
84
  * Converts a string to camelCase, removing non-alphanumeric characters.
@@ -88,6 +90,7 @@ type CamelCaseOptions = {
88
90
  * @param str - The string to convert to camelCase
89
91
  * @param options - Options to control the conversion
90
92
  * @param options.upperFirst - Whether to capitalize the first character (default: false)
93
+ * @param options.retainUnderscores - Whether to retain underscores in place (default: false)
91
94
  * @returns The camelCase version of the input string
92
95
  *
93
96
  * @example Basic usage
@@ -110,7 +113,15 @@ type CamelCaseOptions = {
110
113
  * camelCase('XML_HTTP_Request'); // 'xmlHttpRequest'
111
114
  * camelCase('2fa-enabled'); // '2faEnabled'
112
115
  * ```
116
+ *
117
+ * @example Retaining underscores (retainUnderscores)
118
+ * ```typescript
119
+ * camelCase('user_name', { retainUnderscores: true }); // 'user_name'
120
+ * camelCase('api-key_token', { retainUnderscores: true }); // 'apiKey_token'
121
+ * camelCase('get_user_by_id', { retainUnderscores: true }); // 'get_user_by_id'
122
+ * camelCase('user_profile', { upperFirst: true, retainUnderscores: true }); // 'User_profile'
123
+ * ```
113
124
  */
114
- export declare const camelCase: (str: string, { upperFirst }?: CamelCaseOptions) => string;
125
+ export declare const camelCase: (str: string, { upperFirst, retainUnderscores }?: CamelCaseOptions) => string;
115
126
  export {};
116
127
  //# sourceMappingURL=strings.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/helpers/strings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH;;;;;;;;;;;;GAYG;AACH,OAAO,sBAAsB,CAAC;AAE9B,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,MAExC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,MAE1C,CAAA;AAED;;GAEG;AACH,KAAK,gBAAgB,GAAG;IACtB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,EAAE,iBAAgB,gBAAqB,KAAG,MAgB9E,CAAA"}
1
+ {"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/helpers/strings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH;;;;;;;;;;;;GAYG;AACH,OAAO,sBAAsB,CAAC;AAE9B,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,MAExC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,MAE1C,CAAA;AAED;;GAEG;AACH,KAAK,gBAAgB,GAAG;IACtB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,EAAE,oCAAmC,gBAAqB,KAAG,MA+BjG,CAAA"}
@@ -90,6 +90,7 @@ exports.decapitalize = decapitalize;
90
90
  * @param str - The string to convert to camelCase
91
91
  * @param options - Options to control the conversion
92
92
  * @param options.upperFirst - Whether to capitalize the first character (default: false)
93
+ * @param options.retainUnderscores - Whether to retain underscores in place (default: false)
93
94
  * @returns The camelCase version of the input string
94
95
  *
95
96
  * @example Basic usage
@@ -112,17 +113,39 @@ exports.decapitalize = decapitalize;
112
113
  * camelCase('XML_HTTP_Request'); // 'xmlHttpRequest'
113
114
  * camelCase('2fa-enabled'); // '2faEnabled'
114
115
  * ```
116
+ *
117
+ * @example Retaining underscores (retainUnderscores)
118
+ * ```typescript
119
+ * camelCase('user_name', { retainUnderscores: true }); // 'user_name'
120
+ * camelCase('api-key_token', { retainUnderscores: true }); // 'apiKey_token'
121
+ * camelCase('get_user_by_id', { retainUnderscores: true }); // 'get_user_by_id'
122
+ * camelCase('user_profile', { upperFirst: true, retainUnderscores: true }); // 'User_profile'
123
+ * ```
115
124
  */
116
- const camelCase = (str, { upperFirst } = {}) => {
117
- let doCapitalize = Boolean(upperFirst);
118
- const camelCased = str.replace(/[^a-zA-Z0-9]+([a-zA-Z0-9]+)/g, (_, matched) => {
119
- if (doCapitalize) {
120
- return (0, exports.capitalize)(matched);
125
+ const camelCase = (str, { upperFirst, retainUnderscores } = {}) => {
126
+ // Check if string starts with a delimiter
127
+ const startsWithDelimiter = retainUnderscores
128
+ ? /^[^a-zA-Z0-9_]/.test(str) // Non-alphanumeric except underscore
129
+ : /^[^a-zA-Z0-9]/.test(str); // Non-alphanumeric including underscore
130
+ let isFirstMatch = true;
131
+ // Determine delimiter pattern based on retainUnderscores
132
+ const delimiterPattern = retainUnderscores
133
+ ? /[^a-zA-Z0-9_]+([a-zA-Z0-9_]+)/g // Treat underscore as regular character
134
+ : /[^a-zA-Z0-9]+([a-zA-Z0-9]+)/g; // Treat underscore as delimiter
135
+ const camelCased = str.replace(delimiterPattern, (_, matched) => {
136
+ // If string starts with delimiter and this is the first match:
137
+ // - Capitalize only if upperFirst is true
138
+ // Otherwise, always capitalize (word comes after delimiter)
139
+ if (isFirstMatch && startsWithDelimiter && !upperFirst) {
140
+ isFirstMatch = false;
141
+ return matched;
121
142
  }
122
- doCapitalize = true;
123
- return matched;
143
+ isFirstMatch = false;
144
+ return (0, exports.capitalize)(matched);
124
145
  });
125
- const cleaned = camelCased.replaceAll(/[^a-z0-9]*/gi, '');
146
+ // Clean non-alphanumeric characters (preserving underscores if retainUnderscores)
147
+ const cleanPattern = retainUnderscores ? /[^a-zA-Z0-9_]/g : /[^a-zA-Z0-9]/g;
148
+ const cleaned = camelCased.replace(cleanPattern, '');
126
149
  return upperFirst ? (0, exports.capitalize)(cleaned) : cleaned;
127
150
  };
128
151
  exports.camelCase = camelCase;