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,140 @@
1
+ import { useSelect } from '@wordpress/data';
2
+ import { useState, useCallback, useEffect } from '@wordpress/element';
3
+ import { {{pascalCase}}Attributes, validate{{pascalCase}}Attributes, sanitize{{pascalCase}}Attributes } from './types';
4
+
5
+ /**
6
+ * Hook for managing block attributes with validation
7
+ */
8
+ export const use{{pascalCase}}Attributes = (initialAttributes: Partial<{{pascalCase}}Attributes>) => {
9
+ const [attributes, setAttributes] = useState<{{pascalCase}}Attributes>(() =>
10
+ sanitize{{pascalCase}}Attributes(initialAttributes)
11
+ );
12
+ const [validationErrors, setValidationErrors] = useState<string[]>([]);
13
+
14
+ const updateAttribute = useCallback((key: keyof {{pascalCase}}Attributes, value: any) => {
15
+ setAttributes(prev => {
16
+ const newAttrs = { ...prev, [key]: value };
17
+ const validation = validate{{pascalCase}}Attributes(newAttrs);
18
+
19
+ if (!validation.isValid) {
20
+ setValidationErrors(validation.errors);
21
+ } else {
22
+ setValidationErrors([]);
23
+ }
24
+
25
+ return validation.data || newAttrs;
26
+ });
27
+ }, []);
28
+
29
+ const updateAttributes = useCallback((updates: Partial<{{pascalCase}}Attributes>) => {
30
+ setAttributes(prev => {
31
+ const newAttrs = { ...prev, ...updates };
32
+ const validation = validate{{pascalCase}}Attributes(newAttrs);
33
+
34
+ if (!validation.isValid) {
35
+ setValidationErrors(validation.errors);
36
+ } else {
37
+ setValidationErrors([]);
38
+ }
39
+
40
+ return validation.data || newAttrs;
41
+ });
42
+ }, []);
43
+
44
+ return {
45
+ attributes,
46
+ setAttributes,
47
+ updateAttribute,
48
+ updateAttributes,
49
+ validationErrors,
50
+ isValid: validationErrors.length === 0
51
+ };
52
+ };
53
+
54
+ /**
55
+ * Hook for managing color schemes
56
+ */
57
+ export const useColorScheme = () => {
58
+ const colors = useSelect(select => select('core/block-editor').getColors()?.colors || []);
59
+ const gradients = useSelect(select => select('core/block-editor').getColors()?.gradients || []);
60
+
61
+ const getColorVariable = useCallback((color: string) => {
62
+ if (!color) return '';
63
+
64
+ // Check if it's a system color
65
+ const systemColor = colors.find((c: any) => c.slug === color);
66
+ if (systemColor) {
67
+ return `var(--wp--preset--color--${systemColor.slug})`;
68
+ }
69
+
70
+ // Return custom color as-is
71
+ return color;
72
+ }, [colors]);
73
+
74
+ const getGradientVariable = useCallback((gradient: string) => {
75
+ if (!gradient) return '';
76
+
77
+ // Check if it's a system gradient
78
+ const systemGradient = gradients.find((g: any) => g.slug === gradient);
79
+ if (systemGradient) {
80
+ return `var(--wp--preset--gradient--${systemGradient.slug})`;
81
+ }
82
+
83
+ // Return custom gradient as-is
84
+ return gradient;
85
+ }, [gradients]);
86
+
87
+ return { getColorVariable, getGradientVariable, colors, gradients };
88
+ };
89
+
90
+ /**
91
+ * Hook for responsive design
92
+ */
93
+ export const useResponsiveValue = <T>(values: { mobile?: T; tablet?: T; desktop?: T }, fallback: T) => {
94
+ const [deviceType, setDeviceType] = useState<'mobile' | 'tablet' | 'desktop'>('desktop');
95
+
96
+ useEffect(() => {
97
+ const updateDeviceType = () => {
98
+ const width = window.innerWidth;
99
+ if (width < 768) {
100
+ setDeviceType('mobile');
101
+ } else if (width < 1024) {
102
+ setDeviceType('tablet');
103
+ } else {
104
+ setDeviceType('desktop');
105
+ }
106
+ };
107
+
108
+ updateDeviceType();
109
+ window.addEventListener('resize', updateDeviceType);
110
+ return () => window.removeEventListener('resize', updateDeviceType);
111
+ }, []);
112
+
113
+ return values[deviceType] ?? fallback;
114
+ };
115
+
116
+ /**
117
+ * Hook for managing animation states
118
+ */
119
+ export const useAnimation = (animationType: string) => {
120
+ const [isAnimating, setIsAnimating] = useState(false);
121
+ const [animationClass, setAnimationClass] = useState('');
122
+
123
+ useEffect(() => {
124
+ if (animationType && animationType !== 'none') {
125
+ setAnimationClass(`wp-block-{{slugKebabCase}}--animate-${animationType}`);
126
+ setIsAnimating(true);
127
+
128
+ const timer = setTimeout(() => {
129
+ setIsAnimating(false);
130
+ }, 1000);
131
+
132
+ return () => clearTimeout(timer);
133
+ } else {
134
+ setAnimationClass('');
135
+ setIsAnimating(false);
136
+ }
137
+ }, [animationType]);
138
+
139
+ return { animationClass, isAnimating };
140
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { {{pascalCase}}Attributes } from './types';
5
+ import Edit from './edit';
6
+ import save from './save';
7
+ import metadata from './block.json';
8
+
9
+ export default {
10
+ attributes: metadata.attributes as {{pascalCase}}Attributes,
11
+ apiVersion: metadata.apiVersion,
12
+ name: metadata.name,
13
+ category: metadata.category as any,
14
+ title: metadata.title,
15
+ description: metadata.description,
16
+ example: metadata.example,
17
+ supports: metadata.supports,
18
+ icon: metadata.icon as any,
19
+ keywords: metadata.keywords,
20
+ textdomain: metadata.textdomain,
21
+ editorScript: metadata.editorScript,
22
+ editorStyle: metadata.editorStyle,
23
+ style: metadata.style,
24
+ viewScript: metadata.viewScript,
25
+ edit: Edit,
26
+ save,
27
+ };
@@ -0,0 +1,39 @@
1
+ import { useBlockProps, RichText } from '@wordpress/block-editor';
2
+ import { useColorScheme, useAnimation } from './hooks';
3
+ import type { {{pascalCase}}Attributes } from './types';
4
+
5
+ export default function Save({ attributes }: { attributes: {{pascalCase}}Attributes }) {
6
+ const { getColorVariable } = useColorScheme();
7
+ const { animationClass } = useAnimation(attributes.animation);
8
+
9
+ const blockProps = useBlockProps.save({
10
+ className: `wp-block-{{slugKebabCase}} wp-block-{{slugKebabCase}}--${attributes.fontSize} wp-block-{{slugKebabCase}}--align-${attributes.alignment} ${animationClass}`,
11
+ style: {
12
+ color: getColorVariable(attributes.textColor),
13
+ backgroundColor: getColorVariable(attributes.backgroundColor),
14
+ padding: `${attributes.padding.top}px ${attributes.padding.right}px ${attributes.padding.bottom}px ${attributes.padding.left}px`,
15
+ borderRadius: `${attributes.borderRadius}px`,
16
+ boxShadow: attributes.shadow.enabled
17
+ ? `${attributes.shadow.x}px ${attributes.shadow.y}px ${attributes.shadow.blur}px ${attributes.shadow.spread}px ${attributes.shadow.color}`
18
+ : 'none',
19
+ display: attributes.isVisible ? 'block' : 'none',
20
+ ...(attributes.customCSS ? { cssText: attributes.customCSS } : {})
21
+ },
22
+ 'data-unique-id': attributes.uniqueId,
23
+ 'data-animation': attributes.animation !== 'none' ? attributes.animation : undefined
24
+ });
25
+
26
+ return (
27
+ <div {...blockProps}>
28
+ <div
29
+ className={`{{cssClassName}}__content {{cssClassName}}__content--${attributes.fontSize}`}
30
+ style={{ textAlign: attributes.alignment }}
31
+ >
32
+ <RichText.Content
33
+ tagName="p"
34
+ value={attributes.content}
35
+ />
36
+ </div>
37
+ </div>
38
+ );
39
+ }
@@ -0,0 +1,224 @@
1
+ /**
2
+ * {{title}} Block Styles
3
+ */
4
+
5
+ .wp-block-{{slugKebabCase}} {
6
+ // Base styles
7
+ position: relative;
8
+ transition: all 0.3s ease;
9
+
10
+ // Font size variations
11
+ &--small {
12
+ font-size: var(--wp--preset--font-size--small, 14px);
13
+ }
14
+
15
+ &--medium {
16
+ font-size: var(--wp--preset--font-size--medium, 16px);
17
+ }
18
+
19
+ &--large {
20
+ font-size: var(--wp--preset--font-size--large, 24px);
21
+ }
22
+
23
+ &--xlarge {
24
+ font-size: var(--wp--preset--font-size--x-large, 36px);
25
+ }
26
+
27
+ // Alignment variations
28
+ &--align-left {
29
+ text-align: left;
30
+ margin-left: 0;
31
+ margin-right: auto;
32
+ }
33
+
34
+ &--align-center {
35
+ text-align: center;
36
+ margin-left: auto;
37
+ margin-right: auto;
38
+ }
39
+
40
+ &--align-right {
41
+ text-align: right;
42
+ margin-left: auto;
43
+ margin-right: 0;
44
+ }
45
+
46
+ &--align-justify {
47
+ text-align: justify;
48
+ }
49
+
50
+ // Content wrapper
51
+ .{{cssClassName}}__content {
52
+ position: relative;
53
+ z-index: 1;
54
+
55
+ p {
56
+ margin: 0;
57
+ padding: 0;
58
+ }
59
+
60
+ // Content font size variations
61
+ &--small {
62
+ font-size: 1em;
63
+ line-height: 1.4;
64
+ }
65
+
66
+ &--medium {
67
+ font-size: 1.1em;
68
+ line-height: 1.5;
69
+ }
70
+
71
+ &--large {
72
+ font-size: 1.2em;
73
+ line-height: 1.6;
74
+ }
75
+
76
+ &--xlarge {
77
+ font-size: 1.3em;
78
+ line-height: 1.7;
79
+ }
80
+ }
81
+
82
+ // Animation classes
83
+ &--animate-fadeIn {
84
+ animation: fadeIn 0.6s ease-in-out;
85
+ }
86
+
87
+ &--animate-slideUp {
88
+ animation: slideUp 0.6s ease-out;
89
+ }
90
+
91
+ &--animate-slideDown {
92
+ animation: slideDown 0.6s ease-out;
93
+ }
94
+
95
+ &--animate-zoomIn {
96
+ animation: zoomIn 0.6s ease-out;
97
+ }
98
+
99
+ // Animation keyframes
100
+ @keyframes fadeIn {
101
+ from {
102
+ opacity: 0;
103
+ }
104
+ to {
105
+ opacity: 1;
106
+ }
107
+ }
108
+
109
+ @keyframes slideUp {
110
+ from {
111
+ opacity: 0;
112
+ transform: translateY(30px);
113
+ }
114
+ to {
115
+ opacity: 1;
116
+ transform: translateY(0);
117
+ }
118
+ }
119
+
120
+ @keyframes slideDown {
121
+ from {
122
+ opacity: 0;
123
+ transform: translateY(-30px);
124
+ }
125
+ to {
126
+ opacity: 1;
127
+ transform: translateY(0);
128
+ }
129
+ }
130
+
131
+ @keyframes zoomIn {
132
+ from {
133
+ opacity: 0;
134
+ transform: scale(0.8);
135
+ }
136
+ to {
137
+ opacity: 1;
138
+ transform: scale(1);
139
+ }
140
+ }
141
+
142
+ // Hover effects
143
+ &:hover {
144
+ transform: translateY(-2px);
145
+
146
+ &.wp-block-{{slugKebab-case}}--shadow {
147
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1) !important;
148
+ }
149
+ }
150
+
151
+ // Focus styles
152
+ &:focus-within {
153
+ outline: 2px solid var(--wp--preset--color--primary, #0073aa);
154
+ outline-offset: 2px;
155
+ }
156
+
157
+ // Responsive design
158
+ @media (max-width: 768px) {
159
+ &--align-left,
160
+ &--align-center,
161
+ &--align-right {
162
+ text-align: left;
163
+ margin-left: 0;
164
+ margin-right: 0;
165
+ }
166
+
167
+ .{{cssClassName}}__content {
168
+ font-size: 0.9em;
169
+ }
170
+ }
171
+
172
+ @media (min-width: 769px) and (max-width: 1024px) {
173
+ .{{cssClassName}}__content {
174
+ font-size: 1em;
175
+ }
176
+ }
177
+
178
+ @media (min-width: 1025px) {
179
+ .{{cssClassName}}__content {
180
+ font-size: 1.1em;
181
+ }
182
+ }
183
+
184
+ // High contrast mode support
185
+ @media (prefers-contrast: high) {
186
+ border: 1px solid currentColor;
187
+
188
+ .{{cssClassName}}__content {
189
+ font-weight: 600;
190
+ }
191
+ }
192
+
193
+ // Reduced motion support
194
+ @media (prefers-reduced-motion: reduce) {
195
+ transition: none;
196
+
197
+ &--animate-fadeIn,
198
+ &--animate-slideUp,
199
+ &--animate-slideDown,
200
+ &--animate-zoomIn {
201
+ animation: none;
202
+ }
203
+
204
+ &:hover {
205
+ transform: none;
206
+ }
207
+ }
208
+
209
+ // Dark mode support
210
+ @media (prefers-color-scheme: dark) {
211
+ color-scheme: dark;
212
+ }
213
+
214
+ // Print styles
215
+ @media print {
216
+ box-shadow: none !important;
217
+ animation: none !important;
218
+
219
+ .{{cssClassName}}__content {
220
+ color: black !important;
221
+ background: white !important;
222
+ }
223
+ }
224
+ }
@@ -0,0 +1,34 @@
1
+ import { tags } from "typia";
2
+
3
+ export interface {{pascalCase}}Attributes {
4
+ content: string & tags.MinLength<1> & tags.MaxLength<1000> & tags.Default<"">;
5
+ alignment?: ('left' | 'center' | 'right' | 'justify') & tags.Default<"left">;
6
+ isVisible?: boolean & tags.Default<true>;
7
+ fontSize?: ('small' | 'medium' | 'large' | 'xlarge') & tags.Default<"medium">;
8
+ textColor?: string & tags.Default<"">;
9
+ backgroundColor?: string & tags.Default<"">;
10
+ padding?: {
11
+ top: number & tags.Default<0>;
12
+ right: number & tags.Default<0>;
13
+ bottom: number & tags.Default<0>;
14
+ left: number & tags.Default<0>;
15
+ };
16
+ borderRadius?: number & tags.Default<0>;
17
+ shadow?: {
18
+ enabled: boolean & tags.Default<false>;
19
+ x: number & tags.Default<0>;
20
+ y: number & tags.Default<4>;
21
+ blur: number & tags.Default<8>;
22
+ spread: number & tags.Default<0>;
23
+ color: string & tags.Default<"rgba(0, 0, 0, 0.1)">;
24
+ };
25
+ animation?: ('none' | 'fadeIn' | 'slideUp' | 'slideDown' | 'zoomIn') & tags.Default<"none">;
26
+ customCSS?: string & tags.Default<"">;
27
+ uniqueId?: string & tags.Default<"">;
28
+ }
29
+
30
+ export type {{pascalCase}}ValidationResult = {
31
+ isValid: boolean;
32
+ errors: string[];
33
+ data?: {{pascalCase}}Attributes;
34
+ };
@@ -0,0 +1,84 @@
1
+ import typia from "typia";
2
+ import { {{pascalCase}}Attributes, {{pascalCase}}ValidationResult } from "./types";
3
+
4
+ /**
5
+ * Validates and transforms block attributes
6
+ */
7
+ export const validate{{pascalCase}}Attributes = (attributes: any): {{pascalCase}}ValidationResult => {
8
+ try {
9
+ const validated = typia.validate<{{pascalCase}}Attributes>(attributes);
10
+
11
+ if (validated.success) {
12
+ return {
13
+ isValid: true,
14
+ errors: [],
15
+ data: validated.data
16
+ };
17
+ } else {
18
+ return {
19
+ isValid: false,
20
+ errors: validated.errors.map(e => `${e.path}: ${e.message}`),
21
+ data: attributes
22
+ };
23
+ }
24
+ } catch (error) {
25
+ return {
26
+ isValid: false,
27
+ errors: [`Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`],
28
+ data: attributes
29
+ };
30
+ }
31
+ };
32
+
33
+ /**
34
+ * Creates a safe copy of attributes with defaults
35
+ */
36
+ export const sanitize{{pascalCase}}Attributes = (attributes: Partial<{{pascalCase}}Attributes>): {{pascalCase}}Attributes => {
37
+ return {
38
+ content: attributes.content ?? "",
39
+ alignment: attributes.alignment ?? "left",
40
+ isVisible: attributes.isVisible ?? true,
41
+ fontSize: attributes.fontSize ?? "medium",
42
+ textColor: attributes.textColor ?? "",
43
+ backgroundColor: attributes.backgroundColor ?? "",
44
+ padding: {
45
+ top: attributes.padding?.top ?? 0,
46
+ right: attributes.padding?.right ?? 0,
47
+ bottom: attributes.padding?.bottom ?? 0,
48
+ left: attributes.padding?.left ?? 0
49
+ },
50
+ borderRadius: attributes.borderRadius ?? 0,
51
+ shadow: attributes.shadow ? {
52
+ enabled: attributes.shadow.enabled ?? false,
53
+ x: attributes.shadow.x ?? 0,
54
+ y: attributes.shadow.y ?? 4,
55
+ blur: attributes.shadow.blur ?? 8,
56
+ spread: attributes.shadow.spread ?? 0,
57
+ color: attributes.shadow.color ?? "rgba(0, 0, 0, 0.1)"
58
+ } : {
59
+ enabled: false,
60
+ x: 0,
61
+ y: 4,
62
+ blur: 8,
63
+ spread: 0,
64
+ color: "rgba(0, 0, 0, 0.1)"
65
+ },
66
+ animation: attributes.animation ?? "none",
67
+ customCSS: attributes.customCSS ?? "",
68
+ uniqueId: attributes.uniqueId ?? generateUniqueId()
69
+ };
70
+ };
71
+
72
+ /**
73
+ * Generate unique ID for block instances
74
+ */
75
+ const generateUniqueId = (): string => {
76
+ return `{{slugKebabCase}}-${Math.random().toString(36).substr(2, 9)}`;
77
+ };
78
+
79
+ /**
80
+ * Runtime type guard for checking if an object is {{pascalCase}}Attributes
81
+ */
82
+ export const is{{pascalCase}}Attributes = (obj: any): obj is {{pascalCase}}Attributes => {
83
+ return typia.is<{{pascalCase}}Attributes>(obj);
84
+ };
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "@wordpress/scripts/config/tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "declarationDir": "build/types"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["build/**/*"]
9
+ }
@@ -0,0 +1,85 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const defaultConfig = require("@wordpress/scripts/config/webpack.config");
4
+
5
+ class TypiaManifestAssetPlugin {
6
+ apply(compiler) {
7
+ compiler.hooks.thisCompilation.tap("TypiaManifestAssetPlugin", (compilation) => {
8
+ compilation.hooks.processAssets.tap(
9
+ {
10
+ name: "TypiaManifestAssetPlugin",
11
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
12
+ },
13
+ () => {
14
+ for (const entry of getManifestEntries()) {
15
+ if (compilation.getAsset(entry.outputPath)) {
16
+ continue;
17
+ }
18
+
19
+ compilation.emitAsset(
20
+ entry.outputPath,
21
+ new compiler.webpack.sources.RawSource(fs.readFileSync(entry.inputPath)),
22
+ );
23
+ }
24
+ },
25
+ );
26
+ });
27
+ }
28
+ }
29
+
30
+ function getManifestEntries() {
31
+ const entries = [];
32
+ const rootManifestPath = path.resolve(process.cwd(), "typia.manifest.json");
33
+
34
+ if (fs.existsSync(rootManifestPath)) {
35
+ entries.push({
36
+ inputPath: rootManifestPath,
37
+ outputPath: "typia.manifest.json",
38
+ });
39
+ }
40
+
41
+ const srcDir = path.resolve(process.cwd(), "src");
42
+ if (!fs.existsSync(srcDir)) {
43
+ return entries;
44
+ }
45
+
46
+ for (const inputPath of findManifestFiles(srcDir)) {
47
+ entries.push({
48
+ inputPath,
49
+ outputPath: path.relative(srcDir, inputPath),
50
+ });
51
+ }
52
+
53
+ return entries;
54
+ }
55
+
56
+ function findManifestFiles(directory) {
57
+ const manifestFiles = [];
58
+
59
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
60
+ const entryPath = path.join(directory, entry.name);
61
+
62
+ if (entry.isDirectory()) {
63
+ manifestFiles.push(...findManifestFiles(entryPath));
64
+ continue;
65
+ }
66
+ if (entry.isFile() && entry.name === "typia.manifest.json") {
67
+ manifestFiles.push(entryPath);
68
+ }
69
+ }
70
+
71
+ return manifestFiles;
72
+ }
73
+
74
+ module.exports = async () => {
75
+ const { default: UnpluginTypia } = await import("@typia/unplugin/webpack");
76
+
77
+ return {
78
+ ...defaultConfig,
79
+ plugins: [
80
+ UnpluginTypia(),
81
+ ...(defaultConfig.plugins || []),
82
+ new TypiaManifestAssetPlugin(),
83
+ ],
84
+ };
85
+ };
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "{{slugSnakeCase}}",
3
+ "version": "0.1.0",
4
+ "packageManager": "bun@1.3.10",
5
+ "description": "{{description}}",
6
+ "author": "{{author}}",
7
+ "license": "GPL-2.0-or-later",
8
+ "main": "build/index.js",
9
+ "scripts": {
10
+ "sync-types": "tsx scripts/sync-types-to-block-json.ts",
11
+ "prebuild": "bun run sync-types",
12
+ "build": "wp-scripts build",
13
+ "start": "bun run sync-types && wp-scripts start",
14
+ "dev": "bun run start",
15
+ "lint:js": "wp-scripts lint-js",
16
+ "lint:css": "wp-scripts lint-style",
17
+ "lint": "bun run lint:js && bun run lint:css",
18
+ "format": "wp-scripts format",
19
+ "typecheck": "tsc --noEmit"
20
+ },
21
+ "devDependencies": {
22
+ "ajv": "^8.18.0",
23
+ "@types/wordpress__block-editor": "^11.5.17",
24
+ "@types/wordpress__blocks": "^12.5.18",
25
+ "@types/wordpress__components": "^23.8.0",
26
+ "@wordpress/browserslist-config": "^6.42.0",
27
+ "@wordpress/scripts": "^30.22.0",
28
+ "@typia/unplugin": "^12.0.1",
29
+ "tsx": "^4.20.5",
30
+ "typescript": "^5.9.2",
31
+ "typia": "^12.0.1"
32
+ },
33
+ "dependencies": {
34
+ "@wordpress/block-editor": "^15.2.0",
35
+ "@wordpress/blocks": "^15.2.0",
36
+ "@wordpress/components": "^30.2.0",
37
+ "@wordpress/element": "^6.29.0",
38
+ "@wordpress/i18n": "^6.2.0",
39
+ "@wordpress/interactivity": "^6.29.0"
40
+ }
41
+ }