create-wp-typia 0.1.0

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.
Files changed (90) hide show
  1. package/README.md +33 -0
  2. package/dist/cli.js +87837 -0
  3. package/dist/highlights-eq9cgrbb.scm +604 -0
  4. package/dist/highlights-ghv9g403.scm +205 -0
  5. package/dist/highlights-hk7bwhj4.scm +284 -0
  6. package/dist/highlights-r812a2qc.scm +150 -0
  7. package/dist/highlights-x6tmsnaa.scm +115 -0
  8. package/dist/injections-73j83es3.scm +27 -0
  9. package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
  10. package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
  11. package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
  12. package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
  13. package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
  14. package/lib/entry.js +29 -0
  15. package/lib/node-cli.js +326 -0
  16. package/lib/package-managers.d.ts +29 -0
  17. package/lib/package-managers.js +170 -0
  18. package/lib/scaffold.d.ts +64 -0
  19. package/lib/scaffold.js +565 -0
  20. package/lib/template-registry.d.ts +18 -0
  21. package/lib/template-registry.js +58 -0
  22. package/package.json +64 -0
  23. package/src/cli.ts +329 -0
  24. package/templates/advanced/README.md.mustache +70 -0
  25. package/templates/advanced/block.json.mustache +42 -0
  26. package/templates/advanced/index.js +21 -0
  27. package/templates/advanced/package.json.mustache +48 -0
  28. package/templates/advanced/scripts/generate-migrations.ts.mustache +267 -0
  29. package/templates/advanced/scripts/lib/typia-metadata-core.ts +806 -0
  30. package/templates/advanced/scripts/migration-cli.ts.mustache +260 -0
  31. package/templates/advanced/scripts/sync-types-to-block-json.ts.mustache +25 -0
  32. package/templates/advanced/src/admin/migration-dashboard.tsx.mustache +450 -0
  33. package/templates/advanced/src/components/ErrorBoundary.tsx.mustache +47 -0
  34. package/templates/advanced/src/deprecated.ts.mustache +184 -0
  35. package/templates/advanced/src/edit.tsx.mustache +93 -0
  36. package/templates/advanced/src/hooks/useDebounce.ts.mustache +20 -0
  37. package/templates/advanced/src/hooks/useLocalStorage.ts.mustache +31 -0
  38. package/templates/advanced/src/hooks.ts.mustache +56 -0
  39. package/templates/advanced/src/index.tsx.mustache +16 -0
  40. package/templates/advanced/src/migration-detector.ts.mustache +417 -0
  41. package/templates/advanced/src/migrations/index.ts.mustache +361 -0
  42. package/templates/advanced/src/save.tsx.mustache +40 -0
  43. package/templates/advanced/src/style.scss.mustache +84 -0
  44. package/templates/advanced/src/types/versions.ts.mustache +108 -0
  45. package/templates/advanced/src/types.ts.mustache +45 -0
  46. package/templates/advanced/src/utils/classnames.ts.mustache +51 -0
  47. package/templates/advanced/src/utils/debounce.ts.mustache +37 -0
  48. package/templates/advanced/src/utils/index.ts.mustache +7 -0
  49. package/templates/advanced/src/utils/uuid.ts.mustache +17 -0
  50. package/templates/advanced/src/validators.ts.mustache +39 -0
  51. package/templates/advanced/src/view.ts.mustache +59 -0
  52. package/templates/advanced/tsconfig.json.mustache +9 -0
  53. package/templates/advanced/webpack.config.js.mustache +85 -0
  54. package/templates/basic/package.json.mustache +39 -0
  55. package/templates/basic/scripts/lib/typia-metadata-core.ts +806 -0
  56. package/templates/basic/scripts/sync-types-to-block-json.ts +25 -0
  57. package/templates/basic/src/block.json +51 -0
  58. package/templates/basic/src/edit.tsx +85 -0
  59. package/templates/basic/src/hooks.ts +75 -0
  60. package/templates/basic/src/index.tsx +37 -0
  61. package/templates/basic/src/save.tsx +27 -0
  62. package/templates/basic/src/style.scss +42 -0
  63. package/templates/basic/src/types.ts +47 -0
  64. package/templates/basic/src/validators.ts +39 -0
  65. package/templates/basic/tsconfig.json +20 -0
  66. package/templates/basic/webpack.config.js +85 -0
  67. package/templates/full/package.json.mustache +40 -0
  68. package/templates/full/scripts/lib/typia-metadata-core.ts +806 -0
  69. package/templates/full/scripts/sync-types-to-block-json.ts.mustache +25 -0
  70. package/templates/full/src/block.json.mustache +121 -0
  71. package/templates/full/src/edit.tsx.mustache +300 -0
  72. package/templates/full/src/editor.scss.mustache +251 -0
  73. package/templates/full/src/hooks.ts.mustache +140 -0
  74. package/templates/full/src/index.tsx.mustache +27 -0
  75. package/templates/full/src/save.tsx.mustache +39 -0
  76. package/templates/full/src/style.scss.mustache +224 -0
  77. package/templates/full/src/types.ts.mustache +34 -0
  78. package/templates/full/src/validators.ts.mustache +84 -0
  79. package/templates/full/tsconfig.json.mustache +9 -0
  80. package/templates/full/webpack.config.js.mustache +85 -0
  81. package/templates/interactivity/package.json.mustache +41 -0
  82. package/templates/interactivity/scripts/lib/typia-metadata-core.ts +806 -0
  83. package/templates/interactivity/scripts/sync-types-to-block-json.ts.mustache +25 -0
  84. package/templates/interactivity/src/block.json.mustache +75 -0
  85. package/templates/interactivity/src/edit.tsx.mustache +206 -0
  86. package/templates/interactivity/src/interactivity.ts.mustache +183 -0
  87. package/templates/interactivity/src/save.tsx.mustache +87 -0
  88. package/templates/interactivity/src/types.ts.mustache +29 -0
  89. package/templates/interactivity/tsconfig.json.mustache +9 -0
  90. package/templates/interactivity/webpack.config.js.mustache +85 -0
@@ -0,0 +1,361 @@
1
+ import typia from "typia";
2
+ import {
3
+ {{titleCase}}AttributesV1,
4
+ {{titleCase}}AttributesV2,
5
+ {{titleCase}}AttributesV3,
6
+ {{titleCase}}Attributes,
7
+ BLOCK_VERSIONS
8
+ } from '../types/versions';
9
+ import { generateUUID } from '../utils';
10
+
11
+ export interface MigrationContext {
12
+ originalAttributes: any;
13
+ timestamp: string;
14
+ migrationPath: string[];
15
+ rollbackPossible: boolean;
16
+ }
17
+
18
+ export interface MigrationResult<T> {
19
+ data: T;
20
+ context: MigrationContext;
21
+ rollback: () => any;
22
+ success: boolean;
23
+ error?: Error;
24
+ }
25
+
26
+ /**
27
+ * Migration functions for {{title}} block versions
28
+ */
29
+ }
30
+ /**
31
+ * Migration functions for {{title}} block versions
32
+ */
33
+
34
+ // V1 → V2 Migration
35
+ export function migrateV1ToV2(v1Attrs: {{titleCase}}AttributesV1): MigrationResult<{{titleCase}}AttributesV2> {
36
+ console.log('🔄 Migrating {{title}} from v1 to v2');
37
+ console.log('📄 V1 attributes:', JSON.stringify(v1Attrs, null, 2));
38
+
39
+ const migrated: {{titleCase}}AttributesV2 = {
40
+ content: v1Attrs.content || "",
41
+ alignment: v1Attrs.alignment || "left",
42
+ // New fields with defaults
43
+ isVisible: true,
44
+ className: undefined
45
+ };
46
+
47
+ console.log('🔧 Adding new fields: isVisible=true, className=undefined');
48
+
49
+ // Validate migrated data
50
+ const validation = typia.validate<{{titleCase}}AttributesV2>(migrated);
51
+ if (!validation.success) {
52
+ const error = new Error('V1→V2 migration validation failed');
53
+ console.error('❌ V1→V2 migration validation failed:', validation.errors);
54
+ return {
55
+ data: null,
56
+ context: {
57
+ originalAttributes: v1Attrs,
58
+ timestamp: new Date().toISOString(),
59
+ migrationPath: ['V1', 'V2'],
60
+ rollbackPossible: false
61
+ },
62
+ rollback: () => v1Attrs,
63
+ success: false,
64
+ error
65
+ };
66
+ }
67
+
68
+ console.log('✅ V1→V2 migration successful');
69
+ console.log('📄 V2 attributes:', JSON.stringify(migrated, null, 2));
70
+
71
+ // Create rollback function
72
+ const rollback = (): {{titleCase}}AttributesV1 => {
73
+ return {
74
+ content: migrated.content,
75
+ alignment: migrated.alignment
76
+ };
77
+ };
78
+
79
+ return {
80
+ data: migrated,
81
+ context: {
82
+ originalAttributes: v1Attrs,
83
+ timestamp: new Date().toISOString(),
84
+ migrationPath: ['V1', 'V2'],
85
+ rollbackPossible: true
86
+ },
87
+ rollback,
88
+ success: true
89
+ };
90
+ }
91
+
92
+ // V2 → V3 Migration
93
+ export function migrateV2ToV3(v2Attrs: {{titleCase}}AttributesV2): MigrationResult<{{titleCase}}AttributesV3> {
94
+ console.log('🔄 Migrating {{title}} from v2 to v3');
95
+ console.log('📄 V2 attributes:', JSON.stringify(v2Attrs, null, 2));
96
+
97
+ const migrated: {{titleCase}}AttributesV3 = {
98
+ // New fields
99
+ id: generateUUID(),
100
+ version: 3,
101
+ theme: "default",
102
+
103
+ // Existing fields with potential updates
104
+ content: v2Attrs.content || "",
105
+ alignment: v2Attrs.alignment || "left",
106
+ isVisible: v2Attrs.isVisible ?? true,
107
+
108
+ // Handle className validation change
109
+ className: v2Attrs.className && /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(v2Attrs.className)
110
+ ? v2Attrs.className.slice(0, 50) // Trim to new max length
111
+ : undefined
112
+ };
113
+
114
+ console.log('🔧 Adding new fields:');
115
+ console.log(' - id:', migrated.id);
116
+ console.log(' - version:', migrated.version);
117
+ console.log(' - theme:', migrated.theme);
118
+
119
+ if (v2Attrs.className && !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(v2Attrs.className)) {
120
+ console.log('⚠️ Invalid className format, setting to undefined');
121
+ }
122
+
123
+ if (v2Attrs.className && v2Attrs.className.length > 50) {
124
+ console.log('✂️ Trimming className from', v2Attrs.className.length, 'to 50 characters');
125
+ }
126
+
127
+ // Validate migrated data
128
+ const validation = typia.validate<{{titleCase}}AttributesV3>(migrated);
129
+ if (!validation.success) {
130
+ const error = new Error('V2→V3 migration validation failed');
131
+ console.error('❌ V2→V3 migration validation failed:', validation.errors);
132
+ return {
133
+ data: null,
134
+ context: {
135
+ originalAttributes: v2Attrs,
136
+ timestamp: new Date().toISOString(),
137
+ migrationPath: ['V2', 'V3'],
138
+ rollbackPossible: false
139
+ },
140
+ rollback: () => v2Attrs,
141
+ success: false,
142
+ error
143
+ };
144
+ }
145
+
146
+ console.log('✅ V2→V3 migration successful');
147
+ console.log('📄 V3 attributes:', JSON.stringify(migrated, null, 2));
148
+
149
+ // Create rollback function
150
+ const rollback = (): {{titleCase}}AttributesV2 => {
151
+ return {
152
+ content: migrated.content,
153
+ alignment: migrated.alignment,
154
+ isVisible: migrated.isVisible,
155
+ className: migrated.className
156
+ };
157
+ };
158
+
159
+ return {
160
+ data: migrated,
161
+ context: {
162
+ originalAttributes: v2Attrs,
163
+ timestamp: new Date().toISOString(),
164
+ migrationPath: ['V2', 'V3'],
165
+ rollbackPossible: true
166
+ },
167
+ rollback,
168
+ success: true
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Auto-detect version and migrate to current
174
+ */
175
+ export function autoMigrate(attributes: any): MigrationResult<{{titleCase}}Attributes> {
176
+ // Try to detect version
177
+ const detectedVersion = detectVersion(attributes);
178
+ console.log(`🔍 Detected {{title}} version: ${detectedVersion}`);
179
+
180
+ switch (detectedVersion) {
181
+ case "1.0.0": {
182
+ // V1 → V2 → V3
183
+ const v2Result = migrateV1ToV2(attributes as {{titleCase}}AttributesV1);
184
+ if (!v2Result.success) {
185
+ return v2Result;
186
+ }
187
+
188
+ const v3Result = migrateV2ToV3(v2Result.data);
189
+ if (!v3Result.success) {
190
+ // If V3 migration fails, we still have the V2 data
191
+ return {
192
+ ...v3Result,
193
+ data: v2Result.data as unknown as {{titleCase}}Attributes,
194
+ context: {
195
+ ...v3Result.context,
196
+ migrationPath: [...v2Result.context.migrationPath, ...v3Result.context.migrationPath]
197
+ }
198
+ };
199
+ }
200
+
201
+ return {
202
+ ...v3Result,
203
+ context: {
204
+ ...v3Result.context,
205
+ migrationPath: [...v2Result.context.migrationPath, ...v3Result.context.migrationPath],
206
+ originalAttributes: attributes
207
+ },
208
+ rollback: () => attributes
209
+ };
210
+ }
211
+
212
+
213
+ case "2.0.0": {
214
+ // V2 → V3
215
+ const v3Result = migrateV2ToV3(attributes as {{titleCase}}AttributesV2);
216
+ if (!v3Result.success) {
217
+ return v3Result;
218
+ }
219
+
220
+ return {
221
+ ...v3Result,
222
+ context: {
223
+ ...v3Result.context,
224
+ migrationPath: ['V2', 'V3'],
225
+ originalAttributes: attributes
226
+ },
227
+ rollback: () => attributes
228
+ };
229
+ }
230
+
231
+
232
+ case "3.0.0": {
233
+ // Already current version
234
+ console.log('✅ {{title}} already at current version');
235
+ return {
236
+ data: attributes as {{titleCase}}AttributesV3,
237
+ context: {
238
+ originalAttributes: attributes,
239
+ timestamp: new Date().toISOString(),
240
+ migrationPath: ['V3'],
241
+ rollbackPossible: true
242
+ },
243
+ rollback: () => attributes,
244
+ success: true
245
+ };
246
+ }
247
+
248
+
249
+ default: {
250
+ console.warn('⚠️ Unknown {{title}} version, attempting best-effort migration');
251
+ return attemptBestEffortMigration(attributes);
252
+ }
253
+
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Detect block version based on structure
259
+ */
260
+ function detectVersion(attributes: any): string {
261
+ // Check for V3 features
262
+ if ('id' in attributes || 'version' in attributes || 'theme' in attributes) {
263
+ return "3.0.0";
264
+ }
265
+
266
+ // Check for V2 features
267
+ if ('isVisible' in attributes || 'className' in attributes) {
268
+ return "2.0.0";
269
+ }
270
+
271
+ // Must be V1
272
+ return "1.0.0";
273
+ }
274
+
275
+ /**
276
+ * Best-effort migration for unknown versions
277
+ */
278
+ function attemptBestEffortMigration(attributes: any): MigrationResult<{{titleCase}}Attributes> {
279
+ console.log('🔧 Attempting best-effort migration');
280
+
281
+ const migrated: {{titleCase}}Attributes = {
282
+ id: generateUUID(),
283
+ version: 3,
284
+ content: String(attributes.content || ""),
285
+ alignment: ["left", "center", "right", "justify"].includes(attributes.alignment)
286
+ ? attributes.alignment
287
+ : "left",
288
+ isVisible: Boolean(attributes.isVisible ?? true),
289
+ className: typeof attributes.className === 'string'
290
+ ? attributes.className.slice(0, 50)
291
+ : undefined,
292
+ theme: ["default", "dark", "light", "accent"].includes(attributes.theme)
293
+ ? attributes.theme
294
+ : "default"
295
+ };
296
+
297
+ // Final validation
298
+ const validation = typia.validate<{{titleCase}}Attributes>(migrated);
299
+ if (!validation.success) {
300
+ const error = new Error('Best-effort migration validation failed');
301
+ console.error('❌ Best-effort migration failed:', validation.errors);
302
+ // Return minimal valid structure
303
+ return {
304
+ data: null,
305
+ context: {
306
+ originalAttributes: attributes,
307
+ timestamp: new Date().toISOString(),
308
+ migrationPath: ['BestEffort'],
309
+ rollbackPossible: false
310
+ },
311
+ rollback: () => attributes,
312
+ success: false,
313
+ error
314
+ };
315
+ }
316
+
317
+ console.log('✅ Best-effort migration successful');
318
+ return {
319
+ data: migrated,
320
+ context: {
321
+ originalAttributes: attributes,
322
+ timestamp: new Date().toISOString(),
323
+ migrationPath: ['BestEffort'],
324
+ rollbackPossible: true
325
+ },
326
+ rollback: () => attributes,
327
+ success: true
328
+ };
329
+ }
330
+
331
+ /**
332
+ * Generate migration report
333
+ */
334
+ export function generateMigrationReport(oldAttrs: any, newAttrs: {{titleCase}}Attributes) {
335
+ const report = {
336
+ timestamp: new Date().toISOString(),
337
+ fromVersion: detectVersion(oldAttrs),
338
+ toVersion: "3.0.0",
339
+ changes: [] as string[],
340
+ warnings: [] as string[]
341
+ };
342
+
343
+ // Detect changes
344
+ if (!oldAttrs.id && newAttrs.id) {
345
+ report.changes.push('Added unique ID');
346
+ }
347
+
348
+ if (!oldAttrs.version && newAttrs.version) {
349
+ report.changes.push('Added version tracking');
350
+ }
351
+
352
+ if (!oldAttrs.theme && newAttrs.theme) {
353
+ report.changes.push('Added theme support');
354
+ }
355
+
356
+ if (oldAttrs.content !== newAttrs.content) {
357
+ report.warnings.push('Content may have been modified during migration');
358
+ }
359
+
360
+ return report;
361
+ }
@@ -0,0 +1,40 @@
1
+ import { useBlockProps } from '@wordpress/block-editor';
2
+ import { {{titleCase}}Attributes } from './types';
3
+
4
+ interface SaveProps {
5
+ attributes: {{titleCase}}Attributes;
6
+ }
7
+
8
+ export default function Save({ attributes }: SaveProps) {
9
+ const blockProps = useBlockProps.save();
10
+
11
+ if (!attributes.isVisible) {
12
+ return null;
13
+ }
14
+
15
+ return (
16
+ <div
17
+ {...blockProps}
18
+ data-wp-interactive="{{namespace}}/{{slug}}"
19
+ data-wp-context={JSON.stringify({
20
+ content: attributes.content,
21
+ alignment: attributes.alignment
22
+ })}
23
+ >
24
+ <p
25
+ style={{ textAlign: attributes.alignment ?? 'left' }}
26
+ data-wp-bind--hidden="!state.isVisible"
27
+ data-wp-text="context.content"
28
+ >
29
+ {attributes.content}
30
+ </p>
31
+
32
+ <button
33
+ data-wp-on--click="actions.toggle"
34
+ data-wp-class--active="state.isActive"
35
+ >
36
+ Toggle
37
+ </button>
38
+ </div>
39
+ );
40
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * {{title}} Block Styles
3
+ * Generated with Typia validation
4
+ */
5
+
6
+ .wp-block-{{slugKebabCase}} {
7
+ /* Default block styles */
8
+ padding: 1rem;
9
+ margin: 1rem 0;
10
+
11
+ /* Alignment support */
12
+ &.has-text-align-left {
13
+ text-align: left;
14
+ }
15
+
16
+ &.has-text-align-center {
17
+ text-align: center;
18
+ }
19
+
20
+ &.has-text-align-right {
21
+ text-align: right;
22
+ }
23
+
24
+ &.has-text-align-justify {
25
+ text-align: justify;
26
+ }
27
+
28
+ /* Interactive states */
29
+ button {
30
+ background: #0073aa;
31
+ color: white;
32
+ border: none;
33
+ padding: 0.5rem 1rem;
34
+ border-radius: 4px;
35
+ cursor: pointer;
36
+ transition: all 0.2s ease;
37
+
38
+ &:hover {
39
+ background: #005a87;
40
+ }
41
+
42
+ &.active {
43
+ background: #00a32a;
44
+ }
45
+ }
46
+
47
+ /* Validation error display */
48
+ .block-error {
49
+ background: #f8d7da;
50
+ color: #721c24;
51
+ padding: 0.75rem;
52
+ border-radius: 4px;
53
+ margin-bottom: 1rem;
54
+ border: 1px solid #f5c6cb;
55
+
56
+ pre {
57
+ background: rgba(0, 0, 0, 0.05);
58
+ padding: 0.5rem;
59
+ border-radius: 2px;
60
+ font-size: 0.8rem;
61
+ margin: 0.5rem 0 0 0;
62
+ }
63
+ }
64
+
65
+ /* Block info */
66
+ .block-info {
67
+ font-size: 0.9rem;
68
+ color: #666;
69
+ font-style: italic;
70
+ margin-top: 0.5rem;
71
+ }
72
+ }
73
+
74
+ /* Editor-specific styles */
75
+ .wp-block[data-type="{{namespace}}/{{slug}}"] {
76
+ .wp-block-{{slugKebabCase}} {
77
+ border: 1px dashed #ddd;
78
+ border-radius: 4px;
79
+
80
+ &:hover {
81
+ border-color: #0073aa;
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,108 @@
1
+ import { tags } from "typia";
2
+
3
+ /**
4
+ * Versioned {{title}} attributes with migration support
5
+ */
6
+
7
+ // Version 1.0.0 - Initial version
8
+ export interface {{titleCase}}AttributesV1 {
9
+ /**
10
+ * Simple content field
11
+ */
12
+ content: string & tags.Default<"">;
13
+
14
+ /**
15
+ * Basic alignment
16
+ */
17
+ alignment?: ("left" | "center" | "right") & tags.Default<"left">;
18
+ }
19
+
20
+ // Version 2.0.0 - Added visibility and className
21
+ export interface {{titleCase}}AttributesV2 {
22
+ /**
23
+ * Enhanced content with length validation
24
+ */
25
+ content: string & tags.MinLength<0> & tags.MaxLength<1000> & tags.Default<"">;
26
+
27
+ /**
28
+ * Extended alignment options
29
+ */
30
+ alignment?: ("left" | "center" | "right" | "justify") & tags.Default<"left">;
31
+
32
+ /**
33
+ * Visibility toggle - NEW in v2
34
+ */
35
+ isVisible?: boolean & tags.Default<true>;
36
+
37
+ /**
38
+ * Custom CSS class - NEW in v2
39
+ */
40
+ className?: string & tags.MaxLength<100>;
41
+ }
42
+
43
+ // Version 3.0.0 - Current version with ID and version tracking
44
+ export interface {{titleCase}}AttributesV3 {
45
+ /**
46
+ * Unique identifier - NEW in v3
47
+ */
48
+ id?: string & tags.Format<"uuid">;
49
+
50
+ /**
51
+ * Version tracking - NEW in v3
52
+ */
53
+ version?: number & tags.Type<"uint32"> & tags.Default<3>;
54
+
55
+ /**
56
+ * Enhanced content with better validation
57
+ */
58
+ content: string & tags.MinLength<0> & tags.MaxLength<2000> & tags.Default<"">;
59
+
60
+ /**
61
+ * Extended alignment options
62
+ */
63
+ alignment?: ("left" | "center" | "right" | "justify") & tags.Default<"left">;
64
+
65
+ /**
66
+ * Visibility toggle
67
+ */
68
+ isVisible?: boolean & tags.Default<true>;
69
+
70
+ /**
71
+ * Custom CSS class with better validation
72
+ */
73
+ className?: string & tags.Pattern<"^[a-zA-Z][a-zA-Z0-9_-]*$"> & tags.MaxLength<50>;
74
+
75
+ /**
76
+ * Theme support - NEW in v3
77
+ */
78
+ theme?: ("default" | "dark" | "light" | "accent") & tags.Default<"default">;
79
+ }
80
+
81
+ // Current version type alias
82
+ export type {{titleCase}}Attributes = {{titleCase}}AttributesV3;
83
+
84
+ /**
85
+ * Version history and metadata
86
+ */
87
+ export const BLOCK_VERSIONS = {
88
+ "1.0.0": {
89
+ version: 1,
90
+ type: "{{titleCase}}AttributesV1",
91
+ deprecated: true,
92
+ breaking: false,
93
+ },
94
+ "2.0.0": {
95
+ version: 2,
96
+ type: "{{titleCase}}AttributesV2",
97
+ deprecated: true,
98
+ breaking: true, // Added new required structure
99
+ },
100
+ "3.0.0": {
101
+ version: 3,
102
+ type: "{{titleCase}}AttributesV3",
103
+ deprecated: false,
104
+ breaking: true, // Changed content max length
105
+ }
106
+ } as const;
107
+
108
+ export type BlockVersion = keyof typeof BLOCK_VERSIONS;
@@ -0,0 +1,45 @@
1
+ import { tags } from "typia";
2
+
3
+ /**
4
+ * {{title}} block attributes with Typia validation
5
+ */
6
+ export interface {{titleCase}}Attributes {
7
+ /**
8
+ * Unique identifier
9
+ */
10
+ id?: string & tags.Format<"uuid">;
11
+
12
+ /**
13
+ * Block version for migrations
14
+ */
15
+ version?: number & tags.Type<"uint32"> & tags.Default<1>;
16
+
17
+ /**
18
+ * Custom CSS class
19
+ */
20
+ className?: string & tags.MaxLength<100>;
21
+
22
+ /**
23
+ * Main content
24
+ */
25
+ content: string & tags.MinLength<0> & tags.MaxLength<1000> & tags.Default<"">;
26
+
27
+ /**
28
+ * Text alignment
29
+ */
30
+ alignment?: ("left" | "center" | "right" | "justify") & tags.Default<"left">;
31
+
32
+ /**
33
+ * Is the block visible
34
+ */
35
+ isVisible?: boolean & tags.Default<true>;
36
+ }
37
+
38
+ /**
39
+ * {{title}} interactivity state
40
+ */
41
+ export interface {{titleCase}}State {
42
+ isActive: boolean;
43
+ isLoading: boolean;
44
+ error?: string;
45
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Conditional classnames utility
3
+ */
4
+ export function classNames(...args: (string | Record<string, boolean> | undefined | null | false)[]): string {
5
+ const classes: string[] = [];
6
+
7
+ for (const arg of args) {
8
+ if (!arg) continue;
9
+
10
+ if (typeof arg === 'string') {
11
+ classes.push(arg);
12
+ } else if (typeof arg === 'object') {
13
+ for (const [key, value] of Object.entries(arg)) {
14
+ if (value) {
15
+ classes.push(key);
16
+ }
17
+ }
18
+ }
19
+ }
20
+
21
+ return classes.join(' ');
22
+ }
23
+
24
+ /**
25
+ * Generate block class with BEM methodology
26
+ */
27
+ export function blockClass(
28
+ blockName: string,
29
+ element?: string,
30
+ modifier?: string | Record<string, boolean>
31
+ ): string {
32
+ let className = blockName;
33
+
34
+ if (element) {
35
+ className += `__${element}`;
36
+ }
37
+
38
+ if (modifier) {
39
+ if (typeof modifier === 'string') {
40
+ className += `--${modifier}`;
41
+ } else {
42
+ for (const [mod, enabled] of Object.entries(modifier)) {
43
+ if (enabled) {
44
+ className += `--${mod}`;
45
+ }
46
+ }
47
+ }
48
+ }
49
+
50
+ return className;
51
+ }